commit c478a783c00de3bc22dbf09ba689946c3fda37d2 Author: markpollack Date: Fri May 30 22:55:02 2008 +0000 Initial import! diff --git a/Spring.Net.2002.sln b/Spring.Net.2002.sln new file mode 100644 index 00000000..91b574fa --- /dev/null +++ b/Spring.Net.2002.sln @@ -0,0 +1,34 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.Tests.2002", "test\Spring\Spring.Core.Tests\Spring.Core.Tests.2002.csproj", "{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.2002", "src\Spring\Spring.Core\Spring.Core.2002.csproj", "{710961A3-0DF4-49E4-A26E-F5B9C044AC84}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aop.2002", "src\Spring\Spring.Aop\Spring.Aop.2002.csproj", "{828F16E3-20A4-4EC4-A8F4-CD95B8ED44C9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aop.Tests.2002", "test\Spring\Spring.Aop.Tests\Spring.Aop.Tests.2002.csproj", "{F856BCAE-421E-469A-B75F-E41E5BA7F160}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug-1.0 + ConfigName.1 = Release-1.0 + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug-1.0.ActiveCfg = Debug|.NET + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release-1.0.ActiveCfg = Release|.NET + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug-1.0.ActiveCfg = Debug|.NET + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug-1.0.Build.0 = Debug|.NET + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release-1.0.ActiveCfg = Release|.NET + {828F16E3-20A4-4EC4-A8F4-CD95B8ED44C9}.Debug-1.0.ActiveCfg = Debug|.NET + {828F16E3-20A4-4EC4-A8F4-CD95B8ED44C9}.Release-1.0.ActiveCfg = Release|.NET + {F856BCAE-421E-469A-B75F-E41E5BA7F160}.Debug-1.0.ActiveCfg = Debug|.NET + {F856BCAE-421E-469A-B75F-E41E5BA7F160}.Release-1.0.ActiveCfg = Release|.NET + EndGlobalSection + GlobalSection(SolutionItems) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/Spring.Net.2003.sln b/Spring.Net.2003.sln new file mode 100644 index 00000000..63d7a2c3 --- /dev/null +++ b/Spring.Net.2003.sln @@ -0,0 +1,226 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.2003", "src\Spring\Spring.Core\Spring.Core.2003.csproj", "{710961A3-0DF4-49E4-A26E-F5B9C044AC84}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.Tests.2003", "test\Spring\Spring.Core.Tests\Spring.Core.Tests.2003.csproj", "{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aop.2003", "src\Spring\Spring.Aop\Spring.Aop.2003.csproj", "{3A3A4E65-45A6-4B20-B460-0BEDC302C02C}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aop.Tests.2003", "test\Spring\Spring.Aop.Tests\Spring.Aop.Tests.2003.csproj", "{2111596A-0327-4C9D-8919-294FBD988A23}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Web.2003", "src\Spring\Spring.Web\Spring.Web.2003.csproj", "{BA4789EB-281A-48EA-8763-28B9F0596A18}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Web.Tests.2003", "test\Spring\Spring.Web.Tests\Spring.Web.Tests.2003.csproj", "{EB2587B7-8B26-4FBC-852A-4128D5CACAFC}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Services.2003", "src\Spring\Spring.Services\Spring.Services.2003.csproj", "{B58E34CF-6E70-481D-AC87-1BC2D13C21FB}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Services.Tests.2003", "test\Spring\Spring.Services.Tests\Spring.Services.Tests.2003.csproj", "{EB2687B7-8B26-4FBC-852A-4128D5CACAFC}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.2003", "src\Spring\Spring.Data\Spring.Data.2003.csproj", "{E10A3BED-795F-41CB-A4A8-8C4B342ACDDF}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.Tests.2003", "test\Spring\Spring.Data.Tests\Spring.Data.Tests.2003.csproj", "{C5585365-561C-4EC4-8956-87FFBD9AB1CD}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.Integration.Tests.2003", "test\Spring\Spring.Data.Integration.Tests\Spring.Data.Integration.Tests.2003.csproj", "{A9D3732A-D40B-4C35-80CE-9C4A53D2DEAD}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Testing.NUnit.2003", "src\Spring\Spring.Testing.NUnit\Spring.Testing.NUnit.2003.csproj", "{6B8639E3-88BB-4F7B-9F23-699E84C30D58}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.NHibernate.2003", "src\Spring\Spring.Data.NHibernate\Spring.Data.NHibernate.2003.csproj", "{E8467024-8AE3-44A4-BAAD-1D78747EC7BD}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.NHibernate.Integration.Tests.2003", "test\Spring\Spring.Data.NHibernate.Integration.Tests\Spring.Data.NHibernate.Integration.Tests.2003.csproj", "{8DA67FF2-C473-4329-8FF6-3747182196D7}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.NHibernate.Tests.2003", "test\Spring\Spring.Data.NHibernate.Tests\Spring.Data.NHibernate.Tests.2003.csproj", "{8755D942-7828-4971-835B-37A5992C06C9}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Testing.NUnit.Tests.2003", "test\Spring\Spring.Testing.NUnit.Tests\Spring.Testing.NUnit.Tests.2003.csproj", "{E2689B4A-A951-420E-A274-BE5EBCD5F8D8}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.NHibernate12.2003", "src\Spring\Spring.Data.NHibernate12\Spring.Data.NHibernate12.2003.csproj", "{2E12AE3E-5690-46A5-9F89-80F1D3004ADB}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Debug-1.1 = Debug-1.1 + Release = Release + Release-1.1 = Release-1.1 + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug.ActiveCfg = Debug-1.1|.NET + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug.Build.0 = Debug-1.1|.NET + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug-1.1.Build.0 = Debug-1.1|.NET + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release.ActiveCfg = Release-1.1|.NET + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release.Build.0 = Release-1.1|.NET + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release-1.1.ActiveCfg = Release-1.1|.NET + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release-1.1.Build.0 = Release-1.1|.NET + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug.ActiveCfg = Debug-1.1|.NET + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug.Build.0 = Debug-1.1|.NET + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug-1.1.Build.0 = Debug-1.1|.NET + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release.ActiveCfg = Release-1.1|.NET + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release.Build.0 = Release-1.1|.NET + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release-1.1.ActiveCfg = Release-1.1|.NET + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release-1.1.Build.0 = Release-1.1|.NET + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug.ActiveCfg = Debug-1.1|.NET + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug.Build.0 = Debug-1.1|.NET + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug-1.1.Build.0 = Debug-1.1|.NET + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release.ActiveCfg = Release-1.1|.NET + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release.Build.0 = Release-1.1|.NET + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release-1.1.ActiveCfg = Release-1.1|.NET + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release-1.1.Build.0 = Release-1.1|.NET + {2111596A-0327-4C9D-8919-294FBD988A23}.Debug.ActiveCfg = Debug-1.1|.NET + {2111596A-0327-4C9D-8919-294FBD988A23}.Debug.Build.0 = Debug-1.1|.NET + {2111596A-0327-4C9D-8919-294FBD988A23}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {2111596A-0327-4C9D-8919-294FBD988A23}.Debug-1.1.Build.0 = Debug-1.1|.NET + {2111596A-0327-4C9D-8919-294FBD988A23}.Release.ActiveCfg = Release-1.1|.NET + {2111596A-0327-4C9D-8919-294FBD988A23}.Release.Build.0 = Release-1.1|.NET + {2111596A-0327-4C9D-8919-294FBD988A23}.Release-1.1.ActiveCfg = Release-1.1|.NET + {2111596A-0327-4C9D-8919-294FBD988A23}.Release-1.1.Build.0 = Release-1.1|.NET + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug.ActiveCfg = Debug-1.1|.NET + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug.Build.0 = Debug-1.1|.NET + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug-1.1.Build.0 = Debug-1.1|.NET + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release.ActiveCfg = Release-1.1|.NET + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release.Build.0 = Release-1.1|.NET + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release-1.1.ActiveCfg = Release-1.1|.NET + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release-1.1.Build.0 = Release-1.1|.NET + {EB2587B7-8B26-4FBC-852A-4128D5CACAFC}.Debug.ActiveCfg = Debug-1.1|.NET + {EB2587B7-8B26-4FBC-852A-4128D5CACAFC}.Debug.Build.0 = Debug-1.1|.NET + {EB2587B7-8B26-4FBC-852A-4128D5CACAFC}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {EB2587B7-8B26-4FBC-852A-4128D5CACAFC}.Debug-1.1.Build.0 = Debug-1.1|.NET + {EB2587B7-8B26-4FBC-852A-4128D5CACAFC}.Release.ActiveCfg = Release-1.1|.NET + {EB2587B7-8B26-4FBC-852A-4128D5CACAFC}.Release.Build.0 = Release-1.1|.NET + {EB2587B7-8B26-4FBC-852A-4128D5CACAFC}.Release-1.1.ActiveCfg = Release-1.1|.NET + {EB2587B7-8B26-4FBC-852A-4128D5CACAFC}.Release-1.1.Build.0 = Release-1.1|.NET + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Debug.ActiveCfg = Debug-1.1|.NET + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Debug.Build.0 = Debug-1.1|.NET + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Debug-1.1.Build.0 = Debug-1.1|.NET + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Release.ActiveCfg = Release-1.1|.NET + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Release.Build.0 = Release-1.1|.NET + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Release-1.1.ActiveCfg = Release-1.1|.NET + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Release-1.1.Build.0 = Release-1.1|.NET + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Debug.ActiveCfg = Debug-1.1|.NET + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Debug.Build.0 = Debug-1.1|.NET + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Debug-1.1.Build.0 = Debug-1.1|.NET + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Release.ActiveCfg = Release-1.1|.NET + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Release.Build.0 = Release-1.1|.NET + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Release-1.1.ActiveCfg = Release-1.1|.NET + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Release-1.1.Build.0 = Release-1.1|.NET + {E10A3BED-795F-41CB-A4A8-8C4B342ACDDF}.Debug.ActiveCfg = Debug-1.1|.NET + {E10A3BED-795F-41CB-A4A8-8C4B342ACDDF}.Debug.Build.0 = Debug-1.1|.NET + {E10A3BED-795F-41CB-A4A8-8C4B342ACDDF}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {E10A3BED-795F-41CB-A4A8-8C4B342ACDDF}.Debug-1.1.Build.0 = Debug-1.1|.NET + {E10A3BED-795F-41CB-A4A8-8C4B342ACDDF}.Release.ActiveCfg = Release-1.1|.NET + {E10A3BED-795F-41CB-A4A8-8C4B342ACDDF}.Release.Build.0 = Release-1.1|.NET + {E10A3BED-795F-41CB-A4A8-8C4B342ACDDF}.Release-1.1.ActiveCfg = Release-1.1|.NET + {E10A3BED-795F-41CB-A4A8-8C4B342ACDDF}.Release-1.1.Build.0 = Release-1.1|.NET + {C5585365-561C-4EC4-8956-87FFBD9AB1CD}.Debug.ActiveCfg = Debug-1.1|.NET + {C5585365-561C-4EC4-8956-87FFBD9AB1CD}.Debug.Build.0 = Debug-1.1|.NET + {C5585365-561C-4EC4-8956-87FFBD9AB1CD}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {C5585365-561C-4EC4-8956-87FFBD9AB1CD}.Debug-1.1.Build.0 = Debug-1.1|.NET + {C5585365-561C-4EC4-8956-87FFBD9AB1CD}.Release.ActiveCfg = Release-1.1|.NET + {C5585365-561C-4EC4-8956-87FFBD9AB1CD}.Release.Build.0 = Release-1.1|.NET + {C5585365-561C-4EC4-8956-87FFBD9AB1CD}.Release-1.1.ActiveCfg = Release-1.1|.NET + {C5585365-561C-4EC4-8956-87FFBD9AB1CD}.Release-1.1.Build.0 = Release-1.1|.NET + {A9D3732A-D40B-4C35-80CE-9C4A53D2DEAD}.Debug.ActiveCfg = Debug-1.1|.NET + {A9D3732A-D40B-4C35-80CE-9C4A53D2DEAD}.Debug.Build.0 = Debug-1.1|.NET + {A9D3732A-D40B-4C35-80CE-9C4A53D2DEAD}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {A9D3732A-D40B-4C35-80CE-9C4A53D2DEAD}.Debug-1.1.Build.0 = Debug-1.1|.NET + {A9D3732A-D40B-4C35-80CE-9C4A53D2DEAD}.Release.ActiveCfg = Release-1.1|.NET + {A9D3732A-D40B-4C35-80CE-9C4A53D2DEAD}.Release.Build.0 = Release-1.1|.NET + {A9D3732A-D40B-4C35-80CE-9C4A53D2DEAD}.Release-1.1.ActiveCfg = Release-1.1|.NET + {A9D3732A-D40B-4C35-80CE-9C4A53D2DEAD}.Release-1.1.Build.0 = Release-1.1|.NET + {6B8639E3-88BB-4F7B-9F23-699E84C30D58}.Debug.ActiveCfg = Debug-1.1|.NET + {6B8639E3-88BB-4F7B-9F23-699E84C30D58}.Debug.Build.0 = Debug-1.1|.NET + {6B8639E3-88BB-4F7B-9F23-699E84C30D58}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {6B8639E3-88BB-4F7B-9F23-699E84C30D58}.Debug-1.1.Build.0 = Debug-1.1|.NET + {6B8639E3-88BB-4F7B-9F23-699E84C30D58}.Release.ActiveCfg = Release-1.1|.NET + {6B8639E3-88BB-4F7B-9F23-699E84C30D58}.Release.Build.0 = Release-1.1|.NET + {6B8639E3-88BB-4F7B-9F23-699E84C30D58}.Release-1.1.ActiveCfg = Release-1.1|.NET + {6B8639E3-88BB-4F7B-9F23-699E84C30D58}.Release-1.1.Build.0 = Release-1.1|.NET + {E8467024-8AE3-44A4-BAAD-1D78747EC7BD}.Debug.ActiveCfg = Debug-1.1|.NET + {E8467024-8AE3-44A4-BAAD-1D78747EC7BD}.Debug.Build.0 = Debug-1.1|.NET + {E8467024-8AE3-44A4-BAAD-1D78747EC7BD}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {E8467024-8AE3-44A4-BAAD-1D78747EC7BD}.Debug-1.1.Build.0 = Debug-1.1|.NET + {E8467024-8AE3-44A4-BAAD-1D78747EC7BD}.Release.ActiveCfg = Release-1.1|.NET + {E8467024-8AE3-44A4-BAAD-1D78747EC7BD}.Release.Build.0 = Release-1.1|.NET + {E8467024-8AE3-44A4-BAAD-1D78747EC7BD}.Release-1.1.ActiveCfg = Release-1.1|.NET + {E8467024-8AE3-44A4-BAAD-1D78747EC7BD}.Release-1.1.Build.0 = Release-1.1|.NET + {8DA67FF2-C473-4329-8FF6-3747182196D7}.Debug.ActiveCfg = Debug-1.1|.NET + {8DA67FF2-C473-4329-8FF6-3747182196D7}.Debug.Build.0 = Debug-1.1|.NET + {8DA67FF2-C473-4329-8FF6-3747182196D7}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {8DA67FF2-C473-4329-8FF6-3747182196D7}.Debug-1.1.Build.0 = Debug-1.1|.NET + {8DA67FF2-C473-4329-8FF6-3747182196D7}.Release.ActiveCfg = Release-1.1|.NET + {8DA67FF2-C473-4329-8FF6-3747182196D7}.Release.Build.0 = Release-1.1|.NET + {8DA67FF2-C473-4329-8FF6-3747182196D7}.Release-1.1.ActiveCfg = Release-1.1|.NET + {8DA67FF2-C473-4329-8FF6-3747182196D7}.Release-1.1.Build.0 = Release-1.1|.NET + {8755D942-7828-4971-835B-37A5992C06C9}.Debug.ActiveCfg = Debug-1.1|.NET + {8755D942-7828-4971-835B-37A5992C06C9}.Debug.Build.0 = Debug-1.1|.NET + {8755D942-7828-4971-835B-37A5992C06C9}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {8755D942-7828-4971-835B-37A5992C06C9}.Debug-1.1.Build.0 = Debug-1.1|.NET + {8755D942-7828-4971-835B-37A5992C06C9}.Release.ActiveCfg = Release-1.1|.NET + {8755D942-7828-4971-835B-37A5992C06C9}.Release.Build.0 = Release-1.1|.NET + {8755D942-7828-4971-835B-37A5992C06C9}.Release-1.1.ActiveCfg = Release-1.1|.NET + {8755D942-7828-4971-835B-37A5992C06C9}.Release-1.1.Build.0 = Release-1.1|.NET + {E2689B4A-A951-420E-A274-BE5EBCD5F8D8}.Debug.ActiveCfg = Debug-1.1|.NET + {E2689B4A-A951-420E-A274-BE5EBCD5F8D8}.Debug.Build.0 = Debug-1.1|.NET + {E2689B4A-A951-420E-A274-BE5EBCD5F8D8}.Debug-1.1.ActiveCfg = Debug-1.1|.NET + {E2689B4A-A951-420E-A274-BE5EBCD5F8D8}.Debug-1.1.Build.0 = Debug-1.1|.NET + {E2689B4A-A951-420E-A274-BE5EBCD5F8D8}.Release.ActiveCfg = Release-1.1|.NET + {E2689B4A-A951-420E-A274-BE5EBCD5F8D8}.Release.Build.0 = Release-1.1|.NET + {E2689B4A-A951-420E-A274-BE5EBCD5F8D8}.Release-1.1.ActiveCfg = Release-1.1|.NET + {E2689B4A-A951-420E-A274-BE5EBCD5F8D8}.Release-1.1.Build.0 = Release-1.1|.NET + {2E12AE3E-5690-46A5-9F89-80F1D3004ADB}.Debug.ActiveCfg = Debug|.NET + {2E12AE3E-5690-46A5-9F89-80F1D3004ADB}.Debug.Build.0 = Debug|.NET + {2E12AE3E-5690-46A5-9F89-80F1D3004ADB}.Debug-1.1.ActiveCfg = Debug|.NET + {2E12AE3E-5690-46A5-9F89-80F1D3004ADB}.Debug-1.1.Build.0 = Debug|.NET + {2E12AE3E-5690-46A5-9F89-80F1D3004ADB}.Release.ActiveCfg = Release|.NET + {2E12AE3E-5690-46A5-9F89-80F1D3004ADB}.Release.Build.0 = Release|.NET + {2E12AE3E-5690-46A5-9F89-80F1D3004ADB}.Release-1.1.ActiveCfg = Release|.NET + {2E12AE3E-5690-46A5-9F89-80F1D3004ADB}.Release-1.1.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(SolutionItems) = postSolution + doc\NamespaceSummary.xml = doc\NamespaceSummary.xml + Spring.build = Spring.build + Spring.include = Spring.include + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/Spring.Net.2005.sln b/Spring.Net.2005.sln new file mode 100644 index 00000000..2b9e2f92 --- /dev/null +++ b/Spring.Net.2005.sln @@ -0,0 +1,325 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.2005", "src\Spring\Spring.Core\Spring.Core.2005.csproj", "{710961A3-0DF4-49E4-A26E-F5B9C044AC84}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.Tests.2005", "test\Spring\Spring.Core.Tests\Spring.Core.Tests.2005.csproj", "{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aop.2005", "src\Spring\Spring.Aop\Spring.Aop.2005.csproj", "{3A3A4E65-45A6-4B20-B460-0BEDC302C02C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aop.Tests.2005", "test\Spring\Spring.Aop.Tests\Spring.Aop.Tests.2005.csproj", "{2111596A-0327-4C9D-8919-294FBD988A23}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F04753EF-7A1B-4837-AB63-8C0821E8155D}" + ProjectSection(SolutionItems) = preProject + Spring.build = Spring.build + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Services.2005", "src\Spring\Spring.Services\Spring.Services.2005.csproj", "{B58E34CF-6E70-481D-AC87-1BC2D13C21FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Services.Tests.2005", "test\Spring\Spring.Services.Tests\Spring.Services.Tests.2005.csproj", "{EB2687B7-8B26-4FBC-852A-4128D5CACAFC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Web.2005", "src\Spring\Spring.Web\Spring.Web.2005.csproj", "{BA4789EB-281A-48EA-8763-28B9F0596A18}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Web.Tests.2005", "test\Spring\Spring.Web.Tests\Spring.Web.Tests.2005.csproj", "{C67E47AA-1ACD-41B4-A465-4D336A2319CA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.2005", "src\Spring\Spring.Data\Spring.Data.2005.csproj", "{AE00E5AB-C39A-436F-86D2-33BFE33E2E40}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.Tests.2005", "test\Spring\Spring.Data.Tests\Spring.Data.Tests.2005.csproj", "{ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.Integration.Tests.2005", "test\Spring\Spring.Data.Integration.Tests\Spring.Data.Integration.Tests.2005.csproj", "{91766D21-C568-459F-9BEA-759B011F23CF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Web.Extensions.2005", "src\Spring\Spring.Web.Extensions\Spring.Web.Extensions.2005.csproj", "{E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Testing.NUnit.2005", "src\Spring\Spring.Testing.NUnit\Spring.Testing.NUnit.2005.csproj", "{ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.NHibernate.2005", "src\Spring\Spring.Data.NHibernate\Spring.Data.NHibernate.2005.csproj", "{130E1609-45A7-4F65-B112-105F2DD3E2CE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.NHibernate.Integration.Tests.2005", "test\Spring\Spring.Data.NHibernate.Integration.Tests\Spring.Data.NHibernate.Integration.Tests.2005.csproj", "{4A07E150-ED90-407C-8CAD-4760444DDFD9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.NHibernate12.2005", "src\Spring\Spring.Data.NHibernate12\Spring.Data.NHibernate12.2005.csproj", "{90F2D070-6F98-4926-A626-BD7A6071D6D9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.NHibernate.Tests.2005", "test\Spring\Spring.Data.NHibernate.Tests\Spring.Data.NHibernate.Tests.2005.csproj", "{94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Testing.NUnit.Tests.2005", "test\Spring\Spring.Testing.NUnit.Tests\Spring.Testing.NUnit.Tests.2005.csproj", "{4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Scheduling.Quartz.2005", "src\Spring\Spring.Scheduling.Quartz\Spring.Scheduling.Quartz.2005.csproj", "{E823D54C-CE82-4868-929F-5F95A999F61E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Scheduling.Quartz.Tests.2005", "test\Spring\Spring.Scheduling.Quartz.Tests\Spring.Scheduling.Quartz.Tests.2005.csproj", "{9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Messaging.Nms.2005", "src\Spring\Spring.Messaging.Nms\Spring.Messaging.Nms.2005.csproj", "{AEB1578C-9018-4D49-B440-789F38DD2F29}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Messaging.Nms.Tests.2005", "test\Spring\Spring.Messaging.Nms.Tests\Spring.Messaging.Nms.Tests.2005.csproj", "{FA7A6931-7DBE-4A32-A312-51FAD2E80332}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|.NET = Debug|.NET + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Release|.NET = Release|.NET + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|.NET.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|.NET.Build.0 = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|.NET.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|.NET.Build.0 = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.Build.0 = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|.NET.ActiveCfg = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|.NET.Build.0 = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|.NET.ActiveCfg = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|.NET.Build.0 = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Any CPU.Build.0 = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|.NET.ActiveCfg = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|.NET.Build.0 = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|.NET.ActiveCfg = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|.NET.Build.0 = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Any CPU.Build.0 = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Debug|.NET.ActiveCfg = Debug|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Debug|.NET.Build.0 = Debug|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Release|.NET.ActiveCfg = Release|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Release|.NET.Build.0 = Release|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Release|Any CPU.Build.0 = Release|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2111596A-0327-4C9D-8919-294FBD988A23}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Debug|.NET.ActiveCfg = Debug|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Debug|.NET.Build.0 = Debug|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Release|.NET.ActiveCfg = Release|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Release|.NET.Build.0 = Release|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Release|Any CPU.Build.0 = Release|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Debug|.NET.ActiveCfg = Debug|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Debug|.NET.Build.0 = Debug|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Release|.NET.ActiveCfg = Release|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Release|.NET.Build.0 = Release|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Release|Any CPU.Build.0 = Release|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|.NET.ActiveCfg = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|.NET.Build.0 = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|.NET.ActiveCfg = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|.NET.Build.0 = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Any CPU.Build.0 = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|.NET.ActiveCfg = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|.NET.Build.0 = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|.NET.ActiveCfg = Release|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|.NET.Build.0 = Release|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|Any CPU.Build.0 = Release|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|.NET.ActiveCfg = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|.NET.Build.0 = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|.NET.ActiveCfg = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|.NET.Build.0 = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Any CPU.Build.0 = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|.NET.ActiveCfg = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|.NET.Build.0 = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Release|.NET.ActiveCfg = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Release|.NET.Build.0 = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Release|Any CPU.Build.0 = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Debug|.NET.ActiveCfg = Debug|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Debug|.NET.Build.0 = Debug|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Release|.NET.ActiveCfg = Release|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Release|.NET.Build.0 = Release|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Release|Any CPU.Build.0 = Release|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {91766D21-C568-459F-9BEA-759B011F23CF}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Debug|.NET.ActiveCfg = Debug|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Debug|.NET.Build.0 = Debug|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Release|.NET.ActiveCfg = Release|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Release|.NET.Build.0 = Release|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Release|Any CPU.Build.0 = Release|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Debug|.NET.ActiveCfg = Debug|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Debug|.NET.Build.0 = Debug|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Release|.NET.ActiveCfg = Release|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Release|.NET.Build.0 = Release|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Release|Any CPU.Build.0 = Release|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|.NET.ActiveCfg = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|.NET.Build.0 = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|.NET.ActiveCfg = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|.NET.Build.0 = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|Any CPU.Build.0 = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Debug|.NET.ActiveCfg = Debug|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Debug|.NET.Build.0 = Debug|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Release|.NET.ActiveCfg = Release|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Release|.NET.Build.0 = Release|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Release|Any CPU.Build.0 = Release|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {4A07E150-ED90-407C-8CAD-4760444DDFD9}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Debug|.NET.ActiveCfg = Debug|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Debug|.NET.Build.0 = Debug|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Release|.NET.ActiveCfg = Release|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Release|.NET.Build.0 = Release|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Release|Any CPU.Build.0 = Release|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {90F2D070-6F98-4926-A626-BD7A6071D6D9}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Debug|.NET.ActiveCfg = Debug|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Debug|.NET.Build.0 = Debug|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Release|.NET.ActiveCfg = Release|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Release|.NET.Build.0 = Release|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Release|Any CPU.Build.0 = Release|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Debug|.NET.ActiveCfg = Debug|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Debug|.NET.Build.0 = Debug|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Release|.NET.ActiveCfg = Release|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Release|.NET.Build.0 = Release|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Release|Any CPU.Build.0 = Release|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Debug|.NET.ActiveCfg = Debug|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Release|.NET.ActiveCfg = Release|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Release|Any CPU.Build.0 = Release|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E823D54C-CE82-4868-929F-5F95A999F61E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Debug|.NET.ActiveCfg = Debug|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Release|.NET.ActiveCfg = Release|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Release|Any CPU.Build.0 = Release|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {AEB1578C-9018-4D49-B440-789F38DD2F29}.Debug|.NET.ActiveCfg = Debug|Any CPU + {AEB1578C-9018-4D49-B440-789F38DD2F29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEB1578C-9018-4D49-B440-789F38DD2F29}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEB1578C-9018-4D49-B440-789F38DD2F29}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {AEB1578C-9018-4D49-B440-789F38DD2F29}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {AEB1578C-9018-4D49-B440-789F38DD2F29}.Release|.NET.ActiveCfg = Release|Any CPU + {AEB1578C-9018-4D49-B440-789F38DD2F29}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEB1578C-9018-4D49-B440-789F38DD2F29}.Release|Any CPU.Build.0 = Release|Any CPU + {AEB1578C-9018-4D49-B440-789F38DD2F29}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {AEB1578C-9018-4D49-B440-789F38DD2F29}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FA7A6931-7DBE-4A32-A312-51FAD2E80332}.Debug|.NET.ActiveCfg = Debug|Any CPU + {FA7A6931-7DBE-4A32-A312-51FAD2E80332}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA7A6931-7DBE-4A32-A312-51FAD2E80332}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA7A6931-7DBE-4A32-A312-51FAD2E80332}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FA7A6931-7DBE-4A32-A312-51FAD2E80332}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FA7A6931-7DBE-4A32-A312-51FAD2E80332}.Release|.NET.ActiveCfg = Release|Any CPU + {FA7A6931-7DBE-4A32-A312-51FAD2E80332}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA7A6931-7DBE-4A32-A312-51FAD2E80332}.Release|Any CPU.Build.0 = Release|Any CPU + {FA7A6931-7DBE-4A32-A312-51FAD2E80332}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FA7A6931-7DBE-4A32-A312-51FAD2E80332}.Release|Mixed Platforms.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + NAntAddinLastFileName = Spring.build + EndGlobalSection +EndGlobal diff --git a/Spring.build b/Spring.build new file mode 100644 index 00000000..301b842f --- /dev/null +++ b/Spring.build @@ -0,0 +1,1301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + > + + + + + + + + + + + + + + + + + + + + + + .NET 2.0 Build Skipped for Package + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Spring.include b/Spring.include new file mode 100644 index 00000000..900c7a40 --- /dev/null +++ b/Spring.include @@ -0,0 +1,434 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-support/CruiseControl.xml b/build-support/CruiseControl.xml new file mode 100644 index 00000000..154e3866 --- /dev/null +++ b/build-support/CruiseControl.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-support/install-schema.build b/build-support/install-schema.build new file mode 100644 index 00000000..9b39f8f7 --- /dev/null +++ b/build-support/install-schema.build @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +t + + + + + + + + + + \ No newline at end of file diff --git a/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia new file mode 100644 index 00000000..56c6b66d --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia @@ -0,0 +1,4150 @@ +Comment +Comment +Code Folding Region +Code Folding Region +Comment +Code Folding Region +Comment +Code Folding Region +Comment +Set Variable +Set Variable +Compiler Variable If +If +Set Variable +Set Variable +End +Compiler Variable End +Code Folding Region +Comment +Code Folding Region +Comment +If +Display Dialog +If +Terminate Install +End +Display Dialog +Compiler Variable If +If +Set Variable +Set Variable +(Un)Install MSI Setup +If +MessageBox +Terminate Install +End +If +MessageBox +If +Reboot and Resume +Else +Terminate Install +End +End +Set Variable +End +Compiler Variable End +Hide Dialog +End +Code Folding Region +Comment +Code Folding Region +Comment +Code Folding Region +Comment +Define Component +Comment +Comment +Get System Settings +Get Folder Location +Get Folder Location +Get Folder Location +Get Folder Location +If +Set Variable +End +Get Folder Location +Get Folder Location +Get Folder Location +Get Folder Location +Get Folder Location +Code Folding Region +Comment +Code Folding Region +If +GoTo Label +Else +Compiler Variable If +Comment +Set Variable +Set Variable +Compiler Variable End +End +Comment +Label +Display Dialog +If +GoTo Label +End +Compiler Variable If +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Comment +If +Set Component State +Else +If +Set Component State +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Compiler Variable End +Label +Display Dialog +If +Compiler Variable If +GoTo Label +Compiler Variable Else +GoTo Label +Compiler Variable End +Else +GoTo Label +End +Comment +Label +Comment +Wizard Loop +Display Dialog +Display Dialog +Display Dialog +End +Code Folding Region +Comment +Code Folding Region +Label +Comment +If +Terminate Install +End +Comment +Comment +Set Variable +Set Variable +Set Variable +If +Set Variable +Else +Set Variable +End +Display Dialog +Comment +Comment +Code Folding Region +If +Comment +Comment +Apply Changes +Set Variable +Else +Code Folding Region +Code Folding Region +Comment +Create Shortcut +Web Media Block +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Web Media Block +Get Component State +If +End +Comment +Compiler Variable If +Apply Patch +Compiler Variable Else +If +Apply Changes +Else +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Apply Changes +End +Compiler Variable End +Set Variable +End +Code Folding Region +Code Folding Region +Comment +Code Folding Region +Comment +Hide Dialog +Display Dialog +If +If +If +Reboot Computer +End +End +If +If +If +Comment +End +End +End +End +Code Folding Region +Comment +$ +{0AADAE47-CCD6-4CC9-BF83-32297AB40C37} +{36ADC9A6-0B4F-4245-ACCE-4A50015A80A8} +{9FF95FFA-C00A-4F12-B05E-DB5ED37B659C} +{A4EDA82B-1FE9-4E39-AD3E-B88DCA4DBCC2} +{5076ABBD-5015-4CE7-BD04-2E313FB65264} +{B9949A57-3226-461B-910B-04EED077BFD2} +{B51103C7-8FD1-43CD-9553-57945F20EDE8} +{CDBD7138-E76A-4A1F-A427-97837EAF89CB} +{7A4D8295-AAA0-4D43-8542-2746B3A3CB7E} +{B62B7C2B-4668-4E4E-87C7-CC1525784712} +{C7F738FC-2B5F-4380-9608-0DEA772B94C7} +{8E733ADF-0692-4381-A6CE-1A14FC3B3EFD} +{E3B49129-FB7E-494B-8868-3A7E035A1BB6} +{95C7D535-8788-41B6-B8CA-D3742CDD3E2A} +{DD78225E-3B68-40CE-B4E1-93AE4BE9C37F} +{15A07272-08A0-428D-A9BD-D9B536BFE301} +{73F169C4-5CC8-4F30-9586-F6C6550442B3} +{DCC35A72-84BC-4C7A-97BA-17F3C2C6B183} +{4D692B2D-36CC-437E-8B49-F054053A65B8} +{CFCA2177-B5D4-4498-9341-74DA4A2BF10E} +{007D81FD-F462-41C0-9766-5CA98DB29B4A} +{B0E8FF3A-6587-4329-AD90-A5E8BD6B61D5} +{2F15D329-6E80-4509-829F-C2F3352970ED} +{CC974A69-AA46-4F01-934D-3AD31BE3EA59} +{BA863E14-FDA9-4832-8A5C-8D3C6133D31C} +{F8340D11-FC61-41E9-9E3F-6BC558DF4B68} +{5B250495-2FD6-4B4D-BE79-23040614AF44} +{A07B9E2D-B4D2-4987-9814-D9E37F8042F5} +{B318EA01-0253-43B2-8A21-BB05E733C066} +{D4F36731-7336-4097-A355-B0B1584CBB44} +{CAC97A56-5D1A-405B-A78B-FE91751C7B09} +{0C995B1F-7720-416B-8062-9D30A5BF4199} +{C81A209F-B1AF-42AC-9982-8EC8CCC33464} +{18516BF5-FA02-4D7B-BFD4-07714E2BC054} +{345A0328-D054-4691-8073-84166C595BA5} +{B5CCDB38-F907-47AB-B552-39C7A2B2F100} +{BC322E68-8DCC-4C68-88F9-B1B2E20C4180} +{EDE1F9CC-5316-44E4-8FEA-FE44558E9C84} +{35873C23-DAB9-48D3-AD16-0C5140653771} +{DDD28604-5F9E-464E-B26B-3F7F61EBB045} +{373E3B8A-6592-4D42-9470-D0A5E9A6460E} +{389865AC-1C56-47A5-8B49-B12BDF111071} +{45CF9BB8-19DD-4841-8822-9B5E47CD95A6} +{D6BA809F-6E50-4518-A1E8-9B88CBA27CC7} +{B47D5127-9D59-471B-992B-F006D44A3F2E} +{3BA88535-90E4-471C-8DE1-67DD268DAF38} +{505F5EB8-1FC9-43BD-8064-B0E68BE663CA} +{04ABB7B7-0915-407F-9520-6EEF5B5B6E4F} +{90D54643-FAC1-4FBC-966D-507F1D2B43FB} +{34379777-3444-4208-B99D-DE846461A34C} +{8EE4916B-990A-45A0-91FC-AE6E73BAD205} +{BEE14656-9B19-4E10-A494-FC09B7BE2537} +{2D37F8AF-D65E-43A0-ADFF-D492484E9BB4} +{543486BF-8C7F-46E6-B5E4-AF60671E5A79} +{C65B04D2-F38D-4815-A963-34D67AD027BD} +{A3A8A528-F4F5-4031-8308-7F556B64C129} +{A7335220-876F-4C84-8F58-4C7CF63DAB43} +{5232CF1A-2118-413C-BF18-594406FC60A0} +{E4393F3D-D71C-4B0C-BE63-043394AC87F4} +{89AA7DF5-3C08-48DF-A638-DE6B27BB689A} +{DA7984D1-FFC6-46FD-AA05-CF9705718548} +{93E95378-FCD0-4AD8-859C-F79D0659B4D4} +{46249933-EAFF-48CC-83D4-A2B2D9A539DF} +{8A95A867-00ED-469E-B99D-5FD8ABA81ABF} +{7FFAA3DC-DDA0-4A59-BD77-84B4369F5078} +{7D1B9FBD-2CB8-477E-B19D-5291A9B0DB2F} +{73747553-3B70-47E7-A37E-EA9C3104CFB0} +{8628DC93-23A1-402B-9482-4986B2460DCA} +{3AC507BE-2DCF-4306-9D2A-2C8C6B346E2B} +{8759C800-4115-4C0F-B44B-6E4A0A3B7271} +{FEAA8A2C-3859-4A1D-B5BD-AFAE6A837273} +{8B8FFBAD-57B5-4C26-8914-C49244985EAB} +{17ED505D-22C9-4411-B251-BC1C7E4B1B5C} +{44378BF2-B6DF-4AD7-871D-466D3AE158D3} +{545506AF-96E2-44DE-B49C-3CDD3E25CE68} +{C435DFAE-2EF8-4D28-A93F-3683596F4C3B} +{A3C06778-3246-4A8B-B853-AF8247C33EDC} +{A5001B5C-1CBD-4C2E-9942-64B73BFE5D3B} +{7568CEA0-C91D-4D89-9091-98512904A1D1} +{9CC32567-D081-4F97-8377-F414F884749A} +{4632834D-AC50-48B5-A91B-503C03608212} +{4B7055B6-C079-427E-8928-FAFA59966F0F} +{BF47D251-C4D3-49C5-9CCC-C37F5AE762A0} +{7536F78B-2039-40DE-B54D-9796403C0C65} +{1D2C9F3A-3B3F-4046-8FC7-86624108C2B2} +{0D00582F-F56D-4053-B79E-DB07C8A799D6} +{A53F37B9-1DD2-4A99-956E-A8DE95E33B64} +{1F8EF880-D9B8-4C12-B266-347647DE74EF} +{20E4FDD0-E931-4A87-A6FD-2E57FD487532} +{41DDD353-69A3-4173-808D-E7BDD8083D38} +{0EB6104E-73F2-4562-B9A5-D4CCCAA77D07} +{F1D0F40E-DC0E-4CBB-B78C-3FB463D8671F} +{57144EC7-F2F4-450C-9821-C36B2AB9BEB2} +{E1E3E44C-9A28-48CC-9811-B4F8546B03E1} +{A4B3522B-6C94-4BB8-8E65-3E363A1B07E0} +{63E87256-B27A-4FA6-8D19-0907F58CD451} +{D0462D17-8893-4A99-B803-CEED288F68EB} +{234E1C20-A8B0-43AA-A22B-41F04B752A4F} +{86F727C2-2FE0-41B4-9FAE-EE51EC4D3AC0} +{799297B7-F787-4C53-B04B-D884C74F885E} +{048E3015-272E-475D-B275-CF0012C1B8FD} +{190404D4-7B8C-4A9F-83CA-1347A04CBF6B} +{A68F2EC8-98DD-40D3-8FF3-B8F5BDED323D} +{F526A141-F701-41E5-B9CB-695C074CCE51} +{2504B995-B89E-4EB1-8CD3-C1E4E32485C4} +{1B6047FE-C9FB-4C1B-864E-AF9A7AB0BA0E} +{D9BBD51B-CA5D-46F5-BE0C-900402CAA4A8} +{C1D5C0FA-6D30-4E46-BF90-F71ACA911D60} +{0391C72A-9386-4E58-B69A-CDA4F54F217F} +{69B3686B-22D9-4ECB-91EB-EA9EEFE2AD8F} +{8E64D5B8-C193-4954-B890-61E593B3F039} +{30103E98-4AC5-4ED0-B524-7CC586B358ED} +{A5486F28-FF0B-49ED-A1E8-463362714F25} +{26C60264-6250-46C2-9EB9-1618F808612B} +{1D0C8CC9-2523-41E9-96E0-3BA9F5B6AB8D} +{0C2750E8-5450-4F8E-98EC-8AEBFCAB2462} +{E4B8CFB6-5E34-4FE9-BCD0-6FD9AEADAA08} +{04063350-70E9-4E12-AF22-3963C42B1E60} +{ECC571C3-29CB-4AE6-97AC-C3F80801C5E1} +{DCAEEBD8-66B2-4000-A6D9-CD370CE687BF} +{E68D3F7A-911E-4FDD-934A-9DFFD6AC3A7B} +{EC3EA5A4-8374-412A-A5C1-B2012FC9FFBE} +{B2C9ED87-31EF-4A1B-BD2E-5DED878B210A} +{5F0100BF-0D58-4492-A8AD-FDC458184AB5} +{72C1054E-E429-44BB-8200-3F567FEFD2FE} +{8FCAF2C3-4F88-4E68-89A7-AA3F874086AE} +{12905F17-667A-4D90-AC96-73368992CC76} +{CB3E023C-7C18-4A1E-B510-FA5FDC56D130} +{5749130C-401B-46B6-A1F3-F227D549C673} +{E475EE89-9547-4C98-AA33-0D58B5884209} +{87BA9CDC-31A3-4AEB-8DB0-C1648A287FB2} +{EDB251B5-EFEA-4792-B040-60474F526530} +{14A8ED13-E1A4-42AF-8BBA-EFED617ED0A2} +{4668CE2B-265C-46C6-8A02-759A9B167BC7} +{A171750B-636E-46EB-A82D-AD3081D1E86E} +{30166F16-C73F-4F86-A149-2B99BA37A858} +{28FB5234-8E03-4416-8DA3-8C526C6948B9} +{3C96C8AA-EC8E-476E-8B5A-BD00441FE51E} +{723FB87B-D5CF-4631-9F6A-2BE1A60744DA} +{526008DA-26A8-482F-B6E6-C2AB1793FC9D} +{6E485127-81F0-4E78-A715-542B9BAC5541} +{542E4901-D6B4-4EAF-BD8B-B6096C2F1B3C} +{9501DCFC-E97B-440A-AA24-32AEB384AE8E} +{2A369571-BD93-4C90-92E6-9BA8BA06AA56} +{9115BE28-CD0B-452A-B055-23AF260640D1} +{2F676623-128C-4C34-A1A8-5F1EF0D91DCF} +{7BA3F47A-8115-4048-B47F-54FB1C8B39D2} +{B1DDCFC1-633C-4E49-9516-6E890012B8D4} +{5529E598-81EB-4901-A076-CE6F60CC6CEA} +{CF8730FE-B482-4030-9CBC-A32BCF9032A5} +{452C0F97-7CD5-41F7-9B6C-E0B2F7F662CA} +{C931C77D-104E-467B-BBEF-D0A8FD9A4A56} +{291FFEAD-958D-468E-850E-01B8A64FA4C2} +{11BF4FE6-C05D-448C-A103-BC52BA23CCA5} +{0FFDBB1C-5570-40C5-BC9D-A6A038498FDE} +{DFF6C745-2E12-471A-BF81-EB166CA8B45E} +{A6C0A248-8D0A-48A4-9EE6-2E320E35B86D} +{A6380038-73A6-40BC-B13B-BAD29A36226C} +{DA5F72B1-1E28-4654-89F4-2F5D09CB8A58} +{EC8C2D00-919B-4FFE-B72C-C05B35BE3992} +{BC245C08-2ED2-4E1A-B55C-32B85934E9B9} +{AB79DEAC-D232-45EC-B80F-D602F13F1FFB} +{DD1FC87B-3855-4370-8F31-64FDF70E4466} +{C772DEC6-6D67-4E8B-80A4-587DCFC11E04} +{C09182DB-2BDF-46A0-B6D7-6B6F6BF0F593} +{182CEBF3-0BB0-4BCC-9676-E08A05BCD094} +{85FC71FC-25EA-4ACB-B2ED-DCB1818F0810} +{E6D79DA8-0828-4949-84E1-381593D0BDDE} +{19B13FAF-F05D-4522-8258-31C76DF9D915} +{0C569AC1-5457-4616-A508-005FF24580C2} +{8090FC04-5B14-40FB-AF97-164E299B4A34} +{A6F2320E-A194-482A-A13B-8446A653F1BE} +{3135089D-17EA-4F51-8F35-1771FE4B61A8} +{A7CD16FD-3D01-4E95-AA29-DC23DFE119D5} +{D878A48E-1DC5-45C8-B282-0E36EA6D3473} +{D1AC32FD-4A2D-41DB-9A0E-FCDB2A5B68A6} +{41482B96-2613-47E5-ACCF-D9B854227C88} +{828D4366-191D-40E6-A954-98D6BC2AB6B1} +{2D7FCC84-17B1-4BC9-AE4A-AFE6F2561E74} +{00902F7E-9682-4ECC-97F3-0C989404753B} +{622D7A67-19E9-450F-9221-E0C455600021} +{55A2971A-4283-43A2-933F-94C005EBB5C3} +{E8D49994-8514-4EAF-8181-8DBE75A815D2} +{A4D3550A-6E2F-44F0-9AA2-CB3F558E5B28} +{43606175-29DB-4E0B-A271-A349EC540ADE} +{D77A811D-0421-4824-A290-02DA2F506DBC} +{CA901067-D569-4095-A422-B50D6AED708A} +{B9D9D104-3211-46BE-BEF5-92A881ED7D40} +{AD985BE7-9F53-40B3-8E58-EAC9A884FD52} +{D57BFAD1-D19C-43F6-B54F-1234265A07A3} +{D93C5C9D-3B2F-40F1-AF98-B66F194A35A6} +{632BF2EF-3839-4517-BC09-C542894D3B33} +{765C3B48-EAC5-4004-BFA2-CE4BF53F3650} +{B0014B6C-1116-4E41-BBCC-E2349D4C4038} +{347AE4EA-A715-4810-9AC4-C95A0AEE67C1} +{C7B19EE0-FA22-40B3-B94C-879879BACD77} +{61E88272-A2D3-4A54-8393-51AC2637C12B} +{8EA1DB5A-D36D-4391-A93F-52B824ECCBED} +{E445992E-4D8F-4D0E-B2C6-4C98E0FC7305} +{81F00F0D-5139-4A39-B1E5-5EA513828FBD} +{B502A097-C792-42A2-9007-80AC1988C851} +{34D7AF5E-CC1B-4885-ABFF-37CAE9F00B27} +{4BF2B76B-F5F9-4D46-BF67-779EE7EB56FA} +{D63830B2-3590-4DCF-A77B-F7917A2E7F79} +{90F49D4C-C196-4D02-AF50-E55A0BCFCA7C} +{730C72F5-779C-47C1-9DBF-4F7BC79D8C0A} +{EDEB4872-324A-4839-9037-B0EF3A6773A3} +{67C00CFC-AD88-4390-B122-4ABA4AB39E20} +{AB469D9A-56D5-4E0B-B13E-61BE4B70F773} +{6A1EEA3F-4B75-4692-A758-01F3B78BE905} +{8F5BEE7A-98A2-44E5-BB76-A2B68E108C87} +{9647B504-7C7A-48F9-85D8-BCF9F0B1A0AB} +{F3D1B9D2-7A04-44B6-9F91-860D3A2384C0} +{08508DC4-D337-40B1-93B8-52D813007FB1} +{C27E97F5-6F56-4072-950A-0E8C0ECD22E7} +{074A5CF9-90CF-405D-82D3-6FEE0690119C} +{E6B7586C-28EA-48D2-B7EB-17D42DBD1979} +{4CB85B60-D0A0-442A-A683-94039B70DDA0} +{346ECAFD-25C6-49CF-B532-765C963D176A} +{94BDA812-5706-4256-BB6A-E8F451677F49} +{52B94BDA-4FD4-4788-B28F-EEAB7C162721} +{29F7BFC5-6D34-4A12-9B34-C5805014863F} +{8862B1BF-C8BD-42FF-89D1-B0243A204059} +{C71A3471-6E8E-49B4-BA18-10A4C9564959} +{0359E329-55A1-4792-AB4E-AFEB1F335FF9} +{DE5B6D64-509C-4FC7-BE75-F9B569E4B1B6} +{28A3A0D3-1D22-4845-A97B-AC63DAF3413C} +{432A824B-4E2F-4901-AEAB-DEB65367D193} +{B60FA6EF-C69C-4EB8-9BBE-5EA91611D820} +{1BF94E6D-2B78-4961-9F69-6DBD6632BD3D} +{33DA50A7-F9E5-4CDA-B812-F54169CA8A89} +{712378F3-5C02-4F64-A618-073A000505CC} +{BEDC79F7-85D3-4837-881A-CF1A07342BB0} +{D2D412A9-D472-4A95-BBCB-7FC0C3B8B592} +{269A0E5E-A5B3-416F-B770-12532EE31550} +{30B2A744-1A12-4C9E-BE0F-0E9066E87AFD} +{0EA42729-AF8B-48C6-86E8-D11906E352CE} +{791D8D35-3FEA-4E19-9C77-6329148C64F9} +{5A4411F0-80E6-4F3B-BBB4-CA4A4C9B1096} +{2B0A12F4-18DD-421F-BCA7-9CB0925004B8} +{16B0C775-0F55-4F09-87BB-2264C7C72A11} +{BF19F5CC-67FB-406B-81C3-678CDD88258E} +{24BB4CEB-C041-4023-A448-3AE725EAEAC5} +{F8031368-5974-4C5C-8EA0-001C812628D5} +{EE9964D4-1BF6-48BE-BD08-1AA41E6CECD9} +{E607AA22-7A1D-4CA6-9FA6-FBEC7CAF505D} +{08D02487-7A25-4535-B2C3-D66D666F2884} +{59F13A6B-AF40-45F7-B7F8-9D4DA03320B1} +{D4618425-8024-415B-A39E-EA5A6A757E11} +{E927FC6B-DA58-4F6B-86DE-BEE46E7D6F24} +{3A2D9F38-3C4C-4909-BD58-F94AE94D60A6} +{0D9E47BC-3A38-457C-BD4C-D5BABE1E3168} +{0727C634-3C1B-445F-B8BE-C7D2D4E56FAB} +{C8E7DA02-29FE-4E5E-BA96-8E9B48E6D5F1} +{6028D781-1B56-4CF6-8567-B3266184A580} +{9A899381-224C-4960-8EE9-114927CF3331} +{5A52EB3A-E6DF-4A80-80ED-7479ECB6B587} +{49B7241A-38C5-4AFA-8816-2DF906B2E558} +{689F4852-777B-4865-B493-F42F05CF3EFA} +{8A3D44F9-A8F2-489B-B57E-CC9146833960} +{74EE6033-B67D-4559-A3CD-8EB3B5D601AB} +{ED19961A-2407-4353-B639-99DCA4187BCC} +{55E0BEAD-43B8-4A0F-A94C-D5D23B3C9250} +{5023B1E0-3EE7-4FE8-BDC7-6FE3B0F1CD32} +{F2F64080-E757-42C5-B023-C2B72DED1066} +{E44E20C1-1D79-49EE-A44E-3208D7C0AB49} +{BA9CE954-CC1E-4A8C-9AD5-E4A773242D09} +{3AB6811B-92B2-46F1-98F1-EA0F0B84E196} +{FBC19861-623F-4B92-9ECF-FEBD47DA4AC9} +{A9984778-D722-4BD6-9599-A6E2CA5FDE13} +{C47FA07F-CB64-4C9B-B31E-2469BA86C4E8} +{58C976E0-0F30-4334-AD68-90C293A50E8C} +{ED12CCAD-D43C-4CA0-B6FF-6EF6F1288B75} +{EA3B37A7-6C0A-4E39-BD02-FFD9DE8A1425} +{3AB54DCB-499F-4F7C-9916-6FB2B8D7F427} +{F8AA2809-D6D7-40DD-A34D-7C77EC57F208} +{248669E3-3B34-44F8-B5C6-D19512FAB392} +{5BC2F138-2564-4ADC-9606-8EE54694D6A3} +{ECA4D720-A9AA-41B4-8487-336179456282} +{C2B8D56A-7A67-454D-8DFE-8FA17FCA47F8} +{5A098B01-9784-414B-8B43-F702421DA9F3} +{B9024C07-45AD-45A5-B551-D70AF8A241A4} +{A38A7B60-A88E-4D09-A49E-0CBA4B8272EB} +{5105D931-6D6C-406C-9820-D47CAEDA5C68} +{37F91F10-6667-4967-B167-6CEABB7620C6} +{2B9C2915-A46E-4301-B118-787D8E969E9E} +{24393453-7A7C-4DC8-ADD6-44945E073D70} +{CE007102-289B-4AAD-ACC0-78947B9FA68C} +{97F657B1-22D5-4F55-9D77-8F7D23F8DD0D} +{33BBA500-00B2-4FB1-9535-C74505BFBE15} +{AC02E50D-0805-4672-B66E-42BC308B57E6} +{10A30C9B-AB0E-4622-B39F-2058816C8AC9} +{40E187E4-9812-43DF-BB93-F2729EC5F519} +{F43AAB92-2311-4285-95F5-FBD2503543C3} +{3EDD2AA2-2F08-414A-BD66-37240675D6DC} +{C8FC0883-E862-4A8F-8421-CEA1AF318888} +{E521F49A-503D-4708-9B8C-5F8D68D75CDC} +{93CF5997-A66F-4AA4-AAF5-BBAAD4F951E6} +{C63FE01E-7752-4B7B-9C32-AC04B4547229} +{92F5D05F-D8AA-4996-A5BC-84A8C0CD8C31} +{57388F2C-8AAE-468A-92AC-C6CADBAC344C} +{C3F2E78C-8C6E-41DE-8F22-3CEAFC16702E} +{D1099A76-F2D4-4ED8-9FF8-0CCC28759A8A} +{A55D92B4-821D-4AF4-92A1-EAC924FDEA79} +{D9204FF3-BEC7-47FD-9F8B-FA65852C8244} +{FBE8A4BD-F1AD-4CDC-9BCE-6FFEB7BE5B16} +{152AD548-99D6-4A5C-8941-2D3353EBF9E2} +{94C4A1C7-4455-4C07-81BF-1C839F22E7AA} +{EB267C6B-3753-4884-A293-FA6D434C721A} +{01A1A5C5-4606-4A3B-A1B7-3EB40F900860} +{581B8426-13B8-4FFD-BF40-DDF1B9D8259E} +{6E07EA6C-1806-4732-AEAA-38BD6DE47E2B} +{3EC1445C-8366-4C5C-AAF5-4D4149AB4B41} +{59850623-26EC-4194-89A6-F30061F5062E} +{719DFB9B-4E0F-45F5-AD4E-2C5BA47C906D} +{A63461BB-D0E3-4619-8E23-A94309008D35} +{0348BC02-8BDA-4CA5-AAEB-712972834273} +{CE3DC0F6-F7EC-4D75-A549-21987A274057} +{711AF477-C59C-4C49-BFA1-67FF06A2645C} +{F62CCF0B-9819-449B-8F57-1AFBB40ADE4B} +{E2EB93C1-70A8-49D7-9267-B08DC1139F9F} +{ECBC6F47-D172-4FC2-A5ED-C126CCCD170C} +{69CFC4AE-2A91-41B9-A624-39408E9577AA} +{FAACD365-EC26-49B2-8BAC-04BF168C0512} +{141FD384-7BE4-44B4-9396-2266E446C2B7} +{762D6DFD-3E0F-4F1D-A04F-8BC7247205F9} +{FF09C170-EE49-491E-B2D4-47DDA6BE2922} +{C11FD511-7C71-4098-89B8-73A9B96AEA44} +{31D2EF1E-5B69-4D49-BBAC-99B96E76669B} +{1BF8EC9F-BF4D-4D26-9898-9F9F1EDFF353} +{976658BC-A4FA-4B70-9291-19184485261A} +{F5A36399-5FAC-4B74-BA14-77A64C844745} +{F7D1C08F-E9AE-4A5A-ABDE-8E581D873E96} +{65953F22-79D4-4DD3-A6FF-BC4CDD729792} +{51A49E15-23E3-4559-817C-C38F389C3C72} +{2A9B2662-0E11-4C5C-AF7F-742D1174ED14} +{ADEE302B-F212-4751-8B50-EB8EAC9C8EFB} +{2CD3CFCE-81DE-4466-AED7-12B2BDECA087} +{585B4721-E2A3-404D-ACC0-EB6296E0E695} +{465057F2-972D-4BAA-B6AD-BC8442E847CC} +{23190313-3FA0-4237-8068-921135E8CCAF} +{ECEAFDB1-38CE-4456-959F-8BCB7F8AB42C} +{EDB712F1-60F1-4B32-A175-B958981EDD88} +{C0815807-26C9-4C9B-AE4E-689621CF85A8} +{AD1DA31F-EF7B-4BD9-AED9-254542C97BF6} +{963CBCD6-CE3E-4D95-88EA-98B57180790C} +{7D3AD7A9-DE39-4DFE-855D-D8E2EBC56EFE} +{5DFAB593-E7EA-48F4-A54C-79D401A27AFF} +{2FB2EC42-3861-4FE2-A94F-1D019ED05E48} +{AB970539-32D3-4FB3-A98D-21247FF2E81B} +{7DBC25B6-F82B-428C-853F-B2ED542DD7BB} +{D6C99305-1C60-444F-B81F-6EC6846E0DF9} +{C298BCA5-F5D8-4EF6-8AB9-9C5371DFB610} +{D887138C-5E08-47C4-8A51-53A8F714EFB7} +{0F86BF0F-F836-4FF4-B8AA-B7A826981964} +{57BA5F33-63DD-4884-ABAD-ADC55B447BCA} +{C5A04B8A-7853-4B45-92F3-E831B4C67BF9} +{CA8886A7-0940-49AE-8FBF-B6D8857621C3} +{830DA74D-7462-48B6-A133-06DA43BA64D6} +{2FDC7A9C-BB27-44B0-A855-08F5E4E78C03} +{E4B8248A-5A07-4606-8061-69DEFA22B79E} +{BAAB438D-3BBF-4855-A549-802C1F2CFF14} +{7F1D8FE4-A0A2-49D7-8199-50E1E92F0E20} +{6AE77CFC-DF7B-48CC-BA55-7417EA654C3F} +{40FA7FF1-7EE4-45CF-8FB3-A0EE1427DF27} +{5B0EA42B-8215-4F05-864D-BEA0F71263C6} +{F41EB7B2-6092-43A2-A09F-B3AF3A1382F6} +{4F6BC49A-80EC-4F8C-9648-4265CC7CEB34} +{16E358DF-D903-48F5-A026-FDC700D1C889} +{694A3224-E9B6-498D-8D8B-53824B2D0242} +{D945B365-B8FF-468C-AC0A-A53FF5149AC4} +{AD75674E-20AF-421E-9DD0-1771C5D6BA2F} +{C58C8DFD-EB05-472C-92A3-DD2888DE3674} +{6F8F4166-032E-43A6-B903-AD9FCA26F96A} +{15519B1B-26C0-4A56-A9D1-FB3FB582969A} +{33651349-5F43-40EA-8E32-134B79F45775} +{6AA35938-EA49-48DB-8552-D5F438A10886} +{C52F9C0E-499B-4327-A60F-6F8A715CF1C8} +{6C682C0F-1675-4A6C-97B8-DF16B9DCA351} +{DC607F6D-F656-4907-9609-1B60B2B99AC7} +{E72C40B3-44B4-4A99-AF57-9334DF0EF766} +{E1C662D4-634B-48DC-84E4-BF0BA598483D} +{23CAEA40-4550-45BE-B143-495EA08FC5C8} +{44F053DF-FD18-43CE-9F54-FF5EAA2BDDF5} +{55F68A46-D37C-4EFA-93AF-F5059BAA18A7} +{D485BC83-02BA-4E8F-9B7B-8BE2E32EA86C} +{54E4F522-B431-47C7-9CB8-FB85C3026B67} +{8267DBB8-DF95-4537-A1A8-C6BDC5920DE4} +{EE982A90-6EDC-423E-B1E2-48513B323585} +{3386BE6B-4548-4263-8029-814446D9223A} +{912B32EF-57AA-4281-A278-1BE53F8913B3} +{6AAA80E4-2580-4EF5-9665-744AEC074AC0} +{ACFD5194-BF16-4DFA-B98B-1444325CC84C} +{3F6CFDF8-C369-4201-B7C7-B62C128726AA} +{95AC3B82-B9FF-420D-A6B3-E7BBCBDF456B} +{921BE086-1FF7-43EC-B66E-861590CA0871} +{BDE0D739-0CDF-4BF6-8DB9-B7A1B2D0B60C} +{B28E159D-F89A-43EB-8A0F-EA840FB3E87E} +{092D0AED-2A8E-4EC6-84A0-9DAB5FD7DD10} +{B00D614C-78A3-4680-83A9-DD5D768701EF} +{617665D6-2657-4951-8DB2-5F009331A840} +{1FB0BA56-4363-4128-B870-3938E4523420} +{18C8BB36-009E-4641-9BA9-4EF16CB5FE60} +{017E35F9-9B5A-437E-8C12-13160ACC8ADB} +{588A301D-4159-4D88-A621-DE4ABF961C26} +{36C3EC96-D18F-46A6-9421-F65FF74A3081} +{B1EB5223-05FB-44D9-85EE-A8597E6EEBA9} +{E47B4039-6B59-4330-97BC-B14676A32A9F} +{1F6B7960-BB0F-4260-89DC-05FC2510687F} +{3C2C1319-E0ED-435F-BA01-4169CFB84BBC} +{28544AF2-D585-4602-BDD9-D1BCA1C45F56} +{F63FB5FC-5E28-496A-8C38-BE0175526BB8} +{D5D59AE2-B185-49F6-B4BD-0A7A6F8E52F3} +{99ABD0FB-FFE1-4263-94AB-17A59D5C247C} +{B847DB01-7EB5-49CE-88E9-0C4CA4A16D67} +{4756E785-D8A3-4E29-B993-B01B01AA4D2A} +$ +{0AADAE47-CCD6-4CC9-BF83-32297AB40C37} +Spring.NET 1.1 M1 Setup Project +{36ADC9A6-0B4F-4245-ACCE-4A50015A80A8} + +{5076ABBD-5015-4CE7-BD04-2E313FB65264} + +{B51103C7-8FD1-43CD-9553-57945F20EDE8} + +{7A4D8295-AAA0-4D43-8542-2746B3A3CB7E} +Check setup pre-requisites +{4D692B2D-36CC-437E-8B49-F054053A65B8} + +{007D81FD-F462-41C0-9766-5CA98DB29B4A} +Install setup pre-requisites +{8EE4916B-990A-45A0-91FC-AE6E73BAD205} + +{2D37F8AF-D65E-43A0-ADFF-D492484E9BB4} + +{C65B04D2-F38D-4815-A963-34D67AD027BD} +Define Setup Components +{A7335220-876F-4C84-8F58-4C7CF63DAB43} + +{5232CF1A-2118-413C-BF18-594406FC60A0} +Initialize Setup Globals +{17ED505D-22C9-4411-B251-BC1C7E4B1B5C} + +{7568CEA0-C91D-4D89-9091-98512904A1D1} +First Time Install +{7536F78B-2039-40DE-B54D-9796403C0C65} + +{12905F17-667A-4D90-AC96-73368992CC76} +TO-DO: Customize your minimum setup here +{A7CD16FD-3D01-4E95-AA29-DC23DFE119D5} + +{D1AC32FD-4A2D-41DB-9A0E-FCDB2A5B68A6} +Maintenance Install/Uninstall +{E8D49994-8514-4EAF-8181-8DBE75A815D2} + +{D77A811D-0421-4824-A290-02DA2F506DBC} +Exit Setup if Wizard Loop cancelled +{D57BFAD1-D19C-43F6-B54F-1234265A07A3} + +{D93C5C9D-3B2F-40F1-AF98-B66F194A35A6} +Prepare to install +{B502A097-C792-42A2-9007-80AC1988C851} + +{34D7AF5E-CC1B-4885-ABFF-37CAE9F00B27} +Modify Target System +{90F49D4C-C196-4D02-AF50-E55A0BCFCA7C} +Uninstall product +{730C72F5-779C-47C1-9DBF-4F7BC79D8C0A} +TO-DO: Insert any additional uninstall commands here +{9647B504-7C7A-48F9-85D8-BCF9F0B1A0AB} +Install/Re-Install product +{BAAB438D-3BBF-4855-A549-802C1F2CFF14} +TO-DO: Insert any additional install commands here +{921BE086-1FF7-43EC-B66E-861590CA0871} + +{B28E159D-F89A-43EB-8A0F-EA840FB3E87E} +End of Installation +{3C2C1319-E0ED-435F-BA01-4169CFB84BBC} +TO-DO: Insert command that starts your application here +{4756E785-D8A3-4E29-B993-B01B01AA4D2A} + +{B62B7C2B-4668-4E4E-87C7-CC1525784712} +PREREQ +FALSE +{C7F738FC-2B5F-4380-9608-0DEA772B94C7} +PRELIST + +{95C7D535-8788-41B6-B8CA-D3742CDD3E2A} +PREREQ +TRUE +{DD78225E-3B68-40CE-B4E1-93AE4BE9C37F} +PRELIST +$PRELIST$$NEWLINE$Previous Version Uninstallation +{D4F36731-7336-4097-A355-B0B1584CBB44} +REMOVEOLD + +{CAC97A56-5D1A-405B-A78B-FE91751C7B09} +ERROROLD + +{B47D5127-9D59-471B-992B-F006D44A3F2E} +MAINTENANCE +FALSE +{7FFAA3DC-DDA0-4A59-BD77-84B4369F5078} +SHORTCUTFILESALL +$SHORTCUTFILES$ +{9CC32567-D081-4F97-8377-F414F884749A} +TARGETDIR +$PROGRAMFILES$\$TITLE$ +{4632834D-AC50-48B5-A91B-503C03608212} +STARTMENU +$TITLE$ +{632BF2EF-3839-4517-BC09-C542894D3B33} +PROGRESSTEXT +Installing $TITLE$ +{765C3B48-EAC5-4004-BFA2-CE4BF53F3650} +SUCCESS + +{B0014B6C-1116-4E41-BBCC-E2349D4C4038} +LASTERROR + +{C7B19EE0-FA22-40B3-B94C-879879BACD77} +SHORTCUTFOLDER +$SHORTCUTFILESALL$\$STARTMENU$ +{8EA1DB5A-D36D-4391-A93F-52B824ECCBED} +SHORTCUTFOLDER +$SHORTCUTFILES$\$STARTMENU$ +{67C00CFC-AD88-4390-B122-4ABA4AB39E20} +PROGRESS +100 +{6AAA80E4-2580-4EF5-9665-744AEC074AC0} +PROGRESS +100 +{8E733ADF-0692-4381-A6CE-1A14FC3B3EFD} +BUILDMODE +0 +PATCH +TRUE +{E3B49129-FB7E-494B-8868-3A7E035A1BB6} +NEEDSUPGRADE +0 +TRUE +FALSE +{B0E8FF3A-6587-4329-AD90-A5E8BD6B61D5} +PREREQ +0 +FALSE +TRUE +{CC974A69-AA46-4F01-934D-3AD31BE3EA59} +WIZARD +0 +CANCEL +FALSE +{A07B9E2D-B4D2-4987-9814-D9E37F8042F5} +BUILDMODE +0 +PATCH +TRUE +{B318EA01-0253-43B2-8A21-BB05E733C066} +NEEDSUPGRADE +0 +TRUE +FALSE +{C81A209F-B1AF-42AC-9982-8EC8CCC33464} +REMOVEOLD +0 +ERROR +FALSE +{BC322E68-8DCC-4C68-88F9-B1B2E20C4180} +REMOVEOLD +0 +REBOOT +FALSE +{35873C23-DAB9-48D3-AD16-0C5140653771} +REBOOTNOW +0 +OK +FALSE +{8A95A867-00ED-469E-B99D-5FD8ABA81ABF} +SHORTCUTFILESALL +0 + +FALSE +{545506AF-96E2-44DE-B49C-3CDD3E25CE68} +MAINTENANCE +0 +TRUE +FALSE +{A5001B5C-1CBD-4C2E-9942-64B73BFE5D3B} +BUILDMODE +0 +PATCH +TRUE +{A53F37B9-1DD2-4A99-956E-A8DE95E33B64} +WIZARD +0 +CANCEL +FALSE +{41DDD353-69A3-4173-808D-E7BDD8083D38} +BUILDMODE +0 +PATCH +TRUE +{57144EC7-F2F4-450C-9821-C36B2AB9BEB2} +WIZARD +0 +BACK +FALSE +{63E87256-B27A-4FA6-8D19-0907F58CD451} +WIZARD +0 +CANCEL +FALSE +{190404D4-7B8C-4A9F-83CA-1347A04CBF6B} +WIZARD +0 +BACK +FALSE +{2504B995-B89E-4EB1-8CD3-C1E4E32485C4} +WIZARD +0 +CANCEL +FALSE +{8E64D5B8-C193-4954-B890-61E593B3F039} +WIZARD +0 +BACK +FALSE +{26C60264-6250-46C2-9EB9-1618F808612B} +WIZARD +0 +CANCEL +FALSE +{DCAEEBD8-66B2-4000-A6D9-CD370CE687BF} +WIZARD +0 +BACK +FALSE +{B2C9ED87-31EF-4A1B-BD2E-5DED878B210A} +WIZARD +0 +CANCEL +FALSE +{CB3E023C-7C18-4A1E-B510-FA5FDC56D130} +MINIMUM +0 +TRUE +FALSE +{87BA9CDC-31A3-4AEB-8DB0-C1648A287FB2} +COMPLETE +0 +TRUE +FALSE +{28FB5234-8E03-4416-8DA3-8C526C6948B9} +WIZARD +0 +BACK +FALSE +{526008DA-26A8-482F-B6E6-C2AB1793FC9D} +WIZARD +0 +CANCEL +FALSE +{2F676623-128C-4C34-A1A8-5F1EF0D91DCF} +WIZARD +0 +BACK +FALSE +{5529E598-81EB-4901-A076-CE6F60CC6CEA} +WIZARD +0 +CANCEL +FALSE +{0FFDBB1C-5570-40C5-BC9D-A6A038498FDE} +WIZARD +0 +BACK +FALSE +{A6380038-73A6-40BC-B13B-BAD29A36226C} +WIZARD +0 +CANCEL +FALSE +{C09182DB-2BDF-46A0-B6D7-6B6F6BF0F593} +WIZARD +0 +BACK +FALSE +{182CEBF3-0BB0-4BCC-9676-E08A05BCD094} +BUILDMODE +0 +PATCH +TRUE +{CA901067-D569-4095-A422-B50D6AED708A} +WIZARD +0 +CANCEL +FALSE +{347AE4EA-A715-4810-9AC4-C95A0AEE67C1} +ALLUSERS +0 +TRUE +FALSE +{D63830B2-3590-4DCF-A77B-F7917A2E7F79} +REMOVE +0 +TRUE +FALSE +{2FDC7A9C-BB27-44B0-A855-08F5E4E78C03} +SELECTED +0 +TRUE +FALSE +{7F1D8FE4-A0A2-49D7-8199-50E1E92F0E20} +BUILDMODE +0 +PATCH +FALSE +{5B0EA42B-8215-4F05-864D-BEA0F71263C6} +ADVERTISE +0 +TRUE +FALSE +{617665D6-2657-4951-8DB2-5F009331A840} +SILENT +0 +FALSE +FALSE +{1FB0BA56-4363-4128-B870-3938E4523420} +REBOOTCOMPUTER +0 +TRUE +FALSE +{18C8BB36-009E-4641-9BA9-4EF16CB5FE60} +SUCCESS +0 +REBOOT +FALSE +{B1EB5223-05FB-44D9-85EE-A8597E6EEBA9} +RUNAPP +0 +TRUE +FALSE +{E47B4039-6B59-4330-97BC-B14676A32A9F} +REMOVE +0 +FALSE +FALSE +{1F6B7960-BB0F-4260-89DC-05FC2510687F} +SUCCESS +0 +COMPLETE +FALSE +{18516BF5-FA02-4D7B-BFD4-07714E2BC054} +$TITLE$ Setup +Unable to uninstall old version of $TITLE$. Please uninstall it yourself using Control Panel Add-Remove Programs before attempting to install this product.$NEWLINE$$NEWLINE$$ERROROLD$ +2 +1 + +{EDE1F9CC-5316-44E4-8FEA-FE44558E9C84} +$TITLE$ Setup +Your computer needs to be restarted before $TITLE$ Setup can continue.$NEWLINE$$NEWLINE$Please save your work in all running programs and click OK to restart your computer. Setup will resume once your computer restarts.$NEWLINE$$NEWLINE$If you click CANCEL, setup will exit. You will have to run setup again at a later time to install $TITLE$. +2 +2 +REBOOTNOW +{2F15D329-6E80-4509-829F-C2F3352970ED} +prereq +WIZARD +TRUE + +TRUE +FALSE +{5B250495-2FD6-4B4D-BE79-23040614AF44} +progressprereq +WIZARD +FALSE + +TRUE +FALSE +{0D00582F-F56D-4053-B79E-DB07C8A799D6} +welcome +WIZARD +TRUE + +TRUE +FALSE +{F1D0F40E-DC0E-4CBB-B78C-3FB463D8671F} +licensecheck +WIZARD +TRUE + +TRUE +FALSE +{048E3015-272E-475D-B275-CF0012C1B8FD} +readme +WIZARD +TRUE + +TRUE +FALSE +{69B3686B-22D9-4ECB-91EB-EA9EEFE2AD8F} +registration +WIZARD +TRUE + +TRUE +FALSE +{ECC571C3-29CB-4AE6-97AC-C3F80801C5E1} +setuptype +WIZARD +TRUE + +TRUE +FALSE +{30166F16-C73F-4F86-A149-2B99BA37A858} +componentstree +WIZARD +TRUE +PERSONALIZED +TRUE +FALSE +{9115BE28-CD0B-452A-B055-23AF260640D1} +destination +WIZARD +TRUE + +TRUE +FALSE +{11BF4FE6-C05D-448C-A103-BC52BA23CCA5} +startmenu +WIZARD +TRUE + +TRUE +FALSE +{C772DEC6-6D67-4E8B-80A4-587DCFC11E04} +startinstallation +WIZARD +TRUE + +TRUE +FALSE +{828D4366-191D-40E6-A954-98D6BC2AB6B1} +maintenance +WIZARD +TRUE + +TRUE +FALSE +{2D7FCC84-17B1-4BC9-AE4A-AFE6F2561E74} +componentstree +WIZARD +TRUE +MODIFY +TRUE +FALSE +{00902F7E-9682-4ECC-97F3-0C989404753B} +startinstallation +WIZARD +TRUE + +TRUE +FALSE +{81F00F0D-5139-4A39-B1E5-5EA513828FBD} +progress +WIZARD +FALSE + +TRUE +FALSE +{B00D614C-78A3-4680-83A9-DD5D768701EF} +finish +WIZARD +TRUE + +TRUE +FALSE +{A3A8A528-F4F5-4031-8308-7F556B64C129} +Spring.NET 1.1.2 +TRUE +Spring.NET 1.1.2 +{5749130C-401B-46B6-A1F3-F227D549C673} +Spring.NET 1.1.2 +FALSE +{EDB251B5-EFEA-4792-B040-60474F526530} +Spring.NET 1.1.2 +TRUE +{830DA74D-7462-48B6-A133-06DA43BA64D6} +Spring.NET 1.1.2 +SELECTED +{16E358DF-D903-48F5-A026-FDC700D1C889} +$TARGETDIR$\doc +{694A3224-E9B6-498D-8D8B-53824B2D0242} +$TARGETDIR$\doc\reference +{D945B365-B8FF-468C-AC0A-A53FF5149AC4} +$TARGETDIR$\doc\reference\html +{AD75674E-20AF-421E-9DD0-1771C5D6BA2F} +$TARGETDIR$\doc\reference\htmlhelp +{C58C8DFD-EB05-472C-92A3-DD2888DE3674} +$TARGETDIR$\doc\reference\images +{6F8F4166-032E-43A6-B903-AD9FCA26F96A} +$TARGETDIR$\doc\reference\pdf +{15519B1B-26C0-4A56-A9D1-FB3FB582969A} +$TARGETDIR$\doc\reference\styles +{33651349-5F43-40EA-8E32-134B79F45775} +$TARGETDIR$\examples +{6AA35938-EA49-48DB-8552-D5F438A10886} +$TARGETDIR$\examples\Spring +{C52F9C0E-499B-4327-A60F-6F8A715CF1C8} +$TARGETDIR$\examples\Spring\Spring.AopQuickStart +{6C682C0F-1675-4A6C-97B8-DF16B9DCA351} +$TARGETDIR$\examples\Spring\Spring.Calculator +{DC607F6D-F656-4907-9609-1B60B2B99AC7} +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind +{E72C40B3-44B4-4A99-AF57-9334DF0EF766} +$TARGETDIR$\examples\Spring\Spring.DataQuickStart +{E1C662D4-634B-48DC-84E4-BF0BA598483D} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext +{23CAEA40-4550-45BE-B143-495EA08FC5C8} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry +{44F053DF-FD18-43CE-9F54-FF5EAA2BDDF5} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder +{55F68A46-D37C-4EFA-93AF-F5059BAA18A7} +$TARGETDIR$\examples\Spring\Spring.TxQuickStart +{D485BC83-02BA-4E8F-9B7B-8BE2E32EA86C} +$TARGETDIR$\examples\Spring\Spring.Web.Extensions.Example +{54E4F522-B431-47C7-9CB8-FB85C3026B67} +$TARGETDIR$\examples\Spring\Spring.WebQuickStart +{8267DBB8-DF95-4537-A1A8-C6BDC5920DE4} +$TARGETDIR$\examples\Spring\SpringAir +{EDEB4872-324A-4839-9037-B0EF3A6773A3} +progress +SUCCESS +FALSE +TRUE +{F41EB7B2-6092-43A2-A09F-B3AF3A1382F6} +progress +SUCCESS +FALSE +FALSE +{EE982A90-6EDC-423E-B1E2-48513B323585} +progress +SUCCESS +TRUE +FALSE +{89AA7DF5-3C08-48DF-A638-DE6B27BB689A} +PROGRAMFILES +20 +FALSE +{DA7984D1-FFC6-46FD-AA05-CF9705718548} +COMMONFILES +20 +TRUE +{93E95378-FCD0-4AD8-859C-F79D0659B4D4} +SHORTCUTFILESALL +14 +TRUE +{46249933-EAFF-48CC-83D4-A2B2D9A539DF} +SHORTCUTFILES +14 +FALSE +{73747553-3B70-47E7-A37E-EA9C3104CFB0} +DESKTOPDIR +17 +FALSE +{8628DC93-23A1-402B-9482-4986B2460DCA} +WINDIR +25 +FALSE +{3AC507BE-2DCF-4306-9D2A-2C8C6B346E2B} +WINSYSDIR +23 +FALSE +{8759C800-4115-4C0F-B44B-6E4A0A3B7271} +QUICKLAUNCHDIR +26 +FALSE +{FEAA8A2C-3859-4A1D-B5BD-AFAE6A837273} +WWWROOTDIR +27 +FALSE +{E4393F3D-D71C-4B0C-BE63-043394AC87F4} +ISNT +18 +{C27E97F5-6F56-4072-950A-0E8C0ECD22E7} +L:\projects\Spring.Net\build\package\Spring.NET\readme.txt +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{E6B7586C-28EA-48D2-B7EB-17D42DBD1979} +L:\projects\Spring.Net\build\package\Spring.NET\BreakingChanges-1.1.txt +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{346ECAFD-25C6-49CF-B532-765C963D176A} +L:\projects\Spring.Net\build\package\Spring.NET\changelog.txt +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{52B94BDA-4FD4-4788-B28F-EEAB7C162721} +L:\projects\Spring.Net\build\package\Spring.NET\license.txt +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{8862B1BF-C8BD-42FF-89D1-B0243A204059} +L:\projects\Spring.Net\build\package\Spring.NET\bin\*.* +TRUE| +$TARGETDIR$\bin +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C71A3471-6E8E-49B4-BA18-10A4C9564959} +L:\projects\Spring.Net\build\package\Spring.NET\icons\*.* +TRUE| +$TARGETDIR$\icons +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{0359E329-55A1-4792-AB4E-AFEB1F335FF9} +L:\projects\Spring.Net\build\package\Spring.NET\Spring.Net.1.1.2005.sln +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{28A3A0D3-1D22-4845-A97B-AC63DAF3413C} +L:\projects\Spring.Net\build\package\Spring.NET\Spring.Net.1.1.2003.sln +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{B60FA6EF-C69C-4EB8-9BBE-5EA91611D820} +L:\projects\Spring.Net\build\package\Spring.NET\lib\*.* +TRUE| +$TARGETDIR$\lib +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{1BF94E6D-2B78-4961-9F69-6DBD6632BD3D} +L:\projects\Spring.Net\build\package\Spring.NET\src\*.* +TRUE| +$TARGETDIR$\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{712378F3-5C02-4F64-A618-073A000505CC} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\htmlhelp\htmlhelp.chm +FALSE| +$TARGETDIR$\doc\reference\htmlhelp +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{D2D412A9-D472-4A95-BBCB-7FC0C3B8B592} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\images\admons\*.* +TRUE| +$TARGETDIR$\doc\reference\images\admons +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{269A0E5E-A5B3-416F-B770-12532EE31550} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\images\callouts\*.* +TRUE| +$TARGETDIR$\doc\reference\images\callouts +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{30B2A744-1A12-4C9E-BE0F-0E9066E87AFD} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\pdf\spring-net-reference.pdf +FALSE| +$TARGETDIR$\doc\reference\pdf +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{791D8D35-3FEA-4E19-9C77-6329148C64F9} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\pdf\images\*.* +TRUE| +$TARGETDIR$\doc\reference\pdf\images +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{5A4411F0-80E6-4F3B-BBB4-CA4A4C9B1096} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\styles\html.css +FALSE| +$TARGETDIR$\doc\reference\styles +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{2B0A12F4-18DD-421F-BCA7-9CB0925004B8} +L:\projects\Spring.Net\build\package\Spring.NET\doc\schema\*.* +TRUE| +$TARGETDIR$\doc\schema +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{16B0C775-0F55-4F09-87BB-2264C7C72A11} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.AopQuickStart\readme.txt +FALSE| +$TARGETDIR$\examples\Spring\Spring.AopQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{BF19F5CC-67FB-406B-81C3-678CDD88258E} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.AopQuickStart\Spring.AopQuickStart.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.AopQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{F8031368-5974-4C5C-8EA0-001C812628D5} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.AopQuickStart\Spring.AopQuickStart.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.AopQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{E607AA22-7A1D-4CA6-9FA6-FBEC7CAF505D} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.AopQuickStart\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.AopQuickStart\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{08D02487-7A25-4535-B2C3-D66D666F2884} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.AopQuickStart\Spring.AopQuickStart.build +FALSE| +$TARGETDIR$\examples\Spring\Spring.AopQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{59F13A6B-AF40-45F7-B7F8-9D4DA03320B1} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\Spring.Calculator.snk +FALSE| +$TARGETDIR$\examples\Spring\Spring.Calculator +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{D4618425-8024-415B-A39E-EA5A6A757E11} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\Spring.Calculator.build +FALSE| +$TARGETDIR$\examples\Spring\Spring.Calculator +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{E927FC6B-DA58-4F6B-86DE-BEE46E7D6F24} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\Spring.Calculator.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.Calculator +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{0D9E47BC-3A38-457C-BD4C-D5BABE1E3168} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\Spring.Calculator.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.Calculator +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C8E7DA02-29FE-4E5E-BA96-8E9B48E6D5F1} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\readme.txt +FALSE| +$TARGETDIR$\examples\Spring\Spring.Calculator +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{6028D781-1B56-4CF6-8567-B3266184A580} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\lib\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.Calculator\lib +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{9A899381-224C-4960-8EE9-114927CF3331} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.Calculator\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{5A52EB3A-E6DF-4A80-80ED-7479ECB6B587} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Data.NHibernate.Northwind\test_northwind.sql +FALSE| +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{49B7241A-38C5-4AFA-8816-2DF906B2E558} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Data.NHibernate.Northwind\Spring.Northwind.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{8A3D44F9-A8F2-489B-B57E-CC9146833960} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Data.NHibernate.Northwind\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{74EE6033-B67D-4559-A3CD-8EB3B5D601AB} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Data.NHibernate.Northwind\test\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind\test +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ED19961A-2407-4353-B639-99DCA4187BCC} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.DataQuickStart\Spring.DataQuickStart.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.DataQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{5023B1E0-3EE7-4FE8-BDC7-6FE3B0F1CD32} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.DataQuickStart\Spring.DataQuickStart.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.DataQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{E44E20C1-1D79-49EE-A44E-3208D7C0AB49} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.DataQuickStart\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.DataQuickStart\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{BA9CE954-CC1E-4A8C-9AD5-E4A773242D09} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.DataQuickStart\test\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.DataQuickStart\test +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{3AB6811B-92B2-46F1-98F1-EA0F0B84E196} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.AppContext\Spring.IocQuickStart.AppContext.build +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{FBC19861-623F-4B92-9ECF-FEBD47DA4AC9} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.AppContext\Spring.IocQuickStart.AppContext.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C47FA07F-CB64-4C9B-B31E-2469BA86C4E8} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.AppContext\Spring.IocQuickStart.AppContext.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ED12CCAD-D43C-4CA0-B6FF-6EF6F1288B75} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.AppContext\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{EA3B37A7-6C0A-4E39-BD02-FFD9DE8A1425} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.EventRegistry\Spring.IocQuickStart.EventRegistry.build +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{3AB54DCB-499F-4F7C-9916-6FB2B8D7F427} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.EventRegistry\Spring.IocQuickStart.EventRegistry.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{248669E3-3B34-44F8-B5C6-D19512FAB392} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.EventRegistry\Spring.IocQuickStart.EventRegistry.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ECA4D720-A9AA-41B4-8487-336179456282} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.EventRegistry\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C2B8D56A-7A67-454D-8DFE-8FA17FCA47F8} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.MovieFinder\Spring.IocQuickStart.MovieFinder.build +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{5A098B01-9784-414B-8B43-F702421DA9F3} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.MovieFinder\Spring.IocQuickStart.MovieFinder.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{A38A7B60-A88E-4D09-A49E-0CBA4B8272EB} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.MovieFinder\Spring.IocQuickStart.MovieFinder.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{37F91F10-6667-4967-B167-6CEABB7620C6} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.MovieFinder\lib\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder\lib +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{2B9C2915-A46E-4301-B118-787D8E969E9E} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.MovieFinder\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{24393453-7A7C-4DC8-ADD6-44945E073D70} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.TxQuickStart\Spring.TxQuickStart.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.TxQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{97F657B1-22D5-4F55-9D77-8F7D23F8DD0D} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.TxQuickStart\Spring.TxQuickStart.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.TxQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{AC02E50D-0805-4672-B66E-42BC308B57E6} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.TxQuickStart\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.TxQuickStart\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{10A30C9B-AB0E-4622-B39F-2058816C8AC9} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.TxQuickStart\test\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.TxQuickStart\test +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{40E187E4-9812-43DF-BB93-F2729EC5F519} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Web.Extensions.Example\Spring.Web.Extensions.Example.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.Web.Extensions.Example +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{3EDD2AA2-2F08-414A-BD66-37240675D6DC} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Web.Extensions.Example\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.Web.Extensions.Example\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C8FC0883-E862-4A8F-8421-CEA1AF318888} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.WebQuickStart\Spring.WebQuickStart.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.WebQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{93CF5997-A66F-4AA4-AAF5-BBAAD4F951E6} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.WebQuickStart\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.WebQuickStart\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C63FE01E-7752-4B7B-9C32-AC04B4547229} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\SpringAir.build +FALSE| +$TARGETDIR$\examples\Spring\SpringAir +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{92F5D05F-D8AA-4996-A5BC-84A8C0CD8C31} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\SpringAir.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\SpringAir +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C3F2E78C-8C6E-41DE-8F22-3CEAFC16702E} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\SpringAir.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\SpringAir +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{A55D92B4-821D-4AF4-92A1-EAC924FDEA79} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\readme.txt +FALSE| +$TARGETDIR$\examples\Spring\SpringAir +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{D9204FF3-BEC7-47FD-9F8B-FA65852C8244} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\data\*.* +TRUE| +$TARGETDIR$\examples\Spring\SpringAir\data +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{FBE8A4BD-F1AD-4CDC-9BCE-6FFEB7BE5B16} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\SpringAir\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{152AD548-99D6-4A5C-8941-2D3353EBF9E2} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\test\*.* +TRUE| +$TARGETDIR$\examples\Spring\SpringAir\test +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{94C4A1C7-4455-4C07-81BF-1C839F22E7AA} +L:\projects\Spring.Net\build\package\Spring.NET\test\*.* +TRUE| +$TARGETDIR$\test +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{EB267C6B-3753-4884-A293-FA6D434C721A} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\images\*.* +TRUE| +$TARGETDIR$\doc\reference\html\images +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{581B8426-13B8-4FFD-BF40-DDF1B9D8259E} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\ado.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{6E07EA6C-1806-4732-AEAA-38BD6DE47E2B} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\ajax.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{3EC1445C-8366-4C5C-AAF5-4D4149AB4B41} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\aop.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{59850623-26EC-4194-89A6-F30061F5062E} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\aop-aspect-library.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{719DFB9B-4E0F-45F5-AD4E-2C5BA47C906D} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\aop-quickstart.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{A63461BB-D0E3-4619-8E23-A94309008D35} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\background.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{0348BC02-8BDA-4CA5-AAEB-712972834273} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\dao.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{CE3DC0F6-F7EC-4D75-A549-21987A274057} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\data-quickstart.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{711AF477-C59C-4C49-BFA1-67FF06A2645C} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\dbprovider.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{F62CCF0B-9819-449B-8F57-1AFBB40ADE4B} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\expressions.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{E2EB93C1-70A8-49D7-9267-B08DC1139F9F} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ECBC6F47-D172-4FC2-A5ED-C126CCCD170C} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-core.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{69CFC4AE-2A91-41B9-A624-39408E9577AA} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-javadevelopers.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{FAACD365-EC26-49B2-8BAC-04BF168C0512} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-middle-tier.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{141FD384-7BE4-44B4-9396-2266E446C2B7} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-quickstarts.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{762D6DFD-3E0F-4F1D-A04F-8BC7247205F9} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-services.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{FF09C170-EE49-491E-B2D4-47DDA6BE2922} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-vsnet.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C11FD511-7C71-4098-89B8-73A9B96AEA44} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\introduction.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{31D2EF1E-5B69-4D49-BBAC-99B96E76669B} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\javadevelopers.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{1BF8EC9F-BF4D-4D26-9898-9F9F1EDFF353} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\logging.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{976658BC-A4FA-4B70-9291-19184485261A} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\migration.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{F5A36399-5FAC-4B74-BA14-77A64C844745} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\misc.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{F7D1C08F-E9AE-4A5A-ABDE-8E581D873E96} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\objects.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{65953F22-79D4-4DD3-A6FF-BC4CDD729792} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\objects-misc.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{51A49E15-23E3-4559-817C-C38F389C3C72} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\orm.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{2A9B2662-0E11-4C5C-AF7F-742D1174ED14} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\pool.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ADEE302B-F212-4751-8B50-EB8EAC9C8EFB} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\preface.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{2CD3CFCE-81DE-4466-AED7-12B2BDECA087} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\pt03.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{585B4721-E2A3-404D-ACC0-EB6296E0E695} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\quickstarts.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{465057F2-972D-4BAA-B6AD-BC8442E847CC} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\remoting.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{23190313-3FA0-4237-8068-921135E8CCAF} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\remoting-quickstart.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ECEAFDB1-38CE-4456-959F-8BCB7F8AB42C} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\resources.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{EDB712F1-60F1-4B32-A175-B958981EDD88} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\services.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C0815807-26C9-4C9B-AE4E-689621CF85A8} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\springair.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{AD1DA31F-EF7B-4BD9-AED9-254542C97BF6} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\springobjectsxsd.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{963CBCD6-CE3E-4D95-88EA-98B57180790C} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\testing.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{7D3AD7A9-DE39-4DFE-855D-D8E2EBC56EFE} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\threading.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{5DFAB593-E7EA-48F4-A54C-79D401A27AFF} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\transaction.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{2FB2EC42-3861-4FE2-A94F-1D019ED05E48} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\tx-quickstart.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{AB970539-32D3-4FB3-A98D-21247FF2E81B} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\validation.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{7DBC25B6-F82B-428C-853F-B2ED542DD7BB} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\vsnet.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{D6C99305-1C60-444F-B81F-6EC6846E0DF9} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\web.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C298BCA5-F5D8-4EF6-8AB9-9C5371DFB610} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\web-quickstart.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{D887138C-5E08-47C4-8A51-53A8F714EFB7} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\webservices.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{0F86BF0F-F836-4FF4-B8AA-B7A826981964} +L:\projects\Spring.Net\build\package\Spring.NET\Spring.Net.1.1.2002.sln +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{57BA5F33-63DD-4884-ABAD-ADC55B447BCA} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\extensible-xml.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C5A04B8A-7853-4B45-92F3-E831B4C67BF9} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\xsd-config.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{F3D1B9D2-7A04-44B6-9F91-860D3A2384C0} +$UNINSTALLLINK$ +Uninstall Spring.NET 1.1.2 +$SHORTCUTFOLDER$ +Removes this Interface21 product + + + + +0 +{074A5CF9-90CF-405D-82D3-6FEE0690119C} +$TARGETDIR$\readme.txt +View readme +$SHORTCUTFOLDER$ +View the readme file + + + + +0 +{432A824B-4E2F-4901-AEAB-DEB65367D193} +$TARGETDIR$\Spring.Net.1.1.2003.sln +.NET 1.1 Solution +$SHORTCUTFOLDER$\Source Code + + + + + +0 +{DE5B6D64-509C-4FC7-BE75-F9B569E4B1B6} +$TARGETDIR$\Spring.Net.1.1.2005.sln +.NET 2.0 Solution +$SHORTCUTFOLDER$\Source Code + + + + + +0 +{EE9964D4-1BF6-48BE-BD08-1AA41E6CECD9} +$TARGETDIR$\examples\Spring\Spring.AopQuickStart\Spring.AopQuickStart.2005.sln +AOP +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{24BB4CEB-C041-4023-A448-3AE725EAEAC5} +$TARGETDIR$\examples\Spring\Spring.AopQuickStart\Spring.AopQuickStart.2003.sln +AOP +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{3A2D9F38-3C4C-4909-BD58-F94AE94D60A6} +$TARGETDIR$\examples\Spring\Spring.Calculator\Spring.Calculator.2005.sln +Calculator +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{689F4852-777B-4865-B493-F42F05CF3EFA} +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind\Spring.Northwind.sln +Northwind NHibernate +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{55E0BEAD-43B8-4A0F-A94C-D5D23B3C9250} +$TARGETDIR$\examples\Spring\Spring.DataQuickStart\Spring.DataQuickStart.2005.sln +Data Access +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{A9984778-D722-4BD6-9599-A6E2CA5FDE13} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext\Spring.IocQuickStart.AppContext.2005.sln +Application Context +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{F8AA2809-D6D7-40DD-A34D-7C77EC57F208} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry\Spring.IocQuickStart.EventRegistry.2005.sln +Event Registry +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{B9024C07-45AD-45A5-B551-D70AF8A241A4} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder\Spring.IocQuickStart.MovieFinder.2005.sln +Movie Finder +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{CE007102-289B-4AAD-ACC0-78947B9FA68C} +$TARGETDIR$\examples\Spring\Spring.TxQuickStart\Spring.TxQuickStart.2005.sln +Transactions +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{F43AAB92-2311-4285-95F5-FBD2503543C3} +$TARGETDIR$\examples\Spring\Spring.Web.Extensions.Example\Spring.Web.Extensions.Example.2005.sln +AJAX +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{E521F49A-503D-4708-9B8C-5F8D68D75CDC} +$TARGETDIR$\examples\Spring\Spring.WebQuickStart\Spring.WebQuickStart.2005.sln +Web +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{57388F2C-8AAE-468A-92AC-C6CADBAC344C} +$TARGETDIR$\examples\Spring\SpringAir\SpringAir.2005.sln +Spring Air +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{0727C634-3C1B-445F-B8BE-C7D2D4E56FAB} +$TARGETDIR$\examples\Spring\Spring.Calculator\Spring.Calculator.2003.sln +Calculator +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{F2F64080-E757-42C5-B023-C2B72DED1066} +$TARGETDIR$\examples\Spring\Spring.DataQuickStart\Spring.DataQuickStart.2003.sln +Data Access +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{58C976E0-0F30-4334-AD68-90C293A50E8C} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext\Spring.IocQuickStart.AppContext.2003.sln +Application Context +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{5BC2F138-2564-4ADC-9606-8EE54694D6A3} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry\Spring.IocQuickStart.EventRegistry.2003.sln +Event Registry +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{5105D931-6D6C-406C-9820-D47CAEDA5C68} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder\Spring.IocQuickStart.MovieFinder.2003.sln +Movie Finder +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{33BBA500-00B2-4FB1-9535-C74505BFBE15} +$TARGETDIR$\examples\Spring\Spring.TxQuickStart\Spring.TxQuickStart.2003.sln +Transactions +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{D1099A76-F2D4-4ED8-9FF8-0CCC28759A8A} +$TARGETDIR$\examples\Spring\SpringAir\SpringAir.2003.sln +SpringAir +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{0EA42729-AF8B-48C6-86E8-D11906E352CE} +$TARGETDIR$\doc\reference\pdf\spring-net-reference.pdf +Reference - PDF +$SHORTCUTFOLDER$\Documentation + + + + + +0 +{33DA50A7-F9E5-4CDA-B812-F54169CA8A89} +$TARGETDIR$\doc\reference\html\index.html +Reference - HTML +$SHORTCUTFOLDER$\Documentation + + + + + +0 +{BEDC79F7-85D3-4837-881A-CF1A07342BB0} +$TARGETDIR$\doc\reference\htmlhelp\htmlhelp.chm +Reference - HTMLHELP +$SHORTCUTFOLDER$\Documentation + + + + + +0 +{94BDA812-5706-4256-BB6A-E8F451677F49} +$TARGETDIR$\changelog.txt +View changelog +$SHORTCUTFOLDER$ + + + + + +0 +{29F7BFC5-6D34-4A12-9B34-C5805014863F} +$TARGETDIR$\license.txt +View license +$SHORTCUTFOLDER$ + + + + + +0 +{4CB85B60-D0A0-442A-A683-94039B70DDA0} +$TARGETDIR$\BreakingChanges-1.1.txt +View breaking changes +$SHORTCUTFOLDER$ + + + + + +0 +{01A1A5C5-4606-4A3B-A1B7-3EB40F900860} +$TARGETDIR$\Spring.Net.1.0.2002.sln +.NET 1.0 Solution +$SHORTCUTFOLDER$\Source Code + + + + + +0 +{0C995B1F-7720-416B-8062-9D30A5BF4199} +REMOVE=ALL,TRUE,$PRODUCTCODE$,FALSE,,,REMOVEOLD,ERROROLD,TRUE +mMSI.dll\mMSIExec.dll +{08508DC4-D337-40B1-93B8-52D813007FB1} + + +FALSE +{CA8886A7-0940-49AE-8FBF-B6D8857621C3} +Spring.NET 1.1.2 +Spring.NET 1.1.2.7zip +FALSE +{1D2C9F3A-3B3F-4046-8FC7-86624108C2B2} +Welcome Dialog +{0EB6104E-73F2-4562-B9A5-D4CCCAA77D07} +License Check +{799297B7-F787-4C53-B04B-D884C74F885E} +ReadMe Information +{0391C72A-9386-4E58-B69A-CDA4F54F217F} +User Registration +{04063350-70E9-4E12-AF22-3963C42B1E60} +Setup Type +{A171750B-636E-46EB-A82D-AD3081D1E86E} +Custom Setup +{2A369571-BD93-4C90-92E6-9BA8BA06AA56} +Destination Directory +{291FFEAD-958D-468E-850E-01B8A64FA4C2} +Start Menu +{DD1FC87B-3855-4370-8F31-64FDF70E4466} +Start Installation +{D878A48E-1DC5-45C8-B282-0E36EA6D3473} +Maintenance +{43606175-29DB-4E0B-A271-A349EC540ADE} +Main Install +{C435DFAE-2EF8-4D28-A93F-3683596F4C3B} +Maintenance +{1F8EF880-D9B8-4C12-B266-347647DE74EF} +Main Install +{E1E3E44C-9A28-48CC-9811-B4F8546B03E1} +Welcome Dialog +{D0462D17-8893-4A99-B803-CEED288F68EB} +Main Install +{A68F2EC8-98DD-40D3-8FF3-B8F5BDED323D} +License Check +{1B6047FE-C9FB-4C1B-864E-AF9A7AB0BA0E} +Main Install +{30103E98-4AC5-4ED0-B524-7CC586B358ED} +ReadMe Information +{1D0C8CC9-2523-41E9-96E0-3BA9F5B6AB8D} +Main Install +{E68D3F7A-911E-4FDD-934A-9DFFD6AC3A7B} +User Registration +{5F0100BF-0D58-4492-A8AD-FDC458184AB5} +Main Install +{3C96C8AA-EC8E-476E-8B5A-BD00441FE51E} +Setup Type +{6E485127-81F0-4E78-A715-542B9BAC5541} +Main Install +{7BA3F47A-8115-4048-B47F-54FB1C8B39D2} +Custom Setup +{CF8730FE-B482-4030-9CBC-A32BCF9032A5} +Main Install +{DFF6C745-2E12-471A-BF81-EB166CA8B45E} +Destination Directory +{DA5F72B1-1E28-4654-89F4-2F5D09CB8A58} +Main Install +{85FC71FC-25EA-4ACB-B2ED-DCB1818F0810} +Start Menu +{19B13FAF-F05D-4522-8258-31C76DF9D915} +Welcome Dialog +{A6F2320E-A194-482A-A13B-8446A653F1BE} +Main Install +{6AE77CFC-DF7B-48CC-BA55-7417EA654C3F} +SUCCESS +{9FF95FFA-C00A-4F12-B05E-DB5ED37B659C} +TRUE +Check Application Requirements +{A4EDA82B-1FE9-4E39-AD3E-B88DCA4DBCC2} +FALSE + +{B9949A57-3226-461B-910B-04EED077BFD2} +TRUE +Check/Install Application Pre-Requisites +{CDBD7138-E76A-4A1F-A427-97837EAF89CB} +TRUE +Check Application Pre-Requisites +{DCC35A72-84BC-4C7A-97BA-17F3C2C6B183} +FALSE + +{CFCA2177-B5D4-4498-9341-74DA4A2BF10E} +TRUE +Install Application Pre-Requisites +{34379777-3444-4208-B99D-DE846461A34C} +FALSE + +{BEE14656-9B19-4E10-A494-FC09B7BE2537} +FALSE + +{543486BF-8C7F-46E6-B5E4-AF60671E5A79} +TRUE +Define Setup Globals +{8B8FFBAD-57B5-4C26-8914-C49244985EAB} +FALSE + +{44378BF2-B6DF-4AD7-871D-466D3AE158D3} +TRUE +Setup User Interview +{55A2971A-4283-43A2-933F-94C005EBB5C3} +FALSE + +{A4D3550A-6E2F-44F0-9AA2-CB3F558E5B28} +TRUE +Process (Un)Installation +{4BF2B76B-F5F9-4D46-BF67-779EE7EB56FA} +TRUE +Perform Uninstallation +{6A1EEA3F-4B75-4692-A758-01F3B78BE905} +FALSE + +{8F5BEE7A-98A2-44E5-BB76-A2B68E108C87} +TRUE +Perform First Time or Maintenance Installation +{3F6CFDF8-C369-4201-B7C7-B62C128726AA} +FALSE + +{95AC3B82-B9FF-420D-A6B3-E7BBCBDF456B} +FALSE + +{BDE0D739-0CDF-4BF6-8DB9-B7A1B2D0B60C} +TRUE +Finish Setup +{B847DB01-7EB5-49CE-88E9-0C4CA4A16D67} +FALSE + +$ + + + + + + + + + + + + + + + + + + + + + + +C + + + +C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +C + + + + + + + + +C + + + + + + + + +C + + + + + + + + +C + + + + + + + + + + + + + + + + +C + + + + + + + + + + + + + + + + + +C + + + + + + + + + + + + + + + + + + + + + + + + +C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +$ diff --git a/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia.bak b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia.bak new file mode 100644 index 00000000..56c6b66d --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia.bak @@ -0,0 +1,4150 @@ +Comment +Comment +Code Folding Region +Code Folding Region +Comment +Code Folding Region +Comment +Code Folding Region +Comment +Set Variable +Set Variable +Compiler Variable If +If +Set Variable +Set Variable +End +Compiler Variable End +Code Folding Region +Comment +Code Folding Region +Comment +If +Display Dialog +If +Terminate Install +End +Display Dialog +Compiler Variable If +If +Set Variable +Set Variable +(Un)Install MSI Setup +If +MessageBox +Terminate Install +End +If +MessageBox +If +Reboot and Resume +Else +Terminate Install +End +End +Set Variable +End +Compiler Variable End +Hide Dialog +End +Code Folding Region +Comment +Code Folding Region +Comment +Code Folding Region +Comment +Define Component +Comment +Comment +Get System Settings +Get Folder Location +Get Folder Location +Get Folder Location +Get Folder Location +If +Set Variable +End +Get Folder Location +Get Folder Location +Get Folder Location +Get Folder Location +Get Folder Location +Code Folding Region +Comment +Code Folding Region +If +GoTo Label +Else +Compiler Variable If +Comment +Set Variable +Set Variable +Compiler Variable End +End +Comment +Label +Display Dialog +If +GoTo Label +End +Compiler Variable If +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Comment +If +Set Component State +Else +If +Set Component State +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Label +Display Dialog +If +GoTo Label +Else +If +GoTo Label +End +End +Compiler Variable End +Label +Display Dialog +If +Compiler Variable If +GoTo Label +Compiler Variable Else +GoTo Label +Compiler Variable End +Else +GoTo Label +End +Comment +Label +Comment +Wizard Loop +Display Dialog +Display Dialog +Display Dialog +End +Code Folding Region +Comment +Code Folding Region +Label +Comment +If +Terminate Install +End +Comment +Comment +Set Variable +Set Variable +Set Variable +If +Set Variable +Else +Set Variable +End +Display Dialog +Comment +Comment +Code Folding Region +If +Comment +Comment +Apply Changes +Set Variable +Else +Code Folding Region +Code Folding Region +Comment +Create Shortcut +Web Media Block +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Create Shortcut +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Install Files +Web Media Block +Get Component State +If +End +Comment +Compiler Variable If +Apply Patch +Compiler Variable Else +If +Apply Changes +Else +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Create Folder +Apply Changes +End +Compiler Variable End +Set Variable +End +Code Folding Region +Code Folding Region +Comment +Code Folding Region +Comment +Hide Dialog +Display Dialog +If +If +If +Reboot Computer +End +End +If +If +If +Comment +End +End +End +End +Code Folding Region +Comment +$ +{0AADAE47-CCD6-4CC9-BF83-32297AB40C37} +{36ADC9A6-0B4F-4245-ACCE-4A50015A80A8} +{9FF95FFA-C00A-4F12-B05E-DB5ED37B659C} +{A4EDA82B-1FE9-4E39-AD3E-B88DCA4DBCC2} +{5076ABBD-5015-4CE7-BD04-2E313FB65264} +{B9949A57-3226-461B-910B-04EED077BFD2} +{B51103C7-8FD1-43CD-9553-57945F20EDE8} +{CDBD7138-E76A-4A1F-A427-97837EAF89CB} +{7A4D8295-AAA0-4D43-8542-2746B3A3CB7E} +{B62B7C2B-4668-4E4E-87C7-CC1525784712} +{C7F738FC-2B5F-4380-9608-0DEA772B94C7} +{8E733ADF-0692-4381-A6CE-1A14FC3B3EFD} +{E3B49129-FB7E-494B-8868-3A7E035A1BB6} +{95C7D535-8788-41B6-B8CA-D3742CDD3E2A} +{DD78225E-3B68-40CE-B4E1-93AE4BE9C37F} +{15A07272-08A0-428D-A9BD-D9B536BFE301} +{73F169C4-5CC8-4F30-9586-F6C6550442B3} +{DCC35A72-84BC-4C7A-97BA-17F3C2C6B183} +{4D692B2D-36CC-437E-8B49-F054053A65B8} +{CFCA2177-B5D4-4498-9341-74DA4A2BF10E} +{007D81FD-F462-41C0-9766-5CA98DB29B4A} +{B0E8FF3A-6587-4329-AD90-A5E8BD6B61D5} +{2F15D329-6E80-4509-829F-C2F3352970ED} +{CC974A69-AA46-4F01-934D-3AD31BE3EA59} +{BA863E14-FDA9-4832-8A5C-8D3C6133D31C} +{F8340D11-FC61-41E9-9E3F-6BC558DF4B68} +{5B250495-2FD6-4B4D-BE79-23040614AF44} +{A07B9E2D-B4D2-4987-9814-D9E37F8042F5} +{B318EA01-0253-43B2-8A21-BB05E733C066} +{D4F36731-7336-4097-A355-B0B1584CBB44} +{CAC97A56-5D1A-405B-A78B-FE91751C7B09} +{0C995B1F-7720-416B-8062-9D30A5BF4199} +{C81A209F-B1AF-42AC-9982-8EC8CCC33464} +{18516BF5-FA02-4D7B-BFD4-07714E2BC054} +{345A0328-D054-4691-8073-84166C595BA5} +{B5CCDB38-F907-47AB-B552-39C7A2B2F100} +{BC322E68-8DCC-4C68-88F9-B1B2E20C4180} +{EDE1F9CC-5316-44E4-8FEA-FE44558E9C84} +{35873C23-DAB9-48D3-AD16-0C5140653771} +{DDD28604-5F9E-464E-B26B-3F7F61EBB045} +{373E3B8A-6592-4D42-9470-D0A5E9A6460E} +{389865AC-1C56-47A5-8B49-B12BDF111071} +{45CF9BB8-19DD-4841-8822-9B5E47CD95A6} +{D6BA809F-6E50-4518-A1E8-9B88CBA27CC7} +{B47D5127-9D59-471B-992B-F006D44A3F2E} +{3BA88535-90E4-471C-8DE1-67DD268DAF38} +{505F5EB8-1FC9-43BD-8064-B0E68BE663CA} +{04ABB7B7-0915-407F-9520-6EEF5B5B6E4F} +{90D54643-FAC1-4FBC-966D-507F1D2B43FB} +{34379777-3444-4208-B99D-DE846461A34C} +{8EE4916B-990A-45A0-91FC-AE6E73BAD205} +{BEE14656-9B19-4E10-A494-FC09B7BE2537} +{2D37F8AF-D65E-43A0-ADFF-D492484E9BB4} +{543486BF-8C7F-46E6-B5E4-AF60671E5A79} +{C65B04D2-F38D-4815-A963-34D67AD027BD} +{A3A8A528-F4F5-4031-8308-7F556B64C129} +{A7335220-876F-4C84-8F58-4C7CF63DAB43} +{5232CF1A-2118-413C-BF18-594406FC60A0} +{E4393F3D-D71C-4B0C-BE63-043394AC87F4} +{89AA7DF5-3C08-48DF-A638-DE6B27BB689A} +{DA7984D1-FFC6-46FD-AA05-CF9705718548} +{93E95378-FCD0-4AD8-859C-F79D0659B4D4} +{46249933-EAFF-48CC-83D4-A2B2D9A539DF} +{8A95A867-00ED-469E-B99D-5FD8ABA81ABF} +{7FFAA3DC-DDA0-4A59-BD77-84B4369F5078} +{7D1B9FBD-2CB8-477E-B19D-5291A9B0DB2F} +{73747553-3B70-47E7-A37E-EA9C3104CFB0} +{8628DC93-23A1-402B-9482-4986B2460DCA} +{3AC507BE-2DCF-4306-9D2A-2C8C6B346E2B} +{8759C800-4115-4C0F-B44B-6E4A0A3B7271} +{FEAA8A2C-3859-4A1D-B5BD-AFAE6A837273} +{8B8FFBAD-57B5-4C26-8914-C49244985EAB} +{17ED505D-22C9-4411-B251-BC1C7E4B1B5C} +{44378BF2-B6DF-4AD7-871D-466D3AE158D3} +{545506AF-96E2-44DE-B49C-3CDD3E25CE68} +{C435DFAE-2EF8-4D28-A93F-3683596F4C3B} +{A3C06778-3246-4A8B-B853-AF8247C33EDC} +{A5001B5C-1CBD-4C2E-9942-64B73BFE5D3B} +{7568CEA0-C91D-4D89-9091-98512904A1D1} +{9CC32567-D081-4F97-8377-F414F884749A} +{4632834D-AC50-48B5-A91B-503C03608212} +{4B7055B6-C079-427E-8928-FAFA59966F0F} +{BF47D251-C4D3-49C5-9CCC-C37F5AE762A0} +{7536F78B-2039-40DE-B54D-9796403C0C65} +{1D2C9F3A-3B3F-4046-8FC7-86624108C2B2} +{0D00582F-F56D-4053-B79E-DB07C8A799D6} +{A53F37B9-1DD2-4A99-956E-A8DE95E33B64} +{1F8EF880-D9B8-4C12-B266-347647DE74EF} +{20E4FDD0-E931-4A87-A6FD-2E57FD487532} +{41DDD353-69A3-4173-808D-E7BDD8083D38} +{0EB6104E-73F2-4562-B9A5-D4CCCAA77D07} +{F1D0F40E-DC0E-4CBB-B78C-3FB463D8671F} +{57144EC7-F2F4-450C-9821-C36B2AB9BEB2} +{E1E3E44C-9A28-48CC-9811-B4F8546B03E1} +{A4B3522B-6C94-4BB8-8E65-3E363A1B07E0} +{63E87256-B27A-4FA6-8D19-0907F58CD451} +{D0462D17-8893-4A99-B803-CEED288F68EB} +{234E1C20-A8B0-43AA-A22B-41F04B752A4F} +{86F727C2-2FE0-41B4-9FAE-EE51EC4D3AC0} +{799297B7-F787-4C53-B04B-D884C74F885E} +{048E3015-272E-475D-B275-CF0012C1B8FD} +{190404D4-7B8C-4A9F-83CA-1347A04CBF6B} +{A68F2EC8-98DD-40D3-8FF3-B8F5BDED323D} +{F526A141-F701-41E5-B9CB-695C074CCE51} +{2504B995-B89E-4EB1-8CD3-C1E4E32485C4} +{1B6047FE-C9FB-4C1B-864E-AF9A7AB0BA0E} +{D9BBD51B-CA5D-46F5-BE0C-900402CAA4A8} +{C1D5C0FA-6D30-4E46-BF90-F71ACA911D60} +{0391C72A-9386-4E58-B69A-CDA4F54F217F} +{69B3686B-22D9-4ECB-91EB-EA9EEFE2AD8F} +{8E64D5B8-C193-4954-B890-61E593B3F039} +{30103E98-4AC5-4ED0-B524-7CC586B358ED} +{A5486F28-FF0B-49ED-A1E8-463362714F25} +{26C60264-6250-46C2-9EB9-1618F808612B} +{1D0C8CC9-2523-41E9-96E0-3BA9F5B6AB8D} +{0C2750E8-5450-4F8E-98EC-8AEBFCAB2462} +{E4B8CFB6-5E34-4FE9-BCD0-6FD9AEADAA08} +{04063350-70E9-4E12-AF22-3963C42B1E60} +{ECC571C3-29CB-4AE6-97AC-C3F80801C5E1} +{DCAEEBD8-66B2-4000-A6D9-CD370CE687BF} +{E68D3F7A-911E-4FDD-934A-9DFFD6AC3A7B} +{EC3EA5A4-8374-412A-A5C1-B2012FC9FFBE} +{B2C9ED87-31EF-4A1B-BD2E-5DED878B210A} +{5F0100BF-0D58-4492-A8AD-FDC458184AB5} +{72C1054E-E429-44BB-8200-3F567FEFD2FE} +{8FCAF2C3-4F88-4E68-89A7-AA3F874086AE} +{12905F17-667A-4D90-AC96-73368992CC76} +{CB3E023C-7C18-4A1E-B510-FA5FDC56D130} +{5749130C-401B-46B6-A1F3-F227D549C673} +{E475EE89-9547-4C98-AA33-0D58B5884209} +{87BA9CDC-31A3-4AEB-8DB0-C1648A287FB2} +{EDB251B5-EFEA-4792-B040-60474F526530} +{14A8ED13-E1A4-42AF-8BBA-EFED617ED0A2} +{4668CE2B-265C-46C6-8A02-759A9B167BC7} +{A171750B-636E-46EB-A82D-AD3081D1E86E} +{30166F16-C73F-4F86-A149-2B99BA37A858} +{28FB5234-8E03-4416-8DA3-8C526C6948B9} +{3C96C8AA-EC8E-476E-8B5A-BD00441FE51E} +{723FB87B-D5CF-4631-9F6A-2BE1A60744DA} +{526008DA-26A8-482F-B6E6-C2AB1793FC9D} +{6E485127-81F0-4E78-A715-542B9BAC5541} +{542E4901-D6B4-4EAF-BD8B-B6096C2F1B3C} +{9501DCFC-E97B-440A-AA24-32AEB384AE8E} +{2A369571-BD93-4C90-92E6-9BA8BA06AA56} +{9115BE28-CD0B-452A-B055-23AF260640D1} +{2F676623-128C-4C34-A1A8-5F1EF0D91DCF} +{7BA3F47A-8115-4048-B47F-54FB1C8B39D2} +{B1DDCFC1-633C-4E49-9516-6E890012B8D4} +{5529E598-81EB-4901-A076-CE6F60CC6CEA} +{CF8730FE-B482-4030-9CBC-A32BCF9032A5} +{452C0F97-7CD5-41F7-9B6C-E0B2F7F662CA} +{C931C77D-104E-467B-BBEF-D0A8FD9A4A56} +{291FFEAD-958D-468E-850E-01B8A64FA4C2} +{11BF4FE6-C05D-448C-A103-BC52BA23CCA5} +{0FFDBB1C-5570-40C5-BC9D-A6A038498FDE} +{DFF6C745-2E12-471A-BF81-EB166CA8B45E} +{A6C0A248-8D0A-48A4-9EE6-2E320E35B86D} +{A6380038-73A6-40BC-B13B-BAD29A36226C} +{DA5F72B1-1E28-4654-89F4-2F5D09CB8A58} +{EC8C2D00-919B-4FFE-B72C-C05B35BE3992} +{BC245C08-2ED2-4E1A-B55C-32B85934E9B9} +{AB79DEAC-D232-45EC-B80F-D602F13F1FFB} +{DD1FC87B-3855-4370-8F31-64FDF70E4466} +{C772DEC6-6D67-4E8B-80A4-587DCFC11E04} +{C09182DB-2BDF-46A0-B6D7-6B6F6BF0F593} +{182CEBF3-0BB0-4BCC-9676-E08A05BCD094} +{85FC71FC-25EA-4ACB-B2ED-DCB1818F0810} +{E6D79DA8-0828-4949-84E1-381593D0BDDE} +{19B13FAF-F05D-4522-8258-31C76DF9D915} +{0C569AC1-5457-4616-A508-005FF24580C2} +{8090FC04-5B14-40FB-AF97-164E299B4A34} +{A6F2320E-A194-482A-A13B-8446A653F1BE} +{3135089D-17EA-4F51-8F35-1771FE4B61A8} +{A7CD16FD-3D01-4E95-AA29-DC23DFE119D5} +{D878A48E-1DC5-45C8-B282-0E36EA6D3473} +{D1AC32FD-4A2D-41DB-9A0E-FCDB2A5B68A6} +{41482B96-2613-47E5-ACCF-D9B854227C88} +{828D4366-191D-40E6-A954-98D6BC2AB6B1} +{2D7FCC84-17B1-4BC9-AE4A-AFE6F2561E74} +{00902F7E-9682-4ECC-97F3-0C989404753B} +{622D7A67-19E9-450F-9221-E0C455600021} +{55A2971A-4283-43A2-933F-94C005EBB5C3} +{E8D49994-8514-4EAF-8181-8DBE75A815D2} +{A4D3550A-6E2F-44F0-9AA2-CB3F558E5B28} +{43606175-29DB-4E0B-A271-A349EC540ADE} +{D77A811D-0421-4824-A290-02DA2F506DBC} +{CA901067-D569-4095-A422-B50D6AED708A} +{B9D9D104-3211-46BE-BEF5-92A881ED7D40} +{AD985BE7-9F53-40B3-8E58-EAC9A884FD52} +{D57BFAD1-D19C-43F6-B54F-1234265A07A3} +{D93C5C9D-3B2F-40F1-AF98-B66F194A35A6} +{632BF2EF-3839-4517-BC09-C542894D3B33} +{765C3B48-EAC5-4004-BFA2-CE4BF53F3650} +{B0014B6C-1116-4E41-BBCC-E2349D4C4038} +{347AE4EA-A715-4810-9AC4-C95A0AEE67C1} +{C7B19EE0-FA22-40B3-B94C-879879BACD77} +{61E88272-A2D3-4A54-8393-51AC2637C12B} +{8EA1DB5A-D36D-4391-A93F-52B824ECCBED} +{E445992E-4D8F-4D0E-B2C6-4C98E0FC7305} +{81F00F0D-5139-4A39-B1E5-5EA513828FBD} +{B502A097-C792-42A2-9007-80AC1988C851} +{34D7AF5E-CC1B-4885-ABFF-37CAE9F00B27} +{4BF2B76B-F5F9-4D46-BF67-779EE7EB56FA} +{D63830B2-3590-4DCF-A77B-F7917A2E7F79} +{90F49D4C-C196-4D02-AF50-E55A0BCFCA7C} +{730C72F5-779C-47C1-9DBF-4F7BC79D8C0A} +{EDEB4872-324A-4839-9037-B0EF3A6773A3} +{67C00CFC-AD88-4390-B122-4ABA4AB39E20} +{AB469D9A-56D5-4E0B-B13E-61BE4B70F773} +{6A1EEA3F-4B75-4692-A758-01F3B78BE905} +{8F5BEE7A-98A2-44E5-BB76-A2B68E108C87} +{9647B504-7C7A-48F9-85D8-BCF9F0B1A0AB} +{F3D1B9D2-7A04-44B6-9F91-860D3A2384C0} +{08508DC4-D337-40B1-93B8-52D813007FB1} +{C27E97F5-6F56-4072-950A-0E8C0ECD22E7} +{074A5CF9-90CF-405D-82D3-6FEE0690119C} +{E6B7586C-28EA-48D2-B7EB-17D42DBD1979} +{4CB85B60-D0A0-442A-A683-94039B70DDA0} +{346ECAFD-25C6-49CF-B532-765C963D176A} +{94BDA812-5706-4256-BB6A-E8F451677F49} +{52B94BDA-4FD4-4788-B28F-EEAB7C162721} +{29F7BFC5-6D34-4A12-9B34-C5805014863F} +{8862B1BF-C8BD-42FF-89D1-B0243A204059} +{C71A3471-6E8E-49B4-BA18-10A4C9564959} +{0359E329-55A1-4792-AB4E-AFEB1F335FF9} +{DE5B6D64-509C-4FC7-BE75-F9B569E4B1B6} +{28A3A0D3-1D22-4845-A97B-AC63DAF3413C} +{432A824B-4E2F-4901-AEAB-DEB65367D193} +{B60FA6EF-C69C-4EB8-9BBE-5EA91611D820} +{1BF94E6D-2B78-4961-9F69-6DBD6632BD3D} +{33DA50A7-F9E5-4CDA-B812-F54169CA8A89} +{712378F3-5C02-4F64-A618-073A000505CC} +{BEDC79F7-85D3-4837-881A-CF1A07342BB0} +{D2D412A9-D472-4A95-BBCB-7FC0C3B8B592} +{269A0E5E-A5B3-416F-B770-12532EE31550} +{30B2A744-1A12-4C9E-BE0F-0E9066E87AFD} +{0EA42729-AF8B-48C6-86E8-D11906E352CE} +{791D8D35-3FEA-4E19-9C77-6329148C64F9} +{5A4411F0-80E6-4F3B-BBB4-CA4A4C9B1096} +{2B0A12F4-18DD-421F-BCA7-9CB0925004B8} +{16B0C775-0F55-4F09-87BB-2264C7C72A11} +{BF19F5CC-67FB-406B-81C3-678CDD88258E} +{24BB4CEB-C041-4023-A448-3AE725EAEAC5} +{F8031368-5974-4C5C-8EA0-001C812628D5} +{EE9964D4-1BF6-48BE-BD08-1AA41E6CECD9} +{E607AA22-7A1D-4CA6-9FA6-FBEC7CAF505D} +{08D02487-7A25-4535-B2C3-D66D666F2884} +{59F13A6B-AF40-45F7-B7F8-9D4DA03320B1} +{D4618425-8024-415B-A39E-EA5A6A757E11} +{E927FC6B-DA58-4F6B-86DE-BEE46E7D6F24} +{3A2D9F38-3C4C-4909-BD58-F94AE94D60A6} +{0D9E47BC-3A38-457C-BD4C-D5BABE1E3168} +{0727C634-3C1B-445F-B8BE-C7D2D4E56FAB} +{C8E7DA02-29FE-4E5E-BA96-8E9B48E6D5F1} +{6028D781-1B56-4CF6-8567-B3266184A580} +{9A899381-224C-4960-8EE9-114927CF3331} +{5A52EB3A-E6DF-4A80-80ED-7479ECB6B587} +{49B7241A-38C5-4AFA-8816-2DF906B2E558} +{689F4852-777B-4865-B493-F42F05CF3EFA} +{8A3D44F9-A8F2-489B-B57E-CC9146833960} +{74EE6033-B67D-4559-A3CD-8EB3B5D601AB} +{ED19961A-2407-4353-B639-99DCA4187BCC} +{55E0BEAD-43B8-4A0F-A94C-D5D23B3C9250} +{5023B1E0-3EE7-4FE8-BDC7-6FE3B0F1CD32} +{F2F64080-E757-42C5-B023-C2B72DED1066} +{E44E20C1-1D79-49EE-A44E-3208D7C0AB49} +{BA9CE954-CC1E-4A8C-9AD5-E4A773242D09} +{3AB6811B-92B2-46F1-98F1-EA0F0B84E196} +{FBC19861-623F-4B92-9ECF-FEBD47DA4AC9} +{A9984778-D722-4BD6-9599-A6E2CA5FDE13} +{C47FA07F-CB64-4C9B-B31E-2469BA86C4E8} +{58C976E0-0F30-4334-AD68-90C293A50E8C} +{ED12CCAD-D43C-4CA0-B6FF-6EF6F1288B75} +{EA3B37A7-6C0A-4E39-BD02-FFD9DE8A1425} +{3AB54DCB-499F-4F7C-9916-6FB2B8D7F427} +{F8AA2809-D6D7-40DD-A34D-7C77EC57F208} +{248669E3-3B34-44F8-B5C6-D19512FAB392} +{5BC2F138-2564-4ADC-9606-8EE54694D6A3} +{ECA4D720-A9AA-41B4-8487-336179456282} +{C2B8D56A-7A67-454D-8DFE-8FA17FCA47F8} +{5A098B01-9784-414B-8B43-F702421DA9F3} +{B9024C07-45AD-45A5-B551-D70AF8A241A4} +{A38A7B60-A88E-4D09-A49E-0CBA4B8272EB} +{5105D931-6D6C-406C-9820-D47CAEDA5C68} +{37F91F10-6667-4967-B167-6CEABB7620C6} +{2B9C2915-A46E-4301-B118-787D8E969E9E} +{24393453-7A7C-4DC8-ADD6-44945E073D70} +{CE007102-289B-4AAD-ACC0-78947B9FA68C} +{97F657B1-22D5-4F55-9D77-8F7D23F8DD0D} +{33BBA500-00B2-4FB1-9535-C74505BFBE15} +{AC02E50D-0805-4672-B66E-42BC308B57E6} +{10A30C9B-AB0E-4622-B39F-2058816C8AC9} +{40E187E4-9812-43DF-BB93-F2729EC5F519} +{F43AAB92-2311-4285-95F5-FBD2503543C3} +{3EDD2AA2-2F08-414A-BD66-37240675D6DC} +{C8FC0883-E862-4A8F-8421-CEA1AF318888} +{E521F49A-503D-4708-9B8C-5F8D68D75CDC} +{93CF5997-A66F-4AA4-AAF5-BBAAD4F951E6} +{C63FE01E-7752-4B7B-9C32-AC04B4547229} +{92F5D05F-D8AA-4996-A5BC-84A8C0CD8C31} +{57388F2C-8AAE-468A-92AC-C6CADBAC344C} +{C3F2E78C-8C6E-41DE-8F22-3CEAFC16702E} +{D1099A76-F2D4-4ED8-9FF8-0CCC28759A8A} +{A55D92B4-821D-4AF4-92A1-EAC924FDEA79} +{D9204FF3-BEC7-47FD-9F8B-FA65852C8244} +{FBE8A4BD-F1AD-4CDC-9BCE-6FFEB7BE5B16} +{152AD548-99D6-4A5C-8941-2D3353EBF9E2} +{94C4A1C7-4455-4C07-81BF-1C839F22E7AA} +{EB267C6B-3753-4884-A293-FA6D434C721A} +{01A1A5C5-4606-4A3B-A1B7-3EB40F900860} +{581B8426-13B8-4FFD-BF40-DDF1B9D8259E} +{6E07EA6C-1806-4732-AEAA-38BD6DE47E2B} +{3EC1445C-8366-4C5C-AAF5-4D4149AB4B41} +{59850623-26EC-4194-89A6-F30061F5062E} +{719DFB9B-4E0F-45F5-AD4E-2C5BA47C906D} +{A63461BB-D0E3-4619-8E23-A94309008D35} +{0348BC02-8BDA-4CA5-AAEB-712972834273} +{CE3DC0F6-F7EC-4D75-A549-21987A274057} +{711AF477-C59C-4C49-BFA1-67FF06A2645C} +{F62CCF0B-9819-449B-8F57-1AFBB40ADE4B} +{E2EB93C1-70A8-49D7-9267-B08DC1139F9F} +{ECBC6F47-D172-4FC2-A5ED-C126CCCD170C} +{69CFC4AE-2A91-41B9-A624-39408E9577AA} +{FAACD365-EC26-49B2-8BAC-04BF168C0512} +{141FD384-7BE4-44B4-9396-2266E446C2B7} +{762D6DFD-3E0F-4F1D-A04F-8BC7247205F9} +{FF09C170-EE49-491E-B2D4-47DDA6BE2922} +{C11FD511-7C71-4098-89B8-73A9B96AEA44} +{31D2EF1E-5B69-4D49-BBAC-99B96E76669B} +{1BF8EC9F-BF4D-4D26-9898-9F9F1EDFF353} +{976658BC-A4FA-4B70-9291-19184485261A} +{F5A36399-5FAC-4B74-BA14-77A64C844745} +{F7D1C08F-E9AE-4A5A-ABDE-8E581D873E96} +{65953F22-79D4-4DD3-A6FF-BC4CDD729792} +{51A49E15-23E3-4559-817C-C38F389C3C72} +{2A9B2662-0E11-4C5C-AF7F-742D1174ED14} +{ADEE302B-F212-4751-8B50-EB8EAC9C8EFB} +{2CD3CFCE-81DE-4466-AED7-12B2BDECA087} +{585B4721-E2A3-404D-ACC0-EB6296E0E695} +{465057F2-972D-4BAA-B6AD-BC8442E847CC} +{23190313-3FA0-4237-8068-921135E8CCAF} +{ECEAFDB1-38CE-4456-959F-8BCB7F8AB42C} +{EDB712F1-60F1-4B32-A175-B958981EDD88} +{C0815807-26C9-4C9B-AE4E-689621CF85A8} +{AD1DA31F-EF7B-4BD9-AED9-254542C97BF6} +{963CBCD6-CE3E-4D95-88EA-98B57180790C} +{7D3AD7A9-DE39-4DFE-855D-D8E2EBC56EFE} +{5DFAB593-E7EA-48F4-A54C-79D401A27AFF} +{2FB2EC42-3861-4FE2-A94F-1D019ED05E48} +{AB970539-32D3-4FB3-A98D-21247FF2E81B} +{7DBC25B6-F82B-428C-853F-B2ED542DD7BB} +{D6C99305-1C60-444F-B81F-6EC6846E0DF9} +{C298BCA5-F5D8-4EF6-8AB9-9C5371DFB610} +{D887138C-5E08-47C4-8A51-53A8F714EFB7} +{0F86BF0F-F836-4FF4-B8AA-B7A826981964} +{57BA5F33-63DD-4884-ABAD-ADC55B447BCA} +{C5A04B8A-7853-4B45-92F3-E831B4C67BF9} +{CA8886A7-0940-49AE-8FBF-B6D8857621C3} +{830DA74D-7462-48B6-A133-06DA43BA64D6} +{2FDC7A9C-BB27-44B0-A855-08F5E4E78C03} +{E4B8248A-5A07-4606-8061-69DEFA22B79E} +{BAAB438D-3BBF-4855-A549-802C1F2CFF14} +{7F1D8FE4-A0A2-49D7-8199-50E1E92F0E20} +{6AE77CFC-DF7B-48CC-BA55-7417EA654C3F} +{40FA7FF1-7EE4-45CF-8FB3-A0EE1427DF27} +{5B0EA42B-8215-4F05-864D-BEA0F71263C6} +{F41EB7B2-6092-43A2-A09F-B3AF3A1382F6} +{4F6BC49A-80EC-4F8C-9648-4265CC7CEB34} +{16E358DF-D903-48F5-A026-FDC700D1C889} +{694A3224-E9B6-498D-8D8B-53824B2D0242} +{D945B365-B8FF-468C-AC0A-A53FF5149AC4} +{AD75674E-20AF-421E-9DD0-1771C5D6BA2F} +{C58C8DFD-EB05-472C-92A3-DD2888DE3674} +{6F8F4166-032E-43A6-B903-AD9FCA26F96A} +{15519B1B-26C0-4A56-A9D1-FB3FB582969A} +{33651349-5F43-40EA-8E32-134B79F45775} +{6AA35938-EA49-48DB-8552-D5F438A10886} +{C52F9C0E-499B-4327-A60F-6F8A715CF1C8} +{6C682C0F-1675-4A6C-97B8-DF16B9DCA351} +{DC607F6D-F656-4907-9609-1B60B2B99AC7} +{E72C40B3-44B4-4A99-AF57-9334DF0EF766} +{E1C662D4-634B-48DC-84E4-BF0BA598483D} +{23CAEA40-4550-45BE-B143-495EA08FC5C8} +{44F053DF-FD18-43CE-9F54-FF5EAA2BDDF5} +{55F68A46-D37C-4EFA-93AF-F5059BAA18A7} +{D485BC83-02BA-4E8F-9B7B-8BE2E32EA86C} +{54E4F522-B431-47C7-9CB8-FB85C3026B67} +{8267DBB8-DF95-4537-A1A8-C6BDC5920DE4} +{EE982A90-6EDC-423E-B1E2-48513B323585} +{3386BE6B-4548-4263-8029-814446D9223A} +{912B32EF-57AA-4281-A278-1BE53F8913B3} +{6AAA80E4-2580-4EF5-9665-744AEC074AC0} +{ACFD5194-BF16-4DFA-B98B-1444325CC84C} +{3F6CFDF8-C369-4201-B7C7-B62C128726AA} +{95AC3B82-B9FF-420D-A6B3-E7BBCBDF456B} +{921BE086-1FF7-43EC-B66E-861590CA0871} +{BDE0D739-0CDF-4BF6-8DB9-B7A1B2D0B60C} +{B28E159D-F89A-43EB-8A0F-EA840FB3E87E} +{092D0AED-2A8E-4EC6-84A0-9DAB5FD7DD10} +{B00D614C-78A3-4680-83A9-DD5D768701EF} +{617665D6-2657-4951-8DB2-5F009331A840} +{1FB0BA56-4363-4128-B870-3938E4523420} +{18C8BB36-009E-4641-9BA9-4EF16CB5FE60} +{017E35F9-9B5A-437E-8C12-13160ACC8ADB} +{588A301D-4159-4D88-A621-DE4ABF961C26} +{36C3EC96-D18F-46A6-9421-F65FF74A3081} +{B1EB5223-05FB-44D9-85EE-A8597E6EEBA9} +{E47B4039-6B59-4330-97BC-B14676A32A9F} +{1F6B7960-BB0F-4260-89DC-05FC2510687F} +{3C2C1319-E0ED-435F-BA01-4169CFB84BBC} +{28544AF2-D585-4602-BDD9-D1BCA1C45F56} +{F63FB5FC-5E28-496A-8C38-BE0175526BB8} +{D5D59AE2-B185-49F6-B4BD-0A7A6F8E52F3} +{99ABD0FB-FFE1-4263-94AB-17A59D5C247C} +{B847DB01-7EB5-49CE-88E9-0C4CA4A16D67} +{4756E785-D8A3-4E29-B993-B01B01AA4D2A} +$ +{0AADAE47-CCD6-4CC9-BF83-32297AB40C37} +Spring.NET 1.1 M1 Setup Project +{36ADC9A6-0B4F-4245-ACCE-4A50015A80A8} + +{5076ABBD-5015-4CE7-BD04-2E313FB65264} + +{B51103C7-8FD1-43CD-9553-57945F20EDE8} + +{7A4D8295-AAA0-4D43-8542-2746B3A3CB7E} +Check setup pre-requisites +{4D692B2D-36CC-437E-8B49-F054053A65B8} + +{007D81FD-F462-41C0-9766-5CA98DB29B4A} +Install setup pre-requisites +{8EE4916B-990A-45A0-91FC-AE6E73BAD205} + +{2D37F8AF-D65E-43A0-ADFF-D492484E9BB4} + +{C65B04D2-F38D-4815-A963-34D67AD027BD} +Define Setup Components +{A7335220-876F-4C84-8F58-4C7CF63DAB43} + +{5232CF1A-2118-413C-BF18-594406FC60A0} +Initialize Setup Globals +{17ED505D-22C9-4411-B251-BC1C7E4B1B5C} + +{7568CEA0-C91D-4D89-9091-98512904A1D1} +First Time Install +{7536F78B-2039-40DE-B54D-9796403C0C65} + +{12905F17-667A-4D90-AC96-73368992CC76} +TO-DO: Customize your minimum setup here +{A7CD16FD-3D01-4E95-AA29-DC23DFE119D5} + +{D1AC32FD-4A2D-41DB-9A0E-FCDB2A5B68A6} +Maintenance Install/Uninstall +{E8D49994-8514-4EAF-8181-8DBE75A815D2} + +{D77A811D-0421-4824-A290-02DA2F506DBC} +Exit Setup if Wizard Loop cancelled +{D57BFAD1-D19C-43F6-B54F-1234265A07A3} + +{D93C5C9D-3B2F-40F1-AF98-B66F194A35A6} +Prepare to install +{B502A097-C792-42A2-9007-80AC1988C851} + +{34D7AF5E-CC1B-4885-ABFF-37CAE9F00B27} +Modify Target System +{90F49D4C-C196-4D02-AF50-E55A0BCFCA7C} +Uninstall product +{730C72F5-779C-47C1-9DBF-4F7BC79D8C0A} +TO-DO: Insert any additional uninstall commands here +{9647B504-7C7A-48F9-85D8-BCF9F0B1A0AB} +Install/Re-Install product +{BAAB438D-3BBF-4855-A549-802C1F2CFF14} +TO-DO: Insert any additional install commands here +{921BE086-1FF7-43EC-B66E-861590CA0871} + +{B28E159D-F89A-43EB-8A0F-EA840FB3E87E} +End of Installation +{3C2C1319-E0ED-435F-BA01-4169CFB84BBC} +TO-DO: Insert command that starts your application here +{4756E785-D8A3-4E29-B993-B01B01AA4D2A} + +{B62B7C2B-4668-4E4E-87C7-CC1525784712} +PREREQ +FALSE +{C7F738FC-2B5F-4380-9608-0DEA772B94C7} +PRELIST + +{95C7D535-8788-41B6-B8CA-D3742CDD3E2A} +PREREQ +TRUE +{DD78225E-3B68-40CE-B4E1-93AE4BE9C37F} +PRELIST +$PRELIST$$NEWLINE$Previous Version Uninstallation +{D4F36731-7336-4097-A355-B0B1584CBB44} +REMOVEOLD + +{CAC97A56-5D1A-405B-A78B-FE91751C7B09} +ERROROLD + +{B47D5127-9D59-471B-992B-F006D44A3F2E} +MAINTENANCE +FALSE +{7FFAA3DC-DDA0-4A59-BD77-84B4369F5078} +SHORTCUTFILESALL +$SHORTCUTFILES$ +{9CC32567-D081-4F97-8377-F414F884749A} +TARGETDIR +$PROGRAMFILES$\$TITLE$ +{4632834D-AC50-48B5-A91B-503C03608212} +STARTMENU +$TITLE$ +{632BF2EF-3839-4517-BC09-C542894D3B33} +PROGRESSTEXT +Installing $TITLE$ +{765C3B48-EAC5-4004-BFA2-CE4BF53F3650} +SUCCESS + +{B0014B6C-1116-4E41-BBCC-E2349D4C4038} +LASTERROR + +{C7B19EE0-FA22-40B3-B94C-879879BACD77} +SHORTCUTFOLDER +$SHORTCUTFILESALL$\$STARTMENU$ +{8EA1DB5A-D36D-4391-A93F-52B824ECCBED} +SHORTCUTFOLDER +$SHORTCUTFILES$\$STARTMENU$ +{67C00CFC-AD88-4390-B122-4ABA4AB39E20} +PROGRESS +100 +{6AAA80E4-2580-4EF5-9665-744AEC074AC0} +PROGRESS +100 +{8E733ADF-0692-4381-A6CE-1A14FC3B3EFD} +BUILDMODE +0 +PATCH +TRUE +{E3B49129-FB7E-494B-8868-3A7E035A1BB6} +NEEDSUPGRADE +0 +TRUE +FALSE +{B0E8FF3A-6587-4329-AD90-A5E8BD6B61D5} +PREREQ +0 +FALSE +TRUE +{CC974A69-AA46-4F01-934D-3AD31BE3EA59} +WIZARD +0 +CANCEL +FALSE +{A07B9E2D-B4D2-4987-9814-D9E37F8042F5} +BUILDMODE +0 +PATCH +TRUE +{B318EA01-0253-43B2-8A21-BB05E733C066} +NEEDSUPGRADE +0 +TRUE +FALSE +{C81A209F-B1AF-42AC-9982-8EC8CCC33464} +REMOVEOLD +0 +ERROR +FALSE +{BC322E68-8DCC-4C68-88F9-B1B2E20C4180} +REMOVEOLD +0 +REBOOT +FALSE +{35873C23-DAB9-48D3-AD16-0C5140653771} +REBOOTNOW +0 +OK +FALSE +{8A95A867-00ED-469E-B99D-5FD8ABA81ABF} +SHORTCUTFILESALL +0 + +FALSE +{545506AF-96E2-44DE-B49C-3CDD3E25CE68} +MAINTENANCE +0 +TRUE +FALSE +{A5001B5C-1CBD-4C2E-9942-64B73BFE5D3B} +BUILDMODE +0 +PATCH +TRUE +{A53F37B9-1DD2-4A99-956E-A8DE95E33B64} +WIZARD +0 +CANCEL +FALSE +{41DDD353-69A3-4173-808D-E7BDD8083D38} +BUILDMODE +0 +PATCH +TRUE +{57144EC7-F2F4-450C-9821-C36B2AB9BEB2} +WIZARD +0 +BACK +FALSE +{63E87256-B27A-4FA6-8D19-0907F58CD451} +WIZARD +0 +CANCEL +FALSE +{190404D4-7B8C-4A9F-83CA-1347A04CBF6B} +WIZARD +0 +BACK +FALSE +{2504B995-B89E-4EB1-8CD3-C1E4E32485C4} +WIZARD +0 +CANCEL +FALSE +{8E64D5B8-C193-4954-B890-61E593B3F039} +WIZARD +0 +BACK +FALSE +{26C60264-6250-46C2-9EB9-1618F808612B} +WIZARD +0 +CANCEL +FALSE +{DCAEEBD8-66B2-4000-A6D9-CD370CE687BF} +WIZARD +0 +BACK +FALSE +{B2C9ED87-31EF-4A1B-BD2E-5DED878B210A} +WIZARD +0 +CANCEL +FALSE +{CB3E023C-7C18-4A1E-B510-FA5FDC56D130} +MINIMUM +0 +TRUE +FALSE +{87BA9CDC-31A3-4AEB-8DB0-C1648A287FB2} +COMPLETE +0 +TRUE +FALSE +{28FB5234-8E03-4416-8DA3-8C526C6948B9} +WIZARD +0 +BACK +FALSE +{526008DA-26A8-482F-B6E6-C2AB1793FC9D} +WIZARD +0 +CANCEL +FALSE +{2F676623-128C-4C34-A1A8-5F1EF0D91DCF} +WIZARD +0 +BACK +FALSE +{5529E598-81EB-4901-A076-CE6F60CC6CEA} +WIZARD +0 +CANCEL +FALSE +{0FFDBB1C-5570-40C5-BC9D-A6A038498FDE} +WIZARD +0 +BACK +FALSE +{A6380038-73A6-40BC-B13B-BAD29A36226C} +WIZARD +0 +CANCEL +FALSE +{C09182DB-2BDF-46A0-B6D7-6B6F6BF0F593} +WIZARD +0 +BACK +FALSE +{182CEBF3-0BB0-4BCC-9676-E08A05BCD094} +BUILDMODE +0 +PATCH +TRUE +{CA901067-D569-4095-A422-B50D6AED708A} +WIZARD +0 +CANCEL +FALSE +{347AE4EA-A715-4810-9AC4-C95A0AEE67C1} +ALLUSERS +0 +TRUE +FALSE +{D63830B2-3590-4DCF-A77B-F7917A2E7F79} +REMOVE +0 +TRUE +FALSE +{2FDC7A9C-BB27-44B0-A855-08F5E4E78C03} +SELECTED +0 +TRUE +FALSE +{7F1D8FE4-A0A2-49D7-8199-50E1E92F0E20} +BUILDMODE +0 +PATCH +FALSE +{5B0EA42B-8215-4F05-864D-BEA0F71263C6} +ADVERTISE +0 +TRUE +FALSE +{617665D6-2657-4951-8DB2-5F009331A840} +SILENT +0 +FALSE +FALSE +{1FB0BA56-4363-4128-B870-3938E4523420} +REBOOTCOMPUTER +0 +TRUE +FALSE +{18C8BB36-009E-4641-9BA9-4EF16CB5FE60} +SUCCESS +0 +REBOOT +FALSE +{B1EB5223-05FB-44D9-85EE-A8597E6EEBA9} +RUNAPP +0 +TRUE +FALSE +{E47B4039-6B59-4330-97BC-B14676A32A9F} +REMOVE +0 +FALSE +FALSE +{1F6B7960-BB0F-4260-89DC-05FC2510687F} +SUCCESS +0 +COMPLETE +FALSE +{18516BF5-FA02-4D7B-BFD4-07714E2BC054} +$TITLE$ Setup +Unable to uninstall old version of $TITLE$. Please uninstall it yourself using Control Panel Add-Remove Programs before attempting to install this product.$NEWLINE$$NEWLINE$$ERROROLD$ +2 +1 + +{EDE1F9CC-5316-44E4-8FEA-FE44558E9C84} +$TITLE$ Setup +Your computer needs to be restarted before $TITLE$ Setup can continue.$NEWLINE$$NEWLINE$Please save your work in all running programs and click OK to restart your computer. Setup will resume once your computer restarts.$NEWLINE$$NEWLINE$If you click CANCEL, setup will exit. You will have to run setup again at a later time to install $TITLE$. +2 +2 +REBOOTNOW +{2F15D329-6E80-4509-829F-C2F3352970ED} +prereq +WIZARD +TRUE + +TRUE +FALSE +{5B250495-2FD6-4B4D-BE79-23040614AF44} +progressprereq +WIZARD +FALSE + +TRUE +FALSE +{0D00582F-F56D-4053-B79E-DB07C8A799D6} +welcome +WIZARD +TRUE + +TRUE +FALSE +{F1D0F40E-DC0E-4CBB-B78C-3FB463D8671F} +licensecheck +WIZARD +TRUE + +TRUE +FALSE +{048E3015-272E-475D-B275-CF0012C1B8FD} +readme +WIZARD +TRUE + +TRUE +FALSE +{69B3686B-22D9-4ECB-91EB-EA9EEFE2AD8F} +registration +WIZARD +TRUE + +TRUE +FALSE +{ECC571C3-29CB-4AE6-97AC-C3F80801C5E1} +setuptype +WIZARD +TRUE + +TRUE +FALSE +{30166F16-C73F-4F86-A149-2B99BA37A858} +componentstree +WIZARD +TRUE +PERSONALIZED +TRUE +FALSE +{9115BE28-CD0B-452A-B055-23AF260640D1} +destination +WIZARD +TRUE + +TRUE +FALSE +{11BF4FE6-C05D-448C-A103-BC52BA23CCA5} +startmenu +WIZARD +TRUE + +TRUE +FALSE +{C772DEC6-6D67-4E8B-80A4-587DCFC11E04} +startinstallation +WIZARD +TRUE + +TRUE +FALSE +{828D4366-191D-40E6-A954-98D6BC2AB6B1} +maintenance +WIZARD +TRUE + +TRUE +FALSE +{2D7FCC84-17B1-4BC9-AE4A-AFE6F2561E74} +componentstree +WIZARD +TRUE +MODIFY +TRUE +FALSE +{00902F7E-9682-4ECC-97F3-0C989404753B} +startinstallation +WIZARD +TRUE + +TRUE +FALSE +{81F00F0D-5139-4A39-B1E5-5EA513828FBD} +progress +WIZARD +FALSE + +TRUE +FALSE +{B00D614C-78A3-4680-83A9-DD5D768701EF} +finish +WIZARD +TRUE + +TRUE +FALSE +{A3A8A528-F4F5-4031-8308-7F556B64C129} +Spring.NET 1.1.2 +TRUE +Spring.NET 1.1.2 +{5749130C-401B-46B6-A1F3-F227D549C673} +Spring.NET 1.1.2 +FALSE +{EDB251B5-EFEA-4792-B040-60474F526530} +Spring.NET 1.1.2 +TRUE +{830DA74D-7462-48B6-A133-06DA43BA64D6} +Spring.NET 1.1.2 +SELECTED +{16E358DF-D903-48F5-A026-FDC700D1C889} +$TARGETDIR$\doc +{694A3224-E9B6-498D-8D8B-53824B2D0242} +$TARGETDIR$\doc\reference +{D945B365-B8FF-468C-AC0A-A53FF5149AC4} +$TARGETDIR$\doc\reference\html +{AD75674E-20AF-421E-9DD0-1771C5D6BA2F} +$TARGETDIR$\doc\reference\htmlhelp +{C58C8DFD-EB05-472C-92A3-DD2888DE3674} +$TARGETDIR$\doc\reference\images +{6F8F4166-032E-43A6-B903-AD9FCA26F96A} +$TARGETDIR$\doc\reference\pdf +{15519B1B-26C0-4A56-A9D1-FB3FB582969A} +$TARGETDIR$\doc\reference\styles +{33651349-5F43-40EA-8E32-134B79F45775} +$TARGETDIR$\examples +{6AA35938-EA49-48DB-8552-D5F438A10886} +$TARGETDIR$\examples\Spring +{C52F9C0E-499B-4327-A60F-6F8A715CF1C8} +$TARGETDIR$\examples\Spring\Spring.AopQuickStart +{6C682C0F-1675-4A6C-97B8-DF16B9DCA351} +$TARGETDIR$\examples\Spring\Spring.Calculator +{DC607F6D-F656-4907-9609-1B60B2B99AC7} +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind +{E72C40B3-44B4-4A99-AF57-9334DF0EF766} +$TARGETDIR$\examples\Spring\Spring.DataQuickStart +{E1C662D4-634B-48DC-84E4-BF0BA598483D} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext +{23CAEA40-4550-45BE-B143-495EA08FC5C8} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry +{44F053DF-FD18-43CE-9F54-FF5EAA2BDDF5} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder +{55F68A46-D37C-4EFA-93AF-F5059BAA18A7} +$TARGETDIR$\examples\Spring\Spring.TxQuickStart +{D485BC83-02BA-4E8F-9B7B-8BE2E32EA86C} +$TARGETDIR$\examples\Spring\Spring.Web.Extensions.Example +{54E4F522-B431-47C7-9CB8-FB85C3026B67} +$TARGETDIR$\examples\Spring\Spring.WebQuickStart +{8267DBB8-DF95-4537-A1A8-C6BDC5920DE4} +$TARGETDIR$\examples\Spring\SpringAir +{EDEB4872-324A-4839-9037-B0EF3A6773A3} +progress +SUCCESS +FALSE +TRUE +{F41EB7B2-6092-43A2-A09F-B3AF3A1382F6} +progress +SUCCESS +FALSE +FALSE +{EE982A90-6EDC-423E-B1E2-48513B323585} +progress +SUCCESS +TRUE +FALSE +{89AA7DF5-3C08-48DF-A638-DE6B27BB689A} +PROGRAMFILES +20 +FALSE +{DA7984D1-FFC6-46FD-AA05-CF9705718548} +COMMONFILES +20 +TRUE +{93E95378-FCD0-4AD8-859C-F79D0659B4D4} +SHORTCUTFILESALL +14 +TRUE +{46249933-EAFF-48CC-83D4-A2B2D9A539DF} +SHORTCUTFILES +14 +FALSE +{73747553-3B70-47E7-A37E-EA9C3104CFB0} +DESKTOPDIR +17 +FALSE +{8628DC93-23A1-402B-9482-4986B2460DCA} +WINDIR +25 +FALSE +{3AC507BE-2DCF-4306-9D2A-2C8C6B346E2B} +WINSYSDIR +23 +FALSE +{8759C800-4115-4C0F-B44B-6E4A0A3B7271} +QUICKLAUNCHDIR +26 +FALSE +{FEAA8A2C-3859-4A1D-B5BD-AFAE6A837273} +WWWROOTDIR +27 +FALSE +{E4393F3D-D71C-4B0C-BE63-043394AC87F4} +ISNT +18 +{C27E97F5-6F56-4072-950A-0E8C0ECD22E7} +L:\projects\Spring.Net\build\package\Spring.NET\readme.txt +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{E6B7586C-28EA-48D2-B7EB-17D42DBD1979} +L:\projects\Spring.Net\build\package\Spring.NET\BreakingChanges-1.1.txt +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{346ECAFD-25C6-49CF-B532-765C963D176A} +L:\projects\Spring.Net\build\package\Spring.NET\changelog.txt +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{52B94BDA-4FD4-4788-B28F-EEAB7C162721} +L:\projects\Spring.Net\build\package\Spring.NET\license.txt +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{8862B1BF-C8BD-42FF-89D1-B0243A204059} +L:\projects\Spring.Net\build\package\Spring.NET\bin\*.* +TRUE| +$TARGETDIR$\bin +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C71A3471-6E8E-49B4-BA18-10A4C9564959} +L:\projects\Spring.Net\build\package\Spring.NET\icons\*.* +TRUE| +$TARGETDIR$\icons +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{0359E329-55A1-4792-AB4E-AFEB1F335FF9} +L:\projects\Spring.Net\build\package\Spring.NET\Spring.Net.1.1.2005.sln +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{28A3A0D3-1D22-4845-A97B-AC63DAF3413C} +L:\projects\Spring.Net\build\package\Spring.NET\Spring.Net.1.1.2003.sln +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{B60FA6EF-C69C-4EB8-9BBE-5EA91611D820} +L:\projects\Spring.Net\build\package\Spring.NET\lib\*.* +TRUE| +$TARGETDIR$\lib +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{1BF94E6D-2B78-4961-9F69-6DBD6632BD3D} +L:\projects\Spring.Net\build\package\Spring.NET\src\*.* +TRUE| +$TARGETDIR$\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{712378F3-5C02-4F64-A618-073A000505CC} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\htmlhelp\htmlhelp.chm +FALSE| +$TARGETDIR$\doc\reference\htmlhelp +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{D2D412A9-D472-4A95-BBCB-7FC0C3B8B592} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\images\admons\*.* +TRUE| +$TARGETDIR$\doc\reference\images\admons +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{269A0E5E-A5B3-416F-B770-12532EE31550} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\images\callouts\*.* +TRUE| +$TARGETDIR$\doc\reference\images\callouts +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{30B2A744-1A12-4C9E-BE0F-0E9066E87AFD} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\pdf\spring-net-reference.pdf +FALSE| +$TARGETDIR$\doc\reference\pdf +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{791D8D35-3FEA-4E19-9C77-6329148C64F9} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\pdf\images\*.* +TRUE| +$TARGETDIR$\doc\reference\pdf\images +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{5A4411F0-80E6-4F3B-BBB4-CA4A4C9B1096} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\styles\html.css +FALSE| +$TARGETDIR$\doc\reference\styles +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{2B0A12F4-18DD-421F-BCA7-9CB0925004B8} +L:\projects\Spring.Net\build\package\Spring.NET\doc\schema\*.* +TRUE| +$TARGETDIR$\doc\schema +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{16B0C775-0F55-4F09-87BB-2264C7C72A11} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.AopQuickStart\readme.txt +FALSE| +$TARGETDIR$\examples\Spring\Spring.AopQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{BF19F5CC-67FB-406B-81C3-678CDD88258E} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.AopQuickStart\Spring.AopQuickStart.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.AopQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{F8031368-5974-4C5C-8EA0-001C812628D5} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.AopQuickStart\Spring.AopQuickStart.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.AopQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{E607AA22-7A1D-4CA6-9FA6-FBEC7CAF505D} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.AopQuickStart\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.AopQuickStart\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{08D02487-7A25-4535-B2C3-D66D666F2884} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.AopQuickStart\Spring.AopQuickStart.build +FALSE| +$TARGETDIR$\examples\Spring\Spring.AopQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{59F13A6B-AF40-45F7-B7F8-9D4DA03320B1} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\Spring.Calculator.snk +FALSE| +$TARGETDIR$\examples\Spring\Spring.Calculator +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{D4618425-8024-415B-A39E-EA5A6A757E11} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\Spring.Calculator.build +FALSE| +$TARGETDIR$\examples\Spring\Spring.Calculator +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{E927FC6B-DA58-4F6B-86DE-BEE46E7D6F24} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\Spring.Calculator.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.Calculator +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{0D9E47BC-3A38-457C-BD4C-D5BABE1E3168} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\Spring.Calculator.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.Calculator +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C8E7DA02-29FE-4E5E-BA96-8E9B48E6D5F1} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\readme.txt +FALSE| +$TARGETDIR$\examples\Spring\Spring.Calculator +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{6028D781-1B56-4CF6-8567-B3266184A580} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\lib\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.Calculator\lib +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{9A899381-224C-4960-8EE9-114927CF3331} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Calculator\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.Calculator\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{5A52EB3A-E6DF-4A80-80ED-7479ECB6B587} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Data.NHibernate.Northwind\test_northwind.sql +FALSE| +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{49B7241A-38C5-4AFA-8816-2DF906B2E558} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Data.NHibernate.Northwind\Spring.Northwind.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{8A3D44F9-A8F2-489B-B57E-CC9146833960} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Data.NHibernate.Northwind\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{74EE6033-B67D-4559-A3CD-8EB3B5D601AB} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Data.NHibernate.Northwind\test\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind\test +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ED19961A-2407-4353-B639-99DCA4187BCC} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.DataQuickStart\Spring.DataQuickStart.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.DataQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{5023B1E0-3EE7-4FE8-BDC7-6FE3B0F1CD32} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.DataQuickStart\Spring.DataQuickStart.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.DataQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{E44E20C1-1D79-49EE-A44E-3208D7C0AB49} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.DataQuickStart\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.DataQuickStart\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{BA9CE954-CC1E-4A8C-9AD5-E4A773242D09} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.DataQuickStart\test\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.DataQuickStart\test +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{3AB6811B-92B2-46F1-98F1-EA0F0B84E196} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.AppContext\Spring.IocQuickStart.AppContext.build +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{FBC19861-623F-4B92-9ECF-FEBD47DA4AC9} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.AppContext\Spring.IocQuickStart.AppContext.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C47FA07F-CB64-4C9B-B31E-2469BA86C4E8} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.AppContext\Spring.IocQuickStart.AppContext.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ED12CCAD-D43C-4CA0-B6FF-6EF6F1288B75} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.AppContext\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{EA3B37A7-6C0A-4E39-BD02-FFD9DE8A1425} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.EventRegistry\Spring.IocQuickStart.EventRegistry.build +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{3AB54DCB-499F-4F7C-9916-6FB2B8D7F427} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.EventRegistry\Spring.IocQuickStart.EventRegistry.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{248669E3-3B34-44F8-B5C6-D19512FAB392} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.EventRegistry\Spring.IocQuickStart.EventRegistry.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ECA4D720-A9AA-41B4-8487-336179456282} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.EventRegistry\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C2B8D56A-7A67-454D-8DFE-8FA17FCA47F8} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.MovieFinder\Spring.IocQuickStart.MovieFinder.build +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{5A098B01-9784-414B-8B43-F702421DA9F3} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.MovieFinder\Spring.IocQuickStart.MovieFinder.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{A38A7B60-A88E-4D09-A49E-0CBA4B8272EB} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.MovieFinder\Spring.IocQuickStart.MovieFinder.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{37F91F10-6667-4967-B167-6CEABB7620C6} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.MovieFinder\lib\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder\lib +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{2B9C2915-A46E-4301-B118-787D8E969E9E} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.IoCQuickStart.MovieFinder\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{24393453-7A7C-4DC8-ADD6-44945E073D70} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.TxQuickStart\Spring.TxQuickStart.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.TxQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{97F657B1-22D5-4F55-9D77-8F7D23F8DD0D} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.TxQuickStart\Spring.TxQuickStart.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.TxQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{AC02E50D-0805-4672-B66E-42BC308B57E6} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.TxQuickStart\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.TxQuickStart\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{10A30C9B-AB0E-4622-B39F-2058816C8AC9} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.TxQuickStart\test\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.TxQuickStart\test +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{40E187E4-9812-43DF-BB93-F2729EC5F519} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Web.Extensions.Example\Spring.Web.Extensions.Example.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.Web.Extensions.Example +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{3EDD2AA2-2F08-414A-BD66-37240675D6DC} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.Web.Extensions.Example\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.Web.Extensions.Example\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C8FC0883-E862-4A8F-8421-CEA1AF318888} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.WebQuickStart\Spring.WebQuickStart.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\Spring.WebQuickStart +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{93CF5997-A66F-4AA4-AAF5-BBAAD4F951E6} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\Spring.WebQuickStart\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\Spring.WebQuickStart\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C63FE01E-7752-4B7B-9C32-AC04B4547229} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\SpringAir.build +FALSE| +$TARGETDIR$\examples\Spring\SpringAir +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{92F5D05F-D8AA-4996-A5BC-84A8C0CD8C31} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\SpringAir.2005.sln +FALSE| +$TARGETDIR$\examples\Spring\SpringAir +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C3F2E78C-8C6E-41DE-8F22-3CEAFC16702E} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\SpringAir.2003.sln +FALSE| +$TARGETDIR$\examples\Spring\SpringAir +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{A55D92B4-821D-4AF4-92A1-EAC924FDEA79} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\readme.txt +FALSE| +$TARGETDIR$\examples\Spring\SpringAir +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{D9204FF3-BEC7-47FD-9F8B-FA65852C8244} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\data\*.* +TRUE| +$TARGETDIR$\examples\Spring\SpringAir\data +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{FBE8A4BD-F1AD-4CDC-9BCE-6FFEB7BE5B16} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\src\*.* +TRUE| +$TARGETDIR$\examples\Spring\SpringAir\src +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{152AD548-99D6-4A5C-8941-2D3353EBF9E2} +L:\projects\Spring.Net\build\package\Spring.NET\examples\Spring\SpringAir\test\*.* +TRUE| +$TARGETDIR$\examples\Spring\SpringAir\test +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{94C4A1C7-4455-4C07-81BF-1C839F22E7AA} +L:\projects\Spring.Net\build\package\Spring.NET\test\*.* +TRUE| +$TARGETDIR$\test +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{EB267C6B-3753-4884-A293-FA6D434C721A} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\images\*.* +TRUE| +$TARGETDIR$\doc\reference\html\images +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{581B8426-13B8-4FFD-BF40-DDF1B9D8259E} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\ado.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{6E07EA6C-1806-4732-AEAA-38BD6DE47E2B} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\ajax.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{3EC1445C-8366-4C5C-AAF5-4D4149AB4B41} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\aop.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{59850623-26EC-4194-89A6-F30061F5062E} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\aop-aspect-library.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{719DFB9B-4E0F-45F5-AD4E-2C5BA47C906D} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\aop-quickstart.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{A63461BB-D0E3-4619-8E23-A94309008D35} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\background.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{0348BC02-8BDA-4CA5-AAEB-712972834273} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\dao.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{CE3DC0F6-F7EC-4D75-A549-21987A274057} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\data-quickstart.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{711AF477-C59C-4C49-BFA1-67FF06A2645C} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\dbprovider.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{F62CCF0B-9819-449B-8F57-1AFBB40ADE4B} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\expressions.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{E2EB93C1-70A8-49D7-9267-B08DC1139F9F} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ECBC6F47-D172-4FC2-A5ED-C126CCCD170C} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-core.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{69CFC4AE-2A91-41B9-A624-39408E9577AA} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-javadevelopers.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{FAACD365-EC26-49B2-8BAC-04BF168C0512} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-middle-tier.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{141FD384-7BE4-44B4-9396-2266E446C2B7} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-quickstarts.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{762D6DFD-3E0F-4F1D-A04F-8BC7247205F9} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-services.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{FF09C170-EE49-491E-B2D4-47DDA6BE2922} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\index-vsnet.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C11FD511-7C71-4098-89B8-73A9B96AEA44} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\introduction.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{31D2EF1E-5B69-4D49-BBAC-99B96E76669B} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\javadevelopers.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{1BF8EC9F-BF4D-4D26-9898-9F9F1EDFF353} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\logging.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{976658BC-A4FA-4B70-9291-19184485261A} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\migration.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{F5A36399-5FAC-4B74-BA14-77A64C844745} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\misc.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{F7D1C08F-E9AE-4A5A-ABDE-8E581D873E96} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\objects.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{65953F22-79D4-4DD3-A6FF-BC4CDD729792} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\objects-misc.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{51A49E15-23E3-4559-817C-C38F389C3C72} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\orm.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{2A9B2662-0E11-4C5C-AF7F-742D1174ED14} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\pool.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ADEE302B-F212-4751-8B50-EB8EAC9C8EFB} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\preface.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{2CD3CFCE-81DE-4466-AED7-12B2BDECA087} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\pt03.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{585B4721-E2A3-404D-ACC0-EB6296E0E695} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\quickstarts.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{465057F2-972D-4BAA-B6AD-BC8442E847CC} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\remoting.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{23190313-3FA0-4237-8068-921135E8CCAF} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\remoting-quickstart.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{ECEAFDB1-38CE-4456-959F-8BCB7F8AB42C} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\resources.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{EDB712F1-60F1-4B32-A175-B958981EDD88} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\services.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C0815807-26C9-4C9B-AE4E-689621CF85A8} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\springair.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{AD1DA31F-EF7B-4BD9-AED9-254542C97BF6} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\springobjectsxsd.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{963CBCD6-CE3E-4D95-88EA-98B57180790C} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\testing.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{7D3AD7A9-DE39-4DFE-855D-D8E2EBC56EFE} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\threading.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{5DFAB593-E7EA-48F4-A54C-79D401A27AFF} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\transaction.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{2FB2EC42-3861-4FE2-A94F-1D019ED05E48} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\tx-quickstart.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{AB970539-32D3-4FB3-A98D-21247FF2E81B} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\validation.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{7DBC25B6-F82B-428C-853F-B2ED542DD7BB} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\vsnet.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{D6C99305-1C60-444F-B81F-6EC6846E0DF9} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\web.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C298BCA5-F5D8-4EF6-8AB9-9C5371DFB610} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\web-quickstart.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{D887138C-5E08-47C4-8A51-53A8F714EFB7} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\webservices.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{0F86BF0F-F836-4FF4-B8AA-B7A826981964} +L:\projects\Spring.Net\build\package\Spring.NET\Spring.Net.1.1.2002.sln +FALSE| +$TARGETDIR$ +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{57BA5F33-63DD-4884-ABAD-ADC55B447BCA} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\extensible-xml.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{C5A04B8A-7853-4B45-92F3-E831B4C67BF9} +L:\projects\Spring.Net\build\package\Spring.NET\doc\reference\html\xsd-config.html +FALSE| +$TARGETDIR$\doc\reference\html +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +FALSE +{F3D1B9D2-7A04-44B6-9F91-860D3A2384C0} +$UNINSTALLLINK$ +Uninstall Spring.NET 1.1.2 +$SHORTCUTFOLDER$ +Removes this Interface21 product + + + + +0 +{074A5CF9-90CF-405D-82D3-6FEE0690119C} +$TARGETDIR$\readme.txt +View readme +$SHORTCUTFOLDER$ +View the readme file + + + + +0 +{432A824B-4E2F-4901-AEAB-DEB65367D193} +$TARGETDIR$\Spring.Net.1.1.2003.sln +.NET 1.1 Solution +$SHORTCUTFOLDER$\Source Code + + + + + +0 +{DE5B6D64-509C-4FC7-BE75-F9B569E4B1B6} +$TARGETDIR$\Spring.Net.1.1.2005.sln +.NET 2.0 Solution +$SHORTCUTFOLDER$\Source Code + + + + + +0 +{EE9964D4-1BF6-48BE-BD08-1AA41E6CECD9} +$TARGETDIR$\examples\Spring\Spring.AopQuickStart\Spring.AopQuickStart.2005.sln +AOP +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{24BB4CEB-C041-4023-A448-3AE725EAEAC5} +$TARGETDIR$\examples\Spring\Spring.AopQuickStart\Spring.AopQuickStart.2003.sln +AOP +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{3A2D9F38-3C4C-4909-BD58-F94AE94D60A6} +$TARGETDIR$\examples\Spring\Spring.Calculator\Spring.Calculator.2005.sln +Calculator +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{689F4852-777B-4865-B493-F42F05CF3EFA} +$TARGETDIR$\examples\Spring\Spring.Data.NHibernate.Northwind\Spring.Northwind.sln +Northwind NHibernate +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{55E0BEAD-43B8-4A0F-A94C-D5D23B3C9250} +$TARGETDIR$\examples\Spring\Spring.DataQuickStart\Spring.DataQuickStart.2005.sln +Data Access +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{A9984778-D722-4BD6-9599-A6E2CA5FDE13} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext\Spring.IocQuickStart.AppContext.2005.sln +Application Context +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{F8AA2809-D6D7-40DD-A34D-7C77EC57F208} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry\Spring.IocQuickStart.EventRegistry.2005.sln +Event Registry +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{B9024C07-45AD-45A5-B551-D70AF8A241A4} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder\Spring.IocQuickStart.MovieFinder.2005.sln +Movie Finder +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{CE007102-289B-4AAD-ACC0-78947B9FA68C} +$TARGETDIR$\examples\Spring\Spring.TxQuickStart\Spring.TxQuickStart.2005.sln +Transactions +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{F43AAB92-2311-4285-95F5-FBD2503543C3} +$TARGETDIR$\examples\Spring\Spring.Web.Extensions.Example\Spring.Web.Extensions.Example.2005.sln +AJAX +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{E521F49A-503D-4708-9B8C-5F8D68D75CDC} +$TARGETDIR$\examples\Spring\Spring.WebQuickStart\Spring.WebQuickStart.2005.sln +Web +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{57388F2C-8AAE-468A-92AC-C6CADBAC344C} +$TARGETDIR$\examples\Spring\SpringAir\SpringAir.2005.sln +Spring Air +$SHORTCUTFOLDER$\Examples\.NET 2.0 + + + + + +0 +{0727C634-3C1B-445F-B8BE-C7D2D4E56FAB} +$TARGETDIR$\examples\Spring\Spring.Calculator\Spring.Calculator.2003.sln +Calculator +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{F2F64080-E757-42C5-B023-C2B72DED1066} +$TARGETDIR$\examples\Spring\Spring.DataQuickStart\Spring.DataQuickStart.2003.sln +Data Access +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{58C976E0-0F30-4334-AD68-90C293A50E8C} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.AppContext\Spring.IocQuickStart.AppContext.2003.sln +Application Context +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{5BC2F138-2564-4ADC-9606-8EE54694D6A3} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.EventRegistry\Spring.IocQuickStart.EventRegistry.2003.sln +Event Registry +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{5105D931-6D6C-406C-9820-D47CAEDA5C68} +$TARGETDIR$\examples\Spring\Spring.IoCQuickStart.MovieFinder\Spring.IocQuickStart.MovieFinder.2003.sln +Movie Finder +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{33BBA500-00B2-4FB1-9535-C74505BFBE15} +$TARGETDIR$\examples\Spring\Spring.TxQuickStart\Spring.TxQuickStart.2003.sln +Transactions +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{D1099A76-F2D4-4ED8-9FF8-0CCC28759A8A} +$TARGETDIR$\examples\Spring\SpringAir\SpringAir.2003.sln +SpringAir +$SHORTCUTFOLDER$\Examples\.NET 1.1 + + + + + +0 +{0EA42729-AF8B-48C6-86E8-D11906E352CE} +$TARGETDIR$\doc\reference\pdf\spring-net-reference.pdf +Reference - PDF +$SHORTCUTFOLDER$\Documentation + + + + + +0 +{33DA50A7-F9E5-4CDA-B812-F54169CA8A89} +$TARGETDIR$\doc\reference\html\index.html +Reference - HTML +$SHORTCUTFOLDER$\Documentation + + + + + +0 +{BEDC79F7-85D3-4837-881A-CF1A07342BB0} +$TARGETDIR$\doc\reference\htmlhelp\htmlhelp.chm +Reference - HTMLHELP +$SHORTCUTFOLDER$\Documentation + + + + + +0 +{94BDA812-5706-4256-BB6A-E8F451677F49} +$TARGETDIR$\changelog.txt +View changelog +$SHORTCUTFOLDER$ + + + + + +0 +{29F7BFC5-6D34-4A12-9B34-C5805014863F} +$TARGETDIR$\license.txt +View license +$SHORTCUTFOLDER$ + + + + + +0 +{4CB85B60-D0A0-442A-A683-94039B70DDA0} +$TARGETDIR$\BreakingChanges-1.1.txt +View breaking changes +$SHORTCUTFOLDER$ + + + + + +0 +{01A1A5C5-4606-4A3B-A1B7-3EB40F900860} +$TARGETDIR$\Spring.Net.1.0.2002.sln +.NET 1.0 Solution +$SHORTCUTFOLDER$\Source Code + + + + + +0 +{0C995B1F-7720-416B-8062-9D30A5BF4199} +REMOVE=ALL,TRUE,$PRODUCTCODE$,FALSE,,,REMOVEOLD,ERROROLD,TRUE +mMSI.dll\mMSIExec.dll +{08508DC4-D337-40B1-93B8-52D813007FB1} + + +FALSE +{CA8886A7-0940-49AE-8FBF-B6D8857621C3} +Spring.NET 1.1.2 +Spring.NET 1.1.2.7zip +FALSE +{1D2C9F3A-3B3F-4046-8FC7-86624108C2B2} +Welcome Dialog +{0EB6104E-73F2-4562-B9A5-D4CCCAA77D07} +License Check +{799297B7-F787-4C53-B04B-D884C74F885E} +ReadMe Information +{0391C72A-9386-4E58-B69A-CDA4F54F217F} +User Registration +{04063350-70E9-4E12-AF22-3963C42B1E60} +Setup Type +{A171750B-636E-46EB-A82D-AD3081D1E86E} +Custom Setup +{2A369571-BD93-4C90-92E6-9BA8BA06AA56} +Destination Directory +{291FFEAD-958D-468E-850E-01B8A64FA4C2} +Start Menu +{DD1FC87B-3855-4370-8F31-64FDF70E4466} +Start Installation +{D878A48E-1DC5-45C8-B282-0E36EA6D3473} +Maintenance +{43606175-29DB-4E0B-A271-A349EC540ADE} +Main Install +{C435DFAE-2EF8-4D28-A93F-3683596F4C3B} +Maintenance +{1F8EF880-D9B8-4C12-B266-347647DE74EF} +Main Install +{E1E3E44C-9A28-48CC-9811-B4F8546B03E1} +Welcome Dialog +{D0462D17-8893-4A99-B803-CEED288F68EB} +Main Install +{A68F2EC8-98DD-40D3-8FF3-B8F5BDED323D} +License Check +{1B6047FE-C9FB-4C1B-864E-AF9A7AB0BA0E} +Main Install +{30103E98-4AC5-4ED0-B524-7CC586B358ED} +ReadMe Information +{1D0C8CC9-2523-41E9-96E0-3BA9F5B6AB8D} +Main Install +{E68D3F7A-911E-4FDD-934A-9DFFD6AC3A7B} +User Registration +{5F0100BF-0D58-4492-A8AD-FDC458184AB5} +Main Install +{3C96C8AA-EC8E-476E-8B5A-BD00441FE51E} +Setup Type +{6E485127-81F0-4E78-A715-542B9BAC5541} +Main Install +{7BA3F47A-8115-4048-B47F-54FB1C8B39D2} +Custom Setup +{CF8730FE-B482-4030-9CBC-A32BCF9032A5} +Main Install +{DFF6C745-2E12-471A-BF81-EB166CA8B45E} +Destination Directory +{DA5F72B1-1E28-4654-89F4-2F5D09CB8A58} +Main Install +{85FC71FC-25EA-4ACB-B2ED-DCB1818F0810} +Start Menu +{19B13FAF-F05D-4522-8258-31C76DF9D915} +Welcome Dialog +{A6F2320E-A194-482A-A13B-8446A653F1BE} +Main Install +{6AE77CFC-DF7B-48CC-BA55-7417EA654C3F} +SUCCESS +{9FF95FFA-C00A-4F12-B05E-DB5ED37B659C} +TRUE +Check Application Requirements +{A4EDA82B-1FE9-4E39-AD3E-B88DCA4DBCC2} +FALSE + +{B9949A57-3226-461B-910B-04EED077BFD2} +TRUE +Check/Install Application Pre-Requisites +{CDBD7138-E76A-4A1F-A427-97837EAF89CB} +TRUE +Check Application Pre-Requisites +{DCC35A72-84BC-4C7A-97BA-17F3C2C6B183} +FALSE + +{CFCA2177-B5D4-4498-9341-74DA4A2BF10E} +TRUE +Install Application Pre-Requisites +{34379777-3444-4208-B99D-DE846461A34C} +FALSE + +{BEE14656-9B19-4E10-A494-FC09B7BE2537} +FALSE + +{543486BF-8C7F-46E6-B5E4-AF60671E5A79} +TRUE +Define Setup Globals +{8B8FFBAD-57B5-4C26-8914-C49244985EAB} +FALSE + +{44378BF2-B6DF-4AD7-871D-466D3AE158D3} +TRUE +Setup User Interview +{55A2971A-4283-43A2-933F-94C005EBB5C3} +FALSE + +{A4D3550A-6E2F-44F0-9AA2-CB3F558E5B28} +TRUE +Process (Un)Installation +{4BF2B76B-F5F9-4D46-BF67-779EE7EB56FA} +TRUE +Perform Uninstallation +{6A1EEA3F-4B75-4692-A758-01F3B78BE905} +FALSE + +{8F5BEE7A-98A2-44E5-BB76-A2B68E108C87} +TRUE +Perform First Time or Maintenance Installation +{3F6CFDF8-C369-4201-B7C7-B62C128726AA} +FALSE + +{95AC3B82-B9FF-420D-A6B3-E7BBCBDF456B} +FALSE + +{BDE0D739-0CDF-4BF6-8DB9-B7A1B2D0B60C} +TRUE +Finish Setup +{B847DB01-7EB5-49CE-88E9-0C4CA4A16D67} +FALSE + +$ + + + + + + + + + + + + + + + + + + + + + + +C + + + +C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +C + + + + + + + + +C + + + + + + + + +C + + + + + + + + +C + + + + + + + + + + + + + + + + +C + + + + + + + + + + + + + + + + + +C + + + + + + + + + + + + + + + + + + + + + + + + +C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +$ diff --git a/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia.brk b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia.brk new file mode 100644 index 00000000..4d83659e --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia.brk @@ -0,0 +1,416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia.fld b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia.fld new file mode 100644 index 00000000..c5a3448a --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mia.fld @@ -0,0 +1,8 @@ +Check Application Requirements +Check/Install Application Pre-Requisites +Check Application Pre-Requisites +Install Application Pre-Requisites +Define Setup Globals +Setup User Interview +Perform Uninstallation +Finish Setup diff --git a/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mpr b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mpr new file mode 100644 index 00000000..39889927 --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mpr @@ -0,0 +1,500 @@ +Spring.NET-1.1.mia +componentstree.dfm +componentstree.dfm.miaf +destination.dfm +destination.dfm.miaf +finish.dfm +finish.dfm.miaf +licensecheck.dfm +licensecheck.dfm.miaf +maintenance.dfm +maintenance.dfm.miaf +prereq.dfm +prereq.dfm.miaf +progress.dfm +progress.dfm.miaf +progressprereq.dfm +progressprereq.dfm.miaf +readme.dfm +readme.dfm.miaf +registration.dfm +registration.dfm.miaf +registrationwithserial.dfm +registrationwithserial.dfm.miaf +setuptype.dfm +setuptype.dfm.miaf +startinstallation.dfm +startinstallation.dfm.miaf +startmenu.dfm +startmenu.dfm.miaf +welcome.dfm +welcome.dfm.miaf +wizard.dfm +wizard.dfm.miaf +$ +icon.ico +$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +$ +$ +1 +4 +FALSE +L:\projects\Spring.Net\msi +SpringSource +Spring.NET 1.1.2 +{4D418DBC-09E3-4265-ABBC-541C5610C3F0} +{E4E36CA4-CA2E-4C28-9231-B76761E9D614} +1.1.2 +English +Spring.NET 1.1.2 +Spring.NET 1.1.2 Installation +Spring.NET authors +All rights reserved +{868E6709-F92E-4EA6-BBED-63D32EF7EEA4} +Spring.NET +Spring.NET +http://forum.springframework.net +http://www.springframework.net +All rights reserved +FALSE +Spring.NET-1.1.2 +TRUE +FALSE + + +http://timestamp.verisign.com/scripts/timstamp.dll + + +$ +$ + + +Service Pack +FALSE +FALSE +FALSE +TRUE +TRUE +FALSE +FALSE +FALSE + +$ +FALSE + diff --git a/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mpr.bak b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mpr.bak new file mode 100644 index 00000000..44769465 --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/Spring.NET-1.1.mpr.bak @@ -0,0 +1,500 @@ +Spring.NET-1.1.mia +componentstree.dfm +componentstree.dfm.miaf +destination.dfm +destination.dfm.miaf +finish.dfm +finish.dfm.miaf +licensecheck.dfm +licensecheck.dfm.miaf +maintenance.dfm +maintenance.dfm.miaf +prereq.dfm +prereq.dfm.miaf +progress.dfm +progress.dfm.miaf +progressprereq.dfm +progressprereq.dfm.miaf +readme.dfm +readme.dfm.miaf +registration.dfm +registration.dfm.miaf +registrationwithserial.dfm +registrationwithserial.dfm.miaf +setuptype.dfm +setuptype.dfm.miaf +startinstallation.dfm +startinstallation.dfm.miaf +startmenu.dfm +startmenu.dfm.miaf +welcome.dfm +welcome.dfm.miaf +wizard.dfm +wizard.dfm.miaf +$ +icon.ico +$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +$ +$ +1 +4 +FALSE +L:\projects\Spring.Net\msi +SpringSource +Spring.NET 1.1.2 +{4D418DBC-09E3-4265-ABBC-541C5610C3F0} +{E4E36CA4-CA2E-4C28-9231-B76761E9D614} +1.1.2 +English +Spring.NET 1.1.2 +Spring.NET 1.1.2 Installation +Spring.NET authors +All rights reserved +{A32AB277-1F77-4A4D-BF8C-23EA049EA2F6} +Spring.NET +Spring.NET +http://forum.springframework.net +http://www.springframework.net +All rights reserved +FALSE +Spring.NET-1.1.2 +TRUE +FALSE + + +http://timestamp.verisign.com/scripts/timstamp.dll + + +$ +$ + + +Service Pack +FALSE +FALSE +FALSE +TRUE +TRUE +FALSE +FALSE +FALSE + +$ +FALSE + diff --git a/build-support/installer/installaware/Spring.NET-1.1/componentstree.dfm b/build-support/installer/installaware/Spring.NET-1.1/componentstree.dfm new file mode 100644 index 00000000..f8190feb Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/componentstree.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/componentstree.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/componentstree.dfm.miaf new file mode 100644 index 00000000..e69de29b diff --git a/build-support/installer/installaware/Spring.NET-1.1/destination.dfm b/build-support/installer/installaware/Spring.NET-1.1/destination.dfm new file mode 100644 index 00000000..371d3049 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/destination.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/destination.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/destination.dfm.miaf new file mode 100644 index 00000000..e69de29b diff --git a/build-support/installer/installaware/Spring.NET-1.1/finish.dfm b/build-support/installer/installaware/Spring.NET-1.1/finish.dfm new file mode 100644 index 00000000..0cb0eeb5 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/finish.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/finish.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/finish.dfm.miaf new file mode 100644 index 00000000..34895796 --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/finish.dfm.miaf @@ -0,0 +1,17 @@ +IF (checkSuccess.Caption = COMPLETE) THEN textComplete.Visible := True; +IF (checkSuccess.Caption = REBOOT) THEN textReboot.Visible := True; +IF (checkSuccess.Caption = CANCEL) THEN textCancelled.Visible := True; +IF (checkSuccess.Caption = ERROR) THEN textError.Visible := True; +IF (checkRemove.Caption = TRUE) THEN textRemove.Visible := True; +IF (checkSuccess.Caption <> COMPLETE) THEN textComplete.Visible := False; +IF (checkSuccess.Caption <> REBOOT) THEN textReboot.Visible := False; +IF (checkSuccess.Caption <> CANCEL) THEN textCancelled.Visible := False; +IF (checkSuccess.Caption <> ERROR) THEN textError.Visible := False; +IF (checkRemove.Caption <> TRUE) THEN textRemove.Visible := False; +IF (checkRemove.Caption = TRUE) THEN textComplete.Visible := False; +IF (checkSuccess.Caption = CANCEL) THEN textRemove.Visible := False; +IF (textReboot.Visible = True) THEN textRemove.Visible := false; +IF (textComplete.Visible = True) THEN textRemove.Visible := false; +IF (textError.Visible = True) THEN textRemove.Visible := false; +IF (textCancelled.Visible = True) THEN textRemove.Visible := false; +IF (checkSuccess.Caption = ERROR) THEN textRemove.Visible := False; diff --git a/build-support/installer/installaware/Spring.NET-1.1/icon.ico b/build-support/installer/installaware/Spring.NET-1.1/icon.ico new file mode 100644 index 00000000..cd7967e6 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/icon.ico differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/licensecheck.dfm b/build-support/installer/installaware/Spring.NET-1.1/licensecheck.dfm new file mode 100644 index 00000000..bf044f56 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/licensecheck.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/licensecheck.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/licensecheck.dfm.miaf new file mode 100644 index 00000000..7ccb1346 --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/licensecheck.dfm.miaf @@ -0,0 +1,2 @@ +IF (LicenseCheck.Checked = True) THEN Next.Enabled := True; +IF (LicenseCheck.Checked = False) THEN Next.Enabled := False; diff --git a/build-support/installer/installaware/Spring.NET-1.1/mMSIExec.dll b/build-support/installer/installaware/Spring.NET-1.1/mMSIExec.dll new file mode 100644 index 00000000..a8289b96 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/mMSIExec.dll differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/maintenance.dfm b/build-support/installer/installaware/Spring.NET-1.1/maintenance.dfm new file mode 100644 index 00000000..63f493b2 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/maintenance.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/maintenance.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/maintenance.dfm.miaf new file mode 100644 index 00000000..e69de29b diff --git a/build-support/installer/installaware/Spring.NET-1.1/prereq.dfm b/build-support/installer/installaware/Spring.NET-1.1/prereq.dfm new file mode 100644 index 00000000..39f57028 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/prereq.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/prereq.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/prereq.dfm.miaf new file mode 100644 index 00000000..c6e2d632 --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/prereq.dfm.miaf @@ -0,0 +1,6 @@ +IF (checkWINST.Caption <> TRUE) THEN WINST.Visible := True; +IF (checkJS.Caption <> TRUE) THEN JS.Visible := True; +IF (checkDotNET.Caption <> TRUE) THEN dotNET.Visible := True; +IF (checkWINST.Caption = TRUE) THEN WINST.Visible := False; +IF (checkDotNET.Caption = TRUE) THEN dotNET.Visible := False; +IF (checkJS.Caption = TRUE) THEN JS.Visible := False; diff --git a/build-support/installer/installaware/Spring.NET-1.1/progress.dfm b/build-support/installer/installaware/Spring.NET-1.1/progress.dfm new file mode 100644 index 00000000..443f8669 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/progress.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/progress.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/progress.dfm.miaf new file mode 100644 index 00000000..f69a5df2 --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/progress.dfm.miaf @@ -0,0 +1,4 @@ +IF (TestRemove.Caption <> TRUE) THEN CaptionInstall.Visible := True; +IF (TestRemove.Caption = TRUE) THEN CaptionUninstall.Visible := True; +IF (TestRemove.Caption <> TRUE) THEN CaptionUninstall.Visible := False; +IF (TestRemove.Caption = TRUE) THEN CaptionInstall.Visible := False; diff --git a/build-support/installer/installaware/Spring.NET-1.1/progressprereq.dfm b/build-support/installer/installaware/Spring.NET-1.1/progressprereq.dfm new file mode 100644 index 00000000..182f5d5f Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/progressprereq.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/progressprereq.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/progressprereq.dfm.miaf new file mode 100644 index 00000000..e69de29b diff --git a/build-support/installer/installaware/Spring.NET-1.1/readme.dfm b/build-support/installer/installaware/Spring.NET-1.1/readme.dfm new file mode 100644 index 00000000..5cf56f6b Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/readme.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/readme.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/readme.dfm.miaf new file mode 100644 index 00000000..7ccb1346 --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/readme.dfm.miaf @@ -0,0 +1,2 @@ +IF (LicenseCheck.Checked = True) THEN Next.Enabled := True; +IF (LicenseCheck.Checked = False) THEN Next.Enabled := False; diff --git a/build-support/installer/installaware/Spring.NET-1.1/registration.dfm b/build-support/installer/installaware/Spring.NET-1.1/registration.dfm new file mode 100644 index 00000000..da6fa199 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/registration.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/registration.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/registration.dfm.miaf new file mode 100644 index 00000000..968dd502 --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/registration.dfm.miaf @@ -0,0 +1,4 @@ +IF (Name.Text <> ) THEN Next.Enabled := True; +IF (Company.Text <> ) THEN Next.Enabled := True; +IF (Name.Text = ) THEN Next.Enabled := False; +IF (Company.Text = ) THEN Next.Enabled := False; diff --git a/build-support/installer/installaware/Spring.NET-1.1/registrationwithserial.dfm b/build-support/installer/installaware/Spring.NET-1.1/registrationwithserial.dfm new file mode 100644 index 00000000..722d2a23 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/registrationwithserial.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/registrationwithserial.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/registrationwithserial.dfm.miaf new file mode 100644 index 00000000..4e4af64a --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/registrationwithserial.dfm.miaf @@ -0,0 +1,14 @@ +IF (Name.Text <> ) THEN Next.Enabled := True; +IF (Company.Text <> ) THEN Next.Enabled := True; +IF (Serial1.Text <> ) THEN Next.Enabled := True; +IF (Serial2.Text <> ) THEN Next.Enabled := True; +IF (Serial3.Text <> ) THEN Next.Enabled := True; +IF (Serial4.Text <> ) THEN Next.Enabled := True; +IF (Serial5.Text <> ) THEN Next.Enabled := True; +IF (Name.Text = ) THEN Next.Enabled := False; +IF (Company.Text = ) THEN Next.Enabled := False; +IF (Serial1.Text = ) THEN Next.Enabled := False; +IF (Serial2.Text = ) THEN Next.Enabled := False; +IF (Serial3.Text = ) THEN Next.Enabled := False; +IF (Serial4.Text = ) THEN Next.Enabled := False; +IF (Serial5.Text = ) THEN Next.Enabled := False; diff --git a/build-support/installer/installaware/Spring.NET-1.1/setuptype.dfm b/build-support/installer/installaware/Spring.NET-1.1/setuptype.dfm new file mode 100644 index 00000000..8d3caa4a Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/setuptype.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/setuptype.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/setuptype.dfm.miaf new file mode 100644 index 00000000..e69de29b diff --git a/build-support/installer/installaware/Spring.NET-1.1/startinstallation.dfm b/build-support/installer/installaware/Spring.NET-1.1/startinstallation.dfm new file mode 100644 index 00000000..29c8ed52 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/startinstallation.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/startinstallation.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/startinstallation.dfm.miaf new file mode 100644 index 00000000..e69de29b diff --git a/build-support/installer/installaware/Spring.NET-1.1/startmenu.dfm b/build-support/installer/installaware/Spring.NET-1.1/startmenu.dfm new file mode 100644 index 00000000..53ae879d Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/startmenu.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/startmenu.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/startmenu.dfm.miaf new file mode 100644 index 00000000..bb3a2f01 --- /dev/null +++ b/build-support/installer/installaware/Spring.NET-1.1/startmenu.dfm.miaf @@ -0,0 +1,4 @@ +IF (MenuGroup.Text <> ) THEN Next.Enabled := True; +IF (MenuGroup.Text = ) THEN Next.Enabled := False; +IF (ISNT.Caption = TRUE) THEN AllUsers.Enabled := True; +IF (ISNT.Caption <> TRUE) THEN AllUsers.Enabled := False; diff --git a/build-support/installer/installaware/Spring.NET-1.1/welcome.dfm b/build-support/installer/installaware/Spring.NET-1.1/welcome.dfm new file mode 100644 index 00000000..bf1e5cad Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/welcome.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/welcome.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/welcome.dfm.miaf new file mode 100644 index 00000000..e69de29b diff --git a/build-support/installer/installaware/Spring.NET-1.1/wizard.dfm b/build-support/installer/installaware/Spring.NET-1.1/wizard.dfm new file mode 100644 index 00000000..2ff70aa8 Binary files /dev/null and b/build-support/installer/installaware/Spring.NET-1.1/wizard.dfm differ diff --git a/build-support/installer/installaware/Spring.NET-1.1/wizard.dfm.miaf b/build-support/installer/installaware/Spring.NET-1.1/wizard.dfm.miaf new file mode 100644 index 00000000..e69de29b diff --git a/build-support/tools/NAnt.NUnit2OutProc.Task/NAnt.NUnit2OutProcTasks.dll b/build-support/tools/NAnt.NUnit2OutProc.Task/NAnt.NUnit2OutProcTasks.dll new file mode 100644 index 00000000..6a669fcc Binary files /dev/null and b/build-support/tools/NAnt.NUnit2OutProc.Task/NAnt.NUnit2OutProcTasks.dll differ diff --git a/build-support/tools/antlr-2.7.6/antlr-2.7.6.exe b/build-support/tools/antlr-2.7.6/antlr-2.7.6.exe new file mode 100644 index 00000000..038e7653 Binary files /dev/null and b/build-support/tools/antlr-2.7.6/antlr-2.7.6.exe differ diff --git a/build-support/tools/antlr-2.7.6/antlr.jar b/build-support/tools/antlr-2.7.6/antlr.jar new file mode 100644 index 00000000..3702b645 Binary files /dev/null and b/build-support/tools/antlr-2.7.6/antlr.jar differ diff --git a/build-support/tools/antlr-2.7.6/charset.dll b/build-support/tools/antlr-2.7.6/charset.dll new file mode 100644 index 00000000..6fdc358b Binary files /dev/null and b/build-support/tools/antlr-2.7.6/charset.dll differ diff --git a/build-support/tools/antlr-2.7.6/libiconv-2.dll b/build-support/tools/antlr-2.7.6/libiconv-2.dll new file mode 100644 index 00000000..b53575f1 Binary files /dev/null and b/build-support/tools/antlr-2.7.6/libiconv-2.dll differ diff --git a/build-support/tools/nunit/.cvsignore b/build-support/tools/nunit/.cvsignore new file mode 100644 index 00000000..7ddf2f76 --- /dev/null +++ b/build-support/tools/nunit/.cvsignore @@ -0,0 +1,2 @@ +nunit-console-* + diff --git a/build-support/tools/nunit/nunit-console-net-1.0.exe b/build-support/tools/nunit/nunit-console-net-1.0.exe new file mode 100644 index 00000000..998bae2c Binary files /dev/null and b/build-support/tools/nunit/nunit-console-net-1.0.exe differ diff --git a/build-support/tools/nunit/nunit-console-net-1.0.exe.config b/build-support/tools/nunit/nunit-console-net-1.0.exe.config new file mode 100644 index 00000000..a321ee53 --- /dev/null +++ b/build-support/tools/nunit/nunit-console-net-1.0.exe.config @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build-support/tools/nunit/nunit-console-net-1.0.pdb b/build-support/tools/nunit/nunit-console-net-1.0.pdb new file mode 100644 index 00000000..49c8275c Binary files /dev/null and b/build-support/tools/nunit/nunit-console-net-1.0.pdb differ diff --git a/build-support/tools/nunit/nunit-console-net-1.1.exe b/build-support/tools/nunit/nunit-console-net-1.1.exe new file mode 100644 index 00000000..520bc38e Binary files /dev/null and b/build-support/tools/nunit/nunit-console-net-1.1.exe differ diff --git a/build-support/tools/nunit/nunit-console-net-1.1.exe.config b/build-support/tools/nunit/nunit-console-net-1.1.exe.config new file mode 100644 index 00000000..a321ee53 --- /dev/null +++ b/build-support/tools/nunit/nunit-console-net-1.1.exe.config @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build-support/tools/nunit/nunit-console-net-1.1.pdb b/build-support/tools/nunit/nunit-console-net-1.1.pdb new file mode 100644 index 00000000..ad7dabcd Binary files /dev/null and b/build-support/tools/nunit/nunit-console-net-1.1.pdb differ diff --git a/build-support/tools/nunit/nunit-console-net-2.0.exe b/build-support/tools/nunit/nunit-console-net-2.0.exe new file mode 100644 index 00000000..e2fa7db7 Binary files /dev/null and b/build-support/tools/nunit/nunit-console-net-2.0.exe differ diff --git a/build-support/tools/nunit/nunit-console-net-2.0.exe.config b/build-support/tools/nunit/nunit-console-net-2.0.exe.config new file mode 100644 index 00000000..a321ee53 --- /dev/null +++ b/build-support/tools/nunit/nunit-console-net-2.0.exe.config @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build-support/tools/nunit/nunit-console-net-2.0.pdb b/build-support/tools/nunit/nunit-console-net-2.0.pdb new file mode 100644 index 00000000..9379ebd0 Binary files /dev/null and b/build-support/tools/nunit/nunit-console-net-2.0.pdb differ diff --git a/build-support/tools/nunit/nunit-console-runner.dll b/build-support/tools/nunit/nunit-console-runner.dll new file mode 100644 index 00000000..0b6c40f9 Binary files /dev/null and b/build-support/tools/nunit/nunit-console-runner.dll differ diff --git a/build-support/tools/nunit/nunit.core.dll b/build-support/tools/nunit/nunit.core.dll new file mode 100644 index 00000000..3f06941f Binary files /dev/null and b/build-support/tools/nunit/nunit.core.dll differ diff --git a/build-support/tools/nunit/nunit.core.extensions.dll b/build-support/tools/nunit/nunit.core.extensions.dll new file mode 100644 index 00000000..9802e811 Binary files /dev/null and b/build-support/tools/nunit/nunit.core.extensions.dll differ diff --git a/build-support/tools/nunit/nunit.core.interfaces.dll b/build-support/tools/nunit/nunit.core.interfaces.dll new file mode 100644 index 00000000..aca4c6d5 Binary files /dev/null and b/build-support/tools/nunit/nunit.core.interfaces.dll differ diff --git a/build-support/tools/nunit/nunit.fixtures.dll b/build-support/tools/nunit/nunit.fixtures.dll new file mode 100644 index 00000000..c0fae594 Binary files /dev/null and b/build-support/tools/nunit/nunit.fixtures.dll differ diff --git a/build-support/tools/nunit/nunit.framework.dll b/build-support/tools/nunit/nunit.framework.dll new file mode 100644 index 00000000..42cc12cf Binary files /dev/null and b/build-support/tools/nunit/nunit.framework.dll differ diff --git a/build-support/tools/nunit/nunit.framework.extensions.dll b/build-support/tools/nunit/nunit.framework.extensions.dll new file mode 100644 index 00000000..27568460 Binary files /dev/null and b/build-support/tools/nunit/nunit.framework.extensions.dll differ diff --git a/build-support/tools/nunit/nunit.mocks.dll b/build-support/tools/nunit/nunit.mocks.dll new file mode 100644 index 00000000..b50197b0 Binary files /dev/null and b/build-support/tools/nunit/nunit.mocks.dll differ diff --git a/build-support/tools/nunit/nunit.uikit.dll b/build-support/tools/nunit/nunit.uikit.dll new file mode 100644 index 00000000..5c36138f Binary files /dev/null and b/build-support/tools/nunit/nunit.uikit.dll differ diff --git a/build-support/tools/nunit/nunit.util.dll b/build-support/tools/nunit/nunit.util.dll new file mode 100644 index 00000000..0a70c4d5 Binary files /dev/null and b/build-support/tools/nunit/nunit.util.dll differ diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 00000000..de25eec2 --- /dev/null +++ b/changelog.txt @@ -0,0 +1,959 @@ +SPRING.NET FRAMEWORK CHANGELOG +============================== +http://www.springframework.net + +Release 1.1.2, May 6, 2008 + +Bug + +[SPRNET-930] - Validation user controls not rendering errors. +[SPRNET-931] - PreviousPage property is not set correctly during Server.Transfer + +Improvement + +[SPRNET-558] - Enable Data Validation and Model Management for UserControl +[SPRNET-785] - SessionScope / OSIV should obtain 1 EntityInterceptor per Session instance +[SPRNET-926] - Add PessimisticLockingFailureException as base class for CannotAcquireLockException, CannotSerializeTransactionException, and DeadlockLoserDataAccessException. +[SPRNET-927] - NHibernate Exception mapping improvements. +[SPRNET-932] - Create signed DLLs in debug build configuration +[SPRNET-933] - Support MethodInjection for objects declared inside the WebApplicationContext +[SPRNET-935] - Release DLLs built with /DEBUG:pdbonly +[SPRNET-937] - Add additional logging of cache inserts in Cache advice. + +Task + +[SPRNET-925] - Update documentation for DI in ASP.NET regarding use of name attribute and object definition inheritance +[SPRNET-938] - Add documentation for authoring custom namespace parsers + + + +Release 1.1.1, April 7, 2008 + +Bug + +[SPRNET-394] - Fix recursive calls to Context.GetRegistry() +[SPRNET-538] - Binding fails with binding to property of nullable type +[SPRNET-570] - ObjectNameAutoProxyCreator incompatible with NHibernate.LocalSessionFactoryObject +[SPRNET-576] - Allow formatting null values for .NET 2.0 Nullable Types +[SPRNET-755] - SpEL method invocation fails if the same expression instance is used with two different context types +[SPRNET-607] - TypeAliasConfigurer does not work in Spring.Web applications +[SPRNET-818] - Provide .NET TypeConverter for ITransactionAttribute +[SPRNET-820] - spring:CheckBoxList doesn't allow for children +[SPRNET-821] - HttpRequestBindingContainer can't bind to checkboxes +[SPRNET-827] - TypedDataSetUtils should use IDbCommand instead of provider specified SqlDbCommand. +[SPRNET-829] - Named constructor argument metadata should be stored/compared using consistent CultureInfo +[SPRNET-838] - 404 exception handling in NET 1.1 does not work as expected / differs from standard behaviour +[SPRNET-845] - OSIV/SessionScope generates the wrong key for EntityInterceptorObjectName +[SPRNET-846] - ProxyFactory allows for advisors being added twice +[SPRNET-847] - Implementations of AbstractPointcurAdvisor.Equals() and .GetHashCode() are wrong +[SPRNET-852] - Creating a custom attribute did not take into account public field values that match the named arguments in the attribute declaration. +[SPRNET-853] - Throw HibernateSystemException (part of Spring's DAO exception hierarchy) when can not translate inner exception in NHibernate.ADOException +[SPRNET-854] - Fix NullReferenceException in HibernateAccessor when SqlString in ADOException is null. +[SPRNET-860] - Placeholders not resolved in name-values element +[SPRNET-861] - Couldn't resolve internal properties in Strong Typed resources in Web Application +[SPRNET-862] - Resolve object references that use the Spring expression language while parsing config files +[SPRNET-871] - GenericApplicationContext.ctor(IApplicationContext) always throws NullReferenceException +[SPRNET-872] - Resolve WebResources relative to HttpContext.Current.Request.FilePath +[SPRNET-873] - ReadCommitted was misspelled in spring-tx-1.1.xsd +[SPRNET-874] - Null reference accessing TransactionSynchronizationManager.CurrentTransactionIsolationLevel when no Spring managed transaction is active +[SPRNET-877] - IConfigurableFactoryObject instance cannot be proxied with the AOP auto proxy functionality +[SPRNET-880] - Logging exception handler should continue processing of exception handler chain. +[SPRNET-882] - Test "ExistsValidHttp" fails +[SPRNET-886] - IInitializingObject isn't honored in case of calls to ConfigureObject() +[SPRNET-891] - Failure to create hiearchical context with depth greater than two. +[SPRNET-893] - Retry advice goes into infinite loop when exception type not listed in advice is thrown +[SPRNET-902] - DbProvider ExtractError method should explicitly perform ToString operation on object returned from SpEL expression to obtain error code +[SPRNET-908] - ReflectionUtils.MethodIsOnOneOfTheseInterfaces does not correctly iterate over multiple interface types +[SPRNET-920] - AbstractObjectFactory.GetObject(name,requiredType,arguments) does not propagate 'requiredType' and 'arguments' ParentObjectFactory + +Improvement + +[SPRNET-531] - It is not possible to call Page.SetResult() outside the page (e.g. from a Control) +[SPRNET-558] - Enable Data Validation and Model Management for UserControl +[SPRNET-559] - Change ValidationError control to enable Message resolution for UserControl +[SPRNET-762] - De-couple the url from the actual object definition name in WebServiceExporter +[SPRNET-772] - Distribute Northwind sql server 2005 data files to provide easy 'out-of-the-box' running of NHibernate example application. +[SPRNET-783] - Take TypeConverters into account in SpEL numeric aggregate functions +[SPRNET-785] - SessionScope / OSIV should obtain 1 EntityInterceptor per Session instance +[SPRNET-799] - Add support for IBinding.SetMessage() to DataBindingPanel +[SPRNET-814] - Improve Spring.Web DataBinding error handling ("SetErrorMessage()") documentation +[SPRNET-824] - Make WebApplicationContext and WebObjectFactory accessible from non Web threads +[SPRNET-830] - Improved performance in Spring.Data by optimizing reflection calls. +[SPRNET-835] - Add additional convenience method to set TableAdapter transaction/connection properties using DbProvider. +[SPRNET-841] - Add option to specify external hibernate configuration files (hibernate.cfg.xml) in LocalSessionFactoryObject +[SPRNET-842] - Using DbProvider to configure LocalSessionFactory object will by default integrate with NHibernate's 'external connection' management feature +[SPRNET-855] - Update Oracle error code mapping to include ORA-2292 as a DataIntegrityViolationCodes +[SPRNET-856] - Add 03000 as BadSqlGrammarCode and 40001 as CannotSerializeTransactioncode for Postgress providers. +[SPRNET-857] - Improved MySql provider error code mappings for DataIntegrityViolationCodes +[SPRNET-858] - Added IDbProvider implementation, UserCredentialsDbProvider, to allow changing of username, password connection strings at runtime. +[SPRNET-859] - Add addtional constructor to MultiDelegatingDbProvider that takes dictionary of target name/providers. +[SPRNET-864] - Add additional result mapping variable prefix what will not clash with PropertyPlaceholderConfigurer +[SPRNET-867] - Add line number to top level error message when there are XML parsing errors. +[SPRNET-879] - LocalSessionFactoryObject can configure ISessionFactory to use Spring's IDbProvider as a NHibernate ConnectionProvider +[SPRNET-887] - Add support for asp:HiddenField in DataBindingPanel +[SPRNET-897] - Allow binding/unbding string.Empty to nullable types +[SPRNET-898] - Add additional ICollectionProcessors to SpEL +[SPRNET-901] - Extend SpEL selection with mysql-like LIMIT functionality +[SPRNET-905] - RollbackRuleAttribute checks for null Type and null or empty string in constructor +[SPRNET-906] - Build scripts generate unique revision numbers for assemblies +[SPRNET-910] - Updated DB2 codes for exception translation +[SPRNET-913] - Add convenience method in Spring.Web.UI.Page/UserControl to navigate to a Result using a user provided object for result expression evaluation. +[SPRNET-914] - Add convenience method to get the Spring.Web.UI.Page Result as a url +[SPRNET-915] - Add additional CannotSerializeTransactionCodes for Oracle providers +[SPRNET-916] - Add additional BadSqlGrammarCode for Postgres +[SPRNET-917] - Add additonal db error codes for exception translation for Oracle and MySql providers +[SPRNET-918] - Add property to enable/disable logging of return value in SimpleLoggingAdvice +[SPRNET-919] - Update NHibernate sample application to use NHibernate 1.2.1 + +New Feature + +[SPRNET-892] - Introduce control for finer grained control over when to perform DI on user controls +[SPRNET-907] - Add Required attribute and RequiredObjectFactoryPostProcessor, allowing to enforce required object properties +[SPRNET-911] - Provide ParameterValidationAdvice to use validation framework to validate method arguments. + +Task + +[SPRNET-587] - Add example of validation usage in middle tier services +[SPRNET-689] - Add more documentation describing ITransactionAttributeSource implementations. +[SPRNET-775] - Document using a custom IFactoryObject to help with configuring embedded resources +[SPRNET-780] - Valdiation example for ASP.NET refers to wrong namespace. +[SPRNET-809] - Update to NHibernate 1.2.1 +[SPRNET-819] - Add Multiselection samples (CheckListBox, ListBox) to WebQuickStart DataBindingPanel sample +[SPRNET-826] - Add section about manually registering objects with the container ("registersingleton" etc.) +[SPRNET-831] - SpringAir html doesn't pass VS2005 default validation (not xhtml transitional) +[SPRNET-849] - Add "SetErrorMessage" sample to Data Binding section in reference docs +[SPRNET-865] - Add Documention for the expression attribute. +[SPRNET-866] - Support .NET 1.1 for NHibernate 1.2 +[SPRNET-875] - Documentation incorrectly lists Unspecified as default isolation level, should be ReadCommitted. +[SPRNET-885] - Add doc for server control usage +[SPRNET-909] - Document that WebServiceExporter does not add WebServiceBinding attribute with WSI basic profile 1.1 by default. + + + +Release 1.1 final, December 7, 2007 + +Bug + +[SPRNET-448] - TransactionTimeout setting not being applied correctly. +[SPRNET-453] - ConversionUtils should use ConvertFromInvariantString if failed to convert string value. +[SPRNET-511] - UrlResource Exists only returns true for file:// +[SPRNET-606] - Problems with Attributes in proxy generation +[SPRNET-719] - PropertyPlaceholderConfigurer does not replace property values in +[SPRNET-723] - Head control renders not XHTML valid script tag +[SPRNET-752] - Implement advanced DataSet related methods in AdoTemplate +[SPRNET-759] - Wrong assembly named used to configure NHibernate12's SessionFactory property ExposeTransactionAwareSessionFactory +[SPRNET-760] - StoredProcedure.DeclaredParameters.AddOut("A", OracleType.Cursor); causes double ':' in Oracle10g stored procedure call +[SPRNET-761] - Fix use of Nested Transactions in TxScopeTransactionManager. +[SPRNET-767] - Generic AdoTemplate CommandTimeout property does not set corresponding ClassicAdoTemplate property +[SPRNET-769] - Resume and throw exception if exception is thrown at start of transaction. +[SPRNET-770] - Nested RequiresNew propagaion options not working in AdoPlatformTransaction manager . +[SPRNET-778] - AdoExceptionTranslator in HibernateTransactionManager did not have a default value +[SPRNET-779] - NHibernateTransactionManager can not convert AdoAcessException +[SPRNET-796] - AdoTemplate not using DbMetadata for CommandBuilderDeriveParamtersMethod +[SPRNET-800] - SimpleLoggingAdvice does not correctly check for logging level other than trace. +[SPRNET-802] - Default IsolationLevel should be ReadCommitted. + +Improvement + + +[SPRNET-497] - Print 'resultName's value if Page.SetResult() is called with a non-existant result name +[SPRNET-514] - dbproviders.xml configuration should allow for an optional 'DeriveParameters' method. +[SPRNET-555] - Improve ResourceSetMessageSource documentation and add example for ASP.NET 2.0 +[SPRNET-756] - Add protected method to ErrorCodeExceptionTranslator to allow for a subclass to first attempt exception translation. +[SPRNET-766] - Add support for Sybase provider +[SPRNET-768] - Add support for ODBC provider +[SPRNET-774] - Add Designmode support for DataBindingPanel +[SPRNET-793] - Add Execute methods missing in Generic.AdoTemplate but present in non-generic version. +[SPRNET-797] - Make it such that DefaultTransactionStatus property Rollback can only be set to true +[SPRNET-803] - Allow web service base type to be configurable in WebServiceExporter. + +Task + +[SPRNET-203] - Add test coverage for Spring.Net Web module +[SPRNET-217] - QuickStart documents for Spring.Air +[SPRNET-421] - Document programmatic use of data validation framework +[SPRNET-457] - ErrorCode translation can produce extraneous UncategorizedDataAccessException +[SPRNET-516] - Improve documentation on Spring.Data DataSet functionality +[SPRNET-574] - NHibernate and custom loader +[SPRNET-590] - Include use of Spring's implementation of NHibernate's ICurrentSessionContext in demo appliation. +[SPRNET-610] - AOP documentation improvements +[SPRNET-682] - Change vs.net 2002 solution to be Spring.Net.1.1.2002.sln +[SPRNET-708] - Remove "UniqueID" based usercontrol DI sample from web reference docs +[SPRNET-726] - Improve Spring.Data.NHibernate docs w.r.t FlushMode.Never in OSIV +[SPRNET-744] - update assembly version numbers to current builds in dbproviders.xml +[SPRNET-764] - NUnit 2.4.3 GUI-runner not executing Spring.Testing.NUnit based tests linked to NUnit 2.4.1. +[SPRNET-773] - Unit tests for ServiceDomainTransactionManager +[SPRNET-776] - Unit tests for AdoPlatformTransactionManager +[SPRNET-777] - Provide 10,000 ft intro page on spring.net features on web site +[SPRNET-781] - Add new providers added to dbproviders.xml to spring-dataase-1.1.xsd +[SPRNET-786] - Fix Spring.Data related projects in Spring VS2003 solution +[SPRNET-787] - add examples for DataBindingPanel to WebQuickStart +[SPRNET-788] - integrate Spring.Web.Tests with NUnitAspEx to support real HttpRuntime-integrated tests +[SPRNET-798] - Add overview documentation from web site into reference docs + +Release 1.1 RC2, October 15, 2007 + +(Note: A bug in the dbproviders.xml file was discovered shortly after the initial release of 1.1 RC2. A new version was uploaded with the same name. This should not present a problem, but if you have trouble creating an OracleODP-2.0 provider, please download again as it may take up to a day for the proxy servers to get updated) + +Bug + +[SPRNET-297] - Cannot proxy ODP.NET OracleConnection +[SPRNET-500] - ProxyFactoryObject attempts to add all interfaces target implements even if ProxyInterfaces property is set +[SPRNET-659] - Page doesn't allow to specify neutral cultures for CultureResolver +[SPRNET-664] - Add description and show usage of Spring's WebSupportModule to documentation. +[SPRNET-667] - Update Common.Logging documentation to point to NetCommon sourceforge project web site. +[SPRNET-684] - Remove duplicate logging when completing transaction after thrown exception. +[SPRNET-685] - Rethrow exception during transaction manager rollback or commit and log correctly. +[SPRNET-686] - CacheResultAdvice should cache 'null' return values +[SPRNET-687] - AspNetCache does not differentiate cache-keys if several instances with different names exist +[SPRNET-688] - Registered transaction syncrhonizations should not have to implement IComparator. Add support for implemented Spring's IOrdered interface (optional). +[SPRNET-690] - AbstractSpringContextTests is not caching ApplicationContexts +[SPRNET-692] - Incorrect metadata in dbproviders.xml for parameterDbTypeProperty for OracleODP-2.0 +[SPRNET-696] - AbstractMessageSource messes parameters when calling ParentMessageSource +[SPRNET-704] - UserControl's SharedState doesn't reload after changing/recompiling .ascx file +[SPRNET-706] - Page.DataBound and Page.DataUnbound events are only raised if Binding collection is not empty +[SPRNET-709] - SpEL fails evaluating expression +[SPRNET-710] - DotNetMock based AbstractMessageSourceTests and MessageSourceAccessorTests have both "false positives" and "false negatives" +[SPRNET-714] - SessionFactory not bound to thread local storage if DbProvider for HibernateTransactionManager is null +[SPRNET-715] - SpringSessionSynchronization did not close hibernate session with a nested transaction with TransactionPropagation.NotSupported +[SPRNET-716] - Create new session if thread local storage SessionHolder is marked as SyncrhonizedWithTransaction. +[SPRNET-721] - Intercept all target interfaces when using an introduction with ObjectNameAutoProxyCreator +[SPRNET-722] - ReflectionUtils.getMostSpecificMethod() throws exception when handling Generic methods with the same parameter signature and different generic types +[SPRNET-725] - Concurrency problem with Spring.Expressions.PropertyOrFieldNode.Set()/.Get() +[SPRNET-727] - MethodMatchTransactionAttributeSource does not correctly return transaction attributes on candidate target object if method registered using MethodInfo object is based on an interface +[SPRNET-728] - Add missing ExecuteFind method on IHibernateOperations interface +[SPRNET-731] - OSIV configuration doesn't work as described in reference docs +[SPRNET-733] - PropertyOverrideConfigurer on abstract object definition does not work +[SPRNET-734] - Northwind FulfillmentServiceTests are broken +[SPRNET-739] - fix NHibernate12 solution + nant build script +[SPRNET-746] - StoredProcedure class doesn't return sproc return value in result dictionary +[SPRNET-749] - AdoTempate's QueryCallback did not property extract return or output values from stored procedure. +[SPRNET-750] - Add the property UseParameterPrefixInSql to IDbMetadata + +Improvement + +[SPRNET-301] - Add IInstantiationAwareObjectPostProcessor functionality +[SPRNET-399] - Simplify custom config parser registration +[SPRNET-454] - PropertyPlaceholderConfigurer should be able to modify the values within expression elements +[SPRNET-485] - Add logging of transaction definition name and description when creating new transaction. +[SPRNET-486] - Provide better logging information in TransactionSynchronizationManager to identify ConnectionHolder, DbProvider and thread name +[SPRNET-679] - ResourceManagerConverter should throw a sensible exception in case of missing App_GlobalResources +[SPRNET-683] - Change type of "TimeToLive" parameter in ICache.Insert from int to TimeSpan +[SPRNET-697] - Refactor SessionScope initialization strategy +[SPRNET-705] - Page.SharedState must be discarded by PageHandler after .aspx recompile +[SPRNET-712] - Change ICache interface to be the least common denominator of potential cache implementations +[SPRNET-717] - Add default transaction timeout property on AbstractPlatformTransactionManager +[SPRNET-718] - Make node classes in SpEL public to allow for node traveral. +[SPRNET-720] - Change ObjectNameAutoProxyCreator default behavior to proxy the product of a IFactoryObject and not the IFactoryObject itself +[SPRNET-729] - Improve API documentation in NHibernate12 project using comment-checker program +[SPRNET-730] - Make current AppDomain's config file the default for RemotingConfigurer if filename==null +[SPRNET-732] - CollectionValidator should allow validation of any IEnumerable type +[SPRNET-740] - SpEL doesn't accept unicode characters +[SPRNET-742] - Exception handling aspect supports using SpEL expression as filtering condition as alternative to use of exception name. +[SPRNET-745] - Improved PropertyOverrideConfigurer to support 'ref' and 'expression' values override + + +New Feature + +[SPRNET-344] - Add the ability to bind to collections to data binding framework +[SPRNET-614] - Logging aspect based on Common.Logging +[SPRNET-693] - Add MultiDelegatingDbProvider to main distribution to support easy access to multiple databases selected at runtime. +[SPRNET-748] - Retry Advice + +Task + +[SPRNET-495] - Add information on how to configure Spring under IIS7 to reference documentation +[SPRNET-544] - Update to use Common.Logging 1.2 +[SPRNET-736] - Remove NHibernate integrations from CVS Integration module +[SPRNET-738] - Document configuring aop Namespace parser +[SPRNET-747] - Add description of how to do assembly redirects for database assemblies. + +Release 1.1 RC1, August 10, 2007 + +Summary: Feature and bug fix release + +Bug + +[SPRNET-452] - Improve application context configuration error handling +[SPRNET-475] - ContextRegistry.GetContext() should print a detailed error +[SPRNET-583] - WebApplicationContext.Refresh() should be thread-safe +[SPRNET-607] - TypeAliasConfigurer does not work +[SPRNET-641] - MessageSource related code does not consistently use CultureInfo.CurrentUICulture as default +[SPRNET-642] - ObjectFactory must not call 'ObjectType' or 'GetObject()' of an IFactoryObject before calling IInitializingObject.AfterPropertiesSet() +[SPRNET-643] - ApplicationContextAwareProcessor and ObjectPostProcessorChecker may be registered twice on AbstractApplicationContext.Refresh() +[SPRNET-649] - AbstractLocalizer should by default use Thread.CurrentUICulturer instead of CurrentCulture +[SPRNET-650] - Introductions with AutoProxy +[SPRNET-655] - Proxy a Proxy where class explicitly implements interfaces with same method names and signatures +[SPRNET-658] - Npgsql-1.0 provider has incorrect configuration for parameter name prefix +[SPRNET-662] - DynamicMethod will call target method twice is target throws InvalidCastException +[SPRNET-666] - Change Log4NetFactoryObject to LogFactoryObject in documentation for IFactoryObject implementations (4.3.6) +[SPRNET-671] - Possible Null reference in HibernateAccessor.PrepareQuery +[SPRNET-675] - SpEL does not allow access to private/protected indexer +[SPRNET-677] - ReflectionUtils.GetMethodByArgumentValues() does not choose an exact overload match if available + +Improvement + +[SPRNET-443] - Add support for SqlLite in DbProviders.xml +[SPRNET-461] - Support accessing WebApplicationContext from asynchronous worker threads +[SPRNET-469] - Add Generics support in Spring.Data.Object.* classes (e.g. support IRowMapper) +[SPRNET-478] - Enhance the use of multiple object definition parsers +[SPRNET-481] - DelegateFactoryObject/MethodInvokingFactoryObject does not support generic methods +[SPRNET-490] - Make Model persistence customizable in Spring.Web.UI.Page +[SPRNET-565] - TxScopePlatformTransaction manager to support EnterpriseServicesInteropOption +[SPRNET-578] - Additional documentation for IVariableSource +[SPRNET-585] - Change spring-database.xsd to use 'provider' instead of 'dbprovider' element since it looks redundant in usage, i.e instead of +[SPRNET-603] - Ignore missing local resources for asp.net 2.0 pages and user controls +[SPRNET-635] - Refactor implementation regarding finding correct overloaded factory-method +[SPRNET-644] - Make IConfigurableFactoryObject members of WebServiceProxyFactory virtual +[SPRNET-648] - Make ResourceSetLocalizer ignore missing invariant resourceset +[SPRNET-651] - In Spring.Caching.AspNetCache/Spring.Web replace usages of HttpContext.Current.Cache with HttpRuntime.Cache +[SPRNET-654] - Expose method ConfigureObject( name, target, objectDefinition ) on IListableObjectFactory and all implementations +[SPRNET-660] - Make configurable the choice of reflection library used in AOP proxies. +[SPRNET-661] - Rename CheckBoxListControl -> CheckBoxList and make it a first class citizen for DataBinding +[SPRNET-663] - Change signature of IVariableSource to string ResolveVariable(string name) instead of object return value to provide consistent contract with container property replacement.. +[SPRNET-665] - Enable RegistryVariableSource to deal with REG_MULTI_SZ and REG_DWORD values +[SPRNET-668] - Throw InvalidDataAccessApiUsageException if calling Execute(ICommandCallback action) with a provider whose IDbCommand implementation does not inherit from System.Data.Common.DbCommand. +[SPRNET-669] - Provide IDbProvider to IDbCommandCreator callback to simplify implementations that want to create parameters in provider neutral manner. +[SPRNET-673] - Rename XmlParserRegistry/IXmlObjectDefinitionParser to NamespaceParserRegistry/INamespaceParser for greater naming consistency. +[SPRNET-674] - Renaming/Moving of namespace parsers into Config subnamespace, renaming of *.xsd files to *-1.1.xsd for greater naming consistency +[SPRNET-681] - Add support for DB2 iSeries dbprovider. + +New Feature + +[SPRNET-549] - Support for setting of TableAdapters transaction property for using typed data sets within a Spring demarcated transactional boundary +[SPRNET-556] - Add support for DI +[SPRNET-557] - Add DI support asp.net 2.0 intrinsic providers (membership,role,profile,sitemap) +[SPRNET-560] - Add custom HtmlForm tag to overcome "action"-Attribute rendering troubles +[SPRNET-599] - Create SessionScope based on top of current OSIV functionality +[SPRNET-647] - Add TabularMultiView control +[SPRNET-676] - Exception handling aspect for simple declarative configuration of common exception handling strategies. + +Task + +[SPRNET-434] - Document ResourceHandlersSectionHandler to register custom IResource implementations +[SPRNET-439] - Refactor TransactionAspectSupport to use LogicalThreadContext for storage of TransactionInfo +[SPRNET-612] - Transaction quick start example to use tx schema and declarative exception handling advice. +[SPRNET-638] - Additional documentation regarding how NHibernateSessions are managed by both HibernateTransactionManager and OSIV +[SPRNET-646] - Document use of Client Certificates in WebServiceProxyFactory + +Release 1.1 Milestone 2, July 11, 2007 + +Summary: Primarily a bug fix release + +Bug + +[SPRNET-501] - TxScopeTransactionManager's PromotableTxScopeTransactionObject property TransactionScope is always null. +[SPRNET-517] - Factory method IoC instantiation is not finding correct overloaded method. +[SPRNET-552] - Generic AdoTemplate to support Execute callbacks with IDbCommand as callback argument in addition to current DbCommand callback argument. +[SPRNET-613] - Recursive setting of ExposeTransactionAwareSessionFactory property in LocalSessionFactoryObject for NH12 +[SPRNET-617] - Change aop namespace element from proxy-target-class to proxy-target-type +[SPRNET-621] - AbstractMessageSource does not correctly respect 'UseCodeAsDefaultMessage' when used within a hierarchical application context. +[SPRNET-624] - Support for nested transactions using TxScopeTransactionManager. +[SPRNET-625] - TxScopeTransaction manager did not copy declarative transaction information to TransactionOptions and TransactionScopeOption +[SPRNET-630] - AbstractObjectDefinitionReader did not correctly set default IResourceLoader +[SPRNET-631] - AdoTemplate.DataSet DataSetCreate(CommandType commandType, string sql, string[] tableNames) not using tableNames parameter +[SPRNET-633] - Allow multiple calls to proceed in AOP around advice +[SPRNET-634] - AutoProxyCreators does not take into account IFactoryObject when finding candidates advisors/aspects. + +Improvement + +[SPRNET-140] - Add IVariableSource and VariablePlaceholderConfigurer allowing property placeholder data to be easily extensible to other data sources. +[SPRNET-443] - Add support for SqlLite in DbProviders.xml +[SPRNET-482] - Update MqSql support to use latest 1.0.9 release +[SPRNET-483] - Add support for MySql 1.0.9/5.0/5.1 drivers +[SPRNET-586] - Change name of IRowMapper's method argument IDataReader from 'dataReader' to 'reader' +[SPRNET-608] - Change transaction namespace element from proxy-target-class to proxy-target-type +[SPRNET-623] - Add additional functionality to TransactionSynchronizationManager (name, isolation level, etc) to level of Spring Java 2.x +[SPRNET-626] - BindingManager Configuration is not reloaded if UserControl or Page is modified +[SPRNET-632] - Update to use Common.Logging 1.1 +[SPRNET-636] - Add provider support for DB2 9.0/9.1 databases +[SPRNET-637] - Add provider support for SqlLite database + + +New Feature + +[SPRNET-528] - Add AOP proxy type cache +[SPRNET-563] - Add CommonLoggingAroundAdvice in Calculator & Northwind sample +[SPRNET-629] - Add GenericApplicationContext to simplify creation of ApplicationContext from any (i.e. non XML) source of ObjectDefinitions + +Task + +[SPRNET-253] - Document object scope in web tier. +[SPRNET-525] - Document use of InternalsVisibleTo assembly attribute to apply AOP to internal classes. +[SPRNET-584] - Add AssemblyInfo.cs for those projects under ./src that are missing them in order to have AssemblyTitle and AssemblyDescription set correctly. +[SPRNET-601] - Add tests for the new ProxyTargetAttributes property (Spring.Proxy and Spring.Aop) +[SPRNET-618] - Change MySql.Data.MySqlClient alias to refer to MySql 5.1 provider + + + +Release 1.1 Milestone 1, June 1, 2007 + +Summary: In addition to bug fixes and various improvements to existing features, this +release adds integration with NHibernate 1.0 and 1.2, NUnit, and ASP.NET AJAX. +Other new features are the introduction of aop and transaction namespace +to simplify configuration of aop and delcartive transaction managment. + +Bug + +[SPRNET-356] - Accessing HttpContext.Cache may throw an exception +[SPRNET-384] - Spring.Remoting.RemotingConfigurer.Filename within Windows Service +[SPRNET-385] - Cast to IAdvised wrong behavior +[SPRNET-391] - Table 4.5. - Duplicate entry +[SPRNET-392] - Section 4.5.3. Typos +[SPRNET-405] - ApplicationContext.ConfigureObject() doesn't process IApplicationContextAware, IMessageResolvable etc.? +[SPRNET-407] - BaseNode is not serializable +[SPRNET-420] - Page.IsPostBack is always false during PreInit in NET 1.1 +[SPRNET-425] - Throw CannotGetAdoConnectionException when not able to retrieve Connection +[SPRNET-427] - Different access levels for property getter/setter cause exception +[SPRNET-428] - DynamicReflection doesn't work in DEBUG build if reflected properties are defined in transient modules +[SPRNET-431] - Concurrency issues in DynamicReflectionManager +[SPRNET-435] - Fallback translator should always throw uncategorized exception +[SPRNET-436] - Rollback for TxScopeTransactionManager should call TransactionScope.Dispose(), not System.Transactions.Transaction.Current.Rollback() +[SPRNET-438] - AdoTemplate QueryWithRowMapperDelegate always returns empty list. +[SPRNET-447] - Incorrect class level documentation on Spring.Validation.ExclusiveValidatorGroup +[SPRNET-449] - Spring.Util.CachedTypeResolver doesn't synchronize access to it's typecache +[SPRNET-450] - Numeric enum values not handled correctly by Spring.Expressions.PropertyOrFieldNode +[SPRNET-451] - Improve? support for Nullable Types in Spring.Expressions +[SPRNET-455] - Validation does not work properly if at least one error message is not specified +[SPRNET-460] - Remove "NotImplementedException" in TxScopeTransactionManager +[SPRNET-462] - Method cannot be matched by argument types when null value is passed as an argument +[SPRNET-466] - Stored Procedure call throws System.ArgumentOutOfRangeException if no NamedResultSetProcessors are declared. +[SPRNET-468] - AdoTemplate.AdoResultProcessorsQueryCommandCallback throws IndexOutOfRangeExeption on empty namedResultSetProcessors list +[SPRNET-470] - System.Type properties can't be evaluated by Spring.Expressions +[SPRNET-474] - Content (NET 1.1 version) control's "ContentPlaceHolderId" should match NET 2.0 name +[SPRNET-476] - Fix Spring.Web runtime setup (LogicalThreadContext, default resource protocol etc.) +[SPRNET-477] - ValidationConfigParser doesn't allow for XML comments +[SPRNET-479] - AbstractXmlApplicationContext does not correctly dispose an existing ObjectFactory during RefreshObjectFactory() +[SPRNET-487] - StoredProcedure doesn't return any rows +[SPRNET-489] - Aspects do not work with OUT of REF parameters +[SPRNET-494] - PropertyOrFieldNode can't resolve shadowed properties or fields +[SPRNET-498] - Make UserControl.Controller property virtual +[SPRNET-500] - ProxyFactoryObject attempts to add all interfaces target implements even if ProxyInterfaces property is set +[SPRNET-502] - PropertyOrFieldNode.GetPropertyInfo() should change to GetMemberInfo() +[SPRNET-505] - Proxies should be decorated with target attributes (type, method, parameter, return value attributes) +[SPRNET-509] - Web DI does not work in case of URL-rewriting +[SPRNET-515] - Multiple result sets not handled correctly in AdoTemplate in calls to IDictionary Query(...) and IDictionary Query IDictionary QueryByNamedParam(...) +[SPRNET-520] - WebServiceProxyFactory throws "MissingMethodException" on non-W3K machines +[SPRNET-522] - AOP ProxyFactoryObject seems to lack thread safety +[SPRNET-523] - DynamicReflectionManager race condition +[SPRNET-526] - ProxyFactoryObject recreates ProxyType on each call to GetObject() if non-singleton +[SPRNET-527] - AdvisedSupport lacks synchronization +[SPRNET-532] - Spring.Web.UI.Control.Head should derive from System.Web.UI.HtmlControl.HtmlHead for compatibility +[SPRNET-534] - When using an Oracle SP that only contains an output parameter through the StoredProcedure class, a System.IndexOutOfRangeException is thrown +[SPRNET-535] - Exception handling within AbstractPlatformTransactionManager causes loss of stackinformation +[SPRNET-536] - ContentReplacer resolves contentPlaceholderID incorrectly in case of Master templates +[SPRNET-537] - DbParametersBuilder did not copy size of each declared parameter when creating final IDbParameter collection +[SPRNET-543] - No output parameter is returned when using StoredProcedure with a stored procedure that only returns an output parameter. +[SPRNET-551] - DefaultTransactionStatus.HasTransaction() has reversed logic +[SPRNET-554] - Add a BooleanFormatter to Spring.Globalization.Formatters +[SPRNET-568] - Threading problems with dynamic type generation + +Improvement + +[SPRNET-140] - Source of property placeholder data should be extensible. +[SPRNET-189] - Code duplication: resolving Ant style properties to environment variables. +[SPRNET-364] - ObjectNameAutoProxyCreator support for *xxx* pattern matching of object name in addition to "*xxx" and "xxx*" +[SPRNET-402] - Refactor ValidationErrors and delay the resolution of error messages. +[SPRNET-403] - Extract GetRedirectUri(object page) method from Result.DoRedirect() so it can be called independently +[SPRNET-423] - replace hardcoded html-tags with HtmlTextWriterTag enum values +[SPRNET-429] - Replace hardcoded HTML tag names with appropriate constants +[SPRNET-437] - Make Spring.Expressions serializable +[SPRNET-445] - Enable PropertyPlaceholderConfigurer to use the values from the standard connectionStrings section +[SPRNET-446] - Expression Language array initializer should allow for array size to be specified +[SPRNET-456] - Add support for set operations (union, intersection, difference) to expression language +[SPRNET-459] - Improve ObjectWrapper performance +[SPRNET-463] - Make Page / Control BindingManager's CacheKey customizable +[SPRNET-464] - Allow for "null" values and variable parameter lists in Spring.Expressions +[SPRNET-472] - Add Controller property to UserControl for easier switching databinding target +[SPRNET-473] - Allow users to specify root context and variables for the expressions within object definitions +[SPRNET-480] - Allow WebServiceClientFactory to generate proxy from a wsdl file +[SPRNET-484] - Add error reporting when Common.Logging can't load FactoryAdapter. +[SPRNET-496] - AttributeMatchMethodPointcut does not take into account implemented interfaces with method attributes +[SPRNET-506] - Introduce notion of templates and product templates within container. +[SPRNET-513] - Use of Dynamic Reflection in Spring.Aop +[SPRNET-539] - Add additional ctor argument XmlApplicationContext to supress refresh +[SPRNET-572] - Example project showing Spring.NET NHibernate features. +[SPRNET-581] - Refactor Advisor inheritance hierarchy to sync with Spring Java 2.0 +[SPRNET-582] - Add custom parser infrastructur classes to aid with parsing of custom namespaces, sync with Spring Java 2.0 + +New Feature + +[SPRNET-278] - Implement commonly used validators (email, SSN, URL, credit card, etc.) +[SPRNET-285] - ASP.NET AJAX integration to allow access in JavaScript to container objects as WebService +[SPRNET-408] - Add TypeConverter for Nullable types +[SPRNET-430] - Add collection validator to data validation framework +[SPRNET-541] - Allow registration of custom resource handlers, type aliases, and type converters within standard configuration section. +[SPRNET-545] - Add Transaction namespace to simplify configuration of declarative transactions +[SPRNET-546] - Add AOP namespace to simplify use of AOP. +[SPRNET-571] - Add support for using plain NHiberatne API and participating in Spring managed transactions via custom ICurrentSessionContext implementation. +[SPRNET-573] - Spring NHibernate support +[SPRNET-580] - NUnit integratraion + +Task + +[SPRNET-186] - Add Introductions Example to AOP Quickstart +[SPRNET-234] - Bundle SpringAir as self contained solution +[SPRNET-389] - Change how object id is generated when using ASP.NET pages +[SPRNET-400] - Can not run applications if Spring installed in GAC +[SPRNET-424] - Create NUnit task that runs in own process. +[SPRNET-432] - Remove System.Web dependency from Spring.Core +[SPRNET-512] - Merge WebServiceClientFactory & WebServiceProxyFactory +[SPRNET-566] - Update to NUnit 2.4.1 + +Release 1.1 Preview 3, December 6, 2006 + +Summary: This release is contains bug fixes and various improvements to existing features. +Major new features such as a Transaction Management Abstraction, ADO.NET data access framework, +and Spring "Services" allowing plain .NET objects to be exported as ServicedComponents, +Remoted objects, or Web Service. + +Bug + +[SPRNET-146] - Dependencies not injected into controls within Repeater +[SPRNET-225] - A Problem with Javascript and clientID of Master Page form like "Error: Expected ';' " (If use IE) +[SPRNET-254] - SpringAir WebServices doesn't work. +[SPRNET-260] - Concurent initialization of WebApplicationContext +[SPRNET-293] - Support for TransparentProxy in AOP Proxies +[SPRNET-294] - Page.PreviousPage.Items contents lost +[SPRNET-295] - Using soap header information in an exported PONO doesn't work +[SPRNET-315] - Circular reference not detected in web applications +[SPRNET-318] - IDisposable singletons with request or session scopr are not disposed of. +[SPRNET-319] - TypeConverterRegistry throws ArgumentException for valid converterTypeName +[SPRNET-325] - Hierarchical context issue +[SPRNET-327] - Objects section have to be named "objects" to be validated. +[SPRNET-328] - Converting a string value to IResource during property resolving in a Webapplication uses FileSystemResource +[SPRNET-329] - DefaultWebCultureResolver.GetDefaultLocale() throws exception on unknown Browserlanguage +[SPRNET-332] - Create a Proxy without Target +[SPRNET-333] - Some URLs generated can not be parsed by firefox +[SPRNET-334] - missing dependency injection on UserControl.LoadControl() +[SPRNET-335] - Duplicate context registration +[SPRNET-336] - Hierarchical context issue in Web application. +[SPRNET-337] - DataBinding stops working after Spring.Objects.InvalidPropertyException +[SPRNET-340] - CompositionProxyBuilder _targetMethods syncronization +[SPRNET-341] - AopContext not thread safe +[SPRNET-342] - Root context for the expression not updated properly +[SPRNET-345] - Base path should be ignored when relative path starts with slash +[SPRNET-346] - SessionHolder syncronization +[SPRNET-347] - Nested Contexts don't work in Web Applications +[SPRNET-350] - DynamicProxy doesn't support generic method declarations +[SPRNET-351] - Nested Child Contexts don't work in Web Applications +[SPRNET-354] - Update log4net version and use .NET 2.0 version +[SPRNET-356] - Accessing HttpContext.Cache may throw an exception +[SPRNET-358] - Creation via Factory Method does not support System.Type arguments. +[SPRNET-359] - WebObjectFactory and XmlObjectFactory behave differently regarding circular references +[SPRNET-360] - CacheAdvice does not take into account the parameters passed to the method +[SPRNET-365] - Page DI does not work on Server.Transfer +[SPRNET-367] - CaoExporter lifetime properties are not applied to the CAO +[SPRNET-368] - AutowireConstructor(string, RootObjectDefinition, object[]) does not work in some cases +[SPRNET-369] - Relative resources does not work with WebResource +[SPRNET-373] - .aspx, .ascx paths in object-definitions "type"-attribute don't resolve correctly +[SPRNET-374] - Use IDictionary.Item property instead of IDictionary.Add method to avoid ArgumentException in Spring.Expressions.PropertyOrFieldNode +[SPRNET-376] - ContextRegistry.Clear does not work with contexts registered by configuration +[SPRNET-377] - WebServiceHandlerFactory should not treat every Spring object as a Web Service +[SPRNET-379] - 'value' Attribute in spring objects schema does not allow empty strings +[SPRNET-393] - WebSupportModule throws exception if SessionState is not set to In-Process +[SPRNET-398] - Control DI doesn't work if @OutputCache directive is used +[SPRNET-413] - Sync issues in Common.Logging Adapter implementations + + +Improvement + +[SPRNET-58] - multi-value support in NameValueCollection +[SPRNET-174] - Refactor XmlObjectDefinitionReader inline with changes from Spring 1.2.4 +[SPRNET-175] - replace log4net with something like SLF4J +[SPRNET-249] - Possibility to add other attributes than WebMethodAttribute to the WebServiceExporter +[SPRNET-270] - New Data Binding implementation +[SPRNET-273] - Spring.Web ASP.NET 2.0-compliant +[SPRNET-312] - Preserve exception stack trace in AOP proxy +[SPRNET-330] - Duplicate code in WebObjectDefinitionParser.CalculateId() and WebUtils.GetPageName() +[SPRNET-331] - FileSystemResource doesn't handle files under contention +[SPRNET-352] - Dynamic proxy code refactoring to remove duplicate code +[SPRNET-362] - ProxyTypeBuilder should apply target type/method attributes to the generated proxy. +[SPRNET-366] - Allow CAO to be disconnected from the client +[SPRNET-370] - Add support for cross-application mapping with WebResource +[SPRNET-371] - Make use of the newly Spring.Data module in SpringAir sample +[SPRNET-375] - Allow regular expression pointcut to specify RegexOptions +[SPRNET-380] - Enable data binding within user controls +[SPRNET-390] - Add Expressions support from the IoC container +[SPRNET-395] - Add the abiliity to configure additional type-level attributes to WebServiceExporter +[SPRNET-403] - Extract GetRedirectUri(object page) method from Result.DoRedirect() so it can be called independently +[SPRNET-406] - Allow BasePath-Placeholder in "configFile" configuration element for Common.Logging.Log4net.XXX Adapters + + +New Feature + +[SPRNET-35] - Add data validation support to Spring.Web +[SPRNET-207] - Dynamic Proxy to create MarshalByRefObject objects +[SPRNET-243] - Support hosting of SaoServiceExporter in IIS +[SPRNET-267] - Design time support for Spring.Web +[SPRNET-269] - Create rendering strategies for validation controls +[SPRNET-276] - Add the ability to reference Spring-managed objects to Expression Evaluator +[SPRNET-353] - Support ProxyTargetType=true in Aop Proxies +[SPRNET-378] - Enable Spring.Expressions to be used for DI +[SPRNET-383] - Added RemotingConfigurer convenience class to configure remoting infrastructure from Spring +[SPRNET-415] - ADO.NET Data Access Framework +[SPRNET-416] - Transaction Management abstraction supporting programmatic and declarative transactions for any persistence technology. +[SPRNET-417] - AOP support for for classes with virtual methods/properties +[SPRNET-419] - Create Logging framework to remove dependencies on specific log implementation (i.e. log4net) + +Task + +[SPRNET-157] - Add XML config snippets chapter to reference documentation +[SPRNET-183] - Add IResource documentation chapter +[SPRNET-230] - DocBook callouts are broken in PDF stylesheet +[SPRNET-253] - Document object scope +[SPRNET-280] - Documentation for Validation Framework +[SPRNET-284] - Revert to old implementation of WebServiceProxyFactory +[SPRNET-321] - Documentation of code examples in C# code needs to be modified in order to conform to Document X! parsing rules. +[SPRNET-322] - Remove dependency on Log4Net +[SPRNET-323] - Unit tests for AutoProxy functionality +[SPRNET-324] - Update to use Antlr.NET 2.7.6 +[SPRNET-343] - Reset Binding.IsValid to true after processing +[SPRNET-382] - Move Spring.Interop from Spring.Services to Spring.Interop.Iiop integration project +[SPRNET-397] - Implement expression parser that will allow for simpler attribute definitions in config files +[SPRNET-410] - Create vs.net 2003 solution for web quick start +[SPRNET-411] - Update docs for bi-direction data binding in Spring.Web +[SPRNET-412] - Create docs for Common.Logging +[SPRNET-418] - Create Data Access quick start application showing using of Spring.Data ADO.NET framework + +----------------------------- + +Release 1.0.2, April 27, 2006 + +Summary: This release is contains bug fixes, new features and various improvements to existing features. + +Release Notes - Spring.NET - Version 1.0.2 + +Bug +[SPRNET-170] - Provide thread safety of IApplicationContext/IObjectFactory when retrieving lazy-init singletons. +[SPRNET-193] - ResourceSetMessageSource Not Falling Back To Base Resources +[SPRNET-244] - Registration of custom XML parsers should not use section name starting with 'config'. +[SPRNET-247] - ExpressionEvaluatorTests class ignores Culture of test box +[SPRNET-250] - Instrumentation code shouldn't use CutureInfo.CurrentUICulture as a formatter for debug messages +[SPRNET-256] - Parent objects cannot be referenced via their aliases in child object defintions +[SPRNET-262] - CopyTo method error in Spring.Collections.LinkedList +[SPRNET-263] - LinkedList Contains(object value) throws exception if list is empty. Contains(null) also fails. + +Improvement +[SPRNET-179] - Support setting of .NET Generic Collection Properties of type IList, IDictionary +[SPRNET-261] - StringArrayConverter must not use the ListSeparator of the current Culture +[SPRNET-275] - Refactor ProxyBuilder code +[SPRNET-304] - Support concurent access to ContextRegistry. +[SPRNET-305] - ReflectionUtils.ToInterfaceArray method to take inherited interfaces into account +[SPRNET-306] - Added bool Contains method to CollectionUtils +[SPRNET-307] - Changed IApplicationContext.DisplayName property name to IApplicationContext.Name +[SPRNET-308] - Added ConfigurationUtils to encapsulate configuration framework differences between .NET 1.1 and 2.0 in one place +[SPRNET-309] - Added TypeConverterRegistry and implemented section handler for 'typeConverters' config section. +[SPRNET-313] - Use Innovasys Document X! to generate SDK documentation instead of NDoc. + +New Feature +[SPRNET-101] - Added IInstantiationAwareObjectPostProcessor interface to IObjectFactory lifecycle +[SPRNET-120] - Add Method Injection Functionality +[SPRNET-248] - Add support to set indexer properties +[SPRNET-277] - Implement support for custom actions in the config parser +[SPRNET-298] - Add support for .NET 2.0 generics + - Creation of gernic types + - Dependency injection for generic collection properties (IList, IDictionary) + - Register generic type aliases for more concise configuration + - Generic Factory methods. +[SPRNET-302] - Object expression evaluation language - replaces object navigator +[SPRNET-303] - Added IQueue interface and a PriorityQueue implementation +[SPRNET-310] - Added AOP AutoProxy functionality +[SPRNET-311] - Added Validation Framework + +Task +[SPRNET-131] - Support .NET Framework 2.0 +[SPRNET-246] - Document differences in ResourceManager.GetObject behaviour in .NET 2.0 +[SPRNET-255] - Classes using Regexs are inconsistently named +[SPRNET-264] - Create strongly signed assemblies for ANTLR +[SPRNET-265] - Document AOP AutoProxy functionality +[SPRNET-279] - Test cases for Validation framework +[SPRNET-300] - Documentation for Expressions support +[SPRNET-314] - Use InstallShield instead of VS.NET solution to generate distribution file. + + +---------------------------------------- + +Release 1.1 Preview 2, November 16, 2005 +Release 1.0.1, November 16, 2005 + +Summary: This release is contains bug fixes, new features and various improvements to existing features. + +Bug +[SPRNET-112] - IListableObjectFactory should treat factory methods as special case +[SPRNET-144] - Fix Request Scope on object defintions +[SPRNET-192] - PropertyPlaceholderConfigurer does not work on IFactoryObject's property +[SPRNET-198] - Support overriden 'New' properties on a subclass that change the type of the property. +[SPRNET-200] - ObjectWrapper can not set property if it is a transparent proxy. +[SPRNET-201] - Exceptions from proxies are not being unwrapped from TargetInvocationException. + +New Feature +[SPRNET-6] - Create SaoFactoryObject to access SAO on client. +[SPRNET-56] - Create SaoServiceExporter to export and configure SAO on server +[SPRNET-82] - Add Type aliasing for more concise configuration +[SPRNET-116] - Allow configuration of the IoC container internals - resource handler, xml parsers. +[SPRNET-168] - Add case insensitivity option for object names for better web support. +[SPRNET-181] - Add support for the element tag in the XML object definition format +[SPRNET-240] - Create IRemoteFactory implementation to retrieve obtain CAO/SAO objects via + .NET Remoted ObjectFactory. +[SPRNET-241] - Create QuickStart application to showcase .NET Remoting features + +Task +[SPRNET-191] - Document config of params arguments +[SPRNET-195] - Rename ILocaleResolver to ICultureResolver +[SPRNET-196] - Move ILocaleResolver from Spring.Web to Spring.Core +[SPRNET-197] - Create archived (ZIP / TAR.GZ) files of the latest release. +[SPRNET-221] - Document the semantics of the IsSingleton property of ProxyFactoryObject +[SPRNET-222] - Document using prototype targets with the ProxyFactoryObject +[SPRNET-226] - Change references from DebugInterceptor to DebugAdvice in reference documentation +[SPRNET-229] - Document the use of prototype targets with AOP proxies +[SPRNET-232] - Document how target cannot be specified at end of InterceptorNames for ProxyFactoryObject +[SPRNET-236] - Document Page.GetMessage Resources Not Falling Back To Base Resources without 1.1 SP1 +[SPRNET-218] - Add Clover.Net output to CC.NET implementation + + +Improvement +[SPRNET-142] - StringArrayConverter to automatically trim spaces +[SPRNET-166] - ColorConverter to support more options. +[SPRNET-173] - Refactor RootBeanDefinition ctors inline with 1.2.4 release of Spring +[SPRNET-190] - AbstractFactoryObject To Supply IDisposable-style callback for singletons +[SPRNET-223] - Remove ability to list target name as last element in interceptor list. +[SPRNET-228] - Add documentation for creating advisor using AttributeMatchMethodPointcut +[SPRNET-237] - AbstractObjectFactory.GetObject to support calling constructor with array of arguments +[SPRNET-238] - Expose UserCulture setter within UserControl, in addition to getter +[SPRNET-239] - Use CultureInfo.CreateSpecificCulture instead of new CultureInfo to create culture instances + + +--------------------------------- + +Release 1.0.0, September 14, 2005 + +Summary: + +This release is primarly a bug fix and documentation enhancement release. Minor new features were added. + +** Bugs +[SPRNET-121] - Cannot proxy type declared as an inner class +[SPRNET-123] - ControlFlowPointcut breaks when method is jitted away +[SPRNET-148] - Implement methods in StaticListableObjectFactory and StaticMessageSource that throw NotImplementedException +[SPRNET-152] - Hyperlinks in pdf docs not showing correctly. +[SPRNET-158] - Remove Spring.Objects.Factory.NoSuchObjectDefinitionException thrown for flow control. +[SPRNET-159] - CachedTypeResolver check for a null typeName parameter +[SPRNET-163] - The 'clean' target of the NAnt build is broken +[SPRNET-176] - ContextHandler passes wrong arguments to DescendantContextInstantiator GetContextConstructor() + Fix incorrect processing of resources as applied to child and parent contexts that would create extraneous singleton instances. +[SPRNET-178] - ThrowsAdvice should not examine InnerExceptions + +** New Features +[SPRNET-167] - Configuration of custom collections +[SPRNET-171] - Expose PropertyComparator's SortDefinition +[SPRNET-185] - ContextRegistry.GetContext instantiates IApplicationContext from spring/context configuration section + +** Tasks + +[SPRNET-149] - Tests for IResource implementations to recognize '~' +[SPRNET-150] - Docs for PropertyResourceConfigurer NameValueCollection.Add behavior +[SPRNET-153] - DocBook HTML Help program listings not styled as per others +[SPRNET-154] - 'example programs' link on 1.0 RC1 forum announcement not working +[SPRNET-162] - Detail NameValueSectionHandler configuration in ASP.NET for PropertyXXXConfigurer +[SPRNET-165] - aop-quickstart.html not packaged in .msi + + +** Improvements + +[SPRNET-115] - Add .NET Compact Framework Support. Prebuilt Assemblies not part of distro +[SPRNET-143] - StringArrayConverter to use Culture specific list separator +[SPRNET-170] - Thread safety of IApplicationContext/IObjectFactory +[SPRNET-172] - Expose more information in the ToString of the various IObjectDefinition implementations +[SPRNET-180] - Use log4net in MovieFinder example. +[SPRNET-182] - Sync ProxyFactory / ProxyFactoryObject API with Spring.Java 1.2.4 + Added the missing AddAdvice / RemoveAdvice to the IAdvised interface. + Removed convenience Add/Remove methods for BeforeAdvice, ThrowsAdvice and Interceptor + +-------------------------------- + +Release 1.0 RC1, August 14, 2005 + +Summary: + +This release is both a feature enhancement and bug fix release to the core +container and adds an AOP framework + +** Bug + [SPRNET-55] - PropertyResourceConfigurer should not use NameValueCollection.Add(NameValueCollection) + [SPRNET-74] - thread unsafe access to application registry + [SPRNET-84] - MovieFinder example has misnamed file + [SPRNET-85] - ApplicationListener not receiving ApplicationContext events + [SPRNET-105] - ContextRegistry Silently Overwrites Contexts With The Same Name + [SPRNET-130] - Object instantiation through Factory should not require class attribute + [SPRNET-132] - DefaultListableObjectFactory should not throw an exception when MessageSource is not defined + +** New Features + [SPRNET-43] - Add support for loosely coupled event wiring in configuration file and app context creation + [SPRNET-86] - IResource implementations to recognize '~' + [SPRNET-151] - AOP framework + +** Tasks + [SPRNET-3] - VS.NET help integration + [SPRNET-31] - Document AOP features + [SPRNET-45] - FxCop code review + [SPRNET-47] - build examples as part of nant build script. + [SPRNET-48] - move Spring.Context.Tests into Spring.Core.Tests + [SPRNET-76] - Finish documentation for quickstart examples + [SPRNET-98] - Include block and architecture diagrams in the reference documentation + [SPRNET-99] - Build file needs a finer granularity for build targets + [SPRNET-109] - Investigate ConfigurableResourceLoader support for FTP + [SPRNET-110] - Investigate ResourceConverter support for ${property} expansion + [SPRNET-114] - Drop Support For Java Style Property Object Configuration + [SPRNET-119] - Update example apps to use streamlined XML config syntax. + [SPRNET-124] - ObjectsDtd.ClassAttribute constant is at odds with accepted naming conventions + [SPRNET-125] - ProxyBuilder TargetClass & BaseClass properties at odds with accepted naming conventions + [SPRNET-126] - ObjectsDtd class is inappropriately named + [SPRNET-128] - IClassFilter name is at odds with accepted naming conventions + [SPRNET-138] - Describe the lazy initialization of objects in the reference documentation + +** Improvements + [SPRNET-2] - Integration with System.ComponentModel components dropped. + [SPRNET-111] - XmlObjectFactory now only supports instantiation via the IResource abstraction + [SPRNET-113] - ApplicationEvent refactoring + [SPRNET-122] - remove asserts on exception messages + [SPRNET-129] - IMethodMatcher should use MethodInfo exclusively + [SPRNET-139] - Add support for aliased arrays in the TypeAliasResolver class + [SPRNET-147] - Add factory-method example with constructor arguments. + + [SPRNET-36] - Final pass over Spring.Java codebase to merge refactorings. + +The SPRNET-36 changes consisted of synching with Spring.Java 1.2.2 - major changes were + +Spring.Objects namespace + + - added IDictionary GetObjectsOfType(Type type) to IListableObjectFactory + - added IObjectDefinition GetObjectDefinition(string name) to IConfigurableObjectFactory + - added circular references check exception + - refined ObjectWrapperImpl and PropertyPlaceholderConfigurer to never log property values (which might be sensitive data) + - added "GetObjectPostProcessorCount" method to ConfigurableObjectFactory interface + - added "IgnoreDependencyInterface" method to AbstractAutowireCapableObjectFactory and ConfigurableListableObjectFactory + - reworked AbstractAutowireCapableObjectFactory's dependency exclusion check to support ignored dependency interfaces + - AbstractAutowireCapableObjectFactory registers ObjectFactoryAware as default ignored dependency interface + - Factored out ObjectDefinitionVisitor from PropertyPlaceholderConfigurer, making the object definition traversal reusable + - ConstructorArgumentValues holds generic ValueHolders in a List instead of a Set now, keeping their definition order + - ConstructorArgumentValues offers "Get(Generic)ArgumentValue" variant that excludes already used ValueHolders + - AbstractAutowireCapableObjectFactory applies generic constructor argument values of the same type in definition order + - factored out ObjectDefinitionValueResolver helper class from AbstractAutowireCapableObjectFactory + - added InstantiationAwareObjectPostProcessor extension of ObjecPostProcessor interface, intercepting before instantiation + - added ObjectReferenceFactoryObject, allowing for exposure of a target object under a different name (effectively an alias) + - refined AbstractObjectFactory's "GetType" to only suppress specific exceptions like ObjectCurrentlyInCreationException + - refined AbstractAutowireCapableObjectFactory's "autowireConstructor" to only suppress UnsatisfiedDependencyException + - added "value"/"ref" attributes to XML "property"/"constructor-arg" tag, as shortcut alternative to child elements + - added "key" sub-element to XML "entry" tag for maps, allowing for inner beans, refs, values etc specified as key + - added "key-ref" attribute to XML "entry" tag for maps, as shortcut alternative to a key element with "ref bean=" + +Spring.Context namespace + + - AbstractApplicationContext registers MessageSourceAware/ApplicationContextAware/etc as ignored dependency interfaces + - fixed AbstractApplicationContext to not exclude dependencies of type MessageSource/ApplicationContext/etc by default + - AbstractApplicationContext logs message when a bean is not eligible for getting processed by all ObjectPostProcessors + - fixed GenericApplicationContext to correctly propagate the internal parent ObjectFactory in case of a "SetParent" call + - GenericApplicationContext detects a passed-in ResourceLoader that implements ResourcePatternResolver and uses it + + +------------------------------------------------ +Release 0.6 Release Candidate 3 March 30, 2005. + +Summary: + +This is both a feature enhancement and bug fix release. + +Breaking changes from RC2 are the removal of the DTD. The XML schema file +is used to validate the XML instead. RC2 assemblies were delay signed, if you +disabled assembly verification for any reason, enable it again. The RC3 assemblies +are strongly named. + +Changes: + + +** Bug + * [SPRNET-54] - ConfigurationReader does not close resource stream + * [SPRNET-57] - Empty string not allowed as value for name-values collection + * [SPRNET-59] - Remove delay signing and generate stronly named assemblies. + * [SPRNET-62] - Inner IDisposable objects belonging to prototypes were autodestroyed + * [SPRNET-63] - Set default namespace if omitted. + * [SPRNET-68] - Invoke in EventUitls not throwing base exception. + +** New Feature + * [SPRNET-33] - Add support for configuration of existing objects. Refer to the configure methods on IObjectFactory. + * [SPRNET-38] - Create IObjectPostProcessor for environment variable expansion + * [SPRNET-46] - Add factory class to create log4net log object + * [SPRNET-53] - Type converter for Color from RGB CSV + +** Task + * [SPRNET-37] - Update to NAnt 0.85rc1 + * [SPRNET-49] - Documentation for MethodInvokingFactoryObject + * [SPRNET-64] - Upgrade to NUnit 2.2 + * [SPRNET-65] - Upgrade to .NET Mock 0.7.4 + +** Improvement + * [SPRNET-29] - Add support for schema validation and drop DTD + * [SPRNET-51] - argument names for contructors and MessageInvocationFactory + * [SPRNET-61] - Add convenience methods to load Properties + * [SPRNET-66] - Display line numbers in errors and object definition resource descriptor + * [SPRNET-69] - Sync with Spring.Java APIs and refactorings. + * [SPRNET-72] - Release with strongly named assemblies + * Various miscellaneous documentation improvements and code cleanup. + + +------------------------------------------------- +Release 0.6 Release Candidate 2 February 6, 2005. + +Summary: + +This is both a feature enhancement and bug fix release. + +Breaking changes from the first release candidate to be aware of are +changes to the DTD and the use of a new configuration section handler +to create hierarchical contexts. Spring.Context.dll and +Spring.Collections.dll where removed and their classes placed in +Spring.Core.dll. Refer to the example programs and documentation +for more information if you are upgrading from 0.6 RC1. + +Changes: + +* Moved Spring.Collections and Spring.Context dlls/namespaces to Spring.Core dll. +* Introduced threading utilities in Spring.Threading namespace. +* Introduced object pooling utilities in Spring.Pool namespace. +* Introduced object navigation support in Spring.Navigation namespace. +* Introduced loosely coupled eventing in Spring.Objects.Events namespace. +* Introduced pluggable URI based loading of application contexts. Supports file, http, https, config and embedeed assembly resource locations. +* Introduced ContextRegistry as a service locator to retrieve IApplicationContext. +* Introduced custom configuration section handler (ContextHandler) to create IApplicationContexts from custom configuration section. + +* Introduced AppContext and EventRegistry example programs. +* Added GetSingletonCount() method to AbstractObjectFactory, returning the number of objects in the singleton cache. +* Added support for relative resource creation to URL and AssemblyResource. Refactored common code into base class AbstractResource. (SPRNET-23) +* Added support for configuration of 'read-only' collection properties (ISet, IDictionary, IList). +* Added support to create ConfigSectionResource using URI string, i.e. config:// (SPRNET-22) +* Added convenience method to IMessageSource to use CurrentUICulture +* Added generation of User Documenation in HTML Help (.chm) format (SPRNET-4) +* Added support for retrieval of resource objects from IMessageSource + +* Changed IMessageSource to use variable arguments for replacement text values instead of object array. + +* Removed Close() and DestroySingletons() in IConfigurableObjectFactory and IConfigurableApplicationContext and replaced with use of IDisposable interface. (SPRNET-19) +* Removed Spring.Objects.Factory.Access and Spring.Context.Access namespace and contained classes. Use ContextRegistry instead for service locator style access. + +* Removed Spring.Context.Support.ApplicationContextHandler, use ContextHandler instead. +* Removed Spring's IDisposableObject interface and replaced with standard .NET IDisposable. (SPRNET-18) + +* Fixed parsing of CDATA sections (SPRNET-27) +* Fixed use of namespace in custom configuration section of .NET application configuration file. (SPRNET-1) +* Fixed validation of custom configuration section against DTD. (SPRNET-13) +* Fixed solution file to support building when base directory contains spaces (SPRNET-10) +* Fixed System.ArgumentOutOfRangeException in Spring.Util.Properties (SPRNET-26) +* Fixed registration of abstract IObjectPostProcessors and IObjectFactoryPostProcessor + +* Improvements to user documentation. + +DTD/XSD +* Added 'abstract' element to XSD, was accidentally omitted. +* Changed 'class' to 'type' in DTD/XSD +* Changed to in DTD/XSD diff --git a/doc/BreakingChanges-1.1.txt b/doc/BreakingChanges-1.1.txt new file mode 100644 index 00000000..d5887567 --- /dev/null +++ b/doc/BreakingChanges-1.1.txt @@ -0,0 +1,148 @@ +Expression Language +------------------ +1. Changed TypeNode syntax from + +type('qualifiedTypeName') + +to + +T(qualifiedTypeName) + +which is a bit shorter and doesn't require (or allows) type name to be quoted. + +2. Changed ReferenceNode syntax from + +@ctx:obj + +to + +@(ctx:obj) + +in order to allow '@' character to be used for other purposes as well, such as for AttributeNode definitions (new in 1.1) + +3. Changed string literal escape character from \ (backslash) to ' (single quote). + +The only character that needs to be escaped in Spring.Expressions is single quote, and using standard .NET escape character, backslash, to do that lead to all kinds of problems +with literal strings containing standard .NET escape characters or regular expressions escape characters. Now you only need to double up the single quote if it is needed within +the string -- everything else is copied verbatim and resolved by .NET instead. + + +Configuration +------------- + +1. Renamed ResourcesSectionHandler to ResourceHandlersSectionHandler and changed resource handlers configuration section schema from + + + + + +to + + + + + +The new class and element names better reflect the purpose of this configuration section and help to avoid confusion with the context/resources section. + +Changes (RC2 to final) +----------------------------------- + +Spring.Data + +1. Added additional method to IDbProvider to format parameter names when creating IDataParameter. Will affect code only if you created your own IDbProvider implementation. + +Changes (RC2) +----------------------------------- + +Spring.Core + +1. Changed TimeToLive parameter from int to TimeSpan in ICache + +Changes (M2 to RC1) +----------------------------------- + +These changes are driven primarily by the removal of dependency cycles. Some changes were made to have a consistent naming pattern + +Spring.Core + +1. Refactored XmlResourceReader into interface IObjectDefinitionDocumentReader and class DefaultObjectDefinitionDocumentReader +2. IObjectFactory - removed convenience method ConfigureObject(object target) that would simply delgate to ConfigureObject(object target, string name) with the full type name of the target. +3. Moved GetObjectDefinition methods from IListableObjectFactory to IConfigurableListableObjectFactory to remove dependency cycle. +4. Moved IConfigurableObjectDefinition from namespace Factory.Config to Factory.Support to remove dependency cycle (property MethodOverrides) +5. Removed DependencyCheck and MethodOverrides property from IObjectDefinition to remove dependency cycle. +6. Moved ObjectDefinitionHolder from Factory.Support for Factory.Config to removed dependency cycle (ObjectDefinitionVisitor) +7. Moved ObjectFactoryHandler from Spring.Objects.Support to Spring.Objects.Factory.Xml.ObjectFactorySectionHandler +8. Moved PropertyChangeEventArgs from Spring.Objects to Spring.Core +9. Moved exceptions from Spring.Objects to Spring.Core to remove dependency cycle between Spring.Objects and Spring.Expressions + Added ReflectionException and FatalReflectionException to Spring.Util +10. Moved ICriteria implementations from Spring.Objects.Support to Spring.Core to remove several dependency cycles (ControlFlowFactory) +11. Moved ConversionUtils from Spring.Util to Spring.Core to remove dependency cycle. +12. Moved ObjectUtils from Spring.Objects to Spring.Util +13. Moved TypeRegistry and related classes from Spring.Context.Support to Spring.Core.TypeResolution +14. Renamed ConverstionUtils to TypeConversionUtils +15. Split TypeResolver into non-generic (TypeResolver) and generic version GenericTypeResolver +16. Moved Expressions.MethodNode.GetMethodByArgumentValues to ReflectionUtils. +17. Moved ReferenceNode from Spring.Expressions to Spring.Context.Support to remove dependency cycle with Spring.Context. +18. Moved Spring.Objects.Support.CriteriaMemberFilter to Spring.Core to remove dependency of Spring.Objects.Events on Spring.Objects.Support. +19. Moved Spring.Objects.TypeConverters to Spring.Core.TypeConvesion +20. Renamed XmlParserRegistry to NamespaceParserRegistry and IXmlObjectDefinitionParser to INamespaceParser +21. Renamed DefaultXmlObjectDefinitionParser to ObjectsNamespaceParser + Renamed WebObjectDefinitionParser to WebObjectsNamespaceParser + Renamed spring-objects.xsd to spring-objects-1.1.xsd + Renamed Spring.Validation.ValidationConfigParser to Spring.Validation.Config.ValidationNamespaceParser + Renamed spring-validation.xsd to spring-validation-1.1.xsd and moved to Spring.Validation.Config +22. Renamed ConfigurationParserAttribute to NamespaceParserAttribute +23. Renamed ConfigParsersSectionHandler to NamespaceParsersSectionHandler +24. Moved Spring.Util.DynamicReflection to Spring.Reflection.Dynamic + +Spring.Aop + +1. Moved DefaultAopProxyFactory and CachedAopProxyFactory to Aop.Framework.DynamicProxy +2. Removed Spring.Aop.Advice.DebugAdvice +3. Removed Spring.Aop.Advice.CacheAdvice (New Spring.Aspects.Cache.CacheAspect functionality) + + +Spring.Web + +1. Moved HttpContextSwith from Context.Support to Spring.Util. +2. Moved SupportsWebDependencyInjectionMethodBuilder and SupportWebDependencyInjectionTypeBuilder from Spring.Proxy to Spring.Web.Support +3. Moved methods CreatePageInstance, GetControlType, and GetPageType from Spring.Util.WebUtils to new class Spring.Objects.Factory.Support.WebObjectUtils +4. Moved method InjectDependenciesRecursive from Spring.Util.WebUtils to new class Spring.Web.Support.WebDependencyInjectionUtils +5. Moved Spring.Util.ControlInterceptor, IInterceptionStrategy, InterceptControlCollectionOwnerStrategy, InterceptControlCollectionStrategy, + SupportsWebDependencyInjectionOwnerProxy to Spring.Web.Support +6. Moved WebResource from Spring.Web.IO to Spring.Core.IO +7. Moved Spring.Web.Validation to Spring.Web.UI.Validation +8. Moved Spring.Web.Process.AbstractProcess to Spring.Web.Support.AbstractProcessHandler +9. Added SlidingExpiration property to AspNetCache object and removed from BaseCacheAttribute + +Spring.Data + +1. Moved TransactionTemplate, TransactionDelegate and ITransactionCallback from Spring.Data to Spring.Data.Support to remove dependency cycle +2. Moved AdoTemplate, AdoAccessor, AdoDaoSupport, RowMapperResultSetExtractor from Spring.Data to Spring.Data.Core +3. Moved AdoPlatformTransactionManager, ServiceDomainPlatformTransactionManager, and TxScopeTransactionManager from Spring.Data to Spring.Data.Core +4. Moved ErrorCodes from Spring.Data.Support to Spring.Data.Common to remove dependency cycle +5. Moved IDataReaderWrapper from Spring.Data.Support to Spring.Data +6. Changed schema to use 'provider' instead of 'dbProvider' element, usage is now and not +7. Moved namespace parser from Spring.Data to Spring.Data.Config namespace. +8. Renamed from DatabaseConfigParser to DatabaseNamespaceParser +9. Renamed schema spring-database.xsd to spring-database-1.1.xsd +10. Changed target schema from http://www.springframework.net/schema/tx to http://www.springframework.net/tx + +Spring.Services + + +1. Moved Spring.Remoting.RemotingConfigParser to Spring.Remoting.Config.RemotingNamespaceParser + + +Changes (M2 to RC1) +----------------------------------- + +Spring.Aop + +1. Changed DSL for exception handling. + Instead of "on ArithmeticException log 'Logging an exception thrown from method ' + #method.Name" + now use "on exception name ArithmeticException log 'Logging an exception thrown from method ' + #method.Name + + Basically add the words 'exception name' after the word 'on' + + diff --git a/doc/CodeSmith/CodeSmith.BaseTemplates.dll b/doc/CodeSmith/CodeSmith.BaseTemplates.dll new file mode 100644 index 00000000..a7834c41 Binary files /dev/null and b/doc/CodeSmith/CodeSmith.BaseTemplates.dll differ diff --git a/doc/CodeSmith/CodeSmith.CustomProperties.dll b/doc/CodeSmith/CodeSmith.CustomProperties.dll new file mode 100644 index 00000000..0819048b Binary files /dev/null and b/doc/CodeSmith/CodeSmith.CustomProperties.dll differ diff --git a/doc/CodeSmith/CodeSmith.Engine.dll b/doc/CodeSmith/CodeSmith.Engine.dll new file mode 100644 index 00000000..9c7ca869 Binary files /dev/null and b/doc/CodeSmith/CodeSmith.Engine.dll differ diff --git a/doc/CodeSmith/CodeSmith.Engine.xml b/doc/CodeSmith/CodeSmith.Engine.xml new file mode 100644 index 00000000..32badb4c --- /dev/null +++ b/doc/CodeSmith/CodeSmith.Engine.xml @@ -0,0 +1,19 @@ + + + + CodeSmith.Engine + + + + + Required method for Designer support - do not modify + the contents of this method with the code editor. + + + + + Summary description for CodeSmithSerializerAttribute. + + + + diff --git a/doc/CodeSmith/CodeSmithConsole.exe b/doc/CodeSmith/CodeSmithConsole.exe new file mode 100644 index 00000000..597dc02b Binary files /dev/null and b/doc/CodeSmith/CodeSmithConsole.exe differ diff --git a/doc/CodeSmith/CodeSmithConsole.exe.config b/doc/CodeSmith/CodeSmithConsole.exe.config new file mode 100644 index 00000000..7f37a1c0 --- /dev/null +++ b/doc/CodeSmith/CodeSmithConsole.exe.config @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/CodeSmith/CodeSmithResources.dll b/doc/CodeSmith/CodeSmithResources.dll new file mode 100644 index 00000000..fdac375d Binary files /dev/null and b/doc/CodeSmith/CodeSmithResources.dll differ diff --git a/doc/CodeSmith/license.rtf b/doc/CodeSmith/license.rtf new file mode 100644 index 00000000..546fcfc6 --- /dev/null +++ b/doc/CodeSmith/license.rtf @@ -0,0 +1,87 @@ +{\rtf1\ansi\ansicpg1252\uc1\deff0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\f36\fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}MS Shell Dlg 2;}{\f37\froman\fcharset238\fprq2 Times New Roman CE;}{\f38\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f40\froman\fcharset161\fprq2 Times New Roman Greek;} +{\f41\froman\fcharset162\fprq2 Times New Roman Tur;}{\f42\froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f43\froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f44\froman\fcharset186\fprq2 Times New Roman Baltic;} +{\f45\froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f397\fswiss\fcharset238\fprq2 MS Shell Dlg 2 CE;}{\f398\fswiss\fcharset204\fprq2 MS Shell Dlg 2 Cyr;}{\f400\fswiss\fcharset161\fprq2 MS Shell Dlg 2 Greek;} +{\f401\fswiss\fcharset162\fprq2 MS Shell Dlg 2 Tur;}{\f402\fswiss\fcharset177\fprq2 MS Shell Dlg 2 (Hebrew);}{\f403\fswiss\fcharset178\fprq2 MS Shell Dlg 2 (Arabic);}{\f404\fswiss\fcharset186\fprq2 MS Shell Dlg 2 Baltic;} +{\f405\fswiss\fcharset163\fprq2 MS Shell Dlg 2 (Vietnamese);}{\f406\fswiss\fcharset222\fprq2 MS Shell Dlg 2 (Thai);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255; +\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;} +{\stylesheet{\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 Normal;}{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \snext11 \ssemihidden Normal Table;}}{\*\latentstyles\lsdstimax156\lsdlockeddef0}{\*\rsidtbl \rsid1075826\rsid15156319} +{\*\generator Microsoft Word 11.0.5604;}{\info{\author Eric J. Smith}{\operator Eric J. Smith}{\creatim\yr2004\mo2\dy1\hr12\min44}{\revtim\yr2004\mo2\dy1\hr12\min47}{\version2}{\edmins3}{\nofpages2}{\nofwords1135}{\nofchars6474}{\*\company ActiSolve} +{\nofcharsws7594}{\vern24689}}\widowctrl\ftnbj\aenddoc\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\hyphcaps0\horzdoc\dghspace120\dgvspace120\dghorigin1701\dgvorigin1984\dghshow0\dgvshow3 +\jcompress\viewkind4\viewscale100\nolnhtadjtbl\rsidroot1075826 \fet0\sectd \linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3 +\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}} +{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain +\ql \li0\ri0\nowidctlpar\faauto\rin0\lin0\itap0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f36\fs17\insrsid1075826 CODESMITH LICENSE AGREEMENT +\par +\par IMPORTANT--READ CAREFULLY: This End-User License Agreement ("EULA") is a legal agreement between you (either an individual or an entity) and Eric J. Smith ("SMITH") for the CodeSmith template based code generator +software product and any included components or materials ("SOFTWARE PRODUCT"). BY INSTALLING, COPYING, OR OTHERWISE USING THE SOFTWARE PRODUCT, YOU AGREE TO BE BOUND BY THE TERMS OF THIS EULA. IF YOU DO NOT AGREE TO THE TERMS OF THIS EULA, YOU ARE NOT +AUTHORIZED TO INSTALL, COPY, OR OTHERWISE USE THE SOFTWARE PRODUCT. +\par +\par SOFTWARE PRODUCT LICENSE +\par +\par The SOFTWARE PRODUCT is protected by United States copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The SOFTWARE PRODUCT is licensed, not sold. +\par +\par 1. GRANT OF LICENSE. This EULA grants you the following rights: +\par }{\f36\fs17\insrsid1075826\charrsid1075826 a. }{\f36\fs17\insrsid1075826 Software Product}{\f36\fs17\insrsid1075826\charrsid1075826 . You may install and }{\f36\fs17\insrsid1075826 use the SOFTWARE PRODUCT}{\f36\fs17\insrsid1075826\charrsid1075826 +on a single computer. The primary user of the computer on which the }{\f36\fs17\insrsid1075826 SOFTWARE PRODUCT}{\f36\fs17\insrsid1075826\charrsid1075826 is installed may make a second copy for his or her exclusive use on a portable computer.}{ +\f36\fs17\insrsid1075826 +\par }{\f36\fs17\insrsid1075826 b. Use of Generated Output. You may distribute the output of your custom templates or the included templates in any way. +\par +\par 2. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS. +\par a. Limitations on Reverse Engineering, Decompilation, and Disassembly. You may not reverse engineer, decompile, or disassemble the SOFTWARE PRODUCT, except and onl +y to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation. +\par b. Separation of Components. The SOFTWARE PRODUCT is licensed as a single product. Its component parts may not be separated for use on more than one computer. +\par c. Redistribution. The SOFTWARE PRODUCT may not be redistributed in any way. +\par d. Custom Template Distribution. You may distribute your custom templates for the SOFTWARE PRODUCT only if they are offered free of charge. +\par e. No Rental. You may not rent, lease, lend or provide commercial hosting services to third parties with the SOFTWARE PRODUCT. +\par f. Termination. Without prejudice to any other rights, SMITH may terminate this EULA if you fail to comply with the terms and conditions of this EULA. In such event, you must destroy all copies of the SOFTWARE PRODUCT and all of its component parts. + +\par +\par 3. ADDITIONAL SOFTWARE/SERVICES. +\par a. Support Services. SMITH may, but is not obligated to, provide you with support services related to the SOFTWARE PRODUCT. +\par b. Supplements. This EULA applies to additional software and updates of the SOFTWARE PRODUCT, including without limitation supplements, service packages, hot fixes, or add-on components (collectively "Supplements") that SMITH may +provide to you or make available to you after the date you obtain your initial copy of the SOFTWARE PRODUCT, unless other terms are provided along with such Supplements. +\par +\par 4. COPYRIGHT. All title and copyrights in and to the SOFTWARE PRODUCT (including but +not limited to any images, photographs, animations, video, audio, music, text, SAMPLE CODE, and "applets" incorporated into the SOFTWARE PRODUCT) and any copies of the SOFTWARE PRODUCT are owned by SMITH. The SOFTWARE PRODUCT is protected by copyright law +s and international treaty provisions. Therefore, you must treat the SOFTWARE PRODUCT like any other copyrighted material except that you may install the SOFTWARE PRODUCT. +\par +\par 5. U.S. GOVERNMENT RESTRICTED RIGHTS. All SOFTWARE PRODUCT provided to the U.S. Gove +rnment pursuant to solicitations issued on or after December 1, 1995 is provided with the commercial license rights and restrictions described elsewhere herein. All SOFTWARE PRODUCT provided to the U.S. Government pursuant to solicitations issued prior to + December 1, 1995 is provided with "Restricted Rights" as provided for in FAR, 48 CFR 52.227-14 (JUNE 1987) or DFAR,48 CFR 252.227-7013 (OCT 1988), as applicable. +\par +\par 6. EXPORT RESTRICTIONS. You acknowledge that the SOFTWARE PRODUCT is subject to U.S. export j +urisdiction. You agree to comply with all applicable international and national laws that apply to these products, including the U.S. Export Administration Regulations, as well as end-user, end-use and destination restrictions issued by U.S. and other gov +ernments. +\par +\par 7. DISCLAIMER OF WARRANTY +\par To the maximum extent permitted by applicable law, SMITH provides the SOFTWARE PRODUCT and support services (if any) AS IS AND WITH ALL FAULTS, and hereby disclaim all other warranties and conditions, whether express, im +plied, or statutory, including, but not limited to, any (if any) implied warranties, duties or conditions of merchantability, of fitness for a particular purpose, of reliability or availability, of accuracy or completeness of responses, of results, of wor +k +manlike effort, of lack of viruses, and of lack of negligence, all with regard to the SOFTWARE PRODUCT, and the provision of or failure to provide support or other services, information, software, and related content through the SOFTWARE PRODUCT or otherw +ise arising out of the use of the SOFTWARE PRODUCT. ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION, OR NON-INFRINGEMENT WITH REGARD TO THE SOFTWARE PRODUCT. +\par +\par 8. EXCLUSION OF INCIDENTAL, CONSEQUENTIAL, AND CERTAIN OTHER DAMAGES. +\par TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL SMITH BE LIABLE FOR ANY SPECIAL, INCIDENTAL, PUNITIVE, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, BUT NOT LIMITED TO, DAMAGES FOR L +OSS OF PROFITS OR CONFIDENTIAL OR OTHER INFORMATION, FOR BUSINESS INTERRUPTION, FOR PERSONAL INJURY, FOR LOSS OF PRIVACY, FOR FAILURE TO MEET ANY DUTY INCLUDING OF GOOD FAITH OR OF REASONABLE CARE, FOR NEGLIGENCE, AND FOR ANY OTHER PECUNIARY OR OTHER LOSS + +WHATSOEVER) ARISING OUT OF OR IN ANY WAY RELATED TO THE USE OF OR INABILITY TO USE THE SOFTWARE PRODUCT, THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT OR OTHER SERVICES, INFORMATION, SOFTWARE, AND RELATED CONTENT THROUGH THE SOFTWARE PRODUCT OR OTHERWISE + +ARISING OUT OF THE USE OF THE SOFTWARE PRODUCT, OR OTHERWISE UNDER OR IN CONNECTION WITH ANY PROVISION OF THIS EULA, EVEN IN THE EVENT OF THE FAULT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY, BREACH OF CONTRACT, OR BREACH OF WARRANTY OF SMITH, AND EVE +N IF SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +\par +\par 9. LIMITATION OF LIABILITY AND REMEDIES. NOTWITHSTANDING ANY DAMAGES THAT YOU MIGHT INCUR FOR ANY REASON WHATSOEVER (INCLUDING, WITHOUT LIMITATION, ALL DAMAGES REFERENCED ABOVE AND ALL DIRECT +OR GENERAL DAMAGES), THE ENTIRE LIABILITY OF SMITH UNDER ANY PROVISION OF THIS EULA AND YOUR EXCLUSIVE REMEDY FOR ALL OF THE FOREGOING SHALL BE LIMITED TO THE GREATER OF THE AMOUNT ACTUALLY PAID BY YOU FOR THE SOFTWARE PRODUCT OR U.S.$5.00. THE FOREGOING + LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SHALL APPLY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS ITS ESSENTIAL PURPOSE. +\par +\par 10. APPLICABLE LAW. This EULA is governed by the laws of the State of Texas. +\par +\par 11. ENTIRE AGREEMENT. This + EULA (including any addendum or amendment to this EULA which is included with the SOFTWARE PRODUCT) is the entire agreement between you and SMITH relating to the SOFTWARE PRODUCT and support services (if any), and it supersedes all prior or contemporaneo +u +s oral or written communications, proposals, and representations with respect to the SOFTWARE PRODUCT or any other subject matter covered by this EULA. To the extent the terms of any SMITH policies or programs for support services conflict with the terms +of this EULA, the terms of this EULA shall control. +\par +\par }} \ No newline at end of file diff --git a/doc/CodeSmith/readme.txt b/doc/CodeSmith/readme.txt new file mode 100644 index 00000000..96a11469 --- /dev/null +++ b/doc/CodeSmith/readme.txt @@ -0,0 +1,335 @@ +CodeSmith + +CodeSmith allows you to create templates that will generate code for any ASCII based language. The code generated can be customized by the use of properties. A property can be any .NET object that has a designer (most built in .NET types have designers already) and can be as simple as a boolean property that allows you to conditionally add or remove code from the result, to an object such as the included TableSchema object which provides everything you could possibly want to know about a table in a database. Having access to this information allows you to generate things such as stored procedures, business objects, presentation layer code, or anything else you can think of based on a table schema. + +CodeSmith's syntax is almost identical to ASP.NET. So if you are familiar with ASP.NET then you should be able to quickly learn the template syntax. You can use the C#, VB.NET or JScript.NET languages in your templates. + +The CodeSmith Explorer window allows you to view all templates in a given folder and allows you to drag and drop to any target that supports dropping text. You can also double-click a template to execute it individually after the first time you run CodeSmith. + +I hope to receive feedback, bug reports and hopefully some useful templates to include in future versions. I am also interested if someone would be willing to help with documentation. CodeSmith is a FREEWARE product. I am still considering releasing source code, but I would rather maintain control of the product in a single location. I think this will provide the best opportunity to build a solid community of users and templates. I also plan to build a template repository site that will allow anyone to submit a template and have it added to the repository. + +Enjoy, +Eric J. Smith +http://www.ericjsmith.net/codesmith/ + +-------------------------------------------------- +History +-------------------------------------------------- +Build 2.6.0 (RC1) + * Added GetCodeTemplateInstance method to CodeTemplate. This can be used to compile and create an instance of another template. + * Added Toggle Template Code Expansion feature. This allows easy viewing of the static content in the template. (Shortcut CTRL-SHIFT-M) + * Fixed up the syntax highlighting editor dialog and made it persist the settings. + * Made it so that you can manually enter a delimited list of strings for StringCollection in the property grid. + * Added several options to the options dialog. + * Improved outlining. + * Fixed various minor bugs. +Build 2.5.18 (BETA) + * More performance improvements in the core CodeSmith engine. + * Made it so that if you reselect the same schema object it will refresh the schema information. + * Made the enter key open the selected template in CodeSmith Studio. + * Fixed bug with saving a property set file while overwriting an existing property set file that is set to read only. + * Made CodeSmith Studio a single instance application. + * Fixed bug where trying to open a file that was already open would cause the file to re-compile itself. + * Fixed bug when compiling templates that have ( ) or ' in their file names. +Build 2.5.17 (BETA) + * Fixed issue with CodeSmith Studio hanging sometimes when <% was typed. + * Made the VS.NET custom tool MUCH better. It now reports errors in much more detail. + * Added variable support to the VS.NET custom tool. + * Added default property support to the VS.NET custom tool. + * Made it so that goto line now expands the regions the line is on. +Build 2.5.16 (BETA): + * Made various performance improvements to the engine and CodeSmith Studio. + * Added syntax highlighting to target languages. + * Added outlining support to CodeSmith Studio. + * Added line modification markers to CodeSmith Studio. + * Added auto copy output to clipboard option in CodeSmith Studio. + * Fixed several find and replace bugs. +Build 2.5.15 (BETA): + * Added ToString override to the schema collections so that the names of the selected objects show up. + * Stopped indenting script blocks. + * Fixed highlighting issues with escaped template tags <%% %%>. + * Made register menu item hidden when already registered. + * Fixed bug where pressing F4 on codebehind causes exception. + * Changed RenderToFile using a merge strategy so that it creates a file if it doesn't exist. + * Changed the output encoding to UTF-8. + * Fixed bug in editor control where a black box was sometimes drawn. + * Fixed bug in editor control where a clipboard operation would sometimes cause an exception. + * Updated to the 1.5 version of Chris Nahr's collection templates. + * Various other minor bug fixes. +Build 2.5.14 (Final): + * Turned CodeSmith Professional licensing on. +Build 2.5.13 (RC4): + * Fixed the check to see if a given file is already open (was case-sensative). + * Fixed issue with setting properties programmatically if they were not an exact type match but were still related types. + * Disabled the replace and replace all buttons on the find dialog if a document is read only. + * Fixed the StoredProcedures.cst template to handle user defined types. + * Made it so that the explorer tree doesn't do a complete refresh on every file save. + * Fixed painting issues in the Highlighting Style Editor dialog. + * Fixed issue with the find function not always moving the find result into view. + * Fixed issue with CTRL-TAB and new documents. + * Fixed template parser to allow escaped "'s in the directive attributes. +Build 2.5.12 (RC3): + * Fixed bug in SqlSchemaProvider where tables with .'s in their name would cause an error. + * Changed SchemaExplorer to lazy load extended properties. This made a huge difference in databases with a lot of schema objects. + * Fixed bug with RenderToFile where the file handle was not being released if an error occured during template execution. + * Added the awesome DBDocumenter templates to the samples. + * Added the C# CSLA.NET templates by Ricky Supit. + * Added the StoredProcedureDescriptions.cst template by Oskar Austegard. + * Fixed bug with save all button where not all documents would be saved. + * Added context menu to the output and compiled source editors. + * Fixed bug in the logo header of CodeSmithConsole. + * Fixed bug with determining if a file has been modified in CodeSmith Studio. + * Fixed various issues with the goto line feature in CodeSmith Studio. + * Fixed formatting issue with template comment tags. + * Fixed bugs with commands enabling and disabling in CodeSmith Studio. + * Changed the F6 mapping in Studio to toggle between views of the current document. + * Changed build shortcut to CTRL-SHIFT-B. + * Fixed bug with external change modification notice. Whenever you closed a document and re-opened it you would then get errant external change modification notices. + * Fixed bug with CTRL-F sometimes causing a crash. + * Fixed various painting issues in the editor control. + * Added option to determine code behind open behaviour in CodeSmith Studio. + * Fixed issue with various menu item actions not updating the document title. + * Changed CTRL-TAB and CTRL-SHIFT-TAB to behave the same as VS.NET. + * Added ability to select template editor application from CodeSmith Explorer. + * Made it so the template will recompile if the code behind file has been modified. +Build 2.5.11 (RC2): + * Fixed parser bug where whitespace would not be correctly handled in some scenerios. + * Fixed bug with custom assembly resolution. + * Fixed bug when closing multiple instances of Studio at the same time. + * Fixed bugs in a few sample templates. + * Fixed bug when using a \ in the find dialog. +Build 2.5.10 (RC1): + * Added some new help content. Thanks to James Avery. + * Updated to the latest collection and CSLA.NET templates. + * Added State (values: Default, Rendering, Validating, RestoringProperties, SavingProperties) property to CodeTemplate. This can be used to tell what the template is currently doing. + * Fixed bug when saving a newly created template. + * Made the close start page on open setting work for all ways of opening files. + * Fixed bug in collections where indexers threw an exception for items that did not exist. These indexers now return null if the item is not found. + * Added override for ToString() in CodeSmith.CustomProperties.StringCollection so that the items show up in the propertygrid instead of the type name. + * Fixed bug in RestorePropertiesFromHashtable where you get a NullReferenceException when trying to populate a property that has been removed since the last compile. + * Fixed bug in the about box where some names were being cut off. + * Fixed bug with Load Property Set XML in the stand-alone property grid. + * Fixed bug in CodeSmith Explorer with template folders that no longer exist. + * Fixed bug in CodeSmith Studio with opening files that no longer exist. +Build 2.5.9 (Beta): + * Added context menus to the document tabs. + * Added CopyPropertiesTo method to CodeTemplate. This can be used to copy all matching properties from one template instance to another. + * Dramatically improved compiler performance on large templates. + * Lots of improvements to the CodeSmith Explorer control. + * Rebuilt all of the Schema Explorer collections using the awesome collection templates by Chris Nahr. These collections are now editable, although the instances returned by Schema Explorer are marked as read-only. + * Fixed bug where enum values were not being maintained during template compilation. + * Fixed bug where new files were not added to the MRU list. + * All configuration files have now been moved to the current user's ApplicationData folder. It should now be possible to run CodeSmith as a non-Administrator user. + * Added folder monitoring to Explorer so that new files are automatically picked up. + * Added monitoring to all documents in CodeSmith Studio so that external changes are picked up. +Build 2.2.8 (Beta): + * Added tool tip to document tabs in CodeSmith Studio. + * Added keyboard shortcuts to almost everything in CodeSmith Studio. + * Added ability to open any file type in CodeSmith Studio. + * Added error underlines to the compiled template source when there are compilation errors. These also have tooltips to display the error message. + * Added Select All, Copy Output, Save Output, and Compile To Assembly menu items. + * Added Insert Content menu items and shortcuts. + * Added dialog to ask if you want to open the code behind for a template if it uses one. (this really should be another tab in the template editor.) + * Added F3 support and made various fixes to the find and replace operations. + * Added menu item to save output to file. + * Added Windows menu to CodeSmith Studio. + * Added recent files menu to CodeSmith Studio. + * Added context sensative enabling/disabling of commands in CodeSmith Studio. + * Added options dialog with various settings. + * Fixed parser bug where comments (<%-- --%>) would collapse a line with other content on it. + * Added ability to change language background color in the highlighting style editor dialog. + * Added start page / embedded web browser to CodeSmith Studio. +Build 2.2.7 (Beta): + * The Schema Explorer tool window in CodeSmith Studio works now (still need to have it let you manage your extended properties). + * The properties grid is now used to show properties on just about everything. + * Fixed bug when dropping a template onto the VS.NET Solution Explorer window. + * Updated to the latest version of the CSLA.NET templates. + * Created a new sample template CommandWrapperClass.cst. This template creates a C# wrapper class for a stored procedure. + * There is now a Description property on all schema objects. This is simply a shortcut to the CS_Description extended property. + * Made the find dialog set the currently selected text as the find value instead of it being hard-coded to "int". + * Made the find dialog restore focus to the editor window when the dialog is closed. + * Fixed bug in Studio where the same instance of a template was being used for multiple generations. + * Added CS_IsComputed and CS_IsDeterministic extended properties to ColumnSchema and ViewColumnSchema. + * Added CS_Default extended property to ParameterSchema. + * Added CTRL-TAB and CTRL-SHIFT-TAB support to CodeSmith Studio. + * Fixed printing issue in CodeSmith Studio where lines were not being wrapped. + * Fixed bug in parser that caused <% = expression %> (space between <% and =) to be incorrectly parsed. + * Fixed bug in parser that caused multi-line template comments to be incorrectly parsed. + * Updated to version 1.3.1 of Chris Nahr's collection templates. + * Created CodeSmith101 sample templates. +Build 2.2.6 (Beta): + * Implemented cursor changes in CodeSmith Studio when the application is working. + * Fixed bug in extended properties where extended property value was NULL. + * Fixed several clipboard issues in CodeSmith Studio. + * Added error trapping around template execution so that it's obvious the exception was from bad code in the template and not CodeSmith. + * Updated to version 1.3.0 of Chris Nahr's collection templates. + * Fixed bug in CodeSmith Studio where template properties would be lost after a failed compilation. + * Added code to allow enum property types defined in multiple templates to be converted back and forth. +Build 2.2.5 (Beta): + * Added icons to the Visual Studio .NET tool window and command. + * Added a blank data source to all designers. + * Added context menu items to the property grid to copy, save and load the property set XML. + * Fixed bug with right-clicking in the property grid on a category cell. + * Made a lot of internal changes to cleanup the way CodeSmith was searching for assemblies. + * Replaced VSUserControlHost with CodeSmithUserControlHost. I believe this change will fix the infamous "Invalid VSUserControlHost" error message in Visual Studio. + * Added a ScriptTableData.cst sample template. + * Made CodeSmith Studio persist highlighting style changes. + * Added Schema Explorer tool window to CodeSmith Studio. + * Fixed highlighting bug where single line comments would over-ride the end of a template block (%>). + * Added highlighting support for template comments (<%-- %>). + * Implemented alternate method of retrieving command schema result information in certain scenerios where it would fail otherwise. + * Fixed bug with SQL7 and views. + * Added option to installer to select whether or not VS.NET support is installed. + * Made it so that Undo buffer is cleared right after document load. +Build 2.2.4 (Beta): + * Added GetProperties and GetRequiredProperties to CodeTemplate. + * Added AllInputParameters, AllOutputParameters, and NonReturnValueParameters to the CommandSchema object. + * Fixed various exceptions in CodeSmith Studio. + * Added EngineSample (this was previously ConsoleSample). + * Added ConsoleSamples. This contains various samples of using the command line client. + * Added option to installer to include Visual Studio .NET 2003 support. + * Made CodeSmith Studio handle saving to read-only files. + * Fixed issue with command parameters extended properties. + * Added new sample template that outputs all extended properties for a database. +Build 2.2.3 (Beta): + * Fixed parser bug where first literal line of template would be parsed incorrectly and discarded. + * Fixed parser bug where + + + + Spring.NET miscellanea + + Introduction + + This chapter contains miscellanea information on features, goodies, caveats + that does not belong to any paricular area. + + + + PathMatcher + + Note, Spring.Util.PathMatcher is + currently only available in CVS, not the RC3 release. If you want to use these feature + please get the code from CVS + (instructions) + or from the download section of the + Spring.NET website that contains an .zip with the full CVS tree. + + + + Spring.Util.PathMatcher provides Ant/NAnt-like path name matching + features. + + To do the match, you use the method: + <%= + XmlPeek.HtmlEncode ( + XmlPeek.ExtractAndQueryXPath( + "src/Spring/Spring.Core/Util/PathMatcher.cs", + "fragments/fragment[@name='match-method']")) + %> + + If you want to decide if case is important or not use the method: + <%= + XmlPeek.HtmlEncode ( + XmlPeek.ExtractAndQueryXPath( + "src/Spring/Spring.Core/Util/PathMatcher.cs", + "fragments/fragment[@name='match-method-nocase']")) + %> + + + General rules + + To build your pattern, you use the *, ? + and ** building blocks: + + + *: matches any number of non slash + characters; + + + + ?: matches exactly 1 (one) non slash/dot + character; + + + + **: matches any subdirectory, without + taking care of the depth; + + + + + + + + Matching filenames + + A file name can be matched using the following + notation: + <%= Example("filename", "pattern") %> + matches: + <%= Example("filename", "match") %> + does not match: + <%= Example("filename", "dont.match") %> + + + The classical all files pattern: + <%= Example("filename-all", "pattern") %> + matches: + <%= Example("filename-all", "match") %> + does not match: + <%= Example("filename-all", "dont.match") %> + + + + + Matching subdirectories + + A directory name can be matched at any depth level using the following + notation: + <%= Example("subdir", "pattern") %> + That pattern matches the following paths: + <%= Example("subdir", "match") %> + but does not match these: + <%= Example("subdir", "dont.match") %> + + + You can compose subdirectories to match like this: + <%= Example("double-subdir", "pattern") %> + That pattern matches the following paths: + <%= Example("double-subdir", "match") %> + but does not match these: + <%= Example("double-subdir", "dont.match") %> + + + You can use more advanced patterns: + <%= Example("subdir-2", "pattern") %> + matches: + <%= Example("subdir-2", "match") %> + does not match: + <%= Example("subdir-2", "dont.match") %> + + + + + Case does matter, slashes don't + + .NET is expected to be a cross-platform development ... platform. So, + PathMatcher will match taking care of the case of the pattern + and the case of the path. For example: + <%= Example("case-sensitive", "pattern") %> + matches: + <%= Example("case-sensitive", "match") %> + but does not match: + <%= Example("case-sensitive", "dont.match") %> + + If you do not matter about case, you should explicitly tell the + Pathmatcher. + + Back and forward slashes, in the very same cross-platform spirit, are + not important: + <%= Example("slashes", "pattern") %> + matches all the following paths: + <%= Example("slashes", "match") %> + + + + + + diff --git a/doc/reference/src/templated/pooling-example.xml b/doc/reference/src/templated/pooling-example.xml new file mode 100644 index 00000000..4a114b43 --- /dev/null +++ b/doc/reference/src/templated/pooling-example.xml @@ -0,0 +1,158 @@ +<%@ CodeTemplate %> +<%@ Import Namespace="System.IO" %> +<%@ Assembly Name="AgileDocs.Core" %> +<%@ Import Namespace="AgileDocs.Core" %> + + + + + Pooling example + + The idea is to build an executor backed by a pool of + QueuedExecutor: this will show how Spring.NET + provides some useful low-level/high-quality reusable threading and + pooling abstractions. + This executor will provide parallel executions (in our case + grep-like file scans). Note: This example + is not in the 1.0.0 release to its use of classes in the Spring.Threading + namespace scheduled for release in Spring 1.1. To access ths example + please get the code from CVS (instructions) or from the download section of the + Spring.NET website that contains an .zip with the full CVS tree. + + + Some information on QueuedExecutor is helpful to + better understand the implementation and to possibly disagree with it. + Keep in mind that the point is to show how to develop your own + object-pool. + + + A QueuedExecutor is an executor where + IRunnable instances are run serialy by a worker + thread. When you Execute with a + QueuedExecutor, your request is queued; at some + point in the future your request will be taken and executed by the + worker thread: in case of error the thread is terminated. + However + this executor recreates its worker thread as needed. + + Last but not least, this executor can be shut down in + a few different ways (please refer to the Spring.NET SDK documentation). + Given its simplicity, it is very powerful. + + + The example project Spring.Examples.Pool provides + an implementation of a pooled executor, backed by n instances of + Spring.Threading.QueuedExecutor: please ignore + the fact that Spring.Threading includes already a + very different implementation of a PooledExecutor: + here we wanto to use a pool of QueuedExecutors. + + + This executor will be used to implement a parallel + recursive grep-like console executable. + + + Implementing <literal>Spring.Pool.IPoolableObjectFactory</literal> + + In order to use the SimplePool implementation, + the first thing to do is to implement the IPoolableObjectFactory + interface. This interface is intended to be implemented by objects + that can create the type of objects that should be pooled. + The SimplePool + will call the lifecycle methods on IPoolableObjectFactory interface + (MakeObject, ActivateObject, ValidateObject, PassivateObject, and DestroyObject) + as appropriate when the pool is created, objects are borrowed and returned to the pool, and when + the pool is destroyed. + + + In our case, as already said, we want to to implement a pool + of QueuedExecutor. Ok, here the declaration: + <%= PoolExample("PooledQueuedExecutor.cs", "factory-declaration") %> + the first task a factory should do is to create objects: + <%= PoolExample("PooledQueuedExecutor.cs", "make") %> + and should be also able to destroy them: + <%= PoolExample("PooledQueuedExecutor.cs", "destroy") %> + + + When an object is taken from the pool, to satisfy a client request, + may be the object should be activated. We can possibly implement the + activation like this: + <%= PoolExample("PooledQueuedExecutor.cs", "activate") %> + even if a QueuedExecutor restarts itself as + needed and so a valid implementation could leave this method empty. + + + After activation, and before the pooled object can be succesfully + returned to the client, it is validated (should the object be + invalid, it will be discarded: this can lead to an empty unusable + pool + + You may think that we can provide a smarter + implementation and you are probably right. However, it is not so + difficult to create a new pool in case the old one became unusable. + It could not be your preferred choice but surely it leverages + simplicity and object immutability + + ). + Here we check that the worker thread exists: + <%= PoolExample("PooledQueuedExecutor.cs", "validate") %> + + + Passivation, symmetrical to activation, is the process a pooled + object is subject to when the object is returned to the pool. In our + case we simply do nothing: + <%= PoolExample("PooledQueuedExecutor.cs", "passivate") %> + + + At this point, creating a pool is simply a matter of creating an + SimplePool as in: + <%= PoolExample("PooledQueuedExecutor.cs", "create-pool") %> + + + + Being smart using pooled objects + + Taking advantage of the using keyword seems + to be very important in these c# days, so we + implement a very simple helper (PooledObjectHolder) + that can allow us to do things like: + <%= PoolExample("PooledQueuedExecutor.cs", "execute") %> + without worrying about obtaining and returning an object from/to the + pool. + + + Here is the implementation: + <%= PoolExample("PooledQueuedExecutor.cs", "holder") %> + + + Please don't forget to destroy all the pooled istances once you have + finished! How? Well using something like this in + PooledQueuedExecutor: + <%= PoolExample("PooledQueuedExecutor.cs", "stop") %> + + + + Using the executor to do a parallel <literal>grep</literal> + + The use of the just built executor is quite straigtforward but a + little tricky if we want to really exploit the pool. + <%= PoolExample("Grep.cs", "parallel-grep-class") %> + + + <%= PoolExample("Grep.cs", "parallel-grep-main") %> + + + + diff --git a/doc/reference/src/templated/properties.props b/doc/reference/src/templated/properties.props new file mode 100644 index 00000000..41071e75 --- /dev/null +++ b/doc/reference/src/templated/properties.props @@ -0,0 +1,37 @@ + + + + + + \ No newline at end of file diff --git a/doc/reference/src/templated/windows-service.xml b/doc/reference/src/templated/windows-service.xml new file mode 100644 index 00000000..6830dd73 --- /dev/null +++ b/doc/reference/src/templated/windows-service.xml @@ -0,0 +1,441 @@ +<%@ CodeTemplate %> +<%@ Assembly Name="System.Web" %> +<%@ Import Namespace="System.IO" %> +<%@ Import Namespace="System.Xml" %> +<%@ Import Namespace="System.Web" %> +<%@ Import Namespace="System.Text" %> + +<%@ Assembly Name="AgileDocs.Core" %> +<%@ Import Namespace="AgileDocs.Core" %> + + + + + + + Windows Services + + + Remarks + + This is functionality that will be included after the + 1.0 release. If you want to use these features please get the + code from CVS + (instructions) or from the download section of the Spring.NET website that contains an + .zip with the full CVS tree. + In addition to this documentation + you can refer to the example program located at + examples\Spring\Spring.Examples.WindowsService + to better understand the package. Please check the Spring.NET + website + for the latest updates to this document. + + + + Introduction + + Developers usually create Windows Services using the + Visual Studio .NET wizard. While not difficult to do, this + procedure is repetative and does not encourage separation between + infrastructure code (windows service) and application code. This is + generally considered a "bad thing" but you can certainly disagree. + + + As Spring.NET can provide an explicitly managed + initialize/destroy lifecycle for singleton objects, there is + a natural synergy with the lifecycle of a Windows service. + As such, it could be very convenient to expose a Spring application + context as a Windows service. Starting and stopping the service corresponds + to creating and destroying an application context and its + contained objects. This approach provides a high level means to + declare what objects are created and destroyed when developing + a Windows service. + + + To do that, Spring.NET requires the installation of one physical + service able to run as services as many applications as you want - each a + logical independent service in their own application domain. + By default, the deployment and updating of the service can also + be done by copying the relevant executables to a special directory. + + + The executable that at present provides these features is the + Spring.Services.WindowsService.Process.exe + assembly. It makes heavy use of classes and interfaces definde in + the Spring.Services.WindowsService.Common.dll + assembly. You should reference the common assembly it if you want to + follow the advice on customization contained in the following sections + + + The benefits of this approach, a part from those given by separating + infrastructure code and application code (a field where Spring.NET + tries hard to succeed) is that you can think about installing a new + service at client site by simply dropping a new application assembly + in a remote directory. + + + + + The <literal>Spring.Services.WindowsService.Process.exe</literal> application + + Installing + + The installation can be done in two ways, using the .NET SDK + installutil.exe tool or using the more mundane + Spring.Services.WindowsService.Installer.exe; + while the former is the standard, the latter is probably + more flexible. It allows you to customize the name/display name of the + service and has the ability to install multiple times the same assembly + with different names. This can be useful in a + number of scenarios, especially where you don't like, for some + reasons, to run several different logical services under the + same physical windows service. + + + Be aware of the fact that the service will be installed as + running with the system account (installing with a specific + user account seems a bit buggy on Windows XP) + + + That said, while installutil + + is documented on its own , + the command line for + Spring.Services.WindowsService.Installer.exe + is as follow: + Spring.Services.WindowsService.Installer.exe + +usage: + install service-exe-path service-display-name service-name + uninstall service-name [i|u] service-exe-path service-display-name service-name + for example, to install, you can invoke it with the following: + ... install Spring.Services.WindowsService.Process.exe "Spring.Service Support" spring-service + and to uninstall it: + ... uninstall spring-service + + + + Configuration + + The standard .NET .config file + can be used to tune some parameters of + Spring.Services.WindowsService.Process.exe, + (including log4net settings, for which it is recomended to consult + the log4net documentation). + + + This file also define the context run by this process; here the file in its current beauty: + <%= XmlExample("src/Spring/Spring.Services/App.config", "code") %> + + + As you see, the context is defined in another file: let's review the objects it defines. + + + Firstly, it is worth notice that in order to 'localize' the service (i.e. to know where it is installed to use that directory as + base for the deploy dir as in the above file) you should define an object like this: the name is not + very important, it is important that it is an IObjectFactoryPostProcessor and so will be + automatically applied to this application context: + <%= XmlExample("src/Spring/Spring.Services/WindowsService/Process/service-process-definition.xml", "code/localizer") %> + + + In that object definition you can customize the prefix for the following string + <%= CsExample("src/Spring/Spring.Services/WindowsService/Common/Localizer.cs", "code/process.format") %> + but you usually won't need it; the default value is + <%= CsExample("src/Spring/Spring.Services/WindowsService/Common/Localizer.cs", "code/localizer.def.prefix") %> + + + The sole important object defined by this context, i.e. the main object run by the service. + The thing you can (and should) configure is the path to the folder you will use as the deploy location; + the current definition, to avoid the need for a fully qualified path (e.g.: c:\spring\services) uses + the properties made available by the localizer above: + <%= XmlExample("src/Spring/Spring.Services/WindowsService/Process/service-process-definition.xml", "code/service") %> + + + The above object is then easily remoted using spring remoting utilities (please notice you should tune the remoting configuration + listed in the standard .NET .config file, listed above): + <%= XmlExample("src/Spring/Spring.Services/WindowsService/Process/service-process-definition.xml", "code/remoted.service") %> + + + + + + Running an application context as a windows service + + If you package an application using the layout and + conventions described here, you'll be able to run an + application context as a Windows Service. + The conventions used are modeled after those used by ASP.NET + and are very easy to follow. + + + As already said, you'll have a Spring.NET application context running in + a dedicated AppDomain hosted in a process running + as a windows service: that process is able to run many application contexts + simultaneously. + + A complete application runable as service consists of a + directory containing: + + + + The .NET configuration file + service.config: + this file should define your application context. + Moreover this files will be used + by the CLR to configure the application domain + your application will run in, exactly as you expect. + This file has the same role of ASP.NET Web.config + file. + + + + + Optional: an xml context file (watcher.xml) + defining the watcher for your application. + The watcher controls the automatic redeployment of the + service and is discussed more in the following section. + + + + Recomended: along the lines of ASP.NET convention, a bin + subdirectory containing all + the assemblies your application needs; you can of course put + your assemblies in the same directory where you put + service.config but this is not encouraged ... + + + + + + <literal>service.config</literal> + + This is the standard .NET configuration file for the + AppDomain that will host your application. It is + semantically equivalent to the ASP.NET Web.config + file. + + + log4net users please notice that (as + of 1.2 beta 9) file appenders, when dealing with a relative + file name, assume it is relative to the application + domain code base. If you use log4net, it is very handy with the mechanics used by + Spring Windows Service as every log file you will specify will + be relative the directory containing the service application. + + + + + This file should also define your application context. When the + service is started and stopped, the corresponding lifecycle methods + are called on all the singletons defined. Of course, singletons are + automatically instantiated by the application context when the + service starts. For more information on lifecycles in Spring.NET see + + Here an example taken from the tests: + <%= XmlExample("test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/service.config", "code") %> + + + In this case the context is (again!) defined in another file (author's personal taste...) and the only 'service' is the + echo object (there is also a PropertyPlaceholderConfigurer just to make the example + more realistic): + <%= XmlExample("test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/service.xml", "code") %> + + + Let the application know where it is + + There are some properties you may need at runtime, when your services + will run, and you cannot know in advance. Hopefully, your xml + definition file will allow to find the information it needs using some + predefined variables you can use inside the service definition file + with the standard + NAnt style ${property name} syntax. + These properies are: + + + spring.services.application.fullpath + that will be replaced with the full path of the application's + AppDomain.BaseDirectory, i.e., where your + application has been deployed; + + + spring.services.application.name that + will be replaced with the name of the subdirectory where the + application has been deployed. Each application is deployed in + its own directory, of course; + + + + + These properties are accessible only if one defines a localizer in the + context like this (the localizer is a special IObjectFactoryPostProcessor: + <%= XmlExample("test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/service.xml", "code/localizer") %> + + + As you can see above, one can easily change the prefix used by that localizer and then write someting like: + <%= XmlExample("test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/service.xml", "code/simple") %> + + + + + + <literal>watcher.xml</literal> - optional + + This file allows you to optionally define a watcher for your application + that can automatically redeploy it when needed. + + + The important thing to notice is that you can define your own + application watcher, named watcher. Here it is used + a watcher that listen for changes on the filesystem, configured to + listen for some changes and to ignore others. + + + You can provide your own implementation defining an object named + watcher that implements + Spring.Services.WindowsService.Common.Deploy.IApplicationWatcher: + <%= CsExample("src/Spring/Spring.Services/WindowsService/Common/Deploy/IApplicationWatcher.cs", "code/interface") %> + + + Please notice that this interface is currently a movable target and + will probably change before the first official release (this will probably + affect also the way a watcher will know about the application it should + monitor, as shown in a few lines). + + A tipical example of this file is give here: + <%= XmlExample("test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/watcher.xml", "code") %> + + + As you can see, if you need it, you can reference the + Spring.Services.WindowsService.Common.IApplication + object that your watcher should watch using the name + .injected.application. + + + + <literal>bin</literal> directory - optional + + This is, by default, the folder where your assemblies are placed + in the same way they are in an ASP.NET application. + + + Putting assemblies there is more a convention and maybe a good + practice (they are isolated from other artifacts, but maybe you will + prefer to use another directory (modify the + service.config file accordingly) or the application + directory directly (= bin parent). + + + Be aware of the fact that the process in which your application will + run will have its own PATH environmental variable. As such + don't expect to be successfull using dlls imported + with [DllImport] if they are not in the system PATH of the + hosting machine: while it is well known that the CLR fusion + algorithm will not consider the PATH variable, you may be biten + by assemblies using non-system dlls (SQLite and Firebird ADO.NET + providers are good examples). + + Reiterating, one can put assemblies in another directory + under the application directory tree, and write + the .NET configuration file (service.config) + accordingly: .NET probing algorithm is always in place. + + + Please notice that it is not required that + your application uses or include any of the Spring.NET assemblies: + any object in any assembly, given it has lifecycle methods, can + be run as a service: non invasive infrastructure support courtesy + of Spring.NET! + + + + + + Customizing or extending + + It should be said that support for windows service has been initially + developed with a clear but limited set of 'extension points' in mind, + mainly related to the way you can deploy your services: + deploy location (filesystem, zip archives, mailbox, urls, ...), + (auto-)updating features, and so on. + + + To better understand the following discussion, the following figure + depicts some of the inner details of + Spring.Services.WindowsService.Process.exe + at run-time: + + + + + + Spring.Services.WindowsService.Process.exe run-time details + + + + + The <literal>.config</literal> file + + + The executable Spring.Services.WindowsService.Process.exe + is somewhat configured by the corresponding + .config file. + Please notice that this file is the most important extension point + for windows service support, and it will probably be made more powerful + and flexible in the future. + + + For applications deployed in the standard way (i.e. on the filesystem + as explained above) the updating features are configured by the + watcher.xml file, if present, + as already seen. + + + There should be however, other ways to deploy your applications, + maybe just as zip files dropped somewhere on the web or sent via + e-mail. + + + For these scenarios, your deploy location will be something that + implements + Spring.Services.WindowsService.Common.Deploy.IDeployLocation. + + Please notice that, while questionable, it actually entends + IDisposable this has been done + as it is possible that a deploy location holds resources that should be + released, for example network connections, lock files or the like: + + <%= CsExample("src/Spring/Spring.Services/WindowsService/Common/Deploy/IDeployLocation.cs", "code/interface") %> + <%= CsExample("src/Spring/Spring.Services/WindowsService/Common/Deploy/IDeployEventSource.cs", "code/interface") %> + + + + diff --git a/doc/reference/src/testing.xml b/doc/reference/src/testing.xml new file mode 100644 index 00000000..6c8fcd0a --- /dev/null +++ b/doc/reference/src/testing.xml @@ -0,0 +1,414 @@ + + + Testing + +
+ Introduction + + The Spring team considers developer testing to be an absolutely + integral part of enterprise software development. A thorough treatment of + testing in the enterprise is beyond the scope of this chapter; rather, the + focus here is on the value add that the adoption of the IoC principle can + bring to unit testing; and on the + benefits that the Spring Framework provides in integration testing. +
+ +
+ Unit testing + + One of the main benefits of Dependency Injection is that your code + is much less likely to have any hidden dependencies on the runtime + environment or other configuration subsystems. This allows for unit tests + to be written in a manner such that the object under test can be simply + instantiated with the new operator and have its + dependences set in the unit test code. You can use mock objects (in + conjunction with many other valuable testing techniques) to test your code + in isolation. If you follow the architecture recommendations around Spring + you will find that the resulting clean layering and componentization of + your codebase will naturally faciliate easier unit + testing. For example, you will be able to test service layer objects by + stubbing or mocking DAO interfaces, without any need to access persistent + data while running unit tests. + + True unit tests typically will run extremely quickly, as there is no + runtime infrastructure to set up, i.e., database, ORM tool, or whatever. + Thus emphasizing true unit tests as part of your development methodology + will boost your productivity. The upshot of this is that you do not need + this section of the testing chapter to help you write effective + unit tests for your IoC-based applications. +
+ +
+ Integration testing + + However, it is also important to be able to perform some integration + testing enabling you to test things such as: + + + + The correct wiring of your Spring IoC container contexts. + + + + Data access using ADO.NET or an ORM tool. This would include + such things such as the correctness of SQL statements / or NHibernate + XML mapping files. + + + + The Spring Framework provides first class support for integration + testing in the form of the classes that are packaged in the Spring.Testing.NUnit.dll library. + Please note that these test classes are NUnit-specific. Support + for mbUnit and VSTS are under consideration for future + versions. + + + The Spring.Testing.NUnit.dll library is compiled against NUnit + 2.4.1. At the time of this writing the latest version of NUnit is 2.4.6. + Note that add-in have their own versions of NUnit they use. For example, + ReSharper 3.0 uses 2.2.8. If you are using the GUI-runner that comes + with NUnit then you should add the following to your .config file, (in + the form of MyAssembly.dll.config) + + <runtime> + + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + + <dependentAssembly> + <assemblyIdentity name="nunit.framework" + publicKeyToken="96d09a1eb7f44a77" + culture="neutral"/> + <bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535 + newVersion="2.4.6.0"/> + + </dependentAssembly> + + </assemblyBinding> + +</runtime> + + + The Spring.Testing.NUnit namespace provides + valuable NUnit TestCase superclasses for + integration testing using a Spring container. Note that as of NUnit 2.4 + these can be rewritten in terms of custom attributes via NUnit's new + extensibility mechanism. This will be an additional option in an upcoming + release of Spring.NET and is already present in the Java version of the + Spring framework. + + These superclasses provide the following functionality: + + + + Spring IoC container + caching between test case execution. + + + + The pretty-much-transparent Dependency Injection of test fixture + instances (this is nice). + + + + Transaction management + appropriate to integration testing (this is even nicer). + + + + A number of Spring-specific inherited instance variables + that are really useful when integration testing. + + + +
+ Context management and caching + + The Spring.Testing.NUnit + package provides support for consistent loading of Spring contexts, and + caching of loaded contexts. Support for the caching of loaded contexts + is important, because if you are working on a large project, startup + time may become an issue - not because of the overhead of Spring itself, + but because the objects instantiated by the Spring container will + themselves take time to instantiate. For example, a project with 50-100 + NHibernate mapping files might take 10-20 seconds to load the mapping + files, and incurring that cost before running every single test case in + every single test fixture will lead to slower overall test runs that + could reduce productivity. + + To address this issue, the + AbstractDependencyInjectionSpringContextTests has + an protected property that subclasses must implement + to provide the location of context definition files: + + protected abstract string[] ConfigLocations { get; } + + Implementations of this method must provide an array containing + the IResource locations of XML configuration metadata used to configure + the application. This will be the same, or nearly the same, as the list + of configuration locations specified in + App.config/Web.config or other deployment + configuration. + + By default, once loaded, the configuration file set will be reused + for each test case. Thus the setup cost will be incurred only once (per + test fixture), and subsequent test execution will be much faster. In the + unlikely case that a test may 'dirty' the config location, requiring + reloading - for example, by changing an object definition or the state + of an application object - you can call the + SetDirty() method on + AbstractDependencyInjectionSpringContextTests to + cause the test fixture to reload the configurations and rebuild the + application context before executing the next test case. +
+ +
+ Dependency Injection of test fixtures + + When + AbstractDependencyInjectionSpringContextTests + (and subclasses) load your application context, they can optionally + configure instances of your test classes by Setter Injection. All you + need to do is to define instance variables and the corresponding + setters. + AbstractDependencyInjectionSpringContextTests + will automatically locate the corresponding object in the set of + configuration files specified in the + ConfigLocations property. + + Consider the scenario where we have a class, + HibernateTitleDao, that performs data access + logic for say, the Title domain object. We want + to write integration tests that test all of the following areas: + + + + The Spring configuration; basically, is everything related to + the configuration of the HibernateTitleDao + object correct and present? + + + + The Hibernate mapping file configuration; is everything mapped + correctly and are the correct lazy-loading settings in place? + + + + The logic of the HibernateTitleDao; + does the configured instance of this class perform as + anticipated? + + + + Let's look at the test class itself (we will look at the + configuration immediately afterwards). + + [TestFixture] +public class HibernateTitleDaoTests : AbstractDependencyInjectionSpringContextTests { + + // this instance will be (automatically) dependency injected + private HibernateTitleDao titleDao; + + // a setter method to enable DI of the 'titleDao' instance variable + public HibernateTitleDao HibernateTitleDao { + set { titleDao = value; } + } + + [Test] + public void LoadTitle() { + Title title = this.titleDao.LoadTitle(10); + Assert.IsNotNull(title); + } + + // specifies the Spring configuration to load for this test fixture + protected override string[] ConfigLocations { + return new String[] { "assembly://MyAssembly/MyNamespace/daos.xml" }; + } + +} + + The file referenced by the ConfigLocations method + ('classpath:com/foo/daos.xml') looks like + this: + + <?xml version="1.0" encoding="utf-8" ?> +<objects xmlns="http://www.springframework.net"> + + <!-- this object will be injected into the HibernateTitleDaoTests class --> + <object id="titleDao" type="Spring.Samples.HibernateTitleDao, Spring.Samples"> + <property name="sessionFactory" ref="sessionFactory"/> + </object> + + <object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate"> + <!-- dependencies elided for clarity --> + </object> + +</objects> + + The + AbstractDependencyInjectionSpringContextTests + classes uses autowire + by type. Thus if you have multiple object definitions + of the same type, you cannot rely on this approach for those particular + object. In that case, you can use the inherited + applicationContext instance variable, and explicit + lookup using (for example) an explicit call to + applicationContext.GetObject("titleDao"). + + If you don't want dependency injection applied to your test cases, + simply don't declare any set properties. Alternatively, you can extend + the AbstractSpringContextTests - the root of the + class hierarchy in the Spring.Testing.NUnit + namespace. It merely contains convenience methods to load Spring + contexts, and performs no Dependency Injection of the test + fixture. + +
+ Field level injection + + If, for whatever reason, you don't fancy having setter + properties in your test fixtures, Spring can (in this one case) inject + dependencies into protected fields. Find below a + reworking of the previous example to use field level injection (the + Spring XML configuration does not need to change, merely the test + fixture). + + [TestFixture] +public class HibernateTitleDaoTests : AbstractDependencyInjectionSpringContextTests { + + public HibernateTitleDaoTests() { + // switch on field level injection + PopulateProtectedVariables = true; + } + + // this instance will be (automatically) dependency injected + protected HibernateTitleDao titleDao; + + [Test] + public void LoadTitle() { + Title title = this.titleDao.LoadTitle(10); + Assert.IsNotNull(title); + } + + // specifies the Spring configuration to load for this test fixture + protected override string[] ConfigLocations { + return new String[] { "assembly://MyAssembly/MyNamespace/daos.xml" }; + } + +} + + In the case of field injection, there is no autowiring going on: + the name of your protected instances variable(s) + are used as the lookup object name in the configured Spring + container. +
+
+ +
+ Transaction management + + One common issue in tests that access a real database is their + effect on the state of the persistence store. Even when you're using a + development database, changes to the state may affect future tests. + Also, many operations - such as inserting to or modifying persistent + data - cannot be done (or verified) outside a transaction. + + The + AbstractTransactionalDbProviderSpringContextTests + superclass (and subclasses) exist to meet this need. By default, they + create and roll back a transaction for each test. You simply write code + that can assume the existence of a transaction. If you call + transactionally proxied objects in your tests, they will behave + correctly, according to their transactional semantics. + + AbstractTransactionalSpringContextTests + depends on a IPlatformTransactionManager object + being defined in the application context. The name doesn't matter, due + to the use of autowire by type. + + Typically you will extend the subclass, + AbstractTransactionalDbProviderSpringContextTests. + This also requires that a DbProvider object + definition - again, with any name - be present in the configurations. It + creates an AdoTemplate instance variable that is + useful for convenient querying, and provides handy methods to delete the + contents of selected tables (remember that the transaction will roll + back by default, so this is safe to do). + + If you want a transaction to commit - unusual, but occasionally + useful when you want a particular test to populate the database - you + can call the SetComplete() method inherited + from AbstractTransactionalSpringContextTests. + This will cause the transaction to commit instead of roll back. + + There is also convenient ability to end a transaction before the + test case ends, through calling the + EndTransaction() method. This will roll back + the transaction by default, and commit it only if + SetComplete() had previously been called. This + functionality is useful if you want to test the behavior of + 'disconnected' data objects, such as Hibernate-mapped objects that will + be used in a web or remoting tier outside a transaction. Often, lazy + loading errors are discovered only through UI testing; if you call + EndTransaction() you can ensure correct + operation of the UI through your NUnit test suite. +
+ +
+ Convenience variables + + When you extend the + AbstractTransactionalDbProviderSpringContextTests + class you will have access to the following protected + instance variables: + + + + applicationContext (a + IConfigurableApplicationContext): + inherited from the + AbstractDependencyInjectionSpringContextTests + superclass. Use this to perform explicit object lookup, or test the + state of the context as a whole. + + + + adoTemplate: inherited from + AbstractTransactionalDbProviderSpringContextTests. + Useful for querying to confirm state. For example, you might query + before and after testing application code that creates an object and + persists it using an ORM tool, to verify that the data appears in + the database. (Spring will ensure that the query runs in the scope + of the same transaction.) You will need to tell your ORM tool to + 'flush' its changes for this to work correctly, for example using + the Flush() method on NHibernate's + ISession interface. + + + + Often you will provide an application-wide superclass for + integration tests that provides further useful instance variables used + in many tests +
+ +
+
+ +
+ Further Resources + + This section contains links to further resources about testing in + general. + + + + The NUnit + homepage. The Spring Framework's unit test suite is written + using NUnit as the testing framework. + + +
+
\ No newline at end of file diff --git a/doc/reference/src/threading.xml b/doc/reference/src/threading.xml new file mode 100644 index 00000000..7eb3df3c --- /dev/null +++ b/doc/reference/src/threading.xml @@ -0,0 +1,247 @@ + + + Threading and Concurrency Support + + + Introduction + + The purpose of the Spring.Threading namespace + is to provide a place to keep useful concurrency abstractions that augment + those in the BCL. Since Doug Lea has provided a wealth of mature public + domain concurrency abstractions in his Java based + 'EDU.oswego.cs.dl.util.concurrent' libraries we decided to port a few of + his abstractions to .NET. So far, we've only ported three classes, the + minimum necessary to provide basic object pooling functionality to support + an AOP based pooling aspect and to provide a Semaphore class that was + mistakenly not included in .NET 1.0/1.1. + + There is also an important abstraction, IThreadStorage, for + performing thread local storage. + + + + Thread Local Storage + + Depending on your runtime environment there are different strategies + to use for storing objects in thread local storage. If you are in web + applications a single Request may be executed on different threads. As + such, the location to store thread local objects is in + HttpContext.Current. For other environments + System.Runtime.Remoting.Messaging.CallContext is + used. For more background information on the motivation behind these + choices, say as compared to the attribute [ThreadStatic] refer to + "Piers7"'s blog + and this forum + post. The interface IThreadStorage serves as the basis for the + thread local storage abstraction and various implementations can be + selected from depending on your runtime requirements. Configuring the + implementation of IThreadStorage makes it easier to have more portability + across runtime environments. + + The API is quite simple and shown belowpublic interface IThreadStorage +{ + object GetData(string name) + + void SetData(string name, object value) + + void FreeNamedDataSlot(string name) + +} + + + The methods GetData and + SetData are responsible for retrieving and + setting the object that is to be bound to thread local storage and + associating it with a name. Clearing the thread local storage is done via + the method FreeNamedDataSlot. + + In Spring.Core is the implementation, + CallContextStorage, that directly uses + CallContext and also the implementation + LogicalThreadContext which by default uses + CallContextStorage but can be configured via the + static method SetStorage(IThreadStorage). The + methods on CallContextStorage and LogicalThreadContext are static. + + In Spring.Web is the implementation + HttpContextStorage which uses the + HttpContext to store thread local data and + HybridContextStorage that uses + HttpContext if within a web environment, i.e. + HttpContext.Current != null, and + CallContext otherwise. + + Spring internally uses LogicalThreadContext + as this doesn't require a coupling to the System.Web + namespace. In the case of Spring based web applications, Spring's + WebSupportModule sets the storage strategy of + LogicalThreadContext to be + HybridContextStorage. + + + + Synchronization Primitives + + When you take a look at these synchronization classes, you'll wonder + why it's even necessary when System.Threading provides + plenty of synchronization options. Although + System.Threading provides great synchronization + classes, it doesn't provide well-factored abstractions and interfaces for + us. Without these abstractions, we will tend to code at a low-level. With + enough experience, you'll eventually come up with some abstractions that + work well. Doug Lea has already done a lot of that research and has a + class library that we can take advantage of. + + + ISync + + ISync is the central interface for all classes + that control access to resources from multiple threads. It's a simple + interface which has two basic use cases. The first case is to block + indefinitely until a condition is met: + + void ConcurrentRun(ISync lock) { + lock.Acquire(); // block until condition met + try { + // ... access shared resources + } + finally { + lock.Release(); + } +} + + + The other case is to specify a maximum amount of time to block + before the condition is met: + + void ImpatientConcurrentRun(ISync lock) { + // block for at most 10 milliseconds for condition + if ( lock.Attempt(10) ) { + try { + // ... access shared resources + } + finally { + lock.Release(); + } + } else { + // complain of time out + } +} + + + + + SyncHolder + + The SyncHolder class implements the + System.IDisposable interface and so provides a way to + use an ISync with the using C# + keyword: the ISync will be automatically + Acquired and then Released on + exiting from the block. + + This should simplify the programming model for code using (!) an + ISync: +ISync sync = ... +... +using (new SyncHolder(sync)) + { + // ... code to be executed + // holding the ISync lock + } + There is also the timed version, a little more + cumbersome as you must deal with timeouts: +ISync sync = ... +long msecs = 100; +... +// try to acquire the ISync for msecs milliseconds +try +{ + using (new SyncHolder(sync, msecs)) + { + // ... code to be executed + // holding the ISync lock + } +} +catch (TimeoutException) +{ + // deal with failed lock acquisition +} + + + + + + Latch + + The Latch class implements the + ISync interface and provides an implementation of a + latch. A latch is a boolean condition that is set + at most once, ever. Once a single release is issued, all acquires will + pass. It is similar to a ManualResetEvent initialized + unsignalled (Reset) and can only be Set(). A typical + use is to act as a start signal for a group of worker threads. + + class Boss { + Latch _startPermit; + + void Worker() { + // very slow worker initialization ... + // ... attach to messaging system + // ... connect to database + _startPermit.Acquire(); + // ... use resources initialized in Mush + // ... do real work + } + + void Mush() { + _startPermit = new Latch(); + for (int i=0; i<10; ++i) { + new Thread(new ThreadStart(Worker)).Start(); + } + // very slow main initialization ... + // ... parse configuration + // ... initialize other resources used by workers + _startPermit.Release(); + } + +} + + + + Semaphore + + The Semaphore class implements the + ISync interface and provides an implementation of a + semaphore. Conceptually, a semaphore maintains a set of permits. Each + Acquire() blocks if necessary until a permit is + available, and then takes it. Each Release() adds a + permit. However, no actual permit objects are used; the Semaphore just + keeps a count of the number available and acts accordingly. A typical + use is to control access to a pool of shared objects. + + class LimitedConcurrentUploader { + // ensure we don't exceed maxUpload simultaneous uploads + Semaphore _available; + public LimitedConcurrentUploader(maxUploads) { + _available = new Semaphore(maxUploads); + } + // no matter how many threads call this method no more + // than maxUploads concurrent uploads will occur. + public Upload(IDataTransfer upload) { + _available.Acquire(); + try { + upload.TransferData(); + } + finally { + _available.Release(); + } + } +} + + + + + \ No newline at end of file diff --git a/doc/reference/src/transaction.xml b/doc/reference/src/transaction.xml new file mode 100644 index 00000000..1d567dd3 --- /dev/null +++ b/doc/reference/src/transaction.xml @@ -0,0 +1,2006 @@ + + + Transaction management + + + Introduction + + Spring.NET provides a consistent abstraction for transaction + management that provides the following benefits + + + + Provides a consistent programming model across different + transaction APIs such as ADO.NET, Enterprise Services, + System.Transactions, and NHibernate. + + + + Support for declarative transaction + management with any of the above data access + technologies + + + + Provides a simple API for programmatic transaction management + + + + Integrates with Spring's high level persistence integration APIs + such as AdoTemplate. + + + + This chapter is divided up into a number of sections, each detailing + one of the value-adds or technologies of the Spring Framework's + transaction support. The chapter closes with some discussion of best + practices surrounding transaction management. + + + + The first section, entitled Motivations describes why one would want + to use the Spring Framework's transaction abstraction as opposed to + using System.Transactions or a specific data access technology + transaction API. + + + + The second section, entitled Key Abstractions outline the core + classes as well as how to configure them. + + + + Th third section, entitled Declarative + transaction management, covers support for declarative + transaction management. + + + + The fourth section, entitled Programmatic + transaction management, covers support for programmatic + transaction management. + + + + + + Motivations + + The data access technology landscape is a broad one, within the .NET + BCL there are three APIs for performing transaction management, namely + ADO.NET, Enterprise Services, and System.Transactions. Other data access + technologies such as object relational mappers and result-set mapping + libraries are also gaining in popularity and each come with their own APIs + for transaction management. As such, code is often directly tied to a + particular transaction API which means you must make an up-front decision + which API to use in your application. Furthermore, if the need arises to + change your approach, it quite often will not be a simple refactoring. + Using Spring's transaction API you can keep the same API across different + data access technologies. Changing the underlying transaction + implementation that is used is a simple matter of configuration or a + centralized programmatic change as compared to a major overhauling. + + Hand in hand with the variety of options available is the + establishment generally agreed upon best practices for data access. Martin + Fowler's book, Patterns of Enterprise Application Architecture, is an + excellent source of approaches to data access that have been successful in + the real world. One approach that is quite common is to introduce a data + access layer into your architecture. The data access layer is concerned + not only with providing some portability between different data access + technologies and databases but its scope is strictly related to data + access. A simple data access layer would be not much more than data access + objects (DAOs) with 'Create/Retrieve/Update/Delete' (CRUD) methods devoid + of any business logic. Business logic resides in another application + layer, the business service layer, in which business logic will call one + or more DAOs to fulfill a higher level end-user function. + + In order to perform this end-user function with all-or-nothing + transactional semantics, the transaction context is controlled by the + business service layer (or other 'higher' layers). In such a common + scenario, an important implementation detail is how to make the DAO + objects aware of the 'outer' transaction started in another layer. A + simplistic implementation of a DAO would perform its own connection and + transaction management, but this would not allow grouping of DAO + operations with the same transaction as the DAO is doing its own + transaction/resource management. As such there needs to be a means to + transfer the connection/transaction pair managed in the business service + layer to the DAOs. There are a variety of ways to do this, the most + invasive being the explicitly pass a connection/transaction object as + method arguments to your DAOs. Another way is to store the + connection/transaction pair in thread local storage. In either case, if + you are using ADO.NET you must invent some infrastructure code to perform + this task. + + But wait, doesn't Enterprise Services solve this problem - and what + about the functionality in the System.Transactions namespace? The answer + is yes...and no. Enterprise Services lets you use the 'raw' ADO.NET API + within a transaction context such that multiple DAO operations are grouped + within the same transaction. The downside to Enterprise Services is that + it always uses distributed (global) transactions via the Microsoft + Distributed Transaction Coordinator (MS-DTC). For most applications this + is overkill just to get this functionality as global transactions are + significantly less performant than local ADO.NET transactions. + + There are similar issues with using the 'using TransactionScope' + construct within the new System.Transactions namespace. The goal with + TransactionScope is to define a, well - transaction scope - within a using + statement. Plain ADO.NET code within that using block will then be a local + ADO.NET based transaction if only a single transactional resource is + accessed. However, the 'magic' of System.Transactions (and the database) + is that local transactions will be promoted to distributed transactions + when a second transaction resource is detected. The name that this goes by + is Promotable Single Phase Enlistment (PSPE). However, there is a big + caveat - opening up a second IDbConnection object to the same database + with the same database string will trigger promotion from local to global + transactions. As such, if your DAOs are performing their own connection + management you will end up being bumped up to a distributed transaction. + In order to avoid this situation for the common case of an application + using a single database, you must pass around a connection object to your + DAOs. It is also worth to note that many database providers (Oracle for + sure) do not yet support PSPE and as such will always use a distributed + transaction even if there is only a single database. + + Last but not least is the ability to use declarative transaction + management. Not many topics in database transaction-land give developers + as much 'bang-for-the-buck' as declarative transactions since the noisy + tedious bits of transactional API code in your application are pushed to + the edges, usually in the form of class/method attributes. Only Enterprise + Services offers this feature in the BCL. Spring fills the gap - it + provides declarative transaction management if you are using local ADO.NET + or System.Transactions (the most popular) or other data access + technologies. Enterprise Services is not without it small warts as well, + such as the need to separate your query/retrieve operations from your + create/update/delete operations if you want to use different isolation + levels since declarative transaction metadata can only be applied at the + class level. Nevertheless, all in all, Enterprise Services, in particular + with the new 'Services Without Components' implementation for XP + SP2/Server 2003, and hosted within the same process as your application + code is as good as it gets out of the .NET box. Despite these positive + points, it hasn't gained a significant mindshare in the development + community. + + Spring's transaction support aims to relieve these 'pain-points' + using the data access technologies within the BCL - and for other third + party data access technologies as well. It provides declarative + transaction management with a configurable means to obtain transaction + option metadata - out of the box attributes and XML within Spring's IoC + configuration file are supported. + + Finally, Spring's transaction support lets you mix data access + technologies within a single transaction - for example ADO.NET and + NHibernate operations. + + With this long winded touchy/feely motivational section behind us, + lets move on to see the code. + + + + Key Abstractions + + The key to the Spring transaction management abstraction is the + notion of a transaction strategy. A transaction + strategy is defined by the + Spring.Transaction.IPlatformTransactionManager + interface, shown below: + + public interface IPlatformTransactionManager { + + ITransactionStatus GetTransaction( ITransactionDefinition definition ); + + void Commit( ITransactionStatus transactionStatus ); + + void Rollback( ITransactionStatus transactionStatus ); + +} + + This is primarily a 'SPI' (Service Provider Interface), although it + can be used Programatically. Note that in keeping with the Spring + Framework's philosophy, IPlatformTransactionManager + is an interface, and can thus be easily mocked or stubbed as necessary. + IPlatformTransactionManager implementations + are defined like any other object in the IoC container. The following + implementations are provided + + + + AdoPlatformTransactionManager - local + ADO.NET based transactions + + + + ServiceDomainPlatformTransactionManager - + distributed transaction manager from Enterprise Services + + + + TxScopePlatformTransactionManager - + local/distributed transaction manager from System.Transactions. + + + + HibernatePlatformTransactionManager - + local transaction manager for use with NHibernate or mixed + ADO.NET/NHibernate data access operations. + + + + Under the covers, the following API calls are made. For the + AdoPlatformTransactionManager, Transaction.Begin(), Commit(), Rollback(). + ServiceDomainPlatformTransactionManager uses the 'Services without + Components' update so that your objects do not need to inherit from + ServicedComponent or directly call the Enterprise Services API + ServiceDomain.Enter(), Leave; ContextUtil.SetAbort(). + TxScopePlatformTransactionManager calls; new TransactionScope(); + .Complete(), Dispose(), Transaction.Current.Rollback(). Configuration + properties for each transaction manager are specific to the data access + technology used. Refer to the API docs for comprehensive information but + the examples should give you a good basis for getting started. The + HibernatePlatformTransactionManager is described more in the following + section . + + The GetTransaction(..) method returns a + ITransactionStatus object, depending on a + ITransactionDefinition parameters. The returned + ITransactionStatus might represent a new or + existing transaction (if there was a matching transaction in the current + call stack - with the implication being that a + ITransactionStatus is associated with a logical + thread of execution. + + The ITransactionDefinition interface + specified + + + + Isolation: the degree of isolation this transaction has from the + work of other transactions. For example, can this transaction see + uncommitted writes from other transactions? + + + + Propagation: normally all code executed within a transaction + scope will run in that transaction. However, there are several options + specifying behavior if a transactional method is executed when a + transaction context already exists: for example, simply continue + running in the existing transaction (the common case); or suspending + the existing transaction and creating a new transaction. + + + + Timeout: how long this transaction may run before timing out + (and automatically being rolled back by the underlying transaction + infrastructure). + + + + Read-only status: a read-only transaction does not modify any + data. Read-only transactions can be a useful optimization in some + cases (such as when using NHibernate). + + + + These settings reflect standard transactional concepts. If + necessary, please refer to a resource discussing transaction isolation + levels and other core transaction concepts because understanding such core + concepts is essential to using the Spring Framework or indeed any other + transaction management solution. + + The ITransactionStatus interface + provides a simple way for transactional code to control transaction + execution and query transaction status. + + Regardless of whether you opt for declarative or programmatic + transaction management in Spring, defining the correct + IPlatformTransactionManager implementation + is absolutely essential. In good Spring fashion, this important definition + typically is made using via Dependency Injection. + + IPlatformTransactionManager + implementations normally require knowledge of the environment in which + they work, ADO.NET, NHibernate, etc. The following example shows how a + standard ADO.NET based + IPlatformTransactionManager can be + defined. + + We must define a Spring IDbProvider + and then use Spring's + AdoPlatformTransactionManager, giving it a + reference to the IDbProvider. For more information + on the IDbProvider abstraction refer to the next + chapter. + + <objects xmlns='http://www.springframework.net' + xmlns:db="http://www.springframework.net/database"> + + <db:provider id="DbProvider" + provider="SqlServer-1.1" + connectionString="Data Source=(local);Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/> + + <object id="TransactionManager" + type="Spring.Data.AdoPlatformTransactionManager, Spring.Data"> + <property name="DbProvider" ref="DbProvider"/> + </object> + + . . . other object definitions . . . + +</objects> + + + We can also use a transaction manager based on System.Transactions + just as easily, as shown in the following example + + <object id="TransactionManager" + type="Spring.Data.TxScopeTransactionManager, Spring.Data"> + </object> + + Similarly for the HibernateTransactionManager as shown in the + section on ORM transaction + management. + + Note that in all these cases, application code will not need to + change at all since, dependency injection is a perfect companion to using + the strategy pattern. We can now change how transactions are managed + merely by changing configuration, even if that change means moving from + local to global transactions or vice versa. + + + + Resource synchronization with transactions + + How does application code participate with the resources (i.e. + Connection/Transactions/Sessions) that are created/reused/cleanedup via + the different transaction managers? There are two approaches - a + high-level and a low-level approach + + + High-level approach + + The preferred approach is to use Spring's high level persistence + integration APIs. These do not replace native APIs, but internally + handle resource creation/reuse, cleanup, and optional transaction + synchronization (i.e. event notification) of the resources and exception + mapping so that user data access code doesn't have to worry about these + concerns at all, but can concentrate purely on non-boilerplate + persistence logic. Generally, the same inversion of control approach is + used for all persistence APIs. In this approach the API has a callback + method or delegate that presents the user code with the relevant + resource ready to use - i.e. a DbCommand with its Connection and + Transaction properties set based on the transaction option metadata. + These classes go by the naming scheme 'template', examples of which are + AdoTemplate and HibernateTemplate. Convenient 'one-liner' helper methods + in these template classes build upon the core callback/IoC design by + providing specific implementations of the callback interface. + + + + Low-level approach + + A utility class can be used to directly obtain a + connection/transaction pair that is aware of the transactional calling + context and returns a pair suitable for that context. The class + ConnectionUtils contains the static method + ConnectionTxPair GetConnectionTxPair(IDbProvider provider) + which serves this purpose. + + + + + Declarative transaction management + + Most Spring users choose declarative transaction management. It is + the option with the least impact on application code, and hence is most + consistent with the ideals of a non-invasive + lightweight container. + + Spring's declarative transaction management is made possible with + Spring AOP, although, as the transactional aspects code comes with Spring + and may be used in a boilerplate fashion, AOP concepts do not generally + have to be understood to make effective use of this code. + + The basic approach is to specify transaction behavior (or lack of + it) down to the individual method level. It is also possible to mark a + transaction for rollback by setting the 'RollbackOnly' property on the + ITransactionStatus object returned from the IPlatformTransactionManager + within a transaction context if necessary. Some of the highlights of + Spring's declarative transaction management are: + + + + Declarative Transaction management works in any environment. It + can work with ADO.NET, System.Transactions, NHibernate etc, with + configuration changes only. + + + + Enables declarative transaction management to be applied to any + class, not merely special classes such as those that inherit from + ServicedComponent or other infrastructure related base classes. + + + + Declarative rollback rules. Rollback rules can be control + declaratively and allow for only specified exceptions thrown within a + transactional context to trigger a rollback + + + + Spring gives you an opportunity to customize transactional + behavior, using AOP. For example if you want to insert custom behavior + in the case of a transaction rollback, you can. You can also add + arbitrary advice, along with the transactional advice. + + + + Spring does not support propagation of transaction context + across remote calls. + + + + Note rollback rules as configured from XML are still under + development. + + The concept of rollback rules is important: they enable us to + specify which exceptions should cause automatic roll back. We specify this + declaratively, in configuration, not in code. So, while we can still set + RollbackOnly on the + ITransactionStatus object to roll the + current transaction back Programatically, most often we can specify a rule + that MyApplicationException must always result in rollback. This has the + significant advantage that business objects don't need to depend on the + transaction infrastructure. For example, they typically don't need to + import any Spring APIs, transaction or other. If you would like to + rollback the transaction programmatically and you are using declarative + transaction management, use the utility method + + TransactionInterceptor.CurrentTransactionStatus.RollbackOnly = true; + + + Understanding + Spring's declarative transaction implementation + + The aim of this section is to dispel the mystique that is + sometimes associated with the use of declarative transactions. It is all + very well for this reference documentation to simply tell you to + annotate your classes with the Transaction attribute and add some + boilerplate XML to your IoC configuration, and then expect you to + understand how it all works. This section will explain the inner + workings of Spring's declarative transaction infrastructure to help you + navigate your way back upstream to calmer waters in the event of + transaction-related issues. + + + Looking at the Spring source code is a good way to get a real + understanding of Spring's transaction support. You should find the API + documentation informative and complete. We suggest turning the logging + level to 'DEBUG' in your Spring-enabled application(s) during + development to better see what goes on under the hood. + + + The most important concepts to grasp with regard to Spring's + declarative transaction support are that this support is enabled via AOP + proxies, and that the transactional advice is driven by metadata + (currently XML- or attribute-based). The combination of a proxy with + transactional metadata yields an AOP proxy that uses a + TransactionInterceptor in conjunction with an + appropriate IPlatformTransactionManager + implementation to drive transactions around method invocations. + + + Although knowledge of AOP (and specifically Spring AOP) is not + required in order to use Spring's declarative transaction support, it + can help. Spring AOP is thoroughly covered in the AOP chapter. + + + Conceptually, calling a method on a transactional proxy looks like + this. + + + + + + + + The flow of events is the following. First the set of objects you + would like to apply AOP transactional advice to are identified. There + are a variety of ways to configure the Spring IoC container to create + proxies for the defined object definitions. The standard Spring AOP + based options are + + + + ProxyFactoryObject. The common + properties to set are the reference to the object to proxy (the + target object) and a reference to the transaction advice. See for more details. + + + + AutoProxy - Defines criteria to select a collection of objects + to create a transactional AOP proxy. + + The AutoProxy options are + + + + ObjectNameAutoProxyCreator which + specifies a collection of object names based on wildcard + matching of object names. See + + + + DefaultAdvisorAutoProxyCreator + which specifies one or more "advisors" i.e an object + representing an aspect, including both an advice and a pointcut + targeting it to specific joinpoints. See + + + + + + There is also a convenience subclass of + ProxyFactoryObject, namely + TransactionProxyFactoryObject, that sets some + common default values for the specific case of applying transactional + advice. + + The DefaultAdvisorAutoProxyCreator is very + powerful and is the means by which Spring can be configured to use + attributes to identify the pointcuts where transaction advice should be + applied. The advisor that performs that task is + TransactionAttributeSourceAdvisor. + Note, think of the word 'Attribute' in this class name not as + the .NET attribute but as the transaction 'options' you want to + specify. This name is inherited from the Java version and the name + will be changed in the RC1 release to avoid confusion since a common + naming convention when creating classes are .NET attributes is to + put the word 'Attribute' in the name. + + + Which one of the many options available should you choose for your + development? That depends, each one has it own set of pro's and con's + which will be discussed in turn in the following sections. + + With the transactional AOP proxy now created we can discuss the + flow of events in the code as proxied methods are invoked. When the + method is invoked, before calling the target object's method, a + transaction is created if one hasn't already been created. Then the + target method is invoked. If there was an exception throw, the + transaction is typically rolled back, but it can also be committed if + the exception type specified in the transaction option, NoRollbackFor, + matches the thrown exception. If no exception was thrown, that is taken + as a sign of success and the transaction is committed. + + When using other AOP advice with the transactional advice you can + set the order of the 'interceptor chain' so that, for example, + performance monitoring advice always precede the transactional + advice. + + + + A First Example + + Consider the following interface. The intent is to convey the + concepts to you so you can concentrate on the transaction usage and not + have to worry about domain specific details. The + ITestObjectManager is a poor-mans + business service layer - the implementation of which will make two DAO + calls. Clearly this example is overly simplistic from the service layer + perspective as there isn't any business logic at all!. The 'service' + interface is shown below. + + public interface ITestObjectManager +{ + void SaveTwoTestObjects(TestObject to1, TestObject to2); + + void DeleteTwoTestObjects(string name1, string name2); +} + + The implementation of + ITestObjectManager is shown below + + public class TestObjectManager : ITestObjectManager +{ + + // Fields/Properties ommited + + [Transaction()] + public void SaveTwoTestObjects(TestObject to1, TestObject to2) + { + TestObjectDao.Create(to1.Name, to1.Age); + TestObjectDao.Create(to2.Name, to1.Age); + } + + [Transaction()] + public void DeleteTwoTestObjects(string name1, string name2) + { + TestObjectDao.Delete(name1); + TestObjectDao.Delete(name2); + } +} + + Note the Transaction attribute on the methods. Other options such + as isolation level can also be specified but in this example the default + settings are used. However, please note that the mere presence of the + Transaction attribute is not enough to actually turn on the + transactional behavior - the Transaction attribute is simply metadata + that can be consumed by something that is Transaction attribute-aware + and that can use the said metadata to configure the appropriate objects + with transactional behavior. + + The TestObjectDao property has basic create + update delete and find method for the 'domain' object TestObject. + TestObject in turn has simple properties like name and age. + + public interface ITestObjectDao +{ + void Create(string name, int age); + void Update(TestObject to); + void Delete(string name); + TestObject FindByName(string name); + IList FindAll(); +} + + The Create and Delete method implementation is shown below. Note + that this uses the AdoTemplate class discussed in + the following chapter. Refer to for + information on the interaction between Spring's high level persistence + integration APIs and transaction management features. + + public class TestObjectDao : AdoDaoSupport, ITestObjectDao +{ + public void Create(string name, int age) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + String.Format("insert into TestObjects(Age, Name) VALUES ({0}, '{1}')", + age, name)); + } + + public void Delete(string name) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + String.Format("delete from TestObjects where Name = '{0}'", + name)); + } +} + + The TestObjectManager is configured with + the DAO objects by standard dependency injection techniques. The client + code, which in this case directly asks the Spring IoC container for an + instance of ITestObjectManager, will + receive a transaction proxy with transaction options based on the + attribute metadata. Note that typically the + ITestObjectManager would be set on yet + another higher level object via dependency injection, for example a web + service. + + The client calling code is shown below + + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/autoDeclarativeServices.xml"); + +ITestObjectManager mgr = ctx["testObjectManager"] as ITestObjectManager; + +TestObject to1 = new TestObject(); +to1.Name = "Jack"; +to1.Age = 7; + +TestObject to2 = new TestObject(); +to2.Name = "Jill"; +to2.Age = 8; + +mgr.SaveTwoTestObjects(to1, to2); + +mgr.DeleteTwoTestObjects("Jack", "Jill"); + + + + The configuration of the object definitions of the DAO and manager + classes is shown below. + + <objects xmlns='http://www.springframework.net' + xmlns:db="http://www.springframework.net/database"> + + <db:provider id="DbProvider" + provider="SqlServer-1.1" + connectionString="Data Source=(local);Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/> + + <object id="transactionManager" + type="Spring.Data.AdoPlatformTransactionManager, Spring.Data"> + + <property name="DbProvider" ref="DbProvider"/> + </object> + + <object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data"> + <property name="DbProvider" ref="DbProvider"/> + </object> + + <object id="testObjectDao" type="Spring.Data.TestObjectDao, Spring.Data.Integration.Tests"> + <property name="AdoTemplate" ref="adoTemplate"/> + </object> + + + <!-- The object that performs multiple data access operations --> + <object id="testObjectManager" + type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> + <property name="TestObjectDao" ref="testObjectDao"/> + </object> + + +</objects> + + This is standard Spring configuration and as such provides you + with the flexibility to parameterize your connection string and to + easily switch implementations of your DAO objects. The configuration to + create a transactional proxy for the manager class is shown + below. + + <!-- The rest of the config file is common no matter how many objects you add --> + <!-- that you would like to have declarative tx management applied to --> + + <object id="autoProxyCreator" + type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"> + </object> + + <object id="transactionAdvisor" + type="Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor, Spring.Data"> + <property name="TransactionInterceptor" ref="transactionInterceptor"/> + </object> + + + <!-- Transaction Interceptor --> + <object id="transactionInterceptor" + type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data"> + <property name="TransactionManager" ref="transactionManager"/> + <property name="TransactionAttributeSource" ref="attributeTransactionAttributeSource"/> + </object> + + <object id="attributeTransactionAttributeSource" + type="Spring.Transaction.Interceptor.AttributesTransactionAttributeSource, Spring.Data"> + </object> + + + Granted this is a bit verbose and hard to grok at first sight - + however you only need to grok this once as it is 'boiler plate' XML you + can reuse across multiple projects. What these object definitions are + doing is to instruct Spring's to look for all objects within the IoC + configuration that have the [Transaction] attribute and then apply the + AOP transaction interceptor to them based on the transaction options + contained in the attribute. The attribute serves both as a pointcut and + as the declaration of transactional option information. + + Since this XML fragment is not tied to any specific object + references it can be included in its own file and then imported via the + <import> element. In examples and test code this XML configuration + fragment is named autoDeclarativeServices.xml See for more information. + + The classes and their roles in this configuration fragment are + listed below + + + + TransactionInterceptor is the AOP + advice responsible for performing transaction management + functionality. + + + + TransactionAttributeSourceAdvisor is an + AOP Advisor that holds the TransactionInterceptor, which is the + advice, and a pointcut (where to apply the advice), in the form of a + TransactionAttributeSource. + + + + AttributesTransactionAttributeSource is + an implementation of the + ITransactionAttributeSource interface that + defines where to get the transaction metadata defining the + transaction semantics (isolation level, propagation behavior, etc) + that should be applied to specific methods of specific classes. The + transaction metadata is specified via implementations of the + ITransactionAttributeSource interface. This + example shows the use of the implementation + Spring.Transaction.Interceptor.AttributesTransactionAttributeSource + to obtain that information from standard .NET attributes. By the + very nature of using standard .NET attributes, the attribute serves + double duty in identifying the methods where the transaction + semantics apply. Alternative implementations of + ITransactionAttributeSource available are + MatchAlwaysTransactionAttributeSource, + NameMatchTransactionAttributeSource, or + MethodMapTransactionAttributeSource. + + + + MatchAlwaysTransactionAttributeSource + is configured with a ITransactionAttribute instance that is + applied to all methods. The shorthand string representation, + i.e. PROPAGATION_REQUIRED can be used + + + + AttributesTransactionAttributeSource + : Use a standard. .NET attributes to specify the transactional + information. See TransactionAttribute class + for more information. + + + + NameMatchTransactionAttributeSource + allows ITransactionAttributes to be matched by method name. The + NameMap IDictionary property is used to specify the mapping. For + example + + <object name="nameMatchTxAttributeSource" type="Spring.Transaction.Interceptor.NameMatchTransactionAttributeSource, Spring.Data" + <property name="NameMap"> + <dictionary> + <entry key="Execute" value="PROPAGATION_REQUIRES_NEW, -ApplicationException"/> + <entry key="HandleData" value="PROPAGATION_REQUIRED, -DataHandlerException"/> + <entry key="Find*" value="ISOLATION_READUNCOMMITTED, -DataHandlerException"/> + </dictionary> + </property> + +</object> + + Key values can be prefixed and/or suffixed with wildcards + as well as include the full namespace of the containing + class. + + + + MethodMapTransactionAttributeSource + : Similar to NameMatchTransactionAttributeSource but specifies + that only fully qualified method names (i.e. type.method, + assembly) and wildcards can be used at the start or end of the + method name for matching multiple methods. + + + + + + DefaultAdvisorAutoProxyCreator: looks + for Advisors in the context, and automatically creates proxy objects + which are the transactional wrappers + + + + Refer to the following section for a more convenient way to + achieve the same goal of declarative transaction management using + attributes. + + + + Declarative transactions using the transaction namespace + + Spring provides a custom XML schema to simplify the configuration + of declarative transaction management. If you would like to perform + attribute driven transaction management you first need to register the + custom namespace parser for the transaction namespace. This can be done + in the application configuration file as shown below + + <?xml version="1.0" encoding="utf-8" ?> +<configuration> + + <configSections> + + <sectionGroup name="spring"> + <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" /> + + <!-- other spring config sections like context, typeAliases, etc not shown for brevity --> + + </sectionGroup> + </configSections> + + <spring> + + <parsers> + <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" /> + <parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" /> + <parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" /> + </parsers> + + </spring> + + </configSections> + + Instead of using the XML configuration listed at the end of the + previous section (declarativeServices.xml you can use the following. + Note that the schemaLocation in the objects element is needed only if + you have not installed Spring's schema into the proper VS.NET 2005 + location. See the chapter on VS.NET integration for more details. + + <objects xmlns="http://www.springframework.net" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:tx="http://www.springframework.net/schema/tx" + xmlns:db="http://www.springframework.net/database" + xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/schema/objects/spring-objects.xsd + http://www.springframework.net/schema/tx http://www.springframework.net/schema/tx/spring-tx-1.1.xsd" + http://www.springframework.net/schema/tx http://www.springframework.net/schema/tx/spring-database.xsd"> + + <db:provider id="DbProvider" + provider="SqlServer-1.1" + connectionString="Data Source=(local);Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/> + + <object id="transactionManager" + type="Spring.Data.AdoPlatformTransactionManager, Spring.Data"> + + <property name="DbProvider" ref="DbProvider"/> + </object> + + <object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data"> + <property name="DbProvider" ref="DbProvider"/> + </object> + + <object id="testObjectDao" type="Spring.Data.TestObjectDao, Spring.Data.Integration.Tests"> + <property name="AdoTemplate" ref="adoTemplate"/> + </object> + + + <!-- The object that performs multiple data access operations --> + <object id="testObjectManager" + type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> + <property name="TestObjectDao" ref="testObjectDao"/> + </object> + + + <tx:attribute-driven transaction-manager="transactionManager"/> + +</objects> + + + You can actually omit the + 'transaction-manager' attribute in the + <tx:attribute-driven/> tag if the object + name of the + IPlatformTransactionManager that you + want to wire in has the name + 'transactionManager'. If the + PlatformTransactionManager object + that you want to dependency inject has any other name, then you have + to be explicit and use the 'transaction-manager' + attribute as in the example above. + The various optional elements of the + <tx:attribute-driven/> tag are summarised in the following + table + + + <literal><tx:annotation-driven/></literal> + settings + + + + + Attribute + + Required? + + Default + + Description + + + + + + transaction-manager + + No + + transactionManager + + The name of transaction manager to use. Only + required if the name of the transaction manager is not + transactionManager, as in the example + above. + + + + proxy-target-type + + No + + + + Controls what type of transactional proxies are + created for classes annotated with the + [Transaction] attribute. If + "proxy-target-type" attribute is set to + "true", then class-based proxies will be + created (proxy inherits from target class, however calls are + still delegated to target object via composition. This allows + for casting to base class. If + "proxy-target-type" is + "false" or if the attribute is omitted, + then a pure composition based proxy is created and you can + only cast the proxy to implemented interfaces. (See the + section entitled for a + detailed examination of the different proxy + types.) + + + + order + + No + + + + Defines the order of the transaction advice that + will be applied to objects annotated with + [Transaction]. More on the rules related to + ordering of AOP advice can be found in the AOP chapter (see + section ). Note + that not specifying any ordering will leave the decision as to + what order advice is run in to the AOP + subsystem. + + + +
+ + + The "proxy-target-type" attribute on the + <tx:attribute-driven/> element controls what + type of transactional proxies are created for classes annotated with + the Transaction attribute. If + "proxy-target-type" attribute is set to + "true", then inheritance-based proxies will be + created. If "proxy-target-type" is + "false" or if the attribute is omitted, then + composition based proxies will be created. (See the section entitled + for a detailed examination of + the different proxy types.) + + + You can also define the transactional semantics you want to apply + through the use of a <tx:advice> definition. This lets you define + the transaction metadata such as propagation and isolation level as well + as the methods for which that metadata applies external to the code + unlike the case of using the transaction attribute. The + <tx:advice> definition creates an instance of a + ITransactionAttributeSource during parsing time. Switching to use + <tx:advice> instead of <tx:attribute-driven/> in the example + would look like the following + + <tx:advice id="txAdvice" transaction-manager="transactionManager"> + <tx:attributes> + <tx:method name="Save*"/> + <tx:method name="Delete*"/> + </tx:attributes> +</tx:advice> + + + This says that all methods that start with Save and Delete would + have associated with them the default settings of transaction metadata. + These default values are listed below.. + + Here is an example using other elements of the <tx:method/> + definition + + <!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> object below) --> + <tx:advice id="txAdvice" transaction-manager="transactionManager"> + <!-- the transactional semantics... --> + <tx:attributes> + <!-- all methods starting with 'get' are read-only --> + <tx:method name="Get*" read-only="true"/> + <!-- other methods use the default transaction settings (see below) --> + <tx:method name="*"/> + </tx:attributes> + </tx:advice> + + + The <tx:advice/> definition reads as “... all methods on + starting with 'Get' are to execute in the context of a read-only + transaction, and all other methods are to execute with the default + transaction semanticsâ€. The 'transaction-manager' attribute of the + <tx:advice/> tag is set to the name of the + PlatformTransactionManager object that is going to actually drive the + transactions (in this case the 'transactionManager' object). + + You can also use the AOP namespace <aop:advisor> element to + tie together a pointcut and the above defined advice as shown + below. + + <object id="serviceOperation" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"> + <property name="pattern" value="Spring.TxQuickStart.Services.*"/> +</object> + +<aop:config> + + <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/> + +</aop:config> + + This is assuming that the service layer class, TestObjectManager, + in the namespace Spring.TxQuickStart.Services. The <aop:config/> + definition ensures that the transactional advice defined by the + 'txAdvice' object actually executes at the appropriate points in the + program. First we define a pointcut that matches any operation defined + on classes in the Spring.TxQuickStart.Services (you can be more + selective in your regular expression). Then we associate the pointcut + with the 'txAdvice' using an advisor. In the example, the result + indicates that at the execution of a 'SaveTwoTestObjects' and + 'DeleteTwoTestObject', the advice defined by 'txAdvice' will be + run. + + The various transactional settings that can be specified using the + <tx:advice/> tag. The default <tx:advice/> settings are + listed below and are the same as when you use the Transaction + attribute. + + + + The propagation setting is + TransactionPropagation.Required + + + + The isolation level is + IsolationLevel.ReadCommitted + + + + The transaction is read/write + + + + The transaction timeout defaults to the default timeout of the + underlying transaction system, or none if timeouts are not + supported + + + + EnterpriseServicesInteropOption (.NET 2.0 only with + TxScopeTransactionManager) - options between transaction created + with System.Transactions and transactions created through + COM+ + + + + Any exception will trigger rollback. + + + + These default settings can be changed; the various attributes of + the <tx:method/> tags that are nested within + <tx:advice/> and + <tx:attributes/> tags are summarized + below: + + + <literal><tx:method/></literal> settings + + + + + Attribute + + Required? + + Default + + Description + + + + + + name + + Yes + + + + The method name(s) with which the transaction + attributes are to be associated. The wildcard (*) character + can be used to associate the same transaction attribute + settings with a number of methods; for example, + 'Get*', + 'Handle*', 'On*Event', + and so forth. + + + + propagation + + No + + Required + + The transaction propagation behavior + + + + isolation + + No + + ReadCommitted + + The transaction isolation level + + + + timeout + + No + + -1 + + The transaction timeout value (in seconds) + + + + read-only + + No + + false + + Is this transaction read-only? + + + + EnterpriseServicesInteropOption + + No + + None + + Interoperability options with COM+ transactions. (.NET + 2.0 and TxScopeTransactionManager only) + + + + rollback-for + + No + + + + The Exception(s) that will + trigger rollback; comma-delimited. For example, + 'MyProduct.MyBusinessException,ValidationException' + + + + no-rollback-for + + No + + + + The Exception(s) that will + not trigger rollback; comma-delimited. + For example, + 'MyProduct.MyBusinessException,ValidationException' + + + +
+
+ + + Transaction attribute settings + + The Transaction attribute is metadata that specifies that a class + or method must have transactional semantics. The default Transaction + attribute settings are + + + + The propagation setting is + TransactionPropagation.Required + + + + The isolation level is + IsolationLevel.ReadCommitted + + + + The transaction is read/write + + + + The transaction timeout defaults to the default timeout of the + underlying transaction system, or none if timeouts are not + supported + + + + EnterpriseServicesInteropOption (.NET 2.0 only with + TxScopeTransactionManager) - options between transaction created + with System.Transactions and transactions created through + COM+ + + + + Any exception will trigger rollback. + + + + The default settings can, of course, be changed; the various + properties of the Transaction attribute are summarised in the following + table + + + Transaction attribute properties + + + + + + + + + + + Property + + Type + + Description + + + + TransactionPropagation + + enumeration, + Spring.Transaction.TransactionPropagation + + optional propagation setting. Required, Supports, + Mandatory, RequiresNew, NotSupported, Never, Nested + + + + Isolation + + System.Data.IsolationLevel + + optional isolation level + + + + ReadOnly + + boolean + + + + + read/write vs. read-only transaction + + + + + + + EnterpriseServicesInteropOption + + enumeration + System.Transactions.EnterpriseServicesInteropOption + + Options for interoperability with COM+ transactions (.NET + 2.0 and TxScopeTransactionManager only) + + + + Timeout + + int (in seconds granularity) + + the transaction timeout + + + + RollbackFor + + an array of Type objects + + an optional array of exception classes that must cause rollback + + + + NoRollbackFor + + an array of Type objects + + an optional array of exception classes that must not cause rollback + + + +
+ + Note that setting the TransactionPropagation to Nested will throw + a NestedTransactionNotSupportedException in a case where an actual + nested transaction occurs, i.e. not in the case of applying the Nested + propagation but in fact no nested calls are made. This will be fixed for + the Spring 1.2 release for SqlServer and Oracle which support nested + transactions. Also note, that changing of isolation levels on a + per-method basis is also scheduled for the Spring 1.2 release since it + requires detailed command text metadata for each dbprovider. Please + check the forums for news on when this feature will be introduced into + the nightly builds. + + If you specify an exception type for 'NoRollbackFor' the action + taken is to commit the work that has been done in the database up to the + point where the exception occurred. The exception is still propagated + out to the calling code. + + The ReadOnly boolean is a hint to the data access technology to + enable read-only optimizations. This currently has no effect in Spring's + ADO.NET framework. If you would like to enable read-only optimizations + in ADO.NET this is generally done via the 'Mode=Read' or + 'Mode=Read-Only" options in the connection string. Check your database + provider for more information. In the case of NHibernate the flush mode + is set to Never when a new Session is created for the + transaction. + + Throwing exceptions to indicate failure and assuming success is an + easier and less invasive programming model than performing the same task + Programatically - ContextUtil.MyTransactionVote or + TransactionScope.Complete. The rollback options are a means to influence + the outcome of the transaction based on the exception type which adds an + extra degree of flexibility. + + Having any exception trigger a rollback has similar behavior as + applying the AutoComplete attribute available when using .NET Enterprise + Services. The difference with AutoComplete is that using AutoComplete is + also coupled to the lifetime of the ServicedComponent since it sets + ContextUtil.DeactivateOnReturn to true. For a stateless DAO layer this + is not an issue but it could be in other scenarios. Spring's + transactional aspect does not affect the lifetime of your object. +
+ + + Declarative Transactions using AutoProxy + + if you choose not to use the transaction namespace for declarative + transaction management then you can use 'lower level' object definitions + to configure declarative transactions. This approach was shown in the + first example. The use of + Spring's autoproxy functionality defines criteria to select a collection + of objects to create a transactional AOP proxy. There are two AutoProxy + classes that you can use, + ObjectNameAutoProxyCreator and + DefaultAdvisorAutoProxyCreator. If you are using + the new transaction namespace support you do not need to configure these + objects as a DefaultAdvisorAutoProxyCreator is created 'under the + covers' while parsing the transaction namespace elements + + + Creating transactional proxies with + ObjectNameAutoProxyCreator + + The ObjectNameAutoProxyCreator is useful when you would like to + create transactional proxies for many objects. The definitions for the + TransactionInterceptor and associated attributes is done once. When + you add new objects to your configuration file that need to be proxies + you only need to add them to the list of object referenced in the + ObjectNameAutoProxyCreator. Here is an example showing its use. Look + in the section that use ProxyFactoryObject for the declaration of the + transactionInterceptor. + + <object name="autoProxyCreator" + type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop"> + + <property name="InterceptorNames" value="transactionInterceptor"/> + <property name="ObjectNames"> + <list> + <idref local="testObjectManager"/> + </list> + </property> + </object> + + + + Creating transactional proxies with + DefaultAdvisorAutoProxyCreator + + This is a commonly used way to configure declarative + transactions since it enables you to refer to the transaction + attribute as the pointcut to use for the transactional advice for any + object definition defined in the IoC container. An example of this + configuration approach was shown in Chapter 5. + + + + + Declarative Transactions using + TransactionProxyFactoryObject + + The TransactionProxyFactoryObject is easier to use than a + ProxyFactoryObject for most cases since the transaction interceptor and + transaction attributes are properties of this object. This removes the + need to declare them as separate objects. Also, unlike the case with the + ProxyFactoryObject, you do not have to give fully qualified method + names, just the normal 'short' method name. Wild card matching on the + method name is also allowed, which in practice helps to enforce a common + naming convention for the methods of your DAOs. The example from chapter + 5 is shown here using a TransactionProxyFactoryObject. + + + <object id="testObjectManager" + type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> + + <property name="PlatformTransactionManager" ref="adoTransactionManager"/> + <property name="Target"> + <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> + <property name="TestObjectDao" ref="testObjectDao"/> + </object> + </property> + <property name="TransactionAttributes"> + <name-values> + <add key="Save*" value="PROPAGATION_REQUIRED"/> + <add key="Delete*" value="PROPAGATION_REQUIRED"/> + </name-values> + </property> + </object> + + + + Note the use of an inner object definition for the target which + will make it impossible to obtain an unproxied reference to the + TestObjectManager. + + As can be seen in the above definition, the TransactionAttributes + property holds a collection of name/value pairs. The key of each pair is + a method or methods (a * wildcard ending is optional) to apply + transactional semantics to. Note that the method name is not qualified + with a package name, but rather is considered relative to the class of + the target object being wrapped. The value portion of the name/value + pair is the TransactionAttribute itself that needs to be applied. When + specifying it as a string value as in this example, it's in String + format as defined by TransactionAttributeConverter. This format + is: + + PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2 + + Note that the only mandatory portion of the string is the + propagation setting. The default transactions semantics which apply are + as follows: + + + + Exception Handling: All exceptions thrown trigger a + rollback. + + + + Transactions are read/write + + + + Isolation Level: + TransactionDefinition.ISOLATION_DEFAULT + + + + Timeout: TransactionDefinition.TIMEOUT_DEFAULT + + + + Multiple rollback rules can be specified here, comma-separated. A + - prefix forces rollback; a + prefix specifies commit. Under the covers + the IDictionary of name value pairs will be converted to an instance of + NameMatchTransactionAttributeSource + + The string used for PROPAGATION_NAME are those defined on the + Spring.Transaction.TransactionPropagation enumeration, namely Required, + Supports, Mandatory, RequiresNew, NotSupported, Never, Nested. The + string used for ISOLATION_NAME are those defined on the + System.Data.IsolationLevel enumberateion, namely ReadCommitted, + ReadUncommitted, RepeatableRead, Serializable. + + The TransactionProxyFactoryObject allows you to set optional "pre" + and "post" advice, for additional interception behavior, using the + "PreInterceptors" and "PostInterceptors" properties. Any number of pre + and post advices can be set, and their type may be Advisor (in which + case they can contain a pointcut), MethodInterceptor or any advice type + supported by the current Spring configuration (such as ThrowsAdvice, + AfterReturningAdvice or BeforeAdvice, which are supported by default.) + These advices must support a shared-instance model. If you need + transactional proxying with advanced AOP features such as stateful + mixins, it's normally best to use the generic ProxyFactoryObject, rather + than the TransactionProxyFactoryObject convenience proxy creator. + + + + Concise proxy definitions + + Using abstract object definitions in conjunction with a + TransactionProxyFactoryObject provides you a more concise means to reuse + common configuration information instead of duplicating it over and over + again with a definition of a TransactionProxyFactoryObject per object. + Objects that are to be proxied typically have the same pattern of method + names, Save*, Find*, etc. This commonality can be placed in an abstract + object definition, which other object definitions refer to and change + only the configuration information that is different. An abstract object + definition is shown below + + <object id="txProxyTemplate" abstract="true" + type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> + + <property name="PlatformTransactionManager" ref="adoTransactionManager"/> + + <property name="TransactionAttributes"> + <name-values> + <add key="Save*" value="PROPAGATION_REQUIRED"/> + <add key="Delete*" value="PROPAGATION_REQUIRED"/> + </name-values> + </property> + </object> + + Subsequent definitions can refer to this 'base' configuration as + shown below + + <object id="testObjectManager" parent="txProxyTemplate"> + <property name="Target"> + <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> + <property name="TestObjectDao" ref="testObjectDao"/> + </object> + </property> +</object> + + + + Declarative Transactions using ProxyFactoryObject + + Using the general ProxyFactoryObject to declare transactions gives + you a great deal of control over the proxy created since you can specify + additional advice, such as for logging or performance. Based on the + example shown previously a sample configuration using ProxyFactoryObject + is shown below + + <object id="testObjectManagerTarget" type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> + <property name="TestObjectDao" ref="testObjectDao"/> + </object> + + <object id="testObjectManager" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"> + + <property name="Target" ref="testObjectManagerTarget"/> + <property name="ProxyInterfaces"> + <value>Spring.Data.ITestObjectManager</value> + </property> + <property name="InterceptorNames"> + <value>transactionInterceptor</value> + </property> + + </object> + + The ProxyFactoryObject will create a proxy for the Target, i.e. a + TestObjectManager instance. An inner object definition could also have + been used such that it would make it impossible to obtain an unproxied + object from the container. The interceptor name refers to the following + definition. + + <object id="transactionInterceptor" type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data"> + + <property name="TransactionManager" ref="adoTransactionManager"/> + + <!-- note do not have converter from string to this property type registered --> + <property name="TransactionAttributeSource" ref="methodMapTransactionAttributeSource"/> + </object> + + <object name="methodMapTransactionAttributeSource" + type="Spring.Transaction.Interceptor.MethodMapTransactionAttributeSource, Spring.Data"> + <property name="MethodMap"> + <dictionary> + <entry key="Spring.Data.TestObjectManager.SaveTwoTestObjects, Spring.Data.Integration.Tests" + value="PROPAGATION_REQUIRED"/> + <entry key="Spring.Data.TestObjectManager.DeleteTwoTestObjects, Spring.Data.Integration.Tests" + value="PROPAGATION_REQUIRED"/> + </dictionary> + </property> + </object> + + The transaction options for each method are specified using a + dictionary containing the class name + method name, assembly as the key + and the value is of the form + + + + <Propagation Behavior>, <Isolation Level>, + <ReadOnly>, -Exception, +Exception + + + + All but the propagation behavior are optional. The + and - are + used in front of the name of an exception. Minus indicates to rollback + if the exception is thrown, the Plus indicates to commit if the + exception is thrown. + +
+ + + Programmatic transaction management + + Spring provides two means of programmatic transaction + management: + + + + Using the TransactionTemplate + + + + Using a + IPlatformTransactionManager + implementation directly + + + + These are located in the Spring.Transaction.Support namespace. If + you are going to use programmatic transaction management, the Spring team + generally recommends the first approach (i.e. Using the + TransactionTemplate) + + + Using the <classname>TransactionTemplate</classname> + + The TransactionTemplate adopts the same approach as other Spring + templates such as AdoTemplate and + HibernateTemplate. It uses a callback approach, + to free application code from having to do the boilerplate acquisition + and release of resources, and results in code that is intention driven, + in that the code that is written focuses solely on what the developer + wants to do. Granted that the using construct of System.Transaction + alleviates much of this. One key difference with the approach taken with + the TransactionTemplate is that a commit is assumed - throwing an + exception triggers a rollback instead of using the TransactionScope API + to commit or rollback. This also allows for the use of rollback rules, + that is a commit can still occur for exceptions of certain types. + As you will immediately see in the examples that follow, using + the TransactionTemplate absolutely couples you to + Spring's transaction infrastructure and APIs. Whether or not + programmatic transaction management is suitable for your development + needs is a decision that you will have to make yourself. + + + Application code that must execute in a transaction context looks + like this. You, as an application developer, will write a + ITransactionCallback implementation (typically expressed as an anonymous + delegate) that will contain all of the code that you need to have + execute in the context of a transaction. You will then pass an instance + of your custom ITransactionCallback to the Execute(..) method exposed on + the TransactionTemplate. Note that the + ITransactionCallback can be used to + return a value: + + public class SimpleService : IService +{ + private TransactionTemplate transactionTemplate; + + public SimpleService(IPlatformTransactionManager transactionManager) + { + AssertUtils.ArgumentNotNull(transactionManager, "transactionManager"); + transactionTemplate = new TransactionTemplate(transactionManager); + } + + public object SomeServiceMethod() + { + return tt.Execute(delegate { + UpdateOperation(userId); + return ResultOfUpdateOperation2(); + }); + } +} + + + This code example is specific to .NET 2.0 since it uses anonymous + delegates, which provides a particularly elegant means to invoke a + callback function as local variables can be referred to inside the + delegate, i.e. userId. In this case the + ITransactionStatus was not exposed in the + delegate (delegate can infer the signature to use), but one could also + obtain a reference to the + ITransactionStatus instance and set the + RollbackOnly property to trigger a rollback - or + alternatively throw an exception. This is shown below + + tt.Execute(delegate(ITransactionStatus status) + { + try { + UpdateOperation1(); + UpdateOperation2(); + } catch (SomeBusinessException ex) { + status.RollbackOnly = true; + } + return null; + }); + + If you are using .NET 1.1 then you should provide a normal + delegate reference or an instance of a class that implements the + ITransactionCallback interface. This is + shown below + + tt.Execute(new TransactionRollbackTxCallback(amount)); + + + public class TransactionRollbackTxCallback : ITransactionCallback + { + private decimal amount; + + public TransactionRollbackTxCallback(decimal amount) + { + this.amount = amount + } + + public object DoInTransaction(ITransactionStatus status) + { + adoTemplate.ExecuteNonQuery(CommandType.Text, "insert into dbo.Debits (DebitAmount) VALUES (@amount)", "amount", DbType.Decimal, 0,555); + // decide you need to rollback... + status.RollbackOnly = true; + return null; + } + } + + Application classes wishing to use the + TransactionTemplate must have access to a + IPlatformTransactionManager (which will + typically be supplied to the class via dependency injection). It is easy + to unit test such classes with a mock or stub + IPlatformTransactionManager. + + + Specifying transaction settings + + Transaction settings such as the propagation mode, the isolation + level, the timeout, and so forth can be set on the + TransactionTemplate either programmatically or + in configuration. TransactionTemplate instances + by default have the default transactional settings. Find below an + example of programmatically customizing the transactional settings for + a specific TransactionTemplate. + + public class SimpleService : IService +{ + private TransactionTemplate transactionTemplate; + + public SimpleService(IPlatformTransactionManager transactionManager) + { + AssertUtils.ArgumentNotNull(transactionManager, "transactionManager"); + transactionTemplate = new TransactionTemplate(transactionManager); + + // the transaction settings can be set here explicitly if so desired + + transactionTemplate.TransactionIsolationLevel = IsolationLevel.ReadUncommitted; + transactionTemplate.TransactionTimeout = 30; + + // and so forth... + } + + . . . + +} + + + + Find below an example of defining a + TransactionTemplate with some custom + transactional settings, using Spring XML configuration. The + 'sharedTransactionTemplate' can then be injected + into as many services as are required. + + <object id="sharedTransactionTemplate" + type="Spring.Transaction.Support.TransactionTemplate, Sprng.Data"> + <property name="TransactionIsolationLevel" value="IsolationLevel.ReadUncommitted"/> + <property name="TransactionTimeout" value="30"/> +</object> + + Finally, instances of the + TransactionTemplate class are threadsafe, in + that instances do not maintain any conversational state. + TransactionTemplate instances do however + maintain configuration state, so while a number of classes may choose + to share a single instance of a + TransactionTemplate, if a class needed to use a + TransactionTemplate with different settings + (for example, a different isolation level), then two distinct + TransactionTemplate instances would need to be + created and used. + + + + + Using the PlatformTransactionManager + + You can also use the PlatformTransactionManager directly to manage + your transaction. Simply pass the implementation of the + PlatformTransactionManager you're using to your object via a object + reference through standard Dependency Injection techniques. Then, using + the TransactionDefinition and ITransactionStatus objects, you can + initiate transactions, rollback and commit. + + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); +def.PropagationBehavior = TransactionPropagation.Required; + +ITransactionStatus status = transactionManager.GetTransaction(def); + +try +{ + // execute your business logic here +} catch (Exception e) +{ + transactionManager.Rollback(status); + throw; +} +transactionManager.Commit(status); + + Note that a corresponding 'using TransactionManagerScope' class + can be modeled to get similar API usage to System.Transactions + TransactionScope. + + + + + Choosing between programmatic and declarative transaction + management + + Programmatic transaction management is usually a good idea only if + you have a small number of transactional operations. For example, if you + have a web application that require transactions only for certain update + operations, you may not want to set up transactional proxies using Spring + or any other technology. In this case, using the TransactionTemplate may + be a good approach. On the other hand, if your application has numerous + transactional operations, declarative transaction management is usually + worthwhile. It keeps transaction management out of business logic, and is + not difficult to configure in Spring. + + + + Transaction lifecycle and status information + + You can query the status of the current Spring managed transaction + with the class TransactionSynchronizationManager. + Typical application code should not need to rely on using this class but + in some cases it is convenient to receive events around the lifecycle of + the transaction, i.e. before committing, after committing. + TransactionSynchronizationManager provides a method + to register a callback object that is informed on all significant stages + in the transaction lifecycle. Note that you can register for lifecycle + call back information for any of the transaction managers you use, be it + NHibernate or local ADO.NET transactions. + + The method to register a callback with the + TransactionSynchronizationManager is + + public static void RegisterSynchronization( ITransactionSynchronization synchronization ) + + Please refer to the SDK docs for information on other methods in + this class. + + The ITransactionSynchronization interface + is + + public interface ITransactionSynchronization +{ + + // Typically used by Spring resource management code + void Suspend(); + void Resume(); + + // Transaction lifeycyle callback methods + // Typically used by Spring resource management code but maybe useful in certain cases to application code + void BeforeCommit( bool readOnly ); + void AfterCommit(); + void BeforeCompletion(); + void AfterCompletion( TransactionSynchronizationStatus status ); +} + + The TransactionSynchronizationStatus is an + enum with the values Committed, Rolledback, and Unknown. + +
\ No newline at end of file diff --git a/doc/reference/src/tx-quickstart.xml b/doc/reference/src/tx-quickstart.xml new file mode 100644 index 00000000..c2955d5b --- /dev/null +++ b/doc/reference/src/tx-quickstart.xml @@ -0,0 +1,553 @@ + + + Transactions QuickStart + +
+ Introduction + + The Transaction Quickstart demonstrates Spring's transaction + management features. The database schema are two simple tables, credit and + debit, which contain an Identifier and an Amount. The quick start shows + the use of declarative transactions using attributes and also the ability + to change the transaction manager (local or distributed) via changes to + only the configuration files - no code changes are required. It also + demonstrates some techniques for unit and integration testing an + application as well as separating Spring's configuration files so that one + is responsible for describing how the core business classes are configured + and others that are responsible for the database environment and + application of AOP. + + This quickstart assumes you have installed a way to run NUnit tests + within your IDE. Some excellent tools that let you do this are TestDriven.NET and ReSharper. +
+ +
+ Application Overview + + The design of the application is very simple and consists of two + logical layers, a business service layer in the namespace + Spring.TxQuickStart.Services and a DAO layer in the + namespace Spring.TxQuickStart.Dao. As this is just a + toy example the business service layer does nothing more than call two DAO + objects. The business service is to transfer money in a bank account and + is blatantly taken from the book Pro + ADO.NET by Sahil Malik. The transfer service is defined by the + interface IAccountManager with the + implementation AccountManager located in the + namespace Spring.TxQuickStart.Services. The money + is recorded in a credit and debit table in the database. The SQL Server + schema for the tables is located in the file CreditsDebitsSchema.sql. + Transferring the money requires an ACID operation on these two tables. The + credit operation is defined via a + IAccountCreditDao interface and the debit + operation via an IAccountDebitDao + interface. Implementations of these interfaces using + AdoTemplate are in the namespace + Spring.TxQuickStart.Dao.Ado. + +
+ Interfaces + + The Manager and DAO interfaces are shown below + + public interface IAccountManager + { + void DoTransfer(float creditAmount, float debitAmount); + } + + + public interface IAccountCreditDao + { + void CreateCredit(float creditAmount); + } + + public interface IAccountDebitDao + { + void DebitAccount(float debitAmount); + } +
+
+ +
+ Implementation + + The implementation of the Account Credit DAO is shown below + + public class AccountCreditDao : AdoDaoSupport, IAccountCreditDao + { + public void CreateCredit(float creditAmount) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + "insert into Credits (CreditAmount) VALUES (@amount)", "amount", DbType.Decimal, 0, + creditAmount); + } + } + + and for the Debit DAO + + public class AccountDebitDao : AdoDaoSupport, IAccountDebitDao + { + public void DebitAccount(float debitAmount) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + "insert into dbo.Debits (DebitAmount) VALUES (@amount)", "amount", DbType.Decimal, 0, + debitAmount); + } + } + + Both of these DAO implementations inherit from Spring's + AdoDaoSupport class that provides convenient access + to an AdoTemplate for performing data access + operations. With no other properties that can be configured in these + implementations, the only configuration required is setting of + AdoDaoSupport's DbProvider property representing + the connection to the database. + + The implementation of the service layer interface, + IAccountManager, is shown below. + + public class AccountManager : IAccountManager + { + + private IAccountCreditDao accountCreditDao; + private IAccountDebitDao accountDebitDao; + + private float maxTransferAmount = 1000000; + + public AccountManager(IAccountCreditDao accountCreditDao, IAccountDebitDao accountDebitDao) + { + this.accountCreditDao = accountCreditDao; + this.accountDebitDao = accountDebitDao; + } + + public float MaxTransferAmount + { + get { return maxTransferAmount; } + set { maxTransferAmount = value; } + } + + + [Transaction] + public void DoTransfer(float creditAmount, float debitAmount) + { + accountCreditDao.CreateCredit(creditAmount); + + if (creditAmount > maxTransferAmount || debitAmount > maxTransferAmount) + { + throw new ArithmeticException("see a teller big spender..."); + } + + accountDebitDao.DebitAccount(debitAmount); + } + + } + + The if statement is a poor-mans representation of business logic, + namely that there is a policy that does not allow the use of this service + for amounts larger than $1,000,000. If the credit or debit amount is + larger than 1,000,000 then and exception will be thrown. We can write a + unit test that will test for this business logic and provide stub + implementations of the DAO objects so that our tests are not only + independent of the database but will also execute very quickly. + Notice the Transaction attribute on the + DoTransfer method. This attribute can be read by + Spring and used to create a transactional proxy to AccountManager in + order to perform declarative transaction management. + + + The NUnit unit test for AccountManager is shown below + + public class AccountManagerUnitTests + { + private IAccountManager accountManager; + + [SetUp] + public void Setup() + { + IAccountCreditDao stubCreditDao = new StubAccountCreditDao(); + IAccountDebitDao stubDebitDao = new StubAccountDebitDao(); + accountManager = new AccountManager(stubCreditDao, stubDebitDao); + } + + [Test] + public void TransferBelowMaxAmount() + { + accountManager.DoTransfer(217, 217); + } + + [Test] + [ExpectedException(typeof(ArithmeticException))] + public void TransferAboveMaxAmount() + { + accountManager.DoTransfer(2000000, 200000); + } + } + + Running these tests we exercise both code pathways through the + method DoTransfer. Nothing we have done so far is + Spring specific (aside from the presence of the [Transaction] attribute. + Now that we know the class works in isolation, we can now 'wire' up the + application for use in production by specifying how the service and DAO + layers are related. This configuration file is shown below and can loosely + be referred to as your 'application blueprint'. This configuration file is + named application-config.xml and is an embedded resource inside the 'main' + project, Spring.TxQuickStart. + + <objects xmlns='http://www.springframework.net'> + + <!-- DAO Implementations --> + <object id="accountCreditDao" type="Spring.TxQuickStart.Dao.Ado.AccountCreditDao, Spring.TxQuickStart"> + <property name="DbProvider" ref="CreditDbProvider"/> + </object> + + <object id="accountDebitDao" type="Spring.TxQuickStart.Dao.Ado.AccountDebitDao, Spring.TxQuickStart"> + <property name="DbProvider" ref="DebitDbProvider"/> + </object> + + + <!-- The service that performs multiple data access operations --> + <object id="accountManager" + type="Spring.TxQuickStart.Services.AccountManager, Spring.TxQuickStart"> + <constructor-arg name="accountCreditDao" ref="accountCreditDao"/> + <constructor-arg name="accountDebitDao" ref="accountDebitDao"/> + </object> + +</objects> + + This configuration is selecting the real ADO.NET implementations + that will insert records into the database. We can now write a NUnit + integration test that will test the service and DAO layers. To do this we + add on configuration information specific to our test environment. This + extra configuration information will determine what databases we speak to + and what transaction manager (local or distribute) to use. The code for + this integration style NUnit test is shown below + + [TestFixture] + public class AccountManagerTests + { + private AdoTemplate adoTemplateCredit; + private AdoTemplate adoTemplateDebit; + + private IAccountManager accountManager; + + [SetUp] + public void SetUp() + { + // Configure Spring programmatically + NamespaceParserRegistry.RegisterParser(typeof(DatabaseNamespaceParser)); + NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser)); + NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser)); + IApplicationContext context = new XmlApplicationContext( + "assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/system-test-local-config.xml" + ); + accountManager = context["accountManager"] as IAccountManager; + CleanDb(context); + } + + [Test] + public void TransferBelowMaxAmount() + { + accountManager.DoTransfer(217, 217); + + int numCreditRecords = (int)adoTemplateCredit.ExecuteScalar(CommandType.Text, "select count(*) from Credits"); + int numDebitRecords = (int)adoTemplateDebit.ExecuteScalar(CommandType.Text, "select count(*) from Debits"); + Assert.AreEqual(1, numCreditRecords); + Assert.AreEqual(1, numDebitRecords); + } + + [Test] + [ExpectedException(typeof(ArithmeticException))] + public void TransferAboveMaxAmount() + { + accountManager.DoTransfer(2000000, 200000); + } + + + private void CleanDb(IApplicationContext context) + { + IDbProvider dbProvider = (IDbProvider)context["DebitDbProvider"]; + adoTemplateDebit = new AdoTemplate(dbProvider); + adoTemplateDebit.ExecuteNonQuery(CommandType.Text, "truncate table Debits"); + + dbProvider = (IDbProvider)context["CreditDbProvider"]; + adoTemplateCredit = new AdoTemplate(dbProvider); + adoTemplateCredit.ExecuteNonQuery(CommandType.Text, "truncate table Credits"); + + } + } + + The essential element is to create an instance of Spring's + application context where the relevant layers of the application are + 'wired' together. The IAccountManager + implementation is retrieved from the IoC container and stored as a field + of the test class. The basic logic of the test is the same as in the unit + test but in addition there is the verification of actions performed in the + database. The set up method puts the database tables into a known state + before running the tests. Other techniques for performing integration + testing that can alleviate the need to do extensive database state + management for integration tests is described in the testing section. +
+ +
+ Configuration + + The configuration file system-test-local-config.xml shown in the + previous program listing includes application-config.xml and specifies the + database to use and the local (not distributed) transaction manager + AdoPlatformTransactionManager. This configuration file is shown + below + + <objects xmlns="http://www.springframework.net" + xmlns:db="http://www.springframework.net/database" + xmlns:tx="http://www.springframework.net/tx"> + + + <!-- Imports application configuration --> + <import resource="assembly://Spring.TxQuickStart/Spring.TxQuickStart/application-config.xml"/> + + <!-- Imports additional aspects --> + <!-- + <import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/> + --> + + + <!-- Database Providers --> + + <db:provider id="DebitDbProvider" + provider="System.Data.SqlClient" + connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"/> + + <db:provider id="CreditDbProvider" + provider="System.Data.SqlClient" + connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"/> + + <alias name="DebitDbProvider" alias="CreditDbProvider"/> + + <!-- Transaction Manager if using a single database that contain both credit and debit tables --> + <object id="transactionManager" + type="Spring.Data.Core.AdoPlatformTransactionManager, Spring.Data"> + <property name="DbProvider" ref="DebitDbProvider"/> + </object> + + <!-- Transaction aspect --> + + <tx:attribute-driven/> + +</objects> + + Moving from top to bottom in the configuration file, the + 'application-blueprint' configuration file is included. Then the database + type and connection parameters are specified for the two databases. The + names of these providers must match those specific in + application-config.xml. Since the two names point to the same database, an + alias configuration element is used to have them point to the same + dbProvider under different names. The type of transaction manager is then + selected, in this case we are showing the use of local transactions with + AdoPlatformTransactionManager. Running the tests will result in 217 being + entered into the Credits and Debits table of each database. You can fire + up SQL Server Management Studio or equivalent to verify this. + + To switch to a distributed transaction you can refer to the + configuration file system-test-dtc-config.xml, which is shown below + + objects xmlns='http://www.springframework.net' + xmlns:db="http://www.springframework.net/database" + xmlns:tx="http://www.springframework.net/tx"> + + + <!-- Imports application configuration --> + <import resource="assembly://Spring.TxQuickStart/Spring.TxQuickStart/application-config.xml"/> + + <!-- Imports additional aspects --> + <!-- + <import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/> + --> + + <db:provider id="DebitDbProvider" + provider="System.Data.SqlClient" + connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=Debits;User ID=springqa; Password=springqa"/> + + + <db:provider id="CreditDbProvider" + provider="System.Data.SqlClient" + connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=Credits;User ID=springqa; Password=springqa"/> + + + <!-- Transaction Manager if using two databases, one containing the credit table and the other a debit table --> + + <object id="transactionManager" + type="Spring.Data.Core.TxScopeTransactionManager, Spring.Data"> + </object> + + + <!-- Transaction aspect --> + <tx:attribute-driven/> + +</objects> + + TxScopeTransactionManager uses .NET 2.0 System.Transactions as the + implementation, allowing for distributed transactions between the two + different databases listed. In a larger application the different layers + would typically be broken up into individual configuration files and + imported into the main configuration file. This allows your configuration + to mirror your architecture. + + You can also use the configuration file + system-test-dtc-es-config.xml that will use EnterpriseServices to perform + transaction management. + +
+ Rollback Rules + + Using Rollback rules allows you to specify which exceptions will + not cause a rollback and instead only stop execution flow, committing + the work done up to the exception. An alternative implementation of + AccountManager's DoTransfer method (included in the sample code) is + shown below. + + [Transaction(NoRollbackFor = new Type[] { typeof(ArithmeticException) })] + public void DoTransfer(float creditAmount, float debitAmount) + { + accountCreditDao.CreateCredit(creditAmount); + + if (creditAmount > maxTransferAmount || debitAmount > maxTransferAmount) + { + throw new ArithmeticException("see a teller big spender..."); + } + + accountDebitDao.DebitAccount(debitAmount); + } + + All that has changed is the use of the NoRollbackFor property on + the transaction attribute. + + The expected behavior is that the credit table will be updated + even though the exception is thrown. This is due to specifying that + exceptions of the type ArithmethicException should not rollback the + database transaction. Running the test code below verifies that the + exception still propagates out of the method. + + [Test] + public void DeclarativeWithAttributesNoRollbackFor() + { + try + { + accountManager.DoTransfer(2000000, 2000000); + Assert.Fail("Should have thrown Arithmetic Exception"); + } catch (ArithmeticException) { + int numCreditRecords = (int)adoTemplateCredit.ExecuteScalar(CommandType.Text, "select count(*) from Credits"); + int numDebitRecords = (int)adoTemplateDebit.ExecuteScalar(CommandType.Text, "select count(*) from Debits"); + Assert.AreEqual(1, numCreditRecords); + Assert.AreEqual(0, numDebitRecords); + } + } +
+
+ +
+ Adding additional Aspects + + Transactional advice is just one type of advice that can be applied + to the service layer. You can also configure other pieces of advice to be + executed as part of the general advice chain that is associated with + methods that have the Transaction attribute applied. In this example we + will add logging of thrown exceptions using Spring's + ExceptionHandlerAdvice as well as logging of the service layer method + invocation. No code is required to be changed in order to have this + additional functionality. Instead all you have to do is uncomment the + line + + <import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/> + + in either system-test-dtc-config.xml or system-test-local-config.xml + The aspect configuration file is shown below + + <objects xmlns='http://www.springframework.net' + xmlns:aop="http://www.springframework.net/aop"> + + + + <object name="exceptionAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop"> + <property name="exceptionHandlers"> + <list> + <value>on exception name ArithmeticException log 'Logging an exception thrown from method ' + #method.Name </value> + </list> + </property> + </object> + + <object name="loggingAdvice" type="Spring.Aspects.Logging.SimpleLoggingAdvice, Spring.Aop"> + <property name="logUniqueIdentifier" value="true"/> + <property name="logExecutionTime" value="true"/> + <property name="logMethodArguments" value="true"/> + <property name="Separator" value=";"/> + + <property name="HideProxyTypeNames" value="true"/> + <property name="UseDynamicLogger" value="true"/> + + <property name="LogLevel" value="Info"/> + </object> + + + <object id="txAttributePointcut" type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop"> + <property name="Attribute" value="Spring.Transaction.Interceptor.TransactionAttribute, Spring.Data"/> + </object> + + <aop:config> + + <aop:advisor id="exceptionProcessAdvisor" order="1" + advice-ref="exceptionAdvice" + pointcut-ref="txAttributePointcut"/> + + <aop:advisor id="loggingAdvisor" order="2" + advice-ref="loggingAdvice" + pointcut-ref="txAttributePointcut"/> + + </aop:config> + +</objects> + + The transaction aspect is now additionally configured with an order + value of "10", which will place it after the execution of the exception + aspect, which is configured to use an order value of 1. The behavior for + logging the exception is specified by creating and configuring an instance + of + Spring.Aspects.Exceptions.ExceptionHandlerAdvice. + The location where that behavior is applied, the pointcut, is the + Transaction attribute. The logging of method arguments and execution time + is specified by configuring an instance of + Spring.Aspects.Logging.SimpleLoggingAdvice. + + The AOP configuration section on the bottom is what ties together + the behavior and where it will take place in the program flow. Under the + covers the transaction configuration, <tx:attribute-driven/> creates + similar advice and pointcut definitions. Running the test + TransferBelowMaxAmount will then log the following messages + + INFO - Entering DoTransfer;45b6af04-b736-4efa-a489-45462726ddf2;creditAmount=217; debitAmount=217 +INFO - Exiting DoTransfer;45b6af04-b736-4efa-a489-45462726ddf2;1328.125 ms;return= + + + When the test case of the test TransferAboveMaxAmount is run the + following messages are logged + + INFO - Entering DoTransfer;d94bc81b-a4ff-4ca1-9aaa-f2834f262307;creditAmount=2000000; debitAmount=200000 +INFO - Exception thrown in DoTransferDoTransfer;d94bc81b-a4ff-4ca1-9aaa-f2834f262307;1140.625 +System.ArithmeticException: see a teller big spender... + at Spring.TxQuickStart.Services.AccountManager.DoTransfer(Single creditAmount, Single debitAmount) in L:\projects\Spring.Net\examples\Spring\Spring.TxQuickStart\src\Spring\Spring.TxQuickStart\TxQuickStart\Services\AccountManager.cs:line 36 + at Spring.DynamicReflection.Method_DoTransfer_ec48557f22b149958fd2243413136600.Invoke(Object target, Object[] args) + at Spring.Reflection.Dynamic.SafeMethod.Invoke(Object target, Object[] arguments) in l:\projects\Spring.Net\src\Spring\Spring.Core\Reflection\Dynamic\DynamicMethod.cs:line 108 + at Spring.Aop.Framework.DynamicMethodInvocation.InvokeJoinpoint() in l:\projects\Spring.Net\src\Spring\Spring.Aop\Aop\Framework\DynamicMethodInvocation.cs:line 89 + at Spring.Aop.Framework.AbstractMethodInvocation.Proceed() in l:\projects\Spring.Net\src\Spring\Spring.Aop\Aop\Framework\AbstractMethodInvocation.cs:line 257 + at Spring.Transaction.Interceptor.TransactionInterceptor.Invoke(IMethodInvocation invocation) in l:\projects\Spring.Net\src\Spring\Spring.Data\Transaction\Interceptor\TransactionInterceptor.cs:line 80 + at Spring.Aop.Framework.AbstractMethodInvocation.Proceed() in l:\projects\Spring.Net\src\Spring\Spring.Aop\Aop\Framework\AbstractMethodInvocation.cs:line 282 + at Spring.Aspects.Logging.SimpleLoggingAdvice.InvokeUnderLog(IMethodInvocation invocation, ILog log) in l:\projects\Spring.Net\src\Spring\Spring.Aop\Aspects\Logging\SimpleLoggingAdvice.cs:line 185 +TRACE - Logging an exception thrown from method DoTransfer + + + +
+
\ No newline at end of file diff --git a/doc/reference/src/validation.xml b/doc/reference/src/validation.xml new file mode 100644 index 00000000..acd957b6 --- /dev/null +++ b/doc/reference/src/validation.xml @@ -0,0 +1,866 @@ + + + Validation Framework + +
+ Introduction + + Data validation is a very important part of any enterprise + application. ASP.NET has a validation framework but it is very limited in + scope and starts falling apart as soon as you need to perform more complex + validations. Problems with the out of the box ASP.NET validation framework + are well documented by + Peter Blum on his web site, so we are not going to repeat them here. Peter + has also built a nice replacement for the standard ASP.NET validation + framework, which is worth looking into if you prefer the standard ASP.NET + validation mechanism to the one offered by Spring.NET for some reason. + Both frameworks will allow you to perform very complex validations but we + designed the Spring.NET validation framework differently for the reasons + described below. + + On the Windows Forms side the situation is even worse. Out of the + box data validation features are completely inadequate as pointed out by + Ian Griffiths in this article. + One of the major problems we saw in most validation frameworks available + today, both open source and commercial, is that they are tied to a + specific presentation technology. The ASP.NET validation framework uses + ASP.NET controls to define validation rules, so these rules end up in the + HTML markup of your pages. Peter Blum's framework uses the same approach. + In our opinion, validation is not applicable only to the presentation + layer so there is no reason to tie it to any particular technology. As + such, the Spring.NET Validation Framework is designed in a way that + enables data validation in different application layers using the same + validation rules. + + The goals of the validation framework are the following: + + + + Allow for the validation of any object, whether it is a UI + control or a domain object. + + + + Allow the same validation framework to be used in both Windows + Forms and ASP.NET applications, as well as in the service layer (to + validate parameters passed to the service, for example). + + + + Allow composition of the validation rules so arbitrarily complex + validation rule sets can be constructed. + + + + Allow validators to be conditional so they only execute if a + specific condition is met. + + + + The following sections will describe in more detail how these goals + were achieved and show you how to use the Spring.NET Validation Framework + in your applications. +
+ +
+ Example Usage + + Decoupling validation from presentation was the major goal that + significantly influenced design of the validation framework. We wanted to + be able to define a set of validation rules that are completely + independent from the presentation so we can reuse them (or at least have + the ability to reuse them) in different application layers. This meant + that the approach taken by Microsoft ASP.NET team would not work and + custom validation controls were not an option. The approach taken was to + configure validation rules just like any other object managed by Spring - + within the application context. However, due to possible complexity of the + validation rules we decided not to use the standard Spring.NET + configuration schema for validator definitions but to instead provide a + more specific and easier to use custom configuration schema for + validation. Note that the validation framework is not tied to the use of + XML, you can use its API Programatically. The following example shows + validation rules defined for the Trip object in the SpringAir sample + application: + + <objects xmlns="http://www.springframework.net" xmlns:v="http://www.springframework.net/validation"> + + <object type="TripForm.aspx" parent="standardPage"> + <property name="TripValidator" ref="tripValidator" /> + </object> + + <v:group id="tripValidator"> + + <v:required id="departureAirportValidator" test="StartingFrom.AirportCode"> + <v:message id="error.departureAirport.required" providers="departureAirportErrors, validationSummary"/> + </v:required> + + <v:group id="destinationAirportValidator"> + <v:required test="ReturningFrom.AirportCode"> + <v:message id="error.destinationAirport.required" providers="destinationAirportErrors, validationSummary"/> + </v:required> + <v:condition test="ReturningFrom.AirportCode != StartingFrom.AirportCode" when="ReturningFrom.AirportCode != ''"> + <v:message id="error.destinationAirport.sameAsDeparture" providers="destinationAirportErrors, validationSummary"/> + </v:condition> + </v:group> + + <v:group id="departureDateValidator"> + <v:required test="StartingFrom.Date"> + <v:message id="error.departureDate.required" providers="departureDateErrors, validationSummary"/> + </v:required> + <v:condition test="StartingFrom.Date >= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue"> + <v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/> + </v:condition> + </v:group> + + <v:group id="returnDateValidator" when="Mode == 'RoundTrip'"> + <v:required test="ReturningFrom.Date"> + <v:message id="error.returnDate.required" providers="returnDateErrors, validationSummary"/> + </v:required> + <v:condition test="ReturningFrom.Date >= StartingFrom.Date" when="ReturningFrom.Date != DateTime.MinValue"> + <v:message id="error.returnDate.beforeDeparture" providers="returnDateErrors, validationSummary"/> + </v:condition> + </v:group> + + </v:group> + +</objects>There are a few things to note in the example + above: + + You need to reference the validation schema by adding a + xmlns:v="http://www.springframework.net/validation" + namespace declaration to the root element. + + + + You can mix standard object definitions and validator + definitions in the same configuration file as long as both schemas + are referenced. + + + + The Validator defined in the configuration file is identified + by and id attribute and can be referenced in the standard Spring + way, i.e. the injection of tripValidator into TripForm.aspx page + definition in the first <object> tag above. + + + + The validation framework uses Spring's powerful expression + evaluation engine to evaluate both validation rules and + applicability conditions for the validator. As such, any valid + Spring expression can be specified within the test and when + attributes of any validator. + + + + The example above shows many of the features of the framework, so + let's discuss them one by one in the following sections. +
+ +
+ Validator Groups + + Validators can be grouped together. This is important for many + reasons but the most typical usage scenario is to group multiple + validation rules that apply to the same value. In the example above there + is a validator group for almost every property of the Trip instance. There + is also a top-level group for the Trip object itself that groups all other + validators. + + There are three types of validator groups each with a different + behavior: + + While the first type (AND) is definitely the most useful, the other + two allow you to implement some specific validation scenarios in a very + simple way, so you should keep them in mind when designing your validation + rules. + + + Validator Groups + + + + + + + + + + + Type + + XML Tag + + Behavior + + + + + + AND + + group + + Returns true only + if all contained validators return true. This is the most + commonly used validator group. + + + + OR + + any + + Returns true if one or more of + the contained validators return true. + + + + XOR + + exclusive + + Returns true if only one of the contained validators return + true. + + + +
+ + One thing to remember is that a validator group is a validator like + any other and can be used anywhere validator is expected. You can nest + groups within other groups and reference them using validator reference + syntax (described later), so they really allow you to structure your + validation rules in the most reusable way. +
+ +
+ Validators + + Ultimately, you will have one or more validator definitions for each + piece of data that you want to validate. Spring.NET has several built-in + validators that are sufficient for most validations, even fairly complex + ones. The framework is extensible so you can write your own custom + validators and use them in the same way as the built-in ones. + +
+ Condition Validator + + The condition validator evaluates any logical expression that is + supported by Spring's evaluation engine. The syntax is + + <v:condition id="id" test="testCondition" when="applicabilityCondition" parent="parentValidator"> + actions +</v:condition> + + An example is shown below + + <v:condition test="StartingFrom.Date >= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue"> + <v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/> +</v:condition> + + In this example the StartingFrom property of the Trip object is + compared to see if it is later than the current date, i.e. DateTime but + only when the date has been set (the initial value of StartingFrom.Date + was set to DateTime.MinValue). + + The condition validator could be considered "the mother of all + validators". You can use it to achieve almost anything that can be + achieved by using other validator types, but in some cases the test + expression might be very complex, which is why you should use more + specific validator type if possible. However, condition validator is + still your best bet if you need to check whether particular value + belongs to a particular range, or perform a similar test, as those + conditions are fairly easy to write. + + + Keep in mind that Spring.NET Validation Framework typically + works with domain objects. This is after data binding from the + controls has been performed so that the object being validated is + strongly typed. This means that you can easily compare numbers and + dates without having to worry if the string representation is + comparable. + +
+ +
+ Required Validator + + This validator ensures that the specified test value is not empty. + The syntax is + + <v:required id="id" test="requiredValue" when="applicabilityCondition" parent="parentValidator"> + actions +</v:required> + + An example is shown below + + <v:required test="ReturningFrom.AirportCode"> + <v:message id="error.destinationAirport.required" providers="destinationAirportErrors, validationSummary"/> +</v:required> + + The specific tests done to determine if the required value is set + is listed below + + + Rules to determine if required value is valid + + + + + + + System.Type + + Test + + + + + + System.Type + + Type exists + + + + System.String + + not null or an empty string + + + +
+ + + + Required validator is also one of the most commonly used ones, and + it is much more powerful than the ASP.NET Required validator, because it + works with many other data types other than strings. For example, it + will allow you to validate DateTime instances (both + MinValue and MaxValue return + false), integer and decimal numbers, as well as any + reference type, in which case it returns true for a + non-null value and false for + {{null}}s. + + The test attribute for the required validator will typically + specify an expression that resolves to a property of a domain object, + but it could be any valid expression that returns a value, including a + method call. +
+ +
+ Regular Expression Validator + + The syntax is + + <v:regex id="id" test="valueToEvaluate" when="applicabilityCondition" parent="parentValidator"> + <v:property name="Expression" value="regularExpressionToMatch"/> + <v:property name="Options" value="regexOptions"/> + actions +</v:regex> + + An example is shown below + + <v:regex test="ReturningFrom.AirportCode"> + <v:property name="Expression" value="[A-Z][A-Z][A-Z]"/> + <v:message id="error.destinationAirport.threeCharacters" providers="destinationAirportErrors, validationSummary"/> +</v:regex> + + Regular expression validator is very useful when validating values + that need to conform to some predefined format, such as telephone + numbers, email addresses, URLs, etc. + + One major difference of the regular expression validator compared + to other built-in validator types is that you need to set a required + Expression property to a regular expression to match + against. +
+ +
+ Generic Validator + + The syntax is + + <v:validator id="id" test="requiredValue" when="applicabilityCondition" type="validatorType" parent="parentValidator"> + actions +</v:validator> + + An example is shown below + + <v:validator test="ReturningFrom.AirportCode" type="MyNamespace.MyAirportCodeValidator, MyAssembly"> + <v:message id="error.destinationAirport.invalid" providers="destinationAirportErrors, validationSummary"/> +</v:required> + + Generic validator allows you to plug in your custom validator by + specifying its type name. Custom validators are very simple to + implement, because all you need to do is extend + BaseValidator class and implement abstract + bool Validate(object objectToValidate) method. Your + implementation simply needs to return true if it + determines that object is valid, or false + otherwise +
+ +
+ Conditional Validator Execution + + As you can see from the examples above, each validator (and + validator group) allows you to define its applicability condition by + specifying a logical expression as the value of the when attribute. This + feature is very useful and is one of the major deficiencies in the + standard ASP.NET validation framework, because in many cases specific + validators need to be turned on or off based on the values of the object + being validated. + + For example, when validating a Trip object we need to validate + return date only if the Trip.Mode property is set to the + TripMode.RoundTrip enum value. In order to achieve that we created + following validator definition: + + <v:group id="returnDateValidator" when="Mode == 'RoundTrip'"> + // nested validators +</v:group> + + Validators within this group will only be evaluated for round + trips. + + + You should also note that you can compare enums using the string + value of the enumeration. You can also use fully qualified enum name, + such as: + + Mode == TripMode.RoundTrip + + However, in this case you need to make sure that alias for the + TripMode enum type is registered using Spring's standard type aliasing + mechanism. + +
+
+ +
+ Validator Actions + + Validation actions are executed every time the containing validator + is executed. They allow you to do anything you want based on the result of + the validation. By far the most common use of the validation action is to + add validation error message to the errors collection, but theoretically + you could do anything you want. Because adding validation error messages + to the errors collection is such a common scenario, Spring.NET validation + schema defines a separate XML tag for this type of validation + action. + +
+ Error Message Action + + The syntax is + + <v:message id="messageId" providers="errorProviderList" when="messageApplicabilityCondition"> + <v:param value="paramExpression"/> +</v:message> + + An example is shown below + + <v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"> + <v:param value="StartingFrom.Date.ToString('D')"/> + <v:param value="DateTime.Today.ToString('D')"/> +</v:message> + + There are several things that you have to be aware of when dealing + with error messages: + + + + id is used to look up the error message in + the appropriate Spring.NET message source. + + + + providers specifies a comma separated list + of "error buckets" particular error message should be added to. + These "buckets" will later be used by the particular presentation + technology in order to display error messages as necessary. + + + + a message can have zero or more parameters. Each parameter is + an expression that will be resolved using current validation context + and the resolved values will be passed as parameters to + IMessageSource.GetMessage method, which will + return the fully resolved message. + + +
+ +
+ Generic Actions + + The syntax is + + <v:action type="actionType" when="actionApplicabilityCondition"> + properties +</v:action> + + An example is shown below + + <v:action type="Spring.Validation.Actions.ExpressionAction, Spring.Core" when="#page != null"> + <v:property name="Valid" value="#page.myPanel.Visible = true"/> + <v:property name="Invalid" value="#page.myPanel.Visible = false"/> +</v:action> + + Generic actions can be used to perform all kinds of validation + actions. In simple cases, such as in the example above where we turn + control's visibility on or off depending on the validation result, you + can use the built-in ExpressionAction class and + simply specify expressions to be evaluated based on the validator + result. + + In other situations you may want to create your own action + implementation, which is fairly simple thing to do – all you need to do + is implement IValidationAction interface: + + public interface IValidationAction +{ + /// <summary> + /// Executes the action. + /// </summary> + /// <param name="isValid">Whether associated validator is valid or not.</param> + /// <param name="validationContext">Validation context.</param> + /// <param name="contextParams">Additional context parameters.</param> + /// <param name="errors">Validation errors container.</param> + void Execute(bool isValid, object validationContext, IDictionary contextParams, ValidationErrors errors); +} +
+
+ +
+ Validator References + + Sometimes it is not possible (or desirable) to nest all the + validation rules within a single top-level validator group. For example, + if you have an object graph where both ObjectA and ObjectB have a + reference to ObjectC, you might want to set up validation rules for + ObjectC only once and reference them from the validation rules for both + ObjectA and ObjectB, instead of duplicating them within both + definitions. + + The syntax is shown below + + <v:ref name="referencedValidatorId" context="validationContextForTheReferencedValidator"/> + + An example is shown below + + <v:group id="objectA.validator"> + <v:ref name="objectC.validator" context="MyObjectC"/> + // other validators for ObjectA +</v:group> + +<v:group id="objectB.validator"> + <v:ref name="objectC.validator" context="ObjectCProperty"/> + // other validators for ObjectB +</v:group> + +<v:group id="objectC.Validator"> + // validators for ObjectC +</v:group> + + It is as simple as that — you define validation rules for ObjectC + separately and reference them from within other validation groups. + Important thing to realize that in most cases you will also want to + "narrow" the context for the referenced validator, typically by specifying + the name of the property that holds referenced object. In the example + above, ObjectA.MyObjectC and ObjectB.ObjectCProperty are both of type + ObjectC, which objectC.validator expects to receive as the validation + context. +
+ +
+ Progammatic usage + + You can also create Validators programmatically using the API. An + example is shown below + + UserInfo userInfo = new UserInfo(); // has Name and Password props + +ValidatorGroup userInfoValidator = new ValidatorGroup(); + +userInfoValidator.Validators + .Add(new RequiredValidator("Name", null)); + +userInfoValidator.Validators + .Add(new RequiredValidator("Password", null)); + +ValidationErrors errors = new ValidationErrors(); +bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); + + + No matter if you create your validators programmatically or + declaratively, you can invoke them in service side code via the 'Validate' + method shown above and then handle error conditions. Spring provides AOP + parameter validation advice as part of ithe aspect library which may also be + useful for performing server-side validation. +
+ +
+ Usage tips within ASP.NET + + Now that you know how to configure validation rules, let's see what + it takes to evaluate those rules within your typical ASP.NET application + and to display error messages. + + The first thing you need to do is inject validators you want to use + into your ASP.NET page, as shown in the example below: + + <objects xmlns="http://www.springframework.net" xmlns:v="http://www.springframework.net/validation"> + + <object type="TripForm.aspx" parent="standardPage"> + <property name="TripValidator" ref="tripValidator" /> + </object> + + <v:group id="tripValidator"> + // our validation rules + </v:group> + +</objects> + + Once that's done, you need to perform validation in one or more of + the page event handlers, which typically looks similar to this: + + public void SearchForFlights(object sender, EventArgs e) +{ + if (Validate(Controller.Trip, tripValidator)) + { + Process.SetView(Controller.SearchForFlights()); + } +} + + + Keep in mind that your ASP.NET page needs to extend + Spring.Web.UI.Page in order for the code above to work. + + + Finally, you need to define where validation errors should be + displayed by adding one or more + <spring:validationError/> and + <spring:validationSummary/> controls to the + ASP.NET form: + + <%@ Page Language="c#" MasterPageFile="~/Web/StandardTemplate.master" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> + +<asp:Content ID="head" ContentPlaceHolderID="head" runat="server"> + + <script language="javascript" type="text/javascript"> + <!-- + function showReturnCalendar(isVisible) + { + document.getElementById('<%= returningOnDate.ClientID %>').style.visibility = isVisible? '': 'hidden'; + document.getElementById('returningOnCalendar').style.visibility = isVisible? '': 'hidden'; + } + --> + </script> + +</asp:Content> + +<asp:Content ID="body" ContentPlaceHolderID="body" runat="server"> + <div style="text-align: center"> + <h4><asp:Label ID="caption" runat="server"></asp:Label></h4> + <spring:ValidationSummary ID="validationSummary" runat="server" /> + <table> + <tr class="formLabel"> + <td>&nbsp;</td> + <td colspan="3"> + <spring:RadioButtonGroup ID="tripMode" runat="server"> + <asp:RadioButton ID="OneWay" onclick="showReturnCalendar(false);" runat="server" /> + <asp:RadioButton ID="RoundTrip" onclick="showReturnCalendar(true);" runat="server" /> + </spring:RadioButtonGroup> + </td> + </tr> + <tr> + <td class="formLabel" align="right"> + <asp:Label ID="leavingFrom" runat="server" /></td> + <td nowrap="nowrap"> + <asp:DropDownList ID="leavingFromAirportCode" AutoCallBack="true" runat="server" /> + <spring:ValidationError id="departureAirportErrors" runat="server" /> + </td> + <td class="formLabel" align="right"> + <asp:Label ID="goingTo" runat="server" /></td> + <td nowrap="nowrap"> + <asp:DropDownList ID="goingToAirportCode" AutoCallBack="true" runat="server" /> + <spring:ValidationError id="destinationAirportErrors" runat="server" /> + </td> + </tr> + <tr> + <td class="formLabel" align="right"> + <asp:Label ID="leavingOn" runat="server" /></td> + <td nowrap="nowrap"> + <spring:Calendar ID="leavingFromDate" runat="server" Width="75px" AllowEditing="true" Skin="system" /> + <spring:ValidationError id="departureDateErrors" runat="server" /> + </td> + <td class="formLabel" align="right"> + <asp:Label ID="returningOn" runat="server" /></td> + <td nowrap="nowrap"> + <div id="returningOnCalendar"> + <spring:Calendar ID="returningOnDate" runat="server" Width="75px" AllowEditing="true" Skin="system" /> + <spring:ValidationError id="returnDateErrors" runat="server" /> + </div> + </td> + </tr> + <tr> + <td class="buttonBar" colspan="4"> + <br/> + <asp:Button ID="findFlights" runat="server"/></td> + </tr> + </table> + </div> + + <script language="javascript" type="text/javascript"> + if (document.getElementById('<%= tripMode.ClientID %>').value == 'OneWay') + showReturnCalendar(false); + else + showReturnCalendar(true); + </script> + +</asp:Content> + +
+ Rendering Validation Errors + + Spring.NET allows you to render validation errors within the page + in several different ways, and if none of them suits your needs you can + implement your own validation errors renderer. Implementations of the + Spring.Web.Validation.IValidationErrorsRenderer that + ship with the framework are: + + + Validation Renderers + + + + + + + + + + + Name + + Class + + Description + + + + + + Block + + Spring.Web.Validation.DivValidationErrorsRenderer + + + Renders validation errors as list items within a + <div> tag. Default renderer for + <spring:validationSummary> + control. + + + + Inline + + Spring.Web.Validation.SpanValidationErrorsRenderer + + + Renders validation errors within a + <span> tag. Default renderer for + <spring:validationError> + control. + + + + Icon + + Spring.Web.Validation.IconValidationErrorsRenderer + + Renders validation errors as error icon, with error + messages displayed in a tooltip. Best option when saving screen + real estate is important. + + + +
+ + These three error renderers should be sufficient for most + applications, but in case you want to display errors in some other way + you can write your own renderer by implementing + Spring.Web.Validation.IValidationErrorsRenderer + interface: + + namespace Spring.Web.Validation +{ + /// <summary> + /// This interface should be implemented by all validation errors renderers. + /// </summary> + /// <remarks> + /// <para> + /// Validation errors renderers are used to decouple rendering behavior from the + /// validation errors controls such as <see cref="ValidationError"/> and + /// <see cref="ValidationSummary"/>. + /// </para> + /// <para> + /// This allows users to change how validation errors are rendered by simply plugging in + /// appropriate renderer implementation into the validation errors controls using + /// Spring.NET dependency injection. + /// </para> + /// </remarks> + public interface IValidationErrorsRenderer + { + /// <summary> + /// Renders validation errors using specified <see cref="HtmlTextWriter"/>. + /// </summary> + /// <param name="page">Web form instance.</param> + /// <param name="writer">An HTML writer to use.</param> + /// <param name="errors">The list of validation errors.</param> + void RenderErrors(Page page, HtmlTextWriter writer, IList errors); + } +} + +
+ Configuring which Error Renderer to use. + + The best part of the errors renderer mechanism is that you can + easily change it across the application by modifying configuration + templates for <spring:validationSummary> and + <spring:validationError> controls: + + <!-- Validation errors renderer configuration --> +<object id="Spring.Web.UI.Controls.ValidationError" abstract="true"> + <property name="Renderer"> + <object type="Spring.Web.Validation.IconValidationErrorsRenderer, Spring.Web"> + <property name="IconSrc" value="validation-error.gif"/> + </object> + </property> +</object> + +<object id="Spring.Web.UI.Controls.ValidationSummary" abstract="true"> + <property name="Renderer"> + <object type="Spring.Web.Validation.DivValidationErrorsRenderer, Spring.Web"> + <property name="CssClass" value="validationError"/> + </object> + </property> +</object> + + It's as simple as that! +
+
+
+
\ No newline at end of file diff --git a/doc/reference/src/vsnet.xml b/doc/reference/src/vsnet.xml new file mode 100644 index 00000000..3d5597d6 --- /dev/null +++ b/doc/reference/src/vsnet.xml @@ -0,0 +1,204 @@ + + + Visual Studio.NET Integration + + + + + XML Editing and Validation + + + + Most of this section is well travelled territory for those familiar + with editing XML files in their favorite XML editor. The XML configuration + data that defines the objects that Spring will manage for you are + validated against the Spring.NET XML Schema at runtime. The location of + the XML configuration data to create an + IApplicationContext can be any of the resource + locations supported by Spring's IResource + abstraction. (See for more + information.) To create an IApplicationContext + using a "standalone" XML configuration file the custom configuration + section in the standard .NET application configuration would read: + + + + <spring> + + <context> + <resource uri="file://objects.xml"/> + </context> + +</spring> + + The VS.NET 2005 XML editor can use the attribute + + xsi:schemaLocation + + as a hint to associate the physical location of a schema file with the XML document being edited. VS.NET 2002/2003 do not recognize the + + xsi:schemaLocation + + element. If you reference the Spring.NET XML schema as shown below, you can get intellisense and validation support while editing a Spring configuration file in VS.NET 2005. In order to get this functionality in VS.NET 2002/2003 you will need to register the schema with VS.NET or include the schema as part of your application project. + + <?xml version="1.0" encoding="UTF-8"?> +<objects xmlns="http://www.springframework.net" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd"> + <object id="..." type="..."> + ... + </object> + <object id="..." type="..."> + ... + </object> + ... +</objects> + + + + It is typically more convenient to install the schema in VS.NET, + even for VS.NET 2005, as it makes the xml a little less verbose and you + don't need to keep copying the XSD file for each project you create. For + VS.NET 2003 the schema directory will be either + + + + C:\Program Files\Microsoft Visual Studio .NET + 2003\Common7\Packages\schemas\xml for VS 2003 + + + + or + + + + C:\Program Files\Microsoft Visual Studio + .NET\Common7\Packages\schemas\xml for VS.NET 2002 + + + + The VS.NET 2005 directory for XML schemas is + + + + + C:\Program Files\Microsoft Visual Studio + 8\Xml\Schemas + + + + + Spring's .xsd schemas are located in the directory doc/schema. In + that directory is also a NAnt build file to help copy over the .xsd files + to the appropriate VS.NET locations. To execute this script simply type + 'nant' in the doc/schema directory. + + + + Once you have registered the schema with VS.NET you can adding only + the namespace declaration to the objects element, + + + + + <?xml version="1.0" encoding="UTF-8"?> +<objects xmlns="http://www.springframework.net"> + <object id="..." type="..."> + ... + </object> + <object id="..." type="..."> + ... + </object> + ... +</objects> + + + + + Once registered, the namespace declaration alone is sufficient to + get intellisense and validation of the configuration file from within + VS.NET. Alternatively, you can select the .xsd file to use by setting the + targetSchema property in the Property Sheet for the configuration + file. + + + + As shown in the section + Spring.NET supports using .NET's application configuration file as the + location to store the object definitions that will be managed by the + object factory. + + + + +<configuration> + + <configSections> + <sectionGroup name="spring"> + <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/> + <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> + </sectionGroup> + </configSections> + + <spring> + + <context> + <resource uri="config://spring/objects"/> + </context> + + <objects xmlns="http://www.springframework.net"> + ... + </objects> + + </spring> + +</configuration> + + + + + In this case VS.NET 2002/2003 will still provide you with + intellisense help but you will not be able to fully validate the document + as the entire schema for App.config is not known. To be able to validate + this document one would need to install the .NET + Configuration File schema and an additional schema that + incorporates the <spring> and + <context> section in addition to the + <objects> would need to be created. + + + + Validating schema is a new feature in VS 2005 it is validating all + the time while you edit, you will see any errors that it finds in the + Error List window. + + + + Keep these trade offs in mind as you decide where to place the bulk + of your configuration information. Conventional wisdom is do quick + prototyping with App.config and use another IResource location, file or + embedded assembly resource, for serious development. + + + + + + Versions of XML Schema + + The schema was updated from Spring 1.0.1 to 1.0.2 in order to + support generics. The schema for version 1.0.1 is located under + http://www.springframework.net/xsd/1.0.1/ The schema + for the latest version will always be located under + http://www.springframework.net/xsd/ + + + + Integrated API help + + Spring provides API documentation that can be integrated within + Visual Studio. There are two versions of the documentation, one for VS.NET + 2002/2003 and the other for VS 2005. They differ only in the format + applied, VS 2005 using the sexy new format. Enjoy! + + \ No newline at end of file diff --git a/doc/reference/src/web-quickstart.xml b/doc/reference/src/web-quickstart.xml new file mode 100644 index 00000000..69ddd3c3 --- /dev/null +++ b/doc/reference/src/web-quickstart.xml @@ -0,0 +1,13 @@ + + + Web Quickstarts + +
+ Introduction + + The Web Quickstart solution provides basic 'Hello World' examples + for using Spring.Web features. You can use this solution as a starting + point and then move on to the SpringAir application that uses a wider + range of Spring.Web features. +
+
\ No newline at end of file diff --git a/doc/reference/src/web.xml b/doc/reference/src/web.xml new file mode 100644 index 00000000..a861b91b --- /dev/null +++ b/doc/reference/src/web.xml @@ -0,0 +1,2474 @@ + + + Spring.NET Web Framework + + + Introduction + + One of the objections many developers have to the ASP.NET + programming model is that it is not a "true MVC" (Model-View-Controller) + implementation, because controller-type logic within the page is too + tightly coupled to the view. A good example of this are event handlers + within the page class, which typically have references to view elements, + such as input controls, all over the place. Without getting into academic + discussion of what "true MVC" is, and whether it is even appropriate to + try to fit form-based technology such as ASP.NET into traditionally + request-based MVC pattern when MVP (Model-View-Presenter) or Presentation + Model might be more appropriate, we'd like to agree with the critics on + the most important point they are making: controller-type logic, such as + the code within page event handlers in ASP.NET, should not depend on the + view elements. + + Having said that, there are good things about + ASP.NET. Server-side forms and controls make developers significantly more + productive and allow us to significantly simplify page markup. They also + make cross-browser issues easier to deal with, as each control can make + sure that it renders correct markup based on the user's browser. The + ability to hook custom logic into the lifecycle of the page, as well as to + customize HTTP processing pipeline are also very powerful features. + Finally, being able to interact with the strongly typed server-side + controls instead of manipulating string-based HTTP request collections, + such as Form and QueryString, is a much needed layer of abstraction in web + development. + + For these reasons, we decided that instead of developing a new, + "pure and true MVC" web framework as part of Spring.NET, we should take a + more pragmatic approach and extend ASP.NET in such a way that most, if not + all of its shortcomings are eliminated. It should be noted that with the + introduction of a 'true MVC framework' being added to .NET, with extension + points for IoC containers such as Spring, Spring will continue to play a + role within a MVC based model once that functionality is available from + Microsoft. It is worth noting that Spring Java has a very popular MVC + framework and much of that experience and added value can be + transliterated to help developers be more productive when using the + upcoming ASP.NET MVC support. + + + + Spring.Web also adds support for applying the dependency injection + principle to one's ASP.NET Pages and + Controls as well as http modules and custom + provider modules. This means that application developers can easily inject + service dependencies into web controllers by leveraging the power of the + Spring.NET IoC container. See Dependency Injection + for ASP.NET Pages for more information. + + As we said earlier, event handlers in code-behind classes really + should not have to deal with ASP.NET UI controls directly. Such event + handlers should rather work with the presentation model of the page, + represented either as a hierarchy of domain objects or an ADO.NET + DataSet. It is for that reason that the Spring.NET + team implemented bidirectional data binding framework to handle the + mapping of values to and from the controls on a page to the underlying + data model. The data binding framework also transparently takes care of + data type conversion and formatting, enabling application developers to + work with fully typed data (domain) objects in the event handlers of + code-behind files. See Bidirectional Data + Binding and Model Management for more information. + + The flow of control through an application is another area of + concern that is addressed by Spring.NET Web Framework. Typical ASP.NET + applications will use Response.Redirect or + Server.Transfer calls within + Page logic to navigate to an appropriate page after + an action is executed. This typically leads to hard-coded target URLs in + the Page, which is never a good thing. Result + mapping solves this problem by allowing application developers to specify + aliases for action results that map to target URLs based on information in + an external configuration file that can easily be edited. Under + consideration for future releases of Spring.NET is a process management + framework, which will take this approach to another level, allowing you to + control complex page flows in a very simple way. See Result Mapping for more + information. + + Standard localization support is also limited in versions of ASP.NET + prior to ASP.NET 2.0. Even though Visual Studio 2003 generates a local + resource file for each ASP.NET Page and user + control, those resources are never used by the ASP.NET infrastructure. + This means that application developers have to deal directly with resource + managers whenever they need access to localized resources, which in the + opinion of the Spring.NET team should not be the case. Spring.NET's Web + Framework (hereafter referred to as Spring.Web) adds comprehensive support + for localization using both local resource files and global resources that + are configured within and for a Spring.NET container. See Localization and Message Sources for + more information. + + In addition to the aforementioned features that can be considered to + be the 'core' features of the Spring.Web framework, + Spring.Web also ships with a number of other lesser features that might be + useful to a large number of application developers. Some of these + additional features include back-ports of ASP.NET 2.0 features that can be + used with ASP.NET 1.1, such as Master Page support. See Master Pages in ASP.NET 1.1 for more + information. + + In order to implement some of the above mentioned features the + Spring.NET team had to extend (as in the object-oriented sense) the + standard ASP.NET Page and + UserControl classes. This means that in order to + take advantage of the full feature stack of + Spring.Web (most notably bidirectional data binding, localization and + result mapping), your code-behind classes will have to extend Spring.Web + specific base classes such as Spring.Web.UI.Page; + however, some very powerful features such as dependency injection for + ASP.NET Pages, Controls, and providers can be leveraged without having to + extend Spring.Web-specific base classes. It is worth stating that by + taking advantage of some of the more useful features + offered by Spring.Web you will be coupling the presentation tier of your + application(s) to Spring.Web. The choice of whether or not this is + appropriate is, of course, left to you. + + Finally, please be aware that the standard Spring.NET distribution + (as of v1.1) ships with a number of Web QuickStarts and a complete + reference application, SpringAir. Web QuickStarts are the best way to + learn each Spring.Web feature by following simple examples, and the + SpringAir reference application has a Spring.Web-enabled frontend which + uses many best practices for Spring.NET web applications, so please do + refer to it as you are reading this (reference) material (see ). + + + + Automatic context loading and hierarchical contexts + + + Configuration + + Unsurprisingly, Spring.Web builds on top of the Spring.NET IoC + container, and makes heavy use (internally) of the easy pluggability and + standardized configuration afforded by the IoC container. This also + means that all of the controllers (ASP.NET Pages) + that make up a typical Spring.Web enabled application will be configured + using the same standard Spring.NET XML configuration syntax. Spring.Web + uses a custom PageHandlerFactory implementation + to load and configure a Spring.NET IoC container, which is in turn used + to locate an appropriate Page to handle a HTTP + request. The WebSupportModule configures + miscellaneous Spring infrastructure classes for use in a web + environment, for example setting the storage strategy of + LogicalThreadContext to be + HybridContextStorage. + + The instantiation and configuration of the Spring.NET IoC + container by the Spring.Web infrastructure is wholly transparent to + application developers, who will typically never have to explicitly + instantiate and configure an IoC container manually (by for example + using the new operator in C#). In order to effect the + transparent bootstrapping of the IoC container, the Spring.Web + infrastructure requires the insertion of the following configuration + snippet into each and every Spring.Web-enabled web application's root + Web.config file (the verb and + path properties can of course be changed from the + values that are shown below): + + <system.web> + <httpHandlers> + <add verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/> + </httpHandlers> + <httpModules> + <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/> + </httpModules> + ... +</system.web> + + + + Please note that this snippet of standard ASP.NET configuration is + only required to be present in the root directory + of each Spring.Web web application (i.e. in the + web.config file present in the top level virtual + directory of an ASP.NET web application). + + The above XML configuration snippet will direct the ASP.NET + infrastructure to use Spring.NET's page factory, which will in turn + create instances of the appropriate .aspx + Page, (possibly) inject dependencies into said + Page (as required), and then forward the handling + of the request to said Page. + + After the Spring.Web page factory is configured, you will also + need to define a root application context by adding a Spring.NET + configuration section to that same web.config file. + The final configuration file should look a little like this (your exact + configuration will no doubt vary in particulars)... + + <?xml version="1.0" encoding="utf-8"?> +<configuration> + + <configSections> + <sectionGroup name="spring"> + <section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/> + </sectionGroup> + </configSections> + + <spring> + <context> + <resource uri="~/Config/CommonObjects.xml"/> + <resource uri="~/Config/CommonPages.xml"/> + + <!-- TEST CONFIGURATION --> + <!-- + <resource uri="~/Config/Test/Services.xml"/> + <resource uri="~/Config/Test/Dao.xml"/> + --> + + <!-- PRODUCTION CONFIGURATION --> + + <resource uri="~/Config/Production/Services.xml"/> + <resource uri="~/Config/Production/Dao.xml"/> + + </context> + </spring> + + <system.web> + <httpHandlers> + <add verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/> + </httpHandlers> + <httpModules> + <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/> + </httpModules> + </system.web> + +</configuration> + + + There are a few important points that need to be noted with regard + to the above configuration: + + + + You must define a custom configuration section handler for the + spring/context element. If you use Spring.NET for + many applications on the same web server, it might be easier to move + the whole definition of the Spring.NET section group to your + machine.config file. + + + + The custom configuration section handler is of the type + Spring.Context.Support.WebContextHandler + which will in turn instantiate an IoC container of the type + Spring.Context.Support.WebApplicationContext. + This will ensure that all of the features provided by Spring.Web are + handled properly (such as request and session-scoped object + definitions). + + + + Within the <spring> element you need to define a root + context, and resource locations that contain the object definitions + that will be used within the web application (such as service or + business tier objects) then need to be specified as child elements + within the <context> element. Object definition resources can + be fully-qualified paths or URLs, or non-qualified, as in the + example above. Non-qualified resources will be loaded using the + default resource type for the context, which for the + WebApplicationContext is the + WebResource type. + + + + Please note that the object definition resources do not all + have to be the same resource type (e.g. all + file://, all http://, all + assembly://, etc). This means that you can load + some object definitions from resources embedded directly within + application assemblies (assembly://), while + continuing to load other object definitions from web resources that + can be more easily edited. + + + + + Configuration for IIS7 + + The configuration for IIS7 is shown below + + <system.webServer> + <validation validateIntegratedModeConfiguration="false"/> + <modules> + <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/> + </modules> + <handlers> + <add name="SpringPageHandler" verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/> + <add name="SpringContextMonitor" verb="*" path="ContextMonitor.ashx" type="Spring.Web.Support.ContextMonitor, Spring.Web"/> + </handlers> +</system.webServer> + + + + + Context Hierarchy + + ASP.NET provides a hierarchical configuration mechanism by + allowing application developers to override configuration settings + specified at a higher level in the web application directory hierarchy + with configuration settings specified at the lower level. + + For example, a web application's root + Web.config file overrides settings from the (lower + level) machine.config file. In the same fashion, + settings specified within the web.config file within + a subdirectory of a web application will override settings from the root + Web.config and so on. Lower level + Web.config files can also add settings of their own + that were not previously defined anywhere. + + Spring.Web leverages this ASP.NET feature to provide support for a + context hierarchy. Your lower level Web.config files + can be used to add new object definitions or to override existing ones + per virtual directory. + + What this means to application developers is that one can easily + componentize an application by creating a virtual directory per + component and creating a custom context for each component that contains + the necessary configuration info for that particular context. The + configuration for a lower level component will generally contain only + those definitions for the pages that the component consists of and + (possibly) overrides for some of the definitions from the root context + (for example, menus). + + Because each such lower level component will usually contain only + a few object definitions, application developers are encouraged to embed + those object definitions directly into the Web.config + for the lower level context instead of relying on an external resource + containing object definitions. This is easily accomplished by creating a + component Web.config similar to the following + one: + + <?xml version="1.0" encoding="utf-8"?> +<configuration> + + <configSections> + <sectionGroup name="spring"> + <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/> + </sectionGroup> + </configSections> + + <spring> + <context type="Spring.Context.Support.WebApplicationContext, Spring.Web"> + <resource uri="config://spring/objects"/> + </context> + + <objects xmlns="http://www.springframework.net"> + <object type="MyPage.aspx" parent="basePage"> + <property name="MyRootService" ref="myServiceDefinedInRootContext"/> + <property name="MyLocalService" ref="myServiceDefinedLocally"/> + <property name="Results"> + <!-- ... --> + </property> + </object> + <object id="myServiceDefinedLocally" type="MyCompany.MyProject.Services.MyServiceImpl, MyAssembly"/> + </objects> + </spring> +</configuration> + + The <context/> element seen above + (contained within the <spring/> element) simply + tells the Spring.NET infrastructure code to load (its) object + definitions from the spring/objects section of the + configuration file. + + You can (and should) avoid the need to specify + <configSections/> element by moving the + configuration handler definition for the + <objects> element to a higher level (root) + Web.config file, or even to the level of the + machine.config file if Spring.NET is to be used for + multiple applications on the same server. + + A very important point to be aware of is that this component-level + context can reference definitions from its parent context(s). Basically, + if a referenced object definition is not found in the current context, + Spring.NET will search all the ancestor contexts in the context + hierarchy until it finds said object definition (or ultimately fails and + throws an exception). + + + + + Dependency Injection for ASP.NET Pages + + Spring.Web builds on top of the feature set and capabilities of + ASP.NET; one example of this can be seen the way that Spring.Web has used + the code-behind class of the Page mechanism to + satisfy the Controller portion of the MVC architectural + pattern. In MVC-based (web) applications, the + Controller is typically a thin wrapper around one or + more service objects. In the specific case of Spring.Web, the Spring.NET + team realized that it was very important that service object dependencies + be easily injected into Page + Controllers. Accordingly, Spring.Web provides first + class support for dependency injection in ASP.NET + Pages. This allows application developers to inject + any required service object dependencies (and indeed any other + dependencies) into their Pages using standard + Spring.NET configuration instead of having to rely on custom service + locators or manual object lookups in a Spring.NET application + context. + + Once an application developer has configured the Spring.NET web + application context, said developer can easily create object definitions + for the pages that compose that web application: + + <objects xmlns="http://www.springframework.net"> + + <object name="basePage" abstract="true"> + <property name="MasterPageFile" value="~/Web/StandardTemplate.master"/> + </object> + + <object type="Login.aspx"> + <property name="Authenticator" ref="authenticationService"/> + </object> + + <object type="Default.aspx" parent="basePage"/> + +</objects> + + This example contains three definitions: + + + + The first definition is an abstract definition for the base page + that many other pages in the application will inherit from. In this + case, the definition simply specifies which page is to be referenced + as the master page, but it will typically also configure + localization-related dependencies and root folders for images, scripts + and CSS stylesheets. + + + + The second definition defines a login page that neither inherits + from the base page nor references the master page. What it does show + is how to inject a service object dependency into a page instance (the + authenticationService is defined elsewhere). + + + + The final definition defines a default application page. In this + case it simply inherits from the base page in order to inherit the + master page dependency, but apart from that it doesn't need any + additional dependency injection configuration. + + + + One thing that slightly differentiates the configuration of ASP.NET + pages from the configuration of other .NET classes is in the value passed + to the type attribute. As can be seen in the above + configuration snippet the type name is actually the + path to the .aspx file for the + Page, relative to the directory context it is + defined in. In the case of the above example, those definitions are in the + root context so Login.aspx and + Default.aspx also must be in the root of the web + application's virtual directory. The master page is defined using an + absolute path because it could conceivably be referenced from child + contexts that are defined within subdirectories of the web + application. + + The astute reader may have noticed that the definitions for the + Login and Default pages don't + specify either of the id and name + attributes. This is in marked contrast to typical object definitions in + Spring.NET, where the id or name + attributes are typically mandatory (although not always, as in the case of + inner object definitions). This is actually intentional, because in the + case of Spring.Web Page + Controller instances one typically wants to use the + name of the .aspx file name as the identifier. If an + id is not specified, the Spring.Web infrastructure will + simply use the name of the .aspx file as the object + identifier (minus any leading path information, and minus the file + extension too). + + Nothing prevents an application developer from specifying an + id or name value explicitly; one use + case when the explicit naming might be useful is when one wants to expose + the same page multiple times using a slightly different configuration (Add + / Edit pages for example). If you would like to use abstract object + definitions and have your page inherit from them, the use of the name + attribute should be used instead of the id attribute on the abstract + object definition. + + + Injecting Dependencies into Controls + + Spring.Web also allows application developers to inject + dependencies into controls (both user controls and standard controls) + that are contained within a page. This can be accomplished globally for + all controls of a particular Type by using the + location of the .ascx as the object type identifier. + This is similar to injecting into .aspx pages shown + above. + + <object type="~/controls/MyControl.ascx" abstract="true"> + <!-- inject dependencies here... --> +</object> + + In either case, be sure to mark the object definition as + abstract (by adding + abstract="true" to the attribute list of the + <object/> element). + + + + Injecting dependencies into custom HTTP modules + + You can perform dependency injection on custom HTTP modules + through the use of the class + Spring.Context.Support.HttpApplicationConfigurer. + You register your custom HTTP module as you would normally, for example + a module of the type HtmlCommentAppenderModule, + taken from the Web Quickstart, appends additional comments into the http + response. It is registered as shown below + + <httpModules> + <add name="HtmlCommentAppender" type="HtmlCommentAppenderModule"/> +</httpModules> + + To configure this module, naming conventions are used to identify + the module name with configuration instructions in the Spring + configuration file. The ModuleTemplates property of + HttpApplicationConfigurer is a dictionary that takes as a key the name + of the HTTP module, HtmlCommentAppender, and as a value the + configuration instructions as you would normally use for configuring an + object with Spring. An example is shown below. + HttpApplicationConfigurer' ModuleTemplates property. + + <object name="HttpApplicationConfigurer" type="Spring.Context.Support.HttpApplicationConfigurer, Spring.Web"> + <property name="ModuleTemplates"> + <dictionary> + <entry key="HtmlCommentAppender"> <!-- this name must match the module name --> + <object> + <!-- select "view source" in your browser on any page to see the appended html comment --> + <property name="AppendText" value="My configured comment!" /> + </object> + </entry> + </dictionary> + </property> +</object> + + You can see this example in action in the Web Quickstart. + + + + Injecting dependencies into custom providers + + Custom providers can be configured with Spring. The approach to + configuration is a family of adapters that correspond 1-to-1 with the + standard ASP.NET providers that are registered using the standard + ASP.NET mechanism. The adapters inherit from their correspondingly named + provider class in the BCL. + + + + MembershipProviderAdapter + + + + ProfileProviderAdapter + + + + RoleProviderAdapter + + + + SiteMapProviderAdapter + + + + Here is an example of how to register the adapter for membership + providers. + + <membership defaultProvider="mySqlMembershipProvider"> + <providers> + <clear/> + <add connectionStringName="" name="mySqlMembershipProvider" type="Spring.Web.Providers.MembershipProviderAdapter, Spring.Web"/> + </providers> + </membership> + + The name of the provider must match the name of the object in the + spring configuration that will serve as the actual provider + implementation. For convenience there are configurable versions of the + providers found in ASP.NET so that you can use the full functionality of + spring to configure these standard provider implementations, for example + using property place holders, etc. These are + + + + ConfigurableActiveDirectoryMembershipProvider + + + + ConfigurableSqlMembershipProvider + + + + ConfigurableSqlProfileProvider + + + + ConfigurableSqlRoleProvider + + + + ConfigurableXmlSiteMapProvider + + + + Here is an example configuration taken from the Web Quickstart + that simply sets the description property and connection string. + + <object id="mySqlMembershipProvider" type="Spring.Web.Providers.ConfigurableSqlMembershipProvider"> + <property name="connectionStringName" value="MyLocalSQLServer" /> + <property name="parameters"> + <name-values> + <add key="description" value="membershipprovider description" /> + </name-values> + </property> + </object> + + Your own custom providers of course will contain additional + configuration specific to your implementation. + + + + Customizing control dependency injection + + There might be situations where it is necessary to customize + Spring.Web's dependency injection processing. In particular when using + GridViews, which create a large number of child controls, dependency + injection can slow down your page. To overcome this problem, you may + tell Spring to handle the dependency injection process yourself by + implementing the interface ISupportsWebDependencyInjection as shown + below: + + [C#] +class MyControl : Control, ISupportsWebDependencyInjection +{ + private IApplicationContext _defaultApplicationContext; + + public IApplicationContext DefaultApplicationContext + { + get { return _defaultApplicationContext; } + set { _defaultApplicationContext = value; } + } + + override protected AddedControl( Control control, int index ) + { + // handle DI for children ourselves - + // defaults to a call to InjectDependenciesRecursive + WebUtils.InjectDependenciesRecursive( _defaultApplicationContext, control ); + base.AddedControl( control, index ); + } +} + + There is a Spring server control, Panel, that provides an easier + way to turn of dependency injection for parts of your page. Example use + is shown below + + <spring:Panel runat="server" + suppressDependencyInjection="true" + renderContainerTag="false"> + + .. put your heavy controls here - they won't be touched by DI + +</spring:Panel> + + By wrapping the performance sensitive parts of your page within + this panel, you can easily turn off DI using the attribute + suppressDependencyInjection. By default <spring:Panel/> won't + render a container tag (<div>, <span>, etc.). You can modify + this behavior by setting the attribute "renderContainerTag" + accordingly. + + + + + Object Scope + + Spring.NET web applications support an additional attribute within + object definition elements that allows you to control the scope of an + object: <object id="myObject" type="MyType, MyAssembly" scope="application | session | request"/>As + you can see, there are three possible values for the scope attribute -- + application, session or request. Application scope is the default, and + will be used for all objects that don't have scope attribute defined. As + its name says, it will result in a single instance of an object being + created for the duration of the application, so it works exactly like the + standard singleton objects in non-web applications. Session scope allows + you to define objects in such a way that an instance is created for each + HttpSession. This is the ideal scope for objects that you want bound to a + single user such as user profile, shopping cart, etc. Request scope will + result in a creation of an instance per HTTP request. + + Unlike with prototype objects, calls to + IApplicationContext.GetObject will return the same + instance of the request-scoped object during a single HTTP request. This + allows you, for example, to inject the same request-scoped object into + multiple pages and then use server-side transfer to move from one page to + another. As all the pages are executed within the single HTTP request in + this case, they will share the same instance of the injected + object. + + One thing to keep in mind is that objects can only reference other + objects that are in the same or broader scope. This means that + application-scoped objects can only reference other application-scoped, + session-scoped objects can reference both session and application-scoped + objects, and finally, request-scoped objects can reference other request, + session or application-scoped objects. Also, prototype objects (and that + includes all ASP.NET web pages defined within Spring.NET context) can + reference singleton objects from any scope, as well as other prototype + objects. + + + + Master Pages in ASP.NET 1.1 + + Support for ASP.NET 1.1 master pages in Spring.Web is very similar + to the support for master pages in ASP.NET 2.0. + + The idea is that a web developer can define a layout template for + the site as a master page and specify content place holders that other + pages can then reference and populate. A sample master page + (MasterLayout.ascx) could look like this: + + <%@ Control language="c#" Codebehind="MasterLayout.ascx.cs" AutoEventWireup="false" Inherits="MyApp.Web.UI.MasterLyout" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > +<html> + <head> + <title>Master Page</title> + <link rel="stylesheet" type="text/css" href="<%= Context.Request.ApplicationPath %>/css/styles.css"> + <spring:ContentPlaceHolder id="head" runat="server"/> + </head> + <body> + <form runat="server"> + <table cellPadding="3" width="100%" border="1"> + <tr> + <td colspan="2"> + <spring:ContentPlaceHolder id="title" runat="server"> + <!-- default title content --> + </spring:ContentPlaceHolder> + </td> + </tr> + <tr> + <td> + <spring:ContentPlaceHolder id="leftSidebar" runat="server"> + <!-- default left side content --> + </spring:ContentPlaceHolder> + </td> + <td> + <spring:ContentPlaceHolder id="main" runat="server"> + <!-- default main area content --> + </spring:ContentPlaceHolder> + </td> + </tr> + </table> + </form> + </body> +</html> + + As you can see from the above code, the master page defines the + overall layout for the page, in addition to four content placeholders that + other pages can override. The master page can also include default content + within the placeholder that will be displayed if a derived page does not + override the placeholder. + + A page (Child.aspx) that uses this master page might look like + this: + + <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> +<%@ Page language="c#" Codebehind="Child.aspx.cs" AutoEventWireup="false" Inherits="ArtFair.Web.UI.Forms.Child" %> +<html> + <body> + + <spring:Content id="leftSidebarContent" contentPlaceholderId="leftSidebar" runat="server"> + <!-- left sidebar content --> + </spring:Content> + + <spring:Content id="mainContent" contentPlaceholderId="main" runat="server"> + <!-- main area content --> + </spring:Content> + + </body> +</html> + + The <spring:Content/> control in the above + example uses the contentPlaceholderId attribute + (property) to specify exactly which placeholder from the master page is to + be overridden. Because this particular page does not define content + elements for the head and title place holders, they will be displayed + using the default content supplied in the master page. + + Both the ContentPlaceHolder and + Content controls can contain any valid ASP.NET + markup: HTML, standard ASP.NET controls, user controls, etc. + + + VS.NET 2003 issue + + Technically, the <html> and + <body> tags from the previous example are not + strictly necessary because they are already defined in the master page. + However, if these tags are omitted, then Visual Studio 2003 will + complain about a schema and intellisense won't work, so it's much easier + to work in the HTML view if those tags are included. They will be + ignored when the page is rendered. + + + + Linking child pages to their master + + The Spring.Web.UI.Page class exposes a + property called MasterPageFile, which can be used to + specify the master page. + + The recommended way to do this is by leveraging the Spring.NET IoC + container and creating definitions similar to the following: + + <?xml version="1.0" encoding="utf-8" ?> +<objects xmlns="http://www.springframework.net"> + + <object name="basePage" abstract="true"> + <property name="MasterPageFile" value="~/MasterLayout.ascx"/> + </object> + + <object type="Child.aspx" parent="basePage"> + <!-- inject other objects that page needs --> + </object> + +</objects> + + This approach allows application developers to change the master + page being used for a number of pages within a web application. Of + course, the master page can still be overridden on a per context or per + page basis by creating a new abstract page definition within a child + context, or by specifying the MasterPageFile property + directly. + + + + + Bidirectional Data Binding and Model Management + + A problem with the existing data binding support in ASP.NET is that + it is one-way only. It allows application developers to bind page controls + to the data model and display information from said data model, but it + doesn't allow for the extraction of values from the controls when the form + is submitted. Spring.Web adds such bidirectional data binding to ASP.NET + by allowing developers to specify data binding rules for their page, and + by automatically evaluating configured data binding rules at the + appropriate time in the page's lifecycle. + + ASP.NET also doesn't provide any support for model management within + the postbacks. Sure, it has a ViewState management, but that takes care of + the control state only and not of the state of any presentation model + objects these controls might be bound to. In order to manage model within + ASP.NET, developers will typically use HTTP Session object to store the + model between the postbacks. This results in a decent amount of + boilerplate code that can and should be eliminated, which is exactly what + Spring.Web does by providing a simple set of model management + methods. + + Please note that in order to take advantage of the bidirectional + data binding and model management support provided by Spring.Web, you + will have to couple your presentation layer to + Spring.Web; this is because features requires you to + extend a Spring.Web.UI.Page instead of the usual + System.Web.UI.Page class. + + Spring.Web data binding is very easy to use. Application developers + simply need to override the protected + InitializeDataBindings method and configure data + binding rules for the page. They also need to override three model + management methods: InitializeModel, + LoadModel and SaveModel. This is + perhaps best illustrated by an example from the SpringAir reference + application. First, let's take a look at the page markup:<%@ Page Language="c#" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %> + +<asp:Content ID="body" ContentPlaceHolderID="body" runat="server"> + <div style="text-align: center"> + <h4><asp:Label ID="caption" runat="server"></asp:Label></h4> + <table> + <tr class="formLabel"> + <td>&nbsp;</td> + <td colspan="3"> + <spring:RadioButtonGroup ID="tripMode" runat="server"> + <asp:RadioButton ID="OneWay" runat="server" /> + <asp:RadioButton ID="RoundTrip" runat="server" /> + </spring:RadioButtonGroup> + </td> + </tr> + <tr> + <td class="formLabel" align="right"> + <asp:Label ID="leavingFrom" runat="server" /></td> + <td nowrap="nowrap"> + <asp:DropDownList ID="leavingFromAirportCode" runat="server" /> + </td> + <td class="formLabel" align="right"> + <asp:Label ID="goingTo" runat="server" /></td> + <td nowrap="nowrap"> + <asp:DropDownList ID="goingToAirportCode" runat="server" /> + </td> + </tr> + <tr> + <td class="formLabel" align="right"> + <asp:Label ID="leavingOn" runat="server" /></td> + <td nowrap="nowrap"> + <spring:Calendar ID="departureDate" runat="server" Width="75px" AllowEditing="true" Skin="system" /> + </td> + <td class="formLabel" align="right"> + <asp:Label ID="returningOn" runat="server" /></td> + <td nowrap="nowrap"> + <div id="returningOnCalendar"> + <spring:Calendar ID="returnDate" runat="server" Width="75px" AllowEditing="true" Skin="system" /> + </div> + </td> + </tr> + <tr> + <td class="buttonBar" colspan="4"> + <br/> + <asp:Button ID="findFlights" runat="server"/></td> + </tr> + </table> + </div> + + </asp:Content> + +Ignore for the moment the fact that none of the label + controls have text defined, which will be described later when we discuss + localization in Spring.NET. What is important for the purposes of our + current discussion, is that we have a number of input controls defined: + tripMode radio group, + leavingFromAirportCode and + goingToAirportCode dropdowns, as well as two Spring.NET + Calendar controls, departureDate and + returnDate. + + Next, let's take a look at the model we will be binding this form + to:namespace SpringAir.Domain +{ + [Serializable] + public class Trip + { + // fields + private TripMode mode; + private TripPoint startingFrom; + private TripPoint returningFrom; + + // constructors + public Trip() + { + this.mode = TripMode.RoundTrip; + this.startingFrom = new TripPoint(); + this.returningFrom = new TripPoint(); + } + + public Trip(TripMode mode, TripPoint startingFrom, TripPoint returningFrom) + { + this.mode = mode; + this.startingFrom = startingFrom; + this.returningFrom = returningFrom; + } + + // properties + public TripMode Mode + { + get { return this.mode; } + set { this.mode = value; } + } + + public TripPoint StartingFrom + { + get { return this.startingFrom; } + set { this.startingFrom = value; } + } + + public TripPoint ReturningFrom + { + get { return this.returningFrom; } + set { this.returningFrom = value; } + } + } + + [Serializable] + public class TripPoint + { + // fields + private string airportCode; + private DateTime date; + + // constructors + public TripPoint() + {} + + public TripPoint(string airportCode, DateTime date) + { + this.airportCode = airportCode; + this.date = date; + } + + // properties + public string AirportCode + { + get { return this.airportCode; } + set { this.airportCode = value; } + } + + public DateTime Date + { + get { return this.date; } + set { this.date = value; } + } + } + + [Serializable] + public enum TripMode + { + OneWay, + RoundTrip + } +}As you can see, Trip class uses the + TripPoint class to represent departure and return, + which are exposed as StartingFrom and + ReturningFrom properties. It also uses + TripMode enumeration to specify whether the trip is + one way or return trip, which is exposed as Mode + property. + + Finally, let's see the code-behind class that ties everything + together:public class TripForm : Spring.Web.UI.Page +{ + // model + private Trip trip; + public Trip Trip + { + get { return trip; } + set { trip = value; } + } + + // service dependency, injected by Spring IoC container + private IBookingAgent bookingAgent; + public IBookingAgent BookingAgent + { + set { bookingAgent = value; } + } + + // model management methods + protected override void InitializeModel() + { + trip = new Trip(); + trip.Mode = TripMode.RoundTrip; + trip.StartingFrom.Date = DateTime.Today; + trip.ReturningFrom.Date = DateTime.Today.AddDays(1); + } + + protected override void LoadModel(object savedModel) + { + trip = (Trip) savedModel; + } + + protected override object SaveModel() + { + return trip; + } + + // data binding rules + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("tripMode.Value", "Trip.Mode"); + BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.AirportCode"); + BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.AirportCode"); + BindingManager.AddBinding("departureDate.SelectedDate", "Trip.StartingFrom.Date"); + BindingManager.AddBinding("returnDate.SelectedDate", "Trip.ReturningFrom.Date"); + } + + // event handler for findFlights button, uses injected 'bookingAgent' + // service and model 'trip' object to find flights + private void SearchForFlights(object sender, EventArgs e) + { + FlightSuggestions suggestions = bookingAgent.SuggestFlights(trip); + if (suggestions.HasOutboundFlights) + { + // redirect to SuggestedFlights page + } + } +}There are quite a few things that are happening in this + relatively simple piece of code, so it's worth that we spend some time on + each one: + + When the page is initially loaded (IsPostback == + false), the InitializeModel method is + called which initializes the trip object by creating a new instance + and setting its properties to desired values. Right before the page + is rendered, the SaveModel method will be invoked + and whatever the value it returns will be stored within the HTTP + Session. Finally, on each postback, the LoadModel + method will be called and the value returned by the previous call to + SaveModel will be passed to it as an + argument. + + In this particular case the implementation is very simple + because our whole model is just the trip object. + As such, SaveModel simply returns the + trip object and LoadModel + casts the savedModel argument to + Trip and assigns it to the + trip field within the page. In the more complex + scenarios, you will typically return a dictionary containing your + model objects from the SaveModel method, and read + the values from that dictionary within the + LoadModel. + + + + InitializeDataBindings method defines the + binding rules for all five input controls on our form. It does so by + invoking AddBinding method on the + BindingManager exposed by the page. + AddBinding method is heavily overloaded and it + allows you to specify a binding direction and a + formatter to use in addition to the + source and target binding expressions that are + used above. We'll discuss these optional parameters shortly, but for + now let's focus on the source and target expressions. + + The Data Binding framework uses Spring.NET Expression Language + to define binding expressions. In most cases, like in the example + above, both source and target expression will evaluate to a property + or a field within one of the controls or a data model. This is + always the case when you are setting a bi-directional binding, as + both binding expressions need to be "settable". What is important to + remember about InitializeDataBindings method is + that it is executed only once per page type. + Basically, all of the binding expressions are parsed the first time + the page is instantiated, and are the cached and used by all + instances of that same page type that are created at a later time. + This is done for performance reasons, as data binding expression + parsing on every postback is unnecessary and would add a significant + overhead to the overall page processing time. + + + + If you look at the SearchForFlights event handler, you will + notice that it has no dependencies on the view elements. It simply + uses the injected bookingAgent service and a trip object that in + order to obtain a list of suggested flights. Furthermore, if you + make any modifications to the trip object within your event handler, + bound controls will be updated accordingly just before the page is + rendered. + + This accomplishes one of the major goals we set out to + achieve, allowing developers to remove view element references from + the page event handlers and decouple controller-type methods from + the view. + + + + Now that you have a solid high-level picture of how Spring.NET data + binding and model management are typically used in web applications, let's + take a look at the details and see how data binding is actually + implemented under the hood, what the extension points are, and what are + some additional features that make data binding framework usable in + real-world applications. + + + Data Binding Under the Hood + + Spring.NET Data Binding framework revolves around two main + interfaces: IBinding and + IBindingContainer. The IBinding + interface is definitely the more important one of the two, as it has to + be implemented by all binding types. This interface defines several + methods, with some of them being overloaded for + convenience:public interface IBinding +{ + void BindSourceToTarget(object source, object target, ValidationErrors validationErrors); + + void BindSourceToTarget(object source, object target, ValidationErrors validationErrors, + IDictionary variables); + + void BindTargetToSource(object source, object target, ValidationErrors validationErrors); + + void BindTargetToSource(object source, object target, ValidationErrors validationErrors, + IDictionary variables); + + void SetErrorMessage(string messageId, params string[] errorProviders); +}As their names imply, BindSourceToTarget + method is used to extract and copy bound values from the source object + to the target object, while BindTargetToSource does + the opposite. Both method names and parameter types are very generic for + a good reason -- data binding framework can indeed be used to bind any + two objects. Using it to bind web forms to model objects is just one of + its possible uses, although a very common one and tightly integrated + into the Spring.NET Web Framework. + + The validationErrors parameter requires further + explanation. While the data binding framework is not in any way coupled + to the data validation framework, they are in some ways related. For + example, while the data validation framework is best suited to validate + the populated model according to the business rules, the data binding + framework is in a better position to validate data types during the + binding process. However, regardless of where specific validation is + performed, all error messages should be presented to the user in a + consistent manner. In order to accomplish this, Spring.NET Web Framework + passes the same ValidationErrors instance to binding methods and to any + validators that might be executed within your event handlers. This + ensures that all error messages are stored together and are displayed + consistently to the end user, using Spring.NET validation error + controls. + + The last method in the IBinding interface, + SetErrorMessage, enables this by allowing you to + specify the resource id of the error message to be displayed in the case + of binding error, as well as the list of error providers that messages + should be displayed in. We will see an example of the + SetErrorMessage usage shortly. + + The IBindingContainer interface extends the + IBinding interface and adds the following + members:public interface IBindingContainer : IBinding +{ + bool HasBindings { get; } + + IBinding AddBinding(IBinding binding); + IBinding AddBinding(string sourceExpression, string targetExpression); + IBinding AddBinding(string sourceExpression, string targetExpression, BindingDirection direction); + IBinding AddBinding(string sourceExpression, string targetExpression, IFormatter formatter); + IBinding AddBinding(string sourceExpression, string targetExpression, BindingDirection direction, + IFormatter formatter); +}As you can see, this interface has a number of overloaded + AddBinding methods. The first one, + AddBinding(IBinding binding) is the most generic one, + as it can be used to add any binding type to the container. The other + four are convenience methods that provide a simple way to add the most + commonly used binding type, SimpleExpressionBinding. + The SimpleExpressionBinding is what we used in the + example at the beginning of this section to bind our web form to a + Trip instance. It uses Spring.NET Expression + Language to extract and to set values within source and target objects. + We discussed sourceExpression and + targetExpression arguments earlier, so let's focus on + the remaining ones. + + + Binding Direction + + The direction argument determines whether the binding is + bidirectional or unidirectional. By default, all data bindings are + bidirectional unless the direction argument is set to either + BindingDirection.SourceToTarget or + BindingDirection.TargetToSource. If one of these + two values is specified, binding will be evaluated only when the + appropriate BindDirection + method is invoked, and will be completely ignored in the other + direction. This is very useful when you want to bind some information + from the model into non-input controls, such as labels. + + However, unidirectional data bindings are also useful when your + form doesn't have a simple one-to-one mapping to presentation model. + In our earlier trip form example, the presentation model was + intentionally designed to allow for simple one-to-one mappings. For + the sake of discussion, let's add the Airport + class and modify our TripPoint class like + this:namespace SpringAir.Domain +{ + [Serializable] + public class TripPoint + { + // fields + private Airport airport; + private DateTime date; + + // constructors + public TripPoint() + {} + + public TripPoint(Airport airport, DateTime date) + { + this.airport = airport; + this.date = date; + } + + // properties + public Airport Airport + { + get { return this.airport; } + set { this.airport = value; } + } + + public DateTime Date + { + get { return this.date; } + set { this.date = value; } + } + } + + [Serializable] + public class Airport + { + // fields + private string code; + private string name; + + // properties + public string Code + { + get { return this.code; } + set { this.code = value; } + } + + public string Name + { + get { return this.name; } + set { this.name = value; } + } + } +}Instead of the string property + AirportCode, our TripPoint + class now exposes an Airport property of type + Airport, which is defined above. Now we have a + problem: what used to be a simple string to string binding, with the + airport code selected in a dropdown being copied directly into the + TripPoint.AirportCode property and vice versa, now becomes a not so + simple string to Airport binding, so let's see + how we can solve this mismatch problem. + + First of all, binding from the model to the control is still + very straight forward. We just need to set up one-way bindings from + the model to controls:protected override void InitializeDataBindings() + { + BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.Airport.Code", BindingDirection.TargetToSource); + BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.Airport.Code", BindingDirection.TargetToSource); + ... + }All we need to do is extract airport code value from the + Trip.StartingFrom.Airport.Code instead of + Trip.StartingFrom.AirportCode. Unfortunately, + binding from the control to the model the same way won't work: we + might be able to set Code property of the + Airport object, but that will likely make the + Airport.Name property invalid. What we really want + do is find an instance of the Airport class + based on the airport code and set the + TripPoint.Airport property to it. Fortunately, this + is very simple to do with Spring.NET data binding, especially because + we already have airportDao object defined in the + Spring context, which has GetAirport(string + airportCode) finder method. All we need to do is set up data + bindings from source to target that will invoke this finder method + when evaluating the source expression. Our complete set of bindings + for these two drop down lists will then look like + this:protected override void InitializeDataBindings() + { + BindingManager.AddBinding("@(airportDao).GetAirport(leavingFromAirportCode.SelectedValue)", "Trip.StartingFrom.Airport", BindingDirection.SourceToTarget); + BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.Airport.Code", BindingDirection.TargetToSource); + + BindingManager.AddBinding("@(airportDao).GetAirport(goingToAirportCode.SelectedValue)", "Trip.ReturningFrom.Airport", BindingDirection.SourceToTarget); + BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.Airport.Code", BindingDirection.TargetToSource); + ... + }That's it -- by using two unidirectional bindings with + different expressions and by leveraging the fact that expressions can + reference objects defined in the Spring context, we were able to solve + this non-trivial data binding problem. + + + + Formatters + + The last argument to AddBinding method that + we need to discuss is a formatter argument. This + argument allows you to specify a formatter that should be used to + parse string value from the typical input control before it is bound + to the model, and to format strongly typed model value before it is + bound to the control. + + You will typically use one of the formatters provided in the + Spring.Globalization.Formatters namespace, but if you have + requirements that cannot be satisfied by one of the standard + formatters it is easy enough to write your own -- all you need to do + is implement a very simple IFormatter interface:public interface IFormatter +{ + string Format(object value); + object Parse(string value); +} + + Standard formatters provided with Spring.NET are: + CurrencyFormatter, + DateTimeFormatter, + FloatFormatter, + IntegerFormatter, + NumberFormatter and + PercentFormatter, which should be sufficient for + most usage scenarios. + + + + Type Conversion + + Because the data binding framework uses the same expression + evaluation engine as the Spring.NET IoC container, it will use any + registered type converters to perform data binding. Many type + converters are included with Spring.NET (take a look at the classes in + Spring.Objects.TypeConverters namespace) and automatically registered + for you, but you can implement your own custom converters and register + them using standard Spring.NET type converter registration + mechanisms. + + + + Data Binding Events + + Spring.Web's base Page class adds two + events to the standard .NET page lifecycle - + DataBound and + DataUnbound. + + The DataUnbound event is fired after the data + model has been updated using values from the controls. It is fired + right after the Load event and only on postbacks, + because it doesn't make sense to update the data model using the + controls' initial values. + + The DataBound is fired after controls have + been updated using values from the data model. This happens right + before the PreRender event. + + The fact that data model is updated immediately after the + Load event and that controls are updated right + before the PreRender event means that your event + handlers will be able to work with a correctly updated data model, as + they execute after the Load event, and that any + changes you make to the data model within event handlers will be + reflected in the controls immediately afterwards, as they (the + controls) are updated prior to the actual rendering. + + + + Rendering Binding Errors + + If there are errors in the databinding, for example, trying to + bind a string 'hello' to an integer property on the model, you can + specify how those fundamental binding errors should be rendered. An + example of this shown below taken from the WebQuickStart + 'RobustEmployeeInfo' example. + + [Default.aspx.cs] + +protected override void InitializeDataBindings() +{ + // collect txtId.Text binding errors in "id.errors" collection + BindingManager.AddBinding("txtId.Text", "Employee.Id").SetErrorMessage("ID has to be an integer", "id.errors"); + ... + +[Default.aspx] +... +<asp:TextBox ID="txtId" runat="server" /> +<!-- output validation errors from "id.errors" collection --> +<spring:ValidationError Provider="id.errors" runat="server" /> +... + + The SetErrorMessage specifies the message text or resource id of + the error message to be displayed followed by a variable length list + of strings that specify the collection of error providers message + where the message should be displayed. In the above case the error + provider will be rendered in Spring's ValidationError User Control. + See + + + + HttpRequestListBindingContainer + + HttpRequestListBindingContainer extracts posted raw values from + the request and populates the specified IList by creating objects of + the type specified and populating each of these objects according to + the requestBindings collection. + + Please checkout the WebQuickStart sample's demo of + HttpRequestListBindingContainer. Below + + protected override void InitializeDataBindings() +{ + // HttpRequestListBindingContainer unbinds specified values from Request -> Productlist + HttpRequestListBindingContainer requestBindings = + new HttpRequestListBindingContainer("sku,name,quantity,price", "Products", typeof(ProductInfo)); + requestBindings.AddBinding("sku", "Sku"); + requestBindings.AddBinding("name", "Name"); + requestBindings.AddBinding("quantity", "Quantity", quantityFormatter); + requestBindings.AddBinding("price", "Price", priceFormatter); + + BindingManager.AddBinding(requestBindings); +} + + + Due to the fact, that browsers don't send the values of unchecked checkboxes, you can't use HttpRequestListBindingContainer with <input type="checkbox" > html controls. + + + + + + Using DataBindingPanel + + To simplify use of Spring's Data Binding feature on web pages and + controls, Spring.Web provides a special DataBindingPanel container + control. A DataBindingPanel does not render any html code itself, but + allows for specifying additional, data binding related attributes to its + child controls: + + <%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="DataBinding_EasyEmployeeInfo_Default" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> +<html> +<body> +<spring:DataBindingPanel ID="ctlDataBindingPanel" runat="server"> + <table cellpadding="3" cellspacing="3" border="0"> + <tr> + <td>Employee ID:</td> + <td> + <asp:TextBox ID="txtId" runat="server" BindingTarget="Employee.Id" /> + </td> + </tr> + <tr> + <td>First Name:</td> + <td><asp:TextBox ID="txtFirstName" runat="server" BindingTarget="Employee.FirstName" /></td> + </tr> + </table> +</spring.DataBindingPanel> +</body> +</html> + + Using DataBindingPanel the binding information can be specified + directly on the control declaration. The following attributes are + recognized by a DataBindingPanel: + + + + BindingTarget + + corresponds to the target expression used in + IBindingContainer.AddBinding() + + + + BindingSource + + corresponds to the source expression used in + IBindingContainer.AddBinding(). For standard controls you don't need + to specify the source expression. If you are binding to some custom + control, of course you must specific this attribute. + + + + BindingDirection + + one of the values of the BindingDirection enumeration + + + + BindingFormatter + + if you need a custom formatter, you can specific the object + name of a formatter here. The formatter instance will be obtained by + a call to IApplicationContext.GetObject() each time it is + needed. + + + + BindingType + + In case you need a completely customized binding, specify its + type here. Note that a custom binding type must implement the + following constructor signature: + + ctor(string source,string target, BindingDirection, + IFormatter) + + + + + The Visual Studio Web Form Editor will of course complain about binding attributes because it doesn't know them. You can safely ignore those warnings. + + + + + + Localization and Message Sources + + While recognizing that the .NET framework has excellent support for + localization, the support within ASP.NET 1.x is somewhat + incomplete. + + Every .aspx page in an ASP.NET project has a + resource file associated with it, but those resources are never used (by + the current ASP.NET infrastructure). ASP.NET 2.0 will change that and + allow application developers to use local resources for pages. In the + meantime, the Spring.NET team built support for using local pages + resources into Spring.Web thus allowing application developers to start + using ASP.NET 2.0-like page resources immediately. + + Spring.Web supports several different approaches to localization + within a web application, which can be mixed and matched as appropriate. + Both push and pull mechanisms are supported, as well as the fallback to + globally defined resources when a local resource cannot be found. + Spring.Web also provides support for user culture management and image + localization, which are described in the later sections. + + + Introductory material covering ASP.NET Globalization and + Localization can be found at the following URLs; Globalization + Architecture for ASP.NET and Localization + Practices for ASP.NET 2.0 by Michele Leroux Bustamante. + + + + Automatic Localization Using Localizers ("Push" + Localization) + + The central idea behind 'push' localization is that an application + developer should be able to specify localization resources in the + resource file for the page and have those resources automatically + applied to the user controls on the page by the framework. For example, + an application developer could define a page such as + UserRegistration.aspx... + + <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> +<%@ Page language="c#" Codebehind="UserRegistration.aspx.cs" + AutoEventWireup="false" Inherits="ArtFair.Web.UI.Forms.UserRegistration" %> +<html> + <body> + <spring:Content id="mainContent" contentPlaceholderId="main" runat="server"> + <div align="right"> + <asp:LinkButton ID="english" Runat="server" CommandArgument="en-US">English</asp:LinkButton>&nbsp; + <asp:LinkButton ID="serbian" Runat="server" CommandArgument="sr-SP-Latn">Srpski</asp:LinkButton> + </div> + <table> + <tr> + <td><asp:Label id="emailLabel" Runat="server"/></td> + <td><asp:TextBox id="email" Runat="server" Width="150px"/></td> + </tr> + <tr> + <td><asp:Label id="passwordLabel" Runat="server"/></td> + <td><asp:TextBox id="password" Runat="server" Width="150px"/></td> + </tr> + <tr> + <td><asp:Label id="passwordConfirmationLabel" Runat="server"/></td> + <td><asp:TextBox id="passwordConfirmation" Runat="server" Width="150px"/></td> + </tr> + <tr> + <td><asp:Label id="nameLabel" Runat="server"/></td> + <td><asp:TextBox id="name" Runat="server" Width="150px"/></td> + </tr> +... + + <tr> + <td colspan="2"> + <asp:Button id="saveButton" Runat="server"/>&nbsp; + <asp:Button id="cancelButton" Runat="server"/> + </td> + </tr> + </table> + </spring:Content> + </body> +</html> + + A close inspection of the above .aspx code + reveals that none of the Label or + Button controls have had a value assigned to the + Text property. The values of the + Text property for these controls are stored in the + local resource file (of the page) using the following convention to + identify the resource (string). + + $this.controlId.propertyName + + The corresponding local resource file, + UserRegistration.aspx.resx, is shown below. + + <root> + <data name="$this.emailLabel.Text"> + <value>Email:</value> + </data> + <data name="$this.passwordLabel.Text"> + <value>Password:</value> + </data> + <data name="$this.passwordConfirmationLabel.Text"> + <value>Confirm password:</value> + </data> + <data name="$this.nameLabel.Text"> + <value>Full name:</value> + </data> + +... + + <data name="$this.countryLabel.Text"> + <value>Country:</value> + </data> + <data name="$this.saveButton.Text"> + <value>$messageSource.save</value> + </data> + <data name="$this.cancelButton.Text"> + <value>$messageSource.cancel</value> + </data> +</root> + + + VS2003 + + To view the .resx file for a page, you may need to enable + "Project/Show All Files" in Visual Studio. When "Show All Files" is + enabled, the .resx file appears like a "child" of the code-behind + page. + + When Visual Studio creates the .resx file, it will include a + xds:schema element and several + reshead elements. Your data elements will follow + the reshead elements. When working with the .resx + files, you may want to choose "Open With" from the context menu and + select the "Source Code" text editor. + + + + VS2005 + + To create a resource file in VS2005, open your control or page + in design mode and select "Tools/Generate local resource" from the + menu + + + Finally a localizer must be configured for the page to enable + automatic localization: + + <object id="localizer" type="Spring.Globalization.Localizers.ResourceSetLocalizer, Spring.Core"/> + +<object type="UserRegistration.aspx"> + <property name="Localizer" ref="localizer"/> +</object> + + For more information on configuring localizers see + + + + Global Message Sources + + The last two resource definitions from the previous section + require some additional explanation: + + <data name="$this.saveButton.Text"> + <value>$messageSource.save</value> + </data> + <data name="$this.cancelButton.Text"> + <value>$messageSource.cancel</value> + </data> + + In some cases it makes sense to apply a resource that is defined + globally as opposed to locally. In this example, it + makes better sense to define values for the Save and + Cancel buttons globally as they will probably be used + throughout the application. + + The above example demonstrates how one can achieve that by + defining a resource redirection expression as the + value of a local resource by prefixing a global resource name with the + following string. + + $messageSource. + + Taking the case of the above example, this will tell the localizer + to use the save and cancel + portions of the resource key as lookup keys to retrieve the actual + values from a global message source. The important thing to remember is + that one need only define a resource redirect once, typically in the + invariant resource file – any lookup for a resource redirect will simply + fall back to the invariant culture, and result in a global message + source lookup using the correct culture. + + Global resources are (on a per-context basis) defined as a plain + vanilla object definition using the reserved name of + 'messageSource', which one can add to one's + Spring.NET configuration file as shown below. + + <object id="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core"> + <property name="ResourceManagers"> + <list> + <value>MyApp.Web.Resources.Strings, MyApp.Web</value> + </list> + </property> +</object> + + + NET 2.0 + + To use resources from your App_GlobalResources folder, specify + App_GlobalResources as assembly name (see the + SpringAir example application for more): + + <value>Resources.Strings, + App_GlobalResources</value> + + + The global resources are cached within the Spring.NET + IApplicationContext and are accessible through + the Spring.NET IMessageSource interface. + + The Spring.Web Page and + UserControl classes have a reference to their owning + IApplicationContext and it's associated + IMessageSource. As such, they will automatically + redirect resource lookups to a global message source if a local resource + cannot be found. + + Currently, the ResourceSetMessageSource is the + only message source implementation that ships with Spring.NET. + + + + Working with Localizers + + In order to apply resources automatically, a localizer needs to be + injected into all pages requiring this feature (typically accomplished + using a base page definition that other pages will inherit from). The + injected localizer will inspect the resource file when the page is first + requested, cache the resources that start with the + '$this' marker string value, and apply the values to + the controls that populate the page prior to the page being + rendered. + + A localizer is simply an object that implements the + Spring.Globalization.ILocalizer interface. + Spring.Globalization.AbstractLocalizer is + provided as a convenient base class for localization: this class has one + abstract method, LoadResources. This method must load + and return a list of all the resources that must be automatically + applied from the resource store. + + Spring.NET ships with one concrete implementation of a localizer, + Spring.Globalization.Localizers.ResourceSetLocalizer, + that retrieves a list of resources to apply from the local resource + file. Future releases of Spring.NET may provide other localizers that + read resources from an XML file or even a flat text file that contains + resource name-value pairs which will allow application developers to + store resources within the files in a web application instead of as + embedded resources in an assembly. Of course, if an application + developer would rather store such resources in a database, he or she can + write their own ILocalizer implementation that + will load a list of resources to apply from a database. + + As mentioned previously, one would typically configure the + localizer to be used within an abstract base definition for those pages + that require localization as shown below. + + <object id="localizer" type="Spring.Globalization.Localizers.ResourceSetLocalizer, Spring.Core"/> + +<object name="basePage" abstract="true"> + <description> + Pages that reference this definition as their parent + (see examples below) will automatically inherit following properties. + </description> + <property name="Localizer" ref="localizer"/> +</object> + + Of course, nothing prevents an application developer from defining + a different localizer for each page in the application; in any case, one + can always override the localizer defined in a base (page) definition. + Alternatively, if one does want any resources to be applied + automatically one can completely omit the localizer definition. + + One last thing to note is that Spring.NET + UserControl instances will (by default) inherit + the localizer and other localization settings from the page that they + are contained within, but one can similarly also override that behavior + using explicit dependency injection. + + + + Applying Resources Manually ("Pull" Localization) + + While automatic localization as described above works great for + many form-like pages, it doesn't work nearly as well for the controls + defined within any iterative controls because the IDs for such iterative + controls are not fixed. It also doesn't work well in those cases where + one needs to display the same resource multiple times within the same + page. For example, think of the header columns for outgoing and return + flights tables within the SpringAir application (see ). + + In these situations, one should use a pull-style mechanism for + localization, which boils down to a simple GetMessage + call as shown below. + + <asp:Repeater id="outboundFlightList" Runat="server"> + <HeaderTemplate> + <table border="0" width="90%" cellpadding="0" cellspacing="0" align="center" class="suggestedTable"> + <thead> + <tr class="suggestedTableCaption"> + <th colspan="6"> + <%= GetMessage("outboundFlights") %> + </th> + </tr> + <tr class="suggestedTableColnames"> + <th><%= GetMessage("flightNumber") %></th> + <th><%= GetMessage("departureDate") %></th> + <th><%= GetMessage("departureAirport") %></th> + <th><%= GetMessage("destinationAirport") %></th> + <th><%= GetMessage("aircraft") %></th> + <th><%= GetMessage("seatPlan") %></th> + </tr> + </thead> + <tbody> + </HeaderTemplate> + + The GetMessage method is available within both + the Spring.Web.UI.Page and + Spring.Web.UI.UserControl classes, and it will + automatically fall back to a global message source lookup if a local + resource is not found. + + + + Localizing Images within a Web Application + + Spring.Web provides an easy (and consistent) way to localize + images within a web application. Unlike text resources, which can be + stored within embedded resource files, XML files, or even a database, + images in a typical web application are usually stored as files on the + file system. Using a combination of directory naming conventions and a + custom ASP.NET control, Spring.Web allows application developers to + localize images within the page as easily as text resources. + + The Spring.Web Page class exposes the + ImagesRoot property, which is used to define the root + directory where images are stored. The default value is 'Images', which + means that the localizer expects to find an 'Images' directory within + the application root, but one can set it to any value in the definition + of the page. + + In order to localize images, one needs to create a directory for + each localized culture under the ImagesRoot directory + as shown below. + + /MyApp + /Images + /en + /en-US + /fr + /fr-CA + /sr-SP-Cyrl + /sr-SP-Latn + ... + + Once an appropriate folder hierarchy is in place all one need do + is put the localized images in the appropriate directories and make sure + that different translations of the same image are named the same. In + order to place a localized image on a page, one needs to use the + <spring:LocalizedImage> as shown below. + + <%@ Page language="c#" Codebehind="StandardTemplate.aspx.cs" + AutoEventWireup="false" Inherits="SpringAir.Web.StandardTemplate" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > +<html> + <body> + <spring:LocalizedImage id="logoImage" imageName="spring-air-logo.jpg" borderWidth="0" runat="server" /> + </body> +</html> + + This control will find the most specific directory that contains + an image with the specified name using standard localization fallback + rules and the user's culture. For example, if the user's culture is + 'en-US', the localizer will look for the + spring-air-logo.jpg file in + Images/en-US, then in Images/en + and finally, if the image file has still not been found, in the root + Images directory (which for all practical purposes + serves as an invariant culture folder). + + + + User Culture Management + + In addition to global and local resource management, Spring.Web + also adds support for user culture management by exposing the current + CultureInfo through the + UserCulture property on the + Page and UserControl + classes. + + The UserCulture property will simply delegate + culture resolution to an implementation of + Spring.Globalization.ICultureResolver interface. + One can specify exactly which culture resolver to use by configuring the + CultureResolver property of the + Page class in the relevant object definition as + shown below. + + <object name="BasePage" abstract="true"> + <property name="CultureResolver"> + <object type="Spring.Globalization.Resolvers.CookieCultureResolver, Spring.Web"/> + </property> +</object> + + Several useful implementations of + ICultureResolver ship as part of Spring.Web, so + it is unlikely that application developers will have to implement their + own culture resolver. However, if one does have such a requirement, the + resulting implementation should be fairly straightforward as there are + only two methods that one need implement. The following sections discuss + each available implementation of the + ICultureResolver interface. + + + DefaultWebCultureResolver + + This is default culture resolver implementation. It will be used + if one does not specify a culture resolver for a page, or if one + explicitly injects a DefaultWebCultureResolver + into a page definition explicitly. The latter case (explicit + injection) is sometimes useful because it allows one to specify a + culture that should always be used by providing a value to the + DefaultCulture property on the resolver. + + The DefaultWebCultureResolver will first + look at the DefaultCulture property and return its + value if said property value is not null. If it is null, the + DefaultWebCultureResolver will fall back to + request header inspection, and finally, if no + 'Accept-Lang' request headers are present it will + return the UI culture of the currently executing thread. + + + + RequestCultureResolver + + This resolver works in a similar way to the + DefaultWebCultureResolver with the exception + that it always checks request headers first, and + only then falls back to the value of the + DefaultCulture property or the culture code of the + current thread. + + + + SessionCultureResolver + + This resolver will look for culture information in the user's + session and return it if it finds one. If not, it will fall back to + the behavior of the + DefaultWebCultureResolver. + + + + CookieCultureResolver + + This resolver will look for culture information in a cookie, and + return it if it finds one. If not, it will fall back to the behavior + of the DefaultWebCultureResolver. + + + CookieCultureResolver will not work if + your application uses localhost as the server + URL, which is a typical setting in a development environment. + + In order to work around this limitation you should use + SessionCultureResolver during development and + switch to CookieCultureResolver before you + deploy the application in a production. This is easily accomplished + in Spring.Web (simply change the config file) but is something that + you should be aware of. + + + + + + Changing Cultures + + In order to be able to change the culture application developers + will need to use one of the culture resolvers that support culture + changes, such as SessionCultureResolver or + CookieCultureResolver. One could also write a + custom ICultureResolver that will persist culture + information in a database, as part of a user's profile. + + Once that requirement is satisfied, all that one need do is to set + the UserCulture property to a new + CultureInfo object before the page is rendered. + In the following .aspx example, there are two link + buttons that can be used to change the user's culture. In the + code-behind, this is all one need do to set the new culture. A code + snippet for the code-behind file + (UserRegistration.aspx.cs) is shown below. + + protected override void OnInit(EventArgs e) +{ + InitializeComponent(); + + this.english.Command += new CommandEventHandler(this.SetLanguage); + this.serbian.Command += new CommandEventHandler(this.SetLanguage); + + base.OnInit(e); +} + +private void SetLanguage(object sender, CommandEventArgs e) +{ + this.UserCulture = new CultureInfo((string) e.CommandArgument); +} + + + + + Result Mapping + + One of the problems evident in many ASP.NET applications is that + there is no built-in way to externalize the flow of an application. The + most common way of defining application flow is by hardcoding calls to the + Response.Redirect and + Server.Transfer methods within event handlers. + + This approach is problematic because any changes to the flow of an + application necessitates code changes (with the attendant recompilation, + testing, redeployment, etc). A much better way, and one that has been + proven to work successfully in many MVC ( Model-View-Controller) + web frameworks is to provide the means to externalize the mapping of + action results to target pages. + + Spring.Web adds this functionality to ASP.NET by allowing one to + define result mappings within the definition of a page, and to then simply + use logical result names within event handlers to control application + flow. + + In Spring.Web, a logical result is encapsulated and defined by the + Result class; because of this one can configure + results just like any other object: + + +<objects xmlns="http://www.springframework.net"> + + <object id="homePageResult" type="Spring.Web.Support.Result, Spring.Web"> + <property name="TargetPage" value="~/Default.aspx"/> + <property name="Mode" value="Transfer"/> + <property name="Parameters"> + <dictionary> + <entry key="literal" value="My Text"/> + <entry key="name" value="%{UserInfo.FullName}"/> + <entry key="host" value="%{Request.UserHostName}"/> + </dictionary> + </property> + </object> + + <object id="loginPageResult" type="Spring.Web.Support.Result, Spring.Web"> + <property name="TargetPage" value="Login.aspx"/> + <property name="Mode" value="Redirect"/> + </object> + + <object type="UserRegistration.aspx" parent="basePage"> + <property name="UserManager" ref="userManager"/> + <property name="Results"> + <dictionary> + <entry key="userSaved" value-ref="homePageResult"/> + <entry key="cancel" value-ref="loginPageResult"/> + </dictionary> + </property> + </object> + +</objects> + + + The only property that you must supply a value + for each and every result is the TargetPage property. + The value of the Mode property can be either + Transfer, TransferNoPreserve, + Redirect, and defaults to Transfer + if none is specified. TransferNoPreserve issues a server-side transfer + with 'preserveForm=false', so that QueryString and Form data are not + preserved. + + If one's target page requires parameters, one can define them using + the Parameters dictionary property. One simply + specifies either literal values or object + navigation expressions for such parameter values; if one specifies + an expression, this expression will be evaluated in the context of the + page in which the result is being referenced... in the specific case of + the above example, this means that any page that uses the + homePageResult needs to expose a + UserInfo property on the page class itself. + In Spring 1.1.0 and before the prefix used to indicate an object + navigation expression in the Parameters dictionary + property was the dollar sign, i.e. + ${UserInfo.FullName}.This conflicted with the + prefix used to perform property replacement, the dollar sign, as + described in the section PropertyPlaceholderConfigurer. + As a workaround you can change the prefix and suffix used in + PropertyPlaceholderConfigurer to be different, for example prefix = + $${ and suffix = }. In Spring 1.1.1 a new prefix character, the + percent sign (i.e.%{UserInfo.FullName}.) can be + used in the Parameters dictionary to avoid this + conflict so you can keep the familiar NAnt style + PropertyPlaceholderConfigurer defaults. + + + Parameters will be handled differently depending on the result mode. + For redirect results, every parameter will be converted to a string, then + URL encoded, and finally appended to a redirect query string. On the other + hand, parameters for transfer results will be added to the + HttpContext.Items collection before the request is + transferred to the target page. This means that transfers are more + flexible because any object can be passed as a parameter between pages. + They are also more efficient because they don't require a round-trip to + the client and back to the server, so transfer mode is recommended as the + preferred result mode (it is also the current default). + + The above example shows independent result object definitions, which + are useful for global results such as a home- and login- page. + Result definitions that are only going to be used + by one page should be simply embedded within the definition of a page, + either as inner object definitions or using a special shortcut notation + for defining a result definition: + + +<object type="~/UI/Forms/UserRegistration.aspx" parent="basePage"> + <property name="UserManager"> + <ref object="userManager"/> + </property> + + <property name="Results"> + <dictionary> + <entry key="userSaved" value="redirect:UserRegistered.aspx?status=Registration Successful,user=${UserInfo}"/> + <entry key="cancel" value-ref="homePageResult"/> + </dictionary> + </property> +</object> + + + The short notation for the result must adhere to the following + format... + + [<mode>:]<targetPage>[?param1,param2,...,paramN] + + There are three possible values for the mode + value referred to in the above notation snippet; they are: + + + + redirect + + + + transfer + + + + TransferNoPreserve + + + + They correspond to the values of the ResultMode enumeration. One + thing to notice is that a comma is used instead of an ampersand to + separate parameters; this is done so as to avoid the need for laborious + ampersand escaping within an XML object definition. The use of the + ampersand character is still supported if required, but one will then have + to specify it using the well known & entity reference. + + Once one has defined one's results, it is very simple to use them + within the event handlers of one's pages + (UserRegistration.apsx.cs)... + + private void SaveUser(object sender, EventArgs e) +{ + UserManager.SaveUser(UserInfo); + SetResult("userSaved"); +} + +public void Cancel(object sender, EventArgs e) +{ + SetResult("cancel"); +} + +protected override void OnInit(EventArgs e) +{ + InitializeComponent(); + + this.saveButton.Click += new EventHandler(this.SaveUser); + this.cancelButton.Click += new EventHandler(this.Cancel); + + base.OnInit(e); +} + + One could of course further refactor the above example and use + defined constants. This would be a good thing to do in the case of a + logical result name such as "home" that is likely to be referenced by many + pages. + + + + Client-Side Scripting + + ASP.NET has decent support for client-side scripting through the use + of the Page.RegisterClientScriptBlock and + Page.RegisterStartupScript methods. + + However, neither of these two methods allows you to output a + registered script markup within a <head> section + of a page, which is (in many cases) exactly what you would like to + do. + + + Registering Scripts within the head HTML section + + Spring.Web adds several methods to enhance client-side scripting + to the base Spring.Web.UI.Page class: + RegisterHeadScriptBlock and + RegisterHeadScriptFile, each with a few overrides. + You can call these methods from your custom pages and controls in order + to register script blocks and script files that must be included in the + <head> section of the final HTML page. + + The only additional thing that is required to make this work is + that you use the <spring:Head> server-side + control to define your <head> section instead + of using the standard HTML <head> element. This + is shown below. + + <%@ Page language="c#" Codebehind="StandardTemplate.aspx.cs" + AutoEventWireup="false" Inherits="SpringAir.Web.StandardTemplate" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > +<html> + <spring:Head runat="server" id="Head1"> + <title> + <spring:ContentPlaceHolder id="title" runat="server"> + <%= GetMessage("default.title") %> + </spring:ContentPlaceHolder> + </title> + <LINK href="<%= CssRoot %>/default.css" type="text/css" rel="stylesheet"> + <spring:ContentPlaceHolder id="head" runat="server"></spring:ContentPlaceHolder> + </spring:Head> + <body> + ... + </body> +</html> + + The example above shows you how you would typically set-up a + <head> section within a master page template in + order to be able to change the title value and to add additional + elements to the <head> section from the child + pages using <spring:ContentPlaceholder> + controls. However, only the <spring:Head> + declaration is required in order for Spring.NET + Register* scripts to work properly. + + + + Adding CSS Definitions to the head Section + + In a similar fashion, you can add references to CSS files, or even + specific styles, directly to the <head> HTML + section using Page.RegisterStyle and + Page.RegisterStyleFile methods. The latter one simply + allows you to include a reference to an external CSS file, while the + former one allows you to define embedded style definitions by specifying + the style name and definition as the parameters. The final list of style + definitions registered this way will be rendered within the single + embedded style section of the final HTML + document. + + + + Well-Known Directories + + In order to make the manual inclusion of client-side scripts, CSS + files and images easier, the Spring.Web Page class + exposes several properties that help you reference such artifacts using + absolute paths. This affords web application developers a great deal of + convenience functionality straight out of the box if they stick to + common conventions such as a web application (directory) + structure.. + + These properties are ScriptsRoot, + CssRoot and ImagesRoot. They have + default values of Scripts, CSS and + Images, which will work just fine if you create and + use these directories in your web application root. However, if you + prefer to place them somewhere else, you can always override default + values by injecting new values into your page definitions (you will + typically inject these values only in the base page definition, as they + are normally shared by all the pages in the application). An example of + such configuration is shown below: + + <object name="basePage" abstract="true"> + <description> + Convenience base page definition for all the pages. + + Pages that reference this definition as their parent (see the examples below) + will automatically inherit the following properties.... + + </description> + <property name="CssRoot" value="Web/CSS"/> + <property name="ImagesRoot" value="Web/Images"/> +</object> + + + + + + Spring User Controls + + Spring provides several custom user controls that are located in the + Spring.Web.UI.Controls namespace. This section + primarily lists the controls and points to other documentation to provide + additional information. There are a few other controls not documented + here, please check the SDK docs for their descriptions. + + + Validation Controls + + The location in the web page where validation errors are to be + rendered can be specifies by using the + ValidationSummary and + ValidationError controls. There are two controls + since they have different defaults for how errors are rendered. + ValidationSummary is used to display potentially + multiple errors identified by the validation framework. + ValidationError is used to display field-level + validation errors. Please refer to the section ASP.NET usage tips in the + chapter on the Validation Framework + more information. + + + + Databinding Controls + + Some standard controls are not easy to use with Spring's + databinding support. Examples are check boxes and ratio button groups. + In this case you should use the CheckBoxList and + RadioButtonGroup controls. Databinding itself can + be done using the DataBindingPanel instead of the + using the BindingManager API within the code behind page. + + + + Calendar Control + + A pop-up DHTML calendar control is provided. It is a slightly + modified version of the Dynarch.com DHTML + Calendar control written by Mihai Bazon. + + + + Panel Control + + You can suppress dependency injection for controls inside your + ASP.NET by using the Panel control. See the section Customizing control dependency + injection for more information. + + + \ No newline at end of file diff --git a/doc/reference/src/webservices.xml b/doc/reference/src/webservices.xml new file mode 100644 index 00000000..6d4255cc --- /dev/null +++ b/doc/reference/src/webservices.xml @@ -0,0 +1,536 @@ + + + Web Services + + + Introduction + + While the out-of-the-box support for web services in .NET is + excellent, there are a few areas that the Spring.NET thought could use + some improvement. Spring adds the ability to perform dependency injection + on standard asmx web services. Spring's .NET Web Services support also + allows you to export a 'plain .NET object' as a .NET web service By "plain + .NET object" we mean classes that do not contain infrastructure specific + attributes, such as WebMethod. On the server side, Spring's .NET web + service exporters will automatically create a proxy that adds web service + attributes. On the client side you can use Spring IoC container to + configure a client side proxy that you generated with standard command + line tools. Additionally, Spring provides the functionality to create the + web service proxy dynamically at runtime (much like running the command + line tools but at runtime and without some of the tools quirks) and use + dependency injection to configure the resulting proxy class. On both the + server and client side, you can apply AOP advice to add behavior such as + logging, exception handling, etc. that is not easily encapsulated within + an inheritance hierarchy across the application. + + + + Server-side + + One thing that the Spring.NET team didn't like much is that we had + to have all these .asmx files lying around when all said files did was + specify which class to instantiate to handle web service requests. + + Second, the Spring.NET team also wanted to be able to use the + Spring.NET IoC container to inject dependencies into our web service + instances. Typically, a web service will rely on other objects, service + objects for example, so being able to configure which service object + implementation to use is very useful. + + Last, but not least, the Spring.NET team did not like the fact that + creating a web service is an implementation task. Most (although not all) + services are best implemented as normal classes that use coarse-grained + service interfaces, and the decision as to whether a particular service + should be exposed as a remote object, web service, or even an enterprise + (COM+) component, should only be a matter of configuration, and not + implementation. + + An example using the web service exporter can be found in quickstart + example named 'calculator'. More information can be found here 'Web Services example'. + + + Removing the need for .asmx files + + Unlike web pages, which use .aspx files to + store presentation code, and code-behind classes for the logic, web + services are completely implemented within the code-behind class. This + means that .asmx files serve no useful purpose, and as such they should + neither be necessary nor indeed required at all. + + Spring.NET allows application developers to expose existing web + services easily by registering a custom implementation of the + WebServiceHandlerFactory class and by creating a + standard Spring.NET object definition for the service. + + By way of an example, consider the following web service... + + +namespace MyComany.MyApp.Services +{ + [WebService(Namespace="http://myCompany/services")] + public class HelloWorldService + { + [WebMethod] + public string HelloWorld() + { + return "Hello World!"; + } + } +} + + + This is just a standard class that has methods decorated with the + WebMethod attribute and (at the class-level) the + WebService attribute. Application developers can + create this web service within Visual Studio just like any other + class. + + All that one need to do in order to publish this web service + is: + + 1. Register the + Spring.Web.Services.WebServiceFactoryHandler as + the HTTP handler for *.asmx requests within one's + web.config file. + + +<system.web> + <httpHandlers> + <add verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/> + </httpHandlers> +</system.web> + + + Of course, one can register any other extension as well, but + typically there is no need as Spring.NET's handler factory will behave + exactly the same as a standard handler factory if said handler factory + cannot find the object definition for the specified service name. In + that case the handler factory will simply look for an .asmx file. + + If you are using IIS7 the following configuration is needed + + <system.webServer> + <validation validateIntegratedModeConfiguration="false"/> + <handlers> + <add name="SpringWebServiceSupport" verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/> + </handlers> +</system.webServer> + + 2. Create an object definition for one's web + service. + + <object name="HelloWorld" type="MyComany.MyApp.Services.HelloWorldService, MyAssembly" abstract="true"/> + + Note that one is not absolutely required to make the web service + object definition abstract (via the + abstract="true" attribute), but this is a recommended + best practice in order to avoid creating an unnecessary instance of the + service. Because the .NET infrastructure creates instances of the target + service object internally for each request, all Spring.NET needs to + provide is the System.Type of the service class, + which can be retrieved from the object definition even if it is marked + as abstract. + + That's pretty much it as we can access this web service using the + value specified for the name attribute of the object + definition as the service name: + + http://localhost/MyWebApp/HelloWorld.asmx + + + + Injecting dependencies into web services + + For arguments sake, let's say that we want to change the + implementation of the HelloWorld method to make the + returned message configurable. + + One way to do it would be to use some kind of message locator to + retrieve an appropriate message, but that locator needs to implemented. + Also, it would certainly be an odd architecture that used dependency + injection throughout the application to configure objects, but that + resorted to the service locator approach when dealing with web + services. + + Ideally, one should be able to define a property for the message + within one's web service class and have Spring.NET inject the message + value into it: + + +namespace MyApp.Services +{ + public interface IHelloWorld + { + string HelloWorld(); + } + + [WebService(Namespace="http://myCompany/services")] + public class HelloWorldService : IHelloWorld + { + private string message; + public string Message + { + set { message = value; } + } + + [WebMethod] + public string HelloWorld() + { + return this.message; + } + } +} + + + The problem with standard Spring.NET DI usage in this case is that + Spring.NET does not control the instantiation of the web service. This + happens deep in the internals of the .NET framework, thus making it + quite difficult to plug in the code that will perform the + configuration. + + The solution is to create a dynamic server-side proxy that will + wrap the web service and configure it. That way, the .NET framework gets + a reference to a proxy type from Spring.NET and instantiates it. The + proxy then asks a Spring.NET application context for the actual web + service instance that will process requests. + + This proxying requires that one export the web service explicitly + using the Spring.Web.Services.WebServiceExporter + class; in the specific case of this example, one must also not forget to + configure the Message property for said + service: + + +<object id="HelloWorld" type="MyApp.Services.HelloWorldService, MyApp"> + <property name="Message" value="Hello, World!"/> +</object> + +<object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web"> + <property name="TargetName" value="HelloWorld"/> +</object> + + + The WebServiceExporter copies the existing + web service and method attribute values to the proxy implementation (if + indeed any are defined). Please note however that existing values can be + overridden by setting properties on the + WebServiceExporter. + + + Interface Requirements + + In order to support some advanced usage scenarios, such as the + ability to expose an AOP proxy as a web service (allowing the addition + of AOP advices to web service methods), Spring.NET requires those + objects that need to be exported as web services to implement a + (service) interface. + + Only methods that belong to an interface will be exported by the + WebServiceExporter. + + + + + Exposing PONOs as Web Services + + Now that we are generating a server-side proxy for the service, + there is really no need for it to have all the attributes that web + services need to have, such as WebMethod. Because + .NET infrastructure code never really sees the "real" service, those + attributes are redundant as the proxy needs to have them on its methods, + because that's what .NET deals with, but they are not necessary on the + target service's methods. + + This means that we can safely remove the + WebService and WebMethod + attribute declarations from the service implementation, and what we are + left with is a plain old .NET object (a PONO). The example above would + still work, because the proxy generator will automatically add + WebMethod attributes to all methods of the + exported interfaces. + + However, that is still not the ideal solution. You would lose + information that the optional WebService and + WebMethod attributes provide, such as service + namespace, description, transaction mode, etc. One way to keep those + values is to leave them within the service class and the proxy generator + will simply copy them to the proxy class instead of creating empty ones, + but that really does defeat the purpose. + + To add specific attributes to the exported web service, you can + set all the necessary values within the definition of the service + exporter, like so... + + +<object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web"> + <property name="TargetName" value="HelloWorld"/> + <property name="Namespace" value="http://myCompany/services"/> + <property name="Description" value="My exported HelloWorld web service"/> + <property name="MemberAttributes"> + <dictionary> + <entry key="HelloWorld"> + <object type="System.Web.Services.WebMethodAttribute, System.Web.Services"> + <property name="Description" value="My Spring-configured HelloWorld method."/> + <property name="MessageName" value="ZdravoSvete"/> + </object> + </entry> + </dictionary> + </property> +</object> + +// or, once configuration improvements are implemented... +<web:service targetName="HelloWorld" namespace="http://myCompany/services"> + <description>My exported HelloWorld web service.</description> + <methods> + <method name="HelloWorld" messageName="ZdravoSvete"> + <description>My Spring-configured HelloWorld method.</description> + </method> + </methods> +</web:service> + + + Based on the configuration above, Spring.NET will generate a web + service proxy for all the interfaces implemented by a target and add + attributes as necessary. This accomplishes the same goal while at the + same time moving web service metadata from implementation class to + configuration, which allows one to export pretty much + any class as a web service. + + The WebServiceExporter also has a + TypeAttributes IList property for applying attributes + at the type level. + The attribute to confirms to the WSI basic profile 1.1 is not + added by default. This will be added in a future release. In the + meantime use the TypeAttributes IList property to add + [WebServiceBinding(ConformsTo=WsiProfiles.BasicProfile1_1)] + to the generated proxy. + + + One can also export only certain interfaces that a service class + implements by setting the Interfaces property of the + WebServiceExporter. + + + Distributed Objects Warning + + Distributed Objects Warning + + Just because you can export any object as a + web service, doesn't mean that you should. + Distributed computing principles still apply and you need to make sure + that your services are not chatty and that arguments and return values + are Serializable. + + You still need to exercise common sense when deciding whether to + use web services (or remoting in general) at all, or if local service + objects are all you need. + + + + + Exporting an AOP Proxy as a Web Service + + It is often useful to be able to export an AOP proxy as a web + service. For example, consider the case where you have a service that is + wrapped with an AOP proxy that you want to access both locally and + remotely (as a web service). The local client would simply obtain a + reference to an AOP proxy directly, but any remote client needs to + obtain a reference to an exported web service proxy, that delegates + calls to an AOP proxy, that in turn delegates them to a target object + while applying any configured AOP advice. + + Effecting this setup is actually fairly straightforward; because + an AOP proxy is an object just like any other object, all you need to do + is set the WebServiceExporter's + TargetName property to the id (or + indeed the name or alias) of the + AOP proxy. The following code snippets show how to do this... + + +<object id="DebugAdvice" type="MyApp.AOP.DebugAdvice, MyApp"/> + +<object id="TimerAdvice" type="MyApp.AOP.TimerAdvice, MyApp"/> + +<object id="MyService" type="MyApp.Services.MyService, MyApp"/> + +<object id="MyServiceProxy" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"> + <property name="TargetName" value="MyService"/> + <property name="IsSingleton" value="true"/> + <property name="InterceptorNames"> + <list> + <value>DebugAdvice</value> + <value>TimerAdvice</value> + </list> + </property> +</object> + +<object id="MyServiceExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web"> + <property name="TargetName" value="MyServiceProxy"/> + <property name="Name" value="MyService"/> + <property name="Namespace" value="http://myApp/webservices"/> + <property name="Description" value="My web service"/> +</object> + + + That's it as every call to the methods of the exported web service + will be intercepted by the target AOP proxy, which in turn will apply + the configured debugging and timing advice to it. + + + + + Client-side + + On the client side, the main objection the Spring.NET team has is + that client code becomes tied to a proxy class, and + not to a service interface. Unless you make the proxy + class implement the service interface manually, as described by Juval Lowy + in his book "Programming .NET Components", application code will be less + flexible and it becomes very difficult to plug in different service + implementation in the case when one decides to use a new and improved web + service implementation or a local service instead of a web service. + + The goal for Spring.NET's web services support is to enable the easy + generation of client-side proxies that implement a specific service + interface. + + + Using VS.NET generated proxy + + The problem with the web-service proxy classes that are generated + by VS.NET or the WSDL command line utility is that they don't implement + a service interface. This tightly couples client code with web services + and makes it impossible to change the implementation at a later date + without modifying and recompiling the client. + + Spring.NET provides a simple IFactoryObject + implementation that will generate a "proxy for + proxy" (however obtuse that may sound). Basically, the + Spring.Web.Services.WebServiceProxyFactory class + will create a proxy for the VS.NET- / WSDL-generated proxy that + implements a specified service interface (thus solving the problem with + the web-service proxy classes mentioned in the preceding + paragraph). + + At this point, an example may well be more illustrative in + conveying what is happening; consider the following interface definition + that we wish to expose as a web service... + + +namespace MyCompany.Services +{ + public interface IHelloWorld + { + string HelloWorld(); + } +} + + + In order to be able to reference a web service endpoint through + this interface, you need to add a definition similar to the example + shown below to your client's application context: + + +<object id="HelloWorld" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services"> + <property name="ProxyType" value="MyCompany.WebServices.HelloWorld, MyClientApp"/> + <property name="ServiceInterface" value="MyCompany.Services.IHelloWorld, MyServices"/> +</object> + + + What is important to notice is that the underlying implementation + class for the web service does not have to implement the same + IHelloWorld service interface... so long as + matching methods with compliant signatures exist (a kind of duck + typing), Spring.NET will be able to create a proxy and delegate method + calls appropriately. If a matching method cannot be found, the + Spring.NET infrastructure code will throw an exception. + + That said, if you control both the client and the server it is + probably a good idea to make sure that the web service class on the + server implements the service interface, especially if you plan on + exporting it using Spring.NET's + WebServiceExporter, which requires an interface + in order to work. + + + + Generating proxies dynamically + + The WebServiceProxyFactory can also + dynamically generate a web-service proxy. The XML object definition for + this factory object is shown below + + + <object id="calculatorService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services"> + <property name="ServiceUri" value="http://myServer/Calculator/calculatorService.asmx"/> + <!--<property name="ServiceUri" value="file://~/calculatorService.wsdl"/>--> + <property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract"/> + <!-- Dependency injection on Factory's product : the proxy instance of type SoapHttpClientProtocol --> + <property name="ProductTemplate"> + <object> + <property name="Timeout" value="10000" /> <!-- 10s --> + </object> + </property> + </object> + + + + One use-case where this proxy is very useful is when dealing with + typed data sets through a web service. Leaving the pros and cons of this + approach aside, the current behavior of the proxy generator in .NET is + to create wrapper types for the typed dataset. This not only pollutes + the solution with extraneous classes but also results in multiple + wrapper types being created, one for each web service that uses the + typed dataset. This can quickly get confusing. The proxy created by + Spring allows you to reference you typed datasets directly, avoiding the + above mentioned issues. + + + + Configuring the proxy instance + + The WebServiceProxyFactory also implements + the interface, + Spring.Objects.Factory.IConfigurableFactoryObject, + allowing to specify configuration for the product that the + WebServiceProxyFactory creates. This is done by + specifying the ProductTemplate property. This is particularly useful for + securing the web service. An example is shown below. + + + <object id="PublicarAltasWebService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services"> + <property name="ProxyType" value="My.WebService" /> + <property name="ServiceInterface" value="My.IWebServiceInterface" /> + <property name="ProductTemplate"> + <object> + <!-- Configure the web service URL --> + <property name="Url" value="https://localhost/MyApp/webservice.jws" /> + <!-- Configure the Username and password for the web service --> + <property name="Credentials"> + <object type="System.Net.NetworkCredential, System"> + <property name="UserName" value="user"/> + <property name="Password" value="password"/> + </object> + </property> + <!-- Configure client certificate for the web service --> + <property name="ClientCertificates"> + <list> + <object id="MyCertificate" type="System.Security.Cryptography.X509Certificates.X509Certificate2, System"> + <constructor-arg name="fileName" value="Certificate.p12" /> + <constructor-arg name="password" value="notgoingtotellyou" /> + </object> + </list> + </property> + </object> + </property> +</object> + + + For an example of how using SOAP headers for authentication using + the WebServiceExporter and WebServiceProxyFactory, refer to this solution + on our wiki. + + + \ No newline at end of file diff --git a/doc/reference/src/windows-service.xml b/doc/reference/src/windows-service.xml new file mode 100644 index 00000000..470b2acf --- /dev/null +++ b/doc/reference/src/windows-service.xml @@ -0,0 +1,643 @@ + + Windows Services + + + Remarks + + This is functionality that will be included after the + 1.0 release. If you want to use these features please get the + code from CVS + (instructions) or from the download section of the Spring.NET website that contains an + .zip with the full CVS tree. + In addition to this documentation + you can refer to the example program located at + examples\Spring\Spring.Examples.WindowsService + to better understand the package. Please check the Spring.NET + website + for the latest updates to this document. + + + + Introduction + + Developers usually create Windows Services using the + Visual Studio .NET wizard. While not difficult to do, this + procedure is repetative and does not encourage separation between + infrastructure code (windows service) and application code. This is + generally considered a "bad thing" but you can certainly disagree. + + + As Spring.NET can provide an explicitly managed + initialize/destroy lifecycle for singleton objects, there is + a natural synergy with the lifecycle of a Windows service. + As such, it could be very convenient to expose a Spring application + context as a Windows service. Starting and stopping the service corresponds + to creating and destroying an application context and its + contained objects. This approach provides a high level means to + declare what objects are created and destroyed when developing + a Windows service. + + + To do that, Spring.NET requires the installation of one physical + service able to run as services as many applications as you want - each a + logical independent service in their own application domain. + By default, the deployment and updating of the service can also + be done by copying the relevant executables to a special directory. + + + The executable that at present provides these features is the + Spring.Services.WindowsService.Process.exe + assembly. It makes heavy use of classes and interfaces definde in + the Spring.Services.WindowsService.Common.dll + assembly. You should reference the common assembly it if you want to + follow the advice on customization contained in the following sections + + + The benefits of this approach, a part from those given by separating + infrastructure code and application code (a field where Spring.NET + tries hard to succeed) is that you can think about installing a new + service at client site by simply dropping a new application assembly + in a remote directory. + + + + + The <literal>Spring.Services.WindowsService.Process.exe</literal> application + + Installing + + The installation can be done in two ways, using the .NET SDK + installutil.exe tool or using the more mundane + Spring.Services.WindowsService.Installer.exe; + while the former is the standard, the latter is probably + more flexible. It allows you to customize the name/display name of the + service and has the ability to install multiple times the same assembly + with different names. This can be useful in a + number of scenarios, especially where you don't like, for some + reasons, to run several different logical services under the + same physical windows service. + + + Be aware of the fact that the service will be installed as + running with the system account (installing with a specific + user account seems a bit buggy on Windows XP) + + + That said, while installutil + + is documented on its own , + the command line for + Spring.Services.WindowsService.Installer.exe + is as follow: + Spring.Services.WindowsService.Installer.exe + +usage: + install service-exe-path service-display-name service-name + uninstall service-name [i|u] service-exe-path service-display-name service-name + for example, to install, you can invoke it with the following: + ... install Spring.Services.WindowsService.Process.exe "Spring.Service Support" spring-service + and to uninstall it: + ... uninstall spring-service + + + + Configuration + + The standard .NET .config file + can be used to tune some parameters of + Spring.Services.WindowsService.Process.exe, + (including log4net settings, for which it is recomended to consult + the log4net documentation). + + + This file also define the context run by this process; here the file in its current beauty: + +<configuration> + + <configSections> + <section name="log4net" type="System.Configuration.IgnoreSectionHandler" /> + <sectionGroup name="spring"> + <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" /> + <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> + </sectionGroup> + </configSections> + + <spring> + <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core"> + <resource uri="file://~/service-process-definition.xml" /> + </context> + </spring> + + <system.runtime.remoting> + <application> + <channels> + <channel ref="http" port="1234" /> + </channels> + </application> + </system.runtime.remoting> + + <log4net> + <!-- see http://logging.apache.org/log4net/release/manual/introduction.html --> + <appender name="RollingFile" type="log4net.Appender.RollingFileAppender"> + <layout type="log4net.Layout.PatternLayout"> + <conversionPattern value="%d [%t] %-5p %c{1} - %m%n" /> + </layout> + <file value="logs/Spring.Service.Process.log" /> + <appendToFile value="true" /> + <maximumFileSize value="500KB" /> + <maxSizeRollBackups value="5" /> + </appender> + <appender name="OutputDebugString" type="log4net.Appender.OutputDebugStringAppender"> + <layout type="log4net.Layout.PatternLayout"> + <conversionPattern value="%d{HH:mm:ss,fff} %-5p %c{2}(line:%L) - %m%n" /> + </layout> + </appender> + <root> + <level value="OFF" /> + </root> + <logger name="Spring.Services"> + <level value="ALL" /> + <appender-ref ref="RollingFile" /> + <appender-ref ref="OutputDebugString" /> + </logger> + </log4net> + +</configuration> + + + As you see, the context is defined in another file: let's review the objects it defines. + + + Firstly, it is worth notice that in order to 'localize' the service (i.e. to know where it is installed to use that directory as + base for the deploy dir as in the above file) you should define an object like this: the name is not + very important, it is important that it is an IObjectFactoryPostProcessor and so will be + automatically applied to this application context: + +<!-- provides access to the ${spring.services.process.base.dir} property --> +<object + name="localizer" + type="Spring.Services.WindowsService.Common.Localizer+ForProcess, Spring.Services.WindowsService.Common"> + <!-- change this to access the property with another prefix, for example ${foo.process.base.dir} + <property name="prefix" value="foo"/> + --> +</object> + + + In that object definition you can customize the prefix for the following string + +public static readonly string SpringServicesProcessBaseDirFormat = "{0}.process.base.dir"; + but you usually won't need it; the default value is + +public static readonly string DefaultPrefix = "spring.services"; + + + The sole important object defined by this context, i.e. the main object run by the service. + The thing you can (and should) configure is the path to the folder you will use as the deploy location; + the current definition, to avoid the need for a fully qualified path (e.g.: c:\spring\services) uses + the properties made available by the localizer above: + +<object + name="service" + type="Spring.Services.WindowsService.Common.DefaultService, Spring.Services.WindowsService.Common" + init-method="Start" + destroy-method="Stop"> + <property name="DeployPath" value="${spring.services.process.base.dir}/deploy"/> +</object> + + + The above object is then easily remoted using spring remoting utilities (please notice you should tune the remoting configuration + listed in the standard .NET .config file, listed above): + +<object name="remoted.service" type="Spring.Remoting.SaoExporter, Spring.Services"> + <property name="TargetName" value="service"/> + <property name="ServiceName" value="SpringWindowsService.rem"/> +</object> + + + + + + Running an application context as a windows service + + If you package an application using the layout and + conventions described here, you'll be able to run an + application context as a Windows Service. + The conventions used are modeled after those used by ASP.NET + and are very easy to follow. + + + As already said, you'll have a Spring.NET application context running in + a dedicated AppDomain hosted in a process running + as a windows service: that process is able to run many application contexts + simultaneously. + + A complete application runable as service consists of a + directory containing: + + + + The .NET configuration file + service.config: + this file should define your application context. + Moreover this files will be used + by the CLR to configure the application domain + your application will run in, exactly as you expect. + This file has the same role of ASP.NET Web.config + file. + + + + + Optional: an xml context file (watcher.xml) + defining the watcher for your application. + The watcher controls the automatic redeployment of the + service and is discussed more in the following section. + + + + Recomended: along the lines of ASP.NET convention, a bin + subdirectory containing all + the assemblies your application needs; you can of course put + your assemblies in the same directory where you put + service.config but this is not encouraged ... + + + + + + <literal>service.config</literal> + + This is the standard .NET configuration file for the + AppDomain that will host your application. It is + semantically equivalent to the ASP.NET Web.config + file. + + + log4net users please notice that (as + of 1.2 beta 9) file appenders, when dealing with a relative + file name, assume it is relative to the application + domain code base. If you use log4net, it is very handy with the mechanics used by + Spring Windows Service as every log file you will specify will + be relative the directory containing the service application. + + + + + This file should also define your application context. When the + service is started and stopped, the corresponding lifecycle methods + are called on all the singletons defined. Of course, singletons are + automatically instantiated by the application context when the + service starts. For more information on lifecycles in Spring.NET see + + Here an example taken from the tests: + +<configuration> + + <configSections> + <sectionGroup name="spring"> + <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" /> + <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> + </sectionGroup> + </configSections> + + <appSettings> + <add key="port" value="10"/> + </appSettings> + + <spring> + <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core"> + <resource uri="file://~/service.xml" /> + </context> + </spring> + +</configuration> + + + In this case the context is (again!) defined in another file (author's personal taste...) and the only 'service' is the + echo object (there is also a PropertyPlaceholderConfigurer just to make the example + more realistic): + +<objects xmlns="http://www.springframework.net" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd"> + + <object name="echo" + type="Spring.Services.WindowsService.Samples.Echo, Spring.Services.WindowsService.Tests" + init-method="Start" destroy-method="Stop"> + <property name="port"><value>${port}</value></property> + </object> + + <object id="configurer" type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core"> + <property name="locations"> + <list> + <value>file://~/service.config</value> + </list> + </property> + <property name="configSections"> + <list> + <value>appSettings</value> + </list> + </property> + </object> + +</objects> + + + Let the application know where it is + + There are some properties you may need at runtime, when your services + will run, and you cannot know in advance. Hopefully, your xml + definition file will allow to find the information it needs using some + predefined variables you can use inside the service definition file + with the standard + NAnt style ${property name} syntax. + These properies are: + + + spring.services.application.fullpath + that will be replaced with the full path of the application's + AppDomain.BaseDirectory, i.e., where your + application has been deployed; + + + spring.services.application.name that + will be replaced with the name of the subdirectory where the + application has been deployed. Each application is deployed in + its own directory, of course; + + + + + These properties are accessible only if one defines a localizer in the + context like this (the localizer is a special IObjectFactoryPostProcessor: + +<!-- provides access to the ${spring.services.application.*} properties --> +<object + name="localizer" + type="Spring.Services.WindowsService.Common.Localizer+ForApplication, Spring.Services.WindowsService.Common"> + <!-- change this to access the property with another prefix, for example ${foo.application.base.dir} + --> + <property name="prefix" value="myPrefix"/> +</object> + + + As you can see above, one can easily change the prefix used by that localizer and then write someting like: + +<object name="simple" + type="Spring.Services.WindowsService.Samples.Simple, Spring.Services.WindowsService.Tests" + init-method="Start" destroy-method="Stop"> + <constructor-arg index="0" value="${myPrefix.application.name},${myPrefix.application.fullPath}"/> + <property name="AppName"> + <value>${myPrefix.application.name}</value> + </property> + <property name="AppFullPath"> + <value>${myPrefix.application.fullpath}</value> + </property> +</object> + + + + + + <literal>watcher.xml</literal> - optional + + This file allows you to optionally define a watcher for your application + that can automatically redeploy it when needed. + + + The important thing to notice is that you can define your own + application watcher, named watcher. Here it is used + a watcher that listen for changes on the filesystem, configured to + listen for some changes and to ignore others. + + + You can provide your own implementation defining an object named + watcher that implements + Spring.Services.WindowsService.Common.Deploy.IApplicationWatcher: + +/// <summary> +Interface defining the contract for an application watcher. +<p>An application watcher is responsible to dispatch an +<see cref="IApplicationWatcherFactory">event</see> whenever it thinks the +application has been updated.</p> +<p>Usually it should not raise other kind of events +as they are usually raised by the <see cref="FileSystemApplicationWatcher"/> +that creates the watcher itself</p> +</summary> +<remarks>Usually instances of this interface need to be disposed</remarks> +<seealso cref="DeployEventArgs"/> +<seealso cref="IDeployLocation"/> +<seealso cref="DeployEventType.ApplicationUpdated"/> +<seealso cref="DeployEventAggregator"/> +<seealso cref="IDeployLocation"/> +<seealso cref="DeployEventType"/> +public interface IApplicationWatcher : IDisposable +{ + /// <summary> + /// The watched application + /// </summary> + IApplication Application {get; } + + /// <summary> + /// Start to watch the application, using the given dispatcher to + /// dispatch deply events + /// </summary> + /// <param name="dispatcher">the dispatcher used to raise deploy events</param> + void StartWatching (IDeployEventDispatcher dispatcher); + + /// <summary> + /// Stop to watch the application. + /// </summary> + void StopWatching (); + + /// <summary> + /// If physical events watched by this watcher should be filtered, this methods + /// will allow to set filters that allows and disallows the event to be raised + /// by the watcher. + /// </summary> + /// <param name="allows">the list of allowing filters</param> + /// <param name="disallows">the list of disallowing filters</param> + /// <seealso cref="FilteringSupport"/> + /// <seealso cref="RegularExpressionFilter"/> + void SetFilters (IList allows, IList disallows); +} + + + Please notice that this interface is currently a movable target and + will probably change before the first official release (this will probably + affect also the way a watcher will know about the application it should + monitor, as shown in a few lines). + + A tipical example of this file is give here: + +<objects> + + <object name='watcher' + type='Spring.Services.WindowsService.Common.Deploy.FileSystem.FileSystemApplicationWatcher'> + <!-- + we can get access to the IApplication we are asked to monitor + using a reference like the following + --> + <constructor-arg ref='.injected.application'/> + + <!-- sometimes the windows OS will decide to not give you the same case you see in explorer: + in fact one should consider this OS case-insensitive with regard to file names ... + The following property, true by default can however be tuned + <property name="ignoreCase" value="false"/> + --> + + <property name="includes"> + <list> + <value>wwwroot/bin/*.*</value> + <value>service.config</value> + <value>service.xml</value> + </list> + </property> + + <!-- + <property name="excludes"> + <list> + <value>Db/**/*.*</value> + <value>Jobs</value> + <value>Jobs</value> + <value>**/*.log</value> + </list> + </property> + --> + + </object> + +</objects> + + + As you can see, if you need it, you can reference the + Spring.Services.WindowsService.Common.IApplication + object that your watcher should watch using the name + .injected.application. + + + + <literal>bin</literal> directory - optional + + This is, by default, the folder where your assemblies are placed + in the same way they are in an ASP.NET application. + + + Putting assemblies there is more a convention and maybe a good + practice (they are isolated from other artifacts, but maybe you will + prefer to use another directory (modify the + service.config file accordingly) or the application + directory directly (= bin parent). + + + Be aware of the fact that the process in which your application will + run will have its own PATH environmental variable. As such + don't expect to be successfull using dlls imported + with [DllImport] if they are not in the system PATH of the + hosting machine: while it is well known that the CLR fusion + algorithm will not consider the PATH variable, you may be biten + by assemblies using non-system dlls (SQLite and Firebird ADO.NET + providers are good examples). + + Reiterating, one can put assemblies in another directory + under the application directory tree, and write + the .NET configuration file (service.config) + accordingly: .NET probing algorithm is always in place. + + + Please notice that it is not required that + your application uses or include any of the Spring.NET assemblies: + any object in any assembly, given it has lifecycle methods, can + be run as a service: non invasive infrastructure support courtesy + of Spring.NET! + + + + + + Customizing or extending + + It should be said that support for windows service has been initially + developed with a clear but limited set of 'extension points' in mind, + mainly related to the way you can deploy your services: + deploy location (filesystem, zip archives, mailbox, urls, ...), + (auto-)updating features, and so on. + + + To better understand the following discussion, the following figure + depicts some of the inner details of + Spring.Services.WindowsService.Process.exe + at run-time: + + + + + + Spring.Services.WindowsService.Process.exe run-time details + + + + + The <literal>.config</literal> file + + + The executable Spring.Services.WindowsService.Process.exe + is somewhat configured by the corresponding + .config file. + Please notice that this file is the most important extension point + for windows service support, and it will probably be made more powerful + and flexible in the future. + + + For applications deployed in the standard way (i.e. on the filesystem + as explained above) the updating features are configured by the + watcher.xml file, if present, + as already seen. + + + There should be however, other ways to deploy your applications, + maybe just as zip files dropped somewhere on the web or sent via + e-mail. + + + For these scenarios, your deploy location will be something that + implements + Spring.Services.WindowsService.Common.Deploy.IDeployLocation. + + Please notice that, while questionable, it actually entends + IDisposable this has been done + as it is possible that a deploy location holds resources that should be + released, for example network connections, lock files or the like: + + +/// <summary> +/// Interface defining how a deploy location should look like +/// </summary> +public interface IDeployLocation : IDeployEventSource, IDisposable +{ + /// <summary> + /// The list of applications deployed at this location + /// Usually non-valid applications are not listed + /// </summary> + /// <seealso cref="Application"/> + IList Applications { get; } +} + +/// <summary> +/// Interface defining the contract for an object acting as the source of +/// deploy events (application added, removed, updated) +/// </summary> +/// <seealso cref="DeployEventArgs"/> +/// <seealso cref="DeployEventHandler"/> +public interface IDeployEventSource +{ + /// <summary> + /// The multicaster for deploy events + /// </summary> + event DeployEventHandler DeployEvent; +} + + + + diff --git a/doc/reference/src/xml-config-reference.xml b/doc/reference/src/xml-config-reference.xml new file mode 100644 index 00000000..480fe28b --- /dev/null +++ b/doc/reference/src/xml-config-reference.xml @@ -0,0 +1,609 @@ + + + XML Configuration Reference + + + Introduction + + This chapter contains an exhaustive listing for pretty much every + possible XML configuration scenario for Spring.NET's XML based + configuration. If you need to configure an object in a Spring.NET IoC + container, and you are using Spring.NET's XML configuration option to do + so (which, short of programmatic configuration, is pretty much all you can + use for configuration right now), then this chapter will most probably + have an example XML fragment that can illustrate what you need to + do. + + Please note that this chapter is not a knee-jerk or belated response + to addressing any perceived complexity in the Spring.NET XML + configuration. Spring.NET's XML configuration syntax is, in the opinion of + the developers (for what that's worth), eminently readable... one has + <objects/>, these objects have zero or more + <constructor-arg/> or + <property/> elements that are generally + <ref/>erences to other + <object/>s. To use an analogy, Spring.NET's XML + configuration reads like a William Weaver translation of an Umberto Eco + novel... the words (the XML elements) are easy, but the devil (and + salvation) is in the detail. + + + + Object Configuration + + This section details the configuration of one's object definitions. + It contains fragments of XML that illustrate the absolute basics, such as + how to create a simple object with no dependencies, all the way through to + often overlooked features such as instantiating an object from of a method + call to another object. + + + Objects + + This section details how to define an object in Spring.NET XML. If + you need somewhere to start, this is the place. + + The section starts off with the absolute basics of defining an + object (the <object/> element), and then describes the setting of + constructor arguments and properties. Once you are down with those three + cornerstones of configuration (yes, that is really it), the rest of the + text in this reference is spent describing the values that one can + supply to those constructor arguments and property values. + + + Plain Object Definition + + Find below an example of defining an object that has no + dependencies. + + <object name="service" + type="Example.Foo, FooAssembly"/> + + + + This is the name of the object ( + + + + ). + + + + This is the assembly qualified name of the object's Type or class ( + + + + ). + + + + Please note that the + + scope + + of the object is implicitly + + singleton + + (see + + + + of this chapter and + + + + in the reference documentation). + + + + Defining this object in one's context and then retrieving said + object from said context will result in the creation of an instance of + the Foo class. The default constructor of the + Foo class will be invoked, and since no + properties and other other configuration elementts are present, the + resulting object will be returned as is. The simple case really is as + simple as that. + + Further (un-annotated) examples of defining an object that has + no dependencies can be found below... + + <object id="anException" type="System.ArgumentException, Mscorlib"/> + + <object id="anEmptyList" type="System.Collections.ArrayList, Mscorlib"/> + + <object id="anSqlCommand" type="System.Data.SqlClient.SqlCommand, System.Data"/> + + + + Constructor Arguments + + + + + + + + Properties + + + + + + + + + Object Types + + + + + Primitives + + This section details the various configuration options available + for injecting, handoing, and otherwise defining the classic primitive + types. The string and + date types are not primitives, but they are + described here nevertheless. + + Spring.NET uses the TypeConverter + mechanism that is part of the SDK to handle the conversion from string + values in one's XML configuration to the appropriate type. This + reference does not go into detail about this mechanism, so you may + wish to consult the attendant section of the reference material proper + if you are having type conversion issues... + + + Numbers + + This section describes configuring the various numeric types + supported by the CLR. Any numeric type can be injected into an + object, or made available as an object definition in its own + right. + + Find below the class definition that is used to illustrate + configuring numeric values in the following examples. + + [C#] +namespace Example +{ + public class Gauge + { + private int setting; + private float sensitivity; + + public int Setting + { + set { this.setting = value; } + } + + public float Sensitivity + { + set { this.sensitivity = value; } + } + } +} + + <object id="aGauge" type="Example.Gauge, FooAssembly"> + <property name="setting" value="213"/> +</object> + + We can also use any of the normal supported conventions (such + as hexadecimal) to set values, as shown below. + + <object id="aGauge" type="Example.Gauge, FooAssembly"> + <property name="setting" value="0x10"/> +</object> + + <object id="aGauge" type="Example.Gauge, FooAssembly"> + <property name="sensitivity" value="31000.00"/> +</object> + + Given the above examples, it is trivial to extrapolate the + configuration of longs and the various unsigned variants of the + numeric types, so no examples of such configuration will be + given. + + + + Dates + + + + Find below the class definition that is used to illustrate + configuring date values in the following examples. + + [C#] +namespace Example +{ + public class Gauge + { + private DateTime lastChecked; + + public DateTime LastChecked + { + set { this.lastChecked = value; } + } + } +} + + <object id="aGauge" type="Example.Gauge, FooAssembly"> + <property name="lastChecked" value=""/> +</object> + + <object id="aGauge" type="Example.Gauge, FooAssembly"> + <property name="lastChecked" value=""/> +</object> + + <object id="aGauge" type="Example.Gauge, FooAssembly"> + <property name="lastChecked" value=""/> +</object> + + + + Booleans + + Configuring boolean values in one's configuration file (s) is + pretty much the same as configuring numeric and date values... one + simply uses the value attribute or + <value/> element (as appropriate). The only + caveat (if indeed it can be considered to be a caveat) is that the + value must be one of the following + two values... + + true + + + + false + + + + Find below the class definition that is used to illustrate + configuring boolean values in the following examples. + + [C#] +namespace Example +{ + public class Gauge + { + private bool isSwitchedOn; + + public bool IsSwitchedOn + { + set { this.isSwitchedOn = value; } + } + } +} + + <object id="aGauge" type="Example.Gauge, FooAssembly"> + <property name="IsSwitchedOn" value="true"/> +</object> + + <object id="aGauge" type="Example.Gauge, FooAssembly"> + <property name="IsSwitchedOn" value="false"/> +</object> + + Please note that as with pretty much everything in Spring.NET, + the true and false string + values are not case sensitive. The string values + TRUE, FALSE, + True, etc. are all valid. + + If you wanted to use different values for the + true and false string values + (perhaps on and off values in + the case of the preceding Gauge example), you + would need to register a custom TypeConverter + implementation (see ). + + + + Strings + + Unsurprisingly, String values are the + easiest to configure. Consider the following example of strings that + are defined as top level objects... + + <object id="supportTeamEmail" type="string"> + <constructor-arg index="0" value="support@my.company.com"/> +</object> + + <object id="projectManagerEmail" type="string"> + <constructor-arg index="0" value="projectManager@my.company.com"/> +</object> + + The index="0" attribute value pair of the + constructor-arg element is required so that the + correct constructor of the String class can + be invoked... don't forget to put it in. (If you do forget to put it + in, then a not-very-helpful + UnsatisfiedDependencyException will be thrown + by the Spring.NET container). + + + + Enumerations + + Find below the class definition and XML snippets that + illustrate the configuration of enumerations. + + [C#] +namespace Example +{ + public enum RunningMode + { + Off, + Starting, + Started, + SwitchingOff, + Off + } + + public class Gauge + { + private RunningMode runMode; + + public RunningMode RunMode + { + set { this.runMode = value; } + } + } +} + + <object id="aGauge" type="Example.Gauge, FooAssembly"> + <property name="RunMode" value="Starting"/> +</object> + + <object id="aGauge" type="Example.Gauge, FooAssembly"> + <property name="RunMode" value="SwitchingOff"/> +</object> + + Please note that as with pretty much everything in Spring.NET, + the string passed to the value of the value + attribute is not case sensitive. In the case of this specific + example, the string values starting and + SWITCHINGOFF are both valid (though not + recommended; it's always best to stick to the casing of the original + enum, to aid in refactorings). + + See also . + + + + + Collections + + + + + Arrays + + + + + + Lists + + + + + + Dictionaries + + + + + + Sets + + + + + + Custom Collection Types + + + + + + Everything Else + + + + + + + Nulls + + + + + + + Scope + + + + + Singleton + + + + + + Prototype + + + + + + Everything Else Scope Related + + + + + + + Factories + + Perhaps unsurprisingly, implementations of the classic Factory + pattern can be found all over the Spring.NET codebase... indeed, the + core IApplicationContext class is a compelling + example of a factory implementation (albeit a very sophisticated + example). Spring.NET's support for the factory pattern extends into two + distinct areas... supporting factories that are external to the + framework, and factories that are internal to the framework. + + External factory classes would include any factory classes that + you may have written: examples of this would include (perhaps) + IWiGFactory (to create + IWiG implementations), etc. You can integrate any + such existing factory classes directly into the Spring.NET container + using the factory method support provided by the Spring IoC container. + Examples of such integration are are provided below, but do see and for the full + lowdown. + + Spring.NET also has the notion of a special Factory + Object (and this notion is encapsulated by the + IFactoryObject interface). The + IFactoryObject interface is (unsurprisingly) a + factory for creating one or more objects. Please do read for a + comprehensive explanation of the IFactoryObject + interface and the Spring.NET container's special treatment of objects + that implement said interface. This section of the documentation will + show some example configuration for all (well, most) of the + IFactoryObject implementations that come provided + out of the box with every Spring.NET release. + + + Factory Methods + + + + + + Factory Objects + + This section of the documentation presents examples for most of + the IFactoryObject implementations that come + out of the box with every Spring.NET release. A notable exception to + this catalogue of IFactoryObject configuration + examples is the AOP-specific + ProxyFactoryObject... see for more details regarding that particular + IFactoryObject implementation. + + Most (if not all) of the IFactoryObject + implementations referenced in the following configuration examples can + be found in the Spring.Objects.Factory.Config + namespace; do also consult the attendant API documentation (because + most of the IFactoryObject implementations + carry configuration examples specific to the objects that they + create). + + + DelegateFactoryObject + + One can use the DelegateFactoryObject + to (unsurprisingly) create and configure + Delegate objects. One trenchant use case for + this IFactoryObject (and indeed the very + reason that prompted it's creation) is to create declaratively a + ConfigListener delegate for use with the + IBatis.NET project's SqlMapper class. This + approach (of using the DelegateFactoryObject) + allows one to keep all of one's SqlMapper + configuration together, nice and tidy, in the one place. + + So lets say we have a service object that we need to inject + with a delegate; class definitions for the class that has the + dependency on the delegate, the delegate class itself, and a class + that supplies the method that will be passed to the delegate when it + is created can be found below. + + [C#] +namespace Example +{ + public delegate void GaugeCallback (object sender, GuageEventArgs e); + + public class Gauge + { + private GaugeCallback callback; + + public GaugeCallback Callback + { + set { this.callback = value; } + } + + public void SomeOperation() { + // some logic... + callback(this, new GaugeEventArgs()); + } + } + + public class MyGaugeListener() { + + public void HandleGaugeOperation(object sender, GuageEventArgs e) { + // do something... + } + } +} + + The attendant configuration to supply an instance of the + Gauge class with a configured + GuageCallback delegate would look like + so... + + <objects xmlns="http://www.springframework.net"> + <object id="gauge" type="Example.Gauge, FooAssembly"> + <property name="callback"> + <object type="Spring.Objects.Factory.Config.DelegateFactoryObject"> + <property name="delegateType" value="Example.GaugeCallback, FooAssembly"/> + <property name="targetObject"> + <object type="Example.MyGaugeCallback, FooAssembly"/> + </property> + </object> + </property> + </object> +</objects> + + + + DictionaryFactoryObject + + + + + + Log4NetFactoryObject + + + + + + + + + + + + + + + Context Configuration + + This section details the configuration of one or more contexts... + i.e. not the objects themselves, but rather of the hierarchy of contexts + in which one's object definitions are contained. + + \ No newline at end of file diff --git a/doc/reference/src/xml-custom.xml b/doc/reference/src/xml-custom.xml new file mode 100644 index 00000000..803f9916 --- /dev/null +++ b/doc/reference/src/xml-custom.xml @@ -0,0 +1,388 @@ + + + Extensible XML authoring + +
+ Introduction + + Spring supports adding custom schema-based extensions to the basic + Spring XML format for defining and configuring objects. This section is + devoted to detailing how you would go about writing your own custom XML + object definition parsers and integrating such parsers into the Spring IoC + container. + + To facilitate the authoring of configuration files using a + schema-aware XML editor, Spring's extensible XML configuration mechanism + is based on XML Schema. If you are not familiar with Spring's current XML + configuration extensions that come with the standard Spring distribution, + please first read the appendix entitled . + + Creating new XML configuration extensions can be done by following + these (relatively) simple steps: + + + + Authoring an XML + schema to describe your custom element(s). + + + + Coding + a custom INamespaceParser + implementation (this is an easy step, don't worry). + + + + Coding one or + more IObjectDefinitionParser + implementations (this is where the real work is done). + + + + Registering + the above artifacts with Spring (this too is an easy step). + + + + What follows is a description of each of these steps. For the + example, we will create an XML extension (a custom XML element) that + allows us to configure objects of the type Regex + (from the System.Text.RegularExpressions namespace) in + an easy manner. When we are done, we will be able to define object + definitions of type Regex like this: + + <myns:regex id="regex" + pattern="(^\d{5}$)|(^\d{5}-\d{4}$)" + options="Compiled"/> + +
+ +
+ Authoring the schema + + Creating an XML configuration extension for use with Spring's IoC + container starts with authoring an XML Schema to describe the extension. + What follows is the schema we'll use to configure + Regex objects. + + <?xml version="1.0" encoding="utf-8" ?> +<xsd:schema id="myns" + xmlns="http://www.mycompany.com/schema/myns" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:objects="http://www.springframework.net" + xmlns:vs="http://schemas.microsoft.com/Visual-Studio-Intellisense" + targetNamespace="http://www.mycompany.com/schema/myns" + elementFormDefault="qualified" + attributeFormDefault="unqualified" + vs:friendlyname="Spring Regex Configuration" vs:ishtmlschema="false" + vs:iscasesensitive="true" vs:requireattributequotes="true" + vs:defaultnamespacequalifier="" vs:defaultnsprefix="" + > + + <xsd:import namespace="http://www.springframework.net"/> + + <xsd:element name="regex"> + <xsd:complexType> + <xsd:complexContent> + <xsd:extension base="objects:identifiedType"> + <xsd:attribute name="pattern" type="xsd:string" use="required"/> + <xsd:attribute name="options" type="xsd:string" use="optional"/> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + +</xsd:schema> + + The emphasized line contains an extension base for all tags that + will be identifiable (meaning they have an id attribute + that will be used as the object identifier in the container). We are able + to use this attribute because we imported the Spring-provided + 'objects' namespace. The vs: + prefixed elements are for better integration with intellisense in + VS.NET. + + The above schema will be used to configure + Regex objects, directly in an XML application + context file using the <myns:regex/> + element. + + <myns:regex id="usZipCodeRegex" + pattern="(^\d{5}$)|(^\d{5}-\d{4}$)" + options="Compiled"/> + + Note that after we've created the infrastructure classes, the above + snippet of XML will essentially be exactly the same as the following XML + snippet. In other words, we're just creating an object in the container, + identified by the name 'usZipCodeRegex' of type + Regex, with a couple of constructor arguments + set. + + <object id="usZipCodeRegex" type="System.Text.RegularExpressions.Regex, System"> + <constructor-arg name="pattern" value="(^\d{5}$)|(^\d{5}-\d{4}$)"/> + <constructor-arg name="options" value="Compiled"/> + </object> + + + The schema-based approach to creating configuration format allows + for tight integration with an IDE that has a schema-aware XML editor. + Using a properly authored schema, you can use intellisense to have a + user choose between several configuration options defined in the + enumeration. The schema for creating IDbProvider instances shows the use + of XSD enumerations. + +
+ +
+ Coding a <interfacename>INamespaceParser</interfacename> + + In addition to the schema, we need an + INamespaceParser that will parse all + elements of this specific namespace Spring encounters while parsing + configuration files. The INamespaceParser + should in our case take care of the parsing of the + myns:regex element. + + The INamespaceParser interface is + pretty simple in that it features just two methods: + + + + Init() - allows for initialization of + the INamespaceParser and will be + called by Spring before the handler is used + + + + IObjectDefinition Parse(Element, + ParserContext) - called when Spring encounters a + top-level element (not nested inside a object definition or a + different namespace). This method can register object definitions + itself and/or return a object definition. + + + + Although it is perfectly possible to code your own + INamespaceParser for the entire namespace + (and hence provide code that parses each and every element in the + namespace), it is often the case that each top-level XML element in a + Spring XML configuration file results in a single object definition (as in + our case, where a single <myns:regex/> element + results in a single Regex object definition). + Spring features a number of convenience classes that support this + scenario. In this example, we'll make use the + NamespaceParserSupport class: + + using Spring.Objects.Factory.Xml; + +namespace CustomNamespace +{ + [NamespaceParser( + Namespace = "http://www.mycompany.com/schema/myns", + SchemaLocationAssemblyHint = typeof(MyNamespaceParser), + SchemaLocation = "/CustomNamespace/myns.xsd" + ) + ] + public class MyNamespaceParser : NamespaceParserSupport + { + public override void Init() + { + RegisterObjectDefinitionParser("regex", new RegexObjectDefinitionParser()); + } + } +} + + Notice that there isn't actually a whole lot of parsing logic in + this class. Indeed... the NamespaceParserSupport + class has a built in notion of delegation. It supports the registration of + any number of IObjectDefinitionParser + instances, to which it will delegate to when it needs to parse an element + in it's namespace. This clean separation of concerns allows an + INamespaceParser to handle the + orchestration of the parsing of all of the custom + elements in it's namespace, while delegating to + IObjectDefinitionParsers to do the grunt work of the + XML parsing; this means that each + IObjectDefinitionParser will contain just + the logic for parsing a single custom element, as we can see in the next + step. + + To help in the registration of the parser for this namespace, the + NamespaceParser attribute is used to map the XML + namespace string, i.e. + http://www.mycompany.com/schema/myns, to the location + of the XML Schema file as an embedded assembly resource. +
+ +
+ Coding an + <interfacename>IObjectDefinitionParser</interfacename> + + A IObjectDefinitionParser will be + used if the INamespaceParser encounters an + XML element of the type that has been mapped to the specific object + definition parser (which is 'regex' in this case). In + other words, the IObjectDefinitionParser is + responsible for parsing one distinct top-level XML + element defined in the schema. In the parser, we'll have access to the XML + element (and thus it's subelements too) so that we can parse our custom + XML content, as can be seen in the following example: + + using System; +using System.Text.RegularExpressions; +using System.Xml; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +namespace CustomNamespace +{ + public class RegexObjectDefinitionParser : AbstractSimpleObjectDefinitionParser { + + protected override Type GetObjectType(XmlElement element) + { + return typeof (Regex); + } + + protected override void DoParse(XmlElement element, ObjectDefinitionBuilder builder) + { + // this will never be null since the schema explicitly requires that a value be supplied + string pattern = element.GetAttribute("pattern"); + builder.AddConstructorArg(pattern); + + // this however is an optional property + string options = element.GetAttribute("options"); + if (StringUtils.HasText(options)) + { + RegexOptions regexOptions = (RegexOptions)Enum.Parse(typeof (RegexOptions), options); + builder.AddConstructorArg(regexOptions); + } + } + + protected override bool ShouldGenerateIdAsFallback + { + get { return true; } + } +} + + + + + We use the Spring-provided + AbstractSingleObjectDefinitionParser to handle + a lot of the basic grunt work of creating a + single + IObjectDefinition. + + + + We supply the + AbstractSingleObjectDefinitionParser superclass + with the type that our single + IObjectDefinition will + represent. + + + + In this simple case, this is all that we need to do. The creation of + our single IObjectDefinition is handled by + the AbstractSingleObjectDefinitionParser + superclass, as is the extraction and setting of the object definition's + unique identifier. The property + ShouldGenerateIdAsFallback will generate a throw-away + object id incase one is not specified, this is useful when nesting object + definitions. +
+ +
+ Registering the handler and the schema + + The coding is finished! All that remains to be done is to somehow + make the Spring XML parsing infrastructure aware of our custom element; we + do this by registering our custom + INamespaceParser using a special + configuration section handler. The location of the XML Schema in this + example has been directly assoicated with the parser though the use of the + Namespace attribute. + +
+ <filename>NamespaceParsersSectionHandler</filename> + + The custom configuration section handler is of the type + Spring.Context.Support.NamespaceParsersSectionHandler + and is registered with .NET in the normal manner. The custom + configuration section will simply point to the + INamespaceParser implementation that has the + Namespace attribute. For our example, we need to + write the following: + + <configuration> + + <configSections> + <sectionGroup name="spring"> + <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/> + </sectionGroup> + </configSections> + + <spring> + <parsers> + <parser type="CustomNamespace.MyNamespaceParser, CustomNamespace" /> + </parsers> + </spring> + +</configuration> +
+
+ +
+ Using a custom extension in your Spring XML configuration + + Using a custom extension that you yourself have implemented is no + different from using one of the 'custom' extensions that Spring provides + straight out of the box. Find below an example of using the custom + <regex/> element developed in the previous steps + in a Spring XML configuration file. + + <?xml version="1.0" encoding="utf-8" ?> +<objects xmlns="http://www.springframework.net" + xmlns:myns="http://www.mycompany.com/schema/myns"> + + <!-- as a top level object definition --> + <myns:regex id="usZipCodeRegex" + pattern="(^\d{5}$)|(^\d{5}-\d{4}$)"/> + + <object id="jobDetailTemplate" abstract="true"> + <property name="regex"> + <!-- as an inner object definition --> + <myns:regex pattern="(^\d{5}$)|(^\d{5}-\d{4}$)" + options="Compiled"/> + </property> + </object> + +</objects> +
+ +
+ Further Resources + + Find below links to further resources concerning XML Schema and the + extensible XML support described in this chapter. + + + + The XML Schema + Part 1: Structures Second Edition + + + + The XML Schema + Part 2: Datatypes Second Edition + + +
+
\ No newline at end of file diff --git a/doc/reference/src/xsd-configuration.xml b/doc/reference/src/xsd-configuration.xml new file mode 100644 index 00000000..4f3c7be0 --- /dev/null +++ b/doc/reference/src/xsd-configuration.xml @@ -0,0 +1,225 @@ + + + XML Schema-based configuration + +
+ Introduction + + This appendix details the use of XML Schema-based configuration in + Spring. + + The 'classic' + <object/>-based schema is good, but its + generic-nature comes with a price in terms of configuration overhead. + Creating a custom XML Schema-based configuration makes Spring XML + configuration files substantially clearer to read. In addition, it allows + you to express the intent of an object definition. + + The key thing to remember is that creating custom schema tags work + best for infrastructure or integration objects: for example, AOP, + collections, transactions, integration with 3rd-party frameworks, etc., + while the existing object tags are best suited to application-specific + objects, such as DAOs, service layer objects, etc. + + Please note the fact that the XML configuration mechanism is totally + customisable and extensible. This means you can write your own + domain-specific configuration tags that would better represent your + application's domain; the process involved in doing so is covered in the + appendix entitled . +
+ +
+ XML Schema-based configuration + +
+ Referencing the schemas + + As a reminder, you reference the standard objects schema as shown + below + + +<?xml version="1.0" encoding="UTF-8"?> +<objects xmlns="http://www.springframework.net" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/schema/objects/spring-objects-1.1.xsd"> + + <!-- <object/> definitions here --> + +</objects> + + + The 'xsi:schemaLocation' fragment is not + actually required, but can be included to reference a local copy of a + schema (which can be useful during development) and assumes the XML + editor will look to that location and load the schema. + + + The above Spring XML configuration fragment is boilerplate that + you can copy and paste (!) and then plug + <object/> definitions into like you have always + done. However, the entire point of using custom schema tags is to make + configuration easier. +
+ + The rest of this chapter gives an overview of custom XML Schema + based configuration that are included with the release. + +
+ The <literal>tx</literal> (transaction) schema + + The tx tags deal with configuring objects in + Spring's comprehensive support for transactions. These tags are covered + in the chapter entitled . + + + You are strongly encouraged to look at the + 'spring-tx-1.1.xsd' file that ships with the + Spring distribution. This file is (of course), the XML Schema for + Spring's transaction configuration, and covers all of the various tags + in the tx namespace, including attribute defaults + and suchlike. This file is documented inline, and thus the information + is not repeated here in the interests of adhering to the DRY (Don't + Repeat Yourself) principle. + + + In the interest of completeness, to use the tags in the + tx schema, you need to have the following preamble at + the top of your Spring XML configuration file; the emboldened text in + the following snippet references the correct schema so that the tags in + the tx namespace are available to you. + + <?xml version="1.0" encoding="UTF-8"?> +<object xmlns="http://www.springframework.net" + xmlns:aop="http://www.springframework.org/schema/aop" + xmlns:tx="http://www.springframework.org/schema/tx"> + +<!-- <object/> definitions here --> + +</object> + + + Often when using the tags in the tx namespace + you will also be using the tags from the aop + namespace (since the declarative transaction support in Spring is + implemented using AOP). The above XML snippet contains the relevant + lines needed to reference the aop schema so that + the tags in the aop namespace are available to + you. + +
+ +
+ The <literal>aop</literal> schema + + The aop tags deal with configuring all things + AOP in Spring. These tags are comprehensively covered in the chapter + entitled . + + In the interest of completeness, to use the tags in the + aop schema, you need to have the following preamble + at the top of your Spring XML configuration file; the emboldened text in + the following snippet references the correct schema so that the tags in + the aop namespace are available to you. + + <?xml version="1.0" encoding="UTF-8"?> +<objects xmlns="http://www.springframework.net" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:aop="http://www.springframework.org/schema/aop"> + +<!-- <object/> definitions here --> + +</objects> +
+ +
+ The <literal>db</literal> schema + + The db tags deal with creating + IDbProvider instances for a given database client + library. The following snippet references the correct schema so that the + tags in the db namespace are available to you. The + tags are comprehensively covered in the chapter entitled . + + <?xml version="1.0" encoding="UTF-8"?> +<objects xmlns="http://www.springframework.net" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:db="http://www.springframework.org/schema/db"> + +<!-- <object/> definitions here --> + +</objects> +
+ +
+ The <literal>remoting</literal> schema + + The remoting tags are for use when you want to + export an existing POCO object as a .NET remoted object or to create a + client side .NET remoting proxy. The tags are comprehensively covered in + the chapter + + <?xml version="1.0" encoding="UTF-8"?> +<objects xmlns="http://www.springframework.org/schema/objects" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:r="http://www.springframework.org/schema/remoting"> + +<!-- <object/> definitions here --> + +</objects> +
+ +
+ The <literal>validation</literal> schema + + The validation tags are for use when you want + definte IValidator object instances. The tags are + comprehensively covered in the chapter + + <?xml version="1.0" encoding="UTF-8"?> +<objects xmlns="http://www.springframework.net" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:v="http://www.springframework.org/schema/validation"> + +<!-- <object/> definitions here --> + +</objects> +
+ +
+ The <literal>objects</literal> schema + + Last but not least we have the tags in the + objects schema. Examples of the various tags in the + objects schema are not shown here because they are + quite comprehensively covered in the section entitled (and indeed in that + entire chapter). + + <?xml version="1.0" encoding="UTF-8"?> +<objects xmlns="http://www.springframework.net" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/schema/objects/spring-objects-1.1.xsd"> + + <object id="foo" class="X.Y.Foo, X"> + <property name="name" value="Rick"/> + </object> + +</objects> +
+
+ +
+ Setting up your IDE + + To setup VS.NET to provide intellisence while editing XML file for + your custom XML schemas you will need to copy your XSD files to an + appropriate VS.NET directory. Refer to the following chapter for details, + + + For SharpDevelop, follow the directions on the "Editing + XML" product documentation. +
+
\ No newline at end of file diff --git a/doc/reference/src/xsd-template.xml b/doc/reference/src/xsd-template.xml new file mode 100644 index 00000000..7ab52c33 --- /dev/null +++ b/doc/reference/src/xsd-template.xml @@ -0,0 +1,4 @@ + + Spring.NET's <literal>spring-objects.xsd</literal> + + diff --git a/doc/reference/src/xsd.xml b/doc/reference/src/xsd.xml new file mode 100644 index 00000000..944d63a2 --- /dev/null +++ b/doc/reference/src/xsd.xml @@ -0,0 +1,524 @@ + + Spring.NET's <literal>spring-objects.xsd</literal> + + + + + Spring Objects XML Schema Definition + Based on Spring Beans DTD, authored by Rod Johnson & Juergen Hoeller + + Author: Griffin Caprio + + This defines a simple and consistent way of creating a namespace + of managed objects configured by a Spring XmlObjectFactory. + This document type is used by most Spring functionality, including + web application contexts, which are based on object factories. + + Each object element in this document defines an object. + Typically the object type (System.Type is specified, along with plain vanilla + object properties. + + Object instances can be "singletons" (shared instances) or "prototypes" + (independent instances). + + References among objects are supported, i.e. setting an object property + to refer to another object in the same factory or an ancestor factory. + + As alternative to object references, "inner object definitions" can be used. + Singleton flags and names of such "inner object" are always ignored: + Inner object are anonymous prototypes. + + There is also support for lists, dictionaries, and sets. + + + + Defines a base type for any required string. Defines a string with a minimum length of 0 + + + + + + + + + Element containing informative text describing the purpose of the enclosing + element. Always optional. + Used primarily for user documentation of XML object definition documents. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Defines constructor argument. + + + + + + + + + + + + + + + + + + + Defines property. + + + + + + + + + + + Defines a single named object. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The document root. At least one object definition is required. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + diff --git a/doc/reference/styles/fopdf.xsl b/doc/reference/styles/fopdf.xsl new file mode 100644 index 00000000..bb9020ca --- /dev/null +++ b/doc/reference/styles/fopdf.xsl @@ -0,0 +1,475 @@ + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright ©right; 2004-2006 + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -5em + -5em + + + + + + + + + + + Spring Framework () + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bold + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 0 + 1 + + 1 + + + + + + book toc + + + + 2 + + + + + + + + + + 0 + 0 + 0 + + + 5mm + 10mm + 10mm + + 15mm + 10mm + 0mm + + 18mm + 18mm + + + 0pc + + + + + justify + false + + + 11 + 8 + + + 1.4 + + + + + + + 0.8em + + + + + + 17.4cm + + + + 4pt + 4pt + 4pt + 4pt + + + + 0.1pt + 0.1pt + + + + + 1 + + + + + + + + left + bold + + + pt + + + + + + + + + + + + + + + 0.8em + 0.8em + 0.8em + + + pt + + 0.1em + 0.1em + 0.1em + + + 0.6em + 0.6em + 0.6em + + + pt + + 0.1em + 0.1em + 0.1em + + + 0.4em + 0.4em + 0.4em + + + pt + + 0.1em + 0.1em + 0.1em + + + + + bold + + + pt + + false + 0.4em + 0.6em + 0.8em + + + + + + + + + pt + + + + + 1em + 1em + 1em + #444444 + solid + 0.1pt + 0.5em + 0.5em + 0.5em + 0.5em + 0.5em + 0.5em + + + + 1 + + #F0F0F0 + + + + + + 0 + 1 + + + 90 + + + + + '1' + &admon_gfx_path; + + + + + + figure after + example before + equation before + table before + procedure before + + + + 1 + + + + 0.8em + 0.8em + 0.8em + 0.1em + 0.1em + 0.1em + + + + + + + + + + + + + + + + + diff --git a/doc/reference/styles/html.css b/doc/reference/styles/html.css new file mode 100644 index 00000000..c9ccb50b --- /dev/null +++ b/doc/reference/styles/html.css @@ -0,0 +1,277 @@ +body { + text-align: justify; + margin-right: 2em; + margin-left: 2em; +} + +a, +a[accesskey^="h"], +a[accesskey^="n"], +a[accesskey^="u"], +a[accesskey^="p"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +a:active { + color: #003399; +} + +a:visited { + color: #888888; +} + +p { + font-family: Verdana, Arial; +} + +dt { + font-family: Verdana, Arial; + font-size: 12px; +} + +p, dl, dt, dd, blockquote { + color: #000000; + margin-bottom: 3px; + margin-top: 3px; + padding-top: 0px; +} + +ol, ul, p { + margin-top: 6px; + margin-bottom: 6px; +} + +p, blockquote { + font-size: 90%; +} + +p.releaseinfo { + font-size: 100%; + font-weight: bold; + font-family: Verdana, Arial, helvetica, sans-serif; + padding-top: 10px; +} + +p.pubdate { + font-size: 120%; + font-weight: bold; + font-family: Verdana, Arial, helvetica, sans-serif; +} + +td { + font-size: 80%; +} + +td, th, span { + color: #000000; +} + +td[width^="40%"] { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + color: #003399; +} + +table[summary^="Navigation header"] tbody tr th[colspan^="3"] { + font-family: Verdana, Arial, helvetica, sans-serif; +} + +blockquote { + margin-right: 0px; +} + +h1, h2, h3, h4, h6, H6 { + color: #000000; + font-weight: 500; + margin-top: 0px; + padding-top: 14px; + font-family: Verdana, Arial, helvetica, sans-serif; + margin-bottom: 0px; +} + +h2.title { + font-weight: 800; + margin-bottom: 8px; +} + +h2.subtitle { + font-weight: 800; + margin-bottom: 20px; +} + +.firstname, .surname { + font-size: 12px; + font-family: Verdana, Arial, helvetica, sans-serif; +} + +table { + border-collapse: collapse; + border-spacing: 0; + border: 1px black; + empty-cells: hide; + margin: 10px 0px 30px 50px; + width: 90%; +} + +div.table { + margin: 30px 0px 30px 0px; + border: 1px dashed gray; + padding: 10px; +} + +div.table > p.title { + padding-left: 10px; +} + +table[summary^="Navigation footer"] { + border-collapse: collapse; + border-spacing: 0; + border: 1px black; + empty-cells: hide; + margin: 0px; + width: 100%; +} + +table[summary^="Note"], table[summary^="Warning"], table[summary^="Tip"] { + border-collapse: collapse; + border-spacing: 0; + border: 1px black; + empty-cells: hide; + margin: 10px 0px 10px -20px; + width: 100%; +} + +td { + padding: 4pt; + font-family: Verdana, Arial, helvetica, sans-serif; +} + +div.warning TD { + text-align: justify; +} + +h1 { + font-size: 150%; +} + +h2 { + font-size: 110%; +} + +h3 { + font-size: 100%; font-weight: bold; +} + +h4 { + font-size: 90%; font-weight: bold; +} + +h5 { + font-size: 90%; font-style: italic; +} + +h6 { + font-size: 100%; font-style: italic; +} + +tt { + font-size: 110%; + font-family: "Courier New", Courier, monospace; + color: #000000; +} + +.navheader, .navfooter { + border: none; +} + +div.navfooter table { + border: dashed gray; + border-width: 1px 1px 1px 1px; + background-color: #cde48d; +} + +pre { + font-size: 110%; + padding: 5px; + border-style: solid; + border-width: 1px; + border-color: #CCCCCC; + background-color: #F4F4F4; +} + +ul, ol, li { + list-style: disc; +} + +hr { + width: 100%; + height: 1px; + background-color: #CCCCCC; + border-width: 0px; + padding: 0px; +} + +.variablelist { + padding-top: 10px; + padding-bottom: 10px; + margin: 0; +} + +.term { + font-weight:bold; +} + +.mediaobject { + padding-top: 30px; + padding-bottom: 30px; +} + +.legalnotice { + font-family: Verdana, Arial, helvetica, sans-serif; + font-size: 12px; + font-style: italic; +} + +.sidebar { + float: right; + margin: 10px 0px 10px 30px; + padding: 10px 20px 20px 20px; + width: 33%; + border: 1px solid black; + background-color: #F4F4F4; + font-size: 14px; +} + +.property { + font-family: "Courier New", Courier, monospace; +} + +a code { + font-family: Verdana, Arial; + font-size: 12px; +} + +td code { + font-size: 110%; +} + +div.note * td, +div.tip * td, +div.warning * td { + text-align: justify; + font-size: 100%; +} + +.programlisting .interfacename, +.programlisting .literal, +.programlisting .classname { + font-size: 95%; +} + +/* everything in a is displayed in a nice green, comment-like color */ +.programlisting * .lineannotation, +.programlisting * .lineannotation * { + color: green; +} diff --git a/doc/reference/styles/html.xsl b/doc/reference/styles/html.xsl new file mode 100644 index 00000000..7bcd1582 --- /dev/null +++ b/doc/reference/styles/html.xsl @@ -0,0 +1,100 @@ + + + + + +]> + + + + + + + + ../styles/html.css + + + 1 + 0 + 1 + 0 + + + + + + book toc + + + + 3 + + + + + 1 + + + + + + + 1 + &callout_gfx_path; + + + 90 + + + + + '1' + &admon_gfx_path; + + + + + figure after + example before + equation before + table before + procedure before + + + + , + + + + + + + + +
+

Authors

+

+ +

+
+ +
diff --git a/doc/reference/styles/html_chunk.xsl b/doc/reference/styles/html_chunk.xsl new file mode 100644 index 00000000..7a978158 --- /dev/null +++ b/doc/reference/styles/html_chunk.xsl @@ -0,0 +1,217 @@ + + + + + +]> + + + + '5' + '1' + ../styles/html.css + + 1 + 0 + 1 + 0 + + + + book toc + + + 3 + + + 1 + + + + + 1 + &callout_gfx_path; + + 90 + + + '1' + &admon_gfx_path; + + + + figure after + example before + equation before + table before + procedure before + + + + , + + + + + + + + +
+

Authors

+

+ +

+
+ + + + + + + + 1 + + + + + + + + + + + + + +
diff --git a/doc/reference/styles/htmlhelp-common.xsl b/doc/reference/styles/htmlhelp-common.xsl new file mode 100644 index 00000000..9f8fd0ff --- /dev/null +++ b/doc/reference/styles/htmlhelp-common.xsl @@ -0,0 +1,1230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ID ' + + ' not found in document. + + + + Formatting from + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0x + + + + + + 0x + + + + +[OPTIONS] + + +Auto Index=Yes + + +Binary TOC=Yes + +Compatibility=1.1 or later +Compiled file= +Contents file= + + +Default Window= + +Default topic= + +Display compile progress= + + + No + + + Yes + + + +Full-text search=Yes + + +Index file= + +Language= + + + + + + + +Title= + + +Enhanced decompilation= + + + Yes + + + No + + + + + + +[WINDOWS] + + +=" + +"," +", + + " + + " + +," + +", +" + + + + + + + + +" +, + + " + + " + +, + + " + + " + +, + + " + + " + +, + + " + + " + +, + +,, + +,,,,,,,,0 + + + + + + + + + + +[FILES] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ALIAS] +#include + +[MAP] +#include + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + 0 + + + + 0 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <HTML> +<HEAD> +</HEAD> + <BODY> + + + <OBJECT type="text/site properties"> + <param name="ImageType" value="Folder"> +</OBJECT> + + + +<UL> + + + + + + + + + + + + + + </UL> + + + </BODY> +</HTML> + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + + + + + + <UL> + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + + + + + + <UL> + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + + , + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ]]> + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + ]]> + + + + + ]]> + + ]]> + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + ]]> + + + ]]> + + + + + ]]> + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #define + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + A + B + C + D + E + F + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + + + + + +

    +
    + + diff --git a/doc/reference/styles/htmlhelp.xsl b/doc/reference/styles/htmlhelp.xsl new file mode 100644 index 00000000..f9e6e21a --- /dev/null +++ b/doc/reference/styles/htmlhelp.xsl @@ -0,0 +1,95 @@ + + + + + + + + + + + 1 + #F4F4F4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/reference/styles/profile-htmlhelp-common.xsl b/doc/reference/styles/profile-htmlhelp-common.xsl new file mode 100644 index 00000000..41981669 --- /dev/null +++ b/doc/reference/styles/profile-htmlhelp-common.xsl @@ -0,0 +1,1199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ID ' + + ' not found in document. + + + + Formatting from + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0x + + + + + + 0x + + + + +[OPTIONS] + + +Auto Index=Yes + + +Binary TOC=Yes + +Compatibility=1.1 or later +Compiled file= +Contents file= + + +Default Window= + +Default topic= + +Display compile progress= + + + No + + + Yes + + + +Full-text search=Yes + + +Index file= + +Language= + + + + + + + +Title= + + +Enhanced decompilation= + + + Yes + + + No + + + + + + +[WINDOWS] + + +=" + +"," +", + + " + + " + +," + +", +" + + + + + + + + +" +, + + " + + " + +, + + " + + " + +, + + " + + " + +, + + " + + " + +, + +,, + +,,,,,,,,0 + + + + + + + + + + +[FILES] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ALIAS] +#include + +[MAP] +#include + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + 0 + + + + 0 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <HTML> +<HEAD> +</HEAD> + <BODY> + + + <OBJECT type="text/site properties"> + <param name="ImageType" value="Folder"> +</OBJECT> + + + +<UL> + + + + + + + + + + + + + + </UL> + + + </BODY> +</HTML> + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + + + + + + <UL> + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + + + + + + <UL> + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + </OBJECT> + + + <UL> + + </UL> + + + + + + + + + + + + + + + + + + + + + + + , + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<HTML> +<HEAD> +<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1"> +<!-- Sitemap 1.0 --> +</HEAD><BODY> +<OBJECT type="text/site properties"> +</OBJECT> +<UL> + + + + + + + + + + + +</UL> +</BODY></HTML> + + + + + + + + + + + + + + + + + + + + + + + + + + <UL> + + + + + + + <UL> + + + + + + </UL> + + + </UL> + + + + + + + + + + + + + + + <LI> <OBJECT type="text/sitemap"> + <param name="Name" value=""> + + + + + + + + + + + + + + <param name="Name" value=" + + "> + <param name="Local" value=" + + "> + + + <param name="See Also" value=" + + "> + + </OBJECT> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #define + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + A + B + C + D + E + F + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + + + + + +

    +
    + +
    diff --git a/doc/reference/styles/profile-htmlhelp.xsl b/doc/reference/styles/profile-htmlhelp.xsl new file mode 100644 index 00000000..3d245370 --- /dev/null +++ b/doc/reference/styles/profile-htmlhelp.xsl @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/doc/reference/styles/tld.to.docbook.xsl b/doc/reference/styles/tld.to.docbook.xsl new file mode 100644 index 00000000..139a3529 --- /dev/null +++ b/doc/reference/styles/tld.to.docbook.xsl @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + -intro + + Introduction + + + + One of the view technologies you can use with the Spring Framework + is Java Server Pages (JSPs). To help you implement views using Java Server Pages + the Spring Framework provides you with some tags for evaluating errors, setting + themes and outputting internationalized messages. + + + + This appendix describes the + + + + tag library. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The + + + + tag + + + + + + + + + + + + .table + + + Attributes + + + + 3 + + + + description.span + + + Attribute + + + Runtime.Expression + + + left + + + + + center + + + Attribute + + + + + center + + + Required + + + + + center + + + Runtime.Expression + + + + + + + + center + + Attribute + + + + center + + Required? + + + + center + + Runtime Expression? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + description.span + + + + + + + + + + + + + description.span + + + + + + + + + + + . + + + diff --git a/doc/reference/styles/xsd.to.docbook.xsl b/doc/reference/styles/xsd.to.docbook.xsl new file mode 100644 index 00000000..4a9bb86d --- /dev/null +++ b/doc/reference/styles/xsd.to.docbook.xsl @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + -intro + + + Introduction + + + + + This appendix describes the + + + + schema. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The + + + + element + + + [TODO : insert the description of the element here] + + + + + + + . + + + diff --git a/examples/Spring/Spring.AopQuickStart/Spring.AopQuickStart.2003.sln b/examples/Spring/Spring.AopQuickStart/Spring.AopQuickStart.2003.sln new file mode 100644 index 00000000..8528e9fd --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/Spring.AopQuickStart.2003.sln @@ -0,0 +1,80 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Common.2003", "src\Spring.AopQuickStart.Common\Spring.AopQuickStart.Common.2003.csproj", "{C707543A-8F0E-4483-AECC-BC6EE14C3A29}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step1.2003", "src\Spring.AopQuickStart.Step1\Spring.AopQuickStart.Step1.2003.csproj", "{72D32026-23F5-4C62-8E6C-BFB30210BFD3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step2.2003", "src\Spring.AopQuickStart.Step2\Spring.AopQuickStart.Step2.2003.csproj", "{202672D3-F523-4C62-8E6C-BFB30210BFD3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step3.2003", "src\Spring.AopQuickStart.Step3\Spring.AopQuickStart.Step3.2003.csproj", "{BFB30210-4C62-F523-8E6C-202672D3BFD3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step4.2003", "src\Spring.AopQuickStart.Step4\Spring.AopQuickStart.Step4.2003.csproj", "{672D3BFD-4C62-F523-8E6C-20BFB3021023}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step5.2003", "src\Spring.AopQuickStart.Step5\Spring.AopQuickStart.Step5.2003.csproj", "{F5233BFD-8E6C-FB30-4C62-20BFB3021023}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step6.2003", "src\Spring.AopQuickStart.Step6\Spring.AopQuickStart.Step6.2003.csproj", "{8E620210-4D33-F523-BF6C-202672CBBFD3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step7.2003", "src\Spring.AopQuickStart.Step7\Spring.AopQuickStart.Step7.2003.csproj", "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {C707543A-8F0E-4483-AECC-BC6EE14C3A29}.Debug.ActiveCfg = Debug|.NET + {C707543A-8F0E-4483-AECC-BC6EE14C3A29}.Debug.Build.0 = Debug|.NET + {C707543A-8F0E-4483-AECC-BC6EE14C3A29}.Release.ActiveCfg = Release|.NET + {C707543A-8F0E-4483-AECC-BC6EE14C3A29}.Release.Build.0 = Release|.NET + {72D32026-23F5-4C62-8E6C-BFB30210BFD3}.Debug.ActiveCfg = Debug|.NET + {72D32026-23F5-4C62-8E6C-BFB30210BFD3}.Debug.Build.0 = Debug|.NET + {72D32026-23F5-4C62-8E6C-BFB30210BFD3}.Release.ActiveCfg = Release|.NET + {72D32026-23F5-4C62-8E6C-BFB30210BFD3}.Release.Build.0 = Release|.NET + {202672D3-F523-4C62-8E6C-BFB30210BFD3}.Debug.ActiveCfg = Debug|.NET + {202672D3-F523-4C62-8E6C-BFB30210BFD3}.Debug.Build.0 = Debug|.NET + {202672D3-F523-4C62-8E6C-BFB30210BFD3}.Release.ActiveCfg = Release|.NET + {202672D3-F523-4C62-8E6C-BFB30210BFD3}.Release.Build.0 = Release|.NET + {BFB30210-4C62-F523-8E6C-202672D3BFD3}.Debug.ActiveCfg = Debug|.NET + {BFB30210-4C62-F523-8E6C-202672D3BFD3}.Debug.Build.0 = Debug|.NET + {BFB30210-4C62-F523-8E6C-202672D3BFD3}.Release.ActiveCfg = Release|.NET + {BFB30210-4C62-F523-8E6C-202672D3BFD3}.Release.Build.0 = Release|.NET + {672D3BFD-4C62-F523-8E6C-20BFB3021023}.Debug.ActiveCfg = Debug|.NET + {672D3BFD-4C62-F523-8E6C-20BFB3021023}.Debug.Build.0 = Debug|.NET + {672D3BFD-4C62-F523-8E6C-20BFB3021023}.Release.ActiveCfg = Release|.NET + {672D3BFD-4C62-F523-8E6C-20BFB3021023}.Release.Build.0 = Release|.NET + {F5233BFD-8E6C-FB30-4C62-20BFB3021023}.Debug.ActiveCfg = Debug|.NET + {F5233BFD-8E6C-FB30-4C62-20BFB3021023}.Debug.Build.0 = Debug|.NET + {F5233BFD-8E6C-FB30-4C62-20BFB3021023}.Release.ActiveCfg = Release|.NET + {F5233BFD-8E6C-FB30-4C62-20BFB3021023}.Release.Build.0 = Release|.NET + {8E620210-4D33-F523-BF6C-202672CBBFD3}.Debug.ActiveCfg = Debug|.NET + {8E620210-4D33-F523-BF6C-202672CBBFD3}.Debug.Build.0 = Debug|.NET + {8E620210-4D33-F523-BF6C-202672CBBFD3}.Release.ActiveCfg = Release|.NET + {8E620210-4D33-F523-BF6C-202672CBBFD3}.Release.Build.0 = Release|.NET + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}.Debug.ActiveCfg = Debug|.NET + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}.Debug.Build.0 = Debug|.NET + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}.Release.ActiveCfg = Release|.NET + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(SolutionItems) = postSolution + readme.txt = readme.txt + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.AopQuickStart/Spring.AopQuickStart.2005.sln b/examples/Spring/Spring.AopQuickStart/Spring.AopQuickStart.2005.sln new file mode 100644 index 00000000..9e053726 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/Spring.AopQuickStart.2005.sln @@ -0,0 +1,66 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Common.2005", "src\Spring.AopQuickStart.Common\Spring.AopQuickStart.Common.2005.csproj", "{C707543A-8F0E-4483-AECC-BC6EE14C3A29}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step1.2005", "src\Spring.AopQuickStart.Step1\Spring.AopQuickStart.Step1.2005.csproj", "{1B3559A0-5274-4968-A628-77F036984F02}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step2.2005", "src\Spring.AopQuickStart.Step2\Spring.AopQuickStart.Step2.2005.csproj", "{4ACE3EB7-B60A-4ACE-B4BB-2C33494D6108}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step3.2005", "src\Spring.AopQuickStart.Step3\Spring.AopQuickStart.Step3.2005.csproj", "{2C33494D-B4BB-4ACE-B60A-4ACE3EB76108}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step4.2005", "src\Spring.AopQuickStart.Step4\Spring.AopQuickStart.Step4.2005.csproj", "{3EB76108-4ACE-4ACE-B60A-B4BB3EB76108}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step5.2005", "src\Spring.AopQuickStart.Step5\Spring.AopQuickStart.Step5.2005.csproj", "{61086108-3EB7-4ACE-B4B6-B4B60AB76108}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step6.2005", "src\Spring.AopQuickStart.Step6\Spring.AopQuickStart.Step6.2005.csproj", "{AB33BB4D-B449-4AC1-760A-4ACE3EA56108}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.AopQuickStart.Step7.2005", "src\Spring.AopQuickStart.Step7\Spring.AopQuickStart.Step7.2005.csproj", "{0077A8CA-4C09-42B1-B227-91095CE4CCA2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{919AF8FE-423F-4A8C-BDA4-CB8B39D64780}" + ProjectSection(SolutionItems) = preProject + readme.txt = readme.txt + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C707543A-8F0E-4483-AECC-BC6EE14C3A29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C707543A-8F0E-4483-AECC-BC6EE14C3A29}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C707543A-8F0E-4483-AECC-BC6EE14C3A29}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C707543A-8F0E-4483-AECC-BC6EE14C3A29}.Release|Any CPU.Build.0 = Release|Any CPU + {1B3559A0-5274-4968-A628-77F036984F02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B3559A0-5274-4968-A628-77F036984F02}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B3559A0-5274-4968-A628-77F036984F02}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B3559A0-5274-4968-A628-77F036984F02}.Release|Any CPU.Build.0 = Release|Any CPU + {4ACE3EB7-B60A-4ACE-B4BB-2C33494D6108}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4ACE3EB7-B60A-4ACE-B4BB-2C33494D6108}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4ACE3EB7-B60A-4ACE-B4BB-2C33494D6108}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4ACE3EB7-B60A-4ACE-B4BB-2C33494D6108}.Release|Any CPU.Build.0 = Release|Any CPU + {2C33494D-B4BB-4ACE-B60A-4ACE3EB76108}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2C33494D-B4BB-4ACE-B60A-4ACE3EB76108}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2C33494D-B4BB-4ACE-B60A-4ACE3EB76108}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2C33494D-B4BB-4ACE-B60A-4ACE3EB76108}.Release|Any CPU.Build.0 = Release|Any CPU + {3EB76108-4ACE-4ACE-B60A-B4BB3EB76108}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3EB76108-4ACE-4ACE-B60A-B4BB3EB76108}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3EB76108-4ACE-4ACE-B60A-B4BB3EB76108}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3EB76108-4ACE-4ACE-B60A-B4BB3EB76108}.Release|Any CPU.Build.0 = Release|Any CPU + {61086108-3EB7-4ACE-B4B6-B4B60AB76108}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61086108-3EB7-4ACE-B4B6-B4B60AB76108}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61086108-3EB7-4ACE-B4B6-B4B60AB76108}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61086108-3EB7-4ACE-B4B6-B4B60AB76108}.Release|Any CPU.Build.0 = Release|Any CPU + {AB33BB4D-B449-4AC1-760A-4ACE3EA56108}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB33BB4D-B449-4AC1-760A-4ACE3EA56108}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB33BB4D-B449-4AC1-760A-4ACE3EA56108}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB33BB4D-B449-4AC1-760A-4ACE3EA56108}.Release|Any CPU.Build.0 = Release|Any CPU + {0077A8CA-4C09-42B1-B227-91095CE4CCA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0077A8CA-4C09-42B1-B227-91095CE4CCA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0077A8CA-4C09-42B1-B227-91095CE4CCA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0077A8CA-4C09-42B1-B227-91095CE4CCA2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.AopQuickStart/Spring.AopQuickStart.build b/examples/Spring/Spring.AopQuickStart/Spring.AopQuickStart.build new file mode 100644 index 00000000..41200407 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/Spring.AopQuickStart.build @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/readme.txt b/examples/Spring/Spring.AopQuickStart/readme.txt new file mode 100644 index 00000000..d5516cc7 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/readme.txt @@ -0,0 +1,20 @@ +Step1 : +Use Spring.Aop programmatically + +Step2 : +Use Spring.Aop within IoC container + +Step3 : +Use Spring.NET convenience pointcuts (Match by method name) + +Step4 : +Use Spring.NET convenience pointcuts (Match by attribute) + +Step5 : +Create and use custom pointcuts + +Step6 : +Use Introductions (mixins) + +Step7 : +Use AutoProxy functionality \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingAfterAdvice.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingAfterAdvice.cs new file mode 100644 index 00000000..57dee8f0 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingAfterAdvice.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using Spring.Aop; + +#endregion + +namespace Spring.AopQuickStart.Aspects +{ + /// + /// Simple implementation of the interface + /// for a logging aspect using . + /// + /// Rick Evans + /// $Id: ConsoleLoggingAfterAdvice.cs,v 1.1 2006/11/26 18:57:59 bbaia Exp $ + public class ConsoleLoggingAfterAdvice : IAfterReturningAdvice + { + public void AfterReturning( + object returnValue, MethodInfo method, object[] args, object target) + { + Console.Out.WriteLine("This method call returned successfully : " + method.Name); + Console.Out.WriteLine(" The target was : " + target); + Console.Out.WriteLine(" The arguments were : "); + if (args != null) + { + foreach (object arg in args) + { + Console.Out.WriteLine("\t: " + arg); + } + } + Console.Out.WriteLine(" The return value is : " + returnValue); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingAroundAdvice.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingAroundAdvice.cs new file mode 100644 index 00000000..61328076 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingAroundAdvice.cs @@ -0,0 +1,52 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using AopAlliance.Intercept; + +#endregion + +namespace Spring.AopQuickStart.Aspects +{ + /// + /// Simple implementation of the interface + /// for a logging aspect using . + /// + /// Rick Evans + /// $Id: ConsoleLoggingAroundAdvice.cs,v 1.2 2006/12/03 23:56:17 bbaia Exp $ + public class ConsoleLoggingAroundAdvice : IMethodInterceptor + { + public object Invoke(IMethodInvocation invocation) + { + Console.Out.WriteLine(String.Format( + "Intercepted call : about to invoke method '{0}'", invocation.Method.Name)); + + object returnValue = invocation.Proceed(); + + Console.Out.WriteLine(String.Format( + "Intercepted call : returned '{0}'", returnValue)); + + return returnValue; + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingBeforeAdvice.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingBeforeAdvice.cs new file mode 100644 index 00000000..dd33a9fe --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingBeforeAdvice.cs @@ -0,0 +1,54 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using Spring.Aop; + +#endregion + +namespace Spring.AopQuickStart.Aspects +{ + /// + /// Simple implementation of the interface + /// for a logging aspect using . + /// + /// Rick Evans + /// $Id: ConsoleLoggingBeforeAdvice.cs,v 1.1 2006/11/26 18:57:59 bbaia Exp $ + public class ConsoleLoggingBeforeAdvice : IMethodBeforeAdvice + { + public void Before(MethodInfo method, object[] args, object target) + { + Console.Out.WriteLine("Intercepted call to this method : " + method.Name); + Console.Out.WriteLine(" The target is : " + target); + Console.Out.WriteLine(" The arguments are : "); + if(args != null) + { + foreach (object arg in args) + { + Console.Out.WriteLine("\t: " + arg); + } + } + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingThrowsAdvice.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingThrowsAdvice.cs new file mode 100644 index 00000000..74055284 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Aspects/ConsoleLoggingThrowsAdvice.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using Spring.Aop; + +#endregion + +namespace Spring.AopQuickStart.Aspects +{ + /// + /// Simple implementation of the interface + /// for a logging aspect using . + /// + /// Rick Evans + /// $Id: ConsoleLoggingThrowsAdvice.cs,v 1.2 2006/12/02 13:30:00 bbaia Exp $ + public class ConsoleLoggingThrowsAdvice : IThrowsAdvice + { + public void AfterThrowing(Exception ex) + { + Console.Error.WriteLine( + String.Format("Advised method threw this exception : {0}", ex.Message)); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Commands/ICommand.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Commands/ICommand.cs new file mode 100644 index 00000000..d876f01a --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Commands/ICommand.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + +#endregion + +namespace Spring.AopQuickStart.Commands +{ + /// + /// A simple interface for Command pattern style usage. + /// + /// + ///

    + /// Does not strictly adhere to the common conventions of the + /// Command patten, due to the fact that the remit of this interface + /// (and of it's attendant implementations) is to serve as the + /// basis for illustrating AOP advice. + ///

    + ///
    + /// Rick Evans + /// $Id: ICommand.cs,v 1.1 2006/11/26 18:58:00 bbaia Exp $ + public interface ICommand + { + bool IsUndoCapable { get; } + + void Execute(); + + void UnExecute(); + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Commands/ServiceCommand.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Commands/ServiceCommand.cs new file mode 100644 index 00000000..cfab060d --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Commands/ServiceCommand.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.AopQuickStart.Commands +{ + /// + /// Simple implementation of the + /// interface. + /// + /// Rick Evans + /// $Id: ServiceCommand.cs,v 1.3 2007/02/06 17:40:52 aseovic Exp $ + public class ServiceCommand : ICommand + { + #region ICommand Members + + public bool IsUndoCapable + { + get { return true; } + } + + public void Execute() + { + Console.Out.WriteLine("--- ServiceCommand implementation : Execute()... ---"); + } + + public void UnExecute() + { + Console.Out.WriteLine("--- ServiceCommand implementation : UnExecute()... ---"); + + // Uncomment to see IThrowAdvice implementation in action + //throw new Exception("The method or operation is not supported."); + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Spring.AopQuickStart.Common.2003.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Spring.AopQuickStart.Common.2003.csproj new file mode 100644 index 00000000..8829a55e --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Spring.AopQuickStart.Common.2003.csproj @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Spring.AopQuickStart.Common.2005.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Spring.AopQuickStart.Common.2005.csproj new file mode 100644 index 00000000..fb49b0ee --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Spring.AopQuickStart.Common.2005.csproj @@ -0,0 +1,104 @@ + + + Local + 8.0.50727 + 2.0 + {C707543A-8F0E-4483-AECC-BC6EE14C3A29} + Debug + AnyCPU + + + + + Spring.AopQuickStart.Common + + + JScript + Grid + IE50 + false + Library + Spring.AopQuickStart + OnBuildSuccess + + + + + + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + System + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Spring.AopQuickStart.Common.build b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Spring.AopQuickStart.Common.build new file mode 100644 index 00000000..6013e06a --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Common/Spring.AopQuickStart.Common.build @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Program.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Program.cs new file mode 100644 index 00000000..37e49539 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Program.cs @@ -0,0 +1,73 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Aop.Framework; + +using Spring.AopQuickStart.Aspects; +using Spring.AopQuickStart.Commands; + +#endregion + +namespace Spring.AopQuickStart +{ + /// + /// A simple application that demonstrates the AOP functionality of Spring.NET. + /// + /// $Id: Program.cs,v 1.2 2006/12/02 13:30:00 bbaia Exp $ + public class Program + { + /// + /// The main entry point for the application. + /// + public static void Main(string[] args) + { + try + { + // Create AOP proxy programmatically. + ProxyFactory factory = new ProxyFactory(new ServiceCommand()); + factory.AddAdvice(new ConsoleLoggingBeforeAdvice()); + factory.AddAdvice(new ConsoleLoggingAfterAdvice()); + factory.AddAdvice(new ConsoleLoggingThrowsAdvice()); + ICommand command = (ICommand)factory.GetProxy(); + + command.Execute(); + if (command.IsUndoCapable) + { + command.UnExecute(); + } + } + catch (Exception ex) + { + Console.Out.WriteLine(); + Console.Out.WriteLine(ex); + } + finally + { + Console.Out.WriteLine(); + Console.Out.WriteLine("--- hit to quit ---"); + Console.ReadLine(); + } + } + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Spring.AopQuickStart.Step1.2003.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Spring.AopQuickStart.Step1.2003.csproj new file mode 100644 index 00000000..d3dd6b46 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Spring.AopQuickStart.Step1.2003.csproj @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Spring.AopQuickStart.Step1.2005.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Spring.AopQuickStart.Step1.2005.csproj new file mode 100644 index 00000000..6b496048 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Spring.AopQuickStart.Step1.2005.csproj @@ -0,0 +1,59 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {1B3559A0-5274-4968-A628-77F036984F02} + Exe + Properties + Spring.AopQuickStart + Spring.AopQuickStart.Step1 + Spring.AopQuickStart.Program + + + true + full + false + bin\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + + + + + + + {C707543A-8F0E-4483-AECC-BC6EE14C3A29} + Spring.AopQuickStart.Common.2005 + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Spring.AopQuickStart.Step1.build b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Spring.AopQuickStart.Step1.build new file mode 100644 index 00000000..e1475775 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step1/Spring.AopQuickStart.Step1.build @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/App.config b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/App.config new file mode 100644 index 00000000..3f53fd8a --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/App.config @@ -0,0 +1,41 @@ + + + + + +
    +
    + + + + + + + + + + + An example that demonstrates simple AOP functionality. + + + + + + + + + + + aroundAdvice + throwsAdvice + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Program.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Program.cs new file mode 100644 index 00000000..77e3ed77 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Program.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Context; +using Spring.Context.Support; + +using Spring.AopQuickStart.Commands; + +#endregion + +namespace Spring.AopQuickStart +{ + /// + /// A simple application that demonstrates the AOP functionality of Spring.NET. + /// + /// $Id: Program.cs,v 1.2 2006/12/02 13:30:00 bbaia Exp $ + public class Program + { + /// + /// The main entry point for the application. + /// + public static void Main(string[] args) + { + try + { + // Create AOP proxy using Spring.NET IoC container. + IApplicationContext ctx = ContextRegistry.GetContext(); + ICommand command = (ICommand)ctx["myServiceCommand"]; + + command.Execute(); + if (command.IsUndoCapable) + { + command.UnExecute(); + } + } + catch (Exception ex) + { + Console.Out.WriteLine(); + Console.Out.WriteLine(ex); + } + finally + { + Console.Out.WriteLine(); + Console.Out.WriteLine("--- hit to quit ---"); + Console.ReadLine(); + } + } + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Spring.AopQuickStart.Step2.2003.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Spring.AopQuickStart.Step2.2003.csproj new file mode 100644 index 00000000..03438e1d --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Spring.AopQuickStart.Step2.2003.csproj @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Spring.AopQuickStart.Step2.2005.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Spring.AopQuickStart.Step2.2005.csproj new file mode 100644 index 00000000..0f7903fc --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Spring.AopQuickStart.Step2.2005.csproj @@ -0,0 +1,62 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {4ACE3EB7-B60A-4ACE-B4BB-2C33494D6108} + Exe + Properties + Spring.AopQuickStart + Spring.AopQuickStart.Step2 + Spring.AopQuickStart.Program + + + true + full + false + bin\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + + + + + + + + + + {C707543A-8F0E-4483-AECC-BC6EE14C3A29} + Spring.AopQuickStart.Common.2005 + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Spring.AopQuickStart.Step2.build b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Spring.AopQuickStart.Step2.build new file mode 100644 index 00000000..fd3f2e20 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step2/Spring.AopQuickStart.Step2.build @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/App.config b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/App.config new file mode 100644 index 00000000..1ce90fc5 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/App.config @@ -0,0 +1,50 @@ + + + + + +
    +
    + + + + + + + + + + + An example that demonstrates use of a poincut. + + + + + + + + *Execute + + + + + + + + + + + + + aroundAdvisor + throwsAdvice + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Program.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Program.cs new file mode 100644 index 00000000..52b28e38 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Program.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Context; +using Spring.Context.Support; + +using Spring.AopQuickStart.Commands; + +#endregion + +namespace Spring.AopQuickStart +{ + /// + /// A simple application that uses a pointcut. + /// + /// $Id: Program.cs,v 1.2 2007/07/26 00:50:11 bbaia Exp $ + public class Program + { + /// + /// The main entry point for the application. + /// + public static void Main(string[] args) + { + try + { + // Create AOP proxy using Spring.NET IoC container. + IApplicationContext ctx = ContextRegistry.GetContext(); + ICommand command = (ICommand)ctx["myServiceCommand"]; + + command.Execute(); + if (command.IsUndoCapable) + { + command.UnExecute(); + } + } + catch (Exception ex) + { + Console.Out.WriteLine(); + Console.Out.WriteLine(ex); + } + finally + { + Console.Out.WriteLine(); + Console.Out.WriteLine("--- hit to quit ---"); + Console.ReadLine(); + } + } + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Spring.AopQuickStart.Step3.2003.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Spring.AopQuickStart.Step3.2003.csproj new file mode 100644 index 00000000..10ca9e74 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Spring.AopQuickStart.Step3.2003.csproj @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Spring.AopQuickStart.Step3.2005.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Spring.AopQuickStart.Step3.2005.csproj new file mode 100644 index 00000000..4d59121f --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Spring.AopQuickStart.Step3.2005.csproj @@ -0,0 +1,62 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {2C33494D-B4BB-4ACE-B60A-4ACE3EB76108} + Exe + Properties + Spring.AopQuickStart + Spring.AopQuickStart.Step3 + Spring.AopQuickStart.Program + + + true + full + false + bin\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + + + + + + + + + + {C707543A-8F0E-4483-AECC-BC6EE14C3A29} + Spring.AopQuickStart.Common.2005 + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Spring.AopQuickStart.Step3.build b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Spring.AopQuickStart.Step3.build new file mode 100644 index 00000000..ca230966 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step3/Spring.AopQuickStart.Step3.build @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/App.config b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/App.config new file mode 100644 index 00000000..dca08b3a --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/App.config @@ -0,0 +1,47 @@ + + + + + +
    +
    + + + + + + + + + + + An example that demonstrates use of a poincut. + + + + + + + + + + + + + + + + + aroundAdvisor + throwsAdvice + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Aspects/ConsoleLoggingAdvice.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Aspects/ConsoleLoggingAdvice.cs new file mode 100644 index 00000000..5c7401b1 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Aspects/ConsoleLoggingAdvice.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using AopAlliance.Intercept; + +using Spring.AopQuickStart.Attributes; + +#endregion + +namespace Spring.AopQuickStart.Aspects +{ + /// + /// Simple implementation of the interface + /// for a logging aspect using and informations from the + /// attribute. + /// + /// Bruno Baia + /// $Id: ConsoleLoggingAdvice.cs,v 1.2 2006/12/03 23:56:28 bbaia Exp $ + public class ConsoleLoggingAdvice : IMethodInterceptor + { +#if NET_2_0 + private ConsoleColor _color = ConsoleColor.Gray; + public ConsoleColor Color + { + get { return _color; } + set { _color = value; } + } + + public object Invoke(IMethodInvocation invocation) + { + ConsoleLoggingAttribute[] consoleLoggingInfo = + (ConsoleLoggingAttribute[])invocation.Method.GetCustomAttributes(typeof(ConsoleLoggingAttribute), false); + + if (consoleLoggingInfo.Length > 0) + { + Color = consoleLoggingInfo[0].Color; + } + + ConsoleColor currentColor = Console.ForegroundColor; + + Console.ForegroundColor = Color; + + Console.Out.WriteLine(String.Format( + "Intercepted call : about to invoke method '{0}'", invocation.Method.Name)); + + Console.ForegroundColor = currentColor; + + object returnValue = invocation.Proceed(); + + Console.ForegroundColor = Color; + + Console.Out.WriteLine(String.Format( + "Intercepted call : returned '{0}'", returnValue)); + + Console.ForegroundColor = currentColor; + + return returnValue; + } +#else + public object Invoke(IMethodInvocation invocation) + { + Console.Out.WriteLine("Intercepted call : about to invoke next item in chain..."); + object returnValue = invocation.Proceed(); + Console.Out.WriteLine("Intercepted call : returned " + returnValue); + return returnValue; + } +#endif + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Attributes/ConsoleLoggingAttribute.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Attributes/ConsoleLoggingAttribute.cs new file mode 100644 index 00000000..eba26086 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Attributes/ConsoleLoggingAttribute.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.AopQuickStart.Attributes +{ + /// + /// This attribute can be used to mark properties and methods + /// on which each call should be logged. + /// + /// Bruno Baia + /// $Id: ConsoleLoggingAttribute.cs,v 1.1 2006/12/02 13:30:02 bbaia Exp $ + public class ConsoleLoggingAttribute : Attribute + { +#if NET_2_0 + private ConsoleColor _color = ConsoleColor.Gray; + + /// + /// Gets or sets the foreground color of the console. + /// + public ConsoleColor Color + { + get { return _color; } + set { _color = value; } + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The foreground color of the console. + /// + public ConsoleLoggingAttribute(ConsoleColor color) + { + _color = color; + } +#endif + + /// + /// Creates a new instance of the + /// class. + /// + public ConsoleLoggingAttribute() + { + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Commands/AnotherServiceCommand.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Commands/AnotherServiceCommand.cs new file mode 100644 index 00000000..67243acb --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Commands/AnotherServiceCommand.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.AopQuickStart.Attributes; + +#endregion + +namespace Spring.AopQuickStart.Commands +{ + /// + /// Another simple implementation of the + /// interface. + /// + /// Bruno Baia + /// $Id: AnotherServiceCommand.cs,v 1.1 2006/12/02 13:30:02 bbaia Exp $ + public class AnotherServiceCommand : ICommand + { + #region ICommand Members + + public bool IsUndoCapable + { + get { return true; } + } + +#if NET_2_0 + [ConsoleLogging(Color = ConsoleColor.Red)] +#else + [ConsoleLogging] +#endif + public void Execute() + { + Console.Out.WriteLine("--- AnotherServiceCommand implementation : Execute()... ---"); + } + + public void UnExecute() + { + Console.Out.WriteLine("--- AnotherServiceCommand implementation : UnExecute()... ---"); + + // Uncomment to see IThrowAdvice implementation in action + //throw new Exception("The method or operation is not supported."); + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Program.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Program.cs new file mode 100644 index 00000000..52b28e38 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Program.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Context; +using Spring.Context.Support; + +using Spring.AopQuickStart.Commands; + +#endregion + +namespace Spring.AopQuickStart +{ + /// + /// A simple application that uses a pointcut. + /// + /// $Id: Program.cs,v 1.2 2007/07/26 00:50:11 bbaia Exp $ + public class Program + { + /// + /// The main entry point for the application. + /// + public static void Main(string[] args) + { + try + { + // Create AOP proxy using Spring.NET IoC container. + IApplicationContext ctx = ContextRegistry.GetContext(); + ICommand command = (ICommand)ctx["myServiceCommand"]; + + command.Execute(); + if (command.IsUndoCapable) + { + command.UnExecute(); + } + } + catch (Exception ex) + { + Console.Out.WriteLine(); + Console.Out.WriteLine(ex); + } + finally + { + Console.Out.WriteLine(); + Console.Out.WriteLine("--- hit to quit ---"); + Console.ReadLine(); + } + } + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Spring.AopQuickStart.Step4.2003.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Spring.AopQuickStart.Step4.2003.csproj new file mode 100644 index 00000000..2a50e3d7 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Spring.AopQuickStart.Step4.2003.csproj @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Spring.AopQuickStart.Step4.2005.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Spring.AopQuickStart.Step4.2005.csproj new file mode 100644 index 00000000..ba66573f --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Spring.AopQuickStart.Step4.2005.csproj @@ -0,0 +1,67 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {3EB76108-4ACE-4ACE-B60A-B4BB3EB76108} + Exe + Properties + Spring.AopQuickStart + Spring.AopQuickStart.Step4 + Spring.AopQuickStart.Program + + + true + full + false + bin\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + + + + Code + + + + + + + + + + + {C707543A-8F0E-4483-AECC-BC6EE14C3A29} + Spring.AopQuickStart.Common.2005 + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Spring.AopQuickStart.Step4.build b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Spring.AopQuickStart.Step4.build new file mode 100644 index 00000000..017635b8 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step4/Spring.AopQuickStart.Step4.build @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/App.config b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/App.config new file mode 100644 index 00000000..70392667 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/App.config @@ -0,0 +1,47 @@ + + + + + +
    +
    + + + + + + + + + + + An example that demonstrates use of a custom poincut. + + + + + + + + + + + + + + + + + aroundAdvisor + throwsAdvice + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Aspects/ExpressionDynamicPointcutAdvisor.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Aspects/ExpressionDynamicPointcutAdvisor.cs new file mode 100644 index 00000000..b4d88b40 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Aspects/ExpressionDynamicPointcutAdvisor.cs @@ -0,0 +1,136 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using Spring.Expressions; +using Spring.Aop.Support; + +#endregion + +namespace Spring.AopQuickStart.Aspects +{ + /// + /// Dynamic pointcut using Spring expressions. + /// + /// Bruno Baia + /// $Id: ExpressionDynamicPointcutAdvisor.cs,v 1.1 2007/07/26 00:50:11 bbaia Exp $ + public class ExpressionDynamicPointcutAdvisor : DynamicMethodMatcherPointcutAdvisor + { + private string expression; + public string Expression + { + get { return expression; } + set { expression = value; } + } + + /// + /// Is there a runtime (dynamic) match for the supplied method ? + /// + public override bool Matches(MethodInfo method, Type targetType, object[] args) + { + IExpression expr = Spring.Expressions.Expression.Parse(Expression); + MatchingInfoHolder infoHolder = new MatchingInfoHolder(method, targetType, args); + + try + { + // Evaluate the expression using the current matching info as current context + return (bool)expr.GetValue(infoHolder); + } + catch (Exception e) + { + throw new ArgumentException( + "Expression cannot be evaluate or not return a boolean.", "Expression", e); + } + } + + #region MatchingInfoHolder inner class definition + + public class MatchingInfoHolder + { + #region Fields + + private MethodInfo _methodInfo; + private Type _targetType; + private object[] _args; + + #endregion + + #region Properties + + /// + /// Get or sets the candidate method. + /// + public MethodInfo MethodInfo + { + get { return _methodInfo; } + set { _methodInfo = value; } + } + + /// + /// Gets or sets the target type. + /// + public Type TargetType + { + get { return _targetType; } + set { _targetType = value; } + } + + /// + /// Gets or sets the arguments to the method. + /// + public object[] Args + { + get { return _args; } + set { _args = value; } + } + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public MatchingInfoHolder() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + public MatchingInfoHolder(MethodInfo methodInfo, Type targetType, object[] args) + { + _methodInfo = methodInfo; + _targetType = targetType; + _args = args; + } + + #endregion + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Program.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Program.cs new file mode 100644 index 00000000..6ee8e6b7 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Program.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Context; +using Spring.Context.Support; + +using Spring.AopQuickStart.Commands; + +#endregion + +namespace Spring.AopQuickStart +{ + /// + /// A simple application that uses a custom pointcut. + /// + /// $Id: Program.cs,v 1.2 2007/07/26 00:50:11 bbaia Exp $ + public class Program + { + /// + /// The main entry point for the application. + /// + public static void Main(string[] args) + { + try + { + // Create AOP proxy using Spring.NET IoC container. + IApplicationContext ctx = ContextRegistry.GetContext(); + ICommand command = (ICommand)ctx["myServiceCommand"]; + + command.Execute(); + if (command.IsUndoCapable) + { + command.UnExecute(); + } + } + catch (Exception ex) + { + Console.Out.WriteLine(); + Console.Out.WriteLine(ex); + } + finally + { + Console.Out.WriteLine(); + Console.Out.WriteLine("--- hit to quit ---"); + Console.ReadLine(); + } + } + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Spring.AopQuickStart.Step5.2003.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Spring.AopQuickStart.Step5.2003.csproj new file mode 100644 index 00000000..d75c19e1 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Spring.AopQuickStart.Step5.2003.csproj @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Spring.AopQuickStart.Step5.2005.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Spring.AopQuickStart.Step5.2005.csproj new file mode 100644 index 00000000..5ac3ec26 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Spring.AopQuickStart.Step5.2005.csproj @@ -0,0 +1,67 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {61086108-3EB7-4ACE-B4B6-B4B60AB76108} + Exe + Properties + Spring.AopQuickStart + Spring.AopQuickStart.Step5 + Spring.AopQuickStart.Program + + + true + full + false + bin\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\..\..\bin\net\2.0\debug\antlr.runtime.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + + + + + + + + + + + {C707543A-8F0E-4483-AECC-BC6EE14C3A29} + Spring.AopQuickStart.Common.2005 + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Spring.AopQuickStart.Step5.build b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Spring.AopQuickStart.Step5.build new file mode 100644 index 00000000..efd249ba --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step5/Spring.AopQuickStart.Step5.build @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/App.config b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/App.config new file mode 100644 index 00000000..09eefc74 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/App.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Contact.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Contact.cs new file mode 100644 index 00000000..13d4af0d --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Contact.cs @@ -0,0 +1,26 @@ +namespace Spring.AopQuickStart +{ + public class Contact + { + private string firstName; + public virtual string FirstName + { + get { return firstName; } + set { firstName = value; } + } + + private string lastName; + public virtual string LastName + { + get { return lastName; } + set { lastName = value; } + } + + private string emailAddress; + public virtual string EmailAddress + { + get { return emailAddress; } + set { emailAddress = value; } + } + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/IIsModified.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/IIsModified.cs new file mode 100644 index 00000000..4b93046f --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/IIsModified.cs @@ -0,0 +1,7 @@ +namespace Spring.AopQuickStart +{ + public interface IIsModified + { + bool IsModified { get; set; } + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/IsModifiedMixin.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/IsModifiedMixin.cs new file mode 100644 index 00000000..e0a34234 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/IsModifiedMixin.cs @@ -0,0 +1,23 @@ +using AopAlliance.Aop; +using Spring.Aop.Support; + +namespace Spring.AopQuickStart +{ + public class IsModifiedMixin : IIsModified, IAdvice + { + private bool isModified = false; + + public virtual bool IsModified + { + get { return isModified; } + set { isModified = value; } + } + } + + public class IsModifiedAdvisor : DefaultIntroductionAdvisor + { + public IsModifiedAdvisor() + : base(new IsModifiedMixin()) + {} + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/ModificationAdvice.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/ModificationAdvice.cs new file mode 100644 index 00000000..2a89f9f1 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/ModificationAdvice.cs @@ -0,0 +1,64 @@ +using System; +using System.Reflection; +using AopAlliance.Intercept; +using Spring.Aop.Framework; + +namespace Spring.AopQuickStart +{ + public class ModificationAdvice : IMethodInterceptor + { + public virtual Object Invoke(IMethodInvocation invocation) + { + MethodInfo method = invocation.Method; + object proxy = invocation.Proxy; + + if (proxy is IIsModified) + { + IIsModified obj = (IIsModified) proxy; + if (IsSetter(method)) + { + obj.IsModified = HasModificationOccured(method, invocation.This, invocation.Arguments); + } + } + return invocation.Proceed(); + } + + private bool HasModificationOccured(MethodInfo setter, Object target, Object[] args) + { + PropertyInfo property = target.GetType().GetProperty(setter.Name.Substring(4)); + + if (property != null) + { + // modification check is unimportant + // for write only methods + Object newVal = args[0]; + Object oldVal = property.GetValue(target, null); + + if ((newVal == null) && (oldVal == null)) + { + return false; + } + else if ((newVal == null) && (oldVal != null)) + { + return true; + } + else if ((newVal != null) && (oldVal == null)) + { + return true; + } + else + { + return (!newVal.Equals(oldVal)); + } + } + + return false; + } + + private bool IsSetter(MethodInfo method) + { + return (method.Name.StartsWith("set_")) && (method.GetParameters().Length == 1); + } + + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/ModificationAdvisor.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/ModificationAdvisor.cs new file mode 100644 index 00000000..6036183c --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/ModificationAdvisor.cs @@ -0,0 +1,34 @@ +using System; +using Spring.Aop; +using Spring.Aop.Support; + +namespace Spring.AopQuickStart +{ + public class ModificationAdvisor : DefaultPointcutAdvisor + { + public ModificationAdvisor(Type type) + : base(new SetterPointcut(type), new ModificationAdvice()) + {} + + private class SetterPointcut : IPointcut + { + private IMethodMatcher methodMatcher = TrueMethodMatcher.True; + private ITypeFilter typeFilter; + + public SetterPointcut(Type type) + { + typeFilter = new RootTypeFilter(type); + } + + public ITypeFilter TypeFilter + { + get { return typeFilter; } + } + + public IMethodMatcher MethodMatcher + { + get { return methodMatcher; } + } + } + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Program.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Program.cs new file mode 100644 index 00000000..94a6da4f --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Program.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Aop.Framework; + +#endregion + +namespace Spring.AopQuickStart +{ + /// + /// A simple application that uses an introduction. + /// + /// $Id: Program.cs,v 1.4 2007/07/26 00:50:11 bbaia Exp $ + + public class Program + { + /// + /// The main entry point for the application. + /// + public static void Main(string[] args) + { + try + { + Contact contact = GetProxy(); + IIsModified modificationWatcher = (IIsModified)contact; + + contact.FirstName = "Aleksandar"; + if (modificationWatcher.IsModified) + Console.WriteLine("Should not flag as modification"); + + contact.FirstName = "Goran"; + + if (modificationWatcher.IsModified) + Console.WriteLine("Should flag as modification"); + + } + catch (Exception ex) + { + Console.Out.WriteLine(); + Console.Out.WriteLine(ex); + } + finally + { + Console.Out.WriteLine(); + Console.Out.WriteLine("--- hit to quit ---"); + Console.ReadLine(); + } + } + private static Contact GetProxy() + { + Contact target = new Contact(); + target.FirstName = "Aleksandar"; + target.LastName = "Seovic"; + + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvisor(new ModificationAdvisor(target.GetType())); + pf.AddIntroduction(new IsModifiedAdvisor()); + pf.ProxyTargetType = true; + + return (Contact)pf.GetProxy(); + } + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Spring.AopQuickStart.Step6.2003.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Spring.AopQuickStart.Step6.2003.csproj new file mode 100644 index 00000000..bbf08146 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Spring.AopQuickStart.Step6.2003.csproj @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Spring.AopQuickStart.Step6.2005.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Spring.AopQuickStart.Step6.2005.csproj new file mode 100644 index 00000000..404df50f --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Spring.AopQuickStart.Step6.2005.csproj @@ -0,0 +1,61 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {AB33BB4D-B449-4AC1-760A-4ACE3EA56108} + Exe + Properties + Spring.AopQuickStart + Spring.AopQuickStart.Step6 + Spring.AopQuickStart.Program + + + true + full + false + bin\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Spring.AopQuickStart.Step6.build b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Spring.AopQuickStart.Step6.build new file mode 100644 index 00000000..5e4e8896 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step6/Spring.AopQuickStart.Step6.build @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/App.config b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/App.config new file mode 100644 index 00000000..9aae4511 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/App.config @@ -0,0 +1,65 @@ + + + + + +
    +
    + + + + + + + + + + + An example that demonstrates use of AutoProxy functionality. + + + + + + + + + + + + + + + + + *Execute + + + + + + + + + + *Command + + + + + aroundAdvisor + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Commands/AnotherServiceCommand.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Commands/AnotherServiceCommand.cs new file mode 100644 index 00000000..724f88ba --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Commands/AnotherServiceCommand.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.AopQuickStart.Commands +{ + /// + /// Another simple implementation of the + /// interface. + /// + /// Bruno Baia + /// $Id: AnotherServiceCommand.cs,v 1.1 2006/12/02 13:30:03 bbaia Exp $ + public class AnotherServiceCommand : ICommand + { + #region ICommand Members + + public bool IsUndoCapable + { + get { return true; } + } + + public void Execute() + { + Console.Out.WriteLine("--- AnotherServiceCommand implementation : Execute()... ---"); + } + + public void UnExecute() + { + Console.Out.WriteLine("--- AnotherServiceCommand implementation : UnExecute()... ---"); + + // Uncomment to see IThrowAdvice implementation in action + //throw new Exception("The method or operation is not supported."); + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Program.cs b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Program.cs new file mode 100644 index 00000000..d3743d27 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Program.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using Spring.Context; +using Spring.Context.Support; + +using Spring.AopQuickStart.Commands; + +#endregion + +namespace Spring.AopQuickStart +{ + /// + /// A simple application that uses AutoProxy functionality. + /// + /// $Id: Program.cs,v 1.1 2006/12/02 13:30:03 bbaia Exp $ + public class Program + { + /// + /// The main entry point for the application. + /// + public static void Main(string[] args) + { + try + { + // Create AOP proxy using Spring.NET IoC container. + IApplicationContext ctx = ContextRegistry.GetContext(); + IDictionary commands = ctx.GetObjectsOfType(typeof(ICommand)); + + foreach (ICommand command in commands.Values) + { + command.Execute(); + } + } + catch (Exception ex) + { + Console.Out.WriteLine(); + Console.Out.WriteLine(ex); + } + finally + { + Console.Out.WriteLine(); + Console.Out.WriteLine("--- hit to quit ---"); + Console.ReadLine(); + } + } + } +} diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Spring.AopQuickStart.Step7.2003.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Spring.AopQuickStart.Step7.2003.csproj new file mode 100644 index 00000000..7f1235e9 --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Spring.AopQuickStart.Step7.2003.csproj @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Spring.AopQuickStart.Step7.2005.csproj b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Spring.AopQuickStart.Step7.2005.csproj new file mode 100644 index 00000000..88361c2a --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Spring.AopQuickStart.Step7.2005.csproj @@ -0,0 +1,63 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {0077A8CA-4C09-42B1-B227-91095CE4CCA2} + Exe + Properties + Spring.AopQuickStart + Spring.AopQuickStart.Step7 + Spring.AopQuickStart.Program + + + true + full + false + bin\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + + + + + + + + + + + {C707543A-8F0E-4483-AECC-BC6EE14C3A29} + Spring.AopQuickStart.Common.2005 + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Spring.AopQuickStart.Step7.build b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Spring.AopQuickStart.Step7.build new file mode 100644 index 00000000..c348d5ec --- /dev/null +++ b/examples/Spring/Spring.AopQuickStart/src/Spring.AopQuickStart.Step7/Spring.AopQuickStart.Step7.build @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/Spring.Calculator.2003.sln b/examples/Spring/Spring.Calculator/Spring.Calculator.2003.sln new file mode 100644 index 00000000..625b28b3 --- /dev/null +++ b/examples/Spring/Spring.Calculator/Spring.Calculator.2003.sln @@ -0,0 +1,73 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.Services.2003", "src\Spring.Calculator.Services\Spring.Calculator.Services.2003.csproj", "{D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.Contract.2003", "src\Spring.Calculator.Contract\Spring.Calculator.Contract.2003.csproj", "{284C0873-BBB4-4399-B6F7-CB7EDBD001C7}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.RemoteApp.2003", "src\Spring.Calculator.RemoteApp\Spring.Calculator.RemoteApp.2003.csproj", "{78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.ClientApp.2003", "src\Spring.Calculator.ClientApp\Spring.Calculator.ClientApp.2003.csproj", "{F202BB75-CEFD-400B-9CD3-0914F466EBF5}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.Web.2003", "http://localhost/Spring.Calculator.2003/Spring.Calculator.Web.2003.csproj", "{1AA797F9-51F7-43FE-BDFE-6AC8C1CACC0B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aspects.2003", "src\Spring.Aspects\Spring.Aspects.2003.csproj", "{6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.RegisterComponentServices.2003", "src\Spring.Calculator.RegisterComponentServices\Spring.Calculator.RegisterComponentServices.2003.csproj", "{75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Debug.ActiveCfg = Debug|.NET + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Debug.Build.0 = Debug|.NET + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Release.ActiveCfg = Release|.NET + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Release.Build.0 = Release|.NET + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Debug.ActiveCfg = Debug|.NET + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Debug.Build.0 = Debug|.NET + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Release.ActiveCfg = Release|.NET + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Release.Build.0 = Release|.NET + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Debug.ActiveCfg = Debug|.NET + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Debug.Build.0 = Debug|.NET + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Release.ActiveCfg = Release|.NET + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Release.Build.0 = Release|.NET + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Debug.ActiveCfg = Debug|.NET + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Debug.Build.0 = Debug|.NET + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Release.ActiveCfg = Release|.NET + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Release.Build.0 = Release|.NET + {1AA797F9-51F7-43FE-BDFE-6AC8C1CACC0B}.Debug.ActiveCfg = Debug|.NET + {1AA797F9-51F7-43FE-BDFE-6AC8C1CACC0B}.Debug.Build.0 = Debug|.NET + {1AA797F9-51F7-43FE-BDFE-6AC8C1CACC0B}.Release.ActiveCfg = Release|.NET + {1AA797F9-51F7-43FE-BDFE-6AC8C1CACC0B}.Release.Build.0 = Release|.NET + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Debug.ActiveCfg = Debug|.NET + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Debug.Build.0 = Debug|.NET + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Release.ActiveCfg = Release|.NET + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Release.Build.0 = Release|.NET + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Debug.ActiveCfg = Debug|.NET + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Debug.Build.0 = Debug|.NET + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Release.ActiveCfg = Release|.NET + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(SolutionItems) = postSolution + readme.txt = readme.txt + SpringCalculator.snk = SpringCalculator.snk + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.Calculator/Spring.Calculator.2005.sln b/examples/Spring/Spring.Calculator/Spring.Calculator.2005.sln new file mode 100644 index 00000000..d3f429a8 --- /dev/null +++ b/examples/Spring/Spring.Calculator/Spring.Calculator.2005.sln @@ -0,0 +1,125 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.Services.2005", "src\Spring.Calculator.Services\Spring.Calculator.Services.2005.csproj", "{D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.Contract.2005", "src\Spring.Calculator.Contract\Spring.Calculator.Contract.2005.csproj", "{284C0873-BBB4-4399-B6F7-CB7EDBD001C7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.RemoteApp.2005", "src\Spring.Calculator.RemoteApp\Spring.Calculator.RemoteApp.2005.csproj", "{78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.ClientApp.2005", "src\Spring.Calculator.ClientApp\Spring.Calculator.ClientApp.2005.csproj", "{F202BB75-CEFD-400B-9CD3-0914F466EBF5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aspects.2005", "src\Spring.Aspects\Spring.Aspects.2005.csproj", "{6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Calculator.RegisterComponentServices.2005", "src\Spring.Calculator.RegisterComponentServices\Spring.Calculator.RegisterComponentServices.2005.csproj", "{75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B1FF7E1F-9BF9-4EB3-B38C-39A0E3EA4D0A}" + ProjectSection(SolutionItems) = preProject + readme.txt = readme.txt + SpringCalculator.snk = SpringCalculator.snk + EndProjectSection +EndProject +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "D:\...\Spring.Calculator.Web.2005\", "src\Spring.Calculator.Web.2005", "{9697F719-BA0B-492E-ABAE-6132A8FCEC45}" + ProjectSection(WebsiteProperties) = preProject + ProjectReferences = "{6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}|Spring.Aspects.dll;{284C0873-BBB4-4399-B6F7-CB7EDBD001C7}|Spring.Calculator.Contract.dll;{D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}|Spring.Calculator.Services.dll;" + Debug.AspNetCompiler.VirtualPath = "/Spring.Calculator.Web.2005" + Debug.AspNetCompiler.PhysicalPath = "Spring.Calculator.Web.2005\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.Calculator.Web.2005\" + Debug.AspNetCompiler.Updateable = "true" + Debug.AspNetCompiler.ForceOverwrite = "true" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.VirtualPath = "/Spring.Calculator.Web.2005" + Release.AspNetCompiler.PhysicalPath = "Spring.Calculator.Web.2005\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.Calculator.Web.2005\" + Release.AspNetCompiler.Updateable = "true" + Release.AspNetCompiler.ForceOverwrite = "true" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.Debug = "False" + VWDPort = "1643" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|.NET = Debug|.NET + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Release|.NET = Release|.NET + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Debug|.NET.ActiveCfg = Debug|Any CPU + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Release|.NET.ActiveCfg = Release|Any CPU + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Release|Any CPU.Build.0 = Release|Any CPU + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Debug|.NET.ActiveCfg = Debug|Any CPU + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Release|.NET.ActiveCfg = Release|Any CPU + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Release|Any CPU.Build.0 = Release|Any CPU + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Debug|.NET.ActiveCfg = Debug|Any CPU + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Release|.NET.ActiveCfg = Release|Any CPU + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Release|Any CPU.Build.0 = Release|Any CPU + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Debug|.NET.ActiveCfg = Debug|Any CPU + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Release|.NET.ActiveCfg = Release|Any CPU + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Release|Any CPU.Build.0 = Release|Any CPU + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {F202BB75-CEFD-400B-9CD3-0914F466EBF5}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Debug|.NET.ActiveCfg = Debug|Any CPU + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Release|.NET.ActiveCfg = Release|Any CPU + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Release|Any CPU.Build.0 = Release|Any CPU + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Debug|.NET.ActiveCfg = Debug|Any CPU + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Release|.NET.ActiveCfg = Release|Any CPU + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Release|Any CPU.Build.0 = Release|Any CPU + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {9697F719-BA0B-492E-ABAE-6132A8FCEC45}.Debug|.NET.ActiveCfg = Debug|.NET + {9697F719-BA0B-492E-ABAE-6132A8FCEC45}.Debug|.NET.Build.0 = Debug|.NET + {9697F719-BA0B-492E-ABAE-6132A8FCEC45}.Debug|Any CPU.ActiveCfg = Debug|.NET + {9697F719-BA0B-492E-ABAE-6132A8FCEC45}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET + {9697F719-BA0B-492E-ABAE-6132A8FCEC45}.Debug|Mixed Platforms.Build.0 = Debug|.NET + {9697F719-BA0B-492E-ABAE-6132A8FCEC45}.Release|.NET.ActiveCfg = Debug|.NET + {9697F719-BA0B-492E-ABAE-6132A8FCEC45}.Release|.NET.Build.0 = Debug|.NET + {9697F719-BA0B-492E-ABAE-6132A8FCEC45}.Release|Any CPU.ActiveCfg = Debug|.NET + {9697F719-BA0B-492E-ABAE-6132A8FCEC45}.Release|Mixed Platforms.ActiveCfg = Debug|.NET + {9697F719-BA0B-492E-ABAE-6132A8FCEC45}.Release|Mixed Platforms.Build.0 = Debug|.NET + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.Calculator/Spring.Calculator.build b/examples/Spring/Spring.Calculator/Spring.Calculator.build new file mode 100644 index 00000000..676a14b2 --- /dev/null +++ b/examples/Spring/Spring.Calculator/Spring.Calculator.build @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/Spring.Calculator.snk b/examples/Spring/Spring.Calculator/Spring.Calculator.snk new file mode 100644 index 00000000..a1ab9f35 Binary files /dev/null and b/examples/Spring/Spring.Calculator/Spring.Calculator.snk differ diff --git a/examples/Spring/Spring.Calculator/lib/net/1.1/Common.Logging.Log4Net.dll b/examples/Spring/Spring.Calculator/lib/net/1.1/Common.Logging.Log4Net.dll new file mode 100644 index 00000000..f334f6de Binary files /dev/null and b/examples/Spring/Spring.Calculator/lib/net/1.1/Common.Logging.Log4Net.dll differ diff --git a/examples/Spring/Spring.Calculator/lib/net/1.1/log4net.dll b/examples/Spring/Spring.Calculator/lib/net/1.1/log4net.dll new file mode 100644 index 00000000..995816f2 Binary files /dev/null and b/examples/Spring/Spring.Calculator/lib/net/1.1/log4net.dll differ diff --git a/examples/Spring/Spring.Calculator/lib/net/2.0/Common.Logging.Log4Net.dll b/examples/Spring/Spring.Calculator/lib/net/2.0/Common.Logging.Log4Net.dll new file mode 100644 index 00000000..3900bb87 Binary files /dev/null and b/examples/Spring/Spring.Calculator/lib/net/2.0/Common.Logging.Log4Net.dll differ diff --git a/examples/Spring/Spring.Calculator/lib/net/2.0/log4net.dll b/examples/Spring/Spring.Calculator/lib/net/2.0/log4net.dll new file mode 100644 index 00000000..ffc57e11 Binary files /dev/null and b/examples/Spring/Spring.Calculator/lib/net/2.0/log4net.dll differ diff --git a/examples/Spring/Spring.Calculator/readme.txt b/examples/Spring/Spring.Calculator/readme.txt new file mode 100644 index 00000000..710b56f5 --- /dev/null +++ b/examples/Spring/Spring.Calculator/readme.txt @@ -0,0 +1,13 @@ +In order to be able to run the Calculator example with VS2003 you will need +to create 'Spring.Calculator.2003' virtual directory using IIS Administrator and point it to : + "examples\Spring\Spring.Calculator\Spring.Calculator.Web.2003" + + +Documentation related to this example : +http://www.springframework.net/doc-latest/reference/html/remoting-quickstart.html + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Aspects/AssemblyInfo.cs b/examples/Spring/Spring.Calculator/src/Spring.Aspects/AssemblyInfo.cs new file mode 100644 index 00000000..8e304fc5 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Aspects/AssemblyInfo.cs @@ -0,0 +1,36 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring Aspect library")] +[assembly: AssemblyDescription("Aspect library using Spring.Aop for examples")] +[assembly: AssemblyVersion("1.0.0.0")] + +#if !NET_2_0 +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +#else +[assembly: AssemblyConfiguration("net-2.0.win32; Release")] +#endif +[assembly: AssemblyCompany("http://www.springframework.net")] +[assembly: AssemblyProduct("Spring.NET Framework")] +[assembly: AssemblyCopyright("Copyright 2002-2006 Spring.NET Framework Team.")] +[assembly: AssemblyTrademark("Apache License, Version 2.0")] diff --git a/examples/Spring/Spring.Calculator/src/Spring.Aspects/Logging/CommonLoggingAroundAdvice.cs b/examples/Spring/Spring.Calculator/src/Spring.Aspects/Logging/CommonLoggingAroundAdvice.cs new file mode 100644 index 00000000..02f2bc30 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Aspects/Logging/CommonLoggingAroundAdvice.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using AopAlliance.Intercept; +using Common.Logging; + +#endregion + +namespace Spring.Aspects.Logging +{ + /// + /// Basic implementation of a logging aspect using Common.Logging library. + /// + /// Bruno Baia + /// $Id: CommonLoggingAroundAdvice.cs,v 1.2 2007/06/27 16:00:42 bbaia Exp $ + public class CommonLoggingAroundAdvice : IMethodInterceptor + { + #region Logging + + private static readonly ILog LOG = LogManager.GetLogger(typeof(CommonLoggingAroundAdvice)); + + #endregion + + #region Fields + + private LogLevel _level = LogLevel.All; + + #endregion + + #region Properties + + public LogLevel Level + { + get { return _level; } + set { _level = value; } + } + + #endregion + + #region IMethodInterceptor Members + + public object Invoke(IMethodInvocation invocation) + { + Log("Intercepted call : about to invoke method '{0}'", invocation.Method.Name); + object returnValue = invocation.Proceed(); + Log("Intercepted call : returned '{0}'", returnValue); + return returnValue; + } + + #endregion + + #region Private Methods + + private void Log(string text, params object[] args) + { + switch(Level) + { + case LogLevel.All : + case LogLevel.Debug : + if (LOG.IsDebugEnabled) LOG.Debug(String.Format(text, args)); + break; + case LogLevel.Error : + if (LOG.IsErrorEnabled) LOG.Error(String.Format(text, args)); + break; + case LogLevel.Fatal : + if (LOG.IsFatalEnabled) LOG.Fatal(String.Format(text, args)); + break; + case LogLevel.Info : + if (LOG.IsInfoEnabled) LOG.Info(String.Format(text, args)); + break; + case LogLevel.Warn : + if (LOG.IsWarnEnabled) LOG.Warn(String.Format(text, args)); + break; + case LogLevel.Off: + default : + break; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Aspects/Logging/ConsoleLoggingAroundAdvice.cs b/examples/Spring/Spring.Calculator/src/Spring.Aspects/Logging/ConsoleLoggingAroundAdvice.cs new file mode 100644 index 00000000..0e7b60ff --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Aspects/Logging/ConsoleLoggingAroundAdvice.cs @@ -0,0 +1,93 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; + +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aspects.Logging +{ + /// + /// Basic implementation of a logging aspect using . + /// + /// Bruno Baia + /// $Id: ConsoleLoggingAroundAdvice.cs,v 1.1 2006/11/26 12:26:34 bbaia Exp $ + public class ConsoleLoggingAroundAdvice : IMethodInterceptor + { + #region Fields + +#if NET_2_0 + private ConsoleColor _foregroundColor = ConsoleColor.Gray; +#endif + + #endregion + + #region Properties + +#if NET_2_0 + /// + /// Gets or sets the foreground color of the console. + /// + public ConsoleColor ForegroundColor + { + get { return _foregroundColor; } + set { _foregroundColor = value; } + } +#else + /// + /// The ConsoleColor is only avalaible since 2.0 only. + /// + /// + /// Avoid dependency injection errors. + /// + public string ForegroundColor + { + get { throw new Exception("The method or operation is not supported."); } + set { } + } +#endif + + #endregion + + #region IMethodInterceptor Members + + public object Invoke(IMethodInvocation invocation) + { +#if NET_2_0 + ConsoleColor previousColor = Console.ForegroundColor; + Console.ForegroundColor = _foregroundColor; +#endif + Console.Out.WriteLine(String.Format("Intercepted call : about to invoke method '{0}'", invocation.Method.Name)); + object returnValue = invocation.Proceed(); + Console.Out.WriteLine(String.Format("Intercepted call : returned '{0}'", returnValue)); +#if NET_2_0 + Console.ForegroundColor = previousColor; +#endif + return returnValue; + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Aspects/Spring.Aspects.2003.csproj b/examples/Spring/Spring.Calculator/src/Spring.Aspects/Spring.Aspects.2003.csproj new file mode 100644 index 00000000..94f457ff --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Aspects/Spring.Aspects.2003.csproj @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Aspects/Spring.Aspects.2005.csproj b/examples/Spring/Spring.Calculator/src/Spring.Aspects/Spring.Aspects.2005.csproj new file mode 100644 index 00000000..794d235a --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Aspects/Spring.Aspects.2005.csproj @@ -0,0 +1,104 @@ + + + Local + 8.0.50727 + 2.0 + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC} + Debug + AnyCPU + + + + + Spring.Aspects + + + JScript + Grid + IE50 + false + Library + Spring.Aspects + OnBuildSuccess + + + + + + + false + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0,STRONG + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Common.Logging.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + System + + + + + Code + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Aspects/Spring.Aspects.build b/examples/Spring/Spring.Calculator/src/Spring.Aspects/Spring.Aspects.build new file mode 100644 index 00000000..c43aa4bf --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Aspects/Spring.Aspects.build @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/App.config b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/App.config new file mode 100644 index 00000000..a6b8fefc --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/App.config @@ -0,0 +1,71 @@ + + + + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Definitions of objects to be exported. + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/AssemblyInfo.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/AssemblyInfo.cs new file mode 100644 index 00000000..8975f419 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/AssemblyInfo.cs @@ -0,0 +1,36 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring Calculator client application")] +[assembly: AssemblyDescription("Client application for Spring Calculator example")] +[assembly: AssemblyVersion("1.0.0.0")] + +#if !NET_2_0 +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +#else +[assembly: AssemblyConfiguration("net-2.0.win32; Release")] +#endif +[assembly: AssemblyCompany("http://www.springframework.net")] +[assembly: AssemblyProduct("Spring.NET Framework")] +[assembly: AssemblyCopyright("Copyright 2002-2006 Spring.NET Framework Team.")] +[assembly: AssemblyTrademark("Apache License, Version 2.0")] \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/EnterpriseServices/enterpriseServices.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/EnterpriseServices/enterpriseServices.xml new file mode 100644 index 00000000..5f78ab38 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/EnterpriseServices/enterpriseServices.xml @@ -0,0 +1,17 @@ + + + + enterpriseServices + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/InProcess/inProcess.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/InProcess/inProcess.xml new file mode 100644 index 00000000..d223e748 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/InProcess/inProcess.xml @@ -0,0 +1,8 @@ + + + + inProcess + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao-aop.xml new file mode 100644 index 00000000..58a89a26 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao-aop.xml @@ -0,0 +1,32 @@ + + + + cao + + + + + + + + + + ConsoleLoggingAroundAdvice + + + + + Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao-ctor-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao-ctor-aop.xml new file mode 100644 index 00000000..4fe61209 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao-ctor-aop.xml @@ -0,0 +1,7 @@ + + + + Not supported + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao-ctor.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao-ctor.xml new file mode 100644 index 00000000..323f9646 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao-ctor.xml @@ -0,0 +1,27 @@ + + + + cao + + + + 3 + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao.xml new file mode 100644 index 00000000..fa7db589 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/cao.xml @@ -0,0 +1,18 @@ + + + + cao + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleCall-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleCall-aop.xml new file mode 100644 index 00000000..97299e2f --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleCall-aop.xml @@ -0,0 +1,33 @@ + + + + saoSingleCall-aop + + + + + + + + + + ConsoleLoggingAroundAdvice + + + + + Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleCall.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleCall.xml new file mode 100644 index 00000000..664de664 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleCall.xml @@ -0,0 +1,19 @@ + + + + saoSingleCall + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleton-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleton-aop.xml new file mode 100644 index 00000000..a0f1408d --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleton-aop.xml @@ -0,0 +1,33 @@ + + + + saoSingleton-aop + + + + + + + + + + ConsoleLoggingAroundAdvice + + + + + Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleton.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleton.xml new file mode 100644 index 00000000..790eea5a --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/Remoting/saoSingleton.xml @@ -0,0 +1,19 @@ + + + + saoSingleton + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2003/calculatorService.wsdl b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2003/calculatorService.wsdl new file mode 100644 index 00000000..38f1f927 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2003/calculatorService.wsdl @@ -0,0 +1,272 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spring Calculator Web Services + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2003/webServices-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2003/webServices-aop.xml new file mode 100644 index 00000000..c630ee9e --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2003/webServices-aop.xml @@ -0,0 +1,20 @@ + + + + webServices-aop + + + + + + + + + + + ConsoleLoggingAroundAdvice + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2003/webServices.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2003/webServices.xml new file mode 100644 index 00000000..57dae60b --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2003/webServices.xml @@ -0,0 +1,18 @@ + + + + webServices + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2005/calculatorService.wsdl b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2005/calculatorService.wsdl new file mode 100644 index 00000000..be2fb66a --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2005/calculatorService.wsdl @@ -0,0 +1,351 @@ + + + Spring Calculator Web Services + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spring Calculator Web Services + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2005/webServices-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2005/webServices-aop.xml new file mode 100644 index 00000000..d0c8e048 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2005/webServices-aop.xml @@ -0,0 +1,20 @@ + + + + webServices-aop + + + + + + + + + + + ConsoleLoggingAroundAdvice + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2005/webServices.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2005/webServices.xml new file mode 100644 index 00000000..5d441725 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Config/WebServices2005/webServices.xml @@ -0,0 +1,18 @@ + + + + webServices + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Program.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Program.cs new file mode 100644 index 00000000..5c338c69 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Program.cs @@ -0,0 +1,72 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Context; +using Spring.Context.Support; + +using Spring.Calculator.Interfaces; + +#endregion + +namespace Spring.Calculator.ClientApp +{ + class Program + { + [STAThread] + static void Main() + { + try + { + Pause(); + + IApplicationContext ctx = ContextRegistry.GetContext(); + + Console.WriteLine("Get Calculator..."); + IAdvancedCalculator firstCalc = (IAdvancedCalculator) ctx.GetObject("calculatorService"); + Console.WriteLine("Divide(11, 2) : " + firstCalc.Divide(11, 2)); + Console.WriteLine("Memory = " + firstCalc.GetMemory()); + firstCalc.MemoryAdd(2); + Console.WriteLine("Memory + 2 = " + firstCalc.GetMemory()); + + Console.WriteLine("Get Calculator..."); + IAdvancedCalculator secondCalc = (IAdvancedCalculator) ctx.GetObject("calculatorService"); + Console.WriteLine("Memory = " + secondCalc.GetMemory()); + } + catch (Exception e) + { + Console.WriteLine(e); + } + finally + { + Pause(); + } + } + + public static void Pause() + { + Console.Write("--- Press to continue ---"); + Console.ReadLine(); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.2003.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.2003.csproj new file mode 100644 index 00000000..1aaa6131 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.2003.csproj @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.2005.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.2005.csproj new file mode 100644 index 00000000..96700c02 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.2005.csproj @@ -0,0 +1,142 @@ + + + Local + 8.0.50727 + 2.0 + {F202BB75-CEFD-400B-9CD3-0914F466EBF5} + Debug + AnyCPU + + + + + Spring.Calculator.ClientApp + + + JScript + Grid + IE50 + false + Exe + Spring.Calculator.ClientApp + OnBuildSuccess + + + + + + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + Spring.Aspects.2003 + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + Spring.Calculator.Contract.2003 + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + Spring.Calculator.Services.2003 + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Services.dll + + + system + + + system.web.services + + + + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.build b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.build new file mode 100644 index 00000000..488a8032 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.build @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/AssemblyInfo.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/AssemblyInfo.cs new file mode 100644 index 00000000..2aaea54d --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/AssemblyInfo.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring Calculator contract")] +[assembly: AssemblyDescription("Contract for the Spring Calculator example")] +[assembly: AssemblyVersion("1.0.0.0")] + +#if !NET_2_0 +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +#else +[assembly: AssemblyConfiguration("net-2.0.win32; Release")] +#endif +[assembly: AssemblyCompany("http://www.springframework.net")] +[assembly: AssemblyProduct("Spring.NET Framework")] +[assembly: AssemblyCopyright("Copyright 2002-2006 Spring.NET Framework Team.")] +[assembly: AssemblyTrademark("Apache License, Version 2.0")] + +#if STRONG +[assembly: AssemblyDelaySign(false)] +#if !NET_2_0 +[assembly: AssemblyKeyFile(@"Spring.Calculator.snk")] +#endif +#endif \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Domain/DivisionResult.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Domain/DivisionResult.cs new file mode 100644 index 00000000..36f1ebbd --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Domain/DivisionResult.cs @@ -0,0 +1,67 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Calculator.Domain +{ + [Serializable] + public class DivisionResult + { + private int _quotient = 0; + private int _rest = 0; + + /// + /// Gets or sets the quotient of the division. + /// + public int Quotient + { + get { return _quotient; } + set { _quotient = value; } + } + + /// + /// Gets or sets the rest of the division. + /// + public int Rest + { + get { return _rest; } + set { _rest = value; } + } + + /// + /// Creates a new instance of the class. + /// + public DivisionResult() {} + + /// + /// Rrturns a string represantation of a division's result. + /// + /// + public override string ToString() + { + return String.Format("Quotient: '{0}'; Rest: '{1}'", _quotient, _rest); + } + } +} diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Interfaces/IAdvancedCalculator.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Interfaces/IAdvancedCalculator.cs new file mode 100644 index 00000000..3d7b0474 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Interfaces/IAdvancedCalculator.cs @@ -0,0 +1,42 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +#endregion + +namespace Spring.Calculator.Interfaces +{ + /// + /// An advanced calculator service interface. + /// + /// Bruno Baia + /// $Id: IAdvancedCalculator.cs,v 1.1 2006/10/29 18:39:04 bbaia Exp $ + public interface IAdvancedCalculator : ICalculator + { + int GetMemory(); + + void SetMemory(int memoryValue); + + void MemoryClear(); + + void MemoryAdd(int num); + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Interfaces/ICalculator.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Interfaces/ICalculator.cs new file mode 100644 index 00000000..eb1305a8 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Interfaces/ICalculator.cs @@ -0,0 +1,44 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Calculator.Domain; + +#endregion + +namespace Spring.Calculator.Interfaces +{ + /// + /// A simple calculator service interface. + /// + /// Bruno Baia + /// $Id: ICalculator.cs,v 1.1 2006/10/29 18:39:04 bbaia Exp $ + public interface ICalculator + { + int Add(int n1, int n2); + + int Substract(int n1, int n2); + + DivisionResult Divide(int n1, int n2); + + int Multiply(int n1, int n2); + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.Contract.2003.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.Contract.2003.csproj new file mode 100644 index 00000000..78edd915 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.Contract.2003.csproj @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.Contract.2005.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.Contract.2005.csproj new file mode 100644 index 00000000..99898121 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.Contract.2005.csproj @@ -0,0 +1,95 @@ + + + Local + 8.0.50727 + 2.0 + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7} + Debug + AnyCPU + + + + + Spring.Calculator.Contract + ../../Spring.Calculator.snk + JScript + Grid + IE50 + false + Library + Spring.Calculator + OnBuildSuccess + + + + + + + true + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0;STRONG + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0;STRONG + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + Code + + + + Code + + + Code + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.Contract.build b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.Contract.build new file mode 100644 index 00000000..01bf5481 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.Contract.build @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.snk b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.snk new file mode 100644 index 00000000..a1ab9f35 Binary files /dev/null and b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Contract/Spring.Calculator.snk differ diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/App.config b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/App.config new file mode 100644 index 00000000..890db9c6 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/App.config @@ -0,0 +1,27 @@ + + + + + +
    +
    + + + + + + + + + + + + Definitions of objects to be registered. + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/AssemblyInfo.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/AssemblyInfo.cs new file mode 100644 index 00000000..87cf23ea --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/AssemblyInfo.cs @@ -0,0 +1,36 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring Calculator COM+ registry service")] +[assembly: AssemblyDescription("COM+ registry service for Spring Calculator example")] +[assembly: AssemblyVersion("1.0.0.0")] + +#if !NET_2_0 +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +#else +[assembly: AssemblyConfiguration("net-2.0.win32; Release")] +#endif +[assembly: AssemblyCompany("http://www.springframework.net")] +[assembly: AssemblyProduct("Spring.NET Framework")] +[assembly: AssemblyCopyright("Copyright 2002-2006 Spring.NET Framework Team.")] +[assembly: AssemblyTrademark("Apache License, Version 2.0")] \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Config/enterpriseServices.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Config/enterpriseServices.xml new file mode 100644 index 00000000..74f6f0f0 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Config/enterpriseServices.xml @@ -0,0 +1,55 @@ + + + + enterpriseService + + + + + + + + + + + + + + + + + + + + + + Spring Calculator Application + + + Spring Calculator application. + + + + + ApplicationComponent + + + + + + Admin : Administrator role + User : User role + Manager : Administrator role + + + + + + + + + Spring.Calculator.EnterpriseServices + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Program.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Program.cs new file mode 100644 index 00000000..2d207fe0 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Program.cs @@ -0,0 +1,55 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Context.Support; + +#endregion + +namespace Spring.Calculator.RegisterComponentServices +{ + class Program + { + [STAThread] + static void Main() + { + try + { + Console.Out.WriteLine("Registering Component Services..."); + + ContextRegistry.GetContext(); + + Console.Out.WriteLine("Component Services registered."); + } + catch (Exception e) + { + Console.Out.WriteLine(e); + } + finally + { + Console.Out.WriteLine("--- Press to quit ---"); + Console.ReadLine(); + } + } + } +} diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.2003.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.2003.csproj new file mode 100644 index 00000000..472dfb1f --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.2003.csproj @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.2005.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.2005.csproj new file mode 100644 index 00000000..45ec517d --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.2005.csproj @@ -0,0 +1,113 @@ + + + Local + 8.0.50727 + 2.0 + {75E2042D-1FCF-42F1-8FB9-0C6C40AE4ACD} + Debug + AnyCPU + + + + + Spring.Calculator.RegisterComponentServices + + + JScript + Grid + IE50 + false + Exe + Spring.Calculator.RegisterComponentServices + OnBuildSuccess + + + + + + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + + Code + + + Code + + + + + + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7} + Spring.Calculator.Contract.2005 + + + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827} + Spring.Calculator.Services.2005 + + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Services.dll + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.build b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.build new file mode 100644 index 00000000..f3662f9a --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.build @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/App.config b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/App.config new file mode 100644 index 00000000..3d9b3652 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/App.config @@ -0,0 +1,93 @@ + + + + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + Definitions of objects to be exported. + + + + + + + + + + + + + + + + + + + + + + ConsoleLoggingAroundAdvice + + + + + + + + + + + + + + + + + + + ConsoleLoggingAroundAdvice + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/AssemblyInfo.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/AssemblyInfo.cs new file mode 100644 index 00000000..6b1e3d0a --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/AssemblyInfo.cs @@ -0,0 +1,36 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring Calculator remote application")] +[assembly: AssemblyDescription("Remote application for Spring Calculator example")] +[assembly: AssemblyVersion("1.0.0.0")] + +#if !NET_2_0 +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +#else +[assembly: AssemblyConfiguration("net-2.0.win32; Release")] +#endif +[assembly: AssemblyCompany("http://www.springframework.net")] +[assembly: AssemblyProduct("Spring.NET Framework")] +[assembly: AssemblyCopyright("Copyright 2002-2006 Spring.NET Framework Team.")] +[assembly: AssemblyTrademark("Apache License, Version 2.0")] \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/cao-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/cao-aop.xml new file mode 100644 index 00000000..459e350e --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/cao-aop.xml @@ -0,0 +1,22 @@ + + + + Registers the calculator service as a CAO. + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/cao.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/cao.xml new file mode 100644 index 00000000..e87e69c2 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/cao.xml @@ -0,0 +1,22 @@ + + + + Registers the calculator service as a CAO. + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleCall-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleCall-aop.xml new file mode 100644 index 00000000..b8dcbc04 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleCall-aop.xml @@ -0,0 +1,20 @@ + + + + Registers the calculator service proxied as a SAO in 'SingleCall' mode. + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleCall.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleCall.xml new file mode 100644 index 00000000..763c496e --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleCall.xml @@ -0,0 +1,20 @@ + + + + Registers the calculator service as a SAO in 'SingleCall' mode. + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleton-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleton-aop.xml new file mode 100644 index 00000000..4fb69006 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleton-aop.xml @@ -0,0 +1,20 @@ + + + + Registers the calculator service proxied as a SAO in 'Singleton' mode. + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleton.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleton.xml new file mode 100644 index 00000000..88b0e694 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Config/saoSingleton.xml @@ -0,0 +1,20 @@ + + + + Registers the calculator service as a SAO in 'Singleton' mode. + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Program.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Program.cs new file mode 100644 index 00000000..4bc4ebda --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Program.cs @@ -0,0 +1,55 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Remoting; + +using Spring.Context.Support; + +#endregion + +namespace Spring.Calculator.RemoteApp +{ + class Program + { + [STAThread] + static void Main() + { + try + { + // Force Spring to load configuration + ContextRegistry.GetContext(); + + Console.Out.WriteLine("Server listening..."); + } + catch (Exception e) + { + Console.Out.WriteLine(e); + } + finally + { + Console.Out.WriteLine("--- Press to quit ---"); + Console.ReadLine(); + } + } + } +} diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Spring.Calculator.RemoteApp.2003.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Spring.Calculator.RemoteApp.2003.csproj new file mode 100644 index 00000000..daf94820 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Spring.Calculator.RemoteApp.2003.csproj @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Spring.Calculator.RemoteApp.2005.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Spring.Calculator.RemoteApp.2005.csproj new file mode 100644 index 00000000..2ba72477 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Spring.Calculator.RemoteApp.2005.csproj @@ -0,0 +1,129 @@ + + + Local + 8.0.50727 + 2.0 + {78B542F5-0CE3-4EDD-ACF7-3B2C619ADFC1} + Debug + AnyCPU + + + + + Spring.Calculator.RemoteApp + + + JScript + Grid + IE50 + false + Exe + Spring.Calculator.RemoteApp + OnBuildSuccess + + + + + + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Services.dll + + + System + + + + + + Code + + + Code + + + + + + + + + + + {6B34626E-8D2A-4387-BBB6-CB352D4DBDEC} + Spring.Aspects.2005 + + + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7} + Spring.Calculator.Contract.2005 + + + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827} + Spring.Calculator.Services.2005 + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Spring.Calculator.RemoteApp.build b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Spring.Calculator.RemoteApp.build new file mode 100644 index 00000000..19b822b4 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.RemoteApp/Spring.Calculator.RemoteApp.build @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/AssemblyInfo.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/AssemblyInfo.cs new file mode 100644 index 00000000..e556b1d8 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/AssemblyInfo.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring Calculator services")] +[assembly: AssemblyDescription("Services for the Spring Calculator example")] +[assembly: AssemblyVersion("1.0.0.0")] + +#if !NET_2_0 +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +#else +[assembly: AssemblyConfiguration("net-2.0.win32; Release")] +#endif +[assembly: AssemblyCompany("http://www.springframework.net")] +[assembly: AssemblyProduct("Spring.NET Framework")] +[assembly: AssemblyCopyright("Copyright 2002-2006 Spring.NET Framework Team.")] +[assembly: AssemblyTrademark("Apache License, Version 2.0")] + +#if STRONG +[assembly: AssemblyDelaySign(false)] +#if !NET_2_0 +[assembly: AssemblyKeyFile(@"Spring.Calculator.snk")] +#endif +#endif \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Services/AdvancedCalculator.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Services/AdvancedCalculator.cs new file mode 100644 index 00000000..0fe3687b --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Services/AdvancedCalculator.cs @@ -0,0 +1,78 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Calculator.Interfaces; + +#endregion + +namespace Spring.Calculator.Services +{ + /// + /// An advanced calculator service. + /// + /// Bruno Baia + /// $Id: AdvancedCalculator.cs,v 1.2 2006/12/04 01:38:42 bbaia Exp $ + public class AdvancedCalculator : Calculator, IAdvancedCalculator + { + #region Fields + + private int memoryStore = 0; + + #endregion + + #region Constructor(s) / Destructor + + public AdvancedCalculator() + {} + + public AdvancedCalculator(int initialMemory) + { + memoryStore = initialMemory; + } + + #endregion + + #region IAdvancedCalculator Members + + public int GetMemory() + { + return memoryStore; + } + + public void SetMemory(int memoryValue) + { + memoryStore = memoryValue; + } + + public void MemoryClear() + { + memoryStore = 0; + } + + public void MemoryAdd(int num) + { + memoryStore += num; + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Services/Calculator.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Services/Calculator.cs new file mode 100644 index 00000000..36c84a7a --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Services/Calculator.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Calculator.Domain; +using Spring.Calculator.Interfaces; + +#endregion + +namespace Spring.Calculator.Services +{ + /// + /// A calculator service. + /// + /// Bruno Baia + /// $Id: Calculator.cs,v 1.1 2006/10/29 18:39:13 bbaia Exp $ + public class Calculator : ICalculator + { + #region ICalculator Members + + public int Add(int n1, int n2) + { + return n1 + n2; + } + + public int Substract(int n1, int n2) + { + return n1 - n2; + } + + public DivisionResult Divide(int n1, int n2) + { + DivisionResult result = new DivisionResult(); + result.Quotient = n1 / n2; + result.Rest = n1 % n2; + return result; + } + + public int Multiply(int n1, int n2) + { + return n1 * n2; + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.Services.2003.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.Services.2003.csproj new file mode 100644 index 00000000..50341278 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.Services.2003.csproj @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.Services.2005.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.Services.2005.csproj new file mode 100644 index 00000000..383959b6 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.Services.2005.csproj @@ -0,0 +1,100 @@ + + + Local + 8.0.50727 + 2.0 + {D9FB04E5-D636-4EC2-A3AB-FAB1C4F37827} + Debug + AnyCPU + + + + + Spring.Calculator.Services + ../../Spring.Calculator.snk + JScript + Grid + IE50 + false + Library + Spring.Calculator + OnBuildSuccess + + + + + + + true + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0;STRONG + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0;STRONG + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + Code + + + Code + + + Code + + + + + {284C0873-BBB4-4399-B6F7-CB7EDBD001C7} + Spring.Calculator.Contract.2005 + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.Services.build b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.Services.build new file mode 100644 index 00000000..c8b9a8ed --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.Services.build @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.snk b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.snk new file mode 100644 index 00000000..a1ab9f35 Binary files /dev/null and b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Services/Spring.Calculator.snk differ diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/AssemblyInfo.cs b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/AssemblyInfo.cs new file mode 100644 index 00000000..383a480c --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/AssemblyInfo.cs @@ -0,0 +1,36 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring Calculator Web application")] +[assembly: AssemblyDescription("C Web application for Spring Calculator example")] +[assembly: AssemblyVersion("1.0.0.0")] + +#if !NET_2_0 +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +#else +[assembly: AssemblyConfiguration("net-2.0.win32; Release")] +#endif +[assembly: AssemblyCompany("http://www.springframework.net")] +[assembly: AssemblyProduct("Spring.NET Framework")] +[assembly: AssemblyCopyright("Copyright 2002-2006 Spring.NET Framework Team.")] +[assembly: AssemblyTrademark("Apache License, Version 2.0")] \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Config/webServices-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Config/webServices-aop.xml new file mode 100644 index 00000000..22617648 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Config/webServices-aop.xml @@ -0,0 +1,12 @@ + + + + webServices-aop + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Config/webServices.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Config/webServices.xml new file mode 100644 index 00000000..a9306898 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Config/webServices.xml @@ -0,0 +1,12 @@ + + + + webServices + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Default.aspx b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Default.aspx new file mode 100644 index 00000000..ba146152 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Default.aspx @@ -0,0 +1,18 @@ +<%@ Page %> + + + +Web Services + + + + + + +
    +

    CalculatorService

    +
    +

    CalculatorServiceWeaved

    +
    + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Spring.Calculator.Web.2003.csproj b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Spring.Calculator.Web.2003.csproj new file mode 100644 index 00000000..37087a4e --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Spring.Calculator.Web.2003.csproj @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Spring.Calculator.Web.2003.csproj.webinfo b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Spring.Calculator.Web.2003.csproj.webinfo new file mode 100644 index 00000000..501f95dd --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Spring.Calculator.Web.2003.csproj.webinfo @@ -0,0 +1,4 @@ + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Web.config b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Web.config new file mode 100644 index 00000000..660ae6ac --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2003/Web.config @@ -0,0 +1,111 @@ + + + + + +
    + + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + Definitions of objects to be exported. + + + + + + + + + + + + + + + + CommonLoggingAroundAdvice + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Common.Logging.Log4Net.dll.refresh b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Common.Logging.Log4Net.dll.refresh new file mode 100644 index 00000000..752c9cd6 Binary files /dev/null and b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Common.Logging.Log4Net.dll.refresh differ diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Common.Logging.dll.refresh b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Common.Logging.dll.refresh new file mode 100644 index 00000000..7ae4334d Binary files /dev/null and b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Common.Logging.dll.refresh differ diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Aop.dll.refresh b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Aop.dll.refresh new file mode 100644 index 00000000..cc858ae7 Binary files /dev/null and b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Aop.dll.refresh differ diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Core.dll.refresh b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Core.dll.refresh new file mode 100644 index 00000000..d6773450 Binary files /dev/null and b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Core.dll.refresh differ diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Services.dll.refresh b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Services.dll.refresh new file mode 100644 index 00000000..a87a804e Binary files /dev/null and b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Services.dll.refresh differ diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Web.dll.refresh b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Web.dll.refresh new file mode 100644 index 00000000..b31d8acc Binary files /dev/null and b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/Spring.Web.dll.refresh differ diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/antlr.runtime.dll.refresh b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/antlr.runtime.dll.refresh new file mode 100644 index 00000000..cdcfe328 Binary files /dev/null and b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/antlr.runtime.dll.refresh differ diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/log4net.dll.refresh b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/log4net.dll.refresh new file mode 100644 index 00000000..a0e5896a Binary files /dev/null and b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Bin/log4net.dll.refresh differ diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Config/webServices-aop.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Config/webServices-aop.xml new file mode 100644 index 00000000..22617648 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Config/webServices-aop.xml @@ -0,0 +1,12 @@ + + + + webServices-aop + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Config/webServices.xml b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Config/webServices.xml new file mode 100644 index 00000000..a9306898 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Config/webServices.xml @@ -0,0 +1,12 @@ + + + + webServices + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Default.aspx b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Default.aspx new file mode 100644 index 00000000..ba146152 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Default.aspx @@ -0,0 +1,18 @@ +<%@ Page %> + + + +Web Services + + + + + + +
    +

    CalculatorService

    +
    +

    CalculatorServiceWeaved

    +
    + + \ No newline at end of file diff --git a/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Web.config b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Web.config new file mode 100644 index 00000000..8422cda5 --- /dev/null +++ b/examples/Spring/Spring.Calculator/src/Spring.Calculator.Web.2005/Web.config @@ -0,0 +1,111 @@ + + + + + +
    + + + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + Definitions of objects to be exported. + + + + + + + + + + + + + + + + CommonLoggingAroundAdvice + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/Debug.Spring.Northwind.sln b/examples/Spring/Spring.Data.NHibernate.Northwind/Debug.Spring.Northwind.sln new file mode 100644 index 00000000..fa5a7288 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/Debug.Spring.Northwind.sln @@ -0,0 +1,184 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Northwind.Service", "src\Spring.Northwind.Service\Spring.Northwind.Service.csproj", "{9E15876F-E9E0-43B7-9874-B54F163757D6}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Northwind.Dao", "src\Spring.Northwind.Dao\Spring.Northwind.Dao.csproj", "{7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Northwind.Dao.NHibernate", "src\Spring.Northwind.Dao.NHibernate\Spring.Northwind.Dao.NHibernate.csproj", "{6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Northwind.IntegrationTests", "test\Spring.Northwind.IntegrationTests\Spring.Northwind.IntegrationTests.csproj", "{2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aspects", "src\Spring.Aspects\Spring.Aspects.csproj", "{53B04C54-63EA-45AA-BB83-8950AF3C5D68}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.2005", "..\..\..\src\Spring\Spring.Core\Spring.Core.2005.csproj", "{710961A3-0DF4-49E4-A26E-F5B9C044AC84}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6F8CFB7E-8CC9-477B-83D1-2FADD070EEE0}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aop.2005", "..\..\..\src\Spring\Spring.Aop\Spring.Aop.2005.csproj", "{3A3A4E65-45A6-4B20-B460-0BEDC302C02C}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.2005", "..\..\..\src\Spring\Spring.Data\Spring.Data.2005.csproj", "{AE00E5AB-C39A-436F-86D2-33BFE33E2E40}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Data.NHibernate.2005", "..\..\..\src\Spring\Spring.Data.NHibernate\Spring.Data.NHibernate.2005.csproj", "{130E1609-45A7-4F65-B112-105F2DD3E2CE}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Web.2005", "..\..\..\src\Spring\Spring.Web\Spring.Web.2005.csproj", "{BA4789EB-281A-48EA-8763-28B9F0596A18}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|.NET = Debug|.NET + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Release|.NET = Release|.NET + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Debug|.NET.ActiveCfg = Debug|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Release|.NET.ActiveCfg = Release|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Release|Any CPU.Build.0 = Release|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Debug|.NET.ActiveCfg = Debug|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Release|.NET.ActiveCfg = Release|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Release|Any CPU.Build.0 = Release|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Debug|.NET.ActiveCfg = Debug|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Release|.NET.ActiveCfg = Release|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Release|Any CPU.Build.0 = Release|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Debug|.NET.ActiveCfg = Debug|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Release|.NET.ActiveCfg = Release|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Release|Any CPU.Build.0 = Release|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Debug|.NET.ActiveCfg = Debug|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Release|.NET.ActiveCfg = Release|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Release|Any CPU.Build.0 = Release|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|.NET.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|.NET.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.Build.0 = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|.NET.ActiveCfg = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|.NET.ActiveCfg = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Any CPU.Build.0 = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|.NET.ActiveCfg = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|.NET.ActiveCfg = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Any CPU.Build.0 = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|.NET.ActiveCfg = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|.NET.ActiveCfg = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|Any CPU.Build.0 = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {130E1609-45A7-4F65-B112-105F2DD3E2CE}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|.NET.ActiveCfg = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|.NET.ActiveCfg = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Any CPU.Build.0 = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Mixed Platforms.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/Spring.Northwind.sln b/examples/Spring/Spring.Data.NHibernate.Northwind/Spring.Northwind.sln new file mode 100644 index 00000000..8ddd320b --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/Spring.Northwind.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "L:\...\Spring.Northwind.Web\", "src\Spring.Northwind.Web\", "{3E321CBC-75B0-45C4-AFEF-7CEED28368AE}" + ProjectSection(WebsiteProperties) = preProject + ProjectReferences = "{53B04C54-63EA-45AA-BB83-8950AF3C5D68}|Spring.Aspects.dll;{7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}|Spring.Northwind.Dao.dll;{6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}|Spring.Northwind.Dao.NHibernate.dll;{9E15876F-E9E0-43B7-9874-B54F163757D6}|Spring.Northwind.Service.dll;{FD89FEBE-4914-45F3-9123-B2CB954810DD}|Spring.Northwind.Web.References.dll;" + Debug.AspNetCompiler.VirtualPath = "/Spring.Northwind.Web" + Debug.AspNetCompiler.PhysicalPath = "src\Spring.Northwind.Web\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.Northwind.Web\" + Debug.AspNetCompiler.Updateable = "true" + Debug.AspNetCompiler.ForceOverwrite = "true" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.VirtualPath = "/Spring.Northwind.Web" + Release.AspNetCompiler.PhysicalPath = "src\Spring.Northwind.Web\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.Northwind.Web\" + Release.AspNetCompiler.Updateable = "true" + Release.AspNetCompiler.ForceOverwrite = "true" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.Debug = "False" + VWDPort = "17866" + DefaultWebSiteLanguage = "Visual C#" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Northwind.Service", "src\Spring.Northwind.Service\Spring.Northwind.Service.csproj", "{9E15876F-E9E0-43B7-9874-B54F163757D6}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Northwind.Dao", "src\Spring.Northwind.Dao\Spring.Northwind.Dao.csproj", "{7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Northwind.Dao.NHibernate", "src\Spring.Northwind.Dao.NHibernate\Spring.Northwind.Dao.NHibernate.csproj", "{6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Northwind.IntegrationTests", "test\Spring.Northwind.IntegrationTests\Spring.Northwind.IntegrationTests.csproj", "{2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Aspects", "src\Spring.Aspects\Spring.Aspects.csproj", "{53B04C54-63EA-45AA-BB83-8950AF3C5D68}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Northwind.Web.References", "src\Spring.Northwind.Web.References\Spring.Northwind.Web.References.csproj", "{FD89FEBE-4914-45F3-9123-B2CB954810DD}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|.NET = Debug|.NET + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Release|.NET = Release|.NET + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3E321CBC-75B0-45C4-AFEF-7CEED28368AE}.Debug|.NET.ActiveCfg = Debug|.NET + {3E321CBC-75B0-45C4-AFEF-7CEED28368AE}.Debug|.NET.Build.0 = Debug|.NET + {3E321CBC-75B0-45C4-AFEF-7CEED28368AE}.Debug|Any CPU.ActiveCfg = Debug|.NET + {3E321CBC-75B0-45C4-AFEF-7CEED28368AE}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET + {3E321CBC-75B0-45C4-AFEF-7CEED28368AE}.Debug|Mixed Platforms.Build.0 = Debug|.NET + {3E321CBC-75B0-45C4-AFEF-7CEED28368AE}.Release|.NET.ActiveCfg = Debug|.NET + {3E321CBC-75B0-45C4-AFEF-7CEED28368AE}.Release|.NET.Build.0 = Debug|.NET + {3E321CBC-75B0-45C4-AFEF-7CEED28368AE}.Release|Any CPU.ActiveCfg = Debug|.NET + {3E321CBC-75B0-45C4-AFEF-7CEED28368AE}.Release|Mixed Platforms.ActiveCfg = Debug|.NET + {3E321CBC-75B0-45C4-AFEF-7CEED28368AE}.Release|Mixed Platforms.Build.0 = Debug|.NET + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Debug|.NET.ActiveCfg = Debug|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Release|.NET.ActiveCfg = Release|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Release|Any CPU.Build.0 = Release|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {9E15876F-E9E0-43B7-9874-B54F163757D6}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Debug|.NET.ActiveCfg = Debug|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Release|.NET.ActiveCfg = Release|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Release|Any CPU.Build.0 = Release|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Debug|.NET.ActiveCfg = Debug|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Release|.NET.ActiveCfg = Release|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Release|Any CPU.Build.0 = Release|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Debug|.NET.ActiveCfg = Debug|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Release|.NET.ActiveCfg = Release|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Release|Any CPU.Build.0 = Release|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Debug|.NET.ActiveCfg = Debug|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Release|.NET.ActiveCfg = Release|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Release|Any CPU.Build.0 = Release|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {53B04C54-63EA-45AA-BB83-8950AF3C5D68}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FD89FEBE-4914-45F3-9123-B2CB954810DD}.Debug|.NET.ActiveCfg = Debug|Any CPU + {FD89FEBE-4914-45F3-9123-B2CB954810DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD89FEBE-4914-45F3-9123-B2CB954810DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD89FEBE-4914-45F3-9123-B2CB954810DD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FD89FEBE-4914-45F3-9123-B2CB954810DD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FD89FEBE-4914-45F3-9123-B2CB954810DD}.Release|.NET.ActiveCfg = Release|Any CPU + {FD89FEBE-4914-45F3-9123-B2CB954810DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD89FEBE-4914-45F3-9123-B2CB954810DD}.Release|Any CPU.Build.0 = Release|Any CPU + {FD89FEBE-4914-45F3-9123-B2CB954810DD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FD89FEBE-4914-45F3-9123-B2CB954810DD}.Release|Mixed Platforms.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + NAntAddinLastFileName = + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/readme.txt b/examples/Spring/Spring.Data.NHibernate.Northwind/readme.txt new file mode 100644 index 00000000..4b5fb71c --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/readme.txt @@ -0,0 +1,17 @@ + +Database configuration +====================== + +A database file containing the Northwind database schema and data is located in the +directory src\Spring.Northwind.Web\App_Data named NORTHWND.MDF. This file can me mounted explicitly using SQL Server 2005 or +SQL Server Express. The web project's configuration is +setup to point to this database file. + +The integratation tests point to a copy of that database located in the +directory \test\Spring.Northwind.IntegrationTests\bin\Debug. You will need to copy the .mdf file to the bin\Debug +directory to run the integration test. + +SQL Server Express or 2005 will need to running before the web application or integration test is launched. + +If you prefer to run against your own database, the schema is located in the test_northwind.sql and you will need to +modify your connection string accordingly. \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Aspects/Aspects/Logging/CommonLoggingAroundAdvice.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Aspects/Aspects/Logging/CommonLoggingAroundAdvice.cs new file mode 100644 index 00000000..d0e149d5 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Aspects/Aspects/Logging/CommonLoggingAroundAdvice.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using AopAlliance.Intercept; +using Common.Logging; + +#endregion + +namespace Spring.Aspects.Logging +{ + /// + /// Basic implementation of a logging aspect using Common.Logging library. + /// + /// Bruno Baia + /// $Id: CommonLoggingAroundAdvice.cs,v 1.2 2007/06/27 16:10:59 bbaia Exp $ + public class CommonLoggingAroundAdvice : IMethodInterceptor + { + #region Logging + + private static readonly ILog LOG = LogManager.GetLogger(typeof(CommonLoggingAroundAdvice)); + + #endregion + + #region Fields + + private LogLevel _level = LogLevel.All; + + #endregion + + #region Properties + + public LogLevel Level + { + get { return _level; } + set { _level = value; } + } + + #endregion + + #region IMethodInterceptor Members + + public object Invoke(IMethodInvocation invocation) + { + Log("Intercepted call : about to invoke method '{0}'", invocation.Method.Name); + object returnValue = invocation.Proceed(); + Log("Intercepted call : returned '{0}'", returnValue); + return returnValue; + } + + #endregion + + #region Private Methods + + private void Log(string text, params object[] args) + { + switch (Level) + { + case LogLevel.All: + case LogLevel.Debug: + if (LOG.IsDebugEnabled) LOG.Debug(String.Format(text, args)); + break; + case LogLevel.Error: + if (LOG.IsErrorEnabled) LOG.Error(String.Format(text, args)); + break; + case LogLevel.Fatal: + if (LOG.IsFatalEnabled) LOG.Fatal(String.Format(text, args)); + break; + case LogLevel.Info: + if (LOG.IsInfoEnabled) LOG.Info(String.Format(text, args)); + break; + case LogLevel.Warn: + if (LOG.IsWarnEnabled) LOG.Warn(String.Format(text, args)); + break; + case LogLevel.Off: + default: + break; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Aspects/Properties/AssemblyInfo.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Aspects/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..993267c9 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Aspects/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Spring.Aspects")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Spring.Aspects")] +[assembly: AssemblyCopyright("Copyright © 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a308abe4-e623-4315-aa51-13b52a9324d8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Aspects/Spring.Aspects.csproj b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Aspects/Spring.Aspects.csproj new file mode 100644 index 00000000..dd224740 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Aspects/Spring.Aspects.csproj @@ -0,0 +1,55 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {53B04C54-63EA-45AA-BB83-8950AF3C5D68} + Library + Properties + Spring + Spring.Aspects + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Dao/NHibernate/HibernateCustomerDao.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Dao/NHibernate/HibernateCustomerDao.cs new file mode 100644 index 00000000..98df1d85 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Dao/NHibernate/HibernateCustomerDao.cs @@ -0,0 +1,67 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + +using System.Collections; + +using Spring.Data.NHibernate.Support; +using Spring.Transaction.Interceptor; + +using Spring.Northwind.Domain; + +#endregion + +namespace Spring.Northwind.Dao.NHibernate +{ + public class HibernateCustomerDao : HibernateDaoSupport, ICustomerDao + { + public Customer FindById(string customerId) + { + return HibernateTemplate.Load(typeof (Customer), customerId) as Customer; + } + + public IList FindAll() + { + return HibernateTemplate.LoadAll(typeof (Customer)); + } + + [Transaction(ReadOnly = false)] + public Customer Save(Customer customer) + { + HibernateTemplate.Save(customer); + return customer; + } + + [Transaction(ReadOnly = false)] + public Customer SaveOrUpdate(Customer customer) + { + HibernateTemplate.SaveOrUpdate(customer); + return customer; + } + + [Transaction(ReadOnly = false)] + public void Delete(Customer customer) + { + HibernateTemplate.Delete(customer); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Dao/NHibernate/HibernateOrderDao.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Dao/NHibernate/HibernateOrderDao.cs new file mode 100644 index 00000000..9917ce0c --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Dao/NHibernate/HibernateOrderDao.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +using Spring.Data.NHibernate.Support; + +using Spring.Northwind.Domain; + +#endregion + +namespace Spring.Northwind.Dao.NHibernate +{ + public class HibernateOrderDao : HibernateDaoSupport, IOrderDao + { + public Order FindById(long orderId) + { + return HibernateTemplate.Load(typeof(Order), orderId) as Order; + + } + + public IList FindAll() + { + return HibernateTemplate.LoadAll(typeof(Order)); + } + + public Order Save(Order order) + { + HibernateTemplate.Save(order); + return order; + } + + public Order SaveOrUpdate(Order order) + { + HibernateTemplate.SaveOrUpdate(order); + return order; + } + + public void Delete(Order order) + { + HibernateTemplate.Delete(order); + } + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Dao/NHibernate/HibernateProductDao.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Dao/NHibernate/HibernateProductDao.cs new file mode 100644 index 00000000..a51a2631 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Dao/NHibernate/HibernateProductDao.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +using Spring.Data.NHibernate.Support; + +using Spring.Northwind.Domain; + +#endregion + +namespace Spring.Northwind.Dao.NHibernate +{ + public class HibernateProductDao : HibernateDaoSupport, IProductDao + { + public Product FindById(int productId) + { + return HibernateTemplate.Load(typeof(Product), productId) as Product; + + } + + public IList FindAll() + { + return HibernateTemplate.LoadAll(typeof(Product)); + } + + public Product Save(Product product) + { + HibernateTemplate.Save(product); + return product; + } + + public Product SaveOrUpdate(Product product) + { + HibernateTemplate.SaveOrUpdate(product); + return product; + } + + public void Delete(Product product) + { + HibernateTemplate.Delete(product); + } + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/Customer.hbm.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/Customer.hbm.xml new file mode 100644 index 00000000..6d93b162 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/Customer.hbm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/Oracle/Customer.hbm.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/Oracle/Customer.hbm.xml new file mode 100644 index 00000000..6d93b162 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/Oracle/Customer.hbm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/Order.hbm.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/Order.hbm.xml new file mode 100644 index 00000000..a37c304e --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/Order.hbm.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/OrderDetail.hbm.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/OrderDetail.hbm.xml new file mode 100644 index 00000000..31c7f11d --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Mappings/OrderDetail.hbm.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Properties/AssemblyInfo.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..677e039e --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Spring.Northwind.Dao.NHibernate")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Spring.Northwind.Dao.NHibernate")] +[assembly: AssemblyCopyright("Copyright © 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3cb95513-bb8f-4426-bc10-c4c1cae81853")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Spring.Northwind.Dao.NHibernate.csproj b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Spring.Northwind.Dao.NHibernate.csproj new file mode 100644 index 00000000..f951b6a0 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao.NHibernate/Spring.Northwind.Dao.NHibernate.csproj @@ -0,0 +1,108 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4} + Library + Properties + Spring.Northwind + Spring.Northwind.Dao.NHibernate + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\lib\net\2.0\antlr.runtime.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\Castle.DynamicProxy.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Common.Logging.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Common.Logging.Log4Net.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\Iesi.Collections.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\log4net.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\NHibernate.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Data.NHibernate12.dll + + + + + + + + Code + + + Code + + + Code + + + + + + + + + + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44} + Spring.Northwind.Dao + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Dao/ICustomerDao.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Dao/ICustomerDao.cs new file mode 100644 index 00000000..020ae10e --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Dao/ICustomerDao.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +using Spring.Northwind.Domain; + +#endregion + +namespace Spring.Northwind.Dao +{ + public interface ICustomerDao + { + Customer FindById(string customerId); + IList FindAll(); + Customer Save(Customer customer); + Customer SaveOrUpdate(Customer customer); + void Delete(Customer customer); + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Dao/IOrderDao.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Dao/IOrderDao.cs new file mode 100644 index 00000000..6c1bf29b --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Dao/IOrderDao.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +using Spring.Northwind.Domain; + +#endregion + +namespace Spring.Northwind.Dao +{ + public interface IOrderDao + { + Order FindById(long orderId); + IList FindAll(); + Order Save(Order order); + Order SaveOrUpdate(Order order); + void Delete(Order order); + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Dao/IProductDao.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Dao/IProductDao.cs new file mode 100644 index 00000000..7a830e57 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Dao/IProductDao.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +using Spring.Northwind.Domain; + +#endregion + +namespace Spring.Northwind.Dao +{ + public interface IProductDao + { + Product FindById(int productId); + IList FindAll(); + Product Save(Product product); + Product SaveOrUpdate(Product product); + void Delete(Product product); + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/Customer.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/Customer.cs new file mode 100644 index 00000000..89970834 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/Customer.cs @@ -0,0 +1,150 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.Northwind.Domain +{ + public class Customer + { + #region Fields + + protected string id; + protected string companyName; + protected string contactName; + protected string contactTitle; + protected string address; + protected string city; + protected string region; + protected string postalCode; + protected string country; + protected string phone; + protected string fax; + protected IList orders; + + #endregion + + #region Properties + + public string Id + { + get { return id; } + set { id = value; } + } + + public string CompanyName + { + get { return companyName; } + set { companyName = value; } + } + + public string ContactName + { + get { return contactName; } + set { contactName = value; } + } + + public string ContactTitle + { + get { return contactTitle; } + set { contactTitle = value; } + } + + public string Address + { + get { return address; } + set { address = value; } + } + + public string City + { + get { return city; } + set { city = value; } + } + + public string Region + { + get { return region; } + set { region = value; } + } + + public string PostalCode + { + get { return postalCode; } + set { postalCode = value; } + } + + public string Country + { + get { return country; } + set { country = value; } + } + + public string Phone + { + get { return phone; } + set { phone = value; } + } + + public string Fax + { + get { return fax; } + set { fax = value; } + } + + public IList Orders + { + get + { + if (orders == null) + { + orders = new ArrayList(); + } + return orders; + } + set { orders = value; } + } + + #endregion + + #region Constructor (s) + + public Customer() + { + } + + public Customer(string companyName, string contactName, string contactTitle, string address, string city, + string region, string postalCode, string country, string phone, string fax) + { + this.companyName = companyName; + this.contactName = contactName; + this.contactTitle = contactTitle; + this.address = address; + this.city = city; + this.region = region; + this.postalCode = postalCode; + this.country = country; + this.phone = phone; + this.fax = fax; + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/Order.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/Order.cs new file mode 100644 index 00000000..e46d9ad8 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/Order.cs @@ -0,0 +1,165 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +namespace Spring.Northwind.Domain +{ + public class Order + { + #region Fields + + protected int id; + protected DateTime orderDate; + protected DateTime requiredDate; + protected DateTime shippedDate; + protected decimal freight; + protected string shipName; + protected string shipAddress; + protected string shipCity; + protected string shipRegion; + protected string shipPostalCode; + protected string shipCountry; + protected Customer customer; + + //protected Employee _employee; + //protected Shipper _shipVia; + protected IList orderDetails; + + #endregion + + #region Constructor (s) + + public Order() + { + } + + public Order(DateTime orderDate, DateTime requiredDate, DateTime shippedDate, decimal freight, string shipName, + string shipAddress, string shipCity, string shipRegion, string shipPostalCode, string shipCountry, + Customer customer) + { + this.orderDate = orderDate; + this.requiredDate = requiredDate; + this.shippedDate = shippedDate; + this.freight = freight; + this.shipName = shipName; + this.shipAddress = shipAddress; + this.shipCity = shipCity; + this.shipRegion = shipRegion; + this.shipPostalCode = shipPostalCode; + this.shipCountry = shipCountry; + this.customer = customer; + } + + #endregion + + + #region Properties + + public int Id + { + get { return id; } + set { id = value; } + } + + public DateTime OrderDate + { + get { return orderDate; } + set { orderDate = value; } + } + + public DateTime RequiredDate + { + get { return requiredDate; } + set { requiredDate = value; } + } + + public DateTime ShippedDate + { + get { return shippedDate; } + set { shippedDate = value; } + } + + public decimal Freight + { + get { return freight; } + set { freight = value; } + } + + public string ShipName + { + get { return shipName; } + set { shipName = value; } + } + + public string ShipAddress + { + get { return shipAddress; } + set { shipAddress = value; } + } + + public string ShipCity + { + get { return shipCity; } + set { shipCity = value; } + } + + public string ShipRegion + { + get { return shipRegion; } + set { shipRegion = value; } + } + + public string ShipPostalCode + { + get { return shipPostalCode; } + set { shipPostalCode = value; } + } + + public string ShipCountry + { + get { return shipCountry; } + set { shipCountry = value; } + } + + public Customer Customer + { + get { return customer; } + set { customer = value; } + } + + + public IList OrderDetails + { + get + { + if(orderDetails == null) + { + orderDetails = new ArrayList(); + } + return orderDetails; + } + set { orderDetails = value; } + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/OrderDetail.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/OrderDetail.cs new file mode 100644 index 00000000..0c0617bc --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/OrderDetail.cs @@ -0,0 +1,77 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Northwind.Domain +{ + public class OrderDetail + { + protected double unitPrice; + protected int quantity; + protected double discount; + protected Order order; + protected int productId; + //protected Product product; + + public override bool Equals(object obj) + { + if (this == obj) return true; + OrderDetail orderDetail = obj as OrderDetail; + if (orderDetail == null) return false; + return Equals(order.Id, orderDetail.order.Id) && Equals(productId, orderDetail.productId); + } + + public override int GetHashCode() + { + return order.Id.GetHashCode() + 29*productId.GetHashCode(); + } + + public double UnitPrice + { + get { return this.unitPrice; } + set { this.unitPrice = value; } + } + + public int Quantity + { + get { return this.quantity; } + set { this.quantity = value; } + } + + public double Discount + { + get { return this.discount; } + set { this.discount = value; } + } + + public Order Order + { + get { return this.order; } + set { this.order = value; } + } + + public int ProductId + { + get { return this.productId; } + set { this.productId = value; } + } + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/Product.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/Product.cs new file mode 100644 index 00000000..da254592 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Domain/Product.cs @@ -0,0 +1,37 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Northwind.Domain +{ + public class Product + { + protected int _id; + protected string _productName; + protected string _quantityPerUnit; + protected decimal _unitPrice; + protected short _unitsInStock; + protected short _unitsOnOrder; + protected short _reorderLevel; + protected bool _discontinued; + //protected Category _category; + //protected Supplier _supplier; + protected OrderDetail _orderDetail; + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Properties/AssemblyInfo.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..b77696ab --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Spring.Northwind.Dao")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Spring.Northwind.Dao")] +[assembly: AssemblyCopyright("Copyright © 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bff73ba6-baef-446a-aad4-99cc7b55fcb3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Spring.Northwind.Dao.csproj b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Spring.Northwind.Dao.csproj new file mode 100644 index 00000000..89089ccb --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Dao/Spring.Northwind.Dao.csproj @@ -0,0 +1,51 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44} + Library + Properties + Spring.Northwind + Spring.Northwind.Dao + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Properties/AssemblyInfo.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..8411c2ee --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Spring.Northwind.Service")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Spring.Northwind.Service")] +[assembly: AssemblyCopyright("Copyright © 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("200c1a2d-2866-43eb-b5ff-04e67855ea7d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/FedExShippingService.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/FedExShippingService.cs new file mode 100644 index 00000000..2de081e1 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/FedExShippingService.cs @@ -0,0 +1,42 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Common.Logging; + +using Spring.Northwind.Domain; + +#endregion + +namespace Spring.Northwind.Service +{ + public class FedExShippingService : IShippingService + { + protected static readonly ILog log = LogManager.GetLogger(typeof (FedExShippingService)); + + public void ShipOrder(Order order) + { + log.Info("Shipping order id = " + order.Id); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/FulfillmentService.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/FulfillmentService.cs new file mode 100644 index 00000000..fc81bbae --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/FulfillmentService.cs @@ -0,0 +1,115 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using Spring.Transaction.Interceptor; + +using Spring.Northwind.Dao; +using Spring.Northwind.Domain; + +#endregion + +namespace Spring.Northwind.Service +{ + public class FulfillmentService : IFulfillmentService + { + #region Fields + + private IProductDao productDao; + + private ICustomerDao customerDao; + + private IOrderDao orderDao; + + private IShippingService shippingService; + + #endregion + + #region Properties + + public IProductDao ProductDao + { + get { return productDao; } + set { productDao = value; } + } + + public IOrderDao OrderDao + { + get { return orderDao; } + set { orderDao = value; } + } + + public ICustomerDao CustomerDao + { + get { return customerDao; } + set { customerDao = value; } + } + + public IShippingService ShippingService + { + get { return shippingService; } + set { shippingService = value; } + } + + #endregion + + #region Methods + + + + [Transaction(ReadOnly=false)] + public void ProcessCustomer(string customerId) + { + //Find all orders for customer + Customer customer = CustomerDao.FindById(customerId); + + foreach (Order order in customer.Orders) + { + //Validate Order + Validate(order); + + //Ship with external shipping service + ShippingService.ShipOrder(order); + + //Update shipping date + order.ShippedDate = DateTime.Now; + + //Update shipment date + OrderDao.SaveOrUpdate(order); + + //Other operations...Decrease product quantity... etc + } + + } + + private void Validate(Order order) + { + + //TODO throw exception on error. + + } + + #endregion + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/IFulfillmentService.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/IFulfillmentService.cs new file mode 100644 index 00000000..6ba8cae9 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/IFulfillmentService.cs @@ -0,0 +1,33 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Northwind.Domain; + +#endregion + +namespace Spring.Northwind.Service +{ + public interface IFulfillmentService + { + void ProcessCustomer(string customerId); + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/IShippingService.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/IShippingService.cs new file mode 100644 index 00000000..0e488175 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Service/IShippingService.cs @@ -0,0 +1,33 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Northwind.Domain; + +#endregion + +namespace Spring.Northwind.Service +{ + public interface IShippingService + { + void ShipOrder(Order order); + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Spring.Northwind.Service.csproj b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Spring.Northwind.Service.csproj new file mode 100644 index 00000000..1ef9dd85 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Service/Spring.Northwind.Service.csproj @@ -0,0 +1,67 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {9E15876F-E9E0-43B7-9874-B54F163757D6} + Library + Properties + Spring.Northwind + Spring.Northwind.Service + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + + + + + + + + + + + + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4} + Spring.Northwind.Dao.NHibernate + + + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44} + Spring.Northwind.Dao + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web.References/Spring.Northwind.Web.References.csproj b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web.References/Spring.Northwind.Web.References.csproj new file mode 100644 index 00000000..53f857c0 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web.References/Spring.Northwind.Web.References.csproj @@ -0,0 +1,87 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {FD89FEBE-4914-45F3-9123-B2CB954810DD} + Library + Properties + Spring.Northwind.Web.References + Spring.Northwind.Web.References + + + true + full + false + . + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + . + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\bin\net\2.0\debug\antlr.runtime.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\Castle.DynamicProxy.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Common.Logging.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Common.Logging.Log4Net.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\Iesi.Collections.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\log4net.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\NHibernate.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Data.NHibernate12.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Web.dll + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Code/ICustomerEditController.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Code/ICustomerEditController.cs new file mode 100644 index 00000000..ef71ca72 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Code/ICustomerEditController.cs @@ -0,0 +1,8 @@ +using Spring.Northwind.Domain; + +public interface ICustomerEditController +{ + void EditCustomer(Customer customer); + void Clear(); + Customer CurrentCustomer { get; } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Code/NHibernateCustomerEditController.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Code/NHibernateCustomerEditController.cs new file mode 100644 index 00000000..3eb95704 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Code/NHibernateCustomerEditController.cs @@ -0,0 +1,48 @@ + +using System.Web; +using NHibernate; +using Spring.Data.NHibernate; +using Spring.Northwind.Domain; + +/// +/// +/// erich.eichinger +/// $Id: NHibernateCustomerEditController.cs,v 1.1 2007/09/29 21:32:09 oakinger Exp $ +public class NHibernateCustomerEditController:ICustomerEditController +{ + private readonly ISessionFactory sessionFactory; + private Customer currentCustomer; + + public NHibernateCustomerEditController(ISessionFactory sessionFactory) + { + this.sessionFactory = sessionFactory; + } + + private ISession Session + { + get + { + return SessionFactoryUtils.GetSession( sessionFactory,false ); + } + } + + public void EditCustomer(Customer customer) + { + currentCustomer = customer; + } + + public void Clear() + { + currentCustomer = null; + } + + public Customer CurrentCustomer + { + get + { + Customer customer = currentCustomer; + Session.Lock(customer, LockMode.None); + return customer; + } + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Data/NORTHWND.MDF b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Data/NORTHWND.MDF new file mode 100644 index 00000000..e021679d Binary files /dev/null and b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Data/NORTHWND.MDF differ diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Data/NORTHWND_log.ldf b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Data/NORTHWND_log.ldf new file mode 100644 index 00000000..c8a53a48 Binary files /dev/null and b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/App_Data/NORTHWND_log.ldf differ diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Aspects.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Aspects.xml new file mode 100644 index 00000000..b50ad156 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Aspects.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerEditor.aspx b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerEditor.aspx new file mode 100644 index 00000000..2fd7b637 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerEditor.aspx @@ -0,0 +1,21 @@ +<%@ Page Language="C#" CodeFile="CustomerEditor.aspx.cs" Inherits="CustomerEditor" %> + + + + + Untitled Page + + +
    +
    + + + + +
    ID:
    Contact:
    +
    +
    + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerEditor.aspx.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerEditor.aspx.cs new file mode 100644 index 00000000..6920e3de --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerEditor.aspx.cs @@ -0,0 +1,69 @@ +using System; +using System.Web; +using Spring.Northwind.Dao; +using Spring.Northwind.Domain; +using Spring.Web.UI; + +public partial class CustomerEditor : Page +{ + private ICustomerEditController customerEditController; + private ICustomerDao customerDao; + + public ICustomerDao CustomerDao + { + set { this.customerDao = value; } + } + + public ICustomerEditController CustomerEditController + { + set { this.customerEditController = value; } + } + + public Customer CurrentCustomer + { + get + { + //return (Customer) Session[typeof(CustomerEditor).FullName + ".Customer"]; + return customerEditController.CurrentCustomer; + } + } + +// public static void Edit( Customer customer ) +// { +// HttpContext.Current.Session[typeof(CustomerEditor).FullName + ".Customer"] = customer; +// } + + public CustomerEditor() + { + this.InitializeControls+=new EventHandler(Page_InitializeControls); + this.DataBound+=new EventHandler(Page_DataBound); + this.DataUnbound+=new EventHandler(Page_DataUnbound); + } + + override protected void InitializeDataBindings() + { + base.InitializeDataBindings(); + + // do the "one time" setup for databinding + } + + private void Page_DataBound(object sender, EventArgs e) + { + // perform custom tasks for binding data from model to the form + } + + private void Page_DataUnbound(object sender, EventArgs e) + { + // perform custom tasks for unbinding data from form to the model + } + + private void Page_InitializeControls(object sender, EventArgs e) + { + btnSave.Click += new EventHandler(BtnSave_Click); + } + + private void BtnSave_Click(object sender, EventArgs e) + { + customerDao.SaveOrUpdate(CurrentCustomer); + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerList.aspx b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerList.aspx new file mode 100644 index 00000000..9ac91a23 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerList.aspx @@ -0,0 +1,34 @@ +<%@ Page Language="C#" CodeFile="CustomerList.aspx.cs" Inherits="CustomerList" %> +<%@ Reference page="CustomerEditor.aspx" %> + + + + + Untitled Page + + +
    +
    + + + + + + + + + + + +
    +
    + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerList.aspx.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerList.aspx.cs new file mode 100644 index 00000000..80bddf85 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerList.aspx.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections; +using System.Web.UI.WebControls; +using Spring.Northwind.Dao; +using Spring.Northwind.Domain; + +public partial class CustomerList : Spring.Web.UI.Page +{ + private ICustomerEditController customerEditController; + private ICustomerDao customerDao; + + public ICustomerDao CustomerDao + { + set { this.customerDao = value; } + } + + public ICustomerEditController CustomerEditController + { + set { this.customerEditController = value; } + } + + public Customer SelectedCustomer + { + get + { + return (Customer) this.customerList.SelectedItem.DataItem; + } + } + + public CustomerList() + { + this.InitializeControls+=new EventHandler(Page_InitializeControls); + this.DataBound+=new EventHandler(Page_DataBound); + this.DataUnbound+=new EventHandler(Page_DataUnbound); + } + + override protected void InitializeDataBindings() + { + base.InitializeDataBindings(); + + // do the "one time" setup for databinding + } + + private void Page_DataBound(object sender, EventArgs e) + { + // perform custom tasks for binding data from model to the form + } + + private void Page_DataUnbound(object sender, EventArgs e) + { + // perform custom tasks for unbinding data from form to the model + } + + private void Page_InitializeControls(object sender, EventArgs e) + { + // create/initialize controls here + customerList.DataSource = customerDao.FindAll(); + customerList.ItemCommand+=new DataGridCommandEventHandler(CustomerList_ItemCommand); + customerList.PageIndexChanged+=new DataGridPageChangedEventHandler(CustomerList_PageIndexChanged); + if (!IsPostBack) + { + customerList.DataBind(); + } + else + { + customerList.ItemCreated+=new DataGridItemEventHandler(this.CustomerList_ItemCreated); + } + } + + private void CustomerList_PageIndexChanged(object source, DataGridPageChangedEventArgs e) + { + customerList.CurrentPageIndex = e.NewPageIndex; + customerList.DataBind(); + } + + private void CustomerList_ItemCommand(object source, DataGridCommandEventArgs e) + { + switch(e.CommandName) + { + case "ViewOrders": + case "EditCustomer": + customerList.SelectedIndex = e.Item.ItemIndex; + customerEditController.EditCustomer(this.SelectedCustomer); + SetResult(e.CommandName); + break; + } + } + + private void CustomerList_ItemCreated(object sender, DataGridItemEventArgs e) + { + if(e.Item.DataSetIndex > -1) + { + e.Item.DataItem = ((IList) customerList.DataSource)[e.Item.DataSetIndex]; + } + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerOrders.aspx b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerOrders.aspx new file mode 100644 index 00000000..68c91bc3 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerOrders.aspx @@ -0,0 +1,32 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="CustomerOrders.aspx.cs" Inherits="CustomerOrders" %> + + + + + + Untitled Page + + +
    +
    + + + + + + + + + +
    +
    + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerOrders.aspx.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerOrders.aspx.cs new file mode 100644 index 00000000..b65bfc77 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/CustomerOrders.aspx.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections; +using System.Web.UI.WebControls; +using Spring.Northwind.Domain; + +public partial class CustomerOrders:Spring.Web.UI.Page +{ + private ICustomerEditController customerEditController; + + public ICustomerEditController CustomerEditController + { + set { this.customerEditController = value; } + } + + public Customer SelectedCustomer + { + get + { + return this.customerEditController.CurrentCustomer; + } + } + + public CustomerOrders() + { + this.InitializeControls+=new EventHandler(Page_InitializeControls); + this.DataBound+=new EventHandler(Page_DataBound); + this.DataUnbound+=new EventHandler(Page_DataUnbound); + } + + override protected void InitializeDataBindings() + { + base.InitializeDataBindings(); + + // do the "one time" setup for databinding + } + + private void Page_DataBound(object sender, EventArgs e) + { + // perform custom tasks for binding data from model to the form + } + + private void Page_DataUnbound(object sender, EventArgs e) + { + // perform custom tasks for unbinding data from form to the model + } + + private void Page_InitializeControls(object sender, EventArgs e) + { + // create/initialize controls here + customerOrders.DataSource = SelectedCustomer.Orders; + if (!IsPostBack) + { + customerOrders.DataBind(); + } + else + { + customerOrders.ItemCreated+=new DataGridItemEventHandler( this.CustomerList_ItemCreated ); + } + } + + private void CustomerList_ItemCreated(object sender, DataGridItemEventArgs e) + { + if(e.Item.DataSetIndex > -1) + { + e.Item.DataItem = ((IList)customerOrders.DataSource)[e.Item.DataSetIndex]; + } + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Dao.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Dao.xml new file mode 100644 index 00000000..e09ac262 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Dao.xml @@ -0,0 +1,69 @@ + + + + + + The Northwind object definitions for the Data Access Objects. + + + + + + + + + + + + + + + Spring.Northwind.Dao.NHibernate + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/DeclarativeServicesAttributeDriven.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/DeclarativeServicesAttributeDriven.xml new file mode 100644 index 00000000..64687470 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/DeclarativeServicesAttributeDriven.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/DeclarativeServicesObjectNameDriven.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/DeclarativeServicesObjectNameDriven.xml new file mode 100644 index 00000000..de08c228 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/DeclarativeServicesObjectNameDriven.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/DeclarativeServicesTxProxyFactoryDriven.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/DeclarativeServicesTxProxyFactoryDriven.xml new file mode 100644 index 00000000..9655231b --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/DeclarativeServicesTxProxyFactoryDriven.xml @@ -0,0 +1,40 @@ + + + + + Transactional Proxy for FulfillmentService using the TransactionProxyFactory + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Default.aspx b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Default.aspx new file mode 100644 index 00000000..9943ba9d --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Default.aspx @@ -0,0 +1,2 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> +<%Response.Redirect("CustomerList.aspx");%> diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Default.aspx.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Default.aspx.cs new file mode 100644 index 00000000..efeb8c9a --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Default.aspx.cs @@ -0,0 +1,49 @@ +using System; +using System.Diagnostics; +using Spring.Northwind.Dao; +using Spring.Northwind.Domain; +using Spring.Northwind.Service; + +public partial class _Default : Spring.Web.UI.Page +{ + private ICustomerDao customerDao; + private IFulfillmentService fulfillmentService; + + public IFulfillmentService FulfillmentService + { + set { this.fulfillmentService = value; } + } + + public ICustomerDao CustomerDao + { + set { this.customerDao = value; } + } + + protected void Button1_Click(object sender, EventArgs e) + { + ProcessCustomer(); + } + + public void ProcessCustomer() + { + string customerId = "ERNSH"; + + // check, if exists + Customer customer = customerDao.FindById(customerId); + + //Find all orders for customer and ship them + this.fulfillmentService.ProcessCustomer(customer.Id); + //assertions.... + + // prepare for display - note that OrderDetails are loaded lazy here! + foreach (Order order in customer.Orders) + { + Debug.Assert(order.OrderDetails.Count > 0); + foreach(OrderDetail details in order.OrderDetails) + { + details.Quantity = details.Quantity - 1; + // do something here + } + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Global.asax b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Global.asax new file mode 100644 index 00000000..56861e19 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Global.asax @@ -0,0 +1,38 @@ +<%@ Import namespace="log4net"%> +<%@ Import namespace="log4net.Config"%> +<%@ Application Language="C#" %> + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Log4Net.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Log4Net.xml new file mode 100644 index 00000000..2420455b --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Log4Net.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Logs/log.txt b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Logs/log.txt new file mode 100644 index 00000000..10e51644 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Logs/log.txt @@ -0,0 +1,186 @@ +2008-04-04 17:21:13,343 [8] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Not closing pre-bound Hibernate Session after HibernateTemplate +2008-04-04 17:21:13,390 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:13,390 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:13,390 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:13,390 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@F15783] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [8] +2008-04-04 17:21:13,390 [8] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:13,390 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:21:13,390 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:13,390 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:13,390 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@1EA711C] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [4] +2008-04-04 17:21:13,406 [4] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerOrders.aspx' +2008-04-04 17:21:13,406 [4] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolved url '/Spring.Northwind.Web/CustomerOrders.aspx' from reusable handler cache +2008-04-04 17:21:13,406 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerOrders.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerOrders.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:13,406 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerOrders.aspx'. +2008-04-04 17:21:13,406 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerOrders.aspx' +2008-04-04 17:21:13,406 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerOrders.aspx +2008-04-04 17:21:13,406 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customerorders_aspx' using definition '/CustomerOrders.aspx' +2008-04-04 17:21:13,406 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerOrders.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:13,406 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerEditController'. +2008-04-04 17:21:13,406 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerOrders.aspx' +2008-04-04 17:21:13,406 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerOrders.aspx' +2008-04-04 17:21:13,406 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@1EA711C] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:13,406 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:13,406 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:13,406 [4] INFO NHibernate.Loader.Loader [(null)] - SELECT orders0_.CustomerID as CustomerID__1_, orders0_.OrderID as OrderID1_, orders0_.OrderID as OrderID0_0_, orders0_.OrderDate as OrderDate0_0_, orders0_.RequiredDate as Required3_0_0_, orders0_.ShippedDate as ShippedD4_0_0_, orders0_.ShipName as ShipName0_0_, orders0_.ShipAddress as ShipAddr6_0_0_, orders0_.ShipCity as ShipCity0_0_, orders0_.ShipRegion as ShipRegion0_0_, orders0_.ShipPostalCode as ShipPost9_0_0_, orders0_.ShipCountry as ShipCou10_0_0_ FROM Orders orders0_ WHERE orders0_.CustomerID=@p0 +2008-04-04 17:21:13,453 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:13,453 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:13,453 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:13,453 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@1EA711C] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [4] +2008-04-04 17:21:13,453 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:13,453 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:21:14,703 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:14,703 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:14,703 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@30FCBCE] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [4] +2008-04-04 17:21:14,703 [4] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:14,703 [4] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolved url '/Spring.Northwind.Web/CustomerList.aspx' from reusable handler cache +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerList.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerList.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerList.aspx'. +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerList.aspx +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customerlist_aspx' using definition '/CustomerList.aspx' +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerList.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerEditController'. +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerDao' in object '/CustomerList.aspx' to object 'CustomerDao'. +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerDao'. +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerList.aspx' +2008-04-04 17:21:14,703 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerList.aspx' +2008-04-04 17:21:14,703 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@30FCBCE] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:14,703 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:14,703 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:14,703 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@30FCBCE] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:14,703 [4] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Found thread-bound Session for HibernateTemplate +2008-04-04 17:21:14,703 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@30FCBCE] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:14,703 [4] INFO NHibernate.Loader.Loader [(null)] - SELECT this_.CustomerID as CustomerID1_0_, this_.CompanyName as CompanyN2_1_0_, this_.ContactName as ContactN3_1_0_, this_.ContactTitle as ContactT4_1_0_, this_.Address as Address1_0_, this_.City as City1_0_, this_.Region as Region1_0_, this_.PostalCode as PostalCode1_0_, this_.Country as Country1_0_, this_.Phone as Phone1_0_, this_.Fax as Fax1_0_ FROM Customers this_ +2008-04-04 17:21:14,718 [4] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Not closing pre-bound Hibernate Session after HibernateTemplate +2008-04-04 17:21:14,734 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:14,734 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:14,734 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:14,734 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@30FCBCE] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [4] +2008-04-04 17:21:14,734 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:14,734 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:21:16,062 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:16,062 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:16,062 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@16799F8] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [8] +2008-04-04 17:21:16,062 [8] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:16,062 [8] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolved url '/Spring.Northwind.Web/CustomerList.aspx' from reusable handler cache +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerList.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerList.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerList.aspx'. +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerList.aspx +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customerlist_aspx' using definition '/CustomerList.aspx' +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerList.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerEditController'. +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerDao' in object '/CustomerList.aspx' to object 'CustomerDao'. +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerDao'. +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerList.aspx' +2008-04-04 17:21:16,062 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerList.aspx' +2008-04-04 17:21:16,062 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@16799F8] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:16,062 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:16,062 [8] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:16,062 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@16799F8] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:16,062 [8] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Found thread-bound Session for HibernateTemplate +2008-04-04 17:21:16,062 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@16799F8] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:16,062 [8] INFO NHibernate.Loader.Loader [(null)] - SELECT this_.CustomerID as CustomerID1_0_, this_.CompanyName as CompanyN2_1_0_, this_.ContactName as ContactN3_1_0_, this_.ContactTitle as ContactT4_1_0_, this_.Address as Address1_0_, this_.City as City1_0_, this_.Region as Region1_0_, this_.PostalCode as PostalCode1_0_, this_.Country as Country1_0_, this_.Phone as Phone1_0_, this_.Fax as Fax1_0_ FROM Customers this_ +2008-04-04 17:21:16,078 [8] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Not closing pre-bound Hibernate Session after HibernateTemplate +2008-04-04 17:21:16,125 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:16,125 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:16,125 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:16,125 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@16799F8] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [8] +2008-04-04 17:21:16,125 [8] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:16,125 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:21:16,125 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:16,125 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:16,125 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@3154901] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [4] +2008-04-04 17:21:16,125 [4] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerEditor.aspx' +2008-04-04 17:21:16,125 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - looking up web context 'spring.root' in WebContextCache +2008-04-04 17:21:16,125 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - returning WebContextCache hit '[/spring.northwind.web]:WebApplicationContext(35298535)' for vpath 'spring.root' +2008-04-04 17:21:16,125 [4] DEBUG Spring.Web.Support.AbstractHandlerFactory [(null)] - GetHandler():looking up definition for app-relative url '/CustomerEditor.aspx' +2008-04-04 17:21:16,125 [4] DEBUG Spring.Web.Support.AbstractHandlerFactory [(null)] - GetHandler():found definition for page-url '/CustomerEditor.aspx' +2008-04-04 17:21:16,125 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerEditor.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerEditor.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:16,125 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerEditor.aspx'. +2008-04-04 17:21:16,125 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerEditor.aspx' +2008-04-04 17:21:16,125 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerEditor.aspx +2008-04-04 17:21:16,125 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customereditor_aspx' using definition '/CustomerEditor.aspx' +2008-04-04 17:21:16,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerEditor.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:16,140 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerEditController'. +2008-04-04 17:21:16,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerDao' in object '/CustomerEditor.aspx' to object 'CustomerDao'. +2008-04-04 17:21:16,140 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerDao'. +2008-04-04 17:21:16,156 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerEditor.aspx' +2008-04-04 17:21:16,156 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerEditor.aspx' +2008-04-04 17:21:16,156 [4] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - returning available advisors +2008-04-04 17:21:16,468 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Candidate advisor [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] rejected for type [ASP.customereditor_aspx] +2008-04-04 17:21:16,546 [4] DEBUG Spring.Web.UI.Controls.DataBindingPanel [(null)] - binding control 'ctl03' relative to '__Page' using expression 'FindControl('ctl03').Text' +2008-04-04 17:21:16,640 [4] DEBUG Spring.Web.UI.Controls.DataBindingPanel [(null)] - binding control 'ctl04' relative to '__Page' using expression 'FindControl('ctl04').Text' +2008-04-04 17:21:16,656 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@3154901] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:16,656 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:16,656 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:16,703 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@3154901] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:16,734 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:16,734 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:16,734 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:16,734 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@3154901] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [4] +2008-04-04 17:21:16,734 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:16,734 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:21:18,734 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:18,734 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:18,734 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@62A49D] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [4] +2008-04-04 17:21:18,734 [4] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:18,734 [4] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolved url '/Spring.Northwind.Web/CustomerList.aspx' from reusable handler cache +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerList.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerList.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerList.aspx'. +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerList.aspx +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customerlist_aspx' using definition '/CustomerList.aspx' +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerList.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerEditController'. +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerDao' in object '/CustomerList.aspx' to object 'CustomerDao'. +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerDao'. +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerList.aspx' +2008-04-04 17:21:18,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerList.aspx' +2008-04-04 17:21:18,734 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@62A49D] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:18,734 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:18,734 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:18,734 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@62A49D] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:18,734 [4] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Found thread-bound Session for HibernateTemplate +2008-04-04 17:21:18,734 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@62A49D] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:18,734 [4] INFO NHibernate.Loader.Loader [(null)] - SELECT this_.CustomerID as CustomerID1_0_, this_.CompanyName as CompanyN2_1_0_, this_.ContactName as ContactN3_1_0_, this_.ContactTitle as ContactT4_1_0_, this_.Address as Address1_0_, this_.City as City1_0_, this_.Region as Region1_0_, this_.PostalCode as PostalCode1_0_, this_.Country as Country1_0_, this_.Phone as Phone1_0_, this_.Fax as Fax1_0_ FROM Customers this_ +2008-04-04 17:21:18,750 [4] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Not closing pre-bound Hibernate Session after HibernateTemplate +2008-04-04 17:21:18,765 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:18,765 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:18,765 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:18,765 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@62A49D] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [4] +2008-04-04 17:21:18,765 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:18,765 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:21:21,609 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:21,609 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:21,609 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@1648030] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [8] +2008-04-04 17:21:21,609 [8] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:21,609 [8] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolved url '/Spring.Northwind.Web/CustomerList.aspx' from reusable handler cache +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerList.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerList.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerList.aspx'. +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerList.aspx +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customerlist_aspx' using definition '/CustomerList.aspx' +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerList.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerEditController'. +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerDao' in object '/CustomerList.aspx' to object 'CustomerDao'. +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerDao'. +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerList.aspx' +2008-04-04 17:21:21,625 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerList.aspx' +2008-04-04 17:21:21,625 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@1648030] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:21,625 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:21,625 [8] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:21,625 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@1648030] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:21,625 [8] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Found thread-bound Session for HibernateTemplate +2008-04-04 17:21:21,625 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@1648030] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:21,625 [8] INFO NHibernate.Loader.Loader [(null)] - SELECT this_.CustomerID as CustomerID1_0_, this_.CompanyName as CompanyN2_1_0_, this_.ContactName as ContactN3_1_0_, this_.ContactTitle as ContactT4_1_0_, this_.Address as Address1_0_, this_.City as City1_0_, this_.Region as Region1_0_, this_.PostalCode as PostalCode1_0_, this_.Country as Country1_0_, this_.Phone as Phone1_0_, this_.Fax as Fax1_0_ FROM Customers this_ +2008-04-04 17:21:21,625 [8] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Not closing pre-bound Hibernate Session after HibernateTemplate +2008-04-04 17:21:21,687 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:21,687 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:21,687 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:21,687 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@1648030] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [8] +2008-04-04 17:21:21,687 [8] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:21,687 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:24:18,796 [4] DEBUG Spring.Context.Support.WebSupportModule [(null)] - end session jffsz0255kqrjrgqyardrgs55 because of Removed +2008-04-04 17:24:18,812 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'session'-scoped item cache diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Logs/log.txt.1 b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Logs/log.txt.1 new file mode 100644 index 00000000..cd7c2205 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Logs/log.txt.1 @@ -0,0 +1,536 @@ +2008-04-04 17:19:12,203 [4] DEBUG Spring.Context.Support.WebSupportModule [(null)] - Set default resource protocol to 'web' and installed HttpContext-aware HybridContextStorage +2008-04-04 17:20:59,156 [4] DEBUG Spring.Context.Support.WebSupportModule [(null)] - Set default resource protocol to 'web' and installed HttpContext-aware HybridContextStorage +2008-04-04 17:20:59,312 [4] DEBUG Spring.Context.Support.WebSupportModule [(null)] - Initializing Application instance +2008-04-04 17:20:59,562 [4] DEBUG Spring.Context.Support.WebSupportModule [(null)] - attaching to InProcSessionStateStore +2008-04-04 17:20:59,578 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - BeforeFirstRequest:False +2008-04-04 17:20:59,593 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - looking up web context 'spring.root' in WebContextCache +2008-04-04 17:20:59,593 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - looking up web context 'spring.root' in ContextRegistry +2008-04-04 17:20:59,593 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - web context for vpath 'spring.root' not found. Force creation using filepath '/Spring.Northwind.Web/dummy.context' +2008-04-04 17:20:59,593 [4] DEBUG Spring.Context.Support.ContextHandler [(null)] - creating context '/spring.northwind.web' +2008-04-04 17:20:59,625 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - hooking up event handlers +2008-04-04 17:20:59,859 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loading XML object definitions from file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Aspects.xml] +2008-04-04 17:20:59,875 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Using the following XmlReader implementation : System.Xml.XsdValidatingReader +2008-04-04 17:20:59,875 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Loading object definitions. +2008-04-04 17:20:59,890 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Loading object definitions... +2008-04-04 17:20:59,890 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default lazy init 'false'. +2008-04-04 17:20:59,890 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default dependency check 'none'. +2008-04-04 17:20:59,890 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default autowire 'no'. +2008-04-04 17:21:00,109 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'CommonLoggingAroundAdvice'. +2008-04-04 17:21:00,109 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Found 1 elements defining objects. +2008-04-04 17:21:00,109 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loaded 1 bean definitions from location [~/Aspects.xml] +2008-04-04 17:21:00,109 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loading XML object definitions from file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Dao.xml] +2008-04-04 17:21:00,125 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Using the following XmlReader implementation : System.Xml.XsdValidatingReader +2008-04-04 17:21:00,125 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Loading object definitions. +2008-04-04 17:21:00,125 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Loading object definitions... +2008-04-04 17:21:00,125 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default lazy init 'false'. +2008-04-04 17:21:00,125 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default dependency check 'none'. +2008-04-04 17:21:00,125 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default autowire 'no'. +2008-04-04 17:21:00,125 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - No XML 'id' specified - using '' as the id and '' as aliases. +2008-04-04 17:21:00,125 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Neither XML 'Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer' nor 'id' specified - using object class name [name] as the id. +2008-04-04 17:21:00,125 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer'. +2008-04-04 17:21:00,140 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'DbProvider'. +2008-04-04 17:21:00,156 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'NHibernateSessionFactory'. +2008-04-04 17:21:00,156 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'HibernateTransactionManager'. +2008-04-04 17:21:00,156 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'HibernateTemplate'. +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'CustomerDao'. +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'OrderDao'. +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Found 8 elements defining objects. +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loaded 7 bean definitions from location [~/Dao.xml] +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loading XML object definitions from file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Services.xml] +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Using the following XmlReader implementation : System.Xml.XsdValidatingReader +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Loading object definitions. +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Loading object definitions... +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default lazy init 'false'. +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default dependency check 'none'. +2008-04-04 17:21:00,203 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default autowire 'no'. +2008-04-04 17:21:00,265 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'FulfillmentService'. +2008-04-04 17:21:00,265 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'ShippingService'. +2008-04-04 17:21:00,265 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Attempting to import object definitions from 'DeclarativeServicesAttributeDriven.xml'. +2008-04-04 17:21:00,281 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loading XML object definitions from file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\DeclarativeServicesAttributeDriven.xml] +2008-04-04 17:21:00,281 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Using the following XmlReader implementation : System.Xml.XsdValidatingReader +2008-04-04 17:21:00,281 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Loading object definitions. +2008-04-04 17:21:00,281 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Loading object definitions... +2008-04-04 17:21:00,281 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default lazy init 'false'. +2008-04-04 17:21:00,281 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default dependency check 'none'. +2008-04-04 17:21:00,281 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default autowire 'no'. +2008-04-04 17:21:00,312 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'autoProxyCreator'. +2008-04-04 17:21:00,312 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'transactionAdvisor'. +2008-04-04 17:21:00,312 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'transactionInterceptor'. +2008-04-04 17:21:00,312 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'attributeTransactionAttributeSource'. +2008-04-04 17:21:00,312 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Found 14 elements defining objects. +2008-04-04 17:21:00,312 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Found 14 elements defining objects. +2008-04-04 17:21:00,312 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loaded 6 bean definitions from location [~/Services.xml] +2008-04-04 17:21:00,312 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loading XML object definitions from file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml] +2008-04-04 17:21:00,328 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Using the following XmlReader implementation : System.Xml.XsdValidatingReader +2008-04-04 17:21:00,328 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Loading object definitions. +2008-04-04 17:21:00,328 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Loading object definitions... +2008-04-04 17:21:00,328 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default lazy init 'false'. +2008-04-04 17:21:00,328 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default dependency check 'none'. +2008-04-04 17:21:00,328 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default autowire 'no'. +2008-04-04 17:21:00,328 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - No XML 'id' specified - using 'CustomerEditController' as the id and '' as aliases. +2008-04-04 17:21:00,328 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'CustomerEditController'. +2008-04-04 17:21:00,328 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance 'Default.aspx' +2008-04-04 17:21:00,328 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/Default.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - getting page type for Default.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/Default.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - got page type 'ASP.default_aspx' for vpath '/Spring.Northwind.Web/Default.aspx' +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id '/Default.aspx'. +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - No XML 'id' specified - using 'CustomerEditPage' as the id and '' as aliases. +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'CustomerEditPage'. +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance 'CustomerList.aspx' +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerList.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - getting page type for CustomerList.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerList.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - got page type 'ASP.customerlist_aspx' for vpath '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id '/CustomerList.aspx'. +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance 'CustomerEditor.aspx' +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerEditor.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - getting page type for CustomerEditor.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerEditor.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - got page type 'ASP.customereditor_aspx' for vpath '/Spring.Northwind.Web/CustomerEditor.aspx' +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id '/CustomerEditor.aspx'. +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance 'CustomerOrders.aspx' +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerOrders.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - getting page type for CustomerOrders.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerOrders.aspx +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - got page type 'ASP.customerorders_aspx' for vpath '/Spring.Northwind.Web/CustomerOrders.aspx' +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id '/CustomerOrders.aspx'. +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Found 20 elements defining objects. +2008-04-04 17:21:02,718 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loaded 6 bean definitions from location [~/Web.xml] +2008-04-04 17:21:02,718 [4] DEBUG Spring.Context.Support.AbstractXmlApplicationContext [(null)] - Refreshed ObjectFactory for application context '/spring.northwind.web'. +2008-04-04 17:21:02,734 [4] DEBUG Spring.Context.Support.AbstractApplicationContext [(null)] - 20 objects defined in application context [/spring.northwind.web]. +2008-04-04 17:21:02,765 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer' +2008-04-04 17:21:02,765 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer' with merged definition [Root web object with class [Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Dao.xml]]. +2008-04-04 17:21:02,781 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer'. +2008-04-04 17:21:02,781 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer' to allow for resolving potential circular references +2008-04-04 17:21:02,781 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer' using definition 'Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer' +2008-04-04 17:21:02,828 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer' +2008-04-04 17:21:02,828 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer' +2008-04-04 17:21:02,859 [4] DEBUG Spring.Context.Support.AbstractApplicationContext [(null)] - processed 1 IFactoryObjectPostProcessors defined in application context [/spring.northwind.web]. +2008-04-04 17:21:02,859 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'autoProxyCreator' +2008-04-04 17:21:02,859 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'autoProxyCreator' with merged definition [Root web object with class [Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\DeclarativeServicesAttributeDriven.xml]]. +2008-04-04 17:21:02,859 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'autoProxyCreator'. +2008-04-04 17:21:02,875 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'autoProxyCreator' to allow for resolving potential circular references +2008-04-04 17:21:02,875 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'proxyTargetType=False; proxyTargetAttributes=True; exposeProxy=False; isFrozen=False; optimize=False; aopProxyFactory=Spring.Aop.Framework.DynamicProxy.CachedAopProxyFactory; ' using definition 'autoProxyCreator' +2008-04-04 17:21:02,875 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Setting the name property on the IObjectNameAware object 'autoProxyCreator'. +2008-04-04 17:21:02,875 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Setting the ObjectFactory property on the IObjectFactoryAware object 'autoProxyCreator'. +2008-04-04 17:21:02,875 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'autoProxyCreator' +2008-04-04 17:21:02,875 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Calling AfterPropertiesSet() on object with name 'autoProxyCreator'. +2008-04-04 17:21:02,875 [4] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - instantiating available advisors +2008-04-04 17:21:02,875 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'transactionAdvisor' +2008-04-04 17:21:02,875 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'transactionAdvisor' with merged definition [Root web object with class [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\DeclarativeServicesAttributeDriven.xml]]. +2008-04-04 17:21:02,875 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'transactionAdvisor'. +2008-04-04 17:21:02,937 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'transactionAdvisor' to allow for resolving potential circular references +2008-04-04 17:21:02,937 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor' using definition 'transactionAdvisor' +2008-04-04 17:21:02,937 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'TransactionInterceptor' in object 'transactionAdvisor' to object 'transactionInterceptor'. +2008-04-04 17:21:02,937 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'transactionInterceptor' +2008-04-04 17:21:02,937 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'transactionInterceptor' with merged definition [Root web object with class [Spring.Transaction.Interceptor.TransactionInterceptor] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\DeclarativeServicesAttributeDriven.xml]]. +2008-04-04 17:21:02,937 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'transactionInterceptor'. +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'transactionInterceptor' to allow for resolving potential circular references +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Transaction.Interceptor.TransactionInterceptor' using definition 'transactionInterceptor' +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'TransactionManager' in object 'transactionInterceptor' to object 'HibernateTransactionManager'. +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'HibernateTransactionManager' +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'HibernateTransactionManager' with merged definition [Root web object with class [Spring.Data.NHibernate.HibernateTransactionManager] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Dao.xml]]. +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'HibernateTransactionManager'. +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'HibernateTransactionManager' to allow for resolving potential circular references +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Data.NHibernate.HibernateTransactionManager' using definition 'HibernateTransactionManager' +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'DbProvider' in object 'HibernateTransactionManager' to object 'DbProvider'. +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'DbProvider' +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'DbProvider' with merged definition [Root web object with class [Spring.Data.Common.DbProviderFactoryObject] defined in ]. +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'DbProvider'. +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'DbProvider' to allow for resolving potential circular references +2008-04-04 17:21:02,953 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Data.Common.DbProviderFactoryObject' using definition 'DbProvider' +2008-04-04 17:21:02,984 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'DbProvider' +2008-04-04 17:21:02,984 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Calling AfterPropertiesSet() on object with name 'DbProvider'. +2008-04-04 17:21:02,984 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'DbProvider' +2008-04-04 17:21:02,984 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Object with name 'DbProvider' is a factory object. +2008-04-04 17:21:03,000 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loading XML object definitions from assembly [Spring.Data, Version=1.1.1.20093, Culture=neutral, PublicKeyToken=null], resource [Spring.Data.Common.dbproviders.xml] +2008-04-04 17:21:03,000 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Using the following XmlReader implementation : System.Xml.XsdValidatingReader +2008-04-04 17:21:03,015 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Loading object definitions. +2008-04-04 17:21:03,015 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Loading object definitions... +2008-04-04 17:21:03,015 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default lazy init 'false'. +2008-04-04 17:21:03,015 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default dependency check 'none'. +2008-04-04 17:21:03,015 [4] DEBUG Spring.Objects.Factory.Xml.ObjectDefinitionParserHelper [(null)] - Default autowire 'no'. +2008-04-04 17:21:03,015 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'SqlServer-1.1'. +2008-04-04 17:21:03,015 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'SqlServer-2.0'. +2008-04-04 17:21:03,015 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Registering alias 'System.Data.SqlClient' for object with name 'SqlServer-2.0'. +2008-04-04 17:21:03,015 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'SqlServerCe-3.1'. +2008-04-04 17:21:03,015 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Registering alias 'System.Data.SqlServerCe' for object with name 'SqlServerCe-3.1'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'OleDb-1.1'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'OleDb-2.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Registering alias 'System.Data.OleDb' for object with name 'OleDb-2.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'OracleClient-2.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Registering alias 'System.Data.OracleClient' for object with name 'OracleClient-2.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'OracleODP-2.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Registering alias 'Oracle.DataAccess.Client' for object with name 'OracleODP-2.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'MySql'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'MySql-1.0.9'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'MySql-5.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'MySql-5.0.8.1'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'MySql-5.1'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'MySql-5.1.4'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Registering alias 'MySql.Data.MySqlClient' for object with name 'MySql-5.1.4'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'Npgsql-1.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'Npgsql-2.0-beta1'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'DB2-9.0.0-1.1'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'DB2-9.0.0-2.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Registering alias 'IBM.Data.DB2' for object with name 'DB2-9.0.0-2.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'DB2-9.1.0-1.1'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'DB2-9.1.0.2'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Registering alias 'IBM.Data.DB2.9.1.0' for object with name 'DB2-9.1.0.2'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'iDB2-10.0.0.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'SQLite-1.0.43'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'SQLite-1.0.44'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'SQLite-1.0.47'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Registering alias 'System.Data.SQLite' for object with name 'SQLite-1.0.47'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'SybaseAse-12'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'SybaseAse-15'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'Odbc-1.1'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.ObjectsNamespaceParser [(null)] - Registering object definition with id 'Odbc-2.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Registering alias 'System.Data.Odbc' for object with name 'Odbc-2.0'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Xml.DefaultObjectDefinitionDocumentReader [(null)] - Found 27 elements defining objects. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Objects.Factory.Support.AbstractObjectDefinitionReader [(null)] - Loaded 27 bean definitions from location [assembly://Spring.Data, Version=1.1.1.20093, Culture=neutral, PublicKeyToken=null/Spring.Data.Common/dbproviders.xml] +2008-04-04 17:21:03,031 [4] DEBUG Spring.Context.Support.AbstractXmlApplicationContext [(null)] - Refreshed ObjectFactory for application context 'DBPROVIDERFACTORY_CONTEXT'. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Context.Support.AbstractApplicationContext [(null)] - 27 objects defined in application context [DBPROVIDERFACTORY_CONTEXT]. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Context.Support.AbstractApplicationContext [(null)] - processed 0 IFactoryObjectPostProcessors defined in application context [DBPROVIDERFACTORY_CONTEXT]. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Context.Support.AbstractApplicationContext [(null)] - processed 0 IObjectPostProcessors defined in application context [DBPROVIDERFACTORY_CONTEXT]. +2008-04-04 17:21:03,031 [4] DEBUG Spring.Context.Support.AbstractApplicationContext [(null)] - No IEventRegistry found with name 'eventRegistry' : using default 'Spring.Objects.Events.Support.EventRegistry'. +2008-04-04 17:21:03,046 [4] DEBUG Spring.Context.Support.AbstractApplicationContext [(null)] - No IMessageSource found with name 'messageSource' : using default 'StaticMessageSource : '. +2008-04-04 17:21:03,046 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Pre-instantiating singletons in factory [Spring.Objects.Factory.Support.DefaultListableObjectFactory] +2008-04-04 17:21:03,046 [4] INFO Spring.Data.Common.DbProviderFactory [(null)] - 27 DbProviders Available. [SqlServer-1.1,SqlServer-2.0,SqlServerCe-3.1,OleDb-1.1,OleDb-2.0,OracleClient-2.0,OracleODP-2.0,MySql,MySql-1.0.9,MySql-5.0,MySql-5.0.8.1,MySql-5.1,MySql-5.1.4,Npgsql-1.0,Npgsql-2.0-beta1,DB2-9.0.0-1.1,DB2-9.0.0-2.0,DB2-9.1.0-1.1,DB2-9.1.0.2,iDB2-10.0.0.0,SQLite-1.0.43,SQLite-1.0.44,SQLite-1.0.47,SybaseAse-12,SybaseAse-15,Odbc-1.1,Odbc-2.0] +2008-04-04 17:21:03,046 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'SqlServer-2.0' with merged definition [Root web object with class [Spring.Data.Common.DbProvider] defined in assembly [Spring.Data, Version=1.1.1.20093, Culture=neutral, PublicKeyToken=null], resource [Spring.Data.Common.dbproviders.xml]]. +2008-04-04 17:21:03,046 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'SqlServer-2.0'. +2008-04-04 17:21:03,062 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '(inner object)' with merged definition [Root web object with class [Spring.Data.Common.DbMetadata] defined in assembly [Spring.Data, Version=1.1.1.20093, Culture=neutral, PublicKeyToken=null], resource [Spring.Data.Common.dbproviders.xml]]. +2008-04-04 17:21:03,062 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '(inner object)'. +2008-04-04 17:21:03,078 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Object '(inner object)' instantiated via constructor [Void .ctor(System.String, System.String, System.Type, System.Type, System.Type, System.Type, System.Type, System.String, System.Type, System.String, System.String, System.String, System.Type, Boolean, Boolean, Boolean, System.String)]. +2008-04-04 17:21:03,078 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Data.Common.DbMetadata' using definition '(inner object)' +2008-04-04 17:21:03,125 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '(inner object)' +2008-04-04 17:21:03,125 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '(inner object)' +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Object 'SqlServer-2.0' instantiated via constructor [Void .ctor(Spring.Data.Common.IDbMetadata)]. +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Data.Common.DbProvider' using definition 'SqlServer-2.0' +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'SqlServer-2.0' +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'SqlServer-2.0' +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'DbProvider' +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'SessionFactory' in object 'HibernateTransactionManager' to object 'NHibernateSessionFactory'. +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'NHibernateSessionFactory' +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'NHibernateSessionFactory' with merged definition [Root web object with class [Spring.Data.NHibernate.LocalSessionFactoryObject] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Dao.xml]]. +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'NHibernateSessionFactory'. +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'NHibernateSessionFactory' to allow for resolving potential circular references +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Data.NHibernate.LocalSessionFactoryObject' using definition 'NHibernateSessionFactory' +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'DbProvider' in object 'NHibernateSessionFactory' to object 'DbProvider'. +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'DbProvider'. +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Object with name 'DbProvider' is a factory object. +2008-04-04 17:21:03,140 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'DbProvider' +2008-04-04 17:21:03,171 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'NHibernateSessionFactory' +2008-04-04 17:21:03,171 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Calling AfterPropertiesSet() on object with name 'NHibernateSessionFactory'. +2008-04-04 17:21:03,250 [4] INFO NHibernate.Cfg.Environment [(null)] - NHibernate 1.2.1.4000 (1.2.1.4000) +2008-04-04 17:21:03,250 [4] INFO NHibernate.Cfg.Environment [(null)] - nhibernate section not found in application configuration file +2008-04-04 17:21:03,265 [4] INFO NHibernate.Cfg.Environment [(null)] - Bytecode provider name : lcg +2008-04-04 17:21:03,265 [4] INFO NHibernate.Cfg.Environment [(null)] - Using reflection optimizer +2008-04-04 17:21:03,296 [4] WARN Spring.Data.NHibernate.LocalSessionFactoryObject [(null)] - Overriding use of Spring's Hibernate Connection Provider with [NHibernate.Connection.DriverConnectionProvider] +2008-04-04 17:21:03,296 [4] INFO NHibernate.Cfg.Configuration [(null)] - Searching for mapped documents in assembly: Spring.Northwind.Dao.NHibernate +2008-04-04 17:21:03,296 [4] INFO NHibernate.Cfg.Configuration [(null)] - Mapping resource: Spring.Northwind.Mappings.Order.hbm.xml +2008-04-04 17:21:03,406 [4] INFO NHibernate.Dialect.Dialect [(null)] - Using dialect: NHibernate.Dialect.MsSql2000Dialect +2008-04-04 17:21:03,484 [4] INFO NHibernate.Cfg.HbmBinder [(null)] - Mapping class: Spring.Northwind.Domain.Order -> Orders +2008-04-04 17:21:03,609 [4] INFO NHibernate.Cfg.Configuration [(null)] - Mapping resource: Spring.Northwind.Mappings.Customer.hbm.xml +2008-04-04 17:21:03,609 [4] INFO NHibernate.Dialect.Dialect [(null)] - Using dialect: NHibernate.Dialect.MsSql2000Dialect +2008-04-04 17:21:03,609 [4] INFO NHibernate.Cfg.HbmBinder [(null)] - Mapping class: Spring.Northwind.Domain.Customer -> Customers +2008-04-04 17:21:03,609 [4] INFO NHibernate.Cfg.Configuration [(null)] - Mapping resource: Spring.Northwind.Mappings.OrderDetail.hbm.xml +2008-04-04 17:21:03,609 [4] INFO NHibernate.Dialect.Dialect [(null)] - Using dialect: NHibernate.Dialect.MsSql2000Dialect +2008-04-04 17:21:03,609 [4] INFO NHibernate.Cfg.HbmBinder [(null)] - Mapping class: Spring.Northwind.Domain.OrderDetail -> [Order Details] +2008-04-04 17:21:03,734 [4] INFO Spring.Data.NHibernate.LocalSessionFactoryObject [(null)] - Building new Hibernate SessionFactory +2008-04-04 17:21:03,734 [4] INFO NHibernate.Cfg.Configuration [(null)] - checking mappings queue +2008-04-04 17:21:03,734 [4] INFO NHibernate.Cfg.Configuration [(null)] - processing one-to-many association mappings +2008-04-04 17:21:03,734 [4] INFO NHibernate.Cfg.HbmBinder [(null)] - mapping collection: Spring.Northwind.Domain.Order.OrderDetails -> [Order Details] +2008-04-04 17:21:03,734 [4] INFO NHibernate.Cfg.HbmBinder [(null)] - mapping collection: Spring.Northwind.Domain.Customer.Orders -> Orders +2008-04-04 17:21:03,734 [4] INFO NHibernate.Cfg.Configuration [(null)] - processing one-to-one association property references +2008-04-04 17:21:03,734 [4] INFO NHibernate.Cfg.Configuration [(null)] - processing foreign key constraints +2008-04-04 17:21:03,765 [4] INFO NHibernate.Dialect.Dialect [(null)] - Using dialect: NHibernate.Dialect.MsSql2000Dialect +2008-04-04 17:21:03,765 [4] INFO NHibernate.Connection.ConnectionProviderFactory [(null)] - Initializing connection provider: NHibernate.Connection.DriverConnectionProvider +2008-04-04 17:21:03,765 [4] INFO NHibernate.Connection.ConnectionProvider [(null)] - Configuring ConnectionProvider +2008-04-04 17:21:03,765 [4] INFO NHibernate.Cfg.SettingsFactory [(null)] - Transaction factory: NHibernate.Transaction.AdoNetTransactionFactory +2008-04-04 17:21:03,765 [4] INFO NHibernate.Cfg.SettingsFactory [(null)] - Optimize cache for minimal puts: False +2008-04-04 17:21:03,765 [4] INFO NHibernate.Cfg.SettingsFactory [(null)] - Connection release mode: auto +2008-04-04 17:21:03,765 [4] INFO NHibernate.Cfg.SettingsFactory [(null)] - Query translator: NHibernate.Hql.Classic.ClassicQueryTranslatorFactory +2008-04-04 17:21:03,781 [4] INFO NHibernate.Cfg.SettingsFactory [(null)] - Query language substitutions: {} +2008-04-04 17:21:03,781 [4] INFO NHibernate.Cfg.SettingsFactory [(null)] - cache provider: NHibernate.Cache.NoCacheProvider, NHibernate, Version=1.2.1.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4 +2008-04-04 17:21:03,828 [4] INFO NHibernate.Impl.SessionFactoryImpl [(null)] - building session factory +2008-04-04 17:21:04,000 [4] INFO NHibernate.Impl.SessionFactoryObjectFactory [(null)] - no name configured +2008-04-04 17:21:04,203 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'NHibernateSessionFactory' +2008-04-04 17:21:04,203 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Object with name 'NHibernateSessionFactory' is a factory object. +2008-04-04 17:21:04,203 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'NHibernateSessionFactory' +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Setting the ObjectFactory property on the IObjectFactoryAware object 'HibernateTransactionManager'. +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'HibernateTransactionManager' +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Calling AfterPropertiesSet() on object with name 'HibernateTransactionManager'. +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'HibernateTransactionManager' +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'TransactionAttributeSource' in object 'transactionInterceptor' to object 'attributeTransactionAttributeSource'. +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'attributeTransactionAttributeSource' +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'attributeTransactionAttributeSource' with merged definition [Root web object with class [Spring.Transaction.Interceptor.AttributesTransactionAttributeSource] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\DeclarativeServicesAttributeDriven.xml]]. +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'attributeTransactionAttributeSource'. +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'attributeTransactionAttributeSource' to allow for resolving potential circular references +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Transaction.Interceptor.AttributesTransactionAttributeSource' using definition 'attributeTransactionAttributeSource' +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'attributeTransactionAttributeSource' +2008-04-04 17:21:04,218 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'attributeTransactionAttributeSource' +2008-04-04 17:21:04,234 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'transactionInterceptor' +2008-04-04 17:21:04,234 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Calling AfterPropertiesSet() on object with name 'transactionInterceptor'. +2008-04-04 17:21:04,234 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'transactionInterceptor' +2008-04-04 17:21:04,234 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'transactionAdvisor' +2008-04-04 17:21:04,234 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'transactionAdvisor' +2008-04-04 17:21:04,234 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'autoProxyCreator' +2008-04-04 17:21:04,234 [4] DEBUG Spring.Context.Support.AbstractApplicationContext [(null)] - processed 1 IObjectPostProcessors defined in application context [/spring.northwind.web]. +2008-04-04 17:21:04,234 [4] DEBUG Spring.Context.Support.AbstractApplicationContext [(null)] - No IEventRegistry found with name 'eventRegistry' : using default 'Spring.Objects.Events.Support.EventRegistry'. +2008-04-04 17:21:04,234 [4] DEBUG Spring.Context.Support.AbstractApplicationContext [(null)] - No IMessageSource found with name 'messageSource' : using default 'StaticMessageSource : '. +2008-04-04 17:21:04,234 [4] DEBUG Spring.Objects.Factory.Support.DefaultListableObjectFactory [(null)] - Pre-instantiating singletons in factory [Spring.Objects.Factory.Support.WebObjectFactory] +2008-04-04 17:21:04,234 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'CommonLoggingAroundAdvice' +2008-04-04 17:21:04,234 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'CommonLoggingAroundAdvice' with merged definition [Root web object with class [Spring.Aspects.Logging.CommonLoggingAroundAdvice] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Aspects.xml]]. +2008-04-04 17:21:04,234 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'CommonLoggingAroundAdvice'. +2008-04-04 17:21:04,250 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'CommonLoggingAroundAdvice' to allow for resolving potential circular references +2008-04-04 17:21:04,250 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Aspects.Logging.CommonLoggingAroundAdvice' using definition 'CommonLoggingAroundAdvice' +2008-04-04 17:21:04,250 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'CommonLoggingAroundAdvice' +2008-04-04 17:21:04,250 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'CommonLoggingAroundAdvice' +2008-04-04 17:21:04,250 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'HibernateTemplate' +2008-04-04 17:21:04,250 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'HibernateTemplate' with merged definition [Root web object with class [Spring.Data.NHibernate.HibernateTemplate] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Dao.xml]]. +2008-04-04 17:21:04,250 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'HibernateTemplate'. +2008-04-04 17:21:04,250 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'HibernateTemplate' to allow for resolving potential circular references +2008-04-04 17:21:04,250 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Data.NHibernate.HibernateTemplate' using definition 'HibernateTemplate' +2008-04-04 17:21:04,265 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'SessionFactory' in object 'HibernateTemplate' to object 'NHibernateSessionFactory'. +2008-04-04 17:21:04,265 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'NHibernateSessionFactory'. +2008-04-04 17:21:04,265 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Object with name 'NHibernateSessionFactory' is a factory object. +2008-04-04 17:21:04,265 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'NHibernateSessionFactory' +2008-04-04 17:21:04,265 [4] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - returning available advisors +2008-04-04 17:21:04,359 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Candidate advisor [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] rejected for type [NHibernate.Impl.SessionFactoryImpl] +2008-04-04 17:21:04,375 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Setting the ObjectFactory property on the IObjectFactoryAware object 'HibernateTemplate'. +2008-04-04 17:21:04,390 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'HibernateTemplate' +2008-04-04 17:21:04,390 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Calling AfterPropertiesSet() on object with name 'HibernateTemplate'. +2008-04-04 17:21:04,390 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'HibernateTemplate' +2008-04-04 17:21:04,390 [4] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - returning available advisors +2008-04-04 17:21:04,421 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Candidate advisor [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] rejected for type [Spring.Data.NHibernate.HibernateTemplate] +2008-04-04 17:21:04,421 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'CustomerDao' +2008-04-04 17:21:04,421 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'CustomerDao' with merged definition [Root web object with class [Spring.Northwind.Dao.NHibernate.HibernateCustomerDao] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Dao.xml]]. +2008-04-04 17:21:04,421 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'CustomerDao'. +2008-04-04 17:21:04,421 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'CustomerDao' to allow for resolving potential circular references +2008-04-04 17:21:04,421 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Northwind.Dao.NHibernate.HibernateCustomerDao' using definition 'CustomerDao' +2008-04-04 17:21:04,421 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'HibernateTemplate' in object 'CustomerDao' to object 'HibernateTemplate'. +2008-04-04 17:21:04,421 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'HibernateTemplate'. +2008-04-04 17:21:04,437 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'CustomerDao' +2008-04-04 17:21:04,437 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Calling AfterPropertiesSet() on object with name 'CustomerDao'. +2008-04-04 17:21:04,437 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'CustomerDao' +2008-04-04 17:21:04,437 [4] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - returning available advisors +2008-04-04 17:21:04,437 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Candidate advisor [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] accepted for type [Spring.Northwind.Dao.NHibernate.HibernateCustomerDao] +2008-04-04 17:21:04,453 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Creating implicit proxy for object 'CustomerDao' with 0 common interceptors and 1 specific interceptors +2008-04-04 17:21:04,578 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'OrderDao' +2008-04-04 17:21:04,578 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'OrderDao' with merged definition [Root web object with class [Spring.Northwind.Dao.NHibernate.HibernateOrderDao] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Dao.xml]]. +2008-04-04 17:21:04,578 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'OrderDao'. +2008-04-04 17:21:04,578 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'OrderDao' to allow for resolving potential circular references +2008-04-04 17:21:04,578 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Northwind.Dao.NHibernate.HibernateOrderDao' using definition 'OrderDao' +2008-04-04 17:21:04,578 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'HibernateTemplate' in object 'OrderDao' to object 'HibernateTemplate'. +2008-04-04 17:21:04,578 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'HibernateTemplate'. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'OrderDao' +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Calling AfterPropertiesSet() on object with name 'OrderDao'. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'OrderDao' +2008-04-04 17:21:04,593 [4] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - returning available advisors +2008-04-04 17:21:04,593 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Candidate advisor [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] rejected for type [Spring.Northwind.Dao.NHibernate.HibernateOrderDao] +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'FulfillmentService' +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'FulfillmentService' with merged definition [Root web object with class [Spring.Northwind.Service.FulfillmentService] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Services.xml]]. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'FulfillmentService'. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'FulfillmentService' to allow for resolving potential circular references +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Northwind.Service.FulfillmentService' using definition 'FulfillmentService' +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerDao' in object 'FulfillmentService' to object 'CustomerDao'. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerDao'. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'OrderDao' in object 'FulfillmentService' to object 'OrderDao'. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'OrderDao'. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'ShippingService' in object 'FulfillmentService' to object 'ShippingService'. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Creating shared instance of singleton object 'ShippingService' +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'ShippingService' with merged definition [Root web object with class [Spring.Northwind.Service.FedExShippingService] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Services.xml]]. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'ShippingService'. +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Eagerly caching object 'ShippingService' to allow for resolving potential circular references +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Northwind.Service.FedExShippingService' using definition 'ShippingService' +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'ShippingService' +2008-04-04 17:21:04,593 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'ShippingService' +2008-04-04 17:21:04,593 [4] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - returning available advisors +2008-04-04 17:21:04,593 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Candidate advisor [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] rejected for type [Spring.Northwind.Service.FedExShippingService] +2008-04-04 17:21:04,609 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'FulfillmentService' +2008-04-04 17:21:04,609 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'FulfillmentService' +2008-04-04 17:21:04,609 [4] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - returning available advisors +2008-04-04 17:21:04,609 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Candidate advisor [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] accepted for type [Spring.Northwind.Service.FulfillmentService] +2008-04-04 17:21:04,609 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Creating implicit proxy for object 'FulfillmentService' with 0 common interceptors and 1 specific interceptors +2008-04-04 17:21:04,625 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - created instance [/spring.northwind.web]:WebApplicationContext(35298535) +2008-04-04 17:21:04,625 [4] DEBUG Spring.Context.Support.ContextRegistry [(null)] - Registering context '[/spring.northwind.web]:WebApplicationContext(35298535)' under name '/spring.northwind.web'. +2008-04-04 17:21:04,625 [4] DEBUG Spring.Context.Support.ContextHandler [(null)] - context '[/spring.northwind.web]:WebApplicationContext(35298535)' created for name '/spring.northwind.web' +2008-04-04 17:21:04,625 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - got context '[/spring.northwind.web]:WebApplicationContext(35298535)' for vpath 'spring.root' +2008-04-04 17:21:04,625 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - added context '[/spring.northwind.web]:WebApplicationContext(35298535)' to WebContextCache for vpath 'spring.root' +2008-04-04 17:21:04,625 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - added parent context '[/spring.northwind.web]:WebApplicationContext(35298535)' to WebContextCache for vpath '/spring.northwind.web' +2008-04-04 17:21:04,640 [4] DEBUG Spring.Context.Support.ContextRegistry [(null)] - Returning context '[/spring.northwind.web]:WebApplicationContext(35298535)' registered under name '/spring.northwind.web'. +2008-04-04 17:21:04,640 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'NHibernateSessionFactory'. +2008-04-04 17:21:04,640 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Object with name 'NHibernateSessionFactory' is a factory object. +2008-04-04 17:21:04,640 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'NHibernateSessionFactory' +2008-04-04 17:21:04,640 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:04,640 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:04,640 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@3864659] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [4] +2008-04-04 17:21:04,718 [4] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:04,718 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - looking up web context 'spring.root' in WebContextCache +2008-04-04 17:21:04,718 [4] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - returning WebContextCache hit '[/spring.northwind.web]:WebApplicationContext(35298535)' for vpath 'spring.root' +2008-04-04 17:21:04,718 [4] DEBUG Spring.Web.Support.AbstractHandlerFactory [(null)] - GetHandler():looking up definition for app-relative url '/CustomerList.aspx' +2008-04-04 17:21:04,718 [4] DEBUG Spring.Web.Support.AbstractHandlerFactory [(null)] - GetHandler():found definition for page-url '/CustomerList.aspx' +2008-04-04 17:21:04,718 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerList.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerList.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:04,718 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerList.aspx'. +2008-04-04 17:21:04,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:04,718 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerList.aspx +2008-04-04 17:21:04,718 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customerlist_aspx' using definition '/CustomerList.aspx' +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerList.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'CustomerEditController' with merged definition [Root web object with class [NHibernateCustomerEditController] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'CustomerEditController'. +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'constructor argument with name sessionfactory' in object 'CustomerEditController' to object 'NHibernateSessionFactory'. +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'NHibernateSessionFactory'. +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Object with name 'NHibernateSessionFactory' is a factory object. +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'NHibernateSessionFactory' +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Object 'CustomerEditController' instantiated via constructor [Void .ctor(NHibernate.ISessionFactory)]. +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'NHibernateCustomerEditController' using definition 'CustomerEditController' +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'CustomerEditController' +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'CustomerEditController' +2008-04-04 17:21:04,734 [4] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - returning available advisors +2008-04-04 17:21:04,734 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Candidate advisor [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] rejected for type [NHibernateCustomerEditController] +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerDao' in object '/CustomerList.aspx' to object 'CustomerDao'. +2008-04-04 17:21:04,734 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerDao'. +2008-04-04 17:21:04,765 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerList.aspx' +2008-04-04 17:21:04,765 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerList.aspx' +2008-04-04 17:21:04,765 [4] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - returning available advisors +2008-04-04 17:21:05,156 [4] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Candidate advisor [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] rejected for type [ASP.customerlist_aspx] +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object 'SqlServer-2.0' with merged definition [Root web object with class [Spring.Data.Common.DbProvider] defined in assembly [Spring.Data, Version=1.1.1.20093, Culture=neutral, PublicKeyToken=null], resource [Spring.Data.Common.dbproviders.xml]]. +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of 'SqlServer-2.0'. +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '(inner object)' with merged definition [Root web object with class [Spring.Data.Common.DbMetadata] defined in assembly [Spring.Data, Version=1.1.1.20093, Culture=neutral, PublicKeyToken=null], resource [Spring.Data.Common.dbproviders.xml]]. +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '(inner object)'. +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Object '(inner object)' instantiated via constructor [Void .ctor(System.String, System.String, System.Type, System.Type, System.Type, System.Type, System.Type, System.String, System.Type, System.String, System.String, System.String, System.Type, Boolean, Boolean, Boolean, System.String)]. +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Data.Common.DbMetadata' using definition '(inner object)' +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '(inner object)' +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '(inner object)' +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Object 'SqlServer-2.0' instantiated via constructor [Void .ctor(Spring.Data.Common.IDbMetadata)]. +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'Spring.Data.Common.DbProvider' using definition 'SqlServer-2.0' +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object 'SqlServer-2.0' +2008-04-04 17:21:05,296 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object 'SqlServer-2.0' +2008-04-04 17:21:05,312 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@3864659] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:05,312 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:05,312 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:05,328 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@3864659] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:05,328 [4] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Found thread-bound Session for HibernateTemplate +2008-04-04 17:21:05,343 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@3864659] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:05,406 [4] INFO NHibernate.Loader.Loader [(null)] - SELECT this_.CustomerID as CustomerID1_0_, this_.CompanyName as CompanyN2_1_0_, this_.ContactName as ContactN3_1_0_, this_.ContactTitle as ContactT4_1_0_, this_.Address as Address1_0_, this_.City as City1_0_, this_.Region as Region1_0_, this_.PostalCode as PostalCode1_0_, this_.Country as Country1_0_, this_.Phone as Phone1_0_, this_.Fax as Fax1_0_ FROM Customers this_ +2008-04-04 17:21:06,000 [4] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Not closing pre-bound Hibernate Session after HibernateTemplate +2008-04-04 17:21:06,046 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:06,046 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:06,046 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:06,046 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@3864659] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [4] +2008-04-04 17:21:06,046 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:06,046 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:21:08,921 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:08,921 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:08,921 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@23404EE] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [8] +2008-04-04 17:21:08,921 [8] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:08,921 [8] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolved url '/Spring.Northwind.Web/CustomerList.aspx' from reusable handler cache +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerList.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerList.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerList.aspx'. +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerList.aspx +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customerlist_aspx' using definition '/CustomerList.aspx' +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerList.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerEditController'. +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerDao' in object '/CustomerList.aspx' to object 'CustomerDao'. +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerDao'. +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerList.aspx' +2008-04-04 17:21:08,921 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerList.aspx' +2008-04-04 17:21:08,921 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@23404EE] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:08,921 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:08,921 [8] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:08,921 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@23404EE] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:08,921 [8] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Found thread-bound Session for HibernateTemplate +2008-04-04 17:21:08,921 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@23404EE] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:08,921 [8] INFO NHibernate.Loader.Loader [(null)] - SELECT this_.CustomerID as CustomerID1_0_, this_.CompanyName as CompanyN2_1_0_, this_.ContactName as ContactN3_1_0_, this_.ContactTitle as ContactT4_1_0_, this_.Address as Address1_0_, this_.City as City1_0_, this_.Region as Region1_0_, this_.PostalCode as PostalCode1_0_, this_.Country as Country1_0_, this_.Phone as Phone1_0_, this_.Fax as Fax1_0_ FROM Customers this_ +2008-04-04 17:21:08,937 [8] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Not closing pre-bound Hibernate Session after HibernateTemplate +2008-04-04 17:21:09,062 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:09,062 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:09,062 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:09,062 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@23404EE] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [8] +2008-04-04 17:21:09,062 [8] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:09,062 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:21:09,078 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:09,078 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:09,078 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@15CF103] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [8] +2008-04-04 17:21:09,078 [8] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerOrders.aspx' +2008-04-04 17:21:09,078 [8] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - looking up web context 'spring.root' in WebContextCache +2008-04-04 17:21:09,078 [8] DEBUG Spring.Context.Support.WebApplicationContext [(null)] - returning WebContextCache hit '[/spring.northwind.web]:WebApplicationContext(35298535)' for vpath 'spring.root' +2008-04-04 17:21:09,078 [8] DEBUG Spring.Web.Support.AbstractHandlerFactory [(null)] - GetHandler():looking up definition for app-relative url '/CustomerOrders.aspx' +2008-04-04 17:21:09,078 [8] DEBUG Spring.Web.Support.AbstractHandlerFactory [(null)] - GetHandler():found definition for page-url '/CustomerOrders.aspx' +2008-04-04 17:21:09,078 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerOrders.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerOrders.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:09,078 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerOrders.aspx'. +2008-04-04 17:21:09,078 [8] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerOrders.aspx' +2008-04-04 17:21:09,078 [8] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerOrders.aspx +2008-04-04 17:21:09,078 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customerorders_aspx' using definition '/CustomerOrders.aspx' +2008-04-04 17:21:09,078 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerOrders.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:09,078 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerEditController'. +2008-04-04 17:21:09,093 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerOrders.aspx' +2008-04-04 17:21:09,093 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerOrders.aspx' +2008-04-04 17:21:09,093 [8] DEBUG Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - returning available advisors +2008-04-04 17:21:09,437 [8] INFO Spring.Aop.Framework.AutoProxy.AbstractAutoProxyCreator [(null)] - Candidate advisor [Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor] rejected for type [ASP.customerorders_aspx] +2008-04-04 17:21:09,500 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@15CF103] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:09,500 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:09,500 [8] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:09,546 [8] INFO NHibernate.Loader.Loader [(null)] - SELECT orders0_.CustomerID as CustomerID__1_, orders0_.OrderID as OrderID1_, orders0_.OrderID as OrderID0_0_, orders0_.OrderDate as OrderDate0_0_, orders0_.RequiredDate as Required3_0_0_, orders0_.ShippedDate as ShippedD4_0_0_, orders0_.ShipName as ShipName0_0_, orders0_.ShipAddress as ShipAddr6_0_0_, orders0_.ShipCity as ShipCity0_0_, orders0_.ShipRegion as ShipRegion0_0_, orders0_.ShipPostalCode as ShipPost9_0_0_, orders0_.ShipCountry as ShipCou10_0_0_ FROM Orders orders0_ WHERE orders0_.CustomerID=@p0 +2008-04-04 17:21:09,703 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:09,703 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:09,703 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:09,703 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@15CF103] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [8] +2008-04-04 17:21:09,703 [8] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:09,703 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:21:11,359 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:11,359 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:11,359 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@4BC5D9] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [4] +2008-04-04 17:21:11,375 [4] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:11,375 [4] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolved url '/Spring.Northwind.Web/CustomerList.aspx' from reusable handler cache +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerList.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerList.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerList.aspx'. +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerList.aspx +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customerlist_aspx' using definition '/CustomerList.aspx' +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerList.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerEditController'. +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerDao' in object '/CustomerList.aspx' to object 'CustomerDao'. +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerDao'. +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerList.aspx' +2008-04-04 17:21:11,375 [4] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerList.aspx' +2008-04-04 17:21:11,375 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@4BC5D9] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:11,375 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:11,375 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:11,375 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@4BC5D9] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:11,375 [4] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Found thread-bound Session for HibernateTemplate +2008-04-04 17:21:11,375 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@4BC5D9] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [4] +2008-04-04 17:21:11,375 [4] INFO NHibernate.Loader.Loader [(null)] - SELECT this_.CustomerID as CustomerID1_0_, this_.CompanyName as CompanyN2_1_0_, this_.ContactName as ContactN3_1_0_, this_.ContactTitle as ContactT4_1_0_, this_.Address as Address1_0_, this_.City as City1_0_, this_.Region as Region1_0_, this_.PostalCode as PostalCode1_0_, this_.Country as Country1_0_, this_.Phone as Phone1_0_, this_.Fax as Fax1_0_ FROM Customers this_ +2008-04-04 17:21:11,375 [4] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Not closing pre-bound Hibernate Session after HibernateTemplate +2008-04-04 17:21:11,406 [4] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - disposing 'request'-scoped item cache +2008-04-04 17:21:11,406 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Trying to close SessionScope +2008-04-04 17:21:11,406 [4] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Closing single Hibernate Session in SessionScope +2008-04-04 17:21:11,406 [4] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Removed value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@4BC5D9] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] from thread [4] +2008-04-04 17:21:11,406 [4] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Closing Hibernate Session +2008-04-04 17:21:11,406 [4] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Closed LazySessionHolder +2008-04-04 17:21:13,328 [8] DEBUG Spring.Data.NHibernate.Support.OpenSessionInViewModule [(null)] - Opening single Hibernate Session in SessionScope +2008-04-04 17:21:13,328 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - Created LazySessionHolder +2008-04-04 17:21:13,328 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Bound value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@F15783] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] to thread [8] +2008-04-04 17:21:13,328 [8] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolving url '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:13,328 [8] DEBUG Spring.Web.Support.PageHandlerFactory [(null)] - GetHandler():resolved url '/Spring.Northwind.Web/CustomerList.aspx' from reusable handler cache +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Creating instance of Object '/CustomerList.aspx' with merged definition [Root web object with class [/Spring.Northwind.Web/CustomerList.aspx] defined in file [L:\projects\Spring.Net\examples\Spring\Spring.Data.NHibernate.Northwind\src\Spring.Northwind.Web\Web.xml]]. +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IInstantiationAwareObjectPostProcessors before the instantiation of '/CustomerList.aspx'. +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - creating page instance '/Spring.Northwind.Web/CustomerList.aspx' +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.WebObjectUtils [(null)] - page vpath is /Spring.Northwind.Web/CustomerList.aspx +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - configuring object 'ASP.customerlist_aspx' using definition '/CustomerList.aspx' +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerEditController' in object '/CustomerList.aspx' to object 'CustomerEditController'. +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerEditController'. +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Resolving reference from property 'CustomerDao' in object '/CustomerList.aspx' to object 'CustomerDao'. +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.WebObjectFactory [(null)] - Returning cached instance of singleton object 'CustomerDao'. +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors before initialization of object '/CustomerList.aspx' +2008-04-04 17:21:13,328 [8] DEBUG Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory [(null)] - Invoking IObjectPostProcessors after initialization of object '/CustomerList.aspx' +2008-04-04 17:21:13,328 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@F15783] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:13,328 [8] DEBUG Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder [(null)] - session instance requested - opening new session +2008-04-04 17:21:13,328 [8] DEBUG Spring.Data.NHibernate.SessionFactoryUtils [(null)] - Opening Hibernate Session +2008-04-04 17:21:13,328 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@F15783] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:13,328 [8] DEBUG Spring.Data.NHibernate.HibernateTemplate [(null)] - Found thread-bound Session for HibernateTemplate +2008-04-04 17:21:13,328 [8] DEBUG Spring.Transaction.Support.TransactionSynchronizationManager [(null)] - Retrieved value [Spring.Data.NHibernate.Support.SessionScope+LazySessionHolder@F15783] for key [NHibernate.Impl.SessionFactoryImpl@294C2AB] bound to thread [8] +2008-04-04 17:21:13,328 [8] INFO NHibernate.Loader.Loader [(null)] - SELECT this_.CustomerID as CustomerID1_0_, this_.CompanyName as CompanyN2_1_0_, this_.ContactName as ContactN3_1_0_, this_.ContactTitle as ContactT4_1_0_, this_.Address as Address1_0_, this_.City as City1_0_, this_.Region as Region1_0_, this_.PostalCode as PostalCode1_0_, this_.Country as Country1_0_, this_.Phone as Phone1_0_, this_.Fax as Fax1_0_ FROM Customers this_ diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Services.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Services.xml new file mode 100644 index 00000000..a2669b97 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Services.xml @@ -0,0 +1,30 @@ + + + + + + + The Northwind service layer definitions + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Web.config b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Web.config new file mode 100644 index 00000000..e8b2b4b5 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Web.config @@ -0,0 +1,82 @@ + + + + + +
    + + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Web.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Web.xml new file mode 100644 index 00000000..7883e61d --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/src/Spring.Northwind.Web/Web.xml @@ -0,0 +1,39 @@ + + + + + + + The Northwind web layer definitions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/App.config b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/App.config new file mode 100644 index 00000000..d778b07f --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/App.config @@ -0,0 +1,75 @@ + + + + +
    +
    + + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Aspects.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Aspects.xml new file mode 100644 index 00000000..b50ad156 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Aspects.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Dao.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Dao.xml new file mode 100644 index 00000000..47191809 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Dao.xml @@ -0,0 +1,75 @@ + + + + + + The Northwind object definitions for the Data Access Objects. + + + + + + + + + + + + + + + + Spring.Northwind.Dao.NHibernate + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/DeclarativeServicesAttributeDriven.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/DeclarativeServicesAttributeDriven.xml new file mode 100644 index 00000000..64687470 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/DeclarativeServicesAttributeDriven.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/DeclarativeServicesObjectNameDriven.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/DeclarativeServicesObjectNameDriven.xml new file mode 100644 index 00000000..de08c228 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/DeclarativeServicesObjectNameDriven.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/DeclarativeServicesTxProxyFactoryDriven.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/DeclarativeServicesTxProxyFactoryDriven.xml new file mode 100644 index 00000000..9655231b --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/DeclarativeServicesTxProxyFactoryDriven.xml @@ -0,0 +1,40 @@ + + + + + Transactional Proxy for FulfillmentService using the TransactionProxyFactory + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/IntegrationTests/FulfillmentServiceTests.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/IntegrationTests/FulfillmentServiceTests.cs new file mode 100644 index 00000000..696b6a1b --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/IntegrationTests/FulfillmentServiceTests.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +using Spring.Context; +using Spring.Context.Support; + +using Spring.Northwind.Service; + +#endregion + +namespace Spring.Northwind.IntegrationTests +{ + [TestFixture] + public class FulfillmentServiceTests + { + private IApplicationContext ctx; + + [SetUp] + public void SetUp() + { + ctx = ContextRegistry.GetContext(); + } + + /// + /// The transaction proxy is created by direct + /// configuration of a TransactionProxyFactoryObject + /// + [Test,Explicit( "choose DeclarativeServicesTxProxyFactoryDriven.xml in Services.xml before running this test" )] + public void ProcessCustomerViaTxProxyFactoryObject() + { + IFulfillmentService s = ctx["FulfillmentServiceUsingTxPFO"] as IFulfillmentService; + s.ProcessCustomer("BERGS"); + //assertions.... + } + + /// + /// The transaction proxy is created through use of TransactionAttributeSourceAdvisor + /// that identified classes/methods that have the [Transaction] attribute + /// + [Test] + //[Explicit( "choose DeclarativeServicesAttributeDriven.xml in Services.xml before running this test" )] + public void ProcessCustomer() + { + IFulfillmentService s = ctx["FulfillmentService"] as IFulfillmentService; + s.ProcessCustomer("BERGS"); + //assertions.... + } + } +} diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Properties/AssemblyInfo.cs b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..79ca11f6 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Spring.Northwind.IntegrationTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Spring.Northwind.IntegrationTests")] +[assembly: AssemblyCopyright("Copyright © 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8825faf6-aad5-48c4-9319-2d1a5e7f0264")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Services.xml b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Services.xml new file mode 100644 index 00000000..7dca0866 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Services.xml @@ -0,0 +1,29 @@ + + + + + + + The Northwind service layer definitions + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Spring.Northwind.IntegrationTests.csproj b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Spring.Northwind.IntegrationTests.csproj new file mode 100644 index 00000000..8a4c78b7 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test/Spring.Northwind.IntegrationTests/Spring.Northwind.IntegrationTests.csproj @@ -0,0 +1,136 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {2D0C5DF7-3BDB-44FB-BC1B-58E60B170BAD} + Library + Properties + Spring.Northwind + Spring.Northwind.IntegrationTests + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\lib\Net\2.0\antlr.runtime.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\Castle.DynamicProxy.dll + + + False + ..\..\..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Common.Logging.Log4Net.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\Iesi.Collections.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\log4net.dll + + + False + ..\..\..\..\..\lib\NHibernate12\net\2.0\NHibernate.dll + + + False + ..\..\..\..\..\lib\net\2.0\nunit.framework.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Web.dll + + + + + + + + + + + + + + + {53B04C54-63EA-45AA-BB83-8950AF3C5D68} + Spring.Aspects + + + {6E4F55A0-C281-4706-A08B-BDEC2D2FBDA4} + Spring.Northwind.Dao.NHibernate + + + {7F45EEA2-50AC-44E2-85A6-2FFB02E38C44} + Spring.Northwind.Dao + + + {9E15876F-E9E0-43B7-9874-B54F163757D6} + Spring.Northwind.Service + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + rem echo "Copying .xml files for tests" +rem xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2005\Spring.Northwind.IntegrationTests\$(ConfigurationName)\ /y /s /q + + \ No newline at end of file diff --git a/examples/Spring/Spring.Data.NHibernate.Northwind/test_northwind.sql b/examples/Spring/Spring.Data.NHibernate.Northwind/test_northwind.sql new file mode 100644 index 00000000..3ef85aa9 --- /dev/null +++ b/examples/Spring/Spring.Data.NHibernate.Northwind/test_northwind.sql @@ -0,0 +1,64 @@ +/* +This script installs 2 procedures 'BackupNorthwind' and 'RestoreNorthwind' + +These procedures allow for easy backup and restore the Northwind Database during testing. +The backup is done to C:\northwind.bak and also takes care to kill existing connections before doing a restore. + +NOTE: You must ensure, that your MSSQL-Server process has write access to C:\ (or change directory below) +*/ + +USE [master] +GO + +/****** Object: StoredProcedure [dbo].[spdd_DropDatabaseConnections] *****/ +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_DropDatabaseConnections]') AND type in (N'P', N'PC')) +DROP PROCEDURE [dbo].[sp_DropDatabaseConnections] +GO +CREATE proc [dbo].[sp_DropDatabaseConnections] + @dbname sysname +as +begin + declare @spid int + declare @killCmd nvarchar(50) + declare processes cursor for + select spid from master..sysprocesses + where dbid = DB_ID(@dbname) + + OPEN processes + FETCH processes into @spid + WHILE(@@fetch_status=0) BEGIN + set @killCmd = N'KILL '+CAST(@spid as nvarchar(10)) + print N'Executing ''' + @killCmd + '''' + exec sp_executesql @killCmd + FETCH processes into @spid + END + + CLOSE processes + DEALLOCATE processes +end +GO + +/****** Object: StoredProcedure [dbo].[BackupNorthwind] Script Date: 05/25/2007 13:34:44 ******/ +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BackupNorthwind]') AND type in (N'P', N'PC')) +DROP PROCEDURE [dbo].[BackupNorthwind] +GO +create proc [dbo].[BackupNorthwind] +as +begin +BACKUP DATABASE [northwind] + TO DISK = N'C:\northwind.bak' + WITH NAME = N'Northwind Full Database Backup', FORMAT, INIT, SKIP +end +GO + +/****** Object: StoredProcedure [dbo].[RestoreNorthwind] Script Date: 05/25/2007 13:34:44 ******/ +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[RestoreNorthwind]') AND type in (N'P', N'PC')) +DROP PROCEDURE [dbo].[RestoreNorthwind] +GO +CREATE proc [dbo].[RestoreNorthwind] +as +begin +exec dbo.sp_DropDatabaseConnections N'northwind' +RESTORE DATABASE [northwind] FROM DISK = N'C:\northwind.bak' WITH FILE = 1, REPLACE +end +GO diff --git a/examples/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2003.sln b/examples/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2003.sln new file mode 100644 index 00000000..6d4e45a8 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2003.sln @@ -0,0 +1,29 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.DataQuickStart.2003", "src\Spring\Spring.DataQuickStart\Spring.DataQuickStart.2003.csproj", "{4599FD80-29F6-4542-8945-F93F709DFD3F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.DataQuickStart.Tests.2003", "test\Spring\Spring.DataQuickStart.Tests\Spring.DataQuickStart.Tests.2003.csproj", "{9989A239-3406-4E67-B2B5-C8CF78E2311B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {4599FD80-29F6-4542-8945-F93F709DFD3F}.Debug.ActiveCfg = Debug|.NET + {4599FD80-29F6-4542-8945-F93F709DFD3F}.Debug.Build.0 = Debug|.NET + {4599FD80-29F6-4542-8945-F93F709DFD3F}.Release.ActiveCfg = Release|.NET + {4599FD80-29F6-4542-8945-F93F709DFD3F}.Release.Build.0 = Release|.NET + {9989A239-3406-4E67-B2B5-C8CF78E2311B}.Debug.ActiveCfg = Debug|.NET + {9989A239-3406-4E67-B2B5-C8CF78E2311B}.Debug.Build.0 = Debug|.NET + {9989A239-3406-4E67-B2B5-C8CF78E2311B}.Release.ActiveCfg = Release|.NET + {9989A239-3406-4E67-B2B5-C8CF78E2311B}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2005.sln b/examples/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2005.sln new file mode 100644 index 00000000..d9929cab --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2005.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.DataQuickStart.2005", "src\Spring\Spring.DataQuickStart\Spring.DataQuickStart.2005.csproj", "{92B2A60E-D3B9-4647-8CFE-AC19611869F6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.DataQuickStart.Tests.2005", "test\Spring\Spring.DataQuickStart.Tests\Spring.DataQuickStart.Tests.2005.csproj", "{94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {92B2A60E-D3B9-4647-8CFE-AC19611869F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92B2A60E-D3B9-4647-8CFE-AC19611869F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92B2A60E-D3B9-4647-8CFE-AC19611869F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92B2A60E-D3B9-4647-8CFE-AC19611869F6}.Release|Any CPU.Build.0 = Release|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/CommonAssemblyInfo.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/CommonAssemblyInfo.cs new file mode 100644 index 00000000..faffa87e --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/CommonAssemblyInfo.cs @@ -0,0 +1,69 @@ +using System; +using System.Reflection; +[assembly: CLSCompliant(false)] + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// + +#if !NET_2_0 +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +#else +[assembly: AssemblyConfiguration("net-2.0.win32; Release")] +#endif +[assembly: AssemblyCompany("http://www.springframework.net")] +[assembly: AssemblyProduct("Spring.NET Framework")] +[assembly: AssemblyCopyright("Copyright 2002-2006 Spring.NET Framework Team.")] +[assembly: AssemblyTrademark("Apache License, Version 2.0")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// .NET Framework Version +// Major Version +// Minor Version +// Revision +// +// This is as good a convention as any for supporting side-by-side deployment of +// .NET 1.1 and .NET 2.0 versions of the assembly. + +#if !NET_2_0 +[assembly: AssemblyVersion("1.1.0.0")] +#else +[assembly: AssemblyVersion("2.1.0.0")] +#endif + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +#if STRONG +[assembly: AssemblyDelaySign(false)] +#if !NET_2_0 +[assembly: AssemblyKeyFile("Spring.Net.snk")] +#endif +#endif diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/CommandCallbackDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/CommandCallbackDao.cs new file mode 100644 index 00000000..1f1f8696 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/CommandCallbackDao.cs @@ -0,0 +1,111 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data.Common; +using Spring.Data.Generic; + +namespace Spring.DataQuickStart.Dao.GenericTemplate +{ + /// + /// A simple DAO that uses Generic.AdoTemplate CommandCallback functionality + /// + public class CommandCallbackDao : AdoDaoSupport + { + + private string cmdText = "select count(*) from Customers where PostalCode = @PostalCode"; + + /// + /// Finds the number of customers with the given postal code. + /// + /// The postal code. + /// Number of customers with the given postal code. + public virtual int FindCountWithPostalCodeWithDelegate(string postalCode) + { + // Using anonymous delegates allows you to easily reference the + // surrounding parameters for use with the DbCommand processing. + + return AdoTemplate.Execute(delegate(DbCommand command) + { + // Do whatever you like with the DbCommand... downcast to get + // provider specific funtionality if necesary. + + command.CommandText = cmdText; + + DbParameter p = command.CreateParameter(); + p.ParameterName = "@PostalCode"; + p.Value = postalCode; + command.Parameters.Add(p); + + return (int)command.ExecuteScalar(); + + }); + + } + + + + public virtual int FindCountWithPostalCode(string postalCode) + { + // Type inference allows you not to explicitly write .Execute + + return AdoTemplate.Execute(new PostalCodeCommandCallback(postalCode)).count; + } + + // Note that you can not use System.Array, System.Delegate, System.Enum, + // System.ValueType or object in the constraint. for this example that returns just + // an integer, a simple 'ResultObject' type is used. + // You code would generally have a return object more complex than an 'int'. + + /// + /// A callback implementation that returns a 'ResultObject' containing a the count + /// of customers with a specific zip code. + /// + /// + private class PostalCodeCommandCallback : ICommandCallback where T : ResultObject, new() + { + private string postalCode; + public PostalCodeCommandCallback(string postalCode) + { + this.postalCode = postalCode; + } + + public T DoInCommand(DbCommand command) + { + T resultObject = new T(); + DbParameter p = command.CreateParameter(); + p.ParameterName = "@PostalCode"; + p.Value = postalCode; + command.Parameters.Add(p); + + resultObject.count = (int)command.ExecuteScalar(); + return resultObject; + } + } + + } + + /// + /// Helper class for returning simple integer value via generic class. + /// + public class ResultObject + { + public int count; + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/CustOrdersDetailStoredProc.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/CustOrdersDetailStoredProc.cs new file mode 100644 index 00000000..97ccd064 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/CustOrdersDetailStoredProc.cs @@ -0,0 +1,28 @@ +using System.Collections; +using System.Collections.Generic; +using Spring.Data.Common; +using Spring.Data.Objects.Generic; +using Spring.DataQuickStart.Domain; + +namespace Spring.DataQuickStart.Dao.GenericTemplate +{ + public class CustOrdersDetailStoredProc : StoredProcedure + { + private static string procedureName = "CustOrdersDetail"; + + public CustOrdersDetailStoredProc(IDbProvider dbProvider) : base(dbProvider, procedureName) + { + DeriveParameters(); + AddRowMapper("orderDetailRowMapper", new OrderDetailRowMapper() ); + Compile(); + } + + public virtual List GetOrderDetails(int orderid) + { + + IDictionary outParams = Query(orderid); + return outParams["orderDetailRowMapper"] as List; + } + + } +} diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/CustomerRowMapper.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/CustomerRowMapper.cs new file mode 100644 index 00000000..c4dee18e --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/CustomerRowMapper.cs @@ -0,0 +1,55 @@ +#region Licence + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; +using Spring.Data.Generic; +using Spring.DataQuickStart.Domain; + +namespace Spring.DataQuickStart.Dao.GenericTemplate +{ + public class CustomerRowMapper : IRowMapper where T : Customer, new() + { + /// + /// Map a row of data to a Customer object. + /// + /// This method should not call Next() on the + /// DataReader; it should only extract the values of the current row. + /// + /// The IDataReader to map + /// the number of the current row. + /// A Customer object. + public T MapRow(IDataReader dataReader, int rowNum) + { + T customer = new T(); + customer.Address = dataReader.GetString(0); + customer.City = dataReader.GetString(1); + customer.CompanyName = dataReader.GetString(2); + customer.ContactName = dataReader.GetString(3); + customer.ContactTitle = dataReader.GetString(4); + customer.Country = dataReader.GetString(5); + customer.Fax = dataReader.GetString(6); + customer.Id = dataReader.GetString(7); + customer.Phone = dataReader.GetString(8); + customer.PostalCode = dataReader.GetString(9); + customer.Region = dataReader.GetString(10); + return customer; + } + } +} diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/OrderDetailRowMapper.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/OrderDetailRowMapper.cs new file mode 100644 index 00000000..a57b9e8c --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/OrderDetailRowMapper.cs @@ -0,0 +1,54 @@ +#region Licence + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; +using System.Data.SqlClient; +using Spring.Data.Generic; +using Spring.DataQuickStart.Domain; + +namespace Spring.DataQuickStart.Dao.GenericTemplate +{ + public class OrderDetailRowMapper : IRowMapper where T : OrderDetails, new() + { + /// + /// Map a row of data to a OrderDetails object. + /// + /// This method should not call Next() on the + /// DataReader; it should only extract the values of the current row. + /// + /// The IDataReader to map + /// the number of the current row. + /// A Customer object. + public T MapRow(IDataReader dataReader, int rowNum) + { + SqlDataReader dr = dataReader as SqlDataReader; + + T ods = new T(); + + ods.ProductName = dr.GetString(0); + ods.UnitPrice = dr.GetSqlMoney(1).ToDouble(); + ods.Quantity = dr.GetInt16(2); + ods.Discount = dr.GetInt32(3); + ods.ExtendedPrice = dr.GetSqlMoney(4).ToDouble(); + + return ods; + } + } +} diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/QueryForObjectDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/QueryForObjectDao.cs new file mode 100644 index 00000000..953fe06a --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/QueryForObjectDao.cs @@ -0,0 +1,20 @@ +using System.Data; +using Spring.Data.Generic; +using Spring.DataQuickStart.Domain; + +namespace Spring.DataQuickStart.Dao.GenericTemplate +{ + public class QueryForObjectDao : AdoDaoSupport + { + private string cmdText = "select Address, City, CompanyName, ContactName, " + + "ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " + + "Region from Customers where ContactName = @ContactName"; + + public Customer GetCustomer(string contactName) + { + return AdoTemplate.QueryForObject(CommandType.Text, cmdText, + new CustomerRowMapper(), + "ContactName", DbType.String, 30, contactName); + } + } +} diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/ResultSetExtractorDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/ResultSetExtractorDao.cs new file mode 100644 index 00000000..3244e6c1 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/ResultSetExtractorDao.cs @@ -0,0 +1,280 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections.Generic; +using System.Data; +using Spring.Data; +using Spring.Data.Common; +using Spring.Data.Generic; + +namespace Spring.DataQuickStart.Dao.GenericTemplate +{ + /// + /// A simple DAO that uses Generic.AdoTemplate ResultSetExtractor functionality + /// + public class ResultSetExtractorDao : AdoDaoSupport + { + + private string postalCodeCustomerMappingCommandText = "select ContactName, PostalCode from Customers"; + + private string customerByCountry = + @"select ContactName from Customers where Country = @Country"; + + private string customerByCountryAndCityCommandText = + @"select ContactName from Customers where City = @City and Country = @Country"; + + + + /// + /// Gets a 'multi-map' of postal code, customer names. + /// + /// A multi-map of where the key is a postal code and the + /// value a list of customer names + public virtual IDictionary> GetPostalCodeCustomerMapping() + { + return AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, postalCodeCustomerMappingCommandText, + new PostalDictionaryResultSetExtractor>>() + ); + } + + + public virtual IList GetCustomerNameByCountry(string country) + { + return AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, + customerByCountry, + new CustomerNameResultSetExtractor>(), + "Country", DbType.String, 15, "Brazil"); + + } + + public virtual IList GetCustomerNameByCountryAndCity(string country, string city) + { + // note no need to use parameter prefix. + + // This allows the SQL to be changed via external configuration but the parameter setting code + // can remain the same if no provider specific DbType enumerations are used. + + IDbParameters parameters = CreateDbParameters(); + parameters.AddWithValue("Country", country).DbType = DbType.String; + parameters.Add("City", DbType.String).Value = city; + return AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, + customerByCountryAndCityCommandText, + new CustomerNameResultSetExtractor>(), + parameters); + } + + public virtual IList GetCustomerNameByCountryAndCityWithParamsBuilder(string country, string city) + { + // note no need to use parameter prefix. + + // This allows the SQL to be changed via external configuration but the parameter setting code + // can remain the same if no provider specific DbType enumerations are used. + + + IDbParametersBuilder builder = CreateDbParametersBuilder(); + builder.Create().Name("Country").Type(DbType.String).Size(15).Value(country); + builder.Create().Name("City").Type(DbType.String).Size(15).Value(city); + return AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, + customerByCountryAndCityCommandText, + new CustomerNameResultSetExtractor>(), + builder.GetParameters()); + } + + public virtual IList GetCustomerNameByCountryAndCityWithCommandSetterDelegate(string country, string city) + { + //using anonymous delegate allows for easy access to 'outer' calling parameter values. + //but is not reusable across objects/methods as with the interface based ICommandSetter alternative. + + return AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, + customerByCountryAndCityCommandText, + new CustomerNameResultSetExtractor>(), + delegate(IDbCommand dbCommand) + { + //Down cast to SqlCommand or other provider to access provider specific functionality.... + //In this case just use the general IDbCommand API. + IDbDataParameter countryParameter = dbCommand.CreateParameter(); + countryParameter.ParameterName = "@Country"; + countryParameter.DbType = DbType.String; + countryParameter.Value = country; + + IDbDataParameter cityParameter = dbCommand.CreateParameter(); + cityParameter.ParameterName = "@City"; + cityParameter.DbType = DbType.String; + cityParameter.Value = city; + + dbCommand.Parameters.Add(countryParameter); + dbCommand.Parameters.Add(cityParameter); + }); + } + + public virtual IList GetCustomerNameByCountryAndCityWithAllDelegates(string country, string city) + { + IDbParametersBuilder builder = CreateDbParametersBuilder(); + builder.Create().Name("Country").Type(DbType.String).Size(15).Value(country); + builder.Create().Name("City").Type(DbType.String).Size(15).Value(city); + + ResultSetExtractorDelegate> extractorDelegate = delegate(IDataReader reader) + { + IList customerList = new List(); + while (reader.Read()) + { + string contactName = reader.GetString(0); + customerList.Add(contactName); + } + return customerList; + }; + CommandSetterDelegate commandSetterDelegate = delegate(IDbCommand dbCommand) + { + //Down cast to SqlCommand or other provider to access provider specific functionality.... + //In this case just use the general IDbCommand API. + IDbDataParameter countryParameter = + dbCommand.CreateParameter(); + countryParameter.ParameterName = "@Country"; + countryParameter.DbType = DbType.String; + countryParameter.Value = country; + + IDbDataParameter cityParameter = + dbCommand.CreateParameter(); + cityParameter.ParameterName = "@City"; + cityParameter.DbType = DbType.String; + cityParameter.Value = city; + + dbCommand.Parameters.Add(countryParameter); + dbCommand.Parameters.Add(cityParameter); + }; + + + //TODO compiler can't determine matching method if two anonymous methods are used within method parameters. + // how to 'cast' the anonymous delegate? + return AdoTemplate.QueryWithResultSetExtractorDelegate(CommandType.Text, + customerByCountryAndCityCommandText, + extractorDelegate, + commandSetterDelegate); + } + + + public virtual IList GetCustomerNameByCountryAndCityWithCommandSetter(string country, string city) + { + return AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, + customerByCountryAndCityCommandText, + new CustomerNameResultSetExtractor>(), + new CustomerByCountryCityCommandSetter(country, city)); + } + + /// + /// Gets or sets the command text used to get a multi-map of postal codes/customer names. + /// + /// Can be set in configuration to use vendor specific SQL. + /// The command text. + public string PostalCodeCustomerMappingCommandText + { + get { return postalCodeCustomerMappingCommandText; } + set { postalCodeCustomerMappingCommandText = value; } + } + } + + internal class CustomerByCountryCityCommandSetter : ICommandSetter + { + private string country, city; + + public CustomerByCountryCityCommandSetter(string country, string city) + { + this.country = country; + this.city = city; + } + + public void SetValues(IDbCommand dbCommand) + { + + //Down cast to SqlCommand or other provider to access provider specific functionality.... + //In this case just use the general IDbCommand API. + IDbDataParameter countryParameter = dbCommand.CreateParameter(); + countryParameter.ParameterName = "@Country"; + countryParameter.DbType = DbType.String; + countryParameter.Value = country; + + IDbDataParameter cityParameter = dbCommand.CreateParameter(); + cityParameter.ParameterName = "@City"; + cityParameter.DbType = DbType.String; + cityParameter.Value = city; + + dbCommand.Parameters.Add(countryParameter); + dbCommand.Parameters.Add(cityParameter); + } + } + + internal class CustomerNameResultSetExtractor : IResultSetExtractor where T : IList, new() + { + /// + /// Implementations must implement this method to process all + /// result set and rows in the IDataReader. + /// + /// The IDataReader to extract data from. + /// Implementations should not close this: it will be closed + /// by the AdoTemplate. + /// An arbitrary result object or null if none. The + /// extractor will typically be stateful in the latter case. + public T ExtractData(IDataReader reader) + { + T customerList = new T(); + while (reader.Read()) + { + string contactName = reader.GetString(0); + customerList.Add(contactName); + } + return customerList; + } + } + + internal class PostalDictionaryResultSetExtractor : IResultSetExtractor where T : IDictionary>, new() + { + + /// + /// Implementations must implement this method to process all + /// result set and rows in the IDataReader. + /// + /// The IDataReader to extract data from. + /// Implementations should not close this: it will be closed + /// by the AdoTemplate. + /// An arbitrary result object or null if none. The + /// xtractor will typically be stateful in the latter case. + public T ExtractData(IDataReader reader) + { + T resultDictionary = new T(); + while(reader.Read()) + { + string contactName = reader.GetString(0); + string postalCode = reader.GetString(1); + IList contactNameList; + if (resultDictionary.ContainsKey(postalCode)) + { + contactNameList = resultDictionary[postalCode]; + } + else + { + resultDictionary.Add(postalCode, contactNameList = new List()); + } + contactNameList.Add(contactName); + } + return resultDictionary; + + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/RowCallbackDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/RowCallbackDao.cs new file mode 100644 index 00000000..5f95d607 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/RowCallbackDao.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.Data; +using Spring.Data; +using Spring.Data.Generic; + +namespace Spring.DataQuickStart.Dao.GenericTemplate +{ + public class RowCallbackDao : AdoDaoSupport + { + private string cmdText = "select ContactName, PostalCode from Customers"; + + public virtual IDictionary> GetPostalCodeCustomerMapping() + { + PostalCodeRowCallback statefullCallback = new PostalCodeRowCallback(); + AdoTemplate.QueryWithRowCallback(CommandType.Text, cmdText, + statefullCallback); + + // Do something with results in stateful callback... + return statefullCallback.PostalCodeMultimap; + } + + + } + + internal class PostalCodeRowCallback : IRowCallback + { + private IDictionary> postalCodeMultimap = + new Dictionary>(); + + public IDictionary> PostalCodeMultimap + { + get { return postalCodeMultimap; } + } + + /// + /// Implementations must implement this method to process each row of data + /// in the data reader. + /// + /// + /// This method should not advance the cursor by calling Read() + /// on IDataReader but only extract the current values. The + /// caller does not need to care about closing the reader, command, connection, or + /// about handling transactions: this will all be handled by + /// Spring's AdoTemplate + /// + /// An active IDataReader instance + /// The result object + public void ProcessRow(IDataReader reader) + { + string contactName = reader.GetString(0); + string postalCode = reader.GetString(1); + IList contactNameList; + if (postalCodeMultimap.ContainsKey(postalCode)) + { + contactNameList = postalCodeMultimap[postalCode]; + } + else + { + postalCodeMultimap.Add(postalCode, contactNameList = new List()); + } + contactNameList.Add(contactName); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/RowMapperDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/RowMapperDao.cs new file mode 100644 index 00000000..ccbad871 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/GenericTemplate/RowMapperDao.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Data; +using Spring.Data.Generic; +using Spring.DataQuickStart.Domain; +//TODO move 'classic' AdoDaoSupport under Spring.Data to avoid clashes... + +namespace Spring.DataQuickStart.Dao.GenericTemplate +{ + /// + /// A simple DAO that uses Generic.AdoTemplate RowMapper functionality + /// + public class RowMapperDao : AdoDaoSupport + { + private string cmdText = "select Address, City, CompanyName, ContactName, " + + "ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " + + "Region from Customers"; + + + /// + /// Gets the customers. + /// + /// A list of customers + + + /// + /// Gets the customers using a row mapper delegate. + /// + /// Note that a NullMappingDataReader is used to convert + /// DbNull values to simple defaults (i.e. empty string) + /// when getting data from the DataReader. You can provide AdoTemplate + /// with your own version or not use a dataReader 'wrapper' entirely. + /// + /// A list of customers + public virtual IList GetCustomersWithDelegate() + { + return AdoTemplate.QueryWithRowMapperDelegate(CommandType.Text, cmdText, + delegate(IDataReader dataReader, int rowNum) + { + Customer customer = new Customer(); + customer.Address = dataReader.GetString(0); + customer.City = dataReader.GetString(1); + customer.CompanyName = dataReader.GetString(2); + customer.ContactName = dataReader.GetString(3); + customer.ContactTitle = dataReader.GetString(4); + customer.Country = dataReader.GetString(5); + customer.Fax = dataReader.GetString(6); + customer.Id = dataReader.GetString(7); + customer.Phone = dataReader.GetString(8); + customer.PostalCode = dataReader.GetString(9); + customer.Region = dataReader.GetString(10); + return customer; + }); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CommandCallbackDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CommandCallbackDao.cs new file mode 100644 index 00000000..899dae0e --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CommandCallbackDao.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; +using Spring.Data; +using Spring.Data.Core; + +namespace Spring.DataQuickStart.Dao.Template +{ + /// + /// A simple DAO that uses Generic.AdoTemplate CommandCallback functionality + /// + public class CommandCallbackDao : AdoDaoSupport + { + + + /// + /// Finds the number of customers with the given postal code. + /// + /// The postal code. + /// Number of customers with the given postal code. + public virtual int FindCountWithPostalCode(string postalCode) + { + return (int) AdoTemplate.Execute(new PostalCodeCommandCallback(postalCode)); + } + + // Note that you can not use System.Array, System.Delegate, System.Enum, + // System.ValueType or object in the constraint. for this example that returns just + // an integer, a simple 'ResultObject' type is used. + // You code would generally have a return object more complex than an 'int'. + + /// + /// A callback implementation that returns a 'ResultObject' containing a the count + /// of customers with a specific zip code. + /// + /// + public class PostalCodeCommandCallback : ICommandCallback + { + private string cmdText = "select count(*) from Customers where PostalCode = @PostalCode"; + + private string postalCode; + + public PostalCodeCommandCallback(string postalCode) + { + this.postalCode = postalCode; + } + + public object DoInCommand(IDbCommand command) + { + command.CommandText = cmdText; + + IDbDataParameter p = command.CreateParameter(); + p.ParameterName = "@PostalCode"; + p.Value = postalCode; + command.Parameters.Add(p); + + return command.ExecuteScalar(); + + } + + + } + + } + +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CustOrdersDetailStoredProc.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CustOrdersDetailStoredProc.cs new file mode 100644 index 00000000..ce585c63 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CustOrdersDetailStoredProc.cs @@ -0,0 +1,26 @@ +using System.Collections; +using Spring.Data.Common; +using Spring.Data.Objects; + +namespace Spring.DataQuickStart.Dao.Template +{ + public class CustOrdersDetailStoredProc : StoredProcedure + { + private static string procedureName = "CustOrdersDetail"; + + public CustOrdersDetailStoredProc(IDbProvider dbProvider) : base(dbProvider, procedureName) + { + DeriveParameters(); + AddRowMapper("orderDetailRowMapper", new OrderDetailRowMapper() ); + Compile(); + } + + public virtual IList GetOrderDetails(int orderid) + { + + IDictionary outParams = Query(orderid); + return outParams["orderDetailRowMapper"] as IList; + } + + } +} diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CustomerDataSetDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CustomerDataSetDao.cs new file mode 100644 index 00000000..b2f30305 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CustomerDataSetDao.cs @@ -0,0 +1,68 @@ + + +using System; +using System.Data; +using Spring.Data.Common; +using Spring.Data.Core; + +namespace Spring.DataQuickStart.Dao.Template +{ + public class CustomerDataSetDao : AdoDaoSupport + { + private string selectAll = @"select Address, City, CompanyName, ContactName, " + + "ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " + + "Region from Customers"; + + private string selectLike = @"select Address, City, CompanyName, ContactName, " + + "ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " + + "Region from Customers where ContactName like @ContactName"; + + public DataSet GetCustomers() + { + return AdoTemplate.DataSetCreate(CommandType.Text, selectAll); + } + + public DataSet GetCustomers(string dataSetName) + { + return AdoTemplate.DataSetCreate(CommandType.Text, selectAll, new string[] { dataSetName }); + } + + public DataSet GetCustomersLike(string contactName) + { + + IDbParameters dbParameters = CreateDbParameters(); + dbParameters.Add("ContactName", DbType.String).Value = contactName; + return AdoTemplate.DataSetCreateWithParams(CommandType.Text, selectLike, dbParameters); + } + + public void FillCustomers(DataSet dataSet) + { + AdoTemplate.DataSetFill(dataSet, CommandType.Text, selectAll, new string[] { "Customers"} ); + } + + public void UpdateWithCommandBuilder(DataSet dataSet) + { + AdoTemplate.DataSetUpdateWithCommandBuilder(dataSet, CommandType.Text, selectAll, null, "Customers"); + } + + + public void Update(DataSet dataSet) + { + string insertSql = @"INSERT Customers (CustomerID, CompanyName) VALUES (@CustomerId, @CompanyName)"; + IDbParameters insertParams = CreateDbParameters(); + insertParams.Add("CustomerId", DbType.String, 0, "CustomerId"); //.Value = "NewID"; + insertParams.Add("CompanyName", DbType.String, 0, "CompanyName"); //.Value = "New Company Name"; + + string updateSql = @"update Customers SET Phone=@Phone where CustomerId = @CustomerId"; + IDbParameters updateParams = CreateDbParameters(); + updateParams.Add("Phone", DbType.String, 0, "Phone");//.Value = "030-0074322"; // simple change, last digit changed from 1 to 2. + updateParams.Add("CustomerId", DbType.String, 0, "CustomerId");//.Value = "ALFKI"; + + AdoTemplate.DataSetUpdate(dataSet, "Customers", + CommandType.Text, insertSql, insertParams, + CommandType.Text, updateSql, updateParams, + CommandType.Text, null , null); + + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CustomerRowMapper.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CustomerRowMapper.cs new file mode 100644 index 00000000..e905ca3f --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/CustomerRowMapper.cs @@ -0,0 +1,55 @@ +#region Licence + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; +using Spring.Data; +using Spring.DataQuickStart.Domain; + +namespace Spring.DataQuickStart.Dao.Template +{ + public class CustomerRowMapper : IRowMapper + { + /// + /// Map a row of data to a Customer object. + /// + /// This method should not call Next() on the + /// DataReader; it should only extract the values of the current row. + /// + /// The IDataReader to map + /// the number of the current row. + /// A Customer object. + public object MapRow(IDataReader dataReader, int rowNum) + { + Customer customer = new Customer(); + customer.Address = dataReader.GetString(0); + customer.City = dataReader.GetString(1); + customer.CompanyName = dataReader.GetString(2); + customer.ContactName = dataReader.GetString(3); + customer.ContactTitle = dataReader.GetString(4); + customer.Country = dataReader.GetString(5); + customer.Fax = dataReader.GetString(6); + customer.Id = dataReader.GetString(7); + customer.Phone = dataReader.GetString(8); + customer.PostalCode = dataReader.GetString(9); + customer.Region = dataReader.GetString(10); + return customer; + } + } +} diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/OrderDetailRowMapper.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/OrderDetailRowMapper.cs new file mode 100644 index 00000000..059f62c5 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/OrderDetailRowMapper.cs @@ -0,0 +1,54 @@ +#region Licence + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; +using System.Data.SqlClient; +using Spring.Data; +using Spring.DataQuickStart.Domain; + +namespace Spring.DataQuickStart.Dao.Template +{ + public class OrderDetailRowMapper : IRowMapper + { + /// + /// Map a row of data to a OrderDetails object. + /// + /// This method should not call Next() on the + /// DataReader; it should only extract the values of the current row. + /// + /// The IDataReader to map + /// the number of the current row. + /// A Customer object. + public object MapRow(IDataReader dataReader, int rowNum) + { + SqlDataReader dr = dataReader as SqlDataReader; + + OrderDetails ods = new OrderDetails(); + + ods.ProductName = dr.GetString(0); + ods.UnitPrice = dr.GetSqlMoney(1).ToDouble(); + ods.Quantity = dr.GetInt16(2); + ods.Discount = dr.GetInt32(3); + ods.ExtendedPrice = dr.GetSqlMoney(4).ToDouble(); + + return ods; + } + } +} diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/QueryForObjectDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/QueryForObjectDao.cs new file mode 100644 index 00000000..8f6b4efe --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/QueryForObjectDao.cs @@ -0,0 +1,19 @@ +using System.Data; +using Spring.Data.Core; +using Spring.DataQuickStart.Domain; + +namespace Spring.DataQuickStart.Dao.Template +{ + public class QueryForObjectDao : AdoDaoSupport + { + private string cmdText = @"select Address, City, CompanyName, ContactName, " + + "ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " + + "Region from Customers where ContactName = @ContactName"; + + public Customer GetCustomer(string contactName) + { + return (Customer)AdoTemplate.QueryForObject(CommandType.Text, cmdText, new CustomerRowMapper(), + "ContactName", DbType.String, 30, contactName); + } + } +} diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/ResultSetExtractorDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/ResultSetExtractorDao.cs new file mode 100644 index 00000000..fe509db5 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/ResultSetExtractorDao.cs @@ -0,0 +1,209 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Data; +using Spring.Data; +using Spring.Data.Common; +using Spring.Data.Core; + +namespace Spring.DataQuickStart.Dao.Template +{ + /// + /// A simple DAO that uses Generic.AdoTemplate ResultSetExtractor functionality + /// + public class ResultSetExtractorDao : AdoDaoSupport + { + + private string postalCodeCustomerMappingCommandText = "select ContactName, PostalCode from Customers"; + + private string customerByCountry = + @"select ContactName from Customers where Country = @Country"; + + private string customerByCountryAndCityCommandText = + @"select ContactName from Customers where City = @City and Country = @Country"; + + + + /// + /// Gets a 'multi-map' of postal code, customer names. + /// + /// A multi-map of where the key is a postal code and the + /// value a list of customer names + public virtual IDictionary GetPostalCodeCustomerMapping() + { + return (IDictionary)AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, postalCodeCustomerMappingCommandText, + new PostalDictionaryResultSetExtractor() + ); + } + + + public virtual IList GetCustomerNameByCountry(string country) + { + return (IList)AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, + customerByCountry, + new CustomerNameResultSetExtractor(), + "Country", DbType.String, 15, "Brazil"); + + } + + public virtual IList GetCustomerNameByCountryAndCity(string country, string city) + { + // note no need to use parameter prefix. + + // This allows the SQL to be changed via external configuration but the parameter setting code + // can remain the same if no provider specific DbType enumerations are used. + + IDbParameters parameters = CreateDbParameters(); + parameters.AddWithValue("Country", country).DbType = DbType.String; + parameters.Add("City", DbType.String).Value = city; + return (IList)AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, + customerByCountryAndCityCommandText, + new CustomerNameResultSetExtractor(), + parameters); + } + + public virtual IList GetCustomerNameByCountryAndCityWithParamsBuilder(string country, string city) + { + // note no need to use parameter prefix. + + // This allows the SQL to be changed via external configuration but the parameter setting code + // can remain the same if no provider specific DbType enumerations are used. + + + IDbParametersBuilder builder = CreateDbParametersBuilder(); + builder.Create().Name("Country").Type(DbType.String).Size(15).Value(country); + builder.Create().Name("City").Type(DbType.String).Size(15).Value(city); + return (IList)AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, + customerByCountryAndCityCommandText, + new CustomerNameResultSetExtractor(), + builder.GetParameters()); + } + + + + public virtual IList GetCustomerNameByCountryAndCityWithCommandSetter(string country, string city) + { + return (IList)AdoTemplate.QueryWithResultSetExtractor(CommandType.Text, + customerByCountryAndCityCommandText, + new CustomerNameResultSetExtractor(), + new CustomerByCountryCityCommandSetter(country, city)); + } + + /// + /// Gets or sets the command text used to get a multi-map of postal codes/customer names. + /// + /// Can be set in configuration to use vendor specific SQL. + /// The command text. + public string PostalCodeCustomerMappingCommandText + { + get { return postalCodeCustomerMappingCommandText; } + set { postalCodeCustomerMappingCommandText = value; } + } + } + + internal class CustomerByCountryCityCommandSetter : ICommandSetter + { + private string country, city; + + public CustomerByCountryCityCommandSetter(string country, string city) + { + this.country = country; + this.city = city; + } + + public void SetValues(IDbCommand dbCommand) + { + + //Down cast to SqlCommand or other provider to access provider specific functionality.... + //In this case just use the general IDbCommand API. + IDbDataParameter countryParameter = dbCommand.CreateParameter(); + countryParameter.ParameterName = "@Country"; + countryParameter.DbType = DbType.String; + countryParameter.Value = country; + + IDbDataParameter cityParameter = dbCommand.CreateParameter(); + cityParameter.ParameterName = "@City"; + cityParameter.DbType = DbType.String; + cityParameter.Value = city; + + dbCommand.Parameters.Add(countryParameter); + dbCommand.Parameters.Add(cityParameter); + } + } + + internal class CustomerNameResultSetExtractor : IResultSetExtractor + { + /// + /// Implementations must implement this method to process all + /// result set and rows in the IDataReader. + /// + /// The IDataReader to extract data from. + /// Implementations should not close this: it will be closed + /// by the AdoTemplate. + /// An arbitrary result object or null if none. The + /// extractor will typically be stateful in the latter case. + public object ExtractData(IDataReader reader) + { + ArrayList customerList = new ArrayList(); + while (reader.Read()) + { + string contactName = reader.GetString(0); + customerList.Add(contactName); + } + return customerList; + } + } + + internal class PostalDictionaryResultSetExtractor : IResultSetExtractor + { + + /// + /// Implementations must implement this method to process all + /// result set and rows in the IDataReader. + /// + /// The IDataReader to extract data from. + /// Implementations should not close this: it will be closed + /// by the AdoTemplate. + /// An arbitrary result object or null if none. The + /// xtractor will typically be stateful in the latter case. + public object ExtractData(IDataReader reader) + { + IDictionary resultDictionary = new Hashtable(); + while(reader.Read()) + { + string contactName = reader.GetString(0); + string postalCode = reader.GetString(1); + IList contactNameList; + if (resultDictionary.Contains(postalCode)) + { + contactNameList = resultDictionary[postalCode] as IList; + } + else + { + resultDictionary.Add(postalCode, contactNameList = new ArrayList()); + } + contactNameList.Add(contactName); + } + return resultDictionary; + + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/RowCallbackDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/RowCallbackDao.cs new file mode 100644 index 00000000..39cc131a --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/RowCallbackDao.cs @@ -0,0 +1,63 @@ +using System.Collections; +using System.Data; +using Spring.Data; +using Spring.Data.Core; + +namespace Spring.DataQuickStart.Dao.Template +{ + public class RowCallbackDao : AdoDaoSupport + { + private string cmdText = "select ContactName, PostalCode from Customers"; + + public virtual IDictionary GetPostalCodeCustomerMapping() + { + PostalCodeRowCallback statefullCallback = new PostalCodeRowCallback(); + AdoTemplate.QueryWithRowCallback(CommandType.Text, cmdText, + statefullCallback); + + // Do something with results in stateful callback... + return statefullCallback.PostalCodeMultimap; + } + + + } + + internal class PostalCodeRowCallback : IRowCallback + { + private IDictionary postalCodeMultimap = new Hashtable(); + + public IDictionary PostalCodeMultimap + { + get { return postalCodeMultimap; } + } + + /// + /// Implementations must implement this method to process each row of data + /// in the data reader. + /// + /// + /// This method should not advance the cursor by calling Read() + /// on IDataReader but only extract the current values. The + /// caller does not need to care about closing the reader, command, connection, or + /// about handling transactions: this will all be handled by + /// Spring's AdoTemplate + /// + /// An active IDataReader instance + /// The result object + public void ProcessRow(IDataReader reader) + { + string contactName = reader.GetString(0); + string postalCode = reader.GetString(1); + IList contactNameList; + if (postalCodeMultimap.Contains(postalCode)) + { + contactNameList = postalCodeMultimap[postalCode] as IList; + } + else + { + postalCodeMultimap.Add(postalCode, contactNameList = new ArrayList()); + } + contactNameList.Add(contactName); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/RowMapperDao.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/RowMapperDao.cs new file mode 100644 index 00000000..5ccd7848 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Dao/Template/RowMapperDao.cs @@ -0,0 +1,28 @@ +using System.Collections; +using System.Data; +using Spring.Data.Core; + +namespace Spring.DataQuickStart.Dao.Template +{ + /// + /// A simple DAO that uses Generic.AdoTemplate RowMapper functionality + /// + public class RowMapperDao : AdoDaoSupport + { + private string cmdText = "select Address, City, CompanyName, ContactName, " + + "ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " + + "Region from Customers"; + + + /// + /// Gets the customers. + /// + /// A list of customers + public virtual IList GetCustomers() + { + return AdoTemplate.QueryWithRowMapper(CommandType.Text, cmdText, + new CustomerRowMapper()); + } + + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Domain/Customer.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Domain/Customer.cs new file mode 100644 index 00000000..ba92cb52 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Domain/Customer.cs @@ -0,0 +1,150 @@ +#region Licence + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.DataQuickStart.Domain +{ + public class Customer + { + #region Fields + + protected string id; + protected string companyName; + protected string contactName; + protected string contactTitle; + protected string address; + protected string city; + protected string region; + protected string postalCode; + protected string country; + protected string phone; + protected string fax; + protected IList orders; + + #endregion + + #region Properties + + public string Id + { + get { return id; } + set { id = value; } + } + + public string CompanyName + { + get { return companyName; } + set { companyName = value; } + } + + public string ContactName + { + get { return contactName; } + set { contactName = value; } + } + + public string ContactTitle + { + get { return contactTitle; } + set { contactTitle = value; } + } + + public string Address + { + get { return address; } + set { address = value; } + } + + public string City + { + get { return city; } + set { city = value; } + } + + public string Region + { + get { return region; } + set { region = value; } + } + + public string PostalCode + { + get { return postalCode; } + set { postalCode = value; } + } + + public string Country + { + get { return country; } + set { country = value; } + } + + public string Phone + { + get { return phone; } + set { phone = value; } + } + + public string Fax + { + get { return fax; } + set { fax = value; } + } + + public IList Orders + { + get + { + if (orders == null) + { + orders = new ArrayList(); + } + return orders; + } + set { orders = value; } + } + + #endregion + + #region Constructor (s) + + public Customer() + { + } + + public Customer(string companyName, string contactName, string contactTitle, string address, string city, + string region, string postalCode, string country, string phone, string fax) + { + this.companyName = companyName; + this.contactName = contactName; + this.contactTitle = contactTitle; + this.address = address; + this.city = city; + this.region = region; + this.postalCode = postalCode; + this.country = country; + this.phone = phone; + this.fax = fax; + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Domain/OrderDetails.cs b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Domain/OrderDetails.cs new file mode 100644 index 00000000..11197bb2 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/DataQuickStart/Domain/OrderDetails.cs @@ -0,0 +1,46 @@ + +namespace Spring.DataQuickStart.Domain +{ + /// + /// Class encapsulating OrderDetail information suitable for + /// use with the stored procedure CustOrdersDetail + /// + public class OrderDetails + { + private string productName; + private double unitPrice; + private int quantity; + private int discount; + private double extendedPrice; + + public string ProductName + { + get { return productName; } + set { productName = value; } + } + + public double UnitPrice + { + get { return unitPrice; } + set { unitPrice = value; } + } + + public int Quantity + { + get { return quantity; } + set { quantity = value; } + } + + public int Discount + { + get { return discount; } + set { discount = value; } + } + + public double ExtendedPrice + { + get { return extendedPrice; } + set { extendedPrice = value; } + } + } +} diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2003.csproj b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2003.csproj new file mode 100644 index 00000000..b9bc3476 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2003.csproj @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2005.csproj b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2005.csproj new file mode 100644 index 00000000..fe3cc1fc --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/src/Spring/Spring.DataQuickStart/Spring.DataQuickStart.2005.csproj @@ -0,0 +1,78 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {92B2A60E-D3B9-4647-8CFE-AC19611869F6} + Library + Properties + Spring + Spring.DataQuickStart + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Common.Logging.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/AssemblyInfo.cs b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..177a4f0e --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/GenericTemplate/ExampleTests.cs b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/GenericTemplate/ExampleTests.cs new file mode 100644 index 00000000..7f2acdb8 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/GenericTemplate/ExampleTests.cs @@ -0,0 +1,110 @@ +using System.Collections.Generic; + +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Config; +using Spring.Objects.Factory.Xml; + +using Spring.DataQuickStart.Dao.GenericTemplate; +using Spring.DataQuickStart.Domain; + +namespace Spring.DataQuickStart.GenericTemplate +{ + [TestFixture] + public class ExampleTests + { + private IApplicationContext ctx; + + [SetUp] + public void InitContext() + { + // Configure Spring programmatically + NamespaceParserRegistry.RegisterParser(typeof(DatabaseNamespaceParser)); + ctx = new XmlApplicationContext( + "assembly://Spring.DataQuickStart.Tests/Spring.DataQuickStart.GenericTemplate/ExampleTests.xml"); + } + + + [Test] + public void CallbackDaoTest() + { + CommandCallbackDao commandCallbackDao = ctx["commandCallbackDao"] as CommandCallbackDao; + int count = commandCallbackDao.FindCountWithPostalCodeWithDelegate("1010"); + Assert.AreEqual(3, count); + } + + [Test] + public void ResultSetExtractorDaoTest() + { + ResultSetExtractorDao dao = ctx["resultSetExtractorDao"] as ResultSetExtractorDao; + IDictionary> postalCodeDictionary = dao.GetPostalCodeCustomerMapping(); + Assert.IsNotNull(postalCodeDictionary); + Assert.AreEqual(87, postalCodeDictionary.Count); + + IList contactNameList = dao.GetCustomerNameByCountryAndCity("Brazil", "Sao Paulo"); + Assert.AreEqual(4, contactNameList.Count); + + contactNameList = dao.GetCustomerNameByCountryAndCityWithParamsBuilder("Brazil", "Sao Paulo"); + Assert.AreEqual(4, contactNameList.Count); + + contactNameList = dao.GetCustomerNameByCountryAndCityWithCommandSetterDelegate("Brazil", "Sao Paulo"); + Assert.AreEqual(4, contactNameList.Count); + + contactNameList = dao.GetCustomerNameByCountryAndCityWithAllDelegates("Brazil", "Sao Paulo"); + Assert.AreEqual(4, contactNameList.Count); + + contactNameList = dao.GetCustomerNameByCountryAndCityWithCommandSetter("Brazil", "Sao Paulo"); + Assert.AreEqual(4, contactNameList.Count); + + contactNameList = dao.GetCustomerNameByCountry("Brazil"); + Assert.AreEqual(9, contactNameList.Count); + } + + [Test] + public void RowMapperDaoTest() + { + RowMapperDao dao = ctx["rowMapperDao"] as RowMapperDao; + + IList customerList = dao.GetCustomersWithDelegate(); + + AssertsOnCustomerList(customerList); + + customerList = dao.GetCustomersWithDelegate(); + + AssertsOnCustomerList(customerList); + + } + + [Test] + public void RowCallbackDaoTest() + { + RowCallbackDao dao = ctx["rowCallbackDao"] as RowCallbackDao; + IDictionary> postalCodeDictionary = dao.GetPostalCodeCustomerMapping(); + Assert.IsNotNull(postalCodeDictionary); + Assert.AreEqual(87, postalCodeDictionary.Count); + } + + [Test] + public void QueryForObjectDaoTest() + { + QueryForObjectDao dao = ctx["queryForObjectDao"] as QueryForObjectDao; + Customer customer = dao.GetCustomer("Hanna Moos"); + Assert.AreEqual(customer.ContactName, "Hanna Moos"); + } + + [Test] + public void QueryForDetailsUsingStoredProcObject() + { + CustOrdersDetailStoredProc storedProc = ctx["custOrdersDetailStoredProc"] as CustOrdersDetailStoredProc; + List details = storedProc.GetOrderDetails(10278); + Assert.AreEqual(4, details.Count); + } + + private static void AssertsOnCustomerList(IList customerList) + { + Assert.IsNotNull(customerList); + Assert.AreEqual(91, customerList.Count); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/GenericTemplate/ExampleTests.xml b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/GenericTemplate/ExampleTests.xml new file mode 100644 index 00000000..554cad24 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/GenericTemplate/ExampleTests.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/Template/ExampleTests.cs b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/Template/ExampleTests.cs new file mode 100644 index 00000000..1ae698d0 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/Template/ExampleTests.cs @@ -0,0 +1,203 @@ +using System.Collections; +using System.Data; +using System.Globalization; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Config; +using Spring.Data.Core; +using Spring.Objects.Factory.Xml; + +using Spring.DataQuickStart.Dao.Template; +using Spring.DataQuickStart.Domain; + +namespace Spring.DataQuickStart.Template +{ + [TestFixture] + public class ExampleTests + { + private IApplicationContext ctx; + private AdoTemplate adoTemplate; + + [SetUp] + public void InitContext() + { + // Configure Spring programmatically + NamespaceParserRegistry.RegisterParser(typeof(DatabaseNamespaceParser)); + ctx = new XmlApplicationContext( + "assembly://Spring.DataQuickStart.Tests/Spring.DataQuickStart.Template/ExampleTests.xml"); + adoTemplate = ctx["adoTemplate"] as AdoTemplate; + } + + + [Test] + public void CallbackDaoTest() + { + CommandCallbackDao commandCallbackDao = ctx["commandCallbackDao"] as CommandCallbackDao; + int count = commandCallbackDao.FindCountWithPostalCode("1010"); + Assert.AreEqual(3, count); + } + + [Test] + public void ResultSetExtractorDaoTest() + { + ResultSetExtractorDao dao = ctx["resultSetExtractorDao"] as ResultSetExtractorDao; + IDictionary postalCodeDictionary = dao.GetPostalCodeCustomerMapping(); + Assert.IsNotNull(postalCodeDictionary); + Assert.AreEqual(87, postalCodeDictionary.Count); + + IList contactNameList = dao.GetCustomerNameByCountryAndCity("Brazil", "Sao Paulo"); + Assert.AreEqual(4, contactNameList.Count); + + contactNameList = dao.GetCustomerNameByCountryAndCityWithParamsBuilder("Brazil", "Sao Paulo"); + Assert.AreEqual(4, contactNameList.Count); + + + contactNameList = dao.GetCustomerNameByCountryAndCityWithCommandSetter("Brazil", "Sao Paulo"); + Assert.AreEqual(4, contactNameList.Count); + + contactNameList = dao.GetCustomerNameByCountry("Brazil"); + Assert.AreEqual(9, contactNameList.Count); + } + + [Test] + public void RowMapperDaoTest() + { + RowMapperDao dao = ctx["rowMapperDao"] as RowMapperDao; + + IList customerList = dao.GetCustomers(); + + AssertsOnCustomerList(customerList); + + + } + + [Test] + public void RowCallbackDaoTest() + { + RowCallbackDao dao = ctx["rowCallbackDao"] as RowCallbackDao; + IDictionary postalCodeDictionary = dao.GetPostalCodeCustomerMapping(); + Assert.IsNotNull(postalCodeDictionary); + Assert.AreEqual(87, postalCodeDictionary.Count); + } + + [Test] + public void QueryForObjectDaoTest() + { + QueryForObjectDao dao = ctx["queryForObjectDao"] as QueryForObjectDao; + Customer customer = dao.GetCustomer("Hanna Moos"); + Assert.AreEqual(customer.ContactName, "Hanna Moos"); + } + + [Test] + public void QueryForDetailsUsingStoredProcObject() + { + CustOrdersDetailStoredProc storedProc = ctx["custOrdersDetailStoredProc"] as CustOrdersDetailStoredProc; + IList details = storedProc.GetOrderDetails(10278); + Assert.AreEqual(4, details.Count); + } + + [Test] + public void DataSetDemo() + { + CustomerDataSetDao customerDataSetDao = ctx["customerDataSetDao"] as CustomerDataSetDao; + DataSet dataSet = customerDataSetDao.GetCustomers(); + Assert.AreEqual(91, dataSet.Tables["Table"].Rows.Count); + + dataSet = customerDataSetDao.GetCustomers("Customers"); + Assert.AreEqual(91, dataSet.Tables["Customers"].Rows.Count); + + dataSet = customerDataSetDao.GetCustomersLike("M%"); + Assert.AreEqual(12, dataSet.Tables["Table"].Rows.Count); + + dataSet = new DataSet(); + dataSet.Locale = CultureInfo.InvariantCulture; + + + customerDataSetDao.FillCustomers(dataSet); + Assert.AreEqual(91, dataSet.Tables["Customers"].Rows.Count); + + + AddAndEditRow(dataSet); + + + try + { + customerDataSetDao.UpdateWithCommandBuilder(dataSet); + + int count = + (int)adoTemplate.ExecuteScalar(CommandType.Text, "select count(*) from Customers where CustomerId = 'NewID'"); + Assert.AreEqual(1, count); + + count = (int)adoTemplate.ExecuteScalar(CommandType.Text, "select count(*) from Customers where CustomerId = 'ALFKI' and Phone = '030-0074322'"); + Assert.AreEqual(1, count); + + } + finally + { + //revert changes + adoTemplate.ExecuteNonQuery(CommandType.Text, "delete from Customers where CustomerId = 'NewID'"); + adoTemplate.ExecuteNonQuery(CommandType.Text, + "update Customers SET Phone='030-0074321' where CustomerId = 'ALFKI'"); + } + + + } + + private static void AddAndEditRow(DataSet dataSet) + { + DataRow dataRow = dataSet.Tables["Customers"].NewRow(); + dataRow["CustomerId"] = "NewID"; + dataRow["CompanyName"] = "New Company Name"; + dataRow["ContactName"] = "New Name"; + dataRow["ContactTitle"] = "New Contact Title"; + dataRow["Address"] = "New Address"; + dataRow["City"] = "New City"; + dataRow["Region"] = "NR"; + dataRow["PostalCode"] = "New Code"; + dataRow["Country"] = "New Country"; + dataRow["Phone"] = "New Phone"; + dataRow["Fax"] = "New Fax"; + dataSet.Tables["Customers"].Rows.Add(dataRow); + + DataRow alfkiDataRow = dataSet.Tables["Customers"].Rows[0]; + alfkiDataRow["Phone"] = "030-0074322"; // simple change, last digit changed from 1 to 2. + } + + [Test] + public void UpdateWithoutCommandBuilder() + { + CustomerDataSetDao customerDataSetDao = ctx["customerDataSetDao"] as CustomerDataSetDao; + try + { + DataSet dataSet = new DataSet(); + dataSet.Locale = CultureInfo.InvariantCulture; + customerDataSetDao.FillCustomers(dataSet); + Assert.AreEqual(91, dataSet.Tables["Customers"].Rows.Count); + + AddAndEditRow(dataSet); + + customerDataSetDao.Update(dataSet); + + int count = (int)adoTemplate.ExecuteScalar(CommandType.Text, "select count(*) from Customers where CustomerId = 'NewID'"); + Assert.AreEqual(1, count); + + count = (int)adoTemplate.ExecuteScalar(CommandType.Text, "select count(*) from Customers where CustomerId = 'ALFKI' and Phone = '030-0074322'"); + Assert.AreEqual(1, count); + + } finally + { + //revert changes + adoTemplate.ExecuteNonQuery(CommandType.Text, "delete from Customers where CustomerId = 'NewID'"); + adoTemplate.ExecuteNonQuery(CommandType.Text, + "update Customers SET Phone='030-0074321' where CustomerId = 'ALFKI'"); + } + } + + private static void AssertsOnCustomerList(IList customerList) + { + Assert.IsNotNull(customerList); + Assert.AreEqual(91, customerList.Count); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/Template/ExampleTests.xml b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/Template/ExampleTests.xml new file mode 100644 index 00000000..c6d198e7 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/DataQuickStart/Template/ExampleTests.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/Spring.DataQuickStart.Tests.2003.csproj b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/Spring.DataQuickStart.Tests.2003.csproj new file mode 100644 index 00000000..76c8b0c2 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/Spring.DataQuickStart.Tests.2003.csproj @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/Spring.DataQuickStart.Tests.2005.csproj b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/Spring.DataQuickStart.Tests.2005.csproj new file mode 100644 index 00000000..9a048506 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/Spring.DataQuickStart.Tests.2005.csproj @@ -0,0 +1,82 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048} + Library + Properties + Spring + Spring.DataQuickStart.Tests + + + true + full + false + bin\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\..\lib\Net\2.0\antlr.runtime.dll + + + False + ..\..\..\..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\..\..\..\lib\Net\2.0\nunit.framework.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + + + + + + {92B2A60E-D3B9-4647-8CFE-AC19611869F6} + Spring.DataQuickStart.2005 + + + + + + + + + + + + + Always + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/Spring.DataQuickStart.Tests.dll.config b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/Spring.DataQuickStart.Tests.dll.config new file mode 100644 index 00000000..060e2594 --- /dev/null +++ b/examples/Spring/Spring.DataQuickStart/test/Spring/Spring.DataQuickStart.Tests/Spring.DataQuickStart.Tests.dll.config @@ -0,0 +1,21 @@ + + + + + +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Examples.build b/examples/Spring/Spring.Examples.build new file mode 100644 index 00000000..6c9bfc03 --- /dev/null +++ b/examples/Spring/Spring.Examples.build @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/Spring.IocQuickStart.AppContext.2003.sln b/examples/Spring/Spring.IoCQuickStart.AppContext/Spring.IocQuickStart.AppContext.2003.sln new file mode 100644 index 00000000..5d123b75 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/Spring.IocQuickStart.AppContext.2003.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.IocQuickStart.AppContext.2003", "src/Spring.IocQuickStart.AppContext.2003.csproj", "{880EA873-E951-465A-AC5B-BA899A2C744E}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {880EA873-E951-465A-AC5B-BA899A2C744E}.Debug.ActiveCfg = Debug|.NET + {880EA873-E951-465A-AC5B-BA899A2C744E}.Debug.Build.0 = Debug|.NET + {880EA873-E951-465A-AC5B-BA899A2C744E}.Release.ActiveCfg = Release|.NET + {880EA873-E951-465A-AC5B-BA899A2C744E}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/Spring.IocQuickStart.AppContext.2005.sln b/examples/Spring/Spring.IoCQuickStart.AppContext/Spring.IocQuickStart.AppContext.2005.sln new file mode 100644 index 00000000..7a7cfc8b --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/Spring.IocQuickStart.AppContext.2005.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.IocQuickStart.AppContext.2005", "src/Spring.IocQuickStart.AppContext.2005.csproj", "{880EA873-E951-465A-AC5B-BA899A2C744E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {880EA873-E951-465A-AC5B-BA899A2C744E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {880EA873-E951-465A-AC5B-BA899A2C744E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {880EA873-E951-465A-AC5B-BA899A2C744E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {880EA873-E951-465A-AC5B-BA899A2C744E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/Spring.IocQuickStart.AppContext.build b/examples/Spring/Spring.IoCQuickStart.AppContext/Spring.IocQuickStart.AppContext.build new file mode 100644 index 00000000..dfca8bfb --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/Spring.IocQuickStart.AppContext.build @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/App.config b/examples/Spring/Spring.IoCQuickStart.AppContext/src/App.config new file mode 100644 index 00000000..ca7b74a8 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/App.config @@ -0,0 +1,38 @@ + + + + + +
    +
    + + + + + + + + + + + + + + + Spring.IocQuickStart.AppContext.Images, Spring.IocQuickStart.AppContext + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Images.resx b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Images.resx new file mode 100644 index 00000000..b725b654 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Images.resx @@ -0,0 +1,430 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + R0lGODlhkgB3APcAAP///zk5OUJCQjkxMXNaWlpCQoxaWoQ5OaVrY4RSSqVjWueUhNaEc5RaSs6Ea3NC + Ma1jSmMxIbVzWloxId6Ma0o5MTkYCIRSOUIhENaMY6VrSu+ca8Z7Uq1jObVzSlIpEIRrWlpCMe+lc86E + UkIpGIxSKVopCGsxCIRjSu+cWpxjOWM5GK1jKXtCGIxKGIRCEJRrSpRjOWtCIcZ7OaVaGHNaQko5KSEY + EPelWueUSr1rIcZrGJxSEFJKQmtSOWNKMVpCKXNSMb2ESu+cSnNKIZxjKZRaIb1zKc57Ka1jGKVzObV7 + OfelSq1zMb17MeeUOa1rIcZzGNZ7GIRjOXtaMeecQs6MOZRrMb2EOYxjKe+lQpxrKeecOdaMKeeUKYxr + OXtaKfetQv+1Qt6cOe+lOeecMbV7IZRrKfetOf+1Od6cMe+lMfetMf+1MVpKKUo5GEpCMZRrEFJKEFpS + ECEhGEpKMUJKCEJKGCk5EDE5KSE5GBgxEBAhEEJSSilCORgxKUJKSggQEBAhISlaYylKUilCSkJaY1LG + 90JSWiExOTlaa0paY0qt51Jre1Kt5yFKYzlSY0JjeyE5Skql50qt7zlacylKY0Kc3kpzlDlrlEKEvUqc + 3kKU1jlCSkpjezmM1ikxOSEpMUJac0JjhDFKYzFSc0qMzjmEzlJaY1KEvTlrpTlztTl7xlJznEpzpTla + hEJrnDFajDFzxjFCWjlScyE5WjljnDFrvSljtSlrxjlKYzFShDFKcyFSpSFatRghMSlKhDFapSFKlEJS + czlShDFSlDFKhAgQITlKcyE5czlCWjFCcxg5lCExYzlKjBAxpRAxrTE5WiEpShghShAYOQgQMSExhBAh + hAgYlBAYawgQYwgQezk5QlJSYyEhKUJCjDk5ewgIOQgIUggIYwgIazEpe1JKe0I5c0pCazkxWjkpczEh + Y1JCcwgAGEIpa0oxY1o5a2tCe0pCSiEYIQgACJRSjEoxQnNKY6VrjJRSc4xKY61re3NCSqVrc6VjawAA + ACwAAAAAkgB3AAAI/wCPUcvW69MkRpdOCatFLdC8WRhMWDjxgkQFF0ky0uDh4kSeWnz4/CJlidq0aeGq + Cbt06dMnSr6ukdNWLOY1a9vUwbPmK5s+CSPilfOQIoeCR8D8JAJV5IiOHUlexCkzg4OBBCO8aHnihc0T + NmDDejEjqVizFWvWeJHSwo+qab2SYeARwk+tdv/y0vtHTxi1vIAB79VbzRKrSwhxMatFxwKGEzwyRoni + Y0kSH1CkSOnihbPmLIX84OE2LVQgPokSHavW61oza7k+YdOWTdatW8KYXdvmLp6zePr83TPGrsqCFAYe + PUrWLBSJAKD++PEzjceMGQg8gH3SZk0XK2zSqv8FC+aRpUcuvB4RUEpSqWaWLHHjscLPr7+D957ky3dv + fv/TaEIJLrUkQsIKLkCBhBReNOhgF0iE4ESDZXTWoBpqVLEFIQO4QEURNLjwwgp6CDLPL8cE0k4zp/RS + 0Cm44NILLrcks44/FPSjDjm98MNABimwkxQvuyQSQgxFBFBKLXlwI4AAD5TRRlhrjFFGeFWsEZ4XVxix + AjArNNjHI534UYwwGCDRRB15/BFINMf0N1gtcebXH3//TGNJIhaY8EIUXYyRQw6CquFgZz8EceihapTR + RQ5UcEMDGw46oYMUNMyBx4kpJkPJJqx8csotpzhyim7p6MNPNrcAo88CDBj/8I4vwDQzACgRrPGEAYi4 + IAULuijDzQM6PPGVsZSm9URaXXkRRylAIOGFAVQc0QIvyfCwVgR/EEIIKNLgxR898yQTyJ149nfMHzfw + 8N0MagQ6hhVWzIuhF030gEWjDXJRRpVWdNGFFDoAMUAUT1ToAQwOYqqpN95M08xKlFAyCSef3NLLOOKI + k8wtteRTjzvZxGgLKB2w4MMMWjhx5RMzLCGFEZKAIkMXU4ZXxrK6iscGC8CkJwUSLMxQRhAyQOFCHoTE + EskfqE1DDz3n5iXNL3pNLSdgz1VgRBdqWFHF2DlUUUajVTjhgxNqVchFFVz5e0YFAXAzgCQhWFmhrl4w + /+tFFHPQ8Ys37YQTzCeXVHwKK8KsEowswYjzzjqwCSNMMOlQMEQZOYzgwRNacGAFGmwgUQghtUzQFc9a + 6JpD32qxUMoJD/RBSA9sr6EDIKXYgswPYPjghzTNiBPI1HsFAk6cgo079QAT3ApEB2dbkUO9ZhvqAxV9 + l1GFlV78W0YZR/jxRyneTFBLEQnDneUTZISv1hzU/JKINMckk401lFwSzC2mYAQjrGENcVxDIdsQxi2k + oY8MPGFsIBiB6JZFhjU0AQo+mIYRwoMGY+XgCWOYwVe8IINIJIIUtQACg9TyA1tYwgZJcFAS7qALa4Qj + RVRrx36Qd6fByKMbdRhAIv+4UQIkPCEFVvDeGNZggAKoIS3eW9b4uMCVKNQhPgE4QhAGwAKxJWwNVuAK + s8DghxPgIRGWwB89gDGJU5yCEoy4jTCsMarLCaMXwAiAP1KQAgZIwAES6GAH16AFLSChD9KAwhMgBDsZ + umEQsUhEIbzBAi2VAQylIOIS/5UWM+AhGtsQRzvaQbVj1CJ5gzlef8zAggbUIBqJIIQNSjACPuYAQj0I + 5Bp2Nr4p/ssLIQhiKSLgBSREQwY7e0K8RMisQxrhbyu4gSQsIZDEtYQVt4gRLjJWjGI4Yx3LsEQB8iGB + cnKADEMgw1a8spUokMIGO+hBDcCyli3UgRCACIAl9BD/vQcgAQsPAIQlXLCGD5KBC32Tgh0CUQ5yiOMY + pKRHM5rxn+YFDwtI0AAB+lAKS1RAAVUoWw2UoLPx7XJ8DVKLE7qQhEU0wQtP6EAhXKCFMdj0gVxBQh3g + kFIv6EAOdOCFJWohDFMEgxWcuEUuEjKJTzAjG/l4xzKyUYtslMMHutIC3NAQBq8sqwa1MMKEvMCCIBRi + FkFggRSyQIgkGOERtfhBA5CQgFJEYQ1DqAIXyBAeGvyBGPoTxw1JeQxwHM8/yDuXH3ogBUOVQW2xkA8N + pFCEC/xyDRXsGxk4ucuUcsYLZHhCDaQxg11qNUtk8EAhiiCetHSBB3iQxDJ+UY1q/3Cif5fgxCUOwYhe + WCMf+khGMK7BClukqWxP6OBW1MmGIljiAzsogg860Y0rMKhBNQBEFPjBiwlgQVcw0AXC1MCFsmE2DpZw + BiHWwbFqQLQdtZjGPw6LJ0HMghtBAFujuuCEBnSDQwGAQt/Cg9n4VWmEV1pDFfiqlkX2IARlSMGyvrIG + JLihCwkLLfzKEAU7hIIXhKBHMJJ6i9syYhPDXUcBssGKU9iiFNOYwBMGlYOtVGErXnDBI0DhrUQIuG+k + 80EhuMGCLxwhimRYgi6gADd/fREJcviDN6hxDcG24xj0yB9eksefagBhC1M4wqG8N4MSDKISASjCZ8NT + wWSp5f/N8NOssY7AjQ3+a1leMIIPvBJaMtTYWEbwAx9IwQtq3KIY08DFJg5hCoUkw4CX0BhzKgA3Gmvh + OpUdgCVIEQIo2KMIOatQCIoBCCRsR6sVRgQQQMsFLXCBC2qJQh8EvQ6HVmOUEpWa1gYzjySwQQ1jcJBa + blwEOIABEJIIABWc8EQthCdZlAoPQilVyIKCgRs6IDCl6tAAPpMhpN9+AhHgANtEZKIZwKBGMQKIC1ko + RBinCIYqTmGNaYhDHzMYwQdncIFZJOMRQPBDCLoSA1OTga8RisQEylDBKkHgSgYgRCVbDTe+JmEQc7BD + IkJ5a4hWIxnIC3kgJiS68FHIC0f/mAULiumDmskTBXNtVmuz+m3vdWGuP6gATLXghTMUAgloQMPBh5CD + IYygCoC4QOlIVApp0GMawTiEqC4hC0rcwhrCwCYzhJGNCPCDHz3gxSwI0AAPJGEWJWBDGYQ+7OucQREV + kAIZUrAVepWOGBVAQl71qgUp9KATSTDDDdwxjnCE48rtqDfVdv0PLFTobJCvUBCcUVrvqWARAXBBE7oB + iBqMVTxqGNTY4FaGMJouPWjwQh184IWVku7gKRjCDKJRhDAk7AV02IUl/kENTbAkjrLoBdevQapc9MIX + vhBGMvhxBC15IQuziMIgd0mAz7HhCoooRLYPrqWaskHJFeiA/5aegIQelIIGDZLDL0Jp+JRMQxhbvpMV + 1tCos8mvCX4ogVrajIQKdMIISBADtcANgPAFC9IgT5BXE6YWV4AERcANUMAGNEAIS9AGVPADCBhSndMD + SFBxakADe0Bo4uAMweBGj5MLisEMvXAJmoALvsAMpAUzVtAGOoBWBDYEHDACEqAGYEEFflAI/MA6HQQ3 + bKACpUAIIUAEQTAIpMBaahEHeNBQHdN+wKBr5zI1XdBZasBsahF2NeAFaNAFL3UEbmAE3RACNkAIoZAO + DdADcFABRFACLGBqbLAVY3ABPmAEdRAAL1AHU+AVgSYDP6AEYsAAQ+AEPTAG8FM2ZWAEev8wCrswDdaQ + DMBgCi0Rb7KQC7nACqwQDNYgDfxgAEhSArPQB05wh1hQFFqiK2zAD27gBlgFP1riVWiwBt2gC92ACItQ + AzrQN80iB9JQQIJleNkADDx0LmzjKD6ACEhgQT7gAVYAWl3gA28wAEUwBl1QAaXgCaEQJmAgCYRQCZYQ + DZ3wATTQBUPAFU4gD3+gCKEACjrQBvZECokgA03gARDgBEAACgJmLGGwFhPgB5UQC/TQDsBgKpeAYrKA + CyjIDNYADOdgCc2gDITADVQwAxKQAUCxBkJXi5TSA1BwBgNgalrikT1jBSJkKFDEV10RB3wghVMYDsxQ + DSH3D0/kHaj/IABm0AVfAB7aBgWFIAAsYCxdQAWLEAnc8AIvQAiNMAqqoAq2EE6dpwRbUARpWAiiAAqz + 5AM+UAegMAUwUABXMwukAAoBUAAJUAMXIA+gUAiJMAyWUA3TYAq3wAqUoFu3MFzMgQASAAEaoAJYoIhW + oAAZ8EGYJXRgwQKzcARI0AlQ0B0cWUHhxlUcpGDz1yxdYAe6cA3Z0DEdUw7AEAiHRQ+cYQZmEAd1IAUw + wDYGRilTsAVSUAP04mdQ0AOv8AfcIAqYMAu1IA4SEyPJUAukMAuIcEKzQAgkgARhEIZfUAZDEAN+EAmD + EAAPUAIR4A2EYAuqUAy7QAu/8AuZIBB5/xkMk3AIl7AQ2vANGSACTDAEQ8AEscIBQ6AF6ISYOOYDBbAG + UtAHMsAGQyB0fFWYXYEEl4Usz+YsoNBQ5DAOnakNwUANIXcGXrAFcMANQKADE7IGrQZF4NEG3ZECaXF9 + i9AH7hgJkQAK8jALnQAI6NANVBADVBACiDAKsUAKhfABpuYFaTAGI9ACkfAKfEACB1ADPVAIuxALu5AM + klAIihANurAL/1ALpxB8kzAJveCQ2bAPDnAP7AAPEsABVUB06nRwQRd0VtAJoIYEiKAMIxAGZEA6bIAG + MEAFPAAHSNAF8uADJtUVlBIHfwAKMkEO2eBQwGANi0cPcOADDTAHff8gAHXAGfvHfdrGBgsWFjTADXCQ + B4kQYGpwAaHwA0hgKF7QBlLgBp2gDKMwCrMwADIABC1ABGh4nKHQCbPQDQkgAKCAFMBACpIQCoXQLfI1 + EMLwCZzgiQ4pDenADMVwDg3QnoU0nx1JOmmwBAIwA6lXAPJQmAAKFvnyB3kgBU3gC52ABNx3JV1QB7PA + UNtADuzKoMGAZaLpBFYwBYUACiswBZzBWQcHFqsYFmzQBkiACjQQAvIQAGqlAgJwD3wKFucaQ0VAq96Q + CLGgC4RQCqQgDd5QBNeBBBdwK4OwC7xADEvyC0phC1hTDa/BCqbgW8HADKtgDM3ADhLQAFaQTgf/R6Zv + 2gY+ME+u2QNesJxxGrRQ0Afz0QVgkAQFNn8sVAh8UAsyMagcYxa4loUTWgN18AUUYlJpIakElhYzgARI + UAI1EAXFJgAtEKoDphZBwA1NgAX80AmKAAmgEAtnlQmFUAgBEAI1IF2gIAAEUADKYAnf8gNF6pYESQ3B + sAyaoAmr4BL0lg3XAA/6IA9OgAZDsAZhkAN8RTpqIA/M1AYq0AdgWEFBh1nPRwh5cARt0AVl0Do64wU+ + EAB2MA84sa4cIwy2MEqBQLXecQRXUANNwIMhGh7p5K//ugRTMAV9UAAo8AVAoAjKQARf0ABKoAExEAKD + 0ANfoAEz4AFUgAh0/xABLEAE3DAPihAJpAAIpOAtE+AE+FgL76ACI5ABD9AJoTALpfAPzQBAq3AN89ay + 2ZAN7oAOCCACNzafN8aRbbAErJcG3OEBBMCnm8sGZNAGVxACJcA9MzAGXbsGSdAHd0AEeKAO27CuDpUN + b9EOu4tQahC83nEFQVACXaBtNeavaqAEjvUFZrAEM9ABcCAAhTAAwUsvOyUFbYByUeACAUAKJDADXlAC + 3sAN8vABP2APhPAFU8CV8tAMF+ABI6AC3RAKkJAIryAN/wAMc8QKjPAJ2uki+qMO+CACfHRj7MQGBXAF + bSB0WmAGoqtMpAOgXvAFrFcGSwCpmIVQ2OsrwP+IDdsAteIQDM0wSp+1wz11BIAgAGJmvJsLwVWABlWg + AVxAL11cAyXgA0FgamqACCzQBkczC7gaH9JQCyzgBBdBBPYABLP0r54DAvegCz3QA6FACInwB6IQCplA + StbACYfAWy7RaLggDNfADwtAAZ1sBUD3BJbcBWlAOm2gBH3ABlaABWDBzVVgBdDYBjAgAyHwADO8bW1V + Bi5Zuws6gsHgXobSwiqZUlZQBPJwBV0xi2HhBFgwJWugBFpSA6TwNQzjA3UmA3XQFU1QADJwOvJABIrA + Cy1gx0BAAAnQCT0wA2WjBUyQAwywD/zQNJBACqVQCJVACLsQCPzjCIfgCJ//4G6rsArFmg0JYACoYAQt + QAVpwAY/sGdl2s0+O851+G2zWGNRoAjBUAs8UEQlRAjyegUjvBvtmg2rMA3tkIVdsARv1lps0AVTYGwL + qyXMBhYLPIMqEAuesANKUAUe0AdwQAhAgDNXgLVO0AROTAXPFQU1oAyfI7bWkwJhGgawAge8WgqxEAu8 + UAmFQAsmISDLzAic0CLkYA3B8Myg0CTzYARt4AQCoM2bi86xGLQHNzr7SgYdsAjEEAAXUANXoNiTQilz + kAiMzK7ZMA6qkAzVwBlcOHMhOqHyAAZdIZkoAHQCUwbVkgQ2UAQxQFJpQQSEYAnGLdrZVotaUAY+wMR9 + /xsF3P0FmSsC/QADTzAC91AJgvAIkWCxpZAMyMANr3A806AKmyDTh9AinAkM3ZQIeyAJJCAFMnABefym + YJHFcRp0ecwEN/ttZNAALJAER6DNUoAIAyAFadEGZjAP5YDV7FoMwBAOYPN5ws2nXpC8zciRMGCAPjAA + J1DX8qBqX7AsaLCfcEAFAmCABcANP1BJa5AGH1wJN0BkVpDj/rkB8cALF4AFClAK5hZZszANpTAMJ2TG + x5ANwlAM9404n1gAHeADu0AIuDkP3AB0r4cGZdADVPBs9CIEGTACQYdOdBUEQECdSQAEjyAJGT0lXXDV + jcyu1qAK4vAdYJNSz3boYf9xBWsDFliwBFiAvaPADZ0ACvMwAF/IkVfgs0igAl9QAC6gjZ0gBTVeAISQ + fXxQADlwAVMQBlowBPgAzQYwA+hgC7OwC4/gBwMgDZFACHwQDcdTDcBQDqbAC8UQI7XyAOpTseB44X9M + OkgwDICQAAmgAkIgNitJKTXQByGgCKoACt4ACXkwC0ZgQdIyB7/AyCa8DaqQDUewBLAj1j4TbWqABK/J + BmOgBBZGCJhQCJW+CKIwCxegFj6wBWwwA0Jg1Ez6hV0gAIrwy6MwRDOABfOkBTmQAv0ADuBwAX1QJO6h + DAOgDcOJGrvXDiq4CuAADC5iDZbwb5kgCX5QCrQwDTb/AAYzEAZVUGa60JNWwFfbjAahTDpUgCs7oAM2 + UAqu4A1JEAVZ4QNgEAV3QAe1a8KrAAzM9u7CPR7+WtAGbQUhAAckoAuGMACEUAiRAAQ+0ARbwHqkU0hp + YAUtrkhClpzlFwmrNQMFgATt2QHdIA3AsAyFUAGVkAmvUAuSAAyPQAiSIAjDQEqqQAmmACrPUA7ZEA6+ + qQrAIPiZoArBIA104AJEsAJNQgMeGsoMV9RHQAh64FF9qAiWMAskMH+LJAV18Ad6YA27UcLkIG9j1Ugd + TCXGi7xeMAM1MAUxYA+hwA3eMAtF4AUFIMRhEecdFAMCAAcRYAjcAAZk4HeQEABS/6ACPtAFTPADmLAI + ESAPBDADAvALTfcItvAKxjALf3A/9JAMjOsINC0Lll8MmtAMyWAJg5AIANFnFq9avxJJevRrRZs2bITA + QBMRDRswlmDtUvSL1KM9iqa5YOMhhhcwhbhJw7ZN5TZrwbp4KbNG5ho2NNnUvIkz5xolNdS0QdPmSggq + PmB4QSKvhxc2EclMfLqmQKcXA1QtQpImySJFLZAI6HHERSFENC40kCBPWqRXllTtSkaIDh9ego5NK8bJ + 0aZLnIrFsiWpmR8/fw4CW7ZMWiFz024YeTlkHxmgaLygIARs1qhK836VyqTIAg83hXa8IKRHkLWV264F + 21Jzpv/NnFrI5GzjRU1OJ1nKeCHTpQYLpl6uENrSMOIaLmnSrFHhwUOdPyRm5MhRhUgkUCWQ9FDWB1Kn + JA2+OAFF6NUrar5KlZJEAo+kZfToHUt2ixWuYtmaJUvGklJqSSQPG95Y4Y5ODEvGG1AK8MGHK5p4aQ0v + lCCFFFVSIaQTbgYhZZRYBOEGiBf68OONPcpJaSVnBDBDtzXUUEO3I7roQogqvLACCSSaKKIJIZsIwQcn + ZkDiixqKmEEFbpZqao2omkAiBizS0KIOSEJpoYwqUuiih1FIKEOKPhAhhBsZSoBDB36A6COWSGzZpRRS + 5sFgD1rauS8WTljpRRhhbOGFlEr/fjFhhwpromGRWZqhBoMorOgJBXkEIGqFUkZxxZIBbCgqAFFWsSQR + DOoghIYj8JgGm2tUuuaaPlBowgo1rHBCDSUUqcGJI67I4ocQ4LAhAFBIsKHYRAohgkrhlDiijiYqa6qN + LgQYQIk0gushkkWUsQGKESgAAxJSJpBijS368GGKEGYJIQgjCrBllUxGmYYQQiTxg5Bp/plmlVNYOeWT + XHKJJRFu4AiiDDTIgJiMMuq4I5plQnEhDTbaWMOJWVFAoQdACElkggeKYIGHPCCZp49BBsFACi/mkAYa + bFpkRo0ylvDCC5u8cGGFHvr4IYYtzMDRCSTOUAKpJqgAJAAf/8AoQoogfPBioqbKuKIHQwb4AQs0ZnCj + ElEgiYSQASJIpw9Mvi4hiQK6qMmKC8YLIJNdSMnkF0JmKeECPqShJ5BegjHFFFk+ueSWWci8gAqJ0Hji + ih+isMOaWEDh4YkwtNCaizG8KAKIOiIpBRRuOvEDkxv6DWCQN3aIgw5hnrkZm5zZuNWmLraoow8wYmSo + qY23oIKmoNo4gxAbhgXCj+SaeqoMovsIwgku0AgDilAK8QYTNGcphBBEIlHEjwrAiEiL5/owBA5CRrmI + ml0qKCOBPWY5hp5qbgmGI1hxCVMEIxMkQAIHBtCFMDTFCz2Y1hyasQxJcIMI1GtfRGagA/8wlIIbPDhD + EOogCj9AwgUvqIQlTJAEPCTDZjd7xhWcYIUrXCEKcaiDH84gM4bQhAtVYIMSvuCwNaQgDBqbQgWkcAQf + EAIISOAYG8bgAQv94AhAIYMWhECABhTBG25rRCckoQhFLAIUeehDVrQwhDAgoQ8zmMEAIlGIAVgiAilg + gR9CQThvMKMXq1iFJjhxCWeEYgINEAALnhAUJRRCCm2Iwy+KQYcARAMIUQAKG8iwBi3kgHdJmMET1iAF + O+zCEhOIQhJWkL4T2EEavmDGzZhxBSUs4Qp9KMQjSlaEJVDIZ2t4wgxioAQ2NLCIDrBCGh5oAyOYAQk+ + SOQTKFUGNqT/YQlWeBgabJMDCnSgEJAwBCjgwA1CYEIRnrDAA9I4BC1oYQ0NmMEFuDEKbsijFBPAI/hm + cR9rrKIYmggGLj4RDGnIAAICuAAZntAFblzADCXIgh6M4Y0kNAERATCCJp8gTcqoawZDmIEM6gCLX5Dg + DRWYABBKkYc9zAN3uMOGGmQyM0vAAQpNoOUXUFCDKfygAIjgBpPU0AUt3AMbxfDHE2oQiVA4gQ1eyEIA + 6nCFbGohDBG5KveGUIUeQOIC/oiHPy7Alj+QEQMhCIEVhkCGMDzgAdyoRCICYI9X/EEFD5AEJF5xjGOU + AxebuIUsmKE5SfDAm254AhsusBQoECEJ/3pQRyB20AYePOKUWSvD6KRQgln8IKSDsMQuSDCBQZnGCGnq + QzBe2IsuyOQM3AADU3LSszIgoQ6l8AEKCDAFGFwgBujAxS26EYNCvIIWRvBCGpAQhE6EAAnaHAI7bdPO + ITDhCp2tAgP0UYAS2MMTiOADJpRhggr4wAoKRUEiFJG6aMwCl9KYhh9mMQxp/IManODEKXBxDWk0YxDe + IAEhrvAEDxSiCF6IgQzAoIdaTIMHbNgCLyxRiydqcgouU8RoZ1GKP8TCGy4IwC9c0LM59GERznhhTL0Q + Bz9AQTa/tEwXroACLOSADFWISRWsAAJwEAMdInsPQmNQgB/wIABtev8CE7Io3SGgIQ0XqIUPimAPQijC + EnQYQCYQ4YdWgGIes2CBczSQiO8mohaE8MQs/qYISSSCcMcIxiZMgYtsaEMYcpJEKKDgBXl0IitdCAAh + 8GBmEzTFDDSggiIGkAQvfKEPnSBEKQoRghcIIBYBcAEUzuAFLrCBCH/oRDRsdg1sdMEM0pPNbC6DiCZo + 8zbUqwIXcECBfUhACzFIgAoKkAhC1CAL0BxZDWKwBCxYYQxPqEIVnuCDQoCiFrEQRSpSgQo6IKIZJsbE + L2pRgCdk4B6lgMQ0AKQIvY5CbSvYQyH6lx/F9QIXrFAFKXhRiBM8oBMqyI4X3DAIPeghGxZIrhj/lKkB + RBRiAkXYgUpn4YIumIkXBZhhz3pWh0qAIhTPIPU1Th3b2cikC1SARA2Y4k5NZpELt0mBCJgwBghUtwGE + KIEHxJADNUChD9z4gRJggAIYwKAGNfABN0BRCUy0AhOYaMTX7FELahCiFaKoxSwO0IBkYAJtmcgEJiKR + jFhA4gNQIIEiEvGParQjGaaYBCecEQxgGIMXgcB5G27shR/UoQ56iIYgSoClpzzhCItIXQUE8IMklOEJ + KugGGJBQhjI4AQwvOYMfEkGHWJLaD0GQSUxosoYm1EAJL5FNNrO4yXZWYQ3GZoMTelKBDmhyDWnQASEK + QE3mVGEGQlBCDwhh/whPuMIViBAFmhJBimY8ohSp8EQlquENZBw9M8VYxShEISAu5cHN9JCGKZIRjFOs + whrCAEfUJfEDYKrBjaFYAQ3u0IxskEAKEZuRCxSBiUSswAsce0IMOhEFNGzvCITAPDYwAznYA3BwFWao + g3Qpg01agy5QAhSwgoZ4tc2TEikpOTYYASzwAixQgTWoAn5wAtsYAhwQglHgBSNAthvrpDDQgEMhBFcw + Olown0hwBUVQhVJwhVRoBWngBVgYhU7gBVXIhKebk4oLBULgg0SgB2E4hE04hVMQBlwQBlMqhQE4gjby + ARdYhDSxATtIBGEAhgBogSVIgAIYgD4gBEsghP95OAEjOAIpKIAe6IIxiAkv8DWcMIMWwh1mMIMZmREz + mIIasIKbAI6a2KSTyyIpuTEySAEYGAEl+Dw0QIJZ4DYyyIEMoBQ36AQnEAOIsQ2IUQJAWJaTmIZOkIGf + moVpiAVX0ARTWIRRuJdGkMVWWIRCkARbeIVK8INA4ANvaIdq0IRNmARKCIZeKAboIwVvmAEPQIcCmIEm + 8gZC2INQAAZakIbRSgQ7UQRAGIBWcAVRKIVo8IZdcC4rmBErmIFfWgMkeINowLhS44IGLIkQoJHZuAmZ + 4KYFRAMpWSMJoIIawAI1KAAiCIIQ6AErCANMrK5l8wE2+MSHYasZQAEBqID/PiCAJRiDNZCBSJAGScCE + V0iFRoCLXZiGZsiETRkF0MCEUSgVwxCEfwAGUzgERrgEX8AFVegFaZiHGcCCTOiGDJCAApAEYIiFQrAG + asgDoQMCKNgBKHCB6CmFSEgEs6kEAWgADZgBCNAAK7CCCokDPKAFmKKRJXgXN4DAzbsNm+CkBrqJfXQI + QKgBGFgkKCCEAJACfiCA6BqCHNgANOgCOGgC21iD9iGD53iCEaA5hXoCJyAASyiEQSiFoquFUhiGNuMG + SVAGcQANYAgGYXiFRAgFJawGVTCFTcgvWVgFXOgFtXkAI3CCEeAAboCE6JOGZFiGWRAAUKgAGoCJHsiD + /w+wgD8ohUQYhURwg1wZAzVwgiWoJSW4AgHoA2WIpVmagi+goRhwAs1jwHskg06TCZzwgm6Qh+ciA+MI + gR+4Ag6QB3OEmCrQgjb4gh4YTIiMiKbgHtuIiCpIgysohV14hVZIhmKwhVh4BURgSVKgA0KABGMAhlvo + BVHYBUEgHGnghFvQBE1ghVW4BVwIBl5IBmlIB2WQh3R4hEGQSj5ohmkIBBPAAD8oERoIgADIA0S4AVDI + ulk4gTXILGpig5goAyjIg1kAhVjChitoLd6Zgc2Lx3uUDe+EGJmgAmXAAg1ILCuYgpdQgjGoAQ0QTES8 + lsCciIdZgy/AAogJgzC4sf+tCoFRKDpFCIdrOMlSaARboBNnm79S8IViGIZY+IVfoIdawC/TVAWBksLh + DAV+iEN2AAREKAVC8AOL8YM8KAVF+B5v4AYrg4VQoANlwIRMoIMoGAOurEMvcIIVQJF5IFIzKDYrWIKZ + eFLZuIlNipjbcAIf4IAqQIGY0IJFuo0qaAAfsI0q2KQRYIMsuAD7rB4f0JZg8oAl6IAcKII+GAVP6IQ+ + UMVVeIVMiAVVaIVGCAB+4Ib+FMJXKAQ+ULds0As5G5hPkAVhaIZgMIboSAEDOAAb4IYWkIMvPEJIkEE6 + 6IRHeAVbqIQ/4IZlwYRoOAE1kKYZwCkzIAJ9wQM+sIb/m+HKGJiCgJSpnNAJRRw9NlgCAVACHOACURJW + djq9IRACeVCAZOs7MkACODgC0QuDJkMC3aoBkZEHboiEUWiElTSETLiFWMCEWtgFWBgGb+AHzrSGXAgG + WqiFZsMPR+AE0pSFW7iFU5CFYpAGeEAAD9AHc7CHWRgAGcgDP2iGULABDFAEUggEQlgFZCCF8oEDIuiD + 9ZqGEzgCIiCBX6gAIhgAQgiFOdgDYLiZKViCGSoDNZgBYrMCLqCmhojViBiDGAgBAaiCtaoJ01PEThoC + AtCAvWQnYZUKH+Ck9swBLY2BKvAAGyiFV4AERUAG6XubWPAFVkQHS/gPXZjKa4AG/4RxBmCYhlCghmko + TYKRhWC42oESh3V4h3TIBmEIhqQEN2+wBUKwgBWwBENIhFlIBVrY3gfoGa/Bg0iohRv4g32hgxsYBDjg + gRbQg2XAnRqpkTKwgsRlVQ+oJRgISM9hgxlogALoBgfQJojUghRIgehag+rSAALQgg3IAC0QVjZogh5Q + g/q0ghr4gQvggHuoBDb1BE8YBljQhFFAh0fIBFcYhVbIhGSohWYghUigBeR1BVWIhVgoBWTwU2CAPk1w + UGdQBTAsBWDYBaJUBVvQBUAoBMJwBmIABWVwhVKYhkdwhUywhDzgPyRABUBIAhnoF1zQBF5AnT/AgB+4 + gz+ohf/4hQkKTEvGG4PF3Tmg44cDmIVuSIFLzIFdrYIMWIDsGIPLHYECyIAU2IAmgxgn6IHn4p7FLAAg + CNesgwVRaAVLCFBYiAVgUAVn4AVYdAViiARDqBNYgAUh1IVR6GQlHN5qCKRjxAViiAVeKAZFeIQG04aC + MgIZ+ANrUAVRkARYCAZYoAVXCAZnkIYJcAG6mocPyAM+sIRcAAZQcIVXCIREUMNAkIZYqhEuiMfM+04p + 0ZpnWoQAmIXHYQE0GIFAFoIZGAEhEALYvA4rEAAFcIAUcE+rChMlYAKrcrILSIRIiIRWIAVQPsleKIVW + SAVe9j1NwATfE4WDiIViUAVYcIb/ZJgFP5BQmHSEVQiGVTCFT9gEW5iFeXiAHuApAjCEZcAU6kgGZKAF + XVAGWqAFUsCEZRAQyVOEUZCEMcoEEWY7byCFadgFM/uFQrBmzftOWI3cGagBDygBgDWlAAibIpIAH3CA + DbCxNcqADlCGAqAABsiBIUgBd5qCGEiBfWACJCgBEoiERvC9FD46b/Q9TAiGXFAFTWiFWEgFTaCFRRAF + UciE1AQGZqCGP+CDUAAYSriET8BoQOoFamiB7BiCJ8gBJGkCH+iDAACHZBAEYygFYlAFYOAFzSCBFvAD + QqEFHpaFqvUF4B0FVigGDdGFUIglGNCZbabtTaqSJbAetWkB/xkIgFrgBqPgh1pQAQcYgTXCjtSqBX4Y + BXbohwxwMiqQhxmQALDLBEKIBFE4OmJAOlEYhZqmQXgjhlFg02FI6FlosyAWBgEVBD3gg3+ZBkdwBJrM + hWJwBk1IhgBwAAcQAibgAMQsAxqQBEJABmEQBVpoBVXIPrZLhHmoNkuQhH/2hV6wBWlYBlpYBV/Ym1gI + hmmoZmxwgib4JdpLNTa4AkW4gjYogvKxgd0GgyCoBW/4W0uoJ0RIALWaAV0IBkvwhnWIFRQQAzHIAl6w + hwBQhp5VBKIbhWGY62HwhGUABmD4PlWYa2lT69glhVoABmFQw1bmhUQwCHpoB2C4hJnMUP9bUIViSG4F + YIEYOIAvMEM92JQB9wRB4INZIAVNKIZmkAbrTgRp5mVfWAVVWIZXmBNosIZd6IVnwAUpjKlrqoklnQ0n + uAACcIInIIBHCIUbKAZEiAIv8IFSkAZLKAADOIdhKIQEGIEhaIEBaQZruIRcgAfncANLqIT0ecxB8IRG + 2JCVTIWsiwRnqARY8L09R4VOAIVY5gwB0YWSKtuEEIRAaIc4uwRWiEJfKMoWhnJmSAZh6AVgeA9SiIXV + TIQ/4BtWuAVuP22COQVN8IXT7uVkwAtNUE1ocLdgsOYLkSkdpUDm7BkdrexC+AOTYAExaIBBkYYOwAEH + QAB/QAACSID/HsiEdUiGa6hadSgBMJCvPzAINGuESlgGYgBhdBiFQhBvSNAFQ5AGVagLwjCEZqiFUaAF + 8klGBXACKpADPyCcdtCEvfgEXGj3YXjaQtgFeV8Fc78FVygGTCAGayCFVxCFWCBhYWAFW1i7iHYGTLgF + Db0FZwguYUAGXshoZEgGWyiGmJoRJ6DAHl2CGRBTNpgCfpiBL4CEyQuAIhgGj06GHyhgEeD7DEAAFECH + WbCEYrCGbn8EYMBupJPFmoY6Z5iFTgWGRrDpR6CvaZhrWuCGcP4DYBgFUIgEWJCGCIAADgiDLtADP9X5 + TXCEgjl3UvADSQgAbtCF1xkFHhYGXUKH/2II+IBPhBsggUIoBFKYhU4ohD5vYV0w81tQheIXBG8ABnBY + N7M/vYy8CTVYgowdzO/gACYYgiWoAW7gBnFLUTVsgAUQgQXAgQJ2AGRIgAYwh1IoBldAupW8bkjoYE/A + hFRAYbW+F12ISm8ACG/JSu1SxE0UolKSgCmKNiqUEyY5qni5I+hYLVWbHK3q1UtYgBvc8qjogKTICRKE + LMUitAxYIW6JIM0acIJGkRrd/BAiRUjSH2lCecFyVQsUN26xMnmThk3NmjJsrKhho2bJmDVr2Gj14WQI + GTIpylhR8kVdL2vSLKWjsGFDFSspHETzwICCglncIFUiBMlns1qEBv8t8iQq2ShCw2YhghRp1iBXihSR + ChVNEQkV92K1BMViiBYtXfB4o1YsmMZVwJgFQ0RIkbQHT9KQ6VJHkZ9SjTJlm/cAzqBQoGaFkOIlSh9R + pCpVmpVJGilSiWYREyTITyVXr5A9japVyRIrWtmQYcPmyhQ2aMKCZoIDxz1ouFq4ISQhRQoJGnIg2FWD + AwMbMHAPP7pgkskorvg0GSKKlFJLDywowcIFhBCCCCiQLNKKKLrMokgeByhgTzK1JJOOFWFogYQev/zT + jCmrmOLLNdbEYhAJkIByxBNjPDEFKZAMw0001pAghRvcFOEDILUA8gASOizCkyR9uKLJjYLsQYr/JYTw + MUsikjjTnRfmKQGCeVxtZUUNa6ChHhparDfEBvyccosLUUjCTwpvibABCKTAwIQDOeTAgAMEkBJNAPz4 + EIA0s+QBiSeIADFDGvDFUosloBRSCCmNKDLIMIRI00kt1jzYTARTBaEHNYG0UwwrmnTUSymlkJCEAIT0 + YAUaaHghAyl+/EGLkTwg8oMXT3QRAzqxBOBCC7uQAoogiSyTCSl8SFJJKLS8UgodfPByDVRrXKWGFVhU + sRUaXfhQFRtahBHselqk4E8xvhhQBiCFWLEBfhR0IwEOQxg6xBA4xGBhARzMwI8klvwxkg0h+FBDBwFE + UsoslujyQA+qhALJ/yCGCEcEOtaSIskELqwgyC/L7EJNRo7YYkkhA/SARDezFMKCGFqo6wYkecxizTw2 + wHEEV2l4oYMPBQhAgq72DNPCAIrckkklfyTyhy7SCVIINmN4sS4bbYQXxhpe+DAFaOSRsV4OczKRzzf4 + LECAJFAoPBcBDKuIn8I+JDZNAyN0o8gohdjwBQpUPBAAN4Q0MgwgBRTxxAy6+NDAA4QAkg4QD0TziDQg + j/2HQIfcQs00MRJTiwtNePEDKdJUYEVYM7hQx8XWJBJAEWx4gQQLPvTAjwsyFDJIHqC8w08RRCBCDCKz + /CHJKNwoIohTaqjhRBlbqbdEGW004UOZcb6Jxv8YwJs3hAgivHfFIxcwofAoEICDMJCBCSJIwRC4UINo + jG4JLOBGKVChiAi4KS5K8AE3XDMPommBBRWogQSQIAADjCAGPQCGMB7xB0sARRHtmIYjNnGLWkxDFauI + BQl0YIQAjIIUtfjBGrSgBDjIIA+ECEYijICEI4QgAIWAAxAKQQgbvEAUtJBEKZqRAC/EoQ88EUUmamGs + QiQCG0rAQlTSxIYxKMEMP+hCG8yDhjWQgQtkiOObyCCCAKHBCYqQhxVygAJP5IAJZAjDvbSQBjX0oQRu + GUEfStGHTxXAC4YTAxmaMAUCKEMAJZABInrwgxFwABAcCINVvlCJUiTjEYT/uFg72qGJU+TCF8BoBmqk + YQFuWGIUlrCEPVxghS70oQ96kISRvOAFOJTiFYlgQRcmMAtCYKATrhgFKOAAAySsAQlUgINrQuENDPgh + GthwgprUZ54aEOINV1AjGqrABTXebQbKEEIYkNAHUmDABoqAgIpUxAQ0hCENW+hBDhaQghEUQBQ8EcAR + 8NUGNLQhDW0oAxIeILRhCMAGF4iBPEaQA0RWoQM9KMUjWvKLQNBjFZuQBS5ukQxixKJihEjGK4BhDUI0 + QwVS6AM37DCLbLSgDBUKRiwKgZMBjKISgRBfJkBRghJAgQ1l8MIRdtACbsxCS7pgBlTStxX1gYEQ3GjC + /0TbRh45qsc8ORBCBsKghh7EhBRUwIGc7nVIYfVgCmGwwghwYIUmHAEGRXDCE9iwhAQkYApOSAMTxDCG + I1DBENwARSdAUQEpkFQLTMCCMniBjGKAgh7tUMUhGCGLUxTDFqUQBCFoUYxM8OJB3EDCF+DgBzxYYhou + aEMRFMGK25VAHp/CxDQiUYlEIOMYpeCGC6DQBSRk4QUfwEQpgvIUNa21DUfIQx76sIU4qrGt5TFPGPDz + hDKEYBBALQETmKCiOJFBDGYQwAys8AM1ILKgYrAC/L5QgG16oAc1SIFnxeCFAsABFH1ABCBqoIQyPCFY + WqhBMkAxjF8cwxqpcAQjLv9BS1yUYh6/mIUzVLEMUnDjAsP0whT0kIxpJCENZhgGLTrxgCt0IhGIwIQN + k0EEfhDiF8EARiJIgIdH+OEGpWDFMmqBjRaoYQxc2EobtlAAM1BFCWpoA3mDBSc1aoEMZcgCBH3QhUAa + Mlj3IuYWxJCDBlhBvha+1xXUrEkjEKEQHEhDQasAgzLAIBK1+MADJDHVuRVBHsmwxC/acQxTUGITm+BE + MHAhC174pBKZCEYxptGCQIohDU+YQzNCAbUnhEAGTvACFvKQiOduJwIj6AIqLOGNRSjDEqWIBhwgWIpR + 2OIaAzADWbAwhiv0AY5xLEuaxNzWN7GBC0tQQg1qEID/WUihovG9V7B2VwM2hGGgEikoZBXZACKwAFS7 + mIANsIBJNHgABUxgwS5swQ06cOMNfggADGoggNxEIxDL0IQpEr4JVtxCFoQohChiAQtiSEMSF9CCwuaJ + 6nl04U01+IF5rACIPlTAG7OggwqGgIQIZCIULiiBo6DQgRAA4hGCQMY16hCC8XihB18YTxnYOYX2sSEN + FDWP0a0NAiFU4QluAIQayhxfOVF0C5JoQtHslYMRABrQcprBAAKQhwGQgt8zYEIa2EAAGLTBB8nIxKd4 + AgQk0AYJK+AJPSy9Clbg4hSm4Lsl8nA2XhhkHi6OSxgq0gwSdDwMM1CqGpBAhA/A/6ETHyhECSbmKVLM + AwlW8LwVZgCEQSQiGtuwDRjahh7zzLMN7foCFsAs7TfFEcwDXUMPAIGEu8nXAVqwbx+ywIYhxBc/aRCD + hdGQBi4U4Chjh8MXhJCDIYTBByUIQg2KMMlCzEISK6gwRcHwB2okwxmr0ASoccFwVQzABcEGgjx0UYgf + zIAMVYhXHpphAvWA+QoCGAAQKIJZEUIEFIAN/Eb2LBcJyIAPmEQL+EExCAU2rIEZSEIWdMHPcVfbxBEW + cEFardW0DcEatIEXyIM8eEFoTMQaSA03PEAbhAGd7AMmcB0ikYGcaAEXNIEAeIMPzEDRSV8aIEEncEMB + wJE+yf8AFPRANOjAvayBHPxCLZiCK1iDahCDLfTCKjgDCbhABZSAF4zB+xDCD5RBnJhBKDTDCRQdF8Te + EfSBPKBCAbwBdPWAH9RCEHiBC4yCKvxCInzAB8jajAlCM1xD+5iB2CFBG6hTmlzUEpgHHqlRGoAFGkhB + IRTAE9xNFVRBGtjGuNVLBowAOJhDQSGS0a3HenTBFPTAFRzB+fwAGAiAMhQBf4WBGDRABTjBERRAHvgA + FkCBHnhDFCLcKqxCM4ADLvRCUkWDDZRAESZWCRRCADTAGsRBISTDCdwNWbyJsxiHD7gBDfiBKCRCC3QB + FPjBKtyOIPhEMaiCWmTCNkBFzxH/AhiUifqQV9vUT9qNGbXBjdvMQgGMwAhk4hggQQ/Aj9GFgRDcgzUg + AKANgfLhy92A2QzUQAH4gA/IQw+EgBKIwRN4VhgEwSPYwxNUwQ8kwgpMwEUAwyaoAivIwiasQjIIg6oU + AijYgDKUgqutwRg4Qc21gAxIgjDYgBrcTR6hUhyxAAsQASEowyx4wxfsQB5UQi1I0TTMgjGYCC2Awzuy + QRYEQRQEgBt4gT2qketZQSLKCf/p1RqgADCcARlMBBpcgR8EAZyIRT4wgzngTbC0gUM6AT8AARV0gUXR + kRaAIQwAS7lJARRwQzCEAgtkwAz8QB7sQShUAytQgiwIQy+w/8JLAoPrEMIEzEAHxMAUNAAWqMEF+MQe + lINmaQAd2ZFOksEabIEN+JsrOAcwAEEScMMqWAIkwMKn0IIzNANPvaMhdgEbyEsebAFywlPbYAWYOWK9 + mGIREMIRpMEadAEVFEIWlEkOkEEa+MMzOAOmyM96oFINtMQrFEAZdJ3yeYAPjEGcWIEueEMe7MIskABA + FgE3hMIvxMIhmIIsBIMwJEMxmIIteI8UeYYmkoEVLIEKXEEIeBE40MEOVAHTmVlYcIEhqgIi0MEiDMAA + cAMcSAERVMIyCAIv8MEehMwshEIyXAOboJN5rEEW+EEngIGzTRSYkcESuImbtE1YtE0WnP9gF/yAF7FA + RcVJGozANwADFqRBKeILGqgBIIzCKCCEEgAaG5TaRJaiEiRVBJRCMdRCAYgBEtyBJCxDgJ4CK/QCMPzC + OiSDK+wC933MADhBE8hnRa2BFYBBIQDDPLgAVwgBB1iBVHABFHSCJgQADSRBor0BEnCBbSzXLMQCNZQC + L0hDNMRoD8TAeHAFG5hBH+RBBQRBF4wgj47BErTBHQ1p28hNABhBHRTCz0wUGlAfB5iDMBhAHSFSsBid + GGBBCCBCH3glFqjAVCjBGqhADahBFTieIthDB6ACLxTDbbHAHlDDKhxCpjFcMSSDPbiDL7yCJAgAHABC + CawBFlwiUbb/gRkkAjBAAjf4gBIgwRgMAQeggBp0gRtUAihIwRLIgw8oa9vEgSUMgB7EFi1MQyyQQjZc + Q5toBbVlpwAAARRkgShdgRlUhes5QUVdVSKqQRYITRAgwRa4gatqgaEaADgwAwG0DcOIWbAuWxGwQIq0 + QR/oaABEACIUQQcQlBUcVhgYgCTwAigkARAExSaYAjNcQzDcgjAUA/mtQve4QBkIpvKFQRUQJRrMgVD8 + Qc9QQcBdWwHMwBr4gF5owGCdJheYBBTMAqL5AR9YQi2gAyZswzWkKlTMk7qAgQ0UwKtJQQsAAQBOQRCA + QQ9QwRVcwRdMwRd8Ux9EAZjJzRXYkQKg/0MyWIM/oF1/4UsppsESWKQmtoESCID0+IElFMESTF8hhUEO + OAAcmIoF6IE0UMMmFEM5ZIM6MAMzbEoz2AIi/MEPIAEBhUVo4AsXzEEyJEIJOAEcPIAXPCgVXIABFEEF + 0MEE2IPaXNURdMIWQAEhrMAPKIMMMBMpiILedkEXmIGEzRE7/UANTAEKJAAikGgFLKMN/IFZDcAUbMER + 6AAcoAL7WsEYqAAc8FcK7IMz9MNfSV+9GB1tEGUaWEG0NuEkTdI8VoGyEVScjAAzWcwsJAMwAAMuAMM0 + 6EM8WIONCIMAggIoFEGwkAH15ZHU3EEzzMMRVEEXCAARiGUTdAEW/P8AJCjCi/LDESwJEBRBF9AAIcwd + jxyAK0kDC/dBN4BBDTRi2k7BVlyVGjSBE7QL+3TBFQSBKq5ZL85CDZRB+oAZFRQBnTBAP+RAG2jAIpTB + 3RRUsAZLqYVuGyBBCZQVIqKBu4CbGMzASZFCJDRDMphCMchCmfIDA+yDNRRDLGACKQABHGDeDKDBBjik + etgYKNRCC4xkGswAIKSeGswRjClCc4wyN/yCEmGBDVjCCkzvGCRABdguM1iDGbCv+nTBHZJXj6pqHMnm + VIxB7R0BIJjBkrZNGfQAB4hABkSrwlTYlN6LHk+pBKPBF7iAQcYTPnEBIqUADPgBJESDNKhCMDj/Ayfk + AjAMgAeIgDkAAyyMgjQkQQdIxxUYHVGGRhzQUBKgARfc3xF4EpxwgRckQQtgECYk7I5hbylMALTCpxHg + wTpYA7qwrxdsBceOlXnI01hSxZtgwQYGwQ8k4nl53A8YSnr9D0UFKy1SKaA9JKBFcyLUwAsSEApoIhqI + wQggQR0kQiiQwiH0wjQ8gikkQzRAQRgowDuUwjAUggusQQxYQgvU5d2wAfPSgRRwQTyFhQp0QhF4ARcw + ABZEgRR0ARDEAiioQBZYjR9UBgt4wQUMgBzsQYxmg/lUgVTIC1TUKBmMQexZFEURsxWYJRIsyw4EQfuI + 2RVwwxHgqnoQkPJJ/ykfby24Bas+ucLcKUEBIMESLIG9QNYRBMAveIMwUMIqiAM1KIItTAMcgA4gYAcd + hMIFHEEN1EIJiIFlb2IdZMMKkNsYTARZNEGOKoECAEIpAIIUYJs8NAAZSN4sPEQhhMAApCgfZAMzkIP5 + BPYPXEEcjUd5vMv8pF1FgZlfGwEgAMFcRidFrcEUqOxESbC9BKuUdp3X5rcZVMIrcEMFhMAjXEEZfEEp + hoER+MEvJMMmUMIpWAM/TACxSUIL/MAr9IWHsMAc9UAi6IDRHawkTEMc0EbXagF5eAEIAIMMWMFJRUMD + 5IAXdAEKLAGMPcIyfEARgAIvyFQisDA5UMVWyP+L+mgFkRd00eHRHH14E0CCEcSBALgByIpZHAXBeGH2 + QYaule+x8rUBGERCIeRBAUhBE6hBBd8LmupBIjC4I/BdMkDAGADBp3kDU1mCHsxCJ3QymgJCJ0RBGsDA + GchBMnTeh9vg/HRBDfSAEyABFBgBOijBu1zUGjQBi0XBEZSk3RLD05LDlVlFCCCnOoF0FbTNQ4JuG1wB + IZTAFpTJl4UyRcmL1oKunKgBCrSnlk/pFwjAGZG5GJSaFTwWV5NPg8sQMLxDBoigDGRCJhACHLjBAJTC + jsiJGDjBLADCEViBEeSfDLiqDVabSd+REIKCDnQBGQiBFTwBFsDAUIqvDHj/QQ0kQDpIAx9IQzGQwziY + T6lTQY2C9HjYkRzFkdG1gRrIgxGEAKouaX4HqxlQQUV9eNpFex+oAfLVNPIFqxNsqW8TVKApAxScwLFE + 4UcEQzbEwwbEFxIEgCZwAw2wgVBbQgyUYhsQgSKEwg4wL421AdUNNgdcGQYKACl4QycYwXqsAQTMixeE + gCWwQBGsQzOoxR64wzXQe/10QQjQy1gRuVaEheyR+hXUwA70gSXIgKqOWXr7gBl089GpACLAkQd3s9Ed + ppTetxhEhQ/QwR5gginASC9oQywIwzvs0QhEdiSAAhIoHwvkQQh0ZNGZwcPpgYyBAlqFBUEHURVkQQAc + /4EUEMBK8IO466QPRoE/PkEMAMOR/cIfAMM1jIM4VIEZgIF4i+qQT4V4gGeS86gZkHU5KkIhdJvyFV0p + ioG8FN1lv8kXEAIK7DSWE7QS7LSbiYFZlIAfSMM7N4MNlTAwuMM6oIPQ2ACoJAHy5aBnMIGzuIAf6AIh + gMI0fMCL1+VWkEERPEDsdUE3hAIUdC0dkQEW+AAhfE4NDAAtuILYOANAZBsnTg0YH13YrGGTcE1DNmXK + sKmChg2ZhWjQoOhWxguQUrbOtGGTZuRFLWyuXGmDxmIbNU1CzDqShiYammw81JiRJoxNNGHCkEHy5k8z + SsGELftUq1msXsCWGZiBxf+IJRtltKBZg8rejCs9/BDSE2DZsSRcOOSw2aaJDx8PwDRZ6IFWCS9rtJBp + Q8NSIShrLjxIB8gPKWbkBirJc2WhQoaPF1Zhg6YKGZtdAJlxWeBECBVjVqbhIqTNSjFqejhJY3NMDScx + ABWA0WY1zzRH/HQBGiZrzzQ0BCXTdOrar0SssmFYZslevgVDmCDpU8tIGjJhCCSiY2kAjRB6/gCjg2T1 + CCtgHgAqJI8QoR9e1HhB0QlJm6xl4Fia8Jafgn0H8NjFGsTEAeMNLyBzSCE1sGgjh4QWamONOqYozYsl + 4FtjiTVKI4OM1TBq44waaEtDiRrU0ACLNZRAoQwQ25j/YhYpQOypjS9swEMVSjjpRRt78sjklgEOsOSe + DfBJYQgygvCDGzNWm4KQRCTBAIkr9CCkFmlaoK2NC2Lh5oy7nvhiFh8S6qKPHxTqgohBImiiiGhqcMCB + OOgghhxyBJIjCwgBXQOLLlxasTKG2sjCEitKY6O00sbY8NHS0ihtjSC6WE2NBFDA4qYCetBBQjD6IEWG + JjBCQ4s0iOBjFkpWwQWXbOApQRJNpKEBkQJyYEJJiszooQLdSiBkFEkkecEMPbKhY5ZOjrDJiEda8KKN + I2IAYpACrFDIiVmM8IKHUjqRYgwrEHCAgirmCKUYxAbiJg4IFdLqWjfAUIkNIdSQ/3CNJAqhIkKKFlKj + C1KPGEMNhdvgwlEz3HC0Q+toyiIWQGSgwYbCBgDjidrK+EAQVYIRp5dTeoEngwl2uWWCCzpxwteeuGiD + CEKAmKGOXSzhxRI98OBmGgssmIUEHXRQpgIPhmAhHUIUiYUIIThAwgtUAjBhkFlYQKMLCRgIO4o3FBkw + G3LEsWELkRZqmw0z/KgDITasAM0LIrgRgAdCuVhDjSWscKKOAAppQg01rFADDSsYP4ObJkpL1aY0WCgl + kl0GiMCPR4xow7c1TPCjlGCasUcdWazJBwcdBnBEmhJ6qIEJJrQACokA7iAkAh94ISYTYpThoxlBEilk + Ozq4Kf+GhC6q6KaUf14p5UA0OFDhBUIemYWGh8pgwAF8GGABj2W2gVccN8xwiGCXYDAjCC8wqqIhN0qp + hBtQGlyjjIZKM+N+OMoQOUc1pgk2OIITuDC51bABDISohCX01oNM2QR0v3DEKoohDn+4QxjCgEcKmPCC + 0ZngHsuAQK9sIh0eBKEQESjFKjJhiSk14xftIcQsSrGLTCTDBV4gwC7ygAlgzIMFXnBCBeiQCWCcwAtk + qIIVGLAAc3QgDnoYEIHC0S2HuE1iTkDIGqxQhi5wIxZ0cBKhLDIwJWTBDFOIAYdGQhGaxAgMXdgf40Kz + Bh8QAQ4FGAH8LOMFE/zBEZwARjD/1tGBfbAjGODgAA6sQIJVTGMChehBDoZgBSw0qQVE8AMJClEMYkjD + GzQsxA+gMIMLKIMUlkjGBNSAhCz0oRSlSAQGgICB9rgyAl6wQhVyEEUKsMAOidjGQMQhjmrcZQ1eiEhj + GuM3NThBCWswAyHc4IYzQEEJXCwYh9pgBSWshCJsS0MX6uAFklgBBliI4wVYcIExeGAiaPACBn6xiUNY + gxqZsMYBcDCCWFijAUNIAQ8iYQo6dMINToyJHwYhiTq4IA+CsEQikJGIZoCiWk8QwxBqMItazKIQD/CC + F+ZACF7wghC1JMUHeFCHRLxgDcBcAAPIYIY9SCMbyQxHONrR/4WTNoSoHFFCE7qghoZYIQqdqAF8oNk2 + trmtDViwglSlSgUqrCQMbFDDDDCEty+0gQwjeEIULEAIfcqCGvMY3QRyUAV+MIMf+RhBF0jgiGV4AxBI + eEIQBnFDIkhBCn2QBDfykIhlRIMOEXiAD3QyBCMMIiyzEEABAuAHYqhiF9KwZbXeYAlBfAAJKcCHP7zg + gj1YYyA/bccxknoXZyokcU5g5v6K0INuWCshWgkgoCCzEMCJhCQsaYMXgqCQnqBhDE4oQiFq4U6KuOAG + paAEK07BDGnwYxa44MYYxmAAYEwjAVoYwgsMYYoalsBrRahDIZJQBETkoRB6uMMfjOGN6v8OghrTEMAT + 9PiIw3YiGbGAhCpmoYtalOIRJ6DBH2JRjLY6wXtS0AMotuHTY7SDHkk9XHycUIb4KKgMSehEN5DABi5w + AQtLKAMXwPvMNThsDVppyBLU4M00ZKEJxUWDGLxQh0jkKg1eOAEfrJsLpDCjHPrghiZAcQQyPKEbwajF + BTLwhA+sYh0kgIMXqtCGGHCjBCpQAhHAMIs/SCOjhChEIUgRilrQQAxI6EGykLCFCZSCFn/QBSxWsQwS + YEARrgCGM5ZBh7vSYA/LQFs4qtGOQNAjDmowwxac6WGHQAQKgCjFX/S3BoUtZMUyJipRvaqCernNClfR + ihaqQIM8RGL/GidIAgb8YItg5CIWybCGNeLBgBJgIhkskAwMhAGMaORADDrogTO8UQgjoKENS3gfRjjS + wHIUAnvzCEUnQsE1jByBFDYoQg4uEAlYlEISs6AFKOggCVcUAxwC8obV5PCHcogD0hymBz2YaAYPHy4L + W0DcvwphifedWpoDdJtjtki3blIkjWyoAxxyrIUSPKIUU+JDIBQxCVyoQhjTkEQyshEPCsyAG6voSho6 + UIpm8EMCHvDCBKyRjEjYAH5reMASQFTkmxVjS9woRCikEQIEaWEGYEjGAGbgBEQ84qIkaAEPJvCIaQCj + FJagxgHUEAU8SGMckebwpAMhgDN40WBC/wUDKGyQhSRwgxADcEIz8d7M9CWoYCe9wlXZsIQlRMZhbTDD + NKAgoR+0RxCFSEQxKCEMaqzCGhNogDLWkY8MpOADrshGByhXCGtsFxEVmEABgJGIWiS+DF5M1Ui8AAaZ + R0MQ0ljGk8jKBSt0oQe1aEEVCJAMS0TjBDxwwyPcYIQKgMISobDtC/bgDrNPmh6TrsADlLCwpJpBBoXI + wwm4IQ8enEENHBmqGnQABjMwBOJvC8IcBiAFojbBCQvJgQTIsAYwAMGOLQgLIVzBFByhF0DBAjKhF06A + CSCAFNhhBMLABUpBFSZACoKAEGJBGB4BEBogPrrhacCADbSACSojVTsqxQtkgBaIQYYEgAbaYH+eYAye + oAkAQRpcQDp4ARgegRtm4QWkoAsewB4KgRuSQAqKSRyq79+QkB4CAgA7 + + + \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Keys.cs b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Keys.cs new file mode 100644 index 00000000..44ef3d38 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Keys.cs @@ -0,0 +1,38 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.IocQuickStart.AppContext +{ + /// + /// Contain keys for localization + /// + public class Keys + { + public static string HELLO_MESSAGE = "HelloMessage"; + + public static string FEMALE_GREETING = "FemaleGreeting"; + + public static string BUBBLECHAMBER = "bubblechamber"; + + public Keys() + { + } + } +} diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/MyResource.es.resx b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/MyResource.es.resx new file mode 100644 index 00000000..08a6686b --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/MyResource.es.resx @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Hola {0} {1} + + + Senora + + \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/MyResource.resx b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/MyResource.resx new file mode 100644 index 00000000..4ca5b295 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/MyResource.resx @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Hello {0} {1} + + + Mrs. + + \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Person.cs b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Person.cs new file mode 100644 index 00000000..dc095987 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Person.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.IocQuickStart.AppContext +{ + /// + /// Summary description for Person. + /// + public class Person + { + private int age; + private string name; + + /// + /// Property Name (string) + /// + public string Name + { + get { return this.name; } + set { this.name = value; } + } + + /// + /// Property Age (int) + /// + public int Age + { + get { return this.age; } + set { this.age = value; } + } + + public Person() + { + } + + public override string ToString() + { + return "Person = [Name=" + Name + ", Age="+ Age + "]"; + } + } +} diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Person.resx b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Person.resx new file mode 100644 index 00000000..d9784e16 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Person.resx @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + John + + + 35 + + \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Program.cs b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Program.cs new file mode 100644 index 00000000..cd7e490e --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/Program.cs @@ -0,0 +1,136 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Drawing; +using System.Globalization; +using System.Windows.Forms; + +using Spring.Context; +using Spring.Context.Support; + +#endregion + +namespace Spring.IocQuickStart.AppContext +{ + /// + /// Sample application showing use of IApplicationContext + /// + internal class MainApp + { + private static ResourcesDisplayForm form; + + /// + /// The main entry point for the application. + /// + [STAThread] + private static void Main() + { + try + { + IApplicationContext ctx = ContextRegistry.GetContext(); + form = new ResourcesDisplayForm(); + + DemoMessageLocalization(ctx); + DemoImageResource(ctx); + Application.Run(form); + } + catch (Exception e) + { + write(e.Message); + write(e.StackTrace); + if (e.InnerException != null) + { + write(e.InnerException.Message); + } + } + } + + /// + /// Retrieve the image from the application context. + /// + /// An instance of the Spring application context + public static void DemoImageResource(IApplicationContext ctx) + { + Image image = ctx.GetResourceObject(Keys.BUBBLECHAMBER) as Image; + write("------------"); + write("Loaded image"); + write("Width = " + image.Size.Width + ", Height = " + image.Size.Height); + + form.Image = image; + } + + /// + /// Retrieve various localized messages from the application context. + /// + /// + public static void DemoMessageLocalization(IApplicationContext ctx) + { + CultureInfo spanishCultureInfo = new CultureInfo("es"); + + write("----------------------------------"); + write("Resolve message key 'HelloMessage'"); + write("----------------------------------"); + string msg = ctx.GetMessage(Keys.HELLO_MESSAGE, + CultureInfo.CurrentCulture, + "Mr.", "Anderson"); + + write("Current culture resolved message = " + msg); + + string esMsg = ctx.GetMessage(Keys.HELLO_MESSAGE, + spanishCultureInfo, + "Mr.", "Anderson"); + write("Spanish culture resolved message = " + esMsg); + + + write("--------------------------------------------------------------------------"); + write("Now using message argument that itself implements IMessageSourceResolvable"); + write("--------------------------------------------------------------------------"); + + string[] codes = {Keys.FEMALE_GREETING}; + DefaultMessageSourceResolvable dmr = new DefaultMessageSourceResolvable(codes, null); + + msg = ctx.GetMessage(Keys.HELLO_MESSAGE, + CultureInfo.CurrentCulture, + dmr, "Anderson"); + write("Current culture resolved message = " + msg); + + esMsg = ctx.GetMessage(Keys.HELLO_MESSAGE, + spanishCultureInfo, + dmr, "Anderson"); + write("Spanish culture resolved message = " + esMsg); + + Person p = new Person(); + write("------------------------"); + write("Appling Person resources"); + write("------------------------"); + ctx.ApplyResources(p, "person", CultureInfo.CurrentUICulture); + write(p.ToString()); + } + + public static void write(string text) + { + Console.WriteLine(text); + form.AppendText(text + "\n"); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/ResourcesDisplayForm.cs b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/ResourcesDisplayForm.cs new file mode 100644 index 00000000..ea90f4c9 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/ResourcesDisplayForm.cs @@ -0,0 +1,128 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; + +#endregion + +namespace Spring.IocQuickStart.AppContext +{ + /// + /// Summary description for ShowImageForm. + /// + public class ResourcesDisplayForm : Form + { + private PictureBox pictureBox1; + private RichTextBox richTextBox1; + /// + /// Required designer variable. + /// + private Container components = null; + + /// + /// Property Image (Image) + /// + public Image Image + { + set + { + this.pictureBox1.Image = value; + } + } + + public void AppendText(string text) + { + richTextBox1.AppendText(text); + } + + public ResourcesDisplayForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + + // + // TODO: Add any constructor code after InitializeComponent call + // + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.richTextBox1 = new System.Windows.Forms.RichTextBox(); + this.SuspendLayout(); + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(208, 328); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(146, 119); + this.pictureBox1.TabIndex = 0; + this.pictureBox1.TabStop = false; + // + // richTextBox1 + // + this.richTextBox1.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.richTextBox1.Location = new System.Drawing.Point(16, 16); + this.richTextBox1.Name = "richTextBox1"; + this.richTextBox1.Size = new System.Drawing.Size(536, 288); + this.richTextBox1.TabIndex = 1; + this.richTextBox1.Text = ""; + // + // ResourcesDisplayForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(568, 470); + this.Controls.Add(this.richTextBox1); + this.Controls.Add(this.pictureBox1); + this.Name = "ResourcesDisplayForm"; + this.Text = "ResourcesDisplayForm"; + this.ResumeLayout(false); + + } + #endregion + } +} diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/ResourcesDisplayForm.resx b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/ResourcesDisplayForm.resx new file mode 100644 index 00000000..90b59526 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/AppContext/ResourcesDisplayForm.resx @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + (Default) + + + False + + + ResourcesDisplayForm + + + False + + + 8, 8 + + + True + + + 80 + + + True + + + Private + + \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/Spring.IocQuickStart.AppContext.2003.csproj b/examples/Spring/Spring.IoCQuickStart.AppContext/src/Spring.IocQuickStart.AppContext.2003.csproj new file mode 100644 index 00000000..f671c81a --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/Spring.IocQuickStart.AppContext.2003.csproj @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.AppContext/src/Spring.IocQuickStart.AppContext.2005.csproj b/examples/Spring/Spring.IoCQuickStart.AppContext/src/Spring.IocQuickStart.AppContext.2005.csproj new file mode 100644 index 00000000..a77ff771 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.AppContext/src/Spring.IocQuickStart.AppContext.2005.csproj @@ -0,0 +1,130 @@ + + + Local + 8.0.50727 + 2.0 + {880EA873-E951-465A-AC5B-BA899A2C744E} + Debug + AnyCPU + + + + + Spring.IocQuickStart.AppContext + + + JScript + Grid + IE50 + false + WinExe + Spring.IocQuickStart + OnBuildSuccess + + + + + + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Designer + + + Designer + + + Designer + + + Person.cs + Designer + + + ResourcesDisplayForm.cs + Designer + + + + + + Form + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.EventRegistry/Spring.IocQuickStart.EventRegistry.2003.sln b/examples/Spring/Spring.IoCQuickStart.EventRegistry/Spring.IocQuickStart.EventRegistry.2003.sln new file mode 100644 index 00000000..5ac2b6d3 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.EventRegistry/Spring.IocQuickStart.EventRegistry.2003.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.IocQuickStart.EventRegistry.2003", "src/Spring.IocQuickStart.EventRegistry.2003.csproj", "{0D35F19E-308E-4A0B-88B1-7E7C2D665053}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {0D35F19E-308E-4A0B-88B1-7E7C2D665053}.Debug.ActiveCfg = Debug|.NET + {0D35F19E-308E-4A0B-88B1-7E7C2D665053}.Debug.Build.0 = Debug|.NET + {0D35F19E-308E-4A0B-88B1-7E7C2D665053}.Release.ActiveCfg = Release|.NET + {0D35F19E-308E-4A0B-88B1-7E7C2D665053}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.IoCQuickStart.EventRegistry/Spring.IocQuickStart.EventRegistry.2005.sln b/examples/Spring/Spring.IoCQuickStart.EventRegistry/Spring.IocQuickStart.EventRegistry.2005.sln new file mode 100644 index 00000000..0eeb8bc6 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.EventRegistry/Spring.IocQuickStart.EventRegistry.2005.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.IocQuickStart.EventRegistry.2005", "src/Spring.IocQuickStart.EventRegistry.2005.csproj", "{0D35F19E-308E-4A0B-88B1-7E7C2D665053}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0D35F19E-308E-4A0B-88B1-7E7C2D665053}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D35F19E-308E-4A0B-88B1-7E7C2D665053}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D35F19E-308E-4A0B-88B1-7E7C2D665053}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D35F19E-308E-4A0B-88B1-7E7C2D665053}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.IoCQuickStart.EventRegistry/Spring.IocQuickStart.EventRegistry.build b/examples/Spring/Spring.IoCQuickStart.EventRegistry/Spring.IocQuickStart.EventRegistry.build new file mode 100644 index 00000000..08058fe5 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.EventRegistry/Spring.IocQuickStart.EventRegistry.build @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/App.config b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/App.config new file mode 100644 index 00000000..250f3eee --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/App.config @@ -0,0 +1,34 @@ + + + + + +
    +
    + + + + + + + + + + + An example demonstrating the event registry. + + + + + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/MyClientEventArgs.cs b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/MyClientEventArgs.cs new file mode 100644 index 00000000..3f337294 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/MyClientEventArgs.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.IocQuickStart.EventRegistry +{ + public class MyClientEventArgs : EventArgs + { + private string _eventMessage; + + public MyClientEventArgs(string eventMessage) + { + _eventMessage = eventMessage; + } + + public string EventMessage + { + get { return _eventMessage; } + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/MyEventPublisher.cs b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/MyEventPublisher.cs new file mode 100644 index 00000000..07d7af9c --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/MyEventPublisher.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.IocQuickStart.EventRegistry +{ + public delegate void SimpleClientEvent(object sender, MyClientEventArgs args); + + public class MyEventPublisher + { + private string _publisherName; + + public event SimpleClientEvent MyClientEvent1; + + public MyEventPublisher() + { + } + + public string PublisherName + { + get { return _publisherName; } + set { _publisherName = value; } + } + + public void ClientMethodThatTriggersEvent1() + { + if (MyClientEvent1 != null) + { + MyClientEvent1(this, new MyClientEventArgs("Event 1 raised from " + _publisherName)); + } + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/MyEventSubscriber.cs b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/MyEventSubscriber.cs new file mode 100644 index 00000000..9a7fefb6 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/MyEventSubscriber.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.IocQuickStart.EventRegistry +{ + public class MyEventSubscriber + { + private bool _eventHandled = false; + + public MyEventSubscriber() + { + } + + public void HandleClientEvents(object sender, MyClientEventArgs args) + { + Console.WriteLine("HandleClientEvents handler in subscriber handled event with args: " + args.EventMessage); + _eventHandled = true; + } + + public bool EventHandled + { + get { return _eventHandled; } + } + + public void NeverCall() + { + throw new Exception(); + } + + public string FakeEventHandler(object sender, MyClientEventArgs args) + { + throw new Exception(); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/Program.cs b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/Program.cs new file mode 100644 index 00000000..c34bf560 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/EventRegistry/Program.cs @@ -0,0 +1,96 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Context; +using Spring.Context.Support; + +#endregion + +namespace Spring.IocQuickStart.EventRegistry +{ + /// + /// Small example application showing how objects can publish their events + /// to an IApplicationContex. + /// + /// + ///

    + /// The example then goes on to illustrate how subscribers can subscribe to + /// any events by notifying an IApplicationContext instance. The context + /// will only wire events and event handlers if they have compatible + /// signatures. + ///

    + ///
    + public sealed class Program + { + /// + /// In this example, the subscriber is subscribing by publisher type. + /// + [STAThread] + public static void Main() + { + try + { + // Retrieve context defined in the spring/context section of + // the standard .NET configuration file. + using (IApplicationContext ctx = ContextRegistry.GetContext()) + { + // gets the publisher from the application context... + MyEventPublisher publisher = (MyEventPublisher) ctx.GetObject("MyEventPublisher"); + // publishes events to the context... + ctx.PublishEvents(publisher); + + // gets first instance of subscriber... + MyEventSubscriber subscriber = (MyEventSubscriber) ctx.GetObject("MyEventSubscriber"); + // gets second instance of subscriber... + MyEventSubscriber subscriber2 = (MyEventSubscriber) ctx.GetObject("MyEventSubscriber"); + // subscribes the first instance to any events published by the MyEventPublisher type... + ctx.Subscribe(subscriber, typeof (MyEventPublisher)); + + Console.WriteLine("Publisher name: " + publisher.PublisherName); + // must be false for both subscribers as no event has yet been raised... + Console.WriteLine("Subscriber 1 Event Handled: " + subscriber.EventHandled); + Console.WriteLine("Subscriber 2 Event Handled: " + subscriber2.EventHandled); + + // raises a publisher event... + publisher.ClientMethodThatTriggersEvent1(); + + // must be true (subscribed to any events)... + Console.WriteLine("Subscriber 1 Event Handled: " + subscriber.EventHandled); + // must be false (did not subscribe to any events)... + Console.WriteLine("Subscriber 2 Event Handled: " + subscriber2.EventHandled); + } + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + finally + { + Console.WriteLine(); + Console.WriteLine("--- hit to quit ---"); + Console.ReadLine(); + } + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/Spring.IocQuickStart.EventRegistry.2003.csproj b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/Spring.IocQuickStart.EventRegistry.2003.csproj new file mode 100644 index 00000000..044e447d --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/Spring.IocQuickStart.EventRegistry.2003.csproj @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/Spring.IocQuickStart.EventRegistry.2005.csproj b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/Spring.IocQuickStart.EventRegistry.2005.csproj new file mode 100644 index 00000000..e9400aed --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.EventRegistry/src/Spring.IocQuickStart.EventRegistry.2005.csproj @@ -0,0 +1,100 @@ + + + Local + 8.0.50727 + 2.0 + {0D35F19E-308E-4A0B-88B1-7E7C2D665053} + Debug + AnyCPU + + + + + Spring.IocQuickStart.EventRegistry + + + JScript + Grid + IE50 + false + Exe + Spring.IocQuickStart + OnBuildSuccess + Spring.IocQuickStart.EventRegistry.Program + + + + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + System + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder.2003.sln b/examples/Spring/Spring.IoCQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder.2003.sln new file mode 100644 index 00000000..c686ef4b --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder.2003.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.IocQuickStart.MovieFinder.2003", "src/Spring.IocQuickStart.MovieFinder.2003.csproj", "{C261D9ED-85C3-4B35-BE48-7156F5B17430}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {C261D9ED-85C3-4B35-BE48-7156F5B17430}.Debug.ActiveCfg = Debug|.NET + {C261D9ED-85C3-4B35-BE48-7156F5B17430}.Debug.Build.0 = Debug|.NET + {C261D9ED-85C3-4B35-BE48-7156F5B17430}.Release.ActiveCfg = Release|.NET + {C261D9ED-85C3-4B35-BE48-7156F5B17430}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder.2005.sln b/examples/Spring/Spring.IoCQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder.2005.sln new file mode 100644 index 00000000..42faff40 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder.2005.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.IocQuickStart.MovieFinder.2005", "src/Spring.IocQuickStart.MovieFinder.2005.csproj", "{C261D9ED-85C3-4B35-BE48-7156F5B17430}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C261D9ED-85C3-4B35-BE48-7156F5B17430}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C261D9ED-85C3-4B35-BE48-7156F5B17430}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C261D9ED-85C3-4B35-BE48-7156F5B17430}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C261D9ED-85C3-4B35-BE48-7156F5B17430}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder.build b/examples/Spring/Spring.IoCQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder.build new file mode 100644 index 00000000..e32df937 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder.build @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/1.1/Common.Logging.Log4Net.dll b/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/1.1/Common.Logging.Log4Net.dll new file mode 100644 index 00000000..f334f6de Binary files /dev/null and b/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/1.1/Common.Logging.Log4Net.dll differ diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/1.1/log4net.dll b/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/1.1/log4net.dll new file mode 100644 index 00000000..995816f2 Binary files /dev/null and b/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/1.1/log4net.dll differ diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/2.0/Common.Logging.Log4Net.dll b/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/2.0/Common.Logging.Log4Net.dll new file mode 100644 index 00000000..3900bb87 Binary files /dev/null and b/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/2.0/Common.Logging.Log4Net.dll differ diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/2.0/log4net.dll b/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/2.0/log4net.dll new file mode 100644 index 00000000..ffc57e11 Binary files /dev/null and b/examples/Spring/Spring.IoCQuickStart.MovieFinder/lib/net/2.0/log4net.dll differ diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/App.config b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/App.config new file mode 100644 index 00000000..42438f9d --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/App.config @@ -0,0 +1,88 @@ + + + + + +
    + + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + An example that demonstrates simple IoC features. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/AppContext.xml b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/AppContext.xml new file mode 100644 index 00000000..a923d1d0 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/AppContext.xml @@ -0,0 +1,27 @@ + + + An example that demonstrates simple IoC features. + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/AppContextContribution.xml b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/AppContextContribution.xml new file mode 100644 index 00000000..0939737e --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/AppContextContribution.xml @@ -0,0 +1,12 @@ + + + An example that demonstrates contributing on implementation of MovieFinder progammatically. + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/ColonDelimitedMovieFinder.cs b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/ColonDelimitedMovieFinder.cs new file mode 100644 index 00000000..4c491c10 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/ColonDelimitedMovieFinder.cs @@ -0,0 +1,107 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.IO; +using System.Collections; + +#endregion + +namespace Spring.IocQuickStart.MovieFinder +{ + /// + /// An implementation + /// that uses a colon delimited text file as it's backing data store. + /// + public class ColonDelimitedMovieFinder : IMovieFinder + { + private static readonly char [] Delimeter = new char [] {':'}; + + private FileInfo _movieFile; + private IList _movies; + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The file containing colon delimited movies. + /// + public ColonDelimitedMovieFinder (FileInfo file) + { + MovieFile = file; + } + + /// + /// The file containing colon delimited movies. + /// + /// + /// A pointing to the file containing + /// colon delimited movies. + /// + public FileInfo MovieFile + { + get + { + return _movieFile; + } + set + { + _movieFile = value; + if (_movieFile != null && _movieFile.Exists) + { + InitList (); + } + } + } + + /// + /// Finds all of the movies stored in this + /// implementation. + /// + /// + /// All of the movies stored in this + /// implementation. + /// + public IList FindAll () + { + return _movies; + } + + /// + /// Loads all of the movies in the file referred to by MovieFile. + /// + private void InitList () + { + _movies = new ArrayList (); + using (StreamReader reader = MovieFile.OpenText ()) + { + string line = null; + while ((line = reader.ReadLine ()) != null) + { + string [] tuple = line.Split (Delimeter); + Movie movie = new Movie (tuple [0], tuple [1]); + _movies.Add (movie); + } + } + } + } +} diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/IMovieFinder.cs b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/IMovieFinder.cs new file mode 100644 index 00000000..f8a513f3 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/IMovieFinder.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +#endregion + +namespace Spring.IocQuickStart.MovieFinder +{ + /// + /// Describes the interface for finding a list of movies independent of how + /// said movies are actually stored (text file, database, etc). + /// + public interface IMovieFinder + { + /// Finds all of the stored movies. + /// All of the stored movies. + IList FindAll (); + } +} diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/Movie.cs b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/Movie.cs new file mode 100644 index 00000000..f98b93b8 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/Movie.cs @@ -0,0 +1,73 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.IocQuickStart.MovieFinder +{ + /// + /// An object that describes a movie. + /// + public class Movie + { + private string _title; + private string _director; + + /// + /// Creates a new instance of the + /// class. + /// + /// The title of the movie. + /// The director of the movie. + public Movie (string title, string director) + { + _title = title; + _director = director; + } + + /// + /// Property Title (string). + /// + public string Title + { + get + { + return _title; + } + set + { + _title = value; + } + } + + /// + /// Property Director (string). + /// + public string Director + { + get + { + return _director; + } + set + { + _director = value; + } + } + } +} diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/MovieLister.cs b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/MovieLister.cs new file mode 100644 index 00000000..b67b2029 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/MovieLister.cs @@ -0,0 +1,82 @@ +#region Licence + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using Spring.Objects.Factory.Attributes; + +namespace Spring.IocQuickStart.MovieFinder +{ + /// + /// A class that provides a list of movies directed by a particular director. + /// + public class MovieLister + { + private IMovieFinder _movieFinder; + + /// + /// Creates a new instance of the + /// class. + /// + public MovieLister () + { + } + + /// + /// Property MovieFinder (IMovieFinder). + /// + [Required] + public IMovieFinder MovieFinder + { + get + { + return _movieFinder; + } + set + { + _movieFinder = value; + } + } + + /// + /// Returns a list of those movies directed by the supplied + /// + /// + /// + /// The name of the director whose movies we are to return. + /// + /// + /// A list of those movies directed by the supplied + /// . + /// + public Movie [] MoviesDirectedBy (string director) + { + IList allMovies = _movieFinder.FindAll (); + IList movies = new ArrayList (); + foreach (Movie m in allMovies) + { + if (director.Equals (m.Director)) + { + movies.Add (m); + } + } + return (Movie []) ArrayList.Adapter (movies).ToArray (typeof (Movie)); + } + } +} diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/Program.cs b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/Program.cs new file mode 100644 index 00000000..dec3ca8e --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/Program.cs @@ -0,0 +1,179 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using Common.Logging; +using Common.Logging.Log4Net; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.IocQuickStart.MovieFinder +{ + /// + /// A simple application that demonstrates the IoC functionality of Spring.NET. + /// + /// + ///

    + /// See for the background + /// article on which this example application is based. + ///

    + ///
    + public sealed class Program + { + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof(Program)); + + #endregion + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main() + { + try + { + IApplicationContext ctx = ContextRegistry.GetContext(); + + #region Advanced: Call alternative ways to create application context + //CreateContextMixXmlAndProgrammatic(); + //CreateContextProgrammatically(); + //CreateContextProgrammaticallyWithAutoWire(); + #endregion + + MovieLister lister = (MovieLister) ctx.GetObject("MyMovieLister"); + Movie[] movies = lister.MoviesDirectedBy("Roberto Benigni"); + LOG.Debug("Searching for movie..."); + foreach (Movie movie in movies) + { + LOG.Debug( + string.Format("Movie Title = '{0}', Director = '{1}'.", + movie.Title, movie.Director)); + } + LOG.Debug("MovieApp Done."); + } + catch (Exception e) + { + LOG.Error("Movie Finder is broken.", e); + } + finally + { + Console.WriteLine(); + Console.WriteLine("--- hit to quit ---"); + Console.ReadLine(); + } + } + + + #region Implementation of alternative ways to create application context + + private static IApplicationContext CreateContextProgrammatically() + { + InitializeCommonLogging(); + GenericApplicationContext ctx = new GenericApplicationContext(); + + IObjectDefinitionFactory objectDefinitionFactory = new DefaultObjectDefinitionFactory(); + + + //Create MovieLister and dependency on + ObjectDefinitionBuilder builder = + ObjectDefinitionBuilder.RootObjectDefinition(objectDefinitionFactory, typeof(MovieLister)); + builder.AddPropertyReference("MovieFinder", "AnotherMovieFinder"); + + ctx.RegisterObjectDefinition("MyMovieLister", builder.ObjectDefinition); + + builder = ObjectDefinitionBuilder.RootObjectDefinition(objectDefinitionFactory, typeof(ColonDelimitedMovieFinder)); + builder.AddConstructorArg("movies.txt"); + ctx.RegisterObjectDefinition("AnotherMovieFinder", builder.ObjectDefinition); + + ctx.Refresh(); + + return ctx; + + } + + private static IApplicationContext CreateContextProgrammaticallyWithAutoWire() + { + InitializeCommonLogging(); + GenericApplicationContext ctx = new GenericApplicationContext(); + + IObjectDefinitionFactory objectDefinitionFactory = new DefaultObjectDefinitionFactory(); + + + //Create MovieLister and dependency on + ObjectDefinitionBuilder builder = + ObjectDefinitionBuilder.RootObjectDefinition(objectDefinitionFactory, typeof(MovieLister)); + builder.AddPropertyReference("MovieFinder", "BogusNameOfDependency") + .SetAutowireMode(AutoWiringMode.ByType); + + ctx.RegisterObjectDefinition("MyMovieLister", builder.ObjectDefinition); + + builder = ObjectDefinitionBuilder.RootObjectDefinition(objectDefinitionFactory, typeof(ColonDelimitedMovieFinder)); + builder.AddConstructorArg("movies.txt") + .SetAutowireMode(AutoWiringMode.ByType); + + ctx.RegisterObjectDefinition("AnotherMovieFinder", builder.ObjectDefinition); + + ctx.Refresh(); + + return ctx; + + } + + private static IApplicationContext CreateContextMixXmlAndProgrammatic() + { + + GenericApplicationContext ctx = new GenericApplicationContext(); + + IObjectDefinitionReader objectDefinitionReader = new XmlObjectDefinitionReader(ctx); + objectDefinitionReader.LoadObjectDefinitions("assembly://Spring.IocQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder/AppContextContribution.xml"); + + IObjectDefinitionFactory objectDefinitionFactory = new DefaultObjectDefinitionFactory(); + ObjectDefinitionBuilder builder = + ObjectDefinitionBuilder.RootObjectDefinition(objectDefinitionFactory, typeof(ColonDelimitedMovieFinder)); + builder.AddConstructorArg("movies.txt"); + ctx.RegisterObjectDefinition("AnotherMovieFinder", builder.ObjectDefinition); + + + ctx.Refresh(); + + return ctx; + } + + private static void InitializeCommonLogging() + { + NameValueCollection properties = new NameValueCollection(); + properties["configType"] = "INLINE"; + LogManager.Adapter = new Log4NetLoggerFactoryAdapter(properties); + } + #endregion + + + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/SimpleMovieFinder.cs b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/SimpleMovieFinder.cs new file mode 100644 index 00000000..1e2ebec8 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/SimpleMovieFinder.cs @@ -0,0 +1,76 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +#endregion + +namespace Spring.IocQuickStart.MovieFinder +{ + /// + /// Simple in-memory storage implementation of the + /// interface. + /// + public class SimpleMovieFinder : IMovieFinder + { + private ArrayList _list = new ArrayList(); + + /// + /// Creates a new instance of the + /// class. + /// + public SimpleMovieFinder() + { + InitList(); + } + + /// + /// Add a movie to the list. + /// + /// The movie. + public void AddMovie (Movie m) + { + _list.Add(m); + } + + /// + /// Finds all of the movies stored in this + /// implementation. + /// + /// + /// All of the movies stored in this + /// implementation. + /// + public IList FindAll() + { + return new ArrayList (_list); + } + + /// + /// Initialises the in-mememory list of stored movies. + /// + private void InitList () + { + _list.Add (new Movie ("La vita e bella", "Roberto Benigni")); + } + } +} diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/movies.txt b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/movies.txt new file mode 100644 index 00000000..f1160dc7 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/MovieFinder/movies.txt @@ -0,0 +1,7 @@ +Blue Velvet:David Lynch +Magnolia:Paul T Anderson +Cabin Fever:Eli Roth +In The Company Of Men:Neil Labute +Naked:Mike Leigh +La vita e bella:Roberto Benigni +La vita e bella 2:Roberto Benigni \ No newline at end of file diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/Spring.IocQuickStart.MovieFinder.2003.csproj b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/Spring.IocQuickStart.MovieFinder.2003.csproj new file mode 100644 index 00000000..ee640811 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/Spring.IocQuickStart.MovieFinder.2003.csproj @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/Spring.IocQuickStart.MovieFinder.2005.csproj b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/Spring.IocQuickStart.MovieFinder.2005.csproj new file mode 100644 index 00000000..16137bd7 --- /dev/null +++ b/examples/Spring/Spring.IoCQuickStart.MovieFinder/src/Spring.IocQuickStart.MovieFinder.2005.csproj @@ -0,0 +1,119 @@ + + + Local + 8.0.50727 + 2.0 + {C261D9ED-85C3-4B35-BE48-7156F5B17430} + Debug + AnyCPU + + + + + Spring.IocQuickStart.MovieFinder + + + JScript + Grid + IE50 + false + Exe + Spring.IocQuickStart + Always + Spring.IocQuickStart.MovieFinder.Program + + + + + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\..\bin\net\2.0\debug\Common.Logging.dll + + + False + ..\lib\net\2.0\Common.Logging.Log4Net.dll + + + False + ..\lib\net\2.0\log4net.dll + + + False + ..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + System + + + + + + + + + + + + + + + + + + + + + + + echo "Copying movies.txt file for MovieFinder" +copy "$(ProjectDir)MovieFinder\movies.txt" "$(TargetDir)" /y + + \ No newline at end of file diff --git a/examples/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2003.sln b/examples/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2003.sln new file mode 100644 index 00000000..c6153c7c --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2003.sln @@ -0,0 +1,41 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.TxQuickStart.2003", "src\Spring\Spring.TxQuickStart\Spring.TxQuickStart.2003.csproj", "{B3C676F7-52DA-41D7-953C-38C9E62FFDF6}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.TxQuickStart.Tests.2003", "test\Spring\Spring.TxQuickStart.Tests\Spring.TxQuickStart.Tests.2003.csproj", "{026EED0B-267B-4518-AD3B-B3D4291CC2F8}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Debug-1.1 = Debug-1.1 + Release = Release + Release-1.1 = Release-1.1 + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {B3C676F7-52DA-41D7-953C-38C9E62FFDF6}.Debug.ActiveCfg = Debug|.NET + {B3C676F7-52DA-41D7-953C-38C9E62FFDF6}.Debug.Build.0 = Debug|.NET + {B3C676F7-52DA-41D7-953C-38C9E62FFDF6}.Debug-1.1.ActiveCfg = Debug|.NET + {B3C676F7-52DA-41D7-953C-38C9E62FFDF6}.Debug-1.1.Build.0 = Debug|.NET + {B3C676F7-52DA-41D7-953C-38C9E62FFDF6}.Release.ActiveCfg = Release|.NET + {B3C676F7-52DA-41D7-953C-38C9E62FFDF6}.Release.Build.0 = Release|.NET + {B3C676F7-52DA-41D7-953C-38C9E62FFDF6}.Release-1.1.ActiveCfg = Release|.NET + {B3C676F7-52DA-41D7-953C-38C9E62FFDF6}.Release-1.1.Build.0 = Release|.NET + {026EED0B-267B-4518-AD3B-B3D4291CC2F8}.Debug.ActiveCfg = Debug|.NET + {026EED0B-267B-4518-AD3B-B3D4291CC2F8}.Debug.Build.0 = Debug|.NET + {026EED0B-267B-4518-AD3B-B3D4291CC2F8}.Debug-1.1.ActiveCfg = Debug|.NET + {026EED0B-267B-4518-AD3B-B3D4291CC2F8}.Debug-1.1.Build.0 = Debug|.NET + {026EED0B-267B-4518-AD3B-B3D4291CC2F8}.Release.ActiveCfg = Release|.NET + {026EED0B-267B-4518-AD3B-B3D4291CC2F8}.Release.Build.0 = Release|.NET + {026EED0B-267B-4518-AD3B-B3D4291CC2F8}.Release-1.1.ActiveCfg = Release|.NET + {026EED0B-267B-4518-AD3B-B3D4291CC2F8}.Release-1.1.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2005.sln b/examples/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2005.sln new file mode 100644 index 00000000..82fa5f6d --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2005.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.TxQuickStart.2005", "src\Spring\Spring.TxQuickStart\Spring.TxQuickStart.2005.csproj", "{EDDC48D5-4F25-4476-9BA3-12F2E8BB5A8C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.TxQuickStart.Tests.2005", "test\Spring\Spring.TxQuickStart.Tests\Spring.TxQuickStart.Tests.2005.csproj", "{94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EDDC48D5-4F25-4476-9BA3-12F2E8BB5A8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDDC48D5-4F25-4476-9BA3-12F2E8BB5A8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDDC48D5-4F25-4476-9BA3-12F2E8BB5A8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDDC48D5-4F25-4476-9BA3-12F2E8BB5A8C}.Release|Any CPU.Build.0 = Release|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/CommonAssemblyInfo.cs b/examples/Spring/Spring.TxQuickStart/src/Spring/CommonAssemblyInfo.cs new file mode 100644 index 00000000..faffa87e --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/CommonAssemblyInfo.cs @@ -0,0 +1,69 @@ +using System; +using System.Reflection; +[assembly: CLSCompliant(false)] + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// + +#if !NET_2_0 +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +#else +[assembly: AssemblyConfiguration("net-2.0.win32; Release")] +#endif +[assembly: AssemblyCompany("http://www.springframework.net")] +[assembly: AssemblyProduct("Spring.NET Framework")] +[assembly: AssemblyCopyright("Copyright 2002-2006 Spring.NET Framework Team.")] +[assembly: AssemblyTrademark("Apache License, Version 2.0")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// .NET Framework Version +// Major Version +// Minor Version +// Revision +// +// This is as good a convention as any for supporting side-by-side deployment of +// .NET 1.1 and .NET 2.0 versions of the assembly. + +#if !NET_2_0 +[assembly: AssemblyVersion("1.1.0.0")] +#else +[assembly: AssemblyVersion("2.1.0.0")] +#endif + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +#if STRONG +[assembly: AssemblyDelaySign(false)] +#if !NET_2_0 +[assembly: AssemblyKeyFile("Spring.Net.snk")] +#endif +#endif diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2003.csproj b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2003.csproj new file mode 100644 index 00000000..7d21df98 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2003.csproj @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2005.csproj b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2005.csproj new file mode 100644 index 00000000..f57f59bb --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/Spring.TxQuickStart.2005.csproj @@ -0,0 +1,73 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {EDDC48D5-4F25-4476-9BA3-12F2E8BB5A8C} + Library + Properties + Spring + Spring.TxQuickStart + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Common.Logging.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/Spring.TxQuickStart.xml b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/Spring.TxQuickStart.xml new file mode 100644 index 00000000..99ed7b92 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/Spring.TxQuickStart.xml @@ -0,0 +1,8 @@ + + + + Spring.TxQuickStart + + + + diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/CreditsDebitsSchema.sql b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/CreditsDebitsSchema.sql new file mode 100644 index 00000000..6566603a --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/CreditsDebitsSchema.sql @@ -0,0 +1,51 @@ +USE [CreditsAndDebits] +CREATE TABLE [Credits]( + [CreditID] [int] IDENTITY NOT NULL, + [CreditAmount] [float] NOT NULL, + CONSTRAINT [PK_CreditID] PRIMARY KEY CLUSTERED +( + [CreditID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + + +USE [CreditsAndDebits] +GO +CREATE TABLE [Debits]( + [DebitID] [int] IDENTITY NOT NULL, + [DebitAmount] [float] NOT NULL, + CONSTRAINT [PK_DebitID] PRIMARY KEY CLUSTERED +( + [DebitID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + + + +USE [Credits] +GO +CREATE TABLE [Credits]( + [CreditID] [int] IDENTITY NOT NULL, + [CreditAmount] [float] NOT NULL, + CONSTRAINT [PK_CreditID] PRIMARY KEY CLUSTERED +( + [CreditID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + + +USE [Debits] +GO +CREATE TABLE [Debits]( + [DebitID] [int] IDENTITY NOT NULL, + [DebitAmount] [float] NOT NULL, + CONSTRAINT [PK_DebitID] PRIMARY KEY CLUSTERED +( + [DebitID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/Ado/AccountCreditDao.cs b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/Ado/AccountCreditDao.cs new file mode 100644 index 00000000..8b52cac4 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/Ado/AccountCreditDao.cs @@ -0,0 +1,17 @@ +using System; +using System.Data; +using Spring.Data.Core; + +namespace Spring.TxQuickStart.Dao.Ado +{ + + public class AccountCreditDao : AdoDaoSupport, IAccountCreditDao + { + public void CreateCredit(float creditAmount) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + "insert into Credits (CreditAmount) VALUES (@amount)", "amount", DbType.Decimal, 0, + creditAmount); + } + } +} diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/Ado/AccountDebitDao.cs b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/Ado/AccountDebitDao.cs new file mode 100644 index 00000000..b2f4df07 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/Ado/AccountDebitDao.cs @@ -0,0 +1,16 @@ +using System; +using System.Data; +using Spring.Data.Core; + +namespace Spring.TxQuickStart.Dao.Ado +{ + public class AccountDebitDao : AdoDaoSupport, IAccountDebitDao + { + public void DebitAccount(float debitAmount) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + "insert into dbo.Debits (DebitAmount) VALUES (@amount)", "amount", DbType.Decimal, 0, + debitAmount); + } + } +} diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/IAccountCreditDao.cs b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/IAccountCreditDao.cs new file mode 100644 index 00000000..4f0ccfa8 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/IAccountCreditDao.cs @@ -0,0 +1,9 @@ + + +namespace Spring.TxQuickStart.Dao +{ + public interface IAccountCreditDao + { + void CreateCredit(float creditAmount); + } +} diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/IAccountDebitDao.cs b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/IAccountDebitDao.cs new file mode 100644 index 00000000..4520cc26 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Dao/IAccountDebitDao.cs @@ -0,0 +1,8 @@ + +namespace Spring.TxQuickStart.Dao +{ + public interface IAccountDebitDao + { + void DebitAccount(float debitAmount); + } +} diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Services/AccountManager.cs b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Services/AccountManager.cs new file mode 100644 index 00000000..9ebfae0a --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Services/AccountManager.cs @@ -0,0 +1,53 @@ + +using System; +using System.Data; +using Spring.Transaction; +using Spring.Transaction.Interceptor; +using Spring.TxQuickStart.Dao; + +namespace Spring.TxQuickStart.Services +{ + public class AccountManager : IAccountManager + { + + private IAccountCreditDao accountCreditDao; + private IAccountDebitDao accountDebitDao; + + private float maxTransferAmount = 1000000; + + public AccountManager(IAccountCreditDao accountCreditDao, IAccountDebitDao accountDebitDao) + { + this.accountCreditDao = accountCreditDao; + this.accountDebitDao = accountDebitDao; + } + + public float MaxTransferAmount + { + get { return maxTransferAmount; } + set { maxTransferAmount = value; } + } + + + // The following rollback rule will result in commiting of only work done + // by the CreateCreate DAO method. The exception is still propagated out to the + // calling code. + + // [Transaction(NoRollbackFor = new Type[] { typeof(ArithmeticException) })] + + [Transaction(TransactionPropagation.Required)] + public void DoTransfer(float creditAmount, float debitAmount) + { + accountCreditDao.CreateCredit(creditAmount); + + if (creditAmount > maxTransferAmount || debitAmount > maxTransferAmount) + { + throw new ArithmeticException("see a teller big spender..."); + } + + accountDebitDao.DebitAccount(debitAmount); + } + + } + + +} diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Services/IAccountManager.cs b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Services/IAccountManager.cs new file mode 100644 index 00000000..1bff3cf8 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/Services/IAccountManager.cs @@ -0,0 +1,9 @@ + +namespace Spring.TxQuickStart.Services +{ + public interface IAccountManager + { + + void DoTransfer(float creditAmount, float debitAmount); + } +} diff --git a/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/application-config.xml b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/application-config.xml new file mode 100644 index 00000000..fc34c0d2 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/src/Spring/Spring.TxQuickStart/TxQuickStart/application-config.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/AssemblyInfo.cs b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..177a4f0e --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.2003.csproj b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.2003.csproj new file mode 100644 index 00000000..f8874be9 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.2003.csproj @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.2005.csproj b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.2005.csproj new file mode 100644 index 00000000..64c2a242 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.2005.csproj @@ -0,0 +1,98 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048} + Library + Properties + Spring + Spring.TxQuickStart.Tests + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Common.Logging.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Common.Logging.Log4Net.dll + + + False + ..\..\..\..\..\..\lib\Net\2.0\log4net.dll + + + False + ..\..\..\..\..\..\lib\Net\2.0\nunit.framework.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + + + + + + {EDDC48D5-4F25-4476-9BA3-12F2E8BB5A8C} + Spring.TxQuickStart.2005 + + + + + + + + + + + + + + Always + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.build b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.build new file mode 100644 index 00000000..c7213ddd --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.build @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.dll.config b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.dll.config new file mode 100644 index 00000000..e7787480 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/Spring.TxQuickStart.Tests.dll.config @@ -0,0 +1,72 @@ + + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/AccountManagerTests.cs b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/AccountManagerTests.cs new file mode 100644 index 00000000..45d0b019 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/AccountManagerTests.cs @@ -0,0 +1,132 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using NUnit.Framework; +using Spring.Aop.Config; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Data.Config; +using Spring.Data.Core; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Transaction.Config; +using Spring.TxQuickStart.Services; + +#endregion + +namespace Spring.TxQuickStart +{ + [TestFixture] + public class AccountManagerTests + { + private AdoTemplate adoTemplateCredit; + private AdoTemplate adoTemplateDebit; + + private IAccountManager accountManager; + + [SetUp] + public void SetUp() + { + // Configure Spring programmatically + NamespaceParserRegistry.RegisterParser(typeof(DatabaseNamespaceParser)); + NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser)); + NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser)); + IApplicationContext context = CreateContextFromXml(); + IDictionary dict = context.GetObjectsOfType(typeof (IAccountManager)); + accountManager = context["accountManager"] as IAccountManager; + CleanDb(context); + } + + private static IApplicationContext CreateContextFromXml() + { + return new XmlApplicationContext( + "assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/system-test-local-config.xml" + ); + } + + [Test] + public void TransferBelowMaxAmount() + { + accountManager.DoTransfer(217, 217); + + //asserts to read from db... + + int numCreditRecords = (int)adoTemplateCredit.ExecuteScalar(CommandType.Text, "select count(*) from Credits"); + int numDebitRecords = (int)adoTemplateDebit.ExecuteScalar(CommandType.Text, "select count(*) from Debits"); + Assert.AreEqual(1, numCreditRecords); + Assert.AreEqual(1, numDebitRecords); + } + + [Test] + public void TransferAboveMaxAmount() + { + try + { + accountManager.DoTransfer(2000000, 200000); + Assert.Fail("Should have thrown Arithmethic Exception"); + } catch (ArithmeticException) + { + int numCreditRecords = (int)adoTemplateCredit.ExecuteScalar(CommandType.Text, "select count(*) from Credits"); + int numDebitRecords = (int)adoTemplateDebit.ExecuteScalar(CommandType.Text, "select count(*) from Debits"); + Assert.AreEqual(0, numCreditRecords); + Assert.AreEqual(0, numDebitRecords); + } + + } + + + // Run the following test only if you have changed to the alternate implementation + // of the method DoTransfer in AccountManager that specifies the NoRollbackFor property. + [Test] + [Ignore("Change impl of AccountManager as shown before running this test")] + public void TransferAboveMaxAmountNoRollbackFor() + { + try + { + accountManager.DoTransfer(2000000, 2000000); + Assert.Fail("Should have thrown Arithmethic Exception"); + } catch (ArithmeticException) { + int numCreditRecords = (int)adoTemplateCredit.ExecuteScalar(CommandType.Text, "select count(*) from Credits"); + int numDebitRecords = (int)adoTemplateDebit.ExecuteScalar(CommandType.Text, "select count(*) from Debits"); + Assert.AreEqual(1, numCreditRecords); + Assert.AreEqual(0, numDebitRecords); + } + } + + + private void CleanDb(IApplicationContext context) + { + IDbProvider dbProvider = (IDbProvider)context["DebitDbProvider"]; + adoTemplateDebit = new AdoTemplate(dbProvider); + adoTemplateDebit.ExecuteNonQuery(CommandType.Text, "truncate table Debits"); + + dbProvider = (IDbProvider)context["CreditDbProvider"]; + adoTemplateCredit = new AdoTemplate(dbProvider); + adoTemplateCredit.ExecuteNonQuery(CommandType.Text, "truncate table Credits"); + + } + } +} diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/CreditsDebitsSchema.sql b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/CreditsDebitsSchema.sql new file mode 100644 index 00000000..6566603a --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/CreditsDebitsSchema.sql @@ -0,0 +1,51 @@ +USE [CreditsAndDebits] +CREATE TABLE [Credits]( + [CreditID] [int] IDENTITY NOT NULL, + [CreditAmount] [float] NOT NULL, + CONSTRAINT [PK_CreditID] PRIMARY KEY CLUSTERED +( + [CreditID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + + +USE [CreditsAndDebits] +GO +CREATE TABLE [Debits]( + [DebitID] [int] IDENTITY NOT NULL, + [DebitAmount] [float] NOT NULL, + CONSTRAINT [PK_DebitID] PRIMARY KEY CLUSTERED +( + [DebitID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + + + +USE [Credits] +GO +CREATE TABLE [Credits]( + [CreditID] [int] IDENTITY NOT NULL, + [CreditAmount] [float] NOT NULL, + CONSTRAINT [PK_CreditID] PRIMARY KEY CLUSTERED +( + [CreditID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + + +USE [Debits] +GO +CREATE TABLE [Debits]( + [DebitID] [int] IDENTITY NOT NULL, + [DebitAmount] [float] NOT NULL, + CONSTRAINT [PK_DebitID] PRIMARY KEY CLUSTERED +( + [DebitID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/Services/AccountManagerUnitTests.cs b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/Services/AccountManagerUnitTests.cs new file mode 100644 index 00000000..8b5c5f46 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/Services/AccountManagerUnitTests.cs @@ -0,0 +1,62 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.TxQuickStart.Dao; + +#endregion + +namespace Spring.TxQuickStart.Services +{ + /// + /// This class contains unit tests for IAccountManager + /// + /// Mark Pollack + /// $Id: AccountManagerUnitTests.cs,v 1.1 2007/12/07 02:36:20 markpollack Exp $ + [TestFixture] + public class AccountManagerUnitTests + { + private IAccountManager accountManager; + + [SetUp] + public void Setup() + { + IAccountCreditDao stubCreditDao = new StubAccountCreditDao(); + IAccountDebitDao stubDebitDao = new StubAccountDebitDao(); + accountManager = new AccountManager(stubCreditDao, stubDebitDao); + } + + [Test] + public void TransferBelowMaxAmount() + { + accountManager.DoTransfer(217, 217); + } + + [Test] + [ExpectedException(typeof(ArithmeticException))] + public void TransferAboveMaxAmount() + { + accountManager.DoTransfer(2000000, 200000); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/Services/StubAccountCreditDao.cs b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/Services/StubAccountCreditDao.cs new file mode 100644 index 00000000..addb0cc3 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/Services/StubAccountCreditDao.cs @@ -0,0 +1,12 @@ +using Spring.TxQuickStart.Dao; + +namespace Spring.TxQuickStart.Services +{ + public class StubAccountCreditDao : IAccountCreditDao + { + public void CreateCredit(float creditAmount) + { + + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/Services/StubAccountDebitDao.cs b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/Services/StubAccountDebitDao.cs new file mode 100644 index 00000000..d85a4b02 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/Services/StubAccountDebitDao.cs @@ -0,0 +1,12 @@ +using Spring.TxQuickStart.Dao; + +namespace Spring.TxQuickStart.Services +{ + public class StubAccountDebitDao : IAccountDebitDao + { + public void DebitAccount(float debitAmount) + { + + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/aspects-config.xml b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/aspects-config.xml new file mode 100644 index 00000000..8c36f04f --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/aspects-config.xml @@ -0,0 +1,44 @@ + + + + + + + + + on exception name ArithmeticException log 'Logging an exception thrown from method ' + #method.Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/system-test-dtc-config.xml b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/system-test-dtc-config.xml new file mode 100644 index 00000000..e42321fe --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/system-test-dtc-config.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/system-test-dtc-es-config.xml b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/system-test-dtc-es-config.xml new file mode 100644 index 00000000..a89d7535 --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/system-test-dtc-es-config.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/system-test-local-config.xml b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/system-test-local-config.xml new file mode 100644 index 00000000..e87089ad --- /dev/null +++ b/examples/Spring/Spring.TxQuickStart/test/Spring/Spring.TxQuickStart.Tests/TxQuickStart/system-test-local-config.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Web.Extensions.Example/Spring.Web.Extensions.Example.2005.sln b/examples/Spring/Spring.Web.Extensions.Example/Spring.Web.Extensions.Example.2005.sln new file mode 100644 index 00000000..dd4c233e --- /dev/null +++ b/examples/Spring/Spring.Web.Extensions.Example/Spring.Web.Extensions.Example.2005.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "L:\...\Spring.Web.Extensions.Example.2005\", "src\Spring.Web.Extensions.Example.2005\", "{DE2A587C-01A4-49E2-A3CD-664B293A8A6F}" + ProjectSection(WebsiteProperties) = preProject + ProjectReferences = "" + Debug.AspNetCompiler.VirtualPath = "/Spring.Web.Extensions.Example.2005" + Debug.AspNetCompiler.PhysicalPath = "src\Spring.Web.Extensions.Example.2005\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.Web.Extensions.Example.2005\" + Debug.AspNetCompiler.Updateable = "true" + Debug.AspNetCompiler.ForceOverwrite = "true" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.VirtualPath = "/Spring.Web.Extensions.Example.2005" + Release.AspNetCompiler.PhysicalPath = "src\Spring.Web.Extensions.Example.2005\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.Web.Extensions.Example.2005\" + Release.AspNetCompiler.Updateable = "true" + Release.AspNetCompiler.ForceOverwrite = "true" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.Debug = "False" + VWDPort = "3792" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|.NET = Debug|.NET + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Release|.NET = Release|.NET + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DE2A587C-01A4-49E2-A3CD-664B293A8A6F}.Debug|.NET.ActiveCfg = Debug|.NET + {DE2A587C-01A4-49E2-A3CD-664B293A8A6F}.Debug|.NET.Build.0 = Debug|.NET + {DE2A587C-01A4-49E2-A3CD-664B293A8A6F}.Debug|Any CPU.ActiveCfg = Debug|.NET + {DE2A587C-01A4-49E2-A3CD-664B293A8A6F}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET + {DE2A587C-01A4-49E2-A3CD-664B293A8A6F}.Debug|Mixed Platforms.Build.0 = Debug|.NET + {DE2A587C-01A4-49E2-A3CD-664B293A8A6F}.Release|.NET.ActiveCfg = Debug|.NET + {DE2A587C-01A4-49E2-A3CD-664B293A8A6F}.Release|.NET.Build.0 = Debug|.NET + {DE2A587C-01A4-49E2-A3CD-664B293A8A6F}.Release|Any CPU.ActiveCfg = Debug|.NET + {DE2A587C-01A4-49E2-A3CD-664B293A8A6F}.Release|Mixed Platforms.ActiveCfg = Debug|.NET + {DE2A587C-01A4-49E2-A3CD-664B293A8A6F}.Release|Mixed Platforms.Build.0 = Debug|.NET + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/App_Code/ContactService.cs b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/App_Code/ContactService.cs new file mode 100644 index 00000000..a0687b33 --- /dev/null +++ b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/App_Code/ContactService.cs @@ -0,0 +1,77 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +#region Imports + +using System; +using System.Collections.Generic; + +#endregion + +/// +/// Default implementation of the +/// service interface. +/// +/// Bruno Baia +/// $Id: ContactService.cs,v 1.1 2007/05/31 18:34:54 markpollack Exp $ +public class ContactService : IContactService +{ + private List _emails; + + /// + /// List of emails addresses for all contacts. + /// + public List Emails + { + get { return _emails; } + set { _emails = value; } + } + + /// + /// Creates a new instance of the class. + /// + public ContactService() + { + } + + /// + /// Returns an array of emails, with a maximum of + /// values, that complete the given . + /// + /// Text entered by the user. + /// Maximum number of possible values. + /// The array of emails for completing the text. + public string[] GetEmails(string prefixText, int count) + { + List emails = new List(); + int i = 0; + foreach (string email in _emails) + { + if (email.ToLower().StartsWith(prefixText.ToLower())) + { + emails.Add(email); + if (++i == count) break; + } + } + + return emails.ToArray(); + } +} diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/App_Code/IContactService.cs b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/App_Code/IContactService.cs new file mode 100644 index 00000000..7c83f77d --- /dev/null +++ b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/App_Code/IContactService.cs @@ -0,0 +1,40 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +/// +/// Service interface for contact management. +/// +/// Bruno Baia +/// $Id: IContactService.cs,v 1.1 2007/05/31 18:34:54 markpollack Exp $ +public interface IContactService +{ + /// + /// Returns an array of emails, with a maximum of + /// values, that complete the given . + /// + /// + /// This signature method is required + /// to apply the auto-completion behavior. + /// + /// Text entered by the user. + /// Maximum number of possible values. + /// The array of emails for completing the text. + string[] GetEmails(string prefixText, int count); +} diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/App_Code/StringArrayFilterAdvice.cs b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/App_Code/StringArrayFilterAdvice.cs new file mode 100644 index 00000000..4f925150 --- /dev/null +++ b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/App_Code/StringArrayFilterAdvice.cs @@ -0,0 +1,86 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Generic; + +using AopAlliance.Intercept; +using Spring.Util; + +#endregion + +/// +/// A simple interception around advice that filters +/// a string array matching a given pattern. +/// +/// Bruno Baia +/// $Id: StringArrayFilterAdvice.cs,v 1.1 2007/05/31 18:34:54 markpollack Exp $ +public class StringArrayFilterAdvice : IMethodInterceptor +{ + private string _pattern = string.Empty; + + /// + /// A pattern to be used for filtering a string array. + /// + public string Pattern + { + get { return _pattern; } + set { _pattern = value; } + } + + /// + /// Creates a new instance of the class. + /// + public StringArrayFilterAdvice() + { + } + + #region IMethodInterceptor Members + + public object Invoke(IMethodInvocation invocation) + { + if (invocation.Method.ReturnType != typeof(string[])) + throw new NotSupportedException( + "StringArrayFilterAdvice must be applied on methods " + + "that return an array of string."); + + string[] stringArray = (string[])invocation.Proceed(); + + if (_pattern.Length > 0) + { + List strings = new List(); + foreach (string item in stringArray) + { + if (PatternMatchUtils.SimpleMatch(_pattern, item)) + { + strings.Add(item); + } + } + + return strings.ToArray(); + } + + return stringArray; + } + + #endregion +} \ No newline at end of file diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Common.Logging.dll.refresh b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Common.Logging.dll.refresh new file mode 100644 index 00000000..7ae4334d Binary files /dev/null and b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Common.Logging.dll.refresh differ diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Aop.dll.refresh b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Aop.dll.refresh new file mode 100644 index 00000000..cc858ae7 Binary files /dev/null and b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Aop.dll.refresh differ diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Core.dll.refresh b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Core.dll.refresh new file mode 100644 index 00000000..d6773450 Binary files /dev/null and b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Core.dll.refresh differ diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Web.Extensions.dll.refresh b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Web.Extensions.dll.refresh new file mode 100644 index 00000000..f0151820 Binary files /dev/null and b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Web.Extensions.dll.refresh differ diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Web.dll.refresh b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Web.dll.refresh new file mode 100644 index 00000000..b31d8acc Binary files /dev/null and b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/Spring.Web.dll.refresh differ diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/System.Web.Extensions.dll.refresh b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/System.Web.Extensions.dll.refresh new file mode 100644 index 00000000..8cf56909 Binary files /dev/null and b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Bin/System.Web.Extensions.dll.refresh differ diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/ContactWebServiceMethods.js b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/ContactWebServiceMethods.js new file mode 100644 index 00000000..8029c670 --- /dev/null +++ b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/ContactWebServiceMethods.js @@ -0,0 +1,23 @@ +// JScript File + +// This function calls the Contact Web service method +// passing simple type parameters and the callback function +function GetEmails(prefix, count) +{ + ContactWebService.GetEmails(prefix, count, GetEmailsOnSucceeded); +} + +// This is the callback function invoked if the Web service succeeded. +// It accepts the result object as a parameter. +function GetEmailsOnSucceeded(result, eventArgs) +{ + // Page element to display feedback. + var ResultSpan = document.getElementById("ResultSpan"); + ResultSpan.innerHTML = ''; + var i = 0; + for(email in result) + { + ResultSpan.innerHTML += (result[i] + '
    '); + i++; + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Default.aspx b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Default.aspx new file mode 100644 index 00000000..598134d9 --- /dev/null +++ b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Default.aspx @@ -0,0 +1,46 @@ +<%@ Page Language="C#" %> + + + + + ASP.NET AJAX 1.0 integration with Spring.NET + + +
    + + + + + + + + +
    +

    + Web Service available at this url : ContactWebService.asmx +
    +
    + Client scripts generated at this url : ContactWebService.asmx/js + & ContactWebService.asmx/jsdebug +

    +
    +

    + This sample demonstrates the use of client script with a web service created by + Spring.NET's WebServiceExporter. +

    +

    + Email : + + + (Hint : type the letter 'b' or 'm') +

    +

    + Results : +
    + +

    +
    +
    + + diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Spring.config b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Spring.config new file mode 100644 index 00000000..95c90614 --- /dev/null +++ b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Spring.config @@ -0,0 +1,65 @@ + + + Object definitions. + + + + + + + + + + + GetEmails + + + + + + + + + manuel@spring.pt + bruno@spring.net + marie@spring.net + brice@spring.net + bertrand@spring.net + marcel@spring.net + bernadette@spring.net + bruno@spring.pt + baptiste@spring.net + brad@spring.net + marc@spring.net + barbie@spring.net + bernie@spring.net + betty@spring.net + maria@spring.pt + + + + + + + + + + StringArrayFilterAspect + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Web.config b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Web.config new file mode 100644 index 00000000..ffbbeaf0 --- /dev/null +++ b/examples/Spring/Spring.Web.Extensions.Example/src/Spring.Web.Extensions.Example.2005/Web.config @@ -0,0 +1,93 @@ + + + + +
    + + + +
    + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.WebQuickStart/Spring.WebQuickStart.2005.sln b/examples/Spring/Spring.WebQuickStart/Spring.WebQuickStart.2005.sln new file mode 100644 index 00000000..1c27262a --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/Spring.WebQuickStart.2005.sln @@ -0,0 +1,143 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "D:\...\Spring.WebQuickStart.2005\", "src\Spring.WebQuickStart.2005\", "{B4F56253-6425-4065-8A93-EF88F8730E57}" + ProjectSection(WebsiteProperties) = preProject + ProjectReferences = "{BA4789EB-281A-48EA-8763-28B9F0596A18}|Spring.Web.dll;" + Debug.AspNetCompiler.VirtualPath = "/Spring.WebQuickStart.2005" + Debug.AspNetCompiler.PhysicalPath = "Spring.WebQuickStart.2005\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.WebQuickStart.2005\" + Debug.AspNetCompiler.Updateable = "true" + Debug.AspNetCompiler.ForceOverwrite = "true" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.VirtualPath = "/Spring.WebQuickStart.2005" + Release.AspNetCompiler.PhysicalPath = "Spring.WebQuickStart.2005\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.WebQuickStart.2005\" + Release.AspNetCompiler.Updateable = "true" + Release.AspNetCompiler.ForceOverwrite = "true" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.Debug = "False" + VWDPort = "4112" + DefaultWebSiteLanguage = "Visual C#" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Web.2005", "..\..\..\src\Spring\Spring.Web\Spring.Web.2005.csproj", "{BA4789EB-281A-48EA-8763-28B9F0596A18}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.2005", "..\..\..\src\Spring\Spring.Core\Spring.Core.2005.csproj", "{710961A3-0DF4-49E4-A26E-F5B9C044AC84}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.Tests.2005", "..\..\..\test\Spring\Spring.Core.Tests\Spring.Core.Tests.2005.csproj", "{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "D:\...\Spring.WebQuickStart.Providers\", "src\Spring.WebQuickStart.Providers\", "{F42A333D-D305-4649-8B5A-6F5862F0F62A}" + ProjectSection(WebsiteProperties) = preProject + ProjectReferences = "{710961A3-0DF4-49E4-A26E-F5B9C044AC84}|Spring.Core.dll;{BA4789EB-281A-48EA-8763-28B9F0596A18}|Spring.Web.dll;" + Debug.AspNetCompiler.VirtualPath = "/Spring.WebQuickStart.Providers" + Debug.AspNetCompiler.PhysicalPath = "src\Spring.WebQuickStart.Providers\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.WebQuickStart.Providers\" + Debug.AspNetCompiler.Updateable = "true" + Debug.AspNetCompiler.ForceOverwrite = "true" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.VirtualPath = "/Spring.WebQuickStart.Providers" + Release.AspNetCompiler.PhysicalPath = "src\Spring.WebQuickStart.Providers\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.WebQuickStart.Providers\" + Release.AspNetCompiler.Updateable = "true" + Release.AspNetCompiler.ForceOverwrite = "true" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.Debug = "False" + VWDPort = "4828" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Web.Tests.2005", "..\..\..\test\Spring\Spring.Web.Tests\Spring.Web.Tests.2005.csproj", "{C67E47AA-1ACD-41B4-A465-4D336A2319CA}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|.NET = Debug|.NET + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Release|.NET = Release|.NET + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B4F56253-6425-4065-8A93-EF88F8730E57}.Debug|.NET.ActiveCfg = Debug|.NET + {B4F56253-6425-4065-8A93-EF88F8730E57}.Debug|.NET.Build.0 = Debug|.NET + {B4F56253-6425-4065-8A93-EF88F8730E57}.Debug|Any CPU.ActiveCfg = Debug|.NET + {B4F56253-6425-4065-8A93-EF88F8730E57}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET + {B4F56253-6425-4065-8A93-EF88F8730E57}.Debug|Mixed Platforms.Build.0 = Debug|.NET + {B4F56253-6425-4065-8A93-EF88F8730E57}.Release|.NET.ActiveCfg = Debug|.NET + {B4F56253-6425-4065-8A93-EF88F8730E57}.Release|.NET.Build.0 = Debug|.NET + {B4F56253-6425-4065-8A93-EF88F8730E57}.Release|Any CPU.ActiveCfg = Debug|.NET + {B4F56253-6425-4065-8A93-EF88F8730E57}.Release|Mixed Platforms.ActiveCfg = Debug|.NET + {B4F56253-6425-4065-8A93-EF88F8730E57}.Release|Mixed Platforms.Build.0 = Debug|.NET + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|.NET.ActiveCfg = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|.NET.ActiveCfg = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Any CPU.Build.0 = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {BA4789EB-281A-48EA-8763-28B9F0596A18}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|.NET.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|.NET.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.Build.0 = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|.NET.ActiveCfg = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|.NET.ActiveCfg = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Any CPU.Build.0 = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {F42A333D-D305-4649-8B5A-6F5862F0F62A}.Debug|.NET.ActiveCfg = Debug|.NET + {F42A333D-D305-4649-8B5A-6F5862F0F62A}.Debug|.NET.Build.0 = Debug|.NET + {F42A333D-D305-4649-8B5A-6F5862F0F62A}.Debug|Any CPU.ActiveCfg = Debug|.NET + {F42A333D-D305-4649-8B5A-6F5862F0F62A}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET + {F42A333D-D305-4649-8B5A-6F5862F0F62A}.Debug|Mixed Platforms.Build.0 = Debug|.NET + {F42A333D-D305-4649-8B5A-6F5862F0F62A}.Release|.NET.ActiveCfg = Debug|.NET + {F42A333D-D305-4649-8B5A-6F5862F0F62A}.Release|.NET.Build.0 = Debug|.NET + {F42A333D-D305-4649-8B5A-6F5862F0F62A}.Release|Any CPU.ActiveCfg = Debug|.NET + {F42A333D-D305-4649-8B5A-6F5862F0F62A}.Release|Mixed Platforms.ActiveCfg = Debug|.NET + {F42A333D-D305-4649-8B5A-6F5862F0F62A}.Release|Mixed Platforms.Build.0 = Debug|.NET + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|.NET.ActiveCfg = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|.NET.ActiveCfg = Release|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|Any CPU.Build.0 = Release|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C67E47AA-1ACD-41B4-A465-4D336A2319CA}.Release|Mixed Platforms.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/EmployeeInfo.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/EmployeeInfo.cs new file mode 100644 index 00000000..79039bd1 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/EmployeeInfo.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections; + +/// +/// EmployeeInfo domain class +/// +public class EmployeeInfo +{ + private int id; + private string firstName; + private string lastName; + private DateTime dateOfBirth; + private Gender gender = Gender.Male; + private AddressInfo mailingAddress = new AddressInfo(); + private double salary; + private ArrayList _hobbies = new ArrayList(); + private ArrayList _favoriteFood = new ArrayList(); + + public EmployeeInfo() + {} + + public int Id + { + get { return id; } + set { id = value; } + } + + public string FirstName + { + get { return firstName; } + set { firstName = value; } + } + + public string LastName + { + get { return lastName; } + set { lastName = value; } + } + + public DateTime DateOfBirth + { + get { return dateOfBirth; } + set { dateOfBirth = value; } + } + + public Gender Gender + { + get { return gender; } + set { gender = value; } + } + + public AddressInfo MailingAddress + { + get { return mailingAddress; } + set { mailingAddress = value; } + } + + public double Salary + { + get { return salary; } + set { salary = value; } + } + + public IList Hobbies + { + get { return this._hobbies; } + set { this._hobbies = (value != null) ? new ArrayList(value) : null; } + } + + public IList FavoriteFood + { + get { return this._favoriteFood; } + set { this._favoriteFood = (value != null) ? new ArrayList(value) : null; } + } +} + +public enum Gender +{ + Male, + Female +} + +public class AddressInfo +{ + private AddressType addressType; + private string street1; + private string street2; + private string city; + private string state; + private string postalCode; + private string country; + + public AddressType AddressType + { + get { return addressType; } + set { addressType = value; } + } + + public string Street1 + { + get { return street1; } + set { street1 = value; } + } + + public string Street2 + { + get { return street2; } + set { street2 = value; } + } + + public string City + { + get { return city; } + set { city = value; } + } + + public string State + { + get { return state; } + set { state = value; } + } + + public string PostalCode + { + get { return postalCode; } + set { postalCode = value; } + } + + public string Country + { + get { return country; } + set { country = value; } + } +} + +public enum AddressType +{ + Home, + Office +} + +public class Hobby +{ + private string _uniqueKey; + private string _title; + + public Hobby(string uniqueKey, string title) + { + this._uniqueKey = uniqueKey; + this._title = title; + } + + public string UniqueKey + { + get { return this._uniqueKey; } + } + + public string Title + { + get { return this._title; } + } + + public override string ToString() + { + return this._uniqueKey; + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/HtmlCommentAppenderModule.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/HtmlCommentAppenderModule.cs new file mode 100644 index 00000000..83de2091 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/HtmlCommentAppenderModule.cs @@ -0,0 +1,34 @@ +using System; +using System.Web; + +/// +/// Summary description for CustomModule +/// +public class HtmlCommentAppenderModule : IHttpModule +{ + private string appendText; + + public HtmlCommentAppenderModule() + { + } + + public string AppendText + { + get { return this.appendText; } + set { this.appendText = value; } + } + + public void Init(HttpApplication application) + { + application.PostRequestHandlerExecute += new System.EventHandler(application_PostRequestHandlerExecute); + } + + void application_PostRequestHandlerExecute(object sender, System.EventArgs e) + { + ((HttpApplication)sender).Context.Response.Write( string.Format( "{0}", Environment.NewLine, appendText ) ); + } + + public void Dispose() + { + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/ProductInfo.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/ProductInfo.cs new file mode 100644 index 00000000..682749ff --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/ProductInfo.cs @@ -0,0 +1,37 @@ +/// +/// Summary description for ProductInfo +/// +public class ProductInfo +{ + private string sku; + private string name; + private int quantity; + private double price; + + public ProductInfo() + {} + + public string Sku + { + get { return sku; } + set { sku = value; } + } + + public string Name + { + get { return name; } + set { name = value; } + } + + public int Quantity + { + get { return quantity; } + set { quantity = value; } + } + + public double Price + { + get { return price; } + set { price = value; } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/Default.aspx new file mode 100644 index 00000000..16361140 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/Default.aspx @@ -0,0 +1,22 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DI_Default" %> + + + + + + Spring.NET Web Framework Quick Start Guide - Dependency Injection + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +
    +

    Dependency Injection

    +

    + Samples in this section demonstrate, how dependency injection is done in web projects. +

    +

    Hello World!

    +

    The inevitable "Hello World!" example demonstrates DI for pages, usercontrols and webcontrols

    +

    Nested Contexts

    +

    This sample shows, how contexts are nested in webapplications

    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/Default.aspx.cs new file mode 100644 index 00000000..57766cc5 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/Default.aspx.cs @@ -0,0 +1,18 @@ +using System; +using System.Data; +using System.Configuration; +using System.Collections; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class DI_Default : System.Web.UI.Page +{ + protected void Page_Load(object sender, EventArgs e) + { + + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/CustomControl.ascx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/CustomControl.ascx new file mode 100644 index 00000000..b5abc9f4 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/CustomControl.ascx @@ -0,0 +1,19 @@ +<%@ Control Language="C#" EnableViewState="false" AutoEventWireup="false" CodeFile="CustomControl.ascx.cs" Inherits="DI_HelloWorld_CustomControl" %> + +

    + The message you can see below has been injected by <object type="CustomControl.ascx"> object definition: +

    +

    + '<%=AnotherMessage%>' +

    +

    + The TextBox below has been filled in by typename using the <object name="System.Web.UI.WebControls.TextBox"> object definition: +

    +

    + +

    + Use a spring:Panel to selectively enable/disable automatic dependency injection: +

    + + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/CustomControl.ascx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/CustomControl.ascx.cs new file mode 100644 index 00000000..02948c29 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/CustomControl.ascx.cs @@ -0,0 +1,29 @@ +using System; + +/// +/// Notice that you don't need to extend Spring.Web.UI.UserControl for DI features! +/// +public partial class DI_HelloWorld_CustomControl : System.Web.UI.UserControl +{ + private string anotherMessage; + + public string AnotherMessage + { + get { return this.anotherMessage; } + set { this.anotherMessage = value; } + } + + /// + /// Values are injected right before OnInit() is called. + /// + /// + protected override void OnInit(EventArgs e) + { + if (this.AnotherMessage == null) + { + throw new ArgumentNullException("AnotherMessage"); + } + + base.OnInit(e); + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/Default.aspx new file mode 100644 index 00000000..0513813c --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/Default.aspx @@ -0,0 +1,30 @@ +<%@ Page Language="C#" EnableViewState="false" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DI_HelloWorld_Default" %> +<%@ Register TagPrefix="ctl" TagName="CustomControl" src="CustomControl.ascx" %> + + + + + <%=Title%> + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +

    Dependency Injection

    +
    +

    Hello World example

    +
    +

    + This very simple web form demonstrates dependency injection on pages and controls. +

    +

    + The message you can see below has been injected by <object type="Default.aspx"> object definition: +

    +

    + '<%=Message%>' +

    +

    + +

    +
    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/Default.aspx.cs new file mode 100644 index 00000000..771c1f70 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/Default.aspx.cs @@ -0,0 +1,30 @@ +using System; +using System.Web.UI; + +/// +/// Notice that you don't need to extend Spring.Web.UI.Page for DI features! +/// +public partial class DI_HelloWorld_Default : System.Web.UI.Page +{ + private string message; + + public string Message + { + get { return this.message; } + set { this.message = value; } + } + + /// + /// Values are injected right before OnInit() is called. + /// + /// + protected override void OnInit(EventArgs e) + { + if(this.Message == null) + { + throw new ArgumentNullException("Message"); + } + + base.OnInit(e); + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/Web.config b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/Web.config new file mode 100644 index 00000000..514c74ce --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/HelloWorld/Web.config @@ -0,0 +1,32 @@ + + + + + + + + + + + + + This very simple page demonstrates how to inject dependencies into a Page. + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextA/CustomControl.ascx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextA/CustomControl.ascx new file mode 100644 index 00000000..f18b3729 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextA/CustomControl.ascx @@ -0,0 +1,3 @@ +<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CustomControl.ascx.cs" Inherits="DI_NestedContexts_ContextA_CustomControl" %> + +

    <%=Message%>

    diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextA/CustomControl.ascx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextA/CustomControl.ascx.cs new file mode 100644 index 00000000..573eafa5 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextA/CustomControl.ascx.cs @@ -0,0 +1,12 @@ +using System; + +public partial class DI_NestedContexts_ContextA_CustomControl : System.Web.UI.UserControl +{ + private string _message; + + public string Message + { + get { return this._message; } + set { this._message = value; } + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/CustomControl.ascx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/CustomControl.ascx new file mode 100644 index 00000000..82fff365 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/CustomControl.ascx @@ -0,0 +1,3 @@ +<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CustomControl.ascx.cs" Inherits="DI_NestedContexts_ContextB_CustomControlB" %> + +

    <%=Message%>

    \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/CustomControl.ascx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/CustomControl.ascx.cs new file mode 100644 index 00000000..bcbcf41f --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/CustomControl.ascx.cs @@ -0,0 +1,11 @@ + +public partial class DI_NestedContexts_ContextB_CustomControlB : System.Web.UI.UserControl +{ + private string _message; + + public string Message + { + get { return this._message; } + set { this._message = value; } + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/Default.aspx new file mode 100644 index 00000000..55f47da7 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/Default.aspx @@ -0,0 +1,22 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DI_NestedContexts_ContextB_Default" %> + + + + + + Object definition inheritance example + + +
    +

    + <%=Message%> +

    +

    + Below you can see the message from the definition 'GlobalMessage' that is inherited within this child context: +

    +

    + <%=GlobalMessage%> +

    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/Default.aspx.cs new file mode 100644 index 00000000..5b4e26b1 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/Default.aspx.cs @@ -0,0 +1,18 @@ + +public partial class DI_NestedContexts_ContextB_Default : System.Web.UI.Page +{ + private string _message; + private string _globalMessage; + + public string Message + { + get { return this._message; } + set { this._message = value; } + } + + public string GlobalMessage + { + get { return this._globalMessage; } + set { this._globalMessage = value; } + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/Web.config b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/Web.config new file mode 100644 index 00000000..f4d2157e --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/ContextB/Web.config @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/Default.aspx new file mode 100644 index 00000000..97315e93 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/Default.aspx @@ -0,0 +1,36 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DI_NestedContexts_Default" %> +<%@ Register TagPrefix="ctl" TagName="ControlA" src="ContextA/CustomControl.ascx" %> +<%@ Register TagPrefix="ctl" TagName="ControlB" src="ContextB/CustomControl.ascx" %> + + + + + + Dependency Injection - Nested Contexts example + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +

    Dependency Injection

    +
    +

    Nested Contexts example

    +
    +

    + This sample illustrates nesting of contexts. +

    +

    + Control "A" below demonstrates configuring a control within the page's context: +

    +

    + +

    +

    + Control "B" below demonstrates configuring a control within the page's context, + but overriding the definition in the control's context. +

    +

    + +

    +
    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/Default.aspx.cs new file mode 100644 index 00000000..d7be9768 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/Default.aspx.cs @@ -0,0 +1,18 @@ +using System; +using System.Data; +using System.Configuration; +using System.Collections; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class DI_NestedContexts_Default : System.Web.UI.Page +{ + protected void Page_Load(object sender, EventArgs e) + { + + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/Web.config b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/Web.config new file mode 100644 index 00000000..ec902eec --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/NestedContexts/Web.config @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Collections/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Collections/Default.aspx new file mode 100644 index 00000000..656f991f --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Collections/Default.aspx @@ -0,0 +1,69 @@ +<%@ Page Language="C#" EnableViewState="false" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DataBinding_Collections_Default" %> + + + + + + Data Binding - HttpRequestListBindingContainer example + + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +

    Bi-directional DataBinding

    +
    +
    +

    HttpRequestListBindingContainer example

    +

    + This form demonstrates unbinding data from HttpRequest.Form collection into a list model using HttpRequestListBindingContainer. +

    +

    + HttpRequestListBindingContainer extracts posted values from the request and populates the specified IList by creating objects + of the type specified and populating each of these objects according to the requestBindings collection. +

    +
    +        HttpRequestListBindingContainer requestBindings =
    +            new HttpRequestListBindingContainer("sku,name,quantity,price", "Products", typeof(ProductInfo));
    +        requestBindings.AddBinding("sku", "Sku");
    +        requestBindings.AddBinding("name", "Name");
    +        requestBindings.AddBinding("quantity", "Quantity", quantityFormatter);
    +        requestBindings.AddBinding("price", "Price", priceFormatter);
    +    
    + Edit List:
    +
    + + + <% + foreach (ProductInfo p in Products) + { + %> + + + + + + + <% + } + %> +
    FirstnameLastnameQuantityPrice/Item
    +
    + + + +
    +
    + + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Collections/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Collections/Default.aspx.cs new file mode 100644 index 00000000..11f7a150 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Collections/Default.aspx.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections; +using Spring.DataBinding; +using Spring.Globalization.Formatters; + +/// +/// Notice that your web page has to extend Spring.Web.UI.Page class +/// in order to enable data binding and many other features. +/// +public partial class DataBinding_Collections_Default : Spring.Web.UI.Page +{ + #region Fields + + private IList products; + private readonly IntegerFormatter quantityFormatter = new IntegerFormatter(); + private readonly CurrencyFormatter priceFormatter = new CurrencyFormatter(); + + #endregion + + #region Properties + + public IList Products + { + get { return products; } + } + + public IntegerFormatter QuantityFormatter + { + get { return quantityFormatter; } + } + + public CurrencyFormatter PriceFormatter + { + get { return priceFormatter; } + } + + #endregion + + /// + /// In order to declare data bindings, all you need to do is + /// override InitializeDataBindings method and add all necessary + /// data bindings to the BindingManager. + /// + protected override void InitializeDataBindings() + { + // HttpRequestListBindingContainer unbinds specified values from Request -> Productlist + HttpRequestListBindingContainer requestBindings = + new HttpRequestListBindingContainer("sku,name,quantity,price", "Products", typeof(ProductInfo)); + requestBindings.AddBinding("sku", "Sku"); + requestBindings.AddBinding("name", "Name"); + requestBindings.AddBinding("quantity", "Quantity", quantityFormatter); + requestBindings.AddBinding("price", "Price", priceFormatter); + + BindingManager.AddBinding(requestBindings); + } + + #region Model Management + + protected override void InitializeModel() + { + products = new ArrayList(); + } + + protected override void LoadModel(object savedModel) + { + products = (IList) savedModel; + } + + protected override object SaveModel() + { + return products; + } + + #endregion + + protected void btnAdd_Click(object sender, EventArgs e) + { + products.Add(new ProductInfo()); + } + + protected void btnSave_Click(object sender, EventArgs e) + { + // do something with products... + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Default.aspx new file mode 100644 index 00000000..1bf778b9 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Default.aspx @@ -0,0 +1,48 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DataBinding_Default" %> + + + + + + Spring.NET Web Framework Quick Start Guide - Bi-directional DataBinding + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +
    +

    Bi-directional DataBinding

    +

    + Samples in this section demonstrate Spring.NET's capabilities of bi-directional + binding data between your model and form. +

    +

    Hello World

    +

    + This very simple web form demonstrates basic bi-directional data binding. +

    +

    Event Handling

    +

    + This example builds on the previous one by adding postback event handler + that will convert the name to uppercase. +

    +

    HttpRequestListBindingContainer

    +

    + This example demonstrates unbinding data from request into a list using a HttpRequestListBindingContainer. +

    +

    Employee Info

    +

    + Shows basics of bi-directional databinding with simple webcontrols (TextBox etc.) +

    +

    Robust Employee Info

    +

    + Demonstrates basic model validation capabilities +

    +

    Lists

    +

    + Demonstrates bi-directional binding of multi-selection listboxes +

    +

    Easy Employee Info

    +

    + Finally shows, how easy life can be when using a DataBindingPanel +

    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Default.aspx.cs new file mode 100644 index 00000000..140ae053 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Default.aspx.cs @@ -0,0 +1,18 @@ +using System; +using System.Data; +using System.Configuration; +using System.Collections; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class DataBinding_Default : System.Web.UI.Page +{ + protected void Page_Load(object sender, EventArgs e) + { + + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EasyEmployeeInfo/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EasyEmployeeInfo/Default.aspx new file mode 100644 index 00000000..370b69ad --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EasyEmployeeInfo/Default.aspx @@ -0,0 +1,130 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DataBinding_EasyEmployeeInfo_Default" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> + + + + + + Data Binding - Easy Employee Info example + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +

    Bi-directional DataBinding

    +
    +
    +

    Easy Employee Info example

    +

    + Now that you have an idea of how databinding works, here's the easy way using a DataBindingPanel. +

    +

    + By placing your controls within a DataBindingPanel, you can use additional attributes + to specify binding information on your server controls. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Employee ID: + + +
    First Name:
    Last Name:
    Date of Birth: + + +
    Gender: + + + + + +
    Salary: + + +
    Address Type: + + + + + +
    Street 1:
    Street 2:
    City:
    State:
    Postal Code/ZIP:
    Country:
    Hobbies:
    Favorite Food: + + Cheese + Noodles + Fruits + Vegetables + +
     
    +
    +
    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EasyEmployeeInfo/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EasyEmployeeInfo/Default.aspx.cs new file mode 100644 index 00000000..3708c4b3 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EasyEmployeeInfo/Default.aspx.cs @@ -0,0 +1,56 @@ +using System; +using System.Diagnostics; +using System.Web.UI.WebControls; + +/// +/// Notice that your web page has to extend Spring.Web.UI.Page class +/// in order to enable data binding and many other features. +/// +public partial class DataBinding_EasyEmployeeInfo_Default : Spring.Web.UI.Page +{ + private EmployeeInfo employee = new EmployeeInfo(); + + public EmployeeInfo Employee + { + get { return employee; } + } + + protected override void OnInitializeControls(EventArgs e) + { + base.OnInitializeControls(e); + + InitializeHobbiesList(); + } + + private void InitializeHobbiesList() + { + // fill lstHobbies with 'Hobby' items + Hobby[] hobbies = { + new Hobby("1", "Tennis"), + new Hobby("2", "Climbing"), + new Hobby("3", "Sailing"), + new Hobby("4", "Reading") + }; + + this.lstHobbies.SelectionMode = ListSelectionMode.Multiple; + + // assign the list in any case - this is required for DataSourceItemFormatter to be able + // to access Hobby objects + this.lstHobbies.DataSource = hobbies; + this.lstHobbies.DataTextField = "Title"; + this.lstHobbies.DataValueField = "UniqueKey"; + if (!IsPostBack) + { + this.lstHobbies.DataBind(); + } + } + + protected void btnSave_Click(object sender, EventArgs e) + { + // do something with employee such as: + // employeeDao.Save(Employee); + + Debug.Write(Employee); + } +} + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EasyEmployeeInfo/Web.Config b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EasyEmployeeInfo/Web.Config new file mode 100644 index 00000000..dea69150 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EasyEmployeeInfo/Web.Config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EmployeeInfo/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EmployeeInfo/Default.aspx new file mode 100644 index 00000000..d60d220a --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EmployeeInfo/Default.aspx @@ -0,0 +1,113 @@ +<%@ Page Language="C#" EnableViewState="false" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DataBinding_EmployeeInfo_Default" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> + + + + + + Data Binding - Employee Info example + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +

    Bi-directional DataBinding

    +
    +
    +

    Employee Info example

    +

    + Now we are getting into more interesting stuff :-). +

    +

    + This form allows you to enter employee information and it updates properties + of the Employee property based on the entered values. You should put a breakpoint + on the Debug.Write(Employee) statement in the event handler for the Save button, + so you can see how Employee object was populated by the framework. +

    +

    + You should note that there a number of type conversions are performed quietly + behind the scenes, such as string to int, double, DateTime, and even enum values. + Please also keep in mind that this form is very fragile and it will throw + an exception if you enter a value that cannot be converted using standard type + conversion mechanism, so make sure that you enter valid values for the ID, Date of Birth, + and Salary fields. In the next example we will show you how to make it more robust + using formatters and built-in data type validation. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Employee ID:
    First Name:
    Last Name:
    Date of Birth:
    Gender: + + + + + +
    Salary:
    Address Type: + + + + + +
    Street 1:
    Street 2:
    City:
    State:
    Postal Code/ZIP:
    Country:
     
    +
    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EmployeeInfo/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EmployeeInfo/Default.aspx.cs new file mode 100644 index 00000000..d54b3009 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EmployeeInfo/Default.aspx.cs @@ -0,0 +1,51 @@ +using System; +using System.Diagnostics; + +/// +/// Notice that your web page has to extend Spring.Web.UI.Page class +/// in order to enable data binding and many other features. +/// +public partial class DataBinding_EmployeeInfo_Default : Spring.Web.UI.Page +{ + private EmployeeInfo employee = new EmployeeInfo(); + + public EmployeeInfo Employee + { + get { return employee; } + } + + /// + /// In order to declare data bindings, all you need to do is + /// override InitializeDataBindings method and add all necessary + /// data bindings to the BindingManager. + /// + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("txtId.Text", "Employee.Id"); + BindingManager.AddBinding("txtFirstName.Text", "Employee.FirstName"); + BindingManager.AddBinding("txtLastName.Text", "Employee.LastName"); + BindingManager.AddBinding("txtDOB.Text", "Employee.DateOfBirth"); + BindingManager.AddBinding("txtSalary.Text", "Employee.Salary"); + BindingManager.AddBinding("rbgGender.Value", "Employee.Gender"); + BindingManager.AddBinding("ddlAddressType.SelectedValue", "Employee.MailingAddress.AddressType"); + BindingManager.AddBinding("txtStreet1.Text", "Employee.MailingAddress.Street1"); + BindingManager.AddBinding("txtStreet2.Text", "Employee.MailingAddress.Street2"); + BindingManager.AddBinding("txtCity.Text", "Employee.MailingAddress.City"); + BindingManager.AddBinding("txtState.Text", "Employee.MailingAddress.State"); + BindingManager.AddBinding("txtPostalCode.Text", "Employee.MailingAddress.PostalCode"); + BindingManager.AddBinding("txtCountry.Text", "Employee.MailingAddress.Country"); + } + + protected void Page_Load(object sender, EventArgs e) + {} + + + protected void btnSave_Click(object sender, EventArgs e) + { + // do something with employee such as: + // employeeDao.Save(Employee); + + Debug.Write(Employee); + } +} + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EventHandling/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EventHandling/Default.aspx new file mode 100644 index 00000000..b513448c --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EventHandling/Default.aspx @@ -0,0 +1,42 @@ +<%@ Page Language="C#" EnableViewState="false" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DataBinding_EventHandling_Default" %> + + + + + + Data Binding - Event Handling example + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +

    Bi-directional DataBinding

    +
    +
    +

    Event Handling example

    +

    + This example builds on the previous one by adding postback event handler + that will convert the name to uppercase. +

    +

    + This particular example shows the main reason data binding framework was developed + in a first place. The idea is that developer should almost never have to access + controls directly within postback event handlers. Instead, controls should + automatically populate data model on postback and they should be refreshed + from the data model just before the page is rendered again. +

    +

    + As you can see, developer can modify data model directly within postback event handler + and all the changes will be reflected on the controls when the page is rendered. In + this particular example we use properties within the Page class to hold data model, + but there are other alternatives as well, that we'll show you in the later examples. +

    +

    + Enter Your Name: + +

    +
    + Hello, +
    +
    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EventHandling/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EventHandling/Default.aspx.cs new file mode 100644 index 00000000..18614799 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/EventHandling/Default.aspx.cs @@ -0,0 +1,42 @@ +using System; +using Spring.DataBinding; + +/// +/// Notice that your web page has to extend Spring.Web.UI.Page class +/// in order to enable data binding and many other features. +/// +public partial class DataBinding_EventHandling_Default : Spring.Web.UI.Page +{ + private string name; + + public string Name + { + get { return name; } + set { name = value; } + } + + /// + /// In order to declare data bindings, all you need to do is + /// override InitializeDataBindings method and add all necessary + /// data bindings to the BindingManager. + /// + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("txtName.Text", "Name"); + BindingManager.AddBinding("lblName.Text", "Name", BindingDirection.TargetToSource); + } + + protected void Page_Load(object sender, EventArgs e) + { + divGreeting.Visible = !string.IsNullOrEmpty(Name); + } + + /// + /// Event handler simply manipulates data model directly and + /// changes are immediately reflected on the page. + /// + protected void btnCapitalize_Click(object sender, EventArgs e) + { + Name = Name.ToUpper(); + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/HelloWorld/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/HelloWorld/Default.aspx new file mode 100644 index 00000000..51b7ad63 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/HelloWorld/Default.aspx @@ -0,0 +1,38 @@ +<%@ Page Language="C#" EnableViewState="false" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DataBinding_HelloWorld_Default" %> + + + + + + Data Binding - Hello World example + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +

    Bi-directional DataBinding

    +
    +
    +

    Hello World example

    +

    + This very simple web form demonstrates basic bi-directional data binding. +

    +

    + Once you enter your name into the text box and submit the form (by hitting Enter), + Spring.NET will evaluate data binding rules configured in the code-behind file and + set the Name property of the page to the value of txtName.Text property. +

    +

    + After that, page will be rendered again and Name property will be bound both + to the text box, so you can change the value, and to the label below the text + box, in order to show you how you can use one-way bindings to set non-writable + properties. +

    +

    + Enter Your Name: +

    +
    + Hello, +
    +
    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/HelloWorld/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/HelloWorld/Default.aspx.cs new file mode 100644 index 00000000..b53707b3 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/HelloWorld/Default.aspx.cs @@ -0,0 +1,33 @@ +using System; +using Spring.DataBinding; + +/// +/// Notice that your web page has to extend Spring.Web.UI.Page class +/// in order to enable data binding and many other features. +/// +public partial class DataBinding_HelloWorld_Default : Spring.Web.UI.Page +{ + private string name; + + public string Name + { + get { return name; } + set { name = value; } + } + + /// + /// In order to declare data bindings, all you need to do is + /// override InitializeDataBindings method and add all necessary + /// data bindings to the BindingManager. + /// + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("txtName.Text", "Name"); + BindingManager.AddBinding("lblName.Text", "Name", BindingDirection.TargetToSource); + } + + protected void Page_Load(object sender, EventArgs e) + { + divGreeting.Visible = !string.IsNullOrEmpty(Name); + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Lists/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Lists/Default.aspx new file mode 100644 index 00000000..6b6b1411 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Lists/Default.aspx @@ -0,0 +1,57 @@ +<%@ Page Language="C#" EnableViewState="true" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DataBinding_Lists_Default" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> + + + + + + Data Binding - Multi-Selection Lists Example + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +

    Bi-directional DataBinding

    +
    +
    +

    Multi-Selection Lists example

    +

    + Life is not 1-dimensional ;-). This sample demonstrates binding of multiple selection lists. +

    +

    + This form allows you to enter employee information and it updates properties + of the Employee property based on the entered values. You should put a breakpoint + on the Debug.Write(Employee) statement in the event handler for the Save button, + so you can see how Employee object was populated by the framework. +

    + + + + + + + + + + + + + + + + + + + + + +
    Employee ID:
    First Name:
    Hobbies:
    Favorite Food: + + Cheese + Noodles + Fruits + Vegetables + +
     
    +
    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Lists/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Lists/Default.aspx.cs new file mode 100644 index 00000000..ea80d961 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/Lists/Default.aspx.cs @@ -0,0 +1,85 @@ +using System; +using System.Diagnostics; +using System.Web.UI.WebControls; +using Spring.DataBinding; +using Spring.Globalization; +using Spring.Globalization.Formatters; +using Spring.Web.UI; + +/// +/// Notice that your web page has to extend Spring.Web.UI.Page class +/// in order to enable data binding and many other features. +/// +public partial class DataBinding_Lists_Default : Page +{ + private EmployeeInfo employee = new EmployeeInfo(); + + public EmployeeInfo Employee + { + get { return employee; } + } + + /// + /// In order to declare data bindings, all you need to do is + /// override InitializeDataBindings method and add all necessary + /// data bindings to the BindingManager. + /// + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("txtId.Text", "Employee.Id"); + BindingManager.AddBinding("txtFirstName.Text", "Employee.FirstName"); + + // this is rather verbose to show how it works + + // the formatter must convert between ListControl values and domain objects identified by these values (e.g. a key) + IFormatter dsFormatter = new DataSourceItemFormatter("DataSource", "DataValueField"); + // bind the lstHobbies control to Employee.Hobbies IList + MultipleSelectionListControlBinding listBinding = new MultipleSelectionListControlBinding("lstHobbies", "Employee.Hobbies", BindingDirection.Bidirectional, dsFormatter); + BindingManager.AddBinding(listBinding); + + // use simple name=value binding + BindingManager.AddBinding(new MultipleSelectionListControlBinding("lstFavoriteFood", "Employee.FavoriteFood", BindingDirection.Bidirectional, new NullFormatter())); + } + + protected override void OnInitializeControls(EventArgs e) + { + base.OnInitializeControls(e); + + InitializeHobbiesList(); + } + + private void InitializeHobbiesList() + { + Hobby[] hobbies = { + new Hobby("1", "Tennis"), + new Hobby("2", "Climbing"), + new Hobby("3", "Sailing"), + new Hobby("4", "Reading") + }; + + this.lstHobbies.SelectionMode = ListSelectionMode.Multiple; + + // assign the list in any case - this is required for DataSourceItemFormatter to be able + // to access Hobby objects + this.lstHobbies.DataSource = hobbies; + this.lstHobbies.DataTextField = "Title"; + this.lstHobbies.DataValueField = "UniqueKey"; + if (!IsPostBack) + { + this.lstHobbies.DataBind(); + } + } + + protected void Page_Load(object sender, EventArgs e) + { + } + + + protected void btnSave_Click(object sender, EventArgs e) + { + // do something with employee such as: + // employeeDao.Save(Employee); + + Debug.Write(Employee); + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/RobustEmployeeInfo/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/RobustEmployeeInfo/Default.aspx new file mode 100644 index 00000000..c26beaf9 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/RobustEmployeeInfo/Default.aspx @@ -0,0 +1,124 @@ +<%@ Page Language="C#" EnableViewState="false" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DataBinding_RobustEmployeeInfo_Default" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> + + + + + + Data Binding - Robust Employee Info example + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +

    Bi-directional DataBinding

    +
    +
    +

    Robust Employee Info example

    +

    + Now that we have basic data binding rules configured, let's make it more robust. +

    +

    + In general, you should specify data-type conversion error handlers for every + data binding that converts values from one type to another. Optionally, you can + also specify a formatter if you need to format and/or parse the value during data + binding process, which is exactly what we did for the Salary field. Now you can + enter its value using currency symbol and thousands separator, but entering plain + number will still work (and it will be properly formatted after the postback). +

    +

    + Control definitions below are almost the same as in the previous example. The only + difference is that we added controls that will be used to display validation errors. +

    +

    + Just like in the previous example, you should put a breakpoint + on the Debug.Write(Employee) statement in the event handler for the Save button, + so you can see how Employee object was populated by the framework. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Employee ID: + + +
    First Name:
    Last Name:
    Date of Birth: + + +
    Gender: + + + + + +
    Salary: + + +
    Address Type: + + + + + +
    Street 1:
    Street 2:
    City:
    State:
    Postal Code/ZIP:
    Country:
     
    +
    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/RobustEmployeeInfo/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/RobustEmployeeInfo/Default.aspx.cs new file mode 100644 index 00000000..662961fb --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DataBinding/RobustEmployeeInfo/Default.aspx.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics; +using Spring.Globalization.Formatters; + +/// +/// Notice that your web page has to extend Spring.Web.UI.Page class +/// in order to enable data binding and many other features. +/// +public partial class DataBinding_RobustEmployeeInfo_Default : Spring.Web.UI.Page +{ + private EmployeeInfo employee = new EmployeeInfo(); + + public EmployeeInfo Employee + { + get { return employee; } + } + + /// + /// In order to declare data bindings, all you need to do is + /// override InitializeDataBindings method and add all necessary + /// data bindings to the BindingManager. + /// + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("txtId.Text", "Employee.Id") + .SetErrorMessage("ID has to be an integer", "id.errors"); + BindingManager.AddBinding("txtFirstName.Text", "Employee.FirstName"); + BindingManager.AddBinding("txtLastName.Text", "Employee.LastName"); + BindingManager.AddBinding("txtDOB.Text", "Employee.DateOfBirth") + .SetErrorMessage("Invalid date value", "dob.errors"); + BindingManager.AddBinding("txtSalary.Text", "Employee.Salary", new CurrencyFormatter()) + .SetErrorMessage("Salary must be a valid currency value.", "salary.errors"); + BindingManager.AddBinding("rbgGender.Value", "Employee.Gender"); + BindingManager.AddBinding("ddlAddressType.SelectedValue", "Employee.MailingAddress.AddressType"); + BindingManager.AddBinding("txtStreet1.Text", "Employee.MailingAddress.Street1"); + BindingManager.AddBinding("txtStreet2.Text", "Employee.MailingAddress.Street2"); + BindingManager.AddBinding("txtCity.Text", "Employee.MailingAddress.City"); + BindingManager.AddBinding("txtState.Text", "Employee.MailingAddress.State"); + BindingManager.AddBinding("txtPostalCode.Text", "Employee.MailingAddress.PostalCode"); + BindingManager.AddBinding("txtCountry.Text", "Employee.MailingAddress.Country"); + } + + protected void Page_Load(object sender, EventArgs e) + {} + + + protected void btnSave_Click(object sender, EventArgs e) + { + // do something with employee such as: + // employeeDao.Save(Employee); + + Debug.Write(Employee); + } +} + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/Default.aspx new file mode 100644 index 00000000..460b5055 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/Default.aspx @@ -0,0 +1,73 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> + + + + + + Spring.NET Web Framework Quick Start Guide + + +

    Welcome to Spring.NET Web Framework Quick Start Guide

    +
    +

    + This sample application provides a number of small Quick Starts + that demonstrate various features of the Spring.NET Web Framework. +

    +

    + Spring.NET Web Framework is an extension to ASP.NET that adds many + powerful features, such as Dependency Injection for web pages and + controls, bi-directional data binding, localization support, etc. +

    +

    + It also integrates extremely well with Spring.NET Data Validation Framework + and allows you to declaratively configure very complex and sophisticated + data validation rules. +

    +

    + Once you become acquainted with all the individual features of Spring.NET + Web Framework by studying this application, you should take a look at SpringAir + sample application for a more integrated view, in order to see how many of these + features are combined into an application. +

    +

    Feature Categories

    +

    + Following sections provide a brief overview of different Spring.NET Web Framework + feature catagories. Within each category you will find small examples that clearly + demonstrate various features within that category, which will be very beneficial + to everyone who prefers to learn by example. +

    +

    Dependency Injection

    +

    + Samples in this category show you how to inject dependencies into your web pages + and controls using Spring.NET DI Container. You will learn about different + configuration options, basic steps you need to take in order to enable DI in your + pages, and then move on to more complex examples that will show you how to separate + your application into components using hierarchical web contexts. +

    +

    Bi-directional Data Binding

    +

    + By studying samples in this category, you will learn how to declaratively define + data binding rules that will save you a ton of time and many lines of code when + developing Spring.NET-powered web applications. We will start with basic examples + and then move on to using more advanced features, such as type conversion, formatting, + automatic validation and binding to complex iterative structures. +

    +

    Localization

    +

    + In this section you will learn how to properly localize your web applications. We will + start with very simple automatic localization, and then move on to demonstrate different + user culture management strategies, explicit localization options, and localization of + images within your application. +

    +

    Data Validation

    +

    + Spring.NET includes a very powerful, generic Data Validation Framework. This framework is + very tightly integrated with Spring.NET Web Framework, and it allows you to configure + sophisticated validation rules and execute them as necessary. In this section we will + start with a Hello World! validation example and then move on to show you how you can + compose advanced validation rules that leverage validation groups, conditional validation, + validator composition, as well as how to create and use your custom validators. +

    +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/Default.aspx.cs new file mode 100644 index 00000000..e4822622 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/Default.aspx.cs @@ -0,0 +1,17 @@ +using System; +using System.Data; +using System.Configuration; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class _Default : System.Web.UI.Page +{ + protected void Page_Load(object sender, EventArgs e) + { + + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/Web.Config b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/Web.Config new file mode 100644 index 00000000..bf3acac1 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/Web.Config @@ -0,0 +1,63 @@ + + + + +
    +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/App_Code/TextFileWebEventProvider.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/App_Code/TextFileWebEventProvider.cs new file mode 100644 index 00000000..6a25359f --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/App_Code/TextFileWebEventProvider.cs @@ -0,0 +1,99 @@ +using System; +using System.Web.Management; +using System.Configuration.Provider; +using System.Collections.Specialized; +using System.Web.Hosting; +using System.IO; +using System.Security.Permissions; +using System.Web; + + +public class TextFileWebEventProvider : WebEventProvider +{ + private string _LogFileName; + + public override void Initialize(string name, + NameValueCollection config) + { +// Verify that config isn't null + if (config == null) + throw new ArgumentNullException("config"); +// Assign the provider a default name if it doesn't have one + if (String.IsNullOrEmpty(name)) + name = "TextFileWebEventProvider"; +// Add a default "description" attribute to config if the +// attribute doesn't exist or is empty + if (string.IsNullOrEmpty(config["description"])) + { + config.Remove("description"); + config.Add("description", "Text file Web event provider"); + } +// Call the base class's Initialize method + base.Initialize(name, config); +// Initialize _LogFileName and make sure the path +// is app-relative + string path = config["logFileName"]; + if (String.IsNullOrEmpty(path)) + throw new ProviderException + ("Missing logFileName attribute"); + if (!VirtualPathUtility.IsAppRelative(path)) + throw new ArgumentException + ("logFileName must be app-relative"); + string fullyQualifiedPath = VirtualPathUtility.Combine + (VirtualPathUtility.AppendTrailingSlash + (HttpRuntime.AppDomainAppVirtualPath), path); + _LogFileName = HostingEnvironment.MapPath(fullyQualifiedPath); + config.Remove("logFileName"); +// Make sure we have permission to write to the log file +// throw an exception if we don't + FileIOPermission permission = + new FileIOPermission(FileIOPermissionAccess.Write | + FileIOPermissionAccess.Append, _LogFileName); + permission.Demand(); +// Throw an exception if unrecognized attributes remain + if (config.Count > 0) + { + string attr = config.GetKey(0); + if (!String.IsNullOrEmpty(attr)) + throw new ProviderException + ("Unrecognized attribute: " + attr); + } + } + + public override void ProcessEvent(WebBaseEvent raisedEvent) + { +// Write an entry to the log file + LogEntry(FormatEntry(raisedEvent)); + } + + public override void Flush() + { + } + + public override void Shutdown() + { + } + +// Helper methods + private string FormatEntry(WebBaseEvent e) + { + return String.Format("{0}\t{1}\t{2} (Event Code: {3})", + e.EventTime, e.GetType().ToString(), e.Message, + e.EventCode); + } + + private void LogEntry(string entry) + { + StreamWriter writer = null; + try + { + writer = new StreamWriter(_LogFileName, true); + writer.WriteLine(entry); + } + finally + { + if (writer != null) + writer.Close(); + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/App_Data/ASPNETDB.MDF b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/App_Data/ASPNETDB.MDF new file mode 100644 index 00000000..5ecfc728 Binary files /dev/null and b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/App_Data/ASPNETDB.MDF differ diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/App_Data/aspnetdb_log.ldf b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/App_Data/aspnetdb_log.ldf new file mode 100644 index 00000000..8134239e Binary files /dev/null and b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/App_Data/aspnetdb_log.ldf differ diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Config/Web.xml b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Config/Web.xml new file mode 100644 index 00000000..749fdda8 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Config/Web.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Default.aspx new file mode 100644 index 00000000..b20df1fb --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Default.aspx @@ -0,0 +1,24 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> + + + + + + Untitled Page + + +
    + +
    + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Default.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Default.aspx.cs new file mode 100644 index 00000000..0d49e900 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Default.aspx.cs @@ -0,0 +1,18 @@ +using System; +using System.Data; +using System.Configuration; +using System.Collections; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class _Default : System.Web.UI.Page +{ + protected void Page_Load(object sender, EventArgs e) + { + + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Global.asax b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Global.asax new file mode 100644 index 00000000..72a11eeb --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Global.asax @@ -0,0 +1,35 @@ +<%@ Application Language="C#" %> + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/SitemapTest.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/SitemapTest.aspx new file mode 100644 index 00000000..9e1ef2ed --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/SitemapTest.aspx @@ -0,0 +1,38 @@ +<%@ Page Language="C#" %> + + + +Profile Provider Test + + + +
    + + + +
    + +
    +
    + + +
    +
    + +
    + + \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/SitemapTest.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/SitemapTest.aspx.cs new file mode 100644 index 00000000..489fcea2 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/SitemapTest.aspx.cs @@ -0,0 +1,15 @@ +using System; +using System.Data; +using System.Configuration; +using System.Collections; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class Test1 : Spring.Web.UI.Page +{ + +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/MembershipTest.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/MembershipTest.aspx new file mode 100644 index 00000000..2f9aa1e6 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/MembershipTest.aspx @@ -0,0 +1,38 @@ +<%@ Page Language="C#" %> + + + +Profile Provider Test + + + +
    + + + +
    + +
    +
    + + +
    +
    + +
    + + \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/MembershipTest.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/MembershipTest.aspx.cs new file mode 100644 index 00000000..ea56cee7 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/MembershipTest.aspx.cs @@ -0,0 +1,15 @@ +using System; +using System.Data; +using System.Configuration; +using System.Collections; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class MembershipTest : Spring.Web.UI.Page +{ + +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/ProfileTest.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/ProfileTest.aspx new file mode 100644 index 00000000..ab472564 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/ProfileTest.aspx @@ -0,0 +1,27 @@ +<%@ Page Language="C#" EnableViewState="true" %> +<%@ Import namespace="System.Web.Profile"%> + + + +Profile Provider Test + + + +
    + + + +
    + +
    +
    + + \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/ProfileTest.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/ProfileTest.aspx.cs new file mode 100644 index 00000000..e9c7ae97 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/ProfileTest.aspx.cs @@ -0,0 +1,15 @@ +using System; +using System.Data; +using System.Configuration; +using System.Collections; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class ProfileTest : Spring.Web.UI.Page +{ + +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/RolesTest.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/RolesTest.aspx new file mode 100644 index 00000000..dcca615b --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/RolesTest.aspx @@ -0,0 +1,39 @@ +<%@ Page Language="C#" %> + + + +Profile Provider Test + + + +
    + + + +
    + +
    +
    + + +
    +
    + +
    + + \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/RolesTest.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/RolesTest.aspx.cs new file mode 100644 index 00000000..755d54f0 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/RolesTest.aspx.cs @@ -0,0 +1,15 @@ +using System; +using System.Data; +using System.Configuration; +using System.Collections; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class RolesTest : Spring.Web.UI.Page +{ + +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/Web.config b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/Web.config new file mode 100644 index 00000000..d772f475 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/TestProviders/Web.config @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/UserLogin.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/UserLogin.aspx new file mode 100644 index 00000000..96366dfb --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/UserLogin.aspx @@ -0,0 +1,33 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="UserLogin.aspx.cs" Inherits="Admin_Login" %> + + + + + login + + +
    +
    + Please enter your login information:
    +
    + + + + + + + + + +
    Username: + +
    Password: + +
    +
    +
    + +
    +
    + + \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/UserLogin.aspx.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/UserLogin.aspx.cs new file mode 100644 index 00000000..5296cb96 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/UserLogin.aspx.cs @@ -0,0 +1,25 @@ +using System; +using System.Web.Security; +using Spring.Web.UI; + +public partial class Admin_Login : Page +{ + protected void Page_Load(object sender, EventArgs e) + { + } + + protected void LoginAction_Click(object sender, EventArgs e) + { + string user = UsernameText.Text; + string pass = PasswordText.Text; + + if (Membership.ValidateUser(user, pass)) + { + FormsAuthentication.RedirectFromLoginPage(user, false); + } + else + { + LegendStatus.Text = "Error!"; + } + } +} \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Web.config b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Web.config new file mode 100644 index 00000000..fab1e14f --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Web.config @@ -0,0 +1,93 @@ + + + + +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Web.sitemap b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Web.sitemap new file mode 100644 index 00000000..71175a12 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.Providers/Web.sitemap @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/examples/Spring/SpringAir/SpringAir.2003.sln b/examples/Spring/SpringAir/SpringAir.2003.sln new file mode 100644 index 00000000..f3681514 --- /dev/null +++ b/examples/Spring/SpringAir/SpringAir.2003.sln @@ -0,0 +1,53 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpringAir.Core.2003", "src\SpringAir.Core\SpringAir.Core.2003.csproj", "{5E3427D8-62FC-483F-A86B-B44A79A46BCC}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpringAir.Core.Tests.2003", "test\SpringAir.Core.Tests\SpringAir.Core.Tests.2003.csproj", "{BB9AFE5E-7785-43AC-86ED-CF0A073F006E}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpringAir.Data.Ado.2003", "src\SpringAir.Data.Ado\SpringAir.Data.Ado.2003.csproj", "{78B285FE-27F3-44F4-84B1-7A589AB48EF3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpringAir.Web.2003", "http://localhost/SpringAir.2003/SpringAir.Web.2003.csproj", "{B29E702D-DC32-42B4-8A15-AC7C07CFC48D}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpringAir.Data.Ado.Tests.2003", "test\SpringAir.Data.Ado.Tests\SpringAir.Data.Ado.Tests.2003.csproj", "{B0A6F98E-5363-4575-BB6B-A84E91895468}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Debug.ActiveCfg = Debug|.NET + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Debug.Build.0 = Debug|.NET + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Release.ActiveCfg = Release|.NET + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Release.Build.0 = Release|.NET + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Debug.ActiveCfg = Debug|.NET + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Debug.Build.0 = Debug|.NET + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Release.ActiveCfg = Release|.NET + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Release.Build.0 = Release|.NET + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Debug.ActiveCfg = Debug|.NET + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Debug.Build.0 = Debug|.NET + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Release.ActiveCfg = Release|.NET + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Release.Build.0 = Release|.NET + {B29E702D-DC32-42B4-8A15-AC7C07CFC48D}.Debug.ActiveCfg = Debug|.NET + {B29E702D-DC32-42B4-8A15-AC7C07CFC48D}.Debug.Build.0 = Debug|.NET + {B29E702D-DC32-42B4-8A15-AC7C07CFC48D}.Release.ActiveCfg = Release|.NET + {B29E702D-DC32-42B4-8A15-AC7C07CFC48D}.Release.Build.0 = Release|.NET + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Debug.ActiveCfg = Debug|.NET + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Debug.Build.0 = Debug|.NET + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Release.ActiveCfg = Release|.NET + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/examples/Spring/SpringAir/SpringAir.2005.sln b/examples/Spring/SpringAir/SpringAir.2005.sln new file mode 100644 index 00000000..7e02c9af --- /dev/null +++ b/examples/Spring/SpringAir/SpringAir.2005.sln @@ -0,0 +1,129 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "D:\...\SpringAir.Web.2005\", "src\SpringAir.Web.2005\", "{839602F5-8077-497D-9CCB-C9F02741CA55}" + ProjectSection(WebsiteProperties) = preProject + ProjectReferences = "{5E3427D8-62FC-483F-A86B-B44A79A46BCC}|SpringAir.Core.dll;{78B285FE-27F3-44F4-84B1-7A589AB48EF3}|SpringAir.Data.Ado.dll;{0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}|SpringAir.Web.2005.References.dll;" + Debug.AspNetCompiler.VirtualPath = "/SpringAir.Web.2005" + Debug.AspNetCompiler.PhysicalPath = "src\SpringAir.Web.2005\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\SpringAir.Web.2005\" + Debug.AspNetCompiler.Updateable = "true" + Debug.AspNetCompiler.ForceOverwrite = "true" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.VirtualPath = "/SpringAir.Web.2005" + Release.AspNetCompiler.PhysicalPath = "src\SpringAir.Web.2005\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\SpringAir.Web.2005\" + Release.AspNetCompiler.Updateable = "true" + Release.AspNetCompiler.ForceOverwrite = "true" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.Debug = "False" + VWDPort = "4816" + DefaultWebSiteLanguage = "Visual C#" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpringAir.Core.2005", "src\SpringAir.Core\SpringAir.Core.2005.csproj", "{5E3427D8-62FC-483F-A86B-B44A79A46BCC}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpringAir.Core.Tests.2005", "test\SpringAir.Core.Tests\SpringAir.Core.Tests.2005.csproj", "{BB9AFE5E-7785-43AC-86ED-CF0A073F006E}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpringAir.Data.Ado.2005", "src\SpringAir.Data.Ado\SpringAir.Data.Ado.2005.csproj", "{78B285FE-27F3-44F4-84B1-7A589AB48EF3}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpringAir.Data.Ado.Tests.2005", "test\SpringAir.Data.Ado.Tests\SpringAir.Data.Ado.Tests.2005.csproj", "{B0A6F98E-5363-4575-BB6B-A84E91895468}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpringAir.Web.2005.References", "src\SpringAir.Web.2005.References\SpringAir.Web.2005.References.csproj", "{0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|.NET = Debug|.NET + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Release|.NET = Release|.NET + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {839602F5-8077-497D-9CCB-C9F02741CA55}.Debug|.NET.ActiveCfg = Debug|.NET + {839602F5-8077-497D-9CCB-C9F02741CA55}.Debug|.NET.Build.0 = Debug|.NET + {839602F5-8077-497D-9CCB-C9F02741CA55}.Debug|Any CPU.ActiveCfg = Debug|.NET + {839602F5-8077-497D-9CCB-C9F02741CA55}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET + {839602F5-8077-497D-9CCB-C9F02741CA55}.Debug|Mixed Platforms.Build.0 = Debug|.NET + {839602F5-8077-497D-9CCB-C9F02741CA55}.Release|.NET.ActiveCfg = Debug|.NET + {839602F5-8077-497D-9CCB-C9F02741CA55}.Release|.NET.Build.0 = Debug|.NET + {839602F5-8077-497D-9CCB-C9F02741CA55}.Release|Any CPU.ActiveCfg = Debug|.NET + {839602F5-8077-497D-9CCB-C9F02741CA55}.Release|Mixed Platforms.ActiveCfg = Debug|.NET + {839602F5-8077-497D-9CCB-C9F02741CA55}.Release|Mixed Platforms.Build.0 = Debug|.NET + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Debug|.NET.ActiveCfg = Debug|Any CPU + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Release|.NET.ActiveCfg = Release|Any CPU + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Release|Any CPU.Build.0 = Release|Any CPU + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5E3427D8-62FC-483F-A86B-B44A79A46BCC}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Debug|.NET.ActiveCfg = Debug|Any CPU + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Release|.NET.ActiveCfg = Release|Any CPU + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Release|Any CPU.Build.0 = Release|Any CPU + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Debug|.NET.ActiveCfg = Debug|Any CPU + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Release|.NET.ActiveCfg = Release|Any CPU + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Release|Any CPU.Build.0 = Release|Any CPU + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {78B285FE-27F3-44F4-84B1-7A589AB48EF3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Debug|.NET.ActiveCfg = Debug|Any CPU + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Release|.NET.ActiveCfg = Release|Any CPU + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Release|Any CPU.Build.0 = Release|Any CPU + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B0A6F98E-5363-4575-BB6B-A84E91895468}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}.Debug|.NET.ActiveCfg = Debug|Any CPU + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}.Release|.NET.ActiveCfg = Release|Any CPU + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}.Release|Any CPU.Build.0 = Release|Any CPU + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910}.Release|Mixed Platforms.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/Spring/SpringAir/SpringAir.build b/examples/Spring/SpringAir/SpringAir.build new file mode 100644 index 00000000..a498a6c0 --- /dev/null +++ b/examples/Spring/SpringAir/SpringAir.build @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/data/access/readme.txt b/examples/Spring/SpringAir/data/access/readme.txt new file mode 100644 index 00000000..77791c6b --- /dev/null +++ b/examples/Spring/SpringAir/data/access/readme.txt @@ -0,0 +1 @@ +Contains a Microsoft Access (2003) database for use with the SpringAir reference application. \ No newline at end of file diff --git a/examples/Spring/SpringAir/data/msde/create.sql b/examples/Spring/SpringAir/data/msde/create.sql new file mode 100644 index 00000000..735a6c81 --- /dev/null +++ b/examples/Spring/SpringAir/data/msde/create.sql @@ -0,0 +1,121 @@ +-- +-- Copyright © 2002-2005 the original author or authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- +-- MSDE schema creation script for the SpringAir reference application +-- +-- $Id: create.sql,v 1.2 2005/10/02 00:04:01 springboy Exp $ +-- + +USE MASTER +GO + +IF EXISTS(SELECT * FROM sysdatabases WHERE name='SpringAir') + DROP DATABASE SpringAir +GO + + +CREATE DATABASE SpringAir +GO + +USE SpringAir +GO + +CREATE TABLE cabin_class +( + id INT PRIMARY KEY NOT NULL, + description VARCHAR(20) +) +GO + +CREATE TABLE passenger +( + id INT PRIMARY KEY NOT NULL, + first_name VARCHAR(30), + surname VARCHAR(30) + -- I'll create a proper relation later, this stub is sufficient for now... +) +GO + +CREATE TABLE airport +( + id INT PRIMARY KEY NOT NULL, + code CHAR(3) NOT NULL, + city VARCHAR(50), + description VARCHAR(100) +) +GO + +CREATE TABLE aircraft +( + id INT PRIMARY KEY NOT NULL, + model VARCHAR(50) NOT NULL, + row_count INT NOT NULL, + seats_per_row INT NOT NULL +) +GO + +-- an aircraft may have many cabins; this relation models how many seats there are per cabin per aircraft... +CREATE TABLE aircraft_cabin_seat +( + aircraft_id INT NOT NULL FOREIGN KEY REFERENCES aircraft(id), + cabin_class_id INT NOT NULL FOREIGN KEY REFERENCES cabin_class(id), + seat_count INT NOT NULL DEFAULT 0 +) +GO + +CREATE TABLE flight +( + id INT PRIMARY KEY NOT NULL, + flight_number VARCHAR(20) NOT NULL UNIQUE, + aircraft_id INT NOT NULL FOREIGN KEY REFERENCES aircraft(id), + departure_airport_id INT NOT NULL FOREIGN KEY REFERENCES airport(id), + destination_airport_id INT NOT NULL FOREIGN KEY REFERENCES airport(id), + departure_date DATETIME NOT NULL +) +GO + +-- if a seat is in this relation, then it is reserved for that flight... +CREATE TABLE reserved_seat +( + flight_id int NOT NULL FOREIGN KEY REFERENCES flight(id), + seat_number VARCHAR(20) NOT NULL +) +GO + +CREATE TABLE reservation +( + id INT PRIMARY KEY NOT NULL, + passenger_id INT NOT NULL UNIQUE FOREIGN KEY REFERENCES passenger(id), + price MONEY NOT NULL DEFAULT 0 +) +GO + +CREATE TABLE leg +( + id INT PRIMARY KEY NOT NULL, + reservation_id INT NOT NULL UNIQUE FOREIGN KEY REFERENCES reservation(id), + flight_id INT NOT NULL UNIQUE FOREIGN KEY REFERENCES flight(id), + cabin_class_id INT NOT NULL FOREIGN KEY REFERENCES cabin_class(id) +) +GO + +CREATE TABLE leg_seat +( + leg_id INT NOT NULL UNIQUE FOREIGN KEY REFERENCES leg(id), + reservation_id INT NOT NULL UNIQUE FOREIGN KEY REFERENCES reservation(id), + seat_number VARCHAR(200) NOT NULL +) +GO \ No newline at end of file diff --git a/examples/Spring/SpringAir/data/msde/populate.sql b/examples/Spring/SpringAir/data/msde/populate.sql new file mode 100644 index 00000000..c9a75e96 --- /dev/null +++ b/examples/Spring/SpringAir/data/msde/populate.sql @@ -0,0 +1,94 @@ +-- +-- Copyright © 2002-2005 the original author or authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- +-- MSDE schema population script for the SpringAir reference application +-- +-- $Id: populate.sql,v 1.1 2005/10/01 22:57:09 springboy Exp $ +-- + +USE SpringAir +GO + +INSERT INTO cabin_class(id, description) VALUES(0, "Coach") +INSERT INTO cabin_class(id, description) VALUES(1, "Business") +INSERT INTO cabin_class(id, description) VALUES(2, "First") + +GO + +INSERT INTO airport(id, code, city, description) VALUES(0, "MCO", "Orlando", "Orlando International Airport") +INSERT INTO airport(id, code, city, description) VALUES(1, "SFO", "San Francisco", "San Francisco International Airport") +INSERT INTO airport(id, code, city, description) VALUES(2, "LHR", "Heathrow", "London Heathrow International Airport") +INSERT INTO airport(id, code, city, description) VALUES(3, "ANC", "Anchorage", "Anchorage International Airport") + +GO + +INSERT INTO passenger(id, first_name, surname) VALUES(0, "Ivan", "Goncharov") +INSERT INTO passenger(id, first_name, surname) VALUES(1, "Harriet", "Wheeler") +INSERT INTO passenger(id, first_name, surname) VALUES(3, "Rose", "Kane") +INSERT INTO passenger(id, first_name, surname) VALUES(5, "Leonardo", "Medici") +INSERT INTO passenger(id, first_name, surname) VALUES(6, "Enrico", "Dandolo") + +GO + +INSERT INTO aircraft(id, model, row_count, seats_per_row) VALUES(0, "Boeing 747", 240, 8) +INSERT INTO aircraft(id, model, row_count, seats_per_row) VALUES(1, "Little Nellie", 1, 1) +INSERT INTO aircraft(id, model, row_count, seats_per_row) VALUES(2, "The Fast Lady", 2, 2) +INSERT INTO aircraft(id, model, row_count, seats_per_row) VALUES(3, "Sopwith Camel", 10, 4) +INSERT INTO aircraft(id, model, row_count, seats_per_row) VALUES(4, "Boeing 747", 240, 8) + +GO + +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(0, 0, 180) +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(0, 1, 40) +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(0, 2, 20) +-- Little Nellie defended her honour... admirably. +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(1, 2, 1) +-- I've taken your advice... I've bought the Fast Lady! +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(2, 2, 4) +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(3, 0, 30) +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(3, 2, 10) +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(4, 0, 180) +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(4, 1, 40) +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(4, 2, 20) + +GO + +-- uses British date format for the insert... +INSERT INTO flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(0, "S87r17688972", 0, 0, 1, CONVERT(DATETIME, '09-11-2005 12:50:00', 103)) +INSERT INTO flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(1, "Mjhgsdjhds", 4, 0, 1, CONVERT(DATETIME, '10-11-2005 12:50:00', 103)) +INSERT INTO flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(2, "kbfbxsKLJ34GW", 0, 0, 1, CONVERT(DATETIME, '11-11-2005 12:50:00', 103)) +INSERT INTO flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(3, "KJ2BF3JH", 4, 0, 1, CONVERT(DATETIME, '12-11-2005 12:50:00', 103)) + +INSERT INTO flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(4, "jh34jhdsf", 4, 1, 0, CONVERT(DATETIME, '09-11-2005 13:50:00', 103)) +INSERT INTO flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(5, "kbwdjnkdvb", 0, 1, 0, CONVERT(DATETIME, '10-11-2005 13:50:00', 103)) +INSERT INTO flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(6, "hb3hjwfhjdvs", 4, 1, 0, CONVERT(DATETIME, '11-11-2005 13:50:00', 103)) +INSERT INTO flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(7, "782876bjk", 0, 1, 0, CONVERT(DATETIME, '12-11-2005 13:50:00', 103)) + +GO + +-- reserve a coupla seats on the first flight out of Orlando; I'm off to GatorJUG and Macy is coming wit' me, yeehar... +INSERT INTO reserved_seat(flight_id, seat_number) VALUES(0, "A21") +INSERT INTO reserved_seat(flight_id, seat_number) VALUES(0, "A22") + +GO \ No newline at end of file diff --git a/examples/Spring/SpringAir/data/mysql/create.sql b/examples/Spring/SpringAir/data/mysql/create.sql new file mode 100644 index 00000000..f82a1557 --- /dev/null +++ b/examples/Spring/SpringAir/data/mysql/create.sql @@ -0,0 +1,76 @@ +CREATE TABLE cabin_class +( + id integer NOT NULL PRIMARY KEY AUTO_INCREMENT, + description VARCHAR(16) +); + +CREATE TABLE passenger +( + id integer NOT NULL PRIMARY KEY AUTO_INCREMENT, + first_name VARCHAR(64), + surname VARCHAR(64) + -- I'll create a proper relation later, this stub is sufficient for now... +); + +CREATE TABLE airport +( + id integer NOT NULL PRIMARY KEY AUTO_INCREMENT, + code CHAR(3) NOT NULL, + city VARCHAR(64), + description VARCHAR(64) +); + +CREATE TABLE aircraft +( + id integer NOT NULL PRIMARY KEY AUTO_INCREMENT, + model VARCHAR(50) NOT NULL, + row_count integer NOT NULL, + seats_per_row integer NOT NULL +); + +-- an aircraft may have many cabins; this relation models how many seats there are per cabin per aircraft... +CREATE TABLE aircraft_cabin_seat +( + aircraft_id integer NOT NULL, + cabin_class_id integer NOT NULL, + seat_count integer DEFAULT 0 NOT NULL +); + +CREATE TABLE flight +( + id integer NOT NULL PRIMARY KEY AUTO_INCREMENT, + flight_number VARCHAR(256) NOT NULL, + aircraft_id integer NOT NULL, + departure_airport_id integer NOT NULL, + destination_airport_id integer NOT NULL, + departure_date date NOT NULL +); + +-- if a seat is in this relation, then it is reserved for that flight... +CREATE TABLE reserved_seat +( + flight_id integer NOT NULL, + seat_number VARCHAR(20) NOT NULL +); + +CREATE TABLE reservation +( + id integer NOT NULL PRIMARY KEY AUTO_INCREMENT, + passenger_id integer NOT NULL UNIQUE, + price decimal(8, 2) DEFAULT 0 NOT NULL +); + +CREATE TABLE leg +( + id integer NOT NULL PRIMARY KEY AUTO_INCREMENT, + reservation_id integer NOT NULL UNIQUE, + flight_id integer NOT NULL, + cabin_class_id integer NOT NULL +); + +CREATE TABLE leg_seat +( + leg_id integer NOT NULL UNIQUE, + reservation_id integer NOT NULL UNIQUE, + seat_number VARCHAR(24) NOT NULL +); diff --git a/examples/Spring/SpringAir/data/mysql/drop.sql b/examples/Spring/SpringAir/data/mysql/drop.sql new file mode 100644 index 00000000..8226f562 --- /dev/null +++ b/examples/Spring/SpringAir/data/mysql/drop.sql @@ -0,0 +1,13 @@ +drop table reserved_seat; +drop table leg_seat; + +drop table leg; +drop table reservation; +drop table flight; + +drop table airport; +drop table aircraft_cabin_seat; +drop table aircraft; + +drop table cabin_class; +drop table passenger; diff --git a/examples/Spring/SpringAir/data/mysql/populate.sql b/examples/Spring/SpringAir/data/mysql/populate.sql new file mode 100644 index 00000000..f8910580 --- /dev/null +++ b/examples/Spring/SpringAir/data/mysql/populate.sql @@ -0,0 +1,58 @@ +INSERT INTO cabin_class(description) VALUES('Coach'); +INSERT INTO cabin_class(description) VALUES('Business'); +INSERT INTO cabin_class(description) VALUES('First'); + +INSERT INTO airport(code, city, description) VALUES('MCO', 'Orlando', 'Orlando International Airport'); +INSERT INTO airport(code, city, description) VALUES('SFO', 'San Francisco', 'San Francisco International Airport'); +INSERT INTO airport(code, city, description) VALUES('LHR', 'Heathrow', 'London Heathrow International Airport'); +INSERT INTO airport(code, city, description) VALUES('ANC', 'Anchorage', 'Anchorage International Airport'); + +INSERT INTO passenger(first_name, surname) VALUES('Ivan', 'Goncharov'); +INSERT INTO passenger(first_name, surname) VALUES('Harriet', 'Wheeler'); +INSERT INTO passenger(first_name, surname) VALUES('Rose', 'Kane'); +INSERT INTO passenger(first_name, surname) VALUES('Leonardo', 'Medici'); +INSERT INTO passenger(first_name, surname) VALUES('Enrico', 'Dandolo'); + +INSERT INTO aircraft(model, row_count, seats_per_row) VALUES('Boeing 747', 240, 8); +INSERT INTO aircraft(model, row_count, seats_per_row) VALUES('Little Nellie', 1, 1); +INSERT INTO aircraft(model, row_count, seats_per_row) VALUES('The Fast Lady', 2, 2); +INSERT INTO aircraft(model, row_count, seats_per_row) VALUES('Sopwith Camel', 10, 4); +INSERT INTO aircraft(model, row_count, seats_per_row) VALUES('Boeing 747', 240, 8); + +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(1, 0, 180); +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(1, 1, 40); +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(1, 2, 20); +-- Little Nellie defended her honour... admirably. +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(2, 2, 1); +-- I've taken your advice... I've bought the Fast Lady! +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(3, 2, 4); +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(4, 0, 30); +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(4, 2, 10); +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(5, 0, 180); +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(5, 1, 40); +INSERT INTO aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(5, 2, 20); + +-- uses Universal date format for the insert... +INSERT INTO flight(flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES('S87r17688972', 1, 1, 2, DATE('2005-11-09 12:50')); +INSERT INTO flight(flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES('Mjhgsdjhds', 5, 1, 2, DATE('2005-11-10 12:50')); +INSERT INTO flight(flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES('kbfbxsKLJ34GW', 1, 1, 2, DATE('2005-11-11 12:50')); +INSERT INTO flight(flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES('KJ2BF3JH', 5, 1, 2, DATE('2005-11-12 12:50')); + +INSERT INTO flight(flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES('jh34jhdsf', 5, 2, 1, DATE('2005-11-09 13:50')); +INSERT INTO flight(flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES('kbwdjnkdvb', 1, 2, 1, DATE('2005-11-10 13:50')); +INSERT INTO flight(flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES('hb3hjwfhjdvs', 5, 2, 1, DATE('2005-11-11 13:50')); +INSERT INTO flight(flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES('782876bjk', 1, 2, 1, DATE('2005-11-12 13:50')); + +-- reserve a coupla seats on the first flight out of Orlando; I'm off to GatorJUG and Macy is coming wit' me, yeehar... +INSERT INTO reserved_seat(flight_id, seat_number) VALUES(1, 'A21'); +INSERT INTO reserved_seat(flight_id, seat_number) VALUES(1, 'A22'); + +commit; diff --git a/examples/Spring/SpringAir/data/mysql/readme.txt b/examples/Spring/SpringAir/data/mysql/readme.txt new file mode 100644 index 00000000..9494347a --- /dev/null +++ b/examples/Spring/SpringAir/data/mysql/readme.txt @@ -0,0 +1,5 @@ +This directory contains scripts for creating and populating the SpringAir database on MySQL. + +MySQL - http://www.mysql.com/ + +Note: the MySQL version that I (the author) used to write and tests these scripts against was 5.0.16 (Win32). It has not been tested against any other version (or indeed on any other operating system). diff --git a/examples/Spring/SpringAir/data/mysql/refresh.sql b/examples/Spring/SpringAir/data/mysql/refresh.sql new file mode 100644 index 00000000..f58544eb --- /dev/null +++ b/examples/Spring/SpringAir/data/mysql/refresh.sql @@ -0,0 +1,5 @@ +use springair; +source drop.sql +source create.sql +source populate.sql +commit; diff --git a/examples/Spring/SpringAir/data/mysql/schema.sql b/examples/Spring/SpringAir/data/mysql/schema.sql new file mode 100644 index 00000000..31f17a43 --- /dev/null +++ b/examples/Spring/SpringAir/data/mysql/schema.sql @@ -0,0 +1,7 @@ +drop database springair; +create database springair; +use mysql; +grant all privileges on *.* to 'spring'@'localhost' identified by 'spring'; +grant all privileges on *.* to 'spring'@'%' identified by 'spring'; +commit; +use springair; \ No newline at end of file diff --git a/examples/Spring/SpringAir/data/oracle/create.sql b/examples/Spring/SpringAir/data/oracle/create.sql new file mode 100644 index 00000000..de550c05 --- /dev/null +++ b/examples/Spring/SpringAir/data/oracle/create.sql @@ -0,0 +1,106 @@ +CREATE TABLE t_cabin_class +( + id integer NOT NULL PRIMARY KEY, + description VARCHAR(16) +); + +CREATE TABLE t_passenger +( + id integer NOT NULL PRIMARY KEY, + first_name VARCHAR(64), + surname VARCHAR(64) + -- I'll create a proper relation later, this stub is sufficient for now... +); + +CREATE TABLE t_airport +( + id integer NOT NULL PRIMARY KEY, + code CHAR(3) NOT NULL, + city VARCHAR(64), + description VARCHAR(64) +); + +CREATE TABLE t_aircraft +( + id integer NOT NULL PRIMARY KEY, + model VARCHAR(50) NOT NULL, + row_count integer NOT NULL, + seats_per_row integer NOT NULL +); + +-- an aircraft may have many cabins; this relation models how many seats there are per cabin per aircraft... +CREATE TABLE t_aircraft_cabin_seat +( + aircraft_id integer NOT NULL, + cabin_class_id integer NOT NULL, + seat_count integer DEFAULT(0) NOT NULL +); + +alter table t_aircraft_cabin_seat add ( + constraint fk_cabin_seat_to_aircraft foreign key(aircraft_id) references t_aircraft(id), + constraint fk_cabin_seat_to_cabin foreign key(cabin_class_id) references t_cabin_class(id) +); + +CREATE TABLE t_flight +( + id integer NOT NULL PRIMARY KEY, + flight_number VARCHAR(256) NOT NULL UNIQUE, + aircraft_id integer NOT NULL, + departure_airport_id integer NOT NULL, + destination_airport_id integer NOT NULL, + departure_date date NOT NULL +); + +alter table t_flight add ( + constraint fk_flight_to_aircraft foreign key(aircraft_id) references t_aircraft(id), + constraint fk_flight_to_dep_airport foreign key(departure_airport_id) references t_airport(id), + constraint fk_flight_to_dest_airport foreign key(destination_airport_id) references t_airport(id) +); + +-- if a seat is in this relation, then it is reserved for that flight... +CREATE TABLE t_reserved_seat +( + flight_id integer NOT NULL, + seat_number VARCHAR(20) NOT NULL +); + +alter table t_reserved_seat add ( + constraint fk_reserved_seat_to_flight foreign key(flight_id) references t_flight(id) +); + +CREATE TABLE t_reservation +( + id integer NOT NULL PRIMARY KEY, + passenger_id integer NOT NULL UNIQUE, + price number(8, 2) DEFAULT(0) NOT NULL +); + +alter table t_reservation add ( + constraint fk_reservation_to_flight foreign key(passenger_id) references t_passenger(id) +); + +CREATE TABLE t_leg +( + id integer NOT NULL PRIMARY KEY, + reservation_id integer NOT NULL UNIQUE, + flight_id integer NOT NULL, + cabin_class_id integer NOT NULL +); + +alter table t_leg add ( + constraint fk_leg_to_reservation foreign key(reservation_id) references t_reservation(id), + constraint fk_leg_to_flight foreign key(flight_id) references t_flight(id), + constraint fk_leg_to_cabin_class foreign key(cabin_class_id) references t_cabin_class(id) +); + +CREATE TABLE t_leg_seat +( + leg_id integer NOT NULL UNIQUE, + reservation_id integer NOT NULL UNIQUE, + seat_number VARCHAR(24) NOT NULL +); + +alter table t_leg_seat add ( + constraint fk_leg_seat_to_leg foreign key(leg_id) references t_leg(id), + constraint fk_leg_seat_to_reservation foreign key(reservation_id) references t_reservation(id) +); diff --git a/examples/Spring/SpringAir/data/oracle/drop.sql b/examples/Spring/SpringAir/data/oracle/drop.sql new file mode 100644 index 00000000..70767723 --- /dev/null +++ b/examples/Spring/SpringAir/data/oracle/drop.sql @@ -0,0 +1,13 @@ +drop table t_reserved_seat; +drop table t_leg_seat; + +drop table t_leg; +drop table t_reservation; +drop table t_flight; + +drop table t_airport; +drop table t_aircraft_cabin_seat; +drop table t_aircraft; + +drop table t_cabin_class; +drop table t_passenger; diff --git a/examples/Spring/SpringAir/data/oracle/populate.sql b/examples/Spring/SpringAir/data/oracle/populate.sql new file mode 100644 index 00000000..dc21a153 --- /dev/null +++ b/examples/Spring/SpringAir/data/oracle/populate.sql @@ -0,0 +1,79 @@ +-- +-- Copyright © 2002-2005 the original author or authors. +-- +-- Licensed under the Apache License, Version 2.0 (the 'License'); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an 'AS IS' BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- +-- Oracle schema population script for the SpringAir reference application +-- +-- $Id: populate.sql,v 1.1 2005/12/26 12:29:30 springboy Exp $ +-- + +INSERT INTO t_cabin_class(id, description) VALUES(0, 'Coach'); +INSERT INTO t_cabin_class(id, description) VALUES(1, 'Business'); +INSERT INTO t_cabin_class(id, description) VALUES(2, 'First'); + +INSERT INTO t_airport(id, code, city, description) VALUES(0, 'MCO', 'Orlando', 'Orlando International Airport'); +INSERT INTO t_airport(id, code, city, description) VALUES(1, 'SFO', 'San Francisco', 'San Francisco International Airport'); +INSERT INTO t_airport(id, code, city, description) VALUES(2, 'LHR', 'Heathrow', 'London Heathrow International Airport'); +INSERT INTO t_airport(id, code, city, description) VALUES(3, 'ANC', 'Anchorage', 'Anchorage International Airport'); + +INSERT INTO t_passenger(id, first_name, surname) VALUES(0, 'Ivan', 'Goncharov'); +INSERT INTO t_passenger(id, first_name, surname) VALUES(1, 'Harriet', 'Wheeler'); +INSERT INTO t_passenger(id, first_name, surname) VALUES(3, 'Rose', 'Kane'); +INSERT INTO t_passenger(id, first_name, surname) VALUES(5, 'Leonardo', 'Medici'); +INSERT INTO t_passenger(id, first_name, surname) VALUES(6, 'Enrico', 'Dandolo'); + +INSERT INTO t_aircraft(id, model, row_count, seats_per_row) VALUES(0, 'Boeing 747', 240, 8); +INSERT INTO t_aircraft(id, model, row_count, seats_per_row) VALUES(1, 'Little Nellie', 1, 1); +INSERT INTO t_aircraft(id, model, row_count, seats_per_row) VALUES(2, 'The Fast Lady', 2, 2); +INSERT INTO t_aircraft(id, model, row_count, seats_per_row) VALUES(3, 'Sopwith Camel', 10, 4); +INSERT INTO t_aircraft(id, model, row_count, seats_per_row) VALUES(4, 'Boeing 747', 240, 8); + +INSERT INTO t_aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(0, 0, 180); +INSERT INTO t_aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(0, 1, 40); +INSERT INTO t_aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(0, 2, 20); +-- Little Nellie defended her honour... admirably. +INSERT INTO t_aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(1, 2, 1); +-- I've taken your advice... I've bought the Fast Lady! +INSERT INTO t_aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(2, 2, 4); +INSERT INTO t_aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(3, 0, 30); +INSERT INTO t_aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(3, 2, 10); +INSERT INTO t_aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(4, 0, 180); +INSERT INTO t_aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(4, 1, 40); +INSERT INTO t_aircraft_cabin_seat(aircraft_id, cabin_class_id, seat_count) VALUES(4, 2, 20); + +-- uses British date format for the insert... +INSERT INTO t_flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(0, 'S87r17688972', 0, 0, 1, TO_DATE('09-11-2005 12:50','DD-MM-YYYY HH24:MI')); +INSERT INTO t_flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(1, 'Mjhgsdjhds', 4, 0, 1, TO_DATE('10-11-2005 12:50','DD-MM-YYYY HH24:MI')); +INSERT INTO t_flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(2, 'kbfbxsKLJ34GW', 0, 0, 1, TO_DATE('11-11-2005 12:50','DD-MM-YYYY HH24:MI')); +INSERT INTO t_flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(3, 'KJ2BF3JH', 4, 0, 1, TO_DATE('12-11-2005 12:50','DD-MM-YYYY HH24:MI')); + +INSERT INTO t_flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(4, 'jh34jhdsf', 4, 1, 0, TO_DATE('09-11-2005 13:50','DD-MM-YYYY HH24:MI')); +INSERT INTO t_flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(5, 'kbwdjnkdvb', 0, 1, 0, TO_DATE('10-11-2005 13:50','DD-MM-YYYY HH24:MI')); +INSERT INTO t_flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(6, 'hb3hjwfhjdvs', 4, 1, 0, TO_DATE('11-11-2005 13:50','DD-MM-YYYY HH24:MI')); +INSERT INTO t_flight(id, flight_number, aircraft_id, departure_airport_id, destination_airport_id, departure_date) + VALUES(7, '782876bjk', 0, 1, 0, TO_DATE('12-11-2005 13:50','DD-MM-YYYY HH24:MI')); + +-- reserve a coupla seats on the first flight out of Orlando; I'm off to GatorJUG and Macy is coming wit' me, yeehar... +INSERT INTO t_reserved_seat(flight_id, seat_number) VALUES(0, 'A21'); +INSERT INTO t_reserved_seat(flight_id, seat_number) VALUES(0, 'A22'); + +commit; \ No newline at end of file diff --git a/examples/Spring/SpringAir/data/oracle/readme.txt b/examples/Spring/SpringAir/data/oracle/readme.txt new file mode 100644 index 00000000..2c261210 --- /dev/null +++ b/examples/Spring/SpringAir/data/oracle/readme.txt @@ -0,0 +1,3 @@ +This directory contains scripts for creating and populating the SpringAir database on Oracle. + +Note: the Oracle version that I (the author) used to write and tests these scripts against was Oracle9i Enterprise Edition Release 9.2.0.1.0 (Win32). It has not been tested against any other version (or indeed on any other operating system). diff --git a/examples/Spring/SpringAir/data/oracle/refresh.sql b/examples/Spring/SpringAir/data/oracle/refresh.sql new file mode 100644 index 00000000..fe94a8ee --- /dev/null +++ b/examples/Spring/SpringAir/data/oracle/refresh.sql @@ -0,0 +1,5 @@ +connect springair/spring +@drop.sql +@create.sql +@populate.sql +commit; diff --git a/examples/Spring/SpringAir/data/oracle/schema.sql b/examples/Spring/SpringAir/data/oracle/schema.sql new file mode 100644 index 00000000..5a052dac --- /dev/null +++ b/examples/Spring/SpringAir/data/oracle/schema.sql @@ -0,0 +1,4 @@ +drop user springair cascade; +create user springair identified by spring; +grant create procedure, create session, resource, create table, create view, create synonym to springair; +commit; \ No newline at end of file diff --git a/examples/Spring/SpringAir/data/readme.txt b/examples/Spring/SpringAir/data/readme.txt new file mode 100644 index 00000000..1453d5de --- /dev/null +++ b/examples/Spring/SpringAir/data/readme.txt @@ -0,0 +1,13 @@ +This directory contains scripts for creating and populating the SpringAir database. + +Scripts are provided for a number of popular commercial and open source databases. Hopefully you will be able to find one set of scripts to match your current environment, and if not, well... at least you have some choice in the matter of which database to download and experiment with. Remember, Spring.NET is all about choice :) + +Marketing over, the scripts for each supported database are in subdirectories of the directory in which this current file resides, in subdirectories named after the particular database. (For example, Oracle scripts are in the 'oracle' subdirectory). All of the scripts follow a standard format... + +The 'schema' script creates users / schemas (as appropriate) for the 'springair' database. +The 'drop' script drops all of the *relations* (note that it does not drop users / schemas). +The 'create' script creates all of the required relations. +The 'populate' script adds some reference and test data to the various relations created by the 'create' script. +The 'refresh' script just encapsulates all of the above scripts into one convenient script. You can use it to teardown, create, and populate the 'springair' database in one convenient script execution. + +As with all scripts, *please* do open them up and have a quick shuftie at the contents. They are just simple 'create some relations, bung some data in' scripts, but you run them at your own risk. No really, please look inside the scripts before running them... remember, safety first :) diff --git a/examples/Spring/SpringAir/readme.txt b/examples/Spring/SpringAir/readme.txt new file mode 100644 index 00000000..b581269d --- /dev/null +++ b/examples/Spring/SpringAir/readme.txt @@ -0,0 +1,16 @@ +For .NET 1.1, +The VS.NET solution is SpringAir.2003.sln +In order to be able to run SpringAir you will need to create 'SpringAir.2003' +virtual directory using IIS Administrator and point it to the following directory : + examples\Spring\SpringAir\src\SpringAir.Web.2003 + +For .NET 2.0, +The VS.NET solution is SpringAir.2005.sln + + + +Set your startup project as SpringAir.Web +and the start page to .\Web\Home.aspx + +If you run .NET 2.0 first and then .NET 1.1, run the nant target 'clean-objs' to remove the object files before starting vs.net 2003 +Or do a clean build from vs.net 2005 before starting vs.net 2003. diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/AssemblyInfo.cs b/examples/Spring/SpringAir/src/SpringAir.Core/AssemblyInfo.cs new file mode 100644 index 00000000..1595cd41 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/AssemblyInfo.cs @@ -0,0 +1,40 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.Permissions; + +[assembly: AssemblyTitle("SpringAir core functionality")] +[assembly: AssemblyDescription("Core functionality for SpringAir")] +[assembly: AssemblyVersion("1.0.0.0")] + +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +[assembly: AssemblyCompany("http://www.springframework.net/")] +[assembly: AssemblyProduct("Spring.NET")] +[assembly: AssemblyCopyright("Copyright © 2002-2005. Licensed under the Apache License, Version 2.0")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)] + +[assembly: ComVisible(false)] +[assembly: CLSCompliant(true)] diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Data/IAircraftDao.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Data/IAircraftDao.cs new file mode 100644 index 00000000..fd85d64e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Data/IAircraftDao.cs @@ -0,0 +1,66 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data +{ + /// + /// DAO interface for the + /// domain class. + /// + /// Rick Evans + /// $Id: IAircraftDao.cs,v 1.3 2005/12/21 14:36:12 springboy Exp $ + public interface IAircraftDao + { + /// + /// Gets all of the instances + /// that exist in the system. + /// + /// + /// All of the instances + /// that exist in the system; if no + /// can be found an empty + /// will be returned (but never + /// ). + /// + IList GetAllAircraft(); + + /// + /// Gets the uniquely + /// identified by the supplied . + /// + /// + /// The id that uniquely identifies an . + /// + /// + /// The uniquely + /// identified by the supplied ; or + /// if no such can be found. + /// + Aircraft GetAircraft(long id); + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Data/IAirportDao.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Data/IAirportDao.cs new file mode 100644 index 00000000..d161d321 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Data/IAirportDao.cs @@ -0,0 +1,78 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data +{ + /// + /// DAO interface for the + /// domain class. + /// + /// Rick Evans + /// $Id: IAirportDao.cs,v 1.6 2005/12/21 14:36:32 springboy Exp $ + public interface IAirportDao + { + /// + /// Gets all of the instances + /// that exist in the system. + /// + /// + /// All of the instances + /// that exist in the system; if no + /// instances can be found will return an empty + /// (but never ). + /// + AirportCollection GetAllAirports(); + + /// + /// Gets the uniquely + /// identified by the supplied . + /// + /// + /// The id that uniquely identifies an . + /// + /// + /// The uniquely + /// identified by the supplied ; or + /// if no such can be found. + /// + Airport GetAirport(long id); + + /// + /// Gets the uniquely + /// identified by the supplied . + /// + /// + /// The code that uniquely identifies an . + /// + /// + /// The uniquely + /// identified by the supplied ; or + /// if no such can be found. + /// + Airport GetAirport(string code); + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Data/IFlightDao.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Data/IFlightDao.cs new file mode 100644 index 00000000..d6ef95f5 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Data/IFlightDao.cs @@ -0,0 +1,61 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data +{ + /// + /// DAO interface for the + /// domain class. + /// + /// Rick Evans + /// $Id: IFlightDao.cs,v 1.4 2005/12/21 14:37:02 springboy Exp $ + public interface IFlightDao + { + /// + /// Gets a list of all of those s + /// departing from the supplied on the supplied + /// and going to the supplied + /// . + /// + /// + /// The departure airport. + /// + /// + /// The destination airport. + /// + /// + /// The desired departure date from the supplied airport. + /// + /// + /// A list of all of those s + /// departing from the supplied on the supplied + /// and going to the supplied + /// . + /// + FlightCollection GetFlights(Airport origin, Airport destination, DateTime departureDate); + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Aircraft.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Aircraft.cs new file mode 100644 index 00000000..8776d176 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Aircraft.cs @@ -0,0 +1,104 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Text; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// An aircraft. + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: Aircraft.cs,v 1.6 2007/03/16 13:14:10 bbaia Exp $ + [Serializable] + public class Aircraft : Entity + { + private string model; + private IDictionary cabins = new ListDictionary(); + + #region Constructor (s) / Destructor + + public Aircraft() + { + } + + public Aircraft(string model, params Cabin[] cabins) + : this(Transient, model, cabins) + { + } + + public Aircraft(long id, string model, params Cabin[] cabins) : base(id) + { + this.model = model; + foreach (Cabin cabin in cabins) + { + this.cabins.Add(cabin.CabinClass, cabin); + } + } + + #endregion + + #region Properties + + public string Model + { + get { return this.model; } + set { this.model = value; } + } + + public Cabin[] Cabins + { + get { return (Cabin[]) new ArrayList(cabins.Values).ToArray(typeof(Cabin)); } + } + + #endregion + + /// + /// Returns a representation of this + /// . + /// + /// + /// A representation of this + /// . + /// + public override string ToString() + { + StringBuilder buffer = new StringBuilder(); + buffer.Append(Model).Append(" ["); + + string separator = ""; + foreach (Cabin cabin in cabins.Values) + { + buffer.Append(separator).Append(cabin); + separator = ", "; + } + buffer.Append("]"); + return buffer.ToString(); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Airport.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Airport.cs new file mode 100644 index 00000000..fc028066 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Airport.cs @@ -0,0 +1,153 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// Models an airport. + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: Airport.cs,v 1.7 2005/12/13 00:42:50 bbaia Exp $ + [Serializable] + public class Airport : Entity + { + private string code; + private string city; + private string name; + + #region Constructors + + /// + /// Creates a new instance of the + /// class. + /// + public Airport() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The short code uniquely identifying this airport. + /// + /// + /// The city in which this airport is based. + /// + /// + /// A full name of this airport. + /// + public Airport(string code, string city, string name) + : this (Transient, code, city, name) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The number that uniquely identifies this airport. + /// + /// + /// The short code uniquely identifying this airport. + /// + /// + /// The city in which this airport is based. + /// + /// + /// A full name of this airport. + /// + public Airport(long id, string code, string city, string name) : base(id) + { + this.code = code; + this.city = city; + this.name = name; + } + + #endregion + + #region Properties + + /// + /// Gets or Sets the short code uniquely identifying this airport. + /// + public string Code + { + get { return this.code; } + set { this.code = value; } + } + + /// + /// Gets or Sets the city in which this airport is based. + /// + public string City + { + get { return this.city; } + set { this.city = value; } + } + + /// + /// Gets or Sets the full name of this airport. + /// + /// The name. + public string Name + { + get { return this.name; } + set { this.name = value; } + } + + /// + /// A description of this airport; concatenation of the full + /// name of this airport and airport code. + /// + public string Description + { + get { return this.name + (this.Code.Length > 0 ? " (" + this.Code + ")" : ""); } + } + + #endregion + + /// + /// Returns a representation of this + /// . + /// + /// + /// A representation of this + /// . + /// + public override string ToString() + { + StringBuilder buffer = new StringBuilder(); + buffer.Append(Name).Append(", ") + .Append(City).Append(" (").Append(Code).Append(")"); + return buffer.ToString(); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/AirportCollection.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/AirportCollection.cs new file mode 100644 index 00000000..85d6bd40 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/AirportCollection.cs @@ -0,0 +1,65 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// Provides a strongly typed collection for + /// objects. + /// + /// Bruno Baia + /// $Id: AirportCollection.cs,v 1.2 2005/12/21 14:43:47 springboy Exp $ + [Serializable] + public class AirportCollection : CollectionBase + { + /// + /// Adds an to the end of this collection. + /// + /// + /// The to be added to the end of the collection. + /// + /// + /// The index at which the has been added. + /// + public int Add(Airport value) + { + return base.List.Add(value); + } + + /// + /// Gets or sets the at the specified index. + /// + /// + /// The zero-based index of the to get or set. + /// + public Airport this[int index] + { + get { return (Airport) base.List[index]; } + set { base.List[index] = value; } + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Cabin.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Cabin.cs new file mode 100644 index 00000000..b8d8cc17 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Cabin.cs @@ -0,0 +1,157 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// An aircraft cabin. Models seat map for the cabin. + /// + /// Rick Evans + /// $Id: Cabin.cs,v 1.2 2005/12/13 00:42:50 bbaia Exp $ + [Serializable] + public sealed class Cabin + { + private CabinClass cabinClass; + private int startRow, endRow; + private string seatLetters; + private IDictionary seats = new Hashtable(); + private int availableSeats = 0; + + #region Constructors + + /// + /// Creates a new instance of the + /// class. + public Cabin() + {} + + public Cabin(Cabin cabin) + : this(cabin.cabinClass, cabin.startRow, cabin.endRow, cabin.seatLetters, new ArrayList()) + {} + + public Cabin(CabinClass cabinClass, int startRow, int endRow, string seatLetters) + : this(cabinClass, startRow, endRow, seatLetters, new ArrayList()) + {} + + public Cabin(CabinClass cabinClass, int startRow, int endRow, string seatLetters, IList reservedSeats) + { + this.cabinClass = cabinClass; + this.startRow = startRow; + this.endRow = endRow; + this.seatLetters = seatLetters; + + for (int i = startRow; i <= endRow; i++) + { + foreach (char c in seatLetters) + { + string seatNumber = i.ToString() + c; + bool isReserved = reservedSeats.Contains(seatNumber); + seats.Add(seatNumber, new Seat(seatNumber, cabinClass, isReserved)); + + if (!isReserved) + { + availableSeats++; + } + } + } + } + + #endregion + + #region Properties + + public CabinClass CabinClass + { + get { return cabinClass; } + } + + public int SeatCount + { + get { return seats.Count; } + } + + public int AvailableSeats + { + get { return availableSeats; } + } + + public string SeatPlan + { + get { return "(" + AvailableSeats + "/" + SeatCount + ")"; } + } + + #endregion + + public bool IsReserved(string seatNumber) + { + Seat seat = (Seat) seats[seatNumber]; + if (seat != null) + { + return seat.IsReserved; + } + throw new ArgumentException("Specified seat does not exist in this cabin.", "seatNumber"); + } + + public string ReserveSeat(string seatNumber) + { + Seat seat = (Seat) seats[seatNumber]; + + if (seat == null) + { + throw new ArgumentException("Specified seat does not exist in this cabin.", "seatNumber"); + } + if (seat.IsReserved) + { + return ReserveSeat(); + } + + seat.IsReserved = true; + availableSeats--; + return seat.Number; + } + + public string ReserveSeat() + { + foreach (Seat seat in seats) + { + if (!seat.IsReserved) + { + seat.IsReserved = true; + availableSeats--; + return seat.Number; + } + } + return null; + } + + public override string ToString() + { + return cabinClass.ToString() + " " + SeatPlan; + } + + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/CabinClass.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/CabinClass.cs new file mode 100644 index 00000000..9db51b97 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/CabinClass.cs @@ -0,0 +1,42 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// ... + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: CabinClass.cs,v 1.4 2005/10/08 07:21:15 aseovic Exp $ + [Serializable] + public enum CabinClass + { + Coach, + Business, + First + } +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Entity.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Entity.cs new file mode 100644 index 00000000..163a92b4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Entity.cs @@ -0,0 +1,110 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace SpringAir.Domain +{ + /// + /// Base class for those domain objects that are persistent. + /// + /// + ///

    + /// This is a pragmatic concession to the persistence layer. This base + /// class and its single property are not part of the 'pure' domain + /// but it just makes persistence so much easier if each persistable class + /// has a unique identifier (especially on a greenfield project such as + /// this). + ///

    + ///
    + /// Rick Evans + /// $Id: Entity.cs,v 1.3 2005/12/21 16:06:35 springboy Exp $ + [Serializable] + public abstract class Entity + { + /// + /// Identifies an that is 'new'. + /// + /// + ///

    + /// A 'new' is one that has not + /// yet been persisted. + ///

    + ///
    + public const long Transient = long.MinValue; + + private long id = Transient; + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is an class, and as such exposes + /// no publicly visible constructors. + ///

    + ///
    + protected Entity() + { + } + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is an class, and as such exposes + /// no publicly visible constructors. + ///

    + ///
    + /// + /// The number that uniquely identifies this instance. + /// + protected Entity(long id) + { + this.id = id; + } + + /// + /// The number that uniquely identifies this instance. + /// + public long Id + { + get { return id; } + set { id = value; } + } + + /// + /// Is this instance transient? + /// + /// + ///

    + /// That is, does it exist in permanent storage? + ///

    + ///
    + /// + /// if this instance is transient. + /// + public bool IsTransient + { + get { return id == Transient; } + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Flight.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Flight.cs new file mode 100644 index 00000000..b95855b5 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Flight.cs @@ -0,0 +1,170 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Text; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// A flight. + /// + /// + ///

    + /// An aircraft may have many flights, but a particular flight is for just one + /// aircraft. When a flight is created, there are as many unreserved seats + /// for that flight as the aircraft model that is servicing the flight has seats. + ///

    + ///
    + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: Flight.cs,v 1.6 2006/01/08 00:48:10 bbaia Exp $ + [Serializable] + public class Flight : Entity + { + private string flightNumber; + private Airport departureAirport; + private Airport destinationAirport; + private Aircraft aircraft; + private IDictionary cabins = new ListDictionary(); + private DateTime departureTime; + private string seatPlan; + + #region Constructors + + /// + /// Creates a new instance of the + /// class. + public Flight() + {} + + public Flight( + string flightNumber, Airport departureAirport, Airport destinationAirport, Aircraft aircraft, DateTime departureTime) + : this (Transient, flightNumber, departureAirport, destinationAirport, aircraft, departureTime, new Cabin[] {}) + { + foreach (Cabin cabin in aircraft.Cabins) + { + this.cabins.Add(cabin.CabinClass, new Cabin(cabin)); + } + CalculateSeatPlan(); + } + + public Flight( + long id, string flightNumber, Airport departureAirport, Airport destinationAirport, Aircraft aircraft, DateTime departureTime, params Cabin[] cabins) : base(id) + { + this.flightNumber = flightNumber; + this.departureAirport = departureAirport; + this.destinationAirport = destinationAirport; + this.aircraft = aircraft; + this.departureTime = departureTime; + foreach (Cabin cabin in cabins) + { + this.cabins.Add(cabin.CabinClass, cabin); + } + CalculateSeatPlan(); + } + #endregion + + #region Properties + + public string FlightNumber + { + get { return this.flightNumber; } + set { this.flightNumber = value; } + } + + public Airport DepartureAirport + { + get { return this.departureAirport; } + set { this.departureAirport = value; } + } + + public Airport DestinationAirport + { + get { return this.destinationAirport; } + set { this.destinationAirport = value; } + } + + public Aircraft Aircraft + { + get { return this.aircraft; } + set { this.aircraft = value; } + } + + public DateTime DepartureTime + { + get { return this.departureTime; } + set { this.departureTime = value; } + } + + public string SeatPlan + { + get { return this.seatPlan; } + set { this.seatPlan = value; } + } + + public Cabin[] Cabins + { + get { return (Cabin[]) new ArrayList(cabins.Values).ToArray(typeof(Cabin)); } + } + + #endregion + + public string ReserveSeat(CabinClass cabinClass) + { + if (cabins.Contains(cabinClass)) + { + return ((Cabin) cabins[cabinClass]).ReserveSeat(); + } + CalculateSeatPlan(); + throw new ArgumentException("Specified cabin class does not exist on this flight.", "cabinClass"); + } + + public string ReserveSeat(CabinClass cabinClass, string seatNumber) + { + if (cabins.Contains(cabinClass)) + { + return ((Cabin) cabins[cabinClass]).ReserveSeat(seatNumber); + } + CalculateSeatPlan(); + throw new ArgumentException("Specified cabin class does not exist on this flight.", "cabinClass"); + } + + private void CalculateSeatPlan() + { + StringBuilder sb = new StringBuilder(); + string separator = ""; + foreach (Cabin cabin in this.cabins.Values) + { + sb.Append(separator) + .Append(cabin.CabinClass).Append(' ') + .Append(cabin.SeatPlan); + separator = ", "; + } + this.seatPlan = sb.ToString(); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/FlightCollection.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/FlightCollection.cs new file mode 100644 index 00000000..a4dcc214 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/FlightCollection.cs @@ -0,0 +1,65 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// Provides a strongly typed collection for + /// objects. + /// + /// Bruno Baia + /// $Id: FlightCollection.cs,v 1.3 2006/01/08 00:49:09 bbaia Exp $ + [Serializable] + public class FlightCollection : CollectionBase + { + /// + /// Adds an to the end of this collection. + /// + /// + /// The to be added to the end of the collection. + /// + /// + /// The index at which the has been added. + /// + public int Add(Flight value) + { + return base.List.Add(value); + } + + /// + /// Gets or sets the at the specified index. + /// + /// + /// The zero-based index of the to get or set. + /// + public Flight this[int index] + { + get { return (Flight) base.List[index]; } + set { base.List[index] = value; } + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/FlightSuggestions.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/FlightSuggestions.cs new file mode 100644 index 00000000..b10a6178 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/FlightSuggestions.cs @@ -0,0 +1,183 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace SpringAir.Domain +{ + /// + /// Flight suggestions. + /// + /// + ///

    + /// Maintains two lists... the first is a list of outbound flight legs, the + /// second a list of return flight legs (only applicable on a round trip journey). + ///

    + ///
    + /// Rick Evans + /// $Id: FlightSuggestions.cs,v 1.8 2006/03/13 11:57:44 aseovic Exp $ + [Serializable] + public class FlightSuggestions + { + private FlightCollection outboundFlights; + private FlightCollection returnFlights; + + /// + /// Creates a new instance of the + /// class. + public FlightSuggestions() + { + this.outboundFlights = new FlightCollection(); + this.returnFlights = new FlightCollection(); + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The collection of outbound objects. + /// + public FlightSuggestions(FlightCollection outboundFlights) + : this(outboundFlights, new FlightCollection()) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The collection of outbound objects. + /// + /// + /// The collection of return objects. + /// + public FlightSuggestions(FlightCollection outboundFlights, FlightCollection returnFlights) + { + this.outboundFlights = FlightsNotNull(outboundFlights); + this.returnFlights = FlightsNotNull(returnFlights); + } + + /// + /// The collection of outbound objects. + /// + /// + ///

    + /// Guaranteed to not be . + ///

    + ///
    + /// + /// The collection of outbound objects. + /// + public FlightCollection OutboundFlights + { + get { return outboundFlights; } + set { outboundFlights = FlightsNotNull(value); } + } + + /// + /// The collection of return objects. + /// + /// + ///

    + /// Guaranteed to not be . + ///

    + ///
    + /// + /// The collection of return objects. + /// + public FlightCollection ReturnFlights + { + get { return returnFlights; } + set { returnFlights = FlightsNotNull(value); } + } + + /// + /// Does this instance + /// contain any outbound s? + /// + /// + /// if this + /// instance contains any outbound s. + /// + public bool HasOutboundFlights + { + get { return this.outboundFlights.Count > 0; } + } + + /// + /// Does this instance + /// contain any return s? + /// + /// + /// if this + /// instance contains any return s. + /// + public bool HasReturnFlights + { + get { return this.returnFlights.Count > 0; } + } + + /// + /// Gets the specified at the + /// supplied in this + /// instances collection + /// of . + /// + /// + /// The index of the desired . + /// + /// + /// The . + /// + public Flight GetOutboundFlight(int flightIndex) + { + return GetFlight(this.outboundFlights, flightIndex); + } + + /// + /// Gets the specified at the + /// supplied in this + /// instances collection + /// of . + /// + /// + /// The index of the desired . + /// + /// + /// The . + /// + public Flight GetReturnFlight(int flightIndex) + { + return GetFlight(this.returnFlights, flightIndex); + } + + private Flight GetFlight(FlightCollection flights, int flightIndex) + { + return flights[flightIndex]; + } + + private static FlightCollection FlightsNotNull(FlightCollection flights) + { + return flights == null ? new FlightCollection() : flights; + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Itinerary.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Itinerary.cs new file mode 100644 index 00000000..59156032 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Itinerary.cs @@ -0,0 +1,114 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// Holds information about a flight reservation, such as the flights and the cost. + /// + /// + ///

    + /// In this cut of the application, we do not persist these objects to + /// persistant storage. + ///

    + ///
    + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: Itinerary.cs,v 1.8 2005/12/13 00:42:50 bbaia Exp $ + [Serializable] + public class Itinerary : Entity + { + private decimal price; + private FlightCollection flights; + + #region Constructors + + /// + /// Creates a new instance of the + /// class. + /// + public Itinerary() + {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// Use this constructor for a one way journey. + ///

    + ///
    + /// + /// Flights for this itinerary. + /// + public Itinerary(FlightCollection flights) + : this(Transient, 0.0M, flights) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// Use this constructor for a one way journey. + ///

    + ///
    + /// + /// The number that uniquely identifies this instance. + /// + /// Price for this itinerary. + /// + /// Flights for this itinerary. + /// + public Itinerary(long id, decimal price, FlightCollection flights) : base(id) + { + this.price = price; + this.flights = flights; + } + + #endregion + + #region Properties + + public decimal Price + { + get { return price; } + set { price = value; } + } + + public FlightCollection Flights + { + get { return flights; } + set { flights = value; } + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Passenger.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Passenger.cs new file mode 100644 index 00000000..a2c468c9 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Passenger.cs @@ -0,0 +1,63 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace SpringAir.Domain +{ + /// + /// A person having a (flight) reservation. + /// + /// Rick Evans + /// $Id: Passenger.cs,v 1.1 2005/10/01 22:52:57 springboy Exp $ + [Serializable] + public class Passenger : Entity + { + private string firstName; + private string surname; + + /// + /// Creates a new instance of the + /// class. + /// + public Passenger() + { + } + + + public Passenger(long id, string firstName, string surname) : base(id) + { + this.firstName = firstName; + this.surname = surname; + } + + public string FirstName + { + get { return firstName; } + set { firstName = value; } + } + + public string Surname + { + get { return surname; } + set { surname = value; } + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Reservation.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Reservation.cs new file mode 100644 index 00000000..719d8f10 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Reservation.cs @@ -0,0 +1,109 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// A flight reservation for a single . + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: Reservation.cs,v 1.5 2005/10/09 06:18:45 aseovic Exp $ + [Serializable] + public class Reservation : Entity + { + /// + /// Holds information about a flight reservation, such as the legs and the cost. + /// + private Itinerary itinerary; + + /// + /// The for whom this reservation + /// is made. + /// + private Passenger passenger; + + /// + /// The mapping between the of a journey + /// to the seat (number, a ) that is reserved for + /// that particular . + /// + /// + ///

    + /// More clearly, which seat a passenger is going to plonk their bum into for + /// each leg of a journey. + ///

    + ///
    + //private IDictionary flightSeats; + + /// + /// Creates a new instance of the + /// class. + /// + public Reservation() + { + } + + public Reservation(Passenger passenger, Itinerary itinerary)//, IDictionary flightSeats) + { + this.passenger = passenger; + this.itinerary = itinerary; + //this.flightSeats = flightSeats; + } + + public Itinerary Itinerary + { + get { return this.itinerary; } + set { this.itinerary = value; } + } + +// public IDictionary FlightSeats +// { +// get { return this.flightSeats; } +// set { this.flightSeats = value; } +// } + + public Passenger Passenger + { + get { return passenger; } + set { passenger = value; } + } + + public override bool Equals(object obj) + { + Reservation rhs = obj as Reservation; + return rhs != null + && this.passenger.Id.Equals(rhs.passenger.Id) + && this.itinerary == rhs.itinerary; + } + + public override int GetHashCode() + { + return this.passenger.Id.GetHashCode() + this.itinerary.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/ReservationConfirmation.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/ReservationConfirmation.cs new file mode 100644 index 00000000..f94bfd25 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/ReservationConfirmation.cs @@ -0,0 +1,91 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// ... + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: ReservationConfirmation.cs,v 1.4 2005/12/13 00:42:50 bbaia Exp $ + [Serializable] + public class ReservationConfirmation + { + #region Fields + + private string confirmationNumber; + private Reservation reservation; + + #endregion + + #region Constructor (s) / Destructor + + public ReservationConfirmation() + {} + + public ReservationConfirmation( + string confirmationNumber, Reservation reservation) + { + this.confirmationNumber = confirmationNumber; + this.reservation = reservation; + } + + #endregion + + #region Properties + + public string ConfirmationNumber + { + get { return this.confirmationNumber; } + set { this.confirmationNumber = value; } + } + + public Reservation Reservation + { + get { return this.reservation; } + set { this.reservation = value; } + } + + #endregion + + #region Methods + + public override bool Equals(object obj) + { + ReservationConfirmation rhs = obj as ReservationConfirmation; + return rhs != null && + this.confirmationNumber.Equals(rhs.confirmationNumber); + } + + public override int GetHashCode() + { + return this.confirmationNumber.GetHashCode(); + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Seat.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Seat.cs new file mode 100644 index 00000000..207a63ec --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Seat.cs @@ -0,0 +1,74 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// A seat + /// + /// Rick Evans + /// $Id: Seat.cs,v 1.3 2005/10/08 07:21:15 aseovic Exp $ + [Serializable] + public sealed class Seat + { + private string seatNumber; + private bool isReserved; + private CabinClass cabinClass; + + public Seat(string seatNumber, CabinClass cabinClass, bool isReserved) + { + #region Sanity Check + + if (seatNumber == null || seatNumber.Trim().Length == 0) + { + throw new ArgumentNullException("seatNumber", "The 'seatNumber' argument is required."); + } + + #endregion + + this.seatNumber = seatNumber; + this.cabinClass = cabinClass; + this.isReserved = isReserved; + } + + public string Number + { + get { return seatNumber; } + } + + public bool IsReserved + { + get { return isReserved; } + set { isReserved = value; } + } + + public CabinClass CabinClass + { + get { return cabinClass; } + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/TimeRange.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/TimeRange.cs new file mode 100644 index 00000000..2825d700 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/TimeRange.cs @@ -0,0 +1,160 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// ... + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: TimeRange.cs,v 1.3 2005/10/09 06:18:45 aseovic Exp $ + [Serializable] + [TypeConverter(typeof(TimeRange.TimeRangeTypeConverter))] + public class TimeRange + { + private short minHour; + private short maxHour; + + public TimeRange() + {} + + public TimeRange(short minHour, short maxHour) + { + this.minHour = minHour; + this.maxHour = maxHour; + } + + public short MinHour + { + get { return this.minHour; } + } + + public short MaxHour + { + get { return this.maxHour; } + } + + public override bool Equals(Object obj) + { + TimeRange rhs = obj as TimeRange; + return rhs != null + && this.minHour == rhs.minHour + && this.maxHour == rhs.maxHour; + } + + public override int GetHashCode() + { + return this.minHour.GetHashCode() + this.maxHour.GetHashCode() * 29; + } + + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, + "[{0} - {1}]", this.minHour, this.maxHour); + } + + #region Inner Class : TimeRangeTypeConverter + + /// + /// The default + /// implementation. + /// + /// + ///

    + /// Seems reasonable to supply this here, as the + /// class is pretty vanilla, and + /// one can't see anyone wanting to do a different + /// implementation. This + /// is, at the very least, a reasonable default. + ///

    + ///
    + [Serializable] + public sealed class TimeRangeTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + string text = value as string; + if (text != null) + { + text = text.Trim().Trim(new char[] {'[', ']'}).Trim(); + string[] minMax = text.Split('-'); + if(minMax.Length != 2) + { + throw new FormatException(); + } + try + { + short minHours = short.Parse(minMax[0].Trim()); + short maxHours = short.Parse(minMax[1].Trim()); + return new TimeRange(minHours, maxHours); + } + catch (OverflowException ex) + { + throw new FormatException("Values out of range.", ex); + } + } + return base.ConvertFrom(context, culture, value); + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, destinationType); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, + Type destinationType) + { + if (destinationType == typeof(string)) + { + TimeRange range = (TimeRange) value; + // don't want to rely on TimeRange.ToString implementation (even though its the same)... + return string.Format( + CultureInfo.InvariantCulture, + "[{0} - {1}]", range.MinHour, range.MaxHour); + } + return base.ConvertTo(context, culture, value, destinationType); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Trip.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Trip.cs new file mode 100644 index 00000000..67bab423 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/Trip.cs @@ -0,0 +1,119 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// ... + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: Trip.cs,v 1.2 2005/09/27 21:14:28 springboy Exp $ + [Serializable] + public class Trip + { + #region Fields + + private TripMode mode = TripMode.RoundTrip; + private TripPoint startingFrom = new TripPoint(); + private TripPoint returningFrom = new TripPoint(); + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public Trip() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The desired trip mode (one way or return). + /// + /// + /// The airport, date and time from where and when the journey is to start. + /// + /// + /// The airport, date and time from where and when the return journey is to start. + /// + public Trip(TripMode mode, TripPoint startingFrom, TripPoint returningFrom) + { + this.mode = mode; + this.startingFrom = startingFrom; + this.returningFrom = returningFrom; + } + + #endregion + + #region Properties + + public TripMode Mode + { + get { return this.mode; } + set { this.mode = value; } + } + + public TripPoint StartingFrom + { + get { return this.startingFrom; } + set { this.startingFrom = value; } + } + + public TripPoint ReturningFrom + { + get { return this.returningFrom; } + set { this.returningFrom = value; } + } + + #endregion + + /// + /// Returns a representation of this + /// . + /// + /// + /// A representation of this + /// . + /// + public override string ToString() + { + StringBuilder buffer = new StringBuilder(); + buffer + .Append(Mode).Append(", from ") + .Append(StartingFrom).Append(" to ") + .Append(ReturningFrom); + return buffer.ToString(); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/TripMode.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/TripMode.cs new file mode 100644 index 00000000..213a3d44 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/TripMode.cs @@ -0,0 +1,41 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// ... + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: TripMode.cs,v 1.2 2005/10/05 05:06:49 aseovic Exp $ + [Serializable] + public enum TripMode + { + OneWay, + RoundTrip + } +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Domain/TripPoint.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/TripPoint.cs new file mode 100644 index 00000000..f5fd0ff4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Domain/TripPoint.cs @@ -0,0 +1,101 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// ... + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: TripPoint.cs,v 1.3 2005/10/08 07:21:15 aseovic Exp $ + [Serializable] + public class TripPoint + { + private string airportCode; + private DateTime date; + private TimeRange timeRange = new TimeRange(0, 24); + + /// + /// Creates a new instance of the + /// class. + /// + public TripPoint() + { + } + + public TripPoint(string airportCode, DateTime date) + { + this.airportCode = airportCode; + this.date = date; + } + + public TripPoint(string airportCode, DateTime date, TimeRange timeRange) + { + this.airportCode = airportCode; + this.date = date; + this.timeRange = timeRange; + } + + public string AirportCode + { + get { return this.airportCode; } + set { this.airportCode = value; } + } + + public DateTime Date + { + get { return this.date; } + set { this.date = value; } + } + + public TimeRange TimeRange + { + get { return this.timeRange; } + set { this.timeRange = value; } + } + + /// + /// Returns a representation of this + /// . + /// + /// + /// A representation of this + /// . + /// + public override string ToString() + { + StringBuilder buffer = new StringBuilder(); + buffer + .Append("[") + .Append(AirportCode).Append(" : ") + .Append(Date).Append(" (") + .Append(TimeRange).Append(")]"); + return buffer.ToString(); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Service/CannotConfirmReservationException.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Service/CannotConfirmReservationException.cs new file mode 100644 index 00000000..269d2326 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Service/CannotConfirmReservationException.cs @@ -0,0 +1,97 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace SpringAir.Service +{ + /// + /// Thrown in response to a failure to confirm a + /// . + /// + /// + ///

    + /// This could be for any number of reasons: the backend database is down, the + /// reservation has been rejected, etc. + ///

    + ///
    + /// Rick Evans + /// $Id: CannotConfirmReservationException.cs,v 1.1 2005/10/02 00:06:29 springboy Exp $ + [Serializable] + public class CannotConfirmReservationException : ApplicationException + { + /// + /// Creates a new instance of the + /// class. + /// + public CannotConfirmReservationException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public CannotConfirmReservationException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public CannotConfirmReservationException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected CannotConfirmReservationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Service/DefaultBookingAgent.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Service/DefaultBookingAgent.cs new file mode 100644 index 00000000..1c0112dd --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Service/DefaultBookingAgent.cs @@ -0,0 +1,243 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Validation; +using SpringAir.Data; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Service +{ + #region Production Booking Agent implementation + + /// + /// Default implementation of the + /// service interface. + /// + /// Rick Evans + /// $Id: DefaultBookingAgent.cs,v 1.9 2008/04/02 23:00:00 markpollack Exp $ + public class DefaultBookingAgent : IBookingAgent + { + private IAirportDao airportDao; + private IFlightDao flightDao; + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// An appropriate implementation of the + /// that this DAO + /// can use to find + /// instances with. + /// + /// + /// An appropriate implementation of the + /// that this DAO + /// can use to find + /// instances with. + /// + /// + /// If either of the supplied arguments is . + /// + public DefaultBookingAgent(IAirportDao airportDao, IFlightDao flightDao) + { + #region Sanity Checks + + if (airportDao == null) + { + throw new ArgumentNullException("airportDao", "The 'airportDao' argument is required."); + } + if (flightDao == null) + { + throw new ArgumentNullException("legDao", "The 'legDao' argument is required."); + } + + #endregion + + this.airportDao = airportDao; + this.flightDao = flightDao; + } + + /// + /// Gets those + /// that are applicable for the supplied . + /// + /// + /// The that contains the criteria + /// that are to be used to select the flight suggestions. + /// + /// + /// A comprised of all + /// those instances that + /// are applicable for the supplied . + /// + /// + /// If the supplied is . + /// + public FlightSuggestions SuggestFlights(Trip trip) + { + #region Sanity Check + + if (trip == null) + { + throw new ArgumentNullException("trip", "The 'trip' argument is required."); + } + + #endregion + + Airport origin = this.airportDao.GetAirport(trip.StartingFrom.AirportCode); + Airport destination = this.airportDao.GetAirport(trip.ReturningFrom.AirportCode); + + FlightCollection outboundLegs = GetFlights(origin, destination, trip.StartingFrom.Date); + FlightCollection returnLegs = null; + if (trip.Mode == TripMode.RoundTrip) + { + // swap 'em round, and get the legs for the way back... + returnLegs = GetFlights(destination, origin, trip.ReturningFrom.Date); + } + return new FlightSuggestions(outboundLegs, returnLegs); + } + + /// + /// Goes ahead and actually books what up until this point has been a + /// transient . + /// + /// + /// The that is to be confirmed. + /// + /// + /// A confirmation that the supplied + /// has been accepted by the system. + /// + /// + /// If the supplied cannot be confirmed + /// (for whatever reason). + /// + /// + /// If the supplied is . + /// + /// + public ReservationConfirmation Book(Reservation reservation) + { + if (reservation == null) + { + throw new ArgumentNullException("reservation", "The 'reservation' argument is required."); + } + //TODO do something in the db... + return new ReservationConfirmation(Guid.NewGuid().ToString("N").Substring(17, 5).ToUpper(), reservation); + } + + private FlightCollection GetFlights(Airport origin, Airport destination, DateTime departureDate) + { + return this.flightDao.GetFlights(origin, destination, departureDate); + } + + public AirportCollection GetAirportList() + { + return airportDao.GetAllAirports(); + } + } + + #endregion + + #region Booking Agent Stub + + public class BookingAgentStub : IBookingAgent + { + private static readonly FlightCollection NoSuggestions = new FlightCollection(); + + private IAirportDao airportDao; + private IAircraftDao aircraftDao; + + public BookingAgentStub(IAirportDao airportDao, IAircraftDao aircraftDao) + { + this.airportDao = airportDao; + this.aircraftDao = aircraftDao; + } + + public FlightSuggestions SuggestFlights([Validated("tripValidator")] Trip trip) + { + #region Sanity Check + + if (trip == null) + { + throw new ArgumentNullException("trip", "The 'trip' argument is required."); + } + + #endregion + + Aircraft outboundAircraft, returnAircraft; + bool transAtlantic = trip.StartingFrom.AirportCode == "LHR" || trip.ReturningFrom.AirportCode == "LHR"; + if (transAtlantic) + { + outboundAircraft = aircraftDao.GetAircraft(2); + returnAircraft = aircraftDao.GetAircraft(2); + } + else + { + outboundAircraft = aircraftDao.GetAircraft(1); + returnAircraft = aircraftDao.GetAircraft(3); + } + Airport from = airportDao.GetAirport(trip.StartingFrom.AirportCode); + Airport to = airportDao.GetAirport(trip.ReturningFrom.AirportCode); + + FlightCollection outboundFlights = new FlightCollection(); + outboundFlights.Add(new Flight("UA 0123", from, to, outboundAircraft, trip.StartingFrom.Date.AddHours(6.5))); + outboundFlights.Add(new Flight("AA 2367", from, to, outboundAircraft, trip.StartingFrom.Date.AddHours(10))); + outboundFlights.Add(new Flight("SW 6534", from, to, outboundAircraft, trip.StartingFrom.Date.AddHours(15.75))); + outboundFlights.Add(new Flight("CO 0054", from, to, outboundAircraft, trip.StartingFrom.Date.AddHours(19.25))); + + FlightCollection returnFlights = new FlightCollection(); + returnFlights.Add(new Flight("CO 0112", to, from, returnAircraft, trip.ReturningFrom.Date.AddHours(9))); + returnFlights.Add(new Flight("UA 0230", to, from, returnAircraft, trip.ReturningFrom.Date.AddHours(12.3))); + returnFlights.Add(new Flight("AA 2234", to, from, returnAircraft, trip.ReturningFrom.Date.AddHours(21.5))); + + if (trip.Mode == TripMode.RoundTrip) + { + return new FlightSuggestions(outboundFlights, returnFlights); + } + else + { + return new FlightSuggestions(outboundFlights, NoSuggestions); + } + } + + public ReservationConfirmation Book(Reservation reservation) + { + if (reservation == null) + { + throw new ArgumentNullException("reservation", "The 'reservation' argument is required."); + } + return new ReservationConfirmation(Guid.NewGuid().ToString("N").Substring(17, 5).ToUpper(), reservation); + } + + public AirportCollection GetAirportList() + { + return airportDao.GetAllAirports(); + } + } + + #endregion +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/Service/IBookingAgent.cs b/examples/Spring/SpringAir/src/SpringAir.Core/Service/IBookingAgent.cs new file mode 100644 index 00000000..28185703 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/Service/IBookingAgent.cs @@ -0,0 +1,102 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Service +{ + /// + /// Central service interface for the trip booking use cases of the + /// SpringAir application. + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: IBookingAgent.cs,v 1.7 2005/12/21 14:38:19 springboy Exp $ + public interface IBookingAgent + { + /// + /// Gets those + /// that are applicable for the supplied . + /// + /// + ///

    + /// Guaranteed to not be , although there may not be any + /// actual suggested s for either the + /// outbound (or return) legs. + ///

    + ///
    + /// + /// The that contains the criteria + /// that are to be used to select the flight suggestions. + /// + /// + /// A comprised of all + /// those instances that + /// are applicable for the supplied . + /// + /// + /// If the supplied is . + /// + FlightSuggestions SuggestFlights(Trip trip); + + /// + /// Goes ahead and actually books what up until this point has been a + /// transient . + /// + /// + /// The that is to be confirmed. + /// + /// + /// A confirmation that the supplied + /// has been accepted by the system. + /// + /// + /// If the supplied cannot be confirmed + /// (for whatever reason). + /// + /// + /// If the supplied is . + /// + ReservationConfirmation Book(Reservation reservation); + + /// + /// Returns a collection of all of those + /// instances that can be used for + /// the purposes of booking. + /// + /// + ///

    + /// Guaranteed to not be , although the collection + /// may well be empty (in the unlikely event that every + /// has been purged from the system). + ///

    + ///
    + /// + /// A collection of all of those + /// instances that can be used for the purposes of booking. + /// + AirportCollection GetAirportList(); + } +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/SpringAir.Core.2003.csproj b/examples/Spring/SpringAir/src/SpringAir.Core/SpringAir.Core.2003.csproj new file mode 100644 index 00000000..8228a2a2 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/SpringAir.Core.2003.csproj @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/SpringAir.Core.2005.csproj b/examples/Spring/SpringAir/src/SpringAir.Core/SpringAir.Core.2005.csproj new file mode 100644 index 00000000..e5430cbc --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/SpringAir.Core.2005.csproj @@ -0,0 +1,170 @@ + + + Local + 8.0.50727 + 2.0 + {5E3427D8-62FC-483F-A86B-B44A79A46BCC} + Debug + AnyCPU + + + + + SpringAir.Core + + + JScript + Grid + IE50 + false + Library + SpringAir + OnBuildSuccess + + + + + + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + System + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Core/SpringAir.Core.build b/examples/Spring/SpringAir/src/SpringAir.Core/SpringAir.Core.build new file mode 100644 index 00000000..9daddefa --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Core/SpringAir.Core.build @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Data.Ado/AssemblyInfo.cs b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/AssemblyInfo.cs new file mode 100644 index 00000000..0507bd8f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/AssemblyInfo.cs @@ -0,0 +1,40 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.Permissions; + +[assembly: AssemblyTitle("SpringAir ADO.NET DAO functionality")] +[assembly: AssemblyDescription("ADO.NET DAO functionality for SpringAir")] +[assembly: AssemblyVersion("1.0.0.0")] + +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +[assembly: AssemblyCompany("http://www.springframework.net/")] +[assembly: AssemblyProduct("Spring.NET")] +[assembly: AssemblyCopyright("Copyright © 2002-2005. Licensed under the Apache License, Version 2.0")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)] + +[assembly: ComVisible(false)] +[assembly: CLSCompliant(false)] diff --git a/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AircraftDao.cs b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AircraftDao.cs new file mode 100644 index 00000000..2ed2ce31 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AircraftDao.cs @@ -0,0 +1,140 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Data; + +using Spring.Data; +using Spring.Data.Core; +using Spring.Data.Support; +using Spring.Caching; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data.Ado +{ + /// + /// AdoTemplate based backed implementation of the + /// interface. + /// + /// Mark Pollack + /// $Id: AircraftDao.cs,v 1.10 2007/10/11 17:59:21 markpollack Exp $ + public class AircraftDao : AdoDaoSupport, IAircraftDao + { + #region Queries + + private const string AllAircraft = + "SELECT id, model, row_count, seats_per_row," + + "(SELECT seat_count FROM aircraft_cabin_seat WHERE cabin_class_id = 0 AND aircraft_id = id) coach_seats," + + "(SELECT seat_count FROM aircraft_cabin_seat WHERE cabin_class_id = 1 AND aircraft_id = id) business_seats," + + "(SELECT seat_count FROM aircraft_cabin_seat WHERE cabin_class_id = 2 AND aircraft_id = id) first_class_seats " + + "FROM aircraft"; + + private const string SingleAircraftById = AllAircraft + " WHERE id = @id"; + + #endregion + + private IRowMapper aircraftMapper = new AircraftMapper(); + + /// + /// Gets all of the instances + /// that exist in the system. + /// + /// + /// All of the instances + /// that exist in the system; if no + /// instances can be found will return an empty + /// (but never ). + /// + [CacheResult("AspNetCache", "'Aircrafts'", TimeToLive = "0:1:0")] + [CacheResultItems("AspNetCache", "'Aircraft.Id=' + Id", TimeToLive = "0:1:0")] + public IList GetAllAircraft() + { + return AdoTemplate.QueryWithRowMapper(CommandType.Text, AllAircraft, aircraftMapper); + } + + /// + /// Gets the uniquely + /// identified by the supplied . + /// + /// + /// The id that uniquely identifies an . + /// + /// + /// The uniquely + /// identified by the supplied ; or + /// if no such can be found. + /// + [CacheResult("AspNetCache", "'Aircraft.Id=' + #id", TimeToLive = "0:1:0")] + public Aircraft GetAircraft(long id) + { + return (Aircraft) AdoTemplate.QueryForObject(CommandType.Text, SingleAircraftById, aircraftMapper, + "id", DbType.Int32, 0, id); + } + + public IRowMapper AircraftMapper + { + set { aircraftMapper = value; } + } + } + + #region AircraftDao Stub + + public class AircraftDaoStub : IAircraftDao + { + private IList aircraftList = new ArrayList(); + + public AircraftDaoStub() + { + // create stub aircraft + aircraftList.Add(new Aircraft(1, "Boeing 737", + new Cabin(CabinClass.Business, 1, 4, "ABCD"), + new Cabin(CabinClass.Coach, 5, 29, "ABCDEF") + )); + aircraftList.Add(new Aircraft(2, "Boeing 747", + new Cabin(CabinClass.First, 1, 15, "ABCD"), + new Cabin(CabinClass.Business, 16, 30, "ABCDEF"), + new Cabin(CabinClass.Coach, 31, 85, "ABCDEFGHI") + )); + aircraftList.Add(new Aircraft(3, "Airbus 320", + new Cabin(CabinClass.Business, 1, 6, "ABCD"), + new Cabin(CabinClass.Coach, 7, 32, "ABCDEF") + )); + } + + [CacheResult("AspNetCache", "'Aircrafts'", TimeToLive = "0:1:0")] + [CacheResultItems("AspNetCache", "'Aircraft.Id=' + Id", TimeToLive = "0:1:0")] + public IList GetAllAircraft() + { + return aircraftList; + } + + [CacheResult("AspNetCache", "'Aircraft.Id=' + #id", TimeToLive = "0:1:0")] + public Aircraft GetAircraft(long id) + { + return (Aircraft) aircraftList[(int) (id - 1)]; + } + } + + #endregion +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AircraftMapper.cs b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AircraftMapper.cs new file mode 100644 index 00000000..a474150d --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AircraftMapper.cs @@ -0,0 +1,61 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using Spring.Data; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data.Ado +{ + /// + /// Maps a single row from + /// to an instance. + /// + /// Rick Evans + /// $Id: AircraftMapper.cs,v 1.1 2006/11/28 06:42:26 markpollack Exp $ + public class AircraftMapper : IRowMapper + { + /// + /// Maps values stripped from the supplied + /// object into an instance. + /// + /// + /// The containing the result values. + /// + /// + /// The number of the current row. + /// + /// + /// An instance containing the results + /// of the mapping. + /// + public object MapRow(IDataReader results, int rowNumber) + { + long id = results.GetInt32(0); + string model = results.GetString(1); + return new Aircraft(id, model, new Cabin[] {}); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AirportDao.cs b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AirportDao.cs new file mode 100644 index 00000000..781bcc5b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AirportDao.cs @@ -0,0 +1,212 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Import + +using System.Collections; +using System.Data; + +using Spring.Caching; +using Spring.Data; +using Spring.Data.Core; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data.Ado +{ + /// + /// AdoTemplate backed implementation of the + /// interface. + /// + /// Mark Pollack + /// $Id: AirportDao.cs,v 1.13 2007/10/11 17:59:21 markpollack Exp $ + public class AirportDao : AdoDaoSupport, IAirportDao + { + private IRowMapper airportMapper = new AirportMapper(); + + /// + /// Gets all of the instances + /// that exist in the system. + /// + /// + /// All of the instances + /// that exist in the system; if no + /// instances can be found will return an empty + /// (but never ). + /// + [CacheResult("AspNetCache", "'Airports'", TimeToLive = "0:1:0")] + [CacheResultItems("AspNetCache", "'Airport.Id=' + Id", TimeToLive = "0:1:0")] + [CacheResultItems("AspNetCache", "'Airport.Code=' + Code", TimeToLive = "0:1:0")] + public AirportCollection GetAllAirports() + { + AirportCollection results = new AirportCollection(); + IList list = AdoTemplate.QueryWithRowMapper(CommandType.Text, + "SELECT * FROM airport", airportMapper); + + //TODO - add support to queries for supplying own collection implementation? + // Why not using IResultSetExtrator ? + foreach (Airport airport in list) + { + results.Add(airport); + } + return results; + } + + /// + /// Gets the uniquely + /// identified by the supplied . + /// + /// + /// The id that uniquely identifies an . + /// + /// + /// The uniquely + /// identified by the supplied ; or + /// if no such can be found. + /// + [CacheResult("AspNetCache", "'Airport.Id=' + #id", TimeToLive = "0:1:0")] + public Airport GetAirport(long id) + { + return (Airport) AdoTemplate.QueryForObject(CommandType.Text, + "SELECT * FROM airport WHERE id = @id", + airportMapper, "id", DbType.Int32, 0, id); + // + //return (Airport) QueryForEntity("SELECT * FROM airport WHERE id = @id", id, this.airportMapper); + } + + /// + /// Gets the uniquely + /// identified by the supplied . + /// + /// + /// The code that uniquely identifies an . + /// + /// + /// The uniquely + /// identified by the supplied ; or + /// if no such can be found. + /// + [CacheResult("AspNetCache", "'Airport.Code=' + #code", TimeToLive = "0:1:0")] + public Airport GetAirport(string code) + { + return (Airport) AdoTemplate.QueryForObject(CommandType.Text, + "SELECT * FROM airport WHERE code = @code", + airportMapper, "code", DbType.String, 0, code); + /* + IDbCommand command = GetCommand("SELECT * FROM airport WHERE code = @code"); + IDbDataParameter parameter = command.CreateParameter(); + parameter.ParameterName = "@code"; + parameter.DbType = DbType.String; + parameter.Value = code; + command.Parameters.Add(parameter); + return (Airport) QueryForEntity(command, this.airportMapper); + */ + } + + public IRowMapper AirportMapper + { + set { airportMapper = value; } + } + } + + #region AirportDao Stub + + /// + /// Stub implementation of the interface. + /// + /// Aleksandar Seovic + /// $Id: AirportDao.cs,v 1.13 2007/10/11 17:59:21 markpollack Exp $ + public class AirportDaoStub : IAirportDao + { + private AirportCollection airports = new AirportCollection(); + + public AirportDaoStub() + { + airports.Add(new Airport(1, "TPA", "Tampa", "Tampa International Airport")); + airports.Add(new Airport(2, "MCO", "Orlando", "Orlando International Airport")); + airports.Add(new Airport(3, "SFO", "San Francisco", "San Francisco International Airport")); + airports.Add(new Airport(4, "ORD", "Chicago", "O'Hare International Airport")); + airports.Add(new Airport(5, "LGA", "New York", "La Guardia International Airport")); + airports.Add(new Airport(6, "LHR", "London", "London Heathrow")); + } + + /// + /// Gets all of the instances + /// that exist in the system. + /// + /// + /// All of the instances + /// that exist in the system; if no + /// instances can be found will return an empty + /// (but never ). + /// + [CacheResult("AspNetCache", "'Airports'", TimeToLive = "0:1:0")] + [CacheResultItems("AspNetCache", "'Airport.Id=' + Id", TimeToLive = "0:1:0")] + [CacheResultItems("AspNetCache", "'Airport.Code=' + Code", TimeToLive = "0:1:0")] + public AirportCollection GetAllAirports() + { + return airports; + } + + /// + /// Gets the uniquely + /// identified by the supplied . + /// + /// + /// The id that uniquely identifies an . + /// + /// + /// The uniquely + /// identified by the supplied ; or + /// if no such can be found. + /// + [CacheResult("AspNetCache", "'Airport.Id=' +#id", TimeToLive = "0:1:0")] + public Airport GetAirport(long id) + { + return airports[(int) id - 1]; + } + + /// + /// Gets the uniquely + /// identified by the supplied . + /// + /// + /// The code that uniquely identifies an . + /// + /// + /// The uniquely + /// identified by the supplied ; or + /// if no such can be found. + /// + [CacheResult("AspNetCache", "'Airport.Code=' +#code", TimeToLive = "0:1:0")] + public Airport GetAirport(string code) + { + foreach(Airport airport in airports) + { + if (airport.Code == code) + return airport; + } + return null; + } + } + + #endregion +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AirportMapper.cs b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AirportMapper.cs new file mode 100644 index 00000000..2e2b872e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/AirportMapper.cs @@ -0,0 +1,63 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using Spring.Data; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data.Ado +{ + /// + /// Maps a single row from an + /// to an instance. + /// + /// Rick Evans + /// $Id: AirportMapper.cs,v 1.1 2006/11/28 06:42:26 markpollack Exp $ + public class AirportMapper : IRowMapper + { + /// + /// Maps values stripped from the supplied + /// object into an instance. + /// + /// + /// The containing the result values. + /// + /// + /// The number of the current row. + /// + /// + /// An instance containing the results + /// of the mapping. + /// + public object MapRow(IDataReader results, int rowNumber) + { + long id = results.GetInt32(0); + string code = results.GetString(1); + string city = results.GetString(2); + string description = results.GetString(3); + return new Airport(id, code, city, description); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/FlightDao.cs b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/FlightDao.cs new file mode 100644 index 00000000..c2ff6d17 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/FlightDao.cs @@ -0,0 +1,220 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using Spring.Data; +using Spring.Data.Core; +using Spring.Data.Common; +using Spring.Util; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data.Ado +{ + /// + /// Spring AdoTemlate backed implementation of the + /// interface. + /// + /// Mark Pollack + /// $Id: FlightDao.cs,v 1.6 2007/08/03 20:28:47 markpollack Exp $ + public class FlightDao : AdoDaoSupport, IFlightDao + { + #region Queries + + private const string FlightsQuery = + "SELECT flight.id, flight.flight_number, flight.aircraft_id " + + "FROM flight INNER JOIN ( " + + "SELECT total_seats.id flight_id, (total_seats.tot_seats - reserved_seats.rsrvd_seats) free_seats " + + "FROM " + + "(SELECT flight.id, (row_count * seats_per_row) tot_seats " + + "FROM aircraft INNER JOIN flight ON flight.aircraft_id = aircraft.id " + + "GROUP BY flight.id, row_count, seats_per_row) total_seats " + + "INNER JOIN " + + "(SELECT flight.id, count(flight_id) rsrvd_seats " + + "FROM flight LEFT JOIN reserved_seat ON flight.id = reserved_seat.flight_id " + + "GROUP BY flight.id, flight_id) reserved_seats " + + "ON total_seats.id = reserved_seats.id) available_flights " + + "ON flight.id = available_flights.flight_id " + + "WHERE available_flights.free_seats > 0 " + + //"AND DATEADD(dd, DATEDIFF(dd, 0, flight.departure_date), 0) = DATEADD(dd, DATEDIFF(dd, 0, @departureDate), 0) " + + "AND flight.departure_airport_id = @departureAirport " + + "AND flight.destination_airport_id = @destinationAirport"; + + #endregion + + private IAircraftDao aircraftDao; + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The DAO used to retrieve information about the + /// associated with a . + /// + /// + /// If the supplied is . + /// + public FlightDao(IAircraftDao aircraftDao) + { + if(aircraftDao == null) + { + throw new ArgumentNullException("aircraftDao"); + } + this.aircraftDao = aircraftDao; + } + + #endregion + + #region Methods + + public FlightCollection GetFlights(Airport origin, Airport destination, DateTime departureDate) + { + #region Sanity Checks + AssertUtils.ArgumentNotNull(origin, "origin"); + AssertUtils.ArgumentNotNull(destination, "destination"); + #endregion + + FlightCollection flights = new FlightCollection(); + + + IDbParametersBuilder builder = new DbParametersBuilder(DbProvider); + builder.Create().Name("departureDate").Type(DbType.Date).Value(departureDate); + builder.Create().Name("departureAirport").Type(DbType.Int32).Value(origin.Id); + builder.Create().Name("destinationAirport").Type(DbType.Int32).Value(destination.Id); + +#if NET_2_0 + AdoTemplate.QueryWithRowCallbackDelegate(CommandType.Text, FlightsQuery, + delegate(IDataReader dataReader) + { + int flightId = dataReader.GetInt32(0); + string flightNumber = dataReader.GetString(1); + Aircraft aircraft = aircraftDao.GetAircraft(dataReader.GetInt32(2)); + + //TODO: Load cabins from the database + Cabin[] cabins = aircraft.Cabins; + + Flight flight = new Flight(flightId, flightNumber, origin, destination, aircraft, departureDate, cabins); + flights.Add(flight); + }, + + + builder.GetParameters()); +#else + AdoTemplate.QueryWithRowCallback(CommandType.Text, FlightsQuery, + new FlightRowCallback(flights, aircraftDao, origin,destination,departureDate ), + builder.GetParameters()); +#endif + + + /* + IDbCommand command = GetCommand(FlightsQuery); + using(new ConnectionManager(command.Connection)) + { + SetFlightQueryParameters(command, departureDate, origin.Id, destination.Id); + using (SqlDataReader reader = (SqlDataReader) command.ExecuteReader()) + { + while(reader.Read()) + { + int flightId = reader.GetInt32(0); + string flightNumber = reader.GetString(1); + Aircraft aircraft = aircraftDao.GetAircraft(reader.GetInt32(2)); + + //TODO: Load cabins from the database + Cabin[] cabins = aircraft.Cabins; + + Flight flight = new Flight(flightId, flightNumber, origin, destination, aircraft, departureDate, cabins); + flights.Add(flight); + } + } + } + */ + return flights; + } + + private static void SetFlightQueryParameters( + IDbCommand command, DateTime departureDate, long departureAirportsId, long destinationAirportsId) + { + IDbDataParameter pDepartureDate = command.CreateParameter(); + pDepartureDate.ParameterName = "@departureDate"; + pDepartureDate.DbType = DbType.Date; + pDepartureDate.Value = departureDate; + + IDbDataParameter pDepartureAirport = command.CreateParameter(); + pDepartureAirport.ParameterName = "@departureAirport"; + pDepartureAirport.DbType = DbType.Int32; + pDepartureAirport.Value = departureAirportsId; + + IDbDataParameter pDestinationAirport = command.CreateParameter(); + pDestinationAirport.ParameterName = "@destinationAirport"; + pDestinationAirport.DbType = DbType.Int32; + pDestinationAirport.Value = destinationAirportsId; + + command.Parameters.Add(pDepartureDate); + command.Parameters.Add(pDepartureAirport); + command.Parameters.Add(pDestinationAirport); + } + + #endregion + } + + /// + /// Without anonymous delegates this type of callback can be tedious if it is not reused. + /// An alternative is to use a transaction aware IDbProvider and do you own + /// resource management in order to avoid the back-forth-copying. + /// + internal class FlightRowCallback : IRowCallback + { + FlightCollection flights; + IAircraftDao aircraftDao; + Airport origin, destination; + DateTime departureDate; + public FlightRowCallback(FlightCollection flights, IAircraftDao aircraftDao, + Airport origin, Airport destination, DateTime departureDate) + { + this.flights = flights; + this.aircraftDao = aircraftDao; + this.origin = origin; + this.destination = destination; + this.departureDate = departureDate; + } + + public void ProcessRow(IDataReader dataReader) + { + int flightId = dataReader.GetInt32(0); + string flightNumber = dataReader.GetString(1); + Aircraft aircraft = aircraftDao.GetAircraft(dataReader.GetInt32(2)); + + //TODO: Load cabins from the database + Cabin[] cabins = aircraft.Cabins; + + Flight flight = new Flight(flightId, flightNumber, origin, destination, aircraft, departureDate, cabins); + flights.Add(flight); + } + + + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/ItineraryDao.cs b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/ItineraryDao.cs new file mode 100644 index 00000000..6cfdad06 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/Data/Ado/ItineraryDao.cs @@ -0,0 +1,113 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data.Ado +{ + /// + /// ADO.NET backed implementation of the + /// interface. + /// + /// Rick Evans + /// $Id: ItineraryDao.cs,v 1.3 2005/10/02 14:06:50 springboy Exp $ + public class ItineraryDao : AbstractDao, IItineraryDao + { + private IAircraftDao aircraftDao; + private IAirportDao airportDao; + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// An appropriate implementation of the + /// that this DAO + /// can use to find with. + /// + /// + /// An appropriate implementation of the + /// that this DAO + /// can use to find + /// instances with. + /// + /// + /// If either of the supplied arguments is . + /// + public ItineraryDao(IAircraftDao aircraftDao, IAirportDao airportDao) + { + #region Sanity Checks + + if (aircraftDao == null) + { + throw new ArgumentNullException("aircraftDao", "The 'aircraftDao' argument is required."); + } + if (airportDao == null) + { + throw new ArgumentNullException("airportDao", "The 'airportDao' argument is required."); + } + + #endregion + + this.aircraftDao = aircraftDao; + this.airportDao = airportDao; + } + + /// + /// Gets a comprised of all + /// those instances that + /// are applicable for the supplied . + /// + /// + /// The that contains the criteria + /// that are to be used to select a list of applicable + /// instances. + /// + /// + /// A comprised of all + /// those instances that + /// are applicable for the supplied . + /// + /// + /// If the supplied is . + /// + public IList GetItinerariesFor(Trip trip) + { + #region Sanity Check + + if (trip == null) + { + throw new ArgumentNullException("trip", "The 'trip' argument is required."); + } + + #endregion + + IList itineraries = new ArrayList(); + + return itineraries; + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Data.Ado/SpringAir.Data.Ado.2003.csproj b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/SpringAir.Data.Ado.2003.csproj new file mode 100644 index 00000000..1e435b9b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/SpringAir.Data.Ado.2003.csproj @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Data.Ado/SpringAir.Data.Ado.2005.csproj b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/SpringAir.Data.Ado.2005.csproj new file mode 100644 index 00000000..5a8e881b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/SpringAir.Data.Ado.2005.csproj @@ -0,0 +1,129 @@ + + + Local + 8.0.50727 + 2.0 + {78B285FE-27F3-44F4-84B1-7A589AB48EF3} + Debug + AnyCPU + + + + + SpringAir.Data.Ado + + + JScript + Grid + IE50 + false + Library + SpringAir + OnBuildSuccess + + + + + + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + System + + + System.Data + + + System.XML + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + {5E3427D8-62FC-483F-A86B-B44A79A46BCC} + SpringAir.Core.2005 + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Data.Ado/SpringAir.Data.Ado.build b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/SpringAir.Data.Ado.build new file mode 100644 index 00000000..25fd46c6 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Data.Ado/SpringAir.Data.Ado.build @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/AssemblyInfo.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2003/AssemblyInfo.cs new file mode 100644 index 00000000..5309b1f7 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/AssemblyInfo.cs @@ -0,0 +1,40 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.Permissions; + +[assembly: AssemblyTitle("SpringAir ASP.NET frontend")] +[assembly: AssemblyDescription("SpringAir ASP.NET frontend")] +[assembly: AssemblyVersion("1.0.0.0")] + +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +[assembly: AssemblyCompany("http://www.springframework.net/")] +[assembly: AssemblyProduct("Spring.NET")] +[assembly: AssemblyCopyright("Copyright © 2002-2005. Licensed under the Apache License, Version 2.0")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)] + +[assembly: ComVisible(false)] +[assembly: CLSCompliant(false)] diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Aspects.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Aspects.xml new file mode 100644 index 00000000..c831f2c4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Aspects.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + *Dao + + + + + CacheAspect + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Log4Net.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Log4Net.xml new file mode 100644 index 00000000..b566a056 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Log4Net.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Production/Dao.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Production/Dao.xml new file mode 100644 index 00000000..d83aeb59 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Production/Dao.xml @@ -0,0 +1,37 @@ + + + + + The SpringAir object definitions for the Data Access Objects. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Production/Services.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Production/Services.xml new file mode 100644 index 00000000..17dc3109 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Production/Services.xml @@ -0,0 +1,17 @@ + + + + + The SpringAir object definitions for the middle (service) tier. + + + + + + The main service interface for the SpringAir application. + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Services.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Services.xml new file mode 100644 index 00000000..3b1adb00 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Services.xml @@ -0,0 +1,35 @@ + + + + + The SpringAir object definitions for the middle (service) tier. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Test/Dao.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Test/Dao.xml new file mode 100644 index 00000000..1359996c --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Test/Dao.xml @@ -0,0 +1,13 @@ + + + + + The SpringAir object definitions for the Data Access Objects. + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Test/Services.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Test/Services.xml new file mode 100644 index 00000000..711c783b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Test/Services.xml @@ -0,0 +1,14 @@ + + + + + The SpringAir object definitions for the middle (service) tier. + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Web.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Web.xml new file mode 100644 index 00000000..2edbe7dd --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Config/Web.xml @@ -0,0 +1,41 @@ + + + + + + + + + + SpringAir.Resources.Strings, SpringAir.Web + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Constants.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Constants.cs new file mode 100644 index 00000000..65577ebb --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Constants.cs @@ -0,0 +1,74 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace SpringAir +{ + /// + /// Constants for all of the various lookup keys used throughout the + /// application (to pull objects from the session, request, etc). + /// + /// + ///

    + /// Values could be injected into pages that needed them using the + /// + /// (if one wanted to go there). + ///

    + ///
    + /// Rick Evans + /// $Id: Constants.cs,v 1.1 2006/11/01 01:14:04 bbaia Exp $ + public sealed class Constants + { + /// + /// The key used to index into the AppSettings dictionary containing + /// the path to the Log4Net configuration file. + /// + public const string Log4NetConfigFile = "Log4NetConfigFile"; + + /// + /// The key under which the suggested flights for a user's trip + /// search are stored. + /// + public const string SuggestedFlightsKey = "suggestedFlights"; + + /// + /// The key under which the reservation confirmation for a user's trip + /// search are stored. + /// + public const string ReservationConfirmationKey = "reservationConfirmation"; + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private Constants() + { + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Global.asax b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Global.asax new file mode 100644 index 00000000..0532acc4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Global.asax @@ -0,0 +1 @@ +<%@ Application Inherits="SpringAir.Global" Language="C#" %> diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Global.asax.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Global.asax.cs new file mode 100644 index 00000000..93572385 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Global.asax.cs @@ -0,0 +1,83 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Configuration; +using System.IO; +using System.Web; + +#endregion + +namespace SpringAir +{ + public class Global : HttpApplication + { + private IContainer components = null; + + public Global() + { + InitializeComponent(); + } + + protected void Application_Start(Object sender, EventArgs e) + { + } + + protected void Session_Start(Object sender, EventArgs e) + { + } + + protected void Application_BeginRequest(Object sender, EventArgs e) + { + } + + protected void Application_EndRequest(Object sender, EventArgs e) + { + } + + protected void Application_AuthenticateRequest(Object sender, EventArgs e) + { + } + + protected void Application_Error(Object sender, EventArgs e) + { + } + + protected void Session_End(Object sender, EventArgs e) + { + } + + protected void Application_End(Object sender, EventArgs e) + { + } + + #region Web Form Designer generated code + + private void InitializeComponent() + { + this.components = new Container(); + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Global.asax.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Global.asax.resx new file mode 100644 index 00000000..dd0ea4d8 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Global.asax.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.en.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.en.resx new file mode 100644 index 00000000..9488c89c --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.en.resx @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SpringAir - Spring.NET Reference Application + + + Flight # + + + Departure Time + + + Departure Airport + + + Destination Airport + + + Aircraft Type + + + Seat Plan + + + Outbound Flights + + + Return Flights + + + Itinerary + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.resx new file mode 100644 index 00000000..9488c89c --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.resx @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SpringAir - Spring.NET Reference Application + + + Flight # + + + Departure Time + + + Departure Airport + + + Destination Airport + + + Aircraft Type + + + Seat Plan + + + Outbound Flights + + + Return Flights + + + Itinerary + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.sr-SP-Cyrl.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.sr-SP-Cyrl.resx new file mode 100644 index 00000000..49fcdac9 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.sr-SP-Cyrl.resx @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SpringAir - Референтна апликација за Spring.NET + + + Лет # + + + Време полетања + + + Ðеродром одлаÑка + + + Ðеродром долаÑка + + + Тип авиона + + + План Ñедишта + + + Одлазни летови + + + Повратни летови + + + РезервиÑани летови + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.sr-SP-Latn.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.sr-SP-Latn.resx new file mode 100644 index 00000000..3a381575 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Resources/Strings.sr-SP-Latn.resx @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SpringAir - Referentna aplikacija za Spring.NET + + + Let # + + + Vreme poletanja + + + Aerodrom odlaska + + + Aerodrom dolaska + + + Tip aviona + + + Plan sediÅ¡ta + + + Odlazni letovi + + + Povratni letovi + + + Rezervisani letovi + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-blue.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-blue.css new file mode 100644 index 00000000..62ecf99e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-blue.css @@ -0,0 +1,232 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #556; + font-size: 11px; + color: #000; + cursor: default; + background: #eef; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ +} + +.calendar .nav { + background: #778 url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + background: #fff; + color: #000; + padding: 2px; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ + background: #778; + color: #fff; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #bdf; +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #556; + padding: 2px; + text-align: center; + color: #000; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #a66; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #aaf; + color: #000; + border: 1px solid #04f; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #77c; + padding: 2px 0px 0px 2px; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + color: #456; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #bbb; +} +.calendar tbody .day.othermonth.oweekend { + color: #fbb; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #bdf; +} + +.calendar tbody .rowhilite td { + background: #def; +} + +.calendar tbody .rowhilite td.wn { + background: #eef; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #def; + padding: 1px 3px 1px 1px; + border: 1px solid #bbb; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #cde; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #fff; + color: #000; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #a66; +} + +.calendar tbody td.today { /* Cell showing selected date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ + text-align: center; + background: #556; + color: #fff; +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #fff; + color: #445; + border-top: 1px solid #556; + padding: 1px; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #aaf; + border: 1px solid #04f; + color: #000; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #77c; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #655; + background: #def; + color: #000; + font-size: 90%; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .hilite { + background: #acf; +} + +.calendar .combo .active { + border-top: 1px solid #46a; + border-bottom: 1px solid #46a; + background: #eef; + font-weight: bold; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #f4f0e8; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #667; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-blue2.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-blue2.css new file mode 100644 index 00000000..8570affc --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-blue2.css @@ -0,0 +1,236 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #206A9B; + font-size: 11px; + color: #000; + cursor: default; + background: #F1F8FC; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ +} + +.calendar .nav { + background: #007ED1 url(menuarrow2.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + background: #000; + color: #fff; + padding: 2px; +} + +.calendar thead tr { /* Row containing navigation buttons */ + background: #007ED1; + color: #fff; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #C7E1F3; +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #206A9B; + padding: 2px; + text-align: center; + color: #000; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #a66; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #34ABFA; + color: #000; + border: 1px solid #016DC5; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #006AA9; + border: 1px solid #008AFF; + padding: 2px 0px 0px 2px; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + color: #456; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #bbb; +} +.calendar tbody .day.othermonth.oweekend { + color: #fbb; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #C7E1F3; +} + +.calendar tbody .rowhilite td { + background: #def; +} + +.calendar tbody .rowhilite td.wn { + background: #F1F8FC; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #def; + padding: 1px 3px 1px 1px; + border: 1px solid #8FC4E8; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #cde; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #fff; + color: #000; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #a66; +} + +.calendar tbody td.today { /* Cell showing selected date */ + font-weight: bold; + color: #D50000; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ + text-align: center; + background: #206A9B; + color: #fff; +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #000; + color: #fff; + border-top: 1px solid #206A9B; + padding: 1px; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #B8DAF0; + border: 1px solid #178AEB; + color: #000; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #006AA9; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #655; + background: #def; + color: #000; + font-size: 90%; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .hilite { + background: #34ABFA; + border-top: 1px solid #46a; + border-bottom: 1px solid #46a; + font-weight: bold; +} + +.calendar .combo .active { + border-top: 1px solid #46a; + border-bottom: 1px solid #46a; + background: #F1F8FC; + font-weight: bold; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #E3F0F9; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #F1F8FC; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #267DB7; + color: #fff; +} + +.calendar td.time span.active { + border-color: red; + background-color: #000; + color: #A5FF00; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-brown.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-brown.css new file mode 100644 index 00000000..e7b6285b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-brown.css @@ -0,0 +1,225 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #655; + font-size: 11px; + color: #000; + cursor: default; + background: #ffd; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ +} + +.calendar .nav { + background: #edc url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + background: #654; + color: #fed; + padding: 2px; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ + background: #edc; + color: #000; +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #655; + padding: 2px; + text-align: center; + color: #000; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #faa; + color: #000; + border: 1px solid #f40; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #c77; + padding: 2px 0px 0px 2px; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #fed; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #bbb; +} +.calendar tbody .day.othermonth.oweekend { + color: #fbb; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #fed; +} + +.calendar tbody .rowhilite td { + background: #ddf; +} + +.calendar tbody .rowhilite td.wn { + background: #efe; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #ffe; + padding: 1px 3px 1px 1px; + border: 1px solid #bbb; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #ddc; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #fea; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { font-weight: bold; } + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ + text-align: center; + background: #988; + color: #000; +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + border-top: 1px solid #655; + background: #dcb; + color: #840; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #faa; + border: 1px solid #f40; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #c77; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #655; + background: #ffe; + color: #000; + font-size: 90%; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .hilite { + background: #fc8; +} + +.calendar .combo .active { + border-top: 1px solid #a64; + border-bottom: 1px solid #a64; + background: #fee; + font-weight: bold; +} + +.calendar td.time { + border-top: 1px solid #a88; + padding: 1px 0px; + text-align: center; + background-color: #fed; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #988; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #866; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-green.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-green.css new file mode 100644 index 00000000..564666d4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-green.css @@ -0,0 +1,229 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #565; + font-size: 11px; + color: #000; + cursor: default; + background: #efe; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ + background: #676; + color: #fff; + font-size: 90%; +} + +.calendar .nav { + background: #676 url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + padding: 2px; + background: #250; + color: #efa; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #565; + padding: 2px; + text-align: center; + color: #000; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #a66; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #afa; + color: #000; + border: 1px solid #084; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #7c7; + padding: 2px 0px 0px 2px; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #dfb; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + color: #564; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #bbb; +} +.calendar tbody .day.othermonth.oweekend { + color: #fbb; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #8a8; + background: #dfb; +} + +.calendar tbody .rowhilite td { + background: #dfd; +} + +.calendar tbody .rowhilite td.wn { + background: #efe; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #efd; + padding: 1px 3px 1px 1px; + border: 1px solid #bbb; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #dec; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #f8fff8; + color: #000; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #a66; +} + +.calendar tbody td.today { font-weight: bold; color: #0a0; } + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ + text-align: center; + background: #565; + color: #fff; +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + padding: 2px; + background: #250; + color: #efa; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #afa; + border: 1px solid #084; + color: #000; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #7c7; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #565; + background: #efd; + color: #000; + font-size: 90%; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .hilite { + background: #af8; +} + +.calendar .combo .active { + border-top: 1px solid #6a4; + border-bottom: 1px solid #6a4; + background: #efe; + font-weight: bold; +} + +.calendar td.time { + border-top: 1px solid #8a8; + padding: 1px 0px; + text-align: center; + background-color: #dfb; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #898; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #686; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-setup.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-setup.js new file mode 100644 index 00000000..038bc3c5 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-setup.js @@ -0,0 +1,200 @@ +/* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/ + * --------------------------------------------------------------------------- + * + * The DHTML Calendar + * + * Details and latest version at: + * http://dynarch.com/mishoo/calendar.epl + * + * This script is distributed under the GNU Lesser General Public License. + * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html + * + * This file defines helper functions for setting up the calendar. They are + * intended to help non-programmers get a working calendar on their site + * quickly. This script should not be seen as part of the calendar. It just + * shows you what one can do with the calendar, while in the same time + * providing a quick and simple method for setting it up. If you need + * exhaustive customization of the calendar creation process feel free to + * modify this code to suit your needs (this is recommended and much better + * than modifying calendar.js itself). + */ + +// $Id: calendar-setup.js,v 1.1 2006/11/01 01:14:06 bbaia Exp $ + +/** + * This function "patches" an input field (or other element) to use a calendar + * widget for date selection. + * + * The "params" is a single object that can have the following properties: + * + * prop. name | description + * ------------------------------------------------------------------------------------------------- + * inputField | the ID of an input field to store the date + * displayArea | the ID of a DIV or other element to show the date + * button | ID of a button or other element that will trigger the calendar + * eventName | event that will trigger the calendar, without the "on" prefix (default: "click") + * ifFormat | date format that will be stored in the input field + * daFormat | the date format that will be used to display the date in displayArea + * singleClick | (true/false) wether the calendar is in single click mode or not (default: true) + * firstDay | numeric: 0 to 6. "0" means display Sunday first, "1" means display Monday first, etc. + * align | alignment (default: "Br"); if you don't know what's this see the calendar documentation + * range | array with 2 elements. Default: [1900, 2999] -- the range of years available + * weekNumbers | (true/false) if it's true (default) the calendar will display week numbers + * flat | null or element ID; if not null the calendar will be a flat calendar having the parent with the given ID + * flatCallback | function that receives a JS Date object and returns an URL to point the browser to (for flat calendar) + * disableFunc | function that receives a JS Date object and should return true if that date has to be disabled in the calendar + * onSelect | function that gets called when a date is selected. You don't _have_ to supply this (the default is generally okay) + * onClose | function that gets called when the calendar is closed. [default] + * onUpdate | function that gets called after the date is updated in the input field. Receives a reference to the calendar. + * date | the date that the calendar will be initially displayed to + * showsTime | default: false; if true the calendar will include a time selector + * timeFormat | the time format; can be "12" or "24", default is "12" + * electric | if true (default) then given fields/date areas are updated for each move; otherwise they're updated only on close + * step | configures the step of the years in drop-down boxes; default: 2 + * position | configures the calendar absolute position; default: null + * cache | if "true" (but default: "false") it will reuse the same calendar object, where possible + * showOthers | if "true" (but default: "false") it will show days from other months too + * + * None of them is required, they all have default values. However, if you + * pass none of "inputField", "displayArea" or "button" you'll get a warning + * saying "nothing to setup". + */ +Calendar.setup = function (params) { + function param_default(pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } }; + + param_default("inputField", null); + param_default("displayArea", null); + param_default("button", null); + param_default("eventName", "click"); + param_default("ifFormat", Calendar._TT["DEF_DATE_FORMAT"]); + param_default("daFormat", Calendar._TT["DEF_DATE_FORMAT"]); + param_default("singleClick", true); + param_default("disableFunc", null); + param_default("dateStatusFunc", params["disableFunc"]); // takes precedence if both are defined + param_default("dateText", null); + param_default("firstDay", null); + param_default("align", "Bl"); + param_default("range", [1900, 2999]); + param_default("weekNumbers", true); + param_default("flat", null); + param_default("flatCallback", null); + param_default("onSelect", null); + param_default("onClose", null); + param_default("onUpdate", null); + param_default("date", null); + param_default("showsTime", false); + param_default("timeFormat", "24"); + param_default("electric", true); + param_default("step", 1); + param_default("position", null); + param_default("cache", false); + param_default("showOthers", false); + param_default("multiple", null); + + var tmp = ["inputField", "displayArea", "button"]; + for (var i in tmp) { + if (typeof params[tmp[i]] == "string") { + params[tmp[i]] = document.getElementById(params[tmp[i]]); + } + } + if (!(params.flat || params.multiple || params.inputField || params.displayArea || params.button)) { + alert("Calendar.setup:\n Nothing to setup (no fields found). Please check your code"); + return false; + } + + function onSelect(cal) { + var p = cal.params; + var update = (cal.dateClicked || p.electric); + if (update && p.inputField) { + p.inputField.value = cal.date.print(p.ifFormat); + if (typeof p.inputField.onchange == "function") + p.inputField.onchange(); + } + if (update && p.displayArea) + p.displayArea.innerHTML = cal.date.print(p.daFormat); + if (update && typeof p.onUpdate == "function") + p.onUpdate(cal); + if (update && p.flat) { + if (typeof p.flatCallback == "function") + p.flatCallback(cal); + } + if (update && p.singleClick && cal.dateClicked) + cal.callCloseHandler(); + }; + + if (params.flat != null) { + if (typeof params.flat == "string") + params.flat = document.getElementById(params.flat); + if (!params.flat) { + alert("Calendar.setup:\n Flat specified but can't find parent."); + return false; + } + var cal = new Calendar(params.firstDay, params.date, params.onSelect || onSelect); + cal.showsOtherMonths = params.showOthers; + cal.showsTime = params.showsTime; + cal.time24 = (params.timeFormat == "24"); + cal.params = params; + cal.weekNumbers = params.weekNumbers; + cal.setRange(params.range[0], params.range[1]); + cal.setDateStatusHandler(params.dateStatusFunc); + cal.getDateText = params.dateText; + if (params.ifFormat) { + cal.setDateFormat(params.ifFormat); + } + if (params.inputField && typeof params.inputField.value == "string") { + cal.parseDate(params.inputField.value); + } + cal.create(params.flat); + cal.show(); + return false; + } + + var triggerEl = params.button || params.displayArea || params.inputField; + triggerEl["on" + params.eventName] = function() { + var dateEl = params.inputField || params.displayArea; + var dateFmt = params.inputField ? params.ifFormat : params.daFormat; + var mustCreate = false; + var cal = window.calendar; + if (dateEl) + params.date = Date.parseDate(dateEl.value || dateEl.innerHTML, dateFmt); + if (!(cal && params.cache)) { + window.calendar = cal = new Calendar(params.firstDay, + params.date, + params.onSelect || onSelect, + params.onClose || function(cal) { cal.hide(); }); + cal.showsTime = params.showsTime; + cal.time24 = (params.timeFormat == "24"); + cal.weekNumbers = params.weekNumbers; + mustCreate = true; + } else { + if (params.date) + cal.setDate(params.date); + cal.hide(); + } + if (params.multiple) { + cal.multiple = {}; + for (var i = params.multiple.length; --i >= 0;) { + var d = params.multiple[i]; + var ds = d.print("%Y%m%d"); + cal.multiple[ds] = d; + } + } + cal.showsOtherMonths = params.showOthers; + cal.yearStep = params.step; + cal.setRange(params.range[0], params.range[1]); + cal.params = params; + cal.setDateStatusHandler(params.dateStatusFunc); + cal.getDateText = params.dateText; + cal.setDateFormat(dateFmt); + if (mustCreate) + cal.create(); + cal.refresh(); + if (!params.position) + cal.showAtElement(params.button || params.displayArea || params.inputField, params.align); + else + cal.showAt(params.position[0], params.position[1]); + return false; + }; + + return cal; +}; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-system.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-system.css new file mode 100644 index 00000000..f64493b4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-system.css @@ -0,0 +1,251 @@ +/* The main calendar widget. DIV containing a table. */ + +.calendar { + position: relative; + display: none; + border: 1px solid; + border-color: #fff #000 #000 #fff; + font-size: 11px; + cursor: default; + background: Window; + color: WindowText; + font-family: tahoma,verdana,sans-serif; +} + +.calendar table { + border: 1px solid; + border-color: #fff #000 #000 #fff; + font-size: 11px; + cursor: default; + background: Window; + color: WindowText; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; + padding: 1px; + border: 1px solid; + border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight; + background: ButtonFace; +} + +.calendar .nav { + background: ButtonFace url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; + padding: 1px; + border: 1px solid #000; + background: ActiveCaption; + color: CaptionText; + text-align: center; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid ButtonShadow; + padding: 2px; + text-align: center; + background: ButtonFace; + color: ButtonText; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border: 2px solid; + padding: 0px; + border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + border-width: 1px; + padding: 2px 0px 0px 2px; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid ButtonShadow; + background: ButtonFace; + color: ButtonText; +} + +.calendar tbody .rowhilite td { + background: Highlight; + color: HighlightText; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border: 1px solid; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + border: 1px solid; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; + padding: 2px 2px 0px 2px; + background: ButtonFace; + color: ButtonText; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody td.disabled { color: GrayText; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: ButtonFace; + padding: 1px; + border: 1px solid; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; + color: ButtonText; + text-align: center; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + padding: 1px; + background: #e4e0d8; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + width: 4em; + top: 0px; + left: 0px; + cursor: default; + border: 1px solid; + border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight; + background: Menu; + color: MenuText; + font-size: 90%; + padding: 1px; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .active { + padding: 0px; + border: 1px solid #000; +} + +.calendar .combo .hilite { + background: Highlight; + color: HighlightText; +} + +.calendar td.time { + border-top: 1px solid ButtonShadow; + padding: 1px 0px; + text-align: center; + background-color: ButtonFace; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: Menu; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: Highlight; + color: HighlightText; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-tas.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-tas.css new file mode 100644 index 00000000..a0c9c9d1 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-tas.css @@ -0,0 +1,239 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #655; + font-size: 11px; + color: #000; + cursor: default; + background: #ffd; + font-family: tahoma,verdana,sans-serif; + filter: +progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#DDDCCC,EndColorStr=#FFFFFF); +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ + color:#363636; +} + +.calendar .nav { + background: #edc url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + background: #654; + color: #363636; + padding: 2px; + filter: +progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#ffffff,EndColorStr=#dddccc); +} + +.calendar thead .headrow { /* Row containing navigation buttons */ + /*background: #3B86A0;*/ + color: #363636; + font-weight: bold; +filter: +progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#ffffff,EndColorStr=#3b86a0); +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #655; + padding: 2px; + text-align: center; + color: #363636; + filter: +progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#DDDCCC,EndColorStr=#FFFFFF); +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #ffcc86; + color: #000; + border: 1px solid #b59345; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #c77; + padding: 2px 0px 0px 2px; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #fed; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #fed; +} + +.calendar tbody .rowhilite td { + background: #ddf; + +} + +.calendar tbody .rowhilite td.wn { + background: #efe; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #ffe; + padding: 1px 3px 1px 1px; + border: 1px solid #bbb; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #ddc; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #fea; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { font-weight: bold; } + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ + text-align: center; + background: #988; + color: #000; + +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + border-top: 1px solid #655; + background: #dcb; + color: #363636; + font-weight: bold; + filter: +progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#FFFFFF,EndColorStr=#DDDCCC); +} +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #faa; + border: 1px solid #f40; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #c77; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #655; + background: #ffe; + color: #000; + font-size: smaller; + z-index: 100; +} + +.combo .label, +.combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.combo .label-IEfix { + width: 4em; +} + +.combo .hilite { + background: #fc8; +} + +.combo .active { + border-top: 1px solid #a64; + border-bottom: 1px solid #a64; + background: #fee; + font-weight: bold; +} + +.calendar td.time { + border-top: 1px solid #a88; + padding: 1px 0px; + text-align: center; + background-color: #fed; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #988; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #866; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-1.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-1.css new file mode 100644 index 00000000..56ffdf2e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-1.css @@ -0,0 +1,271 @@ +/* The main calendar widget. DIV containing a table. */ + +.calendar { + position: relative; + display: none; + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + font-size: 11px; + color: #000; + cursor: default; + background: #d4d0c8; + font-family: tahoma,verdana,sans-serif; +} + +.calendar table { + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + font-size: 11px; + color: #000; + cursor: default; + background: #d4d0c8; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; + padding: 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar .nav { + background: transparent url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; + padding: 1px; + border: 1px solid #000; + background: #848078; + color: #fff; + text-align: center; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #000; + padding: 2px; + text-align: center; + background: #f4f0e8; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + padding: 0px; + background-color: #e4e0d8; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + background-color: #c4c0b8; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #f4f0e8; +} + +.calendar tbody .rowhilite td { + background: #e4e0d8; +} + +.calendar tbody .rowhilite td.wn { + background: #d4d0c8; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + padding: 2px 2px 0px 2px; + background: #e4e0d8; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #f4f0e8; + padding: 1px; + border: 1px solid #000; + background: #848078; + color: #fff; + text-align: center; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + padding: 1px; + background: #e4e0d8; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + width: 4em; + top: 0px; + left: 0px; + cursor: default; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + background: #e4e0d8; + font-size: 90%; + padding: 1px; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .active { + background: #c4c0b8; + padding: 0px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar .combo .hilite { + background: #048; + color: #fea; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #f4f0e8; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #766; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-2.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-2.css new file mode 100644 index 00000000..ccb85a01 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-2.css @@ -0,0 +1,271 @@ +/* The main calendar widget. DIV containing a table. */ + +.calendar { + position: relative; + display: none; + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + font-size: 11px; + color: #000; + cursor: default; + background: #d4c8d0; + font-family: tahoma,verdana,sans-serif; +} + +.calendar table { + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + font-size: 11px; + color: #000; + cursor: default; + background: #d4c8d0; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; + padding: 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar .nav { + background: transparent url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; + padding: 1px; + border: 1px solid #000; + background: #847880; + color: #fff; + text-align: center; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #000; + padding: 2px; + text-align: center; + background: #f4e8f0; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + padding: 0px; + background-color: #e4d8e0; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + background-color: #c4b8c0; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #f4e8f0; +} + +.calendar tbody .rowhilite td { + background: #e4d8e0; +} + +.calendar tbody .rowhilite td.wn { + background: #d4c8d0; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + padding: 2px 2px 0px 2px; + background: #e4d8e0; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #f4e8f0; + padding: 1px; + border: 1px solid #000; + background: #847880; + color: #fff; + text-align: center; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + padding: 1px; + background: #e4d8e0; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + width: 4em; + top: 0px; + left: 0px; + cursor: default; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + background: #e4d8e0; + font-size: 90%; + padding: 1px; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .active { + background: #d4c8d0; + padding: 0px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar .combo .hilite { + background: #408; + color: #fea; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #f4f0e8; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #766; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-cold-1.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-cold-1.css new file mode 100644 index 00000000..09a1a806 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-cold-1.css @@ -0,0 +1,265 @@ +/* The main calendar widget. DIV containing a table. */ + +.calendar { + position: relative; + display: none; + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + font-size: 11px; + color: #000; + cursor: default; + background: #c8d0d4; + font-family: tahoma,verdana,sans-serif; +} + +.calendar table { + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + font-size: 11px; + color: #000; + cursor: default; + background: #c8d0d4; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; + padding: 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar .nav { + background: transparent url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; + padding: 1px; + border: 1px solid #000; + background: #788084; + color: #fff; + text-align: center; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #000; + padding: 2px; + text-align: center; + background: #e8f0f4; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + padding: 0px; + background-color: #d8e0e4; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + background-color: #b8c0c4; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #e8f4f0; +} + +.calendar tbody .rowhilite td { + background: #d8e4e0; +} + +.calendar tbody .rowhilite td.wn { + background: #c8d4d0; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border: 1px solid; + border-color: #fff #000 #000 #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border: 1px solid; + border-color: #000 #fff #fff #000; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + padding: 2px 2px 0px 2px; + border: 1px solid; + border-color: #000 #fff #fff #000; + background: #d8e0e4; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #e8f0f4; + padding: 1px; + border: 1px solid #000; + background: #788084; + color: #fff; + text-align: center; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + padding: 1px; + background: #d8e0e4; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + width: 4em; + top: 0px; + left: 0px; + cursor: default; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + background: #d8e0e4; + font-size: 90%; + padding: 1px; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .active { + background: #c8d0d4; + padding: 0px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar .combo .hilite { + background: #048; + color: #aef; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #e8f0f4; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #667; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-cold-2.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-cold-2.css new file mode 100644 index 00000000..0ae6b08e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar-win2k-cold-2.css @@ -0,0 +1,271 @@ +/* The main calendar widget. DIV containing a table. */ + +.calendar { + position: relative; + display: none; + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + font-size: 11px; + color: #000; + cursor: default; + background: #c8d4d0; + font-family: tahoma,verdana,sans-serif; +} + +.calendar table { + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + font-size: 11px; + color: #000; + cursor: default; + background: #c8d4d0; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; + padding: 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar .nav { + background: transparent url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; + padding: 1px; + border: 1px solid #000; + background: #788480; + color: #fff; + text-align: center; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #000; + padding: 2px; + text-align: center; + background: #e8f4f0; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + padding: 0px; + background-color: #d8e4e0; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + background-color: #b8c4c0; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #e8f4f0; +} + +.calendar tbody .rowhilite td { + background: #d8e4e0; +} + +.calendar tbody .rowhilite td.wn { + background: #c8d4d0; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + padding: 2px 2px 0px 2px; + background: #d8e4e0; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #e8f4f0; + padding: 1px; + border: 1px solid #000; + background: #788480; + color: #fff; + text-align: center; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + padding: 1px; + background: #d8e4e0; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + width: 4em; + top: 0px; + left: 0px; + cursor: default; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + background: #d8e4e0; + font-size: 90%; + padding: 1px; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .active { + background: #c8d4d0; + padding: 0px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar .combo .hilite { + background: #048; + color: #aef; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #e8f0f4; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #667; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar.js new file mode 100644 index 00000000..1967a310 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/calendar.js @@ -0,0 +1,1806 @@ +/* Copyright Mihai Bazon, 2002-2005 | www.bazon.net/mishoo + * ----------------------------------------------------------- + * + * The DHTML Calendar, version 1.0 "It is happening again" + * + * Details and latest version at: + * www.dynarch.com/projects/calendar + * + * This script is developed by Dynarch.com. Visit us at www.dynarch.com. + * + * This script is distributed under the GNU Lesser General Public License. + * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html + */ + +// $Id: calendar.js,v 1.1 2006/11/01 01:14:06 bbaia Exp $ + +/** The Calendar object constructor. */ +Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) { + // member variables + this.activeDiv = null; + this.currentDateEl = null; + this.getDateStatus = null; + this.getDateToolTip = null; + this.getDateText = null; + this.timeout = null; + this.onSelected = onSelected || null; + this.onClose = onClose || null; + this.dragging = false; + this.hidden = false; + this.minYear = 1970; + this.maxYear = 2050; + this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"]; + this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"]; + this.isPopup = true; + this.weekNumbers = true; + this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc. + this.showsOtherMonths = false; + this.dateStr = dateStr; + this.ar_days = null; + this.showsTime = false; + this.time24 = true; + this.yearStep = 2; + this.hiliteToday = true; + this.multiple = null; + // HTML elements + this.table = null; + this.element = null; + this.tbody = null; + this.firstdayname = null; + // Combo boxes + this.monthsCombo = null; + this.yearsCombo = null; + this.hilitedMonth = null; + this.activeMonth = null; + this.hilitedYear = null; + this.activeYear = null; + // Information + this.dateClicked = false; + + // one-time initializations + if (typeof Calendar._SDN == "undefined") { + // table of short day names + if (typeof Calendar._SDN_len == "undefined") + Calendar._SDN_len = 3; + var ar = new Array(); + for (var i = 8; i > 0;) { + ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len); + } + Calendar._SDN = ar; + // table of short month names + if (typeof Calendar._SMN_len == "undefined") + Calendar._SMN_len = 3; + ar = new Array(); + for (var i = 12; i > 0;) { + ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len); + } + Calendar._SMN = ar; + } +}; + +// ** constants + +/// "static", needed for event handlers. +Calendar._C = null; + +/// detect a special case of "web browser" +Calendar.is_ie = ( /msie/i.test(navigator.userAgent) && + !/opera/i.test(navigator.userAgent) ); + +Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) ); + +/// detect Opera browser +Calendar.is_opera = /opera/i.test(navigator.userAgent); + +/// detect KHTML-based browsers +Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent); + +// BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate +// library, at some point. + +Calendar.getAbsolutePos = function(el) { + var SL = 0, ST = 0; + var is_div = /^div$/i.test(el.tagName); + if (is_div && el.scrollLeft) + SL = el.scrollLeft; + if (is_div && el.scrollTop) + ST = el.scrollTop; + var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST }; + if (el.offsetParent) { + var tmp = this.getAbsolutePos(el.offsetParent); + r.x += tmp.x; + r.y += tmp.y; + } + return r; +}; + +Calendar.isRelated = function (el, evt) { + var related = evt.relatedTarget; + if (!related) { + var type = evt.type; + if (type == "mouseover") { + related = evt.fromElement; + } else if (type == "mouseout") { + related = evt.toElement; + } + } + while (related) { + if (related == el) { + return true; + } + related = related.parentNode; + } + return false; +}; + +Calendar.removeClass = function(el, className) { + if (!(el && el.className)) { + return; + } + var cls = el.className.split(" "); + var ar = new Array(); + for (var i = cls.length; i > 0;) { + if (cls[--i] != className) { + ar[ar.length] = cls[i]; + } + } + el.className = ar.join(" "); +}; + +Calendar.addClass = function(el, className) { + Calendar.removeClass(el, className); + el.className += " " + className; +}; + +// FIXME: the following 2 functions totally suck, are useless and should be replaced immediately. +Calendar.getElement = function(ev) { + var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget; + while (f.nodeType != 1 || /^div$/i.test(f.tagName)) + f = f.parentNode; + return f; +}; + +Calendar.getTargetElement = function(ev) { + var f = Calendar.is_ie ? window.event.srcElement : ev.target; + while (f.nodeType != 1) + f = f.parentNode; + return f; +}; + +Calendar.stopEvent = function(ev) { + ev || (ev = window.event); + if (Calendar.is_ie) { + ev.cancelBubble = true; + ev.returnValue = false; + } else { + ev.preventDefault(); + ev.stopPropagation(); + } + return false; +}; + +Calendar.addEvent = function(el, evname, func) { + if (el.attachEvent) { // IE + el.attachEvent("on" + evname, func); + } else if (el.addEventListener) { // Gecko / W3C + el.addEventListener(evname, func, true); + } else { + el["on" + evname] = func; + } +}; + +Calendar.removeEvent = function(el, evname, func) { + if (el.detachEvent) { // IE + el.detachEvent("on" + evname, func); + } else if (el.removeEventListener) { // Gecko / W3C + el.removeEventListener(evname, func, true); + } else { + el["on" + evname] = null; + } +}; + +Calendar.createElement = function(type, parent) { + var el = null; + if (document.createElementNS) { + // use the XHTML namespace; IE won't normally get here unless + // _they_ "fix" the DOM2 implementation. + el = document.createElementNS("http://www.w3.org/1999/xhtml", type); + } else { + el = document.createElement(type); + } + if (typeof parent != "undefined") { + parent.appendChild(el); + } + return el; +}; + +// END: UTILITY FUNCTIONS + +// BEGIN: CALENDAR STATIC FUNCTIONS + +/** Internal -- adds a set of events to make some element behave like a button. */ +Calendar._add_evs = function(el) { + with (Calendar) { + addEvent(el, "mouseover", dayMouseOver); + addEvent(el, "mousedown", dayMouseDown); + addEvent(el, "mouseout", dayMouseOut); + if (is_ie) { + addEvent(el, "dblclick", dayMouseDblClick); + el.setAttribute("unselectable", true); + } + } +}; + +Calendar.findMonth = function(el) { + if (typeof el.month != "undefined") { + return el; + } else if (typeof el.parentNode.month != "undefined") { + return el.parentNode; + } + return null; +}; + +Calendar.findYear = function(el) { + if (typeof el.year != "undefined") { + return el; + } else if (typeof el.parentNode.year != "undefined") { + return el.parentNode; + } + return null; +}; + +Calendar.showMonthsCombo = function () { + var cal = Calendar._C; + if (!cal) { + return false; + } + var cal = cal; + var cd = cal.activeDiv; + var mc = cal.monthsCombo; + if (cal.hilitedMonth) { + Calendar.removeClass(cal.hilitedMonth, "hilite"); + } + if (cal.activeMonth) { + Calendar.removeClass(cal.activeMonth, "active"); + } + var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()]; + Calendar.addClass(mon, "active"); + cal.activeMonth = mon; + var s = mc.style; + s.display = "block"; + if (cd.navtype < 0) + s.left = cd.offsetLeft + "px"; + else { + var mcw = mc.offsetWidth; + if (typeof mcw == "undefined") + // Konqueror brain-dead techniques + mcw = 50; + s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px"; + } + s.top = (cd.offsetTop + cd.offsetHeight) + "px"; +}; + +Calendar.showYearsCombo = function (fwd) { + var cal = Calendar._C; + if (!cal) { + return false; + } + var cal = cal; + var cd = cal.activeDiv; + var yc = cal.yearsCombo; + if (cal.hilitedYear) { + Calendar.removeClass(cal.hilitedYear, "hilite"); + } + if (cal.activeYear) { + Calendar.removeClass(cal.activeYear, "active"); + } + cal.activeYear = null; + var Y = cal.date.getFullYear() + (fwd ? 1 : -1); + var yr = yc.firstChild; + var show = false; + for (var i = 12; i > 0; --i) { + if (Y >= cal.minYear && Y <= cal.maxYear) { + yr.innerHTML = Y; + yr.year = Y; + yr.style.display = "block"; + show = true; + } else { + yr.style.display = "none"; + } + yr = yr.nextSibling; + Y += fwd ? cal.yearStep : -cal.yearStep; + } + if (show) { + var s = yc.style; + s.display = "block"; + if (cd.navtype < 0) + s.left = cd.offsetLeft + "px"; + else { + var ycw = yc.offsetWidth; + if (typeof ycw == "undefined") + // Konqueror brain-dead techniques + ycw = 50; + s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px"; + } + s.top = (cd.offsetTop + cd.offsetHeight) + "px"; + } +}; + +// event handlers + +Calendar.tableMouseUp = function(ev) { + var cal = Calendar._C; + if (!cal) { + return false; + } + if (cal.timeout) { + clearTimeout(cal.timeout); + } + var el = cal.activeDiv; + if (!el) { + return false; + } + var target = Calendar.getTargetElement(ev); + ev || (ev = window.event); + Calendar.removeClass(el, "active"); + if (target == el || target.parentNode == el) { + Calendar.cellClick(el, ev); + } + var mon = Calendar.findMonth(target); + var date = null; + if (mon) { + date = new Date(cal.date); + if (mon.month != date.getMonth()) { + date.setMonth(mon.month); + cal.setDate(date); + cal.dateClicked = false; + cal.callHandler(); + } + } else { + var year = Calendar.findYear(target); + if (year) { + date = new Date(cal.date); + if (year.year != date.getFullYear()) { + date.setFullYear(year.year); + cal.setDate(date); + cal.dateClicked = false; + cal.callHandler(); + } + } + } + with (Calendar) { + removeEvent(document, "mouseup", tableMouseUp); + removeEvent(document, "mouseover", tableMouseOver); + removeEvent(document, "mousemove", tableMouseOver); + cal._hideCombos(); + _C = null; + return stopEvent(ev); + } +}; + +Calendar.tableMouseOver = function (ev) { + var cal = Calendar._C; + if (!cal) { + return; + } + var el = cal.activeDiv; + var target = Calendar.getTargetElement(ev); + if (target == el || target.parentNode == el) { + Calendar.addClass(el, "hilite active"); + Calendar.addClass(el.parentNode, "rowhilite"); + } else { + if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2))) + Calendar.removeClass(el, "active"); + Calendar.removeClass(el, "hilite"); + Calendar.removeClass(el.parentNode, "rowhilite"); + } + ev || (ev = window.event); + if (el.navtype == 50 && target != el) { + var pos = Calendar.getAbsolutePos(el); + var w = el.offsetWidth; + var x = ev.clientX; + var dx; + var decrease = true; + if (x > pos.x + w) { + dx = x - pos.x - w; + decrease = false; + } else + dx = pos.x - x; + + if (dx < 0) dx = 0; + var range = el._range; + var current = el._current; + var count = Math.floor(dx / 10) % range.length; + for (var i = range.length; --i >= 0;) + if (range[i] == current) + break; + while (count-- > 0) + if (decrease) { + if (--i < 0) + i = range.length - 1; + } else if ( ++i >= range.length ) + i = 0; + var newval = range[i]; + el.innerHTML = newval; + + cal.onUpdateTime(); + } + var mon = Calendar.findMonth(target); + if (mon) { + if (mon.month != cal.date.getMonth()) { + if (cal.hilitedMonth) { + Calendar.removeClass(cal.hilitedMonth, "hilite"); + } + Calendar.addClass(mon, "hilite"); + cal.hilitedMonth = mon; + } else if (cal.hilitedMonth) { + Calendar.removeClass(cal.hilitedMonth, "hilite"); + } + } else { + if (cal.hilitedMonth) { + Calendar.removeClass(cal.hilitedMonth, "hilite"); + } + var year = Calendar.findYear(target); + if (year) { + if (year.year != cal.date.getFullYear()) { + if (cal.hilitedYear) { + Calendar.removeClass(cal.hilitedYear, "hilite"); + } + Calendar.addClass(year, "hilite"); + cal.hilitedYear = year; + } else if (cal.hilitedYear) { + Calendar.removeClass(cal.hilitedYear, "hilite"); + } + } else if (cal.hilitedYear) { + Calendar.removeClass(cal.hilitedYear, "hilite"); + } + } + return Calendar.stopEvent(ev); +}; + +Calendar.tableMouseDown = function (ev) { + if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) { + return Calendar.stopEvent(ev); + } +}; + +Calendar.calDragIt = function (ev) { + var cal = Calendar._C; + if (!(cal && cal.dragging)) { + return false; + } + var posX; + var posY; + if (Calendar.is_ie) { + posY = window.event.clientY + document.body.scrollTop; + posX = window.event.clientX + document.body.scrollLeft; + } else { + posX = ev.pageX; + posY = ev.pageY; + } + cal.hideShowCovered(); + var st = cal.element.style; + st.left = (posX - cal.xOffs) + "px"; + st.top = (posY - cal.yOffs) + "px"; + return Calendar.stopEvent(ev); +}; + +Calendar.calDragEnd = function (ev) { + var cal = Calendar._C; + if (!cal) { + return false; + } + cal.dragging = false; + with (Calendar) { + removeEvent(document, "mousemove", calDragIt); + removeEvent(document, "mouseup", calDragEnd); + tableMouseUp(ev); + } + cal.hideShowCovered(); +}; + +Calendar.dayMouseDown = function(ev) { + var el = Calendar.getElement(ev); + if (el.disabled) { + return false; + } + var cal = el.calendar; + cal.activeDiv = el; + Calendar._C = cal; + if (el.navtype != 300) with (Calendar) { + if (el.navtype == 50) { + el._current = el.innerHTML; + addEvent(document, "mousemove", tableMouseOver); + } else + addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver); + addClass(el, "hilite active"); + addEvent(document, "mouseup", tableMouseUp); + } else if (cal.isPopup) { + cal._dragStart(ev); + } + if (el.navtype == -1 || el.navtype == 1) { + if (cal.timeout) clearTimeout(cal.timeout); + cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250); + } else if (el.navtype == -2 || el.navtype == 2) { + if (cal.timeout) clearTimeout(cal.timeout); + cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250); + } else { + cal.timeout = null; + } + return Calendar.stopEvent(ev); +}; + +Calendar.dayMouseDblClick = function(ev) { + Calendar.cellClick(Calendar.getElement(ev), ev || window.event); + if (Calendar.is_ie) { + document.selection.empty(); + } +}; + +Calendar.dayMouseOver = function(ev) { + var el = Calendar.getElement(ev); + if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) { + return false; + } + if (el.ttip) { + if (el.ttip.substr(0, 1) == "_") { + el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1); + } + el.calendar.tooltips.innerHTML = el.ttip; + } + if (el.navtype != 300) { + Calendar.addClass(el, "hilite"); + if (el.caldate) { + Calendar.addClass(el.parentNode, "rowhilite"); + } + } + return Calendar.stopEvent(ev); +}; + +Calendar.dayMouseOut = function(ev) { + with (Calendar) { + var el = getElement(ev); + if (isRelated(el, ev) || _C || el.disabled) + return false; + removeClass(el, "hilite"); + if (el.caldate) + removeClass(el.parentNode, "rowhilite"); + if (el.calendar) + el.calendar.tooltips.innerHTML = _TT["SEL_DATE"]; + return stopEvent(ev); + } +}; + +/** + * A generic "click" handler :) handles all types of buttons defined in this + * calendar. + */ +Calendar.cellClick = function(el, ev) { + var cal = el.calendar; + var closing = false; + var newdate = false; + var date = null; + if (typeof el.navtype == "undefined") { + if (cal.currentDateEl) { + Calendar.removeClass(cal.currentDateEl, "selected"); + Calendar.addClass(el, "selected"); + closing = (cal.currentDateEl == el); + if (!closing) { + cal.currentDateEl = el; + } + } + cal.date.setDateOnly(el.caldate); + date = cal.date; + var other_month = !(cal.dateClicked = !el.otherMonth); + if (!other_month && !cal.currentDateEl) + cal._toggleMultipleDate(new Date(date)); + else + newdate = !el.disabled; + // a date was clicked + if (other_month) + cal._init(cal.firstDayOfWeek, date); + } else { + if (el.navtype == 200) { + Calendar.removeClass(el, "hilite"); + cal.callCloseHandler(); + return; + } + date = new Date(cal.date); + if (el.navtype == 0) + date.setDateOnly(new Date()); // TODAY + // unless "today" was clicked, we assume no date was clicked so + // the selected handler will know not to close the calenar when + // in single-click mode. + // cal.dateClicked = (el.navtype == 0); + cal.dateClicked = false; + var year = date.getFullYear(); + var mon = date.getMonth(); + function setMonth(m) { + var day = date.getDate(); + var max = date.getMonthDays(m); + if (day > max) { + date.setDate(max); + } + date.setMonth(m); + }; + switch (el.navtype) { + case 400: + Calendar.removeClass(el, "hilite"); + var text = Calendar._TT["ABOUT"]; + if (typeof text != "undefined") { + text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : ""; + } else { + // FIXME: this should be removed as soon as lang files get updated! + text = "Help and about box text is not translated into this language.\n" + + "If you know this language and you feel generous please update\n" + + "the corresponding file in \"lang\" subdir to match calendar-en.js\n" + + "and send it back to to get it into the distribution ;-)\n\n" + + "Thank you!\n" + + "http://dynarch.com/mishoo/calendar.epl\n"; + } + alert(text); + return; + case -2: + if (year > cal.minYear) { + date.setFullYear(year - 1); + } + break; + case -1: + if (mon > 0) { + setMonth(mon - 1); + } else if (year-- > cal.minYear) { + date.setFullYear(year); + setMonth(11); + } + break; + case 1: + if (mon < 11) { + setMonth(mon + 1); + } else if (year < cal.maxYear) { + date.setFullYear(year + 1); + setMonth(0); + } + break; + case 2: + if (year < cal.maxYear) { + date.setFullYear(year + 1); + } + break; + case 100: + cal.setFirstDayOfWeek(el.fdow); + return; + case 50: + var range = el._range; + var current = el.innerHTML; + for (var i = range.length; --i >= 0;) + if (range[i] == current) + break; + if (ev && ev.shiftKey) { + if (--i < 0) + i = range.length - 1; + } else if ( ++i >= range.length ) + i = 0; + var newval = range[i]; + el.innerHTML = newval; + cal.onUpdateTime(); + return; + case 0: + // TODAY will bring us here + if ((typeof cal.getDateStatus == "function") && + cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) { + return false; + } + break; + } + if (!date.equalsTo(cal.date)) { + cal.setDate(date); + newdate = true; + } else if (el.navtype == 0) + newdate = closing = true; + } + if (newdate) { + ev && cal.callHandler(); + } + if (closing) { + Calendar.removeClass(el, "hilite"); + ev && cal.callCloseHandler(); + } +}; + +// END: CALENDAR STATIC FUNCTIONS + +// BEGIN: CALENDAR OBJECT FUNCTIONS + +/** + * This function creates the calendar inside the given parent. If _par is + * null than it creates a popup calendar inside the BODY element. If _par is + * an element, be it BODY, then it creates a non-popup calendar (still + * hidden). Some properties need to be set before calling this function. + */ +Calendar.prototype.create = function (_par) { + var parent = null; + if (! _par) { + // default parent is the document body, in which case we create + // a popup calendar. + parent = document.getElementsByTagName("body")[0]; + this.isPopup = true; + } else { + parent = _par; + this.isPopup = false; + } + this.date = this.dateStr ? new Date(this.dateStr) : new Date(); + + var table = Calendar.createElement("table"); + this.table = table; + table.cellSpacing = 0; + table.cellPadding = 0; + table.calendar = this; + Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown); + + var div = Calendar.createElement("div"); + this.element = div; + div.className = "calendar"; + if (this.isPopup) { + div.style.position = "absolute"; + div.style.display = "none"; + } + div.appendChild(table); + + var thead = Calendar.createElement("thead", table); + var cell = null; + var row = null; + + var cal = this; + var hh = function (text, cs, navtype) { + cell = Calendar.createElement("td", row); + cell.colSpan = cs; + cell.className = "button"; + if (navtype != 0 && Math.abs(navtype) <= 2) + cell.className += " nav"; + Calendar._add_evs(cell); + cell.calendar = cal; + cell.navtype = navtype; + cell.innerHTML = "
    " + text + "
    "; + return cell; + }; + + row = Calendar.createElement("tr", thead); + var title_length = 6; + (this.isPopup) && --title_length; + (this.weekNumbers) && ++title_length; + + hh("?", 1, 400).ttip = Calendar._TT["INFO"]; + this.title = hh("", title_length, 300); + this.title.className = "title"; + if (this.isPopup) { + this.title.ttip = Calendar._TT["DRAG_TO_MOVE"]; + this.title.style.cursor = "move"; + hh("×", 1, 200).ttip = Calendar._TT["CLOSE"]; + } + + row = Calendar.createElement("tr", thead); + row.className = "headrow"; + + this._nav_py = hh("«", 1, -2); + this._nav_py.ttip = Calendar._TT["PREV_YEAR"]; + + this._nav_pm = hh("‹", 1, -1); + this._nav_pm.ttip = Calendar._TT["PREV_MONTH"]; + + this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0); + this._nav_now.ttip = Calendar._TT["GO_TODAY"]; + + this._nav_nm = hh("›", 1, 1); + this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"]; + + this._nav_ny = hh("»", 1, 2); + this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"]; + + // day names + row = Calendar.createElement("tr", thead); + row.className = "daynames"; + if (this.weekNumbers) { + cell = Calendar.createElement("td", row); + cell.className = "name wn"; + cell.innerHTML = Calendar._TT["WK"]; + } + for (var i = 7; i > 0; --i) { + cell = Calendar.createElement("td", row); + if (!i) { + cell.navtype = 100; + cell.calendar = this; + Calendar._add_evs(cell); + } + } + this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild; + this._displayWeekdays(); + + var tbody = Calendar.createElement("tbody", table); + this.tbody = tbody; + + for (i = 6; i > 0; --i) { + row = Calendar.createElement("tr", tbody); + if (this.weekNumbers) { + cell = Calendar.createElement("td", row); + } + for (var j = 7; j > 0; --j) { + cell = Calendar.createElement("td", row); + cell.calendar = this; + Calendar._add_evs(cell); + } + } + + if (this.showsTime) { + row = Calendar.createElement("tr", tbody); + row.className = "time"; + + cell = Calendar.createElement("td", row); + cell.className = "time"; + cell.colSpan = 2; + cell.innerHTML = Calendar._TT["TIME"] || " "; + + cell = Calendar.createElement("td", row); + cell.className = "time"; + cell.colSpan = this.weekNumbers ? 4 : 3; + + (function(){ + function makeTimePart(className, init, range_start, range_end) { + var part = Calendar.createElement("span", cell); + part.className = className; + part.innerHTML = init; + part.calendar = cal; + part.ttip = Calendar._TT["TIME_PART"]; + part.navtype = 50; + part._range = []; + if (typeof range_start != "number") + part._range = range_start; + else { + for (var i = range_start; i <= range_end; ++i) { + var txt; + if (i < 10 && range_end >= 10) txt = '0' + i; + else txt = '' + i; + part._range[part._range.length] = txt; + } + } + Calendar._add_evs(part); + return part; + }; + var hrs = cal.date.getHours(); + var mins = cal.date.getMinutes(); + var t12 = !cal.time24; + var pm = (hrs > 12); + if (t12 && pm) hrs -= 12; + var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23); + var span = Calendar.createElement("span", cell); + span.innerHTML = ":"; + span.className = "colon"; + var M = makeTimePart("minute", mins, 0, 59); + var AP = null; + cell = Calendar.createElement("td", row); + cell.className = "time"; + cell.colSpan = 2; + if (t12) + AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]); + else + cell.innerHTML = " "; + + cal.onSetTime = function() { + var pm, hrs = this.date.getHours(), + mins = this.date.getMinutes(); + if (t12) { + pm = (hrs >= 12); + if (pm) hrs -= 12; + if (hrs == 0) hrs = 12; + AP.innerHTML = pm ? "pm" : "am"; + } + H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs; + M.innerHTML = (mins < 10) ? ("0" + mins) : mins; + }; + + cal.onUpdateTime = function() { + var date = this.date; + var h = parseInt(H.innerHTML, 10); + if (t12) { + if (/pm/i.test(AP.innerHTML) && h < 12) + h += 12; + else if (/am/i.test(AP.innerHTML) && h == 12) + h = 0; + } + var d = date.getDate(); + var m = date.getMonth(); + var y = date.getFullYear(); + date.setHours(h); + date.setMinutes(parseInt(M.innerHTML, 10)); + date.setFullYear(y); + date.setMonth(m); + date.setDate(d); + this.dateClicked = false; + this.callHandler(); + }; + })(); + } else { + this.onSetTime = this.onUpdateTime = function() {}; + } + + var tfoot = Calendar.createElement("tfoot", table); + + row = Calendar.createElement("tr", tfoot); + row.className = "footrow"; + + cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300); + cell.className = "ttip"; + if (this.isPopup) { + cell.ttip = Calendar._TT["DRAG_TO_MOVE"]; + cell.style.cursor = "move"; + } + this.tooltips = cell; + + div = Calendar.createElement("div", this.element); + this.monthsCombo = div; + div.className = "combo"; + for (i = 0; i < Calendar._MN.length; ++i) { + var mn = Calendar.createElement("div"); + mn.className = Calendar.is_ie ? "label-IEfix" : "label"; + mn.month = i; + mn.innerHTML = Calendar._SMN[i]; + div.appendChild(mn); + } + + div = Calendar.createElement("div", this.element); + this.yearsCombo = div; + div.className = "combo"; + for (i = 12; i > 0; --i) { + var yr = Calendar.createElement("div"); + yr.className = Calendar.is_ie ? "label-IEfix" : "label"; + div.appendChild(yr); + } + + this._init(this.firstDayOfWeek, this.date); + parent.appendChild(this.element); +}; + +/** keyboard navigation, only for popup calendars */ +Calendar._keyEvent = function(ev) { + var cal = window._dynarch_popupCalendar; + if (!cal || cal.multiple) + return false; + (Calendar.is_ie) && (ev = window.event); + var act = (Calendar.is_ie || ev.type == "keypress"), + K = ev.keyCode; + if (ev.ctrlKey) { + switch (K) { + case 37: // KEY left + act && Calendar.cellClick(cal._nav_pm); + break; + case 38: // KEY up + act && Calendar.cellClick(cal._nav_py); + break; + case 39: // KEY right + act && Calendar.cellClick(cal._nav_nm); + break; + case 40: // KEY down + act && Calendar.cellClick(cal._nav_ny); + break; + default: + return false; + } + } else switch (K) { + case 32: // KEY space (now) + Calendar.cellClick(cal._nav_now); + break; + case 27: // KEY esc + act && cal.callCloseHandler(); + break; + case 37: // KEY left + case 38: // KEY up + case 39: // KEY right + case 40: // KEY down + if (act) { + var prev, x, y, ne, el, step; + prev = K == 37 || K == 38; + step = (K == 37 || K == 39) ? 1 : 7; + function setVars() { + el = cal.currentDateEl; + var p = el.pos; + x = p & 15; + y = p >> 4; + ne = cal.ar_days[y][x]; + };setVars(); + function prevMonth() { + var date = new Date(cal.date); + date.setDate(date.getDate() - step); + cal.setDate(date); + }; + function nextMonth() { + var date = new Date(cal.date); + date.setDate(date.getDate() + step); + cal.setDate(date); + }; + while (1) { + switch (K) { + case 37: // KEY left + if (--x >= 0) + ne = cal.ar_days[y][x]; + else { + x = 6; + K = 38; + continue; + } + break; + case 38: // KEY up + if (--y >= 0) + ne = cal.ar_days[y][x]; + else { + prevMonth(); + setVars(); + } + break; + case 39: // KEY right + if (++x < 7) + ne = cal.ar_days[y][x]; + else { + x = 0; + K = 40; + continue; + } + break; + case 40: // KEY down + if (++y < cal.ar_days.length) + ne = cal.ar_days[y][x]; + else { + nextMonth(); + setVars(); + } + break; + } + break; + } + if (ne) { + if (!ne.disabled) + Calendar.cellClick(ne); + else if (prev) + prevMonth(); + else + nextMonth(); + } + } + break; + case 13: // KEY enter + if (act) + Calendar.cellClick(cal.currentDateEl, ev); + break; + default: + return false; + } + return Calendar.stopEvent(ev); +}; + +/** + * (RE)Initializes the calendar to the given date and firstDayOfWeek + */ +Calendar.prototype._init = function (firstDayOfWeek, date) { + var today = new Date(), + TY = today.getFullYear(), + TM = today.getMonth(), + TD = today.getDate(); + this.table.style.visibility = "hidden"; + var year = date.getFullYear(); + if (year < this.minYear) { + year = this.minYear; + date.setFullYear(year); + } else if (year > this.maxYear) { + year = this.maxYear; + date.setFullYear(year); + } + this.firstDayOfWeek = firstDayOfWeek; + this.date = new Date(date); + var month = date.getMonth(); + var mday = date.getDate(); + var no_days = date.getMonthDays(); + + // calendar voodoo for computing the first day that would actually be + // displayed in the calendar, even if it's from the previous month. + // WARNING: this is magic. ;-) + date.setDate(1); + var day1 = (date.getDay() - this.firstDayOfWeek) % 7; + if (day1 < 0) + day1 += 7; + date.setDate(-day1); + date.setDate(date.getDate() + 1); + + var row = this.tbody.firstChild; + var MN = Calendar._SMN[month]; + var ar_days = this.ar_days = new Array(); + var weekend = Calendar._TT["WEEKEND"]; + var dates = this.multiple ? (this.datesCells = {}) : null; + for (var i = 0; i < 6; ++i, row = row.nextSibling) { + var cell = row.firstChild; + if (this.weekNumbers) { + cell.className = "day wn"; + cell.innerHTML = date.getWeekNumber(); + cell = cell.nextSibling; + } + row.className = "daysrow"; + var hasdays = false, iday, dpos = ar_days[i] = []; + for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(iday + 1)) { + iday = date.getDate(); + var wday = date.getDay(); + cell.className = "day"; + cell.pos = i << 4 | j; + dpos[j] = cell; + var current_month = (date.getMonth() == month); + if (!current_month) { + if (this.showsOtherMonths) { + cell.className += " othermonth"; + cell.otherMonth = true; + } else { + cell.className = "emptycell"; + cell.innerHTML = " "; + cell.disabled = true; + continue; + } + } else { + cell.otherMonth = false; + hasdays = true; + } + cell.disabled = false; + cell.innerHTML = this.getDateText ? this.getDateText(date, iday) : iday; + if (dates) + dates[date.print("%Y%m%d")] = cell; + if (this.getDateStatus) { + var status = this.getDateStatus(date, year, month, iday); + if (this.getDateToolTip) { + var toolTip = this.getDateToolTip(date, year, month, iday); + if (toolTip) + cell.title = toolTip; + } + if (status === true) { + cell.className += " disabled"; + cell.disabled = true; + } else { + if (/disabled/i.test(status)) + cell.disabled = true; + cell.className += " " + status; + } + } + if (!cell.disabled) { + cell.caldate = new Date(date); + cell.ttip = "_"; + if (!this.multiple && current_month + && iday == mday && this.hiliteToday) { + cell.className += " selected"; + this.currentDateEl = cell; + } + if (date.getFullYear() == TY && + date.getMonth() == TM && + iday == TD) { + cell.className += " today"; + cell.ttip += Calendar._TT["PART_TODAY"]; + } + if (weekend.indexOf(wday.toString()) != -1) + cell.className += cell.otherMonth ? " oweekend" : " weekend"; + } + } + if (!(hasdays || this.showsOtherMonths)) + row.className = "emptyrow"; + } + this.title.innerHTML = Calendar._MN[month] + ", " + year; + this.onSetTime(); + this.table.style.visibility = "visible"; + this._initMultipleDates(); + // PROFILE + // this.tooltips.innerHTML = "Generated in " + ((new Date()) - today) + " ms"; +}; + +Calendar.prototype._initMultipleDates = function() { + if (this.multiple) { + for (var i in this.multiple) { + var cell = this.datesCells[i]; + var d = this.multiple[i]; + if (!d) + continue; + if (cell) + cell.className += " selected"; + } + } +}; + +Calendar.prototype._toggleMultipleDate = function(date) { + if (this.multiple) { + var ds = date.print("%Y%m%d"); + var cell = this.datesCells[ds]; + if (cell) { + var d = this.multiple[ds]; + if (!d) { + Calendar.addClass(cell, "selected"); + this.multiple[ds] = date; + } else { + Calendar.removeClass(cell, "selected"); + delete this.multiple[ds]; + } + } + } +}; + +Calendar.prototype.setDateToolTipHandler = function (unaryFunction) { + this.getDateToolTip = unaryFunction; +}; + +/** + * Calls _init function above for going to a certain date (but only if the + * date is different than the currently selected one). + */ +Calendar.prototype.setDate = function (date) { + if (!date.equalsTo(this.date)) { + this._init(this.firstDayOfWeek, date); + } +}; + +/** + * Refreshes the calendar. Useful if the "disabledHandler" function is + * dynamic, meaning that the list of disabled date can change at runtime. + * Just * call this function if you think that the list of disabled dates + * should * change. + */ +Calendar.prototype.refresh = function () { + this._init(this.firstDayOfWeek, this.date); +}; + +/** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */ +Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) { + this._init(firstDayOfWeek, this.date); + this._displayWeekdays(); +}; + +/** + * Allows customization of what dates are enabled. The "unaryFunction" + * parameter must be a function object that receives the date (as a JS Date + * object) and returns a boolean value. If the returned value is true then + * the passed date will be marked as disabled. + */ +Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) { + this.getDateStatus = unaryFunction; +}; + +/** Customization of allowed year range for the calendar. */ +Calendar.prototype.setRange = function (a, z) { + this.minYear = a; + this.maxYear = z; +}; + +/** Calls the first user handler (selectedHandler). */ +Calendar.prototype.callHandler = function () { + if (this.onSelected) { + this.onSelected(this, this.date.print(this.dateFormat)); + } +}; + +/** Calls the second user handler (closeHandler). */ +Calendar.prototype.callCloseHandler = function () { + if (this.onClose) { + this.onClose(this); + } + this.hideShowCovered(); +}; + +/** Removes the calendar object from the DOM tree and destroys it. */ +Calendar.prototype.destroy = function () { + var el = this.element.parentNode; + el.removeChild(this.element); + Calendar._C = null; + window._dynarch_popupCalendar = null; +}; + +/** + * Moves the calendar element to a different section in the DOM tree (changes + * its parent). + */ +Calendar.prototype.reparent = function (new_parent) { + var el = this.element; + el.parentNode.removeChild(el); + new_parent.appendChild(el); +}; + +// This gets called when the user presses a mouse button anywhere in the +// document, if the calendar is shown. If the click was outside the open +// calendar this function closes it. +Calendar._checkCalendar = function(ev) { + var calendar = window._dynarch_popupCalendar; + if (!calendar) { + return false; + } + var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev); + for (; el != null && el != calendar.element; el = el.parentNode); + if (el == null) { + // calls closeHandler which should hide the calendar. + window._dynarch_popupCalendar.callCloseHandler(); + return Calendar.stopEvent(ev); + } +}; + +/** Shows the calendar. */ +Calendar.prototype.show = function () { + var rows = this.table.getElementsByTagName("tr"); + for (var i = rows.length; i > 0;) { + var row = rows[--i]; + Calendar.removeClass(row, "rowhilite"); + var cells = row.getElementsByTagName("td"); + for (var j = cells.length; j > 0;) { + var cell = cells[--j]; + Calendar.removeClass(cell, "hilite"); + Calendar.removeClass(cell, "active"); + } + } + this.element.style.display = "block"; + this.hidden = false; + if (this.isPopup) { + window._dynarch_popupCalendar = this; + Calendar.addEvent(document, "keydown", Calendar._keyEvent); + Calendar.addEvent(document, "keypress", Calendar._keyEvent); + Calendar.addEvent(document, "mousedown", Calendar._checkCalendar); + } + this.hideShowCovered(); +}; + +/** + * Hides the calendar. Also removes any "hilite" from the class of any TD + * element. + */ +Calendar.prototype.hide = function () { + if (this.isPopup) { + Calendar.removeEvent(document, "keydown", Calendar._keyEvent); + Calendar.removeEvent(document, "keypress", Calendar._keyEvent); + Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar); + } + this.element.style.display = "none"; + this.hidden = true; + this.hideShowCovered(); +}; + +/** + * Shows the calendar at a given absolute position (beware that, depending on + * the calendar element style -- position property -- this might be relative + * to the parent's containing rectangle). + */ +Calendar.prototype.showAt = function (x, y) { + var s = this.element.style; + s.left = x + "px"; + s.top = y + "px"; + this.show(); +}; + +/** Shows the calendar near a given element. */ +Calendar.prototype.showAtElement = function (el, opts) { + var self = this; + var p = Calendar.getAbsolutePos(el); + if (!opts || typeof opts != "string") { + this.showAt(p.x, p.y + el.offsetHeight); + return true; + } + function fixPosition(box) { + if (box.x < 0) + box.x = 0; + if (box.y < 0) + box.y = 0; + var cp = document.createElement("div"); + var s = cp.style; + s.position = "absolute"; + s.right = s.bottom = s.width = s.height = "0px"; + document.body.appendChild(cp); + var br = Calendar.getAbsolutePos(cp); + document.body.removeChild(cp); + if (Calendar.is_ie) { + br.y += document.body.scrollTop; + br.x += document.body.scrollLeft; + } else { + br.y += window.scrollY; + br.x += window.scrollX; + } + var tmp = box.x + box.width - br.x; + if (tmp > 0) box.x -= tmp; + tmp = box.y + box.height - br.y; + if (tmp > 0) box.y -= tmp; + }; + this.element.style.display = "block"; + Calendar.continuation_for_the_fucking_khtml_browser = function() { + var w = self.element.offsetWidth; + var h = self.element.offsetHeight; + self.element.style.display = "none"; + var valign = opts.substr(0, 1); + var halign = "l"; + if (opts.length > 1) { + halign = opts.substr(1, 1); + } + // vertical alignment + switch (valign) { + case "T": p.y -= h; break; + case "B": p.y += el.offsetHeight; break; + case "C": p.y += (el.offsetHeight - h) / 2; break; + case "t": p.y += el.offsetHeight - h; break; + case "b": break; // already there + } + // horizontal alignment + switch (halign) { + case "L": p.x -= w; break; + case "R": p.x += el.offsetWidth; break; + case "C": p.x += (el.offsetWidth - w) / 2; break; + case "l": p.x += el.offsetWidth - w; break; + case "r": break; // already there + } + p.width = w; + p.height = h + 40; + self.monthsCombo.style.display = "none"; + fixPosition(p); + self.showAt(p.x, p.y); + }; + if (Calendar.is_khtml) + setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10); + else + Calendar.continuation_for_the_fucking_khtml_browser(); +}; + +/** Customizes the date format. */ +Calendar.prototype.setDateFormat = function (str) { + this.dateFormat = str; +}; + +/** Customizes the tooltip date format. */ +Calendar.prototype.setTtDateFormat = function (str) { + this.ttDateFormat = str; +}; + +/** + * Tries to identify the date represented in a string. If successful it also + * calls this.setDate which moves the calendar to the given date. + */ +Calendar.prototype.parseDate = function(str, fmt) { + if (!fmt) + fmt = this.dateFormat; + this.setDate(Date.parseDate(str, fmt)); +}; + +Calendar.prototype.hideShowCovered = function () { + if (!Calendar.is_ie && !Calendar.is_opera) + return; + function getVisib(obj){ + var value = obj.style.visibility; + if (!value) { + if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C + if (!Calendar.is_khtml) + value = document.defaultView. + getComputedStyle(obj, "").getPropertyValue("visibility"); + else + value = ''; + } else if (obj.currentStyle) { // IE + value = obj.currentStyle.visibility; + } else + value = ''; + } + return value; + }; + + var tags = new Array("applet", "iframe", "select"); + var el = this.element; + + var p = Calendar.getAbsolutePos(el); + var EX1 = p.x; + var EX2 = el.offsetWidth + EX1; + var EY1 = p.y; + var EY2 = el.offsetHeight + EY1; + + for (var k = tags.length; k > 0; ) { + var ar = document.getElementsByTagName(tags[--k]); + var cc = null; + + for (var i = ar.length; i > 0;) { + cc = ar[--i]; + + p = Calendar.getAbsolutePos(cc); + var CX1 = p.x; + var CX2 = cc.offsetWidth + CX1; + var CY1 = p.y; + var CY2 = cc.offsetHeight + CY1; + + if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) { + if (!cc.__msh_save_visibility) { + cc.__msh_save_visibility = getVisib(cc); + } + cc.style.visibility = cc.__msh_save_visibility; + } else { + if (!cc.__msh_save_visibility) { + cc.__msh_save_visibility = getVisib(cc); + } + cc.style.visibility = "hidden"; + } + } + } +}; + +/** Internal function; it displays the bar with the names of the weekday. */ +Calendar.prototype._displayWeekdays = function () { + var fdow = this.firstDayOfWeek; + var cell = this.firstdayname; + var weekend = Calendar._TT["WEEKEND"]; + for (var i = 0; i < 7; ++i) { + cell.className = "day name"; + var realday = (i + fdow) % 7; + if (i) { + cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]); + cell.navtype = 100; + cell.calendar = this; + cell.fdow = realday; + Calendar._add_evs(cell); + } + if (weekend.indexOf(realday.toString()) != -1) { + Calendar.addClass(cell, "weekend"); + } + cell.innerHTML = Calendar._SDN[(i + fdow) % 7]; + cell = cell.nextSibling; + } +}; + +/** Internal function. Hides all combo boxes that might be displayed. */ +Calendar.prototype._hideCombos = function () { + this.monthsCombo.style.display = "none"; + this.yearsCombo.style.display = "none"; +}; + +/** Internal function. Starts dragging the element. */ +Calendar.prototype._dragStart = function (ev) { + if (this.dragging) { + return; + } + this.dragging = true; + var posX; + var posY; + if (Calendar.is_ie) { + posY = window.event.clientY + document.body.scrollTop; + posX = window.event.clientX + document.body.scrollLeft; + } else { + posY = ev.clientY + window.scrollY; + posX = ev.clientX + window.scrollX; + } + var st = this.element.style; + this.xOffs = posX - parseInt(st.left); + this.yOffs = posY - parseInt(st.top); + with (Calendar) { + addEvent(document, "mousemove", calDragIt); + addEvent(document, "mouseup", calDragEnd); + } +}; + +// BEGIN: DATE OBJECT PATCHES + +/** Adds the number of days array to the Date object. */ +Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31); + +/** Constants used for time computations */ +Date.SECOND = 1000 /* milliseconds */; +Date.MINUTE = 60 * Date.SECOND; +Date.HOUR = 60 * Date.MINUTE; +Date.DAY = 24 * Date.HOUR; +Date.WEEK = 7 * Date.DAY; + +Date.parseDate = function(str, fmt) { + var today = new Date(); + var y = 0; + var m = -1; + var d = 0; + var a = str.split(/\W+/); + var b = fmt.match(/%./g); + var i = 0, j = 0; + var hr = 0; + var min = 0; + for (i = 0; i < a.length; ++i) { + if (!a[i]) + continue; + switch (b[i]) { + case "%d": + case "%e": + d = parseInt(a[i], 10); + break; + + case "%m": + m = parseInt(a[i], 10) - 1; + break; + + case "%Y": + case "%y": + y = parseInt(a[i], 10); + (y < 100) && (y += (y > 29) ? 1900 : 2000); + break; + + case "%b": + case "%B": + for (j = 0; j < 12; ++j) { + if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; } + } + break; + + case "%H": + case "%I": + case "%k": + case "%l": + hr = parseInt(a[i], 10); + break; + + case "%P": + case "%p": + if (/pm/i.test(a[i]) && hr < 12) + hr += 12; + else if (/am/i.test(a[i]) && hr >= 12) + hr -= 12; + break; + + case "%M": + min = parseInt(a[i], 10); + break; + } + } + if (isNaN(y)) y = today.getFullYear(); + if (isNaN(m)) m = today.getMonth(); + if (isNaN(d)) d = today.getDate(); + if (isNaN(hr)) hr = today.getHours(); + if (isNaN(min)) min = today.getMinutes(); + if (y != 0 && m != -1 && d != 0) + return new Date(y, m, d, hr, min, 0); + y = 0; m = -1; d = 0; + for (i = 0; i < a.length; ++i) { + if (a[i].search(/[a-zA-Z]+/) != -1) { + var t = -1; + for (j = 0; j < 12; ++j) { + if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; } + } + if (t != -1) { + if (m != -1) { + d = m+1; + } + m = t; + } + } else if (parseInt(a[i], 10) <= 12 && m == -1) { + m = a[i]-1; + } else if (parseInt(a[i], 10) > 31 && y == 0) { + y = parseInt(a[i], 10); + (y < 100) && (y += (y > 29) ? 1900 : 2000); + } else if (d == 0) { + d = a[i]; + } + } + if (y == 0) + y = today.getFullYear(); + if (m != -1 && d != 0) + return new Date(y, m, d, hr, min, 0); + return today; +}; + +/** Returns the number of days in the current month */ +Date.prototype.getMonthDays = function(month) { + var year = this.getFullYear(); + if (typeof month == "undefined") { + month = this.getMonth(); + } + if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) { + return 29; + } else { + return Date._MD[month]; + } +}; + +/** Returns the number of day in the year. */ +Date.prototype.getDayOfYear = function() { + var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0); + var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0); + var time = now - then; + return Math.floor(time / Date.DAY); +}; + +/** Returns the number of the week in year, as defined in ISO 8601. */ +Date.prototype.getWeekNumber = function() { + var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0); + var DoW = d.getDay(); + d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu + var ms = d.valueOf(); // GMT + d.setMonth(0); + d.setDate(4); // Thu in Week 1 + return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1; +}; + +/** Checks date and time equality */ +Date.prototype.equalsTo = function(date) { + return ((this.getFullYear() == date.getFullYear()) && + (this.getMonth() == date.getMonth()) && + (this.getDate() == date.getDate()) && + (this.getHours() == date.getHours()) && + (this.getMinutes() == date.getMinutes())); +}; + +/** Set only the year, month, date parts (keep existing time) */ +Date.prototype.setDateOnly = function(date) { + var tmp = new Date(date); + this.setDate(1); + this.setFullYear(tmp.getFullYear()); + this.setMonth(tmp.getMonth()); + this.setDate(tmp.getDate()); +}; + +/** Prints the date in a string according to the given format. */ +Date.prototype.print = function (str) { + var m = this.getMonth(); + var d = this.getDate(); + var y = this.getFullYear(); + var wn = this.getWeekNumber(); + var w = this.getDay(); + var s = {}; + var hr = this.getHours(); + var pm = (hr >= 12); + var ir = (pm) ? (hr - 12) : hr; + var dy = this.getDayOfYear(); + if (ir == 0) + ir = 12; + var min = this.getMinutes(); + var sec = this.getSeconds(); + s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N] + s["%A"] = Calendar._DN[w]; // full weekday name + s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N] + s["%B"] = Calendar._MN[m]; // full month name + // FIXME: %c : preferred date and time representation for the current locale + s["%C"] = 1 + Math.floor(y / 100); // the century number + s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31) + s["%e"] = d; // the day of the month (range 1 to 31) + // FIXME: %D : american date style: %m/%d/%y + // FIXME: %E, %F, %G, %g, %h (man strftime) + s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format) + s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format) + s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366) + s["%k"] = hr; // hour, range 0 to 23 (24h format) + s["%l"] = ir; // hour, range 1 to 12 (12h format) + s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12 + s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59 + s["%n"] = "\n"; // a newline character + s["%p"] = pm ? "PM" : "AM"; + s["%P"] = pm ? "pm" : "am"; + // FIXME: %r : the time in am/pm notation %I:%M:%S %p + // FIXME: %R : the time in 24-hour notation %H:%M + s["%s"] = Math.floor(this.getTime() / 1000); + s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59 + s["%t"] = "\t"; // a tab character + // FIXME: %T : the time in 24-hour notation (%H:%M:%S) + s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn; + s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON) + s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN) + // FIXME: %x : preferred date representation for the current locale without the time + // FIXME: %X : preferred time representation for the current locale without the date + s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99) + s["%Y"] = y; // year with the century + s["%%"] = "%"; // a literal '%' character + + var re = /%./g; + if (!Calendar.is_ie5 && !Calendar.is_khtml) + return str.replace(re, function (par) { return s[par] || par; }); + + var a = str.match(re); + for (var i = 0; i < a.length; i++) { + var tmp = s[a[i]]; + if (tmp) { + re = new RegExp(a[i], 'g'); + str = str.replace(re, tmp); + } + } + + return str; +}; + +Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear; +Date.prototype.setFullYear = function(y) { + var d = new Date(this); + d.__msh_oldSetFullYear(y); + if (d.getMonth() != this.getMonth()) + this.setDate(28); + this.__msh_oldSetFullYear(y); +}; + +// END: DATE OBJECT PATCHES + + +// global object that remembers the calendar +window._dynarch_popupCalendar = null; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/img.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/img.gif new file mode 100644 index 00000000..6774ca5c Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/img.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-af.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-af.js new file mode 100644 index 00000000..ace710e6 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-af.js @@ -0,0 +1,39 @@ +// ** I18N Afrikaans +Calendar._DN = new Array +("Sondag", + "Maandag", + "Dinsdag", + "Woensdag", + "Donderdag", + "Vrydag", + "Saterdag", + "Sondag"); +Calendar._MN = new Array +("Januarie", + "Februarie", + "Maart", + "April", + "Mei", + "Junie", + "Julie", + "Augustus", + "September", + "Oktober", + "November", + "Desember"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["TOGGLE"] = "Verander eerste dag van die week"; +Calendar._TT["PREV_YEAR"] = "Vorige jaar (hou vir keuselys)"; +Calendar._TT["PREV_MONTH"] = "Vorige maand (hou vir keuselys)"; +Calendar._TT["GO_TODAY"] = "Gaan na vandag"; +Calendar._TT["NEXT_MONTH"] = "Volgende maand (hou vir keuselys)"; +Calendar._TT["NEXT_YEAR"] = "Volgende jaar (hou vir keuselys)"; +Calendar._TT["SEL_DATE"] = "Kies datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Sleep om te skuif"; +Calendar._TT["PART_TODAY"] = " (vandag)"; +Calendar._TT["MON_FIRST"] = "Vertoon Maandag eerste"; +Calendar._TT["SUN_FIRST"] = "Display Sunday first"; +Calendar._TT["CLOSE"] = "Close"; +Calendar._TT["TODAY"] = "Today"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-al.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-al.js new file mode 100644 index 00000000..f9cfe8f5 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-al.js @@ -0,0 +1,101 @@ +// Calendar ALBANIAN language +//author Rigels Gordani rige@hotmail.com + +// ditet +Calendar._DN = new Array +("E Diele", +"E Hene", +"E Marte", +"E Merkure", +"E Enjte", +"E Premte", +"E Shtune", +"E Diele"); + +//ditet shkurt +Calendar._SDN = new Array +("Die", +"Hen", +"Mar", +"Mer", +"Enj", +"Pre", +"Sht", +"Die"); + +// muajt +Calendar._MN = new Array +("Janar", +"Shkurt", +"Mars", +"Prill", +"Maj", +"Qeshor", +"Korrik", +"Gusht", +"Shtator", +"Tetor", +"Nentor", +"Dhjetor"); + +// muajte shkurt +Calendar._SMN = new Array +("Jan", +"Shk", +"Mar", +"Pri", +"Maj", +"Qes", +"Kor", +"Gus", +"Sht", +"Tet", +"Nen", +"Dhj"); + +// ndihmesa +Calendar._TT = {}; +Calendar._TT["INFO"] = "Per kalendarin"; + +Calendar._TT["ABOUT"] = +"Zgjedhes i ores/dates ne DHTML \n" + +"\n\n" +"Zgjedhja e Dates:\n" + +"- Perdor butonat \xab, \xbb per te zgjedhur vitin\n" + +"- Perdor butonat" + String.fromCharCode(0x2039) + ", " + +String.fromCharCode(0x203a) + +" per te zgjedhur muajin\n" + +"- Mbani shtypur butonin e mousit per nje zgjedje me te shpejte."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Zgjedhja e kohes:\n" + +"- Kliko tek ndonje nga pjeset e ores per ta rritur ate\n" + +"- ose kliko me Shift per ta zvogeluar ate\n" + +"- ose cliko dhe terhiq per zgjedhje me te shpejte."; + +Calendar._TT["PREV_YEAR"] = "Viti i shkuar (prit per menune)"; +Calendar._TT["PREV_MONTH"] = "Muaji i shkuar (prit per menune)"; +Calendar._TT["GO_TODAY"] = "Sot"; +Calendar._TT["NEXT_MONTH"] = "Muaji i ardhshem (prit per menune)"; +Calendar._TT["NEXT_YEAR"] = "Viti i ardhshem (prit per menune)"; +Calendar._TT["SEL_DATE"] = "Zgjidh daten"; +Calendar._TT["DRAG_TO_MOVE"] = "Terhiqe per te levizur"; +Calendar._TT["PART_TODAY"] = " (sot)"; + +// "%s" eshte dita e pare e javes +// %s do te zevendesohet me emrin e dite +Calendar._TT["DAY_FIRST"] = "Trego te %s te paren"; + + +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Mbyll"; +Calendar._TT["TODAY"] = "Sot"; +Calendar._TT["TIME_PART"] = "Kliko me (Shift-)ose terhiqe per te ndryshuar +vleren"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "Java"; +Calendar._TT["TIME"] = "Koha:"; + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-bg.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-bg.js new file mode 100644 index 00000000..5eb73ec6 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-bg.js @@ -0,0 +1,124 @@ +// ** I18N + +// Calendar BG language +// Author: Mihai Bazon, +// Translator: Valentin Sheiretsky, +// Encoding: Windows-1251 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Íåäåëÿ", + "Ïîíåäåëíèê", + "Âòîðíèê", + "Ñðÿäà", + "×åòâúðòúê", + "Ïåòúê", + "Ñúáîòà", + "Íåäåëÿ"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Íåä", + "Ïîí", + "Âòî", + "Ñðÿ", + "×åò", + "Ïåò", + "Ñúá", + "Íåä"); + +// full month names +Calendar._MN = new Array +("ßíóàðè", + "Ôåâðóàðè", + "Ìàðò", + "Àïðèë", + "Ìàé", + "Þíè", + "Þëè", + "Àâãóñò", + "Ñåïòåìâðè", + "Îêòîìâðè", + "Íîåìâðè", + "Äåêåìâðè"); + +// short month names +Calendar._SMN = new Array +("ßíó", + "Ôåâ", + "Ìàð", + "Àïð", + "Ìàé", + "Þíè", + "Þëè", + "Àâã", + "Ñåï", + "Îêò", + "Íîå", + "Äåê"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Èíôîðìàöèÿ çà êàëåíäàðà"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Date selection:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" + +"- Hold mouse button on any of the above buttons for faster selection."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Time selection:\n" + +"- Click on any of the time parts to increase it\n" + +"- or Shift-click to decrease it\n" + +"- or click and drag for faster selection."; + +Calendar._TT["PREV_YEAR"] = "Ïðåäíà ãîäèíà (çàäðúæòå çà ìåíþ)"; +Calendar._TT["PREV_MONTH"] = "Ïðåäåí ìåñåö (çàäðúæòå çà ìåíþ)"; +Calendar._TT["GO_TODAY"] = "Èçáåðåòå äíåñ"; +Calendar._TT["NEXT_MONTH"] = "Ñëåäâàù ìåñåö (çàäðúæòå çà ìåíþ)"; +Calendar._TT["NEXT_YEAR"] = "Ñëåäâàùà ãîäèíà (çàäðúæòå çà ìåíþ)"; +Calendar._TT["SEL_DATE"] = "Èçáåðåòå äàòà"; +Calendar._TT["DRAG_TO_MOVE"] = "Ïðåìåñòâàíå"; +Calendar._TT["PART_TODAY"] = " (äíåñ)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "%s êàòî ïúðâè äåí"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Çàòâîðåòå"; +Calendar._TT["TODAY"] = "Äíåñ"; +Calendar._TT["TIME_PART"] = "(Shift-)Click èëè drag çà äà ïðîìåíèòå ñòîéíîñòòà"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%A - %e %B %Y"; + +Calendar._TT["WK"] = "Ñåäì"; +Calendar._TT["TIME"] = "×àñ:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-big5-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-big5-utf8.js new file mode 100644 index 00000000..3b6df42a --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-big5-utf8.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar big5-utf8 language +// Author: Gary Fu, +// Encoding: utf8 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("星期日", + "星期一", + "星期二", + "星期三", + "星期四", + "星期五", + "星期六", + "星期日"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("æ—¥", + "一", + "二", + "三", + "å››", + "五", + "å…­", + "æ—¥"); + +// full month names +Calendar._MN = new Array +("一月", + "二月", + "三月", + "四月", + "五月", + "六月", + "七月", + "八月", + "乿œˆ", + "åæœˆ", + "å一月", + "å二月"); + +// short month names +Calendar._SMN = new Array +("一月", + "二月", + "三月", + "四月", + "五月", + "六月", + "七月", + "八月", + "乿œˆ", + "åæœˆ", + "å一月", + "å二月"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "關於"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"æ—¥æœŸé¸æ“‡æ–¹æ³•:\n" + +"- 使用 \xab, \xbb 按鈕å¯é¸æ“‡å¹´ä»½\n" + +"- 使用 " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " 按鈕å¯é¸æ“‡æœˆä»½\n" + +"- 按ä½ä¸Šé¢çš„æŒ‰éˆ•å¯ä»¥åŠ å¿«é¸å–"; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"æ™‚é–“é¸æ“‡æ–¹æ³•:\n" + +"- 點擊任何的時間部份å¯å¢žåР其值\n" + +"- åŒæ™‚按Shiftéµå†é»žæ“Šå¯æ¸›å°‘其值\n" + +"- 點擊並拖曳å¯åŠ å¿«æ”¹è®Šçš„å€¼"; + +Calendar._TT["PREV_YEAR"] = "上一年 (按ä½é¸å–®)"; +Calendar._TT["PREV_MONTH"] = "下一年 (按ä½é¸å–®)"; +Calendar._TT["GO_TODAY"] = "到今日"; +Calendar._TT["NEXT_MONTH"] = "上一月 (按ä½é¸å–®)"; +Calendar._TT["NEXT_YEAR"] = "下一月 (按ä½é¸å–®)"; +Calendar._TT["SEL_DATE"] = "鏿“‡æ—¥æœŸ"; +Calendar._TT["DRAG_TO_MOVE"] = "拖曳"; +Calendar._TT["PART_TODAY"] = " (今日)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "å°‡ %s 顯示在å‰"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "關閉"; +Calendar._TT["TODAY"] = "今日"; +Calendar._TT["TIME_PART"] = "點擊oræ‹–æ›³å¯æ”¹è®Šæ™‚é–“(åŒæ™‚按Shift為減)"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "週"; +Calendar._TT["TIME"] = "Time:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-big5.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-big5.js new file mode 100644 index 00000000..b14335ef --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-big5.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar big5 language +// Author: Gary Fu, +// Encoding: big5 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("¬P´Á¤é", + "¬P´Á¤@", + "¬P´Á¤G", + "¬P´Á¤T", + "¬P´Á¥|", + "¬P´Á¤­", + "¬P´Á¤»", + "¬P´Á¤é"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("¤é", + "¤@", + "¤G", + "¤T", + "¥|", + "¤­", + "¤»", + "¤é"); + +// full month names +Calendar._MN = new Array +("¤@¤ë", + "¤G¤ë", + "¤T¤ë", + "¥|¤ë", + "¤­¤ë", + "¤»¤ë", + "¤C¤ë", + "¤K¤ë", + "¤E¤ë", + "¤Q¤ë", + "¤Q¤@¤ë", + "¤Q¤G¤ë"); + +// short month names +Calendar._SMN = new Array +("¤@¤ë", + "¤G¤ë", + "¤T¤ë", + "¥|¤ë", + "¤­¤ë", + "¤»¤ë", + "¤C¤ë", + "¤K¤ë", + "¤E¤ë", + "¤Q¤ë", + "¤Q¤@¤ë", + "¤Q¤G¤ë"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Ãö©ó"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"¤é´Á¿ï¾Ü¤èªk:\n" + +"- ¨Ï¥Î \xab, \xbb «ö¶s¥i¿ï¾Ü¦~¥÷\n" + +"- ¨Ï¥Î " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " «ö¶s¥i¿ï¾Ü¤ë¥÷\n" + +"- «ö¦í¤W­±ªº«ö¶s¥i¥H¥[§Ö¿ï¨ú"; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"®É¶¡¿ï¾Ü¤èªk:\n" + +"- ÂIÀ»¥ô¦óªº®É¶¡³¡¥÷¥i¼W¥[¨ä­È\n" + +"- ¦P®É«öShiftÁä¦AÂIÀ»¥i´î¤Ö¨ä­È\n" + +"- ÂIÀ»¨Ã©ì¦²¥i¥[§Ö§ïÅܪº­È"; + +Calendar._TT["PREV_YEAR"] = "¤W¤@¦~ («ö¦í¿ï³æ)"; +Calendar._TT["PREV_MONTH"] = "¤U¤@¦~ («ö¦í¿ï³æ)"; +Calendar._TT["GO_TODAY"] = "¨ì¤µ¤é"; +Calendar._TT["NEXT_MONTH"] = "¤W¤@¤ë («ö¦í¿ï³æ)"; +Calendar._TT["NEXT_YEAR"] = "¤U¤@¤ë («ö¦í¿ï³æ)"; +Calendar._TT["SEL_DATE"] = "¿ï¾Ü¤é´Á"; +Calendar._TT["DRAG_TO_MOVE"] = "©ì¦²"; +Calendar._TT["PART_TODAY"] = " (¤µ¤é)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "±N %s Åã¥Ü¦b«e"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Ãö³¬"; +Calendar._TT["TODAY"] = "¤µ¤é"; +Calendar._TT["TIME_PART"] = "ÂIÀ»or©ì¦²¥i§ïÅܮɶ¡(¦P®É«öShift¬°´î)"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "¶g"; +Calendar._TT["TIME"] = "Time:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-br.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-br.js new file mode 100644 index 00000000..8cdf5014 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-br.js @@ -0,0 +1,108 @@ +// ** I18N + +// Calendar pt-BR language +// Author: Fernando Dourado, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Domingo", + "Segunda", + "Terça", + "Quarta", + "Quinta", + "Sexta", + "Sabádo", + "Domingo"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +// [No changes using default values] + +// full month names +Calendar._MN = new Array +("Janeiro", + "Fevereiro", + "Março", + "Abril", + "Maio", + "Junho", + "Julho", + "Agosto", + "Setembro", + "Outubro", + "Novembro", + "Dezembro"); + +// short month names +// [No changes using default values] + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Sobre o calendário"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Translate to portuguese Brazil (pt-BR) by Fernando Dourado (fernando.dourado@ig.com.br)\n" + +"Tradução para o português Brasil (pt-BR) por Fernando Dourado (fernando.dourado@ig.com.br)" + +"\n\n" + +"Selecionar data:\n" + +"- Use as teclas \xab, \xbb para selecionar o ano\n" + +"- Use as teclas " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " para selecionar o mês\n" + +"- Clique e segure com o mouse em qualquer botão para selecionar rapidamente."; + +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selecionar hora:\n" + +"- Clique em qualquer uma das partes da hora para aumentar\n" + +"- ou Shift-clique para diminuir\n" + +"- ou clique e arraste para selecionar rapidamente."; + +Calendar._TT["PREV_YEAR"] = "Ano anterior (clique e segure para menu)"; +Calendar._TT["PREV_MONTH"] = "Mês anterior (clique e segure para menu)"; +Calendar._TT["GO_TODAY"] = "Ir para a data atual"; +Calendar._TT["NEXT_MONTH"] = "Próximo mês (clique e segure para menu)"; +Calendar._TT["NEXT_YEAR"] = "Próximo ano (clique e segure para menu)"; +Calendar._TT["SEL_DATE"] = "Selecione uma data"; +Calendar._TT["DRAG_TO_MOVE"] = "Clique e segure para mover"; +Calendar._TT["PART_TODAY"] = " (hoje)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Exibir %s primeiro"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Fechar"; +Calendar._TT["TODAY"] = "Hoje"; +Calendar._TT["TIME_PART"] = "(Shift-)Clique ou arraste para mudar o valor"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%d de %B de %Y"; + +Calendar._TT["WK"] = "sem"; +Calendar._TT["TIME"] = "Hora:"; + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ca.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ca.js new file mode 100644 index 00000000..bd2e7ba3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ca.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar CA language +// Author: Mihai Bazon, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Diumenge", + "Dilluns", + "Dimarts", + "Dimecres", + "Dijous", + "Divendres", + "Dissabte", + "Diumenge"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Diu", + "Dil", + "Dmt", + "Dmc", + "Dij", + "Div", + "Dis", + "Diu"); + +// full month names +Calendar._MN = new Array +("Gener", + "Febrer", + "Març", + "Abril", + "Maig", + "Juny", + "Juliol", + "Agost", + "Setembre", + "Octubre", + "Novembre", + "Desembre"); + +// short month names +Calendar._SMN = new Array +("Gen", + "Feb", + "Mar", + "Abr", + "Mai", + "Jun", + "Jul", + "Ago", + "Set", + "Oct", + "Nov", + "Des"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Sobre el calendari"; + +Calendar._TT["ABOUT"] = +"DHTML Selector de Data/Hora\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Sel.lecció de Dates:\n" + +"- Fes servir els botons \xab, \xbb per sel.leccionar l'any\n" + +"- Fes servir els botons " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " per se.lecciconar el mes\n" + +"- Manté el ratolí apretat en qualsevol dels anteriors per sel.lecció ràpida."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Time selection:\n" + +"- claca en qualsevol de les parts de la hora per augmentar-les\n" + +"- o Shift-click per decrementar-la\n" + +"- or click and arrastra per sel.lecció ràpida."; + +Calendar._TT["PREV_YEAR"] = "Any anterior (Mantenir per menu)"; +Calendar._TT["PREV_MONTH"] = "Mes anterior (Mantenir per menu)"; +Calendar._TT["GO_TODAY"] = "Anar a avui"; +Calendar._TT["NEXT_MONTH"] = "Mes següent (Mantenir per menu)"; +Calendar._TT["NEXT_YEAR"] = "Any següent (Mantenir per menu)"; +Calendar._TT["SEL_DATE"] = "Sel.leccionar data"; +Calendar._TT["DRAG_TO_MOVE"] = "Arrastrar per moure"; +Calendar._TT["PART_TODAY"] = " (avui)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Mostra %s primer"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Tanca"; +Calendar._TT["TODAY"] = "Avui"; +Calendar._TT["TIME_PART"] = "(Shift-)Click a arrastra per canviar el valor"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "st"; +Calendar._TT["TIME"] = "Hora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-cs-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-cs-utf8.js new file mode 100644 index 00000000..fd0c0602 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-cs-utf8.js @@ -0,0 +1,65 @@ +/* + calendar-cs-win.js + language: Czech + encoding: windows-1250 + author: Lubos Jerabek (xnet@seznam.cz) + Jan Uhlir (espinosa@centrum.cz) +*/ + +// ** I18N +Calendar._DN = new Array('NedÄ›le','PondÄ›lí','Úterý','StÅ™eda','ÄŒtvrtek','Pátek','Sobota','NedÄ›le'); +Calendar._SDN = new Array('Ne','Po','Út','St','ÄŒt','Pá','So','Ne'); +Calendar._MN = new Array('Leden','Únor','BÅ™ezen','Duben','KvÄ›ten','ÄŒerven','ÄŒervenec','Srpen','Září','Říjen','Listopad','Prosinec'); +Calendar._SMN = new Array('Led','Úno','BÅ™e','Dub','KvÄ›','ÄŒrv','ÄŒvc','Srp','Zář','Říj','Lis','Pro'); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O komponentÄ› kalendář"; +Calendar._TT["TOGGLE"] = "ZmÄ›na prvního dne v týdnu"; +Calendar._TT["PREV_YEAR"] = "PÅ™edchozí rok (pÅ™idrž pro menu)"; +Calendar._TT["PREV_MONTH"] = "PÅ™edchozí mÄ›síc (pÅ™idrž pro menu)"; +Calendar._TT["GO_TODAY"] = "DneÅ¡ní datum"; +Calendar._TT["NEXT_MONTH"] = "Další mÄ›síc (pÅ™idrž pro menu)"; +Calendar._TT["NEXT_YEAR"] = "Další rok (pÅ™idrž pro menu)"; +Calendar._TT["SEL_DATE"] = "Vyber datum"; +Calendar._TT["DRAG_TO_MOVE"] = "ChyÅ¥ a táhni, pro pÅ™esun"; +Calendar._TT["PART_TODAY"] = " (dnes)"; +Calendar._TT["MON_FIRST"] = "Ukaž jako první PondÄ›lí"; +//Calendar._TT["SUN_FIRST"] = "Ukaž jako první NedÄ›li"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"VýbÄ›r datumu:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Použijte tlaÄítka " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " k výbÄ›ru mÄ›síce\n" + +"- Podržte tlaÄítko myÅ¡i na jakémkoliv z tÄ›ch tlaÄítek pro rychlejší výbÄ›r."; + +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"VýbÄ›r Äasu:\n" + +"- KliknÄ›te na jakoukoliv z Äástí výbÄ›ru Äasu pro zvýšení.\n" + +"- nebo Shift-click pro snížení\n" + +"- nebo kliknÄ›te a táhnÄ›te pro rychlejší výbÄ›r."; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Zobraz %s první"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Zavřít"; +Calendar._TT["TODAY"] = "Dnes"; +Calendar._TT["TIME_PART"] = "(Shift-)Klikni nebo táhni pro zmÄ›nu hodnoty"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "d.m.yy"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "ÄŒas:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-cs-win.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-cs-win.js new file mode 100644 index 00000000..bd96c521 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-cs-win.js @@ -0,0 +1,65 @@ +/* + calendar-cs-win.js + language: Czech + encoding: windows-1250 + author: Lubos Jerabek (xnet@seznam.cz) + Jan Uhlir (espinosa@centrum.cz) +*/ + +// ** I18N +Calendar._DN = new Array('Nedìle','Pondìlí','Úterý','Støeda','Ètvrtek','Pátek','Sobota','Nedìle'); +Calendar._SDN = new Array('Ne','Po','Út','St','Èt','Pá','So','Ne'); +Calendar._MN = new Array('Leden','Únor','Bøezen','Duben','Kvìten','Èerven','Èervenec','Srpen','Záøí','Øíjen','Listopad','Prosinec'); +Calendar._SMN = new Array('Led','Úno','Bøe','Dub','Kvì','Èrv','Èvc','Srp','Záø','Øíj','Lis','Pro'); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O komponentì kalendáø"; +Calendar._TT["TOGGLE"] = "Zmìna prvního dne v týdnu"; +Calendar._TT["PREV_YEAR"] = "Pøedchozí rok (pøidrž pro menu)"; +Calendar._TT["PREV_MONTH"] = "Pøedchozí mìsíc (pøidrž pro menu)"; +Calendar._TT["GO_TODAY"] = "Dnešní datum"; +Calendar._TT["NEXT_MONTH"] = "Další mìsíc (pøidrž pro menu)"; +Calendar._TT["NEXT_YEAR"] = "Další rok (pøidrž pro menu)"; +Calendar._TT["SEL_DATE"] = "Vyber datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Chy a táhni, pro pøesun"; +Calendar._TT["PART_TODAY"] = " (dnes)"; +Calendar._TT["MON_FIRST"] = "Ukaž jako první Pondìlí"; +//Calendar._TT["SUN_FIRST"] = "Ukaž jako první Nedìli"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Výbìr datumu:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Použijte tlaèítka " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " k výbìru mìsíce\n" + +"- Podržte tlaèítko myši na jakémkoliv z tìch tlaèítek pro rychlejší výbìr."; + +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Výbìr èasu:\n" + +"- Kliknìte na jakoukoliv z èástí výbìru èasu pro zvýšení.\n" + +"- nebo Shift-click pro snížení\n" + +"- nebo kliknìte a táhnìte pro rychlejší výbìr."; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Zobraz %s první"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Zavøít"; +Calendar._TT["TODAY"] = "Dnes"; +Calendar._TT["TIME_PART"] = "(Shift-)Klikni nebo táhni pro zmìnu hodnoty"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "d.m.yy"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Èas:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-da.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-da.js new file mode 100644 index 00000000..2e156570 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-da.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar DA language +// Author: Michael Thingmand Henriksen, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Søndag", +"Mandag", +"Tirsdag", +"Onsdag", +"Torsdag", +"Fredag", +"Lørdag", +"Søndag"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Søn", +"Man", +"Tir", +"Ons", +"Tor", +"Fre", +"Lør", +"Søn"); + +// full month names +Calendar._MN = new Array +("Januar", +"Februar", +"Marts", +"April", +"Maj", +"Juni", +"Juli", +"August", +"September", +"Oktober", +"November", +"December"); + +// short month names +Calendar._SMN = new Array +("Jan", +"Feb", +"Mar", +"Apr", +"Maj", +"Jun", +"Jul", +"Aug", +"Sep", +"Okt", +"Nov", +"Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Om Kalenderen"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For den seneste version besøg: http://www.dynarch.com/projects/calendar/\n"; + +"Distribueret under GNU LGPL. Se http://gnu.org/licenses/lgpl.html for detajler." + +"\n\n" + +"Valg af dato:\n" + +"- Brug \xab, \xbb knapperne for at vælge Ã¥r\n" + +"- Brug " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " knapperne for at vælge mÃ¥ned\n" + +"- Hold knappen pÃ¥ musen nede pÃ¥ knapperne ovenfor for hurtigere valg."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Valg af tid:\n" + +"- Klik pÃ¥ en vilkÃ¥rlig del for større værdi\n" + +"- eller Shift-klik for for mindre værdi\n" + +"- eller klik og træk for hurtigere valg."; + +Calendar._TT["PREV_YEAR"] = "Ét Ã¥r tilbage (hold for menu)"; +Calendar._TT["PREV_MONTH"] = "Én mÃ¥ned tilbage (hold for menu)"; +Calendar._TT["GO_TODAY"] = "GÃ¥ til i dag"; +Calendar._TT["NEXT_MONTH"] = "Én mÃ¥ned frem (hold for menu)"; +Calendar._TT["NEXT_YEAR"] = "Ét Ã¥r frem (hold for menu)"; +Calendar._TT["SEL_DATE"] = "Vælg dag"; +Calendar._TT["DRAG_TO_MOVE"] = "Træk vinduet"; +Calendar._TT["PART_TODAY"] = " (i dag)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Vis %s først"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Luk"; +Calendar._TT["TODAY"] = "I dag"; +Calendar._TT["TIME_PART"] = "(Shift-)klik eller træk for at ændre værdi"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "Uge"; +Calendar._TT["TIME"] = "Tid:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-de.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-de.js new file mode 100644 index 00000000..6e9e7054 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-de.js @@ -0,0 +1,124 @@ +// ** I18N + +// Calendar DE language +// Author: Jack (tR), +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sonntag", + "Montag", + "Dienstag", + "Mittwoch", + "Donnerstag", + "Freitag", + "Samstag", + "Sonntag"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("So", + "Mo", + "Di", + "Mi", + "Do", + "Fr", + "Sa", + "So"); + +// full month names +Calendar._MN = new Array +("Januar", + "Februar", + "M\u00e4rz", + "April", + "Mai", + "Juni", + "Juli", + "August", + "September", + "Oktober", + "November", + "Dezember"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "M\u00e4r", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Okt", + "Nov", + "Dez"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "\u00DCber dieses Kalendarmodul"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Datum ausw\u00e4hlen:\n" + +"- Benutzen Sie die \xab, \xbb Buttons um das Jahr zu w\u00e4hlen\n" + +"- Benutzen Sie die " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " Buttons um den Monat zu w\u00e4hlen\n" + +"- F\u00fcr eine Schnellauswahl halten Sie die Maustaste \u00fcber diesen Buttons fest."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Zeit ausw\u00e4hlen:\n" + +"- Klicken Sie auf die Teile der Uhrzeit, um diese zu erh\u00F6hen\n" + +"- oder klicken Sie mit festgehaltener Shift-Taste um diese zu verringern\n" + +"- oder klicken und festhalten f\u00fcr Schnellauswahl."; + +Calendar._TT["TOGGLE"] = "Ersten Tag der Woche w\u00e4hlen"; +Calendar._TT["PREV_YEAR"] = "Voriges Jahr (Festhalten f\u00fcr Schnellauswahl)"; +Calendar._TT["PREV_MONTH"] = "Voriger Monat (Festhalten f\u00fcr Schnellauswahl)"; +Calendar._TT["GO_TODAY"] = "Heute ausw\u00e4hlen"; +Calendar._TT["NEXT_MONTH"] = "N\u00e4chst. Monat (Festhalten f\u00fcr Schnellauswahl)"; +Calendar._TT["NEXT_YEAR"] = "N\u00e4chst. Jahr (Festhalten f\u00fcr Schnellauswahl)"; +Calendar._TT["SEL_DATE"] = "Datum ausw\u00e4hlen"; +Calendar._TT["DRAG_TO_MOVE"] = "Zum Bewegen festhalten"; +Calendar._TT["PART_TODAY"] = " (Heute)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Woche beginnt mit %s "; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Schlie\u00dfen"; +Calendar._TT["TODAY"] = "Heute"; +Calendar._TT["TIME_PART"] = "(Shift-)Klick oder Festhalten und Ziehen um den Wert zu \u00e4ndern"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d.%m.%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Zeit:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-du.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-du.js new file mode 100644 index 00000000..9c9051fd --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-du.js @@ -0,0 +1,45 @@ +// ** I18N +Calendar._DN = new Array +("Zondag", + "Maandag", + "Dinsdag", + "Woensdag", + "Donderdag", + "Vrijdag", + "Zaterdag", + "Zondag"); +Calendar._MN = new Array +("Januari", + "Februari", + "Maart", + "April", + "Mei", + "Juni", + "Juli", + "Augustus", + "September", + "Oktober", + "November", + "December"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["TOGGLE"] = "Toggle startdag van de week"; +Calendar._TT["PREV_YEAR"] = "Vorig jaar (indrukken voor menu)"; +Calendar._TT["PREV_MONTH"] = "Vorige month (indrukken voor menu)"; +Calendar._TT["GO_TODAY"] = "Naar Vandaag"; +Calendar._TT["NEXT_MONTH"] = "Volgende Maand (indrukken voor menu)"; +Calendar._TT["NEXT_YEAR"] = "Volgend jaar (indrukken voor menu)"; +Calendar._TT["SEL_DATE"] = "Selecteer datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Sleep om te verplaatsen"; +Calendar._TT["PART_TODAY"] = " (vandaag)"; +Calendar._TT["MON_FIRST"] = "Toon Maandag eerst"; +Calendar._TT["SUN_FIRST"] = "Toon Zondag eerst"; +Calendar._TT["CLOSE"] = "Sluiten"; +Calendar._TT["TODAY"] = "Vandaag"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "y-mm-dd"; +Calendar._TT["TT_DATE_FORMAT"] = "D, M d"; + +Calendar._TT["WK"] = "wk"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-el.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-el.js new file mode 100644 index 00000000..43a9b2ce --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-el.js @@ -0,0 +1,89 @@ +// ** I18N +Calendar._DN = new Array +("ΚυÏιακή", + "ΔευτέÏα", + "ΤÏίτη", + "ΤετάÏτη", + "Πέμπτη", + "ΠαÏασκευή", + "Σάββατο", + "ΚυÏιακή"); + +Calendar._SDN = new Array +("Κυ", + "Δε", + "TÏ", + "Τε", + "Πε", + "Πα", + "Σα", + "Κυ"); + +Calendar._MN = new Array +("ΙανουάÏιος", + "ΦεβÏουάÏιος", + "ΜάÏτιος", + "ΑπÏίλιος", + "Μάϊος", + "ΙοÏνιος", + "ΙοÏλιος", + "ΑÏγουστος", + "ΣεπτέμβÏιος", + "ΟκτώβÏιος", + "ÎοέμβÏιος", + "ΔεκέμβÏιος"); + +Calendar._SMN = new Array +("Ιαν", + "Φεβ", + "ΜαÏ", + "ΑπÏ", + "Μαι", + "Ιουν", + "Ιουλ", + "Αυγ", + "Σεπ", + "Οκτ", + "Îοε", + "Δεκ"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Για το ημεÏολόγιο"; + +Calendar._TT["ABOUT"] = +"Επιλογέας ημεÏομηνίας/ÏŽÏας σε DHTML\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Για τελευταία έκδοση: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Επιλογή ημεÏομηνίας:\n" + +"- ΧÏησιμοποιείστε τα κουμπιά \xab, \xbb για επιλογή έτους\n" + +"- ΧÏησιμοποιείστε τα κουμπιά " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " για επιλογή μήνα\n" + +"- ΚÏατήστε κουμπί Ï€Î¿Î½Ï„Î¹ÎºÎ¿Ï Ï€Î±Ï„Î·Î¼Î­Î½Î¿ στα παÏαπάνω κουμπιά για πιο γÏήγοÏη επιλογή."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Επιλογή ÏŽÏας:\n" + +"- Κάντε κλικ σε ένα από τα μέÏη της ÏŽÏας για αÏξηση\n" + +"- ή Shift-κλικ για μείωση\n" + +"- ή κλικ και μετακίνηση για πιο γÏήγοÏη επιλογή."; +Calendar._TT["TOGGLE"] = "ΜπάÏα Ï€Ïώτης ημέÏας της εβδομάδας"; +Calendar._TT["PREV_YEAR"] = "ΠÏοηγ. έτος (κÏατήστε για το μενοÏ)"; +Calendar._TT["PREV_MONTH"] = "ΠÏοηγ. μήνας (κÏατήστε για το μενοÏ)"; +Calendar._TT["GO_TODAY"] = "ΣήμεÏα"; +Calendar._TT["NEXT_MONTH"] = "Επόμενος μήνας (κÏατήστε για το μενοÏ)"; +Calendar._TT["NEXT_YEAR"] = "Επόμενο έτος (κÏατήστε για το μενοÏ)"; +Calendar._TT["SEL_DATE"] = "Επιλέξτε ημεÏομηνία"; +Calendar._TT["DRAG_TO_MOVE"] = "ΣÏÏτε για να μετακινήσετε"; +Calendar._TT["PART_TODAY"] = " (σήμεÏα)"; +Calendar._TT["MON_FIRST"] = "Εμφάνιση ΔευτέÏας Ï€Ïώτα"; +Calendar._TT["SUN_FIRST"] = "Εμφάνιση ΚυÏιακής Ï€Ïώτα"; +Calendar._TT["CLOSE"] = "Κλείσιμο"; +Calendar._TT["TODAY"] = "ΣήμεÏα"; +Calendar._TT["TIME_PART"] = "(Shift-)κλικ ή μετακίνηση για αλλαγή"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "dd-mm-y"; +Calendar._TT["TT_DATE_FORMAT"] = "D, d M"; + +Calendar._TT["WK"] = "εβδ"; + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-en-US.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-en-US.js new file mode 100644 index 00000000..f079c3f1 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-en-US.js @@ -0,0 +1,127 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 0; + +// full month names +Calendar._MN = new Array +("January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "About the calendar"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Date selection:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" + +"- Hold mouse button on any of the above buttons for faster selection."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Time selection:\n" + +"- Click on any of the time parts to increase it\n" + +"- or Shift-click to decrease it\n" + +"- or click and drag for faster selection."; + +Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)"; +Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)"; +Calendar._TT["GO_TODAY"] = "Go Today"; +Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)"; +Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)"; +Calendar._TT["SEL_DATE"] = "Select date"; +Calendar._TT["DRAG_TO_MOVE"] = "Drag to move"; +Calendar._TT["PART_TODAY"] = " (today)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Display %s first"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Close"; +Calendar._TT["TODAY"] = "Today"; +Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%m/%d/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Time:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-en-gb.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-en-gb.js new file mode 100644 index 00000000..d69406f3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-en-gb.js @@ -0,0 +1,127 @@ +// ** I18N + +// Calendar EN-GB language +// Author: Mihai Bazon, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 0; + +// full month names +Calendar._MN = new Array +("January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "About the calendar"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Date selection:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" + +"- Hold mouse button on any of the above buttons for faster selection."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Time selection:\n" + +"- Click on any of the time parts to increase it\n" + +"- or Shift-click to decrease it\n" + +"- or click and drag for faster selection."; + +Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)"; +Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)"; +Calendar._TT["GO_TODAY"] = "Go Today"; +Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)"; +Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)"; +Calendar._TT["SEL_DATE"] = "Select date"; +Calendar._TT["DRAG_TO_MOVE"] = "Drag to move"; +Calendar._TT["PART_TODAY"] = " (today)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Display %s first"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Close"; +Calendar._TT["TODAY"] = "Today"; +Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Time:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-en.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-en.js new file mode 100644 index 00000000..d6f0909b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-en.js @@ -0,0 +1,127 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 0; + +// full month names +Calendar._MN = new Array +("January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "About the calendar"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Date selection:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" + +"- Hold mouse button on any of the above buttons for faster selection."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Time selection:\n" + +"- Click on any of the time parts to increase it\n" + +"- or Shift-click to decrease it\n" + +"- or click and drag for faster selection."; + +Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)"; +Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)"; +Calendar._TT["GO_TODAY"] = "Go Today"; +Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)"; +Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)"; +Calendar._TT["SEL_DATE"] = "Select date"; +Calendar._TT["DRAG_TO_MOVE"] = "Drag to move"; +Calendar._TT["PART_TODAY"] = " (today)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Display %s first"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Close"; +Calendar._TT["TODAY"] = "Today"; +Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Time:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-es.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-es.js new file mode 100644 index 00000000..b9b1843f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-es.js @@ -0,0 +1,129 @@ +// ** I18N + +// Calendar ES (spanish) language +// Author: Mihai Bazon, +// Updater: Servilio Afre Puentes +// Updated: 2004-06-03 +// Encoding: utf-8 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Domingo", + "Lunes", + "Martes", + "Miércoles", + "Jueves", + "Viernes", + "Sábado", + "Domingo"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Dom", + "Lun", + "Mar", + "Mié", + "Jue", + "Vie", + "Sáb", + "Dom"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 1; + +// full month names +Calendar._MN = new Array +("Enero", + "Febrero", + "Marzo", + "Abril", + "Mayo", + "Junio", + "Julio", + "Agosto", + "Septiembre", + "Octubre", + "Noviembre", + "Diciembre"); + +// short month names +Calendar._SMN = new Array +("Ene", + "Feb", + "Mar", + "Abr", + "May", + "Jun", + "Jul", + "Ago", + "Sep", + "Oct", + "Nov", + "Dic"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Acerca del calendario"; + +Calendar._TT["ABOUT"] = +"Selector DHTML de Fecha/Hora\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Para conseguir la última versión visite: http://www.dynarch.com/projects/calendar/\n" + +"Distribuido bajo licencia GNU LGPL. Visite http://gnu.org/licenses/lgpl.html para más detalles." + +"\n\n" + +"Selección de fecha:\n" + +"- Use los botones \xab, \xbb para seleccionar el año\n" + +"- Use los botones " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " para seleccionar el mes\n" + +"- Mantenga pulsado el ratón en cualquiera de estos botones para una selección rápida."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selección de hora:\n" + +"- Pulse en cualquiera de las partes de la hora para incrementarla\n" + +"- o pulse las mayúsculas mientras hace clic para decrementarla\n" + +"- o haga clic y arrastre el ratón para una selección más rápida."; + +Calendar._TT["PREV_YEAR"] = "Año anterior (mantener para menú)"; +Calendar._TT["PREV_MONTH"] = "Mes anterior (mantener para menú)"; +Calendar._TT["GO_TODAY"] = "Ir a hoy"; +Calendar._TT["NEXT_MONTH"] = "Mes siguiente (mantener para menú)"; +Calendar._TT["NEXT_YEAR"] = "Año siguiente (mantener para menú)"; +Calendar._TT["SEL_DATE"] = "Seleccionar fecha"; +Calendar._TT["DRAG_TO_MOVE"] = "Arrastrar para mover"; +Calendar._TT["PART_TODAY"] = " (hoy)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Hacer %s primer día de la semana"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Cerrar"; +Calendar._TT["TODAY"] = "Hoy"; +Calendar._TT["TIME_PART"] = "(Mayúscula-)Clic o arrastre para cambiar valor"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %e de %B de %Y"; + +Calendar._TT["WK"] = "sem"; +Calendar._TT["TIME"] = "Hora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-fi.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-fi.js new file mode 100644 index 00000000..eae2032b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-fi.js @@ -0,0 +1,98 @@ +// ** I18N + +// Calendar FI language (Finnish, Suomi) +// Author: Jarno Käyhkö, +// Encoding: UTF-8 +// Distributed under the same terms as the calendar itself. + +// full day names +Calendar._DN = new Array +("Sunnuntai", + "Maanantai", + "Tiistai", + "Keskiviikko", + "Torstai", + "Perjantai", + "Lauantai", + "Sunnuntai"); + +// short day names +Calendar._SDN = new Array +("Su", + "Ma", + "Ti", + "Ke", + "To", + "Pe", + "La", + "Su"); + +// full month names +Calendar._MN = new Array +("Tammikuu", + "Helmikuu", + "Maaliskuu", + "Huhtikuu", + "Toukokuu", + "Kesäkuu", + "Heinäkuu", + "Elokuu", + "Syyskuu", + "Lokakuu", + "Marraskuu", + "Joulukuu"); + +// short month names +Calendar._SMN = new Array +("Tam", + "Hel", + "Maa", + "Huh", + "Tou", + "Kes", + "Hei", + "Elo", + "Syy", + "Lok", + "Mar", + "Jou"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Tietoja kalenterista"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Uusin versio osoitteessa: http://www.dynarch.com/projects/calendar/\n" + +"Julkaistu GNU LGPL lisenssin alaisuudessa. Lisätietoja osoitteessa http://gnu.org/licenses/lgpl.html" + +"\n\n" + +"Päivämäärä valinta:\n" + +"- Käytä \xab, \xbb painikkeita valitaksesi vuosi\n" + +"- Käytä " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " painikkeita valitaksesi kuukausi\n" + +"- Pitämällä hiiren painiketta minkä tahansa yllä olevan painikkeen kohdalla, saat näkyviin valikon nopeampaan siirtymiseen."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Ajan valinta:\n" + +"- Klikkaa kellonajan numeroita lisätäksesi aikaa\n" + +"- tai pitämällä Shift-näppäintä pohjassa saat aikaa taaksepäin\n" + +"- tai klikkaa ja pidä hiiren painike pohjassa sekä liikuta hiirtä muuttaaksesi aikaa nopeasti eteen- ja taaksepäin."; + +Calendar._TT["PREV_YEAR"] = "Edell. vuosi (paina hetki, näet valikon)"; +Calendar._TT["PREV_MONTH"] = "Edell. kuukausi (paina hetki, näet valikon)"; +Calendar._TT["GO_TODAY"] = "Siirry tähän päivään"; +Calendar._TT["NEXT_MONTH"] = "Seur. kuukausi (paina hetki, näet valikon)"; +Calendar._TT["NEXT_YEAR"] = "Seur. vuosi (paina hetki, näet valikon)"; +Calendar._TT["SEL_DATE"] = "Valitse päivämäärä"; +Calendar._TT["DRAG_TO_MOVE"] = "Siirrä kalenterin paikkaa"; +Calendar._TT["PART_TODAY"] = " (tänään)"; +Calendar._TT["MON_FIRST"] = "Näytä maanantai ensimmäisenä"; +Calendar._TT["SUN_FIRST"] = "Näytä sunnuntai ensimmäisenä"; +Calendar._TT["CLOSE"] = "Sulje"; +Calendar._TT["TODAY"] = "Tänään"; +Calendar._TT["TIME_PART"] = "(Shift-) Klikkaa tai liikuta muuttaaksesi aikaa"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d.%m.%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%d.%m.%Y"; + +Calendar._TT["WK"] = "Vko"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-fr.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-fr.js new file mode 100644 index 00000000..6e011b2e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-fr.js @@ -0,0 +1,125 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// Translator: David Duret, from previous french version + +// full day names +Calendar._DN = new Array +("Dimanche", + "Lundi", + "Mardi", + "Mercredi", + "Jeudi", + "Vendredi", + "Samedi", + "Dimanche"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Dim", + "Lun", + "Mar", + "Mar", + "Jeu", + "Ven", + "Sam", + "Dim"); + +// full month names +Calendar._MN = new Array +("Janvier", + "Février", + "Mars", + "Avril", + "Mai", + "Juin", + "Juillet", + "Août", + "Septembre", + "Octobre", + "Novembre", + "Décembre"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Fev", + "Mar", + "Avr", + "Mai", + "Juin", + "Juil", + "Aout", + "Sep", + "Oct", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "A propos du calendrier"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Heure Selecteur\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Pour la derniere version visitez : http://www.dynarch.com/projects/calendar/\n" + +"Distribué par GNU LGPL. Voir http://gnu.org/licenses/lgpl.html pour les details." + +"\n\n" + +"Selection de la date :\n" + +"- Utiliser les bouttons \xab, \xbb pour selectionner l\'annee\n" + +"- Utiliser les bouttons " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " pour selectionner les mois\n" + +"- Garder la souris sur n'importe quels boutons pour une selection plus rapide"; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selection de l\'heure :\n" + +"- Cliquer sur heures ou minutes pour incrementer\n" + +"- ou Maj-clic pour decrementer\n" + +"- ou clic et glisser-deplacer pour une selection plus rapide"; + +Calendar._TT["PREV_YEAR"] = "Année préc. (maintenir pour menu)"; +Calendar._TT["PREV_MONTH"] = "Mois préc. (maintenir pour menu)"; +Calendar._TT["GO_TODAY"] = "Atteindre la date du jour"; +Calendar._TT["NEXT_MONTH"] = "Mois suiv. (maintenir pour menu)"; +Calendar._TT["NEXT_YEAR"] = "Année suiv. (maintenir pour menu)"; +Calendar._TT["SEL_DATE"] = "Sélectionner une date"; +Calendar._TT["DRAG_TO_MOVE"] = "Déplacer"; +Calendar._TT["PART_TODAY"] = " (Aujourd'hui)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Afficher %s en premier"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Fermer"; +Calendar._TT["TODAY"] = "Aujourd'hui"; +Calendar._TT["TIME_PART"] = "(Maj-)Clic ou glisser pour modifier la valeur"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "Sem."; +Calendar._TT["TIME"] = "Heure :"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-he-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-he-utf8.js new file mode 100644 index 00000000..5fb7dd56 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-he-utf8.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar EN language +// Author: Idan Sofer, +// Encoding: UTF-8 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("ר×שון", + "שני", + "שלישי", + "רביעי", + "חמישי", + "שישי", + "שבת", + "ר×שון"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("×", + "ב", + "×’", + "ד", + "×”", + "ו", + "ש", + "×"); + +// full month names +Calendar._MN = new Array +("ינו×ר", + "פברו×ר", + "מרץ", + "×פריל", + "מ××™", + "יוני", + "יולי", + "×וגוסט", + "ספטמבר", + "×וקטובר", + "נובמבר", + "דצמבר"); + +// short month names +Calendar._SMN = new Array +("×™× ×", + "פבר", + "מרץ", + "×פר", + "מ××™", + "יונ", + "יול", + "×וג", + "ספט", + "×וק", + "נוב", + "דצמ"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "×ודות השנתון"; + +Calendar._TT["ABOUT"] = +"בחרן ת×ריך/שעה DHTML\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"×”×’×™×¨×¡× ×”×חרונה זמינה ב: http://www.dynarch.com/projects/calendar/\n" + +"מופץ תחת זיכיון ×” GNU LGPL. עיין ב http://gnu.org/licenses/lgpl.html ×œ×¤×¨×˜×™× × ×•×¡×¤×™×." + +"\n\n" + +בחירת ת×ריך:\n" + +"- השתמש ×‘×›×¤×ª×•×¨×™× \xab, \xbb לבחירת שנה\n" + +"- השתמש ×‘×›×¤×ª×•×¨×™× " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " לבחירת חודש\n" + +"- ×”×—×–×§ העכבר לחוץ מעל ×”×›×¤×ª×•×¨×™× ×”×ž×•×–×›×¨×™× ×œ×¢×™×œ לבחירה מהירה יותר."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"בחירת זמן:\n" + +"- לחץ על כל ×חד מחלקי הזמן כדי להוסיף\n" + +"- ×ו shift בשילוב ×¢× ×œ×—×™×¦×” כדי להחסיר\n" + +"- ×ו לחץ וגרור לפעולה מהירה יותר."; + +Calendar._TT["PREV_YEAR"] = "שנה קודמת - ×”×—×–×§ לקבלת תפריט"; +Calendar._TT["PREV_MONTH"] = "חודש ×§×•×“× - ×”×—×–×§ לקבלת תפריט"; +Calendar._TT["GO_TODAY"] = "עבור להיו×"; +Calendar._TT["NEXT_MONTH"] = "חודש ×”×‘× - ×”×—×–×§ לתפריט"; +Calendar._TT["NEXT_YEAR"] = "שנה הב××” - ×”×—×–×§ לתפריט"; +Calendar._TT["SEL_DATE"] = "בחר ת×ריך"; +Calendar._TT["DRAG_TO_MOVE"] = "גרור להזזה"; +Calendar._TT["PART_TODAY"] = " )היו×("; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "הצג %s קוד×"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "6"; + +Calendar._TT["CLOSE"] = "סגור"; +Calendar._TT["TODAY"] = "היו×"; +Calendar._TT["TIME_PART"] = "(שיפט-)לחץ וגרור כדי לשנות ערך"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "שעה::"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-hr-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-hr-utf8.js new file mode 100644 index 00000000..d569cfd9 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-hr-utf8.js @@ -0,0 +1,49 @@ +/* Croatian language file for the DHTML Calendar version 0.9.2 +* Author Krunoslav Zubrinic , June 2003. +* Feel free to use this script under the terms of the GNU Lesser General +* Public License, as long as you do not remove or alter this notice. +*/ +Calendar._DN = new Array +("Nedjelja", + "Ponedjeljak", + "Utorak", + "Srijeda", + "ÄŒetvrtak", + "Petak", + "Subota", + "Nedjelja"); +Calendar._MN = new Array +("SijeÄanj", + "VeljaÄa", + "Ožujak", + "Travanj", + "Svibanj", + "Lipanj", + "Srpanj", + "Kolovoz", + "Rujan", + "Listopad", + "Studeni", + "Prosinac"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["TOGGLE"] = "Promjeni dan s kojim poÄinje tjedan"; +Calendar._TT["PREV_YEAR"] = "Prethodna godina (dugi pritisak za meni)"; +Calendar._TT["PREV_MONTH"] = "Prethodni mjesec (dugi pritisak za meni)"; +Calendar._TT["GO_TODAY"] = "Idi na tekući dan"; +Calendar._TT["NEXT_MONTH"] = "Slijedeći mjesec (dugi pritisak za meni)"; +Calendar._TT["NEXT_YEAR"] = "Slijedeća godina (dugi pritisak za meni)"; +Calendar._TT["SEL_DATE"] = "Izaberite datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Pritisni i povuci za promjenu pozicije"; +Calendar._TT["PART_TODAY"] = " (today)"; +Calendar._TT["MON_FIRST"] = "Prikaži ponedjeljak kao prvi dan"; +Calendar._TT["SUN_FIRST"] = "Prikaži nedjelju kao prvi dan"; +Calendar._TT["CLOSE"] = "Zatvori"; +Calendar._TT["TODAY"] = "Danas"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "dd-mm-y"; +Calendar._TT["TT_DATE_FORMAT"] = "DD, dd.mm.y"; + +Calendar._TT["WK"] = "Tje"; \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-hr.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-hr.js new file mode 100644 index 00000000..88d4238b Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-hr.js differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-hu.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-hu.js new file mode 100644 index 00000000..8c70b7b0 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-hu.js @@ -0,0 +1,124 @@ +// ** I18N + +// Calendar HU language +// Author: ??? +// Modifier: KARASZI Istvan, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Vasárnap", + "Hétfõ", + "Kedd", + "Szerda", + "Csütörtök", + "Péntek", + "Szombat", + "Vasárnap"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("v", + "h", + "k", + "sze", + "cs", + "p", + "szo", + "v"); + +// full month names +Calendar._MN = new Array +("január", + "február", + "március", + "április", + "május", + "június", + "július", + "augusztus", + "szeptember", + "október", + "november", + "december"); + +// short month names +Calendar._SMN = new Array +("jan", + "feb", + "már", + "ápr", + "máj", + "jún", + "júl", + "aug", + "sze", + "okt", + "nov", + "dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "A kalendáriumról"; + +Calendar._TT["ABOUT"] = +"DHTML dátum/idõ kiválasztó\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"a legfrissebb verzió megtalálható: http://www.dynarch.com/projects/calendar/\n" + +"GNU LGPL alatt terjesztve. Lásd a http://gnu.org/licenses/lgpl.html oldalt a részletekhez." + +"\n\n" + +"Dátum választás:\n" + +"- használja a \xab, \xbb gombokat az év kiválasztásához\n" + +"- használja a " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " gombokat a hónap kiválasztásához\n" + +"- tartsa lenyomva az egérgombot a gyors választáshoz."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Idõ választás:\n" + +"- kattintva növelheti az idõt\n" + +"- shift-tel kattintva csökkentheti\n" + +"- lenyomva tartva és húzva gyorsabban kiválaszthatja."; + +Calendar._TT["PREV_YEAR"] = "Elõzõ év (tartsa nyomva a menühöz)"; +Calendar._TT["PREV_MONTH"] = "Elõzõ hónap (tartsa nyomva a menühöz)"; +Calendar._TT["GO_TODAY"] = "Mai napra ugrás"; +Calendar._TT["NEXT_MONTH"] = "Köv. hónap (tartsa nyomva a menühöz)"; +Calendar._TT["NEXT_YEAR"] = "Köv. év (tartsa nyomva a menühöz)"; +Calendar._TT["SEL_DATE"] = "Válasszon dátumot"; +Calendar._TT["DRAG_TO_MOVE"] = "Húzza a mozgatáshoz"; +Calendar._TT["PART_TODAY"] = " (ma)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "%s legyen a hét elsõ napja"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Bezár"; +Calendar._TT["TODAY"] = "Ma"; +Calendar._TT["TIME_PART"] = "(Shift-)Klikk vagy húzás az érték változtatásához"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%b %e, %a"; + +Calendar._TT["WK"] = "hét"; +Calendar._TT["TIME"] = "idõ:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-it.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-it.js new file mode 100644 index 00000000..f622c1d3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-it.js @@ -0,0 +1,124 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Translator: Fabio Di Bernardini, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Domenica", + "Lunedì", + "Martedì", + "Mercoledì", + "Giovedì", + "Venerdì", + "Sabato", + "Domenica"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Dom", + "Lun", + "Mar", + "Mer", + "Gio", + "Ven", + "Sab", + "Dom"); + +// full month names +Calendar._MN = new Array +("Gennaio", + "Febbraio", + "Marzo", + "Aprile", + "Maggio", + "Giugno", + "Luglio", + "Augosto", + "Settembre", + "Ottobre", + "Novembre", + "Dicembre"); + +// short month names +Calendar._SMN = new Array +("Gen", + "Feb", + "Mar", + "Apr", + "Mag", + "Giu", + "Lug", + "Ago", + "Set", + "Ott", + "Nov", + "Dic"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Informazioni sul calendario"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Per gli aggiornamenti: http://www.dynarch.com/projects/calendar/\n" + +"Distribuito sotto licenza GNU LGPL. Vedi http://gnu.org/licenses/lgpl.html per i dettagli." + +"\n\n" + +"Selezione data:\n" + +"- Usa \xab, \xbb per selezionare l'anno\n" + +"- Usa " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " per i mesi\n" + +"- Tieni premuto a lungo il mouse per accedere alle funzioni di selezione veloce."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selezione orario:\n" + +"- Clicca sul numero per incrementarlo\n" + +"- o Shift+click per decrementarlo\n" + +"- o click e sinistra o destra per variarlo."; + +Calendar._TT["PREV_YEAR"] = "Anno prec.(clicca a lungo per il menù)"; +Calendar._TT["PREV_MONTH"] = "Mese prec. (clicca a lungo per il menù)"; +Calendar._TT["GO_TODAY"] = "Oggi"; +Calendar._TT["NEXT_MONTH"] = "Pross. mese (clicca a lungo per il menù)"; +Calendar._TT["NEXT_YEAR"] = "Pross. anno (clicca a lungo per il menù)"; +Calendar._TT["SEL_DATE"] = "Seleziona data"; +Calendar._TT["DRAG_TO_MOVE"] = "Trascina per spostarlo"; +Calendar._TT["PART_TODAY"] = " (oggi)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Mostra prima %s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Chiudi"; +Calendar._TT["TODAY"] = "Oggi"; +Calendar._TT["TIME_PART"] = "(Shift-)Click o trascina per cambiare il valore"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a:%b:%e"; + +Calendar._TT["WK"] = "set"; +Calendar._TT["TIME"] = "Ora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-jp.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-jp.js new file mode 100644 index 00000000..b86f0da3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-jp.js @@ -0,0 +1,45 @@ +// ** I18N +Calendar._DN = new Array +("“ú", + "ŒŽ", + "‰Î", + "…", + "–Ø", + "‹à", + "“y", + "“ú"); +Calendar._MN = new Array +("1ŒŽ", + "2ŒŽ", + "3ŒŽ", + "4ŒŽ", + "5ŒŽ", + "6ŒŽ", + "7ŒŽ", + "8ŒŽ", + "9ŒŽ", + "10ŒŽ", + "11ŒŽ", + "12ŒŽ"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["TOGGLE"] = "T‚Ìʼn‚Ì—j“ú‚ðØ‚è‘Ö‚¦"; +Calendar._TT["PREV_YEAR"] = "‘O”N"; +Calendar._TT["PREV_MONTH"] = "‘OŒŽ"; +Calendar._TT["GO_TODAY"] = "¡“ú"; +Calendar._TT["NEXT_MONTH"] = "—‚ŒŽ"; +Calendar._TT["NEXT_YEAR"] = "—‚”N"; +Calendar._TT["SEL_DATE"] = "“ú•t‘I‘ð"; +Calendar._TT["DRAG_TO_MOVE"] = "ƒEƒBƒ“ƒhƒE‚̈ړ®"; +Calendar._TT["PART_TODAY"] = " (¡“ú)"; +Calendar._TT["MON_FIRST"] = "ŒŽ—j“ú‚ðæ“ª‚É"; +Calendar._TT["SUN_FIRST"] = "“ú—j“ú‚ðæ“ª‚É"; +Calendar._TT["CLOSE"] = "•‚¶‚é"; +Calendar._TT["TODAY"] = "¡“ú"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "y-mm-dd"; +Calendar._TT["TT_DATE_FORMAT"] = "%mŒŽ %d“ú (%a)"; + +Calendar._TT["WK"] = "T"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ko-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ko-utf8.js new file mode 100644 index 00000000..cdbc1caf --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ko-utf8.js @@ -0,0 +1,120 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Translation: Yourim Yi +// Encoding: EUC-KR +// lang : ko +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names + +Calendar._DN = new Array +("ì¼ìš”ì¼", + "월요ì¼", + "화요ì¼", + "수요ì¼", + "목요ì¼", + "금요ì¼", + "토요ì¼", + "ì¼ìš”ì¼"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("ì¼", + "ì›”", + "í™”", + "수", + "목", + "금", + "토", + "ì¼"); + +// full month names +Calendar._MN = new Array +("1ì›”", + "2ì›”", + "3ì›”", + "4ì›”", + "5ì›”", + "6ì›”", + "7ì›”", + "8ì›”", + "9ì›”", + "10ì›”", + "11ì›”", + "12ì›”"); + +// short month names +Calendar._SMN = new Array +("1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "calendar ì— ëŒ€í•´ì„œ"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"\n"+ +"최신 ë²„ì „ì„ ë°›ìœ¼ì‹œë ¤ë©´ http://www.dynarch.com/projects/calendar/ ì— ë°©ë¬¸í•˜ì„¸ìš”\n" + +"\n"+ +"GNU LGPL ë¼ì´ì„¼ìŠ¤ë¡œ ë°°í¬ë©ë‹ˆë‹¤. \n"+ +"ë¼ì´ì„¼ìŠ¤ì— ëŒ€í•œ ìžì„¸í•œ ë‚´ìš©ì€ http://gnu.org/licenses/lgpl.html ì„ ì½ìœ¼ì„¸ìš”." + +"\n\n" + +"ë‚ ì§œ ì„ íƒ:\n" + +"- ì—°ë„를 ì„ íƒí•˜ë ¤ë©´ \xab, \xbb ë²„íŠ¼ì„ ì‚¬ìš©í•©ë‹ˆë‹¤\n" + +"- ë‹¬ì„ ì„ íƒí•˜ë ¤ë©´ " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”\n" + +"- ê³„ì† ëˆ„ë¥´ê³  있으면 위 ê°’ë“¤ì„ ë¹ ë¥´ê²Œ ì„ íƒí•˜ì‹¤ 수 있습니다."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"시간 ì„ íƒ:\n" + +"- 마우스로 누르면 ì‹œê°„ì´ ì¦ê°€í•©ë‹ˆë‹¤\n" + +"- Shift 키와 함께 누르면 ê°ì†Œí•©ë‹ˆë‹¤\n" + +"- 누른 ìƒíƒœì—서 마우스를 움ì§ì´ë©´ 좀 ë” ë¹ ë¥´ê²Œ ê°’ì´ ë³€í•©ë‹ˆë‹¤.\n"; + +Calendar._TT["PREV_YEAR"] = "지난 í•´ (길게 누르면 목ë¡)"; +Calendar._TT["PREV_MONTH"] = "지난 달 (길게 누르면 목ë¡)"; +Calendar._TT["GO_TODAY"] = "오늘 날짜로"; +Calendar._TT["NEXT_MONTH"] = "ë‹¤ìŒ ë‹¬ (길게 누르면 목ë¡)"; +Calendar._TT["NEXT_YEAR"] = "ë‹¤ìŒ í•´ (길게 누르면 목ë¡)"; +Calendar._TT["SEL_DATE"] = "날짜를 ì„ íƒí•˜ì„¸ìš”"; +Calendar._TT["DRAG_TO_MOVE"] = "마우스 드래그로 ì´ë™ 하세요"; +Calendar._TT["PART_TODAY"] = " (오늘)"; +Calendar._TT["MON_FIRST"] = "월요ì¼ì„ 한 ì£¼ì˜ ì‹œìž‘ ìš”ì¼ë¡œ"; +Calendar._TT["SUN_FIRST"] = "ì¼ìš”ì¼ì„ 한 ì£¼ì˜ ì‹œìž‘ ìš”ì¼ë¡œ"; +Calendar._TT["CLOSE"] = "닫기"; +Calendar._TT["TODAY"] = "오늘"; +Calendar._TT["TIME_PART"] = "(Shift-)í´ë¦­ ë˜ëŠ” 드래그 하세요"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%b/%e [%a]"; + +Calendar._TT["WK"] = "주"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ko.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ko.js new file mode 100644 index 00000000..dc1da4bb --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ko.js @@ -0,0 +1,120 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Translation: Yourim Yi +// Encoding: EUC-KR +// lang : ko +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names + +Calendar._DN = new Array +("ÀÏ¿äÀÏ", + "¿ù¿äÀÏ", + "È­¿äÀÏ", + "¼ö¿äÀÏ", + "¸ñ¿äÀÏ", + "±Ý¿äÀÏ", + "Åä¿äÀÏ", + "ÀÏ¿äÀÏ"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("ÀÏ", + "¿ù", + "È­", + "¼ö", + "¸ñ", + "±Ý", + "Åä", + "ÀÏ"); + +// full month names +Calendar._MN = new Array +("1¿ù", + "2¿ù", + "3¿ù", + "4¿ù", + "5¿ù", + "6¿ù", + "7¿ù", + "8¿ù", + "9¿ù", + "10¿ù", + "11¿ù", + "12¿ù"); + +// short month names +Calendar._SMN = new Array +("1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "calendar ¿¡ ´ëÇØ¼­"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"\n"+ +"ÃֽйöÀüÀ» ¹ÞÀ¸½Ã·Á¸é http://www.dynarch.com/projects/calendar/ ¿¡ ¹æ¹®Çϼ¼¿ä\n" + +"\n"+ +"GNU LGPL ¶óÀ̼¾½º·Î ¹èÆ÷µË´Ï´Ù. \n"+ +"¶óÀ̼¾½º¿¡ ´ëÇÑ ÀÚ¼¼ÇÑ ³»¿ëÀº http://gnu.org/licenses/lgpl.html À» ÀÐÀ¸¼¼¿ä." + +"\n\n" + +"³¯Â¥ ¼±ÅÃ:\n" + +"- ¿¬µµ¸¦ ¼±ÅÃÇÏ·Á¸é \xab, \xbb ¹öưÀ» »ç¿ëÇÕ´Ï´Ù\n" + +"- ´ÞÀ» ¼±ÅÃÇÏ·Á¸é " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " ¹öưÀ» ´©¸£¼¼¿ä\n" + +"- °è¼Ó ´©¸£°í ÀÖÀ¸¸é À§ °ªµéÀ» ºü¸£°Ô ¼±ÅÃÇÏ½Ç ¼ö ÀÖ½À´Ï´Ù."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"½Ã°£ ¼±ÅÃ:\n" + +"- ¸¶¿ì½º·Î ´©¸£¸é ½Ã°£ÀÌ Áõ°¡ÇÕ´Ï´Ù\n" + +"- Shift Ű¿Í ÇÔ²² ´©¸£¸é °¨¼ÒÇÕ´Ï´Ù\n" + +"- ´©¸¥ »óÅ¿¡¼­ ¸¶¿ì½º¸¦ ¿òÁ÷À̸é Á» ´õ ºü¸£°Ô °ªÀÌ º¯ÇÕ´Ï´Ù.\n"; + +Calendar._TT["PREV_YEAR"] = "Áö³­ ÇØ (±æ°Ô ´©¸£¸é ¸ñ·Ï)"; +Calendar._TT["PREV_MONTH"] = "Áö³­ ´Þ (±æ°Ô ´©¸£¸é ¸ñ·Ï)"; +Calendar._TT["GO_TODAY"] = "¿À´Ã ³¯Â¥·Î"; +Calendar._TT["NEXT_MONTH"] = "´ÙÀ½ ´Þ (±æ°Ô ´©¸£¸é ¸ñ·Ï)"; +Calendar._TT["NEXT_YEAR"] = "´ÙÀ½ ÇØ (±æ°Ô ´©¸£¸é ¸ñ·Ï)"; +Calendar._TT["SEL_DATE"] = "³¯Â¥¸¦ ¼±ÅÃÇϼ¼¿ä"; +Calendar._TT["DRAG_TO_MOVE"] = "¸¶¿ì½º µå·¡±×·Î À̵¿ Çϼ¼¿ä"; +Calendar._TT["PART_TODAY"] = " (¿À´Ã)"; +Calendar._TT["MON_FIRST"] = "¿ù¿äÀÏÀ» ÇÑ ÁÖÀÇ ½ÃÀÛ ¿äÀÏ·Î"; +Calendar._TT["SUN_FIRST"] = "ÀÏ¿äÀÏÀ» ÇÑ ÁÖÀÇ ½ÃÀÛ ¿äÀÏ·Î"; +Calendar._TT["CLOSE"] = "´Ý±â"; +Calendar._TT["TODAY"] = "¿À´Ã"; +Calendar._TT["TIME_PART"] = "(Shift-)Ŭ¸¯ ¶Ç´Â µå·¡±× Çϼ¼¿ä"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%b/%e [%a]"; + +Calendar._TT["WK"] = "ÁÖ"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-lt-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-lt-utf8.js new file mode 100644 index 00000000..964865de --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-lt-utf8.js @@ -0,0 +1,114 @@ +// ** I18N + +// Calendar LT language +// Author: Martynas Majeris, +// Encoding: UTF-8 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sekmadienis", + "Pirmadienis", + "Antradienis", + "TreÄiadienis", + "Ketvirtadienis", + "Pentadienis", + "Å eÅ¡tadienis", + "Sekmadienis"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sek", + "Pir", + "Ant", + "Tre", + "Ket", + "Pen", + "Å eÅ¡", + "Sek"); + +// full month names +Calendar._MN = new Array +("Sausis", + "Vasaris", + "Kovas", + "Balandis", + "Gegužė", + "Birželis", + "Liepa", + "RugpjÅ«tis", + "RugsÄ—jis", + "Spalis", + "Lapkritis", + "Gruodis"); + +// short month names +Calendar._SMN = new Array +("Sau", + "Vas", + "Kov", + "Bal", + "Geg", + "Bir", + "Lie", + "Rgp", + "Rgs", + "Spa", + "Lap", + "Gru"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Apie kalendorių"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"NaujausiÄ… versijÄ… rasite: http://www.dynarch.com/projects/calendar/\n" + +"Platinamas pagal GNU LGPL licencijÄ…. Aplankykite http://gnu.org/licenses/lgpl.html" + +"\n\n" + +"Datos pasirinkimas:\n" + +"- Metų pasirinkimas: \xab, \xbb\n" + +"- MÄ—nesio pasirinkimas: " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + "\n" + +"- Nuspauskite ir laikykite pelÄ—s klavišą greitesniam pasirinkimui."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Laiko pasirinkimas:\n" + +"- Spustelkite ant valandų arba minuÄių - skaiÄius padidÄ—s vienetu.\n" + +"- Jei spausite kartu su Shift, skaiÄius sumažės.\n" + +"- Greitam pasirinkimui spustelkite ir pajudinkite pelÄ™."; + +Calendar._TT["PREV_YEAR"] = "Ankstesni metai (laikykite, jei norite meniu)"; +Calendar._TT["PREV_MONTH"] = "Ankstesnis mÄ—nuo (laikykite, jei norite meniu)"; +Calendar._TT["GO_TODAY"] = "Pasirinkti Å¡iandienÄ…"; +Calendar._TT["NEXT_MONTH"] = "Kitas mÄ—nuo (laikykite, jei norite meniu)"; +Calendar._TT["NEXT_YEAR"] = "Kiti metai (laikykite, jei norite meniu)"; +Calendar._TT["SEL_DATE"] = "Pasirinkite datÄ…"; +Calendar._TT["DRAG_TO_MOVE"] = "Tempkite"; +Calendar._TT["PART_TODAY"] = " (Å¡iandien)"; +Calendar._TT["MON_FIRST"] = "Pirma savaitÄ—s diena - pirmadienis"; +Calendar._TT["SUN_FIRST"] = "Pirma savaitÄ—s diena - sekmadienis"; +Calendar._TT["CLOSE"] = "Uždaryti"; +Calendar._TT["TODAY"] = "Å iandien"; +Calendar._TT["TIME_PART"] = "Spustelkite arba tempkite jei norite pakeisti"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %Y-%m-%d"; + +Calendar._TT["WK"] = "sav"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-lt.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-lt.js new file mode 100644 index 00000000..059cdf4f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-lt.js @@ -0,0 +1,114 @@ +// ** I18N + +// Calendar LT language +// Author: Martynas Majeris, +// Encoding: Windows-1257 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sekmadienis", + "Pirmadienis", + "Antradienis", + "Treèiadienis", + "Ketvirtadienis", + "Pentadienis", + "Ðeðtadienis", + "Sekmadienis"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sek", + "Pir", + "Ant", + "Tre", + "Ket", + "Pen", + "Ðeð", + "Sek"); + +// full month names +Calendar._MN = new Array +("Sausis", + "Vasaris", + "Kovas", + "Balandis", + "Geguþë", + "Birþelis", + "Liepa", + "Rugpjûtis", + "Rugsëjis", + "Spalis", + "Lapkritis", + "Gruodis"); + +// short month names +Calendar._SMN = new Array +("Sau", + "Vas", + "Kov", + "Bal", + "Geg", + "Bir", + "Lie", + "Rgp", + "Rgs", + "Spa", + "Lap", + "Gru"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Apie kalendoriø"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Naujausià versijà rasite: http://www.dynarch.com/projects/calendar/\n" + +"Platinamas pagal GNU LGPL licencijà. Aplankykite http://gnu.org/licenses/lgpl.html" + +"\n\n" + +"Datos pasirinkimas:\n" + +"- Metø pasirinkimas: \xab, \xbb\n" + +"- Mënesio pasirinkimas: " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + "\n" + +"- Nuspauskite ir laikykite pelës klaviðà greitesniam pasirinkimui."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Laiko pasirinkimas:\n" + +"- Spustelkite ant valandø arba minuèiø - skaièus padidës vienetu.\n" + +"- Jei spausite kartu su Shift, skaièius sumaþës.\n" + +"- Greitam pasirinkimui spustelkite ir pajudinkite pelæ."; + +Calendar._TT["PREV_YEAR"] = "Ankstesni metai (laikykite, jei norite meniu)"; +Calendar._TT["PREV_MONTH"] = "Ankstesnis mënuo (laikykite, jei norite meniu)"; +Calendar._TT["GO_TODAY"] = "Pasirinkti ðiandienà"; +Calendar._TT["NEXT_MONTH"] = "Kitas mënuo (laikykite, jei norite meniu)"; +Calendar._TT["NEXT_YEAR"] = "Kiti metai (laikykite, jei norite meniu)"; +Calendar._TT["SEL_DATE"] = "Pasirinkite datà"; +Calendar._TT["DRAG_TO_MOVE"] = "Tempkite"; +Calendar._TT["PART_TODAY"] = " (ðiandien)"; +Calendar._TT["MON_FIRST"] = "Pirma savaitës diena - pirmadienis"; +Calendar._TT["SUN_FIRST"] = "Pirma savaitës diena - sekmadienis"; +Calendar._TT["CLOSE"] = "Uþdaryti"; +Calendar._TT["TODAY"] = "Ðiandien"; +Calendar._TT["TIME_PART"] = "Spustelkite arba tempkite jei norite pakeisti"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %Y-%m-%d"; + +Calendar._TT["WK"] = "sav"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-lv.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-lv.js new file mode 100644 index 00000000..4cb5a138 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-lv.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar LV language +// Author: Juris Valdovskis, +// Encoding: cp1257 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Svçtdiena", + "Pirmdiena", + "Otrdiena", + "Treðdiena", + "Ceturdiena", + "Piektdiena", + "Sestdiena", + "Svçtdiena"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sv", + "Pr", + "Ot", + "Tr", + "Ce", + "Pk", + "Se", + "Sv"); + +// full month names +Calendar._MN = new Array +("Janvâris", + "Februâris", + "Marts", + "Aprîlis", + "Maijs", + "Jûnijs", + "Jûlijs", + "Augusts", + "Septembris", + "Oktobris", + "Novembris", + "Decembris"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "Mai", + "Jûn", + "Jûl", + "Aug", + "Sep", + "Okt", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Par kalendâru"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Datuma izvçle:\n" + +"- Izmanto \xab, \xbb pogas, lai izvçlçtos gadu\n" + +"- Izmanto " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + "pogas, lai izvçlçtos mçnesi\n" + +"- Turi nospiestu peles pogu uz jebkuru no augstâk minçtajâm pogâm, lai paâtrinâtu izvçli."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Laika izvçle:\n" + +"- Uzklikðíini uz jebkuru no laika daïâm, lai palielinâtu to\n" + +"- vai Shift-klikðíis, lai samazinâtu to\n" + +"- vai noklikðíini un velc uz attiecîgo virzienu lai mainîtu âtrâk."; + +Calendar._TT["PREV_YEAR"] = "Iepr. gads (turi izvçlnei)"; +Calendar._TT["PREV_MONTH"] = "Iepr. mçnesis (turi izvçlnei)"; +Calendar._TT["GO_TODAY"] = "Ðodien"; +Calendar._TT["NEXT_MONTH"] = "Nâkoðais mçnesis (turi izvçlnei)"; +Calendar._TT["NEXT_YEAR"] = "Nâkoðais gads (turi izvçlnei)"; +Calendar._TT["SEL_DATE"] = "Izvçlies datumu"; +Calendar._TT["DRAG_TO_MOVE"] = "Velc, lai pârvietotu"; +Calendar._TT["PART_TODAY"] = " (ðodien)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Attçlot %s kâ pirmo"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "1,7"; + +Calendar._TT["CLOSE"] = "Aizvçrt"; +Calendar._TT["TODAY"] = "Ðodien"; +Calendar._TT["TIME_PART"] = "(Shift-)Klikðíis vai pârvieto, lai mainîtu"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %e %b"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Laiks:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-nl.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-nl.js new file mode 100644 index 00000000..9915f6a7 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-nl.js @@ -0,0 +1,73 @@ +// ** I18N +Calendar._DN = new Array +("Zondag", + "Maandag", + "Dinsdag", + "Woensdag", + "Donderdag", + "Vrijdag", + "Zaterdag", + "Zondag"); + +Calendar._SDN_len = 2; + +Calendar._MN = new Array +("Januari", + "Februari", + "Maart", + "April", + "Mei", + "Juni", + "Juli", + "Augustus", + "September", + "Oktober", + "November", + "December"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Info"; + +Calendar._TT["ABOUT"] = +"DHTML Datum/Tijd Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + +"Ga voor de meest recente versie naar: http://www.dynarch.com/projects/calendar/\n" + +"Verspreid onder de GNU LGPL. Zie http://gnu.org/licenses/lgpl.html voor details." + +"\n\n" + +"Datum selectie:\n" + +"- Gebruik de \xab \xbb knoppen om een jaar te selecteren\n" + +"- Gebruik de " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " knoppen om een maand te selecteren\n" + +"- Houd de muis ingedrukt op de genoemde knoppen voor een snellere selectie."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Tijd selectie:\n" + +"- Klik op een willekeurig onderdeel van het tijd gedeelte om het te verhogen\n" + +"- of Shift-klik om het te verlagen\n" + +"- of klik en sleep voor een snellere selectie."; + +//Calendar._TT["TOGGLE"] = "Selecteer de eerste week-dag"; +Calendar._TT["PREV_YEAR"] = "Vorig jaar (ingedrukt voor menu)"; +Calendar._TT["PREV_MONTH"] = "Vorige maand (ingedrukt voor menu)"; +Calendar._TT["GO_TODAY"] = "Ga naar Vandaag"; +Calendar._TT["NEXT_MONTH"] = "Volgende maand (ingedrukt voor menu)"; +Calendar._TT["NEXT_YEAR"] = "Volgend jaar (ingedrukt voor menu)"; +Calendar._TT["SEL_DATE"] = "Selecteer datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Klik en sleep om te verplaatsen"; +Calendar._TT["PART_TODAY"] = " (vandaag)"; +//Calendar._TT["MON_FIRST"] = "Toon Maandag eerst"; +//Calendar._TT["SUN_FIRST"] = "Toon Zondag eerst"; + +Calendar._TT["DAY_FIRST"] = "Toon %s eerst"; + +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Sluiten"; +Calendar._TT["TODAY"] = "(vandaag)"; +Calendar._TT["TIME_PART"] = "(Shift-)Klik of sleep om de waarde te veranderen"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %e %b %Y"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Tijd:"; \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-no.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-no.js new file mode 100644 index 00000000..0bb97d73 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-no.js @@ -0,0 +1,114 @@ +// ** I18N + +// Calendar NO language +// Author: Daniel Holmen, +// Encoding: UTF-8 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Søndag", + "Mandag", + "Tirsdag", + "Onsdag", + "Torsdag", + "Fredag", + "Lørdag", + "Søndag"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Søn", + "Man", + "Tir", + "Ons", + "Tor", + "Fre", + "Lør", + "Søn"); + +// full month names +Calendar._MN = new Array +("Januar", + "Februar", + "Mars", + "April", + "Mai", + "Juni", + "Juli", + "August", + "September", + "Oktober", + "November", + "Desember"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "Mai", + "Jun", + "Jul", + "Aug", + "Sep", + "Okt", + "Nov", + "Des"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Om kalenderen"; + +Calendar._TT["ABOUT"] = +"DHTML Dato-/Tidsvelger\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For nyeste versjon, gÃ¥ til: http://www.dynarch.com/projects/calendar/\n" + +"Distribuert under GNU LGPL. Se http://gnu.org/licenses/lgpl.html for detaljer." + +"\n\n" + +"Datovalg:\n" + +"- Bruk knappene \xab og \xbb for Ã¥ velge Ã¥r\n" + +"- Bruk knappene " + String.fromCharCode(0x2039) + " og " + String.fromCharCode(0x203a) + " for Ã¥ velge mÃ¥ned\n" + +"- Hold inne musknappen eller knappene over for raskere valg."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Tidsvalg:\n" + +"- Klikk pÃ¥ en av tidsdelene for Ã¥ øke den\n" + +"- eller Shift-klikk for Ã¥ senke verdien\n" + +"- eller klikk-og-dra for raskere valg.."; + +Calendar._TT["PREV_YEAR"] = "Forrige. Ã¥r (hold for meny)"; +Calendar._TT["PREV_MONTH"] = "Forrige. mÃ¥ned (hold for meny)"; +Calendar._TT["GO_TODAY"] = "GÃ¥ til idag"; +Calendar._TT["NEXT_MONTH"] = "Neste mÃ¥ned (hold for meny)"; +Calendar._TT["NEXT_YEAR"] = "Neste Ã¥r (hold for meny)"; +Calendar._TT["SEL_DATE"] = "Velg dato"; +Calendar._TT["DRAG_TO_MOVE"] = "Dra for Ã¥ flytte"; +Calendar._TT["PART_TODAY"] = " (idag)"; +Calendar._TT["MON_FIRST"] = "Vis mandag først"; +Calendar._TT["SUN_FIRST"] = "Vis søndag først"; +Calendar._TT["CLOSE"] = "Lukk"; +Calendar._TT["TODAY"] = "Idag"; +Calendar._TT["TIME_PART"] = "(Shift-)Klikk eller dra for Ã¥ endre verdi"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d.%m.%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "uke"; \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-pl-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-pl-utf8.js new file mode 100644 index 00000000..b438a175 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-pl-utf8.js @@ -0,0 +1,93 @@ +// ** I18N + +// Calendar PL language +// Author: Dariusz Pietrzak, +// Author: Janusz Piwowarski, +// Encoding: utf-8 +// Distributed under the same terms as the calendar itself. + +Calendar._DN = new Array +("Niedziela", + "PoniedziaÅ‚ek", + "Wtorek", + "Åšroda", + "Czwartek", + "PiÄ…tek", + "Sobota", + "Niedziela"); +Calendar._SDN = new Array +("Nie", + "Pn", + "Wt", + "Åšr", + "Cz", + "Pt", + "So", + "Nie"); +Calendar._MN = new Array +("StyczeÅ„", + "Luty", + "Marzec", + "KwiecieÅ„", + "Maj", + "Czerwiec", + "Lipiec", + "SierpieÅ„", + "WrzesieÅ„", + "Październik", + "Listopad", + "GrudzieÅ„"); +Calendar._SMN = new Array +("Sty", + "Lut", + "Mar", + "Kwi", + "Maj", + "Cze", + "Lip", + "Sie", + "Wrz", + "Paź", + "Lis", + "Gru"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O kalendarzu"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Aby pobrać najnowszÄ… wersjÄ™, odwiedź: http://www.dynarch.com/projects/calendar/\n" + +"DostÄ™pny na licencji GNU LGPL. Zobacz szczegóły na http://gnu.org/licenses/lgpl.html." + +"\n\n" + +"Wybór daty:\n" + +"- Użyj przycisków \xab, \xbb by wybrać rok\n" + +"- Użyj przycisków " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " by wybrać miesiÄ…c\n" + +"- Przytrzymaj klawisz myszy nad jednym z powyższych przycisków dla szybszego wyboru."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Wybór czasu:\n" + +"- Kliknij na jednym z pól czasu by zwiÄ™kszyć jego wartość\n" + +"- lub kliknij trzymajÄ…c Shift by zmiejszyć jego wartość\n" + +"- lub kliknij i przeciÄ…gnij dla szybszego wyboru."; + +//Calendar._TT["TOGGLE"] = "ZmieÅ„ pierwszy dzieÅ„ tygodnia"; +Calendar._TT["PREV_YEAR"] = "Poprzedni rok (przytrzymaj dla menu)"; +Calendar._TT["PREV_MONTH"] = "Poprzedni miesiÄ…c (przytrzymaj dla menu)"; +Calendar._TT["GO_TODAY"] = "Idź do dzisiaj"; +Calendar._TT["NEXT_MONTH"] = "NastÄ™pny miesiÄ…c (przytrzymaj dla menu)"; +Calendar._TT["NEXT_YEAR"] = "NastÄ™pny rok (przytrzymaj dla menu)"; +Calendar._TT["SEL_DATE"] = "Wybierz datÄ™"; +Calendar._TT["DRAG_TO_MOVE"] = "PrzeciÄ…gnij by przesunąć"; +Calendar._TT["PART_TODAY"] = " (dzisiaj)"; +Calendar._TT["MON_FIRST"] = "WyÅ›wietl poniedziaÅ‚ek jako pierwszy"; +Calendar._TT["SUN_FIRST"] = "WyÅ›wietl niedzielÄ™ jako pierwszÄ…"; +Calendar._TT["CLOSE"] = "Zamknij"; +Calendar._TT["TODAY"] = "Dzisiaj"; +Calendar._TT["TIME_PART"] = "(Shift-)Kliknij lub przeciÄ…gnij by zmienić wartość"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%e %B, %A"; + +Calendar._TT["WK"] = "ty"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-pl.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-pl.js new file mode 100644 index 00000000..86c7b39e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-pl.js @@ -0,0 +1,56 @@ +// ** I18N +// Calendar PL language +// Author: Artur Filipiak, +// January, 2004 +// Encoding: UTF-8 +Calendar._DN = new Array +("Niedziela", "PoniedziaÅ‚ek", "Wtorek", "Åšroda", "Czwartek", "PiÄ…tek", "Sobota", "Niedziela"); + +Calendar._SDN = new Array +("N", "Pn", "Wt", "Åšr", "Cz", "Pt", "So", "N"); + +Calendar._MN = new Array +("StyczeÅ„", "Luty", "Marzec", "KwiecieÅ„", "Maj", "Czerwiec", "Lipiec", "SierpieÅ„", "WrzesieÅ„", "Październik", "Listopad", "GrudzieÅ„"); + +Calendar._SMN = new Array +("Sty", "Lut", "Mar", "Kwi", "Maj", "Cze", "Lip", "Sie", "Wrz", "Paź", "Lis", "Gru"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O kalendarzu"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Wybór daty:\n" + +"- aby wybrać rok użyj przycisków \xab, \xbb\n" + +"- aby wybrać miesiÄ…c użyj przycisków " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + "\n" + +"- aby przyspieszyć wybór przytrzymaj wciÅ›niÄ™ty przycisk myszy nad ww. przyciskami."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Wybór czasu:\n" + +"- aby zwiÄ™kszyć wartość kliknij na dowolnym elemencie selekcji czasu\n" + +"- aby zmniejszyć wartość użyj dodatkowo klawisza Shift\n" + +"- możesz również poruszać myszkÄ™ w lewo i prawo wraz z wciÅ›niÄ™tym lewym klawiszem."; + +Calendar._TT["PREV_YEAR"] = "Poprz. rok (przytrzymaj dla menu)"; +Calendar._TT["PREV_MONTH"] = "Poprz. miesiÄ…c (przytrzymaj dla menu)"; +Calendar._TT["GO_TODAY"] = "Pokaż dziÅ›"; +Calendar._TT["NEXT_MONTH"] = "Nast. miesiÄ…c (przytrzymaj dla menu)"; +Calendar._TT["NEXT_YEAR"] = "Nast. rok (przytrzymaj dla menu)"; +Calendar._TT["SEL_DATE"] = "Wybierz datÄ™"; +Calendar._TT["DRAG_TO_MOVE"] = "PrzesuÅ„ okienko"; +Calendar._TT["PART_TODAY"] = " (dziÅ›)"; +Calendar._TT["MON_FIRST"] = "Pokaż PoniedziaÅ‚ek jako pierwszy"; +Calendar._TT["SUN_FIRST"] = "Pokaż NiedzielÄ™ jako pierwszÄ…"; +Calendar._TT["CLOSE"] = "Zamknij"; +Calendar._TT["TODAY"] = "DziÅ›"; +Calendar._TT["TIME_PART"] = "(Shift-)klik | drag, aby zmienić wartość"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y.%m.%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-pt.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-pt.js new file mode 100644 index 00000000..f7dfbfff --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-pt.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar pt_BR language +// Author: Adalberto Machado, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Domingo", + "Segunda", + "Terca", + "Quarta", + "Quinta", + "Sexta", + "Sabado", + "Domingo"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Dom", + "Seg", + "Ter", + "Qua", + "Qui", + "Sex", + "Sab", + "Dom"); + +// full month names +Calendar._MN = new Array +("Janeiro", + "Fevereiro", + "Marco", + "Abril", + "Maio", + "Junho", + "Julho", + "Agosto", + "Setembro", + "Outubro", + "Novembro", + "Dezembro"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Fev", + "Mar", + "Abr", + "Mai", + "Jun", + "Jul", + "Ago", + "Set", + "Out", + "Nov", + "Dez"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Sobre o calendario"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Ultima versao visite: http://www.dynarch.com/projects/calendar/\n" + +"Distribuido sobre GNU LGPL. Veja http://gnu.org/licenses/lgpl.html para detalhes." + +"\n\n" + +"Selecao de data:\n" + +"- Use os botoes \xab, \xbb para selecionar o ano\n" + +"- Use os botoes " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " para selecionar o mes\n" + +"- Segure o botao do mouse em qualquer um desses botoes para selecao rapida."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selecao de hora:\n" + +"- Clique em qualquer parte da hora para incrementar\n" + +"- ou Shift-click para decrementar\n" + +"- ou clique e segure para selecao rapida."; + +Calendar._TT["PREV_YEAR"] = "Ant. ano (segure para menu)"; +Calendar._TT["PREV_MONTH"] = "Ant. mes (segure para menu)"; +Calendar._TT["GO_TODAY"] = "Hoje"; +Calendar._TT["NEXT_MONTH"] = "Prox. mes (segure para menu)"; +Calendar._TT["NEXT_YEAR"] = "Prox. ano (segure para menu)"; +Calendar._TT["SEL_DATE"] = "Selecione a data"; +Calendar._TT["DRAG_TO_MOVE"] = "Arraste para mover"; +Calendar._TT["PART_TODAY"] = " (hoje)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Mostre %s primeiro"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Fechar"; +Calendar._TT["TODAY"] = "Hoje"; +Calendar._TT["TIME_PART"] = "(Shift-)Click ou arraste para mudar valor"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %e %b"; + +Calendar._TT["WK"] = "sm"; +Calendar._TT["TIME"] = "Hora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ro.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ro.js new file mode 100644 index 00000000..6c45eab2 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ro.js @@ -0,0 +1,66 @@ +// ** I18N +Calendar._DN = new Array +("Duminică", + "Luni", + "MarÅ£i", + "Miercuri", + "Joi", + "Vineri", + "Sâmbătă", + "Duminică"); +Calendar._SDN_len = 2; +Calendar._MN = new Array +("Ianuarie", + "Februarie", + "Martie", + "Aprilie", + "Mai", + "Iunie", + "Iulie", + "August", + "Septembrie", + "Octombrie", + "Noiembrie", + "Decembrie"); + +// tooltips +Calendar._TT = {}; + +Calendar._TT["INFO"] = "Despre calendar"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Pentru ultima versiune vizitaÅ£i: http://www.dynarch.com/projects/calendar/\n" + +"Distribuit sub GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"SelecÅ£ia datei:\n" + +"- FolosiÅ£i butoanele \xab, \xbb pentru a selecta anul\n" + +"- FolosiÅ£i butoanele " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " pentru a selecta luna\n" + +"- TineÅ£i butonul mouse-ului apăsat pentru selecÅ£ie mai rapidă."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"SelecÅ£ia orei:\n" + +"- Click pe ora sau minut pentru a mări valoarea cu 1\n" + +"- Sau Shift-Click pentru a micÅŸora valoarea cu 1\n" + +"- Sau Click ÅŸi drag pentru a selecta mai repede."; + +Calendar._TT["PREV_YEAR"] = "Anul precedent (lung pt menu)"; +Calendar._TT["PREV_MONTH"] = "Luna precedentă (lung pt menu)"; +Calendar._TT["GO_TODAY"] = "Data de azi"; +Calendar._TT["NEXT_MONTH"] = "Luna următoare (lung pt menu)"; +Calendar._TT["NEXT_YEAR"] = "Anul următor (lung pt menu)"; +Calendar._TT["SEL_DATE"] = "Selectează data"; +Calendar._TT["DRAG_TO_MOVE"] = "Trage pentru a miÅŸca"; +Calendar._TT["PART_TODAY"] = " (astăzi)"; +Calendar._TT["DAY_FIRST"] = "AfiÅŸează %s prima zi"; +Calendar._TT["WEEKEND"] = "0,6"; +Calendar._TT["CLOSE"] = "ÃŽnchide"; +Calendar._TT["TODAY"] = "Astăzi"; +Calendar._TT["TIME_PART"] = "(Shift-)Click sau drag pentru a selecta"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %d %B"; + +Calendar._TT["WK"] = "spt"; +Calendar._TT["TIME"] = "Ora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ru.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ru.js new file mode 100644 index 00000000..4e23e386 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ru.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar RU language +// Translation: Sly Golovanov, http://golovanov.net, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("воÑкреÑенье", + "понедельник", + "вторник", + "Ñреда", + "четверг", + "пÑтница", + "Ñуббота", + "воÑкреÑенье"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("вÑк", + "пон", + "втр", + "Ñрд", + "чет", + "пÑÑ‚", + "Ñуб", + "вÑк"); + +// full month names +Calendar._MN = new Array +("Ñнварь", + "февраль", + "март", + "апрель", + "май", + "июнь", + "июль", + "авгуÑÑ‚", + "ÑентÑбрь", + "октÑбрь", + "ноÑбрь", + "декабрь"); + +// short month names +Calendar._SMN = new Array +("Ñнв", + "фев", + "мар", + "апр", + "май", + "июн", + "июл", + "авг", + "Ñен", + "окт", + "ноÑ", + "дек"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "О календаре..."; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Как выбрать дату:\n" + +"- При помощи кнопок \xab, \xbb можно выбрать год\n" + +"- При помощи кнопок " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " можно выбрать меÑÑц\n" + +"- Подержите Ñти кнопки нажатыми, чтобы поÑвилоÑÑŒ меню быÑтрого выбора."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Как выбрать времÑ:\n" + +"- При клике на чаÑах или минутах они увеличиваютÑÑ\n" + +"- при клике Ñ Ð½Ð°Ð¶Ð°Ñ‚Ð¾Ð¹ клавишей Shift они уменьшаютÑÑ\n" + +"- еÑли нажать и двигать мышкой влево/вправо, они будут менÑтьÑÑ Ð±Ñ‹Ñтрее."; + +Calendar._TT["PREV_YEAR"] = "Ðа год назад (удерживать Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ)"; +Calendar._TT["PREV_MONTH"] = "Ðа меÑÑц назад (удерживать Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ)"; +Calendar._TT["GO_TODAY"] = "СегоднÑ"; +Calendar._TT["NEXT_MONTH"] = "Ðа меÑÑц вперед (удерживать Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ)"; +Calendar._TT["NEXT_YEAR"] = "Ðа год вперед (удерживать Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ)"; +Calendar._TT["SEL_DATE"] = "Выберите дату"; +Calendar._TT["DRAG_TO_MOVE"] = "ПеретаÑкивайте мышкой"; +Calendar._TT["PART_TODAY"] = " (ÑегоднÑ)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Первый день недели будет %s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Закрыть"; +Calendar._TT["TODAY"] = "СегоднÑ"; +Calendar._TT["TIME_PART"] = "(Shift-)клик или нажать и двигать"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%e %b, %a"; + +Calendar._TT["WK"] = "нед"; +Calendar._TT["TIME"] = "ВремÑ:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ru_win_.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ru_win_.js new file mode 100644 index 00000000..4d8501d7 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-ru_win_.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar RU language +// Translation: Sly Golovanov, http://golovanov.net, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("âîñêðåñåíüå", + "ïîíåäåëüíèê", + "âòîðíèê", + "ñðåäà", + "÷åòâåðã", + "ïÿòíèöà", + "ñóááîòà", + "âîñêðåñåíüå"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("âñê", + "ïîí", + "âòð", + "ñðä", + "÷åò", + "ïÿò", + "ñóá", + "âñê"); + +// full month names +Calendar._MN = new Array +("ÿíâàðü", + "ôåâðàëü", + "ìàðò", + "àïðåëü", + "ìàé", + "èþíü", + "èþëü", + "àâãóñò", + "ñåíòÿáðü", + "îêòÿáðü", + "íîÿáðü", + "äåêàáðü"); + +// short month names +Calendar._SMN = new Array +("ÿíâ", + "ôåâ", + "ìàð", + "àïð", + "ìàé", + "èþí", + "èþë", + "àâã", + "ñåí", + "îêò", + "íîÿ", + "äåê"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Î êàëåíäàðå..."; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Êàê âûáðàòü äàòó:\n" + +"- Ïðè ïîìîùè êíîïîê \xab, \xbb ìîæíî âûáðàòü ãîä\n" + +"- Ïðè ïîìîùè êíîïîê " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " ìîæíî âûáðàòü ìåñÿö\n" + +"- Ïîäåðæèòå ýòè êíîïêè íàæàòûìè, ÷òîáû ïîÿâèëîñü ìåíþ áûñòðîãî âûáîðà."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Êàê âûáðàòü âðåìÿ:\n" + +"- Ïðè êëèêå íà ÷àñàõ èëè ìèíóòàõ îíè óâåëè÷èâàþòñÿ\n" + +"- ïðè êëèêå ñ íàæàòîé êëàâèøåé Shift îíè óìåíüøàþòñÿ\n" + +"- åñëè íàæàòü è äâèãàòü ìûøêîé âëåâî/âïðàâî, îíè áóäóò ìåíÿòüñÿ áûñòðåå."; + +Calendar._TT["PREV_YEAR"] = "Íà ãîä íàçàä (óäåðæèâàòü äëÿ ìåíþ)"; +Calendar._TT["PREV_MONTH"] = "Íà ìåñÿö íàçàä (óäåðæèâàòü äëÿ ìåíþ)"; +Calendar._TT["GO_TODAY"] = "Ñåãîäíÿ"; +Calendar._TT["NEXT_MONTH"] = "Íà ìåñÿö âïåðåä (óäåðæèâàòü äëÿ ìåíþ)"; +Calendar._TT["NEXT_YEAR"] = "Íà ãîä âïåðåä (óäåðæèâàòü äëÿ ìåíþ)"; +Calendar._TT["SEL_DATE"] = "Âûáåðèòå äàòó"; +Calendar._TT["DRAG_TO_MOVE"] = "Ïåðåòàñêèâàéòå ìûøêîé"; +Calendar._TT["PART_TODAY"] = " (ñåãîäíÿ)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Ïåðâûé äåíü íåäåëè áóäåò %s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Çàêðûòü"; +Calendar._TT["TODAY"] = "Ñåãîäíÿ"; +Calendar._TT["TIME_PART"] = "(Shift-)êëèê èëè íàæàòü è äâèãàòü"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%e %b, %a"; + +Calendar._TT["WK"] = "íåä"; +Calendar._TT["TIME"] = "Âðåìÿ:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-si.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-si.js new file mode 100644 index 00000000..cb3dfb9f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-si.js @@ -0,0 +1,94 @@ +/* Slovenian language file for the DHTML Calendar version 0.9.2 +* Author David Milost , January 2004. +* Feel free to use this script under the terms of the GNU Lesser General +* Public License, as long as you do not remove or alter this notice. +*/ + // full day names +Calendar._DN = new Array +("Nedelja", + "Ponedeljek", + "Torek", + "Sreda", + "ÄŒetrtek", + "Petek", + "Sobota", + "Nedelja"); + // short day names + Calendar._SDN = new Array +("Ned", + "Pon", + "Tor", + "Sre", + "ÄŒet", + "Pet", + "Sob", + "Ned"); +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "Maj", + "Jun", + "Jul", + "Avg", + "Sep", + "Okt", + "Nov", + "Dec"); + // full month names +Calendar._MN = new Array +("Januar", + "Februar", + "Marec", + "April", + "Maj", + "Junij", + "Julij", + "Avgust", + "September", + "Oktober", + "November", + "December"); + +// tooltips +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O koledarju"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Za zadnjo verzijo pojdine na naslov: http://www.dynarch.com/projects/calendar/\n" + +"Distribuirano pod GNU LGPL. Poglejte http://gnu.org/licenses/lgpl.html za podrobnosti." + +"\n\n" + +"Izbor datuma:\n" + +"- Uporabite \xab, \xbb gumbe za izbor leta\n" + +"- Uporabite " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " gumbe za izbor meseca\n" + +"- Zadržite klik na kateremkoli od zgornjih gumbov za hiter izbor."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Izbor ćasa:\n" + +"- Kliknite na katerikoli del ćasa za poveć. le-tega\n" + +"- ali Shift-click za zmanj. le-tega\n" + +"- ali kliknite in povlecite za hiter izbor."; + +Calendar._TT["TOGGLE"] = "Spremeni dan s katerim se prićne teden"; +Calendar._TT["PREV_YEAR"] = "Predhodnje leto (dolg klik za meni)"; +Calendar._TT["PREV_MONTH"] = "Predhodnji mesec (dolg klik za meni)"; +Calendar._TT["GO_TODAY"] = "Pojdi na tekoći dan"; +Calendar._TT["NEXT_MONTH"] = "Naslednji mesec (dolg klik za meni)"; +Calendar._TT["NEXT_YEAR"] = "Naslednje leto (dolg klik za meni)"; +Calendar._TT["SEL_DATE"] = "Izberite datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Pritisni in povleci za spremembo pozicije"; +Calendar._TT["PART_TODAY"] = " (danes)"; +Calendar._TT["MON_FIRST"] = "Prikaži ponedeljek kot prvi dan"; +Calendar._TT["SUN_FIRST"] = "Prikaži nedeljo kot prvi dan"; +Calendar._TT["CLOSE"] = "Zapri"; +Calendar._TT["TODAY"] = "Danes"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "Ted"; \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sk.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sk.js new file mode 100644 index 00000000..2ecfc3c4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sk.js @@ -0,0 +1,99 @@ +// ** I18N + +// Calendar SK language +// Author: Peter Valach (pvalach@gmx.net) +// Encoding: utf-8 +// Last update: 2003/10/29 +// Distributed under the same terms as the calendar itself. + +// full day names +Calendar._DN = new Array +("NedeÄľa", + "Pondelok", + "Utorok", + "Streda", + "Ĺ tvrtok", + "Piatok", + "Sobota", + "NedeÄľa"); + +// short day names +Calendar._SDN = new Array +("Ned", + "Pon", + "Uto", + "Str", + "Ĺ tv", + "Pia", + "Sob", + "Ned"); + +// full month names +Calendar._MN = new Array +("Január", + "Február", + "Marec", + "AprĂ­l", + "Máj", + "JĂşn", + "JĂşl", + "August", + "September", + "OktĂłber", + "November", + "December"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "Máj", + "JĂşn", + "JĂşl", + "Aug", + "Sep", + "Okt", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O kalendári"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + +"PoslednĂş verziu nájdete na: http://www.dynarch.com/projects/calendar/\n" + +"DistribuovanĂ© pod GNU LGPL. ViÄŹ http://gnu.org/licenses/lgpl.html pre detaily." + +"\n\n" + +"VÄ‚Ëber dátumu:\n" + +"- PouĹľite tlaÄŤidlá \xab, \xbb pre vÄ‚Ëber roku\n" + +"- PouĹľite tlaÄŤidlá " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " pre vÄ‚Ëber mesiaca\n" + +"- Ak ktorĂ©koÄľvek z tÄ‚Ëchto tlaÄŤidiel podržíte dlhšie, zobrazĂ­ sa rÄ‚Ëchly vÄ‚Ëber."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"VÄ‚Ëber ÄŤasu:\n" + +"- Kliknutie na niektorĂş poloĹľku ÄŤasu ju zvĂ˚i\n" + +"- Shift-klik ju znĂ­Ĺľi\n" + +"- Ak podržíte tlaÄŤĂ­tko stlaÄŤenĂ©, posĂşvanĂ­m menĂ­te hodnotu."; + +Calendar._TT["PREV_YEAR"] = "PredošlÄ‚Ë rok (podrĹľte pre menu)"; +Calendar._TT["PREV_MONTH"] = "PredošlÄ‚Ë mesiac (podrĹľte pre menu)"; +Calendar._TT["GO_TODAY"] = "PrejsĹĄ na dnešok"; +Calendar._TT["NEXT_MONTH"] = "Nasl. mesiac (podrĹľte pre menu)"; +Calendar._TT["NEXT_YEAR"] = "Nasl. rok (podrĹľte pre menu)"; +Calendar._TT["SEL_DATE"] = "ZvoÄľte dátum"; +Calendar._TT["DRAG_TO_MOVE"] = "PodrĹľanĂ­m tlaÄŤĂ­tka zmenĂ­te polohu"; +Calendar._TT["PART_TODAY"] = " (dnes)"; +Calendar._TT["MON_FIRST"] = "ZobraziĹĄ pondelok ako prvÄ‚Ë"; +Calendar._TT["SUN_FIRST"] = "ZobraziĹĄ nedeÄľu ako prvĂş"; +Calendar._TT["CLOSE"] = "ZavrieĹĄ"; +Calendar._TT["TODAY"] = "Dnes"; +Calendar._TT["TIME_PART"] = "(Shift-)klik/ĹĄahanie zmenĂ­ hodnotu"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "$d. %m. %Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %e. %b"; + +Calendar._TT["WK"] = "tÄ‚ËĹľ"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sp.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sp.js new file mode 100644 index 00000000..bbacd216 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sp.js @@ -0,0 +1,110 @@ +// ** I18N + +// Calendar SP language +// Author: Rafael Velasco +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Domingo", + "Lunes", + "Martes", + "Miercoles", + "Jueves", + "Viernes", + "Sabado", + "Domingo"); + +Calendar._SDN = new Array +("Dom", + "Lun", + "Mar", + "Mie", + "Jue", + "Vie", + "Sab", + "Dom"); + +// full month names +Calendar._MN = new Array +("Enero", + "Febrero", + "Marzo", + "Abril", + "Mayo", + "Junio", + "Julio", + "Agosto", + "Septiembre", + "Octubre", + "Noviembre", + "Diciembre"); + +// short month names +Calendar._SMN = new Array +("Ene", + "Feb", + "Mar", + "Abr", + "May", + "Jun", + "Jul", + "Ago", + "Sep", + "Oct", + "Nov", + "Dic"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Información del Calendario"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Nuevas versiones en: http://www.dynarch.com/projects/calendar/\n" + +"Distribuida bajo licencia GNU LGPL. Para detalles vea http://gnu.org/licenses/lgpl.html ." + +"\n\n" + +"Selección de Fechas:\n" + +"- Use \xab, \xbb para seleccionar el año\n" + +"- Use " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " para seleccionar el mes\n" + +"- Mantenga presionado el botón del ratón en cualquiera de las opciones superiores para un acceso rapido ."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selección del Reloj:\n" + +"- Seleccione la hora para cambiar el reloj\n" + +"- o presione Shift-click para disminuirlo\n" + +"- o presione click y arrastre del ratón para una selección rapida."; + +Calendar._TT["PREV_YEAR"] = "Año anterior (Presione para menu)"; +Calendar._TT["PREV_MONTH"] = "Mes Anterior (Presione para menu)"; +Calendar._TT["GO_TODAY"] = "Ir a Hoy"; +Calendar._TT["NEXT_MONTH"] = "Mes Siguiente (Presione para menu)"; +Calendar._TT["NEXT_YEAR"] = "Año Siguiente (Presione para menu)"; +Calendar._TT["SEL_DATE"] = "Seleccione fecha"; +Calendar._TT["DRAG_TO_MOVE"] = "Arrastre y mueva"; +Calendar._TT["PART_TODAY"] = " (Hoy)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Mostrar %s primero"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Cerrar"; +Calendar._TT["TODAY"] = "Hoy"; +Calendar._TT["TIME_PART"] = "(Shift-)Click o arrastra para cambar el valor"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%dd-%mm-%yy"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %e de %B de %Y"; + +Calendar._TT["WK"] = "Sm"; +Calendar._TT["TIME"] = "Hora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sr-SP-Cyrl.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sr-SP-Cyrl.js new file mode 100644 index 00000000..9a66d6c8 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sr-SP-Cyrl.js @@ -0,0 +1,104 @@ +// ** I18N + +// Calendar - Serbian language (Cyrillic) +// Author: Aleksandar Seovic (aleks@seovic.com) +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("недеља", + "понедељак", + "уторак", + "Ñреда", + "четвртак", + "петак", + "Ñубота", + "недеља"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 1; + +// full month names +Calendar._MN = new Array +("јануар", + "фебруар", + "март", + "април", + "мај", + "јун", + "јул", + "авгуÑÑ‚", + "Ñептембар", + "октобар", + "новембар", + "децембар"); + +// short month names +Calendar._SMN = new Array +("јан", + "феб", + "мар", + "апр", + "мaj", + "јун", + "јул", + "авг", + "Ñеп", + "окт", + "нов", + "дец"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O календару"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Избор датума:\n" + +"- Уз помоћ таÑтера \xab, \xbb можете изабрати годину\n" + +"- Уз помоћ таÑтера " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " можете изабрати меÑец\n" + +"- Држите лево дугме миша притиÑнуто за бржу Ñелекцију."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Избор времена:\n" + +"- Кликните на Ñат или минут да га увећате\n" + +"- или Shift-click да га умањите\n" + +"- или притиÑните лево дугме миша и повуците за бржу Ñелекцију."; + +Calendar._TT["PREV_YEAR"] = "Претходна година"; +Calendar._TT["PREV_MONTH"] = "Претходни меÑец"; +Calendar._TT["GO_TODAY"] = "ДанаÑ"; +Calendar._TT["NEXT_MONTH"] = "Следећи меÑец"; +Calendar._TT["NEXT_YEAR"] = "Следећа година"; +Calendar._TT["SEL_DATE"] = "Изабери датум"; +Calendar._TT["DRAG_TO_MOVE"] = "Повуци за промену"; +Calendar._TT["PART_TODAY"] = " (данаÑ)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Прво прикажи %s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Затвори"; +Calendar._TT["TODAY"] = "ДанаÑ"; +Calendar._TT["TIME_PART"] = "(Shift-)Click или повуци да промениш вредноÑÑ‚"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d.%m.%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "нед"; +Calendar._TT["TIME"] = "Време:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sr-SP-Latn.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sr-SP-Latn.js new file mode 100644 index 00000000..5ec66f1b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sr-SP-Latn.js @@ -0,0 +1,104 @@ +// ** I18N + +// Calendar - Serbian language (Latin) +// Author: Aleksandar Seovic (aleks@seovic.com) +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("nedelja", + "ponedeljak", + "utorak", + "sreda", + "Äetvrtak", + "petak", + "subota", + "nedelja"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 1; + +// full month names +Calendar._MN = new Array +("januar", + "februar", + "mart", + "april", + "maj", + "jun", + "jul", + "avgust", + "septembar", + "oktobar", + "novembar", + "decembar"); + +// short month names +Calendar._SMN = new Array +("jan", + "feb", + "mar", + "apr", + "maj", + "jun", + "jul", + "avg", + "sep", + "okt", + "nov", + "dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O kalendaru"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Izbor datuma:\n" + +"- Uz pomoć tastera \xab, \xbb možete izabrati godinu\n" + +"- Uz pomoć tastera " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " možete izabrati mesec\n" + +"- Držite levo dugme miÅ¡a pritisnuto za bržu selekciju."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Izbor vremena:\n" + +"- Kliknite na sat ili minut da ga uvećate\n" + +"- ili Shift-click da ga umanjite\n" + +"- ili pritisnite levo dugme miÅ¡a i povucite za bržu selekciju."; + +Calendar._TT["PREV_YEAR"] = "Prethodna godina"; +Calendar._TT["PREV_MONTH"] = "Prethodni mesec"; +Calendar._TT["GO_TODAY"] = "Danas"; +Calendar._TT["NEXT_MONTH"] = "Sledeći mesec"; +Calendar._TT["NEXT_YEAR"] = "Sledeća godina"; +Calendar._TT["SEL_DATE"] = "Izaberi datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Povuci za promenu"; +Calendar._TT["PART_TODAY"] = " (danas)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Prvo prikaži %s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Zatvori"; +Calendar._TT["TODAY"] = "Danas"; +Calendar._TT["TIME_PART"] = "(Shift-)Click ili povuci da promeniÅ¡ vrednost"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d.%m.%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "ned"; +Calendar._TT["TIME"] = "Vreme:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sv.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sv.js new file mode 100644 index 00000000..b62df3e1 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-sv.js @@ -0,0 +1,93 @@ +// ** I18N + +// Calendar SV language (Swedish, svenska) +// Author: Mihai Bazon, +// Translation team: +// Translator: Leonard Norrgård +// Last translator: Leonard Norrgård +// Encoding: iso-latin-1 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("söndag", + "måndag", + "tisdag", + "onsdag", + "torsdag", + "fredag", + "lördag", + "söndag"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. +Calendar._SDN_len = 2; +Calendar._SMN_len = 3; + +// full month names +Calendar._MN = new Array +("januari", + "februari", + "mars", + "april", + "maj", + "juni", + "juli", + "augusti", + "september", + "oktober", + "november", + "december"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Om kalendern"; + +Calendar._TT["ABOUT"] = +"DHTML Datum/tid-väljare\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"För senaste version gå till: http://www.dynarch.com/projects/calendar/\n" + +"Distribueras under GNU LGPL. Se http://gnu.org/licenses/lgpl.html för detaljer." + +"\n\n" + +"Val av datum:\n" + +"- Använd knapparna \xab, \xbb för att välja år\n" + +"- Använd knapparna " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " för att välja månad\n" + +"- Håll musknappen nedtryckt på någon av ovanstående knappar för snabbare val."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Val av tid:\n" + +"- Klicka på en del av tiden för att öka den delen\n" + +"- eller skift-klicka för att minska den\n" + +"- eller klicka och drag för snabbare val."; + +Calendar._TT["PREV_YEAR"] = "Föregående år (håll för menu)"; +Calendar._TT["PREV_MONTH"] = "Föregående månad (håll för menu)"; +Calendar._TT["GO_TODAY"] = "Gå till dagens datum"; +Calendar._TT["NEXT_MONTH"] = "Följande månad (håll för menu)"; +Calendar._TT["NEXT_YEAR"] = "Följande år (håll för menu)"; +Calendar._TT["SEL_DATE"] = "Välj datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Drag för att flytta"; +Calendar._TT["PART_TODAY"] = " (idag)"; +Calendar._TT["MON_FIRST"] = "Visa måndag först"; +Calendar._TT["SUN_FIRST"] = "Visa söndag först"; +Calendar._TT["CLOSE"] = "Stäng"; +Calendar._TT["TODAY"] = "Idag"; +Calendar._TT["TIME_PART"] = "(Skift-)klicka eller drag för att ändra tid"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%A %d %b %Y"; + +Calendar._TT["WK"] = "vecka"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-tr.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-tr.js new file mode 100644 index 00000000..2164687f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-tr.js @@ -0,0 +1,58 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// Turkish Translation by Nuri AKMAN +// Location: Ankara/TURKEY +// e-mail : nuriakman@hotmail.com +// Date : April, 9 2003 +// +// Note: if Turkish Characters does not shown on you screen +// please include falowing line your html code: +// +// +// +////////////////////////////////////////////////////////////////////////////////////////////// + +// ** I18N +Calendar._DN = new Array +("Pazar", + "Pazartesi", + "Salý", + "Çarþamba", + "Perþembe", + "Cuma", + "Cumartesi", + "Pazar"); +Calendar._MN = new Array +("Ocak", + "Þubat", + "Mart", + "Nisan", + "Mayýs", + "Haziran", + "Temmuz", + "Aðustos", + "Eylül", + "Ekim", + "Kasým", + "Aralýk"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["TOGGLE"] = "Haftanýn ilk gününü kaydýr"; +Calendar._TT["PREV_YEAR"] = "Önceki Yýl (Menü için basýlý tutunuz)"; +Calendar._TT["PREV_MONTH"] = "Önceki Ay (Menü için basýlý tutunuz)"; +Calendar._TT["GO_TODAY"] = "Bugün'e git"; +Calendar._TT["NEXT_MONTH"] = "Sonraki Ay (Menü için basýlý tutunuz)"; +Calendar._TT["NEXT_YEAR"] = "Sonraki Yýl (Menü için basýlý tutunuz)"; +Calendar._TT["SEL_DATE"] = "Tarih seçiniz"; +Calendar._TT["DRAG_TO_MOVE"] = "Taþýmak için sürükleyiniz"; +Calendar._TT["PART_TODAY"] = " (bugün)"; +Calendar._TT["MON_FIRST"] = "Takvim Pazartesi gününden baþlasýn"; +Calendar._TT["SUN_FIRST"] = "Takvim Pazar gününden baþlasýn"; +Calendar._TT["CLOSE"] = "Kapat"; +Calendar._TT["TODAY"] = "Bugün"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "dd-mm-y"; +Calendar._TT["TT_DATE_FORMAT"] = "d MM y, DD"; + +Calendar._TT["WK"] = "Hafta"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-zh.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-zh.js new file mode 100644 index 00000000..42f0b107 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/calendar-zh.js @@ -0,0 +1,119 @@ +// ** I18N + +// Calendar ZH language +// Author: muziq, +// Encoding: GB2312 or GBK +// Distributed under the same terms as the calendar itself. + +// full day names +Calendar._DN = new Array +("ÐÇÆÚÈÕ", + "ÐÇÆÚÒ»", + "ÐÇÆÚ¶þ", + "ÐÇÆÚÈý", + "ÐÇÆÚËÄ", + "ÐÇÆÚÎå", + "ÐÇÆÚÁù", + "ÐÇÆÚÈÕ"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("ÈÕ", + "Ò»", + "¶þ", + "Èý", + "ËÄ", + "Îå", + "Áù", + "ÈÕ"); + +// full month names +Calendar._MN = new Array +("Ò»ÔÂ", + "¶þÔÂ", + "ÈýÔÂ", + "ËÄÔÂ", + "ÎåÔÂ", + "ÁùÔÂ", + "ÆßÔÂ", + "°ËÔÂ", + "¾ÅÔÂ", + "Ê®ÔÂ", + "ʮһÔÂ", + "Ê®¶þÔÂ"); + +// short month names +Calendar._SMN = new Array +("Ò»ÔÂ", + "¶þÔÂ", + "ÈýÔÂ", + "ËÄÔÂ", + "ÎåÔÂ", + "ÁùÔÂ", + "ÆßÔÂ", + "°ËÔÂ", + "¾ÅÔÂ", + "Ê®ÔÂ", + "ʮһÔÂ", + "Ê®¶þÔÂ"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "°ïÖú"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Ñ¡ÔñÈÕÆÚ:\n" + +"- µã»÷ \xab, \xbb °´Å¥Ñ¡ÔñÄê·Ý\n" + +"- µã»÷ " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " °´Å¥Ñ¡ÔñÔ·Ý\n" + +"- ³¤°´ÒÔÉϰ´Å¥¿É´Ó²Ëµ¥ÖпìËÙÑ¡ÔñÄê·Ý»òÔ·Ý"; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Ñ¡Ôñʱ¼ä:\n" + +"- µã»÷Сʱ»ò·ÖÖÓ¿Éʹ¸ÄÊýÖµ¼ÓÒ»\n" + +"- °´×¡Shift¼üµã»÷Сʱ»ò·ÖÖÓ¿Éʹ¸ÄÊýÖµ¼õÒ»\n" + +"- µã»÷Í϶¯Êó±ê¿É½øÐпìËÙÑ¡Ôñ"; + +Calendar._TT["PREV_YEAR"] = "ÉÏÒ»Äê (°´×¡³ö²Ëµ¥)"; +Calendar._TT["PREV_MONTH"] = "ÉÏÒ»Ô (°´×¡³ö²Ëµ¥)"; +Calendar._TT["GO_TODAY"] = "תµ½½ñÈÕ"; +Calendar._TT["NEXT_MONTH"] = "ÏÂÒ»Ô (°´×¡³ö²Ëµ¥)"; +Calendar._TT["NEXT_YEAR"] = "ÏÂÒ»Äê (°´×¡³ö²Ëµ¥)"; +Calendar._TT["SEL_DATE"] = "Ñ¡ÔñÈÕÆÚ"; +Calendar._TT["DRAG_TO_MOVE"] = "Í϶¯"; +Calendar._TT["PART_TODAY"] = " (½ñÈÕ)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "×î×ó±ßÏÔʾ%s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "¹Ø±Õ"; +Calendar._TT["TODAY"] = "½ñÈÕ"; +Calendar._TT["TIME_PART"] = "(Shift-)µã»÷Êó±ê»òÍ϶¯¸Ä±äÖµ"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %b %eÈÕ"; + +Calendar._TT["WK"] = "ÖÜ"; +Calendar._TT["TIME"] = "ʱ¼ä:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/cn_utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/cn_utf8.js new file mode 100644 index 00000000..a0ef7c6b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Scripts/Calendar/lang/cn_utf8.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Encoding: any +// Translator : Niko +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("\u5468\u65e5",//\u5468\u65e5 + "\u5468\u4e00",//\u5468\u4e00 + "\u5468\u4e8c",//\u5468\u4e8c + "\u5468\u4e09",//\u5468\u4e09 + "\u5468\u56db",//\u5468\u56db + "\u5468\u4e94",//\u5468\u4e94 + "\u5468\u516d",//\u5468\u516d + "\u5468\u65e5");//\u5468\u65e5 + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("\u5468\u65e5", + "\u5468\u4e00", + "\u5468\u4e8c", + "\u5468\u4e09", + "\u5468\u56db", + "\u5468\u4e94", + "\u5468\u516d", + "\u5468\u65e5"); + +// full month names +Calendar._MN = new Array +("\u4e00\u6708", + "\u4e8c\u6708", + "\u4e09\u6708", + "\u56db\u6708", + "\u4e94\u6708", + "\u516d\u6708", + "\u4e03\u6708", + "\u516b\u6708", + "\u4e5d\u6708", + "\u5341\u6708", + "\u5341\u4e00\u6708", + "\u5341\u4e8c\u6708"); + +// short month names +Calendar._SMN = new Array +("\u4e00\u6708", + "\u4e8c\u6708", + "\u4e09\u6708", + "\u56db\u6708", + "\u4e94\u6708", + "\u516d\u6708", + "\u4e03\u6708", + "\u516b\u6708", + "\u4e5d\u6708", + "\u5341\u6708", + "\u5341\u4e00\u6708", + "\u5341\u4e8c\u6708"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "\u5173\u4e8e"; + +Calendar._TT["ABOUT"] = +" DHTML \u65e5\u8d77/\u65f6\u95f4\u9009\u62e9\u63a7\u4ef6\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: \u6700\u65b0\u7248\u672c\u8bf7\u767b\u9646http://www.dynarch.com/projects/calendar/\u5bdf\u770b\n" + +"\u9075\u5faaGNU LGPL. \u7ec6\u8282\u53c2\u9605 http://gnu.org/licenses/lgpl.html" + +"\n\n" + +"\u65e5\u671f\u9009\u62e9:\n" + +"- \u70b9\u51fb\xab(\xbb)\u6309\u94ae\u9009\u62e9\u4e0a(\u4e0b)\u4e00\u5e74\u5ea6.\n" + +"- \u70b9\u51fb" + String.fromCharCode(0x2039) + "(" + String.fromCharCode(0x203a) + ")\u6309\u94ae\u9009\u62e9\u4e0a(\u4e0b)\u4e2a\u6708\u4efd.\n" + +"- \u957f\u65f6\u95f4\u6309\u7740\u6309\u94ae\u5c06\u51fa\u73b0\u66f4\u591a\u9009\u62e9\u9879."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"\u65f6\u95f4\u9009\u62e9:\n" + +"-\u5728\u65f6\u95f4\u90e8\u5206(\u5206\u6216\u8005\u79d2)\u4e0a\u5355\u51fb\u9f20\u6807\u5de6\u952e\u6765\u589e\u52a0\u5f53\u524d\u65f6\u95f4\u90e8\u5206(\u5206\u6216\u8005\u79d2)\n" + +"-\u5728\u65f6\u95f4\u90e8\u5206(\u5206\u6216\u8005\u79d2)\u4e0a\u6309\u4f4fShift\u952e\u540e\u5355\u51fb\u9f20\u6807\u5de6\u952e\u6765\u51cf\u5c11\u5f53\u524d\u65f6\u95f4\u90e8\u5206(\u5206\u6216\u8005\u79d2)."; + +Calendar._TT["PREV_YEAR"] = "\u4e0a\u4e00\u5e74"; +Calendar._TT["PREV_MONTH"] = "\u4e0a\u4e2a\u6708"; +Calendar._TT["GO_TODAY"] = "\u5230\u4eca\u5929"; +Calendar._TT["NEXT_MONTH"] = "\u4e0b\u4e2a\u6708"; +Calendar._TT["NEXT_YEAR"] = "\u4e0b\u4e00\u5e74"; +Calendar._TT["SEL_DATE"] = "\u9009\u62e9\u65e5\u671f"; +Calendar._TT["DRAG_TO_MOVE"] = "\u62d6\u52a8"; +Calendar._TT["PART_TODAY"] = " (\u4eca\u5929)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "%s\u4e3a\u8fd9\u5468\u7684\u7b2c\u4e00\u5929"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "\u5173\u95ed"; +Calendar._TT["TODAY"] = "\u4eca\u5929"; +Calendar._TT["TIME_PART"] = "(\u6309\u7740Shift\u952e)\u5355\u51fb\u6216\u62d6\u52a8\u6539\u53d8\u503c"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e\u65e5"; + +Calendar._TT["WK"] = "\u5468"; +Calendar._TT["TIME"] = "\u65f6\u95f4:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/SpringAir.Web.2003.csproj b/examples/Spring/SpringAir/src/SpringAir.Web.2003/SpringAir.Web.2003.csproj new file mode 100644 index 00000000..7ea0e450 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/SpringAir.Web.2003.csproj @@ -0,0 +1,636 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/SpringAir.Web.2003.csproj.webinfo b/examples/Spring/SpringAir/src/SpringAir.Web.2003/SpringAir.Web.2003.csproj.webinfo new file mode 100644 index 00000000..c3f64c41 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/SpringAir.Web.2003.csproj.webinfo @@ -0,0 +1,4 @@ + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/SpringAir.Web.build b/examples/Spring/SpringAir/src/SpringAir.Web.2003/SpringAir.Web.build new file mode 100644 index 00000000..9456679b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/SpringAir.Web.build @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web.config b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web.config new file mode 100644 index 00000000..b6d91b81 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web.config @@ -0,0 +1,80 @@ + + + +
    + + +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.aspx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.aspx new file mode 100644 index 00000000..bd9e674c --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.aspx @@ -0,0 +1,58 @@ +<%@ Page language="c#" Inherits="SpringAir.Web.ReservationConfirmationPage" CodeBehind="ReservationConfirmationPage.aspx.cs" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> +<%@ Import Namespace="SpringAir.Domain" %> + + + + +

    +

    + +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + <%= GetMessage("itinerary") %> +
    + <%= GetMessage("flightNumber") %> + + <%= GetMessage("departureDate") %> + + <%= GetMessage("departureAirport") %> + + <%= GetMessage("destinationAirport") %> + + <%= GetMessage("aircraft") %> +
    <%# ((Flight) Container.DataItem).FlightNumber %><%# ((Flight) Container.DataItem).DepartureTime.ToString() %><%# ((Flight) Container.DataItem).DepartureAirport.Description %><%# ((Flight) Container.DataItem).DestinationAirport.Description %><%# ((Flight) Container.DataItem).Aircraft.Model %>
    + +
    +
    + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.aspx.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.aspx.cs new file mode 100644 index 00000000..d9079672 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.aspx.cs @@ -0,0 +1,99 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Web.UI.WebControls; +using Spring.Web.UI; +using Spring.Web.UI.Controls; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Web +{ + /// + /// The SpringAir reservation confirmation page. + /// + /// Aleksandar Seovic + /// $Id: ReservationConfirmationPage.aspx.cs,v 1.2 2006/12/07 04:22:00 aseovic Exp $ + public class ReservationConfirmationPage : Page + { + #region Fields + + protected ReservationConfirmation confirmation; + + #endregion + + #region Controls + + protected Content body; + protected Label caption; + protected Label confirmationLabel; + protected Repeater itinerary; + + #endregion + + #region Model Management and Data Binding Methods + + protected override void InitializeModel() + { + confirmation = (ReservationConfirmation) Session[Constants.ReservationConfirmationKey]; + } + + protected override void LoadModel(object savedModel) + { + confirmation = (ReservationConfirmation) savedModel; + } + + protected override object SaveModel() + { + return confirmation; + } + + #endregion + + #region Page Lifecycle Methods + + protected override void OnInitializeControls(EventArgs e) + { + base.OnInitializeControls(e); + + if (!IsPostBack) + { + itinerary.DataSource = confirmation.Reservation.Itinerary.Flights; + itinerary.DataBind(); + + Session.Remove(Constants.ReservationConfirmationKey); + Session.Remove(Constants.SuggestedFlightsKey); + } + } + + protected override void OnPreRender(EventArgs e) + { + base.OnPreRender(e); + confirmationLabel.Text = GetMessage("confirmation", confirmation.ConfirmationNumber); + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.aspx.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.aspx.resx new file mode 100644 index 00000000..ed0ad7ac --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.aspx.resx @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Thank You! + + + Your reservation confirmation number is {0}. + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.en.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.en.resx new file mode 100644 index 00000000..ed0ad7ac --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.en.resx @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Thank You! + + + Your reservation confirmation number is {0}. + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.sr-SP-Cyrl.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.sr-SP-Cyrl.resx new file mode 100644 index 00000000..7cf65849 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.sr-SP-Cyrl.resx @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Хвала Вам! + + + Ваш број резервације је {0} + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.sr-SP-Latn.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.sr-SP-Latn.resx new file mode 100644 index 00000000..326baad8 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/ReservationConfirmationPage.sr-SP-Latn.resx @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Hvala Vam! + + + VaÅ¡ broj rezervacije je {0} + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.aspx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.aspx new file mode 100644 index 00000000..799b9c36 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.aspx @@ -0,0 +1,130 @@ +<%@ Import Namespace="SpringAir.Domain" %> +<%@ Page language="c#" Inherits="SpringAir.Web.SuggestedFlights" CodeBehind="SuggestedFlights.aspx.cs" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> + + + + +

    +

    +
    + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + <%= GetMessage("outboundFlights") %> +
    +   + + <%= GetMessage("flightNumber") %> + + <%= GetMessage("departureDate") %> + + <%= GetMessage("departureAirport") %> + + <%= GetMessage("destinationAirport") %> + + <%= GetMessage("aircraft") %> + + <%= GetMessage("seatPlan") %> +
    + <%# ((Flight) Container.DataItem).FlightNumber %><%# ((Flight) Container.DataItem).DepartureTime.ToString() %><%# ((Flight) Container.DataItem).DepartureAirport.Description %><%# ((Flight) Container.DataItem).DestinationAirport.Description %><%# ((Flight) Container.DataItem).Aircraft.Model %><%# ((Flight) Container.DataItem).SeatPlan %>
    + +
     
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + <%= GetMessage("returnFlights") %> +
    +   + <%= GetMessage("flightNumber") %> + + <%= GetMessage("departureDate") %> + + <%= GetMessage("departureAirport") %> + + <%= GetMessage("destinationAirport") %> + + <%= GetMessage("aircraft") %> + + <%= GetMessage("seatPlan") %> +
    + <%# ((Flight) Container.DataItem).FlightNumber %><%# ((Flight) Container.DataItem).DepartureTime.ToString() %><%# ((Flight) Container.DataItem).DepartureAirport.Description %><%# ((Flight) Container.DataItem).DestinationAirport.Description %><%# ((Flight) Container.DataItem).Aircraft.Model %><%# ((Flight) Container.DataItem).SeatPlan %>
    + +

    +
    +
    + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.aspx.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.aspx.cs new file mode 100644 index 00000000..9426fe1b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.aspx.cs @@ -0,0 +1,215 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Web.UI.HtmlControls; +using System.Web.UI.WebControls; +using Spring.Validation; +using Spring.Web.UI; +using SpringAir.Domain; +using SpringAir.Service; + +#endregion + +namespace SpringAir.Web +{ + /// + /// Displays a list of suggested flights. + /// + /// Rick Evans + /// Aleksandar Seovic + /// Marko Dumic + /// $Id: SuggestedFlights.aspx.cs,v 1.2 2006/12/07 04:22:00 aseovic Exp $ + public class SuggestedFlights : Page + { + #region Fields + + private const string ReservationConfirmed = "reservationConfirmed"; + private const int NoFlightSelected = -1; + + protected FlightSuggestions flights; + private IBookingAgent bookingAgent; + + protected int outboundFlightIndex; + protected int returnFlightIndex; + + private IValidator flightsValidator; + + #endregion + + #region Controls + + protected Label caption; + protected Button bookFlights; + + protected Repeater outboundFlightList; + protected Repeater returnFlightList; + + protected HtmlInputHidden outboundFlight; + protected Spring.Web.UI.Controls.Content body; + protected Spring.Web.UI.Controls.ValidationSummary validationSummary; + + protected HtmlInputHidden returnFlight; + + #endregion + + #region Properties + + public IBookingAgent BookingAgent + { + set { this.bookingAgent = value; } + } + + public IValidator FlightsValidator + { + set { flightsValidator = value; } + } + + #endregion + + #region Model Management and Data Binding Methods + + protected override void InitializeModel() + { + flights = (FlightSuggestions) Session[Constants.SuggestedFlightsKey]; + outboundFlightIndex = NoFlightSelected; + returnFlightIndex = NoFlightSelected; + } + + protected override void LoadModel(object savedModel) + { + IDictionary model = (IDictionary) savedModel; + flights = (FlightSuggestions) model["flights"]; + outboundFlightIndex = (int) model["outboundFlightIndex"]; + returnFlightIndex = (int) model["returnFlightIndex"]; + } + + protected override object SaveModel() + { + IDictionary model = new Hashtable(); + model.Add("flights", flights); + model.Add("outboundFlightIndex", outboundFlightIndex); + model.Add("returnFlightIndex", returnFlightIndex); + return model; + } + + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("outboundFlight.Value", "outboundFlightIndex"); + BindingManager.AddBinding("returnFlight.Value", "returnFlightIndex"); + } + + #endregion + + #region Page Lifecycle Methods + + protected override void OnInit(EventArgs e) + { + InitializeComponent(); + base.OnInit(e); + } + + protected override void OnInitializeControls(EventArgs e) + { + base.OnInitializeControls(e); + + if (!IsPostBack) + { + outboundFlightList.DataSource = flights.OutboundFlights; + outboundFlightList.DataBind(); + if (flights.HasReturnFlights) + { + caption.Text = GetMessage("selectFlights"); + returnFlightList.DataSource = flights.ReturnFlights; + returnFlightList.DataBind(); + } + else + { + caption.Text = GetMessage("selectFlight"); + returnFlightList.Visible = false; + } + } + } + + protected override void OnPreRender(EventArgs e) + { + base.OnPreRender(e); + caption.Text = flights.HasReturnFlights + ? GetMessage("selectFlights") + : GetMessage("selectFlight"); + } + + #endregion + + #region Controller Methods + + private void BookFlights(object sender, EventArgs e) + { +// if(Validate(this, flightsValidator)) +// { + FlightCollection flightsToBook = GetFlightsToBook(); + Itinerary itinerary = new Itinerary(flightsToBook); + // TODO: forward to next logical page and get user details... + ReservationConfirmation confirmation = bookingAgent.Book( + new Reservation(new Passenger(1, "Aleksandar", "Seovic"), itinerary)); + Session[Constants.ReservationConfirmationKey] = confirmation; + SetResult(ReservationConfirmed); +// } +// else +// { +// this.outboundFlightIndex = NoFlightSelected; +// this.returnFlightIndex = NoFlightSelected; +// } + } + + private FlightCollection GetFlightsToBook() + { + FlightCollection flightsToBook = new FlightCollection(); + Flight outboundFlight = this.flights.GetOutboundFlight(outboundFlightIndex); + flightsToBook.Add(outboundFlight); + if (HasReturnFlight) + { + Flight returnFlight = this.flights.GetReturnFlight(returnFlightIndex); + flightsToBook.Add(returnFlight); + } + return flightsToBook; + } + + private bool HasReturnFlight + { + get { return this.returnFlightIndex != NoFlightSelected; } + } + + #endregion + + #region Web Form Designer generated code + + private void InitializeComponent() + { + this.bookFlights.Click += new System.EventHandler(this.BookFlights); + + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.aspx.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.aspx.resx new file mode 100644 index 00000000..af2b0121 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.aspx.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + True + + + Please select flight + + + Please select flights + + + Book Flights + + + Please select an outbound flight. + + + Please select a return flight. + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.en.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.en.resx new file mode 100644 index 00000000..523f12f8 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.en.resx @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Please select flight + + + Please select flights + + + Book Flights + + + Please select an outbound flight. + + + Please select a return flight. + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.sr-SP-Cyrl.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.sr-SP-Cyrl.resx new file mode 100644 index 00000000..1782f06e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.sr-SP-Cyrl.resx @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Изаберите лет + + + Изаберите летове + + + Резервиши лет + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.sr-SP-Latn.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.sr-SP-Latn.resx new file mode 100644 index 00000000..9a7194a4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/SuggestedFlights.sr-SP-Latn.resx @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Izaberite let + + + Izaberite letove + + + RezerviÅ¡i let + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.aspx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.aspx new file mode 100644 index 00000000..c050fe8f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.aspx @@ -0,0 +1,70 @@ +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> +<%@ Page language="c#" Inherits="SpringAir.Web.TripForm" CodeBehind="TripForm.aspx.cs" %> + + + + + + + +
    +

    +

    + + + + + + + + + + + + + + + + + + + + + + +
      + + + +
    + + + + + +
    + + + + +
    + +
    +

    +
    +
    + +
    + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.aspx.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.aspx.cs new file mode 100644 index 00000000..c1ec6e43 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.aspx.cs @@ -0,0 +1,275 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Web.UI.WebControls; + +using Spring.Validation; +using Spring.Web.UI; +using Spring.Web.UI.Controls; + +using SpringAir.Data; +using SpringAir.Domain; +using SpringAir.Service; + +using Calendar = Spring.Web.UI.Controls.Calendar; +using ValidationSummary = Spring.Web.UI.Controls.ValidationSummary; + +#endregion + +namespace SpringAir.Web +{ + /// + /// Handles the 'type in your trip details and we'll see what we can find + /// for you' use case. + /// + /// Rick Evans + /// Aleksandar Seovic + /// Marko Dumic + /// $Id: TripForm.aspx.cs,v 1.2 2006/12/07 04:22:00 aseovic Exp $ + public class TripForm : Page + { + #region Fields + + private const string DisplaySuggestedFlights = "displaySuggestedFlights"; + + private IBookingAgent bookingAgent; + private IAirportDao airportDao; + private Trip trip; + private IValidator tripValidator; + + #endregion + + #region Controls + + protected RadioButton OneWay; + protected RadioButton RoundTrip; + protected Label tripType; + protected Label caption; + protected Label leavingFrom; + protected Label leavingOn; + protected Label goingTo; + protected Label returningOn; + protected Button findFlights; + + protected RadioButtonGroup tripMode; + protected DropDownList leavingFromAirportCode; + protected Calendar leavingFromDate; + protected DropDownList goingToAirportCode; + protected Calendar returningOnDate; + + protected ValidationSummary validationSummary; + protected Content head; + protected Content body; + protected ValidationError firstNameValidator; + protected TextBox firstName; + protected TextBox lastName; + protected ValidationError lastNameValidator; + protected ValidationError departureAirportErrors; + protected ValidationError destinationAirportErrors; + protected ValidationError departureDateErrors; + protected ValidationError returnDateErrors; + private Airport nothingAirport; + + #endregion + + #region Properties + + /// + /// The service interface that will handle the business logic of + /// booking trips and suggesting itineraries. + /// + /// + ///

    + /// Is injected into this page by the Spring IoC container. You might + /// want to look at the definition of this page in the ~/Config/Web.xml + /// file to confirm exactly which implementation of the + /// is being injected, + /// and to see how this is wired together. + ///

    + ///
    + public IBookingAgent BookingAgent + { + set { bookingAgent = value; } + } + + /// + /// This DAO object is used to retrieve a list of airports in order + /// to populate airport drop-down lists. + /// + /// + ///

    + /// Is injected into this page by the Spring IoC container. You might + /// want to look at the definition of this page in the ~/Config/Web.xml + /// file to confirm exactly which implementation of the + /// is being injected, + /// and to see how this is wired together. + ///

    + ///
    + public IAirportDao AirportDao + { + set { airportDao = value; } + } + + /// + /// The user's desired . + /// + /// + ///

    + /// The properties of this domain object are populated from the + /// values present in the controls on this page, according to the + /// data binding definitions. + ///

    + ///
    + public Trip Trip + { + get { return trip; } + set { trip = value; } + } + + /// + /// Sets the trip validator. + /// + /// + ///

    + /// Unsurprisingly, this + /// instance will be used to validate the associated + /// on a form submission. + ///

    + ///
    + /// The trip validator. + public IValidator TripValidator + { + set { tripValidator = value; } + } + + #endregion + + #region Model Management and Data Binding Methods + + protected override void InitializeModel() + { + trip = new Trip(); + trip.Mode = TripMode.RoundTrip; + trip.StartingFrom.Date = DateTime.Today; + trip.ReturningFrom.Date = DateTime.Today.AddDays(1); + } + + protected override void LoadModel(object savedModel) + { + trip = (Trip)savedModel; + } + + protected override object SaveModel() + { + return trip; + } + + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("tripMode.Value", "Trip.Mode"); + BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.AirportCode"); + BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.AirportCode"); + BindingManager.AddBinding("leavingFromDate.SelectedDate", "Trip.StartingFrom.Date"); + BindingManager.AddBinding("returningOnDate.SelectedDate", "Trip.ReturningFrom.Date"); + } + + #endregion + + #region Page Lifecycle Methods + + protected override void OnInitializeControls(EventArgs e) + { + if (!IsPostBack) + { + BindAirportDropdowns(); + } + } + + protected override void OnUserCultureChanged(EventArgs e) + { + BindAirportDropdowns(); + } + + private void BindAirportDropdowns() + { + ArrayList airportList = new ArrayList(); + airportList.Add(GetNothingAirport()); + airportList.AddRange(airportDao.GetAllAirports()); + + leavingFromAirportCode.DataSource = airportList; + leavingFromAirportCode.DataTextField = "Description"; + leavingFromAirportCode.DataValueField = "Code"; + leavingFromAirportCode.DataBind(); + + goingToAirportCode.DataSource = airportList; + goingToAirportCode.DataTextField = "Description"; + goingToAirportCode.DataValueField = "Code"; + goingToAirportCode.DataBind(); + } + + private Airport GetNothingAirport() + { + if (this.nothingAirport == null) + { + this.nothingAirport = new Airport(0, string.Empty, string.Empty, "-- " + GetMessage("selectAirport") + " --"); + } + return this.nothingAirport; + } + + #endregion + + #region Controller Methods + + private void SearchForFlights(object sender, EventArgs e) + { + if (Validate(trip, tripValidator)) + { + FlightSuggestions suggestions = this.bookingAgent.SuggestFlights(Trip); + if (suggestions.HasOutboundFlights) + { + Session[Constants.SuggestedFlightsKey] = suggestions; + SetResult(DisplaySuggestedFlights); + } + } + } + + #endregion + + #region Web Form Designer generated code + + protected override void OnInit(EventArgs e) + { + InitializeComponent(); + base.OnInit(e); + } + + private void InitializeComponent() + { + this.findFlights.Click += new EventHandler(this.SearchForFlights); + + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.aspx.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.aspx.resx new file mode 100644 index 00000000..78bdcce5 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.aspx.resx @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + True + + + + One Way + + + Round Trip + + + Find Flights + + + Leaving from: + + + Departing on: + + + Going to: + + + Returning on: + + + Please enter flight information + + + select airport + + + First name is a required field + + + Last name is a required field + + + Please select departure airport + + + Please select destination airport + + + Departure date is required + + + Return date is required + + + Departure date cannot be in the past + + + Return date cannot be before departure date + + + Destination airport cannot be the same as departure airport + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.en.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.en.resx new file mode 100644 index 00000000..ecd95a64 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.en.resx @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + + One Way + + + Round Trip + + + Find Flights + + + Leaving from: + + + Departing on: + + + Going to: + + + Returning on: + + + Please enter flight information + + + select airport + + + First name is a required field + + + Last name is a required field + + + Please select departure airport + + + Please select destination airport + + + Departure date is required + + + Return date is required + + + Departure date cannot be in the past + + + Return date cannot be before departure date + + + Destination airport cannot be the same as departure airport + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.sr-SP-Cyrl.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.sr-SP-Cyrl.resx new file mode 100644 index 00000000..5f77dd3a --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.sr-SP-Cyrl.resx @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + + ЈедноÑмерни лет + + + Повратни лет + + + Пронађи лет + + + Полазак из: + + + Датум полаÑка: + + + Слетање на: + + + Датум повратка: + + + УнеÑите податке о лету + + + изаберите аеродром + + + Име је обавезно поље + + + Презиме је обавезно поље + + + Молимо Ð²Ð°Ñ Ð¸Ð·Ð°Ð±ÐµÑ€Ð¸Ñ‚Ðµ полазни аеродром + + + Молимо Ð²Ð°Ñ Ð¸Ð·Ð°Ð±ÐµÑ€Ð¸Ñ‚Ðµ одредишни аеродром + + + Датум полаÑка је обавезан + + + Датум повратка је обавезан + + + Датум полаÑка не може бити у прошлоÑти + + + Датум повратка не може бити пре датума полаÑка + + + Одредишни аеродром не може бити иÑти као полазни аеродром + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.sr-SP-Latn.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.sr-SP-Latn.resx new file mode 100644 index 00000000..25b9eb60 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/TripForm.sr-SP-Latn.resx @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + + Jednosmerni let + + + Povratni let + + + Pronadji let + + + Polazak iz: + + + Datum polaska: + + + Sletanje na: + + + Datum povratka: + + + Unesite podatke o letu + + + izaberite aerodrom + + + Ime je obavezno polje + + + Prezime je obavezno polje + + + Molimo vas izaberite polazni aerodrom + + + Molimo vas izaberite odrediÅ¡ni aerodrom + + + Datum polaska je obavezan + + + Datum povratka je obavezan + + + Datum polaska ne može biti u proÅ¡losti + + + Datum povratka ne može biti pre datuma polaska + + + OdrediÅ¡ni aerodrom ne može biti isti kao polazni aerodrom + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/Web.config b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/Web.config new file mode 100644 index 00000000..889c860d --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/BookTrip/Web.config @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/CSS/default.css b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/CSS/default.css new file mode 100644 index 00000000..efd0119b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/CSS/default.css @@ -0,0 +1,348 @@ +body +{ + padding: 0; + margin: 0; + width: 720px; + text-align: center; + font-family: Verdana, Arial, Helvetica, sans-serif; +} + +body, table +{ + font-size: 12px; +} + +form +{ + margin: 0; +} + +td.formLabel +{ + font-weight: bold; + text-align: right; + vertical-align: baseline; + padding: 0 4px 0 16px; +} + +.validationError +{ + font-size: 80%; + color: #c50; +} + +input, select +{ + font-family: Tahoma, Arial, Helvetica, sans-serif; + font-size: 12px; +} + +/* main container box */ +#container +{ + width: 800px; + position: relative; + padding: 0 15px 15px; + margin: 5 auto; + text-align: left; +} + +div#logo +{ + width: 794px; + height: 119px; +} + +div#navigation +{ + width: 720px; + height: 20px; + background: #E2F3B8; + text-align: right; +} + +#container div#content +{ + position: relative; + width: 800px; + height: 100%; + overflow: hidden; + background-color: #ecf1f3; + padding: 0; + margin: 0; +} + +.panel11, .panel12, .panel13, .panel21, .panel23, .panel31, .panel32, .panel33 +{ + position: absolute; + overflow: hidden; + padding: 0; + margin: 0; +} + +.panel11 +{ + top: 0; + left: 0; + width: 16px; + height: 16px; + z-index: 2; +} +.panel12 +{ + top: 0; + left: 0; + width: 100%; + height: 16px; + z-index: 1; +} +.panel13 +{ + top: 0; + right: 0; + width: 16px; + height: 16px; + z-index: 2; +} + +.panel21 +{ + top: 0; + left: 0; + width: 16px; + height: 100%; + z-index: 1; +} +.panel23 +{ + top: 0; + right: 0; + width: 16px; + height: 100%; + z-index: 1; +} + +.panel31 +{ + bottom: -1px; + left: 0; + width: 16px; + height: 16px; + z-index: 2; +} +.panel32 +{ + bottom: -1px; + left: 0; + width: 100%; + height: 16px; + z-index: 1; +} +.panel33 +{ + bottom: -1px; + right: 0; + width: 16px; + height: 16px; + z-index: 2; +} + +.panelContent +{ + position: relative; + overflow: hidden; + padding: 18px; + margin: 0; + z-index: 3; +} + +div#insert +{ + width: 120; + float: right; + text-align: right; +} + +.buttonBar +{ + height: 1.5em; + text-align: right; +} + +#copyright +{ + width: 800px; + text-align: center; + font: bold 90% Tahoma, sans-serif; + color: div#336633; +} + +#copyright img.divider +{ + margin: 4px 0; +} + +#copyright a:link, #copyright a:visited, #copyright a:active +{ + text-decoration: none; + color: #c50; +} + +#copyright a:hover +{ + color: #f60; + text-decoration: underline; +} + +img.imageButton +{ + padding-left: 4px; + padding-right: 4px; +} + +.readOnly +{ + color: #c0c0c0; +} + +.error +{ + color: red; + font-weight: bold; + font-family: Arial, sans-serif; + font-size: 90%; +} + +/* Table */ +.suggestedTable +{ + border: 1px solid #006; +} +.suggestedTableCaption th +{ + padding: 4px 2px; + margin: 4px; + line-height: 18px; + vertical-align: middle; + background-color: #006; + color: #fff; + font-size: 120%; +} +.suggestedTableColnames th +{ + text-align: left; + line-height: 16px; + padding: 4px 2px; + background-color: #159; + color: #fff; +} +.suggestedTableRow td +{ + text-align: left; + border-bottom: 1px solid #006; + padding: 4px 2px; +} + + +/* Text styles */ +content a +{ + text-decoration: none; + font-weight: bold; + color: #000; +} +content a:link +{ +} +content a:visited +{ +} +content a:active +{ +} +content a:hover +{ + text-decoration: underline; +} + +content h1 +{ + font-size: 2.0em; + font-weight: normal; + margin-top: 0em; + margin-bottom: 0em; +} + +content h2 +{ + font-size: 1.7em; + margin: 1.2em 0em 1.2em 0em; + font-weight: normal; +} + +content h3 +{ + font-size: 1.4em; + margin: 1.2em 0em 1.2em 0em; + font-weight: normal; +} + +content h4 +{ + font-size: 1.2em; + margin: 1.2em 0em 1.2em 0em; + font-weight: bold; +} + +content h5 +{ + font-size: 1.0em; + margin: 1.2em 0em 1.2em 0em; + font-weight: bold; +} + +content h6 +{ + font-size: 0.8em; + margin: 1.2em 0em 1.2em 0em; + font-weight: bold; +} + +content img +{ + border: 0; +} + +content ol, content ul, content li +{ + font-size: 1.0em; + line-height: 1.8em; + margin-top: 0.2em; + margin-bottom: 0.1em; +} + +content p +{ + font-size: 1.0em; + line-height: 1.8em; + margin: 1.2em 0em 1.2em 0em; +} + +content li > p +{ + margin-top: 0.2em; +} + +content pre +{ + font-family: monospace; + font-size: 1.0em; +} + +content strong, content b +{ + font-weight: bold; +} + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Home.aspx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Home.aspx new file mode 100644 index 00000000..b7d06249 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Home.aspx @@ -0,0 +1,27 @@ +<%@ Page language="c#" Inherits="SpringAir.Web.Home" CodeBehind="Home.aspx.cs" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> + + + + +

    + SpringAir models a flight reservation system. +
    + Using SpringAir, you can browse flights, book a trip and use Booking Agent Web Service. +

    +

    + SpringAir demonstrates the following features of Spring.NET's web support... +

    +
      +
    • Spring.NET IoC container configuration
    • +
    • Dependency Injection as applied to ASP.NET pages
    • +
    • Master Page support in ASP.NET 1.1
    • +
    • Bi-directional databinding
    • +
    • Web Service support
    • +
    • Declarative validation of domain objects
    • +
    • Internationalization
    • +
    • Result mapping to better encapsulate page navigation flows
    • +
    +
    + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Home.aspx.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Home.aspx.cs new file mode 100644 index 00000000..ef1db38b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Home.aspx.cs @@ -0,0 +1,66 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Common.Logging; +using System; +using Spring.Web.UI; +using Spring.Web.UI.Controls; + +#endregion + +namespace SpringAir.Web +{ + /// + /// The SpringAir home page. + /// + /// Rick Evans + /// $Id: Home.aspx.cs,v 1.2 2006/11/15 20:30:52 aseovic Exp $ + public class Home : Page + { + protected Content body; + private static readonly ILog logger = LogManager.GetLogger(typeof (Home)); + + private void Page_Load(object sender, EventArgs e) + { + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format(this.CultureResolver.ResolveCulture(), + "The [{0}] page is being loaded...", GetType().Name)); + } + } + + #region Web Form Designer generated code + + protected override void OnInit(EventArgs e) + { + InitializeComponent(); + base.OnInit(e); + } + + private void InitializeComponent() + { + this.Load += new EventHandler(this.Page_Load); + } + + #endregion + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Home.aspx.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Home.aspx.resx new file mode 100644 index 00000000..f0e11baa --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Home.aspx.resx @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/calendar-1.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/calendar-1.gif new file mode 100644 index 00000000..6774ca5c Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/calendar-1.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/calendar-2.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/calendar-2.gif new file mode 100644 index 00000000..9d5d666d Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/calendar-2.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/en/spring-air-logo.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/en/spring-air-logo.jpg new file mode 100644 index 00000000..21e6c575 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/en/spring-air-logo.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/line.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/line.jpg new file mode 100644 index 00000000..fedb4290 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/line.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-bottom-left.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-bottom-left.gif new file mode 100644 index 00000000..1323ec04 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-bottom-left.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-bottom-middle.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-bottom-middle.gif new file mode 100644 index 00000000..38aa28ea Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-bottom-middle.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-bottom-right.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-bottom-right.gif new file mode 100644 index 00000000..9815bced Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-bottom-right.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-left-middle.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-left-middle.gif new file mode 100644 index 00000000..76133d5e Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-left-middle.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-right-middle.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-right-middle.gif new file mode 100644 index 00000000..9b1140ef Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-right-middle.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-top-left.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-top-left.gif new file mode 100644 index 00000000..fade635c Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-top-left.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-top-middle.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-top-middle.gif new file mode 100644 index 00000000..f982015e Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-top-middle.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-top-right.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-top-right.gif new file mode 100644 index 00000000..bafcbecb Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/panel-top-right.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/spring-air-logo.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/spring-air-logo.jpg new file mode 100644 index 00000000..21e6c575 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/spring-air-logo.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/sr-SP-Cyrl/spring-air-logo.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/sr-SP-Cyrl/spring-air-logo.jpg new file mode 100644 index 00000000..bfeb3609 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/sr-SP-Cyrl/spring-air-logo.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/sr-SP-Latn/spring-air-logo.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/sr-SP-Latn/spring-air-logo.jpg new file mode 100644 index 00000000..36c8b822 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/sr-SP-Latn/spring-air-logo.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/validation-error.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/validation-error.gif new file mode 100644 index 00000000..0bf0858b Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Images/validation-error.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/StandardTemplate.ascx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/StandardTemplate.ascx new file mode 100644 index 00000000..9c205fe3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/StandardTemplate.ascx @@ -0,0 +1,49 @@ +<%@ Control language="c#" AutoEventWireup="false" inherits="SpringAir.Web.StandardTemplate" codebehind="StandardTemplate.ascx.cs" %> +<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> + + + + + <spring:ContentPlaceHolder id="title" runat="server"> + <%= GetMessage("default.title") %> + </spring:ContentPlaceHolder> + + + + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/StandardTemplate.ascx.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/StandardTemplate.ascx.cs new file mode 100644 index 00000000..00ce2df4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/StandardTemplate.ascx.cs @@ -0,0 +1,74 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Web.UI.WebControls; +using Spring.Web.UI; +using Spring.Web.UI.Controls; + +#endregion + +namespace SpringAir.Web +{ + /// + /// SpringAir master page. + /// + /// Rick Evans + public class StandardTemplate : MasterPage + { + protected ContentPlaceHolder head; + protected System.Web.UI.WebControls.Button submit; + protected Spring.Web.UI.Controls.Head Head1; + protected Spring.Web.UI.Controls.LocalizedImage logoImage; + protected System.Web.UI.HtmlControls.HtmlForm form; + protected ContentPlaceHolder body; + + protected LinkButton english; + protected LinkButton serbianLatin; + protected LinkButton serbianCyrillic; + + private void SetLanguage(object sender, CommandEventArgs e) + { + UserCulture = new CultureInfo((string) e.CommandArgument); + } + + + #region Web Form Designer generated code + + protected override void OnInit(EventArgs e) + { + InitializeComponent(); + base.OnInit(e); + } + + private void InitializeComponent() + { + this.english.Command += new CommandEventHandler(this.SetLanguage); + this.serbianLatin.Command += new CommandEventHandler(this.SetLanguage); + this.serbianCyrillic.Command += new CommandEventHandler(this.SetLanguage); + } + + #endregion + + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/StandardTemplate.ascx.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/StandardTemplate.ascx.resx new file mode 100644 index 00000000..b1fe6ff3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/StandardTemplate.ascx.resx @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + True + + + Private + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Web.config b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Web.config new file mode 100644 index 00000000..c3a1dd4a --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2003/Web/Web.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005.References/AssemblyInfo.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2005.References/AssemblyInfo.cs new file mode 100644 index 00000000..723658af --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005.References/AssemblyInfo.cs @@ -0,0 +1,50 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using Spring.Context.Support; + +[assembly: AssemblyTitle("SpringAir Web References")] +[assembly: AssemblyDescription("SpringAir Web References Only")] +[assembly: AssemblyVersion("1.0.0.0")] + +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +[assembly: AssemblyCompany("http://www.springframework.net/")] +[assembly: AssemblyProduct("Spring.NET")] +[assembly: AssemblyCopyright("Copyright © 2002-2005. Licensed under the Apache License, Version 2.0")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)] + +[assembly: ComVisible(false)] +[assembly: CLSCompliant(true)] + +namespace SpringAir +{ + // this is a "trick" to force csc to copy over referenced assemblies + internal class AssemblyReferences + { + private static Type SpringWeb = typeof(WebApplicationContext); + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005.References/SpringAir.Web.2005.References.csproj b/examples/Spring/SpringAir/src/SpringAir.Web.2005.References/SpringAir.Web.2005.References.csproj new file mode 100644 index 00000000..ad039549 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005.References/SpringAir.Web.2005.References.csproj @@ -0,0 +1,81 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {0925F8C7-B2AB-4DD7-AF6D-10DD028C5910} + Library + SpringAir + SpringAir.Web.2005.References + + + true + full + false + . + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + . + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\lib\Net\2.0\antlr.runtime.dll + + + False + ..\..\..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Aop.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Web.dll + + + + + + + + + + {5E3427D8-62FC-483F-A86B-B44A79A46BCC} + SpringAir.Core.2005 + + + {78B285FE-27F3-44F4-84B1-7A589AB48EF3} + SpringAir.Data.Ado.2005 + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_Code/Constants.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_Code/Constants.cs new file mode 100644 index 00000000..2ddd82b9 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_Code/Constants.cs @@ -0,0 +1,75 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +/// +/// Constants for all of the various lookup keys used throughout the +/// application (to pull objects from the session, request, etc). +/// +/// +///

    +/// Values could be injected into pages that needed them using the +/// +/// (if one wanted to go there). +///

    +///
    +/// Rick Evans +/// $Id: Constants.cs,v 1.1 2006/11/01 02:44:33 bbaia Exp $ +public sealed class Constants +{ + /// + /// The key used to index into the AppSettings dictionary containing + /// the path to the Log4Net configuration file. + /// + public const string Log4NetConfigFile = "Log4NetConfigFile"; + + /// + /// The key under which the suggested flights for a user's trip + /// search are stored. + /// + public const string SuggestedFlightsKey = "suggestedFlights"; + + /// + /// The key under which the reservation confirmation for a user's trip + /// search are stored. + /// + public const string ReservationConfirmationKey = "reservationConfirmation"; + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private Constants() + { + } + + #endregion +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.en.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.en.resx new file mode 100644 index 00000000..9488c89c --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.en.resx @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SpringAir - Spring.NET Reference Application + + + Flight # + + + Departure Time + + + Departure Airport + + + Destination Airport + + + Aircraft Type + + + Seat Plan + + + Outbound Flights + + + Return Flights + + + Itinerary + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.resx new file mode 100644 index 00000000..9488c89c --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.resx @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SpringAir - Spring.NET Reference Application + + + Flight # + + + Departure Time + + + Departure Airport + + + Destination Airport + + + Aircraft Type + + + Seat Plan + + + Outbound Flights + + + Return Flights + + + Itinerary + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-Cyrl-CS.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-Cyrl-CS.resx new file mode 100644 index 00000000..49fcdac9 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-Cyrl-CS.resx @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SpringAir - Референтна апликација за Spring.NET + + + Лет # + + + Време полетања + + + Ðеродром одлаÑка + + + Ðеродром долаÑка + + + Тип авиона + + + План Ñедишта + + + Одлазни летови + + + Повратни летови + + + РезервиÑани летови + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-Latn-CS.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-Latn-CS.resx new file mode 100644 index 00000000..3a381575 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-Latn-CS.resx @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SpringAir - Referentna aplikacija za Spring.NET + + + Let # + + + Vreme poletanja + + + Aerodrom odlaska + + + Aerodrom dolaska + + + Tip aviona + + + Plan sediÅ¡ta + + + Odlazni letovi + + + Povratni letovi + + + Rezervisani letovi + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-SP-Cyrl.resx.exclude b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-SP-Cyrl.resx.exclude new file mode 100644 index 00000000..49fcdac9 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-SP-Cyrl.resx.exclude @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SpringAir - Референтна апликација за Spring.NET + + + Лет # + + + Време полетања + + + Ðеродром одлаÑка + + + Ðеродром долаÑка + + + Тип авиона + + + План Ñедишта + + + Одлазни летови + + + Повратни летови + + + РезервиÑани летови + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-SP-Latn.resx.exclude b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-SP-Latn.resx.exclude new file mode 100644 index 00000000..3a381575 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/App_GlobalResources/Strings.sr-SP-Latn.resx.exclude @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + SpringAir - Referentna aplikacija za Spring.NET + + + Let # + + + Vreme poletanja + + + Aerodrom odlaska + + + Aerodrom dolaska + + + Tip aviona + + + Plan sediÅ¡ta + + + Odlazni letovi + + + Povratni letovi + + + Rezervisani letovi + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Aspects.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Aspects.xml new file mode 100644 index 00000000..63fd8d3f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Aspects.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + *Dao + + + + + CacheAspect + + + + + + + + + + bookingAgent + + + + + validationAdvice + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Log4Net.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Log4Net.xml new file mode 100644 index 00000000..8441780e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Log4Net.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Production/Dao.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Production/Dao.xml new file mode 100644 index 00000000..b587b497 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Production/Dao.xml @@ -0,0 +1,37 @@ + + + + + The SpringAir object definitions for the Data Access Objects. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Production/Services.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Production/Services.xml new file mode 100644 index 00000000..17dc3109 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Production/Services.xml @@ -0,0 +1,17 @@ + + + + + The SpringAir object definitions for the middle (service) tier. + + + + + + The main service interface for the SpringAir application. + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Services.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Services.xml new file mode 100644 index 00000000..ca2763fb --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Services.xml @@ -0,0 +1,35 @@ + + + + + The SpringAir object definitions for the middle (service) tier. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Test/Dao.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Test/Dao.xml new file mode 100644 index 00000000..484d71b8 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Test/Dao.xml @@ -0,0 +1,13 @@ + + + + + The SpringAir object definitions for the Data Access Objects. + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Test/Services.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Test/Services.xml new file mode 100644 index 00000000..bd2a6f89 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Test/Services.xml @@ -0,0 +1,14 @@ + + + + + The SpringAir object definitions for the middle (service) tier. + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Validation.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Validation.xml new file mode 100644 index 00000000..4816dc0e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Validation.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Web.xml b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Web.xml new file mode 100644 index 00000000..16e71ef8 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Config/Web.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + Resources.Strings, App_GlobalResources + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Global.asax b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Global.asax new file mode 100644 index 00000000..f859930c --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Global.asax @@ -0,0 +1,32 @@ +<%@ Application Language="C#" %> + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-blue.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-blue.css new file mode 100644 index 00000000..62ecf99e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-blue.css @@ -0,0 +1,232 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #556; + font-size: 11px; + color: #000; + cursor: default; + background: #eef; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ +} + +.calendar .nav { + background: #778 url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + background: #fff; + color: #000; + padding: 2px; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ + background: #778; + color: #fff; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #bdf; +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #556; + padding: 2px; + text-align: center; + color: #000; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #a66; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #aaf; + color: #000; + border: 1px solid #04f; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #77c; + padding: 2px 0px 0px 2px; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + color: #456; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #bbb; +} +.calendar tbody .day.othermonth.oweekend { + color: #fbb; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #bdf; +} + +.calendar tbody .rowhilite td { + background: #def; +} + +.calendar tbody .rowhilite td.wn { + background: #eef; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #def; + padding: 1px 3px 1px 1px; + border: 1px solid #bbb; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #cde; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #fff; + color: #000; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #a66; +} + +.calendar tbody td.today { /* Cell showing selected date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ + text-align: center; + background: #556; + color: #fff; +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #fff; + color: #445; + border-top: 1px solid #556; + padding: 1px; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #aaf; + border: 1px solid #04f; + color: #000; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #77c; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #655; + background: #def; + color: #000; + font-size: 90%; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .hilite { + background: #acf; +} + +.calendar .combo .active { + border-top: 1px solid #46a; + border-bottom: 1px solid #46a; + background: #eef; + font-weight: bold; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #f4f0e8; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #667; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-blue2.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-blue2.css new file mode 100644 index 00000000..8570affc --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-blue2.css @@ -0,0 +1,236 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #206A9B; + font-size: 11px; + color: #000; + cursor: default; + background: #F1F8FC; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ +} + +.calendar .nav { + background: #007ED1 url(menuarrow2.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + background: #000; + color: #fff; + padding: 2px; +} + +.calendar thead tr { /* Row containing navigation buttons */ + background: #007ED1; + color: #fff; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #C7E1F3; +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #206A9B; + padding: 2px; + text-align: center; + color: #000; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #a66; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #34ABFA; + color: #000; + border: 1px solid #016DC5; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #006AA9; + border: 1px solid #008AFF; + padding: 2px 0px 0px 2px; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + color: #456; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #bbb; +} +.calendar tbody .day.othermonth.oweekend { + color: #fbb; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #C7E1F3; +} + +.calendar tbody .rowhilite td { + background: #def; +} + +.calendar tbody .rowhilite td.wn { + background: #F1F8FC; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #def; + padding: 1px 3px 1px 1px; + border: 1px solid #8FC4E8; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #cde; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #fff; + color: #000; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #a66; +} + +.calendar tbody td.today { /* Cell showing selected date */ + font-weight: bold; + color: #D50000; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ + text-align: center; + background: #206A9B; + color: #fff; +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #000; + color: #fff; + border-top: 1px solid #206A9B; + padding: 1px; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #B8DAF0; + border: 1px solid #178AEB; + color: #000; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #006AA9; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #655; + background: #def; + color: #000; + font-size: 90%; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .hilite { + background: #34ABFA; + border-top: 1px solid #46a; + border-bottom: 1px solid #46a; + font-weight: bold; +} + +.calendar .combo .active { + border-top: 1px solid #46a; + border-bottom: 1px solid #46a; + background: #F1F8FC; + font-weight: bold; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #E3F0F9; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #F1F8FC; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #267DB7; + color: #fff; +} + +.calendar td.time span.active { + border-color: red; + background-color: #000; + color: #A5FF00; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-brown.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-brown.css new file mode 100644 index 00000000..e7b6285b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-brown.css @@ -0,0 +1,225 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #655; + font-size: 11px; + color: #000; + cursor: default; + background: #ffd; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ +} + +.calendar .nav { + background: #edc url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + background: #654; + color: #fed; + padding: 2px; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ + background: #edc; + color: #000; +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #655; + padding: 2px; + text-align: center; + color: #000; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #faa; + color: #000; + border: 1px solid #f40; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #c77; + padding: 2px 0px 0px 2px; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #fed; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #bbb; +} +.calendar tbody .day.othermonth.oweekend { + color: #fbb; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #fed; +} + +.calendar tbody .rowhilite td { + background: #ddf; +} + +.calendar tbody .rowhilite td.wn { + background: #efe; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #ffe; + padding: 1px 3px 1px 1px; + border: 1px solid #bbb; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #ddc; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #fea; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { font-weight: bold; } + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ + text-align: center; + background: #988; + color: #000; +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + border-top: 1px solid #655; + background: #dcb; + color: #840; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #faa; + border: 1px solid #f40; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #c77; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #655; + background: #ffe; + color: #000; + font-size: 90%; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .hilite { + background: #fc8; +} + +.calendar .combo .active { + border-top: 1px solid #a64; + border-bottom: 1px solid #a64; + background: #fee; + font-weight: bold; +} + +.calendar td.time { + border-top: 1px solid #a88; + padding: 1px 0px; + text-align: center; + background-color: #fed; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #988; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #866; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-green.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-green.css new file mode 100644 index 00000000..564666d4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-green.css @@ -0,0 +1,229 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #565; + font-size: 11px; + color: #000; + cursor: default; + background: #efe; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ + background: #676; + color: #fff; + font-size: 90%; +} + +.calendar .nav { + background: #676 url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + padding: 2px; + background: #250; + color: #efa; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #565; + padding: 2px; + text-align: center; + color: #000; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #a66; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #afa; + color: #000; + border: 1px solid #084; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #7c7; + padding: 2px 0px 0px 2px; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #dfb; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + color: #564; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #bbb; +} +.calendar tbody .day.othermonth.oweekend { + color: #fbb; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #8a8; + background: #dfb; +} + +.calendar tbody .rowhilite td { + background: #dfd; +} + +.calendar tbody .rowhilite td.wn { + background: #efe; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #efd; + padding: 1px 3px 1px 1px; + border: 1px solid #bbb; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #dec; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #f8fff8; + color: #000; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #a66; +} + +.calendar tbody td.today { font-weight: bold; color: #0a0; } + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ + text-align: center; + background: #565; + color: #fff; +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + padding: 2px; + background: #250; + color: #efa; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #afa; + border: 1px solid #084; + color: #000; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #7c7; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #565; + background: #efd; + color: #000; + font-size: 90%; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .hilite { + background: #af8; +} + +.calendar .combo .active { + border-top: 1px solid #6a4; + border-bottom: 1px solid #6a4; + background: #efe; + font-weight: bold; +} + +.calendar td.time { + border-top: 1px solid #8a8; + padding: 1px 0px; + text-align: center; + background-color: #dfb; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #898; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #686; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-setup.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-setup.js new file mode 100644 index 00000000..36697064 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-setup.js @@ -0,0 +1,200 @@ +/* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/ + * --------------------------------------------------------------------------- + * + * The DHTML Calendar + * + * Details and latest version at: + * http://dynarch.com/mishoo/calendar.epl + * + * This script is distributed under the GNU Lesser General Public License. + * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html + * + * This file defines helper functions for setting up the calendar. They are + * intended to help non-programmers get a working calendar on their site + * quickly. This script should not be seen as part of the calendar. It just + * shows you what one can do with the calendar, while in the same time + * providing a quick and simple method for setting it up. If you need + * exhaustive customization of the calendar creation process feel free to + * modify this code to suit your needs (this is recommended and much better + * than modifying calendar.js itself). + */ + +// $Id: calendar-setup.js,v 1.1 2006/11/01 02:44:35 bbaia Exp $ + +/** + * This function "patches" an input field (or other element) to use a calendar + * widget for date selection. + * + * The "params" is a single object that can have the following properties: + * + * prop. name | description + * ------------------------------------------------------------------------------------------------- + * inputField | the ID of an input field to store the date + * displayArea | the ID of a DIV or other element to show the date + * button | ID of a button or other element that will trigger the calendar + * eventName | event that will trigger the calendar, without the "on" prefix (default: "click") + * ifFormat | date format that will be stored in the input field + * daFormat | the date format that will be used to display the date in displayArea + * singleClick | (true/false) wether the calendar is in single click mode or not (default: true) + * firstDay | numeric: 0 to 6. "0" means display Sunday first, "1" means display Monday first, etc. + * align | alignment (default: "Br"); if you don't know what's this see the calendar documentation + * range | array with 2 elements. Default: [1900, 2999] -- the range of years available + * weekNumbers | (true/false) if it's true (default) the calendar will display week numbers + * flat | null or element ID; if not null the calendar will be a flat calendar having the parent with the given ID + * flatCallback | function that receives a JS Date object and returns an URL to point the browser to (for flat calendar) + * disableFunc | function that receives a JS Date object and should return true if that date has to be disabled in the calendar + * onSelect | function that gets called when a date is selected. You don't _have_ to supply this (the default is generally okay) + * onClose | function that gets called when the calendar is closed. [default] + * onUpdate | function that gets called after the date is updated in the input field. Receives a reference to the calendar. + * date | the date that the calendar will be initially displayed to + * showsTime | default: false; if true the calendar will include a time selector + * timeFormat | the time format; can be "12" or "24", default is "12" + * electric | if true (default) then given fields/date areas are updated for each move; otherwise they're updated only on close + * step | configures the step of the years in drop-down boxes; default: 2 + * position | configures the calendar absolute position; default: null + * cache | if "true" (but default: "false") it will reuse the same calendar object, where possible + * showOthers | if "true" (but default: "false") it will show days from other months too + * + * None of them is required, they all have default values. However, if you + * pass none of "inputField", "displayArea" or "button" you'll get a warning + * saying "nothing to setup". + */ +Calendar.setup = function (params) { + function param_default(pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } }; + + param_default("inputField", null); + param_default("displayArea", null); + param_default("button", null); + param_default("eventName", "click"); + param_default("ifFormat", Calendar._TT["DEF_DATE_FORMAT"]); + param_default("daFormat", Calendar._TT["DEF_DATE_FORMAT"]); + param_default("singleClick", true); + param_default("disableFunc", null); + param_default("dateStatusFunc", params["disableFunc"]); // takes precedence if both are defined + param_default("dateText", null); + param_default("firstDay", null); + param_default("align", "Bl"); + param_default("range", [1900, 2999]); + param_default("weekNumbers", true); + param_default("flat", null); + param_default("flatCallback", null); + param_default("onSelect", null); + param_default("onClose", null); + param_default("onUpdate", null); + param_default("date", null); + param_default("showsTime", false); + param_default("timeFormat", "24"); + param_default("electric", true); + param_default("step", 1); + param_default("position", null); + param_default("cache", false); + param_default("showOthers", false); + param_default("multiple", null); + + var tmp = ["inputField", "displayArea", "button"]; + for (var i in tmp) { + if (typeof params[tmp[i]] == "string") { + params[tmp[i]] = document.getElementById(params[tmp[i]]); + } + } + if (!(params.flat || params.multiple || params.inputField || params.displayArea || params.button)) { + alert("Calendar.setup:\n Nothing to setup (no fields found). Please check your code"); + return false; + } + + function onSelect(cal) { + var p = cal.params; + var update = (cal.dateClicked || p.electric); + if (update && p.inputField) { + p.inputField.value = cal.date.print(p.ifFormat); + if (typeof p.inputField.onchange == "function") + p.inputField.onchange(); + } + if (update && p.displayArea) + p.displayArea.innerHTML = cal.date.print(p.daFormat); + if (update && typeof p.onUpdate == "function") + p.onUpdate(cal); + if (update && p.flat) { + if (typeof p.flatCallback == "function") + p.flatCallback(cal); + } + if (update && p.singleClick && cal.dateClicked) + cal.callCloseHandler(); + }; + + if (params.flat != null) { + if (typeof params.flat == "string") + params.flat = document.getElementById(params.flat); + if (!params.flat) { + alert("Calendar.setup:\n Flat specified but can't find parent."); + return false; + } + var cal = new Calendar(params.firstDay, params.date, params.onSelect || onSelect); + cal.showsOtherMonths = params.showOthers; + cal.showsTime = params.showsTime; + cal.time24 = (params.timeFormat == "24"); + cal.params = params; + cal.weekNumbers = params.weekNumbers; + cal.setRange(params.range[0], params.range[1]); + cal.setDateStatusHandler(params.dateStatusFunc); + cal.getDateText = params.dateText; + if (params.ifFormat) { + cal.setDateFormat(params.ifFormat); + } + if (params.inputField && typeof params.inputField.value == "string") { + cal.parseDate(params.inputField.value); + } + cal.create(params.flat); + cal.show(); + return false; + } + + var triggerEl = params.button || params.displayArea || params.inputField; + triggerEl["on" + params.eventName] = function() { + var dateEl = params.inputField || params.displayArea; + var dateFmt = params.inputField ? params.ifFormat : params.daFormat; + var mustCreate = false; + var cal = window.calendar; + if (dateEl) + params.date = Date.parseDate(dateEl.value || dateEl.innerHTML, dateFmt); + if (!(cal && params.cache)) { + window.calendar = cal = new Calendar(params.firstDay, + params.date, + params.onSelect || onSelect, + params.onClose || function(cal) { cal.hide(); }); + cal.showsTime = params.showsTime; + cal.time24 = (params.timeFormat == "24"); + cal.weekNumbers = params.weekNumbers; + mustCreate = true; + } else { + if (params.date) + cal.setDate(params.date); + cal.hide(); + } + if (params.multiple) { + cal.multiple = {}; + for (var i = params.multiple.length; --i >= 0;) { + var d = params.multiple[i]; + var ds = d.print("%Y%m%d"); + cal.multiple[ds] = d; + } + } + cal.showsOtherMonths = params.showOthers; + cal.yearStep = params.step; + cal.setRange(params.range[0], params.range[1]); + cal.params = params; + cal.setDateStatusHandler(params.dateStatusFunc); + cal.getDateText = params.dateText; + cal.setDateFormat(dateFmt); + if (mustCreate) + cal.create(); + cal.refresh(); + if (!params.position) + cal.showAtElement(params.button || params.displayArea || params.inputField, params.align); + else + cal.showAt(params.position[0], params.position[1]); + return false; + }; + + return cal; +}; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-system.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-system.css new file mode 100644 index 00000000..f64493b4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-system.css @@ -0,0 +1,251 @@ +/* The main calendar widget. DIV containing a table. */ + +.calendar { + position: relative; + display: none; + border: 1px solid; + border-color: #fff #000 #000 #fff; + font-size: 11px; + cursor: default; + background: Window; + color: WindowText; + font-family: tahoma,verdana,sans-serif; +} + +.calendar table { + border: 1px solid; + border-color: #fff #000 #000 #fff; + font-size: 11px; + cursor: default; + background: Window; + color: WindowText; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; + padding: 1px; + border: 1px solid; + border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight; + background: ButtonFace; +} + +.calendar .nav { + background: ButtonFace url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; + padding: 1px; + border: 1px solid #000; + background: ActiveCaption; + color: CaptionText; + text-align: center; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid ButtonShadow; + padding: 2px; + text-align: center; + background: ButtonFace; + color: ButtonText; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border: 2px solid; + padding: 0px; + border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + border-width: 1px; + padding: 2px 0px 0px 2px; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid ButtonShadow; + background: ButtonFace; + color: ButtonText; +} + +.calendar tbody .rowhilite td { + background: Highlight; + color: HighlightText; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border: 1px solid; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + border: 1px solid; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; + padding: 2px 2px 0px 2px; + background: ButtonFace; + color: ButtonText; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody td.disabled { color: GrayText; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: ButtonFace; + padding: 1px; + border: 1px solid; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; + color: ButtonText; + text-align: center; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + padding: 1px; + background: #e4e0d8; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + width: 4em; + top: 0px; + left: 0px; + cursor: default; + border: 1px solid; + border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight; + background: Menu; + color: MenuText; + font-size: 90%; + padding: 1px; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .active { + padding: 0px; + border: 1px solid #000; +} + +.calendar .combo .hilite { + background: Highlight; + color: HighlightText; +} + +.calendar td.time { + border-top: 1px solid ButtonShadow; + padding: 1px 0px; + text-align: center; + background-color: ButtonFace; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: Menu; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: Highlight; + color: HighlightText; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-tas.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-tas.css new file mode 100644 index 00000000..a0c9c9d1 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-tas.css @@ -0,0 +1,239 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #655; + font-size: 11px; + color: #000; + cursor: default; + background: #ffd; + font-family: tahoma,verdana,sans-serif; + filter: +progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#DDDCCC,EndColorStr=#FFFFFF); +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ + color:#363636; +} + +.calendar .nav { + background: #edc url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + background: #654; + color: #363636; + padding: 2px; + filter: +progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#ffffff,EndColorStr=#dddccc); +} + +.calendar thead .headrow { /* Row containing navigation buttons */ + /*background: #3B86A0;*/ + color: #363636; + font-weight: bold; +filter: +progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#ffffff,EndColorStr=#3b86a0); +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #655; + padding: 2px; + text-align: center; + color: #363636; + filter: +progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#DDDCCC,EndColorStr=#FFFFFF); +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #ffcc86; + color: #000; + border: 1px solid #b59345; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #c77; + padding: 2px 0px 0px 2px; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #fed; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #fed; +} + +.calendar tbody .rowhilite td { + background: #ddf; + +} + +.calendar tbody .rowhilite td.wn { + background: #efe; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #ffe; + padding: 1px 3px 1px 1px; + border: 1px solid #bbb; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #ddc; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #fea; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { font-weight: bold; } + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ + text-align: center; + background: #988; + color: #000; + +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + border-top: 1px solid #655; + background: #dcb; + color: #363636; + font-weight: bold; + filter: +progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#FFFFFF,EndColorStr=#DDDCCC); +} +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #faa; + border: 1px solid #f40; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #c77; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #655; + background: #ffe; + color: #000; + font-size: smaller; + z-index: 100; +} + +.combo .label, +.combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.combo .label-IEfix { + width: 4em; +} + +.combo .hilite { + background: #fc8; +} + +.combo .active { + border-top: 1px solid #a64; + border-bottom: 1px solid #a64; + background: #fee; + font-weight: bold; +} + +.calendar td.time { + border-top: 1px solid #a88; + padding: 1px 0px; + text-align: center; + background-color: #fed; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #988; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #866; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-1.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-1.css new file mode 100644 index 00000000..56ffdf2e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-1.css @@ -0,0 +1,271 @@ +/* The main calendar widget. DIV containing a table. */ + +.calendar { + position: relative; + display: none; + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + font-size: 11px; + color: #000; + cursor: default; + background: #d4d0c8; + font-family: tahoma,verdana,sans-serif; +} + +.calendar table { + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + font-size: 11px; + color: #000; + cursor: default; + background: #d4d0c8; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; + padding: 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar .nav { + background: transparent url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; + padding: 1px; + border: 1px solid #000; + background: #848078; + color: #fff; + text-align: center; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #000; + padding: 2px; + text-align: center; + background: #f4f0e8; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + padding: 0px; + background-color: #e4e0d8; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + background-color: #c4c0b8; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #f4f0e8; +} + +.calendar tbody .rowhilite td { + background: #e4e0d8; +} + +.calendar tbody .rowhilite td.wn { + background: #d4d0c8; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + padding: 2px 2px 0px 2px; + background: #e4e0d8; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #f4f0e8; + padding: 1px; + border: 1px solid #000; + background: #848078; + color: #fff; + text-align: center; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + padding: 1px; + background: #e4e0d8; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + width: 4em; + top: 0px; + left: 0px; + cursor: default; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + background: #e4e0d8; + font-size: 90%; + padding: 1px; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .active { + background: #c4c0b8; + padding: 0px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar .combo .hilite { + background: #048; + color: #fea; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #f4f0e8; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #766; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-2.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-2.css new file mode 100644 index 00000000..ccb85a01 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-2.css @@ -0,0 +1,271 @@ +/* The main calendar widget. DIV containing a table. */ + +.calendar { + position: relative; + display: none; + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + font-size: 11px; + color: #000; + cursor: default; + background: #d4c8d0; + font-family: tahoma,verdana,sans-serif; +} + +.calendar table { + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + font-size: 11px; + color: #000; + cursor: default; + background: #d4c8d0; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; + padding: 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar .nav { + background: transparent url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; + padding: 1px; + border: 1px solid #000; + background: #847880; + color: #fff; + text-align: center; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #000; + padding: 2px; + text-align: center; + background: #f4e8f0; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + padding: 0px; + background-color: #e4d8e0; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + background-color: #c4b8c0; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #f4e8f0; +} + +.calendar tbody .rowhilite td { + background: #e4d8e0; +} + +.calendar tbody .rowhilite td.wn { + background: #d4c8d0; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + padding: 2px 2px 0px 2px; + background: #e4d8e0; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #f4e8f0; + padding: 1px; + border: 1px solid #000; + background: #847880; + color: #fff; + text-align: center; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + padding: 1px; + background: #e4d8e0; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + width: 4em; + top: 0px; + left: 0px; + cursor: default; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + background: #e4d8e0; + font-size: 90%; + padding: 1px; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .active { + background: #d4c8d0; + padding: 0px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar .combo .hilite { + background: #408; + color: #fea; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #f4f0e8; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #766; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-cold-1.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-cold-1.css new file mode 100644 index 00000000..09a1a806 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-cold-1.css @@ -0,0 +1,265 @@ +/* The main calendar widget. DIV containing a table. */ + +.calendar { + position: relative; + display: none; + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + font-size: 11px; + color: #000; + cursor: default; + background: #c8d0d4; + font-family: tahoma,verdana,sans-serif; +} + +.calendar table { + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + font-size: 11px; + color: #000; + cursor: default; + background: #c8d0d4; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; + padding: 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar .nav { + background: transparent url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; + padding: 1px; + border: 1px solid #000; + background: #788084; + color: #fff; + text-align: center; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #000; + padding: 2px; + text-align: center; + background: #e8f0f4; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + padding: 0px; + background-color: #d8e0e4; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + background-color: #b8c0c4; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #e8f4f0; +} + +.calendar tbody .rowhilite td { + background: #d8e4e0; +} + +.calendar tbody .rowhilite td.wn { + background: #c8d4d0; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border: 1px solid; + border-color: #fff #000 #000 #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border: 1px solid; + border-color: #000 #fff #fff #000; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + padding: 2px 2px 0px 2px; + border: 1px solid; + border-color: #000 #fff #fff #000; + background: #d8e0e4; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #e8f0f4; + padding: 1px; + border: 1px solid #000; + background: #788084; + color: #fff; + text-align: center; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + padding: 1px; + background: #d8e0e4; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + width: 4em; + top: 0px; + left: 0px; + cursor: default; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + background: #d8e0e4; + font-size: 90%; + padding: 1px; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .active { + background: #c8d0d4; + padding: 0px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar .combo .hilite { + background: #048; + color: #aef; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #e8f0f4; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #667; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-cold-2.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-cold-2.css new file mode 100644 index 00000000..0ae6b08e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar-win2k-cold-2.css @@ -0,0 +1,271 @@ +/* The main calendar widget. DIV containing a table. */ + +.calendar { + position: relative; + display: none; + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + font-size: 11px; + color: #000; + cursor: default; + background: #c8d4d0; + font-family: tahoma,verdana,sans-serif; +} + +.calendar table { + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + font-size: 11px; + color: #000; + cursor: default; + background: #c8d4d0; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; + padding: 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar .nav { + background: transparent url(menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; + padding: 1px; + border: 1px solid #000; + background: #788480; + color: #fff; + text-align: center; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #000; + padding: 2px; + text-align: center; + background: #e8f4f0; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + padding: 0px; + background-color: #d8e4e0; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + background-color: #b8c4c0; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #e8f4f0; +} + +.calendar tbody .rowhilite td { + background: #d8e4e0; +} + +.calendar tbody .rowhilite td.wn { + background: #c8d4d0; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + padding: 2px 2px 0px 2px; + background: #d8e4e0; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The in footer (only one right now) */ +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #e8f4f0; + padding: 1px; + border: 1px solid #000; + background: #788480; + color: #fff; + text-align: center; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + padding: 1px; + background: #d8e4e0; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + padding: 2px 0px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + width: 4em; + top: 0px; + left: 0px; + cursor: default; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; + background: #d8e4e0; + font-size: 90%; + padding: 1px; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .active { + background: #c8d4d0; + padding: 0px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar .combo .hilite { + background: #048; + color: #aef; +} + +.calendar td.time { + border-top: 1px solid #000; + padding: 1px 0px; + text-align: center; + background-color: #e8f0f4; +} + +.calendar td.time .hour, +.calendar td.time .minute, +.calendar td.time .ampm { + padding: 0px 3px 0px 4px; + border: 1px solid #889; + font-weight: bold; + background-color: #fff; +} + +.calendar td.time .ampm { + text-align: center; +} + +.calendar td.time .colon { + padding: 0px 2px 0px 3px; + font-weight: bold; +} + +.calendar td.time span.hilite { + border-color: #000; + background-color: #667; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar.js new file mode 100644 index 00000000..badc2ffd --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/calendar.js @@ -0,0 +1,1806 @@ +/* Copyright Mihai Bazon, 2002-2005 | www.bazon.net/mishoo + * ----------------------------------------------------------- + * + * The DHTML Calendar, version 1.0 "It is happening again" + * + * Details and latest version at: + * www.dynarch.com/projects/calendar + * + * This script is developed by Dynarch.com. Visit us at www.dynarch.com. + * + * This script is distributed under the GNU Lesser General Public License. + * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html + */ + +// $Id: calendar.js,v 1.1 2006/11/01 02:44:36 bbaia Exp $ + +/** The Calendar object constructor. */ +Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) { + // member variables + this.activeDiv = null; + this.currentDateEl = null; + this.getDateStatus = null; + this.getDateToolTip = null; + this.getDateText = null; + this.timeout = null; + this.onSelected = onSelected || null; + this.onClose = onClose || null; + this.dragging = false; + this.hidden = false; + this.minYear = 1970; + this.maxYear = 2050; + this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"]; + this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"]; + this.isPopup = true; + this.weekNumbers = true; + this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc. + this.showsOtherMonths = false; + this.dateStr = dateStr; + this.ar_days = null; + this.showsTime = false; + this.time24 = true; + this.yearStep = 2; + this.hiliteToday = true; + this.multiple = null; + // HTML elements + this.table = null; + this.element = null; + this.tbody = null; + this.firstdayname = null; + // Combo boxes + this.monthsCombo = null; + this.yearsCombo = null; + this.hilitedMonth = null; + this.activeMonth = null; + this.hilitedYear = null; + this.activeYear = null; + // Information + this.dateClicked = false; + + // one-time initializations + if (typeof Calendar._SDN == "undefined") { + // table of short day names + if (typeof Calendar._SDN_len == "undefined") + Calendar._SDN_len = 3; + var ar = new Array(); + for (var i = 8; i > 0;) { + ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len); + } + Calendar._SDN = ar; + // table of short month names + if (typeof Calendar._SMN_len == "undefined") + Calendar._SMN_len = 3; + ar = new Array(); + for (var i = 12; i > 0;) { + ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len); + } + Calendar._SMN = ar; + } +}; + +// ** constants + +/// "static", needed for event handlers. +Calendar._C = null; + +/// detect a special case of "web browser" +Calendar.is_ie = ( /msie/i.test(navigator.userAgent) && + !/opera/i.test(navigator.userAgent) ); + +Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) ); + +/// detect Opera browser +Calendar.is_opera = /opera/i.test(navigator.userAgent); + +/// detect KHTML-based browsers +Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent); + +// BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate +// library, at some point. + +Calendar.getAbsolutePos = function(el) { + var SL = 0, ST = 0; + var is_div = /^div$/i.test(el.tagName); + if (is_div && el.scrollLeft) + SL = el.scrollLeft; + if (is_div && el.scrollTop) + ST = el.scrollTop; + var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST }; + if (el.offsetParent) { + var tmp = this.getAbsolutePos(el.offsetParent); + r.x += tmp.x; + r.y += tmp.y; + } + return r; +}; + +Calendar.isRelated = function (el, evt) { + var related = evt.relatedTarget; + if (!related) { + var type = evt.type; + if (type == "mouseover") { + related = evt.fromElement; + } else if (type == "mouseout") { + related = evt.toElement; + } + } + while (related) { + if (related == el) { + return true; + } + related = related.parentNode; + } + return false; +}; + +Calendar.removeClass = function(el, className) { + if (!(el && el.className)) { + return; + } + var cls = el.className.split(" "); + var ar = new Array(); + for (var i = cls.length; i > 0;) { + if (cls[--i] != className) { + ar[ar.length] = cls[i]; + } + } + el.className = ar.join(" "); +}; + +Calendar.addClass = function(el, className) { + Calendar.removeClass(el, className); + el.className += " " + className; +}; + +// FIXME: the following 2 functions totally suck, are useless and should be replaced immediately. +Calendar.getElement = function(ev) { + var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget; + while (f.nodeType != 1 || /^div$/i.test(f.tagName)) + f = f.parentNode; + return f; +}; + +Calendar.getTargetElement = function(ev) { + var f = Calendar.is_ie ? window.event.srcElement : ev.target; + while (f.nodeType != 1) + f = f.parentNode; + return f; +}; + +Calendar.stopEvent = function(ev) { + ev || (ev = window.event); + if (Calendar.is_ie) { + ev.cancelBubble = true; + ev.returnValue = false; + } else { + ev.preventDefault(); + ev.stopPropagation(); + } + return false; +}; + +Calendar.addEvent = function(el, evname, func) { + if (el.attachEvent) { // IE + el.attachEvent("on" + evname, func); + } else if (el.addEventListener) { // Gecko / W3C + el.addEventListener(evname, func, true); + } else { + el["on" + evname] = func; + } +}; + +Calendar.removeEvent = function(el, evname, func) { + if (el.detachEvent) { // IE + el.detachEvent("on" + evname, func); + } else if (el.removeEventListener) { // Gecko / W3C + el.removeEventListener(evname, func, true); + } else { + el["on" + evname] = null; + } +}; + +Calendar.createElement = function(type, parent) { + var el = null; + if (document.createElementNS) { + // use the XHTML namespace; IE won't normally get here unless + // _they_ "fix" the DOM2 implementation. + el = document.createElementNS("http://www.w3.org/1999/xhtml", type); + } else { + el = document.createElement(type); + } + if (typeof parent != "undefined") { + parent.appendChild(el); + } + return el; +}; + +// END: UTILITY FUNCTIONS + +// BEGIN: CALENDAR STATIC FUNCTIONS + +/** Internal -- adds a set of events to make some element behave like a button. */ +Calendar._add_evs = function(el) { + with (Calendar) { + addEvent(el, "mouseover", dayMouseOver); + addEvent(el, "mousedown", dayMouseDown); + addEvent(el, "mouseout", dayMouseOut); + if (is_ie) { + addEvent(el, "dblclick", dayMouseDblClick); + el.setAttribute("unselectable", true); + } + } +}; + +Calendar.findMonth = function(el) { + if (typeof el.month != "undefined") { + return el; + } else if (typeof el.parentNode.month != "undefined") { + return el.parentNode; + } + return null; +}; + +Calendar.findYear = function(el) { + if (typeof el.year != "undefined") { + return el; + } else if (typeof el.parentNode.year != "undefined") { + return el.parentNode; + } + return null; +}; + +Calendar.showMonthsCombo = function () { + var cal = Calendar._C; + if (!cal) { + return false; + } + var cal = cal; + var cd = cal.activeDiv; + var mc = cal.monthsCombo; + if (cal.hilitedMonth) { + Calendar.removeClass(cal.hilitedMonth, "hilite"); + } + if (cal.activeMonth) { + Calendar.removeClass(cal.activeMonth, "active"); + } + var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()]; + Calendar.addClass(mon, "active"); + cal.activeMonth = mon; + var s = mc.style; + s.display = "block"; + if (cd.navtype < 0) + s.left = cd.offsetLeft + "px"; + else { + var mcw = mc.offsetWidth; + if (typeof mcw == "undefined") + // Konqueror brain-dead techniques + mcw = 50; + s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px"; + } + s.top = (cd.offsetTop + cd.offsetHeight) + "px"; +}; + +Calendar.showYearsCombo = function (fwd) { + var cal = Calendar._C; + if (!cal) { + return false; + } + var cal = cal; + var cd = cal.activeDiv; + var yc = cal.yearsCombo; + if (cal.hilitedYear) { + Calendar.removeClass(cal.hilitedYear, "hilite"); + } + if (cal.activeYear) { + Calendar.removeClass(cal.activeYear, "active"); + } + cal.activeYear = null; + var Y = cal.date.getFullYear() + (fwd ? 1 : -1); + var yr = yc.firstChild; + var show = false; + for (var i = 12; i > 0; --i) { + if (Y >= cal.minYear && Y <= cal.maxYear) { + yr.innerHTML = Y; + yr.year = Y; + yr.style.display = "block"; + show = true; + } else { + yr.style.display = "none"; + } + yr = yr.nextSibling; + Y += fwd ? cal.yearStep : -cal.yearStep; + } + if (show) { + var s = yc.style; + s.display = "block"; + if (cd.navtype < 0) + s.left = cd.offsetLeft + "px"; + else { + var ycw = yc.offsetWidth; + if (typeof ycw == "undefined") + // Konqueror brain-dead techniques + ycw = 50; + s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px"; + } + s.top = (cd.offsetTop + cd.offsetHeight) + "px"; + } +}; + +// event handlers + +Calendar.tableMouseUp = function(ev) { + var cal = Calendar._C; + if (!cal) { + return false; + } + if (cal.timeout) { + clearTimeout(cal.timeout); + } + var el = cal.activeDiv; + if (!el) { + return false; + } + var target = Calendar.getTargetElement(ev); + ev || (ev = window.event); + Calendar.removeClass(el, "active"); + if (target == el || target.parentNode == el) { + Calendar.cellClick(el, ev); + } + var mon = Calendar.findMonth(target); + var date = null; + if (mon) { + date = new Date(cal.date); + if (mon.month != date.getMonth()) { + date.setMonth(mon.month); + cal.setDate(date); + cal.dateClicked = false; + cal.callHandler(); + } + } else { + var year = Calendar.findYear(target); + if (year) { + date = new Date(cal.date); + if (year.year != date.getFullYear()) { + date.setFullYear(year.year); + cal.setDate(date); + cal.dateClicked = false; + cal.callHandler(); + } + } + } + with (Calendar) { + removeEvent(document, "mouseup", tableMouseUp); + removeEvent(document, "mouseover", tableMouseOver); + removeEvent(document, "mousemove", tableMouseOver); + cal._hideCombos(); + _C = null; + return stopEvent(ev); + } +}; + +Calendar.tableMouseOver = function (ev) { + var cal = Calendar._C; + if (!cal) { + return; + } + var el = cal.activeDiv; + var target = Calendar.getTargetElement(ev); + if (target == el || target.parentNode == el) { + Calendar.addClass(el, "hilite active"); + Calendar.addClass(el.parentNode, "rowhilite"); + } else { + if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2))) + Calendar.removeClass(el, "active"); + Calendar.removeClass(el, "hilite"); + Calendar.removeClass(el.parentNode, "rowhilite"); + } + ev || (ev = window.event); + if (el.navtype == 50 && target != el) { + var pos = Calendar.getAbsolutePos(el); + var w = el.offsetWidth; + var x = ev.clientX; + var dx; + var decrease = true; + if (x > pos.x + w) { + dx = x - pos.x - w; + decrease = false; + } else + dx = pos.x - x; + + if (dx < 0) dx = 0; + var range = el._range; + var current = el._current; + var count = Math.floor(dx / 10) % range.length; + for (var i = range.length; --i >= 0;) + if (range[i] == current) + break; + while (count-- > 0) + if (decrease) { + if (--i < 0) + i = range.length - 1; + } else if ( ++i >= range.length ) + i = 0; + var newval = range[i]; + el.innerHTML = newval; + + cal.onUpdateTime(); + } + var mon = Calendar.findMonth(target); + if (mon) { + if (mon.month != cal.date.getMonth()) { + if (cal.hilitedMonth) { + Calendar.removeClass(cal.hilitedMonth, "hilite"); + } + Calendar.addClass(mon, "hilite"); + cal.hilitedMonth = mon; + } else if (cal.hilitedMonth) { + Calendar.removeClass(cal.hilitedMonth, "hilite"); + } + } else { + if (cal.hilitedMonth) { + Calendar.removeClass(cal.hilitedMonth, "hilite"); + } + var year = Calendar.findYear(target); + if (year) { + if (year.year != cal.date.getFullYear()) { + if (cal.hilitedYear) { + Calendar.removeClass(cal.hilitedYear, "hilite"); + } + Calendar.addClass(year, "hilite"); + cal.hilitedYear = year; + } else if (cal.hilitedYear) { + Calendar.removeClass(cal.hilitedYear, "hilite"); + } + } else if (cal.hilitedYear) { + Calendar.removeClass(cal.hilitedYear, "hilite"); + } + } + return Calendar.stopEvent(ev); +}; + +Calendar.tableMouseDown = function (ev) { + if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) { + return Calendar.stopEvent(ev); + } +}; + +Calendar.calDragIt = function (ev) { + var cal = Calendar._C; + if (!(cal && cal.dragging)) { + return false; + } + var posX; + var posY; + if (Calendar.is_ie) { + posY = window.event.clientY + document.body.scrollTop; + posX = window.event.clientX + document.body.scrollLeft; + } else { + posX = ev.pageX; + posY = ev.pageY; + } + cal.hideShowCovered(); + var st = cal.element.style; + st.left = (posX - cal.xOffs) + "px"; + st.top = (posY - cal.yOffs) + "px"; + return Calendar.stopEvent(ev); +}; + +Calendar.calDragEnd = function (ev) { + var cal = Calendar._C; + if (!cal) { + return false; + } + cal.dragging = false; + with (Calendar) { + removeEvent(document, "mousemove", calDragIt); + removeEvent(document, "mouseup", calDragEnd); + tableMouseUp(ev); + } + cal.hideShowCovered(); +}; + +Calendar.dayMouseDown = function(ev) { + var el = Calendar.getElement(ev); + if (el.disabled) { + return false; + } + var cal = el.calendar; + cal.activeDiv = el; + Calendar._C = cal; + if (el.navtype != 300) with (Calendar) { + if (el.navtype == 50) { + el._current = el.innerHTML; + addEvent(document, "mousemove", tableMouseOver); + } else + addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver); + addClass(el, "hilite active"); + addEvent(document, "mouseup", tableMouseUp); + } else if (cal.isPopup) { + cal._dragStart(ev); + } + if (el.navtype == -1 || el.navtype == 1) { + if (cal.timeout) clearTimeout(cal.timeout); + cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250); + } else if (el.navtype == -2 || el.navtype == 2) { + if (cal.timeout) clearTimeout(cal.timeout); + cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250); + } else { + cal.timeout = null; + } + return Calendar.stopEvent(ev); +}; + +Calendar.dayMouseDblClick = function(ev) { + Calendar.cellClick(Calendar.getElement(ev), ev || window.event); + if (Calendar.is_ie) { + document.selection.empty(); + } +}; + +Calendar.dayMouseOver = function(ev) { + var el = Calendar.getElement(ev); + if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) { + return false; + } + if (el.ttip) { + if (el.ttip.substr(0, 1) == "_") { + el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1); + } + el.calendar.tooltips.innerHTML = el.ttip; + } + if (el.navtype != 300) { + Calendar.addClass(el, "hilite"); + if (el.caldate) { + Calendar.addClass(el.parentNode, "rowhilite"); + } + } + return Calendar.stopEvent(ev); +}; + +Calendar.dayMouseOut = function(ev) { + with (Calendar) { + var el = getElement(ev); + if (isRelated(el, ev) || _C || el.disabled) + return false; + removeClass(el, "hilite"); + if (el.caldate) + removeClass(el.parentNode, "rowhilite"); + if (el.calendar) + el.calendar.tooltips.innerHTML = _TT["SEL_DATE"]; + return stopEvent(ev); + } +}; + +/** + * A generic "click" handler :) handles all types of buttons defined in this + * calendar. + */ +Calendar.cellClick = function(el, ev) { + var cal = el.calendar; + var closing = false; + var newdate = false; + var date = null; + if (typeof el.navtype == "undefined") { + if (cal.currentDateEl) { + Calendar.removeClass(cal.currentDateEl, "selected"); + Calendar.addClass(el, "selected"); + closing = (cal.currentDateEl == el); + if (!closing) { + cal.currentDateEl = el; + } + } + cal.date.setDateOnly(el.caldate); + date = cal.date; + var other_month = !(cal.dateClicked = !el.otherMonth); + if (!other_month && !cal.currentDateEl) + cal._toggleMultipleDate(new Date(date)); + else + newdate = !el.disabled; + // a date was clicked + if (other_month) + cal._init(cal.firstDayOfWeek, date); + } else { + if (el.navtype == 200) { + Calendar.removeClass(el, "hilite"); + cal.callCloseHandler(); + return; + } + date = new Date(cal.date); + if (el.navtype == 0) + date.setDateOnly(new Date()); // TODAY + // unless "today" was clicked, we assume no date was clicked so + // the selected handler will know not to close the calenar when + // in single-click mode. + // cal.dateClicked = (el.navtype == 0); + cal.dateClicked = false; + var year = date.getFullYear(); + var mon = date.getMonth(); + function setMonth(m) { + var day = date.getDate(); + var max = date.getMonthDays(m); + if (day > max) { + date.setDate(max); + } + date.setMonth(m); + }; + switch (el.navtype) { + case 400: + Calendar.removeClass(el, "hilite"); + var text = Calendar._TT["ABOUT"]; + if (typeof text != "undefined") { + text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : ""; + } else { + // FIXME: this should be removed as soon as lang files get updated! + text = "Help and about box text is not translated into this language.\n" + + "If you know this language and you feel generous please update\n" + + "the corresponding file in \"lang\" subdir to match calendar-en.js\n" + + "and send it back to to get it into the distribution ;-)\n\n" + + "Thank you!\n" + + "http://dynarch.com/mishoo/calendar.epl\n"; + } + alert(text); + return; + case -2: + if (year > cal.minYear) { + date.setFullYear(year - 1); + } + break; + case -1: + if (mon > 0) { + setMonth(mon - 1); + } else if (year-- > cal.minYear) { + date.setFullYear(year); + setMonth(11); + } + break; + case 1: + if (mon < 11) { + setMonth(mon + 1); + } else if (year < cal.maxYear) { + date.setFullYear(year + 1); + setMonth(0); + } + break; + case 2: + if (year < cal.maxYear) { + date.setFullYear(year + 1); + } + break; + case 100: + cal.setFirstDayOfWeek(el.fdow); + return; + case 50: + var range = el._range; + var current = el.innerHTML; + for (var i = range.length; --i >= 0;) + if (range[i] == current) + break; + if (ev && ev.shiftKey) { + if (--i < 0) + i = range.length - 1; + } else if ( ++i >= range.length ) + i = 0; + var newval = range[i]; + el.innerHTML = newval; + cal.onUpdateTime(); + return; + case 0: + // TODAY will bring us here + if ((typeof cal.getDateStatus == "function") && + cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) { + return false; + } + break; + } + if (!date.equalsTo(cal.date)) { + cal.setDate(date); + newdate = true; + } else if (el.navtype == 0) + newdate = closing = true; + } + if (newdate) { + ev && cal.callHandler(); + } + if (closing) { + Calendar.removeClass(el, "hilite"); + ev && cal.callCloseHandler(); + } +}; + +// END: CALENDAR STATIC FUNCTIONS + +// BEGIN: CALENDAR OBJECT FUNCTIONS + +/** + * This function creates the calendar inside the given parent. If _par is + * null than it creates a popup calendar inside the BODY element. If _par is + * an element, be it BODY, then it creates a non-popup calendar (still + * hidden). Some properties need to be set before calling this function. + */ +Calendar.prototype.create = function (_par) { + var parent = null; + if (! _par) { + // default parent is the document body, in which case we create + // a popup calendar. + parent = document.getElementsByTagName("body")[0]; + this.isPopup = true; + } else { + parent = _par; + this.isPopup = false; + } + this.date = this.dateStr ? new Date(this.dateStr) : new Date(); + + var table = Calendar.createElement("table"); + this.table = table; + table.cellSpacing = 0; + table.cellPadding = 0; + table.calendar = this; + Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown); + + var div = Calendar.createElement("div"); + this.element = div; + div.className = "calendar"; + if (this.isPopup) { + div.style.position = "absolute"; + div.style.display = "none"; + } + div.appendChild(table); + + var thead = Calendar.createElement("thead", table); + var cell = null; + var row = null; + + var cal = this; + var hh = function (text, cs, navtype) { + cell = Calendar.createElement("td", row); + cell.colSpan = cs; + cell.className = "button"; + if (navtype != 0 && Math.abs(navtype) <= 2) + cell.className += " nav"; + Calendar._add_evs(cell); + cell.calendar = cal; + cell.navtype = navtype; + cell.innerHTML = "
    " + text + "
    "; + return cell; + }; + + row = Calendar.createElement("tr", thead); + var title_length = 6; + (this.isPopup) && --title_length; + (this.weekNumbers) && ++title_length; + + hh("?", 1, 400).ttip = Calendar._TT["INFO"]; + this.title = hh("", title_length, 300); + this.title.className = "title"; + if (this.isPopup) { + this.title.ttip = Calendar._TT["DRAG_TO_MOVE"]; + this.title.style.cursor = "move"; + hh("×", 1, 200).ttip = Calendar._TT["CLOSE"]; + } + + row = Calendar.createElement("tr", thead); + row.className = "headrow"; + + this._nav_py = hh("«", 1, -2); + this._nav_py.ttip = Calendar._TT["PREV_YEAR"]; + + this._nav_pm = hh("‹", 1, -1); + this._nav_pm.ttip = Calendar._TT["PREV_MONTH"]; + + this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0); + this._nav_now.ttip = Calendar._TT["GO_TODAY"]; + + this._nav_nm = hh("›", 1, 1); + this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"]; + + this._nav_ny = hh("»", 1, 2); + this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"]; + + // day names + row = Calendar.createElement("tr", thead); + row.className = "daynames"; + if (this.weekNumbers) { + cell = Calendar.createElement("td", row); + cell.className = "name wn"; + cell.innerHTML = Calendar._TT["WK"]; + } + for (var i = 7; i > 0; --i) { + cell = Calendar.createElement("td", row); + if (!i) { + cell.navtype = 100; + cell.calendar = this; + Calendar._add_evs(cell); + } + } + this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild; + this._displayWeekdays(); + + var tbody = Calendar.createElement("tbody", table); + this.tbody = tbody; + + for (i = 6; i > 0; --i) { + row = Calendar.createElement("tr", tbody); + if (this.weekNumbers) { + cell = Calendar.createElement("td", row); + } + for (var j = 7; j > 0; --j) { + cell = Calendar.createElement("td", row); + cell.calendar = this; + Calendar._add_evs(cell); + } + } + + if (this.showsTime) { + row = Calendar.createElement("tr", tbody); + row.className = "time"; + + cell = Calendar.createElement("td", row); + cell.className = "time"; + cell.colSpan = 2; + cell.innerHTML = Calendar._TT["TIME"] || " "; + + cell = Calendar.createElement("td", row); + cell.className = "time"; + cell.colSpan = this.weekNumbers ? 4 : 3; + + (function(){ + function makeTimePart(className, init, range_start, range_end) { + var part = Calendar.createElement("span", cell); + part.className = className; + part.innerHTML = init; + part.calendar = cal; + part.ttip = Calendar._TT["TIME_PART"]; + part.navtype = 50; + part._range = []; + if (typeof range_start != "number") + part._range = range_start; + else { + for (var i = range_start; i <= range_end; ++i) { + var txt; + if (i < 10 && range_end >= 10) txt = '0' + i; + else txt = '' + i; + part._range[part._range.length] = txt; + } + } + Calendar._add_evs(part); + return part; + }; + var hrs = cal.date.getHours(); + var mins = cal.date.getMinutes(); + var t12 = !cal.time24; + var pm = (hrs > 12); + if (t12 && pm) hrs -= 12; + var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23); + var span = Calendar.createElement("span", cell); + span.innerHTML = ":"; + span.className = "colon"; + var M = makeTimePart("minute", mins, 0, 59); + var AP = null; + cell = Calendar.createElement("td", row); + cell.className = "time"; + cell.colSpan = 2; + if (t12) + AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]); + else + cell.innerHTML = " "; + + cal.onSetTime = function() { + var pm, hrs = this.date.getHours(), + mins = this.date.getMinutes(); + if (t12) { + pm = (hrs >= 12); + if (pm) hrs -= 12; + if (hrs == 0) hrs = 12; + AP.innerHTML = pm ? "pm" : "am"; + } + H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs; + M.innerHTML = (mins < 10) ? ("0" + mins) : mins; + }; + + cal.onUpdateTime = function() { + var date = this.date; + var h = parseInt(H.innerHTML, 10); + if (t12) { + if (/pm/i.test(AP.innerHTML) && h < 12) + h += 12; + else if (/am/i.test(AP.innerHTML) && h == 12) + h = 0; + } + var d = date.getDate(); + var m = date.getMonth(); + var y = date.getFullYear(); + date.setHours(h); + date.setMinutes(parseInt(M.innerHTML, 10)); + date.setFullYear(y); + date.setMonth(m); + date.setDate(d); + this.dateClicked = false; + this.callHandler(); + }; + })(); + } else { + this.onSetTime = this.onUpdateTime = function() {}; + } + + var tfoot = Calendar.createElement("tfoot", table); + + row = Calendar.createElement("tr", tfoot); + row.className = "footrow"; + + cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300); + cell.className = "ttip"; + if (this.isPopup) { + cell.ttip = Calendar._TT["DRAG_TO_MOVE"]; + cell.style.cursor = "move"; + } + this.tooltips = cell; + + div = Calendar.createElement("div", this.element); + this.monthsCombo = div; + div.className = "combo"; + for (i = 0; i < Calendar._MN.length; ++i) { + var mn = Calendar.createElement("div"); + mn.className = Calendar.is_ie ? "label-IEfix" : "label"; + mn.month = i; + mn.innerHTML = Calendar._SMN[i]; + div.appendChild(mn); + } + + div = Calendar.createElement("div", this.element); + this.yearsCombo = div; + div.className = "combo"; + for (i = 12; i > 0; --i) { + var yr = Calendar.createElement("div"); + yr.className = Calendar.is_ie ? "label-IEfix" : "label"; + div.appendChild(yr); + } + + this._init(this.firstDayOfWeek, this.date); + parent.appendChild(this.element); +}; + +/** keyboard navigation, only for popup calendars */ +Calendar._keyEvent = function(ev) { + var cal = window._dynarch_popupCalendar; + if (!cal || cal.multiple) + return false; + (Calendar.is_ie) && (ev = window.event); + var act = (Calendar.is_ie || ev.type == "keypress"), + K = ev.keyCode; + if (ev.ctrlKey) { + switch (K) { + case 37: // KEY left + act && Calendar.cellClick(cal._nav_pm); + break; + case 38: // KEY up + act && Calendar.cellClick(cal._nav_py); + break; + case 39: // KEY right + act && Calendar.cellClick(cal._nav_nm); + break; + case 40: // KEY down + act && Calendar.cellClick(cal._nav_ny); + break; + default: + return false; + } + } else switch (K) { + case 32: // KEY space (now) + Calendar.cellClick(cal._nav_now); + break; + case 27: // KEY esc + act && cal.callCloseHandler(); + break; + case 37: // KEY left + case 38: // KEY up + case 39: // KEY right + case 40: // KEY down + if (act) { + var prev, x, y, ne, el, step; + prev = K == 37 || K == 38; + step = (K == 37 || K == 39) ? 1 : 7; + function setVars() { + el = cal.currentDateEl; + var p = el.pos; + x = p & 15; + y = p >> 4; + ne = cal.ar_days[y][x]; + };setVars(); + function prevMonth() { + var date = new Date(cal.date); + date.setDate(date.getDate() - step); + cal.setDate(date); + }; + function nextMonth() { + var date = new Date(cal.date); + date.setDate(date.getDate() + step); + cal.setDate(date); + }; + while (1) { + switch (K) { + case 37: // KEY left + if (--x >= 0) + ne = cal.ar_days[y][x]; + else { + x = 6; + K = 38; + continue; + } + break; + case 38: // KEY up + if (--y >= 0) + ne = cal.ar_days[y][x]; + else { + prevMonth(); + setVars(); + } + break; + case 39: // KEY right + if (++x < 7) + ne = cal.ar_days[y][x]; + else { + x = 0; + K = 40; + continue; + } + break; + case 40: // KEY down + if (++y < cal.ar_days.length) + ne = cal.ar_days[y][x]; + else { + nextMonth(); + setVars(); + } + break; + } + break; + } + if (ne) { + if (!ne.disabled) + Calendar.cellClick(ne); + else if (prev) + prevMonth(); + else + nextMonth(); + } + } + break; + case 13: // KEY enter + if (act) + Calendar.cellClick(cal.currentDateEl, ev); + break; + default: + return false; + } + return Calendar.stopEvent(ev); +}; + +/** + * (RE)Initializes the calendar to the given date and firstDayOfWeek + */ +Calendar.prototype._init = function (firstDayOfWeek, date) { + var today = new Date(), + TY = today.getFullYear(), + TM = today.getMonth(), + TD = today.getDate(); + this.table.style.visibility = "hidden"; + var year = date.getFullYear(); + if (year < this.minYear) { + year = this.minYear; + date.setFullYear(year); + } else if (year > this.maxYear) { + year = this.maxYear; + date.setFullYear(year); + } + this.firstDayOfWeek = firstDayOfWeek; + this.date = new Date(date); + var month = date.getMonth(); + var mday = date.getDate(); + var no_days = date.getMonthDays(); + + // calendar voodoo for computing the first day that would actually be + // displayed in the calendar, even if it's from the previous month. + // WARNING: this is magic. ;-) + date.setDate(1); + var day1 = (date.getDay() - this.firstDayOfWeek) % 7; + if (day1 < 0) + day1 += 7; + date.setDate(-day1); + date.setDate(date.getDate() + 1); + + var row = this.tbody.firstChild; + var MN = Calendar._SMN[month]; + var ar_days = this.ar_days = new Array(); + var weekend = Calendar._TT["WEEKEND"]; + var dates = this.multiple ? (this.datesCells = {}) : null; + for (var i = 0; i < 6; ++i, row = row.nextSibling) { + var cell = row.firstChild; + if (this.weekNumbers) { + cell.className = "day wn"; + cell.innerHTML = date.getWeekNumber(); + cell = cell.nextSibling; + } + row.className = "daysrow"; + var hasdays = false, iday, dpos = ar_days[i] = []; + for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(iday + 1)) { + iday = date.getDate(); + var wday = date.getDay(); + cell.className = "day"; + cell.pos = i << 4 | j; + dpos[j] = cell; + var current_month = (date.getMonth() == month); + if (!current_month) { + if (this.showsOtherMonths) { + cell.className += " othermonth"; + cell.otherMonth = true; + } else { + cell.className = "emptycell"; + cell.innerHTML = " "; + cell.disabled = true; + continue; + } + } else { + cell.otherMonth = false; + hasdays = true; + } + cell.disabled = false; + cell.innerHTML = this.getDateText ? this.getDateText(date, iday) : iday; + if (dates) + dates[date.print("%Y%m%d")] = cell; + if (this.getDateStatus) { + var status = this.getDateStatus(date, year, month, iday); + if (this.getDateToolTip) { + var toolTip = this.getDateToolTip(date, year, month, iday); + if (toolTip) + cell.title = toolTip; + } + if (status === true) { + cell.className += " disabled"; + cell.disabled = true; + } else { + if (/disabled/i.test(status)) + cell.disabled = true; + cell.className += " " + status; + } + } + if (!cell.disabled) { + cell.caldate = new Date(date); + cell.ttip = "_"; + if (!this.multiple && current_month + && iday == mday && this.hiliteToday) { + cell.className += " selected"; + this.currentDateEl = cell; + } + if (date.getFullYear() == TY && + date.getMonth() == TM && + iday == TD) { + cell.className += " today"; + cell.ttip += Calendar._TT["PART_TODAY"]; + } + if (weekend.indexOf(wday.toString()) != -1) + cell.className += cell.otherMonth ? " oweekend" : " weekend"; + } + } + if (!(hasdays || this.showsOtherMonths)) + row.className = "emptyrow"; + } + this.title.innerHTML = Calendar._MN[month] + ", " + year; + this.onSetTime(); + this.table.style.visibility = "visible"; + this._initMultipleDates(); + // PROFILE + // this.tooltips.innerHTML = "Generated in " + ((new Date()) - today) + " ms"; +}; + +Calendar.prototype._initMultipleDates = function() { + if (this.multiple) { + for (var i in this.multiple) { + var cell = this.datesCells[i]; + var d = this.multiple[i]; + if (!d) + continue; + if (cell) + cell.className += " selected"; + } + } +}; + +Calendar.prototype._toggleMultipleDate = function(date) { + if (this.multiple) { + var ds = date.print("%Y%m%d"); + var cell = this.datesCells[ds]; + if (cell) { + var d = this.multiple[ds]; + if (!d) { + Calendar.addClass(cell, "selected"); + this.multiple[ds] = date; + } else { + Calendar.removeClass(cell, "selected"); + delete this.multiple[ds]; + } + } + } +}; + +Calendar.prototype.setDateToolTipHandler = function (unaryFunction) { + this.getDateToolTip = unaryFunction; +}; + +/** + * Calls _init function above for going to a certain date (but only if the + * date is different than the currently selected one). + */ +Calendar.prototype.setDate = function (date) { + if (!date.equalsTo(this.date)) { + this._init(this.firstDayOfWeek, date); + } +}; + +/** + * Refreshes the calendar. Useful if the "disabledHandler" function is + * dynamic, meaning that the list of disabled date can change at runtime. + * Just * call this function if you think that the list of disabled dates + * should * change. + */ +Calendar.prototype.refresh = function () { + this._init(this.firstDayOfWeek, this.date); +}; + +/** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */ +Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) { + this._init(firstDayOfWeek, this.date); + this._displayWeekdays(); +}; + +/** + * Allows customization of what dates are enabled. The "unaryFunction" + * parameter must be a function object that receives the date (as a JS Date + * object) and returns a boolean value. If the returned value is true then + * the passed date will be marked as disabled. + */ +Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) { + this.getDateStatus = unaryFunction; +}; + +/** Customization of allowed year range for the calendar. */ +Calendar.prototype.setRange = function (a, z) { + this.minYear = a; + this.maxYear = z; +}; + +/** Calls the first user handler (selectedHandler). */ +Calendar.prototype.callHandler = function () { + if (this.onSelected) { + this.onSelected(this, this.date.print(this.dateFormat)); + } +}; + +/** Calls the second user handler (closeHandler). */ +Calendar.prototype.callCloseHandler = function () { + if (this.onClose) { + this.onClose(this); + } + this.hideShowCovered(); +}; + +/** Removes the calendar object from the DOM tree and destroys it. */ +Calendar.prototype.destroy = function () { + var el = this.element.parentNode; + el.removeChild(this.element); + Calendar._C = null; + window._dynarch_popupCalendar = null; +}; + +/** + * Moves the calendar element to a different section in the DOM tree (changes + * its parent). + */ +Calendar.prototype.reparent = function (new_parent) { + var el = this.element; + el.parentNode.removeChild(el); + new_parent.appendChild(el); +}; + +// This gets called when the user presses a mouse button anywhere in the +// document, if the calendar is shown. If the click was outside the open +// calendar this function closes it. +Calendar._checkCalendar = function(ev) { + var calendar = window._dynarch_popupCalendar; + if (!calendar) { + return false; + } + var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev); + for (; el != null && el != calendar.element; el = el.parentNode); + if (el == null) { + // calls closeHandler which should hide the calendar. + window._dynarch_popupCalendar.callCloseHandler(); + return Calendar.stopEvent(ev); + } +}; + +/** Shows the calendar. */ +Calendar.prototype.show = function () { + var rows = this.table.getElementsByTagName("tr"); + for (var i = rows.length; i > 0;) { + var row = rows[--i]; + Calendar.removeClass(row, "rowhilite"); + var cells = row.getElementsByTagName("td"); + for (var j = cells.length; j > 0;) { + var cell = cells[--j]; + Calendar.removeClass(cell, "hilite"); + Calendar.removeClass(cell, "active"); + } + } + this.element.style.display = "block"; + this.hidden = false; + if (this.isPopup) { + window._dynarch_popupCalendar = this; + Calendar.addEvent(document, "keydown", Calendar._keyEvent); + Calendar.addEvent(document, "keypress", Calendar._keyEvent); + Calendar.addEvent(document, "mousedown", Calendar._checkCalendar); + } + this.hideShowCovered(); +}; + +/** + * Hides the calendar. Also removes any "hilite" from the class of any TD + * element. + */ +Calendar.prototype.hide = function () { + if (this.isPopup) { + Calendar.removeEvent(document, "keydown", Calendar._keyEvent); + Calendar.removeEvent(document, "keypress", Calendar._keyEvent); + Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar); + } + this.element.style.display = "none"; + this.hidden = true; + this.hideShowCovered(); +}; + +/** + * Shows the calendar at a given absolute position (beware that, depending on + * the calendar element style -- position property -- this might be relative + * to the parent's containing rectangle). + */ +Calendar.prototype.showAt = function (x, y) { + var s = this.element.style; + s.left = x + "px"; + s.top = y + "px"; + this.show(); +}; + +/** Shows the calendar near a given element. */ +Calendar.prototype.showAtElement = function (el, opts) { + var self = this; + var p = Calendar.getAbsolutePos(el); + if (!opts || typeof opts != "string") { + this.showAt(p.x, p.y + el.offsetHeight); + return true; + } + function fixPosition(box) { + if (box.x < 0) + box.x = 0; + if (box.y < 0) + box.y = 0; + var cp = document.createElement("div"); + var s = cp.style; + s.position = "absolute"; + s.right = s.bottom = s.width = s.height = "0px"; + document.body.appendChild(cp); + var br = Calendar.getAbsolutePos(cp); + document.body.removeChild(cp); + if (Calendar.is_ie) { + br.y += document.body.scrollTop; + br.x += document.body.scrollLeft; + } else { + br.y += window.scrollY; + br.x += window.scrollX; + } + var tmp = box.x + box.width - br.x; + if (tmp > 0) box.x -= tmp; + tmp = box.y + box.height - br.y; + if (tmp > 0) box.y -= tmp; + }; + this.element.style.display = "block"; + Calendar.continuation_for_the_fucking_khtml_browser = function() { + var w = self.element.offsetWidth; + var h = self.element.offsetHeight; + self.element.style.display = "none"; + var valign = opts.substr(0, 1); + var halign = "l"; + if (opts.length > 1) { + halign = opts.substr(1, 1); + } + // vertical alignment + switch (valign) { + case "T": p.y -= h; break; + case "B": p.y += el.offsetHeight; break; + case "C": p.y += (el.offsetHeight - h) / 2; break; + case "t": p.y += el.offsetHeight - h; break; + case "b": break; // already there + } + // horizontal alignment + switch (halign) { + case "L": p.x -= w; break; + case "R": p.x += el.offsetWidth; break; + case "C": p.x += (el.offsetWidth - w) / 2; break; + case "l": p.x += el.offsetWidth - w; break; + case "r": break; // already there + } + p.width = w; + p.height = h + 40; + self.monthsCombo.style.display = "none"; + fixPosition(p); + self.showAt(p.x, p.y); + }; + if (Calendar.is_khtml) + setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10); + else + Calendar.continuation_for_the_fucking_khtml_browser(); +}; + +/** Customizes the date format. */ +Calendar.prototype.setDateFormat = function (str) { + this.dateFormat = str; +}; + +/** Customizes the tooltip date format. */ +Calendar.prototype.setTtDateFormat = function (str) { + this.ttDateFormat = str; +}; + +/** + * Tries to identify the date represented in a string. If successful it also + * calls this.setDate which moves the calendar to the given date. + */ +Calendar.prototype.parseDate = function(str, fmt) { + if (!fmt) + fmt = this.dateFormat; + this.setDate(Date.parseDate(str, fmt)); +}; + +Calendar.prototype.hideShowCovered = function () { + if (!Calendar.is_ie && !Calendar.is_opera) + return; + function getVisib(obj){ + var value = obj.style.visibility; + if (!value) { + if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C + if (!Calendar.is_khtml) + value = document.defaultView. + getComputedStyle(obj, "").getPropertyValue("visibility"); + else + value = ''; + } else if (obj.currentStyle) { // IE + value = obj.currentStyle.visibility; + } else + value = ''; + } + return value; + }; + + var tags = new Array("applet", "iframe", "select"); + var el = this.element; + + var p = Calendar.getAbsolutePos(el); + var EX1 = p.x; + var EX2 = el.offsetWidth + EX1; + var EY1 = p.y; + var EY2 = el.offsetHeight + EY1; + + for (var k = tags.length; k > 0; ) { + var ar = document.getElementsByTagName(tags[--k]); + var cc = null; + + for (var i = ar.length; i > 0;) { + cc = ar[--i]; + + p = Calendar.getAbsolutePos(cc); + var CX1 = p.x; + var CX2 = cc.offsetWidth + CX1; + var CY1 = p.y; + var CY2 = cc.offsetHeight + CY1; + + if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) { + if (!cc.__msh_save_visibility) { + cc.__msh_save_visibility = getVisib(cc); + } + cc.style.visibility = cc.__msh_save_visibility; + } else { + if (!cc.__msh_save_visibility) { + cc.__msh_save_visibility = getVisib(cc); + } + cc.style.visibility = "hidden"; + } + } + } +}; + +/** Internal function; it displays the bar with the names of the weekday. */ +Calendar.prototype._displayWeekdays = function () { + var fdow = this.firstDayOfWeek; + var cell = this.firstdayname; + var weekend = Calendar._TT["WEEKEND"]; + for (var i = 0; i < 7; ++i) { + cell.className = "day name"; + var realday = (i + fdow) % 7; + if (i) { + cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]); + cell.navtype = 100; + cell.calendar = this; + cell.fdow = realday; + Calendar._add_evs(cell); + } + if (weekend.indexOf(realday.toString()) != -1) { + Calendar.addClass(cell, "weekend"); + } + cell.innerHTML = Calendar._SDN[(i + fdow) % 7]; + cell = cell.nextSibling; + } +}; + +/** Internal function. Hides all combo boxes that might be displayed. */ +Calendar.prototype._hideCombos = function () { + this.monthsCombo.style.display = "none"; + this.yearsCombo.style.display = "none"; +}; + +/** Internal function. Starts dragging the element. */ +Calendar.prototype._dragStart = function (ev) { + if (this.dragging) { + return; + } + this.dragging = true; + var posX; + var posY; + if (Calendar.is_ie) { + posY = window.event.clientY + document.body.scrollTop; + posX = window.event.clientX + document.body.scrollLeft; + } else { + posY = ev.clientY + window.scrollY; + posX = ev.clientX + window.scrollX; + } + var st = this.element.style; + this.xOffs = posX - parseInt(st.left); + this.yOffs = posY - parseInt(st.top); + with (Calendar) { + addEvent(document, "mousemove", calDragIt); + addEvent(document, "mouseup", calDragEnd); + } +}; + +// BEGIN: DATE OBJECT PATCHES + +/** Adds the number of days array to the Date object. */ +Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31); + +/** Constants used for time computations */ +Date.SECOND = 1000 /* milliseconds */; +Date.MINUTE = 60 * Date.SECOND; +Date.HOUR = 60 * Date.MINUTE; +Date.DAY = 24 * Date.HOUR; +Date.WEEK = 7 * Date.DAY; + +Date.parseDate = function(str, fmt) { + var today = new Date(); + var y = 0; + var m = -1; + var d = 0; + var a = str.split(/\W+/); + var b = fmt.match(/%./g); + var i = 0, j = 0; + var hr = 0; + var min = 0; + for (i = 0; i < a.length; ++i) { + if (!a[i]) + continue; + switch (b[i]) { + case "%d": + case "%e": + d = parseInt(a[i], 10); + break; + + case "%m": + m = parseInt(a[i], 10) - 1; + break; + + case "%Y": + case "%y": + y = parseInt(a[i], 10); + (y < 100) && (y += (y > 29) ? 1900 : 2000); + break; + + case "%b": + case "%B": + for (j = 0; j < 12; ++j) { + if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; } + } + break; + + case "%H": + case "%I": + case "%k": + case "%l": + hr = parseInt(a[i], 10); + break; + + case "%P": + case "%p": + if (/pm/i.test(a[i]) && hr < 12) + hr += 12; + else if (/am/i.test(a[i]) && hr >= 12) + hr -= 12; + break; + + case "%M": + min = parseInt(a[i], 10); + break; + } + } + if (isNaN(y)) y = today.getFullYear(); + if (isNaN(m)) m = today.getMonth(); + if (isNaN(d)) d = today.getDate(); + if (isNaN(hr)) hr = today.getHours(); + if (isNaN(min)) min = today.getMinutes(); + if (y != 0 && m != -1 && d != 0) + return new Date(y, m, d, hr, min, 0); + y = 0; m = -1; d = 0; + for (i = 0; i < a.length; ++i) { + if (a[i].search(/[a-zA-Z]+/) != -1) { + var t = -1; + for (j = 0; j < 12; ++j) { + if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; } + } + if (t != -1) { + if (m != -1) { + d = m+1; + } + m = t; + } + } else if (parseInt(a[i], 10) <= 12 && m == -1) { + m = a[i]-1; + } else if (parseInt(a[i], 10) > 31 && y == 0) { + y = parseInt(a[i], 10); + (y < 100) && (y += (y > 29) ? 1900 : 2000); + } else if (d == 0) { + d = a[i]; + } + } + if (y == 0) + y = today.getFullYear(); + if (m != -1 && d != 0) + return new Date(y, m, d, hr, min, 0); + return today; +}; + +/** Returns the number of days in the current month */ +Date.prototype.getMonthDays = function(month) { + var year = this.getFullYear(); + if (typeof month == "undefined") { + month = this.getMonth(); + } + if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) { + return 29; + } else { + return Date._MD[month]; + } +}; + +/** Returns the number of day in the year. */ +Date.prototype.getDayOfYear = function() { + var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0); + var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0); + var time = now - then; + return Math.floor(time / Date.DAY); +}; + +/** Returns the number of the week in year, as defined in ISO 8601. */ +Date.prototype.getWeekNumber = function() { + var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0); + var DoW = d.getDay(); + d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu + var ms = d.valueOf(); // GMT + d.setMonth(0); + d.setDate(4); // Thu in Week 1 + return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1; +}; + +/** Checks date and time equality */ +Date.prototype.equalsTo = function(date) { + return ((this.getFullYear() == date.getFullYear()) && + (this.getMonth() == date.getMonth()) && + (this.getDate() == date.getDate()) && + (this.getHours() == date.getHours()) && + (this.getMinutes() == date.getMinutes())); +}; + +/** Set only the year, month, date parts (keep existing time) */ +Date.prototype.setDateOnly = function(date) { + var tmp = new Date(date); + this.setDate(1); + this.setFullYear(tmp.getFullYear()); + this.setMonth(tmp.getMonth()); + this.setDate(tmp.getDate()); +}; + +/** Prints the date in a string according to the given format. */ +Date.prototype.print = function (str) { + var m = this.getMonth(); + var d = this.getDate(); + var y = this.getFullYear(); + var wn = this.getWeekNumber(); + var w = this.getDay(); + var s = {}; + var hr = this.getHours(); + var pm = (hr >= 12); + var ir = (pm) ? (hr - 12) : hr; + var dy = this.getDayOfYear(); + if (ir == 0) + ir = 12; + var min = this.getMinutes(); + var sec = this.getSeconds(); + s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N] + s["%A"] = Calendar._DN[w]; // full weekday name + s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N] + s["%B"] = Calendar._MN[m]; // full month name + // FIXME: %c : preferred date and time representation for the current locale + s["%C"] = 1 + Math.floor(y / 100); // the century number + s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31) + s["%e"] = d; // the day of the month (range 1 to 31) + // FIXME: %D : american date style: %m/%d/%y + // FIXME: %E, %F, %G, %g, %h (man strftime) + s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format) + s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format) + s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366) + s["%k"] = hr; // hour, range 0 to 23 (24h format) + s["%l"] = ir; // hour, range 1 to 12 (12h format) + s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12 + s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59 + s["%n"] = "\n"; // a newline character + s["%p"] = pm ? "PM" : "AM"; + s["%P"] = pm ? "pm" : "am"; + // FIXME: %r : the time in am/pm notation %I:%M:%S %p + // FIXME: %R : the time in 24-hour notation %H:%M + s["%s"] = Math.floor(this.getTime() / 1000); + s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59 + s["%t"] = "\t"; // a tab character + // FIXME: %T : the time in 24-hour notation (%H:%M:%S) + s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn; + s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON) + s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN) + // FIXME: %x : preferred date representation for the current locale without the time + // FIXME: %X : preferred time representation for the current locale without the date + s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99) + s["%Y"] = y; // year with the century + s["%%"] = "%"; // a literal '%' character + + var re = /%./g; + if (!Calendar.is_ie5 && !Calendar.is_khtml) + return str.replace(re, function (par) { return s[par] || par; }); + + var a = str.match(re); + for (var i = 0; i < a.length; i++) { + var tmp = s[a[i]]; + if (tmp) { + re = new RegExp(a[i], 'g'); + str = str.replace(re, tmp); + } + } + + return str; +}; + +Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear; +Date.prototype.setFullYear = function(y) { + var d = new Date(this); + d.__msh_oldSetFullYear(y); + if (d.getMonth() != this.getMonth()) + this.setDate(28); + this.__msh_oldSetFullYear(y); +}; + +// END: DATE OBJECT PATCHES + + +// global object that remembers the calendar +window._dynarch_popupCalendar = null; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/img.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/img.gif new file mode 100644 index 00000000..6774ca5c Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/img.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-af.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-af.js new file mode 100644 index 00000000..ace710e6 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-af.js @@ -0,0 +1,39 @@ +// ** I18N Afrikaans +Calendar._DN = new Array +("Sondag", + "Maandag", + "Dinsdag", + "Woensdag", + "Donderdag", + "Vrydag", + "Saterdag", + "Sondag"); +Calendar._MN = new Array +("Januarie", + "Februarie", + "Maart", + "April", + "Mei", + "Junie", + "Julie", + "Augustus", + "September", + "Oktober", + "November", + "Desember"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["TOGGLE"] = "Verander eerste dag van die week"; +Calendar._TT["PREV_YEAR"] = "Vorige jaar (hou vir keuselys)"; +Calendar._TT["PREV_MONTH"] = "Vorige maand (hou vir keuselys)"; +Calendar._TT["GO_TODAY"] = "Gaan na vandag"; +Calendar._TT["NEXT_MONTH"] = "Volgende maand (hou vir keuselys)"; +Calendar._TT["NEXT_YEAR"] = "Volgende jaar (hou vir keuselys)"; +Calendar._TT["SEL_DATE"] = "Kies datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Sleep om te skuif"; +Calendar._TT["PART_TODAY"] = " (vandag)"; +Calendar._TT["MON_FIRST"] = "Vertoon Maandag eerste"; +Calendar._TT["SUN_FIRST"] = "Display Sunday first"; +Calendar._TT["CLOSE"] = "Close"; +Calendar._TT["TODAY"] = "Today"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-al.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-al.js new file mode 100644 index 00000000..f9cfe8f5 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-al.js @@ -0,0 +1,101 @@ +// Calendar ALBANIAN language +//author Rigels Gordani rige@hotmail.com + +// ditet +Calendar._DN = new Array +("E Diele", +"E Hene", +"E Marte", +"E Merkure", +"E Enjte", +"E Premte", +"E Shtune", +"E Diele"); + +//ditet shkurt +Calendar._SDN = new Array +("Die", +"Hen", +"Mar", +"Mer", +"Enj", +"Pre", +"Sht", +"Die"); + +// muajt +Calendar._MN = new Array +("Janar", +"Shkurt", +"Mars", +"Prill", +"Maj", +"Qeshor", +"Korrik", +"Gusht", +"Shtator", +"Tetor", +"Nentor", +"Dhjetor"); + +// muajte shkurt +Calendar._SMN = new Array +("Jan", +"Shk", +"Mar", +"Pri", +"Maj", +"Qes", +"Kor", +"Gus", +"Sht", +"Tet", +"Nen", +"Dhj"); + +// ndihmesa +Calendar._TT = {}; +Calendar._TT["INFO"] = "Per kalendarin"; + +Calendar._TT["ABOUT"] = +"Zgjedhes i ores/dates ne DHTML \n" + +"\n\n" +"Zgjedhja e Dates:\n" + +"- Perdor butonat \xab, \xbb per te zgjedhur vitin\n" + +"- Perdor butonat" + String.fromCharCode(0x2039) + ", " + +String.fromCharCode(0x203a) + +" per te zgjedhur muajin\n" + +"- Mbani shtypur butonin e mousit per nje zgjedje me te shpejte."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Zgjedhja e kohes:\n" + +"- Kliko tek ndonje nga pjeset e ores per ta rritur ate\n" + +"- ose kliko me Shift per ta zvogeluar ate\n" + +"- ose cliko dhe terhiq per zgjedhje me te shpejte."; + +Calendar._TT["PREV_YEAR"] = "Viti i shkuar (prit per menune)"; +Calendar._TT["PREV_MONTH"] = "Muaji i shkuar (prit per menune)"; +Calendar._TT["GO_TODAY"] = "Sot"; +Calendar._TT["NEXT_MONTH"] = "Muaji i ardhshem (prit per menune)"; +Calendar._TT["NEXT_YEAR"] = "Viti i ardhshem (prit per menune)"; +Calendar._TT["SEL_DATE"] = "Zgjidh daten"; +Calendar._TT["DRAG_TO_MOVE"] = "Terhiqe per te levizur"; +Calendar._TT["PART_TODAY"] = " (sot)"; + +// "%s" eshte dita e pare e javes +// %s do te zevendesohet me emrin e dite +Calendar._TT["DAY_FIRST"] = "Trego te %s te paren"; + + +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Mbyll"; +Calendar._TT["TODAY"] = "Sot"; +Calendar._TT["TIME_PART"] = "Kliko me (Shift-)ose terhiqe per te ndryshuar +vleren"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "Java"; +Calendar._TT["TIME"] = "Koha:"; + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-bg.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-bg.js new file mode 100644 index 00000000..5eb73ec6 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-bg.js @@ -0,0 +1,124 @@ +// ** I18N + +// Calendar BG language +// Author: Mihai Bazon, +// Translator: Valentin Sheiretsky, +// Encoding: Windows-1251 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Íåäåëÿ", + "Ïîíåäåëíèê", + "Âòîðíèê", + "Ñðÿäà", + "×åòâúðòúê", + "Ïåòúê", + "Ñúáîòà", + "Íåäåëÿ"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Íåä", + "Ïîí", + "Âòî", + "Ñðÿ", + "×åò", + "Ïåò", + "Ñúá", + "Íåä"); + +// full month names +Calendar._MN = new Array +("ßíóàðè", + "Ôåâðóàðè", + "Ìàðò", + "Àïðèë", + "Ìàé", + "Þíè", + "Þëè", + "Àâãóñò", + "Ñåïòåìâðè", + "Îêòîìâðè", + "Íîåìâðè", + "Äåêåìâðè"); + +// short month names +Calendar._SMN = new Array +("ßíó", + "Ôåâ", + "Ìàð", + "Àïð", + "Ìàé", + "Þíè", + "Þëè", + "Àâã", + "Ñåï", + "Îêò", + "Íîå", + "Äåê"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Èíôîðìàöèÿ çà êàëåíäàðà"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Date selection:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" + +"- Hold mouse button on any of the above buttons for faster selection."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Time selection:\n" + +"- Click on any of the time parts to increase it\n" + +"- or Shift-click to decrease it\n" + +"- or click and drag for faster selection."; + +Calendar._TT["PREV_YEAR"] = "Ïðåäíà ãîäèíà (çàäðúæòå çà ìåíþ)"; +Calendar._TT["PREV_MONTH"] = "Ïðåäåí ìåñåö (çàäðúæòå çà ìåíþ)"; +Calendar._TT["GO_TODAY"] = "Èçáåðåòå äíåñ"; +Calendar._TT["NEXT_MONTH"] = "Ñëåäâàù ìåñåö (çàäðúæòå çà ìåíþ)"; +Calendar._TT["NEXT_YEAR"] = "Ñëåäâàùà ãîäèíà (çàäðúæòå çà ìåíþ)"; +Calendar._TT["SEL_DATE"] = "Èçáåðåòå äàòà"; +Calendar._TT["DRAG_TO_MOVE"] = "Ïðåìåñòâàíå"; +Calendar._TT["PART_TODAY"] = " (äíåñ)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "%s êàòî ïúðâè äåí"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Çàòâîðåòå"; +Calendar._TT["TODAY"] = "Äíåñ"; +Calendar._TT["TIME_PART"] = "(Shift-)Click èëè drag çà äà ïðîìåíèòå ñòîéíîñòòà"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%A - %e %B %Y"; + +Calendar._TT["WK"] = "Ñåäì"; +Calendar._TT["TIME"] = "×àñ:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-big5-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-big5-utf8.js new file mode 100644 index 00000000..3b6df42a --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-big5-utf8.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar big5-utf8 language +// Author: Gary Fu, +// Encoding: utf8 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("星期日", + "星期一", + "星期二", + "星期三", + "星期四", + "星期五", + "星期六", + "星期日"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("æ—¥", + "一", + "二", + "三", + "å››", + "五", + "å…­", + "æ—¥"); + +// full month names +Calendar._MN = new Array +("一月", + "二月", + "三月", + "四月", + "五月", + "六月", + "七月", + "八月", + "乿œˆ", + "åæœˆ", + "å一月", + "å二月"); + +// short month names +Calendar._SMN = new Array +("一月", + "二月", + "三月", + "四月", + "五月", + "六月", + "七月", + "八月", + "乿œˆ", + "åæœˆ", + "å一月", + "å二月"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "關於"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"æ—¥æœŸé¸æ“‡æ–¹æ³•:\n" + +"- 使用 \xab, \xbb 按鈕å¯é¸æ“‡å¹´ä»½\n" + +"- 使用 " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " 按鈕å¯é¸æ“‡æœˆä»½\n" + +"- 按ä½ä¸Šé¢çš„æŒ‰éˆ•å¯ä»¥åŠ å¿«é¸å–"; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"æ™‚é–“é¸æ“‡æ–¹æ³•:\n" + +"- 點擊任何的時間部份å¯å¢žåР其值\n" + +"- åŒæ™‚按Shiftéµå†é»žæ“Šå¯æ¸›å°‘其值\n" + +"- 點擊並拖曳å¯åŠ å¿«æ”¹è®Šçš„å€¼"; + +Calendar._TT["PREV_YEAR"] = "上一年 (按ä½é¸å–®)"; +Calendar._TT["PREV_MONTH"] = "下一年 (按ä½é¸å–®)"; +Calendar._TT["GO_TODAY"] = "到今日"; +Calendar._TT["NEXT_MONTH"] = "上一月 (按ä½é¸å–®)"; +Calendar._TT["NEXT_YEAR"] = "下一月 (按ä½é¸å–®)"; +Calendar._TT["SEL_DATE"] = "鏿“‡æ—¥æœŸ"; +Calendar._TT["DRAG_TO_MOVE"] = "拖曳"; +Calendar._TT["PART_TODAY"] = " (今日)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "å°‡ %s 顯示在å‰"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "關閉"; +Calendar._TT["TODAY"] = "今日"; +Calendar._TT["TIME_PART"] = "點擊oræ‹–æ›³å¯æ”¹è®Šæ™‚é–“(åŒæ™‚按Shift為減)"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "週"; +Calendar._TT["TIME"] = "Time:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-big5.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-big5.js new file mode 100644 index 00000000..b14335ef --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-big5.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar big5 language +// Author: Gary Fu, +// Encoding: big5 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("¬P´Á¤é", + "¬P´Á¤@", + "¬P´Á¤G", + "¬P´Á¤T", + "¬P´Á¥|", + "¬P´Á¤­", + "¬P´Á¤»", + "¬P´Á¤é"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("¤é", + "¤@", + "¤G", + "¤T", + "¥|", + "¤­", + "¤»", + "¤é"); + +// full month names +Calendar._MN = new Array +("¤@¤ë", + "¤G¤ë", + "¤T¤ë", + "¥|¤ë", + "¤­¤ë", + "¤»¤ë", + "¤C¤ë", + "¤K¤ë", + "¤E¤ë", + "¤Q¤ë", + "¤Q¤@¤ë", + "¤Q¤G¤ë"); + +// short month names +Calendar._SMN = new Array +("¤@¤ë", + "¤G¤ë", + "¤T¤ë", + "¥|¤ë", + "¤­¤ë", + "¤»¤ë", + "¤C¤ë", + "¤K¤ë", + "¤E¤ë", + "¤Q¤ë", + "¤Q¤@¤ë", + "¤Q¤G¤ë"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Ãö©ó"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"¤é´Á¿ï¾Ü¤èªk:\n" + +"- ¨Ï¥Î \xab, \xbb «ö¶s¥i¿ï¾Ü¦~¥÷\n" + +"- ¨Ï¥Î " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " «ö¶s¥i¿ï¾Ü¤ë¥÷\n" + +"- «ö¦í¤W­±ªº«ö¶s¥i¥H¥[§Ö¿ï¨ú"; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"®É¶¡¿ï¾Ü¤èªk:\n" + +"- ÂIÀ»¥ô¦óªº®É¶¡³¡¥÷¥i¼W¥[¨ä­È\n" + +"- ¦P®É«öShiftÁä¦AÂIÀ»¥i´î¤Ö¨ä­È\n" + +"- ÂIÀ»¨Ã©ì¦²¥i¥[§Ö§ïÅܪº­È"; + +Calendar._TT["PREV_YEAR"] = "¤W¤@¦~ («ö¦í¿ï³æ)"; +Calendar._TT["PREV_MONTH"] = "¤U¤@¦~ («ö¦í¿ï³æ)"; +Calendar._TT["GO_TODAY"] = "¨ì¤µ¤é"; +Calendar._TT["NEXT_MONTH"] = "¤W¤@¤ë («ö¦í¿ï³æ)"; +Calendar._TT["NEXT_YEAR"] = "¤U¤@¤ë («ö¦í¿ï³æ)"; +Calendar._TT["SEL_DATE"] = "¿ï¾Ü¤é´Á"; +Calendar._TT["DRAG_TO_MOVE"] = "©ì¦²"; +Calendar._TT["PART_TODAY"] = " (¤µ¤é)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "±N %s Åã¥Ü¦b«e"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Ãö³¬"; +Calendar._TT["TODAY"] = "¤µ¤é"; +Calendar._TT["TIME_PART"] = "ÂIÀ»or©ì¦²¥i§ïÅܮɶ¡(¦P®É«öShift¬°´î)"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "¶g"; +Calendar._TT["TIME"] = "Time:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-br.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-br.js new file mode 100644 index 00000000..8cdf5014 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-br.js @@ -0,0 +1,108 @@ +// ** I18N + +// Calendar pt-BR language +// Author: Fernando Dourado, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Domingo", + "Segunda", + "Terça", + "Quarta", + "Quinta", + "Sexta", + "Sabádo", + "Domingo"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +// [No changes using default values] + +// full month names +Calendar._MN = new Array +("Janeiro", + "Fevereiro", + "Março", + "Abril", + "Maio", + "Junho", + "Julho", + "Agosto", + "Setembro", + "Outubro", + "Novembro", + "Dezembro"); + +// short month names +// [No changes using default values] + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Sobre o calendário"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Translate to portuguese Brazil (pt-BR) by Fernando Dourado (fernando.dourado@ig.com.br)\n" + +"Tradução para o português Brasil (pt-BR) por Fernando Dourado (fernando.dourado@ig.com.br)" + +"\n\n" + +"Selecionar data:\n" + +"- Use as teclas \xab, \xbb para selecionar o ano\n" + +"- Use as teclas " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " para selecionar o mês\n" + +"- Clique e segure com o mouse em qualquer botão para selecionar rapidamente."; + +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selecionar hora:\n" + +"- Clique em qualquer uma das partes da hora para aumentar\n" + +"- ou Shift-clique para diminuir\n" + +"- ou clique e arraste para selecionar rapidamente."; + +Calendar._TT["PREV_YEAR"] = "Ano anterior (clique e segure para menu)"; +Calendar._TT["PREV_MONTH"] = "Mês anterior (clique e segure para menu)"; +Calendar._TT["GO_TODAY"] = "Ir para a data atual"; +Calendar._TT["NEXT_MONTH"] = "Próximo mês (clique e segure para menu)"; +Calendar._TT["NEXT_YEAR"] = "Próximo ano (clique e segure para menu)"; +Calendar._TT["SEL_DATE"] = "Selecione uma data"; +Calendar._TT["DRAG_TO_MOVE"] = "Clique e segure para mover"; +Calendar._TT["PART_TODAY"] = " (hoje)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Exibir %s primeiro"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Fechar"; +Calendar._TT["TODAY"] = "Hoje"; +Calendar._TT["TIME_PART"] = "(Shift-)Clique ou arraste para mudar o valor"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%d de %B de %Y"; + +Calendar._TT["WK"] = "sem"; +Calendar._TT["TIME"] = "Hora:"; + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ca.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ca.js new file mode 100644 index 00000000..bd2e7ba3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ca.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar CA language +// Author: Mihai Bazon, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Diumenge", + "Dilluns", + "Dimarts", + "Dimecres", + "Dijous", + "Divendres", + "Dissabte", + "Diumenge"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Diu", + "Dil", + "Dmt", + "Dmc", + "Dij", + "Div", + "Dis", + "Diu"); + +// full month names +Calendar._MN = new Array +("Gener", + "Febrer", + "Març", + "Abril", + "Maig", + "Juny", + "Juliol", + "Agost", + "Setembre", + "Octubre", + "Novembre", + "Desembre"); + +// short month names +Calendar._SMN = new Array +("Gen", + "Feb", + "Mar", + "Abr", + "Mai", + "Jun", + "Jul", + "Ago", + "Set", + "Oct", + "Nov", + "Des"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Sobre el calendari"; + +Calendar._TT["ABOUT"] = +"DHTML Selector de Data/Hora\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Sel.lecció de Dates:\n" + +"- Fes servir els botons \xab, \xbb per sel.leccionar l'any\n" + +"- Fes servir els botons " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " per se.lecciconar el mes\n" + +"- Manté el ratolí apretat en qualsevol dels anteriors per sel.lecció ràpida."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Time selection:\n" + +"- claca en qualsevol de les parts de la hora per augmentar-les\n" + +"- o Shift-click per decrementar-la\n" + +"- or click and arrastra per sel.lecció ràpida."; + +Calendar._TT["PREV_YEAR"] = "Any anterior (Mantenir per menu)"; +Calendar._TT["PREV_MONTH"] = "Mes anterior (Mantenir per menu)"; +Calendar._TT["GO_TODAY"] = "Anar a avui"; +Calendar._TT["NEXT_MONTH"] = "Mes següent (Mantenir per menu)"; +Calendar._TT["NEXT_YEAR"] = "Any següent (Mantenir per menu)"; +Calendar._TT["SEL_DATE"] = "Sel.leccionar data"; +Calendar._TT["DRAG_TO_MOVE"] = "Arrastrar per moure"; +Calendar._TT["PART_TODAY"] = " (avui)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Mostra %s primer"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Tanca"; +Calendar._TT["TODAY"] = "Avui"; +Calendar._TT["TIME_PART"] = "(Shift-)Click a arrastra per canviar el valor"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "st"; +Calendar._TT["TIME"] = "Hora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-cs-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-cs-utf8.js new file mode 100644 index 00000000..fd0c0602 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-cs-utf8.js @@ -0,0 +1,65 @@ +/* + calendar-cs-win.js + language: Czech + encoding: windows-1250 + author: Lubos Jerabek (xnet@seznam.cz) + Jan Uhlir (espinosa@centrum.cz) +*/ + +// ** I18N +Calendar._DN = new Array('NedÄ›le','PondÄ›lí','Úterý','StÅ™eda','ÄŒtvrtek','Pátek','Sobota','NedÄ›le'); +Calendar._SDN = new Array('Ne','Po','Út','St','ÄŒt','Pá','So','Ne'); +Calendar._MN = new Array('Leden','Únor','BÅ™ezen','Duben','KvÄ›ten','ÄŒerven','ÄŒervenec','Srpen','Září','Říjen','Listopad','Prosinec'); +Calendar._SMN = new Array('Led','Úno','BÅ™e','Dub','KvÄ›','ÄŒrv','ÄŒvc','Srp','Zář','Říj','Lis','Pro'); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O komponentÄ› kalendář"; +Calendar._TT["TOGGLE"] = "ZmÄ›na prvního dne v týdnu"; +Calendar._TT["PREV_YEAR"] = "PÅ™edchozí rok (pÅ™idrž pro menu)"; +Calendar._TT["PREV_MONTH"] = "PÅ™edchozí mÄ›síc (pÅ™idrž pro menu)"; +Calendar._TT["GO_TODAY"] = "DneÅ¡ní datum"; +Calendar._TT["NEXT_MONTH"] = "Další mÄ›síc (pÅ™idrž pro menu)"; +Calendar._TT["NEXT_YEAR"] = "Další rok (pÅ™idrž pro menu)"; +Calendar._TT["SEL_DATE"] = "Vyber datum"; +Calendar._TT["DRAG_TO_MOVE"] = "ChyÅ¥ a táhni, pro pÅ™esun"; +Calendar._TT["PART_TODAY"] = " (dnes)"; +Calendar._TT["MON_FIRST"] = "Ukaž jako první PondÄ›lí"; +//Calendar._TT["SUN_FIRST"] = "Ukaž jako první NedÄ›li"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"VýbÄ›r datumu:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Použijte tlaÄítka " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " k výbÄ›ru mÄ›síce\n" + +"- Podržte tlaÄítko myÅ¡i na jakémkoliv z tÄ›ch tlaÄítek pro rychlejší výbÄ›r."; + +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"VýbÄ›r Äasu:\n" + +"- KliknÄ›te na jakoukoliv z Äástí výbÄ›ru Äasu pro zvýšení.\n" + +"- nebo Shift-click pro snížení\n" + +"- nebo kliknÄ›te a táhnÄ›te pro rychlejší výbÄ›r."; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Zobraz %s první"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Zavřít"; +Calendar._TT["TODAY"] = "Dnes"; +Calendar._TT["TIME_PART"] = "(Shift-)Klikni nebo táhni pro zmÄ›nu hodnoty"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "d.m.yy"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "ÄŒas:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-cs-win.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-cs-win.js new file mode 100644 index 00000000..bd96c521 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-cs-win.js @@ -0,0 +1,65 @@ +/* + calendar-cs-win.js + language: Czech + encoding: windows-1250 + author: Lubos Jerabek (xnet@seznam.cz) + Jan Uhlir (espinosa@centrum.cz) +*/ + +// ** I18N +Calendar._DN = new Array('Nedìle','Pondìlí','Úterý','Støeda','Ètvrtek','Pátek','Sobota','Nedìle'); +Calendar._SDN = new Array('Ne','Po','Út','St','Èt','Pá','So','Ne'); +Calendar._MN = new Array('Leden','Únor','Bøezen','Duben','Kvìten','Èerven','Èervenec','Srpen','Záøí','Øíjen','Listopad','Prosinec'); +Calendar._SMN = new Array('Led','Úno','Bøe','Dub','Kvì','Èrv','Èvc','Srp','Záø','Øíj','Lis','Pro'); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O komponentì kalendáø"; +Calendar._TT["TOGGLE"] = "Zmìna prvního dne v týdnu"; +Calendar._TT["PREV_YEAR"] = "Pøedchozí rok (pøidrž pro menu)"; +Calendar._TT["PREV_MONTH"] = "Pøedchozí mìsíc (pøidrž pro menu)"; +Calendar._TT["GO_TODAY"] = "Dnešní datum"; +Calendar._TT["NEXT_MONTH"] = "Další mìsíc (pøidrž pro menu)"; +Calendar._TT["NEXT_YEAR"] = "Další rok (pøidrž pro menu)"; +Calendar._TT["SEL_DATE"] = "Vyber datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Chy a táhni, pro pøesun"; +Calendar._TT["PART_TODAY"] = " (dnes)"; +Calendar._TT["MON_FIRST"] = "Ukaž jako první Pondìlí"; +//Calendar._TT["SUN_FIRST"] = "Ukaž jako první Nedìli"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Výbìr datumu:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Použijte tlaèítka " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " k výbìru mìsíce\n" + +"- Podržte tlaèítko myši na jakémkoliv z tìch tlaèítek pro rychlejší výbìr."; + +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Výbìr èasu:\n" + +"- Kliknìte na jakoukoliv z èástí výbìru èasu pro zvýšení.\n" + +"- nebo Shift-click pro snížení\n" + +"- nebo kliknìte a táhnìte pro rychlejší výbìr."; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Zobraz %s první"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Zavøít"; +Calendar._TT["TODAY"] = "Dnes"; +Calendar._TT["TIME_PART"] = "(Shift-)Klikni nebo táhni pro zmìnu hodnoty"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "d.m.yy"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Èas:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-da.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-da.js new file mode 100644 index 00000000..2e156570 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-da.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar DA language +// Author: Michael Thingmand Henriksen, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Søndag", +"Mandag", +"Tirsdag", +"Onsdag", +"Torsdag", +"Fredag", +"Lørdag", +"Søndag"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Søn", +"Man", +"Tir", +"Ons", +"Tor", +"Fre", +"Lør", +"Søn"); + +// full month names +Calendar._MN = new Array +("Januar", +"Februar", +"Marts", +"April", +"Maj", +"Juni", +"Juli", +"August", +"September", +"Oktober", +"November", +"December"); + +// short month names +Calendar._SMN = new Array +("Jan", +"Feb", +"Mar", +"Apr", +"Maj", +"Jun", +"Jul", +"Aug", +"Sep", +"Okt", +"Nov", +"Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Om Kalenderen"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For den seneste version besøg: http://www.dynarch.com/projects/calendar/\n"; + +"Distribueret under GNU LGPL. Se http://gnu.org/licenses/lgpl.html for detajler." + +"\n\n" + +"Valg af dato:\n" + +"- Brug \xab, \xbb knapperne for at vælge Ã¥r\n" + +"- Brug " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " knapperne for at vælge mÃ¥ned\n" + +"- Hold knappen pÃ¥ musen nede pÃ¥ knapperne ovenfor for hurtigere valg."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Valg af tid:\n" + +"- Klik pÃ¥ en vilkÃ¥rlig del for større værdi\n" + +"- eller Shift-klik for for mindre værdi\n" + +"- eller klik og træk for hurtigere valg."; + +Calendar._TT["PREV_YEAR"] = "Ét Ã¥r tilbage (hold for menu)"; +Calendar._TT["PREV_MONTH"] = "Én mÃ¥ned tilbage (hold for menu)"; +Calendar._TT["GO_TODAY"] = "GÃ¥ til i dag"; +Calendar._TT["NEXT_MONTH"] = "Én mÃ¥ned frem (hold for menu)"; +Calendar._TT["NEXT_YEAR"] = "Ét Ã¥r frem (hold for menu)"; +Calendar._TT["SEL_DATE"] = "Vælg dag"; +Calendar._TT["DRAG_TO_MOVE"] = "Træk vinduet"; +Calendar._TT["PART_TODAY"] = " (i dag)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Vis %s først"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Luk"; +Calendar._TT["TODAY"] = "I dag"; +Calendar._TT["TIME_PART"] = "(Shift-)klik eller træk for at ændre værdi"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "Uge"; +Calendar._TT["TIME"] = "Tid:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-de.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-de.js new file mode 100644 index 00000000..6e9e7054 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-de.js @@ -0,0 +1,124 @@ +// ** I18N + +// Calendar DE language +// Author: Jack (tR), +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sonntag", + "Montag", + "Dienstag", + "Mittwoch", + "Donnerstag", + "Freitag", + "Samstag", + "Sonntag"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("So", + "Mo", + "Di", + "Mi", + "Do", + "Fr", + "Sa", + "So"); + +// full month names +Calendar._MN = new Array +("Januar", + "Februar", + "M\u00e4rz", + "April", + "Mai", + "Juni", + "Juli", + "August", + "September", + "Oktober", + "November", + "Dezember"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "M\u00e4r", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Okt", + "Nov", + "Dez"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "\u00DCber dieses Kalendarmodul"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Datum ausw\u00e4hlen:\n" + +"- Benutzen Sie die \xab, \xbb Buttons um das Jahr zu w\u00e4hlen\n" + +"- Benutzen Sie die " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " Buttons um den Monat zu w\u00e4hlen\n" + +"- F\u00fcr eine Schnellauswahl halten Sie die Maustaste \u00fcber diesen Buttons fest."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Zeit ausw\u00e4hlen:\n" + +"- Klicken Sie auf die Teile der Uhrzeit, um diese zu erh\u00F6hen\n" + +"- oder klicken Sie mit festgehaltener Shift-Taste um diese zu verringern\n" + +"- oder klicken und festhalten f\u00fcr Schnellauswahl."; + +Calendar._TT["TOGGLE"] = "Ersten Tag der Woche w\u00e4hlen"; +Calendar._TT["PREV_YEAR"] = "Voriges Jahr (Festhalten f\u00fcr Schnellauswahl)"; +Calendar._TT["PREV_MONTH"] = "Voriger Monat (Festhalten f\u00fcr Schnellauswahl)"; +Calendar._TT["GO_TODAY"] = "Heute ausw\u00e4hlen"; +Calendar._TT["NEXT_MONTH"] = "N\u00e4chst. Monat (Festhalten f\u00fcr Schnellauswahl)"; +Calendar._TT["NEXT_YEAR"] = "N\u00e4chst. Jahr (Festhalten f\u00fcr Schnellauswahl)"; +Calendar._TT["SEL_DATE"] = "Datum ausw\u00e4hlen"; +Calendar._TT["DRAG_TO_MOVE"] = "Zum Bewegen festhalten"; +Calendar._TT["PART_TODAY"] = " (Heute)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Woche beginnt mit %s "; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Schlie\u00dfen"; +Calendar._TT["TODAY"] = "Heute"; +Calendar._TT["TIME_PART"] = "(Shift-)Klick oder Festhalten und Ziehen um den Wert zu \u00e4ndern"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d.%m.%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Zeit:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-du.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-du.js new file mode 100644 index 00000000..9c9051fd --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-du.js @@ -0,0 +1,45 @@ +// ** I18N +Calendar._DN = new Array +("Zondag", + "Maandag", + "Dinsdag", + "Woensdag", + "Donderdag", + "Vrijdag", + "Zaterdag", + "Zondag"); +Calendar._MN = new Array +("Januari", + "Februari", + "Maart", + "April", + "Mei", + "Juni", + "Juli", + "Augustus", + "September", + "Oktober", + "November", + "December"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["TOGGLE"] = "Toggle startdag van de week"; +Calendar._TT["PREV_YEAR"] = "Vorig jaar (indrukken voor menu)"; +Calendar._TT["PREV_MONTH"] = "Vorige month (indrukken voor menu)"; +Calendar._TT["GO_TODAY"] = "Naar Vandaag"; +Calendar._TT["NEXT_MONTH"] = "Volgende Maand (indrukken voor menu)"; +Calendar._TT["NEXT_YEAR"] = "Volgend jaar (indrukken voor menu)"; +Calendar._TT["SEL_DATE"] = "Selecteer datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Sleep om te verplaatsen"; +Calendar._TT["PART_TODAY"] = " (vandaag)"; +Calendar._TT["MON_FIRST"] = "Toon Maandag eerst"; +Calendar._TT["SUN_FIRST"] = "Toon Zondag eerst"; +Calendar._TT["CLOSE"] = "Sluiten"; +Calendar._TT["TODAY"] = "Vandaag"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "y-mm-dd"; +Calendar._TT["TT_DATE_FORMAT"] = "D, M d"; + +Calendar._TT["WK"] = "wk"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-el.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-el.js new file mode 100644 index 00000000..43a9b2ce --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-el.js @@ -0,0 +1,89 @@ +// ** I18N +Calendar._DN = new Array +("ΚυÏιακή", + "ΔευτέÏα", + "ΤÏίτη", + "ΤετάÏτη", + "Πέμπτη", + "ΠαÏασκευή", + "Σάββατο", + "ΚυÏιακή"); + +Calendar._SDN = new Array +("Κυ", + "Δε", + "TÏ", + "Τε", + "Πε", + "Πα", + "Σα", + "Κυ"); + +Calendar._MN = new Array +("ΙανουάÏιος", + "ΦεβÏουάÏιος", + "ΜάÏτιος", + "ΑπÏίλιος", + "Μάϊος", + "ΙοÏνιος", + "ΙοÏλιος", + "ΑÏγουστος", + "ΣεπτέμβÏιος", + "ΟκτώβÏιος", + "ÎοέμβÏιος", + "ΔεκέμβÏιος"); + +Calendar._SMN = new Array +("Ιαν", + "Φεβ", + "ΜαÏ", + "ΑπÏ", + "Μαι", + "Ιουν", + "Ιουλ", + "Αυγ", + "Σεπ", + "Οκτ", + "Îοε", + "Δεκ"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Για το ημεÏολόγιο"; + +Calendar._TT["ABOUT"] = +"Επιλογέας ημεÏομηνίας/ÏŽÏας σε DHTML\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Για τελευταία έκδοση: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Επιλογή ημεÏομηνίας:\n" + +"- ΧÏησιμοποιείστε τα κουμπιά \xab, \xbb για επιλογή έτους\n" + +"- ΧÏησιμοποιείστε τα κουμπιά " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " για επιλογή μήνα\n" + +"- ΚÏατήστε κουμπί Ï€Î¿Î½Ï„Î¹ÎºÎ¿Ï Ï€Î±Ï„Î·Î¼Î­Î½Î¿ στα παÏαπάνω κουμπιά για πιο γÏήγοÏη επιλογή."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Επιλογή ÏŽÏας:\n" + +"- Κάντε κλικ σε ένα από τα μέÏη της ÏŽÏας για αÏξηση\n" + +"- ή Shift-κλικ για μείωση\n" + +"- ή κλικ και μετακίνηση για πιο γÏήγοÏη επιλογή."; +Calendar._TT["TOGGLE"] = "ΜπάÏα Ï€Ïώτης ημέÏας της εβδομάδας"; +Calendar._TT["PREV_YEAR"] = "ΠÏοηγ. έτος (κÏατήστε για το μενοÏ)"; +Calendar._TT["PREV_MONTH"] = "ΠÏοηγ. μήνας (κÏατήστε για το μενοÏ)"; +Calendar._TT["GO_TODAY"] = "ΣήμεÏα"; +Calendar._TT["NEXT_MONTH"] = "Επόμενος μήνας (κÏατήστε για το μενοÏ)"; +Calendar._TT["NEXT_YEAR"] = "Επόμενο έτος (κÏατήστε για το μενοÏ)"; +Calendar._TT["SEL_DATE"] = "Επιλέξτε ημεÏομηνία"; +Calendar._TT["DRAG_TO_MOVE"] = "ΣÏÏτε για να μετακινήσετε"; +Calendar._TT["PART_TODAY"] = " (σήμεÏα)"; +Calendar._TT["MON_FIRST"] = "Εμφάνιση ΔευτέÏας Ï€Ïώτα"; +Calendar._TT["SUN_FIRST"] = "Εμφάνιση ΚυÏιακής Ï€Ïώτα"; +Calendar._TT["CLOSE"] = "Κλείσιμο"; +Calendar._TT["TODAY"] = "ΣήμεÏα"; +Calendar._TT["TIME_PART"] = "(Shift-)κλικ ή μετακίνηση για αλλαγή"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "dd-mm-y"; +Calendar._TT["TT_DATE_FORMAT"] = "D, d M"; + +Calendar._TT["WK"] = "εβδ"; + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-en-US.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-en-US.js new file mode 100644 index 00000000..f079c3f1 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-en-US.js @@ -0,0 +1,127 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 0; + +// full month names +Calendar._MN = new Array +("January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "About the calendar"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Date selection:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" + +"- Hold mouse button on any of the above buttons for faster selection."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Time selection:\n" + +"- Click on any of the time parts to increase it\n" + +"- or Shift-click to decrease it\n" + +"- or click and drag for faster selection."; + +Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)"; +Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)"; +Calendar._TT["GO_TODAY"] = "Go Today"; +Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)"; +Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)"; +Calendar._TT["SEL_DATE"] = "Select date"; +Calendar._TT["DRAG_TO_MOVE"] = "Drag to move"; +Calendar._TT["PART_TODAY"] = " (today)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Display %s first"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Close"; +Calendar._TT["TODAY"] = "Today"; +Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%m/%d/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Time:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-en-gb.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-en-gb.js new file mode 100644 index 00000000..d69406f3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-en-gb.js @@ -0,0 +1,127 @@ +// ** I18N + +// Calendar EN-GB language +// Author: Mihai Bazon, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 0; + +// full month names +Calendar._MN = new Array +("January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "About the calendar"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Date selection:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" + +"- Hold mouse button on any of the above buttons for faster selection."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Time selection:\n" + +"- Click on any of the time parts to increase it\n" + +"- or Shift-click to decrease it\n" + +"- or click and drag for faster selection."; + +Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)"; +Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)"; +Calendar._TT["GO_TODAY"] = "Go Today"; +Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)"; +Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)"; +Calendar._TT["SEL_DATE"] = "Select date"; +Calendar._TT["DRAG_TO_MOVE"] = "Drag to move"; +Calendar._TT["PART_TODAY"] = " (today)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Display %s first"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Close"; +Calendar._TT["TODAY"] = "Today"; +Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Time:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-en.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-en.js new file mode 100644 index 00000000..d6f0909b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-en.js @@ -0,0 +1,127 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 0; + +// full month names +Calendar._MN = new Array +("January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "About the calendar"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Date selection:\n" + +"- Use the \xab, \xbb buttons to select year\n" + +"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" + +"- Hold mouse button on any of the above buttons for faster selection."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Time selection:\n" + +"- Click on any of the time parts to increase it\n" + +"- or Shift-click to decrease it\n" + +"- or click and drag for faster selection."; + +Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)"; +Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)"; +Calendar._TT["GO_TODAY"] = "Go Today"; +Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)"; +Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)"; +Calendar._TT["SEL_DATE"] = "Select date"; +Calendar._TT["DRAG_TO_MOVE"] = "Drag to move"; +Calendar._TT["PART_TODAY"] = " (today)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Display %s first"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Close"; +Calendar._TT["TODAY"] = "Today"; +Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Time:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-es.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-es.js new file mode 100644 index 00000000..b9b1843f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-es.js @@ -0,0 +1,129 @@ +// ** I18N + +// Calendar ES (spanish) language +// Author: Mihai Bazon, +// Updater: Servilio Afre Puentes +// Updated: 2004-06-03 +// Encoding: utf-8 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Domingo", + "Lunes", + "Martes", + "Miércoles", + "Jueves", + "Viernes", + "Sábado", + "Domingo"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Dom", + "Lun", + "Mar", + "Mié", + "Jue", + "Vie", + "Sáb", + "Dom"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 1; + +// full month names +Calendar._MN = new Array +("Enero", + "Febrero", + "Marzo", + "Abril", + "Mayo", + "Junio", + "Julio", + "Agosto", + "Septiembre", + "Octubre", + "Noviembre", + "Diciembre"); + +// short month names +Calendar._SMN = new Array +("Ene", + "Feb", + "Mar", + "Abr", + "May", + "Jun", + "Jul", + "Ago", + "Sep", + "Oct", + "Nov", + "Dic"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Acerca del calendario"; + +Calendar._TT["ABOUT"] = +"Selector DHTML de Fecha/Hora\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Para conseguir la última versión visite: http://www.dynarch.com/projects/calendar/\n" + +"Distribuido bajo licencia GNU LGPL. Visite http://gnu.org/licenses/lgpl.html para más detalles." + +"\n\n" + +"Selección de fecha:\n" + +"- Use los botones \xab, \xbb para seleccionar el año\n" + +"- Use los botones " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " para seleccionar el mes\n" + +"- Mantenga pulsado el ratón en cualquiera de estos botones para una selección rápida."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selección de hora:\n" + +"- Pulse en cualquiera de las partes de la hora para incrementarla\n" + +"- o pulse las mayúsculas mientras hace clic para decrementarla\n" + +"- o haga clic y arrastre el ratón para una selección más rápida."; + +Calendar._TT["PREV_YEAR"] = "Año anterior (mantener para menú)"; +Calendar._TT["PREV_MONTH"] = "Mes anterior (mantener para menú)"; +Calendar._TT["GO_TODAY"] = "Ir a hoy"; +Calendar._TT["NEXT_MONTH"] = "Mes siguiente (mantener para menú)"; +Calendar._TT["NEXT_YEAR"] = "Año siguiente (mantener para menú)"; +Calendar._TT["SEL_DATE"] = "Seleccionar fecha"; +Calendar._TT["DRAG_TO_MOVE"] = "Arrastrar para mover"; +Calendar._TT["PART_TODAY"] = " (hoy)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Hacer %s primer día de la semana"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Cerrar"; +Calendar._TT["TODAY"] = "Hoy"; +Calendar._TT["TIME_PART"] = "(Mayúscula-)Clic o arrastre para cambiar valor"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %e de %B de %Y"; + +Calendar._TT["WK"] = "sem"; +Calendar._TT["TIME"] = "Hora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-fi.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-fi.js new file mode 100644 index 00000000..eae2032b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-fi.js @@ -0,0 +1,98 @@ +// ** I18N + +// Calendar FI language (Finnish, Suomi) +// Author: Jarno Käyhkö, +// Encoding: UTF-8 +// Distributed under the same terms as the calendar itself. + +// full day names +Calendar._DN = new Array +("Sunnuntai", + "Maanantai", + "Tiistai", + "Keskiviikko", + "Torstai", + "Perjantai", + "Lauantai", + "Sunnuntai"); + +// short day names +Calendar._SDN = new Array +("Su", + "Ma", + "Ti", + "Ke", + "To", + "Pe", + "La", + "Su"); + +// full month names +Calendar._MN = new Array +("Tammikuu", + "Helmikuu", + "Maaliskuu", + "Huhtikuu", + "Toukokuu", + "Kesäkuu", + "Heinäkuu", + "Elokuu", + "Syyskuu", + "Lokakuu", + "Marraskuu", + "Joulukuu"); + +// short month names +Calendar._SMN = new Array +("Tam", + "Hel", + "Maa", + "Huh", + "Tou", + "Kes", + "Hei", + "Elo", + "Syy", + "Lok", + "Mar", + "Jou"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Tietoja kalenterista"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Uusin versio osoitteessa: http://www.dynarch.com/projects/calendar/\n" + +"Julkaistu GNU LGPL lisenssin alaisuudessa. Lisätietoja osoitteessa http://gnu.org/licenses/lgpl.html" + +"\n\n" + +"Päivämäärä valinta:\n" + +"- Käytä \xab, \xbb painikkeita valitaksesi vuosi\n" + +"- Käytä " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " painikkeita valitaksesi kuukausi\n" + +"- Pitämällä hiiren painiketta minkä tahansa yllä olevan painikkeen kohdalla, saat näkyviin valikon nopeampaan siirtymiseen."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Ajan valinta:\n" + +"- Klikkaa kellonajan numeroita lisätäksesi aikaa\n" + +"- tai pitämällä Shift-näppäintä pohjassa saat aikaa taaksepäin\n" + +"- tai klikkaa ja pidä hiiren painike pohjassa sekä liikuta hiirtä muuttaaksesi aikaa nopeasti eteen- ja taaksepäin."; + +Calendar._TT["PREV_YEAR"] = "Edell. vuosi (paina hetki, näet valikon)"; +Calendar._TT["PREV_MONTH"] = "Edell. kuukausi (paina hetki, näet valikon)"; +Calendar._TT["GO_TODAY"] = "Siirry tähän päivään"; +Calendar._TT["NEXT_MONTH"] = "Seur. kuukausi (paina hetki, näet valikon)"; +Calendar._TT["NEXT_YEAR"] = "Seur. vuosi (paina hetki, näet valikon)"; +Calendar._TT["SEL_DATE"] = "Valitse päivämäärä"; +Calendar._TT["DRAG_TO_MOVE"] = "Siirrä kalenterin paikkaa"; +Calendar._TT["PART_TODAY"] = " (tänään)"; +Calendar._TT["MON_FIRST"] = "Näytä maanantai ensimmäisenä"; +Calendar._TT["SUN_FIRST"] = "Näytä sunnuntai ensimmäisenä"; +Calendar._TT["CLOSE"] = "Sulje"; +Calendar._TT["TODAY"] = "Tänään"; +Calendar._TT["TIME_PART"] = "(Shift-) Klikkaa tai liikuta muuttaaksesi aikaa"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d.%m.%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%d.%m.%Y"; + +Calendar._TT["WK"] = "Vko"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-fr.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-fr.js new file mode 100644 index 00000000..6e011b2e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-fr.js @@ -0,0 +1,125 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// Translator: David Duret, from previous french version + +// full day names +Calendar._DN = new Array +("Dimanche", + "Lundi", + "Mardi", + "Mercredi", + "Jeudi", + "Vendredi", + "Samedi", + "Dimanche"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Dim", + "Lun", + "Mar", + "Mar", + "Jeu", + "Ven", + "Sam", + "Dim"); + +// full month names +Calendar._MN = new Array +("Janvier", + "Février", + "Mars", + "Avril", + "Mai", + "Juin", + "Juillet", + "Août", + "Septembre", + "Octobre", + "Novembre", + "Décembre"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Fev", + "Mar", + "Avr", + "Mai", + "Juin", + "Juil", + "Aout", + "Sep", + "Oct", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "A propos du calendrier"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Heure Selecteur\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Pour la derniere version visitez : http://www.dynarch.com/projects/calendar/\n" + +"Distribué par GNU LGPL. Voir http://gnu.org/licenses/lgpl.html pour les details." + +"\n\n" + +"Selection de la date :\n" + +"- Utiliser les bouttons \xab, \xbb pour selectionner l\'annee\n" + +"- Utiliser les bouttons " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " pour selectionner les mois\n" + +"- Garder la souris sur n'importe quels boutons pour une selection plus rapide"; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selection de l\'heure :\n" + +"- Cliquer sur heures ou minutes pour incrementer\n" + +"- ou Maj-clic pour decrementer\n" + +"- ou clic et glisser-deplacer pour une selection plus rapide"; + +Calendar._TT["PREV_YEAR"] = "Année préc. (maintenir pour menu)"; +Calendar._TT["PREV_MONTH"] = "Mois préc. (maintenir pour menu)"; +Calendar._TT["GO_TODAY"] = "Atteindre la date du jour"; +Calendar._TT["NEXT_MONTH"] = "Mois suiv. (maintenir pour menu)"; +Calendar._TT["NEXT_YEAR"] = "Année suiv. (maintenir pour menu)"; +Calendar._TT["SEL_DATE"] = "Sélectionner une date"; +Calendar._TT["DRAG_TO_MOVE"] = "Déplacer"; +Calendar._TT["PART_TODAY"] = " (Aujourd'hui)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Afficher %s en premier"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Fermer"; +Calendar._TT["TODAY"] = "Aujourd'hui"; +Calendar._TT["TIME_PART"] = "(Maj-)Clic ou glisser pour modifier la valeur"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "Sem."; +Calendar._TT["TIME"] = "Heure :"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-he-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-he-utf8.js new file mode 100644 index 00000000..5fb7dd56 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-he-utf8.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar EN language +// Author: Idan Sofer, +// Encoding: UTF-8 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("ר×שון", + "שני", + "שלישי", + "רביעי", + "חמישי", + "שישי", + "שבת", + "ר×שון"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("×", + "ב", + "×’", + "ד", + "×”", + "ו", + "ש", + "×"); + +// full month names +Calendar._MN = new Array +("ינו×ר", + "פברו×ר", + "מרץ", + "×פריל", + "מ××™", + "יוני", + "יולי", + "×וגוסט", + "ספטמבר", + "×וקטובר", + "נובמבר", + "דצמבר"); + +// short month names +Calendar._SMN = new Array +("×™× ×", + "פבר", + "מרץ", + "×פר", + "מ××™", + "יונ", + "יול", + "×וג", + "ספט", + "×וק", + "נוב", + "דצמ"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "×ודות השנתון"; + +Calendar._TT["ABOUT"] = +"בחרן ת×ריך/שעה DHTML\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"×”×’×™×¨×¡× ×”×חרונה זמינה ב: http://www.dynarch.com/projects/calendar/\n" + +"מופץ תחת זיכיון ×” GNU LGPL. עיין ב http://gnu.org/licenses/lgpl.html ×œ×¤×¨×˜×™× × ×•×¡×¤×™×." + +"\n\n" + +בחירת ת×ריך:\n" + +"- השתמש ×‘×›×¤×ª×•×¨×™× \xab, \xbb לבחירת שנה\n" + +"- השתמש ×‘×›×¤×ª×•×¨×™× " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " לבחירת חודש\n" + +"- ×”×—×–×§ העכבר לחוץ מעל ×”×›×¤×ª×•×¨×™× ×”×ž×•×–×›×¨×™× ×œ×¢×™×œ לבחירה מהירה יותר."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"בחירת זמן:\n" + +"- לחץ על כל ×חד מחלקי הזמן כדי להוסיף\n" + +"- ×ו shift בשילוב ×¢× ×œ×—×™×¦×” כדי להחסיר\n" + +"- ×ו לחץ וגרור לפעולה מהירה יותר."; + +Calendar._TT["PREV_YEAR"] = "שנה קודמת - ×”×—×–×§ לקבלת תפריט"; +Calendar._TT["PREV_MONTH"] = "חודש ×§×•×“× - ×”×—×–×§ לקבלת תפריט"; +Calendar._TT["GO_TODAY"] = "עבור להיו×"; +Calendar._TT["NEXT_MONTH"] = "חודש ×”×‘× - ×”×—×–×§ לתפריט"; +Calendar._TT["NEXT_YEAR"] = "שנה הב××” - ×”×—×–×§ לתפריט"; +Calendar._TT["SEL_DATE"] = "בחר ת×ריך"; +Calendar._TT["DRAG_TO_MOVE"] = "גרור להזזה"; +Calendar._TT["PART_TODAY"] = " )היו×("; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "הצג %s קוד×"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "6"; + +Calendar._TT["CLOSE"] = "סגור"; +Calendar._TT["TODAY"] = "היו×"; +Calendar._TT["TIME_PART"] = "(שיפט-)לחץ וגרור כדי לשנות ערך"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "שעה::"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-hr-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-hr-utf8.js new file mode 100644 index 00000000..d569cfd9 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-hr-utf8.js @@ -0,0 +1,49 @@ +/* Croatian language file for the DHTML Calendar version 0.9.2 +* Author Krunoslav Zubrinic , June 2003. +* Feel free to use this script under the terms of the GNU Lesser General +* Public License, as long as you do not remove or alter this notice. +*/ +Calendar._DN = new Array +("Nedjelja", + "Ponedjeljak", + "Utorak", + "Srijeda", + "ÄŒetvrtak", + "Petak", + "Subota", + "Nedjelja"); +Calendar._MN = new Array +("SijeÄanj", + "VeljaÄa", + "Ožujak", + "Travanj", + "Svibanj", + "Lipanj", + "Srpanj", + "Kolovoz", + "Rujan", + "Listopad", + "Studeni", + "Prosinac"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["TOGGLE"] = "Promjeni dan s kojim poÄinje tjedan"; +Calendar._TT["PREV_YEAR"] = "Prethodna godina (dugi pritisak za meni)"; +Calendar._TT["PREV_MONTH"] = "Prethodni mjesec (dugi pritisak za meni)"; +Calendar._TT["GO_TODAY"] = "Idi na tekući dan"; +Calendar._TT["NEXT_MONTH"] = "Slijedeći mjesec (dugi pritisak za meni)"; +Calendar._TT["NEXT_YEAR"] = "Slijedeća godina (dugi pritisak za meni)"; +Calendar._TT["SEL_DATE"] = "Izaberite datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Pritisni i povuci za promjenu pozicije"; +Calendar._TT["PART_TODAY"] = " (today)"; +Calendar._TT["MON_FIRST"] = "Prikaži ponedjeljak kao prvi dan"; +Calendar._TT["SUN_FIRST"] = "Prikaži nedjelju kao prvi dan"; +Calendar._TT["CLOSE"] = "Zatvori"; +Calendar._TT["TODAY"] = "Danas"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "dd-mm-y"; +Calendar._TT["TT_DATE_FORMAT"] = "DD, dd.mm.y"; + +Calendar._TT["WK"] = "Tje"; \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-hr.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-hr.js new file mode 100644 index 00000000..88d4238b Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-hr.js differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-hu.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-hu.js new file mode 100644 index 00000000..8c70b7b0 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-hu.js @@ -0,0 +1,124 @@ +// ** I18N + +// Calendar HU language +// Author: ??? +// Modifier: KARASZI Istvan, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Vasárnap", + "Hétfõ", + "Kedd", + "Szerda", + "Csütörtök", + "Péntek", + "Szombat", + "Vasárnap"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("v", + "h", + "k", + "sze", + "cs", + "p", + "szo", + "v"); + +// full month names +Calendar._MN = new Array +("január", + "február", + "március", + "április", + "május", + "június", + "július", + "augusztus", + "szeptember", + "október", + "november", + "december"); + +// short month names +Calendar._SMN = new Array +("jan", + "feb", + "már", + "ápr", + "máj", + "jún", + "júl", + "aug", + "sze", + "okt", + "nov", + "dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "A kalendáriumról"; + +Calendar._TT["ABOUT"] = +"DHTML dátum/idõ kiválasztó\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"a legfrissebb verzió megtalálható: http://www.dynarch.com/projects/calendar/\n" + +"GNU LGPL alatt terjesztve. Lásd a http://gnu.org/licenses/lgpl.html oldalt a részletekhez." + +"\n\n" + +"Dátum választás:\n" + +"- használja a \xab, \xbb gombokat az év kiválasztásához\n" + +"- használja a " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " gombokat a hónap kiválasztásához\n" + +"- tartsa lenyomva az egérgombot a gyors választáshoz."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Idõ választás:\n" + +"- kattintva növelheti az idõt\n" + +"- shift-tel kattintva csökkentheti\n" + +"- lenyomva tartva és húzva gyorsabban kiválaszthatja."; + +Calendar._TT["PREV_YEAR"] = "Elõzõ év (tartsa nyomva a menühöz)"; +Calendar._TT["PREV_MONTH"] = "Elõzõ hónap (tartsa nyomva a menühöz)"; +Calendar._TT["GO_TODAY"] = "Mai napra ugrás"; +Calendar._TT["NEXT_MONTH"] = "Köv. hónap (tartsa nyomva a menühöz)"; +Calendar._TT["NEXT_YEAR"] = "Köv. év (tartsa nyomva a menühöz)"; +Calendar._TT["SEL_DATE"] = "Válasszon dátumot"; +Calendar._TT["DRAG_TO_MOVE"] = "Húzza a mozgatáshoz"; +Calendar._TT["PART_TODAY"] = " (ma)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "%s legyen a hét elsõ napja"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Bezár"; +Calendar._TT["TODAY"] = "Ma"; +Calendar._TT["TIME_PART"] = "(Shift-)Klikk vagy húzás az érték változtatásához"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%b %e, %a"; + +Calendar._TT["WK"] = "hét"; +Calendar._TT["TIME"] = "idõ:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-it.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-it.js new file mode 100644 index 00000000..f622c1d3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-it.js @@ -0,0 +1,124 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Translator: Fabio Di Bernardini, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Domenica", + "Lunedì", + "Martedì", + "Mercoledì", + "Giovedì", + "Venerdì", + "Sabato", + "Domenica"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Dom", + "Lun", + "Mar", + "Mer", + "Gio", + "Ven", + "Sab", + "Dom"); + +// full month names +Calendar._MN = new Array +("Gennaio", + "Febbraio", + "Marzo", + "Aprile", + "Maggio", + "Giugno", + "Luglio", + "Augosto", + "Settembre", + "Ottobre", + "Novembre", + "Dicembre"); + +// short month names +Calendar._SMN = new Array +("Gen", + "Feb", + "Mar", + "Apr", + "Mag", + "Giu", + "Lug", + "Ago", + "Set", + "Ott", + "Nov", + "Dic"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Informazioni sul calendario"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Per gli aggiornamenti: http://www.dynarch.com/projects/calendar/\n" + +"Distribuito sotto licenza GNU LGPL. Vedi http://gnu.org/licenses/lgpl.html per i dettagli." + +"\n\n" + +"Selezione data:\n" + +"- Usa \xab, \xbb per selezionare l'anno\n" + +"- Usa " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " per i mesi\n" + +"- Tieni premuto a lungo il mouse per accedere alle funzioni di selezione veloce."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selezione orario:\n" + +"- Clicca sul numero per incrementarlo\n" + +"- o Shift+click per decrementarlo\n" + +"- o click e sinistra o destra per variarlo."; + +Calendar._TT["PREV_YEAR"] = "Anno prec.(clicca a lungo per il menù)"; +Calendar._TT["PREV_MONTH"] = "Mese prec. (clicca a lungo per il menù)"; +Calendar._TT["GO_TODAY"] = "Oggi"; +Calendar._TT["NEXT_MONTH"] = "Pross. mese (clicca a lungo per il menù)"; +Calendar._TT["NEXT_YEAR"] = "Pross. anno (clicca a lungo per il menù)"; +Calendar._TT["SEL_DATE"] = "Seleziona data"; +Calendar._TT["DRAG_TO_MOVE"] = "Trascina per spostarlo"; +Calendar._TT["PART_TODAY"] = " (oggi)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Mostra prima %s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Chiudi"; +Calendar._TT["TODAY"] = "Oggi"; +Calendar._TT["TIME_PART"] = "(Shift-)Click o trascina per cambiare il valore"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a:%b:%e"; + +Calendar._TT["WK"] = "set"; +Calendar._TT["TIME"] = "Ora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-jp.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-jp.js new file mode 100644 index 00000000..b86f0da3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-jp.js @@ -0,0 +1,45 @@ +// ** I18N +Calendar._DN = new Array +("“ú", + "ŒŽ", + "‰Î", + "…", + "–Ø", + "‹à", + "“y", + "“ú"); +Calendar._MN = new Array +("1ŒŽ", + "2ŒŽ", + "3ŒŽ", + "4ŒŽ", + "5ŒŽ", + "6ŒŽ", + "7ŒŽ", + "8ŒŽ", + "9ŒŽ", + "10ŒŽ", + "11ŒŽ", + "12ŒŽ"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["TOGGLE"] = "T‚Ìʼn‚Ì—j“ú‚ðØ‚è‘Ö‚¦"; +Calendar._TT["PREV_YEAR"] = "‘O”N"; +Calendar._TT["PREV_MONTH"] = "‘OŒŽ"; +Calendar._TT["GO_TODAY"] = "¡“ú"; +Calendar._TT["NEXT_MONTH"] = "—‚ŒŽ"; +Calendar._TT["NEXT_YEAR"] = "—‚”N"; +Calendar._TT["SEL_DATE"] = "“ú•t‘I‘ð"; +Calendar._TT["DRAG_TO_MOVE"] = "ƒEƒBƒ“ƒhƒE‚̈ړ®"; +Calendar._TT["PART_TODAY"] = " (¡“ú)"; +Calendar._TT["MON_FIRST"] = "ŒŽ—j“ú‚ðæ“ª‚É"; +Calendar._TT["SUN_FIRST"] = "“ú—j“ú‚ðæ“ª‚É"; +Calendar._TT["CLOSE"] = "•‚¶‚é"; +Calendar._TT["TODAY"] = "¡“ú"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "y-mm-dd"; +Calendar._TT["TT_DATE_FORMAT"] = "%mŒŽ %d“ú (%a)"; + +Calendar._TT["WK"] = "T"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ko-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ko-utf8.js new file mode 100644 index 00000000..cdbc1caf --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ko-utf8.js @@ -0,0 +1,120 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Translation: Yourim Yi +// Encoding: EUC-KR +// lang : ko +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names + +Calendar._DN = new Array +("ì¼ìš”ì¼", + "월요ì¼", + "화요ì¼", + "수요ì¼", + "목요ì¼", + "금요ì¼", + "토요ì¼", + "ì¼ìš”ì¼"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("ì¼", + "ì›”", + "í™”", + "수", + "목", + "금", + "토", + "ì¼"); + +// full month names +Calendar._MN = new Array +("1ì›”", + "2ì›”", + "3ì›”", + "4ì›”", + "5ì›”", + "6ì›”", + "7ì›”", + "8ì›”", + "9ì›”", + "10ì›”", + "11ì›”", + "12ì›”"); + +// short month names +Calendar._SMN = new Array +("1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "calendar ì— ëŒ€í•´ì„œ"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"\n"+ +"최신 ë²„ì „ì„ ë°›ìœ¼ì‹œë ¤ë©´ http://www.dynarch.com/projects/calendar/ ì— ë°©ë¬¸í•˜ì„¸ìš”\n" + +"\n"+ +"GNU LGPL ë¼ì´ì„¼ìŠ¤ë¡œ ë°°í¬ë©ë‹ˆë‹¤. \n"+ +"ë¼ì´ì„¼ìŠ¤ì— ëŒ€í•œ ìžì„¸í•œ ë‚´ìš©ì€ http://gnu.org/licenses/lgpl.html ì„ ì½ìœ¼ì„¸ìš”." + +"\n\n" + +"ë‚ ì§œ ì„ íƒ:\n" + +"- ì—°ë„를 ì„ íƒí•˜ë ¤ë©´ \xab, \xbb ë²„íŠ¼ì„ ì‚¬ìš©í•©ë‹ˆë‹¤\n" + +"- ë‹¬ì„ ì„ íƒí•˜ë ¤ë©´ " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " ë²„íŠ¼ì„ ëˆ„ë¥´ì„¸ìš”\n" + +"- ê³„ì† ëˆ„ë¥´ê³  있으면 위 ê°’ë“¤ì„ ë¹ ë¥´ê²Œ ì„ íƒí•˜ì‹¤ 수 있습니다."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"시간 ì„ íƒ:\n" + +"- 마우스로 누르면 ì‹œê°„ì´ ì¦ê°€í•©ë‹ˆë‹¤\n" + +"- Shift 키와 함께 누르면 ê°ì†Œí•©ë‹ˆë‹¤\n" + +"- 누른 ìƒíƒœì—서 마우스를 움ì§ì´ë©´ 좀 ë” ë¹ ë¥´ê²Œ ê°’ì´ ë³€í•©ë‹ˆë‹¤.\n"; + +Calendar._TT["PREV_YEAR"] = "지난 í•´ (길게 누르면 목ë¡)"; +Calendar._TT["PREV_MONTH"] = "지난 달 (길게 누르면 목ë¡)"; +Calendar._TT["GO_TODAY"] = "오늘 날짜로"; +Calendar._TT["NEXT_MONTH"] = "ë‹¤ìŒ ë‹¬ (길게 누르면 목ë¡)"; +Calendar._TT["NEXT_YEAR"] = "ë‹¤ìŒ í•´ (길게 누르면 목ë¡)"; +Calendar._TT["SEL_DATE"] = "날짜를 ì„ íƒí•˜ì„¸ìš”"; +Calendar._TT["DRAG_TO_MOVE"] = "마우스 드래그로 ì´ë™ 하세요"; +Calendar._TT["PART_TODAY"] = " (오늘)"; +Calendar._TT["MON_FIRST"] = "월요ì¼ì„ 한 ì£¼ì˜ ì‹œìž‘ ìš”ì¼ë¡œ"; +Calendar._TT["SUN_FIRST"] = "ì¼ìš”ì¼ì„ 한 ì£¼ì˜ ì‹œìž‘ ìš”ì¼ë¡œ"; +Calendar._TT["CLOSE"] = "닫기"; +Calendar._TT["TODAY"] = "오늘"; +Calendar._TT["TIME_PART"] = "(Shift-)í´ë¦­ ë˜ëŠ” 드래그 하세요"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%b/%e [%a]"; + +Calendar._TT["WK"] = "주"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ko.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ko.js new file mode 100644 index 00000000..dc1da4bb --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ko.js @@ -0,0 +1,120 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Translation: Yourim Yi +// Encoding: EUC-KR +// lang : ko +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names + +Calendar._DN = new Array +("ÀÏ¿äÀÏ", + "¿ù¿äÀÏ", + "È­¿äÀÏ", + "¼ö¿äÀÏ", + "¸ñ¿äÀÏ", + "±Ý¿äÀÏ", + "Åä¿äÀÏ", + "ÀÏ¿äÀÏ"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("ÀÏ", + "¿ù", + "È­", + "¼ö", + "¸ñ", + "±Ý", + "Åä", + "ÀÏ"); + +// full month names +Calendar._MN = new Array +("1¿ù", + "2¿ù", + "3¿ù", + "4¿ù", + "5¿ù", + "6¿ù", + "7¿ù", + "8¿ù", + "9¿ù", + "10¿ù", + "11¿ù", + "12¿ù"); + +// short month names +Calendar._SMN = new Array +("1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "calendar ¿¡ ´ëÇØ¼­"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"\n"+ +"ÃֽйöÀüÀ» ¹ÞÀ¸½Ã·Á¸é http://www.dynarch.com/projects/calendar/ ¿¡ ¹æ¹®Çϼ¼¿ä\n" + +"\n"+ +"GNU LGPL ¶óÀ̼¾½º·Î ¹èÆ÷µË´Ï´Ù. \n"+ +"¶óÀ̼¾½º¿¡ ´ëÇÑ ÀÚ¼¼ÇÑ ³»¿ëÀº http://gnu.org/licenses/lgpl.html À» ÀÐÀ¸¼¼¿ä." + +"\n\n" + +"³¯Â¥ ¼±ÅÃ:\n" + +"- ¿¬µµ¸¦ ¼±ÅÃÇÏ·Á¸é \xab, \xbb ¹öưÀ» »ç¿ëÇÕ´Ï´Ù\n" + +"- ´ÞÀ» ¼±ÅÃÇÏ·Á¸é " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " ¹öưÀ» ´©¸£¼¼¿ä\n" + +"- °è¼Ó ´©¸£°í ÀÖÀ¸¸é À§ °ªµéÀ» ºü¸£°Ô ¼±ÅÃÇÏ½Ç ¼ö ÀÖ½À´Ï´Ù."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"½Ã°£ ¼±ÅÃ:\n" + +"- ¸¶¿ì½º·Î ´©¸£¸é ½Ã°£ÀÌ Áõ°¡ÇÕ´Ï´Ù\n" + +"- Shift Ű¿Í ÇÔ²² ´©¸£¸é °¨¼ÒÇÕ´Ï´Ù\n" + +"- ´©¸¥ »óÅ¿¡¼­ ¸¶¿ì½º¸¦ ¿òÁ÷À̸é Á» ´õ ºü¸£°Ô °ªÀÌ º¯ÇÕ´Ï´Ù.\n"; + +Calendar._TT["PREV_YEAR"] = "Áö³­ ÇØ (±æ°Ô ´©¸£¸é ¸ñ·Ï)"; +Calendar._TT["PREV_MONTH"] = "Áö³­ ´Þ (±æ°Ô ´©¸£¸é ¸ñ·Ï)"; +Calendar._TT["GO_TODAY"] = "¿À´Ã ³¯Â¥·Î"; +Calendar._TT["NEXT_MONTH"] = "´ÙÀ½ ´Þ (±æ°Ô ´©¸£¸é ¸ñ·Ï)"; +Calendar._TT["NEXT_YEAR"] = "´ÙÀ½ ÇØ (±æ°Ô ´©¸£¸é ¸ñ·Ï)"; +Calendar._TT["SEL_DATE"] = "³¯Â¥¸¦ ¼±ÅÃÇϼ¼¿ä"; +Calendar._TT["DRAG_TO_MOVE"] = "¸¶¿ì½º µå·¡±×·Î À̵¿ Çϼ¼¿ä"; +Calendar._TT["PART_TODAY"] = " (¿À´Ã)"; +Calendar._TT["MON_FIRST"] = "¿ù¿äÀÏÀ» ÇÑ ÁÖÀÇ ½ÃÀÛ ¿äÀÏ·Î"; +Calendar._TT["SUN_FIRST"] = "ÀÏ¿äÀÏÀ» ÇÑ ÁÖÀÇ ½ÃÀÛ ¿äÀÏ·Î"; +Calendar._TT["CLOSE"] = "´Ý±â"; +Calendar._TT["TODAY"] = "¿À´Ã"; +Calendar._TT["TIME_PART"] = "(Shift-)Ŭ¸¯ ¶Ç´Â µå·¡±× Çϼ¼¿ä"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%b/%e [%a]"; + +Calendar._TT["WK"] = "ÁÖ"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-lt-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-lt-utf8.js new file mode 100644 index 00000000..964865de --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-lt-utf8.js @@ -0,0 +1,114 @@ +// ** I18N + +// Calendar LT language +// Author: Martynas Majeris, +// Encoding: UTF-8 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sekmadienis", + "Pirmadienis", + "Antradienis", + "TreÄiadienis", + "Ketvirtadienis", + "Pentadienis", + "Å eÅ¡tadienis", + "Sekmadienis"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sek", + "Pir", + "Ant", + "Tre", + "Ket", + "Pen", + "Å eÅ¡", + "Sek"); + +// full month names +Calendar._MN = new Array +("Sausis", + "Vasaris", + "Kovas", + "Balandis", + "Gegužė", + "Birželis", + "Liepa", + "RugpjÅ«tis", + "RugsÄ—jis", + "Spalis", + "Lapkritis", + "Gruodis"); + +// short month names +Calendar._SMN = new Array +("Sau", + "Vas", + "Kov", + "Bal", + "Geg", + "Bir", + "Lie", + "Rgp", + "Rgs", + "Spa", + "Lap", + "Gru"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Apie kalendorių"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"NaujausiÄ… versijÄ… rasite: http://www.dynarch.com/projects/calendar/\n" + +"Platinamas pagal GNU LGPL licencijÄ…. Aplankykite http://gnu.org/licenses/lgpl.html" + +"\n\n" + +"Datos pasirinkimas:\n" + +"- Metų pasirinkimas: \xab, \xbb\n" + +"- MÄ—nesio pasirinkimas: " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + "\n" + +"- Nuspauskite ir laikykite pelÄ—s klavišą greitesniam pasirinkimui."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Laiko pasirinkimas:\n" + +"- Spustelkite ant valandų arba minuÄių - skaiÄius padidÄ—s vienetu.\n" + +"- Jei spausite kartu su Shift, skaiÄius sumažės.\n" + +"- Greitam pasirinkimui spustelkite ir pajudinkite pelÄ™."; + +Calendar._TT["PREV_YEAR"] = "Ankstesni metai (laikykite, jei norite meniu)"; +Calendar._TT["PREV_MONTH"] = "Ankstesnis mÄ—nuo (laikykite, jei norite meniu)"; +Calendar._TT["GO_TODAY"] = "Pasirinkti Å¡iandienÄ…"; +Calendar._TT["NEXT_MONTH"] = "Kitas mÄ—nuo (laikykite, jei norite meniu)"; +Calendar._TT["NEXT_YEAR"] = "Kiti metai (laikykite, jei norite meniu)"; +Calendar._TT["SEL_DATE"] = "Pasirinkite datÄ…"; +Calendar._TT["DRAG_TO_MOVE"] = "Tempkite"; +Calendar._TT["PART_TODAY"] = " (Å¡iandien)"; +Calendar._TT["MON_FIRST"] = "Pirma savaitÄ—s diena - pirmadienis"; +Calendar._TT["SUN_FIRST"] = "Pirma savaitÄ—s diena - sekmadienis"; +Calendar._TT["CLOSE"] = "Uždaryti"; +Calendar._TT["TODAY"] = "Å iandien"; +Calendar._TT["TIME_PART"] = "Spustelkite arba tempkite jei norite pakeisti"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %Y-%m-%d"; + +Calendar._TT["WK"] = "sav"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-lt.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-lt.js new file mode 100644 index 00000000..059cdf4f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-lt.js @@ -0,0 +1,114 @@ +// ** I18N + +// Calendar LT language +// Author: Martynas Majeris, +// Encoding: Windows-1257 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Sekmadienis", + "Pirmadienis", + "Antradienis", + "Treèiadienis", + "Ketvirtadienis", + "Pentadienis", + "Ðeðtadienis", + "Sekmadienis"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sek", + "Pir", + "Ant", + "Tre", + "Ket", + "Pen", + "Ðeð", + "Sek"); + +// full month names +Calendar._MN = new Array +("Sausis", + "Vasaris", + "Kovas", + "Balandis", + "Geguþë", + "Birþelis", + "Liepa", + "Rugpjûtis", + "Rugsëjis", + "Spalis", + "Lapkritis", + "Gruodis"); + +// short month names +Calendar._SMN = new Array +("Sau", + "Vas", + "Kov", + "Bal", + "Geg", + "Bir", + "Lie", + "Rgp", + "Rgs", + "Spa", + "Lap", + "Gru"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Apie kalendoriø"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Naujausià versijà rasite: http://www.dynarch.com/projects/calendar/\n" + +"Platinamas pagal GNU LGPL licencijà. Aplankykite http://gnu.org/licenses/lgpl.html" + +"\n\n" + +"Datos pasirinkimas:\n" + +"- Metø pasirinkimas: \xab, \xbb\n" + +"- Mënesio pasirinkimas: " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + "\n" + +"- Nuspauskite ir laikykite pelës klaviðà greitesniam pasirinkimui."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Laiko pasirinkimas:\n" + +"- Spustelkite ant valandø arba minuèiø - skaièus padidës vienetu.\n" + +"- Jei spausite kartu su Shift, skaièius sumaþës.\n" + +"- Greitam pasirinkimui spustelkite ir pajudinkite pelæ."; + +Calendar._TT["PREV_YEAR"] = "Ankstesni metai (laikykite, jei norite meniu)"; +Calendar._TT["PREV_MONTH"] = "Ankstesnis mënuo (laikykite, jei norite meniu)"; +Calendar._TT["GO_TODAY"] = "Pasirinkti ðiandienà"; +Calendar._TT["NEXT_MONTH"] = "Kitas mënuo (laikykite, jei norite meniu)"; +Calendar._TT["NEXT_YEAR"] = "Kiti metai (laikykite, jei norite meniu)"; +Calendar._TT["SEL_DATE"] = "Pasirinkite datà"; +Calendar._TT["DRAG_TO_MOVE"] = "Tempkite"; +Calendar._TT["PART_TODAY"] = " (ðiandien)"; +Calendar._TT["MON_FIRST"] = "Pirma savaitës diena - pirmadienis"; +Calendar._TT["SUN_FIRST"] = "Pirma savaitës diena - sekmadienis"; +Calendar._TT["CLOSE"] = "Uþdaryti"; +Calendar._TT["TODAY"] = "Ðiandien"; +Calendar._TT["TIME_PART"] = "Spustelkite arba tempkite jei norite pakeisti"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %Y-%m-%d"; + +Calendar._TT["WK"] = "sav"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-lv.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-lv.js new file mode 100644 index 00000000..4cb5a138 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-lv.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar LV language +// Author: Juris Valdovskis, +// Encoding: cp1257 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Svçtdiena", + "Pirmdiena", + "Otrdiena", + "Treðdiena", + "Ceturdiena", + "Piektdiena", + "Sestdiena", + "Svçtdiena"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Sv", + "Pr", + "Ot", + "Tr", + "Ce", + "Pk", + "Se", + "Sv"); + +// full month names +Calendar._MN = new Array +("Janvâris", + "Februâris", + "Marts", + "Aprîlis", + "Maijs", + "Jûnijs", + "Jûlijs", + "Augusts", + "Septembris", + "Oktobris", + "Novembris", + "Decembris"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "Mai", + "Jûn", + "Jûl", + "Aug", + "Sep", + "Okt", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Par kalendâru"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Datuma izvçle:\n" + +"- Izmanto \xab, \xbb pogas, lai izvçlçtos gadu\n" + +"- Izmanto " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + "pogas, lai izvçlçtos mçnesi\n" + +"- Turi nospiestu peles pogu uz jebkuru no augstâk minçtajâm pogâm, lai paâtrinâtu izvçli."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Laika izvçle:\n" + +"- Uzklikðíini uz jebkuru no laika daïâm, lai palielinâtu to\n" + +"- vai Shift-klikðíis, lai samazinâtu to\n" + +"- vai noklikðíini un velc uz attiecîgo virzienu lai mainîtu âtrâk."; + +Calendar._TT["PREV_YEAR"] = "Iepr. gads (turi izvçlnei)"; +Calendar._TT["PREV_MONTH"] = "Iepr. mçnesis (turi izvçlnei)"; +Calendar._TT["GO_TODAY"] = "Ðodien"; +Calendar._TT["NEXT_MONTH"] = "Nâkoðais mçnesis (turi izvçlnei)"; +Calendar._TT["NEXT_YEAR"] = "Nâkoðais gads (turi izvçlnei)"; +Calendar._TT["SEL_DATE"] = "Izvçlies datumu"; +Calendar._TT["DRAG_TO_MOVE"] = "Velc, lai pârvietotu"; +Calendar._TT["PART_TODAY"] = " (ðodien)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Attçlot %s kâ pirmo"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "1,7"; + +Calendar._TT["CLOSE"] = "Aizvçrt"; +Calendar._TT["TODAY"] = "Ðodien"; +Calendar._TT["TIME_PART"] = "(Shift-)Klikðíis vai pârvieto, lai mainîtu"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %e %b"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Laiks:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-nl.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-nl.js new file mode 100644 index 00000000..9915f6a7 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-nl.js @@ -0,0 +1,73 @@ +// ** I18N +Calendar._DN = new Array +("Zondag", + "Maandag", + "Dinsdag", + "Woensdag", + "Donderdag", + "Vrijdag", + "Zaterdag", + "Zondag"); + +Calendar._SDN_len = 2; + +Calendar._MN = new Array +("Januari", + "Februari", + "Maart", + "April", + "Mei", + "Juni", + "Juli", + "Augustus", + "September", + "Oktober", + "November", + "December"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Info"; + +Calendar._TT["ABOUT"] = +"DHTML Datum/Tijd Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + +"Ga voor de meest recente versie naar: http://www.dynarch.com/projects/calendar/\n" + +"Verspreid onder de GNU LGPL. Zie http://gnu.org/licenses/lgpl.html voor details." + +"\n\n" + +"Datum selectie:\n" + +"- Gebruik de \xab \xbb knoppen om een jaar te selecteren\n" + +"- Gebruik de " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " knoppen om een maand te selecteren\n" + +"- Houd de muis ingedrukt op de genoemde knoppen voor een snellere selectie."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Tijd selectie:\n" + +"- Klik op een willekeurig onderdeel van het tijd gedeelte om het te verhogen\n" + +"- of Shift-klik om het te verlagen\n" + +"- of klik en sleep voor een snellere selectie."; + +//Calendar._TT["TOGGLE"] = "Selecteer de eerste week-dag"; +Calendar._TT["PREV_YEAR"] = "Vorig jaar (ingedrukt voor menu)"; +Calendar._TT["PREV_MONTH"] = "Vorige maand (ingedrukt voor menu)"; +Calendar._TT["GO_TODAY"] = "Ga naar Vandaag"; +Calendar._TT["NEXT_MONTH"] = "Volgende maand (ingedrukt voor menu)"; +Calendar._TT["NEXT_YEAR"] = "Volgend jaar (ingedrukt voor menu)"; +Calendar._TT["SEL_DATE"] = "Selecteer datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Klik en sleep om te verplaatsen"; +Calendar._TT["PART_TODAY"] = " (vandaag)"; +//Calendar._TT["MON_FIRST"] = "Toon Maandag eerst"; +//Calendar._TT["SUN_FIRST"] = "Toon Zondag eerst"; + +Calendar._TT["DAY_FIRST"] = "Toon %s eerst"; + +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Sluiten"; +Calendar._TT["TODAY"] = "(vandaag)"; +Calendar._TT["TIME_PART"] = "(Shift-)Klik of sleep om de waarde te veranderen"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %e %b %Y"; + +Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Tijd:"; \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-no.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-no.js new file mode 100644 index 00000000..0bb97d73 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-no.js @@ -0,0 +1,114 @@ +// ** I18N + +// Calendar NO language +// Author: Daniel Holmen, +// Encoding: UTF-8 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Søndag", + "Mandag", + "Tirsdag", + "Onsdag", + "Torsdag", + "Fredag", + "Lørdag", + "Søndag"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Søn", + "Man", + "Tir", + "Ons", + "Tor", + "Fre", + "Lør", + "Søn"); + +// full month names +Calendar._MN = new Array +("Januar", + "Februar", + "Mars", + "April", + "Mai", + "Juni", + "Juli", + "August", + "September", + "Oktober", + "November", + "Desember"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "Mai", + "Jun", + "Jul", + "Aug", + "Sep", + "Okt", + "Nov", + "Des"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Om kalenderen"; + +Calendar._TT["ABOUT"] = +"DHTML Dato-/Tidsvelger\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For nyeste versjon, gÃ¥ til: http://www.dynarch.com/projects/calendar/\n" + +"Distribuert under GNU LGPL. Se http://gnu.org/licenses/lgpl.html for detaljer." + +"\n\n" + +"Datovalg:\n" + +"- Bruk knappene \xab og \xbb for Ã¥ velge Ã¥r\n" + +"- Bruk knappene " + String.fromCharCode(0x2039) + " og " + String.fromCharCode(0x203a) + " for Ã¥ velge mÃ¥ned\n" + +"- Hold inne musknappen eller knappene over for raskere valg."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Tidsvalg:\n" + +"- Klikk pÃ¥ en av tidsdelene for Ã¥ øke den\n" + +"- eller Shift-klikk for Ã¥ senke verdien\n" + +"- eller klikk-og-dra for raskere valg.."; + +Calendar._TT["PREV_YEAR"] = "Forrige. Ã¥r (hold for meny)"; +Calendar._TT["PREV_MONTH"] = "Forrige. mÃ¥ned (hold for meny)"; +Calendar._TT["GO_TODAY"] = "GÃ¥ til idag"; +Calendar._TT["NEXT_MONTH"] = "Neste mÃ¥ned (hold for meny)"; +Calendar._TT["NEXT_YEAR"] = "Neste Ã¥r (hold for meny)"; +Calendar._TT["SEL_DATE"] = "Velg dato"; +Calendar._TT["DRAG_TO_MOVE"] = "Dra for Ã¥ flytte"; +Calendar._TT["PART_TODAY"] = " (idag)"; +Calendar._TT["MON_FIRST"] = "Vis mandag først"; +Calendar._TT["SUN_FIRST"] = "Vis søndag først"; +Calendar._TT["CLOSE"] = "Lukk"; +Calendar._TT["TODAY"] = "Idag"; +Calendar._TT["TIME_PART"] = "(Shift-)Klikk eller dra for Ã¥ endre verdi"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d.%m.%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "uke"; \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-pl-utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-pl-utf8.js new file mode 100644 index 00000000..b438a175 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-pl-utf8.js @@ -0,0 +1,93 @@ +// ** I18N + +// Calendar PL language +// Author: Dariusz Pietrzak, +// Author: Janusz Piwowarski, +// Encoding: utf-8 +// Distributed under the same terms as the calendar itself. + +Calendar._DN = new Array +("Niedziela", + "PoniedziaÅ‚ek", + "Wtorek", + "Åšroda", + "Czwartek", + "PiÄ…tek", + "Sobota", + "Niedziela"); +Calendar._SDN = new Array +("Nie", + "Pn", + "Wt", + "Åšr", + "Cz", + "Pt", + "So", + "Nie"); +Calendar._MN = new Array +("StyczeÅ„", + "Luty", + "Marzec", + "KwiecieÅ„", + "Maj", + "Czerwiec", + "Lipiec", + "SierpieÅ„", + "WrzesieÅ„", + "Październik", + "Listopad", + "GrudzieÅ„"); +Calendar._SMN = new Array +("Sty", + "Lut", + "Mar", + "Kwi", + "Maj", + "Cze", + "Lip", + "Sie", + "Wrz", + "Paź", + "Lis", + "Gru"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O kalendarzu"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Aby pobrać najnowszÄ… wersjÄ™, odwiedź: http://www.dynarch.com/projects/calendar/\n" + +"DostÄ™pny na licencji GNU LGPL. Zobacz szczegóły na http://gnu.org/licenses/lgpl.html." + +"\n\n" + +"Wybór daty:\n" + +"- Użyj przycisków \xab, \xbb by wybrać rok\n" + +"- Użyj przycisków " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " by wybrać miesiÄ…c\n" + +"- Przytrzymaj klawisz myszy nad jednym z powyższych przycisków dla szybszego wyboru."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Wybór czasu:\n" + +"- Kliknij na jednym z pól czasu by zwiÄ™kszyć jego wartość\n" + +"- lub kliknij trzymajÄ…c Shift by zmiejszyć jego wartość\n" + +"- lub kliknij i przeciÄ…gnij dla szybszego wyboru."; + +//Calendar._TT["TOGGLE"] = "ZmieÅ„ pierwszy dzieÅ„ tygodnia"; +Calendar._TT["PREV_YEAR"] = "Poprzedni rok (przytrzymaj dla menu)"; +Calendar._TT["PREV_MONTH"] = "Poprzedni miesiÄ…c (przytrzymaj dla menu)"; +Calendar._TT["GO_TODAY"] = "Idź do dzisiaj"; +Calendar._TT["NEXT_MONTH"] = "NastÄ™pny miesiÄ…c (przytrzymaj dla menu)"; +Calendar._TT["NEXT_YEAR"] = "NastÄ™pny rok (przytrzymaj dla menu)"; +Calendar._TT["SEL_DATE"] = "Wybierz datÄ™"; +Calendar._TT["DRAG_TO_MOVE"] = "PrzeciÄ…gnij by przesunąć"; +Calendar._TT["PART_TODAY"] = " (dzisiaj)"; +Calendar._TT["MON_FIRST"] = "WyÅ›wietl poniedziaÅ‚ek jako pierwszy"; +Calendar._TT["SUN_FIRST"] = "WyÅ›wietl niedzielÄ™ jako pierwszÄ…"; +Calendar._TT["CLOSE"] = "Zamknij"; +Calendar._TT["TODAY"] = "Dzisiaj"; +Calendar._TT["TIME_PART"] = "(Shift-)Kliknij lub przeciÄ…gnij by zmienić wartość"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%e %B, %A"; + +Calendar._TT["WK"] = "ty"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-pl.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-pl.js new file mode 100644 index 00000000..86c7b39e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-pl.js @@ -0,0 +1,56 @@ +// ** I18N +// Calendar PL language +// Author: Artur Filipiak, +// January, 2004 +// Encoding: UTF-8 +Calendar._DN = new Array +("Niedziela", "PoniedziaÅ‚ek", "Wtorek", "Åšroda", "Czwartek", "PiÄ…tek", "Sobota", "Niedziela"); + +Calendar._SDN = new Array +("N", "Pn", "Wt", "Åšr", "Cz", "Pt", "So", "N"); + +Calendar._MN = new Array +("StyczeÅ„", "Luty", "Marzec", "KwiecieÅ„", "Maj", "Czerwiec", "Lipiec", "SierpieÅ„", "WrzesieÅ„", "Październik", "Listopad", "GrudzieÅ„"); + +Calendar._SMN = new Array +("Sty", "Lut", "Mar", "Kwi", "Maj", "Cze", "Lip", "Sie", "Wrz", "Paź", "Lis", "Gru"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O kalendarzu"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Wybór daty:\n" + +"- aby wybrać rok użyj przycisków \xab, \xbb\n" + +"- aby wybrać miesiÄ…c użyj przycisków " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + "\n" + +"- aby przyspieszyć wybór przytrzymaj wciÅ›niÄ™ty przycisk myszy nad ww. przyciskami."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Wybór czasu:\n" + +"- aby zwiÄ™kszyć wartość kliknij na dowolnym elemencie selekcji czasu\n" + +"- aby zmniejszyć wartość użyj dodatkowo klawisza Shift\n" + +"- możesz również poruszać myszkÄ™ w lewo i prawo wraz z wciÅ›niÄ™tym lewym klawiszem."; + +Calendar._TT["PREV_YEAR"] = "Poprz. rok (przytrzymaj dla menu)"; +Calendar._TT["PREV_MONTH"] = "Poprz. miesiÄ…c (przytrzymaj dla menu)"; +Calendar._TT["GO_TODAY"] = "Pokaż dziÅ›"; +Calendar._TT["NEXT_MONTH"] = "Nast. miesiÄ…c (przytrzymaj dla menu)"; +Calendar._TT["NEXT_YEAR"] = "Nast. rok (przytrzymaj dla menu)"; +Calendar._TT["SEL_DATE"] = "Wybierz datÄ™"; +Calendar._TT["DRAG_TO_MOVE"] = "PrzesuÅ„ okienko"; +Calendar._TT["PART_TODAY"] = " (dziÅ›)"; +Calendar._TT["MON_FIRST"] = "Pokaż PoniedziaÅ‚ek jako pierwszy"; +Calendar._TT["SUN_FIRST"] = "Pokaż NiedzielÄ™ jako pierwszÄ…"; +Calendar._TT["CLOSE"] = "Zamknij"; +Calendar._TT["TODAY"] = "DziÅ›"; +Calendar._TT["TIME_PART"] = "(Shift-)klik | drag, aby zmienić wartość"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y.%m.%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "wk"; \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-pt.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-pt.js new file mode 100644 index 00000000..f7dfbfff --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-pt.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar pt_BR language +// Author: Adalberto Machado, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Domingo", + "Segunda", + "Terca", + "Quarta", + "Quinta", + "Sexta", + "Sabado", + "Domingo"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("Dom", + "Seg", + "Ter", + "Qua", + "Qui", + "Sex", + "Sab", + "Dom"); + +// full month names +Calendar._MN = new Array +("Janeiro", + "Fevereiro", + "Marco", + "Abril", + "Maio", + "Junho", + "Julho", + "Agosto", + "Setembro", + "Outubro", + "Novembro", + "Dezembro"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Fev", + "Mar", + "Abr", + "Mai", + "Jun", + "Jul", + "Ago", + "Set", + "Out", + "Nov", + "Dez"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Sobre o calendario"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Ultima versao visite: http://www.dynarch.com/projects/calendar/\n" + +"Distribuido sobre GNU LGPL. Veja http://gnu.org/licenses/lgpl.html para detalhes." + +"\n\n" + +"Selecao de data:\n" + +"- Use os botoes \xab, \xbb para selecionar o ano\n" + +"- Use os botoes " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " para selecionar o mes\n" + +"- Segure o botao do mouse em qualquer um desses botoes para selecao rapida."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selecao de hora:\n" + +"- Clique em qualquer parte da hora para incrementar\n" + +"- ou Shift-click para decrementar\n" + +"- ou clique e segure para selecao rapida."; + +Calendar._TT["PREV_YEAR"] = "Ant. ano (segure para menu)"; +Calendar._TT["PREV_MONTH"] = "Ant. mes (segure para menu)"; +Calendar._TT["GO_TODAY"] = "Hoje"; +Calendar._TT["NEXT_MONTH"] = "Prox. mes (segure para menu)"; +Calendar._TT["NEXT_YEAR"] = "Prox. ano (segure para menu)"; +Calendar._TT["SEL_DATE"] = "Selecione a data"; +Calendar._TT["DRAG_TO_MOVE"] = "Arraste para mover"; +Calendar._TT["PART_TODAY"] = " (hoje)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Mostre %s primeiro"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Fechar"; +Calendar._TT["TODAY"] = "Hoje"; +Calendar._TT["TIME_PART"] = "(Shift-)Click ou arraste para mudar valor"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %e %b"; + +Calendar._TT["WK"] = "sm"; +Calendar._TT["TIME"] = "Hora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ro.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ro.js new file mode 100644 index 00000000..6c45eab2 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ro.js @@ -0,0 +1,66 @@ +// ** I18N +Calendar._DN = new Array +("Duminică", + "Luni", + "MarÅ£i", + "Miercuri", + "Joi", + "Vineri", + "Sâmbătă", + "Duminică"); +Calendar._SDN_len = 2; +Calendar._MN = new Array +("Ianuarie", + "Februarie", + "Martie", + "Aprilie", + "Mai", + "Iunie", + "Iulie", + "August", + "Septembrie", + "Octombrie", + "Noiembrie", + "Decembrie"); + +// tooltips +Calendar._TT = {}; + +Calendar._TT["INFO"] = "Despre calendar"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Pentru ultima versiune vizitaÅ£i: http://www.dynarch.com/projects/calendar/\n" + +"Distribuit sub GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"SelecÅ£ia datei:\n" + +"- FolosiÅ£i butoanele \xab, \xbb pentru a selecta anul\n" + +"- FolosiÅ£i butoanele " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " pentru a selecta luna\n" + +"- TineÅ£i butonul mouse-ului apăsat pentru selecÅ£ie mai rapidă."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"SelecÅ£ia orei:\n" + +"- Click pe ora sau minut pentru a mări valoarea cu 1\n" + +"- Sau Shift-Click pentru a micÅŸora valoarea cu 1\n" + +"- Sau Click ÅŸi drag pentru a selecta mai repede."; + +Calendar._TT["PREV_YEAR"] = "Anul precedent (lung pt menu)"; +Calendar._TT["PREV_MONTH"] = "Luna precedentă (lung pt menu)"; +Calendar._TT["GO_TODAY"] = "Data de azi"; +Calendar._TT["NEXT_MONTH"] = "Luna următoare (lung pt menu)"; +Calendar._TT["NEXT_YEAR"] = "Anul următor (lung pt menu)"; +Calendar._TT["SEL_DATE"] = "Selectează data"; +Calendar._TT["DRAG_TO_MOVE"] = "Trage pentru a miÅŸca"; +Calendar._TT["PART_TODAY"] = " (astăzi)"; +Calendar._TT["DAY_FIRST"] = "AfiÅŸează %s prima zi"; +Calendar._TT["WEEKEND"] = "0,6"; +Calendar._TT["CLOSE"] = "ÃŽnchide"; +Calendar._TT["TODAY"] = "Astăzi"; +Calendar._TT["TIME_PART"] = "(Shift-)Click sau drag pentru a selecta"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d-%m-%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %d %B"; + +Calendar._TT["WK"] = "spt"; +Calendar._TT["TIME"] = "Ora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ru.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ru.js new file mode 100644 index 00000000..4e23e386 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ru.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar RU language +// Translation: Sly Golovanov, http://golovanov.net, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("воÑкреÑенье", + "понедельник", + "вторник", + "Ñреда", + "четверг", + "пÑтница", + "Ñуббота", + "воÑкреÑенье"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("вÑк", + "пон", + "втр", + "Ñрд", + "чет", + "пÑÑ‚", + "Ñуб", + "вÑк"); + +// full month names +Calendar._MN = new Array +("Ñнварь", + "февраль", + "март", + "апрель", + "май", + "июнь", + "июль", + "авгуÑÑ‚", + "ÑентÑбрь", + "октÑбрь", + "ноÑбрь", + "декабрь"); + +// short month names +Calendar._SMN = new Array +("Ñнв", + "фев", + "мар", + "апр", + "май", + "июн", + "июл", + "авг", + "Ñен", + "окт", + "ноÑ", + "дек"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "О календаре..."; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Как выбрать дату:\n" + +"- При помощи кнопок \xab, \xbb можно выбрать год\n" + +"- При помощи кнопок " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " можно выбрать меÑÑц\n" + +"- Подержите Ñти кнопки нажатыми, чтобы поÑвилоÑÑŒ меню быÑтрого выбора."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Как выбрать времÑ:\n" + +"- При клике на чаÑах или минутах они увеличиваютÑÑ\n" + +"- при клике Ñ Ð½Ð°Ð¶Ð°Ñ‚Ð¾Ð¹ клавишей Shift они уменьшаютÑÑ\n" + +"- еÑли нажать и двигать мышкой влево/вправо, они будут менÑтьÑÑ Ð±Ñ‹Ñтрее."; + +Calendar._TT["PREV_YEAR"] = "Ðа год назад (удерживать Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ)"; +Calendar._TT["PREV_MONTH"] = "Ðа меÑÑц назад (удерживать Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ)"; +Calendar._TT["GO_TODAY"] = "СегоднÑ"; +Calendar._TT["NEXT_MONTH"] = "Ðа меÑÑц вперед (удерживать Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ)"; +Calendar._TT["NEXT_YEAR"] = "Ðа год вперед (удерживать Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ)"; +Calendar._TT["SEL_DATE"] = "Выберите дату"; +Calendar._TT["DRAG_TO_MOVE"] = "ПеретаÑкивайте мышкой"; +Calendar._TT["PART_TODAY"] = " (ÑегоднÑ)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Первый день недели будет %s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Закрыть"; +Calendar._TT["TODAY"] = "СегоднÑ"; +Calendar._TT["TIME_PART"] = "(Shift-)клик или нажать и двигать"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%e %b, %a"; + +Calendar._TT["WK"] = "нед"; +Calendar._TT["TIME"] = "ВремÑ:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ru_win_.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ru_win_.js new file mode 100644 index 00000000..4d8501d7 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-ru_win_.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar RU language +// Translation: Sly Golovanov, http://golovanov.net, +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("âîñêðåñåíüå", + "ïîíåäåëüíèê", + "âòîðíèê", + "ñðåäà", + "÷åòâåðã", + "ïÿòíèöà", + "ñóááîòà", + "âîñêðåñåíüå"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("âñê", + "ïîí", + "âòð", + "ñðä", + "÷åò", + "ïÿò", + "ñóá", + "âñê"); + +// full month names +Calendar._MN = new Array +("ÿíâàðü", + "ôåâðàëü", + "ìàðò", + "àïðåëü", + "ìàé", + "èþíü", + "èþëü", + "àâãóñò", + "ñåíòÿáðü", + "îêòÿáðü", + "íîÿáðü", + "äåêàáðü"); + +// short month names +Calendar._SMN = new Array +("ÿíâ", + "ôåâ", + "ìàð", + "àïð", + "ìàé", + "èþí", + "èþë", + "àâã", + "ñåí", + "îêò", + "íîÿ", + "äåê"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Î êàëåíäàðå..."; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Êàê âûáðàòü äàòó:\n" + +"- Ïðè ïîìîùè êíîïîê \xab, \xbb ìîæíî âûáðàòü ãîä\n" + +"- Ïðè ïîìîùè êíîïîê " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " ìîæíî âûáðàòü ìåñÿö\n" + +"- Ïîäåðæèòå ýòè êíîïêè íàæàòûìè, ÷òîáû ïîÿâèëîñü ìåíþ áûñòðîãî âûáîðà."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Êàê âûáðàòü âðåìÿ:\n" + +"- Ïðè êëèêå íà ÷àñàõ èëè ìèíóòàõ îíè óâåëè÷èâàþòñÿ\n" + +"- ïðè êëèêå ñ íàæàòîé êëàâèøåé Shift îíè óìåíüøàþòñÿ\n" + +"- åñëè íàæàòü è äâèãàòü ìûøêîé âëåâî/âïðàâî, îíè áóäóò ìåíÿòüñÿ áûñòðåå."; + +Calendar._TT["PREV_YEAR"] = "Íà ãîä íàçàä (óäåðæèâàòü äëÿ ìåíþ)"; +Calendar._TT["PREV_MONTH"] = "Íà ìåñÿö íàçàä (óäåðæèâàòü äëÿ ìåíþ)"; +Calendar._TT["GO_TODAY"] = "Ñåãîäíÿ"; +Calendar._TT["NEXT_MONTH"] = "Íà ìåñÿö âïåðåä (óäåðæèâàòü äëÿ ìåíþ)"; +Calendar._TT["NEXT_YEAR"] = "Íà ãîä âïåðåä (óäåðæèâàòü äëÿ ìåíþ)"; +Calendar._TT["SEL_DATE"] = "Âûáåðèòå äàòó"; +Calendar._TT["DRAG_TO_MOVE"] = "Ïåðåòàñêèâàéòå ìûøêîé"; +Calendar._TT["PART_TODAY"] = " (ñåãîäíÿ)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Ïåðâûé äåíü íåäåëè áóäåò %s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Çàêðûòü"; +Calendar._TT["TODAY"] = "Ñåãîäíÿ"; +Calendar._TT["TIME_PART"] = "(Shift-)êëèê èëè íàæàòü è äâèãàòü"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%e %b, %a"; + +Calendar._TT["WK"] = "íåä"; +Calendar._TT["TIME"] = "Âðåìÿ:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-si.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-si.js new file mode 100644 index 00000000..cb3dfb9f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-si.js @@ -0,0 +1,94 @@ +/* Slovenian language file for the DHTML Calendar version 0.9.2 +* Author David Milost , January 2004. +* Feel free to use this script under the terms of the GNU Lesser General +* Public License, as long as you do not remove or alter this notice. +*/ + // full day names +Calendar._DN = new Array +("Nedelja", + "Ponedeljek", + "Torek", + "Sreda", + "ÄŒetrtek", + "Petek", + "Sobota", + "Nedelja"); + // short day names + Calendar._SDN = new Array +("Ned", + "Pon", + "Tor", + "Sre", + "ÄŒet", + "Pet", + "Sob", + "Ned"); +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "Maj", + "Jun", + "Jul", + "Avg", + "Sep", + "Okt", + "Nov", + "Dec"); + // full month names +Calendar._MN = new Array +("Januar", + "Februar", + "Marec", + "April", + "Maj", + "Junij", + "Julij", + "Avgust", + "September", + "Oktober", + "November", + "December"); + +// tooltips +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O koledarju"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Za zadnjo verzijo pojdine na naslov: http://www.dynarch.com/projects/calendar/\n" + +"Distribuirano pod GNU LGPL. Poglejte http://gnu.org/licenses/lgpl.html za podrobnosti." + +"\n\n" + +"Izbor datuma:\n" + +"- Uporabite \xab, \xbb gumbe za izbor leta\n" + +"- Uporabite " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " gumbe za izbor meseca\n" + +"- Zadržite klik na kateremkoli od zgornjih gumbov za hiter izbor."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Izbor ćasa:\n" + +"- Kliknite na katerikoli del ćasa za poveć. le-tega\n" + +"- ali Shift-click za zmanj. le-tega\n" + +"- ali kliknite in povlecite za hiter izbor."; + +Calendar._TT["TOGGLE"] = "Spremeni dan s katerim se prićne teden"; +Calendar._TT["PREV_YEAR"] = "Predhodnje leto (dolg klik za meni)"; +Calendar._TT["PREV_MONTH"] = "Predhodnji mesec (dolg klik za meni)"; +Calendar._TT["GO_TODAY"] = "Pojdi na tekoći dan"; +Calendar._TT["NEXT_MONTH"] = "Naslednji mesec (dolg klik za meni)"; +Calendar._TT["NEXT_YEAR"] = "Naslednje leto (dolg klik za meni)"; +Calendar._TT["SEL_DATE"] = "Izberite datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Pritisni in povleci za spremembo pozicije"; +Calendar._TT["PART_TODAY"] = " (danes)"; +Calendar._TT["MON_FIRST"] = "Prikaži ponedeljek kot prvi dan"; +Calendar._TT["SUN_FIRST"] = "Prikaži nedeljo kot prvi dan"; +Calendar._TT["CLOSE"] = "Zapri"; +Calendar._TT["TODAY"] = "Danes"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "Ted"; \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sk.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sk.js new file mode 100644 index 00000000..2ecfc3c4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sk.js @@ -0,0 +1,99 @@ +// ** I18N + +// Calendar SK language +// Author: Peter Valach (pvalach@gmx.net) +// Encoding: utf-8 +// Last update: 2003/10/29 +// Distributed under the same terms as the calendar itself. + +// full day names +Calendar._DN = new Array +("NedeÄľa", + "Pondelok", + "Utorok", + "Streda", + "Ĺ tvrtok", + "Piatok", + "Sobota", + "NedeÄľa"); + +// short day names +Calendar._SDN = new Array +("Ned", + "Pon", + "Uto", + "Str", + "Ĺ tv", + "Pia", + "Sob", + "Ned"); + +// full month names +Calendar._MN = new Array +("Január", + "Február", + "Marec", + "AprĂ­l", + "Máj", + "JĂşn", + "JĂşl", + "August", + "September", + "OktĂłber", + "November", + "December"); + +// short month names +Calendar._SMN = new Array +("Jan", + "Feb", + "Mar", + "Apr", + "Máj", + "JĂşn", + "JĂşl", + "Aug", + "Sep", + "Okt", + "Nov", + "Dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O kalendári"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + +"PoslednĂş verziu nájdete na: http://www.dynarch.com/projects/calendar/\n" + +"DistribuovanĂ© pod GNU LGPL. ViÄŹ http://gnu.org/licenses/lgpl.html pre detaily." + +"\n\n" + +"VÄ‚Ëber dátumu:\n" + +"- PouĹľite tlaÄŤidlá \xab, \xbb pre vÄ‚Ëber roku\n" + +"- PouĹľite tlaÄŤidlá " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " pre vÄ‚Ëber mesiaca\n" + +"- Ak ktorĂ©koÄľvek z tÄ‚Ëchto tlaÄŤidiel podržíte dlhšie, zobrazĂ­ sa rÄ‚Ëchly vÄ‚Ëber."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"VÄ‚Ëber ÄŤasu:\n" + +"- Kliknutie na niektorĂş poloĹľku ÄŤasu ju zvĂ˚i\n" + +"- Shift-klik ju znĂ­Ĺľi\n" + +"- Ak podržíte tlaÄŤĂ­tko stlaÄŤenĂ©, posĂşvanĂ­m menĂ­te hodnotu."; + +Calendar._TT["PREV_YEAR"] = "PredošlÄ‚Ë rok (podrĹľte pre menu)"; +Calendar._TT["PREV_MONTH"] = "PredošlÄ‚Ë mesiac (podrĹľte pre menu)"; +Calendar._TT["GO_TODAY"] = "PrejsĹĄ na dnešok"; +Calendar._TT["NEXT_MONTH"] = "Nasl. mesiac (podrĹľte pre menu)"; +Calendar._TT["NEXT_YEAR"] = "Nasl. rok (podrĹľte pre menu)"; +Calendar._TT["SEL_DATE"] = "ZvoÄľte dátum"; +Calendar._TT["DRAG_TO_MOVE"] = "PodrĹľanĂ­m tlaÄŤĂ­tka zmenĂ­te polohu"; +Calendar._TT["PART_TODAY"] = " (dnes)"; +Calendar._TT["MON_FIRST"] = "ZobraziĹĄ pondelok ako prvÄ‚Ë"; +Calendar._TT["SUN_FIRST"] = "ZobraziĹĄ nedeÄľu ako prvĂş"; +Calendar._TT["CLOSE"] = "ZavrieĹĄ"; +Calendar._TT["TODAY"] = "Dnes"; +Calendar._TT["TIME_PART"] = "(Shift-)klik/ĹĄahanie zmenĂ­ hodnotu"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "$d. %m. %Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %e. %b"; + +Calendar._TT["WK"] = "tÄ‚ËĹľ"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sp.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sp.js new file mode 100644 index 00000000..bbacd216 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sp.js @@ -0,0 +1,110 @@ +// ** I18N + +// Calendar SP language +// Author: Rafael Velasco +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("Domingo", + "Lunes", + "Martes", + "Miercoles", + "Jueves", + "Viernes", + "Sabado", + "Domingo"); + +Calendar._SDN = new Array +("Dom", + "Lun", + "Mar", + "Mie", + "Jue", + "Vie", + "Sab", + "Dom"); + +// full month names +Calendar._MN = new Array +("Enero", + "Febrero", + "Marzo", + "Abril", + "Mayo", + "Junio", + "Julio", + "Agosto", + "Septiembre", + "Octubre", + "Noviembre", + "Diciembre"); + +// short month names +Calendar._SMN = new Array +("Ene", + "Feb", + "Mar", + "Abr", + "May", + "Jun", + "Jul", + "Ago", + "Sep", + "Oct", + "Nov", + "Dic"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Información del Calendario"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"Nuevas versiones en: http://www.dynarch.com/projects/calendar/\n" + +"Distribuida bajo licencia GNU LGPL. Para detalles vea http://gnu.org/licenses/lgpl.html ." + +"\n\n" + +"Selección de Fechas:\n" + +"- Use \xab, \xbb para seleccionar el año\n" + +"- Use " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " para seleccionar el mes\n" + +"- Mantenga presionado el botón del ratón en cualquiera de las opciones superiores para un acceso rapido ."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Selección del Reloj:\n" + +"- Seleccione la hora para cambiar el reloj\n" + +"- o presione Shift-click para disminuirlo\n" + +"- o presione click y arrastre del ratón para una selección rapida."; + +Calendar._TT["PREV_YEAR"] = "Año anterior (Presione para menu)"; +Calendar._TT["PREV_MONTH"] = "Mes Anterior (Presione para menu)"; +Calendar._TT["GO_TODAY"] = "Ir a Hoy"; +Calendar._TT["NEXT_MONTH"] = "Mes Siguiente (Presione para menu)"; +Calendar._TT["NEXT_YEAR"] = "Año Siguiente (Presione para menu)"; +Calendar._TT["SEL_DATE"] = "Seleccione fecha"; +Calendar._TT["DRAG_TO_MOVE"] = "Arrastre y mueva"; +Calendar._TT["PART_TODAY"] = " (Hoy)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Mostrar %s primero"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Cerrar"; +Calendar._TT["TODAY"] = "Hoy"; +Calendar._TT["TIME_PART"] = "(Shift-)Click o arrastra para cambar el valor"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%dd-%mm-%yy"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %e de %B de %Y"; + +Calendar._TT["WK"] = "Sm"; +Calendar._TT["TIME"] = "Hora:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sr-SP-Cyrl.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sr-SP-Cyrl.js new file mode 100644 index 00000000..9a66d6c8 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sr-SP-Cyrl.js @@ -0,0 +1,104 @@ +// ** I18N + +// Calendar - Serbian language (Cyrillic) +// Author: Aleksandar Seovic (aleks@seovic.com) +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("недеља", + "понедељак", + "уторак", + "Ñреда", + "четвртак", + "петак", + "Ñубота", + "недеља"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 1; + +// full month names +Calendar._MN = new Array +("јануар", + "фебруар", + "март", + "април", + "мај", + "јун", + "јул", + "авгуÑÑ‚", + "Ñептембар", + "октобар", + "новембар", + "децембар"); + +// short month names +Calendar._SMN = new Array +("јан", + "феб", + "мар", + "апр", + "мaj", + "јун", + "јул", + "авг", + "Ñеп", + "окт", + "нов", + "дец"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O календару"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Избор датума:\n" + +"- Уз помоћ таÑтера \xab, \xbb можете изабрати годину\n" + +"- Уз помоћ таÑтера " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " можете изабрати меÑец\n" + +"- Држите лево дугме миша притиÑнуто за бржу Ñелекцију."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Избор времена:\n" + +"- Кликните на Ñат или минут да га увећате\n" + +"- или Shift-click да га умањите\n" + +"- или притиÑните лево дугме миша и повуците за бржу Ñелекцију."; + +Calendar._TT["PREV_YEAR"] = "Претходна година"; +Calendar._TT["PREV_MONTH"] = "Претходни меÑец"; +Calendar._TT["GO_TODAY"] = "ДанаÑ"; +Calendar._TT["NEXT_MONTH"] = "Следећи меÑец"; +Calendar._TT["NEXT_YEAR"] = "Следећа година"; +Calendar._TT["SEL_DATE"] = "Изабери датум"; +Calendar._TT["DRAG_TO_MOVE"] = "Повуци за промену"; +Calendar._TT["PART_TODAY"] = " (данаÑ)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Прво прикажи %s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Затвори"; +Calendar._TT["TODAY"] = "ДанаÑ"; +Calendar._TT["TIME_PART"] = "(Shift-)Click или повуци да промениш вредноÑÑ‚"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d.%m.%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "нед"; +Calendar._TT["TIME"] = "Време:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sr-SP-Latn.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sr-SP-Latn.js new file mode 100644 index 00000000..5ec66f1b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sr-SP-Latn.js @@ -0,0 +1,104 @@ +// ** I18N + +// Calendar - Serbian language (Latin) +// Author: Aleksandar Seovic (aleks@seovic.com) +// Encoding: any +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("nedelja", + "ponedeljak", + "utorak", + "sreda", + "Äetvrtak", + "petak", + "subota", + "nedelja"); + +// First day of the week. "0" means display Sunday first, "1" means display +// Monday first, etc. +Calendar._FD = 1; + +// full month names +Calendar._MN = new Array +("januar", + "februar", + "mart", + "april", + "maj", + "jun", + "jul", + "avgust", + "septembar", + "oktobar", + "novembar", + "decembar"); + +// short month names +Calendar._SMN = new Array +("jan", + "feb", + "mar", + "apr", + "maj", + "jun", + "jul", + "avg", + "sep", + "okt", + "nov", + "dec"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "O kalendaru"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Izbor datuma:\n" + +"- Uz pomoć tastera \xab, \xbb možete izabrati godinu\n" + +"- Uz pomoć tastera " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " možete izabrati mesec\n" + +"- Držite levo dugme miÅ¡a pritisnuto za bržu selekciju."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Izbor vremena:\n" + +"- Kliknite na sat ili minut da ga uvećate\n" + +"- ili Shift-click da ga umanjite\n" + +"- ili pritisnite levo dugme miÅ¡a i povucite za bržu selekciju."; + +Calendar._TT["PREV_YEAR"] = "Prethodna godina"; +Calendar._TT["PREV_MONTH"] = "Prethodni mesec"; +Calendar._TT["GO_TODAY"] = "Danas"; +Calendar._TT["NEXT_MONTH"] = "Sledeći mesec"; +Calendar._TT["NEXT_YEAR"] = "Sledeća godina"; +Calendar._TT["SEL_DATE"] = "Izaberi datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Povuci za promenu"; +Calendar._TT["PART_TODAY"] = " (danas)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Prvo prikaži %s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "Zatvori"; +Calendar._TT["TODAY"] = "Danas"; +Calendar._TT["TIME_PART"] = "(Shift-)Click ili povuci da promeniÅ¡ vrednost"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%d.%m.%Y"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; + +Calendar._TT["WK"] = "ned"; +Calendar._TT["TIME"] = "Vreme:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sv.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sv.js new file mode 100644 index 00000000..b62df3e1 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-sv.js @@ -0,0 +1,93 @@ +// ** I18N + +// Calendar SV language (Swedish, svenska) +// Author: Mihai Bazon, +// Translation team: +// Translator: Leonard Norrgård +// Last translator: Leonard Norrgård +// Encoding: iso-latin-1 +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("söndag", + "måndag", + "tisdag", + "onsdag", + "torsdag", + "fredag", + "lördag", + "söndag"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. +Calendar._SDN_len = 2; +Calendar._SMN_len = 3; + +// full month names +Calendar._MN = new Array +("januari", + "februari", + "mars", + "april", + "maj", + "juni", + "juli", + "augusti", + "september", + "oktober", + "november", + "december"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "Om kalendern"; + +Calendar._TT["ABOUT"] = +"DHTML Datum/tid-väljare\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"För senaste version gå till: http://www.dynarch.com/projects/calendar/\n" + +"Distribueras under GNU LGPL. Se http://gnu.org/licenses/lgpl.html för detaljer." + +"\n\n" + +"Val av datum:\n" + +"- Använd knapparna \xab, \xbb för att välja år\n" + +"- Använd knapparna " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " för att välja månad\n" + +"- Håll musknappen nedtryckt på någon av ovanstående knappar för snabbare val."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Val av tid:\n" + +"- Klicka på en del av tiden för att öka den delen\n" + +"- eller skift-klicka för att minska den\n" + +"- eller klicka och drag för snabbare val."; + +Calendar._TT["PREV_YEAR"] = "Föregående år (håll för menu)"; +Calendar._TT["PREV_MONTH"] = "Föregående månad (håll för menu)"; +Calendar._TT["GO_TODAY"] = "Gå till dagens datum"; +Calendar._TT["NEXT_MONTH"] = "Följande månad (håll för menu)"; +Calendar._TT["NEXT_YEAR"] = "Följande år (håll för menu)"; +Calendar._TT["SEL_DATE"] = "Välj datum"; +Calendar._TT["DRAG_TO_MOVE"] = "Drag för att flytta"; +Calendar._TT["PART_TODAY"] = " (idag)"; +Calendar._TT["MON_FIRST"] = "Visa måndag först"; +Calendar._TT["SUN_FIRST"] = "Visa söndag först"; +Calendar._TT["CLOSE"] = "Stäng"; +Calendar._TT["TODAY"] = "Idag"; +Calendar._TT["TIME_PART"] = "(Skift-)klicka eller drag för att ändra tid"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%A %d %b %Y"; + +Calendar._TT["WK"] = "vecka"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-tr.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-tr.js new file mode 100644 index 00000000..2164687f --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-tr.js @@ -0,0 +1,58 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// Turkish Translation by Nuri AKMAN +// Location: Ankara/TURKEY +// e-mail : nuriakman@hotmail.com +// Date : April, 9 2003 +// +// Note: if Turkish Characters does not shown on you screen +// please include falowing line your html code: +// +// +// +////////////////////////////////////////////////////////////////////////////////////////////// + +// ** I18N +Calendar._DN = new Array +("Pazar", + "Pazartesi", + "Salý", + "Çarþamba", + "Perþembe", + "Cuma", + "Cumartesi", + "Pazar"); +Calendar._MN = new Array +("Ocak", + "Þubat", + "Mart", + "Nisan", + "Mayýs", + "Haziran", + "Temmuz", + "Aðustos", + "Eylül", + "Ekim", + "Kasým", + "Aralýk"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["TOGGLE"] = "Haftanýn ilk gününü kaydýr"; +Calendar._TT["PREV_YEAR"] = "Önceki Yýl (Menü için basýlý tutunuz)"; +Calendar._TT["PREV_MONTH"] = "Önceki Ay (Menü için basýlý tutunuz)"; +Calendar._TT["GO_TODAY"] = "Bugün'e git"; +Calendar._TT["NEXT_MONTH"] = "Sonraki Ay (Menü için basýlý tutunuz)"; +Calendar._TT["NEXT_YEAR"] = "Sonraki Yýl (Menü için basýlý tutunuz)"; +Calendar._TT["SEL_DATE"] = "Tarih seçiniz"; +Calendar._TT["DRAG_TO_MOVE"] = "Taþýmak için sürükleyiniz"; +Calendar._TT["PART_TODAY"] = " (bugün)"; +Calendar._TT["MON_FIRST"] = "Takvim Pazartesi gününden baþlasýn"; +Calendar._TT["SUN_FIRST"] = "Takvim Pazar gününden baþlasýn"; +Calendar._TT["CLOSE"] = "Kapat"; +Calendar._TT["TODAY"] = "Bugün"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "dd-mm-y"; +Calendar._TT["TT_DATE_FORMAT"] = "d MM y, DD"; + +Calendar._TT["WK"] = "Hafta"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-zh.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-zh.js new file mode 100644 index 00000000..42f0b107 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/calendar-zh.js @@ -0,0 +1,119 @@ +// ** I18N + +// Calendar ZH language +// Author: muziq, +// Encoding: GB2312 or GBK +// Distributed under the same terms as the calendar itself. + +// full day names +Calendar._DN = new Array +("ÐÇÆÚÈÕ", + "ÐÇÆÚÒ»", + "ÐÇÆÚ¶þ", + "ÐÇÆÚÈý", + "ÐÇÆÚËÄ", + "ÐÇÆÚÎå", + "ÐÇÆÚÁù", + "ÐÇÆÚÈÕ"); + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("ÈÕ", + "Ò»", + "¶þ", + "Èý", + "ËÄ", + "Îå", + "Áù", + "ÈÕ"); + +// full month names +Calendar._MN = new Array +("Ò»ÔÂ", + "¶þÔÂ", + "ÈýÔÂ", + "ËÄÔÂ", + "ÎåÔÂ", + "ÁùÔÂ", + "ÆßÔÂ", + "°ËÔÂ", + "¾ÅÔÂ", + "Ê®ÔÂ", + "ʮһÔÂ", + "Ê®¶þÔÂ"); + +// short month names +Calendar._SMN = new Array +("Ò»ÔÂ", + "¶þÔÂ", + "ÈýÔÂ", + "ËÄÔÂ", + "ÎåÔÂ", + "ÁùÔÂ", + "ÆßÔÂ", + "°ËÔÂ", + "¾ÅÔÂ", + "Ê®ÔÂ", + "ʮһÔÂ", + "Ê®¶þÔÂ"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "°ïÖú"; + +Calendar._TT["ABOUT"] = +"DHTML Date/Time Selector\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: http://www.dynarch.com/projects/calendar/\n" + +"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." + +"\n\n" + +"Ñ¡ÔñÈÕÆÚ:\n" + +"- µã»÷ \xab, \xbb °´Å¥Ñ¡ÔñÄê·Ý\n" + +"- µã»÷ " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " °´Å¥Ñ¡ÔñÔ·Ý\n" + +"- ³¤°´ÒÔÉϰ´Å¥¿É´Ó²Ëµ¥ÖпìËÙÑ¡ÔñÄê·Ý»òÔ·Ý"; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"Ñ¡Ôñʱ¼ä:\n" + +"- µã»÷Сʱ»ò·ÖÖÓ¿Éʹ¸ÄÊýÖµ¼ÓÒ»\n" + +"- °´×¡Shift¼üµã»÷Сʱ»ò·ÖÖÓ¿Éʹ¸ÄÊýÖµ¼õÒ»\n" + +"- µã»÷Í϶¯Êó±ê¿É½øÐпìËÙÑ¡Ôñ"; + +Calendar._TT["PREV_YEAR"] = "ÉÏÒ»Äê (°´×¡³ö²Ëµ¥)"; +Calendar._TT["PREV_MONTH"] = "ÉÏÒ»Ô (°´×¡³ö²Ëµ¥)"; +Calendar._TT["GO_TODAY"] = "תµ½½ñÈÕ"; +Calendar._TT["NEXT_MONTH"] = "ÏÂÒ»Ô (°´×¡³ö²Ëµ¥)"; +Calendar._TT["NEXT_YEAR"] = "ÏÂÒ»Äê (°´×¡³ö²Ëµ¥)"; +Calendar._TT["SEL_DATE"] = "Ñ¡ÔñÈÕÆÚ"; +Calendar._TT["DRAG_TO_MOVE"] = "Í϶¯"; +Calendar._TT["PART_TODAY"] = " (½ñÈÕ)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "×î×ó±ßÏÔʾ%s"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "¹Ø±Õ"; +Calendar._TT["TODAY"] = "½ñÈÕ"; +Calendar._TT["TIME_PART"] = "(Shift-)µã»÷Êó±ê»òÍ϶¯¸Ä±äÖµ"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%A, %b %eÈÕ"; + +Calendar._TT["WK"] = "ÖÜ"; +Calendar._TT["TIME"] = "ʱ¼ä:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/cn_utf8.js b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/cn_utf8.js new file mode 100644 index 00000000..a0ef7c6b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Scripts/Calendar/lang/cn_utf8.js @@ -0,0 +1,123 @@ +// ** I18N + +// Calendar EN language +// Author: Mihai Bazon, +// Encoding: any +// Translator : Niko +// Distributed under the same terms as the calendar itself. + +// For translators: please use UTF-8 if possible. We strongly believe that +// Unicode is the answer to a real internationalized world. Also please +// include your contact information in the header, as can be seen above. + +// full day names +Calendar._DN = new Array +("\u5468\u65e5",//\u5468\u65e5 + "\u5468\u4e00",//\u5468\u4e00 + "\u5468\u4e8c",//\u5468\u4e8c + "\u5468\u4e09",//\u5468\u4e09 + "\u5468\u56db",//\u5468\u56db + "\u5468\u4e94",//\u5468\u4e94 + "\u5468\u516d",//\u5468\u516d + "\u5468\u65e5");//\u5468\u65e5 + +// Please note that the following array of short day names (and the same goes +// for short month names, _SMN) isn't absolutely necessary. We give it here +// for exemplification on how one can customize the short day names, but if +// they are simply the first N letters of the full name you can simply say: +// +// Calendar._SDN_len = N; // short day name length +// Calendar._SMN_len = N; // short month name length +// +// If N = 3 then this is not needed either since we assume a value of 3 if not +// present, to be compatible with translation files that were written before +// this feature. + +// short day names +Calendar._SDN = new Array +("\u5468\u65e5", + "\u5468\u4e00", + "\u5468\u4e8c", + "\u5468\u4e09", + "\u5468\u56db", + "\u5468\u4e94", + "\u5468\u516d", + "\u5468\u65e5"); + +// full month names +Calendar._MN = new Array +("\u4e00\u6708", + "\u4e8c\u6708", + "\u4e09\u6708", + "\u56db\u6708", + "\u4e94\u6708", + "\u516d\u6708", + "\u4e03\u6708", + "\u516b\u6708", + "\u4e5d\u6708", + "\u5341\u6708", + "\u5341\u4e00\u6708", + "\u5341\u4e8c\u6708"); + +// short month names +Calendar._SMN = new Array +("\u4e00\u6708", + "\u4e8c\u6708", + "\u4e09\u6708", + "\u56db\u6708", + "\u4e94\u6708", + "\u516d\u6708", + "\u4e03\u6708", + "\u516b\u6708", + "\u4e5d\u6708", + "\u5341\u6708", + "\u5341\u4e00\u6708", + "\u5341\u4e8c\u6708"); + +// tooltips +Calendar._TT = {}; +Calendar._TT["INFO"] = "\u5173\u4e8e"; + +Calendar._TT["ABOUT"] = +" DHTML \u65e5\u8d77/\u65f6\u95f4\u9009\u62e9\u63a7\u4ef6\n" + +"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) +"For latest version visit: \u6700\u65b0\u7248\u672c\u8bf7\u767b\u9646http://www.dynarch.com/projects/calendar/\u5bdf\u770b\n" + +"\u9075\u5faaGNU LGPL. \u7ec6\u8282\u53c2\u9605 http://gnu.org/licenses/lgpl.html" + +"\n\n" + +"\u65e5\u671f\u9009\u62e9:\n" + +"- \u70b9\u51fb\xab(\xbb)\u6309\u94ae\u9009\u62e9\u4e0a(\u4e0b)\u4e00\u5e74\u5ea6.\n" + +"- \u70b9\u51fb" + String.fromCharCode(0x2039) + "(" + String.fromCharCode(0x203a) + ")\u6309\u94ae\u9009\u62e9\u4e0a(\u4e0b)\u4e2a\u6708\u4efd.\n" + +"- \u957f\u65f6\u95f4\u6309\u7740\u6309\u94ae\u5c06\u51fa\u73b0\u66f4\u591a\u9009\u62e9\u9879."; +Calendar._TT["ABOUT_TIME"] = "\n\n" + +"\u65f6\u95f4\u9009\u62e9:\n" + +"-\u5728\u65f6\u95f4\u90e8\u5206(\u5206\u6216\u8005\u79d2)\u4e0a\u5355\u51fb\u9f20\u6807\u5de6\u952e\u6765\u589e\u52a0\u5f53\u524d\u65f6\u95f4\u90e8\u5206(\u5206\u6216\u8005\u79d2)\n" + +"-\u5728\u65f6\u95f4\u90e8\u5206(\u5206\u6216\u8005\u79d2)\u4e0a\u6309\u4f4fShift\u952e\u540e\u5355\u51fb\u9f20\u6807\u5de6\u952e\u6765\u51cf\u5c11\u5f53\u524d\u65f6\u95f4\u90e8\u5206(\u5206\u6216\u8005\u79d2)."; + +Calendar._TT["PREV_YEAR"] = "\u4e0a\u4e00\u5e74"; +Calendar._TT["PREV_MONTH"] = "\u4e0a\u4e2a\u6708"; +Calendar._TT["GO_TODAY"] = "\u5230\u4eca\u5929"; +Calendar._TT["NEXT_MONTH"] = "\u4e0b\u4e2a\u6708"; +Calendar._TT["NEXT_YEAR"] = "\u4e0b\u4e00\u5e74"; +Calendar._TT["SEL_DATE"] = "\u9009\u62e9\u65e5\u671f"; +Calendar._TT["DRAG_TO_MOVE"] = "\u62d6\u52a8"; +Calendar._TT["PART_TODAY"] = " (\u4eca\u5929)"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "%s\u4e3a\u8fd9\u5468\u7684\u7b2c\u4e00\u5929"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + +Calendar._TT["CLOSE"] = "\u5173\u95ed"; +Calendar._TT["TODAY"] = "\u4eca\u5929"; +Calendar._TT["TIME_PART"] = "(\u6309\u7740Shift\u952e)\u5355\u51fb\u6216\u62d6\u52a8\u6539\u53d8\u503c"; + +// date formats +Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; +Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e\u65e5"; + +Calendar._TT["WK"] = "\u5468"; +Calendar._TT["TIME"] = "\u65f6\u95f4:"; diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/SpringAir.Web.build b/examples/Spring/SpringAir/src/SpringAir.Web.2005/SpringAir.Web.build new file mode 100644 index 00000000..9456679b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/SpringAir.Web.build @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web.config b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web.config new file mode 100644 index 00000000..1833cd9a --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web.config @@ -0,0 +1,74 @@ + + + + +
    + + +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/App_LocalResources/Home.aspx.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/App_LocalResources/Home.aspx.resx new file mode 100644 index 00000000..4d6e768a --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/App_LocalResources/Home.aspx.resx @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/App_LocalResources/StandardTemplate.master.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/App_LocalResources/StandardTemplate.master.resx new file mode 100644 index 00000000..b1fe6ff3 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/App_LocalResources/StandardTemplate.master.resx @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + True + + + Private + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.en.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.en.resx new file mode 100644 index 00000000..ed0ad7ac --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.en.resx @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Thank You! + + + Your reservation confirmation number is {0}. + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.resx new file mode 100644 index 00000000..ed0ad7ac --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.resx @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Thank You! + + + Your reservation confirmation number is {0}. + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-Cyrl-CS.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-Cyrl-CS.resx new file mode 100644 index 00000000..7cf65849 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-Cyrl-CS.resx @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Хвала Вам! + + + Ваш број резервације је {0} + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-Latn-CS.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-Latn-CS.resx new file mode 100644 index 00000000..326baad8 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-Latn-CS.resx @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Hvala Vam! + + + VaÅ¡ broj rezervacije je {0} + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-SP-Cyrl.resx.exclude b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-SP-Cyrl.resx.exclude new file mode 100644 index 00000000..7cf65849 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-SP-Cyrl.resx.exclude @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Хвала Вам! + + + Ваш број резервације је {0} + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-SP-Latn.resx.exclude b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-SP-Latn.resx.exclude new file mode 100644 index 00000000..326baad8 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/ReservationConfirmationPage.aspx.sr-SP-Latn.resx.exclude @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Hvala Vam! + + + VaÅ¡ broj rezervacije je {0} + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.en.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.en.resx new file mode 100644 index 00000000..523f12f8 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.en.resx @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Please select flight + + + Please select flights + + + Book Flights + + + Please select an outbound flight. + + + Please select a return flight. + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.resx new file mode 100644 index 00000000..af2b0121 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + True + + + Please select flight + + + Please select flights + + + Book Flights + + + Please select an outbound flight. + + + Please select a return flight. + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-Cyrl-CS.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-Cyrl-CS.resx new file mode 100644 index 00000000..1782f06e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-Cyrl-CS.resx @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Изаберите лет + + + Изаберите летове + + + Резервиши лет + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-Latn-CS.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-Latn-CS.resx new file mode 100644 index 00000000..9a7194a4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-Latn-CS.resx @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Izaberite let + + + Izaberite letove + + + RezerviÅ¡i let + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-SP-Cyrl.resx.exclude b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-SP-Cyrl.resx.exclude new file mode 100644 index 00000000..1782f06e --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-SP-Cyrl.resx.exclude @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Изаберите лет + + + Изаберите летове + + + Резервиши лет + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-SP-Latn.resx.exclude b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-SP-Latn.resx.exclude new file mode 100644 index 00000000..9a7194a4 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/SuggestedFlights.aspx.sr-SP-Latn.resx.exclude @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + Izaberite let + + + Izaberite letove + + + RezerviÅ¡i let + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.en.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.en.resx new file mode 100644 index 00000000..ecd95a64 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.en.resx @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + + One Way + + + Round Trip + + + Find Flights + + + Leaving from: + + + Departing on: + + + Going to: + + + Returning on: + + + Please enter flight information + + + select airport + + + First name is a required field + + + Last name is a required field + + + Please select departure airport + + + Please select destination airport + + + Departure date is required + + + Return date is required + + + Departure date cannot be in the past + + + Return date cannot be before departure date + + + Destination airport cannot be the same as departure airport + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.resx new file mode 100644 index 00000000..78bdcce5 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.resx @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + True + + + + One Way + + + Round Trip + + + Find Flights + + + Leaving from: + + + Departing on: + + + Going to: + + + Returning on: + + + Please enter flight information + + + select airport + + + First name is a required field + + + Last name is a required field + + + Please select departure airport + + + Please select destination airport + + + Departure date is required + + + Return date is required + + + Departure date cannot be in the past + + + Return date cannot be before departure date + + + Destination airport cannot be the same as departure airport + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-Cyrl-CS.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-Cyrl-CS.resx new file mode 100644 index 00000000..5f77dd3a --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-Cyrl-CS.resx @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + + ЈедноÑмерни лет + + + Повратни лет + + + Пронађи лет + + + Полазак из: + + + Датум полаÑка: + + + Слетање на: + + + Датум повратка: + + + УнеÑите податке о лету + + + изаберите аеродром + + + Име је обавезно поље + + + Презиме је обавезно поље + + + Молимо Ð²Ð°Ñ Ð¸Ð·Ð°Ð±ÐµÑ€Ð¸Ñ‚Ðµ полазни аеродром + + + Молимо Ð²Ð°Ñ Ð¸Ð·Ð°Ð±ÐµÑ€Ð¸Ñ‚Ðµ одредишни аеродром + + + Датум полаÑка је обавезан + + + Датум повратка је обавезан + + + Датум полаÑка не може бити у прошлоÑти + + + Датум повратка не може бити пре датума полаÑка + + + Одредишни аеродром не може бити иÑти као полазни аеродром + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-Latn-CS.resx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-Latn-CS.resx new file mode 100644 index 00000000..25b9eb60 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-Latn-CS.resx @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + + Jednosmerni let + + + Povratni let + + + Pronadji let + + + Polazak iz: + + + Datum polaska: + + + Sletanje na: + + + Datum povratka: + + + Unesite podatke o letu + + + izaberite aerodrom + + + Ime je obavezno polje + + + Prezime je obavezno polje + + + Molimo vas izaberite polazni aerodrom + + + Molimo vas izaberite odrediÅ¡ni aerodrom + + + Datum polaska je obavezan + + + Datum povratka je obavezan + + + Datum polaska ne može biti u proÅ¡losti + + + Datum povratka ne može biti pre datuma polaska + + + OdrediÅ¡ni aerodrom ne može biti isti kao polazni aerodrom + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-SP-Cyrl.resx.exclude b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-SP-Cyrl.resx.exclude new file mode 100644 index 00000000..5f77dd3a --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-SP-Cyrl.resx.exclude @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + + ЈедноÑмерни лет + + + Повратни лет + + + Пронађи лет + + + Полазак из: + + + Датум полаÑка: + + + Слетање на: + + + Датум повратка: + + + УнеÑите податке о лету + + + изаберите аеродром + + + Име је обавезно поље + + + Презиме је обавезно поље + + + Молимо Ð²Ð°Ñ Ð¸Ð·Ð°Ð±ÐµÑ€Ð¸Ñ‚Ðµ полазни аеродром + + + Молимо Ð²Ð°Ñ Ð¸Ð·Ð°Ð±ÐµÑ€Ð¸Ñ‚Ðµ одредишни аеродром + + + Датум полаÑка је обавезан + + + Датум повратка је обавезан + + + Датум полаÑка не може бити у прошлоÑти + + + Датум повратка не може бити пре датума полаÑка + + + Одредишни аеродром не може бити иÑти као полазни аеродром + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-SP-Latn.resx.exclude b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-SP-Latn.resx.exclude new file mode 100644 index 00000000..25b9eb60 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/App_LocalResources/TripForm.aspx.sr-SP-Latn.resx.exclude @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + Private + + + + Jednosmerni let + + + Povratni let + + + Pronadji let + + + Polazak iz: + + + Datum polaska: + + + Sletanje na: + + + Datum povratka: + + + Unesite podatke o letu + + + izaberite aerodrom + + + Ime je obavezno polje + + + Prezime je obavezno polje + + + Molimo vas izaberite polazni aerodrom + + + Molimo vas izaberite odrediÅ¡ni aerodrom + + + Datum polaska je obavezan + + + Datum povratka je obavezan + + + Datum polaska ne može biti u proÅ¡losti + + + Datum povratka ne može biti pre datuma polaska + + + OdrediÅ¡ni aerodrom ne može biti isti kao polazni aerodrom + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/BookTrip.aspx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/BookTrip.aspx new file mode 100644 index 00000000..5044b2ec --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/BookTrip.aspx @@ -0,0 +1,4 @@ +<%-- +// this is only a dummy placeholder +// requesting this page will start the "booktrip"-Process +--%> \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/ReservationConfirmationPage.aspx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/ReservationConfirmationPage.aspx new file mode 100644 index 00000000..7374dbfb --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/ReservationConfirmationPage.aspx @@ -0,0 +1,53 @@ +<%@ Page language="c#" Inherits="ReservationConfirmationPage" CodeFile="ReservationConfirmationPage.aspx.cs" %> +<%@ Import Namespace="SpringAir.Domain" %> + + +

    +

    + +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + <%= GetMessage("itinerary") %> +
    + <%= GetMessage("flightNumber") %> + + <%= GetMessage("departureDate") %> + + <%= GetMessage("departureAirport") %> + + <%= GetMessage("destinationAirport") %> + + <%= GetMessage("aircraft") %> +
    <%# ((Flight) Container.DataItem).FlightNumber %><%# ((Flight) Container.DataItem).DepartureTime.ToString() %><%# ((Flight) Container.DataItem).DepartureAirport.Description %><%# ((Flight) Container.DataItem).DestinationAirport.Description %><%# ((Flight) Container.DataItem).Aircraft.Model %>
    + +
    +
    diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/ReservationConfirmationPage.aspx.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/ReservationConfirmationPage.aspx.cs new file mode 100644 index 00000000..bcbc1246 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/ReservationConfirmationPage.aspx.cs @@ -0,0 +1,87 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web.UI.WebControls; + +using Spring.Web.UI; +using Spring.Web.UI.Controls; +using SpringAir.Domain; + +#endregion + +/// +/// The SpringAir reservation confirmation page. +/// +/// Aleksandar Seovic +/// $Id: ReservationConfirmationPage.aspx.cs,v 1.2 2006/12/07 04:22:00 aseovic Exp $ +public partial class ReservationConfirmationPage : Page +{ + #region Fields + + protected ReservationConfirmation confirmation; + + #endregion + + #region Model Management and Data Binding Methods + + protected override void InitializeModel() + { + confirmation = (ReservationConfirmation)Session[Constants.ReservationConfirmationKey]; + } + + protected override void LoadModel(object savedModel) + { + confirmation = (ReservationConfirmation)savedModel; + } + + protected override object SaveModel() + { + return confirmation; + } + + #endregion + + #region Page Lifecycle Methods + + protected override void OnInitializeControls(EventArgs e) + { + base.OnInitializeControls(e); + + if (!IsPostBack) + { + itinerary.DataSource = confirmation.Reservation.Itinerary.Flights; + itinerary.DataBind(); + + Session.Remove(Constants.ReservationConfirmationKey); + Session.Remove(Constants.SuggestedFlightsKey); + } + } + + protected override void OnPreRender(EventArgs e) + { + base.OnPreRender(e); + confirmationLabel.Text = GetMessage("confirmation", confirmation.ConfirmationNumber); + } + + #endregion +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/SuggestedFlights.aspx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/SuggestedFlights.aspx new file mode 100644 index 00000000..4e057dbc --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/SuggestedFlights.aspx @@ -0,0 +1,156 @@ +<%@ Page Language="c#" Inherits="SuggestedFlights" CodeFile="SuggestedFlights.aspx.cs" %> +<%@ Import Namespace="SpringAir.Domain" %> + + +

    +

    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + <%= GetMessage("outboundFlights") %> +
    +   + + <%= GetMessage("flightNumber") %> + + <%= GetMessage("departureDate") %> + + <%= GetMessage("departureAirport") %> + + <%= GetMessage("destinationAirport") %> + + <%= GetMessage("aircraft") %> + + <%= GetMessage("seatPlan") %> +
    + + <%# ((Flight) Container.DataItem).FlightNumber %> + + <%# ((Flight) Container.DataItem).DepartureTime.ToString() %> + + <%# ((Flight) Container.DataItem).DepartureAirport.Description %> + + <%# ((Flight) Container.DataItem).DestinationAirport.Description %> + + <%# ((Flight) Container.DataItem).Aircraft.Model %> + + <%# ((Flight) Container.DataItem).SeatPlan %> +
    + +
    +
    +  
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + <%= GetMessage("returnFlights") %> +
    +   + <%= GetMessage("flightNumber") %> + + <%= GetMessage("departureDate") %> + + <%= GetMessage("departureAirport") %> + + <%= GetMessage("destinationAirport") %> + + <%= GetMessage("aircraft") %> + + <%= GetMessage("seatPlan") %> +
    + + <%# ((Flight) Container.DataItem).FlightNumber %> + + <%# ((Flight) Container.DataItem).DepartureTime.ToString() %> + + <%# ((Flight) Container.DataItem).DepartureAirport.Description %> + + <%# ((Flight) Container.DataItem).DestinationAirport.Description %> + + <%# ((Flight) Container.DataItem).Aircraft.Model %> + + <%# ((Flight) Container.DataItem).SeatPlan %> +
    + +
    +
    +
    +
    +
    diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/SuggestedFlights.aspx.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/SuggestedFlights.aspx.cs new file mode 100644 index 00000000..5185d5a5 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/SuggestedFlights.aspx.cs @@ -0,0 +1,173 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Web.UI.HtmlControls; +using System.Web.UI.WebControls; + +using Spring.Validation; +using Spring.Web.UI; +using SpringAir.Domain; +using SpringAir.Service; + +#endregion + +/// +/// Displays a list of suggested flights. +/// +/// Rick Evans +/// Aleksandar Seovic +/// Marko Dumic +/// $Id: SuggestedFlights.aspx.cs,v 1.2 2006/12/07 04:22:00 aseovic Exp $ +public partial class SuggestedFlights : Page +{ + #region Fields + + private const string ReservationConfirmed = "reservationConfirmed"; + private const int NoFlightSelected = -1; + + protected FlightSuggestions flights; + private IBookingAgent bookingAgent; + + protected int outboundFlightIndex = NoFlightSelected; + protected int returnFlightIndex = NoFlightSelected; + + private IValidator flightsValidator; + + #endregion + + #region Properties + + public IBookingAgent BookingAgent + { + set { this.bookingAgent = value; } + } + + public IValidator FlightsValidator + { + set { flightsValidator = value; } + } + + #endregion + + #region Model Management and Data Binding Methods + + protected override void InitializeModel() + { + flights = (FlightSuggestions)Session[Constants.SuggestedFlightsKey]; + outboundFlightIndex = NoFlightSelected; + returnFlightIndex = NoFlightSelected; + } + + protected override void LoadModel(object savedModel) + { + IDictionary model = (IDictionary)savedModel; + flights = (FlightSuggestions) model["flights"]; + outboundFlightIndex = (int) model["outboundFlightIndex"]; + returnFlightIndex = (int) model["returnFlightIndex"]; + } + + protected override object SaveModel() + { + IDictionary model = new Hashtable(); + model.Add("flights", flights); + model.Add("outboundFlightIndex", outboundFlightIndex); + model.Add("returnFlightIndex", returnFlightIndex); + return model; + } + + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("outboundFlight.Value", "outboundFlightIndex"); + BindingManager.AddBinding("returnFlight.Value", "returnFlightIndex"); + } + + #endregion + + #region Page Lifecycle Methods + + protected override void OnInitializeControls(EventArgs e) + { + base.OnInitializeControls(e); + + if (!IsPostBack) + { + outboundFlightList.DataSource = flights.OutboundFlights; + outboundFlightList.DataBind(); + if (flights.HasReturnFlights) + { + caption.Text = GetMessage("selectFlights"); + returnFlightList.DataSource = flights.ReturnFlights; + returnFlightList.DataBind(); + } + else + { + caption.Text = GetMessage("selectFlight"); + returnFlightList.Visible = false; + } + } + } + + protected override void OnPreRender(EventArgs e) + { + base.OnPreRender(e); + caption.Text = flights.HasReturnFlights + ? GetMessage("selectFlights") + : GetMessage("selectFlight"); + } + + #endregion + + #region Controller Methods + + protected void BookFlights(object sender, EventArgs e) + { + FlightCollection flightsToBook = GetFlightsToBook(); + Itinerary itinerary = new Itinerary(flightsToBook); + // TODO: forward to next logical page and get user details... + ReservationConfirmation confirmation = bookingAgent.Book( + new Reservation(new Passenger(1, "Aleksandar", "Seovic"), itinerary)); + Session[Constants.ReservationConfirmationKey] = confirmation; + SetResult(ReservationConfirmed); + } + + private FlightCollection GetFlightsToBook() + { + FlightCollection flightsToBook = new FlightCollection(); + Flight outboundFlight = this.flights.GetOutboundFlight(outboundFlightIndex); + flightsToBook.Add(outboundFlight); + if (HasReturnFlight) + { + Flight returnFlight = this.flights.GetReturnFlight(returnFlightIndex); + flightsToBook.Add(returnFlight); + } + return flightsToBook; + } + + private bool HasReturnFlight + { + get { return this.returnFlightIndex != NoFlightSelected; } + } + + #endregion +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/TripForm.aspx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/TripForm.aspx new file mode 100644 index 00000000..e4810071 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/TripForm.aspx @@ -0,0 +1,76 @@ +<%@ Page Language="c#" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %> + + + + + + + + +
    +

    + + + + + + + + + + + + + + + + + + + + + +
      + + + + +
    + + + + + + + +
    + + + + + +
    + + +
    +
    +
    +
    +
    + + + +
    diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/TripForm.aspx.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/TripForm.aspx.cs new file mode 100644 index 00000000..4ac691d0 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/TripForm.aspx.cs @@ -0,0 +1,214 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Web.UI.WebControls; + +using Spring.Validation; +using Spring.Web.UI; +using Spring.Web.UI.Controls; + +using SpringAir.Data; +using SpringAir.Domain; +using SpringAir.Service; + +using Calendar = Spring.Web.UI.Controls.Calendar; +using ValidationSummary = Spring.Web.UI.Controls.ValidationSummary; + +#endregion + +/// +/// Handles the 'type in your trip details and we'll see what we can find +/// for you' use case. +/// +/// Rick Evans +/// Aleksandar Seovic +/// Marko Dumic +/// $Id: TripForm.aspx.cs,v 1.2 2006/12/07 04:22:01 aseovic Exp $ +public partial class TripForm : Page +{ + #region Fields + + private const string DisplaySuggestedFlights = "displaySuggestedFlights"; + + private IBookingAgent bookingAgent; + private IAirportDao airportDao; + private Trip trip; + private IValidator tripValidator; + + #endregion + + #region Properties + + /// + /// The service interface that will handle the business logic of + /// booking trips and suggesting itineraries. + /// + /// + ///

    + /// Is injected into this page by the Spring IoC container. You might + /// want to look at the definition of this page in the ~/Config/Web.xml + /// file to confirm exactly which implementation of the + /// is being injected, + /// and to see how this is wired together. + ///

    + ///
    + public IBookingAgent BookingAgent + { + set { bookingAgent = value; } + } + + /// + /// This DAO object is used to retrieve a list of airports in order + /// to populate airport drop-down lists. + /// + /// + ///

    + /// Is injected into this page by the Spring IoC container. You might + /// want to look at the definition of this page in the ~/Config/Web.xml + /// file to confirm exactly which implementation of the + /// is being injected, + /// and to see how this is wired together. + ///

    + ///
    + public IAirportDao AirportDao + { + set { airportDao = value; } + } + + /// + /// The user's desired . + /// + /// + ///

    + /// The properties of this domain object are populated from the + /// values present in the controls on this page, according to the + /// data binding definitions. + ///

    + ///
    + public Trip Trip + { + get { return trip; } + set { trip = value; } + } + + /// + /// Sets the trip validator. + /// + /// + ///

    + /// Unsurprisingly, this + /// instance will be used to validate the associated + /// on a form submission. + ///

    + ///
    + /// The trip validator. + public IValidator TripValidator + { + set { tripValidator = value; } + } + + #endregion + + #region Model Management and Data Binding Methods + + protected override void InitializeModel() + { + trip = new Trip(); + trip.Mode = TripMode.RoundTrip; + trip.StartingFrom.Date = DateTime.Today; + trip.ReturningFrom.Date = DateTime.Today.AddDays(1); + } + + protected override void LoadModel(object savedModel) + { + trip = (Trip)savedModel; + } + + protected override object SaveModel() + { + return trip; + } + + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("tripMode.Value", "Trip.Mode"); + BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.AirportCode"); + BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.AirportCode"); + BindingManager.AddBinding("leavingFromDate.SelectedDate", "Trip.StartingFrom.Date"); + BindingManager.AddBinding("returningOnDate.SelectedDate", "Trip.ReturningFrom.Date"); + } + + #endregion + + #region Page Lifecycle Methods + + protected override void OnInitializeControls(EventArgs e) + { + if (!IsPostBack) + { + BindAirportDropdowns(); + } + } + + protected override void OnUserCultureChanged(EventArgs e) + { + BindAirportDropdowns(); + } + + private void BindAirportDropdowns() + { + ArrayList airportList = new ArrayList(); + airportList.Add(new Airport(0, string.Empty, string.Empty, "-- " + GetMessage("selectAirport") + " --")); + airportList.AddRange(airportDao.GetAllAirports()); + + leavingFromAirportCode.DataSource = airportList; + leavingFromAirportCode.DataTextField = "Description"; + leavingFromAirportCode.DataValueField = "Code"; + leavingFromAirportCode.DataBind(); + + goingToAirportCode.DataSource = airportList; + goingToAirportCode.DataTextField = "Description"; + goingToAirportCode.DataValueField = "Code"; + goingToAirportCode.DataBind(); + } + + #endregion + + #region Controller Methods + + protected void SearchForFlights(object sender, EventArgs e) + { + if (Validate(trip, tripValidator)) + { + FlightSuggestions suggestions = this.bookingAgent.SuggestFlights(Trip); + if (suggestions.HasOutboundFlights) + { + Session[Constants.SuggestedFlightsKey] = suggestions; + SetResult(DisplaySuggestedFlights); + } + } + } + + #endregion +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/Web.config b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/Web.config new file mode 100644 index 00000000..6bfd1343 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/BookTrip/Web.config @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/CSS/default.css b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/CSS/default.css new file mode 100644 index 00000000..efd0119b --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/CSS/default.css @@ -0,0 +1,348 @@ +body +{ + padding: 0; + margin: 0; + width: 720px; + text-align: center; + font-family: Verdana, Arial, Helvetica, sans-serif; +} + +body, table +{ + font-size: 12px; +} + +form +{ + margin: 0; +} + +td.formLabel +{ + font-weight: bold; + text-align: right; + vertical-align: baseline; + padding: 0 4px 0 16px; +} + +.validationError +{ + font-size: 80%; + color: #c50; +} + +input, select +{ + font-family: Tahoma, Arial, Helvetica, sans-serif; + font-size: 12px; +} + +/* main container box */ +#container +{ + width: 800px; + position: relative; + padding: 0 15px 15px; + margin: 5 auto; + text-align: left; +} + +div#logo +{ + width: 794px; + height: 119px; +} + +div#navigation +{ + width: 720px; + height: 20px; + background: #E2F3B8; + text-align: right; +} + +#container div#content +{ + position: relative; + width: 800px; + height: 100%; + overflow: hidden; + background-color: #ecf1f3; + padding: 0; + margin: 0; +} + +.panel11, .panel12, .panel13, .panel21, .panel23, .panel31, .panel32, .panel33 +{ + position: absolute; + overflow: hidden; + padding: 0; + margin: 0; +} + +.panel11 +{ + top: 0; + left: 0; + width: 16px; + height: 16px; + z-index: 2; +} +.panel12 +{ + top: 0; + left: 0; + width: 100%; + height: 16px; + z-index: 1; +} +.panel13 +{ + top: 0; + right: 0; + width: 16px; + height: 16px; + z-index: 2; +} + +.panel21 +{ + top: 0; + left: 0; + width: 16px; + height: 100%; + z-index: 1; +} +.panel23 +{ + top: 0; + right: 0; + width: 16px; + height: 100%; + z-index: 1; +} + +.panel31 +{ + bottom: -1px; + left: 0; + width: 16px; + height: 16px; + z-index: 2; +} +.panel32 +{ + bottom: -1px; + left: 0; + width: 100%; + height: 16px; + z-index: 1; +} +.panel33 +{ + bottom: -1px; + right: 0; + width: 16px; + height: 16px; + z-index: 2; +} + +.panelContent +{ + position: relative; + overflow: hidden; + padding: 18px; + margin: 0; + z-index: 3; +} + +div#insert +{ + width: 120; + float: right; + text-align: right; +} + +.buttonBar +{ + height: 1.5em; + text-align: right; +} + +#copyright +{ + width: 800px; + text-align: center; + font: bold 90% Tahoma, sans-serif; + color: div#336633; +} + +#copyright img.divider +{ + margin: 4px 0; +} + +#copyright a:link, #copyright a:visited, #copyright a:active +{ + text-decoration: none; + color: #c50; +} + +#copyright a:hover +{ + color: #f60; + text-decoration: underline; +} + +img.imageButton +{ + padding-left: 4px; + padding-right: 4px; +} + +.readOnly +{ + color: #c0c0c0; +} + +.error +{ + color: red; + font-weight: bold; + font-family: Arial, sans-serif; + font-size: 90%; +} + +/* Table */ +.suggestedTable +{ + border: 1px solid #006; +} +.suggestedTableCaption th +{ + padding: 4px 2px; + margin: 4px; + line-height: 18px; + vertical-align: middle; + background-color: #006; + color: #fff; + font-size: 120%; +} +.suggestedTableColnames th +{ + text-align: left; + line-height: 16px; + padding: 4px 2px; + background-color: #159; + color: #fff; +} +.suggestedTableRow td +{ + text-align: left; + border-bottom: 1px solid #006; + padding: 4px 2px; +} + + +/* Text styles */ +content a +{ + text-decoration: none; + font-weight: bold; + color: #000; +} +content a:link +{ +} +content a:visited +{ +} +content a:active +{ +} +content a:hover +{ + text-decoration: underline; +} + +content h1 +{ + font-size: 2.0em; + font-weight: normal; + margin-top: 0em; + margin-bottom: 0em; +} + +content h2 +{ + font-size: 1.7em; + margin: 1.2em 0em 1.2em 0em; + font-weight: normal; +} + +content h3 +{ + font-size: 1.4em; + margin: 1.2em 0em 1.2em 0em; + font-weight: normal; +} + +content h4 +{ + font-size: 1.2em; + margin: 1.2em 0em 1.2em 0em; + font-weight: bold; +} + +content h5 +{ + font-size: 1.0em; + margin: 1.2em 0em 1.2em 0em; + font-weight: bold; +} + +content h6 +{ + font-size: 0.8em; + margin: 1.2em 0em 1.2em 0em; + font-weight: bold; +} + +content img +{ + border: 0; +} + +content ol, content ul, content li +{ + font-size: 1.0em; + line-height: 1.8em; + margin-top: 0.2em; + margin-bottom: 0.1em; +} + +content p +{ + font-size: 1.0em; + line-height: 1.8em; + margin: 1.2em 0em 1.2em 0em; +} + +content li > p +{ + margin-top: 0.2em; +} + +content pre +{ + font-family: monospace; + font-size: 1.0em; +} + +content strong, content b +{ + font-weight: bold; +} + + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Home.aspx b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Home.aspx new file mode 100644 index 00000000..03109cb9 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Home.aspx @@ -0,0 +1,22 @@ +<%@ Page language="c#" Inherits="HomePage" CodeFile="Home.aspx.cs" %> + + +

    + SpringAir models a flight reservation system. +
    + Using SpringAir, you can browse flights, book a trip and use Booking Agent Web Service. +

    +

    + SpringAir demonstrates the following features of Spring.NET's web support... +

    +
      +
    • Spring.NET IoC container configuration
    • +
    • Dependency Injection as applied to ASP.NET pages
    • +
    • Master Page support
    • +
    • Web Service support
    • +
    • Bi-directional databinding
    • +
    • Declarative validation of domain objects
    • +
    • Internationalization
    • +
    • Result mapping to better encapsulate page navigation flows
    • +
    +
    diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Home.aspx.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Home.aspx.cs new file mode 100644 index 00000000..6903c6aa --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Home.aspx.cs @@ -0,0 +1,61 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using Spring.Web.UI; + +#endregion + +/// +/// The SpringAir home page. +/// +/// Rick Evans +/// $Id: Home.aspx.cs,v 1.2 2006/11/15 20:30:52 aseovic Exp $ +public partial class HomePage : Page +{ + private static readonly ILog logger = LogManager.GetLogger(typeof(HomePage)); + + private void Page_Load(object sender, EventArgs e) + { + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format(this.CultureResolver.ResolveCulture(), + "The [{0}] page is being loaded...", GetType().Name)); + } + } + + #region Web Form Designer generated code + + protected override void OnInit(EventArgs e) + { + InitializeComponent(); + base.OnInit(e); + } + + private void InitializeComponent() + { + this.Load += new EventHandler(this.Page_Load); + } + + #endregion +} diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/Thumbs.db b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/Thumbs.db new file mode 100644 index 00000000..715019bc Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/Thumbs.db differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/calendar-1.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/calendar-1.gif new file mode 100644 index 00000000..6774ca5c Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/calendar-1.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/calendar-2.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/calendar-2.gif new file mode 100644 index 00000000..9d5d666d Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/calendar-2.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/en/Thumbs.db b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/en/Thumbs.db new file mode 100644 index 00000000..90ed854a Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/en/Thumbs.db differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/en/spring-air-logo.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/en/spring-air-logo.jpg new file mode 100644 index 00000000..21e6c575 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/en/spring-air-logo.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/error.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/error.gif new file mode 100644 index 00000000..2fccc977 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/error.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/line.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/line.jpg new file mode 100644 index 00000000..fedb4290 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/line.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-bottom-left.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-bottom-left.gif new file mode 100644 index 00000000..1323ec04 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-bottom-left.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-bottom-middle.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-bottom-middle.gif new file mode 100644 index 00000000..38aa28ea Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-bottom-middle.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-bottom-right.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-bottom-right.gif new file mode 100644 index 00000000..9815bced Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-bottom-right.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-left-middle.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-left-middle.gif new file mode 100644 index 00000000..76133d5e Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-left-middle.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-right-middle.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-right-middle.gif new file mode 100644 index 00000000..9b1140ef Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-right-middle.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-top-left.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-top-left.gif new file mode 100644 index 00000000..fade635c Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-top-left.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-top-middle.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-top-middle.gif new file mode 100644 index 00000000..f982015e Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-top-middle.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-top-right.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-top-right.gif new file mode 100644 index 00000000..bafcbecb Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/panel-top-right.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/spring-air-logo.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/spring-air-logo.jpg new file mode 100644 index 00000000..21e6c575 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/spring-air-logo.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Cyrl-CS/Thumbs.db b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Cyrl-CS/Thumbs.db new file mode 100644 index 00000000..6b490432 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Cyrl-CS/Thumbs.db differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Cyrl-CS/spring-air-logo.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Cyrl-CS/spring-air-logo.jpg new file mode 100644 index 00000000..bfeb3609 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Cyrl-CS/spring-air-logo.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Latn-CS/Thumbs.db b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Latn-CS/Thumbs.db new file mode 100644 index 00000000..5ac9d1c9 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Latn-CS/Thumbs.db differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Latn-CS/spring-air-logo.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Latn-CS/spring-air-logo.jpg new file mode 100644 index 00000000..36c8b822 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-Latn-CS/spring-air-logo.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Cyrl/Thumbs.db b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Cyrl/Thumbs.db new file mode 100644 index 00000000..0ce7072c Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Cyrl/Thumbs.db differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Cyrl/spring-air-logo.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Cyrl/spring-air-logo.jpg new file mode 100644 index 00000000..bfeb3609 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Cyrl/spring-air-logo.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Latn/Thumbs.db b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Latn/Thumbs.db new file mode 100644 index 00000000..b934bdcf Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Latn/Thumbs.db differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Latn/spring-air-logo.jpg b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Latn/spring-air-logo.jpg new file mode 100644 index 00000000..36c8b822 Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/sr-SP-Latn/spring-air-logo.jpg differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/validation-error.gif b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/validation-error.gif new file mode 100644 index 00000000..0bf0858b Binary files /dev/null and b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Images/validation-error.gif differ diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/StandardTemplate.master b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/StandardTemplate.master new file mode 100644 index 00000000..de611850 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/StandardTemplate.master @@ -0,0 +1,51 @@ +<%@ Master Language="C#" AutoEventWireup="false" CodeFile="StandardTemplate.master.cs" Inherits="StandardTemplate" %> + + + + + + + <spring:ContentPlaceHolder id="title" runat="server"> + <%= GetMessage("default.title") %> + </spring:ContentPlaceHolder> + + + + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    + + diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/StandardTemplate.master.cs b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/StandardTemplate.master.cs new file mode 100644 index 00000000..412e9b57 --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/StandardTemplate.master.cs @@ -0,0 +1,113 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Web.UI.WebControls; + +using Spring.Web.UI; + +#endregion + +/// +/// SpringAir2 master page. +/// +/// Aleksandar Seovic +/// $Id: StandardTemplate.master.cs,v 1.2 2007/05/28 19:07:12 markpollack Exp $ +public partial class StandardTemplate : MasterPage +{ + private static bool lookupInitialized = false; + private static bool needsTranslaction = false; + + private void SetLanguage(object sender, CommandEventArgs e) + { + string cultureString = e.CommandArgument as string; + if (cultureString != null) + { + UserCulture = new CultureInfo(TranslateCultureIfNecessary(cultureString)); + } + } + + /// + /// Translates the culture if necessary due to changes in Serbian culture for recent + /// .NET 2.0 releases. + /// + /// If 'sr-Latn-CS' is a valid culture, then translate the following; + /// sr-SP-Latn->sr-Latn-CS and sr-SP-Cyrl->sr-Cyrl-CS. See + /// http://blogs.msdn.com/kierans/archive/2006/08/02/687267.aspx + /// and http://blogs.msdn.com/shawnste/archive/2006/11/14/problems-compiling-resources-in-net-2-0-apps-after-updates.aspx + /// for additional information. + /// The culture string. + /// Translated culture string if necessary. + private string TranslateCultureIfNecessary(string cultureString) + { + if (!lookupInitialized) + { + lock(typeof(StandardTemplate)) + { + lookupInitialized = true; + //TODO maybe check .NET 2.0 build # instead. + foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures)) + { + if (ci.Name.Equals("sr-Latn-CS")) + { + needsTranslaction = true; + break; + } + } + } + } + + if (needsTranslaction) + { + if (cultureString.Equals("sr-SP-Latn")) + { + return "sr-Latn-CS"; + } + else if (cultureString.Equals("sr-SP-Cyrl")) + { + return "sr-Cyrl-CS"; + } + } + + return cultureString; + + + } + + #region Web Form Designer generated code + + protected override void OnInit(EventArgs e) + { + InitializeComponent(); + base.OnInit(e); + } + + private void InitializeComponent() + { + this.english.Command += new CommandEventHandler(this.SetLanguage); + this.serbianLatin.Command += new CommandEventHandler(this.SetLanguage); + this.serbianCyrillic.Command += new CommandEventHandler(this.SetLanguage); + } + + #endregion +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Web.config b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Web.config new file mode 100644 index 00000000..5152fe1a --- /dev/null +++ b/examples/Spring/SpringAir/src/SpringAir.Web.2005/Web/Web.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/AssemblyInfo.cs b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..0c7fb32a --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/AssemblyInfo.cs @@ -0,0 +1,27 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly : ComVisible(false)] +[assembly : AssemblyTitle("SpringAir core functionality tests")] +[assembly : AssemblyDescription("Tests for the core functionality for SpringAir")] \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/FlightCollectionTests.cs b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/FlightCollectionTests.cs new file mode 100644 index 00000000..7faebe12 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/FlightCollectionTests.cs @@ -0,0 +1,63 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// Unit tests for the FlightCollection class. + /// + /// Rick Evans + /// $Id: FlightCollectionTests.cs,v 1.3 2006/01/14 08:37:45 aseovic Exp $ + [TestFixture] + public sealed class FlightCollectionTests + { + + [Test] + public void TypeSafeAdd() + { + FlightCollection flights = new FlightCollection(); + flights.Add(new Flight()); + flights.Add(new Flight()); + Assert.AreEqual(2, flights.Count); + + } + + [Test] + public void TypeSafeIndexer() + { + FlightCollection flights = new FlightCollection(); + Flight firstFlight = new Flight(); + firstFlight.FlightNumber = "1"; + flights.Add(firstFlight); + flights.Add(new Flight()); + Flight f1 = flights[0]; + Assert.IsNotNull(f1); + Assert.AreEqual("1", f1.FlightNumber); + } + + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/FlightSuggestionsTests.cs b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/FlightSuggestionsTests.cs new file mode 100644 index 00000000..07229773 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/FlightSuggestionsTests.cs @@ -0,0 +1,58 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Xml.Serialization; +using NUnit.Framework; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// Unit tests for the FlightSuggestions class. + /// + /// Bruno Baia + /// $Id: FlightSuggestionsTests.cs,v 1.1 2006/01/08 00:38:38 bbaia Exp $ + [TestFixture] + public sealed class FlightSuggestionsTests + { + [Test] + public void XmlSerialisationTest() + { + FlightSuggestions suggestionsBefore = new FlightSuggestions(); + suggestionsBefore.OutboundFlights.Add(new Flight()); + suggestionsBefore.OutboundFlights.Add(new Flight()); + suggestionsBefore.ReturnFlights.Add(new Flight()); + + XmlSerializer s = new XmlSerializer(typeof(FlightSuggestions)); + StringWriter sw = new StringWriter(); + s.Serialize(sw, suggestionsBefore); + StringReader sr = new StringReader (sw.ToString()); + FlightSuggestions suggestionsAfter = (FlightSuggestions) s.Deserialize (sr); + + Assert.AreEqual(2, suggestionsAfter.OutboundFlights.Count); + Assert.AreEqual(1, suggestionsAfter.ReturnFlights.Count); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/FlightTests.cs b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/FlightTests.cs new file mode 100644 index 00000000..83fcf1e9 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/FlightTests.cs @@ -0,0 +1,57 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Xml.Serialization; +using NUnit.Framework; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// Unit tests for the Flight class. + /// + /// Bruno Baia + /// $Id: FlightTests.cs,v 1.1 2006/01/08 00:39:07 bbaia Exp $ + [TestFixture] + public sealed class FlightTests + { + [Test] + public void XmlSerialisationTest() + { + Flight flightBefore = new Flight(1, "UA 0123", new Airport(), new Airport(), new Aircraft(), DateTime.MinValue, new Cabin[1] {new Cabin(CabinClass.Business, 2, 8, "A")}); + + XmlSerializer s = new XmlSerializer(typeof(Flight)); + StringWriter sw = new StringWriter(); + s.Serialize(sw, flightBefore); + StringReader sr = new StringReader (sw.ToString()); + Flight flightAfter = (Flight) s.Deserialize (sr); + + Assert.AreEqual(flightAfter.Id, 1); + Assert.AreEqual(flightAfter.FlightNumber, "UA 0123"); + Assert.AreEqual(flightAfter.DepartureTime, DateTime.MinValue); + Assert.AreEqual(flightBefore.SeatPlan, flightAfter.SeatPlan); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/ReservationConfirmationTests.cs b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/ReservationConfirmationTests.cs new file mode 100644 index 00000000..7702b6b2 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/ReservationConfirmationTests.cs @@ -0,0 +1,67 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// Unit tests for the ReservationConfirmation class. + /// + /// Rick Evans + /// $Id: ReservationConfirmationTests.cs,v 1.7 2005/12/13 00:48:00 bbaia Exp $ + [TestFixture] + public sealed class ReservationConfirmationTests + { + private const string reservationConfirmationNumber = "JX726617H"; + + [Test] + public void EqualsSunnyDay() + { + ReservationConfirmation lhs = new ReservationConfirmation( + reservationConfirmationNumber, new Reservation(null, new Itinerary(1, 1000.0m, null))); + ReservationConfirmation rhs = new ReservationConfirmation( + reservationConfirmationNumber, new Reservation(null, new Itinerary(1, 1000.0m, null))); + Assert.IsTrue(lhs.Equals(rhs)); + } + + [Test] + public void EqualsNull() + { + ReservationConfirmation lhs = new ReservationConfirmation( + reservationConfirmationNumber, new Reservation(null, new Itinerary(1, 1000.0m, null))); + Assert.IsFalse(lhs.Equals(null)); + } + + [Test] + public void EqualsAnotherTypeOfObject() + { + ReservationConfirmation lhs = new ReservationConfirmation( + reservationConfirmationNumber, new Reservation(null, new Itinerary(1, 1000.0m, null))); + Assert.IsFalse(lhs.Equals(12)); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/TimeRangeTests.cs b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/TimeRangeTests.cs new file mode 100644 index 00000000..e83a2424 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Domain/TimeRangeTests.cs @@ -0,0 +1,90 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using NUnit.Framework; + +#endregion + +namespace SpringAir.Domain +{ + /// + /// Unit tests for the TimeRange class. + /// + /// Rick Evans + /// $Id: TimeRangeTests.cs,v 1.1 2005/09/27 21:12:31 springboy Exp $ + [TestFixture] + public sealed class TimeRangeTests + { + [Test] + public void DefaultTypeConverterIsSet() + { + TypeConverter vrtr = TypeDescriptor.GetConverter(typeof(TimeRange)); + Assert.IsNotNull(vrtr, "Must not be null, default converter must be returned."); + Assert.AreEqual(typeof(TimeRange.TimeRangeTypeConverter), vrtr.GetType(), "Wrong type of default converter returned."); + } + } + + /// + /// Unit tests for the TimeRange.TimeRangeTypeConverter class. + /// + /// Rick Evans + [TestFixture] + public sealed class TimeRangeTimeRangeTypeConverterTests + { + [Test] + public void ConvertFromSunnyDay() + { + TimeRange expectedRange = new TimeRange(12, 9); + TimeRange.TimeRangeTypeConverter vrtr = new TimeRange.TimeRangeTypeConverter(); + TimeRange range = (TimeRange) vrtr.ConvertFrom("[12-9]"); + Assert.AreEqual(expectedRange, range); + } + + [Test] + public void ConvertFromSunnyDayWithLotsOfExtraneousWhitespace() + { + TimeRange expectedRange = new TimeRange(12, 9); + TimeRange.TimeRangeTypeConverter vrtr = new TimeRange.TimeRangeTypeConverter(); + TimeRange range = (TimeRange) vrtr.ConvertFrom("[ 12 - 9 ]"); + Assert.AreEqual(expectedRange, range); + } + + [Test] + [ExpectedException(typeof(FormatException))] + public void ConvertFromWithOutOfShortRangeException() + { + TimeRange.TimeRangeTypeConverter vrtr = new TimeRange.TimeRangeTypeConverter(); + vrtr.ConvertFrom("[ 1287876 - 9 ]"); + } + + [Test] + public void ConvertToSunnyDay() + { + string expectedRange = "[12 - 9]"; + TimeRange.TimeRangeTypeConverter vrtr = new TimeRange.TimeRangeTypeConverter(); + string range = (string) vrtr.ConvertTo(new TimeRange(12, 9), typeof(string)); + Assert.AreEqual(expectedRange, range); + } + } +} diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Service/BookingAgentIntegrationTests.cs b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Service/BookingAgentIntegrationTests.cs new file mode 100644 index 00000000..03a62677 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Service/BookingAgentIntegrationTests.cs @@ -0,0 +1,90 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace SpringAir.Service +{ + /// + /// Integration tests for the IBookingAgent service. + /// + /// Keith Donald + /// Rick Evans (.NET) + /// $Id: BookingAgentIntegrationTests.cs,v 1.1 2005/07/26 20:10:23 springboy Exp $ + [TestFixture] + public sealed class BookingAgentIntegrationTests + { + #region SetUp + + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp() + { + } + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp() + { + } + + #endregion + + #region TearDown + + /// + /// The tear down logic executed after the execution of each individual test. + /// + [TearDown] + public void TearDown() + { + } + + /// + /// The tear down logic executed after the entire test fixture has executed. + /// + [TestFixtureTearDown] + public void FixtureTearDown() + { + } + + #endregion + + [Test] + public void SuggestItinerariesSuccess() { + + } + + [Test] + public void SuggestItinerariesWhenNoneExist() + { + + } + } +} diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Service/DefaultBookingAgentIntegrationTests.cs b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Service/DefaultBookingAgentIntegrationTests.cs new file mode 100644 index 00000000..b19b0d67 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/Service/DefaultBookingAgentIntegrationTests.cs @@ -0,0 +1,39 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace SpringAir.Service +{ + /// + /// Integration tests for the DefaultBookingAgentTests class. + /// + /// Rick Evans + /// $Id: DefaultBookingAgentIntegrationTests.cs,v 1.1 2005/10/02 22:50:38 springboy Exp $ + [TestFixture] + public sealed class DefaultBookingAgentTests + { + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/SpringAir.Core.Tests.2003.csproj b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/SpringAir.Core.Tests.2003.csproj new file mode 100644 index 00000000..33189840 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/SpringAir.Core.Tests.2003.csproj @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/SpringAir.Core.Tests.2005.csproj b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/SpringAir.Core.Tests.2005.csproj new file mode 100644 index 00000000..2b877fe7 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/SpringAir.Core.Tests.2005.csproj @@ -0,0 +1,130 @@ + + + Local + 8.0.50727 + 2.0 + {BB9AFE5E-7785-43AC-86ED-CF0A073F006E} + Debug + AnyCPU + + + + + SpringAir.Core.Tests + + + JScript + Grid + IE50 + false + Library + SpringAir + OnBuildSuccess + + + + + + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\..\..\lib\Net\2.0\DotNetMock.dll + + + False + ..\..\..\..\..\lib\Net\2.0\nunit.framework.dll + + + System + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + {5E3427D8-62FC-483F-A86B-B44A79A46BCC} + SpringAir.Core.2005 + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Core.Tests/SpringAir.Core.Tests.build b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/SpringAir.Core.Tests.build new file mode 100644 index 00000000..68468eac --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Core.Tests/SpringAir.Core.Tests.build @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/AssemblyInfo.cs b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..c7884401 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/AssemblyInfo.cs @@ -0,0 +1,27 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly : ComVisible(false)] +[assembly: AssemblyTitle("SpringAir ADO.NET DAO tests")] +[assembly: AssemblyDescription("ADO.NET DAO tests for SpringAir")] \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/AircraftDaoTests.cs b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/AircraftDaoTests.cs new file mode 100644 index 00000000..5f74a63e --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/AircraftDaoTests.cs @@ -0,0 +1,85 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; +using Spring.Data.Common; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data.Ado +{ + /// + /// Integration tests for the ADO.NET-backed AircraftDao class. + /// + /// Rick Evans (.NET) + /// $Id: AircraftDaoTests.cs,v 1.3 2006/11/28 06:42:27 markpollack Exp $ + [TestFixture] + public sealed class AircraftDaoTests + { + private AircraftDao dao; + + [SetUp] + public void SetUp() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("SqlServer-1.1"); + dbProvider.ConnectionString = + "Server=(local);Integrated Security=no;User ID=springqa;PWD=springqa;initial catalog=SpringAir;"; + dao = new AircraftDao(); + dao.DbProvider = dbProvider; + + /* + ReflectiveDbConnectionFactory connectionfactory = new ReflectiveDbConnectionFactory(); + connectionfactory.ConnectionType = typeof (SqlConnection); + connectionfactory.ConnectionString = + "Server=(local)\\Spring;Integrated Security=no;User ID=sa;PWD=spring;initial catalog=SpringAir;"; + + dao = new AircraftDao(); + dao.ConnectionFactory = connectionfactory; + dao.CommandType = typeof (SqlCommand); + dao.AircraftMapper = new AircraftMapper(); + */ + } + + [Test] + public void GetAllAircraftSunnyDay() + { + IList aircraft = dao.GetAllAircraft(); + Assert.IsNotNull(aircraft, "Must never return a null aircraft list."); + Assert.AreEqual(5, aircraft.Count); + foreach (Aircraft plane in aircraft) + { + Console.Out.WriteLine(plane); + } + } + + [Test] + public void GetAircraftSunnyDay() + { + Aircraft plane = dao.GetAircraft(0); + Assert.IsNotNull(plane, "Failed to retrive an Aircraft that is definitely in the database."); + Console.Out.WriteLine(plane); + } + } +} diff --git a/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/AirportDaoTests.cs b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/AirportDaoTests.cs new file mode 100644 index 00000000..18530137 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/AirportDaoTests.cs @@ -0,0 +1,93 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using NUnit.Framework; +using Spring.Data.Common; +using SpringAir.Domain; + +#endregion + +namespace SpringAir.Data.Ado +{ + /// + /// Integration tests for the ADO.NET-backed AirportDao class. + /// + /// Rick Evans (.NET) + /// $Id: AirportDaoTests.cs,v 1.4 2006/11/28 06:42:27 markpollack Exp $ + [TestFixture] + public sealed class AirportDaoTests + { + private AirportDao dao; + + [SetUp] + public void SetUp() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("SqlServer-1.1"); + dbProvider.ConnectionString = + "Server=(local);Integrated Security=no;User ID=springqa;PWD=springqa;initial catalog=SpringAir;"; + + dao = new AirportDao(); + dao.DbProvider = dbProvider; + /* + ReflectiveDbConnectionFactory connectionfactory = new ReflectiveDbConnectionFactory(); + connectionfactory.ConnectionType = typeof (SqlConnection); + connectionfactory.ConnectionString = + "Server=(local)\\Spring;Integrated Security=no;User ID=sa;PWD=spring;initial catalog=SpringAir;"; + + dao = new AirportDao(); + dao.CommandType = typeof (SqlCommand); + dao.ConnectionFactory = connectionfactory; + dao.AirportMapper = new AirportMapper(); + */ + } + + [Test] + public void GetAllAirports() + { + IList airports = dao.GetAllAirports(); + Assert.IsNotNull(airports, "Must never return a null airport list."); + Assert.AreEqual(4, airports.Count); + } + + [Test] + public void GetAirportById() + { + Airport airport = dao.GetAirport(0); + Assert.IsNotNull(airport, "Failed to retrive an Airport that is definitely in the database."); + } + + [Test] + public void GetAirportByCode() + { + Airport airport = dao.GetAirport("SFO"); + Assert.IsNotNull(airport, "Failed to retrive an Airport that is definitely in the database."); + } + + [Test] + [ExpectedException(typeof(Spring.Dao.EmptyResultDataAccessException))] + public void GetAirportByCodeNonExistent() + { + Airport airport = dao.GetAirport("FOO"); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/FlightDaoTests.cs b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/FlightDaoTests.cs new file mode 100644 index 00000000..d4bf0c7d --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/FlightDaoTests.cs @@ -0,0 +1,45 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace SpringAir.Data.Ado +{ + /// + /// Integration tests for the ADO.NET-backed FlightDao class. + /// + /// Rick Evans (.NET) + /// $Id: FlightDaoTests.cs,v 1.1 2005/10/30 09:06:40 springboy Exp $ + [TestFixture] + public sealed class FlightDaoTests + { + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void CtorWithNullAircraftDao() + { + new FlightDao(null); + } + } +} \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/ReflectiveDbConnectionFactoryTests.cs b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/ReflectiveDbConnectionFactoryTests.cs new file mode 100644 index 00000000..a802d363 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/Data/Ado/ReflectiveDbConnectionFactoryTests.cs @@ -0,0 +1,81 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using System.Data.SqlClient; +using NUnit.Framework; + +#endregion + +namespace SpringAir.Data.Ado +{ + /// + /// Unit tests for the ReflectiveDbConnectionFactory class. + /// + /// Rick Evans (.NET) + /// $Id: ReflectiveDbConnectionFactoryTests.cs,v 1.1 2005/10/30 09:06:40 springboy Exp $ + [TestFixture] + public sealed class ReflectiveDbConnectionFactoryTests + { + [Test] + [ExpectedException(typeof (ArgumentException))] + public void CtorWithNullConnectionString() + { + new ReflectiveDbConnectionFactory(typeof(SqlConnection), null); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void CtorWithEmptyConnectionString() + { + new ReflectiveDbConnectionFactory(typeof(SqlConnection), string.Empty); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void CtorWithWhitespacedConnectionString() + { + new ReflectiveDbConnectionFactory(typeof(SqlConnection), "\n"); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void CtorWithNullConnectionType() + { + new ReflectiveDbConnectionFactory(null, "a.connection.string"); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void CtorWithBadConnectionType() + { + new ReflectiveDbConnectionFactory(typeof(string), "a.connection.string"); + } + + [Test] + public void CtorSunnyDay() + { + new ReflectiveDbConnectionFactory(typeof(SqlConnection), "a.connection.string"); + } + } +} diff --git a/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/SpringAir.Data.Ado.Tests.2003.csproj b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/SpringAir.Data.Ado.Tests.2003.csproj new file mode 100644 index 00000000..ae26cc6f --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/SpringAir.Data.Ado.Tests.2003.csproj @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/SpringAir.Data.Ado.Tests.2005.csproj b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/SpringAir.Data.Ado.Tests.2005.csproj new file mode 100644 index 00000000..4ba3e73e --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/SpringAir.Data.Ado.Tests.2005.csproj @@ -0,0 +1,135 @@ + + + Local + 8.0.50727 + 2.0 + {B0A6F98E-5363-4575-BB6B-A84E91895468} + Debug + AnyCPU + + + + + SpringAir.Data.Ado.Tests + + + JScript + Grid + IE50 + false + Library + SpringAir + OnBuildSuccess + + + + + + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\..\..\lib\Net\2.0\DotNetMock.dll + + + False + ..\..\..\..\..\lib\Net\2.0\nunit.framework.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Core.dll + + + False + ..\..\..\..\..\bin\net\2.0\debug\Spring.Data.dll + + + System + + + System.Data + + + System.XML + + + + + Code + + + Code + + + Code + + + Code + + + + + + {5E3427D8-62FC-483F-A86B-B44A79A46BCC} + SpringAir.Core.2005 + + + {78B285FE-27F3-44F4-84B1-7A589AB48EF3} + SpringAir.Data.Ado.2005 + + + + + + + + + + \ No newline at end of file diff --git a/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/SpringAir.Data.Ado.Tests.build b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/SpringAir.Data.Ado.Tests.build new file mode 100644 index 00000000..a6d41bb9 --- /dev/null +++ b/examples/Spring/SpringAir/test/SpringAir.Data.Ado.Tests/SpringAir.Data.Ado.Tests.build @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/NHibernate10/net/1.1/Castle.DynamicProxy.dll b/lib/NHibernate10/net/1.1/Castle.DynamicProxy.dll new file mode 100644 index 00000000..d95b1474 Binary files /dev/null and b/lib/NHibernate10/net/1.1/Castle.DynamicProxy.dll differ diff --git a/lib/NHibernate10/net/1.1/Castle.DynamicProxy.license.txt b/lib/NHibernate10/net/1.1/Castle.DynamicProxy.license.txt new file mode 100644 index 00000000..b7530910 --- /dev/null +++ b/lib/NHibernate10/net/1.1/Castle.DynamicProxy.license.txt @@ -0,0 +1,13 @@ +Copyright 2004-2005 Castle Project - http://www.castleproject.org/ + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/NHibernate10/net/1.1/Iesi.Collections.dll b/lib/NHibernate10/net/1.1/Iesi.Collections.dll new file mode 100644 index 00000000..a326a0a9 Binary files /dev/null and b/lib/NHibernate10/net/1.1/Iesi.Collections.dll differ diff --git a/lib/NHibernate10/net/1.1/Iesi.Collections.license.txt b/lib/NHibernate10/net/1.1/Iesi.Collections.license.txt new file mode 100644 index 00000000..66c4a8dc --- /dev/null +++ b/lib/NHibernate10/net/1.1/Iesi.Collections.license.txt @@ -0,0 +1,9 @@ +Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. + +Copied from http://www.codeproject.com/csharp/sets.asp#xx703510xx that was posted by JasonSmith 12:13 2 Jan '04 + +Feel free to use this code any way you want to. As a favor to me, you can leave the copyright in there. You never know when someone might recognize your name! + +If you do use the code in a commercial product, I would appreciate hearing about it. This message serves as legal notice that I won't be suing you for royalties! The code is in the public domain. + +On the other hand, I don't provide support. The code is actually simple enough that it shouldn't need it. diff --git a/lib/NHibernate10/net/1.1/NHibernate.dll b/lib/NHibernate10/net/1.1/NHibernate.dll new file mode 100644 index 00000000..127f1c9a Binary files /dev/null and b/lib/NHibernate10/net/1.1/NHibernate.dll differ diff --git a/lib/NHibernate10/net/1.1/NHibernate.license.txt b/lib/NHibernate10/net/1.1/NHibernate.license.txt new file mode 100644 index 00000000..8a88d148 --- /dev/null +++ b/lib/NHibernate10/net/1.1/NHibernate.license.txt @@ -0,0 +1,460 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + diff --git a/lib/NHibernate10/net/1.1/log4net.dll b/lib/NHibernate10/net/1.1/log4net.dll new file mode 100644 index 00000000..802b25af Binary files /dev/null and b/lib/NHibernate10/net/1.1/log4net.dll differ diff --git a/lib/NHibernate10/net/2.0/Castle.DynamicProxy.dll b/lib/NHibernate10/net/2.0/Castle.DynamicProxy.dll new file mode 100644 index 00000000..d95b1474 Binary files /dev/null and b/lib/NHibernate10/net/2.0/Castle.DynamicProxy.dll differ diff --git a/lib/NHibernate10/net/2.0/Castle.DynamicProxy.license.txt b/lib/NHibernate10/net/2.0/Castle.DynamicProxy.license.txt new file mode 100644 index 00000000..b7530910 --- /dev/null +++ b/lib/NHibernate10/net/2.0/Castle.DynamicProxy.license.txt @@ -0,0 +1,13 @@ +Copyright 2004-2005 Castle Project - http://www.castleproject.org/ + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/NHibernate10/net/2.0/Iesi.Collections.dll b/lib/NHibernate10/net/2.0/Iesi.Collections.dll new file mode 100644 index 00000000..a326a0a9 Binary files /dev/null and b/lib/NHibernate10/net/2.0/Iesi.Collections.dll differ diff --git a/lib/NHibernate10/net/2.0/Iesi.Collections.license.txt b/lib/NHibernate10/net/2.0/Iesi.Collections.license.txt new file mode 100644 index 00000000..66c4a8dc --- /dev/null +++ b/lib/NHibernate10/net/2.0/Iesi.Collections.license.txt @@ -0,0 +1,9 @@ +Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. + +Copied from http://www.codeproject.com/csharp/sets.asp#xx703510xx that was posted by JasonSmith 12:13 2 Jan '04 + +Feel free to use this code any way you want to. As a favor to me, you can leave the copyright in there. You never know when someone might recognize your name! + +If you do use the code in a commercial product, I would appreciate hearing about it. This message serves as legal notice that I won't be suing you for royalties! The code is in the public domain. + +On the other hand, I don't provide support. The code is actually simple enough that it shouldn't need it. diff --git a/lib/NHibernate10/net/2.0/NHibernate.dll b/lib/NHibernate10/net/2.0/NHibernate.dll new file mode 100644 index 00000000..127f1c9a Binary files /dev/null and b/lib/NHibernate10/net/2.0/NHibernate.dll differ diff --git a/lib/NHibernate10/net/2.0/NHibernate.license.txt b/lib/NHibernate10/net/2.0/NHibernate.license.txt new file mode 100644 index 00000000..8a88d148 --- /dev/null +++ b/lib/NHibernate10/net/2.0/NHibernate.license.txt @@ -0,0 +1,460 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + diff --git a/lib/NHibernate10/net/2.0/log4net.dll b/lib/NHibernate10/net/2.0/log4net.dll new file mode 100644 index 00000000..802b25af Binary files /dev/null and b/lib/NHibernate10/net/2.0/log4net.dll differ diff --git a/lib/NHibernate12/net/1.1/Castle.DynamicProxy.dll b/lib/NHibernate12/net/1.1/Castle.DynamicProxy.dll new file mode 100644 index 00000000..d95b1474 Binary files /dev/null and b/lib/NHibernate12/net/1.1/Castle.DynamicProxy.dll differ diff --git a/lib/NHibernate12/net/1.1/HashCodeProvider.dll b/lib/NHibernate12/net/1.1/HashCodeProvider.dll new file mode 100644 index 00000000..11a3a345 Binary files /dev/null and b/lib/NHibernate12/net/1.1/HashCodeProvider.dll differ diff --git a/lib/NHibernate12/net/1.1/Iesi.Collections.dll b/lib/NHibernate12/net/1.1/Iesi.Collections.dll new file mode 100644 index 00000000..9f7ce06b Binary files /dev/null and b/lib/NHibernate12/net/1.1/Iesi.Collections.dll differ diff --git a/lib/NHibernate12/net/1.1/NHibernate.dll b/lib/NHibernate12/net/1.1/NHibernate.dll new file mode 100644 index 00000000..5a7ca201 Binary files /dev/null and b/lib/NHibernate12/net/1.1/NHibernate.dll differ diff --git a/lib/NHibernate12/net/1.1/log4net.dll b/lib/NHibernate12/net/1.1/log4net.dll new file mode 100644 index 00000000..995816f2 Binary files /dev/null and b/lib/NHibernate12/net/1.1/log4net.dll differ diff --git a/lib/NHibernate12/net/2.0/Castle.DynamicProxy.dll b/lib/NHibernate12/net/2.0/Castle.DynamicProxy.dll new file mode 100644 index 00000000..3feebda1 Binary files /dev/null and b/lib/NHibernate12/net/2.0/Castle.DynamicProxy.dll differ diff --git a/lib/NHibernate12/net/2.0/Castle.DynamicProxy.license.txt b/lib/NHibernate12/net/2.0/Castle.DynamicProxy.license.txt new file mode 100644 index 00000000..b7530910 --- /dev/null +++ b/lib/NHibernate12/net/2.0/Castle.DynamicProxy.license.txt @@ -0,0 +1,13 @@ +Copyright 2004-2005 Castle Project - http://www.castleproject.org/ + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/NHibernate12/net/2.0/Iesi.Collections.dll b/lib/NHibernate12/net/2.0/Iesi.Collections.dll new file mode 100644 index 00000000..c4cc0373 Binary files /dev/null and b/lib/NHibernate12/net/2.0/Iesi.Collections.dll differ diff --git a/lib/NHibernate12/net/2.0/Iesi.Collections.license.txt b/lib/NHibernate12/net/2.0/Iesi.Collections.license.txt new file mode 100644 index 00000000..66c4a8dc --- /dev/null +++ b/lib/NHibernate12/net/2.0/Iesi.Collections.license.txt @@ -0,0 +1,9 @@ +Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. + +Copied from http://www.codeproject.com/csharp/sets.asp#xx703510xx that was posted by JasonSmith 12:13 2 Jan '04 + +Feel free to use this code any way you want to. As a favor to me, you can leave the copyright in there. You never know when someone might recognize your name! + +If you do use the code in a commercial product, I would appreciate hearing about it. This message serves as legal notice that I won't be suing you for royalties! The code is in the public domain. + +On the other hand, I don't provide support. The code is actually simple enough that it shouldn't need it. diff --git a/lib/NHibernate12/net/2.0/NHibernate.dll b/lib/NHibernate12/net/2.0/NHibernate.dll new file mode 100644 index 00000000..19bc3f9e Binary files /dev/null and b/lib/NHibernate12/net/2.0/NHibernate.dll differ diff --git a/lib/NHibernate12/net/2.0/NHibernate.license.txt b/lib/NHibernate12/net/2.0/NHibernate.license.txt new file mode 100644 index 00000000..8a88d148 --- /dev/null +++ b/lib/NHibernate12/net/2.0/NHibernate.license.txt @@ -0,0 +1,460 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + diff --git a/lib/NHibernate12/net/2.0/log4net.dll b/lib/NHibernate12/net/2.0/log4net.dll new file mode 100644 index 00000000..ffc57e11 Binary files /dev/null and b/lib/NHibernate12/net/2.0/log4net.dll differ diff --git a/lib/Net/1.0/Common.Logging.Log4Net.dll b/lib/Net/1.0/Common.Logging.Log4Net.dll new file mode 100644 index 00000000..04f74816 Binary files /dev/null and b/lib/Net/1.0/Common.Logging.Log4Net.dll differ diff --git a/lib/Net/1.0/Common.Logging.dll b/lib/Net/1.0/Common.Logging.dll new file mode 100644 index 00000000..d7a8f158 Binary files /dev/null and b/lib/Net/1.0/Common.Logging.dll differ diff --git a/lib/Net/1.0/DotNetMock.Framework.dll b/lib/Net/1.0/DotNetMock.Framework.dll new file mode 100644 index 00000000..ed369b46 Binary files /dev/null and b/lib/Net/1.0/DotNetMock.Framework.dll differ diff --git a/lib/Net/1.0/DotNetMock.dll b/lib/Net/1.0/DotNetMock.dll new file mode 100644 index 00000000..0db31ce0 Binary files /dev/null and b/lib/Net/1.0/DotNetMock.dll differ diff --git a/lib/Net/1.0/Rhino.Mocks.dll b/lib/Net/1.0/Rhino.Mocks.dll new file mode 100644 index 00000000..bc98d2dc Binary files /dev/null and b/lib/Net/1.0/Rhino.Mocks.dll differ diff --git a/lib/Net/1.0/antlr.runtime.dll b/lib/Net/1.0/antlr.runtime.dll new file mode 100644 index 00000000..9fdbe5a9 Binary files /dev/null and b/lib/Net/1.0/antlr.runtime.dll differ diff --git a/lib/Net/1.0/nunit.framework.dll b/lib/Net/1.0/nunit.framework.dll new file mode 100644 index 00000000..42cc12cf Binary files /dev/null and b/lib/Net/1.0/nunit.framework.dll differ diff --git a/lib/Net/1.1/Common.Logging.Log4Net.dll b/lib/Net/1.1/Common.Logging.Log4Net.dll new file mode 100644 index 00000000..f334f6de Binary files /dev/null and b/lib/Net/1.1/Common.Logging.Log4Net.dll differ diff --git a/lib/Net/1.1/Common.Logging.dll b/lib/Net/1.1/Common.Logging.dll new file mode 100644 index 00000000..d7a8f158 Binary files /dev/null and b/lib/Net/1.1/Common.Logging.dll differ diff --git a/lib/Net/1.1/DotNetMock.Framework.dll b/lib/Net/1.1/DotNetMock.Framework.dll new file mode 100644 index 00000000..f7024192 Binary files /dev/null and b/lib/Net/1.1/DotNetMock.Framework.dll differ diff --git a/lib/Net/1.1/DotNetMock.dll b/lib/Net/1.1/DotNetMock.dll new file mode 100644 index 00000000..b1a6da19 Binary files /dev/null and b/lib/Net/1.1/DotNetMock.dll differ diff --git a/lib/Net/1.1/NMS.dll b/lib/Net/1.1/NMS.dll new file mode 100644 index 00000000..7a8e73fe Binary files /dev/null and b/lib/Net/1.1/NMS.dll differ diff --git a/lib/Net/1.1/NUnitAsp.dll b/lib/Net/1.1/NUnitAsp.dll new file mode 100644 index 00000000..9f45eff8 Binary files /dev/null and b/lib/Net/1.1/NUnitAsp.dll differ diff --git a/lib/Net/1.1/NUnitAspEx.dll b/lib/Net/1.1/NUnitAspEx.dll new file mode 100644 index 00000000..95c1eb4b Binary files /dev/null and b/lib/Net/1.1/NUnitAspEx.dll differ diff --git a/lib/Net/1.1/Nullables.dll b/lib/Net/1.1/Nullables.dll new file mode 100644 index 00000000..8b7805a7 Binary files /dev/null and b/lib/Net/1.1/Nullables.dll differ diff --git a/lib/Net/1.1/Quartz.dll b/lib/Net/1.1/Quartz.dll new file mode 100644 index 00000000..8e0cb265 Binary files /dev/null and b/lib/Net/1.1/Quartz.dll differ diff --git a/lib/Net/1.1/Rhino.Mocks.dll b/lib/Net/1.1/Rhino.Mocks.dll new file mode 100644 index 00000000..bc98d2dc Binary files /dev/null and b/lib/Net/1.1/Rhino.Mocks.dll differ diff --git a/lib/Net/1.1/antlr.runtime.dll b/lib/Net/1.1/antlr.runtime.dll new file mode 100644 index 00000000..0f2be9fd Binary files /dev/null and b/lib/Net/1.1/antlr.runtime.dll differ diff --git a/lib/Net/1.1/nunit.core.dll b/lib/Net/1.1/nunit.core.dll new file mode 100644 index 00000000..3f06941f Binary files /dev/null and b/lib/Net/1.1/nunit.core.dll differ diff --git a/lib/Net/1.1/nunit.core.interfaces.dll b/lib/Net/1.1/nunit.core.interfaces.dll new file mode 100644 index 00000000..aca4c6d5 Binary files /dev/null and b/lib/Net/1.1/nunit.core.interfaces.dll differ diff --git a/lib/Net/1.1/nunit.framework.dll b/lib/Net/1.1/nunit.framework.dll new file mode 100644 index 00000000..42cc12cf Binary files /dev/null and b/lib/Net/1.1/nunit.framework.dll differ diff --git a/lib/Net/2.0/Common.Logging.Log4Net.dll b/lib/Net/2.0/Common.Logging.Log4Net.dll new file mode 100644 index 00000000..3900bb87 Binary files /dev/null and b/lib/Net/2.0/Common.Logging.Log4Net.dll differ diff --git a/lib/Net/2.0/Common.Logging.dll b/lib/Net/2.0/Common.Logging.dll new file mode 100644 index 00000000..d7a8f158 Binary files /dev/null and b/lib/Net/2.0/Common.Logging.dll differ diff --git a/lib/Net/2.0/DotNetMock.Framework.dll b/lib/Net/2.0/DotNetMock.Framework.dll new file mode 100644 index 00000000..f7024192 Binary files /dev/null and b/lib/Net/2.0/DotNetMock.Framework.dll differ diff --git a/lib/Net/2.0/DotNetMock.dll b/lib/Net/2.0/DotNetMock.dll new file mode 100644 index 00000000..b1a6da19 Binary files /dev/null and b/lib/Net/2.0/DotNetMock.dll differ diff --git a/lib/Net/2.0/EULA.rtf b/lib/Net/2.0/EULA.rtf new file mode 100644 index 00000000..1137bf3a --- /dev/null +++ b/lib/Net/2.0/EULA.rtf @@ -0,0 +1,522 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff38\deff0\stshfdbch11\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times New Roman};} +{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New{\*\falt Arial};}{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol{\*\falt Bookshelf Symbol 3};} +{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings{\*\falt Symbol};}{\f11\fbidi \fmodern\fcharset128\fprq1{\*\panose 02020609040205080304}MS Mincho{\*\falt ?l?r ??\'81\'66c};} +{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f38\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Tahoma{\*\falt ?l?r ???};} +{\f51\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0603020202020204}Trebuchet MS{\*\falt Univers};}{\f167\fbidi \fmodern\fcharset128\fprq1{\*\panose 02020609040205080304}@MS Mincho{\*\falt @MS Gothic};} +{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times New Roman};}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times New Roman};} +{\fhimajor\f31502\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times New Roman};} +{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times New Roman};}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times New Roman};} +{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times New Roman};} +{\f183\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times New Roman};}{\f184\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times New Roman};} +{\f186\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times New Roman};}{\f187\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times New Roman};} +{\f188\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times New Roman};}{\f189\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times New Roman};} +{\f190\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times New Roman};}{\f191\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times New Roman};}{\f203\fbidi \fmodern\fcharset238\fprq1 Courier New CE{\*\falt Arial};} +{\f204\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr{\*\falt Arial};}{\f206\fbidi \fmodern\fcharset161\fprq1 Courier New Greek{\*\falt Arial};}{\f207\fbidi \fmodern\fcharset162\fprq1 Courier New Tur{\*\falt Arial};} +{\f208\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew){\*\falt Arial};}{\f209\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic){\*\falt Arial};}{\f210\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic{\*\falt Arial};} +{\f211\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese){\*\falt Arial};}{\f295\fbidi \fmodern\fcharset0\fprq1 MS Mincho Western{\*\falt ?l?r ??\'81\'66c};}{\f293\fbidi \fmodern\fcharset238\fprq1 MS Mincho CE{\*\falt ?l?r ??\'81\'66c};} +{\f294\fbidi \fmodern\fcharset204\fprq1 MS Mincho Cyr{\*\falt ?l?r ??\'81\'66c};}{\f296\fbidi \fmodern\fcharset161\fprq1 MS Mincho Greek{\*\falt ?l?r ??\'81\'66c};}{\f297\fbidi \fmodern\fcharset162\fprq1 MS Mincho Tur{\*\falt ?l?r ??\'81\'66c};} +{\f300\fbidi \fmodern\fcharset186\fprq1 MS Mincho Baltic{\*\falt ?l?r ??\'81\'66c};}{\f523\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}{\f524\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;} +{\f526\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f527\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f530\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}{\f563\fbidi \fswiss\fcharset238\fprq2 Tahoma CE{\*\falt ?l?r ???};} +{\f564\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr{\*\falt ?l?r ???};}{\f566\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek{\*\falt ?l?r ???};}{\f567\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur{\*\falt ?l?r ???};} +{\f568\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew){\*\falt ?l?r ???};}{\f569\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic){\*\falt ?l?r ???};}{\f570\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic{\*\falt ?l?r ???};} +{\f571\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese){\*\falt ?l?r ???};}{\f572\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai){\*\falt ?l?r ???};}{\f693\fbidi \fswiss\fcharset238\fprq2 Trebuchet MS CE{\*\falt Univers};} +{\f694\fbidi \fswiss\fcharset204\fprq2 Trebuchet MS Cyr{\*\falt Univers};}{\f696\fbidi \fswiss\fcharset161\fprq2 Trebuchet MS Greek{\*\falt Univers};}{\f697\fbidi \fswiss\fcharset162\fprq2 Trebuchet MS Tur{\*\falt Univers};} +{\f700\fbidi \fswiss\fcharset186\fprq2 Trebuchet MS Baltic{\*\falt Univers};}{\f1855\fbidi \fmodern\fcharset0\fprq1 @MS Mincho Western{\*\falt @MS Gothic};}{\f1853\fbidi \fmodern\fcharset238\fprq1 @MS Mincho CE{\*\falt @MS Gothic};} +{\f1854\fbidi \fmodern\fcharset204\fprq1 @MS Mincho Cyr{\*\falt @MS Gothic};}{\f1856\fbidi \fmodern\fcharset161\fprq1 @MS Mincho Greek{\*\falt @MS Gothic};}{\f1857\fbidi \fmodern\fcharset162\fprq1 @MS Mincho Tur{\*\falt @MS Gothic};} +{\f1860\fbidi \fmodern\fcharset186\fprq1 @MS Mincho Baltic{\*\falt @MS Gothic};}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times New Roman};} +{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times New Roman};}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times New Roman};} +{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times New Roman};}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times New Roman};} +{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times New Roman};}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times New Roman};} +{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times New Roman};}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times New Roman};} +{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times New Roman};}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times New Roman};} +{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times New Roman};}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times New Roman};} +{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times New Roman};}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times New Roman};} +{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times New Roman};}{\fhimajor\f31528\fbidi \froman\fcharset238\fprq2 Cambria CE;}{\fhimajor\f31529\fbidi \froman\fcharset204\fprq2 Cambria Cyr;} +{\fhimajor\f31531\fbidi \froman\fcharset161\fprq2 Cambria Greek;}{\fhimajor\f31532\fbidi \froman\fcharset162\fprq2 Cambria Tur;}{\fhimajor\f31535\fbidi \froman\fcharset186\fprq2 Cambria Baltic;} +{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times New Roman};}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times New Roman};} +{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times New Roman};}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times New Roman};} +{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times New Roman};}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times New Roman};} +{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times New Roman};}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times New Roman};} +{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times New Roman};}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times New Roman};} +{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times New Roman};}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times New Roman};} +{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times New Roman};}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times New Roman};} +{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times New Roman};}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times New Roman};} +{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times New Roman};}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times New Roman};} +{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times New Roman};}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times New Roman};} +{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times New Roman};}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times New Roman};} +{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times New Roman};}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times New Roman};} +{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} +{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times New Roman};} +{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times New Roman};}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times New Roman};} +{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times New Roman};}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times New Roman};} +{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times New Roman};}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times New Roman};} +{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times New Roman};}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0; +\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp +\fs22\dbch\af11 }{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{ +\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\snext0 \sautoupd \sqformat \spriority0 \styrsid2453294 Normal;}{\s1\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 +\b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext1 \slink15 \sqformat \styrsid2453294 heading 1;}{\s2\ql \fi-363\li720\ri0\sb120\sa120\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl1\outlinelevel1\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 \b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext2 \slink16 \sqformat \styrsid2453294 heading 2;}{\s3\ql \li0\ri0\sb120\sa120\widctlpar\tx1077\wrapdefault\aspalpha\aspnum\faauto\outlinelevel2\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext3 \slink17 \sqformat \styrsid2453294 heading 3;}{\s4\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar +\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl3\outlinelevel3\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext4 \slink18 \sqformat \styrsid2453294 heading 4;}{\s5\ql \fi-357\li1792\ri0\sb120\sa120\widctlpar\tx1792\jclisttab\tx2155\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl4\outlinelevel4\adjustright\rin0\lin1792\itap0 \rtlch\fcs1 +\af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext5 \slink19 \sqformat \styrsid2453294 heading 5;}{\s6\ql \fi-357\li2149\ri0\sb120\sa120\widctlpar +\jclisttab\tx2152\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl5\outlinelevel5\adjustright\rin0\lin2149\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext6 \slink20 \sqformat \styrsid2453294 heading 6;}{\s7\ql \fi-357\li2506\ri0\sb120\sa120\widctlpar\jclisttab\tx2509\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl6\outlinelevel6\adjustright\rin0\lin2506\itap0 \rtlch\fcs1 +\af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext7 \slink21 \sqformat \styrsid2453294 heading 7;}{\s8\ql \fi-357\li2863\ri0\sb120\sa120\widctlpar +\jclisttab\tx2866\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl7\outlinelevel7\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext8 \slink22 \sqformat \styrsid2453294 heading 8;}{\s9\ql \fi-358\li3221\ri0\sb120\sa120\widctlpar\jclisttab\tx3223\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl8\outlinelevel8\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 +\af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext9 \slink23 \sqformat \styrsid2453294 heading 9;}{\*\cs10 \additive \slink51 \slocked \ssemihidden \styrsid2453294 +Default Paragraph Font;}{\*\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblind0\tblindtype3\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv +\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f0\hich\af0\dbch\af11\cgrid\langnp1033\langfenp1033 +\snext11 \ssemihidden \sunhideused \sqformat Normal Table;}{\*\cs15 \additive \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\f38\fs20 \sbasedon10 \slink1 \slocked \styrsid6178267 Heading 1 Char;}{\*\cs16 \additive \rtlch\fcs1 \ab\ai\af31503\afs28 \ltrch\fcs0 +\b\i\fs28\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink2 \slocked \spriority9 Heading 2 Char;}{\*\cs17 \additive \rtlch\fcs1 \ab\af31503\afs26 \ltrch\fcs0 \b\fs26\loch\f31502\hich\af31502\dbch\af31501 +\sbasedon10 \slink3 \slocked \ssemihidden \spriority9 Heading 3 Char;}{\*\cs18 \additive \rtlch\fcs1 \ab\af31507\afs28 \ltrch\fcs0 \b\fs28\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink4 \slocked \ssemihidden \spriority9 Heading 4 Char;}{\* +\cs19 \additive \rtlch\fcs1 \ab\ai\af31507\afs26 \ltrch\fcs0 \b\i\fs26\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink5 \slocked \ssemihidden \spriority9 Heading 5 Char;}{\*\cs20 \additive \rtlch\fcs1 \ab\af31507 \ltrch\fcs0 +\b\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink6 \slocked \ssemihidden \spriority9 Heading 6 Char;}{\*\cs21 \additive \rtlch\fcs1 \af31507\afs24 \ltrch\fcs0 \fs24\loch\f31506\hich\af31506\dbch\af31505 +\sbasedon10 \slink7 \slocked \ssemihidden \spriority9 Heading 7 Char;}{\*\cs22 \additive \rtlch\fcs1 \ai\af31507\afs24 \ltrch\fcs0 \i\fs24\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink8 \slocked \ssemihidden \spriority9 Heading 8 Char;}{\* +\cs23 \additive \rtlch\fcs1 \af31503 \ltrch\fcs0 \loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink9 \slocked \ssemihidden \spriority9 Heading 9 Char;}{ +\s24\ql \li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin357\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext24 \styrsid2453294 Body 1;}{\s25\ql \li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext25 \styrsid2453294 Body 2;}{\s26\ql \li1077\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 +\af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext26 \styrsid2453294 Body 3;}{ +\s27\ql \li1435\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext27 \styrsid2453294 Body 4;}{\s28\ql \li1803\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1803\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext28 \styrsid2453294 Body 5;}{\s29\ql \li2160\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2160\itap0 \rtlch\fcs1 +\af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext29 \styrsid2453294 Body 6;}{ +\s30\ql \li2506\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2506\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext30 \styrsid2453294 Body 7;}{\s31\ql \li2863\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext31 \styrsid2453294 Body 8;}{\s32\ql \li3221\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 +\af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext32 \styrsid2453294 Body 9;}{\s33\ql \fi-357\li357\ri0\sb120\sa120\widctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls1\adjustright\rin0\lin357\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext33 \styrsid2453294 +Bullet 1;}{\s34\ql \fi-363\li720\ri0\sb120\sa120\widctlpar\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext34 \styrsid2453294 Bullet 2;}{\s35\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar +\jclisttab\tx1080\wrapdefault\aspalpha\aspnum\faauto\ls3\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext35 \styrsid2453294 +Bullet 3;}{\s36\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext36 \styrsid2453294 Bullet 4;}{\s37\ql \fi-357\li1792\ri0\sb120\sa120\widctlpar +\jclisttab\tx1795\wrapdefault\aspalpha\aspnum\faauto\ls5\adjustright\rin0\lin1792\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext37 \styrsid2453294 +Bullet 5;}{\s38\ql \fi-357\li2149\ri0\sb120\sa120\widctlpar\jclisttab\tx2152\wrapdefault\aspalpha\aspnum\faauto\ls6\adjustright\rin0\lin2149\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext38 \styrsid2453294 Bullet 6;}{\s39\ql \fi-357\li2506\ri0\sb120\sa120\widctlpar +\jclisttab\tx2509\wrapdefault\aspalpha\aspnum\faauto\ls7\adjustright\rin0\lin2506\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext39 \styrsid2453294 +Bullet 7;}{\s40\ql \fi-357\li2863\ri0\sb120\sa120\widctlpar\jclisttab\tx2866\wrapdefault\aspalpha\aspnum\faauto\ls8\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext40 \styrsid2453294 Bullet 8;}{\s41\ql \fi-358\li3221\ri0\sb120\sa120\widctlpar +\jclisttab\tx3223\wrapdefault\aspalpha\aspnum\faauto\ls9\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon32 \snext41 \styrsid2453294 Bullet 9;}{\s42\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs28\alang1025 \ltrch\fcs0 +\b\fs28\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \styrsid2453294 Heading EULA;}{\s43\ql \li0\ri0\sb120\sa120\widctlpar\brdrb\brdrs\brdrw10\brsp20 +\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs28\alang1025 \ltrch\fcs0 \b\fs28\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \styrsid2453294 +Heading Software Title;}{\s44\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 +\b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext44 \styrsid2453294 Preamble;}{\s45\ql \li0\ri0\sb120\sa120\widctlpar\brdrb\brdrs\brdrw10\brsp20 +\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 \b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext1 \styrsid2453294 Preamble Border;}{ +\s46\qc \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 \b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext46 \styrsid2453294 Heading Warranty;}{\s47\ql \fi-360\li360\ri0\sb120\sa120\widctlpar\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls11\outlinelevel0\adjustright\rin0\lin360\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \styrsid2453294 Heading 1 Warranty;}{\s48\ql \fi-360\li720\ri0\sb120\sa120\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls11\ilvl1\outlinelevel1\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext0 \styrsid2453294 Heading 2 Warranty;}{\*\cs49 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2 \sbasedon10 \styrsid12483380 Hyperlink;}{\s50\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar +\jclisttab\tx1080\wrapdefault\aspalpha\aspnum\faauto\ls13\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon35 \snext50 \sautoupd \styrsid2453294 Bullet 3 Underline;}{\s51\ql \li0\ri0\sa160\sl-240\slmult0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext51 \slink10 \styrsid11613206 Char;}{\s52\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext52 \slink53 \ssemihidden \styrsid16123551 footnote text;}{\*\cs53 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\f38\fs20 \sbasedon10 \slink52 \slocked \ssemihidden Footnote Text Char;}{\*\cs54 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \super \sbasedon10 \ssemihidden \styrsid16123551 footnote reference;}{ +\s55\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext55 \slink56 \ssemihidden \styrsid16123551 endnote text;}{\*\cs56 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 \sbasedon10 \slink55 \slocked \ssemihidden Endnote Text Char;}{\*\cs57 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \super +\sbasedon10 \ssemihidden \styrsid16123551 endnote reference;}{\s58\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext58 \slink59 \ssemihidden \styrsid16123551 annotation text;}{\*\cs59 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 +\sbasedon10 \slink58 \slocked \ssemihidden Comment Text Char;}{\*\cs60 \additive \rtlch\fcs1 \af0\afs16 \ltrch\fcs0 \fs16 \sbasedon10 \ssemihidden \styrsid16123551 annotation reference;}{\s61\ql \li0\ri0\sa160\sl-240\slmult0 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext61 \styrsid8833 Char1;}{ +\s62\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\tx1077\jclisttab\tx1440\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl2\outlinelevel2\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 +\b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon3 \snext62 \styrsid2453294 Heading 3 Bold;}{\s63\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs20\alang1025 \ltrch\fcs0 \fs20\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon36 \snext63 \styrsid2453294 Bullet 4 Underline;}{ +\s64\ql \li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon25 \snext64 \styrsid2453294 Body 2 Underline;}{\s65\ql \li0\ri0\sb120\sa120\widctlpar\brdrt\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 +\b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon44 \snext65 \styrsid2453294 Preamble Border Above;}{\*\cs66 \additive \v\f2\cf12\sub \styrsid14319332 tw4winMark;}{ +\s67\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs16\alang1025 \ltrch\fcs0 \fs16\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext67 \slink68 \ssemihidden \styrsid6453702 Balloon Text;}{\*\cs68 \additive \rtlch\fcs1 \af38\afs16 \ltrch\fcs0 \f38\fs16 \sbasedon10 \slink67 \slocked \ssemihidden Balloon Text Char;}{ +\s69\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 \b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon58 \snext58 \slink70 \ssemihidden \sunhideused \styrsid16201594 annotation subject;}{\*\cs70 \additive \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\f38\fs20 \sbasedon59 \slink69 \slocked \ssemihidden \styrsid16201594 Comment Subject Char;}{ +\s71\ql \li0\ri0\sb120\sa120\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext71 \slink72 \ssemihidden \sunhideused \styrsid16473449 header;}{\*\cs72 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 \sbasedon10 \slink71 \slocked \ssemihidden \styrsid16473449 Header Char;}{ +\s73\ql \li0\ri0\sb120\sa120\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext73 \slink74 \ssemihidden \sunhideused \styrsid16473449 footer;}{\*\cs74 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 \sbasedon10 \slink73 \slocked \ssemihidden \styrsid16473449 Footer Char;}}{\*\listtable +{\list\listtemplateid176468498\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid692200086\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \s41 +\fi-358\li3221\jclisttab\tx3223\lin3221 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440 +\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160 +\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880 +\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\jclisttab\tx3600\lin3600 +}{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid196815738} +{\list\listtemplateid-1085748990\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat600\levelspace0\levelindent0{\leveltext\leveltemplateid-21470828\'01\u-3913 ?;}{\levelnumbers;} +\loch\af3\hich\af3\dbch\af11\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;} +\f2\fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} +\f10\fbias0\hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0\hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 +\fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 +\fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 +\fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\lin5760 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\lin6480 }{\listname +;}\listid325669543}{\list\listtemplateid-1793664660{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af0 \ltrch\fcs0 +\b\i0\fbias0\hres0\chhres0 \s47\fi-360\li360\jclisttab\tx360\lin360 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af0 \ltrch\fcs0 +\b\i0\fbias0\hres0\chhres0 \s48\fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fbias0\hres0\chhres0 \fi-360\li1080\jclisttab\tx1080\lin1080 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fbias0\hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'04);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fbias0\hres0\chhres0 \fi-360\li1800\jclisttab\tx1800\lin1800 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fbias0\hres0\chhres0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fbias0\hres0\chhres0 \fi-360\li2520\jclisttab\tx2520\lin2520 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fbias0\hres0\chhres0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fbias0\hres0\chhres0 \fi-360\li3240\jclisttab\tx3240\lin3240 }{\listname ;}\listid394402059}{\list\listtemplateid1470557282{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat600\levelspace360\levelindent0{\leveltext +\'01\u-3913 ?;}{\levelnumbers;}\b\i0\fs20\loch\af3\hich\af3\dbch\af11\fbias0\hres0\chhres0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af51\afs20 \ltrch\fcs0 \b\i0\f51\fs20\fbias0\hres0\chhres0 \s2\fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0\hres0\chhres0 \s62\fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af51\afs20 \ltrch\fcs0 \b0\i0\strike0\f51\fs20\ulnone\fbias0\hres0\chhres0 \s4\fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af51\afs20 \ltrch\fcs0 \b0\i0\strike0\f51\fs20\ulnone\fbias0\hres0\chhres0 \s5\fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel +\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af51\afs20 \ltrch\fcs0 \b0\i0\f51\fs20\fbias0\hres0\chhres0 \s6\fi-357\li2149 +\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af51\afs20 \ltrch\fcs0 \b0\i0\f51\fs20\fbias0\hres0\chhres0 +\s7\fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af51\afs20 \ltrch\fcs0 +\b0\i0\f51\fs20\fbias0\hres0\chhres0 \s8\fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af51\afs20 +\ltrch\fcs0 \b0\i0\f51\fs20\fbias0\hres0\chhres0 \s9\fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid398796681}{\list\listtemplateid789093748\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\leveltemplateid-317712510\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \s34\fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid477573462}{\list\listtemplateid67698717{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li360\jclisttab\tx360\lin360 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'01);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02);}{\levelnumbers +\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1080\jclisttab\tx1080\lin1080 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'04);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\hres0\chhres0 \fi-360\li1800\jclisttab\tx1800\lin1800 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 +\fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2520 +\jclisttab\tx2520\lin2520 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880 +\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3240 +\jclisttab\tx3240\lin3240 }{\listname ;}\listid700712945}{\list\listtemplateid-296591990\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid-48305026 +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \s40\fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid810947713}{\list\listtemplateid-258053656\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace360\levelindent0{\leveltext\leveltemplateid67698703\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative +\levelspace360\levelindent0{\leveltext\leveltemplateid67698713\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative +\levelspace360\levelindent0{\leveltext\leveltemplateid67698715\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative +\levelspace360\levelindent0{\leveltext\leveltemplateid67698703\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative +\levelspace360\levelindent0{\leveltext\leveltemplateid67698713\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative +\levelspace360\levelindent0{\leveltext\leveltemplateid67698715\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative +\levelspace360\levelindent0{\leveltext\leveltemplateid67698703\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative +\levelspace360\levelindent0{\leveltext\leveltemplateid67698713\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative +\levelspace360\levelindent0{\leveltext\leveltemplateid67698715\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid988437699}{\list\listtemplateid285099256\listhybrid{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace720\levelindent0{\leveltext\leveltemplateid-308626962\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \s50\fi-357\li1077\jclisttab\tx1080\lin1077 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace720\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace720\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace720\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace720\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace720\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace720 +\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace720\levelindent0 +{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace720\levelindent0{\leveltext +\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1121073746}{\list\listtemplateid-1813845996\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid2033377338\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \s39\fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1219436735}{\list\listtemplateid-41362566\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\leveltemplateid-1175557160\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \s36\fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1559511898}{\list\listtemplateid-743794326\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\leveltemplateid1229593488\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \s35\fi-357\li1077\jclisttab\tx1080\lin1077 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1567649130}{\list\listtemplateid1363474438\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace1077\levelindent0{\leveltext\leveltemplateid1637229796\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \s37\fi-357\li1792\jclisttab\tx1795\lin1792 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace1077\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077 +\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0 +{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext +\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext +\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1848404271}{\list\listtemplateid-1802592190\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid961321180\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \s38\fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1877695764}{\list\listtemplateid1186249844\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\leveltemplateid1182702444\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \s33\fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid2054619191}}{\*\listoverridetable{\listoverride\listid2054619191\listoverridecount0\ls1}{\listoverride\listid477573462 +\listoverridecount0\ls2}{\listoverride\listid1567649130\listoverridecount0\ls3}{\listoverride\listid1559511898\listoverridecount0\ls4}{\listoverride\listid1848404271\listoverridecount0\ls5}{\listoverride\listid1877695764\listoverridecount0\ls6} +{\listoverride\listid1219436735\listoverridecount0\ls7}{\listoverride\listid810947713\listoverridecount0\ls8}{\listoverride\listid196815738\listoverridecount0\ls9}{\listoverride\listid398796681\listoverridecount0\ls10}{\listoverride\listid394402059 +\listoverridecount0\ls11}{\listoverride\listid700712945\listoverridecount0\ls12}{\listoverride\listid1121073746\listoverridecount0\ls13}{\listoverride\listid325669543\listoverridecount0\ls14}{\listoverride\listid398796681\listoverridecount0\ls15} +{\listoverride\listid988437699\listoverridecount0\ls16}}{\*\pgptbl {\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid8833\rsid25262\rsid149813\rsid202786\rsid224501\rsid336100\rsid619688\rsid687529\rsid993583 +\rsid1050666\rsid1063606\rsid1123747\rsid1247323\rsid1271240\rsid1314581\rsid1340611\rsid1396639\rsid1397504\rsid1445017\rsid1522967\rsid1660354\rsid1908946\rsid1967913\rsid1985256\rsid2040602\rsid2055273\rsid2169328\rsid2173862\rsid2294074\rsid2299921 +\rsid2303227\rsid2325299\rsid2325781\rsid2438325\rsid2443042\rsid2453294\rsid2631094\rsid2767599\rsid2837055\rsid2891929\rsid2908896\rsid3041681\rsid3084461\rsid3089876\rsid3090601\rsid3153954\rsid3176869\rsid3428779\rsid3494352\rsid3551572\rsid3565740 +\rsid3567813\rsid3607608\rsid3611273\rsid3737799\rsid3745708\rsid3805003\rsid3807310\rsid3893477\rsid3943274\rsid3952936\rsid4080061\rsid4130624\rsid4131293\rsid4151172\rsid4261399\rsid4285630\rsid4420257\rsid4538955\rsid4653850\rsid4657166\rsid4658437 +\rsid4665525\rsid4736258\rsid4927714\rsid5003640\rsid5009230\rsid5054803\rsid5055072\rsid5076588\rsid5077281\rsid5112338\rsid5140732\rsid5200145\rsid5252592\rsid5253692\rsid5272300\rsid5398118\rsid5575913\rsid5586591\rsid5660232\rsid5723753\rsid5733021 +\rsid5792090\rsid5839501\rsid5845217\rsid5848133\rsid5965347\rsid5969393\rsid5984178\rsid6178267\rsid6182090\rsid6306943\rsid6423491\rsid6423682\rsid6453702\rsid6499651\rsid6648707\rsid6692412\rsid6693430\rsid6759000\rsid6819414\rsid6952772\rsid6973360 +\rsid6976059\rsid7030393\rsid7091096\rsid7236981\rsid7293866\rsid7354836\rsid7481530\rsid7492415\rsid7627513\rsid7798874\rsid7951946\rsid7961682\rsid8137451\rsid8144333\rsid8264327\rsid8279245\rsid8333386\rsid8335631\rsid8349552\rsid8350558\rsid8392700 +\rsid8456026\rsid8457665\rsid8523053\rsid8532277\rsid8542814\rsid8586611\rsid8596935\rsid8602482\rsid8727711\rsid8938190\rsid9043979\rsid9069536\rsid9139129\rsid9196121\rsid9337714\rsid9378118\rsid9587393\rsid9587545\rsid9786004\rsid9857806\rsid9859629 +\rsid9989898\rsid10179210\rsid10289240\rsid10441824\rsid10565710\rsid10683655\rsid10705127\rsid10760272\rsid10951817\rsid11015424\rsid11095783\rsid11107534\rsid11224150\rsid11229606\rsid11230219\rsid11369554\rsid11613206\rsid11632249\rsid11676698 +\rsid11745537\rsid11936795\rsid11994093\rsid12016559\rsid12321879\rsid12483380\rsid12548342\rsid12613461\rsid12649626\rsid12791161\rsid12804081\rsid12870507\rsid12980889\rsid13004705\rsid13007617\rsid13110106\rsid13112801\rsid13240722\rsid13328841 +\rsid13336212\rsid13589070\rsid13637968\rsid13654305\rsid13783570\rsid14101503\rsid14107866\rsid14117292\rsid14240631\rsid14254048\rsid14299190\rsid14303978\rsid14311941\rsid14319332\rsid14352839\rsid14429059\rsid14434712\rsid14699709\rsid14749252 +\rsid14756287\rsid14825882\rsid15040283\rsid15142898\rsid15167476\rsid15231427\rsid15286622\rsid15290249\rsid15410853\rsid15491589\rsid15676556\rsid15825446\rsid15874102\rsid15946803\rsid16018894\rsid16069256\rsid16072846\rsid16078530\rsid16085697 +\rsid16123551\rsid16201594\rsid16340170\rsid16392012\rsid16451237\rsid16473449\rsid16524744\rsid16542872\rsid16543614\rsid16723387}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0 +\mnaryLim1}{\info{\creatim\yr2006\mo11\dy27\hr13\min3}{\revtim\yr2006\mo11\dy27\hr13\min3}{\version1}{\edmins0}{\nofpages1}{\nofwords169}{\nofchars1015}{\nofcharsws1182}{\vern32857}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordm +l}{\xmlns2 urn:schemas-microsoft-com:office:smarttags}}\paperw12240\paperh15840\margl1152\margr1152\margt1152\margb1152\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves1\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\noxlattoyen +\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\hyphcaps0\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1152\dgvorigin1152\dghshow1\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\rempersonalinfo\allowfieldendsel +\wrppunct\asianbrkrule\rsidroot12483380\newtblstyruls\nogrowautofit\viewbksp1\remdttm\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \fet0{\*\wgrffmtfilter 013f}\ilfomacatclnup0 +{\*\docvar {db_xml}{\'0d\'0dhttp://usetermassembly/dealbuilder_live/DealBuilderNET/dealbuilder.aspxmicrosoftmicrosoftmicrosoft96729672tbctbcUSETERMS_SUPPSUPER_ENGLISH2.6List of Supplement License Terms Languages\'0d2006032 +9495trueuniquetruetruetruetruetruetruetrueeagerday_month_year,.day +_month_year,._blankrtffalsedraftingindefinitetrueautosave|text|falseowner|text|REDMOND\'5cmalinftruefalsetruepromptvaluedeferredgrouppagesureunknownunsurefalsealiasfalseascendingfalsetruefalseRepeatCheckPromptAnswerDeferralGuidanceInsert your comments belowVaria +ble/dealbuilder_live/help/dealbuilder/help.htmlonsubmittruetruefalsetruefalsefalsetrue2dropdownsureUnknownfirstOtherlast20 +204Specify othe +rs:Specify other:11, and and/or or YesNo(%1 of %2)&\'3bnbsp\'3bvisibledigitsPrevNext&\'3bnbsp\'3b|&\'3bnbsp\'3b*afteraftertruefalseclient_side<\'3bU>\'3bWARNING:&l +t\'3b/U>\'3b That page is no longer relevant because of answers given on this page or a previous page!enabledrelevant_pages11100Chinese (Simplified)Chinese (Traditional)CzechDanishDutchEnglishFinnishFrenchGermanGreekHungarianItalianJapaneseKoreanNorwegian (Bokmal)PolishPortuguese (Brazil)Portuguese (Portugal)RussianSpanishSwedishTurkish101Microsoft .NET Framework 3.002006/11/012009/09/01NAAdditional terms for the supplementWindows operating + system}}{\*\ftnsep \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid6952772 \chftnsep +\par }}{\*\ftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid6952772 \chftnsepc +\par }}{\*\aftnsep \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid6952772 \chftnsep +\par }}{\*\aftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid6952772 \chftnsepc +\par }}\ltrpar \sectd \ltrsect\sbknone\linex0\endnhere\sectlinegrid360\sectdefaultcl\sectrsid7030393\sftnbj {\headerl \ltrpar \pard\plain \ltrpar\s71\ql \li0\ri0\sb120\sa120\widctlpar +\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38 \ltrch\fcs0 +\insrsid16473449 +\par }}{\headerr \ltrpar \pard\plain \ltrpar\s71\ql \li0\ri0\sb120\sa120\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid16473449 +\par }}{\footerl \ltrpar \pard\plain \ltrpar\s73\ql \li0\ri0\sb120\sa120\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid16473449 +\par }}{\footerr \ltrpar \pard\plain \ltrpar\s73\ql \li0\ri0\sb120\sa120\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid16473449 +\par }}{\headerf \ltrpar \pard\plain \ltrpar\s71\ql \li0\ri0\sb120\sa120\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid16473449 +\par }}{\footerf \ltrpar \pard\plain \ltrpar\s73\ql \li0\ri0\sb120\sa120\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs20\alang1025 \ltrch\fcs0 +\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid16473449 +\par }}{\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}} +{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar +\s42\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6648707 \rtlch\fcs1 \ab\af38\afs28\alang1025 \ltrch\fcs0 \b\fs28\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid12483380\charrsid6952772 \hich\af38\dbch\af11\loch\f38 MICROSOFT SOFTWARE SUPPLEMENT LICENSE TERMS +\par }\pard\plain \ltrpar\s43\ql \li0\ri0\sb120\sa120\widctlpar\brdrb\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6648707 \rtlch\fcs1 \ab\af38\afs28\alang1025 \ltrch\fcs0 +\b\fs28\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid6648707 \hich\af38\dbch\af11\loch\f38 MICROSOFT }{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid6453702 \hich\af38\dbch\af11\loch\f38 +ASP.NET 2.0 {\*\xmlopen\xmlns2{\factoidname City}}{\*\xmlopen\xmlns2{\factoidname place}}\hich\af38\dbch\af11\loch\f38 AJAX{\*\xmlclose}{\*\xmlclose} EXTENSIONS}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid12483380\charrsid6952772 +\par }\pard\plain \ltrpar\s44\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6648707 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 +\b\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid12483380\charrsid6952772 \hich\af38\dbch\af11\loch\f38 +Microsoft Corporation (or based on where you live, one of its affiliates) licenses this supplement to you. If you are licensed to use }{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid8532277\charrsid6952772 \hich\af38\dbch\af11\loch\f38 Microsoft }{ +\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid6648707 \hich\af38\dbch\af11\loch\f38 Windows operating system}{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid12483380\charrsid6952772 \hich\af38\dbch\af11\loch\f38 \hich\f38 software (the \'93\loch\f38 +\hich\f38 software\'94\loch\f38 ), you may use this supplement. You may not use it if you do \hich\af38\dbch\af11\loch\f38 not have a license for the software. You may use a copy of this supplement with each validly licensed copy of the software. +\par \hich\af38\dbch\af11\loch\f38 The following license terms describe}{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid3893477\charrsid6952772 \hich\af38\dbch\af11\loch\f38 }{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid12483380\charrsid6952772 +\hich\af38\dbch\af11\loch\f38 additional use }{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid15410853\charrsid6952772 \hich\af38\dbch\af11\loch\f38 terms}{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid12483380\charrsid6952772 \hich\af38\dbch\af11\loch\f38 + for this supplement. }{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid6178267\charrsid6952772 \hich\af38\dbch\af11\loch\f38 }{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid12483380\charrsid6952772 \hich\af38\dbch\af11\loch\f38 +These terms and the license terms for the software \hich\af38\dbch\af11\loch\f38 apply to your use of the supplement. If there is a conflict, these supplemental license terms apply.}{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid2837055\charrsid6952772 + +\par }{\rtlch\fcs1 \af38 \ltrch\fcs0 \caps\insrsid12483380\charrsid6952772 \hich\af38\dbch\af11\loch\f38 By using this supplement, you accept these terms. If you do not accept them, do not use this supplement.}{\rtlch\fcs1 \af38 \ltrch\fcs0 +\insrsid12483380\charrsid6952772 +\par }\pard\plain \ltrpar\s65\ql \li0\ri0\sb120\sa120\widctlpar\brdrt\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6648707 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 +\b\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid12483380\charrsid6952772 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af0\afs20 \ltrch\fcs0 \b\f38\fs20\insrsid12483380\charrsid6952772 \hich\af38\dbch\af11\loch\f38 1.\tab}}\pard\plain \ltrpar +\s1\ql \fi-360\li360\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls16\outlinelevel0 +\pnrdate-2035639326\pnrstart0\pnrxst2\pnrxst0\pnrxst0\pnrxst0\pnrxst46\pnrxst0\pnrstop6\pnrstart1\pnrrgb1\pnrrgb0\pnrrgb0\pnrrgb0\pnrrgb0\pnrrgb0\pnrrgb0\pnrrgb0\pnrrgb0\pnrstop9\pnrstart2\pnrnfc0\pnrnfc4\pnrnfc2\pnrnfc3\pnrnfc1\pnrnfc0\pnrnfc4 +\pnrnfc255\pnrnfc255\pnrnfc0\pnrnfc0\pnrnfc1\pnrnfc0\pnrnfc0\pnrnfc0\pnrnfc0\pnrnfc0\pnrnfc0\pnrstop18\pnrstart3\pnrpnbr1\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0 +\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrpnbr0\pnrstop36\adjustright\rin0\lin360\itap0\pararsid5200145 +\rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 \b\fs20\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid12483380\charrsid6952772 \hich\af38\dbch\af11\loch\f38 +SUPPORT SERVICES FOR SUPPLEMENT. }{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid12483380\charrsid6952772 \hich\af38\dbch\af11\loch\f38 Microsoft \hich\af38\dbch\af11\loch\f38 provides support services for }{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 +\b0\insrsid15410853\charrsid6952772 \hich\af38\dbch\af11\loch\f38 this software}{\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid12483380\charrsid6952772 \hich\af38\dbch\af11\loch\f38 as described at }{\field\flddirty{\*\fldinst {\rtlch\fcs1 \ab0\af38 +\ltrch\fcs0 \cs49\b0\ul\cf2\insrsid10565710\charrsid10565710 \hich\af38\dbch\af11\loch\f38 HYPERLINK "http://www.support.microsoft.com/common/international.aspx" }{\rtlch\fcs1 \af38 \ltrch\fcs0 \b0\ul\cf2\insrsid10760272\charrsid10565710 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000017000000340000007700770077002e0073007500700070006f00720074002e006d006900630072006f0073006f00660074002e0063006f006d002f0063006f006d006d006f006e002f0069006e007400650072006e006100740069006f006e0061006c002e0061007300 +700078000000e0c9ea79f9bace118c8200aa004ba90b7600000068007400740070003a002f002f007700770077002e0073007500700070006f00720074002e006d006900630072006f0073006f00660074002e0063006f006d002f0063006f006d006d006f006e002f0069006e007400650072006e006100740069006f006e +0061006c002e0061007300700078000000002100000064}}}{\fldrslt {\rtlch\fcs1 \af38 \ltrch\fcs0 \cs49\b0\ul\cf2\insrsid10565710\charrsid10565710 \hich\af38\dbch\af11\loch\f38 www.support.microsoft.com/common/international.aspx}}}\sectd \ltrsect +\sbknone\linex0\endnhere\sectlinegrid360\sectdefaultcl\sectrsid7030393\sftnbj {\rtlch\fcs1 \ab0\af38 \ltrch\fcs0 \b0\insrsid12483380\charrsid10565710 .}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid11613206\charrsid10565710 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af0\afs20 \ltrch\fcs0 \b\f38\fs20\insrsid9069536\charrsid9069536 \hich\af38\dbch\af11\loch\f38 2.\tab}}\pard \ltrpar +\s1\ql \fi-360\li360\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls16\outlinelevel0\adjustright\rin0\lin360\itap0\pararsid14303978 {\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid9069536\charrsid9069536 \hich\af38\dbch\af11\loch\f38 +MICROSOFT AJAX LIBRARY. }{\rtlch\fcs1 \af38 \ltrch\fcs0 \b0\insrsid9069536\charrsid9069536 \hich\af38\dbch\af11\loch\f38 This supplement }{\rtlch\fcs1 \af38 \ltrch\fcs0 \b0\insrsid9069536 \hich\af38\dbch\af11\loch\f38 includes the Microsoft AJAX Library}{ +\rtlch\fcs1 \af38 \ltrch\fcs0 \b0\insrsid5965347 .}{\rtlch\fcs1 \af38 \ltrch\fcs0 \b0\insrsid14825882 \hich\af38\dbch\af11\loch\f38 }{\rtlch\fcs1 \af38 \ltrch\fcs0 \b0\insrsid5965347 \hich\af38\dbch\af11\loch\f38 +The license terms accompanying that additional}{\rtlch\fcs1 \af38 \ltrch\fcs0 \b0\insrsid6693430 \hich\af38\dbch\af11\loch\f38 }{\rtlch\fcs1 \af38 \ltrch\fcs0 \b0\insrsid5848133 \hich\af38\dbch\af11\loch\f38 software }{\rtlch\fcs1 \af38 \ltrch\fcs0 +\b0\insrsid5965347 \hich\af38\dbch\af11\loch\f38 apply to it}{\rtlch\fcs1 \af38 \ltrch\fcs0 \b0\insrsid5848133 .}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13004705\charrsid13004705 +\par }\pard \ltrpar\s1\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0\pararsid13004705 {\rtlch\fcs1 \af38 \ltrch\fcs0 \b0\insrsid9069536\charrsid9069536 \hich\af38\dbch\af11\loch\f38 }{ +\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid3807310\charrsid9069536 +\par }{\*\themedata 504b030414000600080000002100828abc13fa0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb6ac3301045f785fe83d0b6d8 +72ba28a5d8cea249777d2cd20f18e4b12d6a8f843409c9df77ecb850ba082d74231062ce997b55ae8fe3a00e1893f354e9555e6885647de3a8abf4fbee29bbd7 +2a3150038327acf409935ed7d757e5ee14302999a654e99e393c18936c8f23a4dc072479697d1c81e51a3b13c07e4087e6b628ee8cf5c4489cf1c4d075f92a0b +44d7a07a83c82f308ac7b0a0f0fbf90c2480980b58abc733615aa2d210c2e02cb04430076a7ee833dfb6ce62e3ed7e14693e8317d8cd0433bf5c60f53fea2fe7 +065bd80facb647e9e25c7fc421fd2ddb526b2e9373fed4bb902e182e97b7b461e6bfad3f010000ffff0300504b030414000600080000002100a5d6a7e7c00000 +00360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4fc7060abb08 +84a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b63095120f88d94fbc +52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462a1a82fe353 +bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f7468656d652f7468 +656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b4b0d592c9c +070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b4757e8d3f7 +29e245eb2b260a0238fd010000ffff0300504b0304140006000800000021000bcba3ae95060000561b0000160000007468656d652f7468656d652f7468656d65 +312e786d6cec594d6f1b4518be23f11f467b6f6327b61b4775aad8b11b48d346b15bd4e37877bc3bcdecce6a669cd437d41e9190100571a012370e08a8d44a5c +caaf09144191fa17786766d7dec96e48d24650417d48bcb3cfbcdfef331fbe7aed7eccd0011192f2a4e3d52fd73c44129f0734093bdeedd1e0d2aa87a4c24980 +194f48c79b11e95d5b7fffbdab784d45242608e627720d77bc48a9746d6949fa308ce5659e9204de4db888b18247112e05021f82dc982d2dd76aada518d3c443 +098e41ecadc984fa048db4486f3d17de67f09828a9077c26865a347166186cb05fd70839933d26d001661d0ff404fc7044ee2b0f312c15bce87835f3f196d6af +2ee1b56c125327cc2dcc1b984f362f9b10ec2f1b9d221ccf95d6078df695cdb97c0360aa8cebf7fbbd7e7d2ecf00b0ef83a7d696a2ccc660b5decd651640f66b +5976afd6ac355c7c41fe4ac9e676b7db6db6335bac5003b25f1b25fc6aadd5d85876f00664f1cd12bed1dde8f55a0ede802cbe55c20faeb45b0d176f4011a3c9 +7e09ad133a1864d2e79009675b95f05580afd632f80205d530af2ead62c2137552adc5f81e1703006820c38a2648cd5232c13e54710fc76341b15680d7082ebc +b143be2c0d695d48fa82a6aae37d9862e88885bc57cfbf7ff5fc293a7af0ece8c14f470f1f1e3df8d10a72666de1242cce7af9ed677f3efe18fdf1f49b978fbe +a8c6cb22fed71f3ef9e5e7cfab81d03e0b735e7cf9e4b7674f5e7cf5e9efdf3daa806f083c2ec247342612dd2487688fc7e098898a6b39198bf3cd1845981667 +6c24a1c409d65a2ae4f755e4a06fce30cbb2e3d8d1256e04ef08a08f2ae0f5e93dc7e06124a68a5668de8e6207b8c339eb725119856dadab10e6d13409ab958b +6911b787f14195ee1e4e9cfcf6a729f0665e968ee3bd883866ee329c281c928428a4dff17d422abcbb4ba913d71dea0b2ef944a1bb147531ad0cc9888e9d6a5a +4cdaa231e46556e533e4db89cdce1dd4e5accaeb4d72e022a12b30ab307e449813c6eb78aa705c25728463560cf80daca22a238733e117717da920d321611cf5 +032265d59c5b02fc2d247d1b036355a67d87cd62172914ddaf927903735e446ef2fd5e84e3b40a3ba44954c47e20f7a14431dae5aa0abec3dd0ed1cf90079c9c +98ee3b9438e93e9d0d6ed3d031695120facd54e85c02553b0c1cd3e4efe89851e0635b031747c740802fbe7e5c51596f2b116fc09a54d5095bc7e8f724dc71d2 +ed7111d0b79f7337f134d92550e6e585e71de5bea35cef3f4fb927f5f3598976c1ad40bb7adf6037c5668b1c9fb8439e50c6866ac6c80d6936c912d689600083 +7a9e391d92f989298de06bc6eb0e2e14d8cc4182ab8fa88a86114e61835df7b4905066a24389522ee16067862b656b3c6cd2953d1636f581c1f281c46a870776 +78450fe7e782b918b3da84e6f0992b5ad102ceaa6ce54a2614dc7e1d65756dd499b5d58d6986ea1c6d7397218765d760701e4dd88020d8b640945b703ed7aae1 +6082190974dcedda9ba7c564e1225324231c902c47daef728eea264979ad989b00a89d8a1ce943de29512b686b6bb16fa0ed2c492aaa6b9ca02ecfde9b6429af +e0459674df1e6b4796149b9325e8b0e3b59bcb4d0ff938ed781338d3c2d73885ac4bbde7c32c848b215f095bf6a736b3e9f24536dbb9636e13d4e19ac2c6bde4 +b0c303a9906a13cbc89686799595004bb4266bff7213c27a510ed84a7f0d2b5656a118fe352b208e6e6ac964427c554c766144c7ce3e6654caa78a8861141ca2 +319b8a3d0ce9d7a50afe0454c2d5846104fd00f7683adae6954bce59d3156faf0cce8e63964638a35bdda279275bb8e9e3b90de6a9601ef85669bb71eefcae98 +96bf20578a65fc3f7345af277053b012e80cf8708d2b30d2fddaf1b8501107164a23ea0f046c1c0c7740b5c05d2cbc86a282cb64f35f9003fddff69c9561da1a +0e7c6a8f864850588f542408d9055a32d5778ab07ab67659912c13642aaa60ae4cadd9637240d84873604bafed1e8aa0d40d9b64346070c7ebcf7dce3a681cea +4d4eb1df1c0e99afbdb607fee99d8f6d6670cae561b3a1c9e33f37b16255b5f3cdf47ced2d3aa25f2cb6598dbc2b4059612968676dff9a269c73a9b58c55f278 +b9991b07592c7b0c83f30d510af73d48ff81f58f0a9fd95f26f4823ae27bc0ad087e68c88c4250d6975a9aee3c040c998f8e33e2cc23ac85d9e0669b271db77c +b9bee0bdaedd0795fc3ff35ef79ce19e6fcfdcec3add78a1e1ce63ec863b1bb5dd5b116ec8eff14685a1497e9c31c9313f6c157f7be2e37b90ee4db8e59f3225 +4d48e197258161033a34dd001460359aa9eb7f010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d652f7468 +656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f2451 +eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e319872 +0e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528a2 +c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100828abc13fa0000001c020000130000000000000000000000000000 +0000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000000000 +2b0100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000014020000746865 +6d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d00140006000800000021000bcba3ae95060000561b00001600000000000000000000 +000000d10200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027000000000000 +000000000000009a0900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000950a00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax266\lsdlockeddef0\lsdsemihiddendef1\lsdunhideuseddef1\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdlocked0 heading 1;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9; +\lsdpriority39 \lsdlocked0 toc 1;\lsdpriority39 \lsdlocked0 toc 2;\lsdpriority39 \lsdlocked0 toc 3;\lsdpriority39 \lsdlocked0 toc 4;\lsdpriority39 \lsdlocked0 toc 5;\lsdpriority39 \lsdlocked0 toc 6;\lsdpriority39 \lsdlocked0 toc 7; +\lsdpriority39 \lsdlocked0 toc 8;\lsdpriority39 \lsdlocked0 toc 9;\lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdpriority1 \lsdlocked0 Default Paragraph Font; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority59 \lsdlocked0 Table Grid;\lsdunhideused0 \lsdlocked0 Placeholder Text;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdunhideused0 \lsdlocked0 Revision; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdpriority37 \lsdlocked0 Bibliography;}}{\*\datastore 0105000002000000180000004d73786d6c322e534158584d4c5265616465722e352e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffffec69d9888b8b3d4c859eaf6cd158be0f0000000000000000000000009011 +9a8b6712c701feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/lib/Net/2.0/NMS.dll b/lib/Net/2.0/NMS.dll new file mode 100644 index 00000000..7a8e73fe Binary files /dev/null and b/lib/Net/2.0/NMS.dll differ diff --git a/lib/Net/2.0/NUnitAsp.dll b/lib/Net/2.0/NUnitAsp.dll new file mode 100644 index 00000000..9f45eff8 Binary files /dev/null and b/lib/Net/2.0/NUnitAsp.dll differ diff --git a/lib/Net/2.0/NUnitAspEx.dll b/lib/Net/2.0/NUnitAspEx.dll new file mode 100644 index 00000000..95c1eb4b Binary files /dev/null and b/lib/Net/2.0/NUnitAspEx.dll differ diff --git a/lib/Net/2.0/Quartz.dll b/lib/Net/2.0/Quartz.dll new file mode 100644 index 00000000..29196034 Binary files /dev/null and b/lib/Net/2.0/Quartz.dll differ diff --git a/lib/Net/2.0/Rhino.Mocks.dll b/lib/Net/2.0/Rhino.Mocks.dll new file mode 100644 index 00000000..2fe6ed8a Binary files /dev/null and b/lib/Net/2.0/Rhino.Mocks.dll differ diff --git a/lib/Net/2.0/System.Web.Extensions.dll b/lib/Net/2.0/System.Web.Extensions.dll new file mode 100644 index 00000000..11fdd5c9 Binary files /dev/null and b/lib/Net/2.0/System.Web.Extensions.dll differ diff --git a/lib/Net/2.0/antlr.runtime.dll b/lib/Net/2.0/antlr.runtime.dll new file mode 100644 index 00000000..f242d024 Binary files /dev/null and b/lib/Net/2.0/antlr.runtime.dll differ diff --git a/lib/Net/2.0/log4net.dll b/lib/Net/2.0/log4net.dll new file mode 100644 index 00000000..ffc57e11 Binary files /dev/null and b/lib/Net/2.0/log4net.dll differ diff --git a/lib/Net/2.0/log4net.license.txt b/lib/Net/2.0/log4net.license.txt new file mode 100644 index 00000000..29f81d81 --- /dev/null +++ b/lib/Net/2.0/log4net.license.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib/Net/2.0/nunit.core.dll b/lib/Net/2.0/nunit.core.dll new file mode 100644 index 00000000..3f06941f Binary files /dev/null and b/lib/Net/2.0/nunit.core.dll differ diff --git a/lib/Net/2.0/nunit.core.interfaces.dll b/lib/Net/2.0/nunit.core.interfaces.dll new file mode 100644 index 00000000..aca4c6d5 Binary files /dev/null and b/lib/Net/2.0/nunit.core.interfaces.dll differ diff --git a/lib/Net/2.0/nunit.framework.dll b/lib/Net/2.0/nunit.framework.dll new file mode 100644 index 00000000..42cc12cf Binary files /dev/null and b/lib/Net/2.0/nunit.framework.dll differ diff --git a/license.txt b/license.txt new file mode 100644 index 00000000..29f81d81 --- /dev/null +++ b/license.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/readme.txt b/readme.txt new file mode 100644 index 00000000..827340f5 --- /dev/null +++ b/readme.txt @@ -0,0 +1,176 @@ +THE SPRING.NET FRAMEWORK, Release 1.1.2 (May 6, 2008) +-------------------------------------------------------------------- +http://www.springframework.net/ + + +1. INTRODUCTION + +The 1.1 release of Spring.NET contains + + * A full featured Inversion of Control container + * An Aspect Oriented Programming framework + * Expression Language for lightweight scripting + * UI-agnostic validation framework + * ASP.NET Framework + - Dependency Injection for pages and user controls, bi-directional data binding, and more. + * Declarative transaction management abstraction + - Declarative transaction management via use of common XML configuration and attributes across different transaction APIs + * ADO.NET framework + - Simplifies use of ADO.NET. DAO support classes and integration with Spring's declarative transaction management functionality + * Portable Service Abstractions + - Export plain .NET objects via .NET Remoting, Web Service or .NET Serviced Component and create client side proxies based on endpoint URL and service interface. + * NHibernate Integation + - NHibernate 1.0 and 1.2 integration to simplify use of NHibernate and participate in Spring's declarative transaction management functionality. + * ASP.NET AJAX Integration + - Exporter to expose plain object on which Dependency Injection and AOP have been applied to JavaScript. + * NUnit integration + - Provides Dependency Injection of test cases and Spring container loading and caching. . Data access and transaction management features aid with integration testing. + +Spring.NET is a port of the Java based Spring Framework. In turn, the Java/J2EE Spring Framework is based on code published in "Expert One-on-One J2EE Design and Development" by Rod Johnson (Wrox, 2002). + + +2. KNOWN ISSUES + +The fallback rules for localized resources seem to have a bug that is fixed by applying Service Pack 1 for .NET 1.1. This affects the use of IMessageSource.GetMessage methods that specify CultureInfo. + +3. RELEASE INFO + +Release contents: + +* "src" contains the C# source files for the framework +* "test" contains the C# source files for Spring.NET's test suite +* "bin" contains various Spring.NET distribution dll files +* "lib/Net" contains shared third-party libraries needed for building the framework +* "lib/NHibernate10" contains NHibernate 1.0 dlls +* "lib/NHibernate12" contains NHibernate 1.2 dlls +* "doc" contains reference documentation, MSDN-style API help, and the Spring.NET xsd. +* "examples" contains sample applications. + +debug build is done using /DEBUG:full and release build using /DEBUG:pdbonly flags. + +The VS.NET solution for the framework and examples are provided. + +Latest info is available at the public website: http://www.springframework.net/ + +Project info at the SourceForge site: http://sourceforge.net/projects/springnet/ + +The Spring Framework is released under the terms of the Apache Software License (see license.txt). + + +4. DISTRIBUTION DLLs + +The "bin" directory contains the following distinct dll files for use in applications. Dependencies are those other than on the .NET BCL. + +* "Spring.Core" (~596 KB) +- Contents: Inversion of control container. Collection classes. +- Dependencies: antlr.runtime, Common.Logging + +* "Spring.Aop" (~144 KB) +- Contents: Abstract Oriented Programming Framework. +- Dependencies: Spring.Core, Common.Logging + +* "Spring.Data" (~312 KB) +- Contents: Transaction and ADO.NET Framework. +- Dependencies: Spring.Core, Spring.Aop + +* "Spring.Data.NHibernate" (~68 KB) +- Contents: NHibernate 1.0 integration +- Dependencies: Spring.Core, Spring.Aop, Spring.Data, NHibernate + +* "Spring.Data.NHibernate12" (~84 KB) +- Contents: NHibernate 1.2 integration +- Dependencies: Spring.Core, Spring.Aop, Spring.Data, NHibernate + +* "Spring.Services" (~72 KB) +- Contents: Web Services, Remoting, and Enterprise Component based services. +- Dependencies: Spring.Core, Spring.Aop + +* "Spring.Web" (~156 KB) +- Contents: ASP.NET based Web Application Framework. +- Dependencies: Spring.Core, Spring.Aop + +* "Spring.Web.Extensions" (~8 KB) +- Contents: ASP.NET AJAX Integartion +- Dependencies: Spring.Core, Spring.Aop, System.Web.Extensions + +* "Spring.Testing.NUnit" (~24 KB) +- Contents: NUnit Integration +- Dependencies: Spring.Core, Spring.Data, NUnit + +5. WHERE TO START? + +Documentation can be found in the "docs" directory: +* The Spring reference documentation + +Documented sample applications can be found in "examples": +* IoCQuickStart.MovieFinder - A simple example demonstrating basic IoC container behavior. +* IoCQuickStart.AppContext - Show use of various IApplicationContext features. +* IoCQuickStart.EventRegistry - Show use of loosely coupled eventing features. +* AopQuickStart - Show use of AOP features. +* SpringAir - Show use of Spring.Web features. +* Calculator - Show use of Spring.Services features. +* WebQuickStart - Show step by step usage of Spring.Web features. +* Web.Extensions.Example - Show ASP.NET AJAX integartion. +* DataQuickStart - Show use of Spring.Data data access features. +* TxQuickStart - Show use of Spring's transaction features. +* Data.NHibernate.Northwind - Show use of Spring's NHibernate features. + +6. How to build + +VS.NET +------ +There are three solution file for different version of VS.NET + +* Spring.Net.1.0.2002.sln for use with VS.NET 2002 +* Spring.Net.1.1.2003.sln for use with VS.NET 2003 +* Spring.Net.1.1.2005.sln for use with VS.NET 2005 + +NAnt +---- + +A NAnt build script can be obtained via CVS or nightly builds. You will need to +get the supporting tools directory from CVS as well. + +To build the source and run the unit tests type + +nant test -D:project.build.sign=false + +NOTE! You need to comment out the section of NAnt.exe.config in order to +property run unit tests under different version of the .NET framework. + +Debug builds are strongly named using the Spring.Net.snk key file. The ANTLR assemblies +are signed with this key as well until such time as the ANTR distribution provides their +own strongly signed assemblies. If you want to run the build to creat strongly signed +assebmlies you can generate a key file by executing the following commands + +sn -k Spring.Net.snk +nant + +The installation of the docbook toolchain is required to generate the reference +documentation and make a final release build. Refer to the Spring.NET Wiki at +http://opensource.atlassian.com/confluence/spring/display/NET/Docbook+Reference +for information on how to install the docbook toolchain. + +Innovasys Document X! is used to generate the SDK documentation. + + +7. Support + +The user forms at http://forum.springframework.net/ are available for you to submit questions, support requests, and interact with other Spring.NET users. + +Bug and issue tracking can be found at http://opensource.atlassian.com/projects/spring/secure/BrowseProject.jspa?id=10020 + +The Spring.NET wiki is located at http://opensource.atlassian.com/confluence/spring/display/NET/Home + +A Fisheye repository browser is located at http://fisheye1.cenqua.com/viewrep/springnet + +8. Acknowledgements + +InnovaSys Document X! +--------------------- +InnovSys has kindly provided a license to generate the SDK documentation and supporting utilities for +integration with Visual Studio. + + + + diff --git a/src/Spring/CommonAssemblyInfo.cs b/src/Spring/CommonAssemblyInfo.cs new file mode 100644 index 00000000..3c7daf91 --- /dev/null +++ b/src/Spring/CommonAssemblyInfo.cs @@ -0,0 +1,69 @@ +using System; +using System.Reflection; +[assembly: CLSCompliant(false)] + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// + +#if !NET_2_0 +[assembly: AssemblyConfiguration("net-1.1.win32; Release")] +#else +[assembly: AssemblyConfiguration("net-2.0.win32; Release")] +#endif +[assembly: AssemblyCompany("http://www.springframework.net")] +[assembly: AssemblyProduct("Spring.NET Framework 1.1")] +[assembly: AssemblyCopyright("Copyright 2002-2007 Spring.NET Framework Team.")] +[assembly: AssemblyTrademark("Apache License, Version 2.0")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// .NET Framework Version (RR) +// Revision = "1" for builds with VS.NET, nant build is # of days since 'project.year' +// property +// +// +// This is to support side-by-side deployment of .NET 1.1 and .NET 2.0 versions of the assembly. +#if !NET_2_0 +[assembly: AssemblyVersion("1.1.1.11001")] +#else +[assembly: AssemblyVersion("1.1.1.20001")] +#endif + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +#if STRONG +[assembly: AssemblyDelaySign(false)] +#if !NET_2_0 +[assembly: AssemblyKeyFile("Spring.Net.snk")] +#endif +#endif diff --git a/src/Spring/GenCommonAssemblyInfo.cs b/src/Spring/GenCommonAssemblyInfo.cs new file mode 100644 index 00000000..43cd3a2b --- /dev/null +++ b/src/Spring/GenCommonAssemblyInfo.cs @@ -0,0 +1,23 @@ +using System; +using System.Reflection; + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.1433 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +[assembly: CLSCompliantAttribute(false)] +[assembly: AssemblyConfigurationAttribute("net-2.0.win32; debug")] +[assembly: AssemblyProductAttribute("Spring.NET Framework 1.1.2 for .NET 2.0")] +[assembly: AssemblyCompanyAttribute("SpringSource")] +[assembly: AssemblyCopyrightAttribute("Copyright 2002-2008 Spring.NET Framework Team.")] +[assembly: AssemblyTrademarkAttribute("Apache License, Version 2.0")] +[assembly: AssemblyCultureAttribute("")] +[assembly: AssemblyVersionAttribute("1.1.2.20148")] +[assembly: AssemblyDelaySignAttribute(false)] + diff --git a/src/Spring/Spring.Aop/Aop/Config/AopNamespaceParser.cs b/src/Spring/Spring.Aop/Aop/Config/AopNamespaceParser.cs new file mode 100644 index 00000000..3a54a5ea --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Config/AopNamespaceParser.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Objects.Factory.Xml; + +namespace Spring.Aop.Config +{ + /// + /// Namespace parser for the aop namespace. + /// + /// + /// Using the advisor tag you can configure an and have it + /// applied to all the relevant objects in your application context automatically. The + /// advisor tag supports only referenced s. + /// + /// Rob harrop + /// Adrian Colyer + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: AopNamespaceParser.cs,v 1.3 2007/08/09 02:42:35 markpollack Exp $ + [ + NamespaceParser( + Namespace = "http://www.springframework.net/aop", + SchemaLocationAssemblyHint = typeof (AopNamespaceParser), + SchemaLocation = "/Spring.Aop.Config/spring-aop-1.1.xsd" + ) + ] + public class AopNamespaceParser : NamespaceParserSupport + { + /// + /// Register the for the 'config' tag. + /// + public override void Init() + { + RegisterObjectDefinitionParser("config", new ConfigObjectDefinitionParser()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Config/AopNamespaceUtils.cs b/src/Spring/Spring.Aop/Aop/Config/AopNamespaceUtils.cs new file mode 100644 index 00000000..63d44501 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Config/AopNamespaceUtils.cs @@ -0,0 +1,99 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Xml; +using Spring.Aop.Framework.AutoProxy; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +#endregion + + +namespace Spring.Aop.Config +{ + /// + /// Utility class for handling registration of auto-proxy creators used internally by the + /// aop namespace tags. + /// + /// Rob Harrop + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: AopNamespaceUtils.cs,v 1.4 2007/05/26 19:25:48 markpollack Exp $ + public class AopNamespaceUtils + { + + /// + /// The object name of the internally managed auto-proxy creator. + /// + public const string AUTO_PROXY_CREATOR_OBJECT_NAME = + "Spring.Aop.Config.InternalAutoProxyCreator"; + + + + /// + /// Registers the auto proxy creator if necessary. + /// + /// The parser context. + /// The source element. + public static void RegisterAutoProxyCreatorIfNecessary(ParserContext parserContext, XmlElement sourceElement) + { + RegisterApcAsRequired(typeof(DefaultAdvisorAutoProxyCreator), parserContext); + } + + /// + /// Registries the or escalate apc as required. + /// + /// The type. + /// The parser context. + private static void RegisterApcAsRequired(Type type, ParserContext parserContext) + { + AssertUtils.ArgumentNotNull(parserContext, "parserContext"); + IObjectDefinitionRegistry registry = parserContext.Registry; + + + if (!registry.ContainsObjectDefinition(AUTO_PROXY_CREATOR_OBJECT_NAME)) + { + RootObjectDefinition objectDefinition = new RootObjectDefinition(type); + //TODO source/role not yet implemented in .NET + objectDefinition.PropertyValues.Add("order", int.MaxValue); + registry.RegisterObjectDefinition(AUTO_PROXY_CREATOR_OBJECT_NAME, objectDefinition); + } + + } + + /// + /// Forces the auto proxy creator to use decorator proxy. + /// + /// The registry. + public static void ForceAutoProxyCreatorToUseDecoratorProxy(IObjectDefinitionRegistry registry) + { + if (registry.ContainsObjectDefinition(AUTO_PROXY_CREATOR_OBJECT_NAME)) + { + IObjectDefinition definition = registry.GetObjectDefinition(AUTO_PROXY_CREATOR_OBJECT_NAME); + definition.PropertyValues.Add("ProxyTargetType", true); + } + } + } +} diff --git a/src/Spring/Spring.Aop/Aop/Config/ConfigObjectDefinitionParser.cs b/src/Spring/Spring.Aop/Aop/Config/ConfigObjectDefinitionParser.cs new file mode 100644 index 00000000..4767c280 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Config/ConfigObjectDefinitionParser.cs @@ -0,0 +1,159 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +using System.Xml; +using Spring.Aop.Support; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +namespace Spring.Aop.Config +{ + /// + /// The for the <aop:config> tag. + /// + /// Mark Pollack (.NET) + /// $Id: ConfigObjectDefinitionParser.cs,v 1.3 2008/02/20 14:29:13 bbaia Exp $ + public class ConfigObjectDefinitionParser : IObjectDefinitionParser + { + /// + /// The 'proxy-target-type' attribute + /// + private static readonly string PROXY_TARGET_TYPE = "proxy-target-type"; + + private static readonly string ID = "id"; + + private static readonly string ORDER_PROPERTY = "order"; + + private static readonly string ADVICE_REF = "advice-ref"; + + private static readonly string ADVICE_OBJECT_NAME = "adviceObjectName"; + + private static readonly string POINTCUT_REF = "pointcut-ref"; + + + #region IObjectDefinitionParser Members + + /// + /// Parse the specified XmlElement and register the resulting + /// ObjectDefinitions with the IObjectDefinitionRegistry + /// embedded in the supplied + /// + /// The element to be parsed. + /// The object encapsulating the current state of the parsing process. + /// Provides access to a IObjectDefinitionRegistry + /// The primary object definition. + /// + ///

    + /// This method is never invoked if the parser is namespace aware + /// and was called to process the root node. + ///

    + ///
    + public IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + ConfigureAutoProxyCreator(parserContext, element); + XmlNodeList advisorNodes = element.GetElementsByTagName("advisor", element.NamespaceURI); + + //XmlNodeList advisorNodes = element.SelectNodes("*[local-name()='advisor' and namespace-uri()='" + element.NamespaceURI + "']"); + foreach (XmlElement advisorElement in advisorNodes) + { + ParseAdvisor(advisorElement, parserContext); + } + + return null; + } + + /// + /// Parses the supplied advisor element and registers the resulting + /// + /// The advisor element. + /// The parser context. + private void ParseAdvisor(XmlElement advisorElement, ParserContext parserContext) + { + AbstractObjectDefinition advisorDef = CreateAdvisorObjectDefinition(advisorElement, parserContext); + string id = advisorElement.GetAttribute(ID); + + string pointcutObjectName = ParsePointcutProperty(advisorElement, parserContext); + advisorDef.PropertyValues.Add(POINTCUT_REF, new RuntimeObjectReference(pointcutObjectName)); + string advisorObjectName = id; + if (StringUtils.HasText(advisorObjectName)) + { + parserContext.Registry.RegisterObjectDefinition(advisorObjectName, advisorDef); + } + else + { + parserContext.ReaderContext.RegisterWithGeneratedName(advisorDef); + } + } + + + private string ParsePointcutProperty(XmlElement element, ParserContext parserContext) + { + if (element.HasAttribute(POINTCUT_REF)) + { + string pointcutRef = element.GetAttribute(POINTCUT_REF); + if (!StringUtils.HasText(pointcutRef)) + { + parserContext.ReaderContext.ReportException(element, "advisor", "'pointcut-ref' attribute contains empty value."); + } + return pointcutRef; + } + else + { + parserContext.ReaderContext.ReportException(element, "advisor", "'must define 'pointcut-ref' on tag."); + return null; + } + } + + + private AbstractObjectDefinition CreateAdvisorObjectDefinition(XmlElement advisorElement, ParserContext parserContext) + { + ObjectDefinitionBuilder advisorDefinitionBuilder = + parserContext.ParserHelper.CreateRootObjectDefinitionBuilder(typeof(DefaultObjectFactoryPointcutAdvisor)); + + if (advisorElement.HasAttribute(ORDER_PROPERTY)) + { + advisorDefinitionBuilder.AddPropertyValue(ORDER_PROPERTY, advisorElement.GetAttribute(ORDER_PROPERTY)); + } + + advisorDefinitionBuilder.AddPropertyValue(ADVICE_OBJECT_NAME, advisorElement.GetAttribute(ADVICE_REF)); + + + return advisorDefinitionBuilder.ObjectDefinition; + } + + + private static void ConfigureAutoProxyCreator(ParserContext parserContext, XmlElement element) + { + AopNamespaceUtils.RegisterAutoProxyCreatorIfNecessary(parserContext, element); + + bool proxyTargetClass = + parserContext.ParserHelper.IsTrueStringValue(element.GetAttribute(PROXY_TARGET_TYPE)); + if (proxyTargetClass) + { + AopNamespaceUtils.ForceAutoProxyCreatorToUseDecoratorProxy(parserContext.Registry); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Config/spring-aop-1.1.xsd b/src/Spring/Spring.Aop/Aop/Config/spring-aop-1.1.xsd new file mode 100644 index 00000000..552faf60 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Config/spring-aop-1.1.xsd @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Aop/Aop/Config/spring-aop-1.1.xsx b/src/Spring/Spring.Aop/Aop/Config/spring-aop-1.1.xsx new file mode 100644 index 00000000..ff71343b --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Config/spring-aop-1.1.xsx @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AbstractMethodInvocation.cs b/src/Spring/Spring.Aop/Aop/Framework/AbstractMethodInvocation.cs new file mode 100644 index 00000000..ddb7dbf8 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AbstractMethodInvocation.cs @@ -0,0 +1,350 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Text; + +using AopAlliance.Intercept; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Convenience base class for + /// implementations. + /// + /// + ///

    + /// Subclasses can override the + /// + /// method to change this behavior, so this is a useful/ base class for + /// implementations. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// Rick Evans (.NET) + /// Bruno Baia (.NET) + /// $Id: AbstractMethodInvocation.cs,v 1.8 2007/07/05 20:29:21 bbaia Exp $ + [Serializable] + public abstract class AbstractMethodInvocation : IMethodInvocation + { + /// + /// The arguments (if any = may be ) to the method + /// that is to be invoked. + /// + protected object[] arguments; + + /// + /// The target object that the method is to be invoked on. + /// + protected object target; + + /// + /// The AOP proxy for the target object. + /// + protected object proxy; + + /// + /// The method invocation that is to be invoked. + /// + protected MethodInfo method; + + /// + /// The list of and + /// + /// that need dynamic checks. + /// + protected IList interceptors; + + /// + /// The declaring type of the method that is to be invoked. + /// + protected Type targetType; + + /// + /// The index from 0 of the current interceptor we're invoking. + /// + protected int currentInterceptorIndex; + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an abstract class, and as such exposes no publicly visible + /// constructors. + ///

    + ///

    + /// + /// The list can also contain any + /// s + /// that need evaluation at runtime. + /// s included in an + /// + /// must already have been found to have matched as far as was possible + /// statically. Passing an array might be about 10% faster, but + /// would complicate the code, and it would work only for static + /// pointcuts. + /// + ///

    + ///
    + /// The AOP proxy. + /// The target object. + /// the target method. + /// The target method's arguments. + /// + /// The of the target object. + /// + /// The list of interceptors that are to be applied. May be + /// . + /// + /// + /// If the is . + /// + protected AbstractMethodInvocation(object proxy, object target, + MethodInfo method, object[] arguments, Type targetType, IList interceptors) + { + #region Sanity Check + + AssertUtils.ArgumentNotNull(target, "target"); + AssertUtils.ArgumentNotNull(method, "method"); + + #endregion + + this.proxy = proxy; + this.target = target; + this.method = method; + this.targetType = targetType; + this.arguments = arguments; + this.interceptors = interceptors; + } + + /// + /// Gets the method invocation that is to be invoked. + /// + /// + ///

    + /// May or may not correspond with a method invoked on an underlying + /// implementation of that interface. + ///

    + ///
    + /// + public virtual MethodInfo Method + { + get { return method; } + } + + /// + /// Gets the static part of this joinpoint. + /// + /// + /// The proxied member's information. + /// + /// + public virtual MemberInfo StaticPart + { + get { return Method; } + } + + /// + /// Gets the proxy that this interception was made through. + /// + /// + /// The proxy that this interception was made through. + /// + public virtual object Proxy + { + get { return this.proxy; } + } + /// + /// Gets the target object for the invocation. + /// + /// + /// The target object for this method invocation. + /// + public virtual object Target + { + get { return this.target; } + } + /// + /// Gets the type of the target object. + /// + /// + /// The type of the target object. + /// + public virtual Type TargetType + { + get { return this.targetType; } + } + + /// + /// Gets and sets the arguments (if any - may be ) + /// to the method that is to be invoked. + /// + /// + /// The arguments (if any - may be ) to the + /// method that is to be invoked. + /// + /// + public virtual object[] Arguments + { + get { return this.arguments; } + set { this.arguments = value; } + } + + /// + /// The list of method interceptors. + /// + /// + ///

    + /// May be . + ///

    + ///
    + public virtual IList Interceptors + { + get { return this.interceptors; } + set { this.interceptors = value; } + } + + /// + /// Gets the target object. + /// + public virtual object This + { + get { return this.target; } + } + + /// + /// Proceeds to the next interceptor in the chain. + /// + /// + /// The return value of the method invocation. + /// + /// + /// If any of the interceptors at the joinpoint throws an exception. + /// + /// + public virtual object Proceed() + { + if (this.interceptors == null || + this.currentInterceptorIndex == this.interceptors.Count) + { + return InvokeJoinpoint(); + } + object interceptor = this.interceptors[this.currentInterceptorIndex]; + InterceptorAndDynamicMethodMatcher dynamicMatcher + = interceptor as InterceptorAndDynamicMethodMatcher; + IMethodInvocation nextInvocation = PrepareMethodInvocationForProceed(this); + if (dynamicMatcher != null) + { + // evaluate dynamic method matcher here: static part will already have + // been evaluated and found to match... + if (dynamicMatcher.MethodMatcher.Matches( + nextInvocation.Method, nextInvocation.TargetType, nextInvocation.Arguments)) + { + return dynamicMatcher.Interceptor.Invoke(nextInvocation); + } + else + { + // dynamic match failed; skip this interceptor and invoke the next in the chain... + return nextInvocation.Proceed(); + } + } + else + { + // it's an interceptor so we just invoke it: the pointcut will have + // been evaluated statically before this object was constructed... + return ((IMethodInterceptor)interceptor).Invoke(nextInvocation); + } + } + + /// + /// Retrieves a new instance + /// for the next Proceed method call. + /// + /// + /// The current instance. + /// + /// + /// The new instance to use. + /// + /// + protected abstract IMethodInvocation PrepareMethodInvocationForProceed( + IMethodInvocation invocation); + + /// + /// Invokes the joinpoint. + /// + /// + ///

    + /// Subclasses can override this to use custom invocation. + ///

    + ///
    + /// + /// The return value of the invocation of the joinpoint. + /// + /// + /// If invoking the joinpoint resulted in an exception. + /// + /// + protected abstract object InvokeJoinpoint(); + + /// + /// A that represents the current + /// invocation. + /// + /// + ///

    + /// + /// Does not invoke on the + /// target + /// object, as that too may be proxied. + /// + ///

    + ///
    + /// + /// A that represents the current invocation. + /// + public override string ToString() + { + StringBuilder buffer = new StringBuilder("Invocation: method '"); + buffer.Append(Method.Name).Append("', ").Append("arguments "); + buffer.Append(this.arguments != null ? StringUtils.ArrayToCommaDelimitedString(this.arguments) : "[none]"); + buffer.Append("; "); + if (this.target == null) + { + buffer.Append("target is null."); + } + else + { + buffer.Append("target is of Type [").Append(this.targetType.FullName).Append(']'); + } + return buffer.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/AdvisorAdapterRegistrationManager.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/AdvisorAdapterRegistrationManager.cs new file mode 100644 index 00000000..943ec624 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/AdvisorAdapterRegistrationManager.cs @@ -0,0 +1,110 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// implementation + /// that registers instances of any non-default + /// instances with the + /// + /// singleton. + /// + /// + ///

    + /// The only requirement for it to work is that it needs to be defined + /// in an application context along with any arbitrary "non-native" Spring.NET + /// instances that need + /// to be recognized by Spring.NET's AOP framework. + ///

    + ///
    + /// Dmitriy Kopylenko + /// Aleksandar Seovic (.NET) + /// $Id: AdvisorAdapterRegistrationManager.cs,v 1.5 2007/08/22 08:49:08 markpollack Exp $ + public class AdvisorAdapterRegistrationManager : IObjectPostProcessor + { + /// + /// Apply this + /// to the given new object instance before any object initialization callbacks. + /// + /// + ///

    + /// Does nothing, simply returns the supplied as is. + ///

    + ///
    + /// + /// The new object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// In case of errors. + /// + public virtual object PostProcessBeforeInitialization(object instance, string name) + { + return instance; + } + + /// + /// Apply this to the + /// given new object instance after any object initialization callbacks. + /// + /// + ///

    + /// Registers the supplied with the + /// + /// singleton if it is an + /// instance. + ///

    + ///
    + /// + /// The new object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// In case of errors. + /// + public virtual object PostProcessAfterInitialization(object instance, string objectName) + { + IAdvisorAdapter adapter = instance as IAdvisorAdapter; + if (adapter != null) + { + GlobalAdvisorAdapterRegistry.Instance.RegisterAdvisorAdapter(adapter); + } + return instance; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/AfterReturningAdviceAdapter.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/AfterReturningAdviceAdapter.cs new file mode 100644 index 00000000..ad2d38ff --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/AfterReturningAdviceAdapter.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// implementation + /// to enable to be used in the + /// Spring.NET AOP framework. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: AfterReturningAdviceAdapter.cs,v 1.5 2007/03/16 04:01:20 aseovic Exp $ + [Serializable] + internal class AfterReturningAdviceAdapter : IAdvisorAdapter + { + /// + /// Returns if the supplied + /// is an instance of the + /// interface. + /// + /// The advice to check. + /// + /// if the supplied is + /// an instance of the interface; + /// if not or if the supplied + /// is . + /// + public virtual bool SupportsAdvice(IAdvice advice) + { + return advice is IAfterReturningAdvice; + } + + /// + /// Wraps the supplied 's + /// within a + /// + /// instance. + /// + /// + /// The advisor exposing the that + /// is to be wrapped. + /// + /// + /// The supplied 's + /// wrapped within a + /// + /// instance. + /// + public virtual IInterceptor GetInterceptor(IAdvisor advisor) + { + IAfterReturningAdvice advice = (IAfterReturningAdvice) advisor.Advice; + return new AfterReturningAdviceInterceptor(advice); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/AfterReturningAdviceInterceptor.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/AfterReturningAdviceInterceptor.cs new file mode 100644 index 00000000..3288fcd9 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/AfterReturningAdviceInterceptor.cs @@ -0,0 +1,97 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Intercept; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// Interceptor to wrap an + /// instance. + /// + /// + ///

    + /// A more efficient alternative solution in cases where there is no + /// interception advice and therefore no need to create an + /// object may be + /// offered in future. + ///

    + ///

    + /// Used internally by the AOP framework: application developers should not need + /// to use this class directly. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: AfterReturningAdviceInterceptor.cs,v 1.8 2007/03/16 04:01:21 aseovic Exp $ + [Serializable] + public sealed class AfterReturningAdviceInterceptor : IMethodInterceptor + { + private IAfterReturningAdvice advice; + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The advice to be applied after a target method successfully + /// returns. + /// + /// + /// If the supplied is . + /// + public AfterReturningAdviceInterceptor(IAfterReturningAdvice advice) + { + AssertUtils.ArgumentNotNull(advice, "advice"); + this.advice = advice; + } + + /// + /// Executes interceptor after the target method successfully returns. + /// + /// + /// The method invocation that is being intercepted. + /// + /// + /// The result of the call to the + /// method of + /// the supplied ; this return value may + /// well have been intercepted by the interceptor. + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + public object Invoke(IMethodInvocation invocation) + { + object returnValue = invocation.Proceed(); + advice.AfterReturning( + returnValue, invocation.Method, invocation.Arguments, invocation.This); + return returnValue; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/BeforeAdviceAdapter.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/BeforeAdviceAdapter.cs new file mode 100644 index 00000000..4bff5394 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/BeforeAdviceAdapter.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// implementation + /// to enable to be used in the + /// Spring.NET AOP framework. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: BeforeAdviceAdapter.cs,v 1.4 2007/03/16 04:01:21 aseovic Exp $ + [Serializable] + internal class BeforeAdviceAdapter : IAdvisorAdapter + { + /// + /// Returns if the supplied + /// is an instance of the + /// interface. + /// + /// The advice to check. + /// + /// if the supplied is + /// an instance of the interface; + /// if not or if the supplied + /// is . + /// + public virtual bool SupportsAdvice(IAdvice advice) + { + return advice is IMethodBeforeAdvice; + } + + /// + /// Wraps the supplied 's + /// within a + /// + /// instance. + /// + /// + /// The advisor exposing the that + /// is to be wrapped. + /// + /// + /// The supplied 's + /// wrapped within a + /// + /// instance. + /// + public virtual IInterceptor GetInterceptor(IAdvisor advisor) + { + IMethodBeforeAdvice advice = (IMethodBeforeAdvice) advisor.Advice; + return new MethodBeforeAdviceInterceptor(advice); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/DefaultAdvisorAdapterRegistry.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/DefaultAdvisorAdapterRegistry.cs new file mode 100644 index 00000000..432d4e8b --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/DefaultAdvisorAdapterRegistry.cs @@ -0,0 +1,156 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using AopAlliance.Aop; +using AopAlliance.Intercept; + +using Spring.Aop; +using Spring.Aop.Support; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// Default implementation of the + /// + /// interface. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: DefaultAdvisorAdapterRegistry.cs,v 1.5 2006/04/09 07:18:35 markpollack Exp $ + public class DefaultAdvisorAdapterRegistry : IAdvisorAdapterRegistry + { + private IList adapters = new ArrayList(); + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This constructor will also register the well-known + /// types. + ///

    + ///
    + /// + public DefaultAdvisorAdapterRegistry() + { + // register well-known adapters... + RegisterAdvisorAdapter(new BeforeAdviceAdapter()); + RegisterAdvisorAdapter(new AfterReturningAdviceAdapter()); + RegisterAdvisorAdapter(new ThrowsAdviceAdapter()); + } + + /// + /// Returns an wrapping the supplied + /// . + /// + /// + /// The object that should be an advice, such as + /// or + /// . + /// + /// + /// An wrapping the supplied + /// . Never returns . If + /// the parameter is an + /// , it will simply be returned. + /// + /// + /// If no registered + /// can wrap + /// the supplied . + /// + public virtual IAdvisor Wrap(object advice) + { + if (advice is IAdvisor) + { + return (IAdvisor) advice; + } + if (!(advice is IAdvice)) + { + throw new UnknownAdviceTypeException(advice); + } + IAdvice adviceObject = (IAdvice) advice; + if (adviceObject is IInterceptor) + { + // so well-known it doesn't even need an adapter... + return new DefaultPointcutAdvisor(adviceObject); + } + foreach (IAdvisorAdapter adapter in this.adapters) + { + // check that it is supported... + if (adapter.SupportsAdvice(adviceObject)) + { + return new DefaultPointcutAdvisor(adviceObject); + } + } + throw new UnknownAdviceTypeException(advice); + } + + /// + /// Returns an to + /// allow the use of the supplied in an + /// interception-based framework. + /// + /// The advisor to find an interceptor for. + /// + /// An interceptor to expose this advisor's behaviour. + /// + /// + /// If the advisor type is not understood by any registered + /// . + /// + public virtual IInterceptor GetInterceptor(IAdvisor advisor) + { + IAdvice advice = advisor.Advice; + if (advice is IInterceptor) + { + return (IInterceptor) advice; + } + foreach (IAdvisorAdapter adapter in this.adapters) + { + if (adapter.SupportsAdvice(advice)) + { + return adapter.GetInterceptor(advisor); + } + } + throw new UnknownAdviceTypeException(advice); + } + + /// + /// Register the given . + /// + /// + /// An that + /// understands the particular advisor and advice types. + /// + public virtual void RegisterAdvisorAdapter(IAdvisorAdapter adapter) + { + this.adapters.Add(adapter); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/GlobalAdvisorAdapterRegistry.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/GlobalAdvisorAdapterRegistry.cs new file mode 100644 index 00000000..8bb50524 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/GlobalAdvisorAdapterRegistry.cs @@ -0,0 +1,67 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// Provides Singleton-style access to the default + /// instance. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: GlobalAdvisorAdapterRegistry.cs,v 1.5 2006/04/09 07:18:35 markpollack Exp $ + public sealed class GlobalAdvisorAdapterRegistry : DefaultAdvisorAdapterRegistry + { + private static readonly GlobalAdvisorAdapterRegistry instance + = new GlobalAdvisorAdapterRegistry(); + + /// + /// The default instance. + /// + public static GlobalAdvisorAdapterRegistry Instance + { + get { return instance; } + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This contructor is marked as to enforce the + /// Singleton pattern + ///

    + ///
    + private GlobalAdvisorAdapterRegistry() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/IAdvisorAdapter.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/IAdvisorAdapter.cs new file mode 100644 index 00000000..ffbc16e2 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/IAdvisorAdapter.cs @@ -0,0 +1,100 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// Permits the handling of new advisors and advice types as extensions to + /// the Spring AOP framework. + /// + /// + ///

    + /// Implementors can create AOP Alliance + /// s from custom advice + /// types, enabling these advice types to be used in the Spring.NET AOP + /// framework, which uses interception under the covers. + ///

    + ///

    + /// There is no need for most Spring.NET users to implement this interface; + /// do so only if you need to introduce more + /// or + /// types to Spring.NET. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IAdvisorAdapter.cs,v 1.4 2006/04/09 07:18:35 markpollack Exp $ + public interface IAdvisorAdapter + { + /// + /// Does this adapter understand the supplied ? + /// + /// + ///

    + /// Is it valid to invoke the + /// + /// method with the given advice as an argument? + ///

    + ///
    + /// + /// such as + /// . + /// + /// if this adapter understands the + /// supplied . + /// + bool SupportsAdvice(IAdvice advice); + + /// + /// Return an AOP Alliance + /// exposing the + /// behaviour of the given advice to an interception-based AOP + /// framework. + /// + /// + ///

    + /// Don't worry about any + /// contained in the supplied ; + /// the AOP framework will take care of checking the pointcut. + ///

    + ///
    + /// + /// The advice. The + /// + /// method must have previously returned on the + /// supplied . + /// + /// + /// An AOP Alliance + /// exposing the + /// behaviour of the given advice to an interception-based AOP + /// framework. + /// + IInterceptor GetInterceptor(IAdvisor advisor); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/IAdvisorAdapterRegistry.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/IAdvisorAdapterRegistry.cs new file mode 100644 index 00000000..125cd0a1 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/IAdvisorAdapterRegistry.cs @@ -0,0 +1,114 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// A registry of + /// instances. + /// + /// + ///

    + /// Implementations must also automatically register adapters for + /// types. + ///

    + /// + /// This is an SPI interface, that should not need to be implemented by any + /// Spring.NET user. + /// + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IAdvisorAdapterRegistry.cs,v 1.3 2006/04/09 07:18:35 markpollack Exp $ + public interface IAdvisorAdapterRegistry + { + /// + /// Returns an wrapping the supplied + /// . + /// + /// + /// The object that should be an advice, such as + /// or + /// . + /// + /// + /// An wrapping the supplied + /// . Never returns . If + /// the parameter is an + /// , it will simply be returned. + /// + /// + /// If no registered + /// can wrap + /// the supplied . + /// + IAdvisor Wrap(object advice); + + /// + /// Returns an to + /// allow the use of the supplied in an + /// interception-based framework. + /// + /// + ///

    + /// Don't worry about the pointcut associated with the + /// ; if it's an + /// , just return an + /// interceptor. + ///

    + ///
    + /// + /// The advisor to find an interceptor for. + /// + /// + /// An interceptor to expose this advisor's behaviour. + /// + /// + /// If the advisor type is not understood by any registered + /// . + /// + IInterceptor GetInterceptor(IAdvisor advisor); + + /// + /// Register the given . + /// + /// + ///

    + /// Note that it is not necessary to register adapters for + /// instances: these + /// must be automatically recognized by an + /// + /// implementation. + ///

    + ///
    + /// + /// An that + /// understands the particular advisor and advice types. + /// + void RegisterAdvisorAdapter(IAdvisorAdapter adapter); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/MethodBeforeAdviceInterceptor.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/MethodBeforeAdviceInterceptor.cs new file mode 100644 index 00000000..ad558e5a --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/MethodBeforeAdviceInterceptor.cs @@ -0,0 +1,93 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Intercept; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// implementation that + /// wraps instances. + /// + /// + ///

    + /// In the future Spring.NET may also offer a more efficient alternative + /// solution in cases where there is no interception advice and therefore + /// no need to create an + /// object. + ///

    + ///

    + /// Used internally by the Spring.NET AOP framework: application developers + /// should not need to use this class directly. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: MethodBeforeAdviceInterceptor.cs,v 1.5 2007/03/16 04:01:21 aseovic Exp $ + [Serializable] + internal sealed class MethodBeforeAdviceInterceptor : IMethodInterceptor + { + private IMethodBeforeAdvice advice; + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The that is to be wrapped. + /// + /// + /// If the supplied is . + /// + public MethodBeforeAdviceInterceptor(IMethodBeforeAdvice advice) + { + AssertUtils.ArgumentNotNull(advice, "advice"); + this.advice = advice; + } + + /// + /// Executes interceptor before the target method successfully returns. + /// + /// + /// The method invocation that is being intercepted. + /// + /// + /// The result of the call to the + /// method of + /// the supplied . + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + public object Invoke(IMethodInvocation invocation) + { + advice.Before(invocation.Method, invocation.Arguments, invocation.This); + return invocation.Proceed(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/ThrowsAdviceAdapter.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/ThrowsAdviceAdapter.cs new file mode 100644 index 00000000..5730836a --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/ThrowsAdviceAdapter.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// implementation + /// to enable to be used in the + /// Spring.NET AOP framework. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: ThrowsAdviceAdapter.cs,v 1.4 2007/03/16 04:01:21 aseovic Exp $ + [Serializable] + internal class ThrowsAdviceAdapter : IAdvisorAdapter + { + /// + /// Returns if the supplied + /// is an instance of the + /// interface. + /// + /// The advice to check. + /// + /// if the supplied is + /// an instance of the interface; + /// if not or if the supplied + /// is . + /// + public virtual bool SupportsAdvice(IAdvice advice) + { + return advice is IThrowsAdvice; + } + + /// + /// Wraps the supplied 's + /// within a + /// + /// instance. + /// + /// + /// The advisor exposing the that + /// is to be wrapped. + /// + /// + /// The supplied 's + /// wrapped within a + /// + /// instance. + /// + public virtual IInterceptor GetInterceptor(IAdvisor advisor) + { + return new ThrowsAdviceInterceptor(advisor.Advice); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/ThrowsAdviceInterceptor.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/ThrowsAdviceInterceptor.cs new file mode 100644 index 00000000..217a9c48 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/ThrowsAdviceInterceptor.cs @@ -0,0 +1,319 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using AopAlliance.Intercept; +using Common.Logging; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// Interceptor to wrap an after throwing advice. + /// + ///

    + /// Implementations of the interface + /// must define methods of the form... + /// + /// AfterThrowing([MethodInfo method, Object[] args, Object target], Exception subclass); + /// + /// The method name is fixed (i.e. your methods must be named + /// AfterThrowing. The first three arguments (as a whole) are + /// optional, and only useful if futher information about the joinpoint is + /// required. The return type can be anything, but is almost always + /// by convention. + ///

    + ///

    + /// Please note that the object encapsulating the throws advice does not + /// need to implement the interface. + /// Throws advice methods are discovered via reflection... the + /// interface serves merely to + /// discover objects that are to be considered as throws advice. + /// Other mechanisms for discovering throws advice such as attributes are + /// also equally valid... all that this class cares about is that a throws + /// advice object implement one or more methods with a valid throws advice + /// signature (see above, and the examples below). + ///

    + ///

    + /// This is a framework class that should not normally need to be used + /// directly by Spring.NET users. + ///

    + ///
    + /// + ///

    + /// Find below some examples of valid + /// method signatures... + ///

    + /// + /// public class GlobalExceptionHandlingAdvice : IThrowsAdvice + /// { + /// public void AfterThrowing(Exception ex) { + /// // handles absolutely any and every Exception... + /// } + /// } + /// + /// + /// public class RemotingExceptionHandlingAdvice : IThrowsAdvice + /// { + /// public void AfterThrowing(RemotingException ex) { + /// // handles any and every RemotingException (and subclasses of RemotingException)... + /// } + /// } + /// + /// + /// using System.Data; + /// + /// public class DataExceptionHandlingAdvice + /// { + /// public void AfterThrowing(ConstraintException ex) { + /// // specialised handling of ConstraintExceptions + /// } + /// + /// public void AfterThrowing(NoNullAllowedException ex) { + /// // specialised handling of NoNullAllowedExceptions + /// } + /// + /// public void AfterThrowing(DataException ex) { + /// // handles all other DataExceptions... + /// } + /// } + /// + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: ThrowsAdviceInterceptor.cs,v 1.8 2007/05/04 13:16:44 bbaia Exp $ + /// + [Serializable] + public sealed class ThrowsAdviceInterceptor : IMethodInterceptor + { + private static readonly ILog log = LogManager.GetLogger(typeof(ThrowsAdviceInterceptor)); + + private const string SpecialThrowingMethodName = "AfterThrowing"; + + private readonly object throwsAdvice; + + /// + /// The mapping of exception Types to MethodInfo handlers. + /// + private readonly IDictionary exceptionHandlers; + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// + /// + /// The throws advice to check for exception handler methods. + /// + /// + /// If the supplied is . + /// + /// + /// If no (0) handler methods were discovered on the supplied ; + /// or if more than one handler method suitable for a particular + /// type was discovered on the supplied + /// . + /// + public ThrowsAdviceInterceptor(object advice) + { + AssertUtils.ArgumentNotNull(advice, "advice"); + this.exceptionHandlers = new Hashtable(); + this.throwsAdvice = advice; + MapAllExceptionHandlingMethods(advice); + if (exceptionHandlers.Count == 0) + { + throw new ArgumentException( + "At least one handler method must be found in class [" + + advice.GetType().FullName + "]."); + } + } + + private void MapAllExceptionHandlingMethods(object advice) + { + MethodInfo[] methods = advice.GetType().GetMethods(); + foreach (MethodInfo method in methods) + { + int numParams = method.GetParameters().Length; + if (method.Name.Equals(SpecialThrowingMethodName) + && (numParams == 1 || numParams == 4)) + { + Type lastParametersType = method.GetParameters()[numParams - 1].ParameterType; + if (typeof (Exception).IsAssignableFrom(lastParametersType)) + { + #region Instrumentation + + if(log.IsDebugEnabled) + { + log.Debug("Found exception handler method: " + method); + } + + #endregion + + if(this.exceptionHandlers.Contains(lastParametersType)) + { + throw new ArgumentException( + "Throws advice handler method for the [" + + lastParametersType + "] type already exists; don't define " + + "both single and multiple argument methods for the same " + + "Exception type in the same class."); + } + this.exceptionHandlers[lastParametersType] = method; + } + } + } + } + + /// + /// Convenience property that returns the number of exception handler + /// methods managed by this interceptor. + /// + /// + /// The number of exception handler methods managed by this interceptor. + /// + public int HandlerMethodCount + { + get { return exceptionHandlers.Count; } + } + + /// + /// Executes interceptor if (and only if) the supplied + /// throws an exception that is mapped to + /// an appropriate exception handler. + /// + /// + /// The method invocation that is being intercepted. + /// + /// + /// The result of the call to the + /// method of + /// the supplied (this assumes no + /// exception was thrown by the call to the supplied . + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + /// + public object Invoke(IMethodInvocation invocation) + { + try + { + return invocation.Proceed(); + } + catch (TargetInvocationException ex) + { + // bah, this is a tad gross... + Exception realException = ex.InnerException; + LookupAndInvokeAnyHandler(realException, invocation); + throw realException; + } + catch (Exception ex) + { + LookupAndInvokeAnyHandler(ex, invocation); + throw ex; + } + } + + private void LookupAndInvokeAnyHandler(Exception ex, IMethodInvocation invocation) + { + MethodInfo handlerMethod = GetExceptionHandler(ex); + if (handlerMethod != null) + { + InvokeHandlerMethod(invocation, ex, handlerMethod); + } + } + + /// + /// Gets the exception handler (if any) that has been mapped to the + /// supplied . + /// + /// + ///

    + /// Will return if not found. + ///

    + ///
    + /// + /// The exception handler for the of the + /// supplied given exception. + /// + /// exception that was thrown + private MethodInfo GetExceptionHandler(Exception exception) + { + Type exceptionClass = exception.GetType(); + + #region Instrumentation + + if(log.IsDebugEnabled) + { + log.Debug("Trying to find handler for exception of type [" + exception.GetType().Name + "]."); + } + + #endregion + + MethodInfo handler = (MethodInfo) this.exceptionHandlers[exceptionClass]; + while (handler == null && !exceptionClass.Equals(typeof(Exception))) + { + exceptionClass = exceptionClass.BaseType; + handler = (MethodInfo) this.exceptionHandlers[exceptionClass]; + } + return handler; + } + + /// + /// Invokes handler method with appropriate number of parameters + /// + /// + /// The original method invocation that was intercepted. + /// + /// + /// The exception that triggered this interceptor. + /// + /// + /// The exception handler method to invoke. + /// + private void InvokeHandlerMethod( + IMethodInvocation invocation, Exception triggeringException, MethodInfo handlerMethod) + { + object[] handlerArgs; + if (handlerMethod.GetParameters().Length == 1) + { + handlerArgs = new object[] {triggeringException}; + } + else + { + handlerArgs = new object[] {invocation.Method, invocation.Arguments, invocation.This, triggeringException}; + } + try + { + handlerMethod.Invoke(this.throwsAdvice, handlerArgs); + } + catch (TargetInvocationException ex) + { + throw ex.InnerException; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/Adapter/UnknownAdviceTypeException.cs b/src/Spring/Spring.Aop/Aop/Framework/Adapter/UnknownAdviceTypeException.cs new file mode 100644 index 00000000..9adf977b --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/Adapter/UnknownAdviceTypeException.cs @@ -0,0 +1,105 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// Exception thrown when an attempt is made to use an unsupported + /// or + /// type. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: UnknownAdviceTypeException.cs,v 1.4 2006/04/09 07:18:35 markpollack Exp $ + [Serializable] + public class UnknownAdviceTypeException : ArgumentException + { + /// + /// Creates a new instance of the + /// class. + /// + /// The advice that caused the exception. + public UnknownAdviceTypeException(object advice) + : base("No adapter for IAdvice of type [" + + (advice != null ? advice.GetType().FullName : "null") + "].") + { + } + + /// + /// Creates a new instance of the + /// class. + /// + public UnknownAdviceTypeException() + { + } + + /// + /// Creates a new instance of the + /// class with + /// the specified message. + /// + /// + /// A message about the exception. + /// + public UnknownAdviceTypeException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the + /// class with + /// the specified message and root cause. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public UnknownAdviceTypeException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected UnknownAdviceTypeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AdvisedSupport.cs b/src/Spring/Spring.Aop/Aop/Framework/AdvisedSupport.cs new file mode 100644 index 00000000..056bfe46 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AdvisedSupport.cs @@ -0,0 +1,1563 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Reflection; +using System.Text; + +using AopAlliance.Aop; +using AopAlliance.Intercept; +using Spring.Aop; +using Spring.Aop.Support; +using Spring.Aop.Target; +using Spring.Util; +using Spring.Proxy; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Superclass for AOP proxy configuration managers. + /// + /// + ///

    + /// Instances of this class are not themselves AOP proxies, but + /// subclasses of this class are normally factories from which AOP proxy + /// instances are obtained directly. + ///

    + ///

    + /// This class frees subclasses of the housekeeping of + /// and + /// instances, but doesn't actually + /// implement proxy creation methods, the functionality for which + /// is provided by subclasses. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: AdvisedSupport.cs,v 1.36 2008/01/14 20:49:47 oakinger Exp $ + /// + [Serializable] + public class AdvisedSupport : ProxyConfig, IAdvised + { + #region Fields + + /// The list of advice. + /// + ///

    + /// If an is added, it + /// will be wrapped in an advice before being added to this list. + ///

    + ///
    + private IList _advisors = new ArrayList(); + + /// + /// Array updated on changes to the advisors list, which is easier to + /// manipulate internally + /// + private IAdvisor[] _advisorsArray = new IAdvisor[] {}; + + /// + /// List of introductions. + /// + private IList _introductions = new ArrayList(); + + /// + /// Array updated on changes to the advisors list, which is easier to + /// manipulate internally + /// + private IIntroductionAdvisor[] _introductionsArray + = new IIntroductionAdvisor[] {}; + + /// + /// Interface map specifying which object should interface methods be + /// delegated to. + /// + /// + ///

    + /// If entry value is methods should be delegated + /// to the target object. + ///

    + ///
    + private IDictionary interfaceMap = new ListDictionary(); + + /// + /// The for this instance. + /// + protected internal ITargetSource m_targetSource = EmptyTargetSource.Empty; + + /// + /// Set to when the first AOP proxy has been + /// created, meaning that we must track advice changes via the + /// OnAdviceChange() callback. + /// + private bool isActive; + + private Type proxyType; + private ConstructorInfo proxyConstructor; + + /// + /// The list of event listeners. + /// + private IList listeners = new ArrayList(); + + /// + /// The advisor chain factory. + /// + private IAdvisorChainFactory advisorChainFactory; + + #endregion + + #region Constructor(s) + + /// + /// Creates a new instance of the + /// class using the + /// default advisor chain factory. + /// + public AdvisedSupport() + { + AdvisorChainFactory = new HashtableCachingAdvisorChainFactory(); + } + + /// + /// Creates a new instance of the + /// class. + /// + /// The interfaces that are to be proxied. + /// + /// If this + /// + public AdvisedSupport(Type[] interfaces) : this() + { + if (interfaces != null) + { + foreach (Type intf in interfaces) + { + AddInterfaceInternal(intf); + } + } + } + + #endregion + + #region IAdvised implementation + + /// + /// Gets and sets the + /// + /// implementation that will be used to get the interceptor + /// chains for the advised + /// . + /// + /// + /// The + /// implementation that will be used to get the interceptor + /// chains for the advised + /// . + /// + public virtual IAdvisorChainFactory AdvisorChainFactory + { + get + { + lock(this.SyncRoot) + { + return this.advisorChainFactory; + } + } + set + { + lock(this.SyncRoot) + { + if (this.advisorChainFactory != null) + { + RemoveListener(this.advisorChainFactory); + } + this.advisorChainFactory = value; + AddListener(this.advisorChainFactory); + } + } + } + + /// + /// Returns the current used + /// by this object. + /// + /// + /// The used by this + /// object. + /// + /// + public ITargetSource TargetSource + { + get { return this.m_targetSource; } + set + { + bool initialized = !(this.m_targetSource is EmptyTargetSource); + this.m_targetSource = value; + + if (this.m_targetSource != null && !initialized && interfaceMap.Count == 0) + { + Type[] interfaces = ReflectionUtils.GetInterfaces(this.m_targetSource.TargetType); + foreach (Type intf in interfaces) + { + AddInterfaceInternal(intf); + } + } + } + } + + /// + /// Returns a boolean specifying if this + /// instance can be serialized. + /// + /// + /// true if this instance can be serialized, false otherwise. + /// + public bool IsSerializable + { + get + { + bool canBeSerialized = TargetSource.TargetType.IsSerializable; + if (canBeSerialized) + { + for (int i = 0; canBeSerialized && i < _advisorsArray.Length; i++) + { + IAdvisor advisor = _advisorsArray[i]; + canBeSerialized = advisor.GetType().IsSerializable + && advisor.Advice.GetType().IsSerializable; + } + for (int i = 0; canBeSerialized && i < _introductionsArray.Length; i++) + { + IIntroductionAdvisor advisor = _introductionsArray[i]; + canBeSerialized = advisor.GetType().IsSerializable + && advisor.Advice.GetType().IsSerializable; + } + } + + return canBeSerialized; + } + } + + /// + /// Returns the collection of interface s + /// to be (or that are being) proxied by this proxy. + /// + /// + /// The collection of interface s + /// to be (or that are being) proxied by this proxy. + /// + /// + public virtual Type[] Interfaces + { + get + { + lock(this.SyncRoot) + { + Type[] proxiedInterfaces = new Type[this.interfaceMap.Keys.Count]; + this.interfaceMap.Keys.CopyTo(proxiedInterfaces, 0); + return proxiedInterfaces; + } + } + set + { + lock(this.SyncRoot) + { + this.interfaceMap.Clear(); + for (int i = 0; i < value.Length; i++) + { + AddInterfaceInternal(value[i]); + } + InterfacesChanged(); + } + } + } + + /// + /// Returns the mapping of the proxied interface + /// s to their delegates. + /// + /// + /// The mapping of the proxied interface + /// s to their delegates. + /// + /// + public virtual IDictionary InterfaceMap + { + get + { + lock(this.SyncRoot) + { + return new Hashtable(this.interfaceMap); + } + } + } + + /// + /// Is the supplied (interface) + /// proxied? + /// + /// + /// The interface to test. + /// + /// + /// if the supplied + /// (interface) is proxied; + /// if not or the supplied + /// is . + /// + /// + public virtual bool IsInterfaceProxied(Type intf) + { + if (intf != null) + { + lock(this.SyncRoot) + { + foreach (Type proxyInterface in this.interfaceMap.Keys) + { + if (intf.IsAssignableFrom(proxyInterface)) + { + return true; + } + } + } + } + return false; + } + + /// + /// Returns the collection of + /// instances that have been applied to this proxy. + /// + /// + /// The collection of + /// instances that have been applied to this proxy. + /// + /// + public virtual IAdvisor[] Advisors + { + get + { + lock(this.SyncRoot) + { + return (IAdvisor[]) this._advisorsArray.Clone(); + } + } + } + + /// + /// Returns the collection of + /// instances that have been applied to this proxy. + /// + /// + ///

    + /// Will never return , but may return an + /// empty array (in the case where no + /// instances have been + /// applied to this proxy). + ///

    + ///
    + /// + /// The collection of + /// instances that have been applied to this proxy. + /// + /// + public virtual IIntroductionAdvisor[] Introductions + { + get + { + lock(this.SyncRoot) + { + return (IIntroductionAdvisor[]) this._introductionsArray.Clone(); + } + } + } + + /// + /// Adds the supplied to the end (or tail) + /// of the advice (interceptor) chain. + /// + /// + /// The to be added. + /// + /// + /// + public void AddAdvice(IAdvice advice) + { + //int position = this._advisors != null ? this._advisors.Count : 0; + AddAdvice(-1, advice); + } + + /// + /// Adds the supplied to the supplied + /// in the advice (interceptor) chain. + /// + /// + /// The zero (0) indexed position (from the head) at which the + /// supplied is to be inserted into the + /// advice (interceptor) chain. + /// + /// + /// The to be added. + /// + /// + /// If the supplied is ; + /// or is not an + /// reference; or if the supplied is a + /// . + /// + /// + /// + public void AddAdvice(int position, IAdvice advice) + { + if (advice is IInterceptor && !(advice is IMethodInterceptor)) + { + throw new AopConfigException( + GetType().FullName + " can only handle AOP Alliance IMethodInterceptor advice."); + } + if (advice is IIntroductionInterceptor) + { + throw + new AopConfigException( + "IIntroductionInterceptors may only be added as part of IIntroductionAdvisor."); + } + + AddAdvisor(position, new DefaultPointcutAdvisor(advice)); + } + + /// + /// Return the index (0 based) of the supplied + /// in the interceptor + /// (advice) chain for this proxy. + /// + /// + /// The to search for. + /// + /// + /// The zero (0) based index of this advisor, or -1 if the + /// supplied is not an advisor for this + /// proxy. + /// + public virtual int IndexOf(IAdvisor advisor) + { + lock(this.SyncRoot) + { + return IndexOfInternal(advisor); + } + } + + /// + /// Return the index (0 based) of the supplied + /// in the introductions + /// for this proxy. + /// + /// + /// The to search for. + /// + /// + /// The zero (0) based index of this advisor, or -1 if the + /// supplied is not an introduction advisor + /// for this proxy. + /// + public virtual int IndexOf(IIntroductionAdvisor advisor) + { + lock(this.SyncRoot) + { + return IndexOfInternal(advisor); + } + } + + /// + /// Removes the supplied the list of advisors + /// for this proxy. + /// + /// The advisor to remove. + /// + /// if advisor was found in the list of + /// for this + /// proxy and was successfully removed; if not + /// or if the supplied is . + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be removed. + /// + public bool RemoveAdvisor(IAdvisor advisor) + { + DieIfFrozen("Cannot remove advisor: config is frozen"); + bool wasRemoved = false; + if (advisor != null) + { + lock(this.SyncRoot) + { + int index = IndexOf(advisor); + if (index == -1) + { + wasRemoved = false; + } + else + { + RemoveAdvisorInternal(index); + wasRemoved = true; + } + } + } + return wasRemoved; + } + + /// + /// Removes the at the supplied + /// in the + /// list + /// from the list of + /// for this proxy. + /// + /// + /// The index of the to remove. + /// + /// + /// If this proxy configuration is frozen and the + /// at the supplied + /// cannot be removed; or if the supplied is out of + /// range. + /// + public virtual void RemoveAdvisor(int index) + { + DieIfFrozen("Cannot remove advisor: config is frozen"); + lock(this.SyncRoot) + { + RemoveAdvisorInternal(index); + } + } + + /// + /// Removes the supplied from the list + /// of . + /// + /// + /// The to remove. + /// + /// + /// if the supplied was + /// found in the list of + /// and successfully removed. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be removed. + /// + public bool RemoveAdvice(IAdvice advice) + { + lock(this.SyncRoot) + { + int index = IndexOf(advice); + if (index == -1) + { + return false; + } + else + { + RemoveAdvisorInternal(index); + return true; + } + } + } + + /// + /// Removes the supplied from the list + /// of . + /// + /// + /// The to remove. + /// + /// + /// if the supplied was + /// found in the list of + /// and successfully removed. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be removed. + /// + public bool RemoveIntroduction(IIntroductionAdvisor introduction) + { + DieIfFrozen("Cannot remove introduction: config is frozen"); + bool wasRemoved = false; + if (introduction != null) + { + lock(this.SyncRoot) + { + int index = IndexOf(introduction); + if (index == -1) + { + wasRemoved = false; + } + else + { + RemoveIntroduction(index); + wasRemoved = true; + } + } + } + return wasRemoved; + } + + /// + /// Removes the at the supplied + /// in the list of + /// for this proxy. + /// + /// The index of the advisor to remove. + /// + /// + /// If this proxy configuration is frozen and the + /// at the supplied + /// cannot be removed; or if the supplied + /// is out of range. + /// + public virtual void RemoveIntroduction(int index) + { + DieIfFrozen("Cannot remove introduction: config is frozen"); + lock(this.SyncRoot) + { + if (index < 0 || index >= _introductions.Count) + { + throw new AopConfigException( + "Introduction index " + index + " is out of bounds: Only have " + _introductions.Count + + " introductions."); + } + IIntroductionAdvisor advisor = (IIntroductionAdvisor) _introductions[index]; + // remove all interfaces introduced by the advisor... + foreach (Type intf in advisor.Interfaces) + { + RemoveInterface(intf); + } + this._introductions.RemoveAt(index); + UpdateIntroductionsArray(); + } + } + +// +// /// +// /// Removes the supplied from the list of +// /// for this +// /// proxy. +// /// +// /// +// /// The to be removed. +// /// +// /// +// /// If this proxy configuration is frozen and the +// /// cannot be added. +// /// +// public bool RemoveInterceptor(IInterceptor interceptor) +// { +// AssertFrozen("Cannot remove interceptor: config is frozen"); +// int index = IndexOf(interceptor); +// if (index == -1) +// { +// return false; +// } +// else +// { +// RemoveAdvisor(index); +// return true; +// } +// } + + /// + /// Adds the supplied to the list + /// of . + /// + /// + /// The index in the + /// list at which the supplied + /// is to be inserted. If -1, appends to the end of the list. + /// + /// + /// The to add. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be added. + /// + public virtual void AddAdvisor(int index, IAdvisor advisor) + { + DieIfFrozen("Cannot add advisor: config is frozen"); + lock(this.SyncRoot) + { + // advisor already in list (SPRNET-846) + if (_advisors.Contains(advisor)) return; + + if(index == -1) + { + this._advisors.Add(advisor); + } + else + { + this._advisors.Insert(index, advisor); + } + UpdateAdvisorsArray(); + InterceptorsChanged(); + } + } + + /// + /// Adds the supplied to the list + /// of . + /// + /// + /// The to add. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be added. + /// + public virtual void AddAdvisor(IAdvisor advisor) + { + AddAdvisor(this._advisors.Count, advisor); + } + + /// + /// Adds the advisors from the supplied + /// to the list of . + /// + /// + /// The to add advisors from. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be added. + /// + public void AddAdvisors(IAdvisors advisors) + { + foreach (IAdvisor advisor in advisors.Advisors) + { + if (advisor is IIntroductionAdvisor) + { + AddIntroduction((IIntroductionAdvisor) advisor); + } + else + { + AddAdvisor(advisor); + } + } + } + + /// + /// Adds the supplied to the list + /// of . + /// + /// + /// The index in the + /// list at which the supplied + /// is to be inserted. + /// + /// + /// The to add. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be added. + /// + public virtual void AddIntroduction(int index, IIntroductionAdvisor introductionAdvisor) + { + DieIfFrozen("Cannot add introduction: config is frozen"); + introductionAdvisor.ValidateInterfaces(); + + lock(this.SyncRoot) + { + if (index < this._introductions.Count) + { + this._introductions.RemoveAt(index); + } + this._introductions.Insert(index, introductionAdvisor); + + int intfCount = this.interfaceMap.Count; + // If the advisor passed validation we can make the change + foreach (Type intf in introductionAdvisor.Interfaces) + { + this.interfaceMap[intf] = introductionAdvisor; + } + UpdateIntroductionsArray(); + if (this.interfaceMap.Count != intfCount) + { + InterfacesChanged(); + } + } + } + + /// + /// Adds the supplied to the list + /// of . + /// + /// + /// The to add. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be added. + /// + public virtual void AddIntroduction(IIntroductionAdvisor introductionAdvisor) + { + Type introductionType = introductionAdvisor.Advice.GetType(); + lock(this.SyncRoot) + { + int pos = this._introductions.Count; + for (int i = 0; i < pos; i++) + { + IIntroductionAdvisor introduction + = (IIntroductionAdvisor) this._introductions[i]; + if (introduction.Advice.GetType() == introductionType) + { + pos = i; + } + } + AddIntroduction(pos, introductionAdvisor); + } + } + + /// + /// Replaces the that + /// exists at the supplied in the list of + /// + /// with the supplied . + /// + /// + /// The index of the + /// in the list of + /// + /// that is to be replaced. + /// + /// + /// The new (replacement) . + /// + /// + /// If the supplied is out of range. + /// + public virtual void ReplaceIntroduction(int index, IIntroductionAdvisor introduction) + { + lock(this.SyncRoot) + { + if(index < 0 || index >= _introductions.Count) + { + throw new AopConfigException( + "Introduction index " + index + " is out of bounds:" + + " there are currently " + _introductions.Count + + " introductions." ); + } + + _introductions[index] = introduction; + } + } + + /// + /// Replaces the with the + /// . + /// + /// + /// The original (old) advisor to be replaced. + /// + /// + /// The new advisor to replace the with. + /// + /// + /// if the was + /// replaced; if the was not found in the + /// advisors collection (or the is + /// , this method returns + /// and (effectively) does nothing. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be replaced. + /// + /// + public bool ReplaceAdvisor(IAdvisor oldAdvisor, IAdvisor newAdvisor) + { + DieIfFrozen("Cannot replace advisor: config is frozen."); + lock(this.SyncRoot) + { + int index = IndexOf(oldAdvisor); + if (index == -1 || newAdvisor == null) + { + return false; + } + RemoveAdvisor(index); + AddAdvisor(index, newAdvisor); + } + return true; + } + + /// + /// As will normally be passed straight through + /// to the advised target, this method returns the + /// equivalent for the AOP proxy itself. + /// + /// + /// A description of the proxy configuration. + /// + public virtual string ToProxyConfigString() + { + lock(this.SyncRoot) + { + return ToStringInternal(); + } + } + + #endregion + + #region ITargetTypeAware implementation + + /// + /// Gets the target type behind the implementing object. + /// Ttypically a proxy configuration or an actual proxy. + /// + /// The type of the target or null if not known. + public Type TargetType + { + get { return TargetSource.TargetType; } + } + + #endregion + + #region Properties + /// + /// Sets the target object that is to be advised. + /// + /// + ///

    + /// This is a convenience write-only property that allows client code + /// to set the target object... the target object will be implicitly + /// wrapped within a new + /// instance. + ///

    + ///
    + public virtual object Target + { + set { TargetSource = new SingletonTargetSource(value); } + } + + /// + /// Called by subclasses to get a value indicating whether any AOP proxies have been created yet. + /// + /// true if this AOp proxies have been created; otherwise, false. + protected bool IsActive + { + get { return isActive; } + } + + #endregion + + /// + /// Specifies the of proxies that are to be + /// created for this instance of proxy config. + /// + /// + ///

    + /// If this property value is it simply means that + /// no proxies have been created yet. Only when the first proxy is + /// created will this property value be set by the AOP framework. + ///

    + ///

    + /// Users will be able to add interceptors dynamically without proxy + /// regeneration, but if they add introductions the proxy + /// will have to be regenerated. + ///

    + ///
    + /// + /// The of proxies that are to be + /// created for this instance of proxy config; if + /// no proxies have been created yet. + /// + internal Type ProxyType + { + get { return this.proxyType; } + set { this.proxyType = value; } + } + + /// + /// Caches proxy constructor for performance reasons. + /// + internal ConstructorInfo ProxyConstructor + { + get { return this.proxyConstructor; } + set { this.proxyConstructor = value; } + } + + /// + /// Registers the supplied as a listener for + /// notifications. + /// + /// + /// The to + /// register. + /// + public virtual void AddListener(IAdvisedSupportListener listener) + { + lock(this.SyncRoot) + { + this.listeners.Add(listener); + } + } + + /// + /// Removes the supplied . + /// + /// + /// The to + /// be removed. + /// + public virtual void RemoveListener(IAdvisedSupportListener listener) + { + lock(this.SyncRoot) + { + this.listeners.Remove(listener); + } + } + + /// + /// Adds a new interface to the list of interfaces that are proxied by this proxy. + /// + /// + /// The interface to be proxied by this proxy. + /// + /// + /// If this proxy configuration is frozen + /// (); + /// + /// + /// If the supplied is . + /// + public virtual void AddInterface(Type intf) + { + DieIfFrozen("Cannot add interface: configuration is frozen."); + AssertUtils.ArgumentNotNull(intf, "intf", "Cannot proxy a null interface."); + + lock(this.SyncRoot) + { + AddInterfaceInternal(intf); + InterfacesChanged(); + } + } + + /// + /// Adds a new interface to the list of interfaces that are proxied by this proxy. + /// + /// + /// The interface to be proxied by this proxy. + /// + /// + /// Access is not synchronized. + /// + protected virtual void AddInterfaceInternal(Type intf) + { + this.interfaceMap[intf] = null; + } + + /// + /// Removes the supplied (proxied) . + /// + /// + ///

    + /// Does nothing if the supplied (proxied) + /// isn't proxied. + ///

    + ///
    + /// The interface to remove. + /// + /// if the interface was removed. + public virtual bool RemoveInterface(Type intf) + { + DieIfFrozen("Cannot remove interface: configuration is frozen."); + lock(this.SyncRoot) + { + if (intf != null && this.interfaceMap.Contains(intf)) + { + this.interfaceMap.Remove(intf); + InterfacesChanged(); + return true; + } + } + return false; + } + + /// + /// Return the index (0 based) of the supplied + /// in the interceptor + /// (advice) chain for this proxy. + /// + /// + ///

    + /// The return value of this method can be used to index into + /// the + /// list. + ///

    + ///
    + /// + /// The to search for. + /// + /// + /// The zero (0) based index of this interceptor, or -1 if the + /// supplied is not an advice for this + /// proxy. + /// + public virtual int IndexOf(IAdvice advice) + { + lock(this.SyncRoot) + { + return IndexOfInternal(advice); + } + } + + /// + /// Return the index (0 based) of the supplied + /// in the interceptor + /// (advice) chain for this proxy. + /// + /// + ///

    Acces is not synchronized

    + ///

    + /// The return value of this method can be used to index into + /// the + /// list. + ///

    + ///
    + /// + /// The to search for. + /// + /// + /// The zero (0) based index of this interceptor, or -1 if the + /// supplied is not an advice for this + /// proxy. + /// + private int IndexOfInternal(IAdvice advice) + { + if (this._advisors != null) + { + for (int i = 0; i < this._advisors.Count; ++i) + { + IAdvisor advisor = (IAdvisor) this._advisors[i]; + if (advisor.Advice == advice) + { + return i; + } + } + } + return -1; + } + + /// + /// Return the index (0 based) of the supplied + /// in the interceptor + /// (advice) chain for this proxy. + /// + /// + /// The to search for. + /// + /// + /// The zero (0) based index of this advisor, or -1 if the + /// supplied is not an advisor for this + /// proxy. + /// + /// + /// Access is not synchronized. + /// + private int IndexOfInternal(IAdvisor advisor) + { + return this._advisors != null ? this._advisors.IndexOf(advisor) : -1; + } + + /// + /// Return the index (0 based) of the supplied + /// in the introductions + /// for this proxy. + /// + /// + /// The to search for. + /// + /// + /// The zero (0) based index of this advisor, or -1 if the + /// supplied is not an introduction advisor + /// for this proxy. + /// + /// + /// Access is not synchronized + /// + private int IndexOfInternal(IIntroductionAdvisor advisor) + { + return this._introductions.IndexOf(advisor); + } + + /// + /// Removes the at the supplied + /// in the + /// list + /// from the list of + /// for this proxy. + /// + /// + /// The index of the to remove. + /// + /// + /// If this proxy configuration is frozen and the + /// at the supplied + /// cannot be removed; or if the supplied is out of + /// range. + /// + /// + /// Does not synchronize access. + /// + private void RemoveAdvisorInternal(int index) + { + if (index < 0 || index >= this._advisors.Count) + { + throw + new AopConfigException( + "Advisor index " + index + " is out of bounds: Only have " + this._advisors.Count + " advisors"); + } + this._advisors.RemoveAt(index); + this.UpdateAdvisorsArray(); + this.InterceptorsChanged(); + } + + /// + /// Is the supplied included in any + /// advisor? + /// + /// + /// The to check for the + /// inclusion of. + /// + /// + /// if the supplied + /// could be run in an invocation (this does not imply that said + /// will be run). + /// + public bool AdviceIncluded(IAdvice advice) + { + return (IndexOf(advice) != -1); + } + + /// + /// Returns a count of all of the + /// type-compatible with the supplied . + /// + /// + /// The of the + /// to check. + /// + /// + /// A count of all of the + /// type-compatible with the supplied . + /// + public int CountAdviceOfType(Type interceptorType) + { + int count = 0; + lock(this.SyncRoot) + { + foreach (IAdvisor advisor in this._advisors) + { + if (interceptorType.IsAssignableFrom(advisor.Advice.GetType())) + { + ++count; + } + } + } + return count; + } + + /// + /// Throws an if + /// this instances proxy configuration data is frozen. + /// + /// + /// The message that will be passed through to the constructor of any + /// thrown . + /// + /// + /// If the configuration for this proxy is frozen. + /// + /// + private void DieIfFrozen(string message) + { + if (IsFrozen) + { + throw new AopConfigException(message); + } + } + + /// + /// Bring the advisors array up to date with the list. + /// + private void UpdateAdvisorsArray() + { + this._advisorsArray = new IAdvisor[this._advisors.Count]; + this._advisors.CopyTo(this._advisorsArray, 0); + } + + /// + /// Bring the introductions array up to date with the list. + /// + private void UpdateIntroductionsArray() + { + this._introductionsArray = new IIntroductionAdvisor[this._introductions.Count]; + this._introductions.CopyTo(this._introductionsArray, 0); + } + + /// + /// Callback method that is invoked when the list of proxied interfaces + /// has changed. + /// + /// + ///

    + /// An example of such a change would be when a new introduction is + /// added. Resetting + /// to + /// will cause a new proxy + /// to be generated on the next call to get a proxy. + ///

    + ///
    + private void InterfacesChanged() + { + ProxyType = null; + if (this.isActive) + { + foreach (IAdvisedSupportListener listener in this.listeners) + { + listener.InterfacesChanged(this); + } + } + } + + /// + /// Callback method that is invoked when the interceptor list has changed. + /// + private void InterceptorsChanged() + { + if (this.isActive) + { + foreach (IAdvisedSupportListener listener in this.listeners) + { + listener.AdviceChanged(this); + } + } + } + + /// + /// Activates this instance. + /// + protected void Activate() + { + lock (this.SyncRoot) + { + this.isActive = true; + foreach (IAdvisedSupportListener listener in this.listeners) + { + listener.Activated(this); + } + } + } + + /// + /// Creates an AOP proxy using this instance's configuration data. + /// + /// + ///

    + /// Subclasses must not create a proxy by any other means (at least + /// without having a well thought out and cogent reason for doing so). + /// This is because the implementation of this method performs some + /// required housekeeping logic prior to creating an AOP proxy. + ///

    + ///
    + /// + protected internal virtual IAopProxy CreateAopProxy() + { + lock (this.SyncRoot) + { + if (!this.isActive) + { + Activate(); + } + return AopProxyFactory.CreateAopProxy(this); + } + } + + /// + /// Copies the configuration from the supplied other + /// into this instance. + /// + /// + ///

    + /// Useful when this instance has been created using the no-argument + /// constructor, and needs to get all of its confiuration data from + /// another (most + /// usually to have an independant copy of said configuration data). + ///

    + ///
    + /// + /// The instance + /// containing the configiration data that is to be copied into this + /// instance. + /// + protected internal virtual void CopyConfigurationFrom(AdvisedSupport other) + { + CopyFrom(other); + this.m_targetSource = other.m_targetSource; + this.proxyType = other.proxyType; + this.proxyConstructor = other.proxyConstructor; + + foreach (Type intf in other.interfaceMap.Keys) + { + this.interfaceMap[intf] = other.interfaceMap[intf]; + } + this._advisors = new ArrayList(); + foreach (IAdvisor advisor in other._advisors) + { + AddAdvisor(advisor); + } + this._introductions = new ArrayList(); + foreach (IIntroductionAdvisor advisor in other._introductions) + { + AddIntroduction(advisor); + } + } + + /// + /// A that represents the current + /// configuration. + /// + /// + /// A that represents the current + /// configuration. + /// + public override string ToString() + { + lock(this.SyncRoot) + { + return ToStringInternal(); + } + } + + /// + /// A that represents the current + /// configuration. + /// + /// + /// A that represents the current + /// configuration. + /// + private string ToStringInternal() + { + StringBuilder buffer = new StringBuilder(this.GetType().FullName + ":\n"); + buffer.Append(this.interfaceMap.Count + " interfaces=["); + this.InterfacesToString(buffer); + buffer.Append("];\n"); + buffer.Append(this._advisors.Count + " pointcuts=["); + this.AdvisorsToString(buffer); + buffer.Append("];\n"); + buffer.Append("targetSource=[" + this.m_targetSource + "];\n"); + buffer.Append("advisorChainFactory=" + this.advisorChainFactory + ";\n"); + buffer.Append(base.ToString()); + return buffer.ToString(); + } + + /// + /// Helper method that adds the names of all of the proxied interfaces + /// to the buffer of the supplied . + /// + /// + /// The to append the proxied interface + /// names to. + /// + private void InterfacesToString(StringBuilder buffer) + { + string separator = string.Empty; + foreach (Type intf in this.interfaceMap.Keys) + { + buffer.Append(separator).Append("[").Append(intf.FullName).Append("] -> "); + IIntroductionAdvisor advisor = this.interfaceMap[intf] as IIntroductionAdvisor; + if (advisor == null) + { + if (TargetSource.TargetType != null) + { + buffer.Append("target[").Append(TargetSource.TargetType.FullName).Append("]"); + } + else + { + buffer.Append("target[NOT SPECIFIED (=null)]"); + } + } + else + { + buffer.Append("introduction[").Append(advisor.Advice.GetType().FullName).Append("]"); + } + separator = ", "; + } + } + + /// + /// Helper method that adds advisor's + /// to the buffer of the supplied . + /// + /// + /// The to append the advisor details to. + /// + private void AdvisorsToString(StringBuilder buffer) + { + string separator = string.Empty; + foreach (IAdvisor advisor in this._advisors) + { + buffer.Append(separator).Append(advisor); + separator = ", "; + } + } + + /// + /// Gets all of the interfaces implemented by the + /// of the supplied + /// . + /// + /// + /// The object to get the interfaces of. + /// + /// + /// All of the interfaces implemented by the + /// of the supplied + /// . + /// + /// + /// If the supplied is . + /// + protected static Type[] GetInterfaces(object target) + { + if (target == null) + { + throw new AopConfigException("Can't proxy null object"); + } + return ReflectionUtils.GetInterfaces(target.GetType()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AdvisorChainFactoryUtils.cs b/src/Spring/Spring.Aop/Aop/Framework/AdvisorChainFactoryUtils.cs new file mode 100644 index 00000000..ecb8821f --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AdvisorChainFactoryUtils.cs @@ -0,0 +1,122 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using AopAlliance.Intercept; +using Spring.Aop.Framework.Adapter; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Utility methods for use by + /// implementations. + /// + /// + ///

    + /// Not intended to be used directly by applications. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: AdvisorChainFactoryUtils.cs,v 1.7 2006/04/09 07:18:35 markpollack Exp $ + public sealed class AdvisorChainFactoryUtils + { + /// + /// Gets the list of + /// interceptors and dynamic interception + /// advice that may apply to the supplied + /// invocation. + /// + /// The proxy configuration. + /// The object proxy. + /// + /// The method to evaluate interceptors for. + /// + /// + /// The of the target object. + /// + /// + /// A of + /// (if there's + /// a dynamic method matcher that needs evaluation at runtime). + /// + public static IList CalculateInterceptors( + IAdvised config, object proxy, MethodInfo method, Type targetType) + { + IList interceptors = new ArrayList(config.Advisors.Length); + foreach (IAdvisor advisor in config.Advisors) + { + if (advisor is IPointcutAdvisor) + { + IPointcutAdvisor pointcutAdvisor = (IPointcutAdvisor) advisor; + if (pointcutAdvisor.Pointcut.TypeFilter.Matches(targetType)) + { + IMethodInterceptor interceptor = + (IMethodInterceptor) GlobalAdvisorAdapterRegistry.Instance.GetInterceptor(advisor); + IMethodMatcher mm = pointcutAdvisor.Pointcut.MethodMatcher; + if (mm.Matches(method, targetType)) + { + if (mm.IsRuntime) + { + // Creating a new object instance in the GetInterceptor() method + // isn't a problem as we normally cache created chains... + interceptors.Add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); + } + else + { + interceptors.Add(interceptor); + } + } + } + } + } + return interceptors; + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly visible + /// constructors. + ///

    + ///
    + private AdvisorChainFactoryUtils() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AopConfigException.cs b/src/Spring/Spring.Aop/Aop/Framework/AopConfigException.cs new file mode 100644 index 00000000..e471ecc4 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AopConfigException.cs @@ -0,0 +1,93 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Thrown in response to the misconfiguration of an AOP proxy. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: AopConfigException.cs,v 1.4 2006/04/09 07:18:35 markpollack Exp $ + [Serializable] + public class AopConfigException : ApplicationException + { + /// + /// Creates a new instance of the + /// class. + /// + public AopConfigException () + { + } + + /// + /// Creates a new instance of the + /// class with + /// the specified message. + /// + /// + /// A message about the exception. + /// + public AopConfigException (string message) : base(message) + { + } + + /// + /// Creates a new instance of the + /// class with + /// the specified message and root cause. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public AopConfigException (string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected AopConfigException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AopContext.cs b/src/Spring/Spring.Aop/Aop/Framework/AopContext.cs new file mode 100644 index 00000000..42e1e534 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AopContext.cs @@ -0,0 +1,172 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using Spring.Threading; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// This class contains various methods used to + /// obtain information about the current AOP invocation. + /// + /// + ///

    + /// The + /// property is + /// usable if the AOP framework is configured to expose the current proxy + /// (not the default)... it returns the AOP proxy in use. Target objects or + /// advice can use this to make advised calls. They can also use it to find + /// advice configuration. + ///

    + /// + /// The AOP framework does not expose proxies by default, as there is a + /// performance cost in doing so. + /// + ///

    + /// The functionality in this class might be used by a target object that + /// needed access to resources on the invocation. However, this approach + /// should not be used when there is a reasonable alternative, as it makes + /// application code dependent on usage under AOP and the Spring.NET AOP + /// framework. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: AopContext.cs,v 1.7 2006/09/15 21:25:16 markpollack Exp $ + public sealed class AopContext + { + private const string CURRENTPROXY_SLOTNAME = "AopContext.CurrentProxySlotName"; + + /// + /// The AOP proxy associated with this thread. + /// + /// + ///

    + /// Will be unless the + /// property + /// on the controlling proxy has been set to . + ///

    + ///

    + /// The default value for the + /// property + /// is , for performance reasons. + ///

    + ///
    + private static Stack ProxyStack + { + get + { + Stack proxyStack = LogicalThreadContext.GetData(CURRENTPROXY_SLOTNAME) as Stack; + if (proxyStack == null) + { + proxyStack = new Stack(); + LogicalThreadContext.SetData(CURRENTPROXY_SLOTNAME, proxyStack); + } + return proxyStack; + } + } + + /// + /// Gets the current AOP proxy. + /// + /// + /// If the proxy stack is empty. + /// + public static object CurrentProxy + { + get + { + if (ProxyStack.Count == 0) + { + throw new AopConfigException( + "Cannot find proxy: Set the 'ExposeProxy' property " + + "to 'true' on IAdvised to make it available."); + } + return ProxyStack.Peek(); + } + } + + /// + /// Sets the current proxy by pushing it to the proxy stack. + /// + /// + ///

    + /// This method is for internal use only, and should never be called by + /// client code. + ///

    + ///
    + /// + /// The proxy to put on top of the proxy stack. + /// + public static void PushProxy(object proxy) + { + ProxyStack.Push(proxy); + } + + /// + /// Removes the current proxy from the proxy stack, making the previous + /// proxy (if any) the current proxy. + /// + /// + ///

    + /// This method is for internal use only, and should never be called by + /// client code. + ///

    + ///
    + /// + /// If the proxy stack is empty. + /// + public static void PopProxy() + { + if (ProxyStack.Count == 0) + { + throw new AopConfigException( + "Proxy stack empty. Always call 'PushProxy' before 'PopProxy'."); + } + ProxyStack.Pop(); + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private AopContext() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AopUtils.cs b/src/Spring/Spring.Aop/Aop/Framework/AopUtils.cs new file mode 100644 index 00000000..02d83063 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AopUtils.cs @@ -0,0 +1,272 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Collections; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Utility methods used by the AOP framework. + /// + /// + ///

    + /// Not intended to be used directly by applications. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Aleksandar Seovic (.NET) + /// $Id: AopUtils.cs,v 1.4 2007/10/10 18:07:38 markpollack Exp $ + public sealed class AopUtils + { + + // This is a leaky abstraction as we have hardcoded known IAopProxyFactory implementations. + private const string COMPOSITION_PROXY_TYPE_NAME = "CompositionAopProxy"; + + private const string DECORATOR_PROXY_TYPE_NAME = "DecoratorAopProxy"; + + /// + /// Is the supplied an AOP proxy? + /// + /// + /// Return whether the given object is either + /// a composition-based proxy or a decorator-based proxy. + /// + /// The instance to be checked. + /// + /// if the supplied is + /// an AOP proxy. + /// + public static bool IsAopProxy(object instance) + { + return IsCompositionAopProxy(instance) || IsDecoratorAopProxy(instance); + } + + /// + /// Is the supplied a composition-based AOP proxy? + /// + /// The instance to be checked. + /// + /// if the supplied is + /// an composition-based AOP proxy. + /// + public static bool IsCompositionAopProxy(Object instance) + { + return ((instance != null) && instance.GetType().FullName.StartsWith(COMPOSITION_PROXY_TYPE_NAME)); + } + + /// + /// Is the supplied a decorator-based AOP proxy? + /// + /// The instance to be checked. + /// + /// if the supplied is + /// an decorator-based AOP proxy. + /// + public static bool IsDecoratorAopProxy(Object instance) + { + return ((instance != null) && instance.GetType().FullName.StartsWith(DECORATOR_PROXY_TYPE_NAME)); + } + + /// + /// Gets all of the interfaces that the of the + /// supplied implements. + /// + /// + ///

    + /// This includes interfaces implemented by any superclasses. + ///

    + ///
    + /// + /// The object to analyse for interfaces. + /// + /// + /// All of the interfaces that the of the + /// supplied implements; or an empty + /// array if the supplied is + /// . + /// + public static Type[] GetAllInterfaces(object instance) + { + if (instance != null) + { + ISet interfaces = new HybridSet(); + Type type = instance.GetType(); + do + { + Type[] ifcs = type.GetInterfaces(); + foreach (Type ifc in ifcs) + { + interfaces.Add(ifc); + } + type = type.BaseType; + } while (type != null); + if (interfaces.Count > 0) + { + Type[] types = new Type[interfaces.Count]; + interfaces.CopyTo(types, 0); + return types; + } + } + return Type.EmptyTypes; + } + + /// + /// Can the supplied apply at all on the + /// supplied ? + /// + /// + ///

    + /// This is an important test as it can be used to optimize out a + /// pointcut for a class. + ///

    + ///

    + /// Invoking this method with a that is + /// an interface type will always yield a + /// return value. + ///

    + ///
    + /// The pointcut being tested. + /// The class being tested. + /// + /// The interfaces being proxied. If , all + /// methods on a class may be proxied. + /// + /// + /// if the pointcut can apply on any method. + /// + public static bool CanApply( + IPointcut pointcut, Type targetType, Type[] proxyInterfaces) + { + if (!pointcut.TypeFilter.Matches(targetType)) + { + return false; + } + + // It may apply to the class + // Check whether it can apply on any method + // Checks public methods, including inherited methods + MethodInfo[] methods = targetType.GetMethods(); + for (int i = 0; i < methods.Length; ++i) + { + MethodInfo m = methods[i]; + // If we're looking only at interfaces and this method + // isn't on any of them, skip it + if (proxyInterfaces != null + && !ReflectionUtils.MethodIsOnOneOfTheseInterfaces(m, proxyInterfaces)) + { + continue; + } + if (pointcut.MethodMatcher.Matches(m, targetType)) + { + return true; + } + } + return false; + } + + /// + /// Can the supplied apply at all on the + /// supplied ? + /// + /// + ///

    + /// This is an important test as it can be used to optimize out an + /// advisor for a class. + ///

    + ///
    + /// The advisor to check. + /// The class being tested. + /// + /// The interfaces being proxied. If , all + /// methods on a class may be proxied. + /// + /// + /// if the advisor can apply on any method. + /// + public static bool CanApply( + IAdvisor advisor, Type targetType, Type[] proxyInterfaces) + { + if (advisor is IIntroductionAdvisor) + { + return ((IIntroductionAdvisor) advisor).TypeFilter.Matches(targetType); + } + else if (advisor is IPointcutAdvisor) + { + IPointcutAdvisor pca = (IPointcutAdvisor) advisor; + return CanApply(pca.Pointcut, targetType, proxyInterfaces); + } + // no pointcut specified so assume it applies... + return true; + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + private AopUtils() + { + } + + // CLOVER:ON + + #endregion + + /// + /// Gets the type of the target. + /// + /// The candidate. + /// + public static Type GetTargetType(object candidate) + { + AssertUtils.ArgumentNotNull(candidate,"candidate", "Candidate object must not be null"); + if (candidate is ITargetSource) + { + return ((ITargetSource) candidate).TargetType; + } + if (candidate is IAdvised) + { + return ((IAdvised) candidate).TargetSource.TargetType; + } + if (IsDecoratorAopProxy(candidate)) + { + return candidate.GetType().BaseType; + } + return candidate.GetType(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractAdvisorAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractAdvisorAutoProxyCreator.cs new file mode 100644 index 00000000..32c77446 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractAdvisorAutoProxyCreator.cs @@ -0,0 +1,166 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Aop.Framework.DynamicProxy; +using Spring.Core; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// Abstract IOBjectPostProcessor implementation that creates AOP proxies. + /// This class is completely generic; it contains no special code to handle + /// any particular aspects, such as pooling aspects. + /// + /// + ///

    Subclasses must implement the abstract findCandidateAdvisors() method + /// to return a list of Advisors applying to any object. Subclasses can also + /// override the inherited shouldSkip() method to exclude certain objects + /// from autoproxying, but they must be careful to invoke the shouldSkip() + /// method of this class, which tries to avoid circular reference problems + /// and infinite loops.

    + ///

    Advisors or advices requiring ordering should implement the Ordered interface. + /// This class sorts advisors by Ordered order value. Advisors that don't implement + /// the Ordered interface will be considered to be unordered, and will appear + /// at the end of the advisor chain in undefined order.

    + ///
    + /// + /// Rod Johnson + /// Adhari C Mahendra (.NET) + /// $Id: AbstractAdvisorAutoProxyCreator.cs,v 1.5 2007/08/22 08:49:08 markpollack Exp $ + public abstract class AbstractAdvisorAutoProxyCreator : AbstractAutoProxyCreator + { + /// + /// We override this method to ensure that all candidate advisors are materialized + /// under a stack trace including this object. Otherwise, the dependencies won't + /// be apparent to the circular-reference prevention strategy in AbstractObjectFactory. + /// + public override IObjectFactory ObjectFactory + { + //TODO investigate override... + set + { + base.ObjectFactory = value; + if (!(value is IConfigurableListableObjectFactory)) + { + throw new InvalidOperationException( + "Can not use AdvisorAutoProxyCreator without a ConfigurableListableObjectFactory"); + } + } + get { return base.ObjectFactory; } + } + + /// + /// Return whether the given object is to be proxied, what additional + /// advices (e.g. AOP Alliance interceptors) and advisors to apply. + /// + /// the new object instance + /// the name of the object + /// targetSource returned by TargetSource property: + /// may be ignored. Will be null unless a custom target source is in use. + /// + /// an array of additional interceptors for the particular object; + /// or an empty array if no additional interceptors but just the common ones; + /// or null if no proxy at all, not even with the common interceptors. + /// + /// + ///

    The previous name of this method was "GetInterceptorAndAdvisorForObject". + /// It has been renamed in the course of general terminology clarification + /// in Spring 1.1. An AOP Alliance Interceptor is just a special form of + /// Advice, so the generic Advice term is preferred now.

    + ///

    The third parameter, customTargetSource, is new in Spring 1.1; + /// add it to existing implementations of this method.

    + ///
    + protected override object[] GetAdvicesAndAdvisorsForObject(Type objType, string name, ITargetSource customTargetSource) + { + IList advisors = FindEligibleAdvisors(objType); + if (advisors.Count == 0) + { + return DO_NOT_PROXY; + } + advisors = SortAdvisors(advisors); + if (advisors is ArrayList) + return ((ArrayList) advisors).ToArray(); + else + { + return advisors as object[]; + } + } + + /// + /// Find all eligible advices and for autoproxying this class. + /// + /// + /// the empty list, not null, if there are no pointcuts or interceptors + protected IList FindEligibleAdvisors(Type type) + { + IList candidateAdvisors = FindCandidateAdvisors(); + IList eligibleAdvisors = new ArrayList(); + for (int i = 0; i < candidateAdvisors.Count; i++) + { + IAdvisor candidate = (IAdvisor) candidateAdvisors[i]; + if (AopUtils.CanApply(candidate, type, null)) + { + eligibleAdvisors.Add(candidate); + if (logger.IsInfoEnabled) + { + logger.Info(string.Format("Candidate advisor [{0}] accepted for type [{1}]", candidate, type.ToString())); + } + } + else + { + if (logger.IsInfoEnabled) + { + logger.Info(string.Format("Candidate advisor [{0}] rejected for type [{1}]", candidate, type.ToString())); + } + } + } + return eligibleAdvisors; + } + + + /// + /// Sorts the advisors. + /// + /// The advisors. + /// + protected IList SortAdvisors(IList advisors) + { + if (advisors is ArrayList) + ((ArrayList) advisors).Sort(new OrderComparator()); + else if (advisors is Array) + Array.Sort((Array) advisors, new OrderComparator()); + return advisors; + } + + /// + /// Find all candidate advisors to use in auto-proxying. + /// + /// list of Advisors + protected abstract IList FindCandidateAdvisors(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractAutoProxyCreator.cs new file mode 100644 index 00000000..1334a10b --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractAutoProxyCreator.cs @@ -0,0 +1,647 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Runtime.Remoting; +using AopAlliance.Aop; +using Common.Logging; +using Spring.Aop.Framework.Adapter; +using Spring.Aop.Target; +using Spring.Collections; +using Spring.Core; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// ObjectPostProcessor implementation that wraps a group of objects with AOP proxies + /// that delegate to the given interceptors before invoking the object itself. + /// + /// + ///

    This class distinguishes between "common" interceptors: shared for all proxies it + /// creates, and "specific" interceptors: unique per object instance. There need not + /// be any common interceptors. If there are, they are set using the interceptorNames + /// property. As with ProxyFactoryObject, interceptors names in the current factory + /// are used rather than object references to allow correct handling of prototype + /// advisors and interceptors: for example, to support stateful mixins. + /// Any advice type is supported for "interceptorNames" entries.

    + ///

    Such autoproxying is particularly useful if there's a large number of objects that need + /// to be wrapped with similar proxies, i.e. delegating to the same interceptors. + /// Instead of x repetitive proxy definitions for x target objects, you can register + /// one single such post processor with the object factory to achieve the same effect.

    + ///

    Subclasses can apply any strategy to decide if a object is to be proxied, + /// e.g. by type, by name, by definition details, etc. They can also return + /// additional interceptors that should just be applied to the specific object + /// instance. The default concrete implementation is ObjectNameAutoProxyCreator, + /// identifying the objects to be proxied via a list of object names.

    + ///

    Any number of TargetSourceCreator implementations can be used with any subclass, + /// to create a custom target source - for example, to pool prototype objects. + /// Autoproxying will occur even if there is no advice if a TargetSourceCreator specifies + /// a custom TargetSource. If there are no TargetSourceCreators set, or if none matches, + /// a SingletonTargetSource will be used by default to wrap the object to be autoproxied.

    + ///
    + /// Juergen Hoeller + /// Rod Johnson + /// Adhari C Mahendra (.NET) + /// + /// + /// $Id: AbstractAutoProxyCreator.cs,v 1.15 2008/03/03 09:28:49 bbaia Exp $ + public abstract class AbstractAutoProxyCreator : ProxyConfig, IInstantiationAwareObjectPostProcessor, IObjectFactoryAware, IOrdered + { + #region Protected Fields + + /// + /// The logger for this class hierarchy. + /// + protected readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Convenience constant for subclasses: Return value for "do not proxy". + /// + protected static readonly object[] DO_NOT_PROXY = null; + + /// + /// Convenience constant for subclasses: Return value for + /// "proxy without additional interceptors, just the common ones". + /// + protected static readonly object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new object[0]; + + #endregion + + #region Private Fields + + /// + /// Default value is same as non-ordered + /// + private int order = int.MaxValue; + + /// + /// Default is global AdvisorAdapterRegistry + /// + private IAdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.Instance; + + + /// + /// + /// + private bool freezeProxy = false; + + /// + /// Names of common interceptors. + /// We must use object name rather than object references + /// to handle prototype advisors/interceptors. + /// Default is the empty array: no common interceptors. + /// + private string[] interceptorNames = new string[0]; + + private bool applyCommonInterceptorsFirst = true; + private IList customTargetSourceCreators = new ArrayList(); + private IObjectFactory owningObjectFactory; + + /// + /// Set of object type + name strings, referring to all objects that this auto-proxy + /// creator created a custom TargetSource for. Used to detect own pre-built proxies + /// (from "PostProcessBeforeInstantiation") in the "PostProcessAfterInitialization" method. + /// + private ISet targetSourcedObjects = new SynchronizedSet(new HashedSet()); + + private ISet advisedObjects = new SynchronizedSet(new HashedSet()); + + private ISet nonAdvisedObjects = new SynchronizedSet(new HashedSet()); + + #endregion + + #region Properties + + /// + /// Sets the AdvisorAdapterRegistry to use. + /// + /// + /// Default is the global AdvisorAdapterRegistry. + /// + public IAdvisorAdapterRegistry AdvisorAdapterRegistry + { + set { advisorAdapterRegistry = value; } + } + + /// + /// Sets custom TargetSourceCreators to be applied in this order. + /// + /// + /// + /// If the list is empty, or they all return null, a SingletonTargetSource + /// will be created. + /// + /// + /// TargetSourceCreators can only be invoked if this post processor is used + /// in a IObjectFactory, and its ObjectFactoryAware callback is used. + /// + /// + public IList CustomTargetSourceCreators + { + set { customTargetSourceCreators = value; } + } + + /// + /// Sets the common interceptors, a list of , + /// and introduction object names. + /// + /// + /// + /// If this property isn't set, there will be zero common interceptors. + /// This is perfectly valid, if "specific" interceptors such as + /// matching Advisors are all we want. + /// + /// + /// + /// The list of , + /// and introduction object names. + /// + /// + /// + public string[] InterceptorNames + { + set { interceptorNames = value; } + } + + /// + /// Sets whether the common interceptors should be applied before + /// object-specific ones. + /// + /// + /// Default is true; else, object-specific interceptors will get applied first. + /// + public bool ApplyCommonInterceptorsFirst + { + set { applyCommonInterceptorsFirst = value; } + } + + /// + /// Set whether or not the proxy should be frozen, preventing advice + /// from being added to it once it is created. + /// + /// + ///

    Overridden from the super class to prevent the proxy configuration + /// from being frozen before the proxy is created. The default is not frozen. + ///

    + ///
    + public override bool IsFrozen + { + get { return freezeProxy; } + set { this.freezeProxy = value; } + } + + #endregion + + #region IObjectPostProcessor Members + + /// + /// Create a proxy with the configured interceptors if the object is + /// identified as one to proxy by the subclass. + /// + public virtual object PostProcessAfterInitialization(object obj, string objectName) + { + if (targetSourcedObjects.Contains(objectName)) + { + return obj; + } + + object cacheKey = GetCacheKey(obj.GetType(), objectName); + if (nonAdvisedObjects.Contains(cacheKey)) + { + return obj; + } + if (IsInfrastructureType(obj.GetType(), objectName) || ShouldSkip(obj.GetType(), objectName)) + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format("Did not attempt to autoproxy infrastructure type [{0}]", obj.GetType().ToString())); + } + + #endregion + + nonAdvisedObjects.Add(cacheKey); + return obj; + } + + //ITargetSource targetSource = GetCustomTargetSource(obj.GetType(), objectName); + object[] specificInterceptors; + if (RemotingServices.IsTransparentProxy(obj)) + { + specificInterceptors = GetAdvicesAndAdvisorsForObject(ObjectFactory.GetType(objectName), objectName, null); + } + else + { + specificInterceptors = GetAdvicesAndAdvisorsForObject(obj.GetType(), objectName, null); + } + + + // proxy if we have advice or if a TargetSourceCreator wants to do some + // fancy stuff such as pooling + if (specificInterceptors != DO_NOT_PROXY) + { + advisedObjects.Add(cacheKey); + return CreateProxy(obj.GetType(), objectName, specificInterceptors, new SingletonTargetSource(obj)); + } + nonAdvisedObjects.Add(cacheKey); + return obj; + } + + /// + /// No-op for before initialization. + /// + /// The obj. + /// The name. + /// + public virtual object PostProcessBeforeInitialization(object obj, string name) + { + return obj; + } + + #endregion + + #region IObjectFactoryAware Members + + /// + /// Callback that supplies the owning factory to an object instance. + /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + /// + ///

    + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

    + ///
    + /// + /// In case of initialization errors. + /// + public virtual IObjectFactory ObjectFactory + { + get { return owningObjectFactory; } + set { owningObjectFactory = value; } + } + + #endregion + + #region IOrdered Members + + /// + /// Propery Order + /// + /// + /// Ordering which will apply to this class's implementation + /// of Ordered, used when applying multiple ObjectPostProcessors. + /// Default value is int.MaxValue, meaning that it's non-ordered. + /// + public virtual int Order + { + get { return order; } + set { order = value; } + } + + #endregion + + #region Protected Methods + + /// + /// Subclasses should override this method to return true if this + /// object should not be considered for autoproxying by this post processor. + /// Sometimes we need to be able to avoid this happening if it will lead to + /// a circular reference. This implementation returns true. + /// + /// the type of the object + /// the name of the object + /// if remarkable to skip + protected virtual bool ShouldSkip(Type objectType, string objectName) + { + return false; + } + + /// + /// Subclasses may choose to implement this: for example, + /// to change the interfaces exposed + /// + /// + /// ProxyFactory that will be used to create the proxy immediably after this method returns. + /// + protected virtual void CustomizeProxyFactory(ProxyFactory pf) + { + // This implementation does nothing + } + + /// + /// Determines whether the object is an infrastructure type, + /// IAdvisor, IAdvice, IAdvisors or AbstractAutoProxyCreator + /// + /// The object type to compare + /// The name of the object + /// + /// true if [is infrastructure type] [the specified obj]; otherwise, false. + /// + protected virtual bool IsInfrastructureType(Type type, String name) + { + return typeof (IAdvisor).IsAssignableFrom(type) + || typeof (IAdvice).IsAssignableFrom(type) + || typeof (IAdvisors).IsAssignableFrom(type) + || typeof (AbstractAutoProxyCreator).IsAssignableFrom(type); + } + + + /// + /// Create a target source for object instances. Uses any + /// TargetSourceCreators if set. Returns null if no Custom TargetSource + /// should be used. + /// This implementation uses the customTargetSourceCreators property. + /// Subclasses can override this method to use a different mechanism. + /// + /// the type of the object to create a TargetSource for + /// the name of the object + /// a TargetSource for this object + protected virtual ITargetSource GetCustomTargetSource(Type objectType, string name) + { + // We can't create fancy target sources for directly registered singletons. + if (customTargetSourceCreators != null && + owningObjectFactory != null && owningObjectFactory.ContainsObject(name)) + { + for (int i = 0; i < customTargetSourceCreators.Count; i++) + { + ITargetSourceCreator tsc = (ITargetSourceCreator) customTargetSourceCreators[i]; + ITargetSource ts = tsc.GetTargetSource(objectType, name, owningObjectFactory); + if (ts != null) + { + // found a match + if (logger.IsInfoEnabled) + { + logger.Info(string.Format("TargetSourceCreator [{0} found custom TargetSource for object with objectName '{1}'", tsc, name)); + } + return ts; + } + } + } + + // no custom TargetSource found + return null; + } + + /// + /// Return whether the given object is to be proxied, what additional + /// advices (e.g. AOP Alliance interceptors) and advisors to apply. + /// + /// + ///

    The previous name of this method was "GetInterceptorAndAdvisorForObject". + /// It has been renamed in the course of general terminology clarification + /// in Spring 1.1. An AOP Alliance Interceptor is just a special form of + /// Advice, so the generic Advice term is preferred now.

    + ///

    The third parameter, customTargetSource, is new in Spring 1.1; + /// add it to existing implementations of this method.

    + ///
    + /// the new object instance + /// the name of the object + /// targetSource returned by TargetSource property: + /// may be ignored. Will be null unless a custom target source is in use. + /// an array of additional interceptors for the particular object; + /// or an empty array if no additional interceptors but just the common ones; + /// or null if no proxy at all, not even with the common interceptors. + protected abstract object[] GetAdvicesAndAdvisorsForObject(Type objType, string name, ITargetSource customTargetSource); + + /// + /// Create an AOP proxy for the given object. + /// + /// Type of the object. + /// The name of the object. + /// The set of interceptors that is specific to this + /// object (may be empty but not null) + /// The target source for the proxy, already pre-configured to access the object. + /// The AOP Proxy for the object. + protected virtual object CreateProxy(Type objectType, string objectName, object[] specificInterceptors, ITargetSource targetSource) + { + ProxyFactory proxyFactory = CreateProxyFactory(); + // copy our properties (proxyTargetClass) inherited from ProxyConfig + proxyFactory.CopyFrom(this); + + object target = targetSource.GetTarget(); + + + if(!ProxyTargetType) + { + // Must allow for introductions; can't just set interfaces to + // the target's interfaces only. + Type[] targetInterfaceTypes = AopUtils.GetAllInterfaces(target); + foreach (Type interfaceType in targetInterfaceTypes) + { + proxyFactory.AddInterface(interfaceType); + } + } + + + IAdvisor[] advisors = BuildAdvisors(objectName, specificInterceptors); + + foreach (IAdvisor advisor in advisors) + { + if (advisor is IIntroductionAdvisor) + { + proxyFactory.AddIntroduction((IIntroductionAdvisor)advisor); + } + else + { + proxyFactory.AddAdvisor(advisor); + } + } + proxyFactory.TargetSource = targetSource; + CustomizeProxyFactory(proxyFactory); + + proxyFactory.IsFrozen = freezeProxy; + return proxyFactory.GetProxy(); + } + + /// + /// Obtain a new proxy factory instance to be used for proxying a particular object + /// + /// A proxy factory instance for proxying a particular object + protected virtual ProxyFactory CreateProxyFactory() + { + return new ProxyFactory(); + } + + /// + /// Determines the advisors for the given object, including the specific interceptors + /// as well as the common interceptor, all adapted to the Advisor interface. + /// + /// The name of the object. + /// The set of interceptors that is specific to this + /// object (may be empty, but not null) + /// The list of Advisors for the given object + protected virtual IAdvisor[] BuildAdvisors(string objectName, object[] specificInterceptors) + { + // handle prototypes correctly + IAdvisor[] commonInterceptors = ResolveInterceptorNames(); + + ArrayList allInterceptors = new ArrayList(); + if (specificInterceptors != null) + { + allInterceptors.AddRange(specificInterceptors); + if (commonInterceptors != null) + { + if (applyCommonInterceptorsFirst) + { + allInterceptors.InsertRange(0, commonInterceptors); + } + else + { + allInterceptors.AddRange(commonInterceptors); + } + } + } + if (logger.IsInfoEnabled) + { + int nrOfCommonInterceptors = commonInterceptors != null ? commonInterceptors.Length : 0; + int nrOfSpecificInterceptors = specificInterceptors != null ? specificInterceptors.Length : 0; + logger.Info(string.Format("Creating implicit proxy for object '{0}' with {1} common interceptors and {2} specific interceptors", objectName, nrOfCommonInterceptors, nrOfSpecificInterceptors)); + } + + + IAdvisor[] advisors = new IAdvisor[allInterceptors.Count]; + for (int i = 0; i < allInterceptors.Count; i++) + { + advisors[i] = advisorAdapterRegistry.Wrap(allInterceptors[i]); + } + return advisors; + } + + /// + /// Build a cache key for the given object type and object name + /// + /// The object type. + /// The object name. + /// The cache key for the given type and name + protected virtual object GetCacheKey(Type objectType, string objectName) + { + return objectType.FullName + "_" + objectName; + } + + + #endregion + + #region Private Methods + + private IAdvisor[] ResolveInterceptorNames() + { + ArrayList advisors = new ArrayList(); + foreach(string name in interceptorNames) + { + object next = owningObjectFactory.GetObject(name); + if (next is IAdvisors) + { + advisors.AddRange(((IAdvisors)next).Advisors); + } + else + { + advisors.Add(advisorAdapterRegistry.Wrap(next)); + } + } + return (IAdvisor[])advisors.ToArray(typeof(IAdvisor)); + } + + #endregion + + #region IInstantiationAwareObjectPostProcessor Members + + /// + /// Create the proxy if have a custom TargetSource + /// + /// The object type + /// The object name + /// null if not creating a proxy, otherwise return the proxy. + public object PostProcessBeforeInstantiation(Type objectType, string objectName) + { + object cacheKey = GetCacheKey(objectType, objectName); + if (!targetSourcedObjects.Contains(cacheKey)) + { + if (advisedObjects.Contains(cacheKey) || nonAdvisedObjects.Contains(cacheKey)) + { + return null; + } + if (IsInfrastructureType(objectType, objectName) || ShouldSkip(objectType, objectName)) + { + nonAdvisedObjects.Add(cacheKey); + return null; + } + } + // Create proxy here if we have a custom TargetSource. + // Suppresses unnecessary default instantiation of the target object: + // The TargetSource will handle target instances in a custom fashion. + ITargetSource targetSource = GetCustomTargetSource(objectType, objectName); + if (targetSource != null) + { + targetSourcedObjects.Add(objectName); + object[] specificInterceptors = GetAdvicesAndAdvisorsForObject(objectType, objectName, targetSource); + return CreateProxy(objectType, objectName, specificInterceptors, targetSource); + } + return null; + } + + + /// + /// Default behavior, return true and continue processing. + /// + /// The object instance + /// The object name. + /// true + public bool PostProcessAfterInstantiation(object objectInstance, string objectName) + { + return true; + } + + /// + /// Default behavior, return passed in PropertyValues + /// + /// The property values that the factory is about to apply (never null). + /// he relevant property infos for the target object (with ignored + /// dependency types - which the factory handles specifically - already filtered out) + /// The object instance created, but whose properties have not yet + /// been set. + /// Name of the object. + /// The passed in PropertyValues + public IPropertyValues PostProcessPropertyValues(IPropertyValues pvs, PropertyInfo[] pis, object objectInstance, + string objectName) + { + return pvs; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/DefaultAdvisorAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/DefaultAdvisorAutoProxyCreator.cs new file mode 100644 index 00000000..dfc44061 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/DefaultAdvisorAutoProxyCreator.cs @@ -0,0 +1,198 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// ObjectPostProcessor implementation that creates AOP proxies based on all candidate + /// Advisors in the current IObjectFactory. This class is completely generic; it contains + /// no special code to handle any particular aspects, such as pooling aspects. + /// + /// Rod Johnson + /// Adhari C Mahendra (.NET) + /// $Id: DefaultAdvisorAutoProxyCreator.cs,v 1.9 2007/10/08 22:04:51 markpollack Exp $ + public class DefaultAdvisorAutoProxyCreator : AbstractAdvisorAutoProxyCreator, IObjectNameAware, IInitializingObject + { + + /// + /// Separator between prefix and remainder of object name + /// + public static readonly string SEPARATOR = "."; + private bool usePrefix; + private string advisorObjectNamePrefix; + private IList advisors; + + #region Properties + + /// + /// Gets or sets a value indicating whether to exclude + /// advisors with a certain prefix. + /// + /// true if [use prefix]; otherwise, false. + public bool UsePrefix + { + get { return usePrefix; } + set { usePrefix = value; } + } + + /// + /// Set the prefix for object names that will cause them to be included for + /// auto-proxying by this object. This prefix should be set to avoid circular + /// references. Default value is the object name of this object + a dot. + /// + /// The advisor object name prefix. + public string AdvisorObjectNamePrefix + { + get { return advisorObjectNamePrefix; } + set { advisorObjectNamePrefix = value; } + } + + #endregion + /// + /// Find all candidate advices to use in auto proxying. + /// + /// list of Advice + protected override IList FindCandidateAdvisors() + { + if (advisors == null) + { + throw new InvalidOperationException("Must not be called before AfterPropertiesSet()"); + } + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format("returning available advisors")); + } + return advisors; + } + + private IList InstantiateCandidateAdvisors() + { + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format("instantiating available advisors")); + } + + //This is ensured in AbstractAdvisorAutoProxyCreator. Will be more type safe once sync with Spring Java 2.x + IConfigurableListableObjectFactory owningFactory = ObjectFactory as IConfigurableListableObjectFactory; + if (owningFactory == null) + { + throw new InvalidOperationException("Cannot use DefaultAdvisorAutoProxyCreator without a IListableObjectFactory"); + } + + ArrayList candidateAdvisors = new ArrayList(); + + string[] advisorNames = ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors( + owningFactory, typeof(IAdvisor), true, false); + for (int i = 0; i < advisorNames.Length; i++) + { + string name = advisorNames[i]; + if ( (!usePrefix || name.StartsWith(advisorObjectNamePrefix)) && !owningFactory.IsCurrentlyInCreation(name)) + { + try + { + IAdvisor advisor = (IAdvisor) owningFactory.GetObject(name); + candidateAdvisors.Add(advisor); + } catch (ObjectCreationException ex) + { + Exception rootEx = ex.GetBaseException(); + + + if (rootEx is ObjectCurrentlyInCreationException) + { + ObjectCurrentlyInCreationException oce = (ObjectCurrentlyInCreationException) rootEx; + if (owningFactory.IsCurrentlyInCreation(oce.ObjectName)) + { + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format("Ignoring currently created advisor '{0}': exception message = {1}", + name, ex.Message)); + } + continue; + } + } + throw; + } + } + } + + string[] aspectNames = ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors( + owningFactory, typeof(IAdvisors), true, false); + for (int i = 0; i < aspectNames.Length; i++) + { + string name = aspectNames[i]; + if (!usePrefix || name.StartsWith(advisorObjectNamePrefix)) + { + IAdvisors advisors = (IAdvisors)owningFactory.GetObject(name); + candidateAdvisors.AddRange(advisors.Advisors); + } + } + + return candidateAdvisors; + } + + + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + public void AfterPropertiesSet() + { + advisors = InstantiateCandidateAdvisors(); + } + + #region IObjectNameAware Members + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// The name of the object in the factory. + /// + ///

    + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

    + ///
    + public string ObjectName + { + set + { + // If no infrastructure object name prefix has been set, override it. + if (advisorObjectNamePrefix == null) + { + advisorObjectNamePrefix = value + SEPARATOR; + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/ITargetSourceCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/ITargetSourceCreator.cs new file mode 100644 index 00000000..dffa583a --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/ITargetSourceCreator.cs @@ -0,0 +1,53 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// Implementations can create special target sources, such as pooling target + /// sources, for particular objects. For example, they may base their choice + /// on attributes, such as a pooling attribute, on the target type. + /// + ///

    AbstractAutoProxyCreator can support a number of TargetSourceCreators, + /// which will be applied in order.

    + ///
    + /// Rod Johnson + /// Adhari C Mahendra (.NET) + /// $Id: ITargetSourceCreator.cs,v 1.3 2007/08/22 08:49:08 markpollack Exp $ + public interface ITargetSourceCreator + { + /// + /// Create a special TargetSource for the given object, if any. + /// + /// The type of the object to create a TargetSource for + /// the name of the object + /// the containing factory + /// a special TargetSource or null if this TargetSourceCreator isn't + /// interested in the particular object + ITargetSource GetTargetSource(Type objectType, string objectName, IObjectFactory factory); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/ObjectNameAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/ObjectNameAutoProxyCreator.cs new file mode 100644 index 00000000..37f16e67 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/ObjectNameAutoProxyCreator.cs @@ -0,0 +1,112 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Objects.Factory; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// Object Auto Proxy Creator + /// + /// + /// + /// Auto proxy creator that identifies objects to proxy via a list of names. + /// Checks for direct, "xxx*", "*xxx" and "*xxx*" matches. + /// + /// In case of a IFactoryObject, only the objects created by the + /// FactoryBean will get proxied. If you intend to proxy a IFactoryObject instance itself + /// specify the object name of the IFactoryObject including + /// the factory-object prefix "&" e.g. "&MyFactoryObject". + /// + /// + /// + /// Juergen Hoeller + /// Adhari C Mahendra (.NET) + /// $Id: ObjectNameAutoProxyCreator.cs,v 1.8 2008/03/03 09:28:49 bbaia Exp $ + public class ObjectNameAutoProxyCreator : AbstractAutoProxyCreator + { + private IList objectNames; + + /// + /// Set the names of the objects in IList fashioned way that should automatically + /// get wrapped with proxies. + /// A name can specify a prefix to match by ending with "*", e.g. "myObject,tx*" + /// will match the object named "myObject" and all objects whose name start with "tx". + /// + public IList ObjectNames + { + set { objectNames = value; } + } + + /// + /// Identify as object to proxy if the object name is in the configured list of names. + /// + protected override object[] GetAdvicesAndAdvisorsForObject(Type objType, string name, ITargetSource customTargetSource) + { + if (objectNames != null) + { + for (int i = 0; i < objectNames.Count; i++) + { + string mappedName = String.Copy((string) objectNames[i]); + if (typeof (IFactoryObject).IsAssignableFrom(objType)) + { + if (!name.StartsWith(ObjectFactoryUtils.FactoryObjectPrefix)) + { + continue; + } + mappedName = mappedName.Substring(ObjectFactoryUtils.FactoryObjectPrefix.Length); + } + if (IsMatch(name, mappedName)) + { + return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS; + } + } + } + return DO_NOT_PROXY; + + } + + /// + /// Return if the given object name matches the mapped name. + /// + /// + ///

    + /// The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, + /// as well as direct equality. Can be overridden in subclasses. + ///

    + ///
    + /// the object name to check + /// the name in the configured list of names + /// if the names match + protected virtual bool IsMatch(string objectName, string mappedName) + { + return PatternMatchUtils.SimpleMatch(mappedName, objectName); + } + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/Target/AbstractPrototypeTargetSourceCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/Target/AbstractPrototypeTargetSourceCreator.cs new file mode 100644 index 00000000..55af4478 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/Target/AbstractPrototypeTargetSourceCreator.cs @@ -0,0 +1,107 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using Spring.Aop.Target; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy.Target +{ + /// + /// Summary description for AbstractPrototypeBasedTargetSourceCreator. + /// + public abstract class AbstractPrototypeTargetSourceCreator : ITargetSourceCreator + { + /// + /// The logger + /// + protected readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + #region ITargetSourceCreator Members + + /// + /// Create a special TargetSource for the given object, if any. + /// + /// the type of the object to create a TargetSource for + /// the name of the object + /// the containing factory + /// + /// a special TargetSource or null if this TargetSourceCreator isn't + /// interested in the particular object + /// + public ITargetSource GetTargetSource(Type objectType, string name, IObjectFactory factory) + { + AbstractPrototypeTargetSource prototypeTargetSource = CreatePrototypeTargetSource(objectType, name, factory); + if (prototypeTargetSource == null) + { + return null; + } + else + { + if (!(factory is IObjectDefinitionRegistry)) + { + if (logger.IsWarnEnabled) + logger.Warn("Cannot do autopooling with a IObjectFactory that doesn't implement IObjectDefinitionRegistry"); + return null; + } + IObjectDefinitionRegistry definitionRegistry = (IObjectDefinitionRegistry) factory; + RootObjectDefinition definition = (RootObjectDefinition) definitionRegistry.GetObjectDefinition(name); + + if (logger.IsInfoEnabled) + logger.Info("Configuring AbstractPrototypeBasedTargetSource..."); + + // Infinite cycle will result if we don't use a different factory, + // because a GetObject() call with this objectName will go through the autoproxy + // infrastructure again. + // We to override just this object definition, as it may reference other objects + // and we're happy to take the parent's definition for those. + DefaultListableObjectFactory objectFactory = new DefaultListableObjectFactory(factory); + + // Override the prototype object + objectFactory.RegisterObjectDefinition(name, definition); + + // Complete configuring the PrototypeTargetSource + prototypeTargetSource.TargetObjectName = name; + prototypeTargetSource.ObjectFactory = objectFactory; + + return prototypeTargetSource; + } + } + + #endregion + + /// + /// Creates the prototype target source. + /// + /// The type of the object to create a target source for. + /// The name. + /// The factory. + /// + protected abstract AbstractPrototypeTargetSource CreatePrototypeTargetSource(Type objectType, string name, + IObjectFactory factory); + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicMethodInvocation.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicMethodInvocation.cs new file mode 100644 index 00000000..8ce1283b --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicMethodInvocation.cs @@ -0,0 +1,126 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; + +using Spring.Util; +using Spring.Reflection.Dynamic; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Invokes a target method using dynamic reflection. + /// + /// + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: DynamicMethodInvocation.cs,v 1.3 2008/02/06 18:28:52 bbaia Exp $ + [Serializable] + public class DynamicMethodInvocation : AbstractMethodInvocation + { + /// + /// The method invocation that is to be invoked on the proxy. + /// + protected MethodInfo proxyMethod; + + /// + /// Creates a new instance of the + /// class. + /// + /// The AOP proxy. + /// The target object. + /// The target method proxied. + /// The method to invoke on proxy. + /// The target method's arguments. + /// + /// The of the target object. + /// + /// The list of interceptors that are to be applied. May be + /// . + /// + /// + /// If any of the or + /// parameters is . + /// + public DynamicMethodInvocation( + object proxy, object target, MethodInfo method, MethodInfo proxyMethod, + object[] arguments, Type targetType, IList interceptors) + : base(proxy, target, method, arguments, targetType, interceptors) + { + this.proxyMethod = proxyMethod; + } + + /// + /// Invokes the joinpoint using dynamic reflection. + /// + /// + ///

    + /// Subclasses can override this to use custom invocation. + ///

    + ///
    + /// + /// The return value of the invocation of the joinpoint. + /// + /// + /// If invoking the joinpoint resulted in an exception. + /// + /// + protected override object InvokeJoinpoint() + { + IDynamicMethod targetMethod = (this.proxyMethod == null) ? new SafeMethod(method) : new SafeMethod(proxyMethod); + try + { + return targetMethod.Invoke(target, arguments); + } + // Only happens if fallback to standard reflection. + catch (TargetInvocationException ex) + { + throw ReflectionUtils.UnwrapTargetInvocationException(ex); + } + } + + /// + /// Creates a new instance + /// from the specified and + /// increments the interceptor index. + /// + /// + /// The current instance. + /// + /// + /// The new instance to use. + /// + protected override IMethodInvocation PrepareMethodInvocationForProceed(IMethodInvocation invocation) + { + DynamicMethodInvocation rmi = new DynamicMethodInvocation( + this.proxy, this.target, this.method, this.proxyMethod, this.arguments, this.targetType, this.interceptors); + rmi.currentInterceptorIndex = this.currentInterceptorIndex + 1; + + return rmi; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/AbstractAopProxyMethodBuilder.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/AbstractAopProxyMethodBuilder.cs new file mode 100644 index 00000000..247ab977 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/AbstractAopProxyMethodBuilder.cs @@ -0,0 +1,788 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; + +using Spring.Proxy; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Base class for AOP method builders that contains common functionalities. + /// + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: AbstractAopProxyMethodBuilder.cs,v 1.10 2008/05/21 08:04:34 bbaia Exp $ + public abstract class AbstractAopProxyMethodBuilder : AbstractProxyMethodBuilder + { + #region Fields + + /// + /// The implementation to use. + /// + protected IAopProxyTypeGenerator aopProxyGenerator; + + /// + /// The dictionary to cache the list of target + /// s. + /// + protected IDictionary targetMethods; + + /// + /// The dictionary to cache the list of target + /// s defined on the proxy. + /// + protected IDictionary onProxyTargetMethods; + + // variables + + /// + /// The local variable to store the list of method interceptors. + /// + protected LocalBuilder interceptors; + + /// + /// The local variable to store the target type being proxied. + /// + protected LocalBuilder targetType; + + /// + /// The local variable to store method arguments. + /// + protected LocalBuilder arguments; + + /// + /// The local variable to store the return value. + /// + protected LocalBuilder returnValue; + +#if NET_2_0 + /// + /// The local variable to store the closed generic method + /// when the target method is generic. + /// + protected LocalBuilder genericTargetMethod; + + /// + /// The local variable to store the closed generic method + /// when the target method defined on the proxy is generic. + /// + protected LocalBuilder genericOnProxyTargetMethod; +#endif + /// + /// The field to cache the target . + /// + protected FieldBuilder targetMethodCacheField; + + /// + /// The field to cache the target + /// defined on the proxy. + /// + protected FieldBuilder onProxyTargetMethodCacheField; + + // convinience fields + + /// + /// Indicates if the method returns a value. + /// + protected bool methodReturnsValue; + + // private fields + + private static IDictionary ldindOpCodes; + + #endregion + + #region Constructor(s) / Destructor + + static AbstractAopProxyMethodBuilder() + { + ldindOpCodes = new Hashtable(); + ldindOpCodes[typeof(sbyte)] = OpCodes.Ldind_I1; + ldindOpCodes[typeof(short)] = OpCodes.Ldind_I2; + ldindOpCodes[typeof(int)] = OpCodes.Ldind_I4; + ldindOpCodes[typeof(long)] = OpCodes.Ldind_I8; + ldindOpCodes[typeof(byte)] = OpCodes.Ldind_U1; + ldindOpCodes[typeof(ushort)] = OpCodes.Ldind_U2; + ldindOpCodes[typeof(uint)] = OpCodes.Ldind_U4; + ldindOpCodes[typeof(ulong)] = OpCodes.Ldind_I8; + ldindOpCodes[typeof(float)] = OpCodes.Ldind_R4; + ldindOpCodes[typeof(double)] = OpCodes.Ldind_R8; + ldindOpCodes[typeof(char)] = OpCodes.Ldind_U2; + ldindOpCodes[typeof(bool)] = OpCodes.Ldind_I1; + } + + /// + /// Creates a new instance of the method builder. + /// + /// The type builder to use. + /// + /// The implementation to use. + /// + /// + /// if the interface is to be + /// implemented explicitly; otherwise . + /// + /// + /// The dictionary to cache the list of target + /// s. + /// + protected AbstractAopProxyMethodBuilder( + TypeBuilder typeBuilder, IAopProxyTypeGenerator aopProxyGenerator, + bool explicitImplementation, IDictionary targetMethods) + : this(typeBuilder, aopProxyGenerator, explicitImplementation, targetMethods, new Hashtable()) + { + } + + /// + /// Creates a new instance of the method builder. + /// + /// The type builder to use. + /// + /// The implementation to use. + /// + /// + /// if the interface is to be + /// implemented explicitly; otherwise . + /// + /// + /// The dictionary to cache the list of target + /// s. + /// + /// + /// The dictionary to cache the list of target + /// s defined on the proxy. + /// + protected AbstractAopProxyMethodBuilder( + TypeBuilder typeBuilder, IAopProxyTypeGenerator aopProxyGenerator, + bool explicitImplementation, IDictionary targetMethods, IDictionary onProxyTargetMethods) + : base(typeBuilder, aopProxyGenerator, explicitImplementation) + { + this.aopProxyGenerator = aopProxyGenerator; + this.targetMethods = targetMethods; + this.onProxyTargetMethods = onProxyTargetMethods; + } + + #endregion + + #region Protected Members + + /// + /// Generates the proxy method. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected override void GenerateMethod( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + methodReturnsValue = (method.ReturnType != typeof(void)); + + DeclareLocals(il, method); + + GenerateTargetMethodCacheField(il, method); + GenerateOnProxyTargetMethodCacheField(il, method); + + BeginMethod(il, method); + GenerateMethodLogic(il, method, interfaceMethod); + EndMethod(il, method); + } + + /// + /// Generates unique method id for the cache field. + /// + /// The target method. + /// An unique method name. + protected virtual string GenerateMethodCacheFieldId(MethodInfo method) + { + return "_m" + Guid.NewGuid().ToString("N"); + } + + /// + /// Create static field that will cache target method. + /// + /// The IL generator to use. + /// The target method. + protected virtual void GenerateTargetMethodCacheField( + ILGenerator il, MethodInfo method) + { + string methodId = GenerateMethodCacheFieldId(method); + targetMethods.Add(methodId, method); + + targetMethodCacheField = typeBuilder.DefineField(methodId, typeof(MethodInfo), + FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly); + +#if NET_2_0 + MakeGenericMethod(il, method, targetMethodCacheField, genericTargetMethod); +#endif + } + + /// + /// Create static field that will cache target method when defined on the proxy. + /// + /// The IL generator to use. + /// The target method. + protected virtual void GenerateOnProxyTargetMethodCacheField( + ILGenerator il, MethodInfo method) + { + } + +#if NET_2_0 + /// + /// Create a closed generic method for the current call + /// if target method is a generic definition. + /// + /// The IL generator to use. + /// The target method. + /// + /// The field that contains the method generic definition + /// + /// + /// The local variable to store the closed generic method. + /// + protected void MakeGenericMethod(ILGenerator il, MethodInfo method, + FieldBuilder methodCacheField, LocalBuilder localMethod) + { + // if target method is a generic definition, + // create a closed generic method for the current call. + if (method.IsGenericMethodDefinition) + { + Type[] genericArgs = method.GetGenericArguments(); + + LocalBuilder typeArgs = il.DeclareLocal(typeof(Type[])); + + il.Emit(OpCodes.Ldsfld, methodCacheField); + + // specify array size and create an array + il.Emit(OpCodes.Ldc_I4, genericArgs.Length); + il.Emit(OpCodes.Newarr, typeof(Type)); + il.Emit(OpCodes.Stloc, typeArgs); + + // populate array with type arguments + for (int i = 0; i < genericArgs.Length; i++) + { + il.Emit(OpCodes.Ldloc, typeArgs); + il.Emit(OpCodes.Ldc_I4, i); + il.Emit(OpCodes.Ldtoken, genericArgs[i]); + il.EmitCall(OpCodes.Call, References.GetTypeFromHandle, null); + il.Emit(OpCodes.Stelem_Ref); + } + + il.Emit(OpCodes.Ldloc, typeArgs); + il.Emit(OpCodes.Callvirt, References.MakeGenericMethod); + il.Emit(OpCodes.Stloc, localMethod); + } + } +#endif + + /// + /// Generates the IL instructions that pushes + /// the target type on stack. + /// + /// The IL generator to use. + protected virtual void PushTargetType(ILGenerator il) + { + aopProxyGenerator.PushAdvisedProxy(il); + il.Emit(OpCodes.Ldfld, References.TargetTypeField); + } + + /// + /// Generates the IL instructions that pushes + /// the current + /// instance on stack. + /// + /// The IL generator to use. + protected virtual void PushAdvisedProxy(ILGenerator il) + { + aopProxyGenerator.PushAdvisedProxy(il); + } + + /// + /// Pushes the target to stack. + /// + /// The IL generator to use. + /// The method to proxy. + protected virtual void PushTargetMethodInfo(ILGenerator il, MethodInfo method) + { +#if NET_2_0 + if (method.IsGenericMethodDefinition) + { + il.Emit(OpCodes.Ldloc, genericTargetMethod); + return; + } +#endif + il.Emit(OpCodes.Ldsfld, targetMethodCacheField); + } + + /// + /// Pushes the target defined on the proxy to stack. + /// + /// The IL generator to use. + /// The method to proxy. + protected virtual void PushOnProxyTargetMethodInfo(ILGenerator il, MethodInfo method) + { + if (onProxyTargetMethodCacheField != null) + { +#if NET_2_0 + if (method.IsGenericMethodDefinition) + { + il.Emit(OpCodes.Ldloc, genericOnProxyTargetMethod); + return; + } +#endif + il.Emit(OpCodes.Ldsfld, onProxyTargetMethodCacheField); + } + else + { + il.Emit(OpCodes.Ldnull); + } + } + + /// + /// Creates local variable declarations. + /// + /// The IL generator to use. + /// The method to proxy. + protected virtual void DeclareLocals(ILGenerator il, MethodInfo method) + { + interceptors = il.DeclareLocal(typeof(IList)); + targetType = il.DeclareLocal(typeof(Type)); + arguments = il.DeclareLocal(typeof(Object[])); + +#if NET_2_0 + if (method.IsGenericMethodDefinition) + { + genericTargetMethod = il.DeclareLocal(typeof(MethodInfo)); + genericOnProxyTargetMethod = il.DeclareLocal(typeof(MethodInfo)); + } +#endif + if (methodReturnsValue) + { + returnValue = il.DeclareLocal(method.ReturnType); + } + +#if DEBUG + interceptors.SetLocalSymInfo("interceptors"); + targetType.SetLocalSymInfo("targetType"); + arguments.SetLocalSymInfo("arguments"); +#if NET_2_0 + if (method.IsGenericMethodDefinition) + { + genericTargetMethod.SetLocalSymInfo("genericTargetMethod"); + genericOnProxyTargetMethod.SetLocalSymInfo("genericOnProxyTargetMethod"); + } +#endif + if (methodReturnsValue) + { + returnValue.SetLocalSymInfo("returnValue"); + } +#endif + } + + /// + /// Initializes local variables + /// + /// The IL generator to use. + /// The method to proxy. + protected virtual void BeginMethod(ILGenerator il, MethodInfo method) + { + Label jmpProxyNotExposed = il.DefineLabel(); + + // set current proxy to this object + PushAdvisedProxy(il); + il.Emit(OpCodes.Ldfld, References.AdvisedField); + il.EmitCall(OpCodes.Callvirt, References.ExposeProxyProperty, null); + il.Emit(OpCodes.Brfalse_S, jmpProxyNotExposed); + il.Emit(OpCodes.Ldarg_0); + il.EmitCall(OpCodes.Call, References.PushProxyMethod, null); + + il.MarkLabel(jmpProxyNotExposed); + + // initialize targetType + PushTargetType(il); + il.Emit(OpCodes.Stloc, targetType); + + // initialize interceptors + PushAdvisedProxy(il); + il.Emit(OpCodes.Ldloc, targetType); + PushTargetMethodInfo(il, method); + il.EmitCall(OpCodes.Call, References.GetInterceptorsMethod, null); + il.Emit(OpCodes.Stloc, interceptors); + } + + /// + /// Generates method logic. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected virtual void GenerateMethodLogic( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + Label jmpDirectCall = il.DefineLabel(); + Label jmpEndIf = il.DefineLabel(); + + // check if there are any interceptors + il.Emit(OpCodes.Ldloc, interceptors); + il.EmitCall(OpCodes.Callvirt, References.CountProperty, null); + il.Emit(OpCodes.Ldc_I4_0); + + // if not jump to direct call + il.Emit(OpCodes.Ble, jmpDirectCall); + + // otherwise call Invoke and jump to method end + CallInvoke(il, method); + il.Emit(OpCodes.Br, jmpEndIf); + + // call method directly + il.MarkLabel(jmpDirectCall); + CallDirectProxiedMethod(il, method, interfaceMethod); + if (methodReturnsValue) + { + // store return value, unboxing is not necessary because we called method directly + il.Emit(OpCodes.Stloc, returnValue); + } + + il.MarkLabel(jmpEndIf); + + if (methodReturnsValue) + { + if (!method.ReturnType.IsValueType) + { + ProcessReturnValue(il, returnValue); + } + } + } + + /// + /// Calls method using Invoke + /// + /// The IL generator to use. + /// The method to proxy. + protected virtual void CallInvoke(ILGenerator il, MethodInfo method) + { + ParameterInfo[] parameters = method.GetParameters(); + + SetupMethodArguments(il, method, parameters); + + PushAdvisedProxy(il); + + // setup parameters for call + il.Emit(OpCodes.Ldarg_0); // proxy + PushTarget(il); // target + il.Emit(OpCodes.Ldloc, targetType); // target type + PushTargetMethodInfo(il, method); // method + PushOnProxyTargetMethodInfo(il, method); // method defined on proxy + il.Emit(OpCodes.Ldloc, arguments); // args + il.Emit(OpCodes.Ldloc, interceptors); // interceptors + + // call Invoke + il.EmitCall(OpCodes.Call, References.InvokeMethod, null); + + // process return value + if (methodReturnsValue) + { + EmitUnboxIfNeeded(il, method.ReturnType); + il.Emit(OpCodes.Stloc, returnValue); + } + else + { + il.Emit(OpCodes.Pop); + } + + // process byRef arguments + for (int i = 0; i < parameters.Length; i++) + { + if (parameters[i].ParameterType.IsByRef) + { + il.Emit(OpCodes.Ldarg_S, i + 1); + il.Emit(OpCodes.Ldloc, arguments); + il.Emit(OpCodes.Ldc_I4_S, i); + il.Emit(OpCodes.Ldelem_Ref); + Type type = parameters[i].ParameterType.GetElementType(); + EmitUnboxIfNeeded(il, type); + EmitStoreValueIndirect(il, type); + } + } + } + + /// + /// Setup proxied method arguments. + /// + /// The IL generator to use. + /// The method to proxy. + /// The method's parameters. + protected void SetupMethodArguments( + ILGenerator il, MethodInfo method, ParameterInfo[] parameters) + { + if (parameters.Length > 0) + { + // specify array size and create an array + il.Emit(OpCodes.Ldc_I4, parameters.Length); + il.Emit(OpCodes.Newarr, typeof(Object)); + il.Emit(OpCodes.Stloc, arguments); + + // populate array with params + for (int i = 0; i < parameters.Length; i++) + { + Type type = parameters[i].ParameterType; + + il.Emit(OpCodes.Ldloc, arguments); + il.Emit(OpCodes.Ldc_I4, i); + il.Emit(OpCodes.Ldarg_S, i + 1); + + // setup byRef arguments + if (type.IsByRef) + { + type = type.GetElementType(); + EmitLoadValueIndirect(il, type); + } + +#if NET_2_0 + if (type.IsValueType || type.IsGenericParameter) +#else + if (type.IsValueType) +#endif + { + il.Emit(OpCodes.Box, type); + } + + il.Emit(OpCodes.Stelem_Ref); + } + } + else + { + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Stloc, arguments); + } + } + + /// + /// Calls proxied method directly. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected abstract void CallDirectProxiedMethod( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod); + + /// + /// Ends method by returning return value if appropriate. + /// + /// The IL generator to use. + /// The method to proxy. + protected virtual void EndMethod(ILGenerator il, MethodInfo method) + { + Label jmpProxyNotExposed = il.DefineLabel(); + + // reset current proxy to old value + PushAdvisedProxy(il); + il.Emit(OpCodes.Ldfld, References.AdvisedField); + il.EmitCall(OpCodes.Callvirt, References.ExposeProxyProperty, null); + il.Emit(OpCodes.Brfalse_S, jmpProxyNotExposed); + il.EmitCall(OpCodes.Call, References.PopProxyMethod, null); + + il.MarkLabel(jmpProxyNotExposed); + + if (methodReturnsValue) + { + il.Emit(OpCodes.Ldloc, returnValue); + } + } + + #endregion + + #region Reflection.Emit utility methods + + /// + /// Emits MSIL instructions to load a value of the specified + /// onto the evaluation stack indirectly. + /// + /// The IL generator to use. + /// The type of the value. + protected static void EmitLoadValueIndirect(ILGenerator il, Type type) + { + if (type.IsValueType) + { + if (type == typeof(int)) il.Emit(OpCodes.Ldind_I4); + else if (type == typeof(uint)) il.Emit(OpCodes.Ldind_U4); + else if (type == typeof(char)) il.Emit(OpCodes.Ldind_I2); + else if (type == typeof(bool)) il.Emit(OpCodes.Ldind_I1); + else if (type == typeof(float)) il.Emit(OpCodes.Ldind_R4); + else if (type == typeof(double)) il.Emit(OpCodes.Ldind_R8); + else if (type == typeof(short)) il.Emit(OpCodes.Ldind_I2); + else if (type == typeof(ushort)) il.Emit(OpCodes.Ldind_U2); + else if (type == typeof(long) || type == typeof(ulong)) il.Emit(OpCodes.Ldind_I8); + else il.Emit(OpCodes.Ldobj, type); + } + else + { + il.Emit(OpCodes.Ldind_Ref); + } + } + + /// + /// Emit MSIL instructions to store a value of the specified + /// at a supplied address. + /// + /// The IL generator to use. + /// The type of the value. + protected static void EmitStoreValueIndirect(ILGenerator il, Type type) + { + if (type.IsValueType) + { + if (type.IsEnum) EmitStoreValueIndirect(il, Enum.GetUnderlyingType(type)); + else if (type == typeof(int)) il.Emit(OpCodes.Stind_I4); + else if (type == typeof(short)) il.Emit(OpCodes.Stind_I2); + else if (type == typeof(long) || type == typeof(ulong)) il.Emit(OpCodes.Stind_I8); + else if (type == typeof(char)) il.Emit(OpCodes.Stind_I2); + else if (type == typeof(bool)) il.Emit(OpCodes.Stind_I1); + else if (type == typeof(float)) il.Emit(OpCodes.Stind_R4); + else if (type == typeof(double)) il.Emit(OpCodes.Stind_R8); + else il.Emit(OpCodes.Stobj, type); + } + else + { + il.Emit(OpCodes.Stind_Ref); + } + } + + /// + /// Emits MSIL instructions to convert the boxed representation + /// of the supplied to its unboxed form. + /// + /// The IL generator to use. + /// The type specified in the instruction. + protected static void EmitUnboxIfNeeded(ILGenerator il, Type type) + { +#if NET_2_0 + if (type.IsValueType || type.IsGenericParameter) + { + il.Emit(OpCodes.Unbox_Any, type); + } +#else + if (type.IsValueType) + { + il.Emit(OpCodes.Unbox, type); + il.Emit(OpCodes.Ldobj, type); + } +#endif + } + + #endregion + } + + #region References helper class definition + + internal struct References + { + // fields + public static readonly FieldInfo AdvisedField = + typeof(AdvisedProxy).GetField("m_advised", BindingFlags.Instance | BindingFlags.Public); + + public static readonly FieldInfo TargetTypeField = + typeof(AdvisedProxy).GetField("m_targetType", BindingFlags.Instance | BindingFlags.Public); + + public static readonly FieldInfo IntroductionsField = + typeof(AdvisedProxy).GetField("m_introductions", BindingFlags.Instance | BindingFlags.Public); + + public static readonly FieldInfo TargetSourceWrapperField = + typeof(AdvisedProxy).GetField("m_targetSourceWrapper", BindingFlags.Instance | BindingFlags.Public); + + // constructors + public static readonly ConstructorInfo BaseCompositionAopProxyConstructor = + typeof(BaseCompositionAopProxy).GetConstructor(new Type[] { typeof(IAdvised) }); + + public static readonly ConstructorInfo BaseCompositionAopProxySerializationConstructor = + typeof(BaseCompositionAopProxy).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, + new Type[] {typeof(SerializationInfo), typeof(StreamingContext)}, + null); + + public static readonly ConstructorInfo AdvisedProxyConstructor = + typeof(AdvisedProxy).GetConstructor(new Type[] { typeof(IAdvised), typeof(IAopProxy) }); + + public static readonly ConstructorInfo AdvisedProxySerializationConstructor = + typeof(AdvisedProxy).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, + new Type[] {typeof (SerializationInfo), typeof (StreamingContext)}, + null); + public static readonly ConstructorInfo ObjectConstructor = + typeof(Object).GetConstructor(Type.EmptyTypes); + + // methods + public static readonly MethodInfo PushProxyMethod = + typeof(AopContext).GetMethod("PushProxy", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(Object) }, null); + + public static readonly MethodInfo PopProxyMethod = + typeof(AopContext).GetMethod("PopProxy", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null); + + public static readonly MethodInfo InvokeMethod = + typeof(AdvisedProxy).GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof(Object), typeof(Object), typeof(Type), typeof(MethodInfo), typeof(MethodInfo), typeof(Object[]), typeof(IList) }, null); + + public static readonly MethodInfo GetInterceptorsMethod = + typeof(AdvisedProxy).GetMethod("GetInterceptors", BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof(Type), typeof(MethodInfo) }, null); + + public static readonly MethodInfo GetTargetMethod = + typeof(ITargetSourceWrapper).GetMethod("GetTarget", Type.EmptyTypes); + + public static readonly MethodInfo GetTypeMethod = + typeof(Object).GetMethod("GetType", Type.EmptyTypes); + + public static readonly MethodInfo GetTypeFromHandle = + typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }); + +#if NET_2_0 + public static readonly MethodInfo MakeGenericMethod = + typeof(MethodInfo).GetMethod("MakeGenericMethod", new Type[] { typeof(Type[]) }); +#endif + + public static readonly MethodInfo DisposeMethod = + typeof(IDisposable).GetMethod("Dispose", Type.EmptyTypes); + + public static readonly MethodInfo AddSerializationValue = + typeof(SerializationInfo).GetMethod("AddValue", new Type[] { typeof(string), typeof(object) }); + + public static readonly MethodInfo GetSerializationValue = + typeof(SerializationInfo).GetMethod("GetValue", new Type[] { typeof(string), typeof(Type) }); + + // properties + public static readonly MethodInfo ExposeProxyProperty = + typeof(IAdvised).GetProperty("ExposeProxy", typeof(Boolean)).GetGetMethod(); + + public static readonly MethodInfo CountProperty = + typeof(ICollection).GetProperty("Count", typeof(Int32)).GetGetMethod(); + } + + #endregion +} diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/AbstractAopProxyTypeBuilder.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/AbstractAopProxyTypeBuilder.cs new file mode 100644 index 00000000..785afe97 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/AbstractAopProxyTypeBuilder.cs @@ -0,0 +1,106 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection.Emit; + +using Spring.Proxy; + + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Base class for proxy builders that can be used + /// to create an AOP proxy for any object. + /// + /// Bruno Baia + /// $Id: AbstractAopProxyTypeBuilder.cs,v 1.5 2007/12/03 09:06:58 bbaia Exp $ + public abstract class AbstractAopProxyTypeBuilder : + AbstractProxyTypeBuilder, IAopProxyTypeGenerator + { + #region IProxyTypeGenerator Members + + /// + /// Generates the IL instructions that pushes + /// the target instance on which calls should be delegated to. + /// + /// The IL generator to use. + public override void PushTarget(ILGenerator il) + { + PushAdvisedProxy(il); + il.Emit(OpCodes.Ldfld, References.TargetSourceWrapperField); + il.EmitCall(OpCodes.Callvirt, References.GetTargetMethod, null); + } + + #endregion + + #region IAopProxyTypeGenerator Members + + /// + /// Generates the IL instructions that pushes + /// the current + /// instance on stack. + /// + /// The IL generator to use. + public abstract void PushAdvisedProxy(ILGenerator il); + + #endregion + + #region Protected Methods + + /// + /// Calculates and returns the list of attributes that apply to the + /// specified type. + /// + /// + /// Removes from the list. + /// + /// The type to find attributes for. + /// + /// A list of custom attributes that should be applied to type. + /// + protected override IList GetTypeAttributes(Type type) + { + IList attrs = base.GetTypeAttributes(type); + int i = 0; + while (i < attrs.Count) + { + if (IsAttributeMatchingType(attrs[i], typeof(SerializableAttribute))) + { + attrs.RemoveAt(i); + } + else + { + i++; + } + } + + return attrs; + } + + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/AdvisedProxy.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/AdvisedProxy.cs new file mode 100644 index 00000000..af4d7e42 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/AdvisedProxy.cs @@ -0,0 +1,432 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Permissions; + +using AopAlliance.Aop; +using AopAlliance.Intercept; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Represents the AOP configuration data built-in with the proxy. + /// + /// Bruno Baia + /// $Id: AdvisedProxy.cs,v 1.10 2008/02/06 18:28:52 bbaia Exp $ + [Serializable] + public class AdvisedProxy : IAdvised //, ISerializable + { + #region Fields + + /// + /// Should we use dynamic reflection for method invocation ? + /// + public static bool UseDynamicReflection; + + /// + /// Optimization fields + /// + private static IList EmptyList = ArrayList.ReadOnly(new ArrayList()); + + /// + /// IAdvised delegate + /// + public IAdvised m_advised; + + /// + /// Array of introduction delegates + /// + public IAdvice[] m_introductions; + + /// + /// Target source wrapper + /// + public ITargetSourceWrapper m_targetSourceWrapper; + + /// + /// Type of target object. + /// + public Type m_targetType; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the class. + /// + static AdvisedProxy() + { + string appSettingsKey = typeof(AdvisedProxy).FullName + ".UseDynamicReflection"; + NameValueCollection appSettings = + ConfigurationUtils.GetSection("appSettings") as NameValueCollection; + + if (appSettings != null && StringUtils.HasLength(appSettings[appSettingsKey])) + { + UseDynamicReflection = bool.Parse(appSettings[appSettingsKey]); + } + else + { + UseDynamicReflection = true; + } + } + + /// + /// Creates a new instance of the class. + /// + public AdvisedProxy() + {} + + /// + /// Creates a new instance of the class. + /// + /// The proxy configuration. + protected AdvisedProxy(IAdvised advised) + { + m_advised = advised; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// The proxy configuration. + /// The proxy. + public AdvisedProxy(IAdvised advised, IAopProxy proxy) + { + Initialize(advised, proxy); + } + + /// + /// Deserialization constructor. + /// + /// Serialization data. + /// Serialization context. + protected AdvisedProxy(SerializationInfo info, StreamingContext context) + { + m_advised = (IAdvised) info.GetValue("advised", typeof(IAdvised)); + m_introductions = (IAdvice[]) info.GetValue("introductions", typeof(IAdvice[])); + m_targetSourceWrapper = (ITargetSourceWrapper) info.GetValue("tsWrapper", typeof(ITargetSourceWrapper)); + m_targetType = (Type) info.GetValue("targetType", typeof(Type)); + } + + /// + /// Serializes this instance. + /// + /// Serialization data. + /// Serialization context. + [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("advised", m_advised); + info.AddValue("introductions", m_introductions); + info.AddValue("tsWrapper", m_targetSourceWrapper); + info.AddValue("targetType", m_targetType); + } + + #endregion + + #region Protected Methods + + /// + /// Initialization method. + /// + /// The proxy configuration. + /// + /// The current implementation. + /// + protected void Initialize(IAdvised advised, IAopProxy proxy) + { + this.m_advised = advised; + this.m_targetType = advised.TargetSource.TargetType; + + // initialize target + if (advised.TargetSource.IsStatic) + { + this.m_targetSourceWrapper = new StaticTargetSourceWrapper(advised.TargetSource); + } + else + { + this.m_targetSourceWrapper = new DynamicTargetSourceWrapper(advised.TargetSource); + } + + // initialize introduction advice + this.m_introductions = new IAdvice[advised.Introductions.Length]; + for (int i = 0; i < advised.Introductions.Length; i++) + { + this.m_introductions[i] = advised.Introductions[i].Advice; + + // set target proxy on introduction instance if it implements ITargetAware + if (this.m_introductions[i] is ITargetAware) + { + ((ITargetAware) this.m_introductions[i]).TargetProxy = proxy; + } + } + } + + #endregion + + #region Public Methods + + /// + /// Invokes intercepted methods using reflection + /// + /// proxy object + /// target object to invoke method on + /// target type + /// taget method to invoke + /// The method to invoke on proxy. + /// method arguments + /// interceptor chain + /// value returned by invocation chain + public object Invoke(object proxy, object target, Type targetType, + MethodInfo targetMethod, MethodInfo proxyMethod, object[] args, IList interceptors) + { + IMethodInvocation invocation = null; + if (UseDynamicReflection) + { + invocation = new DynamicMethodInvocation( + proxy, target, targetMethod, proxyMethod, args, targetType, interceptors); + } + else + { + invocation = new ReflectiveMethodInvocation( + proxy, target, targetMethod, proxyMethod, args, targetType, interceptors); + } + return invocation.Proceed(); + } + + /// + /// Returns a list of method interceptors + /// + /// target type + /// target method + /// list of inteceptors for the specified method + public IList GetInterceptors(Type targetType, MethodInfo method) + { + if (m_advised.Advisors.Length == 0) + { + return EmptyList; + } + else + { + return m_advised.AdvisorChainFactory.GetInterceptors(m_advised, this, method, targetType); + } + } + + #endregion + + #region IAdvised Members + + bool IAdvised.ExposeProxy + { + get { return m_advised.ExposeProxy; } + } + + IAdvisorChainFactory IAdvised.AdvisorChainFactory + { + get { return m_advised.AdvisorChainFactory; } + } + + bool IAdvised.ProxyTargetType + { + get { return m_advised.ProxyTargetType; } + } + + bool IAdvised.ProxyTargetAttributes + { + get { return m_advised.ProxyTargetAttributes; } + } + + IAdvisor[] IAdvised.Advisors + { + get { return m_advised.Advisors; } + } + + IIntroductionAdvisor[] IAdvised.Introductions + { + get { return m_advised.Introductions; } + } + + Type[] IAdvised.Interfaces + { + get { return m_advised.Interfaces; } + } + + IDictionary IAdvised.InterfaceMap + { + get { return m_advised.InterfaceMap; } + } + + bool IAdvised.IsFrozen + { + get { return m_advised.IsFrozen; } + } + + ITargetSource IAdvised.TargetSource + { + get { return m_advised.TargetSource; } + } + + bool IAdvised.IsSerializable + { + get { return m_advised.IsSerializable; } + } + + /// + /// Adds the supplied to the end (or tail) + /// of the advice (interceptor) chain. + /// + /// + /// The to be added. + /// + /// + /// + public void AddAdvice(IAdvice advice) + { + this.m_advised.AddAdvice(advice); + } + + /// + /// Adds the supplied to the supplied + /// in the advice (interceptor) chain. + /// + /// + /// The zero (0) indexed position (from the head) at which the + /// supplied is to be inserted into the + /// advice (interceptor) chain. + /// + /// + /// The to be added. + /// + /// + /// + public void AddAdvice(int position, IAdvice advice) + { + this.m_advised.AddAdvice(position, advice); + } + + bool IAdvised.IsInterfaceProxied(Type intf) + { + return m_advised.IsInterfaceProxied(intf); + } + + void IAdvised.AddAdvisors(IAdvisors advisors) + { + m_advised.AddAdvisors(advisors); + } + + void IAdvised.AddAdvisor(IAdvisor advisor) + { + m_advised.AddAdvisor(advisor); + } + + void IAdvised.AddAdvisor(int pos, IAdvisor advisor) + { + m_advised.AddAdvisor(pos, advisor); + } + + void IAdvised.AddIntroduction(IIntroductionAdvisor advisor) + { + m_advised.AddIntroduction(advisor); + } + + void IAdvised.AddIntroduction(int pos, IIntroductionAdvisor advisor) + { + m_advised.AddIntroduction(pos, advisor); + } + + int IAdvised.IndexOf(IAdvisor advisor) + { + return m_advised.IndexOf(advisor); + } + + int IAdvised.IndexOf(IIntroductionAdvisor advisor) + { + return m_advised.IndexOf(advisor); + } + + bool IAdvised.RemoveAdvisor(IAdvisor advisor) + { + return m_advised.RemoveAdvisor(advisor); + } + + void IAdvised.RemoveAdvisor(int index) + { + m_advised.RemoveAdvisor(index); + } + + bool IAdvised.RemoveAdvice(IAdvice advice) + { + return m_advised.RemoveAdvice(advice); + } + + bool IAdvised.RemoveIntroduction(IIntroductionAdvisor advisor) + { + return m_advised.RemoveIntroduction(advisor); + } + + void IAdvised.RemoveIntroduction(int index) + { + m_advised.RemoveIntroduction(index); + } + + void IAdvised.ReplaceIntroduction(int index, IIntroductionAdvisor advisor) + { + m_advised.ReplaceIntroduction(index, advisor); + } + + bool IAdvised.ReplaceAdvisor(IAdvisor a, IAdvisor b) + { + return m_advised.ReplaceAdvisor(a, b); + } + + string IAdvised.ToProxyConfigString() + { + return m_advised.ToProxyConfigString(); + } + + #endregion + + #region ITargetTypeAware implementation + + /// + /// Gets the target type behind the implementing object. + /// Ttypically a proxy configuration or an actual proxy. + /// + /// The type of the target or null if not known. + public Type TargetType + { + get { return m_targetType; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/BaseAopProxyMethodBuilder.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/BaseAopProxyMethodBuilder.cs new file mode 100644 index 00000000..99234cfa --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/BaseAopProxyMethodBuilder.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// implementation + /// that delegates method calls to the base method. + /// + /// Bruno Baia + /// $Id: BaseAopProxyMethodBuilder.cs,v 1.1 2008/02/06 18:28:52 bbaia Exp $ + public class BaseAopProxyMethodBuilder : AbstractAopProxyMethodBuilder + { + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the method builder. + /// + /// The type builder to use. + /// + /// The implementation to use. + /// + /// + /// The dictionary to cache the list of target + /// s. + /// + /// + /// The dictionary to cache the list of target + /// s defined on the proxy. + /// + public BaseAopProxyMethodBuilder( + TypeBuilder typeBuilder, IAopProxyTypeGenerator aopProxyGenerator, + IDictionary targetMethods, IDictionary onProxyTargetMethods) + : base(typeBuilder, aopProxyGenerator, false, targetMethods, onProxyTargetMethods) + { + } + + #endregion + + #region Protected Methods + + /// + /// Create static field that will cache target method when defined on the proxy. + /// + /// The IL generator to use. + /// The target method. + protected override void GenerateOnProxyTargetMethodCacheField( + ILGenerator il, MethodInfo method) + { + if (method.IsVirtual && !method.IsFinal) + { + // generate proxy method + MethodBuilder baseMethod = typeBuilder.DefineMethod("proxy_" + method.Name, + MethodAttributes.Public | MethodAttributes.HideBySig, + CallingConventions.Standard, + method.ReturnType, ReflectionUtils.GetParameterTypes(method)); + +#if NET_2_0 + DefineGenericParameters(baseMethod, method); +#endif + DefineParameters(baseMethod, method); + + ILGenerator localIL = baseMethod.GetILGenerator(); + + localIL.Emit(OpCodes.Ldarg_0); + // setup parameters for call + for (int i = 0; i < method.GetParameters().Length; i++) + { + localIL.Emit(OpCodes.Ldarg_S, i + 1); + } + localIL.EmitCall(OpCodes.Call, method, null); + localIL.Emit(OpCodes.Ret); + + // create static field that will cache proxy method + string methodId = GenerateMethodCacheFieldId(method); + onProxyTargetMethods.Add(methodId, method); + + onProxyTargetMethodCacheField = typeBuilder.DefineField( + methodId, typeof(MethodInfo), FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly); + +#if NET_2_0 + MakeGenericMethod(il, method, onProxyTargetMethodCacheField, genericOnProxyTargetMethod); +#endif + } + } + + /// + /// Calls target method directly. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected override void CallDirectProxiedMethod( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + CallDirectBaseMethod(il, method); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/BaseCompositionAopProxy.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/BaseCompositionAopProxy.cs new file mode 100644 index 00000000..5ebe7421 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/BaseCompositionAopProxy.cs @@ -0,0 +1,122 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Base class that each dynamic composition proxy has to extend. + /// + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: BaseCompositionAopProxy.cs,v 1.2 2007/03/16 04:01:22 aseovic Exp $ + [Serializable] + public abstract class BaseCompositionAopProxy : AdvisedProxy, IAopProxy, ISerializable + { + #region Constructor (s) / Destructor + + /// + /// Default constructor. + /// + public BaseCompositionAopProxy() + {} + + /// + /// Creates a new instance of the + /// class. + /// + /// The proxy configuration. + public BaseCompositionAopProxy(IAdvised advised) : base(advised) + { + base.Initialize(advised, this); + } + + /// + /// Deserialization constructor. + /// + /// Serialization data. + /// Serialization context. + protected BaseCompositionAopProxy(SerializationInfo info, StreamingContext context) : base(info, context) + {} + + #endregion + + #region IAopProxy Members + + /// + /// Returns this proxy instance + /// + /// + object IAopProxy.GetProxy() + { + return this; + } + + #endregion + + #region Equal, HashCode and ToString overrides + + /// + /// Delegate to target object handling of equals method. + /// + /// The object to compare with the current target object + /// true if the specified Object is equal to the current target object; otherwise, false + public override bool Equals(object obj) + { + using (m_targetSourceWrapper) + { + return m_targetSourceWrapper.GetTarget().Equals(obj); + } + } + + /// + /// Delgate to the target object generation of the hash code. + /// + /// A hash code for the target object. + public override int GetHashCode() + { + using (m_targetSourceWrapper) + { + return m_targetSourceWrapper.GetTarget().GetHashCode(); + } + } + + /// + /// Returns a String the represents the target object. + /// + /// A String that represents the target object + public override string ToString() + { + using (m_targetSourceWrapper) + { + return m_targetSourceWrapper.GetTarget().ToString(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/CachedAopProxyFactory.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/CachedAopProxyFactory.cs new file mode 100644 index 00000000..aa5e6a86 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/CachedAopProxyFactory.cs @@ -0,0 +1,184 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text; +using System.Collections; + +using Common.Logging; +using Spring.Proxy; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Implementation of the + /// interface that caches the AOP proxy instance. + /// + /// + ///

    + /// Caches against a key based on : + /// - the base type + /// - the target type + /// - the interfaces to proxy + ///

    + ///
    + /// Bruno Baia + /// Erich Eichinger + /// + /// + /// $Id: CachedAopProxyFactory.cs,v 1.1 2007/08/02 04:15:21 markpollack Exp $ + [Serializable] + public class CachedAopProxyFactory : DefaultAopProxyFactory + { + /// + /// The shared instance for this class. + /// + private static readonly ILog logger = LogManager.GetLogger(typeof(CachedAopProxyFactory)); + + private static Hashtable typeCache = new Hashtable(); + + /// + /// Generates the proxy type and caches the + /// instance against the base type and the interfaces to proxy. + /// + /// + /// The to use + /// + /// The generated or cached proxy class. + protected override Type BuildProxyType(IProxyTypeBuilder typeBuilder) + { + ProxyTypeCacheKey cacheKey = new ProxyTypeCacheKey( + typeBuilder.BaseType, typeBuilder.TargetType, typeBuilder.Interfaces); + Type proxyType = null; + lock (typeCache) + { + proxyType = typeCache[cacheKey] as Type; + if (proxyType == null) + { + proxyType = typeBuilder.BuildProxyType(); + typeCache[cacheKey] = proxyType; + } + else + { + #region Instrumentation + + if (logger.IsInfoEnabled) + { + logger.Info(String.Format( + "AOP proxy type found in cache for '{0}'.", cacheKey)); + } + + #endregion + } + } + return proxyType; + } + + #region ProxyTypeCacheKey inner class implementation + + /// + /// Uniquely identifies a proxytype in the cache + /// + private sealed class ProxyTypeCacheKey + { + private sealed class HashCodeComparer : IComparer + { + public int Compare(object x, object y) + { + return x.GetHashCode().CompareTo(y.GetHashCode()); + } + } + + private static IComparer interfaceComparer = new HashCodeComparer(); + + private Type baseType; + private Type targetType; + private Type[] interfaceTypes; + + public ProxyTypeCacheKey(Type baseType, Type targetType, Type[] interfaceTypes) + { + this.baseType = baseType; + this.targetType = targetType; + Array.Sort(interfaceTypes, interfaceComparer); // sort by GetHashcode()? to have a defined order + this.interfaceTypes = interfaceTypes; + } + + public override bool Equals(object obj) + { + if (this == obj) + { + return true; + } + ProxyTypeCacheKey proxyTypeCacheKey = obj as ProxyTypeCacheKey; + if (proxyTypeCacheKey == null) + { + return false; + } + if (!Equals(targetType, proxyTypeCacheKey.targetType)) + { + return false; + } + if (!Equals(baseType, proxyTypeCacheKey.baseType)) + { + return false; + } + for (int i = 0; i < interfaceTypes.Length; i++) + { + if (!Equals(interfaceTypes[i], proxyTypeCacheKey.interfaceTypes[i])) + { + return false; + } + } + return true; + } + + public override int GetHashCode() + { + int result = baseType.GetHashCode(); + result = 29*result + targetType.GetHashCode(); + for (int i = 0; i < interfaceTypes.Length; i++) + { + result = 29 * result + interfaceTypes[i].GetHashCode(); + } + return result; + } + + public override string ToString() + { + StringBuilder buffer = new StringBuilder(); + buffer.Append("baseType=" + baseType + "; "); + buffer.Append("targetType=" + targetType + "; "); + buffer.Append("interfaceTypes=["); + foreach (Type intf in interfaceTypes) + { + buffer.Append(intf + ";"); + } + buffer.Append("]; "); + return buffer.ToString(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/CompositionAopProxyTypeBuilder.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/CompositionAopProxyTypeBuilder.cs new file mode 100644 index 00000000..2f2256f8 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/CompositionAopProxyTypeBuilder.cs @@ -0,0 +1,213 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; + +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Builds an AOP proxy type using composition. + /// + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: CompositionAopProxyTypeBuilder.cs,v 1.15 2007/12/07 17:58:56 bbaia Exp $ + public class CompositionAopProxyTypeBuilder : AbstractAopProxyTypeBuilder + { + #region Fields + + private const string PROXY_TYPE_NAME = "CompositionAopProxy"; + + private IAdvised advised; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// The proxy configuration. + public CompositionAopProxyTypeBuilder(IAdvised advised) + { + this.advised = advised; + + Name = PROXY_TYPE_NAME; + BaseType = typeof(BaseCompositionAopProxy); + TargetType = advised.TargetSource.TargetType.IsInterface ? typeof(object) : advised.TargetSource.TargetType; + Interfaces = GetProxiableInterfaces(advised.Interfaces); + ProxyTargetAttributes = advised.ProxyTargetAttributes; + } + + #endregion + + #region IProxyTypeBuilder Members + + /// + /// Creates the proxy type. + /// + /// The generated proxy type. + public override Type BuildProxyType() + { + IDictionary targetMethods = new Hashtable(); + + TypeBuilder typeBuilder = CreateTypeBuilder(Name, BaseType); + + // apply custom attributes to the proxy type. + ApplyTypeAttributes(typeBuilder, TargetType); + + if (advised.IsSerializable) + { + typeBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute(typeof(SerializableAttribute))); + ImplementSerializationConstructor(typeBuilder); + } + + // create constructors + ImplementConstructors(typeBuilder); + + // implement interfaces + IDictionary interfaceMap = advised.InterfaceMap; + foreach (Type intf in Interfaces) + { + object target = interfaceMap[intf]; + if (target == null) + { + // implement interface + ImplementInterface(typeBuilder, + new TargetAopProxyMethodBuilder(typeBuilder, this, false, targetMethods), + intf, TargetType); + } + else if (target is IIntroductionAdvisor) + { + // implement introduction + ImplementInterface(typeBuilder, + new IntroductionProxyMethodBuilder(typeBuilder, this, targetMethods, advised.IndexOf((IIntroductionAdvisor) target)), + intf, TargetType); + } + } + + Type proxyType; + proxyType = typeBuilder.CreateType(); + + // set target method references + foreach (DictionaryEntry entry in targetMethods) + { + FieldInfo field = proxyType.GetField((string) entry.Key, BindingFlags.NonPublic | BindingFlags.Static); + field.SetValue(proxyType, (MethodInfo) entry.Value); + } + + return proxyType; + } + + #endregion + + #region IAopProxyTypeGenerator Members + + /// + /// Generates the IL instructions that pushes + /// the current + /// instance on stack. + /// + /// The IL generator to use. + public override void PushAdvisedProxy(ILGenerator il) + { + il.Emit(OpCodes.Ldarg_0); + } + + #endregion + + #region Protected Methods + + /// + /// Implements serialization constructor. + /// + /// Type builder to use. + private void ImplementSerializationConstructor(TypeBuilder typeBuilder) + { + ConstructorBuilder cb = + typeBuilder.DefineConstructor(MethodAttributes.Family, + CallingConventions.Standard, + new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }); + + ILGenerator il = cb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Call, References.BaseCompositionAopProxySerializationConstructor); + il.Emit(OpCodes.Ret); + } + + /// + /// Implements constructors for the proxy class. + /// + /// + ///

    + /// This implementation calls the base constructor. + ///

    + ///
    + /// + /// The builder to use. + /// + protected override void ImplementConstructors(TypeBuilder typeBuilder) + { + ConstructorBuilder cb = + typeBuilder.DefineConstructor(References.BaseCompositionAopProxyConstructor.Attributes, + References.BaseCompositionAopProxyConstructor.CallingConvention, + new Type[] { typeof(IAdvised) }); + + ILGenerator il = cb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, References.BaseCompositionAopProxyConstructor); + il.Emit(OpCodes.Ret); + } + + #endregion + + #region Public Methods + + /// + /// Determines if the specified + /// is one of those generated by this builder. + /// + /// The type to check. + /// + /// if the type is a composition-based proxy; + /// otherwise . + /// + public static bool IsCompositionProxy(Type type) + { + return type.FullName.StartsWith(PROXY_TYPE_NAME); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/DecoratorAopProxyTypeBuilder.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/DecoratorAopProxyTypeBuilder.cs new file mode 100644 index 00000000..1f76c7f5 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/DecoratorAopProxyTypeBuilder.cs @@ -0,0 +1,317 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; + +using Spring.Util; +using Spring.Proxy; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Builds an AOP proxy type using the decorator pattern. + /// + /// Bruno Baia + /// $Id: DecoratorAopProxyTypeBuilder.cs,v 1.17 2007/12/07 17:58:56 bbaia Exp $ + public class DecoratorAopProxyTypeBuilder : AbstractAopProxyTypeBuilder + { + #region Fields + + private const string PROXY_TYPE_NAME = "DecoratorAopProxy"; + + private IAdvised advised; + + /// + /// AdvisedProxy instance calls should be delegated to. + /// + protected FieldBuilder advisedProxyField; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// The proxy configuration. + public DecoratorAopProxyTypeBuilder(IAdvised advised) + { + if (!ReflectionUtils.IsTypeVisible(advised.TargetSource.TargetType, DynamicProxyManager.ASSEMBLY_NAME)) + { + throw new AopConfigException(String.Format( + "Cannot create decorator-based IAopProxy for a non visible class [{0}]", + advised.TargetSource.TargetType.FullName)); + } + if (advised.TargetSource.TargetType.IsSealed) + { + throw new AopConfigException(String.Format( + "Cannot create decorator-based IAopProxy for a sealed class [{0}]", + advised.TargetSource.TargetType.FullName)); + } + this.advised = advised; + + Name = PROXY_TYPE_NAME; + TargetType = advised.TargetSource.TargetType.IsInterface ? typeof(object) : advised.TargetSource.TargetType; + BaseType = TargetType; + Interfaces = GetProxiableInterfaces(advised.Interfaces); + ProxyTargetAttributes = advised.ProxyTargetAttributes; + } + + #endregion + + #region IProxyTypeBuilder Members + + /// + /// Creates the proxy type. + /// + /// The generated proxy class. + public override Type BuildProxyType() + { + IDictionary targetMethods = new Hashtable(); + + TypeBuilder typeBuilder = CreateTypeBuilder(Name, BaseType); + + // apply custom attributes to the proxy type. + ApplyTypeAttributes(typeBuilder, TargetType); + + // declare fields + DeclareAdvisedProxyInstanceField(typeBuilder); + + // implement ISerializable if possible + if (advised.IsSerializable) + { + typeBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute(typeof(SerializableAttribute))); + ImplementSerializationConstructor(typeBuilder); + ImplementGetObjectDataMethod(typeBuilder); + } + + // create constructors + ImplementConstructors(typeBuilder); + + // implement interfaces + IDictionary interfaceMap = advised.InterfaceMap; + foreach (Type intf in Interfaces) + { + object target = interfaceMap[intf]; + if (target == null) + { + // implement interface (proxy only final methods) + ImplementInterface(typeBuilder, + new TargetAopProxyMethodBuilder(typeBuilder, this, true, targetMethods), + intf, TargetType, false); + } + else if (target is IIntroductionAdvisor) + { + // implement introduction + ImplementInterface(typeBuilder, + new IntroductionProxyMethodBuilder(typeBuilder, this, targetMethods, advised.IndexOf((IIntroductionAdvisor)target)), + intf, TargetType); + } + } + + // inherit from target type + InheritType(typeBuilder, + new TargetAopProxyMethodBuilder(typeBuilder, this, false, targetMethods), + TargetType); + + // implement IAdvised interface + ImplementInterface(typeBuilder, + new IAdvisedProxyMethodBuilder(typeBuilder, this), + typeof(IAdvised), TargetType); + + // implement IAopProxy interface + ImplementIAopProxy(typeBuilder); + + Type proxyType; + proxyType = typeBuilder.CreateType(); + + // set target method references + foreach (DictionaryEntry entry in targetMethods) + { + FieldInfo field = proxyType.GetField((string)entry.Key, BindingFlags.NonPublic | BindingFlags.Static); + field.SetValue(proxyType, (MethodInfo)entry.Value); + } + + return proxyType; + } + + #endregion + + #region IAopProxyTypeGenerator Members + + /// + /// Generates the IL instructions that pushes + /// the current + /// instance on stack. + /// + /// The IL generator to use. + public override void PushAdvisedProxy(ILGenerator il) + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, advisedProxyField); + } + + #endregion + + #region Protected Methods + + /// + /// Declares field that holds the + /// instance used by the proxy. + /// + /// + /// The builder to use for code generation. + /// + protected virtual void DeclareAdvisedProxyInstanceField(TypeBuilder builder) + { + advisedProxyField = builder.DefineField("__advisedProxy", typeof(AdvisedProxy), FieldAttributes.Private); + } + + /// + /// Implements serialization method. + /// + /// + private void ImplementGetObjectDataMethod(TypeBuilder typeBuilder) + { + typeBuilder.AddInterfaceImplementation(typeof(ISerializable)); + + MethodBuilder mb = + typeBuilder.DefineMethod("GetObjectData", + MethodAttributes.Public | MethodAttributes.HideBySig | + MethodAttributes.NewSlot | MethodAttributes.Virtual, + typeof (void), + new Type[] {typeof (SerializationInfo), typeof (StreamingContext)}); + + ILGenerator il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldstr, "advisedProxy"); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, advisedProxyField); + il.EmitCall(OpCodes.Callvirt, References.AddSerializationValue, null); + il.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(mb, typeof(ISerializable).GetMethod("GetObjectData")); + } + + + /// + /// Implements serialization constructor. + /// + /// Type builder to use. + private void ImplementSerializationConstructor(TypeBuilder typeBuilder) + { + ConstructorBuilder cb = + typeBuilder.DefineConstructor(MethodAttributes.Family, + CallingConventions.Standard, + new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }); + + ILGenerator il = cb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldstr, "advisedProxy"); + il.Emit(OpCodes.Ldtoken, typeof(AdvisedProxy)); + il.EmitCall(OpCodes.Call, References.GetTypeFromHandle, null); + il.EmitCall(OpCodes.Callvirt, References.GetSerializationValue, null); + il.Emit(OpCodes.Castclass, typeof(AdvisedProxy)); + il.Emit(OpCodes.Stfld, advisedProxyField); + il.Emit(OpCodes.Ret); + } + + /// + /// Implements constructors for the proxy class. + /// + /// + ///

    + /// This implementation creates a new instance + /// of the class. + ///

    + ///
    + /// + /// The builder to use. + /// + protected override void ImplementConstructors(TypeBuilder typeBuilder) + { + ConstructorBuilder cb = + typeBuilder.DefineConstructor(References.ObjectConstructor.Attributes, + References.ObjectConstructor.CallingConvention, + new Type[] { typeof(IAdvised) }); + + ILGenerator il = cb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Newobj, References.AdvisedProxyConstructor); + il.Emit(OpCodes.Stfld, advisedProxyField); + il.Emit(OpCodes.Ret); + } + + /// + /// Implements interface. + /// + /// The type builder to use. + protected virtual void ImplementIAopProxy(TypeBuilder typeBuilder) + { + Type intf = typeof(IAopProxy); + MethodInfo getProxyMethod = intf.GetMethod("GetProxy", Type.EmptyTypes); + + typeBuilder.AddInterfaceImplementation(intf); + + MethodBuilder mb = typeBuilder.DefineMethod(typeof(IAdvised).FullName + "." + getProxyMethod.Name, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, + getProxyMethod.CallingConvention, getProxyMethod.ReturnType, Type.EmptyTypes); + + ILGenerator il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(mb, getProxyMethod); + } + + #endregion + + #region Public Methods + + /// + /// Determines if the specified + /// is one of those generated by this builder. + /// + /// The type to check. + /// + /// if the type is a decorator-based proxy; + /// otherwise . + /// + public static bool IsDecoratorProxy(Type type) + { + return type.FullName.StartsWith(PROXY_TYPE_NAME); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/DefaultAopProxyFactory.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/DefaultAopProxyFactory.cs new file mode 100644 index 00000000..8f509519 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/DefaultAopProxyFactory.cs @@ -0,0 +1,110 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Proxy; +using Spring.Aop.Target; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Default implementation of the + /// interface, + /// either creating a decorator-based dynamic proxy or + /// a composition-based dynamic proxy. + /// + /// + ///

    + /// Creates a decorator-base proxy if one the following is true : + /// - the "ProxyTargetType" property is set + /// - no interfaces have been specified + ///

    + ///

    + /// In general, specify "ProxyTargetType" to enforce a decorator-based proxy, + /// or specify one or more interfaces to use a composition-based proxy. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// Bruno Baia (.NET) + /// + /// $Id: DefaultAopProxyFactory.cs,v 1.2 2007/08/02 16:28:29 bbaia Exp $ + [Serializable] + public class DefaultAopProxyFactory : IAopProxyFactory + { + + /// + /// Creates an for the + /// supplied configuration. + /// + /// The AOP configuration. + /// An . + /// + /// If the supplied configuration is + /// invalid. + /// + /// + public virtual IAopProxy CreateAopProxy(AdvisedSupport advisedSupport) + { + if (advisedSupport == null) + { + throw new AopConfigException("Cannot create IAopProxy with null ProxyConfig"); + } + if (advisedSupport.Advisors.Length == 0 && advisedSupport.TargetSource == EmptyTargetSource.Empty) + { + throw new AopConfigException("Cannot create IAopProxy with no advisors and no target source"); + } + if (advisedSupport.ProxyType == null) + { + IProxyTypeBuilder typeBuilder; + if ((advisedSupport.ProxyTargetType) || + (advisedSupport.Interfaces.Length == 0)) + { + typeBuilder = new DecoratorAopProxyTypeBuilder(advisedSupport); + } + else + { + typeBuilder = new CompositionAopProxyTypeBuilder(advisedSupport); + } + advisedSupport.ProxyType = BuildProxyType(typeBuilder); + advisedSupport.ProxyConstructor = advisedSupport.ProxyType.GetConstructor(new Type[] { typeof(IAdvised) }); + } + + return (IAopProxy)advisedSupport.ProxyConstructor.Invoke(new object[] { advisedSupport }); + } + + /// + /// Generates the proxy type. + /// + /// + /// The to use + /// + /// The generated proxy class. + protected virtual Type BuildProxyType(IProxyTypeBuilder typeBuilder) + { + return typeBuilder.BuildProxyType(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/IAdvisedProxyMethodBuilder.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/IAdvisedProxyMethodBuilder.cs new file mode 100644 index 00000000..5c21830b --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/IAdvisedProxyMethodBuilder.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Util; +using Spring.Proxy; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// implementation that delegates + /// method calls to an instance. + /// + /// Bruno Baia + /// $Id: IAdvisedProxyMethodBuilder.cs,v 1.3 2006/11/12 01:37:47 bbaia Exp $ + public class IAdvisedProxyMethodBuilder : TargetProxyMethodBuilder + { + #region Fields + + /// + /// The implementation to use. + /// + private IAopProxyTypeGenerator _aopProxyGenerator; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the method builder. + /// + /// The type builder to use. + /// + /// The implementation to use. + /// + public IAdvisedProxyMethodBuilder( + TypeBuilder typeBuilder, IAopProxyTypeGenerator aopProxyGenerator) + : base(typeBuilder, aopProxyGenerator, true) + { + this._aopProxyGenerator = aopProxyGenerator; + } + + #endregion + + #region Protected Methods + + /// + /// Generates the IL instructions that pushes + /// the target instance on which calls should be delegated to. + /// + /// The IL generator to use. + protected override void PushTarget(ILGenerator il) + { + _aopProxyGenerator.PushAdvisedProxy(il); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/IAopProxyTypeGenerator.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/IAopProxyTypeGenerator.cs new file mode 100644 index 00000000..5aaa2eb8 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/IAopProxyTypeGenerator.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection.Emit; + +using Spring.Proxy; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Describes the operations that generates IL instructions + /// used to build the Aop proxy type. + /// + /// Bruno Baia + /// $Id: IAopProxyTypeGenerator.cs,v 1.2 2006/11/05 20:36:59 bbaia Exp $ + public interface IAopProxyTypeGenerator : IProxyTypeGenerator + { + /// + /// Generates the IL instructions that pushes + /// the current + /// instance on stack. + /// + /// The IL generator to use. + void PushAdvisedProxy(ILGenerator il); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/InheritanceAopProxyTypeBuilder.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/InheritanceAopProxyTypeBuilder.cs new file mode 100644 index 00000000..b1fc1654 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/InheritanceAopProxyTypeBuilder.cs @@ -0,0 +1,397 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Runtime.Serialization; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Core; +using Spring.Proxy; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Builds an AOP proxy type using inheritance. + /// + /// Bruno Baia + /// $Id: InheritanceAopProxyTypeBuilder.cs,v 1.2 2008/03/03 09:28:50 bbaia Exp $ + public class InheritanceAopProxyTypeBuilder : AbstractAopProxyTypeBuilder + { + #region Fields + + private const string PROXY_TYPE_NAME = "InheritanceAopProxy"; + + private IAdvised advised; + private bool proxyDeclaredMembersOnly = true; + + /// + /// AdvisedProxy instance calls should be delegated to. + /// + protected FieldBuilder advisedProxyField; + + #endregion + + #region Properties + + /// + /// Gets or sets a value indicating whether inherited members should be proxied. + /// + /// + /// if inherited members should be proxied; + /// otherwise, . + /// + public bool ProxyDeclaredMembersOnly + { + get { return proxyDeclaredMembersOnly; } + set { proxyDeclaredMembersOnly = value; } + } + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// The proxy configuration. + public InheritanceAopProxyTypeBuilder(IAdvised advised) + { + if (!ReflectionUtils.IsTypeVisible(advised.TargetSource.TargetType, DynamicProxyManager.ASSEMBLY_NAME)) + { + throw new AopConfigException(String.Format( + "Cannot create inheritance-based IAopProxy for a non visible class [{0}]", + advised.TargetSource.TargetType.FullName)); + } + if (advised.TargetSource.TargetType.IsSealed) + { + throw new AopConfigException(String.Format( + "Cannot create inheritance-based IAopProxy for a sealed class [{0}]", + advised.TargetSource.TargetType.FullName)); + } + this.advised = advised; + + Name = PROXY_TYPE_NAME; + TargetType = advised.TargetSource.TargetType; + BaseType = TargetType; + Interfaces = GetProxiableInterfaces(advised.Interfaces); + ProxyTargetAttributes = advised.ProxyTargetAttributes; + } + + #endregion + + #region IProxyTypeBuilder Members + + /// + /// Creates the proxy type. + /// + /// The generated proxy class. + public override Type BuildProxyType() + { + IDictionary targetMethods = new Hashtable(); + IDictionary proxyMethods = new Hashtable(); + + TypeBuilder typeBuilder = CreateTypeBuilder(Name, BaseType); + + // apply custom attributes to the proxy type. + ApplyTypeAttributes(typeBuilder, TargetType); + + // declare fields + DeclareAdvisedProxyInstanceField(typeBuilder); + + // implement ISerializable if possible + if (advised.IsSerializable) + { + typeBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute(typeof(SerializableAttribute))); + ImplementSerializationConstructor(typeBuilder); + ImplementGetObjectDataMethod(typeBuilder); + } + + // create constructors + ImplementConstructors(typeBuilder); + + // implement interfaces + IDictionary interfaceMap = advised.InterfaceMap; + foreach (Type intf in Interfaces) + { + object target = interfaceMap[intf]; + if (target == null) + { + // implement interface (proxy only final methods) + ImplementInterface(typeBuilder, + new BaseAopProxyMethodBuilder(typeBuilder, this, targetMethods, proxyMethods), + intf, TargetType, false); + } + else if (target is IIntroductionAdvisor) + { + // implement introduction + ImplementInterface(typeBuilder, + new IntroductionProxyMethodBuilder(typeBuilder, this, targetMethods, advised.IndexOf((IIntroductionAdvisor)target)), + intf, TargetType); + } + } + + // inherit from target type + InheritType(typeBuilder, + new BaseAopProxyMethodBuilder(typeBuilder, this, targetMethods, proxyMethods), + TargetType, ProxyDeclaredMembersOnly); + + // implement IAdvised interface + ImplementInterface(typeBuilder, + new IAdvisedProxyMethodBuilder(typeBuilder, this), + typeof(IAdvised), TargetType); + + // implement IAopProxy interface + ImplementIAopProxy(typeBuilder); + + Type proxyType; + proxyType = typeBuilder.CreateType(); + + // set target method references + foreach (DictionaryEntry entry in targetMethods) + { + FieldInfo field = proxyType.GetField((string)entry.Key, BindingFlags.NonPublic | BindingFlags.Static); + field.SetValue(proxyType, (MethodInfo)entry.Value); + } + + // set proxy method references + foreach (DictionaryEntry entry in proxyMethods) + { + FieldInfo field = proxyType.GetField((string)entry.Key, BindingFlags.NonPublic | BindingFlags.Static); + field.SetValue(proxyType, FindProxyMethod(proxyType, (MethodInfo)entry.Value)); + } + + return proxyType; + } + + private MethodInfo FindProxyMethod(Type targetType, MethodInfo method) + { + ComposedCriteria searchCriteria = new ComposedCriteria(); + searchCriteria.Add(new MethodNameMatchCriteria("proxy_" + method.Name)); + searchCriteria.Add(new MethodParametersCountCriteria(method.GetParameters().Length)); +#if NET_2_0 + searchCriteria.Add(new MethodGenericArgumentsCountCriteria( + method.GetGenericArguments().Length)); +#endif + searchCriteria.Add(new MethodParametersCriteria(ReflectionUtils.GetParameterTypes(method))); + + MemberInfo[] matchingMethods = targetType.FindMembers( + MemberTypes.Method, + BindingFlags.Instance | BindingFlags.Public, + new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria), + searchCriteria); + + if (matchingMethods != null && matchingMethods.Length == 1) + { + return matchingMethods[0] as MethodInfo; + } + else + { + throw new AmbiguousMatchException(); + } + } + + #endregion + + #region IAopProxyTypeGenerator Members + + /// + /// Generates the IL instructions that pushes + /// the target instance on which calls should be delegated to. + /// + /// The IL generator to use. + public override void PushTarget(ILGenerator il) + { + il.Emit(OpCodes.Ldarg_0); + } + + /// + /// Generates the IL instructions that pushes + /// the current + /// instance on stack. + /// + /// The IL generator to use. + public override void PushAdvisedProxy(ILGenerator il) + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, advisedProxyField); + } + + #endregion + + #region Protected Methods + + /// + /// Declares field that holds the + /// instance used by the proxy. + /// + /// + /// The builder to use for code generation. + /// + protected virtual void DeclareAdvisedProxyInstanceField(TypeBuilder builder) + { + advisedProxyField = builder.DefineField("__advisedProxy", typeof(AdvisedProxy), FieldAttributes.Private); + } + + /// + /// Implements serialization method. + /// + /// + private void ImplementGetObjectDataMethod(TypeBuilder typeBuilder) + { + typeBuilder.AddInterfaceImplementation(typeof(ISerializable)); + + MethodBuilder mb = + typeBuilder.DefineMethod("GetObjectData", + MethodAttributes.Public | MethodAttributes.HideBySig | + MethodAttributes.NewSlot | MethodAttributes.Virtual, + typeof(void), + new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }); + + ILGenerator il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldstr, "advisedProxy"); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, advisedProxyField); + il.EmitCall(OpCodes.Callvirt, References.AddSerializationValue, null); + il.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(mb, typeof(ISerializable).GetMethod("GetObjectData")); + } + + /// + /// Implements serialization constructor. + /// + /// Type builder to use. + private void ImplementSerializationConstructor(TypeBuilder typeBuilder) + { + ConstructorBuilder cb = + typeBuilder.DefineConstructor(MethodAttributes.Family, + CallingConventions.Standard, + new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }); + + ILGenerator il = cb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldstr, "advisedProxy"); + il.Emit(OpCodes.Ldtoken, typeof(AdvisedProxy)); + il.EmitCall(OpCodes.Call, References.GetTypeFromHandle, null); + il.EmitCall(OpCodes.Callvirt, References.GetSerializationValue, null); + il.Emit(OpCodes.Castclass, typeof(AdvisedProxy)); + il.Emit(OpCodes.Stfld, advisedProxyField); + il.Emit(OpCodes.Ret); + } + + /// + /// Defines the types of the parameters for the specified constructor. + /// + /// The constructor to use. + /// The types for constructor's parameters. + protected override Type[] DefineConstructorParameters(ConstructorInfo constructor) + { + Type[] currentParams = ReflectionUtils.GetParameterTypes(constructor.GetParameters()); + Type[] newParams = new Type[currentParams.Length + 1]; + + newParams[currentParams.Length] = typeof(IAdvised); + currentParams.CopyTo(newParams, 0); + + return newParams; + } + + /// + /// Generates the proxy constructor. + /// + /// + ///

    + /// This implementation creates instance of the AdvisedProxy object. + ///

    + ///
    + /// The constructor builder to use. + /// The IL generator to use. + /// The constructor to delegate the creation to. + protected override void GenerateConstructor( + ConstructorBuilder builder, ILGenerator il, ConstructorInfo constructor) + { + int paramCount = constructor.GetParameters().Length; + il.Emit(OpCodes.Ldarg_0); + for (int i = 0; i < paramCount; i++) + { + il.Emit(OpCodes.Ldarg_S, i + 1); + } + il.Emit(OpCodes.Call, constructor); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_S, paramCount + 1); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Newobj, References.AdvisedProxyConstructor); + il.Emit(OpCodes.Stfld, advisedProxyField); + } + + /// + /// Implements interface. + /// + /// The type builder to use. + protected virtual void ImplementIAopProxy(TypeBuilder typeBuilder) + { + Type intf = typeof(IAopProxy); + MethodInfo getProxyMethod = intf.GetMethod("GetProxy", Type.EmptyTypes); + + typeBuilder.AddInterfaceImplementation(intf); + + MethodBuilder mb = typeBuilder.DefineMethod(typeof(IAdvised).FullName + "." + getProxyMethod.Name, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, + getProxyMethod.CallingConvention, getProxyMethod.ReturnType, Type.EmptyTypes); + + ILGenerator il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(mb, getProxyMethod); + } + + #endregion + + #region Public Methods + + /// + /// Determines if the specified + /// is one of those generated by this builder. + /// + /// The type to check. + /// + /// if the type is a inheritance-based proxy; + /// otherwise . + /// + public static bool IsInheritanceProxy(Type type) + { + return type.FullName.StartsWith(PROXY_TYPE_NAME); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/IntroductionProxyMethodBuilder.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/IntroductionProxyMethodBuilder.cs new file mode 100644 index 00000000..b58f1829 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/IntroductionProxyMethodBuilder.cs @@ -0,0 +1,117 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// implementation + /// that delegates method calls to introduction object. + /// + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: IntroductionProxyMethodBuilder.cs,v 1.5 2006/11/05 20:36:59 bbaia Exp $ + public class IntroductionProxyMethodBuilder : AbstractAopProxyMethodBuilder + { + #region Fields + + /// + /// The index of the introduction to delegate call to. + /// + protected int index; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the method builder. + /// + /// The type builder to use. + /// + /// The implementation to use. + /// + /// + /// + /// + /// index of the introduction to delegate call to + public IntroductionProxyMethodBuilder( + TypeBuilder typeBuilder, IAopProxyTypeGenerator aopProxyGenerator, + IDictionary targetMethods, int index) + : base(typeBuilder, aopProxyGenerator, true, targetMethods) + { + this.index = index; + } + + #endregion + + #region Protected Methods + + /// + /// Generates the IL instructions that pushes + /// the introduction type on stack. + /// + /// The IL generator to use. + protected override void PushTargetType(ILGenerator il) + { + PushTarget(il); + il.EmitCall(OpCodes.Call, References.GetTypeMethod, null); + } + + /// + /// Generates the IL instructions that pushes + /// the introduction instance on stack. + /// + /// The IL generator to use. + protected override void PushTarget(ILGenerator il) + { + PushAdvisedProxy(il); + il.Emit(OpCodes.Ldfld, References.IntroductionsField); + il.Emit(OpCodes.Ldc_I4, index); + il.Emit(OpCodes.Ldelem_Ref); + } + + /// + /// Calls proxied method directly. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected override void CallDirectProxiedMethod( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + CallDirectTargetMethod(il, interfaceMethod); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/TargetAopProxyMethodBuilder.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/TargetAopProxyMethodBuilder.cs new file mode 100644 index 00000000..ac3190dc --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicProxy/TargetAopProxyMethodBuilder.cs @@ -0,0 +1,152 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// implementation + /// that delegates method calls to target object. + /// + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: TargetAopProxyMethodBuilder.cs,v 1.3 2008/01/29 18:24:50 markpollack Exp $ + public class TargetAopProxyMethodBuilder : AbstractAopProxyMethodBuilder + { + #region Fields + + /// + /// The local variable to store + /// the instance. + /// + protected LocalBuilder targetSource; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the method builder. + /// + /// The type builder to use. + /// + /// The implementation to use. + /// + /// + /// if the interface is to be + /// implemented explicitly; otherwise . + /// + /// + /// The dictionary to cache the list of target + /// s. + /// + public TargetAopProxyMethodBuilder(TypeBuilder typeBuilder, + IAopProxyTypeGenerator aopProxyGenerator, bool explicitImplementation, IDictionary targetMethods) + : base(typeBuilder, aopProxyGenerator, explicitImplementation, targetMethods) + { + } + + #endregion + + #region Protected Methods + + /// + /// Creates local variable declarations. + /// + /// The IL generator to use. + /// The method to proxy. + protected override void DeclareLocals(ILGenerator il, MethodInfo method) + { + base.DeclareLocals(il, method); + targetSource = il.DeclareLocal(typeof(ITargetSourceWrapper)); + +#if DEBUG + targetSource.SetLocalSymInfo("targetSource"); +#endif + } + + /// + /// Generates method logic. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected override void GenerateMethodLogic( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + Label jmpEndFinally = il.DefineLabel(); + + // save target source so we can call Dispose later + PushAdvisedProxy(il); + il.Emit(OpCodes.Ldfld, References.TargetSourceWrapperField); + il.Emit(OpCodes.Stloc, targetSource); + + // open try/finally block + il.BeginExceptionBlock(); + + base.GenerateMethodLogic(il, method, interfaceMethod); + + // open finally block + il.BeginFinallyBlock(); + + // call Dispose on target source + il.Emit(OpCodes.Ldloc, targetSource); + il.Emit(OpCodes.Brfalse, jmpEndFinally); + il.Emit(OpCodes.Ldloc, targetSource); + il.EmitCall(OpCodes.Callvirt, References.DisposeMethod, null); + + il.MarkLabel(jmpEndFinally); + + // close try/finally block + il.EndExceptionBlock(); + } + + /// + /// Calls proxied method directly. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected override void CallDirectProxiedMethod( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + if (interfaceMethod != null) + CallDirectTargetMethod(il, interfaceMethod); + else + CallDirectTargetMethod(il, method); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicTargetSourceWrapper.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicTargetSourceWrapper.cs new file mode 100644 index 00000000..d7f51989 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicTargetSourceWrapper.cs @@ -0,0 +1,98 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Decorates a target source with the + /// interface. + /// + /// + ///

    + /// This implementation will release the target object when said object + /// is disposed. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: DynamicTargetSourceWrapper.cs,v 1.4 2007/03/16 04:01:17 aseovic Exp $ + [Serializable] + public sealed class DynamicTargetSourceWrapper : ITargetSourceWrapper + { + private ITargetSource targetSource; + private object target; + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The target object that proxy methods will be delegated to. + /// + /// + /// If the supplied is + /// . + /// + internal DynamicTargetSourceWrapper(ITargetSource targetSource) + { + AssertUtils.ArgumentNotNull(targetSource, "targetSource"); + this.targetSource = targetSource; + } + + /// + /// Returns the target object that proxy methods will be delegated to. + /// + /// The target object. + public object GetTarget() + { + if (this.target == null) + { + this.target = targetSource.GetTarget(); + } + + return this.target; + } + + /// + /// Releases the dynamic target when this object is disposed. + /// + public void Dispose() + { + GC.SuppressFinalize(this); + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (disposing && this.target != null) + { + this.targetSource.ReleaseTarget(this.target); + this.target = null; + } + } + } +} diff --git a/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs b/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs new file mode 100644 index 00000000..ca75241a --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs @@ -0,0 +1,104 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// implementation + /// that caches advisor chains on a per-advised-method basis. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: HashtableCachingAdvisorChainFactory.cs,v 1.9 2007/03/16 04:01:18 aseovic Exp $ + [Serializable] + public sealed class HashtableCachingAdvisorChainFactory : IAdvisorChainFactory + { + private IDictionary methodCache = new Hashtable(); + + /// + /// Gets the list of and + /// + /// instances for the supplied . + /// + /// The proxy configuration object. + /// The object proxy. + /// + /// The method for which the interceptors are to be evaluated. + /// + /// + /// The of the target object. + /// + /// + /// The list of and + /// + /// instances for the supplied . + /// + public IList GetInterceptors(IAdvised advised, object proxy, MethodInfo method, Type targetType) + { + IList cached = (IList) this.methodCache[method]; + if (cached == null) + { + // recalculate... + cached = AdvisorChainFactoryUtils.CalculateInterceptors(advised, proxy, method, targetType); + this.methodCache[method] = cached; + } + return cached; + } + + /// + /// Invoked when the first proxy is created. + /// + /// + /// The relevant source. + /// + public void Activated(AdvisedSupport source) + { + } + + /// + /// Invoked when advice is changed after a proxy is created. + /// + /// + /// The relevant source. + /// + public void AdviceChanged(AdvisedSupport source) + { + methodCache.Clear(); + } + + /// + /// Invoked when interfaces are changed after a proxy is created. + /// + /// + /// The relevant source. + /// + public void InterfacesChanged(AdvisedSupport source) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/IAdvised.cs b/src/Spring/Spring.Aop/Aop/Framework/IAdvised.cs new file mode 100644 index 00000000..7fe13cfa --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/IAdvised.cs @@ -0,0 +1,500 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using AopAlliance.Aop; +using Spring.Aop; +using Spring.Proxy; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Configuration data for an AOP proxy factory. + /// + /// + ///

    + /// This configuration includes the + /// s, + /// s, and (any) proxied interfaces. + ///

    + ///

    + /// Any AOP proxy obtained from Spring.NET can be cast to this interface to + /// allow the manipulation of said proxy's AOP advice. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IAdvised.cs,v 1.15 2007/10/10 18:07:38 markpollack Exp $ + /// + [ProxyIgnore] + public interface IAdvised + { + /// + /// Should proxies obtained from this configuration expose + /// the AOP proxy to the + /// class? + /// + /// + ///

    + /// This is useful if an advised object needs to call another advised + /// method on itself. (If it uses the this reference (Me + /// in Visual Basic.NET), the invocation will not be advised). + ///

    + ///
    + bool ExposeProxy { get; } + + /// + /// Gets the + /// + /// implementation that will be used to get the interceptor + /// chains for the advised + /// . + /// + /// + /// The + /// implementation that will be used to get the interceptor + /// chains for the advised + /// . + /// + IAdvisorChainFactory AdvisorChainFactory { get; } + + /// + /// Is the target to be proxied in addition + /// to any interfaces declared on the proxied ? + /// + bool ProxyTargetType { get; } + + /// + /// Is target type attributes, method attributes, method's return type attributes + /// and method's parameter attributes to be proxied in addition + /// to any interfaces declared on the proxied ? + /// + bool ProxyTargetAttributes { get; } + + /// + /// Returns the collection of + /// instances that have been applied to this proxy. + /// + /// + ///

    + /// Will never return , but may return an + /// empty array (in the case where no + /// instances have been applied to + /// this proxy). + ///

    + ///
    + /// + /// The collection of + /// instances that have been applied to this proxy. + /// + IAdvisor[] Advisors { get; } + + /// + /// Returns the collection of + /// instances that have been applied to this proxy. + /// + /// + ///

    + /// Will never return , but may return an + /// empty array (in the case where no + /// instances have been + /// applied to this proxy). + ///

    + ///
    + /// + /// The collection of + /// instances that have been applied to this proxy. + /// + IIntroductionAdvisor[] Introductions { get; } + + /// + /// Returns the collection of interface s + /// to be (or that are being) proxied by this proxy. + /// + /// + /// The collection of interface s + /// to be (or that are being) proxied by this proxy. + /// + Type[] Interfaces { get; } + + /// + /// Returns the mapping of the proxied interface + /// s to their delegates. + /// + /// + /// The mapping of the proxied interface + /// s to their delegates. + /// + IDictionary InterfaceMap { get; } + + /// + /// Is this configuration frozen? + /// + /// + ///

    + /// When a config is frozen, no advice changes can be made. This is + /// useful for optimization, and useful when we don't want callers + /// to be able to manipulate configuration after casting to + /// . + ///

    + ///
    + bool IsFrozen { get; } + + /// + /// Returns the used by this + /// object. + /// + /// + /// The used by this + /// object. + /// + ITargetSource TargetSource { get; } + + /// + /// Returns a boolean specifying if this + /// instance can be serialized. + /// + /// + /// true if this instance can be serialized, false otherwise. + /// + bool IsSerializable { get; } + + /// + /// Adds the supplied to the end (or tail) + /// of the advice (interceptor) chain. + /// + /// + ///

    + /// Please be aware that Spring.NET's AOP implementation only supports + /// method advice (as encapsulated by the + /// interface). + ///

    + ///
    + /// + /// The to be added. + /// + /// + /// + void AddAdvice(IAdvice advice); + + /// + /// Adds the supplied to the supplied + /// in the advice (interceptor) chain. + /// + /// + ///

    + /// Please be aware that Spring.NET's AOP implementation only supports + /// method advice (as encapsulated by the + /// interface). + ///

    + ///
    + /// + /// The zero (0) indexed position (from the head) at which the + /// supplied is to be inserted into the + /// advice (interceptor) chain. + /// + /// + /// The to be added. + /// + /// + /// + void AddAdvice(int position, IAdvice advice); + + /// + /// Is the supplied (interface) + /// proxied? + /// + /// + /// The interface to test. + /// + /// + /// if the supplied + /// (interface) is proxied; + /// if not or the supplied + /// is . + /// + bool IsInterfaceProxied(Type intf); + + /// + /// Adds the advisors from the supplied + /// to the list of . + /// + /// + /// The to add advisors from. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be added. + /// + void AddAdvisors(IAdvisors advisors); + + /// + /// Adds the supplied to the list + /// of . + /// + /// + /// The to add. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be added. + /// + void AddAdvisor(IAdvisor advisor); + + /// + /// Adds the supplied to the list + /// of . + /// + /// + /// The index in the + /// list at which the supplied + /// is to be inserted. + /// + /// + /// The to add. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be added. + /// + void AddAdvisor(int index, IAdvisor advisor); + + /// + /// Adds the supplied to the list + /// of . + /// + /// + /// The to add. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be added. + /// + void AddIntroduction(IIntroductionAdvisor introductionAdvisor); + + /// + /// Adds the supplied to the list + /// of . + /// + /// + /// The index in the + /// list at which the supplied + /// is to be inserted. + /// + /// + /// The to add. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be added. + /// + void AddIntroduction(int index, IIntroductionAdvisor introductionAdvisor); + + /// + /// Return the index (0 based) of the supplied + /// in the interceptor + /// (advice) chain for this proxy. + /// + /// + ///

    + /// The return value of this method can be used to index into + /// the + /// list. + ///

    + ///
    + /// + /// The to search for. + /// + /// + /// The zero (0) based index of this advisor, or -1 if the + /// supplied is not an advisor for this + /// proxy. + /// + int IndexOf(IAdvisor advisor); + + /// + /// Return the index (0 based) of the supplied + /// in the introductions + /// for this proxy. + /// + /// + ///

    + /// The return value of this method can be used to index into + /// the + /// list. + ///

    + ///
    + /// + /// The to search for. + /// + /// + /// The zero (0) based index of this advisor, or -1 if the + /// supplied is not an introduction advisor + /// for this proxy. + /// + int IndexOf(IIntroductionAdvisor advisor); + + /// + /// Removes the supplied the list of advisors + /// for this proxy. + /// + /// The advisor to remove. + /// + /// if advisor was found in the list of + /// for this + /// proxy and was successfully removed; if not + /// or if the supplied is . + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be removed. + /// + bool RemoveAdvisor(IAdvisor advisor); + + /// + /// Removes the at the supplied + /// in the + /// list + /// from the list of + /// for this proxy. + /// + /// + /// The index of the to remove. + /// + /// + /// If this proxy configuration is frozen and the + /// at the supplied + /// cannot be removed; or if the supplied is out of + /// range. + /// + void RemoveAdvisor(int index); + + /// + /// Removes the supplied from the list + /// of . + /// + /// + /// The to remove. + /// + /// + /// if the supplied was + /// found in the list of + /// and successfully removed. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be removed. + /// + bool RemoveAdvice(IAdvice advice); + + /// + /// Removes the supplied from the list + /// of . + /// + /// + /// The to remove. + /// + /// + /// if the supplied was + /// found in the list of + /// and successfully removed. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be removed. + /// + bool RemoveIntroduction(IIntroductionAdvisor introduction); + + /// + /// Removes the at the supplied + /// in the list of + /// for this proxy. + /// + /// The index of the advisor to remove. + /// + /// + /// If this proxy configuration is frozen and the + /// at the supplied + /// cannot be removed; or if the supplied + /// is out of range. + /// + void RemoveIntroduction(int index); + + /// + /// Replaces the that + /// exists at the supplied in the list of + /// + /// with the supplied . + /// + /// + /// The index of the + /// in the list of + /// + /// that is to be replaced. + /// + /// + /// The new (replacement) . + /// + /// + /// If the supplied is out of range. + /// + void ReplaceIntroduction(int index, IIntroductionAdvisor introduction); + + /// + /// Replaces the with the + /// . + /// + /// + /// The original (old) advisor to be replaced. + /// + /// + /// The new advisor to replace the with. + /// + /// + /// if the was + /// replaced; if the was not found in the + /// advisors collection, this method returns + /// and (effectively) does nothing. + /// + /// + /// If this proxy configuration is frozen and the + /// cannot be replaced. + /// + /// + bool ReplaceAdvisor(IAdvisor oldAdvisor, IAdvisor newAdvisor); + + /// + /// As will normally be passed + /// straight through to the advised target, this method returns the + /// equivalent for the AOP + /// proxy itself. + /// + /// + /// A description of the proxy configuration. + /// + string ToProxyConfigString(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/IAdvisedSupportListener.cs b/src/Spring/Spring.Aop/Aop/Framework/IAdvisedSupportListener.cs new file mode 100644 index 00000000..4085ceba --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/IAdvisedSupportListener.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Aop.Framework +{ + /// + /// Callback interface for + /// listeners. + /// + /// + ///

    + /// Allows + /// implementations to be notified of notable lifecycle events relating + /// to the creation of a proxy, and changes to the configuration data of a + /// proxy. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IAdvisedSupportListener.cs,v 1.5 2006/04/09 07:18:35 markpollack Exp $ + public interface IAdvisedSupportListener + { + /// + /// Invoked when the first proxy is created. + /// + /// + /// The relevant source. + /// + void Activated(AdvisedSupport source); + + /// + /// Invoked when advice is changed after a proxy is created. + /// + /// + /// The relevant source. + /// + void AdviceChanged(AdvisedSupport source); + + /// + /// Invoked when interfaces are changed after a proxy is created. + /// + /// + /// The relevant source. + /// + void InterfacesChanged(AdvisedSupport source); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/IAdvisorChainFactory.cs b/src/Spring/Spring.Aop/Aop/Framework/IAdvisorChainFactory.cs new file mode 100644 index 00000000..f1965d4d --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/IAdvisorChainFactory.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Factory interface for advisor chains. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IAdvisorChainFactory.cs,v 1.8 2006/09/14 21:05:23 bbaia Exp $ + public interface IAdvisorChainFactory : IAdvisedSupportListener + { + /// + /// Gets the list of and + /// + /// instances for the supplied . + /// + /// The proxy configuration object. + /// The object proxy. + /// + /// The method for which the interceptors are to be evaluated. + /// + /// + /// The of the target object. + /// + /// + /// The list of and + /// + /// instances for the supplied . + /// + IList GetInterceptors( + IAdvised advised, object proxy, MethodInfo method, Type targetType); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/IAopProxy.cs b/src/Spring/Spring.Aop/Aop/Framework/IAopProxy.cs new file mode 100644 index 00000000..55238299 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/IAopProxy.cs @@ -0,0 +1,45 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Proxy; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// The central interface for Spring.NET based AOP proxies. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IAopProxy.cs,v 1.4 2006/11/16 02:30:49 bbaia Exp $ + [ProxyIgnore] + public interface IAopProxy + { + /// + /// Creates a new proxy object. + /// + object GetProxy(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/IAopProxyFactory.cs b/src/Spring/Spring.Aop/Aop/Framework/IAopProxyFactory.cs new file mode 100644 index 00000000..e03900e9 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/IAopProxyFactory.cs @@ -0,0 +1,47 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Aop.Framework +{ + /// + /// Factory interface for the creation of AOP proxies based on + /// configuration + /// objects. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IAopProxyFactory.cs,v 1.3 2006/04/09 07:18:35 markpollack Exp $ + public interface IAopProxyFactory + { + /// + /// Creates an for the + /// supplied configuration. + /// + /// The AOP configuration. + /// An . + /// + /// If the supplied configuration is + /// invalid. + /// + IAopProxy CreateAopProxy(AdvisedSupport advisedSupport); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/ITargetAware.cs b/src/Spring/Spring.Aop/Aop/Framework/ITargetAware.cs new file mode 100644 index 00000000..864345cc --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/ITargetAware.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Aop.Framework +{ + /// + /// Provides access to the target object of an AOP proxy. + /// + /// + ///

    + /// To be implemented by introduction aspects in order to obtain access to + /// the target object. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ITargetAware.cs,v 1.3 2006/04/09 07:18:35 markpollack Exp $ + public interface ITargetAware + { + /// + /// Sets the target object. + /// + IAopProxy TargetProxy + { + set; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/ITargetSourceWrapper.cs b/src/Spring/Spring.Aop/Aop/Framework/ITargetSourceWrapper.cs new file mode 100644 index 00000000..373cdb98 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/ITargetSourceWrapper.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Aop.Framework +{ + /// + /// Decorates a target source with the + /// interface. + /// + /// Aleksandar Seovic + /// $Id: ITargetSourceWrapper.cs,v 1.1 2006/08/10 18:45:52 bbaia Exp $ + public interface ITargetSourceWrapper : IDisposable + { + /// + /// Returns the target object that proxy methods will be delegated to. + /// + /// The target object. + object GetTarget(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/InterceptorAndDynamicMethodMatcher.cs b/src/Spring/Spring.Aop/Aop/Framework/InterceptorAndDynamicMethodMatcher.cs new file mode 100644 index 00000000..73853ce5 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/InterceptorAndDynamicMethodMatcher.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using AopAlliance.Intercept; +using Spring.Aop; + +namespace Spring.Aop.Framework +{ + + /// Internal framework class. + /// This class is required because if we put an interceptor that implements IInterceptionAdvice + /// in the interceptor list passed to MethodInvocation, it may be mistaken for an + /// advice that requires dynamic method matching. + /// + /// Rod Johnson + /// Aleksandar Seovic (.Net) + /// $Id: InterceptorAndDynamicMethodMatcher.cs,v 1.3 2007/03/16 04:01:18 aseovic Exp $ + [Serializable] + internal class InterceptorAndDynamicMethodMatcher + { + internal IMethodMatcher MethodMatcher; + internal IMethodInterceptor Interceptor; + + public InterceptorAndDynamicMethodMatcher(IMethodInterceptor interceptor, IMethodMatcher methodMatcher) + { + this.Interceptor = interceptor; + this.MethodMatcher = methodMatcher; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/ProxyConfig.cs b/src/Spring/Spring.Aop/Aop/Framework/ProxyConfig.cs new file mode 100644 index 00000000..c019aef6 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/ProxyConfig.cs @@ -0,0 +1,222 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text; +using Spring.Aop.Framework.DynamicProxy; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Convenience superclass for configuration used in creating proxies, + /// to ensure that all proxy creators have consistent properties. + /// + /// + ///

    + /// Note that it is no longer possible to configure subclasses to + /// expose the . + /// Interceptors should normally manage their own thread locals if they + /// need to make resources available to advised objects. If it is + /// absolutely necessary to expose the + /// , use an + /// interceptor to do so. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: ProxyConfig.cs,v 1.13 2007/09/07 01:51:49 markpollack Exp $ + [Serializable] + public class ProxyConfig + { + #region Fields + private bool proxyTargetType; + private bool proxyTargetAttributes = true; + private bool optimize; + private bool frozen; + + private IAopProxyFactory aopProxyFactory = + ObjectUtils.InstantiateType( typeof(ProxyConfig).Assembly, "Spring.Aop.Framework.DynamicProxy.CachedAopProxyFactory") as IAopProxyFactory; + private bool exposeProxy; + private object syncRoot = new object(); + #endregion + + #region Properites + + /// + /// Use to synchronize access to this ProxyConfig instance + /// + public object SyncRoot + { + get { return syncRoot; } + } + + /// + /// Is the target to be proxied in addition + /// to any interfaces declared on the proxied ? + /// + public virtual bool ProxyTargetType + { + get { return this.proxyTargetType; } + set { this.proxyTargetType = value; } + } + + /// + /// Is target type attributes, method attributes, method's return type attributes + /// and method's parameter attributes to be proxied in addition + /// to any interfaces declared on the proxied ? + /// + public virtual bool ProxyTargetAttributes + { + get { return this.proxyTargetAttributes; } + set { this.proxyTargetAttributes = value; } + } + + /// + /// Are any agressive optimizations to be performed? + /// + /// + ///

    + /// The exact meaning of agressive optimizations will differ + /// between proxies, but there is usually some tradeoff. + ///

    + ///

    + /// For example, optimization will usually mean that advice changes + /// won't take effect after a proxy has been created. For this reason, + /// optimization is disabled by default. An optimize value of + /// may be ignored if other settings preclude + /// optimization: for example, if the + /// property + /// is set to and such a value is not compatible + /// with the optimization. + ///

    + ///

    + /// The default is . + ///

    + ///
    + public virtual bool Optimize + { + get { return this.optimize; } + set { this.optimize = value; } + } + + /// + /// Should proxies obtained from this configuration expose + /// the AOP proxy to the + /// class? + /// + /// + ///

    + /// The default is , as enabling this property + /// may impair performance. + ///

    + ///
    + public bool ExposeProxy + { + get { return this.exposeProxy; } + set { this.exposeProxy = value; } + } + + /// + /// Gets and set the factory to be used to create AOP proxies. + /// + /// + ///

    + /// This obviously allows one to customise the + /// implementation, + /// allowing different strategies to be dropped in without changing the + /// core framework. For example, an + /// implementation + /// could return an + /// using remoting proxies, Reflection.Emit or a code generation + /// strategy. + ///

    + ///
    + public virtual IAopProxyFactory AopProxyFactory + { + get { return this.aopProxyFactory; } + set { this.aopProxyFactory = value; } + } + + /// + /// Is this configuration frozen? + /// + /// + ///

    + /// The default is not frozen. + ///

    + ///
    + public virtual bool IsFrozen + { + get { return frozen; } + set { this.frozen = value; } + } + + #endregion + + /// + /// Copies the configuration from the supplied + /// into this instance. + /// + /// + /// The configuration to be copied. + /// + /// + /// If the supplied is + /// . + /// + public virtual void CopyFrom(ProxyConfig otherConfiguration) + { + AssertUtils.ArgumentNotNull(otherConfiguration, "otherConfiguration"); + + this.optimize = otherConfiguration.optimize; + this.proxyTargetType = otherConfiguration.proxyTargetType; + this.proxyTargetAttributes = otherConfiguration.proxyTargetAttributes; + this.exposeProxy = otherConfiguration.exposeProxy; + this.frozen = otherConfiguration.frozen; + this.aopProxyFactory = otherConfiguration.aopProxyFactory; + } + + /// + /// A that represents the current + /// configuration. + /// + /// + /// A that represents the current + /// configuration. + /// + public override string ToString() + { + StringBuilder buffer = new StringBuilder(); + buffer.Append("proxyTargetType=" + ProxyTargetType + "; "); + buffer.Append("proxyTargetAttributes=" + ProxyTargetAttributes + "; "); + buffer.Append("exposeProxy=" + ExposeProxy + "; "); + buffer.Append("isFrozen=" + IsFrozen + "; "); + buffer.Append("optimize=" + Optimize + "; "); + buffer.Append("aopProxyFactory=" + AopProxyFactory.GetType().FullName + "; "); + return buffer.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/ProxyFactory.cs b/src/Spring/Spring.Aop/Aop/Framework/ProxyFactory.cs new file mode 100644 index 00000000..e562143a --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/ProxyFactory.cs @@ -0,0 +1,129 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Factory for AOP proxies for programmatic use, rather than via a + /// Spring.NET IoC container. + /// + /// + ///

    + /// This class provides a simple way of obtaining and configuring AOP + /// proxies in code. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: ProxyFactory.cs,v 1.8 2007/03/16 04:01:19 aseovic Exp $ + [Serializable] + public class ProxyFactory : AdvisedSupport + { + /// + /// Creates a new instance of the + /// class. + /// + public ProxyFactory() + { + } + + /// + /// Creates a new instance of the + /// class that proxys all of the interfaces exposed by the supplied + /// . + /// + /// The object to proxy. + /// + /// If the is . + /// + public ProxyFactory(object target) : base(GetInterfaces(target)) + { + Target = target; + } + + /// + /// Creates a new instance of the + /// class that has no target object, only interfaces. + /// + /// + ///

    + /// Interceptors must be added if this factory is to do anything useful. + ///

    + ///
    + /// The interfaces to implement. + public ProxyFactory(Type[] interfaces) : base(interfaces) {} + + /// + /// Creates a new proxy according to the settings in this factory. + /// + /// + ///

    + /// Can be called repeatedly; the effect of repeated invocations will + /// (of course) vary if interfaces have been added or removed. + ///

    + ///
    + /// An AOP proxy for target object. + public virtual object GetProxy() + { + IAopProxy proxy = CreateAopProxy(); + return proxy.GetProxy(); + } + + #region Convenience Methods (Static) For Proxy Creation + + /// + /// Creates a new proxy for the supplied + /// and . + /// + /// + ///

    + /// This is a convenience method for creating a proxy for a single + /// interceptor. + ///

    + ///
    + /// + /// The interface that the proxy must implement. + /// + /// + /// The interceptor that the proxy must invoke. + /// + /// + /// A new AOP proxy for the supplied + /// and . + /// + public static object GetProxy(Type proxyInterface, IInterceptor interceptor) + { + ProxyFactory proxyFactory = new ProxyFactory(); + proxyFactory.AddInterface(proxyInterface); + proxyFactory.AddAdvice(interceptor); + return proxyFactory.GetProxy(); + } + + #endregion + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/ProxyFactoryObject.cs b/src/Spring/Spring.Aop/Aop/Framework/ProxyFactoryObject.cs new file mode 100644 index 00000000..a45de17a --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/ProxyFactoryObject.cs @@ -0,0 +1,918 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using AopAlliance.Aop; +using AopAlliance.Intercept; +using Common.Logging; +using Spring.Aop.Framework.Adapter; +using Spring.Aop.Support; +using Spring.Aop.Target; +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Objects.Factory; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// implementation to + /// source AOP proxies from a Spring.NET IoC container (an + /// ). + /// + /// + ///

    + /// s and + /// s are identified by a list of object + /// names in the current container.

    + ///

    + /// Global interceptors and advisors can be added at the factory level + /// (that is, outside the context of a + /// definition). The + /// specified interceptors and advisors are expanded in an interceptor list + /// (see + /// ) + /// where an 'xxx*' wildcard-style entry is included in the list, + /// matching the given prefix with the object names. For example, + /// 'global*' would match both 'globalObject1' and + /// 'globalObjectBar', and '*' would match all defined + /// interceptors. The matching interceptors get applied according to their + /// returned order value, if they implement the + /// interface. An interceptor name list + /// may not conclude with a global 'xxx*' pattern, as global + /// interceptors cannot invoke targets. + ///

    + ///

    + /// It is possible to cast a proxy obtained from this factory to an + /// reference, or to obtain the + /// reference and + /// programmatically manipulate it. This won't work for existing prototype + /// references, which are independent... however, it will work for prototypes + /// subsequently obtained from the factory. Changes to interception will + /// work immediately on singletons (including existing references). + /// However, to change interfaces or the target it is necessary to obtain a + /// new instance from the surrounding container. This means that singleton + /// instances obtained from the factory do not have the same object + /// identity... however, they do have the same interceptors and target, and + /// changing any reference will change all objects. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Federico Spinazzi (.NET) + /// Choy Rim (.NET) + /// Mark Pollack (.NET) + /// Aleksandar Seovic (.NET) + /// $Id: ProxyFactoryObject.cs,v 1.26 2007/08/03 14:38:30 markpollack Exp $ + /// + /// + /// + /// + /// + [Serializable] + public class ProxyFactoryObject + : AdvisedSupport, IFactoryObject, IObjectFactoryAware, IAdvisedSupportListener + { + #region Fields + + /// + /// The shared instance for this class. + /// + private static readonly ILog logger = LogManager.GetLogger(typeof (ProxyFactoryObject)); + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + private bool singleton = true; + + /// + /// This suffix in a value in an interceptor list indicates to expand globals. + /// + public const string GlobalInterceptorSuffix = "*"; + + /// + /// The cached instance if this proxy factory object is a singleton. + /// + private object singletonInstance; + + /// + /// The owning object factory (which cannot be changed after this object is initialized). + /// + private IObjectFactory objectFactory; + + /// + /// The mapping from an or interceptor + /// to an object name (or ), depending on where it was + /// sourced from. + /// + /// + ///

    + /// If it's sourced from object name, it will need to be + /// refreshed each time a new prototype instance is created. + ///

    + ///
    + private IDictionary sourceDictionary = new Hashtable(); + + /// + /// Names of interceptors and pointcut objects in the factory. + /// + /// + ///

    + /// Default is for globals expansion only. + ///

    + ///
    + private string[] interceptorNames = null; + + /// + /// Names of introductions and pointcut objects in the factory. + /// + /// + ///

    + /// Default is for globals expansion only. + ///

    + ///
    + private string[] introductionNames = null; + + /// + /// The name of the target object(in the enclosing + /// ). + /// + private string targetName = null; + + #endregion + + #region Properties + + /// + /// Sets the names of the interfaces that are to be implemented by the proxy. + /// + /// + /// The names of the interfaces that are to be implemented by the proxy. + /// + /// + /// If the supplied value (or any of its elements) is ; + /// or if any of the element values is not the (assembly qualified) name of + /// an interface type. + /// + public virtual string[] ProxyInterfaces + { + set + { + try + { + Interfaces = TypeResolutionUtils.ResolveInterfaceArray(value); + } + catch (Exception ex) + { + throw new AopConfigException("Bad value passed to the ProxyInterfaces property (see inner exception).", ex); + } + } + } + + /// + /// Sets the name of the target object being proxied. + /// + /// + ///

    + /// Only works when the + /// + /// property is set; it is a logic error on the part of the programmer + /// if this value is set and the accompanying + /// is not also set. + ///

    + ///
    + /// + /// The name of the target object being proxied. + /// + public virtual string TargetName + { + set { this.targetName = value; } + } + + /// + /// Sets the list of and + /// object names. + /// + /// + ///

    + /// This property must always be set (configured) when using a + /// in an + /// context. + ///

    + ///
    + /// + /// The list of and + /// object names. + /// + /// + /// + /// + /// + public virtual string[] InterceptorNames + { + set { this.interceptorNames = value; } + } + + /// + /// Sets the list of introduction object names. + /// + /// + ///

    + /// Only works when the + /// + /// property is set; it is a logic error on the part of the programmer + /// if this value is set and the accompanying + /// is not supplied. + ///

    + ///
    + /// + /// The list of introduction object names. . + /// + public virtual string[] IntroductionNames + { + set { this.introductionNames = value; } + } + + #endregion + + #region IFactoryObjectAware implementation + + /// + /// Callback that supplies the owning factory to an object instance. + /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + /// + /// In case of initialization errors. + /// + /// + /// + public virtual IObjectFactory ObjectFactory + { + set + { + this.objectFactory = value; + + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug("Setting IObjectFactory. Will configure target, interceptors and introductions..."); + } + + #endregion + + ConfigureAdvisorChain(); + ConfigureIntroductions(); + + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug("ProxyFactoryObject config: " + this); + } + + #endregion + + if (IsSingleton) + { + if (this.targetName != null) + { + TargetSource = NamedObjectToTargetSource(this.objectFactory.GetObject(this.targetName)); + } + + // eagerly initialize the shared singleton instance... + this.singletonInstance = CreateAopProxy().GetProxy(); + + // must listen to superclass advice and interface change + // events to recache singleton instance if necessary... + AddListener(this); + } + } + } + + #endregion + + #region IFactoryObject implementation + + /// + /// Creates an instance of the AOP proxy to be returned by this factory + /// + /// + ///

    + /// Invoked when clients obtain objects from this factory object. The + /// (proxy) instance will be cached for a singleton, and created on each + /// call to + /// for a prototype. + ///

    + ///
    + /// + /// A fresh AOP proxy reflecting the current state of this factory. + /// + /// + public virtual object GetObject() + { + lock(this.SyncRoot) + { + return (this.IsSingleton ? GetSingletonInstance() : NewPrototypeInstance()); + } + } + + /// + /// Return the of the proxy. + /// + /// + /// Will check the singleton instance if already created, + /// else fall back to the proxy interface (if a single one), + /// the target bean type, or the TargetSource's target class. + /// + /// Return the of object that this + /// creates, or + /// if not known in advance. + public virtual Type ObjectType + { + get + { + if (this.singletonInstance != null) + { + return this.singletonInstance.GetType(); + } + else if (Interfaces.Length == 1) + { + return Interfaces[0]; + } + else if (this.targetName != null && this.objectFactory != null) + { + return this.objectFactory.GetType(this.targetName); + } + else + { + return TargetSource.TargetType; + } + } + } + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + public virtual bool IsSingleton + { + get { return this.singleton; } + set { this.singleton = value; } + } + + #endregion + + #region Private Methods + + private object NewPrototypeInstance() + { + RefreshAdvisorChain(); + RefreshTarget(); + RefreshIntroductions(); + + // in the case of a prototype, we need to give the proxy + // an independent instance of the configuration... + + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug("Creating copy of prototype ProxyFactoryObject config: " + this); + } + + #endregion + + AdvisedSupport copy = new AdvisedSupport(); + copy.CopyConfigurationFrom(this); + + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug("Copy has config: " + copy); + } + + #endregion + + object generatedProxy = copy.CreateAopProxy().GetProxy(); + this.ProxyType = copy.ProxyType; + this.ProxyConstructor = copy.ProxyConstructor; + return generatedProxy; + } + + /// Create the advisor (interceptor) chain. + /// + /// The advisors that are sourced from an ObjectFactory will be refreshed each time + /// a new prototype instance is added. Interceptors added programmatically through + /// the factory API are unaffected by such changes. + /// + private void ConfigureAdvisorChain() + { + if (this.interceptorNames == null || this.interceptorNames.Length == 0) + { + return; + } + + // materialize interceptor chain from object names... + for (int i = 0; i < this.interceptorNames.Length; ++i) + { + string name = this.interceptorNames[i]; + + if(name == null) + { + throw new AopConfigException("Found null interceptor name value in the InterceptorNames list; check your configuration."); + } + + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug("Configuring interceptor '" + name + "'"); + } + + #endregion + + if (name.EndsWith(GlobalInterceptorSuffix)) + { + IListableObjectFactory lof = this.objectFactory as IListableObjectFactory; + if (lof == null) + { + // TODO : test this... + throw new AopConfigException( + "Can only use global advisors or interceptors in conjunction with an IListableObjectFactory."); + } + else + { + AddGlobalAdvisor((IListableObjectFactory) this.objectFactory, + name.Substring(0, (name.Length - GlobalInterceptorSuffix.Length))); + continue; + } + } + + else if (i == this.interceptorNames.Length - 1 && + this.targetName == null && + this.m_targetSource == EmptyTargetSource.Empty) + { + // the last name in the chain may be an IAdvisor/IAdvice or a target/ITargetSource; + // unfortunately we don't know; we must look at type of the object... + if (!IsNamedObjectAnAdvisorOrAdvice(name)) + { + this.targetName = name; + continue; + } + } + + object advice = null; + if(this.IsSingleton || + this.objectFactory.IsSingleton(name)) + { + advice = this.objectFactory.GetObject(name); + } + else + { + advice = this.objectFactory.GetObject(name); + } + if (advice is IAdvisors) + { + IAdvisors advisors = (IAdvisors)advice; + foreach (object advisor in advisors.Advisors) + { + AddAdvisor(advisor, name); + } + } + else + { + AddAdvisor(advice, name); + } + } + } + + + private bool IsNamedObjectAnAdvisorOrAdvice(string name) + { + Type namedObjectType = this.objectFactory.GetType(name); + if (namedObjectType != null) + { + return typeof(IAdvisors).IsAssignableFrom(namedObjectType) + || typeof(IAdvisor).IsAssignableFrom(namedObjectType) + || typeof(IAdvice).IsAssignableFrom(namedObjectType); + } + // treat it as an IAdvisor if we can't tell... + return true; + } + + + /// + /// Configures introductions for this proxy. + /// + private void ConfigureIntroductions() + { + if (this.introductionNames == null || this.introductionNames.Length == 0) + { + return; + } + + // Materialize introductions from object names... + for (int i = 0; i < this.introductionNames.Length; ++i) + { + string name = this.introductionNames[i]; + + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug("Configuring introduction '" + name + "'"); + } + + #endregion + + if (name.EndsWith(GlobalInterceptorSuffix)) + { + if (!(this.objectFactory is IListableObjectFactory)) + { + throw new AopConfigException("Can only use global introductions with a ListableObjectFactory"); + } + else + { + AddGlobalIntroduction((IListableObjectFactory) this.objectFactory, + name.Substring(0, (name.Length - GlobalInterceptorSuffix.Length))); + } + } + else + { + // add a named introduction + object introduction = this.objectFactory.GetObject(this.introductionNames[i]); + AddIntroduction(introduction, this.introductionNames[i]); + } + } + } + + /// Add all global interceptors and pointcuts. + private void AddGlobalAdvisor(IListableObjectFactory objectFactory, string prefix) + { + string[] globalAspectNames = + ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors(objectFactory, typeof(IAdvisors)); + string[] globalAdvisorNames = + ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors(objectFactory, typeof(IAdvisor)); + string[] globalInterceptorNames = + ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors(objectFactory, typeof(IInterceptor)); + IList objects = new ArrayList(); + IDictionary names = new Hashtable(); + + for (int i = 0; i < globalAspectNames.Length; i++) + { + string name = globalAspectNames[i]; + if (name.StartsWith(prefix)) + { + IAdvisors advisors = (IAdvisors) objectFactory.GetObject(name); + foreach (object advisor in advisors.Advisors) + { + // exclude introduction advisors from interceptor list + if (!(advisor is IIntroductionAdvisor)) + { + objects.Add(advisor); + names[advisor] = name; + } + } + } + } + for (int i = 0; i < globalAdvisorNames.Length; i++) + { + string name = globalAdvisorNames[i]; + if (name.StartsWith(prefix)) + { + object obj = objectFactory.GetObject(name); + // exclude introduction advisors from interceptor list + if (!(obj is IIntroductionAdvisor)) + { + objects.Add(obj); + names[obj] = name; + } + } + } + for (int i = 0; i < globalInterceptorNames.Length; i++) + { + string name = globalInterceptorNames[i]; + if (name.StartsWith(prefix)) + { + object obj = objectFactory.GetObject(name); + objects.Add(obj); + names[obj] = name; + } + } + ((ArrayList) objects).Sort(new OrderComparator()); + foreach (object obj in objects) + { + string name = (string) names[obj]; + AddAdvisor(obj, name); + } + } + + /// Add all global introductions. + private void AddGlobalIntroduction(IListableObjectFactory objectFactory, string prefix) + { + string[] globalAspectNames = + ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors(objectFactory, typeof(IAdvisors)); + string[] globalAdvisorNames = + ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors(objectFactory, typeof (IAdvisor)); + string[] globalIntroductionNames = + ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors(objectFactory, typeof (IAdvice)); + IList objects = new ArrayList(); + IDictionary names = new Hashtable(); + + for (int i = 0; i < globalAspectNames.Length; i++) + { + string name = globalAspectNames[i]; + if (name.StartsWith(prefix)) + { + IAdvisors advisors = (IAdvisors)objectFactory.GetObject(name); + foreach (object advisor in advisors.Advisors) + { + // only include introduction advisors + if (advisor is IIntroductionAdvisor) + { + objects.Add(advisor); + names[advisor] = name; + } + } + } + } + for (int i = 0; i < globalAdvisorNames.Length; i++) + { + string name = globalAdvisorNames[i]; + if (name.StartsWith(prefix)) + { + object obj = objectFactory.GetObject(name); + // only include introduction advisors + if (obj is IIntroductionAdvisor) + { + objects.Add(obj); + names[obj] = name; + } + } + } + for (int i = 0; i < globalIntroductionNames.Length; i++) + { + string name = globalIntroductionNames[i]; + if (name.StartsWith(prefix)) + { + object obj = objectFactory.GetObject(name); + // exclude other advice types + if (!(obj is IInterceptor || obj is IBeforeAdvice || obj is IAfterReturningAdvice)) + { + objects.Add(obj); + names[obj] = name; + } + } + } + ((ArrayList) objects).Sort(new OrderComparator()); + foreach (object obj in objects) + { + string name = (string) names[obj]; + AddIntroduction(obj, name); + } + } + + /// Add the given interceptor or pointcut to the interceptor list. + /// interceptor or pointcut to add + /// object name from which we obtained this object in our owning object factory + private void AddAdvisor(object next, string name) + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug("Adding advisor with name '" + name + "'."); + } + + #endregion + + IAdvisor advisor = NamedObjectToAdvisor(next); + AddAdvisor(advisor); + + // Record the pointcut as descended from the given object name. + // This allows us to refresh the interceptor list, which we'll need to + // do if we have to create a new prototype instance. Otherwise the new + // prototype instance wouldn't be truly independent, because it might + // reference the original instances of prototype interceptors. + this.sourceDictionary[advisor] = name; + } + + /// Add the introduction to the introduction list. + /// + /// If specified parameter is IIntroducionAdvisor it is added directly, otherwise it is wrapped + /// with DefaultIntroductionAdvisor first. + /// + /// introducion to add + /// object name from which we obtained this object in our owning object factory + private void AddIntroduction(object introduction, string name) + { + logger.Debug("Adding introduction with name [" + name + "]"); + IIntroductionAdvisor advisor = NamedObjectToIntroduction(introduction); + AddIntroduction(advisor); + + // Record the introduction as descended from the given object name. + // This allows us to refresh the introduction list, which we'll need to + // do if we have to create a new prototype instance. Otherwise the new + // prototype instance wouldn't be truly independent, because it might + // reference the original instances of prototype introductions. + this.sourceDictionary[advisor] = name; + } + + /// Refresh named objects from the interceptor chain. + /// We need to do this every time a new prototype instance is returned, + /// to return distinct instances of prototype interfaces and pointcuts. + /// + private void RefreshAdvisorChain() + { + IAdvisor[] advisors = Advisors; + for (int i = 0; i < advisors.Length; ++i) + { + string objectName = (string) this.sourceDictionary[advisors[i]]; + if (objectName != null) + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug("Refreshing object named '" + objectName + "'"); + } + + #endregion + + IAdvisor refreshedAdvisor + = NamedObjectToAdvisor(this.objectFactory.GetObject(objectName)); + ReplaceAdvisor(advisors[i], refreshedAdvisor); + this.sourceDictionary.Remove(advisors[i]); + // keep name mapping up to date... + this.sourceDictionary[refreshedAdvisor] = objectName; + } + else + { + // We can't throw an exception here, as the user may have added additional + // pointcuts programmatically we don't know about + logger.Info( + "Cannot find object name for Advisor [" + advisors[i] + "] when refreshing advisor chain"); + } + } + } + + /// + /// Refreshes target object for prototype instances. + /// + private void RefreshTarget() + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug("Refreshing target with name '" + this.targetName + "'"); + } + + #endregion + + if (StringUtils.IsNullOrEmpty(this.targetName)) + { + // TODO test... + throw new AopConfigException("Target name cannot be null (or composed wholly of whitespace) for prototype factory."); + } + object target = this.objectFactory.GetObject(this.targetName); + TargetSource = NamedObjectToTargetSource(target); + } + + /// Refresh named objects from the interceptor chain. + /// We need to do this every time a new prototype instance is returned, + /// to return distinct instances of prototype interfaces and pointcuts. + /// + private void RefreshIntroductions() + { + IIntroductionAdvisor[] introductions = Introductions; + for (int i = 0; i < introductions.Length; i++) + { + string objectName = (string) this.sourceDictionary[introductions[i]]; + if (objectName != null) + { + logger.Info("Refreshing introduction named '" + objectName + "'"); + object obj = this.objectFactory.GetObject(objectName); + IIntroductionAdvisor refreshedIntroduction = NamedObjectToIntroduction(obj); + + ReplaceIntroduction(i, refreshedIntroduction); + this.sourceDictionary.Remove(introductions[i]); + this.sourceDictionary[refreshedIntroduction] = objectName; + } + else + { + // We can't throw an exception here, as the user may have added additional + // introductions programmatically we don't know about + logger.Info( + "Cannot find object name for Introduction [" + introductions[i] + + "] when refreshing introduction list"); + } + } + } + + /// Wraps pointcut or interceptor with appropriate advisor + /// pointcut or interceptor that needs to be wrapped with advisor + /// Advisor + private IAdvisor NamedObjectToAdvisor(object next) + { + return GlobalAdvisorAdapterRegistry.Instance.Wrap(next); + } + + /// Wraps target with SingletonTargetSource if necessary + /// target or target source object + /// target source passed or target wrapped with SingletonTargetSource + private ITargetSource NamedObjectToTargetSource(object target) + { + if (target is ITargetSource) + { + return (ITargetSource) target; + } + else + { + // It's an object that needs target source around it. + return new SingletonTargetSource(target); + } + } + + /// Wraps introduction with IIntroductionAdvisor if necessary + /// object to wrap + /// Introduction advisor + private IIntroductionAdvisor NamedObjectToIntroduction(object introduction) + { + if (introduction is IIntroductionAdvisor) + { + return (IIntroductionAdvisor) introduction; + } + else + { + return new DefaultIntroductionAdvisor((IAdvice) introduction); + } + } + + private object GetSingletonInstance() + { + if (this.singletonInstance == null) + { + this.singletonInstance = CreateAopProxy().GetProxy(); + } + return this.singletonInstance; + } + + #endregion + + #region IAdvisedSupportListener implementation + + /// + /// + public virtual void Activated(AdvisedSupport advisedSupport) + { + } + + /// No need to do anything when advice change, proxy can handle those changes by itself. + /// + /// + public virtual void AdviceChanged(AdvisedSupport advisedSupport) + { + } + + /// Implementation of listener for AdvisedSupport.InterfacesChanged event + /// event source + public virtual void InterfacesChanged(AdvisedSupport advisedSupport) + { + logger.Info("Implemented interfaces have changed; reseting singleton instance"); + this.singletonInstance = null; + this.ProxyType = null; + this.ProxyConstructor = null; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/ReflectiveMethodInvocation.cs b/src/Spring/Spring.Aop/Aop/Framework/ReflectiveMethodInvocation.cs new file mode 100644 index 00000000..0901bbc0 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/ReflectiveMethodInvocation.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; + +using Spring.Util; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Invokes a target method using standard reflection. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// Rick Evans (.NET) + /// Bruno Baia (.NET) + /// $Id: ReflectiveMethodInvocation.cs,v 1.18 2008/02/06 18:28:52 bbaia Exp $ + [Serializable] + public class ReflectiveMethodInvocation : AbstractMethodInvocation + { + /// + /// The method invocation that is to be invoked on the proxy. + /// + protected MethodInfo proxyMethod; + + /// + /// Creates a new instance of the + /// class. + /// + /// The AOP proxy. + /// The target object. + /// The target method proxied. + /// The method to invoke on proxy. + /// The target method's arguments. + /// + /// The of the target object. + /// + /// The list of interceptors that are to be applied. May be + /// . + /// + /// + /// If any of the or + /// parameters is . + /// + public ReflectiveMethodInvocation( + object proxy, object target, MethodInfo method, MethodInfo proxyMethod, + object[] arguments, Type targetType, IList interceptors) + : base(proxy, target, method, arguments, targetType, interceptors) + { + this.proxyMethod = proxyMethod; + } + + /// + /// Invokes the joinpoint using standard reflection. + /// + /// + ///

    + /// Subclasses can override this to use custom invocation. + ///

    + ///
    + /// + /// The return value of the invocation of the joinpoint. + /// + /// + /// If invoking the joinpoint resulted in an exception. + /// + /// + protected override object InvokeJoinpoint() + { + try + { + if (proxyMethod == null) + { + return method.Invoke(target, arguments); + } + else + { + return proxyMethod.Invoke(target, arguments); + } + } + catch (TargetInvocationException ex) + { + throw ReflectionUtils.UnwrapTargetInvocationException(ex); + } + } + + /// + /// Creates a new instance + /// from the specified and + /// increments the interceptor index. + /// + /// + /// The current instance. + /// + /// + /// The new instance to use. + /// + protected override IMethodInvocation PrepareMethodInvocationForProceed(IMethodInvocation invocation) + { + ReflectiveMethodInvocation rmi = new ReflectiveMethodInvocation( + this.proxy, this.target, this.method, this.proxyMethod, this.arguments, this.targetType, this.interceptors); + rmi.currentInterceptorIndex = this.currentInterceptorIndex + 1; + + return rmi; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/StaticTargetSourceWrapper.cs b/src/Spring/Spring.Aop/Aop/Framework/StaticTargetSourceWrapper.cs new file mode 100644 index 00000000..8ee45180 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/StaticTargetSourceWrapper.cs @@ -0,0 +1,88 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Decorates a target source with the + /// interface. + /// + /// + ///

    + /// Because the target source is static, the target object can be cached + /// and simply returned as is. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: StaticTargetSourceWrapper.cs,v 1.3 2007/03/16 04:01:20 aseovic Exp $ + [Serializable] + public sealed class StaticTargetSourceWrapper : ITargetSourceWrapper + { + private object target; + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The target object that proxy methods will be delegated to. + /// + /// + /// If the supplied is + /// . + /// + internal StaticTargetSourceWrapper(ITargetSource targetSource) + { + AssertUtils.ArgumentNotNull(targetSource, "targetSource"); + this.target = targetSource.GetTarget(); + } + + /// + /// Returns the target object that proxy methods will be delegated to. + /// + /// The target object. + public object GetTarget() + { + return this.target; + } + + /// + /// Performs application-defined tasks associated with freeing, + /// releasing, or resetting unmanaged resources. + /// + /// + /// + /// This is a no-op operation in this implementation. + /// + /// + public void Dispose() + { + // do nothing, this is static target source wrapper... + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IAdvisor.cs b/src/Spring/Spring.Aop/Aop/IAdvisor.cs new file mode 100644 index 00000000..c28b0294 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IAdvisor.cs @@ -0,0 +1,96 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; + +#endregion + +namespace Spring.Aop +{ + /// + /// Base interface holding AOP advice and a filter determining the + /// applicability of the advice (such as a pointcut). + /// + /// + /// + /// This interface is not for use by Spring.NET users, but exists rather to + /// allow for commonality in the support for different types of advice + /// within the framework. + /// + ///

    + /// Spring.NET AOP is centered on around advice delivered via method + /// interception, compliant with the AOP Alliance interception API. + /// The interface allows support for + /// different types of advice, such as before and after + /// advice, which need not be implemented using interception. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// + /// + /// + /// + /// $Id: IAdvisor.cs,v 1.7 2006/04/09 07:18:36 markpollack Exp $ + public interface IAdvisor + { + /// + /// Is this advice associated with a particular instance? + /// + /// + ///

    + /// An advisor that was creating a mixin would be a per instance + /// operation and would thus return . If the + /// advisor is not per instance, it is shared with all instances of the + /// advised class obtained from the same Spring.NET IoC container. + ///

    + ///

    + /// Use singleton and prototype object definitions or + /// appropriate programmatic proxy creation to ensure that + /// s have the correct lifecycle model. + ///

    + /// + /// This method is not currently used by the framework. + /// + ///
    + /// + /// if this advice is associated with a + /// particular instance. + /// + bool IsPerInstance { get; } + + /// + /// Return the advice part of this aspect. + /// + /// + ///

    + /// An advice may be an interceptor, a throws advice, before advice, + /// introduction etc. + ///

    + ///
    + /// + /// The advice that should apply if the pointcut matches. + /// + IAdvice Advice { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IAdvisors.cs b/src/Spring/Spring.Aop/Aop/IAdvisors.cs new file mode 100644 index 00000000..530f85a8 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IAdvisors.cs @@ -0,0 +1,47 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using AopAlliance.Aop; + +#endregion + +namespace Spring.Aop +{ + /// + /// AOP Aspect abstraction, holding a list of s + /// + /// + /// Aleksandar Seovic (.NET) + /// $Id: IAdvisors.cs,v 1.1 2007/08/03 14:38:30 markpollack Exp $ + public interface IAdvisors + { + /// + /// Gets or sets a list of advisors. + /// + /// + /// A list of advisors. + /// + IList Advisors { get; set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IAfterReturningAdvice.cs b/src/Spring/Spring.Aop/Aop/IAfterReturningAdvice.cs new file mode 100644 index 00000000..f2fc9cfd --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IAfterReturningAdvice.cs @@ -0,0 +1,78 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using AopAlliance.Aop; + +#endregion + +namespace Spring.Aop +{ + /// + /// Advice that executes after a method returns successfully. + /// + /// + ///

    + /// After returning advice is invoked only on a normal method + /// return, but not if an exception is thrown. Such advice can see + /// the return value of the advised method invocation, but cannot change it. + ///

    + ///

    + /// Possible uses for this type of advice would include performing access + /// control checks on the return value of an advised method invocation, the + /// ubiquitous logging of method invocation return values (useful during + /// development), etc. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// + /// + /// + /// $Id: IAfterReturningAdvice.cs,v 1.6 2006/04/09 07:18:36 markpollack Exp $ + public interface IAfterReturningAdvice : IAdvice + { + /// + /// Executes after + /// returns successfully. + /// + /// + ///

    + /// Note that the supplied cannot + /// be changed by this type of advice... use the around advice type + /// () if you + /// need to change the return value of an advised method invocation. + /// The data encapsulated by the supplied + /// can of course be modified though. + ///

    + ///
    + /// + /// The value returned by the . + /// + /// The intecepted method. + /// The intercepted method's arguments. + /// The target object. + /// + void AfterReturning(object returnValue, MethodInfo method, object[] args, object target); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IBeforeAdvice.cs b/src/Spring/Spring.Aop/Aop/IBeforeAdvice.cs new file mode 100644 index 00000000..70d84389 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IBeforeAdvice.cs @@ -0,0 +1,55 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; + +#endregion + +namespace Spring.Aop +{ + /// + /// Superinterface for all before advice. + /// + /// + ///

    + /// Before advice is advice that executes before a joinpoint, but + /// which does not have the ability to prevent execution flow proceeding to + /// the joinpoint (unless it throws an ). + ///

    + ///

    + /// Spring.NET only supports method before advice. Although this + /// is unlikely to change, this API is designed to allow field + /// before advice in future if desired. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// + /// + /// + /// + /// $Id: IBeforeAdvice.cs,v 1.4 2006/04/09 07:18:36 markpollack Exp $ + public interface IBeforeAdvice : IAdvice + { + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IIntroductionAdvisor.cs b/src/Spring/Spring.Aop/Aop/IIntroductionAdvisor.cs new file mode 100644 index 00000000..0c238edc --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IIntroductionAdvisor.cs @@ -0,0 +1,93 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Aop +{ + /// + /// Superinterface for advisors that perform one or more AOP + /// introductions. + /// + /// + ///

    + /// This interface cannot be implemented directly; subinterfaces must + /// provide the advice type implementing the introduction. + ///

    + ///

    + /// Introduction is the implementation of additional interfaces (not + /// implemented by a target) via AOP advice. + ///

    + ///
    + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IIntroductionAdvisor.cs,v 1.8 2006/04/09 07:18:36 markpollack Exp $ + public interface IIntroductionAdvisor : IAdvisor + { + /// + /// Returns the filter determining which target classes this + /// introduction should apply to. + /// + /// + ///

    + /// This is the part of a pointcut. + /// Be advised that method matching doesn't make sense in the context + /// of introductions. + ///

    + ///
    + /// + /// The filter determining which target classes this introduction + /// should apply to. + /// + ITypeFilter TypeFilter { get; } + + /// + /// Gets the interfaces introduced by this + /// . + /// + /// + /// The interfaces introduced by this + /// . + /// + Type[] Interfaces { get; } + + /// + /// Can the advised interfaces be implemented by the introduction + /// advice? + /// + /// + ///

    + /// Invoked before adding an + /// . + ///

    + ///
    + /// + /// If the advised interfaces cannot be implemented by the introduction + /// advice. + /// + /// + void ValidateInterfaces(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IIntroductionInterceptor.cs b/src/Spring/Spring.Aop/Aop/IIntroductionInterceptor.cs new file mode 100644 index 00000000..c7d00885 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IIntroductionInterceptor.cs @@ -0,0 +1,62 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop +{ + /// + /// Subinterface of the AOP Alliance + /// interface that + /// allows additional interfaces to be implemented by the interceptor, and + /// available via a proxy using that interceptor. + /// + /// + ///

    + /// This is a fundamental AOP concept called introduction. + ///

    + ///

    + /// Introductions are often mixins, enabling the building of composite + /// objects that can achieve many of the goals of multiple inheritance. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IIntroductionInterceptor.cs,v 1.3 2006/04/09 07:18:36 markpollack Exp $ + public interface IIntroductionInterceptor : IMethodInterceptor + { + /// + /// Does this + /// implement the given interface? + /// + /// The interface to check. + /// + /// if this + /// + /// implements the given interface. + /// + bool ImplementsInterface(Type intf); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IMethodBeforeAdvice.cs b/src/Spring/Spring.Aop/Aop/IMethodBeforeAdvice.cs new file mode 100644 index 00000000..171ac9a8 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IMethodBeforeAdvice.cs @@ -0,0 +1,73 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Aop +{ + /// + /// Advice executed before a method is invoked. + /// + /// + ///

    + /// Such advice cannot prevent the method call proceeding, short of + /// throwing an . + ///

    + ///

    + /// The main advantage of before advice is that there is no + /// possibility of inadvertently failing to proceed down the interceptor + /// chain, since there is no need (and indeed means) to invoke the next + /// interceptor in the call chain. + ///

    + ///

    + /// Possible uses for this type of advice would include performing class + /// invariant checks prior to the actual method invocation, the ubiquitous + /// logging of method invocations (useful during development), etc. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// + /// + /// + /// + /// $Id: IMethodBeforeAdvice.cs,v 1.6 2006/04/09 07:18:36 markpollack Exp $ + public interface IMethodBeforeAdvice : IBeforeAdvice + { + /// + /// The callback before a given method is invoked. + /// + /// The method being invoked. + /// The arguments to the method. + /// + /// The target of the method invocation. May be . + /// + /// + /// Thrown when and if this object wishes to abort the call. Any + /// exception so thrown will be propagated to the caller. + /// + void Before(MethodInfo method, object[] args, object target); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IMethodMatcher.cs b/src/Spring/Spring.Aop/Aop/IMethodMatcher.cs new file mode 100644 index 00000000..9383073a --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IMethodMatcher.cs @@ -0,0 +1,151 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Aop +{ + /// + /// That part of an that checks whether a + /// target method is eligible for advice. + /// + /// + ///

    + /// An may be evaluated + /// statically or at runtime (dynamically). Static + /// matching involves only the method signature and (possibly) any + /// s that have been applied to a method. + /// Dynamic matching additionally takes into account the actual argument + /// values passed to a method invocation. + ///

    + ///

    + /// If the value of the + /// property of an implementation instance returns , + /// evaluation can be performed statically, and the result will be the same + /// for all invocations of this method, whatever their arguments. This + /// means that if the value of the + /// is + /// , the three argument + /// + /// method will never be invoked for the lifetime of the + /// . + ///

    + ///

    + /// If an implementation returns in its two argument + /// + /// method, and the value of it's + /// property is + /// , the three argument + /// + /// method will be invoked immediately before each and every potential + /// execution of the related advice, to decide whether the advice + /// should run. All previous advice, such as earlier interceptors in an + /// interceptor chain, will have run, so any state changes they have + /// produced in parameters or thread local storage, will be available at + /// the time of evaluation. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IMethodMatcher.cs,v 1.9 2006/04/09 07:18:36 markpollack Exp $ + /// + public interface IMethodMatcher + { + /// + /// Is this dynamic? + /// + /// + ///

    + /// If , the three argument + /// + /// method will be invoked if the two argument + /// + /// method returns . + ///

    + ///

    + /// Note that this property can be checked when an AOP proxy is created, + /// and implementations need not check the value of this property again + /// before each method invocation. + ///

    + ///
    + /// + /// if this + /// is dynamic. + /// + bool IsRuntime { get; } + + /// + /// Does the supplied satisfy this matcher? + /// + /// + ///

    + /// This is a static check. If this method invocation returns + /// ,or if the + /// property is + /// , then no runtime check will be made. + ///

    + ///
    + /// The candidate method. + /// + /// The target (may be , + /// in which case the candidate must be taken + /// to be the 's declaring class). + /// + /// + /// if this this method matches statically. + /// + bool Matches(MethodInfo method, Type targetType); + + /// + /// Is there a runtime (dynamic) match for the supplied + /// ? + /// + /// + ///

    + /// In order for this method to have even been invoked, the supplied + /// must have matched + /// statically. This method is invoked only if the two argument + /// + /// method returns for the supplied + /// and , and + /// if the property + /// is . + ///

    + ///

    + /// Invoked immediately before any potential running of the + /// advice, and after any advice earlier in the advice chain has + /// run. + ///

    + ///
    + /// The candidate method. + /// + /// The target . + /// + /// The arguments to the method + /// + /// if there is a runtime match. + bool Matches(MethodInfo method, Type targetType, object[] args); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IPointcut.cs b/src/Spring/Spring.Aop/Aop/IPointcut.cs new file mode 100644 index 00000000..362b8550 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IPointcut.cs @@ -0,0 +1,61 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Aop +{ + /// + /// Spring.NET's core pointcut abstraction. + /// + /// + ///

    + /// A pointcut is composed of s and + /// s. Both these basic terms and an + /// itself can be combined to build up + /// sophisticated combinations. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: IPointcut.cs,v 1.9 2006/04/09 07:18:36 markpollack Exp $ + public interface IPointcut + { + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + ITypeFilter TypeFilter { get; } + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + IMethodMatcher MethodMatcher { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/IPointcutAdvisor.cs new file mode 100644 index 00000000..13418271 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IPointcutAdvisor.cs @@ -0,0 +1,50 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Aop +{ + /// + /// Superinterface for all s that are + /// driven by a pointcut. + /// + /// + ///

    + /// This covers nearly all advisors except introduction advisors, for which + /// method-level matching does not apply. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// + /// $Id: IPointcutAdvisor.cs,v 1.3 2006/04/09 07:18:36 markpollack Exp $ + public interface IPointcutAdvisor : IAdvisor + { + /// + /// The that drives this advisor. + /// + IPointcut Pointcut { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/ITargetSource.cs b/src/Spring/Spring.Aop/Aop/ITargetSource.cs new file mode 100644 index 00000000..2a50f8c8 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/ITargetSource.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Aop +{ + /// + /// Used to obtain the current "target" of an AOP invocation + /// + /// + ///

    + /// This target will be invoked via reflection if no around advice chooses + /// to end the interceptor chain itself. + ///

    + ///

    + /// If an is "static", it + /// will always return the same target, allowing optimizations in the AOP + /// framework. Dynamic target sources can support pooling, hot swapping etc. + ///

    + ///

    + /// Application developers don't usually need to work with target sources + /// directly: this is an AOP framework interface. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: ITargetSource.cs,v 1.9 2007/10/10 18:07:38 markpollack Exp $ + public interface ITargetSource + { + /// + /// The of the target object. + /// + Type TargetType { get; } + + /// + /// Is the target source static? + /// + /// + /// if the target source is static. + /// + bool IsStatic { get; } + + /// + /// Returns the target object. + /// + /// The target object. + /// + /// If unable to obtain the target object. + /// + object GetTarget(); + + /// + /// Releases the target object. + /// + /// The target object to release. + void ReleaseTarget(object target); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/IThrowsAdvice.cs b/src/Spring/Spring.Aop/Aop/IThrowsAdvice.cs new file mode 100644 index 00000000..dde6e761 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/IThrowsAdvice.cs @@ -0,0 +1,61 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; + +#endregion + +namespace Spring.Aop +{ + /// + /// Simple marker interface for throws advice. + /// + /// + ///

    + /// There are no methods on this interface, as methods are discovered and + /// invoked via reflection. Please do see read the API documentation for the + /// class; + /// said documention describes in detail the signature of the methods that + /// implementations of the interface + /// must adhere to in the specific case of Spring.NET's implementation of + /// throws advice. + ///

    + ///

    + /// There are any number of possible uses for this type of advice. Some + /// examples would include the ubiquitous logging of any such exceptions, + /// monitoring the number and type of exceptions and sending emails to + /// a support desk once certain criteria have been met, wrapping generic + /// exceptions such as in + /// exceptions that are more meaningful to your business logic, etc. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// + /// + /// + /// $Id: IThrowsAdvice.cs,v 1.5 2006/04/09 07:18:36 markpollack Exp $ + public interface IThrowsAdvice : IAdvice + { + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/ITypeFilter.cs b/src/Spring/Spring.Aop/Aop/ITypeFilter.cs new file mode 100644 index 00000000..6ca69514 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/ITypeFilter.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Aop +{ + /// + /// A filter that restricts the matching of a pointcut or introduction to + /// a given set of target types. + /// + /// + ///

    + /// Can be used as part of a pointcut, or for the entire targeting of an + /// introduction. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// + /// + /// $Id: ITypeFilter.cs,v 1.2 2006/04/09 07:18:36 markpollack Exp $ + public interface ITypeFilter + { + /// + /// Should the pointcut apply to the supplied + /// ? + /// + /// + /// The candidate . + /// + /// + /// if the advice should apply to the supplied + /// + /// + bool Matches(Type type); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/AbstractGenericPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/AbstractGenericPointcutAdvisor.cs new file mode 100644 index 00000000..3b7a1905 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/AbstractGenericPointcutAdvisor.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using AopAlliance.Aop; + +namespace Spring.Aop.Support +{ + /// + /// Abstract PointcutAdvisor that allows for any Advice to be configured. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: AbstractGenericPointcutAdvisor.cs,v 1.2 2007/08/10 17:39:44 bbaia Exp $ + [Serializable] + public abstract class AbstractGenericPointcutAdvisor : AbstractPointcutAdvisor + { + private IAdvice advice; + + + /// + /// Return the advice part of this advisor. + /// + /// + /// The advice that should apply if the pointcut matches. + /// + /// + public override IAdvice Advice + { + get { return this.advice; } + set { this.advice = value; } + } + + + /// + /// Returns a that represents the current + /// . + /// + /// + /// A representation of this advisor. + /// + public override string ToString() + { + return GetType().Name + ": advice=[" + Advice + "]"; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/AbstractObjectFactoryPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/AbstractObjectFactoryPointcutAdvisor.cs new file mode 100644 index 00000000..f4805a8b --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/AbstractObjectFactoryPointcutAdvisor.cs @@ -0,0 +1,137 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using AopAlliance.Aop; +using Spring.Objects.Factory; +using Spring.Util; + +namespace Spring.Aop.Support +{ + /// + /// Abstract ObjectFactory-based IPointcutAdvisor that allows for any Advice to be + /// configured as reference to an Advice object in an ObjectFactory. + /// + /// + /// specifying the name of an advice object instead of the advice object itself + /// (if running within an ObjectFactory/ApplicationContext increses loose coupling + /// at initialization time, in order not to initialize the advice object until the + /// pointcut actually matches. + /// + /// Juergen Hoeller + /// Mark Pollack + /// $Id: AbstractObjectFactoryPointcutAdvisor.cs,v 1.2 2007/08/10 17:39:44 bbaia Exp $ + public abstract class AbstractObjectFactoryPointcutAdvisor : AbstractPointcutAdvisor, IObjectFactoryAware + { + private string adviceObjectName; + + private IObjectFactory objectFactory; + + private IAdvice advice; + + private object adviceMonitor = new object(); + + + /// + /// Gets or sets the name of the advice object that this advisor should refer to. + /// + /// An instance of the specified object will be obtained on first access of + /// this advisor's advice. This advisor will only ever obtain at most one + /// single instance of the advice object, caching the instance for the lifetime of + /// the advisor. + /// The name of the advice object. + public string AdviceObjectName + { + get { return adviceObjectName; } + set { adviceObjectName = value; } + } + + #region IObjectFactoryAware Members + + /// + /// Callback that supplies the owning factory to an object instance. + /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + /// + ///

    + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

    + ///
    + /// + /// In case of initialization errors. + /// + public IObjectFactory ObjectFactory + { + set { objectFactory = value; } + } + + #endregion + + /// + /// Return the advice part of this aspect. + /// + /// + ///

    + /// An advice may be an interceptor, a throws advice, before advice, + /// introduction etc. + ///

    + ///
    + /// + /// The advice that should apply if the pointcut matches. + /// + public override IAdvice Advice + { + get + { + lock (adviceMonitor) + { + if (advice == null && adviceObjectName != null) + { + AssertUtils.State(objectFactory != null, + "ObjectFactory must be set to resolve 'adviceObjectName'"); + advice = objectFactory.GetObject(adviceObjectName, typeof (IAdvice)) as IAdvice; + } + } + return advice; + } + set + { + advice = value; + } + } + + /// + /// Describe this Advisor, showing name of advice object. + /// + /// + /// Type name and advice object name. + /// + public override string ToString() + { + return GetType().Name + ": advice object '" + AdviceObjectName + "'"; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/AbstractPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/AbstractPointcutAdvisor.cs new file mode 100644 index 00000000..2c90ff85 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/AbstractPointcutAdvisor.cs @@ -0,0 +1,166 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using AopAlliance.Aop; +using Spring.Core; + +namespace Spring.Aop.Support +{ + /// + /// Abstract base class for implementations. + /// + /// + /// Can be subclassed for returning a specific pointcut/advice or a freely configurable pointcut/advice. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: AbstractPointcutAdvisor.cs,v 1.2 2008/01/14 20:49:47 oakinger Exp $ + [Serializable] + public abstract class AbstractPointcutAdvisor : IPointcutAdvisor, IOrdered + { + #region Fields + + private int _order = Int32.MaxValue; + + #endregion + + #region IOrdered Members + + /// + /// Returns this s order in the + /// interception chain. + /// + /// + /// This s order in the + /// interception chain. + /// + public virtual int Order + { + get { return this._order; } + set { this._order = value; } + } + + #endregion + + #region IAdvisor Members + + /// + /// Return the advice part of this aspect. + /// + /// + ///

    + /// An advice may be an interceptor, a throws advice, before advice, + /// introduction etc. + ///

    + ///
    + /// + /// The advice that should apply if the pointcut matches. + /// + public abstract IAdvice Advice { get; set; } + + /// + /// Is this advice associated with a particular instance? + /// + /// + ///

    + /// Not supported for dynamic advisors. + ///

    + ///
    + /// + /// if this advice is associated with a + /// particular instance. + /// + /// Always. + /// + public virtual bool IsPerInstance + { + get + { + throw new NotSupportedException( + "The 'IsPerInstance' property of the IAdvisor interface " + + "is not yet supported in Spring.NET."); + } + } + + #endregion + + #region IPointcutAdvisor Members + + /// + /// The that drives this advisor. + /// + public abstract IPointcut Pointcut { get; set; } + + #endregion + + #region Methods + /// + /// Determines whether the specified + /// is equal to the current . + /// + /// The advisor to compare with. + /// + /// if this instance is equal to the + /// specified . + /// + public override bool Equals(object o) + { + if (!(o is AbstractPointcutAdvisor)) + { + return false; + } + IPointcutAdvisor otherAdvisor = (IPointcutAdvisor)o; + if (otherAdvisor.Advice == null && otherAdvisor.Pointcut == null) + { + return (this.Advice == null && this.Pointcut == null); + } + else if (otherAdvisor.Advice == null) + { + return (Advice == null && otherAdvisor.Pointcut.Equals(this.Pointcut)); + } + else if (otherAdvisor.Pointcut == null) + { + return (this.Pointcut == null && otherAdvisor.Advice.Equals(this.Advice)); + } + else + { + return otherAdvisor.Advice.Equals(this.Advice) && otherAdvisor.Pointcut.Equals(this.Pointcut); + } + } + + /// + /// Serves as a hash function for a particular type, suitable for use + /// in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return 0 // (SPRNET-847) base.GetHashCode() + + 13 * (Pointcut == null ? 0 : Pointcut.GetHashCode()) + + 27 * (Advice == null ? 0 : Advice.GetHashCode()); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs new file mode 100644 index 00000000..b6840af2 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs @@ -0,0 +1,275 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Permissions; +using AopAlliance.Aop; +using Spring.Core; +using Spring.Objects; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Abstract base regular expression pointcut object. + /// + /// + ///

    + /// The regular expressions must be a match. For example, the + /// .*Get.* pattern will match Com.Mycom.Foo.GetBar(), and + /// Get.* will not. + ///

    + ///

    + /// This base class is serializable. Subclasses should decorate all + /// fields with the - the + /// + /// method in this class will be invoked again on the client side on deserialization. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Simon White (.NET) + [Serializable] + public abstract class AbstractRegularExpressionMethodPointcut + : StaticMethodMatcherPointcut, ITypeFilter, ISerializable + { + [NonSerialized] + private object[] _patterns = ObjectUtils.EmptyObjects; + + #region Constructors + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an abstract class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + protected AbstractRegularExpressionMethodPointcut() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + /// + /// If an error was encountered during the deserialization process. + /// + protected AbstractRegularExpressionMethodPointcut( + SerializationInfo info, StreamingContext context) + { + _patterns = (object[]) info.GetValue("Patterns", typeof(object[])); + try + { + InitPatternRepresentation(_patterns); + } + catch (Exception ex) + { + throw new AspectException( + "Failed to deserialize AOP regular expression pointcut: " + ex.Message); + } + } + + #endregion + + #region Properties + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public override ITypeFilter TypeFilter + { + get { return this; } + } + + /// + /// Convenience property for setting a single pattern. + /// + /// + /// Use this property or Patterns, not both. + /// + public virtual object Pattern + { + get { return (_patterns.Length > 0 ? _patterns[0] : null); } + set + { + AssertUtils.ArgumentNotNull(value, "Pattern"); + this.Patterns = new object[] {value}; + } + } + + /// + /// The regular expressions defining methods to match. + /// + /// + /// Matching will be the union of all these; if any match, + /// the pointcut matches. + /// + public virtual object[] Patterns + { + get { return _patterns; } + set + { + AssertUtils.ArgumentNotNull(value, "Patterns"); + this._patterns = value; + InitPatternRepresentation(this.Patterns); + } + } + + #endregion + + #region Methods + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Patterns", _patterns); + } + + /// + /// Subclasses must implement this to initialize regular expression pointcuts. + /// + /// + ///

    + /// Can be invoked multiple times. + ///

    + ///

    + /// This method will be invoked from the property, + /// and also on deserialization. + ///

    + ///
    + /// + /// The patterns to initialize. + /// + /// + /// In the case of an invalid pattern. + /// + protected abstract void InitPatternRepresentation(object[] patterns); + + /// + /// Does the pattern at the supplied + /// match this ? + /// + /// The pattern to match + /// The index of pattern. + /// + /// if there is a match. + /// + protected abstract bool Matches(string pattern, int patternIndex); + + /// + /// Does the supplied satisfy this matcher? + /// + /// + ///

    + /// Try to match the regular expression against the fully qualified name + /// of the method's declaring , plus the name of + /// the supplied . + ///

    + ///

    + /// Note that the declaring is that + /// that originally declared + /// the method, not necessarily the that is + /// currently exposing it. For example, + /// matches any subclass of 's + /// method. + ///

    + ///
    + /// The candidate method. + /// + /// The target (may be , + /// in which case the candidate must be taken + /// to be the 's declaring class). + /// + /// + /// if this this method matches statically. + /// + public override bool Matches(MethodInfo method, Type targetType) + { + string patt = method.DeclaringType.FullName + "." + method.Name; + for (int i = 0; i < this.Patterns.Length; ++i) + { + bool matched = Matches(patt, i); + if (matched) + { + return true; + } + } + return false; + } + + /// + /// Should the pointcut apply to the supplied + /// ? + /// + /// + ///

    + /// In this instance, simply returns . + ///

    + ///
    + /// + /// The candidate . + /// + /// + /// if the advice should apply to the supplied + /// + /// + public bool Matches(Type type) + { + return true; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/AttributeMatchMethodPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/AttributeMatchMethodPointcut.cs new file mode 100644 index 00000000..70decf3e --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/AttributeMatchMethodPointcut.cs @@ -0,0 +1,204 @@ +#region License + +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// implementation that matches methods + /// that have been decorated with a specified . + /// + /// Aleksandar Seovic + /// Ronald Wildenberg + /// $Id: AttributeMatchMethodPointcut.cs,v 1.8 2007/05/21 16:43:45 bbaia Exp $ + [Serializable] + public class AttributeMatchMethodPointcut : StaticMethodMatcherPointcut + { + private Type _attribute; + private bool _inherit = true; + private bool _checkInterfaces = false; + + /// + /// Creates a new instance of the + /// class. + /// + public AttributeMatchMethodPointcut() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The to match. + /// + public AttributeMatchMethodPointcut(Type attribute) + : this(attribute, true, false) + { + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The to match. + /// + /// + /// Flag that controls whether or not the inheritance tree of the + /// method to be included in the search for the ? + /// + public AttributeMatchMethodPointcut(Type attribute, bool inherit) + : this(attribute, inherit, false) + { + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The to match. + /// + /// + /// Flag that controls whether or not the inheritance tree of the + /// method to be included in the search for the ? + /// + /// + /// Flag that controls whether or not interfaces attributes of the + /// method to be included in the search for the ? + /// + public AttributeMatchMethodPointcut(Type attribute, bool inherit, bool checkInterfaces) + { + Attribute = attribute; + Inherit = inherit; + CheckInterfaces = checkInterfaces; + } + + /// + /// The to match. + /// + /// + /// If the supplied value is not a that + /// derives from the class. + /// + public virtual Type Attribute + { + get { return _attribute; } + set + { + if (value != null) + { + if (!typeof (Attribute).IsAssignableFrom(value)) + { + throw new ArgumentException( + string.Format( + "The [{0}] Type must be derived from the [System.Attribute] class.", + value)); + } + } + _attribute = value; + } + } + + /// + /// Is the inheritance tree of the method to be included in the search for the + /// ? + /// + /// + ///

    + /// The default is . + ///

    + ///
    + public virtual bool Inherit + { + get { return _inherit; } + set { _inherit = value; } + } + + /// + /// Is the interfaces attributes of the method to be included in the search for the + /// ? + /// + /// + ///

    + /// The default is . + ///

    + ///
    + public virtual bool CheckInterfaces + { + get { return _checkInterfaces; } + set { _checkInterfaces = value; } + } + + /// + /// Does the supplied satisfy this matcher? + /// + /// The candidate method. + /// + /// The target (may be , + /// in which case the candidate must be taken + /// to be the 's declaring class). + /// + /// + /// if this this method matches statically. + /// + public override bool Matches(MethodInfo method, Type targetType) + { + if (method.IsDefined(Attribute, Inherit)) + { + // Checks whether the attribute is defined on the method or a super definition of the method + // but does not check attributes on implemented interfaces. + return true; + } + else + { + if (CheckInterfaces) + { + Type[] parameterTypes = ReflectionUtils.GetParameterTypes(method); + + // Also check whether the attribute is defined on a method implemented from an interface. + // First find all interfaces for the type that contains the method. + // Next, check each interface for the presence of the attribute on the corresponding + // method from the interface. + foreach (Type interfaceType in method.DeclaringType.GetInterfaces()) + { + MethodInfo intfMethod = interfaceType.GetMethod(method.Name, parameterTypes); + if (intfMethod != null && intfMethod.IsDefined(Attribute, Inherit)) + { + return true; + } + } + } + return false; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/AttributeMatchMethodPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/AttributeMatchMethodPointcutAdvisor.cs new file mode 100644 index 00000000..3e1d80d4 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/AttributeMatchMethodPointcutAdvisor.cs @@ -0,0 +1,118 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using Spring.Core; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Convenient class for attribute-match method pointcuts that hold an Interceptor, + /// making them an Advisor. + /// + /// Bruno Baia + /// $Id: AttributeMatchMethodPointcutAdvisor.cs,v 1.3 2007/03/16 04:01:23 aseovic Exp $ + [Serializable] + public class AttributeMatchMethodPointcutAdvisor + : AttributeMatchMethodPointcut, IPointcutAdvisor, IOrdered + { + private int _order = Int32.MaxValue; + private IAdvice _advice; + + /// + /// Creates a new instance of the + /// class. + /// + public AttributeMatchMethodPointcutAdvisor() + { + } + + /// + /// Creates a new instance of the + /// class + /// for the supplied . + /// + /// + public AttributeMatchMethodPointcutAdvisor(IAdvice advice) + { + this._advice = advice; + } + + /// + /// Is this advice associated with a particular instance? + /// + /// + /// if this advice is associated with a + /// particular instance. + /// + /// + /// Always; this property is not yet supported. + /// + public virtual bool IsPerInstance + { + get + { + throw new NotSupportedException( + "The 'IsPerInstance' property of the IAdvisor interface is " + + "not yet supported in Spring.NET."); + } + } + + /// + /// Returns this s order in the + /// interception chain. + /// + /// + /// This s order in the + /// interception chain. + /// + public virtual int Order + { + get { return this._order; } + set { this._order = value; } + } + + /// + /// Return the advice part of this advisor. + /// + /// + /// The advice that should apply if the pointcut matches. + /// + /// + public virtual IAdvice Advice + { + get { return this._advice; } + set { this._advice = value; } + } + + /// + /// The that drives this advisor. + /// + public virtual IPointcut Pointcut + { + get { return this; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/ComposablePointcut.cs b/src/Spring/Spring.Aop/Aop/Support/ComposablePointcut.cs new file mode 100644 index 00000000..c8f7d885 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/ComposablePointcut.cs @@ -0,0 +1,176 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Convenient class for building up pointcuts. + /// + /// + ///

    + /// All methods return a + /// instance, which facilitates the following concise usage pattern... + ///

    + /// + /// IPointcut pointcut = new ComposablePointcut() + /// .Union(typeFilter) + /// .Intersection(methodMatcher) + /// .Intersection(pointcut); + /// + ///

    + /// There is no Union() method on this class. Use the + /// method for such functionality. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: ComposablePointcut.cs,v 1.7 2007/03/16 04:01:23 aseovic Exp $ + [Serializable] + public class ComposablePointcut : IPointcut + { + private ITypeFilter _typeFilter; + private IMethodMatcher _methodMatcher; + + /// + /// Creates a new instance of the + /// class + /// that matches all the methods on all s. + /// + public ComposablePointcut() + { + _typeFilter = TrueTypeFilter.True; + _methodMatcher = TrueMethodMatcher.True; + } + + /// + /// Creates a new instance of the + /// class + /// that uses the supplied and + /// . + /// + /// + /// The type filter to use. + /// + /// + /// The method matcher to use. + /// + public ComposablePointcut(ITypeFilter typeFilter, IMethodMatcher methodMatcher) + { + _typeFilter = typeFilter; + _methodMatcher = methodMatcher; + } + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public virtual ITypeFilter TypeFilter + { + get { return _typeFilter; } + } + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public virtual IMethodMatcher MethodMatcher + { + get { return _methodMatcher; } + } + + /// + /// Changes the current type filter to be the union of the existing filter and the + /// supplied . + /// + /// The filter to union with. + /// + /// The union of the existing filter and the supplied . + /// + public virtual ComposablePointcut Union(ITypeFilter filter) + { + _typeFilter = TypeFilters.Union(_typeFilter, filter); + return this; + } + + /// + /// Changes the current type filter to be the intersection of the existing filter + /// and the supplied . + /// + /// The filter to diff against. + /// + /// The intersection of the existing filter and the supplied . + /// + public virtual ComposablePointcut Intersection(ITypeFilter filter) + { + _typeFilter = TypeFilters.Intersection(_typeFilter, filter); + return this; + } + + /// + /// Changes the current method matcher to be the union of the existing matcher and the + /// supplied . + /// + /// The matcher to union with. + /// + /// The union of the existing matcher and the supplied . + /// + public virtual ComposablePointcut Union(IMethodMatcher matcher) + { + _methodMatcher = MethodMatchers.Union(_methodMatcher, matcher); + return this; + } + + /// + /// Changes the current method matcher to be the intersection of the existing matcher + /// and the supplied . + /// + /// The matcher to diff against. + /// + /// The intersection of the existing matcher and the supplied . + /// + public virtual ComposablePointcut Intersection(IMethodMatcher matcher) + { + _methodMatcher = MethodMatchers.Intersection(_methodMatcher, matcher); + return this; + } + + /// + /// Changes current pointcut to intersection of the current and supplied pointcut + /// + /// pointcut to diff against + /// updated pointcut + public virtual ComposablePointcut Intersection(IPointcut other) + { + _typeFilter = TypeFilters.Intersection(_typeFilter, other.TypeFilter); + _methodMatcher = MethodMatchers.Intersection(_methodMatcher, other.MethodMatcher); + return this; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/ControlFlowPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/ControlFlowPointcut.cs new file mode 100644 index 00000000..2fd163f9 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/ControlFlowPointcut.cs @@ -0,0 +1,244 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Core; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Pointcut and method matcher for use in simple cflow-style + /// pointcuts. + /// + /// + ///

    + /// Evaluating such pointcuts is slower than evaluating normal pointcuts, + /// but can nevertheless be useful in some cases. Of course, your mileage + /// may vary as to what 'slower' actually means. + ///

    + ///
    + /// Rod Johnson + /// Simon White (.NET) + /// $Id: ControlFlowPointcut.cs,v 1.6 2006/04/09 07:18:37 markpollack Exp $ + [Serializable] + public class ControlFlowPointcut : IPointcut, ITypeFilter, IMethodMatcher + { + #region Fields + + private Type _type; + private string _methodName; + private int _evaluationCount; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The class under which all control flows are to be matched. + /// + public ControlFlowPointcut(Type type) : this(type, null) + { + } + + /// + /// Construct a new pointcut that matches all calls below the + /// given method in the given class. + /// + /// + ///

    + /// If the supplied is + /// , all control flows below the given + /// class will be successfully matched. + ///

    + ///
    + /// + /// The class under which all control flows are to be matched. + /// + /// + /// The method name under which all control flows are to be matched. + /// + public ControlFlowPointcut(Type type, string methodName) + { + _type = type; + _methodName = methodName; + } + + #endregion + + #region Properties + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public ITypeFilter TypeFilter + { + get { return this; } + } + + /// + /// Gets the number of times this pointcut has been evaluated. + /// + /// + ///

    + /// Useful as a debugging aid. + ///

    + ///

    + /// Note that this value is distinct from the number of times that this + /// pointcut sucessfully matches a target method, in that a + /// may be evaluated many times but + /// never actually match even once. + ///

    + ///
    + /// + /// The number of times this pointcut has been evaluated. + /// + public int EvaluationCount + { + get { return _evaluationCount; } + } + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public IMethodMatcher MethodMatcher + { + get { return this; } + } + + /// + /// Is this a runtime pointcut? + /// + /// + ///

    + /// This implementation is a runtime pointcut, and so always returns + /// . + ///

    + ///
    + /// + /// if this is a runtime pointcut. + /// + /// + public bool IsRuntime + { + get { return true; } + } + + #endregion + + #region Methods + + /// + /// Should the pointcut apply to the supplied ? + /// + /// + ///

    + /// Subclasses are encouraged to override this method for greater + /// filtering (and performance). + ///

    + ///

    + /// This, the default, implementation always matches (returns + /// ). + ///

    + ///
    + /// The candidate target class. + /// + /// if the advice should apply to the supplied + /// + /// + public virtual bool Matches(Type type) + { + return true; + } + + /// + /// Does the supplied satisfy this matcher? + /// Perform static checking. If this returns false, or if the isRuntime() method + /// returns false, no runtime check will be made. + /// + /// + ///

    + /// Subclasses are encouraged to override this method if it is possible + /// to filter out some candidate classes. + ///

    + ///

    + /// This, the default, implementation always matches (returns + /// ). This means that the three argument + /// + /// method will always be invoked. + ///

    + ///
    + /// The candidate method. + /// + /// The target class (may be , in which case the + /// candidate class must be taken to be the 's + /// declaring class). + /// + /// + /// if this this method matches statically. + /// + /// + public virtual bool Matches(MethodInfo method, Type targetType) + { + return true; + } + + /// + /// Is there a runtime (dynamic) match for the supplied + /// ? + /// + /// + ///

    + /// Subclasses are encouraged to override this method if it is possible + /// to filter out some candidate classes. + ///

    + ///
    + /// The candidate method. + /// The target class. + /// The arguments to the method + /// + /// if there is a runtime match. + /// + public virtual bool Matches(MethodInfo method, Type targetType, object[] args) + { + ++_evaluationCount; + IControlFlow cflow = ControlFlowFactory.CreateControlFlow(); + return (_methodName != null) + ? cflow.Under(_type, _methodName) + : cflow.Under(_type); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/DefaultIntroductionAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/DefaultIntroductionAdvisor.cs new file mode 100644 index 00000000..00987686 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/DefaultIntroductionAdvisor.cs @@ -0,0 +1,241 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using Spring.Collections; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Simple implementation that + /// by default applies to any class. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: DefaultIntroductionAdvisor.cs,v 1.11 2007/03/16 04:01:23 aseovic Exp $ + [Serializable] + public class DefaultIntroductionAdvisor : IIntroductionAdvisor, ITypeFilter + { + private IAdvice _introduction; + private ISet _interfaces = new HybridSet(); + + /// + /// Creates a new instance of the + /// class using + /// the supplied + /// + /// + ///

    + /// This constructor adds all interfaces implemented by the supplied + /// (except the + /// interface) to the list of + /// interfaces to introduce. + ///

    + ///
    + /// The introduction to use. + public DefaultIntroductionAdvisor(IAdvice introduction) + : this(introduction, introduction.GetType().GetInterfaces()) + { + } + + /// + /// Creates a new instance of the + /// class using + /// the supplied + /// + /// The introduction to use. + /// + /// The interface to introduce. + /// + public DefaultIntroductionAdvisor(IAdvice introduction, Type intf) + : this(introduction, new Type[] {intf}) + { + } + + /// + /// Creates a new instance of the + /// class using + /// the supplied + /// + /// The introduction to use. + /// + /// The interfaces to introduce. + /// + /// + /// If the supplied is . + /// + public DefaultIntroductionAdvisor(IAdvice introduction, Type[] interfaces) + { + if (introduction == null) + { + throw new ArgumentNullException("introduction", "Introduction cannot be null"); + } + _introduction = introduction; + foreach (Type intf in interfaces) + { + if (intf != typeof (IAdvice)) + { + AddInterface(intf); + } + } + } + + /// + /// Returns the filter determining which target classes this + /// introduction should apply to. + /// + /// + /// The filter determining which target classes this introduction + /// should apply to. + /// + public virtual ITypeFilter TypeFilter + { + get { return this; } + } + + /// + /// Gets the interfaces introduced by this + /// . + /// + /// + /// The interfaces introduced by this + /// . + /// + public virtual Type[] Interfaces + { + get + { + Type[] interfaces = new Type[_interfaces.Count]; + _interfaces.CopyTo(interfaces, 0); + return interfaces; + } + } + + /// + /// Is this advice associated with a particular instance? + /// + /// + ///

    + /// Default for an introduction is per-instance interception. + ///

    + ///
    + /// + /// if this advice is associated with a + /// particular instance. + /// + public virtual bool IsPerInstance + { + get { return true; } + } + + /// + /// Return the advice part of this aspect. + /// + /// + /// The advice that should apply if the pointcut matches. + /// + public virtual IAdvice Advice + { + get { return this._introduction; } + } + + /// + /// Adds the supplied to the list of + /// introduced interfaces. + /// + /// The interface to add. + /// + /// If any of the are not interface . + /// + public virtual void AddInterface(Type intf) + { + if(intf != null) + { + BailIfNotAnInterfaceType(intf); + _interfaces.Add(intf); + } + } + + /// + /// Should the pointcut apply to the supplied + /// ? + /// + /// + ///

    + /// This, the default, implementation always returns . + ///

    + ///
    + /// + /// The candidate . + /// + /// + /// if the advice should apply to the supplied + /// + /// + public virtual bool Matches(Type type) + { + return true; + } + + /// + /// Can the advised interfaces be implemented by the introduction + /// advice? + /// + /// + ///

    + /// Invoked before adding an + /// . + ///

    + ///
    + /// + /// If the advised interfaces cannot be implemented by the introduction + /// advice. + /// + /// + /// + /// If any of the are not interface . + /// + public virtual void ValidateInterfaces() + { + foreach (Type intf in _interfaces) + { + BailIfNotAnInterfaceType(intf); + if (! intf.IsAssignableFrom(_introduction.GetType())) + { + throw new ArgumentException("Introduction [" + _introduction.GetType().FullName + "] " + + "does not implement interface '" + intf.FullName + "' specified in introduction advice."); + } + } + } + + private static void BailIfNotAnInterfaceType(Type intf) + { + if (intf != null && !intf.IsInterface) + { + throw new ArgumentException("Type [" + intf.FullName + "] is not an interface; cannot be used in an introduction."); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/DefaultObjectFactoryPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/DefaultObjectFactoryPointcutAdvisor.cs new file mode 100644 index 00000000..07a0d700 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/DefaultObjectFactoryPointcutAdvisor.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Concrete ObjectFactory-based IPointcutAdvisor thta allows for any Advice to be + /// configured as reference to an Advice object in the ObjectFatory, as well as + /// the Pointcut to be configured through an object property. + /// + /// + /// Specifying the name of an advice object instead of the advice object itself + /// (if running within a ObjectFactory/ApplicationContext) increases loose coupling + /// at initialization time, in order to not intialize the advice object until the pointcut + /// actually matches. + /// + /// Juerge Hoeller + /// Mark Pollack + /// $Id: DefaultObjectFactoryPointcutAdvisor.cs,v 1.1 2007/05/30 22:35:43 markpollack Exp $ + public class DefaultObjectFactoryPointcutAdvisor : AbstractObjectFactoryPointcutAdvisor + { + private IPointcut pointcut = TruePointcut.True; + + + /// + /// The that drives this advisor. + /// + public override IPointcut Pointcut + { + get { return pointcut; } + set { pointcut = (pointcut != null ? value : TruePointcut.True); } + } + + /// + /// Describe this Advisor, showing pointcut and name of advice object. + /// + /// Type name , pointcut, and advice object name. + public override string ToString() + { + return GetType().Name + ": pointcut [" + Pointcut + "]; advice object = '" + AdviceObjectName + "'"; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/DefaultPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/DefaultPointcutAdvisor.cs new file mode 100644 index 00000000..0a0929a0 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/DefaultPointcutAdvisor.cs @@ -0,0 +1,111 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Convenient pointcut-driven advisor implementation. + /// + /// + ///

    + /// This is the most commonly used implementation. + /// It can be used with any pointcut and advice type, except for introductions. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: DefaultPointcutAdvisor.cs,v 1.8 2007/05/30 22:35:43 markpollack Exp $ + [Serializable] + public class DefaultPointcutAdvisor : AbstractGenericPointcutAdvisor + { + + private IPointcut pointcut = TruePointcut.True; + + /// + /// Creates a new instance of the + /// class. + /// + public DefaultPointcutAdvisor() + { + } + + /// + /// Creates a new instance of the + /// + /// class for the supplied , + /// + /// + /// The advice to use. + /// + public DefaultPointcutAdvisor(IAdvice advice) + : this(TruePointcut.True, advice) + { + } + + /// + /// Creates a new instance of the + /// + /// class for the supplied and + /// . + /// + /// + /// The advice to use. + /// + /// + /// The pointcut to use. + /// + public DefaultPointcutAdvisor(IPointcut pointcut, IAdvice advice) + { + this.pointcut = pointcut; + Advice = advice; + } + + /// + /// The that drives this advisor. + /// + public override IPointcut Pointcut + { + get { return pointcut; } + set { pointcut = value;} + } + + + + + /// + /// Returns a that represents the current + /// . + /// + /// + /// A representation of this advisor. + /// + public override string ToString() + { + return GetType().Name + ": pointcut=[" + pointcut + "] advice=[" + Advice + "]"; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/DynamicMethodMatcher.cs b/src/Spring/Spring.Aop/Aop/Support/DynamicMethodMatcher.cs new file mode 100644 index 00000000..5b3301ba --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/DynamicMethodMatcher.cs @@ -0,0 +1,114 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Convenient abstract superclass for dynamic method matchers that do + /// care about arguments at runtime. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: DynamicMethodMatcher.cs,v 1.5 2006/04/09 07:18:37 markpollack Exp $ + public abstract class DynamicMethodMatcher : IMethodMatcher + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + protected DynamicMethodMatcher() + { + } + + #endregion + + /// + /// Is this dynamic? + /// + /// + /// Always returns , to specify that this is a + /// dynamic matcher. + /// + public virtual bool IsRuntime + { + get { return true; } + } + + /// + /// Does the supplied satisfy this matcher? + /// + /// + ///

    + /// Derived classes can override this method to add preconditions for + /// dynamic matching. + ///

    + ///

    + /// This implementation always returns . + ///

    + ///
    + /// The candidate method. + /// + /// The target (may be , + /// in which case the candidate must be taken + /// to be the 's declaring class). + /// + /// + /// if this this method matches statically. + /// + public virtual bool Matches(MethodInfo method, Type targetType) + { + return true; + } + + /// + /// Is there a runtime (dynamic) match for the supplied + /// ? + /// + /// + ///

    + /// Must be overriden by derived classes to provide criteria for dynamic matching. + ///

    + ///
    + /// The candidate method. + /// + /// The target . + /// + /// The arguments to the method + /// + /// if there is a runtime match. + public abstract bool Matches(MethodInfo method, Type targetType, object[] args); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/DynamicMethodMatcherPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/DynamicMethodMatcherPointcutAdvisor.cs new file mode 100644 index 00000000..00371f96 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/DynamicMethodMatcherPointcutAdvisor.cs @@ -0,0 +1,176 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using Spring.Core; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Convenient superclass for s + /// that are also dynamic pointcuts. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: DynamicMethodMatcherPointcutAdvisor.cs,v 1.6 2007/03/16 04:01:24 aseovic Exp $ + [Serializable] + public abstract class DynamicMethodMatcherPointcutAdvisor + : DynamicMethodMatcher, IPointcutAdvisor, IPointcut, IOrdered + { + private int _order = Int32.MaxValue; + private IAdvice _advice; + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an abstract class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + protected DynamicMethodMatcherPointcutAdvisor() + { + } + + /// + /// Creates a new instance of the + /// + /// class for the supplied . + /// + /// + ///

    + /// This is an abstract class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + /// + /// The advice portion of this advisor. + /// + protected DynamicMethodMatcherPointcutAdvisor(IAdvice advice) + { + this._advice = advice; + } + + /// + /// Is this advice associated with a particular instance? + /// + /// + ///

    + /// Not supported for dynamic advisors. + ///

    + ///
    + /// + /// if this advice is associated with a + /// particular instance. + /// + /// Always. + /// + public virtual bool IsPerInstance + { + get + { + throw new NotSupportedException( + "The 'IsPerInstance' property of the IAdvisor interface " + + "is not yet supported in Spring.NET."); + } + } + + /// + /// The for this pointcut. + /// + /// + ///

    + /// This implementation always returns a filter that evaluates to + /// for any . + ///

    + ///
    + /// + /// The current . + /// + public virtual ITypeFilter TypeFilter + { + get { return TrueTypeFilter.True; } + } + + /// + /// The for this pointcut. + /// + /// + ///

    + /// This implementation always returns itself (this object). + ///

    + ///
    + /// + /// The current . + /// + public virtual IMethodMatcher MethodMatcher + { + get { return this; } + } + + /// + /// Returns this s order in the + /// interception chain. + /// + /// + /// This s order in the + /// interception chain. + /// + public virtual int Order + { + get { return this._order; } + set { this._order = value; } + } + + /// + /// Return the advice part of this aspect. + /// + /// + /// The advice that should apply if the pointcut matches. + /// + /// + public virtual IAdvice Advice + { + get { return this._advice; } + set { this._advice = value; } + } + + /// + /// The that drives this advisor. + /// + /// + ///

    + /// This implementation always returns itself (this object). + ///

    + ///
    + public IPointcut Pointcut + { + get { return this; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/MethodMatchers.cs b/src/Spring/Spring.Aop/Aop/Support/MethodMatchers.cs new file mode 100644 index 00000000..9047ac75 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/MethodMatchers.cs @@ -0,0 +1,181 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Various utility methods relating to the composition of + /// s. + /// + /// + ///

    + /// A method matcher may be evaluated statically (based on method and target + /// class) or need further evaluation dynamically (based on arguments at + /// the time of method invocation). + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: MethodMatchers.cs,v 1.7 2007/03/16 04:01:24 aseovic Exp $ + public sealed class MethodMatchers + { + /// + /// Creates a new that is the + /// union of the two supplied s. + /// + /// + ///

    + /// The newly created matcher will match all the methods that either of the two + /// supplied matchers would match. + ///

    + ///
    + /// The first method matcher. + /// The second method matcher. + /// + /// A new that is the + /// union of the two supplied s + /// + public static IMethodMatcher Union( + IMethodMatcher firstMatcher, IMethodMatcher secondMatcher) + { + return new UnionMethodMatcher(firstMatcher, secondMatcher); + } + + /// + /// Creates a new that is the + /// intersection of the two supplied s. + /// + /// + ///

    + /// The newly created matcher will match only those methods that both + /// of the supplied matchers would match. + ///

    + ///
    + /// The first method matcher. + /// The second method matcher. + /// + /// A new that is the + /// intersection of the two supplied s + /// + public static IMethodMatcher Intersection( + IMethodMatcher firstMatcher, IMethodMatcher secondMatcher) + { + return new IntersectionMethodMatcher(firstMatcher, secondMatcher); + } + + #region Inner Class : UnionMethodMatcher + + [Serializable] + private sealed class UnionMethodMatcher : IMethodMatcher + { + private IMethodMatcher a; + private IMethodMatcher b; + + public UnionMethodMatcher(IMethodMatcher a, IMethodMatcher b) + { + this.a = a; + this.b = b; + } + + public bool IsRuntime + { + get { return a.IsRuntime || b.IsRuntime; } + } + + public bool Matches(MethodInfo m, Type targetType) + { + return a.Matches(m, targetType) || b.Matches(m, targetType); + } + + public bool Matches(MethodInfo m, Type targetType, object[] args) + { + return a.Matches(m, targetType, args) || b.Matches(m, targetType, args); + } + } + + #endregion + + #region Inner Class : IntersectionMethodMatcher + + [Serializable] + private sealed class IntersectionMethodMatcher : IMethodMatcher + { + private IMethodMatcher a; + private IMethodMatcher b; + + public IntersectionMethodMatcher(IMethodMatcher a, IMethodMatcher b) + { + this.a = a; + this.b = b; + } + + public bool IsRuntime + { + get { return a.IsRuntime || b.IsRuntime; } + } + + public bool Matches(MethodInfo m, Type targetType) + { + return a.Matches(m, targetType) && b.Matches(m, targetType); + } + + public bool Matches(MethodInfo m, Type targetType, object[] args) + { + // Because a dynamic intersection may be composed of a static and dynamic part, + // we must avoid calling the 3-arg matches method on a dynamic matcher, as + // it will probably be an unsupported operation. + bool aMatches = a.IsRuntime ? a.Matches(m, targetType, args) : a.Matches(m, targetType); + bool bMatches = b.IsRuntime ? b.Matches(m, targetType, args) : b.Matches(m, targetType); + return aMatches && bMatches; + } + } + + #endregion + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + private MethodMatchers() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/NameMatchMethodPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/NameMatchMethodPointcut.cs new file mode 100644 index 00000000..72a91684 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/NameMatchMethodPointcut.cs @@ -0,0 +1,122 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Pointcut object for simple method name matches, useful as an alternative to pure + /// regular expression based patterns. + /// + /// Juergen Hoeller + /// Aleksandar Seovic (.NET) + /// $Id: NameMatchMethodPointcut.cs,v 1.9 2007/03/16 04:01:24 aseovic Exp $ + [Serializable] + public class NameMatchMethodPointcut : StaticMethodMatcherPointcut + { + private string[] _mappedNames = new string[0]; + + /// + /// Convenience property when we have only a single method name + /// to match. + /// + /// + /// + /// Use either this property or the + /// property, + /// not both. + /// + /// + public virtual string MappedName + { + set { MappedNames = new string[] {value}; } + } + + /// + /// Set the method names defining methods to match. + /// + /// + ///

    + /// Matching will be the union of all these; if any match, the pointcut matches. + ///

    + ///
    + public virtual string[] MappedNames + { + set { this._mappedNames = value; } + } + + /// + /// Does the of the supplied + /// matches any of the mapped names? + /// + /// + /// The to check. + /// + /// + /// The of the target class. + /// + /// + /// if the name of the supplied + /// matches one of the mapped names. + /// + public override bool Matches(MethodInfo method, Type targetType) + { + for (int i = 0; i < this._mappedNames.Length; i++) + { + string mappedName = this._mappedNames[i]; + if (mappedName.Equals(method.Name) || IsMatch(method.Name, mappedName)) + { + return true; + } + } + return false; + } + + /// + /// Does the supplied match the supplied ? + /// + /// + ///

    + /// The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, + /// as well as direct equality. Can be overridden in subclasses. + ///

    + ///
    + /// + /// The method name of the class. + /// + /// + /// The name in the descriptor. + /// + /// + /// True if the names match. + /// + protected virtual bool IsMatch(string methodName, string mappedName) + { + return PatternMatchUtils.SimpleMatch(mappedName, methodName); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/NameMatchMethodPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/NameMatchMethodPointcutAdvisor.cs new file mode 100644 index 00000000..0e86300c --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/NameMatchMethodPointcutAdvisor.cs @@ -0,0 +1,130 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Convenient class for name-match method pointcuts that hold an Interceptor, + /// making them an Advisor. + /// + /// Juergen Hoeller + /// Aleksandar Seovic (.NET) + /// Mark Pollack (.NET) + /// $Id: NameMatchMethodPointcutAdvisor.cs,v 1.5 2007/05/30 22:35:43 markpollack Exp $ + [Serializable] + public class NameMatchMethodPointcutAdvisor : AbstractGenericPointcutAdvisor + { + + private NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); + + #region Constructor(s) + + /// + /// Creates a new instance of the + /// class. + /// + public NameMatchMethodPointcutAdvisor() + { + } + + /// + /// Creates a new instance of the + /// class + /// for the supplied . + /// + /// + public NameMatchMethodPointcutAdvisor(IAdvice advice) + { + Advice = advice; + } + + #endregion + + + #region Properties + /// + /// The for this pointcut. + /// + /// Default is + /// + /// The current . + /// + public ITypeFilter TypeFilter { + set + { + pointcut.TypeFilter = value; + } + } + + /// + /// Convenience property when we have only a single method name + /// to match. + /// + /// + /// + /// Use either this property or the + /// property, + /// not both. + /// + /// + public string MappedName + { + set { pointcut.MappedName = value; } + } + + /// + /// Set the method names defining methods to match. + /// + /// + ///

    + /// Matching will be the union of all these; if any match, the pointcut matches. + ///

    + ///
    + public string[] MappedNames + { + set { pointcut.MappedNames = value; } + } + + /// + /// The that drives this advisor. + /// + public override IPointcut Pointcut + { + get { return pointcut; } + set + { + AssertUtils.AssertArgumentType(value, "pointcut", typeof(NameMatchMethodPointcut), + "Pointcut most be compatible with type NameMatchMethodPointcut"); + pointcut = value as NameMatchMethodPointcut; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/Pointcuts.cs b/src/Spring/Spring.Aop/Aop/Support/Pointcuts.cs new file mode 100644 index 00000000..33c86860 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/Pointcuts.cs @@ -0,0 +1,146 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Various related utility methods. + /// + /// + ///

    + /// These methods are particularly useful for composing pointcuts + /// using the union and intersection methods. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: Pointcuts.cs,v 1.6 2007/03/16 04:01:25 aseovic Exp $ + public sealed class Pointcuts + { + /// + /// Creates a union of the two supplied pointcuts. + /// + /// The first pointcut. + /// The second pointcut. + /// + /// The union of the two supplied pointcuts. + /// + /// + public static IPointcut Union(IPointcut firstPointcut, IPointcut secondPointcut) + { + return new UnionPointcut(firstPointcut, secondPointcut); + } + + /// + /// Creates an that is the + /// intersection of the two supplied pointcuts. + /// + /// The first pointcut. + /// The second pointcut. + /// + /// An that is the + /// intersection of the two supplied pointcuts. + /// + public static IPointcut Intersection(IPointcut firstPointcut, IPointcut secondPointcut) + { + return new ComposablePointcut( + firstPointcut.TypeFilter, firstPointcut.MethodMatcher) + .Intersection(secondPointcut); + } + + /// + /// Performs the least expensive check for a match. + /// + /// + /// The to be evaluated. + /// + /// The candidate method. + /// + /// The target . + /// + /// The arguments to the method + /// if there is a runtime match. + /// + public static bool Matches( + IPointcut pointcut, MethodInfo method, Type targetType, object[] args) + { + if(pointcut != null) + { + if (pointcut == TruePointcut.True) + { + return true; + } + if (pointcut.TypeFilter.Matches(targetType)) + { + IMethodMatcher mm = pointcut.MethodMatcher; + if (mm.Matches(method, targetType)) + { + return mm.IsRuntime ? mm.Matches(method, targetType, args) : true; + } + } + } + return false; + } + + /// + /// Are the supplied s equal? + /// + /// The first pointcut. + /// The second pointcut. + /// + /// if the supplied s + /// are equal. + /// + public static bool AreEqual(IPointcut firstPointcut, IPointcut secondPointcut) + { + return firstPointcut.TypeFilter == secondPointcut.TypeFilter + && firstPointcut.MethodMatcher == secondPointcut.MethodMatcher; + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + private Pointcuts() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/RegularExpressionMethodPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/RegularExpressionMethodPointcutAdvisor.cs new file mode 100644 index 00000000..2195a67a --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/RegularExpressionMethodPointcutAdvisor.cs @@ -0,0 +1,140 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Convenient class for regular expression method pointcuts that hold an + /// , making them an + /// . + /// + /// + ///

    + /// Configure this class using the and + /// pass-through properties. These are analogous + /// to the and + /// s properties of the + /// class. + ///

    + ///

    + /// Can delegate to any type of regular expression pointcut. Currently only + /// pointcuts based on the regular expression classes from the .NET Base + /// Class Library are supported. The + /// + /// property must be a subclass of the + /// class. + ///

    + ///

    + /// This should not normally be set directly. + ///

    + ///
    + /// Dmitriy Kopylenko + /// Rod Johnson + /// Simon White (.NET) + /// Mark Pollack (.NET) + /// + [Serializable] + public class RegularExpressionMethodPointcutAdvisor : AbstractGenericPointcutAdvisor + { + private AbstractRegularExpressionMethodPointcut pointcut; + + #region Constructors + + /// + /// Creates a new instance of the + /// class. + /// + public RegularExpressionMethodPointcutAdvisor() + { + InitPointcut(); + } + + /// + /// Creates a new instance of the + /// class for the supplied . + /// + /// The target advice. + /// + public RegularExpressionMethodPointcutAdvisor(IAdvice advice) + { + Advice = advice; + InitPointcut(); + } + + #endregion + + #region Properties + + /// + /// A single pattern to be used during method evaluation. + /// + public string Pattern + { + set + { + AbstractRegularExpressionMethodPointcut armp = (AbstractRegularExpressionMethodPointcut) Pointcut; + armp.Pattern = value; + } + } + + /// + /// Multiple patterns to be used during method evaluation. + /// + public string[] Patterns + { + set + { + AbstractRegularExpressionMethodPointcut armp = (AbstractRegularExpressionMethodPointcut) Pointcut; + armp.Patterns = value; + } + } + + /// + /// The that drives this advisor. + /// + public override IPointcut Pointcut + { + get { return pointcut; } + set { + AssertUtils.AssertArgumentType(value, "pointcut", typeof(AbstractRegularExpressionMethodPointcut), + "Pointcut most be compatible with type AbstractRegularExpressionMethodPointuct"); + pointcut = value as AbstractRegularExpressionMethodPointcut; + } + } + + #endregion + + /// + /// Initialises the pointcut. + /// + protected void InitPointcut() + { + pointcut = new SdkRegularExpressionMethodPointcut(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/RootTypeFilter.cs b/src/Spring/Spring.Aop/Aop/Support/RootTypeFilter.cs new file mode 100644 index 00000000..f4ad59da --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/RootTypeFilter.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Simple implementation that matches + /// all classes classes (and any derived subclasses) of a give root + /// . + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: RootTypeFilter.cs,v 1.3 2007/03/16 04:01:25 aseovic Exp $ + [Serializable] + public class RootTypeFilter : ITypeFilter + { + private Type _rootType; + + /// + /// Creates a new instance of the + /// for the supplied + /// . + /// + /// The root . + /// + /// If the supplied is . + /// + public RootTypeFilter(Type rootType) + { + AssertUtils.ArgumentNotNull(rootType, "rootType"); + _rootType = rootType; + } + + /// + /// Should the pointcut apply to the supplied + /// ? + /// + /// + ///

    + /// Returns if the supplied + /// can be assigned to the root . + ///

    + ///
    + /// + /// The candidate . + /// + /// + /// if the advice should apply to the supplied + /// + /// + public virtual bool Matches(Type type) + { + return _rootType.IsAssignableFrom(type); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/SdkRegularExpressionMethodPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/SdkRegularExpressionMethodPointcut.cs new file mode 100644 index 00000000..fb7d5900 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/SdkRegularExpressionMethodPointcut.cs @@ -0,0 +1,207 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; +using Common.Logging; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Regular expression based pointcut object. + /// + /// + ///

    + /// Uses the regular expression classes from the .NET Base Class Library. + ///

    + ///

    + /// The regular expressions must be a match. For example, the + /// .*Get* pattern will match Com.Mycom.Foo.GetBar(), and + /// Get.* will not. + ///

    + ///
    + /// Rod Johnson + /// Simon White (.NET) + [Serializable] + public class SdkRegularExpressionMethodPointcut : AbstractRegularExpressionMethodPointcut + { + private ILog _logger = LogManager.GetLogger(typeof(SdkRegularExpressionMethodPointcut)); + private Regex[] _compiledPatterns = new Regex[0]; + private RegexOptions _defaultOptions = RegexOptions.None; + + #region Constructors + + /// + /// Creates a new instance of the + /// class. + /// + public SdkRegularExpressionMethodPointcut() + { + } + + /// + /// Creates a new instance of the + /// class, + /// using the supplied pattern or . + /// + /// + /// The intial pattern value(s) to be matched against. + /// + public SdkRegularExpressionMethodPointcut(params string[] patterns) + { + Patterns = patterns; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + /// + /// If an error was encountered during the deserialization process. + /// + protected SdkRegularExpressionMethodPointcut(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + #endregion + + #region Properties + + /// + /// Gets or sets default options that should be used by + /// regular expressions that don't have options explicitly set. + /// + /// + /// Default options that should be used by regular expressions + /// that don't have options explicitly set. + /// + public RegexOptions DefaultOptions + { + get { return _defaultOptions; } + set + { + _defaultOptions = value; + InitPatternRepresentation(Patterns); + } + } + + #endregion + + #region Methods + + /// + /// Initializes the regular expression pointcuts. + /// + /// + ///

    + /// Can be invoked multiple times. + ///

    + ///

    + /// This method will be invoked from the + /// property, + /// and also on deserialization. + ///

    + ///
    + /// + /// The patterns to initialize. + /// + /// + /// In the case of an invalid pattern. + /// + /// + /// If the supplied is . + /// + protected override void InitPatternRepresentation(object[] patterns) + { + AssertUtils.ArgumentNotNull(patterns, "patterns"); + + if (patterns.Length > 0) + { + _compiledPatterns = new Regex[patterns.Length]; + for (int i = 0; i < patterns.Length; i++) + { + if (patterns[i] == null) + { + throw new ArgumentNullException( + "Null is not a valid value for an element of the Patterns property."); + } + else if (patterns[i] is Regex) + { + _compiledPatterns[i] = (Regex)patterns[i]; + } + else if (patterns[i] is string) + { + _compiledPatterns[i] = new Regex((string)patterns[i], DefaultOptions); + } + else + { + throw new ArgumentException( + "You can only specify a string value or an instance of a Regex class " + + "as an element of the 'Patterns' property."); + } + } + } + } + + /// + /// Does the pattern at the supplied + /// match this ? + /// + /// The pattern to match + /// The index of pattern. + /// + /// if there is a match. + /// + protected override bool Matches(string pattern, int patternIndex) + { + Match match = _compiledPatterns[patternIndex].Match(pattern); + bool matched = match.Success; + + #region Instrumentation + + if (_logger.IsDebugEnabled) + { + _logger.Debug("Candidate is: '" + pattern + "'; pattern is '" + + _compiledPatterns[patternIndex].ToString() + "'; matched=" + matched); + } + + #endregion + + return matched; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcher.cs b/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcher.cs new file mode 100644 index 00000000..0f930290 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcher.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Convenient abstract superclass for static method matchers that don't care + /// about arguments at runtime. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: StaticMethodMatcher.cs,v 1.6 2006/04/09 07:18:37 markpollack Exp $ + [Serializable] + public abstract class StaticMethodMatcher : IMethodMatcher + { + /// + /// Is this dynamic? + /// + /// + ///

    + /// Always returns . + ///

    + ///
    + /// + /// Always returns . + /// + public bool IsRuntime + { + get { return false; } + } + + /// + /// Is there a runtime (dynamic) match for the supplied + /// ? + /// + /// + ///

    + /// Always throws a . This + /// method should never be called on a static matcher. + ///

    + ///
    + /// The candidate method. + /// + /// The target . + /// + /// The arguments to the method + /// + /// Always throws a . + /// + /// + /// Always. + /// + public bool Matches(MethodInfo method, Type targetType, object[] args) + { + throw new NotSupportedException( + "Illegal IMethodMatcher usage. Cannot call 3-arg Matches method on a static matcher."); + } + + /// + /// Does the supplied satisfy this matcher? + /// + /// + ///

    + /// Must be implemented by a derived class in order to specify matching + /// rules. + ///

    + ///
    + /// The candidate method. + /// + /// The target (may be , + /// in which case the candidate must be taken + /// to be the 's declaring class). + /// + /// + /// if this this method matches statically. + /// + public abstract bool Matches(MethodInfo method, Type targetType); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcherPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcherPointcut.cs new file mode 100644 index 00000000..89c7c2fa --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcherPointcut.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +namespace Spring.Aop.Support +{ + /// + /// Convenient superclass when one wants to force subclasses to + /// implement the interface + /// but subclasses will still want to be pointcuts. + /// + /// + ///

    + /// The + /// property can be overriden to customize filter + /// behavior as well. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// Mark Pollack (.NET) + /// $Id: StaticMethodMatcherPointcut.cs,v 1.8 2007/05/30 22:35:43 markpollack Exp $ + [Serializable] + public abstract class StaticMethodMatcherPointcut : StaticMethodMatcher, IPointcut + { + private ITypeFilter typeFilter = TrueTypeFilter.True; + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an abstract class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + protected StaticMethodMatcherPointcut() + { + } + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public virtual ITypeFilter TypeFilter + { + get { return typeFilter; } + set { typeFilter = value;} + } + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public virtual IMethodMatcher MethodMatcher + { + get { return this; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcherPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcherPointcutAdvisor.cs new file mode 100644 index 00000000..e4f6ccdd --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcherPointcutAdvisor.cs @@ -0,0 +1,134 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using Spring.Core; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Convenient superclass for s that + /// are also static pointcuts. + /// + /// Rod Johnson + /// Aleksandar Seovic (.Net) + /// $Id: StaticMethodMatcherPointcutAdvisor.cs,v 1.6 2006/04/09 07:18:37 markpollack Exp $ + [Serializable] + public abstract class StaticMethodMatcherPointcutAdvisor + : StaticMethodMatcherPointcut, IPointcutAdvisor, IOrdered + { + private int _order = Int32.MaxValue; + private IAdvice _advice; + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an abstract class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + protected StaticMethodMatcherPointcutAdvisor() + { + } + + /// + /// Creates a new instance of the + /// + /// class for the supplied + /// + /// + ///

    + /// This is an abstract class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + /// + /// The advice to use. + /// + public StaticMethodMatcherPointcutAdvisor(IAdvice advice) + { + this._advice = advice; + } + + /// + /// Is this advice associated with a particular instance? + /// + /// + /// if this advice is associated with a + /// particular instance. + /// + /// + /// Always; this property is not yet supported. + /// + public virtual bool IsPerInstance + { + get + { + throw new NotSupportedException( + "The 'IsPerInstance' property of the IAdvisor interface is " + + "not yet supported in Spring.NET."); + } + } + + /// + /// Returns this s order in the + /// interception chain. + /// + /// + /// This s order in the + /// interception chain. + /// + public virtual int Order + { + get { return this._order; } + set { this._order = value; } + } + + /// + /// Return the advice part of this advisor. + /// + /// + /// The advice that should apply if the pointcut matches. + /// + /// + public virtual IAdvice Advice + { + get { return this._advice; } + set { this._advice = value; } + } + + /// + /// The that drives this advisor. + /// + public virtual IPointcut Pointcut + { + get { return this; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/TypeFilters.cs b/src/Spring/Spring.Aop/Aop/Support/TypeFilters.cs new file mode 100644 index 00000000..2ad45c5f --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/TypeFilters.cs @@ -0,0 +1,158 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Defines miscellaneous filter operations. + /// + /// Rod Johnson + /// Aleksandar Seovic (.Net) + /// $Id: TypeFilters.cs,v 1.4 2007/03/16 04:01:25 aseovic Exp $ + public sealed class TypeFilters + { + /// + /// Creates a union of two filters. + /// + /// + ///

    + /// The filter arising from the union will match all of the + /// that either of the two supplied filters + /// would match. + ///

    + ///
    + /// + /// The first filter. + /// + /// + /// The second filter. + /// + /// + /// The union of the supplied filters. + /// + public static ITypeFilter Union(ITypeFilter first, ITypeFilter second) + { + return new UnionTypeFilter(new ITypeFilter[] {first, second}); + } + + /// + /// Creates the intersection of two filters. + /// + /// + ///

    + /// The filter arising from the intersection will match all of the + /// that both of the two supplied filters + /// would match. + ///

    + ///
    + /// + /// The first filter. + /// + /// + /// The second filter. + /// + /// + /// The intersection of the supplied filters. + /// + public static ITypeFilter Intersection(ITypeFilter first, ITypeFilter second) + { + return new IntersectionTypeFilter(new ITypeFilter[] {first, second}); + } + + /// + /// Union class filter implementation. + /// + [Serializable] + private sealed class UnionTypeFilter : ITypeFilter + { + private ITypeFilter[] _filters; + + public UnionTypeFilter(ITypeFilter[] filters) + { + _filters = filters; + } + + public bool Matches(Type type) + { + for (int i = 0; i < _filters.Length; i++) + { + if (_filters[i].Matches(type)) + { + return true; + } + } + return false; + } + } + + /// + /// Intersection implementation. + /// + [Serializable] + private sealed class IntersectionTypeFilter : ITypeFilter + { + private ITypeFilter[] _filters; + + public IntersectionTypeFilter(ITypeFilter[] filters) + { + _filters = filters; + } + + public bool Matches(Type type) + { + for (int i = 0; i < _filters.Length; i++) + { + if (!_filters[i].Matches(type)) + { + return false; + } + } + return true; + } + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly visible constructors. + ///

    + ///
    + private TypeFilters() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/UnionPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/UnionPointcut.cs new file mode 100644 index 00000000..85956bae --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/UnionPointcut.cs @@ -0,0 +1,124 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// A union. + /// + /// + ///

    + /// Such pointcut unions are tricky, because one cannot simply OR + /// the respective s: one has to + /// ascertain that each 's + /// is also satisfied. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: UnionPointcut.cs,v 1.8 2007/03/16 04:01:25 aseovic Exp $ + [Serializable] + internal class UnionPointcut : IPointcut + { + private IPointcut a; + private IPointcut b; + private IMethodMatcher mm; + + /// + /// Creates a new instance of the + /// class. + /// + /// The first pointcut. + /// The second pointcut. + public UnionPointcut(IPointcut firstPointcut, IPointcut secondPointcut) + { + this.a = firstPointcut; + this.b = secondPointcut; + this.mm = new PointcutUnionMethodMatcher(this); + } + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public ITypeFilter TypeFilter + { + get { return TypeFilters.Union(a.TypeFilter, b.TypeFilter); } + + } + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public IMethodMatcher MethodMatcher + { + get { return mm; } + } + + /// + /// Internal method matcher class for union pointcut. + /// + private sealed class PointcutUnionMethodMatcher : IMethodMatcher + { + private UnionPointcut _enclosingInstance; + + public PointcutUnionMethodMatcher(UnionPointcut enclosingInstance) + { + this._enclosingInstance = enclosingInstance; + } + + public bool IsRuntime + { + get + { + return _enclosingInstance.a.MethodMatcher.IsRuntime + || _enclosingInstance.b.MethodMatcher.IsRuntime; + } + } + + public bool Matches(MethodInfo method, Type targetType) + { + return (_enclosingInstance.a.TypeFilter.Matches(targetType) + && _enclosingInstance.a.MethodMatcher.Matches(method, targetType)) + || (_enclosingInstance.b.TypeFilter.Matches(targetType) + && _enclosingInstance.b.MethodMatcher.Matches(method, targetType)); + } + + public bool Matches(MethodInfo method, Type targetType, object[] args) + { + // 2-arg matcher will already have run, so we don't need to do type filtering again... + return _enclosingInstance.a.MethodMatcher.Matches(method, targetType, args) + || _enclosingInstance.b.MethodMatcher.Matches(method, targetType, args); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Target/AbstractPoolingTargetSource.cs b/src/Spring/Spring.Aop/Aop/Target/AbstractPoolingTargetSource.cs new file mode 100644 index 00000000..e6977f7c --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Target/AbstractPoolingTargetSource.cs @@ -0,0 +1,204 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; +using Spring.Aop.Support; +using Spring.Objects; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// Abstract superclass for pooling s. + /// + /// + ///

    + /// Maintains a pool of target instances, acquiring and releasing a target + /// object from the pool for each method invocation. + ///

    + ///

    + /// This class is independent of pooling technology. + ///

    + ///

    + /// Subclasses must implement the + /// and + /// + /// methods to work with their chosen pool. The + /// + /// method inherited from the + /// base class + /// can be used to create objects to put in the pool. Subclasses must also + /// implement some of the monitoring methods from the + /// interface. This class + /// provides the + /// + /// method to return an + /// making these statistics available on proxied objects. + ///

    + ///

    + /// This class implements the interface in + /// order to force subclasses to implement the + /// method to cleanup and close + /// down their pool. + ///

    + ///
    + /// Rod Johnson + /// Federico Spinazzi (.NET) + /// $Id: AbstractPoolingTargetSource.cs,v 1.7 2007/03/16 04:01:26 aseovic Exp $ + [Serializable] + public abstract class AbstractPoolingTargetSource + : AbstractPrototypeTargetSource, PoolingConfig, IDisposable, IAdvice + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + protected AbstractPoolingTargetSource() + { + } + + #endregion + + /// + /// Returns the target object (acquired from the pool). + /// + /// The target object (acquired from the pool). + /// + /// If unable to obtain the target object. + /// + public abstract override object GetTarget(); + + /// + /// Gets the mixin. + /// + /// + /// An exposing statistics + /// about the pool maintained by this object. + /// + public DefaultIntroductionAdvisor GetPoolingConfigMixin() + { + return new DefaultIntroductionAdvisor(this, typeof (PoolingConfig)); + } + + /// + /// The maximum number of object instances in this pool. + /// + public int MaxSize + { + get { return _maxSize; } + set { _maxSize = value; } + } + + /// + /// The number of active object instances in this pool. + /// + public abstract int Active { get; } + + /// + /// The number of free object instances in this pool. + /// + public abstract int Free { get; } + + /// + /// The target factory that will be used to perform the lookup + /// of the object referred to by the + /// + /// property. + /// + /// + /// The owning + /// (will never be ). + /// + /// + /// In case of initialization errors. + /// + /// + public override IObjectFactory ObjectFactory + { + set + { + base.ObjectFactory = value; + try + { + CreatePool(value); + } + catch (ObjectsException) + { + throw; + } + catch (Exception ex) + { + throw new ObjectInitializationException("Could not create instance pool.", ex); + } + } + } + + /// + /// Create the pool. + /// + /// + /// The owning , in + /// case one needs collaborators from it (normally one's own properties + /// are sufficient). + /// + /// + /// In the case of errors encountered during the creation of the pool. + /// + protected abstract void CreatePool(IObjectFactory factory); + + /// + /// Releases the target object (returns it to the pool). + /// + /// + /// The target object to release (return to the pool). + /// + /// + /// In the case that the could not be released. + /// + public abstract override void ReleaseTarget(object target); + + /// + /// Performs application-defined tasks associated with freeing, releasing, or + /// resetting unmanaged resources. + /// + /// + ///

    + /// Disposes of the pool. + ///

    + ///
    + public abstract void Dispose(); + + private int _maxSize; + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Target/AbstractPrototypeTargetSource.cs b/src/Spring/Spring.Aop/Aop/Target/AbstractPrototypeTargetSource.cs new file mode 100644 index 00000000..902ffd7e --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Target/AbstractPrototypeTargetSource.cs @@ -0,0 +1,238 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using Common.Logging; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Support; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// Base class for dynamic + /// implementations that can create new prototype object instances to + /// support a pooling or new-instance-per-invocation strategy. + /// + /// + ///

    + /// All such s must run in an + /// , as they need to + /// call the + /// method to create a new prototype instance. + ///

    + ///
    + /// Rod Johnson + /// Federico Spinazzi (.NET) + /// $Id: AbstractPrototypeTargetSource.cs,v 1.11 2007/07/28 07:32:52 markpollack Exp $ + public abstract class AbstractPrototypeTargetSource + : ITargetSource, IObjectFactoryAware, IInitializingObject + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + protected AbstractPrototypeTargetSource() + { + } + + #endregion + + #region Properties + + /// + /// The name of the target object to be created on each invocation. + /// + /// + ///

    + /// This object should be a prototype, or the same instance will always + /// be obtained from the owning . + ///

    + ///
    + public virtual string TargetObjectName + { + get { return _targetObjectName; } + set { + _targetObjectName = value; + } + } + + /// + /// The of the target object. + /// + public virtual Type TargetType + { + get { return _targetType; } + } + + /// + /// Is the target source static? + /// + /// + /// if the target source is static. + /// + public virtual bool IsStatic + { + get { return false; } + } + + /// + /// The target factory that will be used to perform the lookup + /// of the object referred to by the + /// property. + /// + /// + ///

    + /// Needed so that prototype instances can be created as necessary. + ///

    + ///
    + /// + /// The owning + /// (will never be ). + /// + /// + /// In case of initialization errors. + /// + /// + public virtual IObjectFactory ObjectFactory + { + get { return _owningObjectFactory; } + set + { + _owningObjectFactory = value; + if (!value.IsPrototype(TargetObjectName)) + { + throw new ObjectDefinitionStoreException( + "Cannot use PrototypeTargetSource against a " + + "Singleton object; instances would not be independent."); + } + + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format( + "Getting object with name '{0}' to determine class.", + TargetObjectName)); + } + + #endregion + + _targetType = _owningObjectFactory.GetType(TargetObjectName); + } + } + + #endregion + + #region Methods + + /// + /// Subclasses should use this method to create a new prototype instance. + /// + protected virtual object NewPrototypeInstance() + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format( + "Creating new target from object '{0}'.", + TargetObjectName)); + } + + #endregion + + return ObjectFactory.GetObject(TargetObjectName); + } + + /// + /// Returns the target object. + /// + /// The target object. + /// + /// If unable to obtain the target object. + /// + public abstract object GetTarget(); + + /// + /// Releases the target object. + /// + /// The target object to release. + public virtual void ReleaseTarget(object target) + { + } + + /// + /// Invoked by an + /// after it has set all object properties supplied + /// (and satisfied the + /// + /// and + /// interfaces). + /// + /// + ///

    + /// Ensures that the property has been + /// set to a valid value (i.e. is not or a string + /// that consists solely of whitespace). + ///

    + ///
    + /// + /// In the event of misconfiguration (such as failure to set an essential + /// property) or if initialization fails. + /// + /// + public virtual void AfterPropertiesSet() + { + AssertUtils.ArgumentHasText( + TargetObjectName, "TargetObjectName", + "The 'TargetObjectName' property must have a value."); + } + + #endregion + + #region Fields + + /// + /// The shared instance for this class (and derived classes). + /// + protected readonly ILog logger = LogManager.GetLogger(typeof (AbstractPrototypeTargetSource)); + + private String _targetObjectName; + private IObjectFactory _owningObjectFactory; + private Type _targetType; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Target/EmptyTargetSource.cs b/src/Spring/Spring.Aop/Aop/Target/EmptyTargetSource.cs new file mode 100644 index 00000000..8ffe6569 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Target/EmptyTargetSource.cs @@ -0,0 +1,161 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// The to be used + /// when there is no target object, and behavior is supplied by the + /// advisors. + /// + /// + ///

    + /// This class is exposed as a singleton. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: EmptyTargetSource.cs,v 1.4 2007/10/08 22:04:51 markpollack Exp $ + [Serializable] + public sealed class EmptyTargetSource : ITargetSource, ISerializable + { + /// + /// The to be used + /// when there is no target object, and behavior is supplied by the + /// advisors. + /// + public static readonly ITargetSource Empty = new EmptyTargetSource(); + + private object _dummyTarget = new object(); + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly visible constructors. + ///

    + ///
    + private EmptyTargetSource() + { + } + + /// + /// The of the target object. + /// + public Type TargetType + { + get { return typeof(object); } + } + + /// + /// Is the target source static? + /// + /// + ///

    + /// The + /// instance is static, and this always returns . + ///

    + ///
    + /// + /// if the target source is static. + /// + public bool IsStatic + { + get { return true; } + } + + /// + /// Returns the target object. + /// + /// The target object. + /// + /// If unable to obtain the target object. + /// + public object GetTarget() + { + return _dummyTarget; + } + + /// + /// Releases the target object. + /// + /// + /// + /// This is a no-op operation in this implementation. + /// + /// + /// The target object to release. + public void ReleaseTarget(object target) + { + } + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + return "EmptyTargetSource: no target"; + } + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.SetType(typeof (EmptyTargetSourceObjectReference)); + } + + + + [Serializable] + private sealed class EmptyTargetSourceObjectReference : IObjectReference + { + public object GetRealObject(StreamingContext context) + { + return EmptyTargetSource.Empty; + } + } + } +} diff --git a/src/Spring/Spring.Aop/Aop/Target/HotSwappableTargetSource.cs b/src/Spring/Spring.Aop/Aop/Target/HotSwappableTargetSource.cs new file mode 100644 index 00000000..fe051edf --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Target/HotSwappableTargetSource.cs @@ -0,0 +1,172 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// implementation that caches a + /// local target object, but allows the target to be swapped while the + /// application is running + /// + /// + ///

    + /// If configuring an object of this class in a Spring IoC container, + /// use constructor injection to supply the intial target. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.Net) + /// $Id: HotSwappableTargetSource.cs,v 1.6 2007/03/16 04:01:26 aseovic Exp $ + [Serializable] + public class HotSwappableTargetSource : ITargetSource + { + private object _target; + + /// + /// Creates a new instance of the + /// with the initial target. + /// + /// + /// The initial target. May be . + /// + public HotSwappableTargetSource(object initialTarget) + { + _target = initialTarget; + } + + /// + /// The of the target object. + /// + /// + ///

    + /// Can return . + ///

    + ///
    + public virtual Type TargetType + { + get { return _target.GetType(); } + } + + /// + /// Is the target source static? + /// + /// + /// if the target source is static. + /// + public virtual bool IsStatic + { + get { return false; } + } + + /// + /// Returns the target object. + /// + /// The target object. + /// + /// If unable to obtain the target object. + /// + public object GetTarget() + { + // synchronization around something that takes so little time is fine... + lock (this) + { + return _target; + } + } + + /// + /// Releases the target object. + /// + /// + ///

    + /// No-op implementation. + ///

    + ///
    + /// The target object to release. + public virtual void ReleaseTarget(Object target) + { + } + + /// + /// Swap the target, returning the old target. + /// + /// The new target. + /// The old target. + /// + /// If the new target is . + /// + public virtual object Swap(object newTarget) + { + AssertUtils.ArgumentNotNull(newTarget, "newTarget", "Cannot swap to null."); + lock (this) + { + // TODO: type checks + object old = _target; + _target = newTarget; + return old; + } + } + + /// + /// Determines whether the specified + /// is equal to the current . + /// + /// + ///

    + /// Two invoker interceptors are equal if they have the same target or + /// if the targets are equal. + ///

    + ///
    + /// The target source to compare with. + /// + /// if this instance is equal to the + /// specified . + /// + public override bool Equals(object other) + { + HotSwappableTargetSource otherTargetSource = other as HotSwappableTargetSource; + if (other == null) + { + return false; + } + return otherTargetSource._target == _target || otherTargetSource._target.Equals(_target); + } + + /// + /// Serves as a hash function for a particular type, suitable for use + /// in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return base.GetHashCode() + 13 * + (_target == null ? 0 : _target.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Target/IThreadLocalTargetSourceStats.cs b/src/Spring/Spring.Aop/Aop/Target/IThreadLocalTargetSourceStats.cs new file mode 100644 index 00000000..16af7515 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Target/IThreadLocalTargetSourceStats.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// Statistics for a thread local . + /// + /// Rod Johnson + /// Federico Spinazzi (.NET) + /// $Id: IThreadLocalTargetSourceStats.cs,v 1.3 2006/04/09 07:18:37 markpollack Exp $ + public interface IThreadLocalTargetSourceStats + { + /// + /// Gets the number of invocations of the + /// and + /// methods. + /// + /// + /// The number of invocations of the + /// and + /// methods. + /// + int Invocations { get; } + + /// + /// Gets the number of hits that were satisfied by a thread bound object. + /// + /// + /// The number of hits that were satisfied by a thread bound object. + /// + int Hits { get; } + + /// + /// Gets the number of thread bound objects created. + /// + /// The number of thread bound objects created. + int Objects { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Target/PoolingConfig.cs b/src/Spring/Spring.Aop/Aop/Target/PoolingConfig.cs new file mode 100644 index 00000000..dabed2e1 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Target/PoolingConfig.cs @@ -0,0 +1,52 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// Configuration interface for a pooling invoker. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: PoolingConfig.cs,v 1.4 2006/04/09 07:18:37 markpollack Exp $ + public interface PoolingConfig + { + /// + /// The number of active object instances in a pool. + /// + int Active { get; } + + /// + /// The number of free object instances in a pool. + /// + int Free { get; } + + /// + /// The maximum number of object instances in a pool. + /// + int MaxSize { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Target/PrototypeTargetSource.cs b/src/Spring/Spring.Aop/Aop/Target/PrototypeTargetSource.cs new file mode 100644 index 00000000..728b5455 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Target/PrototypeTargetSource.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// implementation that creates a + /// new instance of the target object for each request. + /// + /// + ///

    + /// Can only be used in an object factory. + ///

    + ///
    + /// Rod Johnson + /// Spinazzi Federico (.NET) + /// $Id: PrototypeTargetSource.cs,v 1.3 2006/04/09 07:18:37 markpollack Exp $ + /// + public sealed class PrototypeTargetSource : AbstractPrototypeTargetSource + { + /// + /// Returns the target object. + /// + /// The target object. + /// + /// If unable to obtain the target object. + /// + public override Object GetTarget() + { + return NewPrototypeInstance(); + } + + /// + /// Releases the target object. + /// + /// + ///

    + /// No-op implementation. + ///

    + ///
    + /// The target object to release. + public override void ReleaseTarget(object target) + { + } + + /// + /// Is the target source static? + /// + /// + /// because this target source is never static. + /// + public override bool IsStatic + { + get { return false; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Target/SimplePoolTargetSource.cs b/src/Spring/Spring.Aop/Aop/Target/SimplePoolTargetSource.cs new file mode 100644 index 00000000..977167b0 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Target/SimplePoolTargetSource.cs @@ -0,0 +1,209 @@ +#region License + +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +#region Imports + +using System; +using Spring.Objects.Factory; +using Spring.Pool; +using Spring.Pool.Support; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// Pooling target source implementation based on the + /// + /// + /// Rod Johnson + /// Federico Spinazzi + /// $Id: SimplePoolTargetSource.cs,v 1.9 2007/03/16 04:01:26 aseovic Exp $ + /// + [Serializable] + public class SimplePoolTargetSource : AbstractPoolingTargetSource, IPoolableObjectFactory + { + private IObjectPool objectPool; + + /// + /// Returns the target object (acquired from the pool). + /// + /// + /// The target object (acquired from the pool). + /// + /// + /// If unable to obtain the target object. + /// + public override object GetTarget() + { + return this.objectPool.BorrowObject(); + } + + /// + /// Creates the pool. + /// + /// + /// The owning , in + /// case one needs collaborators from it (normally one's own properties + /// are sufficient). + /// + /// + protected override void CreatePool(IObjectFactory factory) + { + #region Instrumentation + + if(logger.IsDebugEnabled) + { + logger.Debug("Creating object pool."); + } + + #endregion + + this.objectPool = CreateObjectPool(); + } + + /// + /// Creates a new instance of an appropriate + /// implementation. + /// + /// + ///

    + /// Subclasses can, of course, override this method if they want to + /// return a different implementation. + ///

    + ///
    + /// + /// An empty . + /// + protected virtual IObjectPool CreateObjectPool() + { + return new SimplePool(this, MaxSize); + } + + /// + /// Releases the target object (returns it to the pool). + /// + /// The target object to release (return to the pool). + /// + /// In the case that the could not be released. + /// + public override void ReleaseTarget(object target) + { + this.objectPool.ReturnObject(target); + } + + /// + /// The number of active object instances in this pool. + /// + public override int Active + { + get { return this.objectPool.NumActive; } + } + + /// + /// The number of free object instances in this pool. + /// + public override int Free + { + get { return this.objectPool.NumIdle; } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or + /// resetting unmanaged resources. + /// + /// + ///

    + /// Disposes of the pool. + ///

    + ///
    + public override void Dispose() + { + #region Instrumentation + + if(logger.IsDebugEnabled) + { + logger.Debug("Closing pool..."); + } + + #endregion + + this.objectPool.Close(); + } + + /// + /// Creates an instance that can be returned by the pool. + /// + /// + /// An instance that can be returned by the pool. + /// + /// + public virtual Object MakeObject() + { + return NewPrototypeInstance(); + } + + /// + /// Destroys an instance no longer needed by the pool. + /// + /// The instance to be destroyed. + /// + public virtual void DestroyObject(object obj) + { + if (obj is IDisposable) + { + ((IDisposable) obj).Dispose(); + } + } + + /// + /// Ensures that the instance is safe to be returned by the pool. + /// Returns false if this object should be destroyed. + /// + /// The instance to validate. + /// + /// if this object is not valid and + /// should be dropped from the pool, otherwise . + /// + /// + public virtual bool ValidateObject(object obj) + { + return true; + } + + /// + /// Reinitialize an instance to be returned by the pool. + /// + /// The instance to be activated. + /// + public virtual void ActivateObject(object obj) + { + } + + /// + /// Passivates the object. + /// + /// The instance returned to the pool. + /// + public virtual void PassivateObject(object obj) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Target/SingletonTargetSource.cs b/src/Spring/Spring.Aop/Aop/Target/SingletonTargetSource.cs new file mode 100644 index 00000000..beb57231 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Target/SingletonTargetSource.cs @@ -0,0 +1,162 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// implementation that holds a local + /// object. + /// + /// + ///

    + /// This is the default implementation of the + /// interface used by the AOP + /// framework. There should be no need to create objects of this class in + /// application code. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: SingletonTargetSource.cs,v 1.8 2008/03/21 14:11:57 markpollack Exp $ + [Serializable] + public sealed class SingletonTargetSource : ITargetSource + { + private object target; + + /// + /// Creates a new instance of the + /// + /// for the specified target object. + /// + /// The target object to expose. + /// + /// If the supplied is + /// . + /// + public SingletonTargetSource(object target) + { + AssertUtils.ArgumentNotNull(target, "target"); + this.target = target; + } + + #region ITarget Source impl + + /// + /// The of the target object. + /// + public Type TargetType + { + get { return target.GetType(); } + } + + /// + /// Is the target source static? + /// + /// + /// because this target source is always static. + /// + public bool IsStatic + { + get { return true; } + } + + /// + /// Returns the target object. + /// + /// The target object. + /// + /// If unable to obtain the target object. + /// + public object GetTarget() + { + return target; + } + + /// + /// Releases the target object. + /// + /// + ///

    + /// No-op implementation. + ///

    + ///
    + /// The target object to release. + public void ReleaseTarget(object target) + { + + } + + #endregion + + /// + /// Returns a stringified representation of this target source. + /// + /// + /// A stringified representation of this target source. + /// + public override string ToString() + { + return "Singleton target source (not dynamic): target=[" + target + "]"; + } + + /// + /// Determines whether the specified + /// is equal to the current . + /// + /// The target source to compare with. + /// + /// if this instance is equal to the + /// specified . + /// + public override bool Equals(object other) + { + if (this == other) + { + return true; + } + SingletonTargetSource b = other as SingletonTargetSource; + if (b == null) + { + return false; + } + return target.Equals(b.target); + } + + /// + /// Serves as a hash function for a particular type, suitable for use + /// in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return base.GetHashCode() + 13 * + (target == null ? 0 : target.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Target/ThreadLocalTargetSource.cs b/src/Spring/Spring.Aop/Aop/Target/ThreadLocalTargetSource.cs new file mode 100644 index 00000000..91c8a446 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Target/ThreadLocalTargetSource.cs @@ -0,0 +1,244 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Threading; +using AopAlliance.Intercept; +using Spring.Aop.Support; +using Spring.Collections; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// implementation that uses a + /// threading model in which every thread has its own copy of the target. + /// + /// + ///

    + /// Alternative to an object pool. + ///

    + ///

    + /// Application code is written as to a normal pool; callers can't assume + /// they will be dealing with the same instance in invocations in different + /// threads. However, state can be relied on during the operations of a + /// single thread: for example, if one caller makes repeated calls on the + /// AOP proxy. + ///

    + ///

    + /// This class act both as an introduction and as an interceptor, so it + /// should be added twice, once as an introduction and once as an + /// interceptor. + ///

    + ///
    + /// Rod Johnson + /// Federico Spinazzi (.NET) + /// $Id: ThreadLocalTargetSource.cs,v 1.8 2006/04/09 07:18:37 markpollack Exp $ + public sealed class ThreadLocalTargetSource : AbstractPrototypeTargetSource, + IThreadLocalTargetSourceStats, IDisposable, IMethodInterceptor + { + #region Fields + + /// + /// ThreadLocal holding the target associated with the current thread. + /// + /// + ///

    + /// Unlike most thread local storage which is static, this variable is + /// meant to be per thread per instance of this class. + ///

    + ///
    + private LocalDataStoreSlot _targetInThread = Thread.AllocateDataSlot(); + + /// + /// The set of managed targets, enabling us to keep track of the + /// targets we've created. + /// + private ISet _targetSet = new ListSet(); + + private int _invocations; + private int _hits; + + #endregion + + #region Properties + + /// + /// Gets the number of invocations of the and + /// methods. + /// + /// + /// The number of invocations of the and + /// methods. + /// + public int Invocations + { + get { return _invocations; } + } + + /// + /// Gets the number of hits that were satisfied by a thread bound object. + /// + /// + /// The number of hits that were satisfied by a thread bound object. + /// + public int Hits + { + get { return _hits; } + } + + /// + /// Gets the number of thread bound objects created. + /// + /// The number of thread bound objects created. + public int Objects + { + get { return _targetSet.Count; } + } + + private object ThreadBoundTarget + { + get { return Thread.GetData(_targetInThread); } + } + + #endregion + + #region Methods + + /// + /// Returns the target object. + /// + /// + ///

    + /// Tries to locate the target from thread local storage. If no target + /// is found, a target will be obtained and bound to the thread. + ///

    + ///
    + /// The target object. + /// + /// If unable to obtain the target object. + /// + /// + public override Object GetTarget() + { + ++_invocations; + object target = ThreadBoundTarget; + if (target == null) + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format( + "No target for apartment prototype '{0}' " + + "found in thread: creating one and binding it to thread '#{1}'", + TargetObjectName, Thread.CurrentThread.GetHashCode())); + } + + #endregion + + target = NewPrototypeInstance(); + Thread.SetData(_targetInThread, target); + _targetSet.Add(target); + } + else + { + ++_hits; + } + return ThreadBoundTarget; + } + + /// + /// Return an introduction advisor mixin that allows the AOP proxy to be + /// cast to an reference. + /// + public IIntroductionAdvisor GetStatsMixin() + { + return new DefaultIntroductionAdvisor(this, typeof (IThreadLocalTargetSourceStats)); + } + + /// + /// Cleans up this instance's thread storage, and disposes of any + /// targets as necessary. + /// + /// + public void Dispose() + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug("Destroying ThreadLocal bindings"); + } + + #endregion + + foreach (object target in _targetSet) + { + if (target is IDisposable) + { + try + { + ((IDisposable) target).Dispose(); + } + catch (Exception ex) + { + #region Instrumentation + + if (logger.IsWarnEnabled) + { + logger.Warn(string.Format( + "Thread-bound target of class '{0}' " + + "threw exception from it's IDisposable.Dispose() method.", + target.GetType()), ex); + } + + #endregion + } + } + } + _targetSet.Clear(); + _targetInThread = null; + } + + /// + /// Increments Invocations and Hits statistics + /// + /// The method invocation joinpoint + /// The result of the call to IJoinpoint.Proceed(), might be intercepted by the interceptor. + /// if the interceptors or the target-object throws an exception. + public object Invoke(IMethodInvocation invocation) + { + if (ReflectionUtils.MethodIsOnOneOfTheseInterfaces( + invocation.Method, new Type[] {typeof (IThreadLocalTargetSourceStats)})) + { + return invocation.Method.Invoke(this, invocation.Arguments); + } + return invocation.Method.Invoke(GetTarget(), invocation.Arguments); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/TrueMethodMatcher.cs b/src/Spring/Spring.Aop/Aop/TrueMethodMatcher.cs new file mode 100644 index 00000000..12c43729 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/TrueMethodMatcher.cs @@ -0,0 +1,155 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Permissions; + +#endregion + +namespace Spring.Aop +{ + /// + /// Canonical that matches + /// all methods. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: TrueMethodMatcher.cs,v 1.5 2006/04/09 07:18:36 markpollack Exp $ + [Serializable] + public sealed class TrueMethodMatcher : IMethodMatcher, ISerializable + { + /// + /// Canonical instance that matches all methods. + /// + /// + ///

    + /// It is not dynamic. + ///

    + ///
    + public static readonly IMethodMatcher True = new TrueMethodMatcher(); + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly visible + /// constructors. + ///

    + ///
    + private TrueMethodMatcher() + { + } + + /// + /// Is this dynamic? + /// + /// + /// if this + /// is dynamic. + /// + /// + public bool IsRuntime + { + get { return false; } + } + + /// + /// Does the supplied satisfy this matcher? + /// Perform static checking. If this returns false, or if the isRuntime() method + /// returns false, no runtime check will be made. + /// + /// The candidate method. + /// + /// The target class (may be , in which case the + /// candidate class must be taken to be the 's + /// declaring class). + /// + /// + /// if this this method matches statically. + /// + /// + public bool Matches(MethodInfo method, Type targetType) + { + return true; + } + + /// + /// Is there a runtime (dynamic) match for the supplied + /// ? + /// + /// The candidate method. + /// The target class. + /// The arguments to the method + /// + /// if there is a runtime match. + /// + public bool Matches(MethodInfo method, Type targetType, object[] args) + { + // should never be invoked if IsRuntime is false + return true; + } + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + return "TrueMethodMatcher.True"; + } + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission (SecurityAction.Demand,SerializationFormatter=true)] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.SetType(typeof (TrueMethodMatcherObjectReference)); + } + + [Serializable] + private sealed class TrueMethodMatcherObjectReference : IObjectReference + { + public object GetRealObject(StreamingContext context) + { + return TrueMethodMatcher.True; + } + } + } +} diff --git a/src/Spring/Spring.Aop/Aop/TruePointcut.cs b/src/Spring/Spring.Aop/Aop/TruePointcut.cs new file mode 100644 index 00000000..0b467f3f --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/TruePointcut.cs @@ -0,0 +1,121 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; + +#endregion + +namespace Spring.Aop +{ + /// + /// Canonical instance that matches + /// everything. + /// + /// Rod Johnson + /// Aleksandar Seovic (.NET) + [Serializable] + public sealed class TruePointcut : IPointcut, ISerializable + { + /// + /// Canonical instance that matches everything. + /// + public static readonly IPointcut True = new TruePointcut(); + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + private TruePointcut() + { + } + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public ITypeFilter TypeFilter + { + get { return Aop.TrueTypeFilter.True; } + } + + /// + /// The for this pointcut. + /// + /// + /// The current . + /// + public IMethodMatcher MethodMatcher + { + get { return TrueMethodMatcher.True; } + } + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + return "TruePointcut.TRUE"; + } + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission (SecurityAction.Demand,SerializationFormatter=true)] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.SetType(typeof (TruePointcutObjectReference)); + } + + [Serializable] + private sealed class TruePointcutObjectReference : IObjectReference + { + public object GetRealObject(StreamingContext context) + { + return TruePointcut.True; + } + } + } +} diff --git a/src/Spring/Spring.Aop/Aop/TrueTypeFilter.cs b/src/Spring/Spring.Aop/Aop/TrueTypeFilter.cs new file mode 100644 index 00000000..f3f78242 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/TrueTypeFilter.cs @@ -0,0 +1,124 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; + +#endregion + +namespace Spring.Aop +{ + /// + /// Canonical instances. + /// + /// + ///

    + /// Only one canonical instance is + /// provided out of the box. The + /// matches all classes. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: TrueTypeFilter.cs,v 1.4 2006/04/09 07:18:36 markpollack Exp $ + [Serializable] + public sealed class TrueTypeFilter : ITypeFilter, ISerializable + { + /// + /// Canonical instance that + /// matches all classes. + /// + public static readonly ITypeFilter True = new TrueTypeFilter(); + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly visible + /// constructors. + ///

    + ///
    + private TrueTypeFilter() + { + } + + /// + /// Should the pointcut apply to the supplied + /// ? + /// + /// + /// The candidate . + /// + /// + /// if the advice should apply to the supplied + /// + /// + /// + public bool Matches(Type type) + { + return true; + } + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + return "TrueTypeFilter.True"; + } + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.SetType(typeof (TrueTypeFilterObjectReference)); + } + + [Serializable] + private sealed class TrueTypeFilterObjectReference : IObjectReference + { + public object GetRealObject(StreamingContext context) + { + return TrueTypeFilter.True; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/AopAlliance/Aop/AspectException.cs b/src/Spring/Spring.Aop/AopAlliance/Aop/AspectException.cs new file mode 100644 index 00000000..90fa9526 --- /dev/null +++ b/src/Spring/Spring.Aop/AopAlliance/Aop/AspectException.cs @@ -0,0 +1,80 @@ +#region License + +/* + * All the source code provided by AOP Alliance is Public Domain. + * + * http://aopalliance.sourceforge.net/ + * + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace AopAlliance.Aop +{ + /// + /// Superclass for all AOP infrastructure exceptions. + /// + /// Aleksandar Seovic + /// $Id: AspectException.cs,v 1.3 2006/04/09 07:18:33 markpollack Exp $ + [Serializable] + public class AspectException : Exception + { + /// + /// Creates a new instance of the + /// class. + /// + public AspectException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public AspectException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public AspectException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected AspectException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/AopAlliance/Aop/IAdvice.cs b/src/Spring/Spring.Aop/AopAlliance/Aop/IAdvice.cs new file mode 100644 index 00000000..5b4436d4 --- /dev/null +++ b/src/Spring/Spring.Aop/AopAlliance/Aop/IAdvice.cs @@ -0,0 +1,31 @@ +#region License + +/* + * All the source code provided by AOP Alliance is Public Domain. + * + * http://aopalliance.sourceforge.net/ + * + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace AopAlliance.Aop +{ + /// + /// Tag interface for advice. + /// + /// + ///

    + /// Implementations can be any type of advice, such as interceptors. + ///

    + ///
    + public interface IAdvice + { + } +} diff --git a/src/Spring/Spring.Aop/AopAlliance/Intercept/IConstructorInterceptor.cs b/src/Spring/Spring.Aop/AopAlliance/Intercept/IConstructorInterceptor.cs new file mode 100644 index 00000000..46d2b2df --- /dev/null +++ b/src/Spring/Spring.Aop/AopAlliance/Intercept/IConstructorInterceptor.cs @@ -0,0 +1,54 @@ +#region License + +/* + * All the source code provided by AOP Alliance is Public Domain. + * + * http://aopalliance.sourceforge.net/ + * + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace AopAlliance.Intercept +{ + /// + /// Intercepts the construction of a new object. + /// + /// + ///

    + /// Such interceptions are nested "on top" of the target. + ///

    + ///
    + public interface IConstructorInterceptor : IInterceptor + { + /// + /// Implement this method to perform extra treatments before and after + /// the consruction of a new object. + /// + /// + ///

    + /// Polite implementations would certainly like to invoke + /// . + ///

    + ///
    + /// + /// The constructor invocation that is being intercepted. + /// + /// + /// The newly created object, which is also the result of the call to + /// , and might be + /// replaced by the interceptor. + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + object Construct(IConstructorInvocation invocation); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/AopAlliance/Intercept/IConstructorInvocation.cs b/src/Spring/Spring.Aop/AopAlliance/Intercept/IConstructorInvocation.cs new file mode 100644 index 00000000..1da95926 --- /dev/null +++ b/src/Spring/Spring.Aop/AopAlliance/Intercept/IConstructorInvocation.cs @@ -0,0 +1,51 @@ +#region License + +/* + * All the source code provided by AOP Alliance is Public Domain. + * + * http://aopalliance.sourceforge.net/ + * + */ + +#endregion + +#region Imports + +using System.Reflection; + +#endregion + +namespace AopAlliance.Intercept +{ + /// + /// A description of an invocation to a constuctor, given to an interceptor + /// upon constructor-call. + /// + /// + ///

    + /// A constructor invocation is a joinpoint and can be intercepted by a + /// constructor interceptor. + ///

    + ///
    + /// + public interface IConstructorInvocation : IInvocation + { + /// + /// Gets the constructor invocation that is to be invoked. + /// + /// + ///

    + /// This property is a friendly implementation of the + /// property. + /// It should be used in preference to the + /// property + /// because it provides immediate access to the underlying constructor + /// without the need to resort to a cast. + ///

    + ///
    + /// + /// The constructor invocation that is to be invoked. + /// + ConstructorInfo Constructor { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/AopAlliance/Intercept/IInterceptor.cs b/src/Spring/Spring.Aop/AopAlliance/Intercept/IInterceptor.cs new file mode 100644 index 00000000..121c2ef4 --- /dev/null +++ b/src/Spring/Spring.Aop/AopAlliance/Intercept/IInterceptor.cs @@ -0,0 +1,39 @@ +#region License + +/* + * All the source code provided by AOP Alliance is Public Domain. + * + * http://aopalliance.sourceforge.net/ + * + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Aop; + +#endregion + +namespace AopAlliance.Intercept +{ + /// + /// Represents a generic interceptor. + /// + /// + ///

    + /// A generic interceptor can intercept runtime events that occur within a + /// base program. Those events are materialized by (reified in) joinpoints. + /// Runtime joinpoints can be invocations, field access, exceptions, etc. + ///

    + ///

    + /// This interface is not used directly. Use the various derived interfaces + /// to intercept specific events. + ///

    + ///
    + /// + public interface IInterceptor : IAdvice + { + } +} diff --git a/src/Spring/Spring.Aop/AopAlliance/Intercept/IInvocation.cs b/src/Spring/Spring.Aop/AopAlliance/Intercept/IInvocation.cs new file mode 100644 index 00000000..af4d0e8f --- /dev/null +++ b/src/Spring/Spring.Aop/AopAlliance/Intercept/IInvocation.cs @@ -0,0 +1,45 @@ +#region License + +/* + * All the source code provided by AOP Alliance is Public Domain. + * + * http://aopalliance.sourceforge.net/ + * + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace AopAlliance.Intercept +{ + /// + /// Represents an invocation in the program. + /// + /// + ///

    + /// An invocation is a joinpoint and can be intercepted by an interceptor. + /// Typical examples would be a constructor invocation and a method call. + ///

    + ///
    + public interface IInvocation : IJoinpoint + { + /// + /// Gets the arguments to an invocation. + /// + /// + ///

    + /// It is of course possible to change element values within this array + /// to change the arguments to an intercepted invocation. + ///

    + ///
    + /// + /// The arguments to an invocation. + /// + object[] Arguments { get; } + } +} diff --git a/src/Spring/Spring.Aop/AopAlliance/Intercept/IJoinpoint.cs b/src/Spring/Spring.Aop/AopAlliance/Intercept/IJoinpoint.cs new file mode 100644 index 00000000..971fa7e9 --- /dev/null +++ b/src/Spring/Spring.Aop/AopAlliance/Intercept/IJoinpoint.cs @@ -0,0 +1,88 @@ +#region License + +/* + * All the source code provided by AOP Alliance is Public Domain. + * + * http://aopalliance.sourceforge.net/ + * + */ + +#endregion + +#region Imports + +using System.Reflection; + +#endregion + +namespace AopAlliance.Intercept +{ + /// + /// Represents a generic runtime joinpoint (in the AOP terminology). + /// + /// + ///

    + /// A runtime joinpoint is an event that occurs on a static + /// joinpoint (i.e. a location in a program). For instance, an + /// invocation is the runtime joinpoint on a method (static joinpoint). + /// The static part of a given joinpoint can be generically retrieved + /// using the + /// property. + ///

    + ///

    + /// In the context of an interception framework, a runtime joinpoint + /// is then the reification of an access to an accessible object (a + /// method, a constructor, a field), i.e. the static part of the + /// joinpoint. It is passed to the interceptors that are installed on + /// the static joinpoint. + ///

    + ///
    + /// + public interface IJoinpoint + { + /// + /// Gets the static part of this joinpoint. + /// + /// + ///

    + /// The static part is an accessible object on which a chain of + /// interceptors are installed. + ///

    + ///
    + /// + /// The static part of this joinpoint. + /// + MemberInfo StaticPart { get; } + + /// + /// Gets the object that holds the current joinpoint's static part. + /// + /// + ///

    + /// For instance, the target object for a method invocation. + ///

    + ///
    + /// + /// The object that holds the current joinpoint's static part. + /// + object This { get; } + + /// + /// Proceeds to the next interceptor in the chain. + /// + /// + ///

    + /// The implementation and semantics of this method depend on the + /// actual joinpoint type. Consult the derived interfaces of this + /// interface for specifics. + ///

    + ///
    + /// + /// Consult the derived interfaces of this interface for specifics. + /// + /// + /// If any of the interceptors at the joinpoint throws an exception. + /// + object Proceed(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/AopAlliance/Intercept/IMethodInterceptor.cs b/src/Spring/Spring.Aop/AopAlliance/Intercept/IMethodInterceptor.cs new file mode 100644 index 00000000..79bc1c16 --- /dev/null +++ b/src/Spring/Spring.Aop/AopAlliance/Intercept/IMethodInterceptor.cs @@ -0,0 +1,55 @@ +#region License + +/* + * All the source code provided by AOP Alliance is Public Domain. + * + * http://aopalliance.sourceforge.net/ + * + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace AopAlliance.Intercept +{ + /// + /// Intercepts calls on an interface on its way to the target. + /// + /// + ///

    + /// Such interceptions are nested "on top" of the target. + ///

    + ///
    + public interface IMethodInterceptor : IInterceptor + { + /// + /// Implement this method to perform extra treatments before and after + /// the call to the supplied . + /// + /// + ///

    + /// Polite implementations would certainly like to invoke + /// . + ///

    + ///
    + /// + /// The method invocation that is being intercepted. + /// + /// + /// The result of the call to the + /// method of + /// the supplied ; this return value may + /// well have been intercepted by the interceptor. + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + object Invoke(IMethodInvocation invocation); + } +} diff --git a/src/Spring/Spring.Aop/AopAlliance/Intercept/IMethodInvocation.cs b/src/Spring/Spring.Aop/AopAlliance/Intercept/IMethodInvocation.cs new file mode 100644 index 00000000..e4c27839 --- /dev/null +++ b/src/Spring/Spring.Aop/AopAlliance/Intercept/IMethodInvocation.cs @@ -0,0 +1,77 @@ +#region License + +/* + * All the source code provided by AOP Alliance is Public Domain. + * + * http://aopalliance.sourceforge.net/ + * + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace AopAlliance.Intercept +{ + /// + /// Description of an invocation to a method, given to an interceptor + /// upon method-call. + /// + /// + ///

    + /// A method invocation is a joinpoint and can be intercepted by a method + /// interceptor. + ///

    + ///
    + /// + public interface IMethodInvocation : IInvocation + { + /// + /// Gets the method invocation that is to be invoked. + /// + /// + ///

    + /// This property is a friendly implementation of the + /// property. + /// It should be used in preference to the + /// property + /// because it provides immediate access to the underlying method + /// without the need to resort to a cast. + ///

    + ///
    + /// + /// The method invocation that is to be invoked. + /// + MethodInfo Method { get; } + + /// + /// Gets the proxy object for the invocation. + /// + /// + /// The proxy object for this method invocation. + /// + object Proxy { get; } + + /// + /// Gets the target object for the invocation. + /// + /// + /// The target object for this method invocation. + /// + object Target { get; } + + /// + /// Gets the type of the target object. + /// + /// + /// The type of the target object. + /// + Type TargetType { get; } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/AbstractExceptionHandler.cs b/src/Spring/Spring.Aop/Aspects/AbstractExceptionHandler.cs new file mode 100644 index 00000000..9304354e --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/AbstractExceptionHandler.cs @@ -0,0 +1,184 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using Common.Logging; +using Spring.Expressions; + +namespace Spring.Aspects +{ + /// + /// An abstract base class providing all necessary functionality for typical IExceptionHandler implementations. + /// + /// Mark Pollack + /// $Id: AbstractExceptionHandler.cs,v 1.2 2007/10/10 18:07:46 markpollack Exp $ + public abstract class AbstractExceptionHandler : IExceptionHandler + { + #region Fields + + /// + /// The logging instance + /// + protected readonly ILog log; + + private IList sourceExceptionNames = new ArrayList(); + private IList sourceExceptionTypes = new ArrayList(); + private string actionExpressionText; + private bool continueProcessing = false; + private string constraintExpressionText; + + #endregion + + #region Constructor(s) + + /// + /// Initializes a new instance of the class. + /// + public AbstractExceptionHandler() + { + log = LogManager.GetLogger(GetType()); + } + + /// + /// Initializes a new instance of the class. + /// + /// The exception names. + public AbstractExceptionHandler(string[] exceptionNames) + { + log = LogManager.GetLogger(GetType()); + foreach (string exceptionName in exceptionNames) + { + SourceExceptionNames.Add(exceptionName); + } + } + + #endregion + + #region Implementation of IExceptionHandler + + #region Properties + + /// + /// Gets the source exception names. + /// + /// The source exception names. + public IList SourceExceptionNames + { + get { return sourceExceptionNames; } + set { sourceExceptionNames = value; } + } + + /// + /// Gets the source exception types. + /// + /// The source exception types. + public IList SourceExceptionTypes + { + get { return sourceExceptionTypes; } + set { sourceExceptionTypes = value; } + } + + /// + /// Gets the action translation expression text + /// + /// The action translation expression. + public string ActionExpressionText + { + get { return actionExpressionText; } + set { actionExpressionText = value; } + } + + + /// + /// Gets or sets the constraint expression text. + /// + /// The constraint expression text. + public string ConstraintExpressionText + { + get { return constraintExpressionText; } + set { constraintExpressionText = value; } + } + + /// + /// Gets a value indicating whether to continue processing. + /// + /// true if continue processing; otherwise, false. + public bool ContinueProcessing + { + get { return continueProcessing; } + set { continueProcessing = value; } + } + + #endregion + + + + /// + /// Determines whether this instance can handle the exception the specified exception. + /// + /// The exception. + /// The call context dictionary. + /// + /// true if this instance can handle the specified exception; otherwise, false. + /// + public bool CanHandleException(Exception ex, IDictionary callContextDictionary) + { + if (SourceExceptionNames != null) + { + foreach (string exceptionName in SourceExceptionNames) + { + if (ex.GetType().Name.IndexOf(exceptionName) >= 0) + { + return true; + } + } + } + if (ConstraintExpressionText != null) + { + bool canProcess; + try + { + IExpression expression = Expression.Parse(ConstraintExpressionText); + canProcess = (bool) expression.GetValue(null, callContextDictionary); + } catch (InvalidCastException e) + { + log.Warn("Was not able to unbox constraint expression to boolean [" + ConstraintExpressionText + "]", e); + return false; + } catch (Exception e) + { + log.Warn("Was not able to evaluate constraint expression [" + ConstraintExpressionText + "]",e); + return false; + } + return canProcess; + } + + return false; + } + + /// + /// Handles the exception. + /// + /// The return value from handling the exception, if not rethrown or a new exception is thrown. + public abstract object HandleException(IDictionary callContextDictionary); + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/AbstractExceptionHandlerAdvice.cs b/src/Spring/Spring.Aop/Aspects/AbstractExceptionHandlerAdvice.cs new file mode 100644 index 00000000..b84f1962 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/AbstractExceptionHandlerAdvice.cs @@ -0,0 +1,157 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Text.RegularExpressions; +using AopAlliance.Intercept; +using Spring.Objects.Factory; +using Spring.Util; + +namespace Spring.Aspects +{ + /// + /// This is + /// + /// + /// + /// + /// Mark Pollack + /// $Id: AbstractExceptionHandlerAdvice.cs,v 1.1 2007/10/08 22:05:16 markpollack Exp $ + public abstract class AbstractExceptionHandlerAdvice : IMethodInterceptor, IInitializingObject + { + /// + /// Gets or sets the Regex string used to parse advice expressions starting with 'on exception name' and subclass specific actions. + /// + /// The regex string to parse advice expressions starting with 'on exception name' and subclass specific actions. + public abstract string OnExceptionNameRegex + { + get; set; + } + + /// + /// Gets or sets the Regex string used to parse advice expressions starting with 'on exception (constraint)' and subclass specific actions. + /// + /// The regex string to parse advice expressions starting with 'on exception (constraint)' and subclass specific actions. + public abstract string OnExceptionRegex + { + get; set; + } + + /// + /// Implement this method to perform extra treatments before and after + /// the call to the supplied . + /// + /// + ///

    + /// Polite implementations would certainly like to invoke + /// . + ///

    + ///
    + /// + /// The method invocation that is being intercepted. + /// + /// + /// The result of the call to the + /// method of + /// the supplied ; this return value may + /// well have been intercepted by the interceptor. + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + public abstract object Invoke(IMethodInvocation invocation); + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public abstract void AfterPropertiesSet(); + + /// + /// Parses the advice expression. + /// + /// The advice expression. + /// An instance of ParsedAdviceExpression + protected virtual ParsedAdviceExpression ParseAdviceExpression(string adviceExpression) + { + ParsedAdviceExpression parsedAdviceExpression = new ParsedAdviceExpression(adviceExpression); + + Match match = GetMatch(adviceExpression, OnExceptionNameRegex); + if (match.Success) + { + parsedAdviceExpression.Success = true; + //using exception names for exception filter + parsedAdviceExpression.ExceptionNames = StringUtils.CommaDelimitedListToStringArray(match.Groups[2].Value.Trim()); + parsedAdviceExpression.ActionText = match.Groups[3].Value.Trim(); + parsedAdviceExpression.ActionExpressionText = match.Groups[4].Value.Trim(); + } + else + { + match = GetMatch(adviceExpression, OnExceptionRegex); + if (match.Success) + { + parsedAdviceExpression.Success = true; + //using constratin expression for exception filter + string constraintExpression = match.Groups[2].Value.Trim().Remove(0, 1); + parsedAdviceExpression.ConstraintExpression = constraintExpression.Substring(0, constraintExpression.Length - 1); + parsedAdviceExpression.ActionText = match.Groups[3].Value.Trim(); + parsedAdviceExpression.ActionExpressionText = match.Groups[4].Value.Trim(); + } + } + return parsedAdviceExpression; + } + + + /// + /// Gets the match using exception constraint expression. + /// + /// The advice expression string. + /// The regex string. + /// The Match object resulting from the regular expression match. + protected virtual Match GetMatch(string adviceExpressionString, string regexString) + { + RegexOptions options = ((RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline) | RegexOptions.IgnoreCase); + Regex reg = new Regex(regexString, options); + return reg.Match(adviceExpressionString); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/BaseCacheAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/BaseCacheAdvice.cs new file mode 100644 index 00000000..28359226 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Cache/BaseCacheAdvice.cs @@ -0,0 +1,160 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using Spring.Caching; +using Spring.Context; +using Spring.Expressions; +using System.Collections; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Base class for different cache advice implementations that provide + /// access to common functionality, such as obtaining a cache instance. + /// + /// Aleksandar Seovic + /// $Id: BaseCacheAdvice.cs,v 1.3 2007/04/01 15:04:04 bbaia Exp $ + public class BaseCacheAdvice : IApplicationContextAware + { + private IApplicationContext applicationContext; + + /// + /// Set the that this + /// object runs in. + /// + public IApplicationContext ApplicationContext + { + get { return applicationContext; } + set { applicationContext = value; } + } + + /// + /// Returns an instance based on the cache name. + /// + /// The name of the cache. + /// + /// Cache instance for the specified if one + /// is registered in the application context, or null if it isn't. + /// + public ICache GetCache(string name) + { + return applicationContext.GetObject(name) as ICache; + } + + /// + /// Prepares variables for expression evaluation by packaging all + /// method arguments into a dictionary, keyed by argument name. + /// + /// + /// Method to get parameters info from. + /// + /// + /// Argument values to package. + /// + /// + /// A dictionary containing all method arguments, keyed by method name. + /// + protected static IDictionary PrepareVariables(MethodInfo method, object[] arguments) + { + ParameterInfo[] parameters = method.GetParameters(); + + IDictionary vars = new Hashtable(); + for (int i = 0; i < parameters.Length; i++) + { + ParameterInfo p = parameters[i]; + vars[p.Name] = arguments[i]; + } + return vars; + } + + /// + /// Evaluates a SpEL expression as a boolean value. + /// + /// + /// The expression string that should be evaluated. + /// + /// + /// The SpEL expression instance that should be evaluated. + /// + /// + /// The object to evaluate expression against. + /// + /// + /// The expression variables dictionary. + /// + /// + /// The evaluated boolean. + /// + /// + /// If the SpEL expression could not be successfuly resolved to a boolean. + /// + protected static bool EvalCondition(string condition, + IExpression conditionExpression, object context, IDictionary variables) + { + if (conditionExpression == null) + { + return true; + } + else + { + object value = conditionExpression.GetValue(context, variables); + if (value is bool) + { + return (bool)value; + } + else + { + throw new InvalidOperationException(String.Format( + "The SpEL expression '{0}' could not be successfuly resolved to a boolean.", + condition)); + } + } + } + + /// + /// Retrieves custom attribute for the specified attribute type. + /// + /// + /// Method to get attribute from. + /// + /// + /// Attribute type. + /// + /// + /// Attribute instance if one is found, null otherwise. + /// + protected static object GetCustomAttribute(MethodInfo method, Type attributeType) + { + object[] attributes = method.GetCustomAttributes(attributeType, false); + if (attributes.Length > 0) + { + return attributes[0]; + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/CacheAspect.cs b/src/Spring/Spring.Aop/Aspects/Cache/CacheAspect.cs new file mode 100644 index 00000000..f574e551 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Cache/CacheAspect.cs @@ -0,0 +1,107 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Aop; +using Spring.Context; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Caching aspect implementation. + /// + /// + /// This class encapsulates all the advisors that need to be configured to support + /// Spring's full caching functionality. + /// + /// + /// + /// + /// Aleksandar Seovic + /// $Id: CacheAspect.cs,v 1.2 2007/08/03 14:38:35 markpollack Exp $ + public class CacheAspect : IAdvisors, IApplicationContextAware + { + private IAdvisor[] advisors; + private IApplicationContext applicationContext; + + /// + /// Creates a new instance. + /// + public CacheAspect() + { + advisors = new IAdvisor[] { new CacheResultAdvisor(), new CacheParameterAdvisor(), new InvalidateCacheAdvisor() }; + } + + /// + /// Gets or sets a list of advisors for this aspect. + /// + /// + /// A list of advisors for this aspect. + /// + public IList Advisors + { + get { return advisors; } + set { throw new NotSupportedException("Cache aspect advisors cannot be set externally."); } + } + /// + /// Set the that this + /// object runs in. + /// + /// + ///

    + /// Normally this call will be used to initialize the object. + ///

    + ///

    + /// Invoked after population of normal object properties but before an + /// init callback such as + /// 's + /// + /// or a custom init-method. Invoked after the setting of any + /// 's + /// + /// property. + ///

    + ///
    + /// + /// In the case of application context initialization errors. + /// + /// + /// If thrown by any application context methods. + /// + /// + public IApplicationContext ApplicationContext + { + get { return applicationContext; } + set + { + applicationContext = value; + for (int i = 0; i < advisors.Length; i++) + { + ((IApplicationContextAware) advisors[i]).ApplicationContext = applicationContext; + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs new file mode 100644 index 00000000..f3b705f6 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs @@ -0,0 +1,113 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Reflection; +using Common.Logging; +using Spring.Aop; +using Spring.Caching; +using Spring.Util; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Implementation of a parameter caching advice. + /// + /// + ///

    + /// This advice can be used to cache the parameter of the method. + ///

    + ///

    + /// Information that determines where, how and for how long the return value + /// will be cached are retrieved from the s + /// that are defined on the pointcut. + ///

    + ///

    + /// Parameter values are cached *after* the target method is invoked in order to + /// capture any parameter state changes it might make (for example, it is common + /// to set an object identifier within the save method for the persistent entity). + ///

    + ///
    + /// + /// Aleksandar Seovic + /// $Id: CacheParameterAdvice.cs,v 1.7 2008/05/05 15:00:55 markpollack Exp $ + public class CacheParameterAdvice : BaseCacheAdvice, IAfterReturningAdvice + { + // shared logger instance + private static readonly ILog logger = LogManager.GetLogger(typeof(CacheParameterAdvice)); + + /// + /// Executes after target + /// returns successfully. + /// + /// + ///

    + /// Note that the supplied cannot + /// be changed by this type of advice... use the around advice type + /// () if you + /// need to change the return value of an advised method invocation. + /// The data encapsulated by the supplied + /// can of course be modified though. + ///

    + ///
    + /// + /// The value returned by the . + /// + /// The intecepted method. + /// The intercepted method's arguments. + /// The target object. + /// + public void AfterReturning(object returnValue, MethodInfo method, object[] arguments, object target) + { + ParameterInfo[] parameters = method.GetParameters(); + for (int i = 0; i < parameters.Length; i++) + { + ParameterInfo p = parameters[i]; + CacheParameterAttribute[] paramInfoArray = + (CacheParameterAttribute[])p.GetCustomAttributes(typeof(CacheParameterAttribute), false); + + bool isLogDebugEnabled = logger.IsDebugEnabled; + foreach (CacheParameterAttribute paramInfo in paramInfoArray) + { + if (EvalCondition(paramInfo.Condition, paramInfo.ConditionExpression, arguments[i], null)) + { + ICache cache = GetCache(paramInfo.CacheName); + AssertUtils.ArgumentNotNull(cache, "CacheName", + "Parameter cache with the specified name [" + paramInfo.CacheName + + "] does not exist."); + object key = paramInfo.KeyExpression.GetValue(arguments[i]); + #region Instrumentation + + if (isLogDebugEnabled) + { + logger.Debug("Caching parameter for key [" + key + "]."); + } + + #endregion + cache.Insert(key, arguments[i], paramInfo.TimeToLiveTimeSpan); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvisor.cs b/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvisor.cs new file mode 100644 index 00000000..5186f4d6 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvisor.cs @@ -0,0 +1,113 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Aop.Support; +using Spring.Caching; +using Spring.Context; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Convinience advisor implementation that applies + /// to all the methods that have defined on one or + /// more of their parameters. + /// + /// Aleksandar Seovic + /// $Id: CacheParameterAdvisor.cs,v 1.1 2007/02/16 03:12:56 aseovic Exp $ + public class CacheParameterAdvisor : AttributeMatchMethodPointcutAdvisor, IApplicationContextAware + { + /// + /// Creates new advisor instance. + /// + public CacheParameterAdvisor() + { + Advice = new CacheParameterAdvice(); + Attribute = typeof(CacheParameterAttribute); + Inherit = false; + } + + /// + /// Set the that this + /// object runs in. + /// + /// + ///

    + /// Normally this call will be used to initialize the object. + ///

    + ///

    + /// Invoked after population of normal object properties but before an + /// init callback such as + /// 's + /// + /// or a custom init-method. Invoked after the setting of any + /// 's + /// + /// property. + ///

    + ///
    + /// + /// In the case of application context initialization errors. + /// + /// + /// If thrown by any application context methods. + /// + /// + public IApplicationContext ApplicationContext + { + get { return ((IApplicationContextAware) Advice).ApplicationContext; } + set { ((IApplicationContextAware)Advice).ApplicationContext = value; } + } + + /// + /// Returns true if any of the parameters of the specified + /// has applied. + /// + /// + /// Method to check. + /// + /// + /// Type of target object. + /// + /// + /// true if any of the parameters of the specified + /// has applied; false otherwise. + /// + public override bool Matches(MethodInfo method, Type targetType) + { + ParameterInfo[] parameters = method.GetParameters(); + for (int i = 0; i < parameters.Length; i++) + { + ParameterInfo p = parameters[i]; + if (p.IsDefined(Attribute, Inherit)) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs new file mode 100644 index 00000000..17836b70 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs @@ -0,0 +1,222 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Reflection; +using AopAlliance.Intercept; +using Common.Logging; +using Spring.Caching; +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Implementation of a result caching advice. + /// + /// + ///

    + /// This advice can be used to cache the return value of the method. + ///

    + ///

    + /// Parameters that determine where, how and for how long the return value + /// will be cached are retrieved from the and/or + /// that are defined on the pointcut. + ///

    + ///
    + /// + /// + /// Aleksandar Seovic + /// $Id: CacheResultAdvice.cs,v 1.6 2008/05/05 15:00:55 markpollack Exp $ + public class CacheResultAdvice : BaseCacheAdvice, IMethodInterceptor + { + // shared logger instance + private static readonly ILog logger = LogManager.GetLogger(typeof (CacheResultAdvice)); + + // NullValue + private static readonly object NullValue = new object(); + + /// + /// Applies caching around a method invocation. + /// + /// + ///

    + /// This method tries to retrieve an object from the cache, using the supplied + /// to generate a cache key. If an object is found + /// in the cache, the cached value is returned and the method call does not + /// proceed any further down the invocation chain. + ///

    + ///

    + /// If object does not exist in the cache, the advised method is called (using + /// ) + /// and any return value is cached for the next method invocation. + ///

    + ///
    + /// + /// The method invocation that is being intercepted. + /// + /// + /// A cached object or the result of the + /// call. + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + public object Invoke(IMethodInvocation invocation) + { + CacheResultAttribute resultInfo = + (CacheResultAttribute) GetCustomAttribute(invocation.Method, typeof (CacheResultAttribute)); + CacheResultItemsAttribute[] itemInfoArray = + (CacheResultItemsAttribute[])invocation.Method.GetCustomAttributes(typeof(CacheResultItemsAttribute), false); + + bool cacheHit = false; + object returnValue = GetReturnValue(invocation, resultInfo, out cacheHit); + + if (!cacheHit && itemInfoArray.Length > 0 && returnValue is ICollection) + { + CacheResultItems((ICollection)returnValue, itemInfoArray); + } + + return returnValue; + } + + /// + /// Obtains return value either from cache or by invoking target method + /// and caches it if necessary. + /// + /// + /// The method invocation that is being intercepted. + /// + /// + /// Attribute specifying where and how to cache return value. Can be null, + /// in which case no caching of the result as a whole will be performed + /// (if the result is collection, individual items could still be cached separately). + /// + /// + /// Returns true if the return value was found in cache, false otherwise. + /// + /// + /// Return value for the specified . + /// + private object GetReturnValue(IMethodInvocation invocation, CacheResultAttribute resultInfo, out bool cacheHit) + { + if (resultInfo != null) + { + object returnValue = null; + bool isLogDebugEnabled = logger.IsDebugEnabled; + + IDictionary vars = PrepareVariables(invocation.Method, invocation.Arguments); + + object resultKey = resultInfo.KeyExpression.GetValue(null, vars); + ICache cache = GetCache(resultInfo.CacheName); + AssertUtils.ArgumentNotNull(cache, "CacheName", + "Result cache with the specified name [" + resultInfo.CacheName + + "] does not exist."); + + returnValue = cache.Get(resultKey); + cacheHit = (returnValue != null); + if (!cacheHit) + { + #region Instrumentation + + if (isLogDebugEnabled) + { + logger.Debug("Object for key [" + resultKey + "] was not found in cache. Proceeding..."); + } + + #endregion + + returnValue = invocation.Proceed(); + if (EvalCondition(resultInfo.Condition, resultInfo.ConditionExpression, returnValue, vars)) + { + #region Instrumentation + + if (isLogDebugEnabled) + { + logger.Debug("Caching object for key [" + resultKey + "]."); + } + + #endregion + cache.Insert(resultKey, (returnValue==null)?NullValue:returnValue, resultInfo.TimeToLiveTimeSpan); + } + } + else + { + #region Instrumentation + + if (isLogDebugEnabled) + { + logger.Debug("Cache hit for [" + resultKey + "]. Aborting invocation..."); + } + + #endregion + } + + return (returnValue==NullValue)?null:returnValue; + } + + cacheHit = false; + return invocation.Proceed(); + } + + /// + /// Caches each item from the collection returned by target method. + /// + /// + /// A collection of items to cache. + /// + /// + /// Attributes specifying where and how to cache each item from the collection. + /// + private void CacheResultItems(ICollection items, CacheResultItemsAttribute[] itemInfoArray) + { + foreach (CacheResultItemsAttribute itemInfo in itemInfoArray) + { + ICache cache = GetCache(itemInfo.CacheName); + AssertUtils.ArgumentNotNull(cache, "CacheName", + "Result item cache with the specified name [" + itemInfo.CacheName + + "] does not exist."); + + bool isDebugEnabled = logger.IsDebugEnabled; + foreach (object item in items) + { + if (EvalCondition(itemInfo.Condition, itemInfo.ConditionExpression, item, null)) + { + object itemKey = itemInfo.KeyExpression.GetValue(item); + #region Instrumentation + + if (isDebugEnabled) + { + logger.Debug("Caching collection item for key [" + itemKey + "]."); + } + + #endregion + cache.Insert(itemKey, (item==null?NullValue:item), itemInfo.TimeToLiveTimeSpan); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvisor.cs b/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvisor.cs new file mode 100644 index 00000000..160cfd5b --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvisor.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Aop.Support; +using Spring.Caching; +using Spring.Context; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Convinience advisor implementation that applies + /// to all the methods that have defined. + /// + /// Aleksandar Seovic + /// $Id: CacheResultAdvisor.cs,v 1.1 2007/02/16 03:13:01 aseovic Exp $ + public class CacheResultAdvisor : AttributeMatchMethodPointcutAdvisor, IApplicationContextAware + { + /// + /// Creates new advisor instance. + /// + public CacheResultAdvisor() + { + Advice = new CacheResultAdvice(); + Inherit = false; + } + + /// + /// Set the that this + /// object runs in. + /// + /// + ///

    + /// Normally this call will be used to initialize the object. + ///

    + ///

    + /// Invoked after population of normal object properties but before an + /// init callback such as + /// 's + /// + /// or a custom init-method. Invoked after the setting of any + /// 's + /// + /// property. + ///

    + ///
    + /// + /// In the case of application context initialization errors. + /// + /// + /// If thrown by any application context methods. + /// + /// + public IApplicationContext ApplicationContext + { + get { return ((IApplicationContextAware) Advice).ApplicationContext; } + set { ((IApplicationContextAware) Advice).ApplicationContext = value; } + } + + /// + /// Returns true if either or + /// is applied to the + /// . + /// + /// + /// Method to check. + /// + /// + /// Type of target object. + /// + /// + /// true if either or + /// is applied to the + /// ; false otherwise. + /// + public override bool Matches(System.Reflection.MethodInfo method, Type targetType) + { + return method.IsDefined(typeof (CacheResultAttribute), Inherit) + || method.IsDefined(typeof (CacheResultItemsAttribute), Inherit); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs new file mode 100644 index 00000000..993f29ad --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs @@ -0,0 +1,121 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Reflection; + +using Spring.Aop; +using Spring.Caching; +using Spring.Util; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Implementation of a cache invalidation advice. + /// + /// + ///

    + /// This advice can be used to evict items from the cache. + ///

    + ///

    + /// Information that determines which items should be evicted and from which cache + /// are retrieved from the s that are defined + /// on the pointcut. + ///

    + ///

    + /// Items are evicted *after* target method is invoked. Return value of the method, + /// as well as method arguments, can be used to determine a list of keys for the items + /// that should be evicted (return value will be passed as a context for + /// expression evaluation, and method + /// arguments will be passed as variables, keyed by argument name). + ///

    + ///
    + /// + /// Aleksandar Seovic + /// $Id: InvalidateCacheAdvice.cs,v 1.4 2007/04/01 15:04:05 bbaia Exp $ + public class InvalidateCacheAdvice : BaseCacheAdvice, IAfterReturningAdvice + { + // shared logger instance + //private static readonly ILog logger = LogManager.GetLogger(typeof(InvalidateCacheAdvice)); + + /// + /// Executes after + /// returns successfully. + /// + /// + ///

    + /// Note that the supplied cannot + /// be changed by this type of advice... use the around advice type + /// () if you + /// need to change the return value of an advised method invocation. + /// The data encapsulated by the supplied + /// can of course be modified though. + ///

    + ///
    + /// + /// The value returned by the . + /// + /// The intecepted method. + /// The intercepted method's arguments. + /// The target object. + /// + public void AfterReturning(object returnValue, MethodInfo method, object[] arguments, object target) + { + InvalidateCacheAttribute[] cacheInfoArray = + (InvalidateCacheAttribute[]) method.GetCustomAttributes(typeof(InvalidateCacheAttribute), false); + + if (cacheInfoArray.Length > 0) + { + IDictionary vars = PrepareVariables(method, arguments); + foreach (InvalidateCacheAttribute cacheInfo in cacheInfoArray) + { + if (EvalCondition(cacheInfo.Condition, cacheInfo.ConditionExpression, returnValue, vars)) + { + ICache cache = GetCache(cacheInfo.CacheName); + AssertUtils.ArgumentNotNull(cache, "CacheName", + "Cache with the specified name [" + cacheInfo.CacheName + + "] does not exist."); + + if (cacheInfo.KeysExpression != null) + { + object keys = cacheInfo.KeysExpression.GetValue(returnValue, vars); + if (keys is ICollection) + { + cache.RemoveAll(keys as ICollection); + } + else + { + cache.Remove(keys); + } + } + else + { + cache.Clear(); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvisor.cs b/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvisor.cs new file mode 100644 index 00000000..5eaf46e6 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvisor.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Aop.Support; +using Spring.Caching; +using Spring.Context; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Convinience advisor implementation that applies + /// to all the methods that have defined. + /// + /// Aleksandar Seovic + /// $Id: InvalidateCacheAdvisor.cs,v 1.1 2007/02/16 03:13:01 aseovic Exp $ + public class InvalidateCacheAdvisor : AttributeMatchMethodPointcutAdvisor, IApplicationContextAware + { + /// + /// Creates new advisor instance. + /// + public InvalidateCacheAdvisor() + { + Advice = new InvalidateCacheAdvice(); + Attribute = typeof(InvalidateCacheAttribute); + Inherit = false; + } + + /// + /// Set the that this + /// object runs in. + /// + /// + ///

    + /// Normally this call will be used to initialize the object. + ///

    + ///

    + /// Invoked after population of normal object properties but before an + /// init callback such as + /// 's + /// + /// or a custom init-method. Invoked after the setting of any + /// 's + /// + /// property. + ///

    + ///
    + /// + /// In the case of application context initialization errors. + /// + /// + /// If thrown by any application context methods. + /// + /// + public IApplicationContext ApplicationContext + { + get { return ((IApplicationContextAware) Advice).ApplicationContext; } + set { ((IApplicationContextAware) Advice).ApplicationContext = value; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Exceptions/ExceptionHandlerAdvice.cs b/src/Spring/Spring.Aop/Aspects/Exceptions/ExceptionHandlerAdvice.cs new file mode 100644 index 00000000..17bc8b35 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Exceptions/ExceptionHandlerAdvice.cs @@ -0,0 +1,391 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Reflection; +using AopAlliance.Intercept; +using Common.Logging; + +namespace Spring.Aspects.Exceptions +{ + /// + /// Exception advice to perform exception translation, conversion of exceptions to default return values, and + /// exception swallowing. Configuration is via a DSL like string for ease of use in common cases as well as + /// allowing for custom translation logic by leveraging the Spring expression language. + /// + /// + /// + /// The exception handler collection can be filled with either instances of objects that implement the interface + /// or a string that follows a simple syntax for most common exception management + /// needs. The source exceptions to perform processing on are listed immediately after the keyword 'on' and can + /// be comma delmited. Following that is the action to perform, either log, translate, wrap, replace, return, or + /// swallow. Following the action is a Spring expression language (SpEL) fragment that is used to either create the + /// translated/wrapped/replaced exception or specify an alternative return value. The variables available to be + /// used in the expression language fragment are, #method, #args, #target, and #e which are 1) the method that + /// threw the exception, the arguments to the method, the target object itself, and the exception that was thrown. + /// Using SpEL gives you great flexibility in creating a translation of an exception that has access to the calling context. + /// + /// Common translation cases, wrap and rethrow, are supported with a shorter syntax where you can specify only + /// the exception text for the new translated exception. If you ommit the exception text a default value will be + /// used. + /// The exceptionsHandlers are compared to the thrown exception in the order they are listed. logging + /// an exception will continue the evaluation process, in all other cases exceution stops at that point and the + /// appropriate exceptions handler is executed. + /// + /// + /// + /// on FooException1 log 'My Message, Method Name ' + #method.Name + /// on FooException1 translate new BarException('My Message, Method Called = ' + #method.Name", #e) + /// on FooException2,Foo3Exception wrap BarException 'My Bar Message' + /// on FooException4 replace BarException 'My Bar Message' + /// on FooException5 return 32 + /// on FooException6 swallow + /// + /// + /// + /// + /// + /// + /// Mark Pollack + /// $Id: ExceptionHandlerAdvice.cs,v 1.9 2007/10/11 01:29:42 markpollack Exp $ + public class ExceptionHandlerAdvice : AbstractExceptionHandlerAdvice + { + #region Fields + + private static readonly ILog log = LogManager.GetLogger(typeof(ExceptionHandlerAdvice)); + + private IList exceptionHandlers = new ArrayList(); + + private string onExceptionNameRegex = @"^(on\s+exception\s+name)\s+(.*?)\s+(log|translate|wrap|replace|return|swallow)\s*(.*?)$"; + + private string onExceptionRegex = @"^(on\s+exception\s+)(\(.*?\))\s+(log|translate|wrap|replace|return|swallow)\s*(.*?)$"; + + #endregion + + #region Properties + + /// + /// Gets or sets the Regex string used to parse advice expressions starting with 'on exception name' and exception handling actions. + /// + /// The regex string to parse advice expressions starting with 'on exception name' and exception handling actions. + public override string OnExceptionNameRegex + { + get { return onExceptionNameRegex; } + set { onExceptionNameRegex = value; } + } + + /// + /// Gets or sets the Regex string used to parse advice expressions starting with 'on exception (constraint)' and exception handling actions. + /// + /// The regex string to parse advice expressions starting with 'on exception (constraint)' and exception handling actions. + public override string OnExceptionRegex + { + get { return onExceptionRegex; } + set { onExceptionRegex = value; } + } + + /// + /// Gets or sets the exception handler. + /// + /// The exception handler. + public IList ExceptionHandlers + { + get { return exceptionHandlers; } + set { exceptionHandlers = value; } + } + + #endregion + + #region IMethodInterceptor implementation + + /// + /// Implement this method to perform extra treatments before and after + /// the call to the supplied . + /// + /// + ///

    + /// Polite implementations would certainly like to invoke + /// . + ///

    + ///
    + /// + /// The method invocation that is being intercepted. + /// + /// + /// The result of the call to the + /// method of + /// the supplied ; this return value may + /// well have been intercepted by the interceptor. + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + public override object Invoke(IMethodInvocation invocation) + { + try + { + return invocation.Proceed(); + } + catch (TargetInvocationException ex) + { + Exception realException = ex.InnerException; + InvokeHandlers(realException, invocation); + throw realException; + } + catch (Exception ex) + { + object returnVal = InvokeHandlers(ex, invocation); + + if (returnVal == null) + { + return null; + } + + // if only logged + if (returnVal.Equals("logged")) + { + throw; + } + + //only here if we only are swallowing, returning alternative value, no matching handler was found. + + // no matching handler. + if (returnVal.Equals("nomatch")) + { + throw; + } + else + { + //TODO make spring specific value. + if (!returnVal.Equals("swallow")) + { + return returnVal; + } + else + { + return null; + } + } + } + } + + #endregion + + #region IInitializingObject implementation + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public override void AfterPropertiesSet() + { + if (exceptionHandlers.Count == 0) + { + throw new ArgumentException("At least one handler is required"); + } + IList newExceptionHandlers = new ArrayList(); + foreach (object o in exceptionHandlers) + { + string handlerString = o as string; + if (handlerString != null) + { + IExceptionHandler handler = Parse(handlerString); + if (handler == null) + { + throw new ArgumentException("Was not able to parse exception handler string [" + handlerString + + "]"); + } + newExceptionHandlers.Add(handler); + } + + } + //TODO sync. + exceptionHandlers = newExceptionHandlers; + } + + #endregion + + #region Methods + + /// + /// Invokes handlers registered for the passed exception and + /// + /// The exception to be handled + /// The that raised this exception. + /// The output of + protected virtual object InvokeHandlers(Exception ex, IMethodInvocation invocation) + { + IDictionary callContextDictionary = new Hashtable(); + callContextDictionary.Add("method", invocation.Method); + callContextDictionary.Add("args", invocation.Arguments); + callContextDictionary.Add("target", invocation.Target); + callContextDictionary.Add("e", ex); + object retValue = "nomatch"; + foreach (IExceptionHandler handler in exceptionHandlers) + { + if (handler != null) + { + if (handler.CanHandleException(ex, callContextDictionary)) + { + retValue = handler.HandleException(callContextDictionary); + if (!handler.ContinueProcessing) + { + return retValue; + } + } + } + } + return retValue; + } + + /// + /// Parses the specified handler string, creating an instance of IExceptionHander. + /// + /// The handler string. + /// an instance of an exception handler or null if was not able to correctly parse + /// handler string. + protected virtual IExceptionHandler Parse(string handlerString) + { + ParsedAdviceExpression parsedAdviceExpression = ParseAdviceExpression(handlerString); + + if (!parsedAdviceExpression.Success) + { + log.Warn("Could not parse exception hander statement " + handlerString); + return null; + } + + return CreateExceptionHandler(parsedAdviceExpression); + } + + private static IExceptionHandler CreateExceptionHandler(ParsedAdviceExpression parsedAdviceExpression) + { + if (parsedAdviceExpression.ActionText.IndexOf("log") >= 0) + { + //TODO support user selection of level, log.Debug , log.Info etc. + LogExceptionHandler handler = new LogExceptionHandler(parsedAdviceExpression.ExceptionNames); + handler.ConstraintExpressionText = parsedAdviceExpression.ConstraintExpression; + handler.ActionExpressionText = "#log.Trace(" + parsedAdviceExpression.ActionExpressionText + ")"; + return handler; + } + else if (parsedAdviceExpression.ActionText.IndexOf("translate") >= 0) + { + TranslationExceptionHandler handler = new TranslationExceptionHandler(parsedAdviceExpression.ExceptionNames); + handler.ConstraintExpressionText = parsedAdviceExpression.ConstraintExpression; + handler.ActionExpressionText = parsedAdviceExpression.ActionExpressionText; + return handler; + } + else if (parsedAdviceExpression.ActionText.IndexOf("wrap") >= 0) + { + TranslationExceptionHandler handler = new TranslationExceptionHandler(parsedAdviceExpression.ExceptionNames); + handler.ConstraintExpressionText = parsedAdviceExpression.ConstraintExpression; + handler.ActionExpressionText = ParseWrappedExceptionExpression("wrap", parsedAdviceExpression.AdviceExpression); + return handler; + } + else if (parsedAdviceExpression.ActionText.IndexOf("replace") >= 0) + { + TranslationExceptionHandler handler = new TranslationExceptionHandler(parsedAdviceExpression.ExceptionNames); + handler.ConstraintExpressionText = parsedAdviceExpression.ConstraintExpression; + handler.ActionExpressionText = ParseWrappedExceptionExpression("replace", parsedAdviceExpression.AdviceExpression); + return handler; + } + else if (parsedAdviceExpression.ActionText.IndexOf("swallow") >= 0) + { + SwallowExceptionHandler handler = new SwallowExceptionHandler(parsedAdviceExpression.ExceptionNames); + handler.ConstraintExpressionText = parsedAdviceExpression.ConstraintExpression; + return handler; + } + else if (parsedAdviceExpression.ActionText.IndexOf("return") >= 0) + { + ReturnValueExceptionHandler handler = new ReturnValueExceptionHandler(parsedAdviceExpression.ExceptionNames); + handler.ConstraintExpressionText = parsedAdviceExpression.ConstraintExpression; + handler.ActionExpressionText = parsedAdviceExpression.ActionExpressionText; + return handler; + } + else + { + log.Warn("Could not parse exception hander statement " + parsedAdviceExpression.AdviceExpression); + } + return null; + } + + private static string ParseWrappedExceptionExpression(string action, string handlerString) + { + int endOfActionIndex = handlerString.IndexOf(action) + action.Length; + string exceptionAndMessage = handlerString.Substring(endOfActionIndex).Trim(); + int endOfExceptionTypeIndex = exceptionAndMessage.IndexOf(" "); + + string rawExpressionTextPart; + string exception; + //Has two pieces. + if (endOfExceptionTypeIndex > 0) + { + exception = exceptionAndMessage.Substring(0, endOfExceptionTypeIndex).Trim(); + rawExpressionTextPart = exceptionAndMessage.Substring(endOfExceptionTypeIndex).Trim(); + } + else + { + exception = exceptionAndMessage; + if (action.Equals("wrap")) + { + rawExpressionTextPart = "'Wrapped ' + #e.GetType().Name"; + } else + { + rawExpressionTextPart = "'Replaced ' + #e.GetType().Name"; + } + } + + if (action.Equals("wrap")) + { + return string.Format("new {0}({1}, #e)", exception, rawExpressionTextPart); + } else + { + return string.Format("new {0}({1})", exception, rawExpressionTextPart); + } + } + + + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Exceptions/LogExceptionHandler.cs b/src/Spring/Spring.Aop/Aspects/Exceptions/LogExceptionHandler.cs new file mode 100644 index 00000000..56305bc1 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Exceptions/LogExceptionHandler.cs @@ -0,0 +1,91 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using Common.Logging; +using Spring.Expressions; + +namespace Spring.Aspects.Exceptions +{ + /// + /// Log the exceptions. Default log nameis "LogExceptionHandler" and log level is Debug + /// + /// Mark Pollack + /// $Id: LogExceptionHandler.cs,v 1.6 2008/02/26 00:03:24 markpollack Exp $ + public class LogExceptionHandler : AbstractExceptionHandler + { + private string logName = "LogExceptionHandler"; + + + /// + /// Initializes a new instance of the class. + /// + public LogExceptionHandler() + { + ContinueProcessing = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// The exception names. + public LogExceptionHandler(string[] exceptionNames) : base(exceptionNames) + { + ContinueProcessing = true; + } + + + + /// + /// Gets or sets the name of the log. + /// + /// The name of the log. + public string LogName + { + get { return logName; } + set { logName = value; } + } + + + /// + /// Handles the exception. + /// + /// the calling context dictionary + /// + /// The return value from handling the exception, if not rethrown or a new exception is thrown. + /// + public override object HandleException(IDictionary callContextDictionary) + { + ILog log = LogManager.GetLogger(logName); + callContextDictionary.Add("log", log); + try + { + IExpression expression = Expression.Parse(ActionExpressionText); + expression.GetValue(null, callContextDictionary); + } + catch (Exception e) + { + log.Warn("Was not able to evaluate action expression [" + ActionExpressionText + "]", e); + } + return "logged"; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Exceptions/ReturnValueExceptionHandler.cs b/src/Spring/Spring.Aop/Aspects/Exceptions/ReturnValueExceptionHandler.cs new file mode 100644 index 00000000..46c239df --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Exceptions/ReturnValueExceptionHandler.cs @@ -0,0 +1,72 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using Common.Logging; +using Spring.Expressions; + +namespace Spring.Aspects.Exceptions +{ + /// + /// Evaluates the expression for the return value of the method. + /// + /// Mark Pollack + /// $Id: ReturnValueExceptionHandler.cs,v 1.3 2007/10/10 18:07:46 markpollack Exp $ + public class ReturnValueExceptionHandler : AbstractExceptionHandler + { + + /// + /// Initializes a new instance of the class. + /// + public ReturnValueExceptionHandler() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The exception names. + public ReturnValueExceptionHandler(string[] exceptionNames) : base(exceptionNames) + { + } + + + + /// + /// Returns the result of evaluating the translation expression. + /// + /// The return value from handling the exception, if not rethrown or a new exception is thrown. + public override object HandleException(IDictionary callContextDictionary) + { + object returnVal = null; + try + { + IExpression expression = Expression.Parse(ActionExpressionText); + returnVal = expression.GetValue(null, callContextDictionary); + } + catch (Exception e) + { + log.Warn("Was not able to evaluate action expression [" + ActionExpressionText + "]", e); + } + return returnVal; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Exceptions/SwallowExceptionHandler.cs b/src/Spring/Spring.Aop/Aspects/Exceptions/SwallowExceptionHandler.cs new file mode 100644 index 00000000..f7f22db2 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Exceptions/SwallowExceptionHandler.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.Aspects.Exceptions +{ + /// + /// Returns a token to indicate that this exception should be swallowed. + /// + /// Mark Pollack + /// $Id: SwallowExceptionHandler.cs,v 1.2 2007/10/02 21:56:53 markpollack Exp $ + public class SwallowExceptionHandler : AbstractExceptionHandler + { + /// + /// Initializes a new instance of the class. + /// + public SwallowExceptionHandler() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The exception names. + public SwallowExceptionHandler(string[] exceptionNames) : base(exceptionNames) + { + } + + + + /// + /// Handles the exception. + /// + /// The return value from handling the exception, if not rethrown or a new exception is thrown. + public override object HandleException(IDictionary callContextDictionary) + { + return "swallow"; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Exceptions/TranslationExceptionHandler.cs b/src/Spring/Spring.Aop/Aspects/Exceptions/TranslationExceptionHandler.cs new file mode 100644 index 00000000..65700a5a --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Exceptions/TranslationExceptionHandler.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using Common.Logging; +using Spring.Expressions; + +namespace Spring.Aspects.Exceptions +{ + /// + /// Translates from one exception to another based. My wrap or replace exception depending on the expression. + /// + /// Mark Pollack + /// $Id: TranslationExceptionHandler.cs,v 1.3 2007/10/10 18:07:46 markpollack Exp $ + public class TranslationExceptionHandler : AbstractExceptionHandler + { + /// + /// Initializes a new instance of the class. + /// + public TranslationExceptionHandler() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The exception names. + public TranslationExceptionHandler(string[] exceptionNames) : base(exceptionNames) + { + } + + + + /// + /// Handles the exception. + /// + /// The return value from handling the exception, if not rethrown or a new exception is thrown. + public override object HandleException(IDictionary callContextDictionary) + { + object o = null; + try { + IExpression expression = Expression.Parse(ActionExpressionText); + o = expression.GetValue(null, callContextDictionary); + } + catch (Exception e) + { + log.Warn("Was not able to evaluate action expression [" + ActionExpressionText + "]", e); + } + Exception translatedException = o as Exception; + if (translatedException != null) + { + ThrowTranslatedException(translatedException); + } + return null; + } + + private void ThrowTranslatedException(Exception exception) + { + throw exception; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/IExceptionHandler.cs b/src/Spring/Spring.Aop/Aspects/IExceptionHandler.cs new file mode 100644 index 00000000..3ac61223 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/IExceptionHandler.cs @@ -0,0 +1,97 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +namespace Spring.Aspects +{ + /// + /// Handles a thrown exception providing calling context. + /// + /// Mark Pollack + /// $Id: IExceptionHandler.cs,v 1.1 2007/10/08 22:05:16 markpollack Exp $ + public interface IExceptionHandler + { + /// + /// Determines whether this instance can handle the exception the specified exception. + /// + /// The exception. + /// The call context dictionary. + /// + /// true if this instance can handle the specified exception; otherwise, false. + /// + bool CanHandleException(Exception ex, IDictionary callContextDictionary); + + /// + /// Handles the exception. + /// + /// The call context dictionary. + /// + /// The return value from handling the exception, if not rethrown or a new exception is thrown. + /// + object HandleException(IDictionary callContextDictionary); + + /// + /// Gets the source exception names. + /// + /// The source exception names. + IList SourceExceptionNames + { + get; set; + } + + /// + /// Gets the source exception types. + /// + /// The source exception types. + IList SourceExceptionTypes + { + get; set; + } + + /// + /// Gets the translation expression text + /// + /// The translation expression text + string ActionExpressionText + { + get; set; + } + + /// + /// Gets or sets the constraint expression text. + /// + /// The constraint expression text. + string ConstraintExpressionText + { + get; set; + } + + /// + /// Gets a value indicating whether to continue processing. + /// + /// true if continue processing; otherwise, false. + bool ContinueProcessing + { + get; set; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Logging/AbstractLoggingAdvice.cs b/src/Spring/Spring.Aop/Aspects/Logging/AbstractLoggingAdvice.cs new file mode 100644 index 00000000..a0b0b581 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Logging/AbstractLoggingAdvice.cs @@ -0,0 +1,240 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using AopAlliance.Intercept; +using Common.Logging; +using Spring.Aop.Framework; + +namespace Spring.Aspects.Logging +{ + /// + /// Abstract base class for logging advice + /// + /// + /// + /// + /// Mark Pollack + /// $Id: AbstractLoggingAdvice.cs,v 1.2 2007/12/06 17:17:19 markpollack Exp $ + public abstract class AbstractLoggingAdvice : IMethodInterceptor + { + #region Fields + + /// + /// The default ILog instance used to write logging messages. + /// + protected ILog defaultLogger = LogManager.GetLogger(MethodInfo.GetCurrentMethod().DeclaringType); + + /// + /// Indicates whether or not proxy type names should be hidden when using dynamic loggers. + /// + private bool hideProxyTypeNames = false; + + #endregion + + #region Properties + + /// + /// Sets a value indicating whether to use a dynamic logger or static logger + /// + /// Default is to use a static logger. + /// + /// Used to determine which ILog instance should be used to write log messages for + /// a particular method invocation: a dynamic one for the Type getting called, + /// or a static one for the Type of the trace interceptor. + /// + /// + /// Specify either this property or LoggerName, not both. + /// + /// + /// true if use dynamic logger; otherwise, false. + public bool UseDynamicLogger + { + set + { + defaultLogger = (value ? null : LogManager.GetLogger(GetType())); + } + } + + /// + /// Sets the name of the logger to use. + /// + /// + /// The name will be passed to the underlying logging implementation through Common.Logging, + /// getting interpreted as the log category according to the loggers configuration. + /// + /// This can be specified to not log into the category of a Type (whether this + /// interceptor's class or the class getting called) but rather to a specific named category. + /// + /// + /// Specify either this property or UseDynamicLogger, but not both. + /// + /// + /// The name of the logger. + public string LoggerName + { + set + { + defaultLogger = LogManager.GetLogger(value); + } + } + + + /// + /// Sets a value indicating whether hide proxy type names (whenever possible) + /// when using dynamic loggers, i.e. property UseDynamicLogger is set to true. + /// + /// true if [hide proxy type names]; otherwise, false. + public bool HideProxyTypeNames + { + set { hideProxyTypeNames = value; } + } + + #endregion + + #region Methods + + /// + /// Adds logging to the method invocation. + /// + /// + /// The method IsInterceptorEnabled is called + /// as an optimization to determine if logging should be applied. If logging should be + /// applied, the method invocation is passed to the InvokeUnderLog method for handling. + /// If not, the method proceeds as normal. + /// + /// + /// The method invocation that is being intercepted. + /// + /// + /// The result of the call to the + /// method of + /// the supplied ; this return value may + /// well have been intercepted by the interceptor. + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + public object Invoke(IMethodInvocation invocation) + { + object o = invocation.This; + ILog log = GetLoggerForInvocation(invocation); + if (IsInterceptorEnabled(invocation, log)) + { + return InvokeUnderLog(invocation, log); + } + else + { + return invocation.Proceed(); + } + + } + + /// + /// Determines whether the interceptor is enabled for the specified invocation, that + /// is, whether the method InvokeUnderLog is called. + /// + /// The default behavior is to check whether the given ILog instance + /// is enabled by calling IsLogEnabled, whose default behavior is to check if + /// the TRACE level of logging is enabled. Subclasses + /// The invocation. + /// The log to write messages to + /// + /// true if [is interceptor enabled] [the specified invocation]; otherwise, false. + /// + protected virtual bool IsInterceptorEnabled(IMethodInvocation invocation, ILog log) + { + return IsLogEnabled(log); + } + + /// + /// Determines whether the given log is enabled. + /// + /// + /// Default is true when the trace level is enabled. Subclasses may override this + /// to change the level at which logging occurs, or return true to ignore level + /// checks. + /// The log instance to check. + /// + /// true if log is for a given log level; otherwise, false. + /// + protected virtual bool IsLogEnabled(ILog log) + { + return log.IsTraceEnabled; + } + + /// + /// Subclasses must override this method to perform any tracing around the supplied + /// IMethodInvocation. + /// + /// + /// Subclasses are resonsible for ensuring that the IMethodInvocation actually executes + /// by calling IMethodInvocation.Proceed(). + /// + /// By default, the passed-in ILog instance will have log level + /// "trace" enabled. Subclasses do not have to check for this again, unless + /// they overwrite the IsInterceptorEnabled method to modify + /// the default behavior. + /// + /// + /// The method invocation to log + /// The log to write messages to + /// The result of the call to IMethodInvocation.Proceed() + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + protected abstract object InvokeUnderLog(IMethodInvocation invocation, ILog log); + + + /// + /// Gets the appropriate log instance to use for the given IMethodInvocation. + /// + /// + /// If the UseDynamicLogger property is set to true, the ILog instance will be + /// for the target class of the IMethodInvocation, otherwise the log will be the + /// default static logger. + /// + /// The method invocation being logged. + /// The ILog instance to use. + protected virtual ILog GetLoggerForInvocation(IMethodInvocation invocation) + { + if (defaultLogger != null) + { + return defaultLogger; + } + else + { + object target = invocation.This; + Type logCategoryType = target.GetType(); + if (hideProxyTypeNames) + { + logCategoryType = AopUtils.GetTargetType(target); + } + return LogManager.GetLogger(logCategoryType); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Logging/SimpleLoggingAdvice.cs b/src/Spring/Spring.Aop/Aspects/Logging/SimpleLoggingAdvice.cs new file mode 100644 index 00000000..c7374ec8 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Logging/SimpleLoggingAdvice.cs @@ -0,0 +1,459 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using System.Text; +using AopAlliance.Intercept; +using Common.Logging; + +namespace Spring.Aspects.Logging +{ + /// + /// Configurable advice for logging. + /// + /// + /// + /// + /// Mark Pollack + /// $Id: SimpleLoggingAdvice.cs,v 1.7 2008/04/04 15:30:13 markpollack Exp $ + public class SimpleLoggingAdvice : AbstractLoggingAdvice + { + #region Fields + + /// + /// Flag to indicate if unique identifier should be in the log message. + /// + private bool logUniqueIdentifier; + + /// + /// Flag to indicate if the execution time should be in the log message. + /// + private bool logExecutionTime; + + /// + /// Flag to indicate if the method arguments should be in the log message. + /// + private bool logMethodArguments; + + /// + /// Flag to indicate if the return value should be in the log message. + /// + private bool logReturnValue; + + /// + /// The separator string to use for delmiting log message fields. + /// + private string separator = ", "; + + /// + /// The log level to use for logging the entry, exit, exception messages. + /// + private LogLevel logLevel = LogLevel.Trace; + + + #endregion + + #region Constructor(s) + + /// + /// Initializes a new instance of the class. + /// + public SimpleLoggingAdvice() + { + } + + + /// + /// Initializes a new instance of the class. + /// + /// if set to true to use dynamic logger, if + /// false use static logger. + public SimpleLoggingAdvice(bool useDynamicLogger) + { + UseDynamicLogger = useDynamicLogger; + } + + #endregion + + #region Properties + + /// + /// Gets or sets a value indicating whether to log a unique identifier with the log message. + /// + /// true if [log unique identifier]; otherwise, false. + public bool LogUniqueIdentifier + { + get { return logUniqueIdentifier; } + set { logUniqueIdentifier = value; } + } + + /// + /// Gets or sets a value indicating whether to log execution time. + /// + /// true if log execution time; otherwise, false. + public bool LogExecutionTime + { + get { return logExecutionTime; } + set { logExecutionTime = value; } + } + + /// + /// Gets or sets a value indicating whether log method arguments. + /// + /// true if log method arguments]; otherwise, false. + public bool LogMethodArguments + { + get { return logMethodArguments; } + set { logMethodArguments = value; } + } + + /// + /// Gets or sets a value indicating whether log return value. + /// + /// true if log return value; otherwise, false. + public bool LogReturnValue + { + get { return logReturnValue; } + set { logReturnValue = value; } + } + + /// + /// Gets or sets the seperator string to use for delmiting log message fields. + /// + /// The seperator. + public string Separator + { + get { return separator; } + set { separator = value; } + } + + /// + /// Gets or sets the entry log level. + /// + /// The entry log level. + public LogLevel LogLevel + { + get { return logLevel; } + set { logLevel = value; } + } + + #endregion + + #region Protected Methods + + /// + /// Subclasses must override this method to perform any tracing around the supplied + /// IMethodInvocation. + /// + /// The method invocation to log + /// The log to write messages to + /// + /// The result of the call to IMethodInvocation.Proceed() + /// + /// + /// Subclasses are resonsible for ensuring that the IMethodInvocation actually executes + /// by calling IMethodInvocation.Proceed(). + /// + /// By default, the passed-in ILog instance will have log level + /// "trace" enabled. Subclasses do not have to check for this again, unless + /// they overwrite the IsInterceptorEnabled method to modify + /// the default behavior. + /// + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + protected override object InvokeUnderLog(IMethodInvocation invocation, ILog log) + { + object returnValue = null; + bool exitThroughException = false; + + DateTime startTime = DateTime.Now; + string uniqueIdentifier = null; + + if (LogUniqueIdentifier) + { + uniqueIdentifier = CreateUniqueIdentifier(); + } + try + { + WriteToLog(LogLevel, log, GetEntryMessage(invocation, uniqueIdentifier), null); + returnValue = invocation.Proceed(); + return returnValue; + } catch (Exception e) + { + TimeSpan executionTimeSpan = DateTime.Now - startTime; + WriteToLog(LogLevel, log, GetExceptionMessage(invocation, e, executionTimeSpan, uniqueIdentifier), e); + exitThroughException = true; + throw; + } + finally + { + if (!exitThroughException) + { + TimeSpan executionTimeSpan = DateTime.Now - startTime; + WriteToLog(LogLevel, log, GetExitMessage(invocation, returnValue, executionTimeSpan, uniqueIdentifier), null); + } + } + } + + /// + /// Determines whether the given log is enabled. + /// + /// The log instance to check. + /// + /// true if log is for a given log level; otherwise, false. + /// + /// + /// Default is true when the trace level is enabled. Subclasses may override this + /// to change the level at which logging occurs, or return true to ignore level + /// checks. + protected override bool IsLogEnabled(ILog log) + { + switch (LogLevel) + { + case LogLevel.All: + case LogLevel.Trace: + if (log.IsTraceEnabled) + { + return true; + } + break; + case LogLevel.Debug: + if (log.IsDebugEnabled) + { + return true; + } + break; + case LogLevel.Error: + if (log.IsErrorEnabled) + { + return true; + } + break; + case LogLevel.Fatal: + if (log.IsFatalEnabled) + { + return true; + } + break; + case LogLevel.Info: + if (log.IsInfoEnabled) + { + return true; + } + break; + case LogLevel.Warn: + if (log.IsWarnEnabled) + { + return true; + } + break; + case LogLevel.Off: + default: + break; + } + return false; + } + + /// + /// Creates a unique identifier. + /// + /// + /// Default implementation uses Guid.NewGuid(). Subclasses may override to provide an alternative + /// ID generation implementation. + /// + /// A unique identifier + protected virtual string CreateUniqueIdentifier() + { + return Guid.NewGuid().ToString(); + } + + /// + /// Gets the entry message to log + /// + /// The invocation. + /// The id string. + /// The entry log message + protected virtual string GetEntryMessage(IMethodInvocation invocation, string idString) + { + StringBuilder sb = new StringBuilder(128); + sb.Append("Entering "); + AppendCommonInformation(sb, invocation, idString); + if (logMethodArguments) + { + sb.Append(GetMethodArgumentAsString(invocation)); + } + + return RemoveLastSeparator(sb.ToString(), Separator); + } + + /// + /// Gets the exception message. + /// + /// The method invocation. + /// The thown exception. + /// The execution time span. + /// The id string. + /// The exception log message. + protected virtual string GetExceptionMessage(IMethodInvocation invocation, Exception e, TimeSpan executionTimeSpan, string idString) + { + StringBuilder sb = new StringBuilder(128); + sb.Append("Exception thrown in "); + sb.Append(invocation.Method.Name).Append(Separator); + AppendCommonInformation(sb, invocation, idString); + if (LogExecutionTime) + { + sb.Append(executionTimeSpan.TotalMilliseconds).Append(" ms"); + } + + return RemoveLastSeparator(sb.ToString(), Separator); + } + + + /// + /// Gets the exit log message. + /// + /// The method invocation. + /// The return value. + /// The execution time span. + /// The id string. + /// the exit log message + protected virtual string GetExitMessage(IMethodInvocation invocation, object returnValue, TimeSpan executionTimeSpan, string idString) + { + StringBuilder sb = new StringBuilder(128); + sb.Append("Exiting "); + AppendCommonInformation(sb, invocation, idString); + if (LogReturnValue && invocation.Method.ReturnType != typeof(void)) + { + sb.Append("return=").Append(returnValue).Append(Separator); + } + if (LogExecutionTime) + { + sb.Append(executionTimeSpan.TotalMilliseconds).Append(" ms"); + } + return RemoveLastSeparator(sb.ToString(), Separator); + } + + + /// + /// Appends common information across entry,exit, exception logging + /// + /// Add method name and unique identifier if required. + /// The string buffer building logging message. + /// The method invocation. + /// The unique identifier string. + protected virtual void AppendCommonInformation(StringBuilder sb, IMethodInvocation invocation, string idString) + { + sb.Append(invocation.Method.Name); + if (LogUniqueIdentifier) + { + sb.Append(Separator).Append(idString); + } + sb.Append(Separator); + } + + /// + /// Gets the method argument as argumen name/value pairs. + /// + /// The method invocation. + /// string for logging method argument name and values. + protected virtual string GetMethodArgumentAsString(IMethodInvocation invocation) + { + StringBuilder sb = new StringBuilder(128); + ParameterInfo[] parameterInfos = invocation.Method.GetParameters(); + object[] argValues = invocation.Arguments; + for (int i=0; i< parameterInfos.Length; i++) + { + sb.Append(parameterInfos[i].Name).Append("=").Append(argValues[i]); + if (i != parameterInfos.Length) sb.Append("; "); + } + + return RemoveLastSeparator(sb.ToString(), "; "); + } + + #endregion + + #region Private Methods + + private string RemoveLastSeparator(string str, string separator) + { + if (str.EndsWith(separator)) + { + return str.Substring(0, str.Length - separator.Length); + } + else + { + return str; + } + } + + private void WriteToLog(LogLevel logLevel, ILog log, string text, Exception e) + { + switch (logLevel) + { + case LogLevel.All: + case LogLevel.Trace: + if (log.IsTraceEnabled) + { + if (e == null) log.Trace(text); else log.Trace(text, e); + } + break; + case LogLevel.Debug: + if (log.IsDebugEnabled) + { + if (e == null) log.Debug(text); else log.Debug(text, e); + } + break; + case LogLevel.Error: + if (log.IsErrorEnabled) + { + if (e == null) log.Error(text); else log.Error(text, e); + } + break; + case LogLevel.Fatal: + if (log.IsFatalEnabled) + { + if (e == null) log.Fatal(text); else log.Fatal(text, e); + } + break; + case LogLevel.Info: + if (log.IsInfoEnabled) + { + if (e == null) log.Info(text); else log.Info(text, e); + } + break; + case LogLevel.Warn: + if (log.IsWarnEnabled) + { + if (e == null) log.Warn(text); else log.Warn(text, e); + } + break; + case LogLevel.Off: + default: + break; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/ParsedAdviceExpression.cs b/src/Spring/Spring.Aop/Aspects/ParsedAdviceExpression.cs new file mode 100644 index 00000000..0c127b7b --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/ParsedAdviceExpression.cs @@ -0,0 +1,115 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Aspects +{ + /// + /// This class contains the results of parsing an advice expresion of the form + /// on exception name [ExceptionName1,ExceptionName2,...] [action] [action expression] + /// or + /// on exception [constraint expression] [action] [action expression] + /// + /// + /// + /// + /// Mark Pollack + /// $Id: ParsedAdviceExpression.cs,v 1.1 2007/10/08 22:05:16 markpollack Exp $ + public class ParsedAdviceExpression + { + private string adviceExpression; + + private string[] exceptionNames = new string[0]; + private string constraintExpression = null; + private string actionExpressionText = null; + private string actionText = null; + private bool success; + + + /// + /// Initializes a new instance of the class. + /// + /// The advice expression. + public ParsedAdviceExpression(string adviceExpression) + { + this.adviceExpression = adviceExpression; + } + + + /// + /// Gets or sets the advice expression. + /// + /// The advice expression. + public string AdviceExpression + { + get { return adviceExpression; } + set { adviceExpression = value; } + } + + /// + /// Gets or sets the exception names. + /// + /// The exception names. + public string[] ExceptionNames + { + get { return exceptionNames; } + set { exceptionNames = value; } + } + + /// + /// Gets or sets the constraint expression. + /// + /// The constraint expression. + public string ConstraintExpression + { + get { return constraintExpression; } + set { constraintExpression = value; } + } + + /// + /// Gets or sets the action expression text. + /// + /// The action expression text. + public string ActionExpressionText + { + get { return actionExpressionText; } + set { actionExpressionText = value; } + } + + /// + /// Gets or sets the action text. + /// + /// The action text. + public string ActionText + { + get { return actionText; } + set { actionText = value; } + } + + /// + /// Gets or sets a value indicating whether this is success. + /// + /// true if success; otherwise, false. + public bool Success + { + get { return success; } + set { success = value; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs b/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs new file mode 100644 index 00000000..514e43af --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs @@ -0,0 +1,316 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Text.RegularExpressions; +using System.Threading; +using AopAlliance.Intercept; +using Common.Logging; +using Spring.Core.TypeConversion; +using Spring.Expressions; + +namespace Spring.Aspects +{ + /// + /// AOP Advice to retry a method invocation on an exception. The retry semantics are defined by a DSL of the + /// form on exception name [ExceptionName1,ExceptionName2,...] retry [number of times] [delay|rate] [delay time|rate expression]. + /// For example, on exception name ArithmeticException retry 3x delay 1s + /// + /// + /// + /// + /// Mark Pollack + /// $Id: RetryAdvice.cs,v 1.5 2008/03/17 20:25:34 markpollack Exp $ + public class RetryAdvice : AbstractExceptionHandlerAdvice + { + #region Fields + + private static readonly ILog log = LogManager.GetLogger(typeof (RetryAdvice)); + + private TimeSpanConverter timeSpanConverter = new TimeSpanConverter(); + + private RetryExceptionHandler retryExceptionHandler; + + private string retryExpression; + + private string onExceptionNameRegex = @"^(on\s+exception\s+name)\s+(.*?)\s+(retry)\s*(.*?)$"; + + private string onExceptionRegex = @"^(on\s+exception\s+)(\(.*?\))\s+(retry)\s*(.*?)$"; + + //retry 3x delay 10s + private string delayRegex = @"^(\d+)x\s+(delay)\s+(\d+\w+)?$"; + + //retry 3x rate 10n+5 + private string rateRegex = @"^(\d+)x\s+(rate)\s+(\(.*?\))?$"; + #endregion + + #region Properties + + /// + /// Gets or sets the retry expression. + /// + /// The retry expression. + public string RetryExpression + { + get { return retryExpression; } + set { retryExpression = value; } + } + + /// + /// Gets or sets the Regex string used to parse advice expressions starting with 'on exception name' and exception handling actions. + /// + /// The regex string to parse advice expressions starting with 'on exception name' and exception handling actions. + public override string OnExceptionNameRegex + { + get { return onExceptionNameRegex; } + set { onExceptionNameRegex = value; } + } + + /// + /// Gets or sets the Regex string used to parse advice expressions starting with 'on exception (constraint)' and exception handling actions. + /// + /// The regex string to parse advice expressions starting with 'on exception (constraint)' and exception handling actions. + public override string OnExceptionRegex + { + get { return onExceptionRegex; } + set { onExceptionRegex = value; } + } + + #endregion + + #region IMethodInterceptor implementation + + /// + /// Implement this method to perform extra treatments before and after + /// the call to the supplied . + /// + /// The method invocation that is being intercepted. + /// + /// The result of the call to the + /// method of + /// the supplied ; this return value may + /// well have been intercepted by the interceptor. + /// + /// + ///

    + /// Polite implementations would certainly like to invoke + /// . + ///

    + ///
    + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + public override object Invoke(IMethodInvocation invocation) + { + IDictionary callContextDictionary = new Hashtable(); + callContextDictionary.Add("method", invocation.Method); + callContextDictionary.Add("args", invocation.Arguments); + callContextDictionary.Add("target", invocation.Target); + int numAttempts = 0; + + object returnVal = null; + do + { + try + { + returnVal = invocation.Proceed(); + break; + } + catch (Exception ex) + { + callContextDictionary["e"] = ex; + if (retryExceptionHandler.CanHandleException(ex, callContextDictionary)) + { + numAttempts++; + if (numAttempts == retryExceptionHandler.MaximumRetryCount) + { + throw; + } + else + { + if (log.IsTraceEnabled) + { + log.Trace("Retrying " + invocation.Method.Name); + } + callContextDictionary["n"] = numAttempts; + Sleep(retryExceptionHandler, callContextDictionary); + } + } + else + { + throw; + } + } + } while (numAttempts <= retryExceptionHandler.MaximumRetryCount); + + + log.Debug("Invoked successfully after " + numAttempts + " attempt(s)"); + return returnVal; + } + + private static void Sleep(RetryExceptionHandler handler, IDictionary callContextDictionary) + { + if (handler.IsDelayBased) + { + Thread.Sleep(handler.DelayTimeSpan); + } + else + { + try + { + IExpression expression = Expression.Parse(handler.DelayRateExpression); + object result = expression.GetValue(null, callContextDictionary); + decimal d = decimal.Parse(result.ToString()); + decimal rounded = decimal.Round(d*1000,0); + int sleepInSeconds = decimal.ToInt32(rounded); + Thread.Sleep(sleepInSeconds); + } + catch (InvalidCastException e) + { + log.Warn("Was not able to cast expression to decimal [" + handler.DelayRateExpression + "]. Sleeping for 1 second", e); + Thread.Sleep(1000); + } + catch (Exception e) + { + log.Warn("Was not able to evaluate rate expression [" + handler.DelayRateExpression + "]. Sleeping for 1 second", e); + Thread.Sleep(1000); + } + } + } + + #endregion + + #region IInitializingObject implementation + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public override void AfterPropertiesSet() + { + if (retryExpression == null) + { + throw new ArgumentException("Must specify retry expression."); + } + RetryExceptionHandler handler = Parse(retryExpression); + if (handler == null) + { + throw new ArgumentException("Was not able to parse retry expression string [" + retryExpression + "]"); + } + retryExceptionHandler = handler; + } + + #endregion + + /// + /// Parses the specified handler string. + /// + /// The handler string. + /// + protected virtual RetryExceptionHandler Parse(string retryExpressionString) + { + + ParsedAdviceExpression parsedAdviceExpression = ParseAdviceExpression(retryExpressionString); + + if (!parsedAdviceExpression.Success) + { + log.Warn("Could not parse retry expression " + retryExpressionString); + return null; + } + + RetryExceptionHandler handler = new RetryExceptionHandler(parsedAdviceExpression.ExceptionNames); + handler.ConstraintExpressionText = parsedAdviceExpression.ConstraintExpression; + handler.ActionExpressionText = parsedAdviceExpression.AdviceExpression; + + Match match = GetMatchForActionExpression(parsedAdviceExpression.ActionExpressionText, delayRegex); + + if (match.Success) + { + handler.MaximumRetryCount = int.Parse(match.Groups[1].Value.Trim()); + handler.IsDelayBased = true; + + try + { + string ts = match.Groups[3].Value.Trim(); + handler.DelayTimeSpan = (TimeSpan) timeSpanConverter.ConvertFrom(null, null, ts); + } catch (Exception) + { + log.Warn("Could not parse timespan " + match.Groups[3].Value.Trim()); + return null; + } + return handler; + } + else + { + match = GetMatchForActionExpression(parsedAdviceExpression.ActionExpressionText, rateRegex); + if (match.Success) + { + handler.MaximumRetryCount = int.Parse(match.Groups[1].Value.Trim()); + handler.IsDelayBased = false; + handler.DelayRateExpression = match.Groups[3].Value.Trim(); + return handler; + } + else + { + return null; + } + } + + } + + /// + /// Gets the match for action expression. + /// + /// The action expression string. + /// The regex string. + /// The Match object resulting from the regular expression match. + protected virtual Match GetMatchForActionExpression(string actionExpressionString, string regexString) + { + RegexOptions options = ((RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline) | RegexOptions.IgnoreCase); + Regex reg = new Regex(regexString, options); + return reg.Match(actionExpressionString); + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/RetryExceptionHandler.cs b/src/Spring/Spring.Aop/Aspects/RetryExceptionHandler.cs new file mode 100644 index 00000000..660fbc50 --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/RetryExceptionHandler.cs @@ -0,0 +1,130 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +namespace Spring.Aspects +{ + /// + /// Sleeps for the appropriate amount of time for an exception. + /// + /// + /// + /// + /// Mark Pollack + /// $Id: RetryExceptionHandler.cs,v 1.1 2007/10/10 07:38:17 markpollack Exp $ + public class RetryExceptionHandler : AbstractExceptionHandler + { + #region Fields + + private int maximumRetryCount; + + private bool isDelayBased; + + private TimeSpan delayTimeSpan; + private string delayRateExpression; + + #endregion + + #region Constructor + + /// + /// Initializes a new instance of the class. + /// + public RetryExceptionHandler() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The exception names. + public RetryExceptionHandler(string[] exceptionNames) : base(exceptionNames) + { + } + + #endregion + + #region Properties + + /// + /// Gets the maximum retry count. + /// + /// The maximum retry count. + public int MaximumRetryCount + { + get { return maximumRetryCount; } + set { maximumRetryCount = value; } + } + + + /// + /// Gets a value indicating whether this instance is delay based. + /// + /// + /// true if this instance is delay based; otherwise, false. + /// + public bool IsDelayBased + { + get { return isDelayBased; } + set { isDelayBased = value; } + } + + /// + /// Gets or sets the delay time span to sleep after an exception is thrown and a rety is + /// attempted. + /// + /// The delay time span. + public TimeSpan DelayTimeSpan + { + get { return delayTimeSpan; } + set { delayTimeSpan = value; } + } + + /// + /// Gets or sets the delay rate expression. + /// + /// The delay rate expression. + public string DelayRateExpression + { + get { return delayRateExpression; } + set { delayRateExpression = value; } + } + + #endregion + + #region Methods + + /// + /// Handles the exception. + /// + /// + /// + /// The return value from handling the exception, if not rethrown or a new exception is thrown. + /// + public override object HandleException(IDictionary callContextDictionary) + { + return null; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Validation/ParameterValidationAdvice.cs b/src/Spring/Spring.Aop/Aspects/Validation/ParameterValidationAdvice.cs new file mode 100644 index 00000000..c1ea994a --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Validation/ParameterValidationAdvice.cs @@ -0,0 +1,76 @@ +using System.Reflection; +using Spring.Aop; +using Spring.Context; +using Spring.Validation; + +namespace Spring.Aspects.Validation +{ + /// + /// This advice is typically applied to service-layer methods in order to validate + /// method arguments. + /// + /// + /// Each argument that should be validated has to be marked with one or more + /// s. + /// If the validation fails, this advice will throw , + /// thus preventing target method invocation. + /// + /// + /// Damjan Tomic + /// Aleksandar Seovic + /// $Id: ParameterValidationAdvice.cs,v 1.2 2008/04/02 23:00:28 markpollack Exp $ + public class ParameterValidationAdvice : IMethodBeforeAdvice, IApplicationContextAware + { + private IApplicationContext applicationContext; + + /// + /// Intercepts method invocation and validates arguments. + /// + /// + /// Method invocation. + /// + /// + /// Method arguments. + /// + /// + /// Target object. + /// + /// + /// If one or more method arguments fail validation. + /// + public void Before(MethodInfo method, object[] args, object target) + { + ValidationErrors errors = new ValidationErrors(); + ParameterInfo[] parameters = method.GetParameters(); + + for (int i = 0; i < parameters.Length; i++) + { + ParameterInfo info = parameters[i]; + ValidatedAttribute[] attributes = (ValidatedAttribute[]) info.GetCustomAttributes(typeof(ValidatedAttribute), true); + + foreach (ValidatedAttribute attribute in attributes) + { + // throws NoSuchObjectDefinitionException if validator cannot be found + IValidator validator = (IValidator) applicationContext.GetObject(attribute.ValidatorName); + validator.Validate(args[i], errors); + } + } + if (!errors.IsEmpty) + { + throw new ValidationException(errors); + } + } + + /// + /// Gets or sets the application context to search for validators in. + /// + /// + /// The application context to search for validators in. + /// + public IApplicationContext ApplicationContext + { + get { return this.applicationContext; } + set { this.applicationContext = value; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Validation/ParameterValidationAdvisor.cs b/src/Spring/Spring.Aop/Aspects/Validation/ParameterValidationAdvisor.cs new file mode 100644 index 00000000..ad3a83ac --- /dev/null +++ b/src/Spring/Spring.Aop/Aspects/Validation/ParameterValidationAdvisor.cs @@ -0,0 +1,114 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using Spring.Aop.Support; +using Spring.Validation; +using Spring.Context; + +#endregion + +namespace Spring.Aspects.Validation +{ + /// + /// Convinience advisor implementation that applies + /// to all the methods that have defined on one or + /// more of their parameters. + /// + /// Bruno Baia + /// $Id: ParameterValidationAdvisor.cs,v 1.1 2008/05/26 09:17:50 bbaia Exp $ + public class ParameterValidationAdvisor : AttributeMatchMethodPointcutAdvisor, IApplicationContextAware + { + /// + /// Creates new advisor instance. + /// + public ParameterValidationAdvisor() + { + Advice = new ParameterValidationAdvice(); + Attribute = typeof(ValidatedAttribute); + Inherit = false; + } + + /// + /// Set the that this + /// object runs in. + /// + /// + ///

    + /// Normally this call will be used to initialize the object. + ///

    + ///

    + /// Invoked after population of normal object properties but before an + /// init callback such as + /// 's + /// + /// or a custom init-method. Invoked after the setting of any + /// 's + /// + /// property. + ///

    + ///
    + /// + /// In the case of application context initialization errors. + /// + /// + /// If thrown by any application context methods. + /// + /// + public IApplicationContext ApplicationContext + { + get { return ((IApplicationContextAware)Advice).ApplicationContext; } + set { ((IApplicationContextAware)Advice).ApplicationContext = value; } + } + + /// + /// Returns true if any of the parameters of the specified + /// has applied. + /// + /// + /// Method to check. + /// + /// + /// Type of target object. + /// + /// + /// true if any of the parameters of the specified + /// has applied; false otherwise. + /// + public override bool Matches(MethodInfo method, Type targetType) + { + ParameterInfo[] parameters = method.GetParameters(); + for (int i = 0; i < parameters.Length; i++) + { + ParameterInfo p = parameters[i]; + if (p.IsDefined(Attribute, Inherit)) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/AssemblyInfo.cs b/src/Spring/Spring.Aop/AssemblyInfo.cs new file mode 100644 index 00000000..9d6e9d0d --- /dev/null +++ b/src/Spring/Spring.Aop/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring.Net AOP support")] +[assembly: AssemblyDescription("Interfaces and classes that provide AOP support in Spring.Net")] \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Spring.Aop.2002.csproj b/src/Spring/Spring.Aop/Spring.Aop.2002.csproj new file mode 100644 index 00000000..341e1152 --- /dev/null +++ b/src/Spring/Spring.Aop/Spring.Aop.2002.csproj @@ -0,0 +1,800 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Aop/Spring.Aop.2003.csproj b/src/Spring/Spring.Aop/Spring.Aop.2003.csproj new file mode 100644 index 00000000..69a7cb75 --- /dev/null +++ b/src/Spring/Spring.Aop/Spring.Aop.2003.csproj @@ -0,0 +1,813 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Aop/Spring.Aop.2005.csproj b/src/Spring/Spring.Aop/Spring.Aop.2005.csproj new file mode 100644 index 00000000..dd9fc546 --- /dev/null +++ b/src/Spring/Spring.Aop/Spring.Aop.2005.csproj @@ -0,0 +1,434 @@ + + + Local + 8.0.50727 + 2.0 + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Debug + AnyCPU + + + + + Spring.Aop + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + false + + + ..\..\..\build\VS.Net.2005\Spring.Aop\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0;DEBUG_DYNAMIC + Spring.Aop.xml + true + 4096 + false + + + false + false + false + true + 4 + full + prompt + + + ..\..\..\build\VS.Net.2005\Spring.Aop\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\antlr.runtime.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + System + + + System.Data + + + System.XML + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + CommonAssemblyInfo.cs + Code + + + Designer + + + spring-aop-1.1.xsd + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Spring.Aop.build b/src/Spring/Spring.Aop/Spring.Aop.build new file mode 100644 index 00000000..9b372bce --- /dev/null +++ b/src/Spring/Spring.Aop/Spring.Aop.build @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Aop/Spring.Aop.xml b/src/Spring/Spring.Aop/Spring.Aop.xml new file mode 100644 index 00000000..23fe1a27 --- /dev/null +++ b/src/Spring/Spring.Aop/Spring.Aop.xml @@ -0,0 +1,10739 @@ + + + + Spring.Aop + + + + + The central interface for Spring.NET based AOP proxies. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IAopProxy.cs,v 1.4 2006/11/16 02:30:49 bbaia Exp $ + + + + Creates a new proxy object. + + + + + implementation + that caches advisor chains on a per-advised-method basis. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: HashtableCachingAdvisorChainFactory.cs,v 1.9 2007/03/16 04:01:18 aseovic Exp $ + + + + Factory interface for advisor chains. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IAdvisorChainFactory.cs,v 1.8 2006/09/14 21:05:23 bbaia Exp $ + + + + Callback interface for + listeners. + + +

    + Allows + implementations to be notified of notable lifecycle events relating + to the creation of a proxy, and changes to the configuration data of a + proxy. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IAdvisedSupportListener.cs,v 1.5 2006/04/09 07:18:35 markpollack Exp $ +
    + + + Invoked when the first proxy is created. + + + The relevant source. + + + + + Invoked when advice is changed after a proxy is created. + + + The relevant source. + + + + + Invoked when interfaces are changed after a proxy is created. + + + The relevant source. + + + + + Gets the list of and + + instances for the supplied . + + The proxy configuration object. + The object proxy. + + The method for which the interceptors are to be evaluated. + + + The of the target object. + + + The list of and + + instances for the supplied . + + + + + Gets the list of and + + instances for the supplied . + + The proxy configuration object. + The object proxy. + + The method for which the interceptors are to be evaluated. + + + The of the target object. + + + The list of and + + instances for the supplied . + + + + + Invoked when the first proxy is created. + + + The relevant source. + + + + + Invoked when advice is changed after a proxy is created. + + + The relevant source. + + + + + Invoked when interfaces are changed after a proxy is created. + + + The relevant source. + + + + + Represents the AOP configuration data built-in with the proxy. + + Bruno Baia + $Id: AdvisedProxy.cs,v 1.10 2008/02/06 18:28:52 bbaia Exp $ + + + + Configuration data for an AOP proxy factory. + + +

    + This configuration includes the + s, + s, and (any) proxied interfaces. +

    +

    + Any AOP proxy obtained from Spring.NET can be cast to this interface to + allow the manipulation of said proxy's AOP advice. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IAdvised.cs,v 1.15 2007/10/10 18:07:38 markpollack Exp $ + +
    + + + Adds the supplied to the end (or tail) + of the advice (interceptor) chain. + + +

    + Please be aware that Spring.NET's AOP implementation only supports + method advice (as encapsulated by the + interface). +

    +
    + + The to be added. + + + +
    + + + Adds the supplied to the supplied + in the advice (interceptor) chain. + + +

    + Please be aware that Spring.NET's AOP implementation only supports + method advice (as encapsulated by the + interface). +

    +
    + + The zero (0) indexed position (from the head) at which the + supplied is to be inserted into the + advice (interceptor) chain. + + + The to be added. + + + +
    + + + Is the supplied (interface) + proxied? + + + The interface to test. + + + if the supplied + (interface) is proxied; + if not or the supplied + is . + + + + + Adds the advisors from the supplied + to the list of . + + + The to add advisors from. + + + If this proxy configuration is frozen and the + cannot be added. + + + + + Adds the supplied to the list + of . + + + The to add. + + + If this proxy configuration is frozen and the + cannot be added. + + + + + Adds the supplied to the list + of . + + + The index in the + list at which the supplied + is to be inserted. + + + The to add. + + + If this proxy configuration is frozen and the + cannot be added. + + + + + Adds the supplied to the list + of . + + + The to add. + + + If this proxy configuration is frozen and the + cannot be added. + + + + + Adds the supplied to the list + of . + + + The index in the + list at which the supplied + is to be inserted. + + + The to add. + + + If this proxy configuration is frozen and the + cannot be added. + + + + + Return the index (0 based) of the supplied + in the interceptor + (advice) chain for this proxy. + + +

    + The return value of this method can be used to index into + the + list. +

    +
    + + The to search for. + + + The zero (0) based index of this advisor, or -1 if the + supplied is not an advisor for this + proxy. + +
    + + + Return the index (0 based) of the supplied + in the introductions + for this proxy. + + +

    + The return value of this method can be used to index into + the + list. +

    +
    + + The to search for. + + + The zero (0) based index of this advisor, or -1 if the + supplied is not an introduction advisor + for this proxy. + +
    + + + Removes the supplied the list of advisors + for this proxy. + + The advisor to remove. + + if advisor was found in the list of + for this + proxy and was successfully removed; if not + or if the supplied is . + + + If this proxy configuration is frozen and the + cannot be removed. + + + + + Removes the at the supplied + in the + list + from the list of + for this proxy. + + + The index of the to remove. + + + If this proxy configuration is frozen and the + at the supplied + cannot be removed; or if the supplied is out of + range. + + + + + Removes the supplied from the list + of . + + + The to remove. + + + if the supplied was + found in the list of + and successfully removed. + + + If this proxy configuration is frozen and the + cannot be removed. + + + + + Removes the supplied from the list + of . + + + The to remove. + + + if the supplied was + found in the list of + and successfully removed. + + + If this proxy configuration is frozen and the + cannot be removed. + + + + + Removes the at the supplied + in the list of + for this proxy. + + The index of the advisor to remove. + + + If this proxy configuration is frozen and the + at the supplied + cannot be removed; or if the supplied + is out of range. + + + + + Replaces the that + exists at the supplied in the list of + + with the supplied . + + + The index of the + in the list of + + that is to be replaced. + + + The new (replacement) . + + + If the supplied is out of range. + + + + + Replaces the with the + . + + + The original (old) advisor to be replaced. + + + The new advisor to replace the with. + + + if the was + replaced; if the was not found in the + advisors collection, this method returns + and (effectively) does nothing. + + + If this proxy configuration is frozen and the + cannot be replaced. + + + + + + As will normally be passed + straight through to the advised target, this method returns the + equivalent for the AOP + proxy itself. + + + A description of the proxy configuration. + + + + + Should proxies obtained from this configuration expose + the AOP proxy to the + class? + + +

    + This is useful if an advised object needs to call another advised + method on itself. (If it uses the this reference (Me + in Visual Basic.NET), the invocation will not be advised). +

    +
    +
    + + + Gets the + + implementation that will be used to get the interceptor + chains for the advised + . + + + The + implementation that will be used to get the interceptor + chains for the advised + . + + + + + Is the target to be proxied in addition + to any interfaces declared on the proxied ? + + + + + Is target type attributes, method attributes, method's return type attributes + and method's parameter attributes to be proxied in addition + to any interfaces declared on the proxied ? + + + + + Returns the collection of + instances that have been applied to this proxy. + + +

    + Will never return , but may return an + empty array (in the case where no + instances have been applied to + this proxy). +

    +
    + + The collection of + instances that have been applied to this proxy. + +
    + + + Returns the collection of + instances that have been applied to this proxy. + + +

    + Will never return , but may return an + empty array (in the case where no + instances have been + applied to this proxy). +

    +
    + + The collection of + instances that have been applied to this proxy. + +
    + + + Returns the collection of interface s + to be (or that are being) proxied by this proxy. + + + The collection of interface s + to be (or that are being) proxied by this proxy. + + + + + Returns the mapping of the proxied interface + s to their delegates. + + + The mapping of the proxied interface + s to their delegates. + + + + + Is this configuration frozen? + + +

    + When a config is frozen, no advice changes can be made. This is + useful for optimization, and useful when we don't want callers + to be able to manipulate configuration after casting to + . +

    +
    +
    + + + Returns the used by this + object. + + + The used by this + object. + + + + + Returns a boolean specifying if this + instance can be serialized. + + + true if this instance can be serialized, false otherwise. + + + + + Should we use dynamic reflection for method invocation ? + + + + + Optimization fields + + + + + IAdvised delegate + + + + + Array of introduction delegates + + + + + Target source wrapper + + + + + Type of target object. + + + + + Creates a new instance of the class. + + + + + Creates a new instance of the class. + + + + + Creates a new instance of the class. + + The proxy configuration. + + + + Creates a new instance of the + class. + + The proxy configuration. + The proxy. + + + + Deserialization constructor. + + Serialization data. + Serialization context. + + + + Serializes this instance. + + Serialization data. + Serialization context. + + + + Initialization method. + + The proxy configuration. + + The current implementation. + + + + + Invokes intercepted methods using reflection + + proxy object + target object to invoke method on + target type + taget method to invoke + The method to invoke on proxy. + method arguments + interceptor chain + value returned by invocation chain + + + + Returns a list of method interceptors + + target type + target method + list of inteceptors for the specified method + + + + Adds the supplied to the end (or tail) + of the advice (interceptor) chain. + + + The to be added. + + + + + + + Adds the supplied to the supplied + in the advice (interceptor) chain. + + + The zero (0) indexed position (from the head) at which the + supplied is to be inserted into the + advice (interceptor) chain. + + + The to be added. + + + + + + + Gets the target type behind the implementing object. + Ttypically a proxy configuration or an actual proxy. + + The type of the target or null if not known. + + + + Base class for proxy builders that can be used + to create an AOP proxy for any object. + + Bruno Baia + $Id: AbstractAopProxyTypeBuilder.cs,v 1.5 2007/12/03 09:06:58 bbaia Exp $ + + + + Describes the operations that generates IL instructions + used to build the Aop proxy type. + + Bruno Baia + $Id: IAopProxyTypeGenerator.cs,v 1.2 2006/11/05 20:36:59 bbaia Exp $ + + + + Generates the IL instructions that pushes + the current + instance on stack. + + The IL generator to use. + + + + Generates the IL instructions that pushes + the target instance on which calls should be delegated to. + + The IL generator to use. + + + + Generates the IL instructions that pushes + the current + instance on stack. + + The IL generator to use. + + + + Calculates and returns the list of attributes that apply to the + specified type. + + + Removes from the list. + + The type to find attributes for. + + A list of custom attributes that should be applied to type. + + + + + Object Auto Proxy Creator + + + + Auto proxy creator that identifies objects to proxy via a list of names. + Checks for direct, "xxx*", "*xxx" and "*xxx*" matches. + + In case of a IFactoryObject, only the objects created by the + FactoryBean will get proxied. If you intend to proxy a IFactoryObject instance itself + specify the object name of the IFactoryObject including + the factory-object prefix "&" e.g. "&MyFactoryObject". + + + + Juergen Hoeller + Adhari C Mahendra (.NET) + $Id: ObjectNameAutoProxyCreator.cs,v 1.8 2008/03/03 09:28:49 bbaia Exp $ + + + + ObjectPostProcessor implementation that wraps a group of objects with AOP proxies + that delegate to the given interceptors before invoking the object itself. + + +

    This class distinguishes between "common" interceptors: shared for all proxies it + creates, and "specific" interceptors: unique per object instance. There need not + be any common interceptors. If there are, they are set using the interceptorNames + property. As with ProxyFactoryObject, interceptors names in the current factory + are used rather than object references to allow correct handling of prototype + advisors and interceptors: for example, to support stateful mixins. + Any advice type is supported for "interceptorNames" entries.

    +

    Such autoproxying is particularly useful if there's a large number of objects that need + to be wrapped with similar proxies, i.e. delegating to the same interceptors. + Instead of x repetitive proxy definitions for x target objects, you can register + one single such post processor with the object factory to achieve the same effect.

    +

    Subclasses can apply any strategy to decide if a object is to be proxied, + e.g. by type, by name, by definition details, etc. They can also return + additional interceptors that should just be applied to the specific object + instance. The default concrete implementation is ObjectNameAutoProxyCreator, + identifying the objects to be proxied via a list of object names.

    +

    Any number of TargetSourceCreator implementations can be used with any subclass, + to create a custom target source - for example, to pool prototype objects. + Autoproxying will occur even if there is no advice if a TargetSourceCreator specifies + a custom TargetSource. If there are no TargetSourceCreators set, or if none matches, + a SingletonTargetSource will be used by default to wrap the object to be autoproxied.

    +
    + Juergen Hoeller + Rod Johnson + Adhari C Mahendra (.NET) + + + $Id: AbstractAutoProxyCreator.cs,v 1.15 2008/03/03 09:28:49 bbaia Exp $ +
    + + + Convenience superclass for configuration used in creating proxies, + to ensure that all proxy creators have consistent properties. + + +

    + Note that it is no longer possible to configure subclasses to + expose the . + Interceptors should normally manage their own thread locals if they + need to make resources available to advised objects. If it is + absolutely necessary to expose the + , use an + interceptor to do so. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: ProxyConfig.cs,v 1.13 2007/09/07 01:51:49 markpollack Exp $ +
    + + + Copies the configuration from the supplied + into this instance. + + + The configuration to be copied. + + + If the supplied is + . + + + + + A that represents the current + configuration. + + + A that represents the current + configuration. + + + + + Use to synchronize access to this ProxyConfig instance + + + + + Is the target to be proxied in addition + to any interfaces declared on the proxied ? + + + + + Is target type attributes, method attributes, method's return type attributes + and method's parameter attributes to be proxied in addition + to any interfaces declared on the proxied ? + + + + + Are any agressive optimizations to be performed? + + +

    + The exact meaning of agressive optimizations will differ + between proxies, but there is usually some tradeoff. +

    +

    + For example, optimization will usually mean that advice changes + won't take effect after a proxy has been created. For this reason, + optimization is disabled by default. An optimize value of + may be ignored if other settings preclude + optimization: for example, if the + property + is set to and such a value is not compatible + with the optimization. +

    +

    + The default is . +

    +
    +
    + + + Should proxies obtained from this configuration expose + the AOP proxy to the + class? + + +

    + The default is , as enabling this property + may impair performance. +

    +
    +
    + + + Gets and set the factory to be used to create AOP proxies. + + +

    + This obviously allows one to customise the + implementation, + allowing different strategies to be dropped in without changing the + core framework. For example, an + implementation + could return an + using remoting proxies, Reflection.Emit or a code generation + strategy. +

    +
    +
    + + + Is this configuration frozen? + + +

    + The default is not frozen. +

    +
    +
    + + + The logger for this class hierarchy. + + + + + Convenience constant for subclasses: Return value for "do not proxy". + + + + + Convenience constant for subclasses: Return value for + "proxy without additional interceptors, just the common ones". + + + + + Default value is same as non-ordered + + + + + Default is global AdvisorAdapterRegistry + + + + + + + + + + Names of common interceptors. + We must use object name rather than object references + to handle prototype advisors/interceptors. + Default is the empty array: no common interceptors. + + + + + Set of object type + name strings, referring to all objects that this auto-proxy + creator created a custom TargetSource for. Used to detect own pre-built proxies + (from "PostProcessBeforeInstantiation") in the "PostProcessAfterInitialization" method. + + + + + Create a proxy with the configured interceptors if the object is + identified as one to proxy by the subclass. + + + + + No-op for before initialization. + + The obj. + The name. + + + + + Subclasses should override this method to return true if this + object should not be considered for autoproxying by this post processor. + Sometimes we need to be able to avoid this happening if it will lead to + a circular reference. This implementation returns true. + + the type of the object + the name of the object + if remarkable to skip + + + + Subclasses may choose to implement this: for example, + to change the interfaces exposed + + + ProxyFactory that will be used to create the proxy immediably after this method returns. + + + + + Determines whether the object is an infrastructure type, + IAdvisor, IAdvice, IAdvisors or AbstractAutoProxyCreator + + The object type to compare + The name of the object + + true if [is infrastructure type] [the specified obj]; otherwise, false. + + + + + Create a target source for object instances. Uses any + TargetSourceCreators if set. Returns null if no Custom TargetSource + should be used. + This implementation uses the customTargetSourceCreators property. + Subclasses can override this method to use a different mechanism. + + the type of the object to create a TargetSource for + the name of the object + a TargetSource for this object + + + + Return whether the given object is to be proxied, what additional + advices (e.g. AOP Alliance interceptors) and advisors to apply. + + +

    The previous name of this method was "GetInterceptorAndAdvisorForObject". + It has been renamed in the course of general terminology clarification + in Spring 1.1. An AOP Alliance Interceptor is just a special form of + Advice, so the generic Advice term is preferred now.

    +

    The third parameter, customTargetSource, is new in Spring 1.1; + add it to existing implementations of this method.

    +
    + the new object instance + the name of the object + targetSource returned by TargetSource property: + may be ignored. Will be null unless a custom target source is in use. + an array of additional interceptors for the particular object; + or an empty array if no additional interceptors but just the common ones; + or null if no proxy at all, not even with the common interceptors. +
    + + + Create an AOP proxy for the given object. + + Type of the object. + The name of the object. + The set of interceptors that is specific to this + object (may be empty but not null) + The target source for the proxy, already pre-configured to access the object. + The AOP Proxy for the object. + + + + Obtain a new proxy factory instance to be used for proxying a particular object + + A proxy factory instance for proxying a particular object + + + + Determines the advisors for the given object, including the specific interceptors + as well as the common interceptor, all adapted to the Advisor interface. + + The name of the object. + The set of interceptors that is specific to this + object (may be empty, but not null) + The list of Advisors for the given object + + + + Build a cache key for the given object type and object name + + The object type. + The object name. + The cache key for the given type and name + + + + Create the proxy if have a custom TargetSource + + The object type + The object name + null if not creating a proxy, otherwise return the proxy. + + + + Default behavior, return true and continue processing. + + The object instance + The object name. + true + + + + Default behavior, return passed in PropertyValues + + The property values that the factory is about to apply (never null). + he relevant property infos for the target object (with ignored + dependency types - which the factory handles specifically - already filtered out) + The object instance created, but whose properties have not yet + been set. + Name of the object. + The passed in PropertyValues + + + + Sets the AdvisorAdapterRegistry to use. + + + Default is the global AdvisorAdapterRegistry. + + + + + Sets custom TargetSourceCreators to be applied in this order. + + + + If the list is empty, or they all return null, a SingletonTargetSource + will be created. + + + TargetSourceCreators can only be invoked if this post processor is used + in a IObjectFactory, and its ObjectFactoryAware callback is used. + + + + + + Sets the common interceptors, a list of , + and introduction object names. + + + + If this property isn't set, there will be zero common interceptors. + This is perfectly valid, if "specific" interceptors such as + matching Advisors are all we want. + + + + The list of , + and introduction object names. + + + + + + + Sets whether the common interceptors should be applied before + object-specific ones. + + + Default is true; else, object-specific interceptors will get applied first. + + + + + Set whether or not the proxy should be frozen, preventing advice + from being added to it once it is created. + + +

    Overridden from the super class to prevent the proxy configuration + from being frozen before the proxy is created. The default is not frozen. +

    +
    +
    + + + Callback that supplies the owning factory to an object instance. + + + Owning + (may not be ). The object can immediately + call methods on the factory. + + +

    + Invoked after population of normal object properties but before an init + callback like 's + + method or a custom init-method. +

    +
    + + In case of initialization errors. + +
    + + + Propery Order + + + Ordering which will apply to this class's implementation + of Ordered, used when applying multiple ObjectPostProcessors. + Default value is int.MaxValue, meaning that it's non-ordered. + + + + + Identify as object to proxy if the object name is in the configured list of names. + + + + + Return if the given object name matches the mapped name. + + +

    + The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, + as well as direct equality. Can be overridden in subclasses. +

    +
    + the object name to check + the name in the configured list of names + if the names match +
    + + + Set the names of the objects in IList fashioned way that should automatically + get wrapped with proxies. + A name can specify a prefix to match by ending with "*", e.g. "myObject,tx*" + will match the object named "myObject" and all objects whose name start with "tx". + + + + + The for the <aop:config> tag. + + Mark Pollack (.NET) + $Id: ConfigObjectDefinitionParser.cs,v 1.3 2008/02/20 14:29:13 bbaia Exp $ + + + + The 'proxy-target-type' attribute + + + + + Parse the specified XmlElement and register the resulting + ObjectDefinitions with the IObjectDefinitionRegistry + embedded in the supplied + + The element to be parsed. + The object encapsulating the current state of the parsing process. + Provides access to a IObjectDefinitionRegistry + The primary object definition. + +

    + This method is never invoked if the parser is namespace aware + and was called to process the root node. +

    +
    +
    + + + Parses the supplied advisor element and registers the resulting + + The advisor element. + The parser context. + + + + Utility class for handling registration of auto-proxy creators used internally by the + aop namespace tags. + + Rob Harrop + Juergen Hoeller + Mark Pollack (.NET) + $Id: AopNamespaceUtils.cs,v 1.4 2007/05/26 19:25:48 markpollack Exp $ + + + + The object name of the internally managed auto-proxy creator. + + + + + Registers the auto proxy creator if necessary. + + The parser context. + The source element. + + + + Registries the or escalate apc as required. + + The type. + The parser context. + + + + Forces the auto proxy creator to use decorator proxy. + + The registry. + + + + A description of an invocation to a constuctor, given to an interceptor + upon constructor-call. + + +

    + A constructor invocation is a joinpoint and can be intercepted by a + constructor interceptor. +

    +
    + +
    + + + Represents an invocation in the program. + + +

    + An invocation is a joinpoint and can be intercepted by an interceptor. + Typical examples would be a constructor invocation and a method call. +

    +
    +
    + + + Represents a generic runtime joinpoint (in the AOP terminology). + + +

    + A runtime joinpoint is an event that occurs on a static + joinpoint (i.e. a location in a program). For instance, an + invocation is the runtime joinpoint on a method (static joinpoint). + The static part of a given joinpoint can be generically retrieved + using the + property. +

    +

    + In the context of an interception framework, a runtime joinpoint + is then the reification of an access to an accessible object (a + method, a constructor, a field), i.e. the static part of the + joinpoint. It is passed to the interceptors that are installed on + the static joinpoint. +

    +
    + +
    + + + Proceeds to the next interceptor in the chain. + + +

    + The implementation and semantics of this method depend on the + actual joinpoint type. Consult the derived interfaces of this + interface for specifics. +

    +
    + + Consult the derived interfaces of this interface for specifics. + + + If any of the interceptors at the joinpoint throws an exception. + +
    + + + Gets the static part of this joinpoint. + + +

    + The static part is an accessible object on which a chain of + interceptors are installed. +

    +
    + + The static part of this joinpoint. + +
    + + + Gets the object that holds the current joinpoint's static part. + + +

    + For instance, the target object for a method invocation. +

    +
    + + The object that holds the current joinpoint's static part. + +
    + + + Gets the arguments to an invocation. + + +

    + It is of course possible to change element values within this array + to change the arguments to an intercepted invocation. +

    +
    + + The arguments to an invocation. + +
    + + + Gets the constructor invocation that is to be invoked. + + +

    + This property is a friendly implementation of the + property. + It should be used in preference to the + property + because it provides immediate access to the underlying constructor + without the need to resort to a cast. +

    +
    + + The constructor invocation that is to be invoked. + +
    + + + Canonical instances. + + +

    + Only one canonical instance is + provided out of the box. The + matches all classes. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: TrueTypeFilter.cs,v 1.4 2006/04/09 07:18:36 markpollack Exp $ +
    + + + A filter that restricts the matching of a pointcut or introduction to + a given set of target types. + + +

    + Can be used as part of a pointcut, or for the entire targeting of an + introduction. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + + + $Id: ITypeFilter.cs,v 1.2 2006/04/09 07:18:36 markpollack Exp $ +
    + + + Should the pointcut apply to the supplied + ? + + + The candidate . + + + if the advice should apply to the supplied + + + + + + Canonical instance that + matches all classes. + + + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such has no publicly visible + constructors. +

    +
    +
    + + + Should the pointcut apply to the supplied + ? + + + The candidate . + + + if the advice should apply to the supplied + + + + + + + A that represents the current + . + + + A that represents the current + . + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + implementation that uses a + threading model in which every thread has its own copy of the target. + + +

    + Alternative to an object pool. +

    +

    + Application code is written as to a normal pool; callers can't assume + they will be dealing with the same instance in invocations in different + threads. However, state can be relied on during the operations of a + single thread: for example, if one caller makes repeated calls on the + AOP proxy. +

    +

    + This class act both as an introduction and as an interceptor, so it + should be added twice, once as an introduction and once as an + interceptor. +

    +
    + Rod Johnson + Federico Spinazzi (.NET) + $Id: ThreadLocalTargetSource.cs,v 1.8 2006/04/09 07:18:37 markpollack Exp $ +
    + + + Base class for dynamic + implementations that can create new prototype object instances to + support a pooling or new-instance-per-invocation strategy. + + +

    + All such s must run in an + , as they need to + call the + method to create a new prototype instance. +

    +
    + Rod Johnson + Federico Spinazzi (.NET) + $Id: AbstractPrototypeTargetSource.cs,v 1.11 2007/07/28 07:32:52 markpollack Exp $ +
    + + + Used to obtain the current "target" of an AOP invocation + + +

    + This target will be invoked via reflection if no around advice chooses + to end the interceptor chain itself. +

    +

    + If an is "static", it + will always return the same target, allowing optimizations in the AOP + framework. Dynamic target sources can support pooling, hot swapping etc. +

    +

    + Application developers don't usually need to work with target sources + directly: this is an AOP framework interface. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: ITargetSource.cs,v 1.9 2007/10/10 18:07:38 markpollack Exp $ +
    + + + Returns the target object. + + The target object. + + If unable to obtain the target object. + + + + + Releases the target object. + + The target object to release. + + + + The of the target object. + + + + + Is the target source static? + + + if the target source is static. + + + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    +
    + + + Subclasses should use this method to create a new prototype instance. + + + + + Returns the target object. + + The target object. + + If unable to obtain the target object. + + + + + Releases the target object. + + The target object to release. + + + + Invoked by an + after it has set all object properties supplied + (and satisfied the + + and + interfaces). + + +

    + Ensures that the property has been + set to a valid value (i.e. is not or a string + that consists solely of whitespace). +

    +
    + + In the event of misconfiguration (such as failure to set an essential + property) or if initialization fails. + + +
    + + + The shared instance for this class (and derived classes). + + + + + The name of the target object to be created on each invocation. + + +

    + This object should be a prototype, or the same instance will always + be obtained from the owning . +

    +
    +
    + + + The of the target object. + + + + + Is the target source static? + + + if the target source is static. + + + + + The target factory that will be used to perform the lookup + of the object referred to by the + property. + + +

    + Needed so that prototype instances can be created as necessary. +

    +
    + + The owning + (will never be ). + + + In case of initialization errors. + + +
    + + + Statistics for a thread local . + + Rod Johnson + Federico Spinazzi (.NET) + $Id: IThreadLocalTargetSourceStats.cs,v 1.3 2006/04/09 07:18:37 markpollack Exp $ + + + + Gets the number of invocations of the + and + methods. + + + The number of invocations of the + and + methods. + + + + + Gets the number of hits that were satisfied by a thread bound object. + + + The number of hits that were satisfied by a thread bound object. + + + + + Gets the number of thread bound objects created. + + The number of thread bound objects created. + + + + Intercepts calls on an interface on its way to the target. + + +

    + Such interceptions are nested "on top" of the target. +

    +
    +
    + + + Represents a generic interceptor. + + +

    + A generic interceptor can intercept runtime events that occur within a + base program. Those events are materialized by (reified in) joinpoints. + Runtime joinpoints can be invocations, field access, exceptions, etc. +

    +

    + This interface is not used directly. Use the various derived interfaces + to intercept specific events. +

    +
    + +
    + + + Tag interface for advice. + + +

    + Implementations can be any type of advice, such as interceptors. +

    +
    +
    + + + Implement this method to perform extra treatments before and after + the call to the supplied . + + +

    + Polite implementations would certainly like to invoke + . +

    +
    + + The method invocation that is being intercepted. + + + The result of the call to the + method of + the supplied ; this return value may + well have been intercepted by the interceptor. + + + If any of the interceptors in the chain or the target object itself + throws an exception. + +
    + + + ThreadLocal holding the target associated with the current thread. + + +

    + Unlike most thread local storage which is static, this variable is + meant to be per thread per instance of this class. +

    +
    +
    + + + The set of managed targets, enabling us to keep track of the + targets we've created. + + + + + Returns the target object. + + +

    + Tries to locate the target from thread local storage. If no target + is found, a target will be obtained and bound to the thread. +

    +
    + The target object. + + If unable to obtain the target object. + + +
    + + + Return an introduction advisor mixin that allows the AOP proxy to be + cast to an reference. + + + + + Cleans up this instance's thread storage, and disposes of any + targets as necessary. + + + + + + Increments Invocations and Hits statistics + + The method invocation joinpoint + The result of the call to IJoinpoint.Proceed(), might be intercepted by the interceptor. + if the interceptors or the target-object throws an exception. + + + + Gets the number of invocations of the and + methods. + + + The number of invocations of the and + methods. + + + + + Gets the number of hits that were satisfied by a thread bound object. + + + The number of hits that were satisfied by a thread bound object. + + + + + Gets the number of thread bound objects created. + + The number of thread bound objects created. + + + + implementation that holds a local + object. + + +

    + This is the default implementation of the + interface used by the AOP + framework. There should be no need to create objects of this class in + application code. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: SingletonTargetSource.cs,v 1.8 2008/03/21 14:11:57 markpollack Exp $ +
    + + + Creates a new instance of the + + for the specified target object. + + The target object to expose. + + If the supplied is + . + + + + + Returns the target object. + + The target object. + + If unable to obtain the target object. + + + + + Releases the target object. + + +

    + No-op implementation. +

    +
    + The target object to release. +
    + + + Returns a stringified representation of this target source. + + + A stringified representation of this target source. + + + + + Determines whether the specified + is equal to the current . + + The target source to compare with. + + if this instance is equal to the + specified . + + + + + Serves as a hash function for a particular type, suitable for use + in hashing algorithms and data structures like a hash table. + + + A hash code for the current . + + + + + The of the target object. + + + + + Is the target source static? + + + because this target source is always static. + + + + + Simple implementation that matches + all classes classes (and any derived subclasses) of a give root + . + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: RootTypeFilter.cs,v 1.3 2007/03/16 04:01:25 aseovic Exp $ + + + + Creates a new instance of the + for the supplied + . + + The root . + + If the supplied is . + + + + + Should the pointcut apply to the supplied + ? + + +

    + Returns if the supplied + can be assigned to the root . +

    +
    + + The candidate . + + + if the advice should apply to the supplied + + +
    + + + Factory for AOP proxies for programmatic use, rather than via a + Spring.NET IoC container. + + +

    + This class provides a simple way of obtaining and configuring AOP + proxies in code. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: ProxyFactory.cs,v 1.8 2007/03/16 04:01:19 aseovic Exp $ +
    + + + Superclass for AOP proxy configuration managers. + + +

    + Instances of this class are not themselves AOP proxies, but + subclasses of this class are normally factories from which AOP proxy + instances are obtained directly. +

    +

    + This class frees subclasses of the housekeeping of + and + instances, but doesn't actually + implement proxy creation methods, the functionality for which + is provided by subclasses. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: AdvisedSupport.cs,v 1.36 2008/01/14 20:49:47 oakinger Exp $ + +
    + + The list of advice. + +

    + If an is added, it + will be wrapped in an advice before being added to this list. +

    +
    +
    + + + Array updated on changes to the advisors list, which is easier to + manipulate internally + + + + + List of introductions. + + + + + Array updated on changes to the advisors list, which is easier to + manipulate internally + + + + + Interface map specifying which object should interface methods be + delegated to. + + +

    + If entry value is methods should be delegated + to the target object. +

    +
    +
    + + + The for this instance. + + + + + Set to when the first AOP proxy has been + created, meaning that we must track advice changes via the + OnAdviceChange() callback. + + + + + The list of event listeners. + + + + + The advisor chain factory. + + + + + Creates a new instance of the + class using the + default advisor chain factory. + + + + + Creates a new instance of the + class. + + The interfaces that are to be proxied. + + If this + + + + + Is the supplied (interface) + proxied? + + + The interface to test. + + + if the supplied + (interface) is proxied; + if not or the supplied + is . + + + + + + Adds the supplied to the end (or tail) + of the advice (interceptor) chain. + + + The to be added. + + + + + + + Adds the supplied to the supplied + in the advice (interceptor) chain. + + + The zero (0) indexed position (from the head) at which the + supplied is to be inserted into the + advice (interceptor) chain. + + + The to be added. + + + If the supplied is ; + or is not an + reference; or if the supplied is a + . + + + + + + + Return the index (0 based) of the supplied + in the interceptor + (advice) chain for this proxy. + + + The to search for. + + + The zero (0) based index of this advisor, or -1 if the + supplied is not an advisor for this + proxy. + + + + + Return the index (0 based) of the supplied + in the introductions + for this proxy. + + + The to search for. + + + The zero (0) based index of this advisor, or -1 if the + supplied is not an introduction advisor + for this proxy. + + + + + Removes the supplied the list of advisors + for this proxy. + + The advisor to remove. + + if advisor was found in the list of + for this + proxy and was successfully removed; if not + or if the supplied is . + + + If this proxy configuration is frozen and the + cannot be removed. + + + + + Removes the at the supplied + in the + list + from the list of + for this proxy. + + + The index of the to remove. + + + If this proxy configuration is frozen and the + at the supplied + cannot be removed; or if the supplied is out of + range. + + + + + Removes the supplied from the list + of . + + + The to remove. + + + if the supplied was + found in the list of + and successfully removed. + + + If this proxy configuration is frozen and the + cannot be removed. + + + + + Removes the supplied from the list + of . + + + The to remove. + + + if the supplied was + found in the list of + and successfully removed. + + + If this proxy configuration is frozen and the + cannot be removed. + + + + + Removes the at the supplied + in the list of + for this proxy. + + The index of the advisor to remove. + + + If this proxy configuration is frozen and the + at the supplied + cannot be removed; or if the supplied + is out of range. + + + + + Adds the supplied to the list + of . + + + The index in the + list at which the supplied + is to be inserted. If -1, appends to the end of the list. + + + The to add. + + + If this proxy configuration is frozen and the + cannot be added. + + + + + Adds the supplied to the list + of . + + + The to add. + + + If this proxy configuration is frozen and the + cannot be added. + + + + + Adds the advisors from the supplied + to the list of . + + + The to add advisors from. + + + If this proxy configuration is frozen and the + cannot be added. + + + + + Adds the supplied to the list + of . + + + The index in the + list at which the supplied + is to be inserted. + + + The to add. + + + If this proxy configuration is frozen and the + cannot be added. + + + + + Adds the supplied to the list + of . + + + The to add. + + + If this proxy configuration is frozen and the + cannot be added. + + + + + Replaces the that + exists at the supplied in the list of + + with the supplied . + + + The index of the + in the list of + + that is to be replaced. + + + The new (replacement) . + + + If the supplied is out of range. + + + + + Replaces the with the + . + + + The original (old) advisor to be replaced. + + + The new advisor to replace the with. + + + if the was + replaced; if the was not found in the + advisors collection (or the is + , this method returns + and (effectively) does nothing. + + + If this proxy configuration is frozen and the + cannot be replaced. + + + + + + As will normally be passed straight through + to the advised target, this method returns the + equivalent for the AOP proxy itself. + + + A description of the proxy configuration. + + + + + Registers the supplied as a listener for + notifications. + + + The to + register. + + + + + Removes the supplied . + + + The to + be removed. + + + + + Adds a new interface to the list of interfaces that are proxied by this proxy. + + + The interface to be proxied by this proxy. + + + If this proxy configuration is frozen + (); + + + If the supplied is . + + + + + Adds a new interface to the list of interfaces that are proxied by this proxy. + + + The interface to be proxied by this proxy. + + + Access is not synchronized. + + + + + Removes the supplied (proxied) . + + +

    + Does nothing if the supplied (proxied) + isn't proxied. +

    +
    + The interface to remove. + + if the interface was removed. +
    + + + Return the index (0 based) of the supplied + in the interceptor + (advice) chain for this proxy. + + +

    + The return value of this method can be used to index into + the + list. +

    +
    + + The to search for. + + + The zero (0) based index of this interceptor, or -1 if the + supplied is not an advice for this + proxy. + +
    + + + Return the index (0 based) of the supplied + in the interceptor + (advice) chain for this proxy. + + +

    Acces is not synchronized

    +

    + The return value of this method can be used to index into + the + list. +

    +
    + + The to search for. + + + The zero (0) based index of this interceptor, or -1 if the + supplied is not an advice for this + proxy. + +
    + + + Return the index (0 based) of the supplied + in the interceptor + (advice) chain for this proxy. + + + The to search for. + + + The zero (0) based index of this advisor, or -1 if the + supplied is not an advisor for this + proxy. + + + Access is not synchronized. + + + + + Return the index (0 based) of the supplied + in the introductions + for this proxy. + + + The to search for. + + + The zero (0) based index of this advisor, or -1 if the + supplied is not an introduction advisor + for this proxy. + + + Access is not synchronized + + + + + Removes the at the supplied + in the + list + from the list of + for this proxy. + + + The index of the to remove. + + + If this proxy configuration is frozen and the + at the supplied + cannot be removed; or if the supplied is out of + range. + + + Does not synchronize access. + + + + + Is the supplied included in any + advisor? + + + The to check for the + inclusion of. + + + if the supplied + could be run in an invocation (this does not imply that said + will be run). + + + + + Returns a count of all of the + type-compatible with the supplied . + + + The of the + to check. + + + A count of all of the + type-compatible with the supplied . + + + + + Throws an if + this instances proxy configuration data is frozen. + + + The message that will be passed through to the constructor of any + thrown . + + + If the configuration for this proxy is frozen. + + + + + + Bring the advisors array up to date with the list. + + + + + Bring the introductions array up to date with the list. + + + + + Callback method that is invoked when the list of proxied interfaces + has changed. + + +

    + An example of such a change would be when a new introduction is + added. Resetting + to + will cause a new proxy + to be generated on the next call to get a proxy. +

    +
    +
    + + + Callback method that is invoked when the interceptor list has changed. + + + + + Activates this instance. + + + + + Creates an AOP proxy using this instance's configuration data. + + +

    + Subclasses must not create a proxy by any other means (at least + without having a well thought out and cogent reason for doing so). + This is because the implementation of this method performs some + required housekeeping logic prior to creating an AOP proxy. +

    +
    + +
    + + + Copies the configuration from the supplied other + into this instance. + + +

    + Useful when this instance has been created using the no-argument + constructor, and needs to get all of its confiuration data from + another (most + usually to have an independant copy of said configuration data). +

    +
    + + The instance + containing the configiration data that is to be copied into this + instance. + +
    + + + A that represents the current + configuration. + + + A that represents the current + configuration. + + + + + A that represents the current + configuration. + + + A that represents the current + configuration. + + + + + Helper method that adds the names of all of the proxied interfaces + to the buffer of the supplied . + + + The to append the proxied interface + names to. + + + + + Helper method that adds advisor's + to the buffer of the supplied . + + + The to append the advisor details to. + + + + + Gets all of the interfaces implemented by the + of the supplied + . + + + The object to get the interfaces of. + + + All of the interfaces implemented by the + of the supplied + . + + + If the supplied is . + + + + + Gets and sets the + + implementation that will be used to get the interceptor + chains for the advised + . + + + The + implementation that will be used to get the interceptor + chains for the advised + . + + + + + Returns the current used + by this object. + + + The used by this + object. + + + + + + Returns a boolean specifying if this + instance can be serialized. + + + true if this instance can be serialized, false otherwise. + + + + + Returns the collection of interface s + to be (or that are being) proxied by this proxy. + + + The collection of interface s + to be (or that are being) proxied by this proxy. + + + + + + Returns the mapping of the proxied interface + s to their delegates. + + + The mapping of the proxied interface + s to their delegates. + + + + + + Returns the collection of + instances that have been applied to this proxy. + + + The collection of + instances that have been applied to this proxy. + + + + + + Returns the collection of + instances that have been applied to this proxy. + + +

    + Will never return , but may return an + empty array (in the case where no + instances have been + applied to this proxy). +

    +
    + + The collection of + instances that have been applied to this proxy. + + +
    + + + Gets the target type behind the implementing object. + Ttypically a proxy configuration or an actual proxy. + + The type of the target or null if not known. + + + + Sets the target object that is to be advised. + + +

    + This is a convenience write-only property that allows client code + to set the target object... the target object will be implicitly + wrapped within a new + instance. +

    +
    +
    + + + Called by subclasses to get a value indicating whether any AOP proxies have been created yet. + + true if this AOp proxies have been created; otherwise, false. + + + + Specifies the of proxies that are to be + created for this instance of proxy config. + + +

    + If this property value is it simply means that + no proxies have been created yet. Only when the first proxy is + created will this property value be set by the AOP framework. +

    +

    + Users will be able to add interceptors dynamically without proxy + regeneration, but if they add introductions the proxy + will have to be regenerated. +

    +
    + + The of proxies that are to be + created for this instance of proxy config; if + no proxies have been created yet. + +
    + + + Caches proxy constructor for performance reasons. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class that proxys all of the interfaces exposed by the supplied + . + + The object to proxy. + + If the is . + + + + + Creates a new instance of the + class that has no target object, only interfaces. + + +

    + Interceptors must be added if this factory is to do anything useful. +

    +
    + The interfaces to implement. +
    + + + Creates a new proxy according to the settings in this factory. + + +

    + Can be called repeatedly; the effect of repeated invocations will + (of course) vary if interfaces have been added or removed. +

    +
    + An AOP proxy for target object. +
    + + + Creates a new proxy for the supplied + and . + + +

    + This is a convenience method for creating a proxy for a single + interceptor. +

    +
    + + The interface that the proxy must implement. + + + The interceptor that the proxy must invoke. + + + A new AOP proxy for the supplied + and . + +
    + + Internal framework class. + This class is required because if we put an interceptor that implements IInterceptionAdvice + in the interceptor list passed to MethodInvocation, it may be mistaken for an + advice that requires dynamic method matching. + + Rod Johnson + Aleksandar Seovic (.Net) + $Id: InterceptorAndDynamicMethodMatcher.cs,v 1.3 2007/03/16 04:01:18 aseovic Exp $ + + + + implementation + that delegates method calls to introduction object. + + Aleksandar Seovic + Bruno Baia + $Id: IntroductionProxyMethodBuilder.cs,v 1.5 2006/11/05 20:36:59 bbaia Exp $ + + + + Base class for AOP method builders that contains common functionalities. + + Aleksandar Seovic + Bruno Baia + $Id: AbstractAopProxyMethodBuilder.cs,v 1.10 2008/05/21 08:04:34 bbaia Exp $ + + + + The implementation to use. + + + + + The dictionary to cache the list of target + s. + + + + + The dictionary to cache the list of target + s defined on the proxy. + + + + + The local variable to store the list of method interceptors. + + + + + The local variable to store the target type being proxied. + + + + + The local variable to store method arguments. + + + + + The local variable to store the return value. + + + + + The local variable to store the closed generic method + when the target method is generic. + + + + + The local variable to store the closed generic method + when the target method defined on the proxy is generic. + + + + + The field to cache the target . + + + + + The field to cache the target + defined on the proxy. + + + + + Indicates if the method returns a value. + + + + + Creates a new instance of the method builder. + + The type builder to use. + + The implementation to use. + + + if the interface is to be + implemented explicitly; otherwise . + + + The dictionary to cache the list of target + s. + + + + + Creates a new instance of the method builder. + + The type builder to use. + + The implementation to use. + + + if the interface is to be + implemented explicitly; otherwise . + + + The dictionary to cache the list of target + s. + + + The dictionary to cache the list of target + s defined on the proxy. + + + + + Generates the proxy method. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + Generates unique method id for the cache field. + + The target method. + An unique method name. + + + + Create static field that will cache target method. + + The IL generator to use. + The target method. + + + + Create static field that will cache target method when defined on the proxy. + + The IL generator to use. + The target method. + + + + Create a closed generic method for the current call + if target method is a generic definition. + + The IL generator to use. + The target method. + + The field that contains the method generic definition + + + The local variable to store the closed generic method. + + + + + Generates the IL instructions that pushes + the target type on stack. + + The IL generator to use. + + + + Generates the IL instructions that pushes + the current + instance on stack. + + The IL generator to use. + + + + Pushes the target to stack. + + The IL generator to use. + The method to proxy. + + + + Pushes the target defined on the proxy to stack. + + The IL generator to use. + The method to proxy. + + + + Creates local variable declarations. + + The IL generator to use. + The method to proxy. + + + + Initializes local variables + + The IL generator to use. + The method to proxy. + + + + Generates method logic. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + Calls method using Invoke + + The IL generator to use. + The method to proxy. + + + + Setup proxied method arguments. + + The IL generator to use. + The method to proxy. + The method's parameters. + + + + Calls proxied method directly. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + Ends method by returning return value if appropriate. + + The IL generator to use. + The method to proxy. + + + + Emits MSIL instructions to load a value of the specified + onto the evaluation stack indirectly. + + The IL generator to use. + The type of the value. + + + + Emit MSIL instructions to store a value of the specified + at a supplied address. + + The IL generator to use. + The type of the value. + + + + Emits MSIL instructions to convert the boxed representation + of the supplied to its unboxed form. + + The IL generator to use. + The type specified in the instruction. + + + + The index of the introduction to delegate call to. + + + + + Creates a new instance of the method builder. + + The type builder to use. + + The implementation to use. + + + + + index of the introduction to delegate call to + + + + Generates the IL instructions that pushes + the introduction type on stack. + + The IL generator to use. + + + + Generates the IL instructions that pushes + the introduction instance on stack. + + The IL generator to use. + + + + Calls proxied method directly. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + implementation + to enable to be used in the + Spring.NET AOP framework. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: BeforeAdviceAdapter.cs,v 1.4 2007/03/16 04:01:21 aseovic Exp $ + + + + Permits the handling of new advisors and advice types as extensions to + the Spring AOP framework. + + +

    + Implementors can create AOP Alliance + s from custom advice + types, enabling these advice types to be used in the Spring.NET AOP + framework, which uses interception under the covers. +

    +

    + There is no need for most Spring.NET users to implement this interface; + do so only if you need to introduce more + or + types to Spring.NET. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IAdvisorAdapter.cs,v 1.4 2006/04/09 07:18:35 markpollack Exp $ +
    + + + Does this adapter understand the supplied ? + + +

    + Is it valid to invoke the + + method with the given advice as an argument? +

    +
    + + such as + . + + if this adapter understands the + supplied . + +
    + + + Return an AOP Alliance + exposing the + behaviour of the given advice to an interception-based AOP + framework. + + +

    + Don't worry about any + contained in the supplied ; + the AOP framework will take care of checking the pointcut. +

    +
    + + The advice. The + + method must have previously returned on the + supplied . + + + An AOP Alliance + exposing the + behaviour of the given advice to an interception-based AOP + framework. + +
    + + + Returns if the supplied + is an instance of the + interface. + + The advice to check. + + if the supplied is + an instance of the interface; + if not or if the supplied + is . + + + + + Wraps the supplied 's + within a + + instance. + + + The advisor exposing the that + is to be wrapped. + + + The supplied 's + wrapped within a + + instance. + + + + + Interceptor to wrap an + instance. + + +

    + A more efficient alternative solution in cases where there is no + interception advice and therefore no need to create an + object may be + offered in future. +

    +

    + Used internally by the AOP framework: application developers should not need + to use this class directly. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: AfterReturningAdviceInterceptor.cs,v 1.8 2007/03/16 04:01:21 aseovic Exp $ +
    + + + Creates a new instance of the + + class. + + + The advice to be applied after a target method successfully + returns. + + + If the supplied is . + + + + + Executes interceptor after the target method successfully returns. + + + The method invocation that is being intercepted. + + + The result of the call to the + method of + the supplied ; this return value may + well have been intercepted by the interceptor. + + + If any of the interceptors in the chain or the target object itself + throws an exception. + + + + + Handles a thrown exception providing calling context. + + Mark Pollack + $Id: IExceptionHandler.cs,v 1.1 2007/10/08 22:05:16 markpollack Exp $ + + + + Determines whether this instance can handle the exception the specified exception. + + The exception. + The call context dictionary. + + true if this instance can handle the specified exception; otherwise, false. + + + + + Handles the exception. + + The call context dictionary. + + The return value from handling the exception, if not rethrown or a new exception is thrown. + + + + + Gets the source exception names. + + The source exception names. + + + + Gets the source exception types. + + The source exception types. + + + + Gets the translation expression text + + The translation expression text + + + + Gets or sets the constraint expression text. + + The constraint expression text. + + + + Gets a value indicating whether to continue processing. + + true if continue processing; otherwise, false. + + + + A union. + + +

    + Such pointcut unions are tricky, because one cannot simply OR + the respective s: one has to + ascertain that each 's + is also satisfied. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: UnionPointcut.cs,v 1.8 2007/03/16 04:01:25 aseovic Exp $ +
    + + + Spring.NET's core pointcut abstraction. + + +

    + A pointcut is composed of s and + s. Both these basic terms and an + itself can be combined to build up + sophisticated combinations. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IPointcut.cs,v 1.9 2006/04/09 07:18:36 markpollack Exp $ +
    + + + The for this pointcut. + + + The current . + + + + + The for this pointcut. + + + The current . + + + + + Creates a new instance of the + class. + + The first pointcut. + The second pointcut. + + + + The for this pointcut. + + + The current . + + + + + The for this pointcut. + + + The current . + + + + + Internal method matcher class for union pointcut. + + + + + That part of an that checks whether a + target method is eligible for advice. + + +

    + An may be evaluated + statically or at runtime (dynamically). Static + matching involves only the method signature and (possibly) any + s that have been applied to a method. + Dynamic matching additionally takes into account the actual argument + values passed to a method invocation. +

    +

    + If the value of the + property of an implementation instance returns , + evaluation can be performed statically, and the result will be the same + for all invocations of this method, whatever their arguments. This + means that if the value of the + is + , the three argument + + method will never be invoked for the lifetime of the + . +

    +

    + If an implementation returns in its two argument + + method, and the value of it's + property is + , the three argument + + method will be invoked immediately before each and every potential + execution of the related advice, to decide whether the advice + should run. All previous advice, such as earlier interceptors in an + interceptor chain, will have run, so any state changes they have + produced in parameters or thread local storage, will be available at + the time of evaluation. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IMethodMatcher.cs,v 1.9 2006/04/09 07:18:36 markpollack Exp $ + +
    + + + Does the supplied satisfy this matcher? + + +

    + This is a static check. If this method invocation returns + ,or if the + property is + , then no runtime check will be made. +

    +
    + The candidate method. + + The target (may be , + in which case the candidate must be taken + to be the 's declaring class). + + + if this this method matches statically. + +
    + + + Is there a runtime (dynamic) match for the supplied + ? + + +

    + In order for this method to have even been invoked, the supplied + must have matched + statically. This method is invoked only if the two argument + + method returns for the supplied + and , and + if the property + is . +

    +

    + Invoked immediately before any potential running of the + advice, and after any advice earlier in the advice chain has + run. +

    +
    + The candidate method. + + The target . + + The arguments to the method + + if there is a runtime match. +
    + + + Is this dynamic? + + +

    + If , the three argument + + method will be invoked if the two argument + + method returns . +

    +

    + Note that this property can be checked when an AOP proxy is created, + and implementations need not check the value of this property again + before each method invocation. +

    +
    + + if this + is dynamic. + +
    + + + This class contains various methods used to + obtain information about the current AOP invocation. + + +

    + The + property is + usable if the AOP framework is configured to expose the current proxy + (not the default)... it returns the AOP proxy in use. Target objects or + advice can use this to make advised calls. They can also use it to find + advice configuration. +

    + + The AOP framework does not expose proxies by default, as there is a + performance cost in doing so. + +

    + The functionality in this class might be used by a target object that + needed access to resources on the invocation. However, this approach + should not be used when there is a reasonable alternative, as it makes + application code dependent on usage under AOP and the Spring.NET AOP + framework. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: AopContext.cs,v 1.7 2006/09/15 21:25:16 markpollack Exp $ +
    + + + Sets the current proxy by pushing it to the proxy stack. + + +

    + This method is for internal use only, and should never be called by + client code. +

    +
    + + The proxy to put on top of the proxy stack. + +
    + + + Removes the current proxy from the proxy stack, making the previous + proxy (if any) the current proxy. + + +

    + This method is for internal use only, and should never be called by + client code. +

    +
    + + If the proxy stack is empty. + +
    + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such exposes no public constructors. +

    +
    +
    + + + The AOP proxy associated with this thread. + + +

    + Will be unless the + property + on the controlling proxy has been set to . +

    +

    + The default value for the + property + is , for performance reasons. +

    +
    +
    + + + Gets the current AOP proxy. + + + If the proxy stack is empty. + + + + + A registry of + instances. + + +

    + Implementations must also automatically register adapters for + types. +

    + + This is an SPI interface, that should not need to be implemented by any + Spring.NET user. + +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IAdvisorAdapterRegistry.cs,v 1.3 2006/04/09 07:18:35 markpollack Exp $ +
    + + + Returns an wrapping the supplied + . + + + The object that should be an advice, such as + or + . + + + An wrapping the supplied + . Never returns . If + the parameter is an + , it will simply be returned. + + + If no registered + can wrap + the supplied . + + + + + Returns an to + allow the use of the supplied in an + interception-based framework. + + +

    + Don't worry about the pointcut associated with the + ; if it's an + , just return an + interceptor. +

    +
    + + The advisor to find an interceptor for. + + + An interceptor to expose this advisor's behaviour. + + + If the advisor type is not understood by any registered + . + +
    + + + Register the given . + + +

    + Note that it is not necessary to register adapters for + instances: these + must be automatically recognized by an + + implementation. +

    +
    + + An that + understands the particular advisor and advice types. + +
    + + + Intercepts the construction of a new object. + + +

    + Such interceptions are nested "on top" of the target. +

    +
    +
    + + + Implement this method to perform extra treatments before and after + the consruction of a new object. + + +

    + Polite implementations would certainly like to invoke + . +

    +
    + + The constructor invocation that is being intercepted. + + + The newly created object, which is also the result of the call to + , and might be + replaced by the interceptor. + + + If any of the interceptors in the chain or the target object itself + throws an exception. + +
    + + + Returns a token to indicate that this exception should be swallowed. + + Mark Pollack + $Id: SwallowExceptionHandler.cs,v 1.2 2007/10/02 21:56:53 markpollack Exp $ + + + + An abstract base class providing all necessary functionality for typical IExceptionHandler implementations. + + Mark Pollack + $Id: AbstractExceptionHandler.cs,v 1.2 2007/10/10 18:07:46 markpollack Exp $ + + + + The logging instance + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The exception names. + + + + Determines whether this instance can handle the exception the specified exception. + + The exception. + The call context dictionary. + + true if this instance can handle the specified exception; otherwise, false. + + + + + Handles the exception. + + The return value from handling the exception, if not rethrown or a new exception is thrown. + + + + Gets the source exception names. + + The source exception names. + + + + Gets the source exception types. + + The source exception types. + + + + Gets the action translation expression text + + The action translation expression. + + + + Gets or sets the constraint expression text. + + The constraint expression text. + + + + Gets a value indicating whether to continue processing. + + true if continue processing; otherwise, false. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The exception names. + + + + Handles the exception. + + The return value from handling the exception, if not rethrown or a new exception is thrown. + + + + implementation that caches a + local target object, but allows the target to be swapped while the + application is running + + +

    + If configuring an object of this class in a Spring IoC container, + use constructor injection to supply the intial target. +

    +
    + Rod Johnson + Aleksandar Seovic (.Net) + $Id: HotSwappableTargetSource.cs,v 1.6 2007/03/16 04:01:26 aseovic Exp $ +
    + + + Creates a new instance of the + with the initial target. + + + The initial target. May be . + + + + + Returns the target object. + + The target object. + + If unable to obtain the target object. + + + + + Releases the target object. + + +

    + No-op implementation. +

    +
    + The target object to release. +
    + + + Swap the target, returning the old target. + + The new target. + The old target. + + If the new target is . + + + + + Determines whether the specified + is equal to the current . + + +

    + Two invoker interceptors are equal if they have the same target or + if the targets are equal. +

    +
    + The target source to compare with. + + if this instance is equal to the + specified . + +
    + + + Serves as a hash function for a particular type, suitable for use + in hashing algorithms and data structures like a hash table. + + + A hash code for the current . + + + + + The of the target object. + + +

    + Can return . +

    +
    +
    + + + Is the target source static? + + + if the target source is static. + + + + + Abstract superclass for pooling s. + + +

    + Maintains a pool of target instances, acquiring and releasing a target + object from the pool for each method invocation. +

    +

    + This class is independent of pooling technology. +

    +

    + Subclasses must implement the + and + + methods to work with their chosen pool. The + + method inherited from the + base class + can be used to create objects to put in the pool. Subclasses must also + implement some of the monitoring methods from the + interface. This class + provides the + + method to return an + making these statistics available on proxied objects. +

    +

    + This class implements the interface in + order to force subclasses to implement the + method to cleanup and close + down their pool. +

    +
    + Rod Johnson + Federico Spinazzi (.NET) + $Id: AbstractPoolingTargetSource.cs,v 1.7 2007/03/16 04:01:26 aseovic Exp $ +
    + + + Configuration interface for a pooling invoker. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: PoolingConfig.cs,v 1.4 2006/04/09 07:18:37 markpollack Exp $ + + + + The number of active object instances in a pool. + + + + + The number of free object instances in a pool. + + + + + The maximum number of object instances in a pool. + + + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    +
    + + + Returns the target object (acquired from the pool). + + The target object (acquired from the pool). + + If unable to obtain the target object. + + + + + Gets the mixin. + + + An exposing statistics + about the pool maintained by this object. + + + + + Create the pool. + + + The owning , in + case one needs collaborators from it (normally one's own properties + are sufficient). + + + In the case of errors encountered during the creation of the pool. + + + + + Releases the target object (returns it to the pool). + + + The target object to release (return to the pool). + + + In the case that the could not be released. + + + + + Performs application-defined tasks associated with freeing, releasing, or + resetting unmanaged resources. + + +

    + Disposes of the pool. +

    +
    +
    + + + The maximum number of object instances in this pool. + + + + + The number of active object instances in this pool. + + + + + The number of free object instances in this pool. + + + + + The target factory that will be used to perform the lookup + of the object referred to by the + + property. + + + The owning + (will never be ). + + + In case of initialization errors. + + + + + + Pointcut object for simple method name matches, useful as an alternative to pure + regular expression based patterns. + + Juergen Hoeller + Aleksandar Seovic (.NET) + $Id: NameMatchMethodPointcut.cs,v 1.9 2007/03/16 04:01:24 aseovic Exp $ + + + + Convenient superclass when one wants to force subclasses to + implement the interface + but subclasses will still want to be pointcuts. + + +

    + The + property can be overriden to customize filter + behavior as well. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + Mark Pollack (.NET) + $Id: StaticMethodMatcherPointcut.cs,v 1.8 2007/05/30 22:35:43 markpollack Exp $ +
    + + + Convenient abstract superclass for static method matchers that don't care + about arguments at runtime. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: StaticMethodMatcher.cs,v 1.6 2006/04/09 07:18:37 markpollack Exp $ + + + + Is there a runtime (dynamic) match for the supplied + ? + + +

    + Always throws a . This + method should never be called on a static matcher. +

    +
    + The candidate method. + + The target . + + The arguments to the method + + Always throws a . + + + Always. + +
    + + + Does the supplied satisfy this matcher? + + +

    + Must be implemented by a derived class in order to specify matching + rules. +

    +
    + The candidate method. + + The target (may be , + in which case the candidate must be taken + to be the 's declaring class). + + + if this this method matches statically. + +
    + + + Is this dynamic? + + +

    + Always returns . +

    +
    + + Always returns . + +
    + + + Creates a new instance of the + + class. + + +

    + This is an abstract class, and as such has no publicly + visible constructors. +

    +
    +
    + + + The for this pointcut. + + + The current . + + + + + The for this pointcut. + + + The current . + + + + + Does the of the supplied + matches any of the mapped names? + + + The to check. + + + The of the target class. + + + if the name of the supplied + matches one of the mapped names. + + + + + Does the supplied match the supplied ? + + +

    + The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, + as well as direct equality. Can be overridden in subclasses. +

    +
    + + The method name of the class. + + + The name in the descriptor. + + + True if the names match. + +
    + + + Convenience property when we have only a single method name + to match. + + + + Use either this property or the + property, + not both. + + + + + + Set the method names defining methods to match. + + +

    + Matching will be the union of all these; if any match, the pointcut matches. +

    +
    +
    + + + Concrete ObjectFactory-based IPointcutAdvisor thta allows for any Advice to be + configured as reference to an Advice object in the ObjectFatory, as well as + the Pointcut to be configured through an object property. + + + Specifying the name of an advice object instead of the advice object itself + (if running within a ObjectFactory/ApplicationContext) increases loose coupling + at initialization time, in order to not intialize the advice object until the pointcut + actually matches. + + Juerge Hoeller + Mark Pollack + $Id: DefaultObjectFactoryPointcutAdvisor.cs,v 1.1 2007/05/30 22:35:43 markpollack Exp $ + + + + Abstract ObjectFactory-based IPointcutAdvisor that allows for any Advice to be + configured as reference to an Advice object in an ObjectFactory. + + + specifying the name of an advice object instead of the advice object itself + (if running within an ObjectFactory/ApplicationContext increses loose coupling + at initialization time, in order not to initialize the advice object until the + pointcut actually matches. + + Juergen Hoeller + Mark Pollack + $Id: AbstractObjectFactoryPointcutAdvisor.cs,v 1.2 2007/08/10 17:39:44 bbaia Exp $ + + + + Abstract base class for implementations. + + + Can be subclassed for returning a specific pointcut/advice or a freely configurable pointcut/advice. + + Rod Johnson + Juergen Hoeller + Mark Pollack (.NET) + $Id: AbstractPointcutAdvisor.cs,v 1.2 2008/01/14 20:49:47 oakinger Exp $ + + + + Superinterface for all s that are + driven by a pointcut. + + +

    + This covers nearly all advisors except introduction advisors, for which + method-level matching does not apply. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + + $Id: IPointcutAdvisor.cs,v 1.3 2006/04/09 07:18:36 markpollack Exp $ +
    + + + Base interface holding AOP advice and a filter determining the + applicability of the advice (such as a pointcut). + + + + This interface is not for use by Spring.NET users, but exists rather to + allow for commonality in the support for different types of advice + within the framework. + +

    + Spring.NET AOP is centered on around advice delivered via method + interception, compliant with the AOP Alliance interception API. + The interface allows support for + different types of advice, such as before and after + advice, which need not be implemented using interception. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + + + + + $Id: IAdvisor.cs,v 1.7 2006/04/09 07:18:36 markpollack Exp $ +
    + + + Is this advice associated with a particular instance? + + +

    + An advisor that was creating a mixin would be a per instance + operation and would thus return . If the + advisor is not per instance, it is shared with all instances of the + advised class obtained from the same Spring.NET IoC container. +

    +

    + Use singleton and prototype object definitions or + appropriate programmatic proxy creation to ensure that + s have the correct lifecycle model. +

    + + This method is not currently used by the framework. + +
    + + if this advice is associated with a + particular instance. + +
    + + + Return the advice part of this aspect. + + +

    + An advice may be an interceptor, a throws advice, before advice, + introduction etc. +

    +
    + + The advice that should apply if the pointcut matches. + +
    + + + The that drives this advisor. + + + + + Determines whether the specified + is equal to the current . + + The advisor to compare with. + + if this instance is equal to the + specified . + + + + + Serves as a hash function for a particular type, suitable for use + in hashing algorithms and data structures like a hash table. + + + A hash code for the current . + + + + + Returns this s order in the + interception chain. + + + This s order in the + interception chain. + + + + + Return the advice part of this aspect. + + +

    + An advice may be an interceptor, a throws advice, before advice, + introduction etc. +

    +
    + + The advice that should apply if the pointcut matches. + +
    + + + Is this advice associated with a particular instance? + + +

    + Not supported for dynamic advisors. +

    +
    + + if this advice is associated with a + particular instance. + + Always. + +
    + + + The that drives this advisor. + + + + + Describe this Advisor, showing name of advice object. + + + Type name and advice object name. + + + + + Gets or sets the name of the advice object that this advisor should refer to. + + An instance of the specified object will be obtained on first access of + this advisor's advice. This advisor will only ever obtain at most one + single instance of the advice object, caching the instance for the lifetime of + the advisor. + The name of the advice object. + + + + Callback that supplies the owning factory to an object instance. + + + Owning + (may not be ). The object can immediately + call methods on the factory. + + +

    + Invoked after population of normal object properties but before an init + callback like 's + + method or a custom init-method. +

    +
    + + In case of initialization errors. + +
    + + + Return the advice part of this aspect. + + +

    + An advice may be an interceptor, a throws advice, before advice, + introduction etc. +

    +
    + + The advice that should apply if the pointcut matches. + +
    + + + Describe this Advisor, showing pointcut and name of advice object. + + Type name , pointcut, and advice object name. + + + + The that drives this advisor. + + + + + Superinterface for all before advice. + + +

    + Before advice is advice that executes before a joinpoint, but + which does not have the ability to prevent execution flow proceeding to + the joinpoint (unless it throws an ). +

    +

    + Spring.NET only supports method before advice. Although this + is unlikely to change, this API is designed to allow field + before advice in future if desired. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + + + + + $Id: IBeforeAdvice.cs,v 1.4 2006/04/09 07:18:36 markpollack Exp $ +
    + + + Implementations can create special target sources, such as pooling target + sources, for particular objects. For example, they may base their choice + on attributes, such as a pooling attribute, on the target type. + +

    AbstractAutoProxyCreator can support a number of TargetSourceCreators, + which will be applied in order.

    +
    + Rod Johnson + Adhari C Mahendra (.NET) + $Id: ITargetSourceCreator.cs,v 1.3 2007/08/22 08:49:08 markpollack Exp $ +
    + + + Create a special TargetSource for the given object, if any. + + The type of the object to create a TargetSource for + the name of the object + the containing factory + a special TargetSource or null if this TargetSourceCreator isn't + interested in the particular object + + + + ObjectPostProcessor implementation that creates AOP proxies based on all candidate + Advisors in the current IObjectFactory. This class is completely generic; it contains + no special code to handle any particular aspects, such as pooling aspects. + + Rod Johnson + Adhari C Mahendra (.NET) + $Id: DefaultAdvisorAutoProxyCreator.cs,v 1.9 2007/10/08 22:04:51 markpollack Exp $ + + + + Abstract IOBjectPostProcessor implementation that creates AOP proxies. + This class is completely generic; it contains no special code to handle + any particular aspects, such as pooling aspects. + + +

    Subclasses must implement the abstract findCandidateAdvisors() method + to return a list of Advisors applying to any object. Subclasses can also + override the inherited shouldSkip() method to exclude certain objects + from autoproxying, but they must be careful to invoke the shouldSkip() + method of this class, which tries to avoid circular reference problems + and infinite loops.

    +

    Advisors or advices requiring ordering should implement the Ordered interface. + This class sorts advisors by Ordered order value. Advisors that don't implement + the Ordered interface will be considered to be unordered, and will appear + at the end of the advisor chain in undefined order.

    +
    + + Rod Johnson + Adhari C Mahendra (.NET) + $Id: AbstractAdvisorAutoProxyCreator.cs,v 1.5 2007/08/22 08:49:08 markpollack Exp $ +
    + + + Return whether the given object is to be proxied, what additional + advices (e.g. AOP Alliance interceptors) and advisors to apply. + + the new object instance + the name of the object + targetSource returned by TargetSource property: + may be ignored. Will be null unless a custom target source is in use. + + an array of additional interceptors for the particular object; + or an empty array if no additional interceptors but just the common ones; + or null if no proxy at all, not even with the common interceptors. + + +

    The previous name of this method was "GetInterceptorAndAdvisorForObject". + It has been renamed in the course of general terminology clarification + in Spring 1.1. An AOP Alliance Interceptor is just a special form of + Advice, so the generic Advice term is preferred now.

    +

    The third parameter, customTargetSource, is new in Spring 1.1; + add it to existing implementations of this method.

    +
    +
    + + + Find all eligible advices and for autoproxying this class. + + + the empty list, not null, if there are no pointcuts or interceptors + + + + Sorts the advisors. + + The advisors. + + + + + Find all candidate advisors to use in auto-proxying. + + list of Advisors + + + + We override this method to ensure that all candidate advisors are materialized + under a stack trace including this object. Otherwise, the dependencies won't + be apparent to the circular-reference prevention strategy in AbstractObjectFactory. + + + + + Separator between prefix and remainder of object name + + + + + Find all candidate advices to use in auto proxying. + + list of Advice + + + + Invoked by an + after it has injected all of an object's dependencies. + + + + + Gets or sets a value indicating whether to exclude + advisors with a certain prefix. + + true if [use prefix]; otherwise, false. + + + + Set the prefix for object names that will cause them to be included for + auto-proxying by this object. This prefix should be set to avoid circular + references. Default value is the object name of this object + a dot. + + The advisor object name prefix. + + + + Set the name of the object in the object factory that created this object. + + The name of the object in the factory. + +

    + Invoked after population of normal object properties but before an init + callback like 's + + method or a custom init-method. +

    +
    +
    + + + Convenient pointcut-driven advisor implementation. + + +

    + This is the most commonly used implementation. + It can be used with any pointcut and advice type, except for introductions. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: DefaultPointcutAdvisor.cs,v 1.8 2007/05/30 22:35:43 markpollack Exp $ +
    + + + Abstract PointcutAdvisor that allows for any Advice to be configured. + + Juergen Hoeller + Mark Pollack (.NET) + $Id: AbstractGenericPointcutAdvisor.cs,v 1.2 2007/08/10 17:39:44 bbaia Exp $ + + + + Returns a that represents the current + . + + + A representation of this advisor. + + + + + Return the advice part of this advisor. + + + The advice that should apply if the pointcut matches. + + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + + class for the supplied , + + + The advice to use. + + + + + Creates a new instance of the + + class for the supplied and + . + + + The advice to use. + + + The pointcut to use. + + + + + Returns a that represents the current + . + + + A representation of this advisor. + + + + + The that drives this advisor. + + + + + Decorates a target source with the + interface. + + +

    + This implementation will release the target object when said object + is disposed. +

    +
    + Aleksandar Seovic + $Id: DynamicTargetSourceWrapper.cs,v 1.4 2007/03/16 04:01:17 aseovic Exp $ +
    + + + Decorates a target source with the + interface. + + Aleksandar Seovic + $Id: ITargetSourceWrapper.cs,v 1.1 2006/08/10 18:45:52 bbaia Exp $ + + + + Returns the target object that proxy methods will be delegated to. + + The target object. + + + + Creates a new instance of the + + class. + + + The target object that proxy methods will be delegated to. + + + If the supplied is + . + + + + + Returns the target object that proxy methods will be delegated to. + + The target object. + + + + Releases the dynamic target when this object is disposed. + + + + + Superclass for all AOP infrastructure exceptions. + + Aleksandar Seovic + $Id: AspectException.cs,v 1.3 2006/04/09 07:18:33 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Base class for different cache advice implementations that provide + access to common functionality, such as obtaining a cache instance. + + Aleksandar Seovic + $Id: BaseCacheAdvice.cs,v 1.3 2007/04/01 15:04:04 bbaia Exp $ + + + + Returns an instance based on the cache name. + + The name of the cache. + + Cache instance for the specified if one + is registered in the application context, or null if it isn't. + + + + + Prepares variables for expression evaluation by packaging all + method arguments into a dictionary, keyed by argument name. + + + Method to get parameters info from. + + + Argument values to package. + + + A dictionary containing all method arguments, keyed by method name. + + + + + Evaluates a SpEL expression as a boolean value. + + + The expression string that should be evaluated. + + + The SpEL expression instance that should be evaluated. + + + The object to evaluate expression against. + + + The expression variables dictionary. + + + The evaluated boolean. + + + If the SpEL expression could not be successfuly resolved to a boolean. + + + + + Retrieves custom attribute for the specified attribute type. + + + Method to get attribute from. + + + Attribute type. + + + Attribute instance if one is found, null otherwise. + + + + + Set the that this + object runs in. + + + + + Pooling target source implementation based on the + + + Rod Johnson + Federico Spinazzi + $Id: SimplePoolTargetSource.cs,v 1.9 2007/03/16 04:01:26 aseovic Exp $ + + + + + Returns the target object (acquired from the pool). + + + The target object (acquired from the pool). + + + If unable to obtain the target object. + + + + + Creates the pool. + + + The owning , in + case one needs collaborators from it (normally one's own properties + are sufficient). + + + + + + Creates a new instance of an appropriate + implementation. + + +

    + Subclasses can, of course, override this method if they want to + return a different implementation. +

    +
    + + An empty . + +
    + + + Releases the target object (returns it to the pool). + + The target object to release (return to the pool). + + In the case that the could not be released. + + + + + Performs application-defined tasks associated with freeing, releasing, or + resetting unmanaged resources. + + +

    + Disposes of the pool. +

    +
    +
    + + + Creates an instance that can be returned by the pool. + + + An instance that can be returned by the pool. + + + + + + Destroys an instance no longer needed by the pool. + + The instance to be destroyed. + + + + + Ensures that the instance is safe to be returned by the pool. + Returns false if this object should be destroyed. + + The instance to validate. + + if this object is not valid and + should be dropped from the pool, otherwise . + + + + + + Reinitialize an instance to be returned by the pool. + + The instance to be activated. + + + + + Passivates the object. + + The instance returned to the pool. + + + + + The number of active object instances in this pool. + + + + + The number of free object instances in this pool. + + + + + Convenient class for attribute-match method pointcuts that hold an Interceptor, + making them an Advisor. + + Bruno Baia + $Id: AttributeMatchMethodPointcutAdvisor.cs,v 1.3 2007/03/16 04:01:23 aseovic Exp $ + + + + implementation that matches methods + that have been decorated with a specified . + + Aleksandar Seovic + Ronald Wildenberg + $Id: AttributeMatchMethodPointcut.cs,v 1.8 2007/05/21 16:43:45 bbaia Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The to match. + + + + + Creates a new instance of the + + class. + + + The to match. + + + Flag that controls whether or not the inheritance tree of the + method to be included in the search for the ? + + + + + Creates a new instance of the + + class. + + + The to match. + + + Flag that controls whether or not the inheritance tree of the + method to be included in the search for the ? + + + Flag that controls whether or not interfaces attributes of the + method to be included in the search for the ? + + + + + Does the supplied satisfy this matcher? + + The candidate method. + + The target (may be , + in which case the candidate must be taken + to be the 's declaring class). + + + if this this method matches statically. + + + + + The to match. + + + If the supplied value is not a that + derives from the class. + + + + + Is the inheritance tree of the method to be included in the search for the + ? + + +

    + The default is . +

    +
    +
    + + + Is the interfaces attributes of the method to be included in the search for the + ? + + +

    + The default is . +

    +
    +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class + for the supplied . + + + + + + Is this advice associated with a particular instance? + + + if this advice is associated with a + particular instance. + + + Always; this property is not yet supported. + + + + + Returns this s order in the + interception chain. + + + This s order in the + interception chain. + + + + + Return the advice part of this advisor. + + + The advice that should apply if the pointcut matches. + + + + + + The that drives this advisor. + + + + + Subinterface of the AOP Alliance + interface that + allows additional interfaces to be implemented by the interceptor, and + available via a proxy using that interceptor. + + +

    + This is a fundamental AOP concept called introduction. +

    +

    + Introductions are often mixins, enabling the building of composite + objects that can achieve many of the goals of multiple inheritance. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IIntroductionInterceptor.cs,v 1.3 2006/04/09 07:18:36 markpollack Exp $ +
    + + + Does this + implement the given interface? + + The interface to check. + + if this + + implements the given interface. + + + + + Base class that each dynamic composition proxy has to extend. + + Aleksandar Seovic + Bruno Baia + $Id: BaseCompositionAopProxy.cs,v 1.2 2007/03/16 04:01:22 aseovic Exp $ + + + + Default constructor. + + + + + Creates a new instance of the + class. + + The proxy configuration. + + + + Deserialization constructor. + + Serialization data. + Serialization context. + + + + Returns this proxy instance + + + + + + Delegate to target object handling of equals method. + + The object to compare with the current target object + true if the specified Object is equal to the current target object; otherwise, false + + + + Delgate to the target object generation of the hash code. + + A hash code for the target object. + + + + Returns a String the represents the target object. + + A String that represents the target object + + + + Provides Singleton-style access to the default + instance. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: GlobalAdvisorAdapterRegistry.cs,v 1.5 2006/04/09 07:18:35 markpollack Exp $ + + + + Default implementation of the + + interface. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: DefaultAdvisorAdapterRegistry.cs,v 1.5 2006/04/09 07:18:35 markpollack Exp $ + + + + Creates a new instance of the + class. + + +

    + This constructor will also register the well-known + types. +

    +
    + +
    + + + Returns an wrapping the supplied + . + + + The object that should be an advice, such as + or + . + + + An wrapping the supplied + . Never returns . If + the parameter is an + , it will simply be returned. + + + If no registered + can wrap + the supplied . + + + + + Returns an to + allow the use of the supplied in an + interception-based framework. + + The advisor to find an interceptor for. + + An interceptor to expose this advisor's behaviour. + + + If the advisor type is not understood by any registered + . + + + + + Register the given . + + + An that + understands the particular advisor and advice types. + + + + + Creates a new instance of the + class. + + +

    + This contructor is marked as to enforce the + Singleton pattern +

    +
    +
    + + + The default instance. + + + + + Pointcut and method matcher for use in simple cflow-style + pointcuts. + + +

    + Evaluating such pointcuts is slower than evaluating normal pointcuts, + but can nevertheless be useful in some cases. Of course, your mileage + may vary as to what 'slower' actually means. +

    +
    + Rod Johnson + Simon White (.NET) + $Id: ControlFlowPointcut.cs,v 1.6 2006/04/09 07:18:37 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + The class under which all control flows are to be matched. + + + + + Construct a new pointcut that matches all calls below the + given method in the given class. + + +

    + If the supplied is + , all control flows below the given + class will be successfully matched. +

    +
    + + The class under which all control flows are to be matched. + + + The method name under which all control flows are to be matched. + +
    + + + Should the pointcut apply to the supplied ? + + +

    + Subclasses are encouraged to override this method for greater + filtering (and performance). +

    +

    + This, the default, implementation always matches (returns + ). +

    +
    + The candidate target class. + + if the advice should apply to the supplied + + +
    + + + Does the supplied satisfy this matcher? + Perform static checking. If this returns false, or if the isRuntime() method + returns false, no runtime check will be made. + + +

    + Subclasses are encouraged to override this method if it is possible + to filter out some candidate classes. +

    +

    + This, the default, implementation always matches (returns + ). This means that the three argument + + method will always be invoked. +

    +
    + The candidate method. + + The target class (may be , in which case the + candidate class must be taken to be the 's + declaring class). + + + if this this method matches statically. + + +
    + + + Is there a runtime (dynamic) match for the supplied + ? + + +

    + Subclasses are encouraged to override this method if it is possible + to filter out some candidate classes. +

    +
    + The candidate method. + The target class. + The arguments to the method + + if there is a runtime match. + +
    + + + The for this pointcut. + + + The current . + + + + + Gets the number of times this pointcut has been evaluated. + + +

    + Useful as a debugging aid. +

    +

    + Note that this value is distinct from the number of times that this + pointcut sucessfully matches a target method, in that a + may be evaluated many times but + never actually match even once. +

    +
    + + The number of times this pointcut has been evaluated. + +
    + + + The for this pointcut. + + + The current . + + + + + Is this a runtime pointcut? + + +

    + This implementation is a runtime pointcut, and so always returns + . +

    +
    + + if this is a runtime pointcut. + + +
    + + Interceptor to wrap an after throwing advice. + +

    + Implementations of the interface + must define methods of the form... + + AfterThrowing([MethodInfo method, Object[] args, Object target], Exception subclass); + + The method name is fixed (i.e. your methods must be named + AfterThrowing. The first three arguments (as a whole) are + optional, and only useful if futher information about the joinpoint is + required. The return type can be anything, but is almost always + by convention. +

    +

    + Please note that the object encapsulating the throws advice does not + need to implement the interface. + Throws advice methods are discovered via reflection... the + interface serves merely to + discover objects that are to be considered as throws advice. + Other mechanisms for discovering throws advice such as attributes are + also equally valid... all that this class cares about is that a throws + advice object implement one or more methods with a valid throws advice + signature (see above, and the examples below). +

    +

    + This is a framework class that should not normally need to be used + directly by Spring.NET users. +

    +
    + +

    + Find below some examples of valid + method signatures... +

    + + public class GlobalExceptionHandlingAdvice : IThrowsAdvice + { + public void AfterThrowing(Exception ex) { + // handles absolutely any and every Exception... + } + } + + + public class RemotingExceptionHandlingAdvice : IThrowsAdvice + { + public void AfterThrowing(RemotingException ex) { + // handles any and every RemotingException (and subclasses of RemotingException)... + } + } + + + using System.Data; + + public class DataExceptionHandlingAdvice + { + public void AfterThrowing(ConstraintException ex) { + // specialised handling of ConstraintExceptions + } + + public void AfterThrowing(NoNullAllowedException ex) { + // specialised handling of NoNullAllowedExceptions + } + + public void AfterThrowing(DataException ex) { + // handles all other DataExceptions... + } + } + +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: ThrowsAdviceInterceptor.cs,v 1.8 2007/05/04 13:16:44 bbaia Exp $ + +
    + + + The mapping of exception Types to MethodInfo handlers. + + + + + Creates a new instance of the + class. + + + + + The throws advice to check for exception handler methods. + + + If the supplied is . + + + If no (0) handler methods were discovered on the supplied ; + or if more than one handler method suitable for a particular + type was discovered on the supplied + . + + + + + Executes interceptor if (and only if) the supplied + throws an exception that is mapped to + an appropriate exception handler. + + + The method invocation that is being intercepted. + + + The result of the call to the + method of + the supplied (this assumes no + exception was thrown by the call to the supplied . + + + If any of the interceptors in the chain or the target object itself + throws an exception. + + + + + + Gets the exception handler (if any) that has been mapped to the + supplied . + + +

    + Will return if not found. +

    +
    + + The exception handler for the of the + supplied given exception. + + exception that was thrown +
    + + + Invokes handler method with appropriate number of parameters + + + The original method invocation that was intercepted. + + + The exception that triggered this interceptor. + + + The exception handler method to invoke. + + + + + Convenience property that returns the number of exception handler + methods managed by this interceptor. + + + The number of exception handler methods managed by this interceptor. + + + + + Convinience advisor implementation that applies + to all the methods that have defined on one or + more of their parameters. + + Bruno Baia + $Id: ParameterValidationAdvisor.cs,v 1.1 2008/05/26 09:17:50 bbaia Exp $ + + + + Creates new advisor instance. + + + + + Returns true if any of the parameters of the specified + has applied. + + + Method to check. + + + Type of target object. + + + true if any of the parameters of the specified + has applied; false otherwise. + + + + + Set the that this + object runs in. + + +

    + Normally this call will be used to initialize the object. +

    +

    + Invoked after population of normal object properties but before an + init callback such as + 's + + or a custom init-method. Invoked after the setting of any + 's + + property. +

    +
    + + In the case of application context initialization errors. + + + If thrown by any application context methods. + + +
    + + + Translates from one exception to another based. My wrap or replace exception depending on the expression. + + Mark Pollack + $Id: TranslationExceptionHandler.cs,v 1.3 2007/10/10 18:07:46 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The exception names. + + + + Handles the exception. + + The return value from handling the exception, if not rethrown or a new exception is thrown. + + + + Canonical that matches + all methods. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: TrueMethodMatcher.cs,v 1.5 2006/04/09 07:18:36 markpollack Exp $ + + + + Canonical instance that matches all methods. + + +

    + It is not dynamic. +

    +
    +
    + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such has no publicly visible + constructors. +

    +
    +
    + + + Does the supplied satisfy this matcher? + Perform static checking. If this returns false, or if the isRuntime() method + returns false, no runtime check will be made. + + The candidate method. + + The target class (may be , in which case the + candidate class must be taken to be the 's + declaring class). + + + if this this method matches statically. + + + + + + Is there a runtime (dynamic) match for the supplied + ? + + The candidate method. + The target class. + The arguments to the method + + if there is a runtime match. + + + + + A that represents the current + . + + + A that represents the current + . + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + Is this dynamic? + + + if this + is dynamic. + + + + + + Provides access to the target object of an AOP proxy. + + +

    + To be implemented by introduction aspects in order to obtain access to + the target object. +

    +
    + Aleksandar Seovic + $Id: ITargetAware.cs,v 1.3 2006/04/09 07:18:35 markpollack Exp $ +
    + + + Sets the target object. + + + + + Builds an AOP proxy type using the decorator pattern. + + Bruno Baia + $Id: DecoratorAopProxyTypeBuilder.cs,v 1.17 2007/12/07 17:58:56 bbaia Exp $ + + + + AdvisedProxy instance calls should be delegated to. + + + + + Creates a new instance of the + class. + + The proxy configuration. + + + + Creates the proxy type. + + The generated proxy class. + + + + Generates the IL instructions that pushes + the current + instance on stack. + + The IL generator to use. + + + + Declares field that holds the + instance used by the proxy. + + + The builder to use for code generation. + + + + + Implements serialization method. + + + + + + Implements serialization constructor. + + Type builder to use. + + + + Implements constructors for the proxy class. + + +

    + This implementation creates a new instance + of the class. +

    +
    + + The builder to use. + +
    + + + Implements interface. + + The type builder to use. + + + + Determines if the specified + is one of those generated by this builder. + + The type to check. + + if the type is a decorator-based proxy; + otherwise . + + + + + Thrown in response to the misconfiguration of an AOP proxy. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: AopConfigException.cs,v 1.4 2006/04/09 07:18:35 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class with + the specified message. + + + A message about the exception. + + + + + Creates a new instance of the + class with + the specified message and root cause. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Namespace parser for the aop namespace. + + + Using the advisor tag you can configure an and have it + applied to all the relevant objects in your application context automatically. The + advisor tag supports only referenced s. + + Rob harrop + Adrian Colyer + Rod Johnson + Mark Pollack (.NET) + $Id: AopNamespaceParser.cs,v 1.3 2007/08/09 02:42:35 markpollack Exp $ + + + + Register the for the 'config' tag. + + + + + Abstract base class for logging advice + + + + + Mark Pollack + $Id: AbstractLoggingAdvice.cs,v 1.2 2007/12/06 17:17:19 markpollack Exp $ + + + + The default ILog instance used to write logging messages. + + + + + Indicates whether or not proxy type names should be hidden when using dynamic loggers. + + + + + Adds logging to the method invocation. + + + The method IsInterceptorEnabled is called + as an optimization to determine if logging should be applied. If logging should be + applied, the method invocation is passed to the InvokeUnderLog method for handling. + If not, the method proceeds as normal. + + + The method invocation that is being intercepted. + + + The result of the call to the + method of + the supplied ; this return value may + well have been intercepted by the interceptor. + + + If any of the interceptors in the chain or the target object itself + throws an exception. + + + + + Determines whether the interceptor is enabled for the specified invocation, that + is, whether the method InvokeUnderLog is called. + + The default behavior is to check whether the given ILog instance + is enabled by calling IsLogEnabled, whose default behavior is to check if + the TRACE level of logging is enabled. Subclasses + The invocation. + The log to write messages to + + true if [is interceptor enabled] [the specified invocation]; otherwise, false. + + + + + Determines whether the given log is enabled. + + + Default is true when the trace level is enabled. Subclasses may override this + to change the level at which logging occurs, or return true to ignore level + checks. + The log instance to check. + + true if log is for a given log level; otherwise, false. + + + + + Subclasses must override this method to perform any tracing around the supplied + IMethodInvocation. + + + Subclasses are resonsible for ensuring that the IMethodInvocation actually executes + by calling IMethodInvocation.Proceed(). + + By default, the passed-in ILog instance will have log level + "trace" enabled. Subclasses do not have to check for this again, unless + they overwrite the IsInterceptorEnabled method to modify + the default behavior. + + + The method invocation to log + The log to write messages to + The result of the call to IMethodInvocation.Proceed() + + + If any of the interceptors in the chain or the target object itself + throws an exception. + + + + + Gets the appropriate log instance to use for the given IMethodInvocation. + + + If the UseDynamicLogger property is set to true, the ILog instance will be + for the target class of the IMethodInvocation, otherwise the log will be the + default static logger. + + The method invocation being logged. + The ILog instance to use. + + + + Sets a value indicating whether to use a dynamic logger or static logger + + Default is to use a static logger. + + Used to determine which ILog instance should be used to write log messages for + a particular method invocation: a dynamic one for the Type getting called, + or a static one for the Type of the trace interceptor. + + + Specify either this property or LoggerName, not both. + + + true if use dynamic logger; otherwise, false. + + + + Sets the name of the logger to use. + + + The name will be passed to the underlying logging implementation through Common.Logging, + getting interpreted as the log category according to the loggers configuration. + + This can be specified to not log into the category of a Type (whether this + interceptor's class or the class getting called) but rather to a specific named category. + + + Specify either this property or UseDynamicLogger, but not both. + + + The name of the logger. + + + + Sets a value indicating whether hide proxy type names (whenever possible) + when using dynamic loggers, i.e. property UseDynamicLogger is set to true. + + true if [hide proxy type names]; otherwise, false. + + + + Exception advice to perform exception translation, conversion of exceptions to default return values, and + exception swallowing. Configuration is via a DSL like string for ease of use in common cases as well as + allowing for custom translation logic by leveraging the Spring expression language. + + + + The exception handler collection can be filled with either instances of objects that implement the interface + or a string that follows a simple syntax for most common exception management + needs. The source exceptions to perform processing on are listed immediately after the keyword 'on' and can + be comma delmited. Following that is the action to perform, either log, translate, wrap, replace, return, or + swallow. Following the action is a Spring expression language (SpEL) fragment that is used to either create the + translated/wrapped/replaced exception or specify an alternative return value. The variables available to be + used in the expression language fragment are, #method, #args, #target, and #e which are 1) the method that + threw the exception, the arguments to the method, the target object itself, and the exception that was thrown. + Using SpEL gives you great flexibility in creating a translation of an exception that has access to the calling context. + + Common translation cases, wrap and rethrow, are supported with a shorter syntax where you can specify only + the exception text for the new translated exception. If you ommit the exception text a default value will be + used. + The exceptionsHandlers are compared to the thrown exception in the order they are listed. logging + an exception will continue the evaluation process, in all other cases exceution stops at that point and the + appropriate exceptions handler is executed. + + + + on FooException1 log 'My Message, Method Name ' + #method.Name + on FooException1 translate new BarException('My Message, Method Called = ' + #method.Name", #e) + on FooException2,Foo3Exception wrap BarException 'My Bar Message' + on FooException4 replace BarException 'My Bar Message' + on FooException5 return 32 + on FooException6 swallow + + + + + + + Mark Pollack + $Id: ExceptionHandlerAdvice.cs,v 1.9 2007/10/11 01:29:42 markpollack Exp $ + + + + This is + + + + + Mark Pollack + $Id: AbstractExceptionHandlerAdvice.cs,v 1.1 2007/10/08 22:05:16 markpollack Exp $ + + + + Implement this method to perform extra treatments before and after + the call to the supplied . + + +

    + Polite implementations would certainly like to invoke + . +

    +
    + + The method invocation that is being intercepted. + + + The result of the call to the + method of + the supplied ; this return value may + well have been intercepted by the interceptor. + + + If any of the interceptors in the chain or the target object itself + throws an exception. + +
    + + + Invoked by an + after it has injected all of an object's dependencies. + + +

    + This method allows the object instance to perform the kind of + initialization only possible when all of it's dependencies have + been injected (set), and to throw an appropriate exception in the + event of misconfiguration. +

    +

    + Please do consult the class level documentation for the + interface for a + description of exactly when this method is invoked. In + particular, it is worth noting that the + + and + callbacks will have been invoked prior to this method being + called. +

    +
    + + In the event of misconfiguration (such as the failure to set a + required property) or if initialization fails. + +
    + + + Parses the advice expression. + + The advice expression. + An instance of ParsedAdviceExpression + + + + Gets the match using exception constraint expression. + + The advice expression string. + The regex string. + The Match object resulting from the regular expression match. + + + + Gets or sets the Regex string used to parse advice expressions starting with 'on exception name' and subclass specific actions. + + The regex string to parse advice expressions starting with 'on exception name' and subclass specific actions. + + + + Gets or sets the Regex string used to parse advice expressions starting with 'on exception (constraint)' and subclass specific actions. + + The regex string to parse advice expressions starting with 'on exception (constraint)' and subclass specific actions. + + + + Implement this method to perform extra treatments before and after + the call to the supplied . + + +

    + Polite implementations would certainly like to invoke + . +

    +
    + + The method invocation that is being intercepted. + + + The result of the call to the + method of + the supplied ; this return value may + well have been intercepted by the interceptor. + + + If any of the interceptors in the chain or the target object itself + throws an exception. + +
    + + + Invoked by an + after it has injected all of an object's dependencies. + + +

    + This method allows the object instance to perform the kind of + initialization only possible when all of it's dependencies have + been injected (set), and to throw an appropriate exception in the + event of misconfiguration. +

    +

    + Please do consult the class level documentation for the + interface for a + description of exactly when this method is invoked. In + particular, it is worth noting that the + + and + callbacks will have been invoked prior to this method being + called. +

    +
    + + In the event of misconfiguration (such as the failure to set a + required property) or if initialization fails. + +
    + + + Invokes handlers registered for the passed exception and + + The exception to be handled + The that raised this exception. + The output of + + + + Parses the specified handler string, creating an instance of IExceptionHander. + + The handler string. + an instance of an exception handler or null if was not able to correctly parse + handler string. + + + + Gets or sets the Regex string used to parse advice expressions starting with 'on exception name' and exception handling actions. + + The regex string to parse advice expressions starting with 'on exception name' and exception handling actions. + + + + Gets or sets the Regex string used to parse advice expressions starting with 'on exception (constraint)' and exception handling actions. + + The regex string to parse advice expressions starting with 'on exception (constraint)' and exception handling actions. + + + + Gets or sets the exception handler. + + The exception handler. + + + + Convenient superclass for s + that are also dynamic pointcuts. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: DynamicMethodMatcherPointcutAdvisor.cs,v 1.6 2007/03/16 04:01:24 aseovic Exp $ + + + + Convenient abstract superclass for dynamic method matchers that do + care about arguments at runtime. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: DynamicMethodMatcher.cs,v 1.5 2006/04/09 07:18:37 markpollack Exp $ + + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    +
    + + + Does the supplied satisfy this matcher? + + +

    + Derived classes can override this method to add preconditions for + dynamic matching. +

    +

    + This implementation always returns . +

    +
    + The candidate method. + + The target (may be , + in which case the candidate must be taken + to be the 's declaring class). + + + if this this method matches statically. + +
    + + + Is there a runtime (dynamic) match for the supplied + ? + + +

    + Must be overriden by derived classes to provide criteria for dynamic matching. +

    +
    + The candidate method. + + The target . + + The arguments to the method + + if there is a runtime match. +
    + + + Is this dynamic? + + + Always returns , to specify that this is a + dynamic matcher. + + + + + Creates a new instance of the + + class. + + +

    + This is an abstract class, and as such has no publicly + visible constructors. +

    +
    +
    + + + Creates a new instance of the + + class for the supplied . + + +

    + This is an abstract class, and as such has no publicly + visible constructors. +

    +
    + + The advice portion of this advisor. + +
    + + + Is this advice associated with a particular instance? + + +

    + Not supported for dynamic advisors. +

    +
    + + if this advice is associated with a + particular instance. + + Always. + +
    + + + The for this pointcut. + + +

    + This implementation always returns a filter that evaluates to + for any . +

    +
    + + The current . + +
    + + + The for this pointcut. + + +

    + This implementation always returns itself (this object). +

    +
    + + The current . + +
    + + + Returns this s order in the + interception chain. + + + This s order in the + interception chain. + + + + + Return the advice part of this aspect. + + + The advice that should apply if the pointcut matches. + + + + + + The that drives this advisor. + + +

    + This implementation always returns itself (this object). +

    +
    +
    + + + implementation that + wraps instances. + + +

    + In the future Spring.NET may also offer a more efficient alternative + solution in cases where there is no interception advice and therefore + no need to create an + object. +

    +

    + Used internally by the Spring.NET AOP framework: application developers + should not need to use this class directly. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: MethodBeforeAdviceInterceptor.cs,v 1.5 2007/03/16 04:01:21 aseovic Exp $ +
    + + + Creates a new instance of the + + class. + + + The that is to be wrapped. + + + If the supplied is . + + + + + Executes interceptor before the target method successfully returns. + + + The method invocation that is being intercepted. + + + The result of the call to the + method of + the supplied . + + + If any of the interceptors in the chain or the target object itself + throws an exception. + + + + + Default implementation of the + interface, + either creating a decorator-based dynamic proxy or + a composition-based dynamic proxy. + + +

    + Creates a decorator-base proxy if one the following is true : + - the "ProxyTargetType" property is set + - no interfaces have been specified +

    +

    + In general, specify "ProxyTargetType" to enforce a decorator-based proxy, + or specify one or more interfaces to use a composition-based proxy. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + Bruno Baia (.NET) + + $Id: DefaultAopProxyFactory.cs,v 1.2 2007/08/02 16:28:29 bbaia Exp $ +
    + + + Factory interface for the creation of AOP proxies based on + configuration + objects. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IAopProxyFactory.cs,v 1.3 2006/04/09 07:18:35 markpollack Exp $ + + + + Creates an for the + supplied configuration. + + The AOP configuration. + An . + + If the supplied configuration is + invalid. + + + + + Creates an for the + supplied configuration. + + The AOP configuration. + An . + + If the supplied configuration is + invalid. + + + + + + Generates the proxy type. + + + The to use + + The generated proxy class. + + + + Convinience advisor implementation that applies + to all the methods that have defined. + + Aleksandar Seovic + $Id: InvalidateCacheAdvisor.cs,v 1.1 2007/02/16 03:13:01 aseovic Exp $ + + + + Creates new advisor instance. + + + + + Set the that this + object runs in. + + +

    + Normally this call will be used to initialize the object. +

    +

    + Invoked after population of normal object properties but before an + init callback such as + 's + + or a custom init-method. Invoked after the setting of any + 's + + property. +

    +
    + + In the case of application context initialization errors. + + + If thrown by any application context methods. + + +
    + + + Convenience base class for + implementations. + + +

    + Subclasses can override the + + method to change this behavior, so this is a useful/ base class for + implementations. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + Rick Evans (.NET) + Bruno Baia (.NET) + $Id: AbstractMethodInvocation.cs,v 1.8 2007/07/05 20:29:21 bbaia Exp $ +
    + + + Description of an invocation to a method, given to an interceptor + upon method-call. + + +

    + A method invocation is a joinpoint and can be intercepted by a method + interceptor. +

    +
    + +
    + + + Gets the method invocation that is to be invoked. + + +

    + This property is a friendly implementation of the + property. + It should be used in preference to the + property + because it provides immediate access to the underlying method + without the need to resort to a cast. +

    +
    + + The method invocation that is to be invoked. + +
    + + + Gets the proxy object for the invocation. + + + The proxy object for this method invocation. + + + + + Gets the target object for the invocation. + + + The target object for this method invocation. + + + + + Gets the type of the target object. + + + The type of the target object. + + + + + The arguments (if any = may be ) to the method + that is to be invoked. + + + + + The target object that the method is to be invoked on. + + + + + The AOP proxy for the target object. + + + + + The method invocation that is to be invoked. + + + + + The list of and + + that need dynamic checks. + + + + + The declaring type of the method that is to be invoked. + + + + + The index from 0 of the current interceptor we're invoking. + + + + + Creates a new instance of the + class. + + +

    + This is an abstract class, and as such exposes no publicly visible + constructors. +

    +

    + + The list can also contain any + s + that need evaluation at runtime. + s included in an + + must already have been found to have matched as far as was possible + statically. Passing an array might be about 10% faster, but + would complicate the code, and it would work only for static + pointcuts. + +

    +
    + The AOP proxy. + The target object. + the target method. + The target method's arguments. + + The of the target object. + + The list of interceptors that are to be applied. May be + . + + + If the is . + +
    + + + Proceeds to the next interceptor in the chain. + + + The return value of the method invocation. + + + If any of the interceptors at the joinpoint throws an exception. + + + + + + Retrieves a new instance + for the next Proceed method call. + + + The current instance. + + + The new instance to use. + + + + + + Invokes the joinpoint. + + +

    + Subclasses can override this to use custom invocation. +

    +
    + + The return value of the invocation of the joinpoint. + + + If invoking the joinpoint resulted in an exception. + + +
    + + + A that represents the current + invocation. + + +

    + + Does not invoke on the + target + object, as that too may be proxied. + +

    +
    + + A that represents the current invocation. + +
    + + + Gets the method invocation that is to be invoked. + + +

    + May or may not correspond with a method invoked on an underlying + implementation of that interface. +

    +
    + +
    + + + Gets the static part of this joinpoint. + + + The proxied member's information. + + + + + + Gets the proxy that this interception was made through. + + + The proxy that this interception was made through. + + + + + Gets the target object for the invocation. + + + The target object for this method invocation. + + + + + Gets the type of the target object. + + + The type of the target object. + + + + + Gets and sets the arguments (if any - may be ) + to the method that is to be invoked. + + + The arguments (if any - may be ) to the + method that is to be invoked. + + + + + + The list of method interceptors. + + +

    + May be . +

    +
    +
    + + + Gets the target object. + + + + + Sleeps for the appropriate amount of time for an exception. + + + + + Mark Pollack + $Id: RetryExceptionHandler.cs,v 1.1 2007/10/10 07:38:17 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The exception names. + + + + Handles the exception. + + + + The return value from handling the exception, if not rethrown or a new exception is thrown. + + + + + Gets the maximum retry count. + + The maximum retry count. + + + + Gets a value indicating whether this instance is delay based. + + + true if this instance is delay based; otherwise, false. + + + + + Gets or sets the delay time span to sleep after an exception is thrown and a rety is + attempted. + + The delay time span. + + + + Gets or sets the delay rate expression. + + The delay rate expression. + + + + Convenient class for building up pointcuts. + + +

    + All methods return a + instance, which facilitates the following concise usage pattern... +

    + + IPointcut pointcut = new ComposablePointcut() + .Union(typeFilter) + .Intersection(methodMatcher) + .Intersection(pointcut); + +

    + There is no Union() method on this class. Use the + method for such functionality. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: ComposablePointcut.cs,v 1.7 2007/03/16 04:01:23 aseovic Exp $ +
    + + + Creates a new instance of the + class + that matches all the methods on all s. + + + + + Creates a new instance of the + class + that uses the supplied and + . + + + The type filter to use. + + + The method matcher to use. + + + + + Changes the current type filter to be the union of the existing filter and the + supplied . + + The filter to union with. + + The union of the existing filter and the supplied . + + + + + Changes the current type filter to be the intersection of the existing filter + and the supplied . + + The filter to diff against. + + The intersection of the existing filter and the supplied . + + + + + Changes the current method matcher to be the union of the existing matcher and the + supplied . + + The matcher to union with. + + The union of the existing matcher and the supplied . + + + + + Changes the current method matcher to be the intersection of the existing matcher + and the supplied . + + The matcher to diff against. + + The intersection of the existing matcher and the supplied . + + + + + Changes current pointcut to intersection of the current and supplied pointcut + + pointcut to diff against + updated pointcut + + + + The for this pointcut. + + + The current . + + + + + The for this pointcut. + + + The current . + + + + + Advice that executes after a method returns successfully. + + +

    + After returning advice is invoked only on a normal method + return, but not if an exception is thrown. Such advice can see + the return value of the advised method invocation, but cannot change it. +

    +

    + Possible uses for this type of advice would include performing access + control checks on the return value of an advised method invocation, the + ubiquitous logging of method invocation return values (useful during + development), etc. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + + + + $Id: IAfterReturningAdvice.cs,v 1.6 2006/04/09 07:18:36 markpollack Exp $ +
    + + + Executes after + returns successfully. + + +

    + Note that the supplied cannot + be changed by this type of advice... use the around advice type + () if you + need to change the return value of an advised method invocation. + The data encapsulated by the supplied + can of course be modified though. +

    +
    + + The value returned by the . + + The intecepted method. + The intercepted method's arguments. + The target object. + +
    + + + Invokes a target method using standard reflection. + + Rod Johnson + Aleksandar Seovic (.NET) + Rick Evans (.NET) + Bruno Baia (.NET) + $Id: ReflectiveMethodInvocation.cs,v 1.18 2008/02/06 18:28:52 bbaia Exp $ + + + + The method invocation that is to be invoked on the proxy. + + + + + Creates a new instance of the + class. + + The AOP proxy. + The target object. + The target method proxied. + The method to invoke on proxy. + The target method's arguments. + + The of the target object. + + The list of interceptors that are to be applied. May be + . + + + If any of the or + parameters is . + + + + + Invokes the joinpoint using standard reflection. + + +

    + Subclasses can override this to use custom invocation. +

    +
    + + The return value of the invocation of the joinpoint. + + + If invoking the joinpoint resulted in an exception. + + +
    + + + Creates a new instance + from the specified and + increments the interceptor index. + + + The current instance. + + + The new instance to use. + + + + + Builds an AOP proxy type using inheritance. + + Bruno Baia + $Id: InheritanceAopProxyTypeBuilder.cs,v 1.2 2008/03/03 09:28:50 bbaia Exp $ + + + + AdvisedProxy instance calls should be delegated to. + + + + + Creates a new instance of the + class. + + The proxy configuration. + + + + Creates the proxy type. + + The generated proxy class. + + + + Generates the IL instructions that pushes + the target instance on which calls should be delegated to. + + The IL generator to use. + + + + Generates the IL instructions that pushes + the current + instance on stack. + + The IL generator to use. + + + + Declares field that holds the + instance used by the proxy. + + + The builder to use for code generation. + + + + + Implements serialization method. + + + + + + Implements serialization constructor. + + Type builder to use. + + + + Defines the types of the parameters for the specified constructor. + + The constructor to use. + The types for constructor's parameters. + + + + Generates the proxy constructor. + + +

    + This implementation creates instance of the AdvisedProxy object. +

    +
    + The constructor builder to use. + The IL generator to use. + The constructor to delegate the creation to. +
    + + + Implements interface. + + The type builder to use. + + + + Determines if the specified + is one of those generated by this builder. + + The type to check. + + if the type is a inheritance-based proxy; + otherwise . + + + + + Gets or sets a value indicating whether inherited members should be proxied. + + + if inherited members should be proxied; + otherwise, . + + + + + This class contains the results of parsing an advice expresion of the form + on exception name [ExceptionName1,ExceptionName2,...] [action] [action expression] + or + on exception [constraint expression] [action] [action expression] + + + + + Mark Pollack + $Id: ParsedAdviceExpression.cs,v 1.1 2007/10/08 22:05:16 markpollack Exp $ + + + + Initializes a new instance of the class. + + The advice expression. + + + + Gets or sets the advice expression. + + The advice expression. + + + + Gets or sets the exception names. + + The exception names. + + + + Gets or sets the constraint expression. + + The constraint expression. + + + + Gets or sets the action expression text. + + The action expression text. + + + + Gets or sets the action text. + + The action text. + + + + Gets or sets a value indicating whether this is success. + + true if success; otherwise, false. + + + + Implementation of a cache invalidation advice. + + +

    + This advice can be used to evict items from the cache. +

    +

    + Information that determines which items should be evicted and from which cache + are retrieved from the s that are defined + on the pointcut. +

    +

    + Items are evicted *after* target method is invoked. Return value of the method, + as well as method arguments, can be used to determine a list of keys for the items + that should be evicted (return value will be passed as a context for + expression evaluation, and method + arguments will be passed as variables, keyed by argument name). +

    +
    + + Aleksandar Seovic + $Id: InvalidateCacheAdvice.cs,v 1.4 2007/04/01 15:04:05 bbaia Exp $ +
    + + + Executes after + returns successfully. + + +

    + Note that the supplied cannot + be changed by this type of advice... use the around advice type + () if you + need to change the return value of an advised method invocation. + The data encapsulated by the supplied + can of course be modified though. +

    +
    + + The value returned by the . + + The intecepted method. + The intercepted method's arguments. + The target object. + +
    + + + Implementation of a result caching advice. + + +

    + This advice can be used to cache the return value of the method. +

    +

    + Parameters that determine where, how and for how long the return value + will be cached are retrieved from the and/or + that are defined on the pointcut. +

    +
    + + + Aleksandar Seovic + $Id: CacheResultAdvice.cs,v 1.6 2008/05/05 15:00:55 markpollack Exp $ +
    + + + Applies caching around a method invocation. + + +

    + This method tries to retrieve an object from the cache, using the supplied + to generate a cache key. If an object is found + in the cache, the cached value is returned and the method call does not + proceed any further down the invocation chain. +

    +

    + If object does not exist in the cache, the advised method is called (using + ) + and any return value is cached for the next method invocation. +

    +
    + + The method invocation that is being intercepted. + + + A cached object or the result of the + call. + + + If any of the interceptors in the chain or the target object itself + throws an exception. + +
    + + + Obtains return value either from cache or by invoking target method + and caches it if necessary. + + + The method invocation that is being intercepted. + + + Attribute specifying where and how to cache return value. Can be null, + in which case no caching of the result as a whole will be performed + (if the result is collection, individual items could still be cached separately). + + + Returns true if the return value was found in cache, false otherwise. + + + Return value for the specified . + + + + + Caches each item from the collection returned by target method. + + + A collection of items to cache. + + + Attributes specifying where and how to cache each item from the collection. + + + + + Defines miscellaneous filter operations. + + Rod Johnson + Aleksandar Seovic (.Net) + $Id: TypeFilters.cs,v 1.4 2007/03/16 04:01:25 aseovic Exp $ + + + + Creates a union of two filters. + + +

    + The filter arising from the union will match all of the + that either of the two supplied filters + would match. +

    +
    + + The first filter. + + + The second filter. + + + The union of the supplied filters. + +
    + + + Creates the intersection of two filters. + + +

    + The filter arising from the intersection will match all of the + that both of the two supplied filters + would match. +

    +
    + + The first filter. + + + The second filter. + + + The intersection of the supplied filters. + +
    + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such has no publicly visible constructors. +

    +
    +
    + + + Union class filter implementation. + + + + + Intersection implementation. + + + + + Convenient class for name-match method pointcuts that hold an Interceptor, + making them an Advisor. + + Juergen Hoeller + Aleksandar Seovic (.NET) + Mark Pollack (.NET) + $Id: NameMatchMethodPointcutAdvisor.cs,v 1.5 2007/05/30 22:35:43 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class + for the supplied . + + + + + + The for this pointcut. + + Default is + + The current . + + + + + Convenience property when we have only a single method name + to match. + + + + Use either this property or the + property, + not both. + + + + + + Set the method names defining methods to match. + + +

    + Matching will be the union of all these; if any match, the pointcut matches. +

    +
    +
    + + + The that drives this advisor. + + + + + Simple marker interface for throws advice. + + +

    + There are no methods on this interface, as methods are discovered and + invoked via reflection. Please do see read the API documentation for the + class; + said documention describes in detail the signature of the methods that + implementations of the interface + must adhere to in the specific case of Spring.NET's implementation of + throws advice. +

    +

    + There are any number of possible uses for this type of advice. Some + examples would include the ubiquitous logging of any such exceptions, + monitoring the number and type of exceptions and sending emails to + a support desk once certain criteria have been met, wrapping generic + exceptions such as in + exceptions that are more meaningful to your business logic, etc. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + + + + $Id: IThrowsAdvice.cs,v 1.5 2006/04/09 07:18:36 markpollack Exp $ +
    + + + Exception thrown when an attempt is made to use an unsupported + or + type. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: UnknownAdviceTypeException.cs,v 1.4 2006/04/09 07:18:35 markpollack Exp $ + + + + Creates a new instance of the + class. + + The advice that caused the exception. + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class with + the specified message. + + + A message about the exception. + + + + + Creates a new instance of the + class with + the specified message and root cause. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + implementation that creates a + new instance of the target object for each request. + + +

    + Can only be used in an object factory. +

    +
    + Rod Johnson + Spinazzi Federico (.NET) + $Id: PrototypeTargetSource.cs,v 1.3 2006/04/09 07:18:37 markpollack Exp $ + +
    + + + Returns the target object. + + The target object. + + If unable to obtain the target object. + + + + + Releases the target object. + + +

    + No-op implementation. +

    +
    + The target object to release. +
    + + + Is the target source static? + + + because this target source is never static. + + + + + Regular expression based pointcut object. + + +

    + Uses the regular expression classes from the .NET Base Class Library. +

    +

    + The regular expressions must be a match. For example, the + .*Get* pattern will match Com.Mycom.Foo.GetBar(), and + Get.* will not. +

    +
    + Rod Johnson + Simon White (.NET) +
    + + + Abstract base regular expression pointcut object. + + +

    + The regular expressions must be a match. For example, the + .*Get.* pattern will match Com.Mycom.Foo.GetBar(), and + Get.* will not. +

    +

    + This base class is serializable. Subclasses should decorate all + fields with the - the + + method in this class will be invoked again on the client side on deserialization. +

    +
    + Rod Johnson + Juergen Hoeller + Simon White (.NET) +
    + + + Creates a new instance of the + + class. + + +

    + This is an abstract class, and as such has no publicly + visible constructors. +

    +
    +
    + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + If an error was encountered during the deserialization process. + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + Subclasses must implement this to initialize regular expression pointcuts. + + +

    + Can be invoked multiple times. +

    +

    + This method will be invoked from the property, + and also on deserialization. +

    +
    + + The patterns to initialize. + + + In the case of an invalid pattern. + +
    + + + Does the pattern at the supplied + match this ? + + The pattern to match + The index of pattern. + + if there is a match. + + + + + Does the supplied satisfy this matcher? + + +

    + Try to match the regular expression against the fully qualified name + of the method's declaring , plus the name of + the supplied . +

    +

    + Note that the declaring is that + that originally declared + the method, not necessarily the that is + currently exposing it. For example, + matches any subclass of 's + method. +

    +
    + The candidate method. + + The target (may be , + in which case the candidate must be taken + to be the 's declaring class). + + + if this this method matches statically. + +
    + + + Should the pointcut apply to the supplied + ? + + +

    + In this instance, simply returns . +

    +
    + + The candidate . + + + if the advice should apply to the supplied + + +
    + + + The for this pointcut. + + + The current . + + + + + Convenience property for setting a single pattern. + + + Use this property or Patterns, not both. + + + + + The regular expressions defining methods to match. + + + Matching will be the union of all these; if any match, + the pointcut matches. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class, + using the supplied pattern or . + + + The intial pattern value(s) to be matched against. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + If an error was encountered during the deserialization process. + + + + + Initializes the regular expression pointcuts. + + +

    + Can be invoked multiple times. +

    +

    + This method will be invoked from the + property, + and also on deserialization. +

    +
    + + The patterns to initialize. + + + In the case of an invalid pattern. + + + If the supplied is . + +
    + + + Does the pattern at the supplied + match this ? + + The pattern to match + The index of pattern. + + if there is a match. + + + + + Gets or sets default options that should be used by + regular expressions that don't have options explicitly set. + + + Default options that should be used by regular expressions + that don't have options explicitly set. + + + + + Simple implementation that + by default applies to any class. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: DefaultIntroductionAdvisor.cs,v 1.11 2007/03/16 04:01:23 aseovic Exp $ + + + + Superinterface for advisors that perform one or more AOP + introductions. + + +

    + This interface cannot be implemented directly; subinterfaces must + provide the advice type implementing the introduction. +

    +

    + Introduction is the implementation of additional interfaces (not + implemented by a target) via AOP advice. +

    +
    + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: IIntroductionAdvisor.cs,v 1.8 2006/04/09 07:18:36 markpollack Exp $ +
    + + + Can the advised interfaces be implemented by the introduction + advice? + + +

    + Invoked before adding an + . +

    +
    + + If the advised interfaces cannot be implemented by the introduction + advice. + + +
    + + + Returns the filter determining which target classes this + introduction should apply to. + + +

    + This is the part of a pointcut. + Be advised that method matching doesn't make sense in the context + of introductions. +

    +
    + + The filter determining which target classes this introduction + should apply to. + +
    + + + Gets the interfaces introduced by this + . + + + The interfaces introduced by this + . + + + + + Creates a new instance of the + class using + the supplied + + +

    + This constructor adds all interfaces implemented by the supplied + (except the + interface) to the list of + interfaces to introduce. +

    +
    + The introduction to use. +
    + + + Creates a new instance of the + class using + the supplied + + The introduction to use. + + The interface to introduce. + + + + + Creates a new instance of the + class using + the supplied + + The introduction to use. + + The interfaces to introduce. + + + If the supplied is . + + + + + Adds the supplied to the list of + introduced interfaces. + + The interface to add. + + If any of the are not interface . + + + + + Should the pointcut apply to the supplied + ? + + +

    + This, the default, implementation always returns . +

    +
    + + The candidate . + + + if the advice should apply to the supplied + + +
    + + + Can the advised interfaces be implemented by the introduction + advice? + + +

    + Invoked before adding an + . +

    +
    + + If the advised interfaces cannot be implemented by the introduction + advice. + + + + If any of the are not interface . + +
    + + + Returns the filter determining which target classes this + introduction should apply to. + + + The filter determining which target classes this introduction + should apply to. + + + + + Gets the interfaces introduced by this + . + + + The interfaces introduced by this + . + + + + + Is this advice associated with a particular instance? + + +

    + Default for an introduction is per-instance interception. +

    +
    + + if this advice is associated with a + particular instance. + +
    + + + Return the advice part of this aspect. + + + The advice that should apply if the pointcut matches. + + + + + Summary description for AbstractPrototypeBasedTargetSourceCreator. + + + + + The logger + + + + + Create a special TargetSource for the given object, if any. + + the type of the object to create a TargetSource for + the name of the object + the containing factory + + a special TargetSource or null if this TargetSourceCreator isn't + interested in the particular object + + + + + Creates the prototype target source. + + The type of the object to create a target source for. + The name. + The factory. + + + + + implementation + to enable to be used in the + Spring.NET AOP framework. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: ThrowsAdviceAdapter.cs,v 1.4 2007/03/16 04:01:21 aseovic Exp $ + + + + Returns if the supplied + is an instance of the + interface. + + The advice to check. + + if the supplied is + an instance of the interface; + if not or if the supplied + is . + + + + + Wraps the supplied 's + within a + + instance. + + + The advisor exposing the that + is to be wrapped. + + + The supplied 's + wrapped within a + + instance. + + + + + Log the exceptions. Default log nameis "LogExceptionHandler" and log level is Debug + + Mark Pollack + $Id: LogExceptionHandler.cs,v 1.6 2008/02/26 00:03:24 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The exception names. + + + + Handles the exception. + + the calling context dictionary + + The return value from handling the exception, if not rethrown or a new exception is thrown. + + + + + Gets or sets the name of the log. + + The name of the log. + + + + Implementation of a parameter caching advice. + + +

    + This advice can be used to cache the parameter of the method. +

    +

    + Information that determines where, how and for how long the return value + will be cached are retrieved from the s + that are defined on the pointcut. +

    +

    + Parameter values are cached *after* the target method is invoked in order to + capture any parameter state changes it might make (for example, it is common + to set an object identifier within the save method for the persistent entity). +

    +
    + + Aleksandar Seovic + $Id: CacheParameterAdvice.cs,v 1.7 2008/05/05 15:00:55 markpollack Exp $ +
    + + + Executes after target + returns successfully. + + +

    + Note that the supplied cannot + be changed by this type of advice... use the around advice type + () if you + need to change the return value of an advised method invocation. + The data encapsulated by the supplied + can of course be modified though. +

    +
    + + The value returned by the . + + The intecepted method. + The intercepted method's arguments. + The target object. + +
    + + + Caching aspect implementation. + + + This class encapsulates all the advisors that need to be configured to support + Spring's full caching functionality. + + + + + Aleksandar Seovic + $Id: CacheAspect.cs,v 1.2 2007/08/03 14:38:35 markpollack Exp $ + + + + AOP Aspect abstraction, holding a list of s + + + Aleksandar Seovic (.NET) + $Id: IAdvisors.cs,v 1.1 2007/08/03 14:38:30 markpollack Exp $ + + + + Gets or sets a list of advisors. + + + A list of advisors. + + + + + Creates a new instance. + + + + + Gets or sets a list of advisors for this aspect. + + + A list of advisors for this aspect. + + + + + Set the that this + object runs in. + + +

    + Normally this call will be used to initialize the object. +

    +

    + Invoked after population of normal object properties but before an + init callback such as + 's + + or a custom init-method. Invoked after the setting of any + 's + + property. +

    +
    + + In the case of application context initialization errors. + + + If thrown by any application context methods. + + +
    + + + The to be used + when there is no target object, and behavior is supplied by the + advisors. + + +

    + This class is exposed as a singleton. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: EmptyTargetSource.cs,v 1.4 2007/10/08 22:04:51 markpollack Exp $ +
    + + + The to be used + when there is no target object, and behavior is supplied by the + advisors. + + + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such has no publicly visible constructors. +

    +
    +
    + + + Returns the target object. + + The target object. + + If unable to obtain the target object. + + + + + Releases the target object. + + + + This is a no-op operation in this implementation. + + + The target object to release. + + + + A that represents the current + . + + + A that represents the current + . + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + The of the target object. + + + + + Is the target source static? + + +

    + The + instance is static, and this always returns . +

    +
    + + if the target source is static. + +
    + + + Advice executed before a method is invoked. + + +

    + Such advice cannot prevent the method call proceeding, short of + throwing an . +

    +

    + The main advantage of before advice is that there is no + possibility of inadvertently failing to proceed down the interceptor + chain, since there is no need (and indeed means) to invoke the next + interceptor in the call chain. +

    +

    + Possible uses for this type of advice would include performing class + invariant checks prior to the actual method invocation, the ubiquitous + logging of method invocations (useful during development), etc. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + + + + + $Id: IMethodBeforeAdvice.cs,v 1.6 2006/04/09 07:18:36 markpollack Exp $ +
    + + + The callback before a given method is invoked. + + The method being invoked. + The arguments to the method. + + The target of the method invocation. May be . + + + Thrown when and if this object wishes to abort the call. Any + exception so thrown will be propagated to the caller. + + + + + Decorates a target source with the + interface. + + +

    + Because the target source is static, the target object can be cached + and simply returned as is. +

    +
    + Aleksandar Seovic + $Id: StaticTargetSourceWrapper.cs,v 1.3 2007/03/16 04:01:20 aseovic Exp $ +
    + + + Creates a new instance of the + + class. + + + The target object that proxy methods will be delegated to. + + + If the supplied is + . + + + + + Returns the target object that proxy methods will be delegated to. + + The target object. + + + + Performs application-defined tasks associated with freeing, + releasing, or resetting unmanaged resources. + + + + This is a no-op operation in this implementation. + + + + + + implementation + that registers instances of any non-default + instances with the + + singleton. + + +

    + The only requirement for it to work is that it needs to be defined + in an application context along with any arbitrary "non-native" Spring.NET + instances that need + to be recognized by Spring.NET's AOP framework. +

    +
    + Dmitriy Kopylenko + Aleksandar Seovic (.NET) + $Id: AdvisorAdapterRegistrationManager.cs,v 1.5 2007/08/22 08:49:08 markpollack Exp $ +
    + + + Apply this + to the given new object instance before any object initialization callbacks. + + +

    + Does nothing, simply returns the supplied as is. +

    +
    + + The new object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + In case of errors. + +
    + + + Apply this to the + given new object instance after any object initialization callbacks. + + +

    + Registers the supplied with the + + singleton if it is an + instance. +

    +
    + + The new object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + In case of errors. + +
    + + + AOP Advice to retry a method invocation on an exception. The retry semantics are defined by a DSL of the + form on exception name [ExceptionName1,ExceptionName2,...] retry [number of times] [delay|rate] [delay time|rate expression]. + For example, on exception name ArithmeticException retry 3x delay 1s + + + + + Mark Pollack + $Id: RetryAdvice.cs,v 1.5 2008/03/17 20:25:34 markpollack Exp $ + + + + Implement this method to perform extra treatments before and after + the call to the supplied . + + The method invocation that is being intercepted. + + The result of the call to the + method of + the supplied ; this return value may + well have been intercepted by the interceptor. + + +

    + Polite implementations would certainly like to invoke + . +

    +
    + + If any of the interceptors in the chain or the target object itself + throws an exception. + +
    + + + Invoked by an + after it has injected all of an object's dependencies. + + +

    + This method allows the object instance to perform the kind of + initialization only possible when all of it's dependencies have + been injected (set), and to throw an appropriate exception in the + event of misconfiguration. +

    +

    + Please do consult the class level documentation for the + interface for a + description of exactly when this method is invoked. In + particular, it is worth noting that the + + and + callbacks will have been invoked prior to this method being + called. +

    +
    + + In the event of misconfiguration (such as the failure to set a + required property) or if initialization fails. + +
    + + + Parses the specified handler string. + + The handler string. + + + + + Gets the match for action expression. + + The action expression string. + The regex string. + The Match object resulting from the regular expression match. + + + + Gets or sets the retry expression. + + The retry expression. + + + + Gets or sets the Regex string used to parse advice expressions starting with 'on exception name' and exception handling actions. + + The regex string to parse advice expressions starting with 'on exception name' and exception handling actions. + + + + Gets or sets the Regex string used to parse advice expressions starting with 'on exception (constraint)' and exception handling actions. + + The regex string to parse advice expressions starting with 'on exception (constraint)' and exception handling actions. + + + + Convinience advisor implementation that applies + to all the methods that have defined on one or + more of their parameters. + + Aleksandar Seovic + $Id: CacheParameterAdvisor.cs,v 1.1 2007/02/16 03:12:56 aseovic Exp $ + + + + Creates new advisor instance. + + + + + Returns true if any of the parameters of the specified + has applied. + + + Method to check. + + + Type of target object. + + + true if any of the parameters of the specified + has applied; false otherwise. + + + + + Set the that this + object runs in. + + +

    + Normally this call will be used to initialize the object. +

    +

    + Invoked after population of normal object properties but before an + init callback such as + 's + + or a custom init-method. Invoked after the setting of any + 's + + property. +

    +
    + + In the case of application context initialization errors. + + + If thrown by any application context methods. + + +
    + + + Convinience advisor implementation that applies + to all the methods that have defined. + + Aleksandar Seovic + $Id: CacheResultAdvisor.cs,v 1.1 2007/02/16 03:13:01 aseovic Exp $ + + + + Creates new advisor instance. + + + + + Returns true if either or + is applied to the + . + + + Method to check. + + + Type of target object. + + + true if either or + is applied to the + ; false otherwise. + + + + + Set the that this + object runs in. + + +

    + Normally this call will be used to initialize the object. +

    +

    + Invoked after population of normal object properties but before an + init callback such as + 's + + or a custom init-method. Invoked after the setting of any + 's + + property. +

    +
    + + In the case of application context initialization errors. + + + If thrown by any application context methods. + + +
    + + + implementation to + source AOP proxies from a Spring.NET IoC container (an + ). + + +

    + s and + s are identified by a list of object + names in the current container.

    +

    + Global interceptors and advisors can be added at the factory level + (that is, outside the context of a + definition). The + specified interceptors and advisors are expanded in an interceptor list + (see + ) + where an 'xxx*' wildcard-style entry is included in the list, + matching the given prefix with the object names. For example, + 'global*' would match both 'globalObject1' and + 'globalObjectBar', and '*' would match all defined + interceptors. The matching interceptors get applied according to their + returned order value, if they implement the + interface. An interceptor name list + may not conclude with a global 'xxx*' pattern, as global + interceptors cannot invoke targets. +

    +

    + It is possible to cast a proxy obtained from this factory to an + reference, or to obtain the + reference and + programmatically manipulate it. This won't work for existing prototype + references, which are independent... however, it will work for prototypes + subsequently obtained from the factory. Changes to interception will + work immediately on singletons (including existing references). + However, to change interfaces or the target it is necessary to obtain a + new instance from the surrounding container. This means that singleton + instances obtained from the factory do not have the same object + identity... however, they do have the same interceptors and target, and + changing any reference will change all objects. +

    +
    + Rod Johnson + Juergen Hoeller + Federico Spinazzi (.NET) + Choy Rim (.NET) + Mark Pollack (.NET) + Aleksandar Seovic (.NET) + $Id: ProxyFactoryObject.cs,v 1.26 2007/08/03 14:38:30 markpollack Exp $ + + + + + +
    + + + This suffix in a value in an interceptor list indicates to expand globals. + + + + + The shared instance for this class. + + + + + Is the object managed by this factory a singleton or a prototype? + + + + + The cached instance if this proxy factory object is a singleton. + + + + + The owning object factory (which cannot be changed after this object is initialized). + + + + + The mapping from an or interceptor + to an object name (or ), depending on where it was + sourced from. + + +

    + If it's sourced from object name, it will need to be + refreshed each time a new prototype instance is created. +

    +
    +
    + + + Names of interceptors and pointcut objects in the factory. + + +

    + Default is for globals expansion only. +

    +
    +
    + + + Names of introductions and pointcut objects in the factory. + + +

    + Default is for globals expansion only. +

    +
    +
    + + + The name of the target object(in the enclosing + ). + + + + + Creates an instance of the AOP proxy to be returned by this factory + + +

    + Invoked when clients obtain objects from this factory object. The + (proxy) instance will be cached for a singleton, and created on each + call to + for a prototype. +

    +
    + + A fresh AOP proxy reflecting the current state of this factory. + + +
    + + Create the advisor (interceptor) chain. + + The advisors that are sourced from an ObjectFactory will be refreshed each time + a new prototype instance is added. Interceptors added programmatically through + the factory API are unaffected by such changes. + + + + + Configures introductions for this proxy. + + + + Add all global interceptors and pointcuts. + + + Add all global introductions. + + + Add the given interceptor or pointcut to the interceptor list. + interceptor or pointcut to add + object name from which we obtained this object in our owning object factory + + + Add the introduction to the introduction list. + + If specified parameter is IIntroducionAdvisor it is added directly, otherwise it is wrapped + with DefaultIntroductionAdvisor first. + + introducion to add + object name from which we obtained this object in our owning object factory + + + Refresh named objects from the interceptor chain. + We need to do this every time a new prototype instance is returned, + to return distinct instances of prototype interfaces and pointcuts. + + + + + Refreshes target object for prototype instances. + + + + Refresh named objects from the interceptor chain. + We need to do this every time a new prototype instance is returned, + to return distinct instances of prototype interfaces and pointcuts. + + + + Wraps pointcut or interceptor with appropriate advisor + pointcut or interceptor that needs to be wrapped with advisor + Advisor + + + Wraps target with SingletonTargetSource if necessary + target or target source object + target source passed or target wrapped with SingletonTargetSource + + + Wraps introduction with IIntroductionAdvisor if necessary + object to wrap + Introduction advisor + + + + + + + No need to do anything when advice change, proxy can handle those changes by itself. + + + + + Implementation of listener for AdvisedSupport.InterfacesChanged event + event source + + + + Sets the names of the interfaces that are to be implemented by the proxy. + + + The names of the interfaces that are to be implemented by the proxy. + + + If the supplied value (or any of its elements) is ; + or if any of the element values is not the (assembly qualified) name of + an interface type. + + + + + Sets the name of the target object being proxied. + + +

    + Only works when the + + property is set; it is a logic error on the part of the programmer + if this value is set and the accompanying + is not also set. +

    +
    + + The name of the target object being proxied. + +
    + + + Sets the list of and + object names. + + +

    + This property must always be set (configured) when using a + in an + context. +

    +
    + + The list of and + object names. + + + + + +
    + + + Sets the list of introduction object names. + + +

    + Only works when the + + property is set; it is a logic error on the part of the programmer + if this value is set and the accompanying + is not supplied. +

    +
    + + The list of introduction object names. . + +
    + + + Callback that supplies the owning factory to an object instance. + + + Owning + (may not be ). The object can immediately + call methods on the factory. + + + In case of initialization errors. + + + + + + + Return the of the proxy. + + + Will check the singleton instance if already created, + else fall back to the proxy interface (if a single one), + the target bean type, or the TargetSource's target class. + + Return the of object that this + creates, or + if not known in advance. + + + + Is the object managed by this factory a singleton or a prototype? + + + + + implementation + that delegates method calls to target object. + + Aleksandar Seovic + Bruno Baia + $Id: TargetAopProxyMethodBuilder.cs,v 1.3 2008/01/29 18:24:50 markpollack Exp $ + + + + The local variable to store + the instance. + + + + + Creates a new instance of the method builder. + + The type builder to use. + + The implementation to use. + + + if the interface is to be + implemented explicitly; otherwise . + + + The dictionary to cache the list of target + s. + + + + + Creates local variable declarations. + + The IL generator to use. + The method to proxy. + + + + Generates method logic. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + Calls proxied method directly. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + Configurable advice for logging. + + + + + Mark Pollack + $Id: SimpleLoggingAdvice.cs,v 1.7 2008/04/04 15:30:13 markpollack Exp $ + + + + Flag to indicate if unique identifier should be in the log message. + + + + + Flag to indicate if the execution time should be in the log message. + + + + + Flag to indicate if the method arguments should be in the log message. + + + + + Flag to indicate if the return value should be in the log message. + + + + + The separator string to use for delmiting log message fields. + + + + + The log level to use for logging the entry, exit, exception messages. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + if set to true to use dynamic logger, if + false use static logger. + + + + Subclasses must override this method to perform any tracing around the supplied + IMethodInvocation. + + The method invocation to log + The log to write messages to + + The result of the call to IMethodInvocation.Proceed() + + + Subclasses are resonsible for ensuring that the IMethodInvocation actually executes + by calling IMethodInvocation.Proceed(). + + By default, the passed-in ILog instance will have log level + "trace" enabled. Subclasses do not have to check for this again, unless + they overwrite the IsInterceptorEnabled method to modify + the default behavior. + + + + If any of the interceptors in the chain or the target object itself + throws an exception. + + + + + Determines whether the given log is enabled. + + The log instance to check. + + true if log is for a given log level; otherwise, false. + + + Default is true when the trace level is enabled. Subclasses may override this + to change the level at which logging occurs, or return true to ignore level + checks. + + + + Creates a unique identifier. + + + Default implementation uses Guid.NewGuid(). Subclasses may override to provide an alternative + ID generation implementation. + + A unique identifier + + + + Gets the entry message to log + + The invocation. + The id string. + The entry log message + + + + Gets the exception message. + + The method invocation. + The thown exception. + The execution time span. + The id string. + The exception log message. + + + + Gets the exit log message. + + The method invocation. + The return value. + The execution time span. + The id string. + the exit log message + + + + Appends common information across entry,exit, exception logging + + Add method name and unique identifier if required. + The string buffer building logging message. + The method invocation. + The unique identifier string. + + + + Gets the method argument as argumen name/value pairs. + + The method invocation. + string for logging method argument name and values. + + + + Gets or sets a value indicating whether to log a unique identifier with the log message. + + true if [log unique identifier]; otherwise, false. + + + + Gets or sets a value indicating whether to log execution time. + + true if log execution time; otherwise, false. + + + + Gets or sets a value indicating whether log method arguments. + + true if log method arguments]; otherwise, false. + + + + Gets or sets a value indicating whether log return value. + + true if log return value; otherwise, false. + + + + Gets or sets the seperator string to use for delmiting log message fields. + + The seperator. + + + + Gets or sets the entry log level. + + The entry log level. + + + + Evaluates the expression for the return value of the method. + + Mark Pollack + $Id: ReturnValueExceptionHandler.cs,v 1.3 2007/10/10 18:07:46 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The exception names. + + + + Returns the result of evaluating the translation expression. + + The return value from handling the exception, if not rethrown or a new exception is thrown. + + + + Canonical instance that matches + everything. + + Rod Johnson + Aleksandar Seovic (.NET) + + + + Canonical instance that matches everything. + + + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such has no publicly + visible constructors. +

    +
    +
    + + + A that represents the current + . + + + A that represents the current + . + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + The for this pointcut. + + + The current . + + + + + The for this pointcut. + + + The current . + + + + + Convenient superclass for s that + are also static pointcuts. + + Rod Johnson + Aleksandar Seovic (.Net) + $Id: StaticMethodMatcherPointcutAdvisor.cs,v 1.6 2006/04/09 07:18:37 markpollack Exp $ + + + + Creates a new instance of the + + class. + + +

    + This is an abstract class, and as such has no publicly + visible constructors. +

    +
    +
    + + + Creates a new instance of the + + class for the supplied + + +

    + This is an abstract class, and as such has no publicly + visible constructors. +

    +
    + + The advice to use. + +
    + + + Is this advice associated with a particular instance? + + + if this advice is associated with a + particular instance. + + + Always; this property is not yet supported. + + + + + Returns this s order in the + interception chain. + + + This s order in the + interception chain. + + + + + Return the advice part of this advisor. + + + The advice that should apply if the pointcut matches. + + + + + + The that drives this advisor. + + + + + Convenient class for regular expression method pointcuts that hold an + , making them an + . + + +

    + Configure this class using the and + pass-through properties. These are analogous + to the and + s properties of the + class. +

    +

    + Can delegate to any type of regular expression pointcut. Currently only + pointcuts based on the regular expression classes from the .NET Base + Class Library are supported. The + + property must be a subclass of the + class. +

    +

    + This should not normally be set directly. +

    +
    + Dmitriy Kopylenko + Rod Johnson + Simon White (.NET) + Mark Pollack (.NET) + +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class for the supplied . + + The target advice. + + + + + Initialises the pointcut. + + + + + A single pattern to be used during method evaluation. + + + + + Multiple patterns to be used during method evaluation. + + + + + The that drives this advisor. + + + + + Various related utility methods. + + +

    + These methods are particularly useful for composing pointcuts + using the union and intersection methods. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: Pointcuts.cs,v 1.6 2007/03/16 04:01:25 aseovic Exp $ +
    + + + Creates a union of the two supplied pointcuts. + + The first pointcut. + The second pointcut. + + The union of the two supplied pointcuts. + + + + + + Creates an that is the + intersection of the two supplied pointcuts. + + The first pointcut. + The second pointcut. + + An that is the + intersection of the two supplied pointcuts. + + + + + Performs the least expensive check for a match. + + + The to be evaluated. + + The candidate method. + + The target . + + The arguments to the method + if there is a runtime match. + + + + + Are the supplied s equal? + + The first pointcut. + The second pointcut. + + if the supplied s + are equal. + + + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such has no publicly + visible constructors. +

    +
    +
    + + + implementation that delegates + method calls to an instance. + + Bruno Baia + $Id: IAdvisedProxyMethodBuilder.cs,v 1.3 2006/11/12 01:37:47 bbaia Exp $ + + + + The implementation to use. + + + + + Creates a new instance of the method builder. + + The type builder to use. + + The implementation to use. + + + + + Generates the IL instructions that pushes + the target instance on which calls should be delegated to. + + The IL generator to use. + + + + implementation + that delegates method calls to the base method. + + Bruno Baia + $Id: BaseAopProxyMethodBuilder.cs,v 1.1 2008/02/06 18:28:52 bbaia Exp $ + + + + Creates a new instance of the method builder. + + The type builder to use. + + The implementation to use. + + + The dictionary to cache the list of target + s. + + + The dictionary to cache the list of target + s defined on the proxy. + + + + + Create static field that will cache target method when defined on the proxy. + + The IL generator to use. + The target method. + + + + Calls target method directly. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + Implementation of the + interface that caches the AOP proxy instance. + + +

    + Caches against a key based on : + - the base type + - the target type + - the interfaces to proxy +

    +
    + Bruno Baia + Erich Eichinger + + + $Id: CachedAopProxyFactory.cs,v 1.1 2007/08/02 04:15:21 markpollack Exp $ +
    + + + The shared instance for this class. + + + + + Generates the proxy type and caches the + instance against the base type and the interfaces to proxy. + + + The to use + + The generated or cached proxy class. + + + + Uniquely identifies a proxytype in the cache + + + + + Utility methods used by the AOP framework. + + +

    + Not intended to be used directly by applications. +

    +
    + Rod Johnson + Juergen Hoeller + Aleksandar Seovic (.NET) + $Id: AopUtils.cs,v 1.4 2007/10/10 18:07:38 markpollack Exp $ +
    + + + Is the supplied an AOP proxy? + + + Return whether the given object is either + a composition-based proxy or a decorator-based proxy. + + The instance to be checked. + + if the supplied is + an AOP proxy. + + + + + Is the supplied a composition-based AOP proxy? + + The instance to be checked. + + if the supplied is + an composition-based AOP proxy. + + + + + Is the supplied a decorator-based AOP proxy? + + The instance to be checked. + + if the supplied is + an decorator-based AOP proxy. + + + + + Gets all of the interfaces that the of the + supplied implements. + + +

    + This includes interfaces implemented by any superclasses. +

    +
    + + The object to analyse for interfaces. + + + All of the interfaces that the of the + supplied implements; or an empty + array if the supplied is + . + +
    + + + Can the supplied apply at all on the + supplied ? + + +

    + This is an important test as it can be used to optimize out a + pointcut for a class. +

    +

    + Invoking this method with a that is + an interface type will always yield a + return value. +

    +
    + The pointcut being tested. + The class being tested. + + The interfaces being proxied. If , all + methods on a class may be proxied. + + + if the pointcut can apply on any method. + +
    + + + Can the supplied apply at all on the + supplied ? + + +

    + This is an important test as it can be used to optimize out an + advisor for a class. +

    +
    + The advisor to check. + The class being tested. + + The interfaces being proxied. If , all + methods on a class may be proxied. + + + if the advisor can apply on any method. + +
    + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such has no publicly + visible constructors. +

    +
    +
    + + + Gets the type of the target. + + The candidate. + + + + + This advice is typically applied to service-layer methods in order to validate + method arguments. + + + Each argument that should be validated has to be marked with one or more + s. + If the validation fails, this advice will throw , + thus preventing target method invocation. + + + Damjan Tomic + Aleksandar Seovic + $Id: ParameterValidationAdvice.cs,v 1.2 2008/04/02 23:00:28 markpollack Exp $ + + + + Intercepts method invocation and validates arguments. + + + Method invocation. + + + Method arguments. + + + Target object. + + + If one or more method arguments fail validation. + + + + + Gets or sets the application context to search for validators in. + + + The application context to search for validators in. + + + + + Various utility methods relating to the composition of + s. + + +

    + A method matcher may be evaluated statically (based on method and target + class) or need further evaluation dynamically (based on arguments at + the time of method invocation). +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: MethodMatchers.cs,v 1.7 2007/03/16 04:01:24 aseovic Exp $ +
    + + + Creates a new that is the + union of the two supplied s. + + +

    + The newly created matcher will match all the methods that either of the two + supplied matchers would match. +

    +
    + The first method matcher. + The second method matcher. + + A new that is the + union of the two supplied s + +
    + + + Creates a new that is the + intersection of the two supplied s. + + +

    + The newly created matcher will match only those methods that both + of the supplied matchers would match. +

    +
    + The first method matcher. + The second method matcher. + + A new that is the + intersection of the two supplied s + +
    + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such has no publicly + visible constructors. +

    +
    +
    + + + Builds an AOP proxy type using composition. + + Aleksandar Seovic + Bruno Baia + $Id: CompositionAopProxyTypeBuilder.cs,v 1.15 2007/12/07 17:58:56 bbaia Exp $ + + + + Creates a new instance of the + class. + + The proxy configuration. + + + + Creates the proxy type. + + The generated proxy type. + + + + Generates the IL instructions that pushes + the current + instance on stack. + + The IL generator to use. + + + + Implements serialization constructor. + + Type builder to use. + + + + Implements constructors for the proxy class. + + +

    + This implementation calls the base constructor. +

    +
    + + The builder to use. + +
    + + + Determines if the specified + is one of those generated by this builder. + + The type to check. + + if the type is a composition-based proxy; + otherwise . + + + + + Utility methods for use by + implementations. + + +

    + Not intended to be used directly by applications. +

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: AdvisorChainFactoryUtils.cs,v 1.7 2006/04/09 07:18:35 markpollack Exp $ +
    + + + Gets the list of + interceptors and dynamic interception + advice that may apply to the supplied + invocation. + + The proxy configuration. + The object proxy. + + The method to evaluate interceptors for. + + + The of the target object. + + + A of + (if there's + a dynamic method matcher that needs evaluation at runtime). + + + + + Creates a new instance of the + + class. + + +

    + This is a utility class, and as such has no publicly visible + constructors. +

    +
    +
    + + + implementation + to enable to be used in the + Spring.NET AOP framework. + + Rod Johnson + Aleksandar Seovic (.NET) + $Id: AfterReturningAdviceAdapter.cs,v 1.5 2007/03/16 04:01:20 aseovic Exp $ + + + + Returns if the supplied + is an instance of the + interface. + + The advice to check. + + if the supplied is + an instance of the interface; + if not or if the supplied + is . + + + + + Wraps the supplied 's + within a + + instance. + + + The advisor exposing the that + is to be wrapped. + + + The supplied 's + wrapped within a + + instance. + + + + + Invokes a target method using dynamic reflection. + + + Aleksandar Seovic + Bruno Baia + $Id: DynamicMethodInvocation.cs,v 1.3 2008/02/06 18:28:52 bbaia Exp $ + + + + The method invocation that is to be invoked on the proxy. + + + + + Creates a new instance of the + class. + + The AOP proxy. + The target object. + The target method proxied. + The method to invoke on proxy. + The target method's arguments. + + The of the target object. + + The list of interceptors that are to be applied. May be + . + + + If any of the or + parameters is . + + + + + Invokes the joinpoint using dynamic reflection. + + +

    + Subclasses can override this to use custom invocation. +

    +
    + + The return value of the invocation of the joinpoint. + + + If invoking the joinpoint resulted in an exception. + + +
    + + + Creates a new instance + from the specified and + increments the interceptor index. + + + The current instance. + + + The new instance to use. + + +
    +
    diff --git a/src/Spring/Spring.Core/AssemblyInfo.cs b/src/Spring/Spring.Core/AssemblyInfo.cs new file mode 100644 index 00000000..d6adf3f5 --- /dev/null +++ b/src/Spring/Spring.Core/AssemblyInfo.cs @@ -0,0 +1,34 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.Permissions; +[assembly: ComVisible(false)] +[assembly: AssemblyTitle("Spring.Net Core functionality")] +[assembly: AssemblyDescription("Core functionality for Spring.Net IoC container")] + +// +// Security Permissions +// +// we need full, unrestricted access to reflection metadata... +[assembly: ReflectionPermission(SecurityAction.RequestMinimum, Unrestricted=true)] +//[assembly: AssemblyKeyFile(@"C:\users\aseovic\projects\OpenSource\Spring.Net\Spring.Net.PrivateKey.keys")] +//[assembly: AssemblyKeyFile(@"C:\projects\Spring.Net\Spring.Net.snk")] \ No newline at end of file diff --git a/src/Spring/Spring.Core/Caching/AbstractCache.cs b/src/Spring/Spring.Core/Caching/AbstractCache.cs new file mode 100644 index 00000000..94c5c746 --- /dev/null +++ b/src/Spring/Spring.Core/Caching/AbstractCache.cs @@ -0,0 +1,202 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +namespace Spring.Caching +{ + /// + /// An abstract implementation that can + /// be used as base class for concrete implementations. + /// + /// Aleksandar Seovic + /// Erich Eichinger + /// $Id: AbstractCache.cs,v 1.7 2007/10/11 01:29:49 markpollack Exp $ + public abstract class AbstractCache : ICache + { + #region Fields + + private bool _enforceTimeToLive = false; + private TimeSpan _timeToLive = TimeSpan.Zero; + + #endregion + + #region Properties + + /// + /// Gets/Set the Default time-to-live (TTL) for items inserted into this cache. + /// Used by + /// + public TimeSpan TimeToLive + { + get { return _timeToLive; } + set { _timeToLive = value; } + } + + /// + /// Gets/Sets a value, whether the this cache instance's + /// shall be applied to all items, regardless of their individual TTL + /// when is called. + /// + public bool EnforceTimeToLive + { + get { return _enforceTimeToLive; } + set { _enforceTimeToLive = value; } + } + + #endregion + + #region ICache Implementation + + #region + + /// + /// Gets the number of items in the cache. + /// + /// + /// May be overridden by subclasses for cache-specific efficient implementation. + /// + public virtual int Count + { + get + { + return Keys.Count; + } + } + + /// + /// Gets a collection of all cache item keys. + /// + public abstract ICollection Keys { get; } + + + + /// + /// Retrieves an item from the cache. + /// + /// + /// Item key. + /// + /// + /// Item for the specified , or null. + /// + public abstract object Get(object key); + + /// + /// Removes an item from the cache. + /// + /// + /// Item key. + /// + public abstract void Remove(object key); + + /// + /// Removes collection of items from the cache. + /// + /// + /// Collection of keys to remove. + /// + public virtual void RemoveAll(ICollection keys) + { + foreach (object key in keys) + { + Remove(key); + } + } + + /// + /// Removes all items from the cache. + /// + public virtual void Clear() + { + RemoveAll(this.Keys); + } + + + /// + /// Inserts an item into the cache. + /// + /// + /// Items inserted using this method use the default + /// + /// + /// Item key. + /// + /// + /// Item value. + /// + public virtual void Insert(object key, object value) + { + Insert(key, value, TimeToLive); + } + + /// + /// Inserts an item into the cache. + /// + /// + /// If equals , + /// or is true, this cache + /// instance's value will be applied. + /// + /// + /// Item key. + /// + /// + /// Item value. + /// + /// + /// Item's time-to-live (TTL). + /// + public virtual void Insert(object key, object value, TimeSpan timeToLive) + { + if (_enforceTimeToLive || (timeToLive < TimeSpan.Zero)) + { + timeToLive = _timeToLive; + } + DoInsert(key, value, timeToLive); + } + + #endregion + + #endregion + + #region Methods + + /// + /// Actually does the cache implementation specific insert operation into the cache. + /// + /// + /// Items inserted using this method have default cache priority. + /// + /// + /// Item key. + /// + /// + /// Item value. + /// + /// + /// Item's time-to-live (TTL). + /// + protected abstract void DoInsert(object key, object value, TimeSpan timeToLive); + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs b/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs new file mode 100644 index 00000000..c88378e8 --- /dev/null +++ b/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs @@ -0,0 +1,176 @@ +using System; +using Spring.Core.TypeConversion; +using Spring.Expressions; + +namespace Spring.Caching +{ + /// + /// Abstract base class containing shared properties for all cache attributes. + /// + /// Aleksandar Seovic + /// $Id: BaseCacheAttribute.cs,v 1.5 2007/08/29 17:29:10 oakinger Exp $ + [Serializable] + public abstract class BaseCacheAttribute : Attribute + { + #region Fields + + /// + /// The instance used to parse values. + /// + /// + /// + protected static readonly TimeSpanConverter TimeSpanConverter = new TimeSpanConverter(); + + private string cacheName; + private string key; + private IExpression keyExpression; + private string condition; + private IExpression conditionExpression; + private string timeToLive = null; + private TimeSpan timeToLiveTimeSpan = TimeSpan.MinValue; + + #endregion + + #region Constructors + + /// + /// Creates an attribute instance. + /// + public BaseCacheAttribute() + { + } + + /// + /// Creates an attribute instance. + /// + /// + /// The name of the cache to use. + /// + /// + /// An expression string that should be evaluated in order to determine + /// the cache key for the item. + /// + public BaseCacheAttribute(string cacheName, string key) + { + this.CacheName = cacheName; + this.Key = key; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the name of the cache to use. + /// + /// + /// The name of the cache to use. + /// + public string CacheName + { + get { return cacheName; } + set { cacheName = value; } + } + + /// + /// Gets or sets a SpEL expression that should be evaluated in order + /// to determine the cache key for the item. + /// + /// + /// An expression string that should be evaluated in order to determine + /// the cache key for the item. + /// + public string Key + { + get { return key; } + set + { + key = value; + keyExpression = Expression.Parse(value); + } + } + + /// + /// Gets an expression instance that should be evaluated in order + /// to determine the cache key for the item. + /// + /// + /// An expression instance that should be evaluated in order to determine + /// the cache key for the item. + /// + public IExpression KeyExpression + { + get { return keyExpression; } + } + + /// + /// Gets or sets a SpEL expression that should be evaluated in order + /// to determine whether the item should be cached. + /// + /// + /// An expression string that should be evaluated in order to determine + /// whether the item should be cached. + /// + public string Condition + { + get { return condition; } + set + { + condition = value; + conditionExpression = Expression.Parse(value); + } + } + + /// + /// Gets an expression instance that should be evaluated in order + /// to determine whether the item should be cached. + /// + /// + /// An expression instance that should be evaluated in order to determine + /// whether the item should be cached. + /// + public IExpression ConditionExpression + { + get { return conditionExpression; } + } + + /// + /// The amount of time an object should remain in the cache. + /// + /// + /// If no TTL is specified, the default TTL defined by the + /// cache's policy will be applied. + /// + /// + /// The amount of time object should remain in the cache + /// formatted to be recognizable by . + /// + public string TimeToLive + { + get { return timeToLive; } + set + { + timeToLive = value; + timeToLiveTimeSpan = (timeToLive == null) ? TimeSpan.MinValue : (TimeSpan)TimeSpanConverter.ConvertFrom(timeToLive); + } + } + + + /// + /// The amount of time an object should remain in the cache (in seconds). + /// + /// + /// If no TTL is specified, the default TTL defined by the + /// cache's policy will be applied. + /// + /// + /// The amount of time object should remain in the cache (in seconds). + /// + public TimeSpan TimeToLiveTimeSpan + { + get { return timeToLiveTimeSpan; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Caching/CacheParameterAttribute.cs b/src/Spring/Spring.Core/Caching/CacheParameterAttribute.cs new file mode 100644 index 00000000..63d0a728 --- /dev/null +++ b/src/Spring/Spring.Core/Caching/CacheParameterAttribute.cs @@ -0,0 +1,72 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Caching +{ + /// + /// This attribute should be used to mark methods whose argument(s) + /// need to be cached. + /// + /// + ///

    + /// This attribute allows application developers to specify that an argument + /// of the method should be cached, but it will not do any caching by itself. + ///

    + ///

    + /// In order to actually cache the result, an application developer + /// must apply a Spring.Aspects.Cache.CacheParameterAdvice to + /// all of the members that have this attribute defined. + ///

    + ///

    + /// You can specify this attribute multiple times on the same method in order to + /// cache several method parameters. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: CacheParameterAttribute.cs,v 1.2 2007/03/31 01:07:26 bbaia Exp $ + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = false)] + [Serializable] + public sealed class CacheParameterAttribute : BaseCacheAttribute + { + /// + /// Creates an attribute instance. + /// + public CacheParameterAttribute() + { + } + + /// + /// Creates an attribute instance. + /// + /// + /// The name of the cache to use. + /// + /// + /// An expression string that should be evaluated in order to determine + /// the cache key for the item. + /// + public CacheParameterAttribute(string cacheName, string key) + : base(cacheName, key) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Caching/CacheResultAttribute.cs b/src/Spring/Spring.Core/Caching/CacheResultAttribute.cs new file mode 100644 index 00000000..bee2ef45 --- /dev/null +++ b/src/Spring/Spring.Core/Caching/CacheResultAttribute.cs @@ -0,0 +1,69 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Caching +{ + /// + /// This attribute should be used to mark methods whose result + /// needs to be cached. + /// + /// + ///

    + /// This attribute allows application developers to mark that a result + /// of the method invocation should be cached, but it will not do any + /// caching by itself. + ///

    + ///

    + /// In order to actually cache the result, an application developer + /// must apply a Spring.Aspects.Cache.CacheResultAdvice to + /// all of the members that have this attribute defined. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: CacheResultAttribute.cs,v 1.1 2007/02/09 07:12:23 aseovic Exp $ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + [Serializable] + public sealed class CacheResultAttribute : BaseCacheAttribute + { + /// + /// Creates an attribute instance. + /// + public CacheResultAttribute() + { + } + + /// + /// Creates an attribute instance. + /// + /// + /// The name of the cache to use. + /// + /// + /// An expression string that should be evaluated in order to determine + /// the cache key for the item. + /// + public CacheResultAttribute(string cacheName, string key) + : base(cacheName, key) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Caching/CacheResultItemsAttribute.cs b/src/Spring/Spring.Core/Caching/CacheResultItemsAttribute.cs new file mode 100644 index 00000000..91824086 --- /dev/null +++ b/src/Spring/Spring.Core/Caching/CacheResultItemsAttribute.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +namespace Spring.Caching +{ + /// + /// This attribute should be used with methods that return an + /// in order to cache each item separately. + /// + /// + ///

    + /// This attribute allows application developers to specify that each item + /// from the collection returned by the method should be cached, + /// but it will not do any caching by itself. + ///

    + ///

    + /// In order to actually cache the result, an application developer + /// must apply a Spring.Aspects.Cache.CacheResultAdvice to + /// all of the members that have this attribute defined. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: CacheResultItemsAttribute.cs,v 1.2 2007/03/31 02:59:48 bbaia Exp $ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] + [Serializable] + public sealed class CacheResultItemsAttribute : BaseCacheAttribute + { + /// + /// Creates an attribute instance. + /// + public CacheResultItemsAttribute() + { + } + + /// + /// Creates an attribute instance. + /// + /// + /// The name of the cache to use. + /// + /// + /// An expression string that should be evaluated in order to determine + /// the cache key for the item. + /// + public CacheResultItemsAttribute(string cacheName, string key) + : base(cacheName, key) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Caching/ICache.cs b/src/Spring/Spring.Core/Caching/ICache.cs new file mode 100644 index 00000000..86511f4b --- /dev/null +++ b/src/Spring/Spring.Core/Caching/ICache.cs @@ -0,0 +1,108 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +namespace Spring.Caching +{ + /// + /// Defines a contract that all cache implementations have to fulfill. + /// + /// Aleksandar Seovic + /// Erich Eichinger + /// $Id: ICache.cs,v 1.5 2007/08/27 09:38:11 oakinger Exp $ + public interface ICache + { + /// + /// Gets the number of items in the cache. + /// + int Count { get; } + + /// + /// Gets a collection of all cache item keys. + /// + ICollection Keys { get; } + + /// + /// Retrieves an item from the cache. + /// + /// + /// Item key. + /// + /// + /// Item for the specified , or null. + /// + object Get(object key); + + /// + /// Removes an item from the cache. + /// + /// + /// Item key. + /// + void Remove(object key); + + /// + /// Removes collection of items from the cache. + /// + /// + /// Collection of keys to remove. + /// + void RemoveAll(ICollection keys); + + /// + /// Removes all items from the cache. + /// + void Clear(); + + /// + /// Inserts an item into the cache. + /// + /// + /// Items inserted using this method have no expiration time + /// and default cache priority. + /// + /// + /// Item key. + /// + /// + /// Item value. + /// + void Insert(object key, object value); + + /// + /// Inserts an item into the cache. + /// + /// + /// Items inserted using this method have default cache priority. + /// + /// + /// Item key. + /// + /// + /// Item value. + /// + /// + /// Item's time-to-live. + /// + void Insert(object key, object value, TimeSpan timeToLive); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Caching/InvalidateCacheAttribute.cs b/src/Spring/Spring.Core/Caching/InvalidateCacheAttribute.cs new file mode 100644 index 00000000..b16283d2 --- /dev/null +++ b/src/Spring/Spring.Core/Caching/InvalidateCacheAttribute.cs @@ -0,0 +1,146 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Expressions; + +namespace Spring.Caching +{ + /// + /// This attribute should be used to mark method that should + /// invalidate one or more cache items when invoked. + /// + /// + ///

    + /// This attribute allows application developers to specify that some + /// cache items should be evicted from cache when the method is invoked, + /// but it will not do any eviction by itself. + ///

    + ///

    + /// In order to actually evict cache items, an application developer + /// must apply a Spring.Aspects.Cache.InvalidateCacheAdvice to + /// all of the members that have this attribute defined. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: InvalidateCacheAttribute.cs,v 1.2 2007/04/01 15:04:39 bbaia Exp $ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] + [Serializable] + public sealed class InvalidateCacheAttribute : Attribute + { + private string cacheName; + private string keys; + private IExpression keysExpression; + private string condition; + private IExpression conditionExpression; + + /// + /// Creates an attribute instance. + /// + public InvalidateCacheAttribute() + { + } + + /// + /// Creates an attribute instance. + /// + /// + /// The name of the cache to use. + /// + public InvalidateCacheAttribute(string cacheName) + { + this.cacheName = cacheName; + } + + /// + /// Gets or sets the name of the cache to use. + /// + /// + /// The name of the cache to use. + /// + public string CacheName + { + get { return cacheName; } + set { cacheName = value; } + } + + /// + /// Gets or sets a SpEL expression that should be evaluated in order + /// to determine the keys for the items that should be evicted. + /// + /// + /// An expression string that should be evaluated in order + /// to determine the keys for the items that should be evicted. + /// + public string Keys + { + get { return keys; } + set + { + keys = value; + keysExpression = Expression.Parse(value); + } + } + + /// + /// Gets an expression instance that should be evaluated in order + /// to determine the keys for the items that should be evicted. + /// + /// + /// An expression instance that should be evaluated in order + /// to determine the keys for the items that should be evicted. + /// + public IExpression KeysExpression + { + get { return keysExpression; } + } + + /// + /// Gets or sets a SpEL expression that should be evaluated in order + /// to determine whether items should be evicted. + /// + /// + /// An expression string that should be evaluated in order to determine + /// whether items should be evicted. + /// + public string Condition + { + get { return condition; } + set + { + condition = value; + conditionExpression = Expression.Parse(value); + } + } + + /// + /// Gets an expression instance that should be evaluated in order + /// to determine whether items should be evicted. + /// + /// + /// An expression instance that should be evaluated in order to determine + /// whether items should be evicted. + /// + public IExpression ConditionExpression + { + get { return conditionExpression; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Caching/NonExpiringCache.cs b/src/Spring/Spring.Core/Caching/NonExpiringCache.cs new file mode 100644 index 00000000..3b368f4e --- /dev/null +++ b/src/Spring/Spring.Core/Caching/NonExpiringCache.cs @@ -0,0 +1,143 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +namespace Spring.Caching +{ + /// + /// A simple implementation backed by a dictionary that + /// never expires cache items. + /// + /// Aleksandar Seovic + /// $Id: NonExpiringCache.cs,v 1.4 2007/08/24 22:43:55 oakinger Exp $ + public class NonExpiringCache : AbstractCache + { + private readonly IDictionary itemStore = new Hashtable(); + + /// + /// Gets the number of items in the cache. + /// + public override int Count + { + get + { + lock (itemStore.SyncRoot) + { + return itemStore.Count; + } + } + } + + /// + /// Gets a collection of all cache item keys. + /// + public override ICollection Keys + { + get + { + lock (itemStore.SyncRoot) + { + return itemStore.Keys; + } + } + } + + /// + /// Retrieves an item from the cache. + /// + /// + /// Item key. + /// + /// + /// Item for the specified , or null. + /// + public override object Get(object key) + { + lock (itemStore.SyncRoot) + { + return itemStore[key]; + } + } + + /// + /// Removes an item from the cache. + /// + /// + /// Item key. + /// + public override void Remove(object key) + { + lock (itemStore.SyncRoot) + { + itemStore.Remove(key); + } + } + + /// + /// Removes collection of items from the cache. + /// + /// + /// Collection of keys to remove. + /// + public override void RemoveAll(ICollection keys) + { + lock (itemStore.SyncRoot) + { + foreach (object key in keys) + { + itemStore.Remove(key); + } + } + } + + /// + /// Removes all items from the cache. + /// + public override void Clear() + { + lock (itemStore.SyncRoot) + { + itemStore.Clear(); + } + } + + /// + /// Inserts an item into the cache. + /// + /// + /// Item key. + /// + /// + /// Item value. + /// + /// + /// Item's time-to-live (TTL) in milliseconds. + /// + protected override void DoInsert(object key, object value, TimeSpan timeToLive) + { + lock (itemStore.SyncRoot) + { + itemStore[key] = value; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/AbstractQueue.cs b/src/Spring/Spring.Core/Collections/AbstractQueue.cs new file mode 100644 index 00000000..5fd846a7 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/AbstractQueue.cs @@ -0,0 +1,347 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Collections +{ + /// + /// This class provides skeletal implementations of some + /// operations. + /// + /// + ///

    + /// The implementations in this class are appropriate when the base + /// implementation does not allow elements. The methods + /// , + /// , and + /// are based on + /// the , + /// , and + /// methods + /// respectively but throw exceptions instead of indicating failure via + /// or returns. + ///

    + /// An implementation that extends this class must + /// minimally define a method + /// which does + /// not permit the insertion of elements, along with methods + /// , and + /// . Typically, + /// additional methods will be overridden as well. If these requirements + /// cannot be met, consider instead subclassing + /// }. + ///

    + ///
    + /// Doug Lea + /// Griffin Caprio (.NET) + /// $Id: AbstractQueue.cs,v 1.8 2007/08/27 09:38:11 oakinger Exp $ + [Serializable] + public abstract class AbstractQueue : IQueue + { + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is an abstract class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + protected AbstractQueue() + {} + + /// + /// Inserts the specified element into this queue if it is possible + /// to do so immediately without violating capacity restrictions. + /// + /// + /// The element to add. + /// + /// + /// if successful. + /// + /// + /// If the element cannot be added at this time due to capacity restrictions. + /// + public virtual bool Add(object objectToAdd) + { + if(Offer(objectToAdd)) + { + return true; + } + else + { + throw new InvalidOperationException("Queue full."); + } + } + + /// + /// Retrieves and removes the head of this queue. + /// + /// + ///

    + /// This method differs from + /// only in that + /// it throws an exception if this queue is empty. + ///

    + ///
    + /// + /// The head of this queue + /// + /// + /// If this queue is empty. + /// + public virtual object Remove() + { + object element = Poll(); + if(element != null) + { + return element; + } + else + { + throw new NoElementsException("Queue is empty."); + } + } + + + /// + /// Retrieves, but does not remove, the head of this queue. + /// + /// + ///

    + /// This method differs from + /// only in that it throws an exception if this queue is empty. + ///

    + ///

    + /// ALso note that this implementation returns the result of + /// unless the queue + /// is empty. + ///

    + ///
    + /// The head of this queue. + /// + /// If this queue is empty. + /// + public virtual object Element() + { + object element = Peek(); + if(element != null) + { + return element; + } + else + { + throw new NoElementsException("Queue is empty."); + } + } + + /// + /// Removes all of the elements from this queue. + /// + /// + ///

    + /// The queue will be empty after this call returns. + ///

    + ///

    + /// This implementation repeatedly invokes + /// until it + /// returns . + ///

    + ///
    + public virtual void Clear() + { + while(Poll() != null) + { + ; + } + } + + /// + /// Adds all of the elements in the supplied + /// to this queue. + /// + /// + ///

    + /// Attempts to + /// + /// of a queue to itself result in . + /// Further, the behavior of this operation is undefined if the specified + /// collection is modified while the operation is in progress. + ///

    + ///

    + /// This implementation iterates over the specified collection, + /// and adds each element returned by the iterator to this queue, in turn. + /// An exception encountered while trying to add an element (including, + /// in particular, a element) may result in only some + /// of the elements having been successfully added when the associated + /// exception is thrown. + ///

    + ///
    + /// + /// The collection containing the elements to be added to this queue. + /// + /// + /// if this queue changed as a result of the call. + /// + /// + /// If the supplied or any one of its elements are . + /// + /// + /// If the collection is the current or + /// the collection size is greater than the queue capacity. + /// + public virtual bool AddAll(ICollection collection) + { + if(collection == null) + { + throw new ArgumentNullException("Collection cannot be null."); + } + if(collection == this) + { + throw new ArgumentException(); + } + if(collection.Count > Capacity) + { + throw new ArgumentException("Collcation size greater than queue capacity."); + } + bool modified = false; + foreach(object element in collection) + { + if(element == null) + { + throw new ArgumentNullException("Cannot add null elements to this queue."); + } + else if(Add(element)) + { + modified = true; + } + } + return modified; + } + + /// + /// Inserts the specified element into this queue if it is possible to do + /// so immediately without violating capacity restrictions. + /// + /// + ///

    + /// When using a capacity-restricted queue, this method is generally + /// preferable to , + /// which can fail to insert an element only by throwing an exception. + ///

    + ///
    + /// + /// The element to add. + /// + /// + /// if the element was added to this queue. + /// + /// + /// If the element cannot be added at this time due to capacity restrictions. + /// + /// + /// If the supplied is + /// . + /// + /// + /// If some property of the supplied prevents + /// it from being added to this queue. + /// + public abstract bool Offer(object objectToAdd); + + /// + /// Retrieves, but does not remove, the head of this queue, + /// or returns if this queue is empty. + /// + /// + /// The head of this queue, or if this queue is empty. + /// + public abstract object Peek(); + + /// + /// Retrieves and removes the head of this queue, + /// or returns if this queue is empty. + /// + /// + /// The head of this queue, or if this queue is empty. + /// + public abstract object Poll(); + + /// + /// Returns if there are no elements in the , otherwise. + /// + public abstract bool IsEmpty { get; } + + /// + /// Returns the current capacity of this queue. + /// + public abstract int Capacity { get; } + + /// + ///Copies the elements of the to an , starting at a particular index. + /// + ///The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + ///The zero-based index in array at which copying begins. + ///array is null. + ///index is less than zero. + ///array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source is greater than the available space from index to the end of the destination array. + ///The type of the source cannot be cast automatically to the type of the destination array. 2 + public abstract void CopyTo(Array array, int index); + + /// + ///Gets the number of elements contained in the . + /// + /// + ///The number of elements contained in the . + /// + public abstract int Count { get; } + + /// + ///Gets an object that can be used to synchronize access to the . + /// + /// + ///An object that can be used to synchronize access to the . + /// + public abstract object SyncRoot { get; } + + /// + ///Gets a value indicating whether access to the is synchronized (thread safe). + /// + /// + ///true if access to the is synchronized (thread safe); otherwise, false. + /// + public abstract bool IsSynchronized { get; } + + /// + ///Returns an enumerator that iterates through a collection. + /// + /// + ///An object that can be used to iterate through the collection. + /// + public abstract IEnumerator GetEnumerator(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/DictionarySet.cs b/src/Spring/Spring.Core/Collections/DictionarySet.cs new file mode 100644 index 00000000..0d112d31 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/DictionarySet.cs @@ -0,0 +1,394 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Collections +{ + /// + /// is an + /// class that supports the creation of new + /// types where the underlying data + /// store is an instance. + /// + /// + ///

    + /// You can use any object that implements the + /// interface to hold set + /// data. You can define your own, or you can use one of the objects + /// provided in the framework. The type of + /// you + /// choose will affect both the performance and the behavior of the + /// using it. + ///

    + ///

    + /// This object overrides the method, + /// but not the method, because + /// the class is mutable. + /// Therefore, it is not safe to use as a key value in a dictionary. + ///

    + ///

    + /// To make a typed based on your + /// own , simply derive a new + /// class with a constructor that takes no parameters. Some + /// implmentations cannot be defined + /// with a default constructor. If this is the case for your class, you + /// will need to override clone as well. + ///

    + ///

    + /// It is also standard practice that at least one of your constructors + /// takes an or an + /// as an argument. + ///

    + ///
    + /// + /// $Id: DictionarySet.cs,v 1.7 2007/03/16 04:01:27 aseovic Exp $ + [Serializable] + public abstract class DictionarySet : Set + { + private IDictionary _internalDictionary; + + private static readonly object PlaceholderObject = new object(); + private static readonly object NullPlaceHolderKey = new object(); + + /// + /// Provides the storage for elements in the + /// , stored as the key-set + /// of the object. + /// + /// + ///

    + /// Set this object in the constructor if you create your own + /// class. + ///

    + ///
    + protected IDictionary InternalDictionary + { + get { return _internalDictionary; } + set { _internalDictionary = value; } + } + + /// + /// The placeholder object used as the value for the + /// instance. + /// + /// + /// There is a single instance of this object globally, used for all + /// s. + /// + protected static object Placeholder + { + get { return PlaceholderObject; } + } + + /// + /// Adds the specified element to this set if it is not already present. + /// + /// The object to add to the set. + /// + /// is the object was added, + /// if the object was already present. + /// + public override bool Add(object element) + { + element = MaskNull(element); + if (InternalDictionary[element] != null) + { + return false; + } + else + { + //The object we are adding is just a placeholder. The thing we are + //really concerned with is 'o', the key. + InternalDictionary.Add(element, PlaceholderObject); + return true; + } + } + + /// + /// Adds all the elements in the specified collection to the set if + /// they are not already present. + /// + /// A collection of objects to add to the set. + /// + /// is the set changed as a result of this + /// operation. + /// + public override bool AddAll(ICollection collection) + { + bool changed = false; + foreach (object o in collection) + { + changed |= this.Add(o); + } + return changed; + } + + /// + /// Removes all objects from this set. + /// + public override void Clear() + { + InternalDictionary.Clear(); + } + + /// + /// Returns if this set contains the specified + /// element. + /// + /// The element to look for. + /// + /// if this set contains the specified element. + /// + public override bool Contains(object element) + { + element = MaskNull(element); + return InternalDictionary[element] != null; + } + + /// + /// Returns if the set contains all the + /// elements in the specified collection. + /// + /// A collection of objects. + /// + /// if the set contains all the elements in the + /// specified collection; also if the + /// supplied is . + /// + public override bool ContainsAll(ICollection collection) + { + if(collection == null) + { + return false; + } + foreach (object o in collection) + { + if (!this.Contains(MaskNull(o))) + { + return false; + } + } + return true; + } + + /// + /// Returns if this set contains no elements. + /// + public override bool IsEmpty + { + get { return InternalDictionary.Count == 0; } + } + + /// + /// Removes the specified element from the set. + /// + /// The element to be removed. + /// + /// if the set contained the specified element. + /// + public override bool Remove(object element) + { + element = MaskNull(element); + bool contained = this.Contains(element); + if (contained) + { + InternalDictionary.Remove(element); + } + return contained; + } + + /// + /// Remove all the specified elements from this set, if they exist in + /// this set. + /// + /// A collection of elements to remove. + /// + /// if the set was modified as a result of this + /// operation. + /// + public override bool RemoveAll(ICollection collection) + { + bool changed = false; + foreach (object o in collection) + { + changed |= this.Remove(o); + } + return changed; + } + + /// + /// Retains only the elements in this set that are contained in the + /// specified collection. + /// + /// + /// The collection that defines the set of elements to be retained. + /// + /// + /// if this set changed as a result of this + /// operation. + /// + public override bool RetainAll(ICollection collection) + { + //Put data from C into a set so we can use the Contains() method. + Set cSet = new HybridSet(collection); + + //We are going to build a set of elements to remove. + Set removeSet = new HybridSet(); + + foreach (object o in this) + { + //If C does not contain O, then we need to remove O from our + //set. We can't do this while iterating through our set, so + //we put it into RemoveSet for later. + if (!cSet.Contains(o)) + { + removeSet.Add(o); + } + } + return this.RemoveAll(removeSet); + } + + /// + /// Copies the elements in the to + /// an array. + /// + /// + ///

    + /// The type of array needs to be compatible with the objects in the + /// , obviously. + ///

    + ///
    + /// + /// An array that will be the target of the copy operation. + /// + /// + /// The zero-based index where copying will start. + /// + public override void CopyTo(Array array, int index) + { + int i = index; + foreach (object o in this) + { + array.SetValue(UnmaskNull(o), i++); + } + } + + /// + /// The number of elements currently contained in this collection. + /// + public override int Count + { + get { return InternalDictionary.Count; } + } + + /// + /// Returns if the + /// is synchronized across + /// threads. + /// + /// + public override bool IsSynchronized + { + get { return false; } + } + + /// + /// An object that can be used to synchronize this collection to make + /// it thread-safe. + /// + /// + /// An object that can be used to synchronize this collection to make + /// it thread-safe. + /// + /// + public override object SyncRoot + { + get { return InternalDictionary.SyncRoot; } + } + + /// + /// Gets an enumerator for the elements in the + /// . + /// + /// + /// An over the elements + /// in the . + /// + public override IEnumerator GetEnumerator() + { + return new DictionarySetEnumerator(InternalDictionary.Keys.GetEnumerator()); + } + + private static object MaskNull(object key) + { + return key == null ? NullPlaceHolderKey : key; + } + + private static object UnmaskNull(object key) + { + return key == NullPlaceHolderKey ? null : key; + } + + #region Inner Class : DictionarySetEnumerator + + private sealed class DictionarySetEnumerator : IEnumerator + { + #region Constructor (s) / Destructor + + public DictionarySetEnumerator(IEnumerator enumerator) + { + _enumerator = enumerator; + } + + #endregion + + #region IEnumerator Members + + public void Reset() + { + _enumerator.Reset(); + } + + public object Current + { + get { return UnmaskNull(_enumerator.Current); } + } + + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + #endregion + + private IEnumerator _enumerator; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/HashedSet.cs b/src/Spring/Spring.Core/Collections/HashedSet.cs new file mode 100644 index 00000000..b1ac2f15 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/HashedSet.cs @@ -0,0 +1,67 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Collections +{ + /// + /// Implements an based on a + /// hash table. + /// + /// + ///

    + /// This will give the best lookup, add, and remove performance for very + /// large data-sets, but iteration will occur in no particular order. + ///

    + ///
    + /// + /// $Id: HashedSet.cs,v 1.6 2007/03/16 04:01:27 aseovic Exp $ + [Serializable] + public class HashedSet : DictionarySet + { + /// + /// Creates a new instance of the class. + /// + public HashedSet() + { + InternalDictionary = new Hashtable(); + } + + /// + /// Creates a new instance of the class, and + /// initializes it based on a collection of elements. + /// + /// + /// A collection of elements that defines the initial set contents. + /// + public HashedSet(ICollection initialValues) : this() + { + this.AddAll(initialValues); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/HybridSet.cs b/src/Spring/Spring.Core/Collections/HybridSet.cs new file mode 100644 index 00000000..f1264c4f --- /dev/null +++ b/src/Spring/Spring.Core/Collections/HybridSet.cs @@ -0,0 +1,76 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; + +#endregion + +namespace Spring.Collections +{ + /// + /// Implements an that automatically + /// changes from a list based implementation to a hashtable based + /// implementation when the size reaches a certain threshold. + /// + /// + ///

    + /// This is good if you are unsure about whether you data-set will be tiny + /// or huge. + ///

    + /// + /// Because this uses a dual implementation, iteration order is not + /// guaranteed! + /// + ///
    + /// + /// $Id: HybridSet.cs,v 1.6 2007/03/16 04:01:28 aseovic Exp $ + [Serializable] + public class HybridSet : DictionarySet + { + /// + /// Creates a new set instance based on either a list or a hash table, + /// depending on which will be more efficient based on the data-set + /// size. + /// + public HybridSet() + { + InternalDictionary = new HybridDictionary(); + } + + /// + /// Creates a new set instance based on either a list or a hash table, + /// depending on which will be more efficient based on the data-set + /// size, and initializes it based on a collection of elements. + /// + /// + /// A collection of elements that defines the initial set contents. + /// + public HybridSet(ICollection initialValues) : this() + { + this.AddAll(initialValues); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/IQueue.cs b/src/Spring/Spring.Core/Collections/IQueue.cs new file mode 100644 index 00000000..990d6dc7 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/IQueue.cs @@ -0,0 +1,233 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Collections +{ + /// + /// A collection designed for holding elements prior to processing. + /// + /// + ///

    + /// Besides basic operations, + /// queues provide additional insertion, extraction, and inspection + /// operations. + ///

    + ///

    + /// Each of these methods exists in two forms: one throws + /// an exception if the operation fails, the other returns a special + /// value (either or , depending on the + /// operation). The latter form of the insert operation is designed + /// specifically for use with capacity-restricted + /// implementations; in most implementations, insert operations cannot + /// fail. + ///

    + ///

    + /// Queues typically, but do not necessarily, order elements in a + /// FIFO (first-in-first-out) manner. Among the exceptions are + /// priority queues, which order elements according to a supplied + /// comparator, or the elements' natural ordering, and LIFO queues (or + /// stacks) which order the elements LIFO (last-in-first-out). + /// Whatever the ordering used, the head of the queue is that + /// element which would be removed by a call to + /// or + /// . In a FIFO queue, all new + /// elements are inserted at the tail of the queue. Other kinds of queues may + /// use different placement rules. Every implementation + /// must specify its ordering properties. + ///

    + ///

    + /// The method inserts an + /// element if possible, otherwise returning . This differs from the + /// method, which can fail to + /// add an element only by throwing an exception. The + /// method is designed for + /// use when failure is a normal, rather than exceptional occurrence, for example, + /// in fixed-capacity (or "bounded" queues. + ///

    + ///

    + /// The + /// methods remove and + /// return the head of the queue. Exactly which element is removed from the + /// queue is a function of the queue's ordering policy, which differs from + /// implementation to implementation. The + /// and + /// methods differ only in their + /// behavior when the queue is empty: the + /// method throws an exception, + /// while the method returns + /// . + ///

    + ///

    + /// The and + /// methods return, but do + /// not remove, the head of the queue. + ///

    + ///

    + /// The interface does not define the blocking queue + /// methods, which are common in concurrent programming. + ///

    + ///

    + /// implementations generally do not allow insertion + /// of elements, although some implementations, such as + /// a linked list, do not prohibit the insertion of . + /// Even in the implementations that permit it, should + /// not be inserted into a , as is also + /// used as a special return value by the + /// method to + /// indicate that the queue contains no elements. + ///

    + ///

    + /// implementations generally do not define + /// element-based versions of methods + /// and , but instead inherit the + /// identity based versions from the class object, because element-based equality + /// is not always well-defined for queues with the same elements but different + /// ordering properties. + ///

    + ///

    + /// Based on the back port of JCP JSR-166. + ///

    + ///
    + /// Doug Lea + /// Griffin Caprio (.NET) + /// $Id: IQueue.cs,v 1.7 2006/09/30 18:39:24 gcaprio Exp $ + public interface IQueue : ICollection + { + /// + /// Inserts the specified element into this queue if it is possible to do so + /// immediately without violating capacity restrictions, returning + /// upon success and throwing an + /// if no space is + /// currently available. + /// + /// + /// The element to add. + /// + /// + /// if successful. + /// + /// + /// If the element cannot be added at this time due to capacity restrictions. + /// + /// + /// If the class of the supplied prevents it + /// from being added to this queue. + /// + /// + /// If the specified element is and this queue does not + /// permit elements. + /// + /// + /// If some property of the supplied prevents + /// it from being added to this queue. + /// + bool Add(object objectToAdd); + + /// + /// Inserts the specified element into this queue if it is possible to do + /// so immediately without violating capacity restrictions. + /// + /// + ///

    + /// When using a capacity-restricted queue, this method is generally + /// preferable to , + /// which can fail to insert an element only by throwing an exception. + ///

    + ///
    + /// + /// The element to add. + /// + /// + /// if the element was added to this queue. + /// + /// + /// If the element cannot be added at this time due to capacity restrictions. + /// + /// + /// If the supplied is + /// . + /// + /// + /// If some property of the supplied prevents + /// it from being added to this queue. + /// + bool Offer(object objectToAdd); + + /// + /// Retrieves and removes the head of this queue. + /// + /// + ///

    + /// This method differs from + /// only in that it throws an exception if this queue is empty. + ///

    + ///
    + /// + /// The head of this queue + /// + /// if this queue is empty + object Remove(); + + /// + /// Retrieves and removes the head of this queue, + /// or returns if this queue is empty. + /// + /// + /// The head of this queue, or if this queue is empty. + /// + object Poll(); + + /// + /// Retrieves, but does not remove, the head of this queue. + /// + /// + ///

    + /// This method differs from + /// only in that it throws an exception if this queue is empty. + ///

    + ///
    + /// + /// The head of this queue. + /// + /// If this queue is empty. + object Element(); + + /// + /// Retrieves, but does not remove, the head of this queue, + /// or returns if this queue is empty. + /// + /// + /// The head of this queue, or if this queue is empty. + /// + object Peek(); + + /// + /// Returns if there are no elements in the , otherwise. + /// + bool IsEmpty { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/ISet.cs b/src/Spring/Spring.Core/Collections/ISet.cs new file mode 100644 index 00000000..fb27dd38 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/ISet.cs @@ -0,0 +1,275 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Collections +{ + /// + /// A collection that contains no duplicate elements. + /// + /// + ///

    + /// This interface models the mathematical + /// abstraction. The order of + /// elements in a set is dependant on (a)the data-structure implementation, and + /// (b)the implementation of the various + /// methods, and thus is not + /// guaranteed. + ///

    + ///

    + /// overrides the + /// method to test for "equivalency": + /// whether the two sets contain the same elements. The "==" and "!=" + /// operators are not overridden by design, since it is often desirable to + /// compare object references for equality. + ///

    + ///

    + /// Also, the method is not + /// implemented on any of the set implementations, since none of them are + /// truly immutable. This is by design, and it is the way almost all + /// collections in the .NET framework function. So as a general rule, don't + /// store collection objects inside + /// instances. You would typically want to use a keyed + /// instead. + ///

    + ///

    + /// None of the implementations in + /// this library are guaranteed to be thread-safe in any way unless wrapped + /// in a . + ///

    + ///

    + /// The following table summarizes the binary operators that are supported + /// by the class. + ///

    + /// + /// + /// Operation + /// Description + /// Method + /// + /// + /// Union (OR) + /// + /// Element included in result if it exists in either A OR + /// B. + /// + /// Union() + /// + /// + /// Intersection (AND) + /// + /// Element included in result if it exists in both A AND + /// B. + /// + /// InterSect() + /// + /// + /// Exclusive Or (XOR) + /// + /// Element included in result if it exists in one, but not both, + /// of A and B. + /// + /// ExclusiveOr() + /// + /// + /// Minus (n/a) + /// + /// Take all the elements in A. Now, if any of them exist in + /// B, remove them. Note that unlike the other operators, + /// A - B is not the same as B - A. + /// + /// Minus() + /// + /// + ///
    + /// $Id: ISet.cs,v 1.5 2006/04/09 07:18:37 markpollack Exp $ + public interface ISet : ICollection, ICloneable + { + /// + /// Performs a "union" of the two sets, where all the elements + /// in both sets are present. + /// + /// + ///

    + /// That is, the element is included if it is in either + /// or this set. Neither this set nor the input + /// set are modified during the operation. The return value is a + /// clone of this set with the extra elements added in. + ///

    + ///
    + /// A collection of elements. + /// + /// A new containing the union of + /// this with the specified + /// collection. Neither of the input objects is modified by the union. + /// + ISet Union(ISet setOne); + + /// + /// Performs an "intersection" of the two sets, where only the elements + /// that are present in both sets remain. + /// + /// + ///

    + /// That is, the element is included if it exists in both sets. The + /// Intersect() operation does not modify the input sets. It + /// returns a clone of this set with the appropriate elements + /// removed. + ///

    + ///
    + /// A set of elements. + /// + /// The intersection of this set with . + /// + ISet Intersect(ISet setOne); + + /// + /// Performs a "minus" of this set from the + /// set. + /// + /// + ///

    + /// This returns a set of all the elements in set + /// , removing the elements that are also in + /// this set. The original sets are not modified during this operation. + /// The result set is a clone of this + /// containing the elements from + /// the operation. + ///

    + ///
    + /// A set of elements. + /// + /// A set containing the elements from this set with the elements in + /// removed. + /// + ISet Minus(ISet setOne); + + /// + /// Performs an "exclusive-or" of the two sets, keeping only those + /// elements that are in one of the sets, but not in both. + /// + /// + ///

    + /// The original sets are not modified during this operation. The + /// result set is a clone of this set containing the elements + /// from the exclusive-or operation. + ///

    + ///
    + /// A set of elements. + /// + /// A set containing the result of + /// ^ this. + /// + ISet ExclusiveOr(ISet setOne); + + /// + /// Returns if this set contains the specified + /// element. + /// + /// The element to look for. + /// + /// if this set contains the specified element. + /// + bool Contains(object element); + + /// + /// Returns if the set contains all the + /// elements in the specified collection. + /// + /// A collection of objects. + /// + /// if the set contains all the elements in the + /// specified collection. + /// + bool ContainsAll(ICollection collection); + + /// + /// Returns if this set contains no elements. + /// + bool IsEmpty { get; } + + /// + /// Adds the specified element to this set if it is not already present. + /// + /// The object to add to the set. + /// + /// is the object was added, + /// if the object was already present. + /// + bool Add(object element); + + /// + /// Adds all the elements in the specified collection to the set if + /// they are not already present. + /// + /// A collection of objects to add to the set. + /// + /// is the set changed as a result of this + /// operation. + /// + bool AddAll(ICollection collection); + + /// + /// Removes the specified element from the set. + /// + /// The element to be removed. + /// + /// if the set contained the specified element. + /// + bool Remove(object element); + + /// + /// Remove all the specified elements from this set, if they exist in + /// this set. + /// + /// A collection of elements to remove. + /// + /// if the set was modified as a result of this + /// operation. + /// + bool RemoveAll(ICollection collection); + + /// + /// Retains only the elements in this set that are contained in the + /// specified collection. + /// + /// + /// The collection that defines the set of elements to be retained. + /// + /// + /// if this set changed as a result of this + /// operation. + /// + bool RetainAll(ICollection collection); + + /// + /// Removes all objects from this set. + /// + void Clear(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/ImmutableSet.cs b/src/Spring/Spring.Core/Collections/ImmutableSet.cs new file mode 100644 index 00000000..399c4e3b --- /dev/null +++ b/src/Spring/Spring.Core/Collections/ImmutableSet.cs @@ -0,0 +1,357 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Collections +{ + /// + /// Implements an immutable (read-only) + /// wrapper. + /// + /// + ///

    + /// Although this class is advertised as immutable, it really isn't. + /// Anyone with access to the wrapped + /// can still change the data. So + /// is not implemented for this , as + /// is the case for all + /// implementations in this library. This design decision was based on the + /// efficiency of not having to clone the wrapped + /// every time you wrap a mutable + /// . + ///

    + ///
    + /// $Id: ImmutableSet.cs,v 1.6 2007/03/16 04:01:28 aseovic Exp $ + [Serializable] + public sealed class ImmutableSet : Set + { + private const string ErrorMessage = "Object is immutable."; + private ISet _mBasisSet; + + internal ISet BasisSet + { + get { return _mBasisSet; } + } + + /// + /// Constructs an immutable (read-only) + /// wrapper. + /// + /// + /// The that is to be wrapped. + /// + public ImmutableSet(ISet basisSet) + { + _mBasisSet = basisSet; + } + + /// + /// Adds the specified element to this set if it is not already present. + /// + /// The object to add to the set. + /// + /// is the object was added, + /// if the object was already present. + /// + /// + public override sealed bool Add(object element) + { + throw CreateNotSupportedException(); + } + + /// + /// Adds all the elements in the specified collection to the set if + /// they are not already present. + /// + /// A collection of objects to add to the set. + /// + /// is the set changed as a result of this + /// operation. + /// + /// + public override sealed bool AddAll(ICollection collection) + { + throw CreateNotSupportedException(); + } + + /// + /// Removes all objects from this set. + /// + /// + public override sealed void Clear() + { + throw CreateNotSupportedException(); + } + + /// + /// Returns if this set contains the specified + /// element. + /// + /// The element to look for. + /// + /// if this set contains the specified element. + /// + public override sealed bool Contains(object element) + { + return _mBasisSet.Contains(element); + } + + /// + /// Returns if the set contains all the + /// elements in the specified collection. + /// + /// A collection of objects. + /// + /// if the set contains all the elements in the + /// specified collection. + /// + public override sealed bool ContainsAll(ICollection collection) + { + return _mBasisSet.ContainsAll(collection); + } + + /// + /// Returns if this set contains no elements. + /// + public override sealed bool IsEmpty + { + get { return _mBasisSet.IsEmpty; } + } + + /// + /// Removes the specified element from the set. + /// + /// The element to be removed. + /// + /// if the set contained the specified element. + /// + /// + public override sealed bool Remove(object element) + { + throw CreateNotSupportedException(); + } + + /// + /// Remove all the specified elements from this set, if they exist in + /// this set. + /// + /// A collection of elements to remove. + /// + /// if the set was modified as a result of this + /// operation. + /// + /// + public override sealed bool RemoveAll(ICollection collection) + { + throw CreateNotSupportedException(); + } + + /// + /// Retains only the elements in this set that are contained in the + /// specified collection. + /// + /// + /// The collection that defines the set of elements to be retained. + /// + /// + /// if this set changed as a result of this + /// operation. + /// + /// + public override sealed bool RetainAll(ICollection collection) + { + throw CreateNotSupportedException(); + } + + private static NotSupportedException CreateNotSupportedException() + { + return new NotSupportedException(ImmutableSet.ErrorMessage); + } + + /// + /// Copies the elements in the to + /// an array. + /// + /// + ///

    + /// The type of array needs to be compatible with the objects in the + /// , obviously. + ///

    + ///
    + /// + /// An array that will be the target of the copy operation. + /// + /// + /// The zero-based index where copying will start. + /// + public override sealed void CopyTo(Array array, int index) + { + _mBasisSet.CopyTo(array, index); + } + + /// + /// The number of elements currently contained in this collection. + /// + public override sealed int Count + { + get { return _mBasisSet.Count; } + } + + /// + /// Returns if the + /// is synchronized across + /// threads. + /// + /// + ///

    + /// Note that enumeration is inherently not thread-safe. Use the + /// to lock the object during enumeration. + ///

    + ///
    + public override sealed bool IsSynchronized + { + get { return _mBasisSet.IsSynchronized; } + } + + /// + /// An object that can be used to synchronize this collection to make + /// it thread-safe. + /// + /// + /// An object that can be used to synchronize this collection to make + /// it thread-safe. + /// + public override sealed object SyncRoot + { + get { return _mBasisSet.SyncRoot; } + } + + /// + /// Gets an enumerator for the elements in the + /// . + /// + /// + /// An over the elements + /// in the . + /// + public override sealed IEnumerator GetEnumerator() + { + return _mBasisSet.GetEnumerator(); + } + + /// + /// Returns a clone of the + /// instance. + /// + /// A clone of this object. + public override sealed object Clone() + { + return new ImmutableSet(_mBasisSet); + } + + /// + /// Performs a "union" of the two sets, where all the elements + /// in both sets are present. + /// + /// A collection of elements. + /// + /// A new containing the union of + /// this with the specified + /// collection. Neither of the input objects is modified by the union. + /// + /// + public override sealed ISet Union(ISet setOne) + { + ISet m = this; + while (m is ImmutableSet) + { + m = ((ImmutableSet) m).BasisSet; + } + return new ImmutableSet(m.Union(setOne)); + } + + /// + /// Performs an "intersection" of the two sets, where only the elements + /// that are present in both sets remain. + /// + /// A set of elements. + /// + /// The intersection of this set with . + /// + /// + public override sealed ISet Intersect(ISet setOne) + { + ISet m = this; + while (m is ImmutableSet) + { + m = ((ImmutableSet) m).BasisSet; + } + return new ImmutableSet(m.Intersect(setOne)); + } + + /// + /// Performs a "minus" of this set from the + /// set. + /// + /// A set of elements. + /// + /// A set containing the elements from this set with the elements in + /// removed. + /// + /// + public override sealed ISet Minus(ISet setOne) + { + ISet m = this; + while (m is ImmutableSet) + { + m = ((ImmutableSet) m).BasisSet; + } + return new ImmutableSet(m.Minus(setOne)); + } + + /// + /// Performs an "exclusive-or" of the two sets, keeping only those + /// elements that are in one of the sets, but not in both. + /// + /// A set of elements. + /// + /// A set containing the result of + /// ^ this. + /// + /// + public override sealed ISet ExclusiveOr(ISet setOne) + { + ISet m = this; + while (m is ImmutableSet) + { + m = ((ImmutableSet) m).BasisSet; + } + return new ImmutableSet(m.ExclusiveOr(setOne)); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/LinkedList.cs b/src/Spring/Spring.Core/Collections/LinkedList.cs new file mode 100644 index 00000000..18e188dc --- /dev/null +++ b/src/Spring/Spring.Core/Collections/LinkedList.cs @@ -0,0 +1,545 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Collections +{ + /// + /// Simple linked list implementation. + /// + /// Simon White + /// $Id: LinkedList.cs,v 1.9 2007/03/16 04:01:28 aseovic Exp $ + [Serializable] + public class LinkedList : IList + { + private int _nodeIndex; + private Node _rootNode; + private int _modId; + + #region Constructors + + /// + /// Creates a new instance of the + /// class. + /// + public LinkedList() + { + _rootNode = new Node(null, null, null); + _rootNode.PreviousNode = _rootNode; + _rootNode.NextNode = _rootNode; + } + + /// + /// Creates a new instance of the + /// class that contains all + /// elements of the specified list. + /// + /// + /// A list of elements that defines the initial contents. + /// + public LinkedList(IList list) : this() + { + AddAll(list); + } + + #endregion + + #region IList Members + + /// + /// Is list read only? + /// + /// + /// if the list is read only. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Returns the node at the specified index. + /// + /// + ///

    + /// This is the indexer for the + /// class. + ///

    + ///
    + /// + public object this[int index] + { + get { return GetNode(index).Value; } + set { GetNode(index).Value = value; } + } + + /// + /// Removes the object at the specified index. + /// + /// The lookup index. + /// + /// If the specified is greater than the + /// number of objects within the list. + /// + public void RemoveAt(int index) + { + CheckUpdateState(); + RemoveNode(GetNode(index)); + } + + /// + /// Inserts an object at the specified index. + /// + /// The lookup index. + /// The object to be inserted. + /// + /// If the specified is greater than the + /// number of objects within the list. + /// + public void Insert(int index, object value) + { + CheckUpdateState(); + + Node node = null; + if (index == _nodeIndex) + { + node = new Node(value, _rootNode.PreviousNode, _rootNode); + } + else + { + Node insert = GetNode(index); + node = new Node(value, insert.PreviousNode, insert); + } + node.PreviousNode.NextNode = node; + node.NextNode.PreviousNode = node; + _nodeIndex++; + _modId++; + } + + /// + /// Removes the first instance of the specified object found. + /// + /// The object to remove + public void Remove(object value) + { + CheckUpdateState(); + NodeHolder nh = GetNode(value); + RemoveNode(nh.Node); + } + + /// + /// Returns if this list contains the specified + /// element. + /// + /// The element to look for. + /// + /// if this list contains the specified element. + /// + public bool Contains(object value) + { + return GetNode(value) != null; + } + + /// + /// Removes all objects from the list. + /// + public void Clear() + { + _rootNode = new Node(null, null, null); + _nodeIndex = 0; + _modId++; + } + + /// + /// Returns the index of the first instance of the specified + /// found. + /// + /// The object to search for + /// + /// The index of the first instance found, or -1 if the element was not + /// found. + /// + public int IndexOf(object value) + { + NodeHolder nh = GetNode(value); + if (nh == null) + { + return -1; + } + return nh.Index; + } + + /// + /// Adds the specified object to the end of the list. + /// + /// The object to add + /// The index that the object was added at. + public int Add(object value) + { + Insert(_nodeIndex, value); + return _nodeIndex - 1; + } + + /// + /// Adds all of the elements of the supplied + /// list to the end of this list. + /// + /// The list of objects to add. + public void AddAll(IList elements) + { + foreach (object obj in elements) + { + Add(obj); + } + } + + /// + /// Is the list a fixed size? + /// + /// + /// if the list is a fixed size list. + /// + public bool IsFixedSize + { + get { return false; } + } + + #endregion + + #region Private Methods + + /// + /// Checks whether the list can be modified. + /// + /// + /// If the list cannot be modified. + /// + private void CheckUpdateState() + { + if (IsReadOnly || IsFixedSize) + { + throw new NotSupportedException("LinkedList cannot be modified."); + } + } + + /// + /// Validates the specified index. + /// + /// The lookup index. + /// + /// If the index is invalid. + /// + private void ValidateIndex(int index) + { + if (index < 0 || index >= _nodeIndex) + { + throw new ArgumentOutOfRangeException("index"); + } + } + + /// + /// Returns the node at the specified index. + /// + /// The lookup index. + /// The node at the specified index. + /// + /// If the specified is greater than the + /// number of objects within the list. + /// + private Node GetNode(int index) + { + ValidateIndex(index); + Node node = _rootNode; + for (int i = 0; i <= index; i++) + { + node = node.NextNode; + } + return node; + } + + /// + /// Returns the node (and index) of the first node that contains + /// the specified value. + /// + /// The value to search for. + /// + /// The node, or if not found. + /// + private NodeHolder GetNode(object value) + { + int i = 0; + if (value == null) + { + + for (Node n = _rootNode.NextNode; n != _rootNode; n = n.NextNode) + { + if (n.Value == null) + { + return new NodeHolder(n, i); + } + i++; + } + } + else + { + + for (Node n = _rootNode.NextNode; n != _rootNode; n = n.NextNode) + { + if (value.Equals(n.Value)) + { + return new NodeHolder(n, i); + } + i++; + } + } + return null; + } + + /// + /// Removes the specified node. + /// + /// The node to be removed. + private void RemoveNode(Node node) + { + Node previousNode = node.PreviousNode; + previousNode.NextNode = node.NextNode; + node.NextNode.PreviousNode = previousNode; + node.PreviousNode = null; + node.NextNode = null; + _nodeIndex--; + _modId++; + } + + #endregion + + #region ICollection Members + + /// + /// Returns if the list is synchronized across + /// threads. + /// + /// + /// + /// This implementation always returns . + /// + ///

    + /// Note that enumeration is inherently not thread-safe. Use the + /// to lock the object during enumeration. + ///

    + ///
    + public bool IsSynchronized + { + get { return false; } + } + + /// + /// The number of objects within the list. + /// + public int Count + { + get { return _nodeIndex; } + } + + /// + /// Copies the elements in this list to an array. + /// + /// + ///

    + /// The type of array needs to be compatible with the objects in this + /// list, obviously. + ///

    + ///
    + /// + /// An array that will be the target of the copy operation. + /// + /// + /// The zero-based index where copying will start. + /// + /// + /// If the supplied is . + /// + /// + /// If the supplied is less than zero + /// or is greater than the length of . + /// + /// + /// If the supplied is of insufficient size. + /// + public void CopyTo(Array array, int index) + { + if (array == null) + { + throw new ArgumentNullException("array"); + } + if ((index < 0) || (index > array.Length)) + { + throw new ArgumentOutOfRangeException("index", String.Format("Index {0} is out of range.", index)); + } + if ((array.Length - index) < this._nodeIndex) + { + throw new ArgumentException("Array is of insufficient size."); + } + + Node node = this._rootNode; + for (int i = 0, pos = index; i < this._nodeIndex; i++, pos++) + { + node = node.NextNode; + array.SetValue(node.Value, pos); + } + } + + /// + /// An object that can be used to synchronize this + /// to make it thread-safe. + /// + /// + /// An object that can be used to synchronize this + /// to make it thread-safe. + /// + public object SyncRoot + { + get { return this; } + } + + #endregion + + #region IEnumerable Members + + /// + /// Gets an enumerator for the elements in the + /// . + /// + /// + ///

    + /// Enumerators are fail fast. + ///

    + ///
    + /// + /// An over the elements + /// in the . + /// + public IEnumerator GetEnumerator() + { + return new LinkedListEnumerator(this); + } + + #endregion + + #region Inner Classes + + [Serializable] + private class Node + { + private object _value; + private Node _next; + private Node _previous; + + public object Value + { + get { return _value; } + set { _value = value; } + } + + public Node NextNode + { + get { return _next; } + set { this._next = value; } + } + + public Node PreviousNode + { + get { return _previous; } + set { this._previous = value; } + } + + public Node(object val, Node previous, Node next) + { + this._value = val; + this._next = next; + this._previous = previous; + } + } + + private class NodeHolder + { + private int _index; + private Node _node; + + public int Index + { + get { return _index; } + } + + public Node Node + { + get { return _node; } + } + + public NodeHolder(Node node, int index) + { + this._node = node; + this._index = index; + } + } + + private class LinkedListEnumerator : IEnumerator + { + private LinkedList _ll; + private Node _current; + private int _modId; + + public object Current + { + get { return _current.Value; } + } + + public LinkedListEnumerator(LinkedList ll) + { + this._ll = ll; + this._modId = ll._modId; + this._current = _ll._rootNode; + } + + public bool MoveNext() + { + if (this._modId != this._ll._modId) + { + throw new InvalidOperationException("LinkedList has been modified."); + } + + _current = _current.NextNode; + return (_current == _ll._rootNode ? false : true); + } + + public void Reset() + { + _current = _ll._rootNode; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/ListSet.cs b/src/Spring/Spring.Core/Collections/ListSet.cs new file mode 100644 index 00000000..ace9afc2 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/ListSet.cs @@ -0,0 +1,70 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; + +#endregion + +namespace Spring.Collections +{ + /// + /// Implements a based on a list. + /// + /// + ///

    + /// Performance is much better for very small lists than either + /// or . + /// However, performance degrades rapidly as the data-set gets bigger. Use a + /// instead if you are not sure your data-set + /// will always remain very small. Iteration produces elements in the order they were added. + /// However, element order is not guaranteed to be maintained by the various + /// mathematical operators. + ///

    + ///
    + [Serializable] + public class ListSet : DictionarySet + { + /// + /// Creates a new set instance based on a list. + /// + public ListSet() + { + InternalDictionary = new ListDictionary(); + } + + /// + /// Creates a new set instance based on a list and initializes it based on a + /// collection of elements. + /// + /// + /// A collection of elements that defines the initial set contents. + /// + public ListSet(ICollection initialValues) : this() + { + this.AddAll(initialValues); + } + } +} diff --git a/src/Spring/Spring.Core/Collections/NoElementsException.cs b/src/Spring/Spring.Core/Collections/NoElementsException.cs new file mode 100644 index 00000000..af54bfe1 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/NoElementsException.cs @@ -0,0 +1,91 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Collections +{ + /// + /// Thrown when an element is requested from an empty . + /// + /// Griffin Caprio + /// $Id: NoElementsException.cs,v 1.4 2006/04/09 07:18:37 markpollack Exp $ + [Serializable] + public class NoElementsException : ApplicationException + { + /// + /// Creates a new instance of the + /// class. + /// + public NoElementsException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected NoElementsException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Creates a new instance of the + /// class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + public NoElementsException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the + /// class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public NoElementsException(string message, Exception rootCause) + : base(message, rootCause) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/PriorityQueue.cs b/src/Spring/Spring.Core/Collections/PriorityQueue.cs new file mode 100644 index 00000000..338141c0 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/PriorityQueue.cs @@ -0,0 +1,833 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.Serialization; + +namespace Spring.Collections +{ + /// + /// An unbounded priority based on a priority + /// heap. This queue orders elements according to an order specified + /// at construction time, which is specified either according to their + /// natural order (see , or according to a + /// , depending on which constructor is + /// used. A priority queue does not permit elements. + /// A priority queue relying on natural ordering also does not + /// permit insertion of non-comparable objects (doing so will result + /// . + /// + ///

    + /// The head of this queue is the lowest element + /// with respect to the specified ordering. If multiple elements are + /// tied for lowest value, the head is one of those elements -- ties are + /// broken arbitrarily. + /// + ///

    + /// A priority queue is unbounded, but has an internal + /// capacity governing the size of an array used to store the + /// elements on the queue. It is always at least as large as the queue + /// size. As elements are added to a priority queue, its capacity + /// grows automatically. The details of the growth policy are not + /// specified. + /// + ///

    + /// This class and its enumerator implement all of the + /// optional methods of the and + /// interfaces. + /// The enumerator provided in method + /// is not guaranteed to traverse the elements of the PriorityQueue in any + /// particular order. + /// + ///

    + /// Note that this implementation is NOT synchronized. + /// Multiple threads should not access a + /// instance concurrently if any of the threads modifies the list + /// structurally. Instead, use the thread-safe PriorityBlockingQueue. + ///

    + /// Josh Bloch + /// Griffin Caprio (.NET) + [Serializable] + public class PriorityQueue : AbstractQueue, ISerializable + { + private class PriorityQueueEnumerator : IEnumerator + { + private PriorityQueue _enclosingInstance; + + public PriorityQueueEnumerator(PriorityQueue enclosingInstance) + { + _enclosingInstance = enclosingInstance; + } + + public Object Current + { + get + { + Object result = null; + if (_cursorIndex <= _enclosingInstance._priorityQueueSize) + { + result = _enclosingInstance._queue[_cursorIndex]; + } + return result; + } + + } + + /// + /// Index (into queue array) of element to be returned by subsequent call to next. + /// + private int _cursorIndex = 0; + + public bool MoveNext() + { + if ( _cursorIndex < _enclosingInstance._priorityQueueSize ) + { + _cursorIndex++; + return true; + } + return false; + } + + public void Reset() + { + _cursorIndex = 0; + } + } + + + #region Private Fields + + private const int DEFAULT_INITIAL_CAPACITY = 11; + + /// + /// Priority queue represented as a balanced binary heap: the two children + /// of queue[n] are queue[2*n] and queue[2*n + 1]. The priority queue is + /// ordered by comparator, or by the elements' natural ordering, if + /// comparator is null: For each node n in the heap and each descendant d + /// of n, n <= d. + /// + /// The element with the lowest value is in queue[1], assuming the queue is + /// nonempty. (A one-based array is used in preference to the traditional + /// zero-based array to simplify parent and child calculations.) + /// + /// queue.length must be >= 2, even if size == 0. + /// + [NonSerialized] private object[] _queue; + + /// The number of elements in the priority queue. + private int _priorityQueueSize = 0; + + /// + /// The comparator, or null if priority queue uses elements' + /// natural ordering. + /// + private IComparer _comparator; + + /// + /// The number of times this priority queue has been + /// structurally modified. + /// + [NonSerialized] private int _queueModificationCount = 0; + + #endregion + + #region Constructors + + /// + /// Creates a with the default initial capacity + /// (11) that orders its elements according to their natural + /// ordering (using ). + /// + public PriorityQueue() : this(DEFAULT_INITIAL_CAPACITY, null) + { + } + + /// + /// Creates a with the specified initial capacity + /// that orders its elements according to their natural ordering + /// (using ). + /// + /// the initial capacity for this priority queue. + /// + /// if is less than 1. + public PriorityQueue(int initialCapacity) : this(initialCapacity, null) + { + } + + /// + /// Creates a with the specified initial capacity + /// that orders its elements according to the specified comparator. + /// + /// the initial capacity for this priority queue. + /// the comparator used to order this priority queue. + /// If then the order depends on the elements' natural ordering. + /// + /// if is less than 1. + public PriorityQueue(int initialCapacity, IComparer comparator) + { + if (initialCapacity < 1) + throw new ArgumentException("initialCapacity"); + _queue = new object[initialCapacity + 1]; + _comparator = comparator; + } + + /// + /// Creates a containing the elements in the + /// specified collection. The priority queue has an initial + /// capacity of 110% of the size of the specified collection or 1 + /// if the collection is empty. If the specified collection is an + /// instance of a , the priority queue will be sorted + /// according to the same comparator, or according to its elements' + /// natural order if the collection is sorted according to its + /// elements' natural order. Otherwise, the priority queue is + /// ordered according to its elements' natural order. + /// + /// the collection whose elements are to be placed into this priority queue. + /// if elements of cannot be + /// compared to one another according to the priority queue's ordering + /// if or any element with it is + /// + /// + public PriorityQueue(ICollection collection) + { + if ( null == collection ) + throw new ArgumentNullException("collection"); + initializeArray(collection); + if (collection is PriorityQueue) + { + PriorityQueue s = (PriorityQueue) collection; + _comparator = s.Comparator(); + fillFromSorted(s); + } + else + { + _comparator = null; + fillFromUnsorted(collection); + } + } + + #endregion + + #region Private Helper Methods + + /// + /// Common code to initialize underlying queue array across + /// constructors below. + /// + private void initializeArray(ICollection c) + { + int size = c.Count; + int initialCapacity = getQueueSizeBasedOnPercentage(size, 110); + if (initialCapacity < 1) + initialCapacity = 1; + + _queue = new Object[initialCapacity + 1]; + } + + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + private int urShift(int number, int bits) + { + if (number >= 0) + return number >> bits; + else + return (number >> bits) + (2 << ~bits); + } + + /// + /// Establishes the heap invariant assuming the heap + /// satisfies the invariant except possibly for the leaf-node indexed by k + /// (which may have a nextExecutionTime less than its parent's). + /// + /// + /// This method functions by "promoting" queue[k] up the hierarchy + /// (by swapping it with its parent) repeatedly until queue[k] + /// is greater than or equal to its parent. + /// + private void fixUp(int k) + { + if (_comparator == null) + { + while (k > 1) + { + int j = k >> 1; + if (((IComparable) _queue[j]).CompareTo(_queue[k]) <= 0) + break; + Object tmp = _queue[j]; + _queue[j] = _queue[k]; + _queue[k] = tmp; + k = j; + } + } + else + { + while (k > 1) + { + int j = urShift(k, 1); + if (_comparator.Compare(_queue[j], _queue[k]) <= 0) + break; + Object tmp = _queue[j]; + _queue[j] = _queue[k]; + _queue[k] = tmp; + k = j; + } + } + } + + /// + /// Establishes the heap invariant (described above) in the subtree + /// rooted at k, which is assumed to satisfy the heap invariant except + /// possibly for node k itself (which may be greater than its children). + /// + /// + /// This method functions by "demoting" queue[k] down the hierarchy + /// (by swapping it with its smaller child) repeatedly until queue[k] + /// is less than or equal to its children. + /// + private void fixDown(int k) + { + int j; + if (_comparator == null) + { + while ((j = k << 1) <= _priorityQueueSize && (j > 0)) + { + if (j < _priorityQueueSize && ((IComparable) _queue[j]).CompareTo(_queue[j + 1]) > 0) + j++; // j indexes smallest kid + + if (((IComparable) _queue[k]).CompareTo(_queue[j]) <= 0) + break; + Object tmp = _queue[j]; + _queue[j] = _queue[k]; + _queue[k] = tmp; + k = j; + } + } + else + { + while ((j = k << 1) <= _priorityQueueSize && (j > 0)) + { + if (j < _priorityQueueSize && _comparator.Compare(_queue[j], _queue[j + 1]) > 0) + j++; // j indexes smallest kid + if (_comparator.Compare(_queue[k], _queue[j]) <= 0) + break; + Object tmp = _queue[j]; + _queue[j] = _queue[k]; + _queue[k] = tmp; + k = j; + } + } + } + + /// + /// Establishes the heap invariant in the entire tree, + /// assuming nothing about the order of the elements prior to the call. + /// + private void heapify() + { + for (int i = _priorityQueueSize/2; i >= 1; i--) + fixDown(i); + } + + /// + /// Returns the of or - 1, + /// whichever is smaller. + /// + /// base size + /// percentage to return + /// of + private int getQueueSizeBasedOnPercentage(int size, long percentage) + { + return (int) Math.Min((size*percentage)/100, Int32.MaxValue - 1); + } + + /// + /// Initially fill elements of the queue array under the + /// knowledge that it is sorted or is another , in which + /// case we can just place the elements in the order presented. + /// + private void fillFromSorted(ICollection collection) + { + fillArray(collection, true); + } + + private void fillArray(ICollection collection, bool sorted) + { + foreach (object currentObject in collection) + { + if (null == currentObject ) + throw new ArgumentNullException("collection", "Cannot add null elements to queue."); + _queue[++_priorityQueueSize] = currentObject; + } + if (! sorted) + { + heapify(); + } + } + + /// + /// Initially fill elements of the queue array that is not to our knowledge + /// sorted, so we must rearrange the elements to guarantee the heap + /// invariant. + /// + private void fillFromUnsorted(ICollection collection) + { + fillArray(collection, false); + } + + /// + /// Removes and returns element located at from queue. (Recall that the queue + /// is one-based, so 1 <= i <= size.) + /// + /// + /// Normally this method leaves the elements at positions from 1 up to i-1, + /// inclusive, untouched. Under these circumstances, it returns . + /// Occasionally, in order to maintain the heap invariant, it must move + /// the last element of the list to some index in the range [2, i-1], + /// and move the element previously at position (i/2) to position i. + /// Under these circumstances, this method returns the element that was + /// previously at the end of the list and is now at some position between + /// 2 and i-1 inclusive. + /// + private Object removeAt(int index) + { + Debug.Assert(index > 0 && index <= _priorityQueueSize); + _queueModificationCount++; + + Object moved = _queue[_priorityQueueSize]; + _queue[index] = moved; + _queue[_priorityQueueSize--] = null; + if (index <= _priorityQueueSize) + { + fixDown(index); + if (_queue[index] == moved) + { + fixUp(index); + if (_queue[index] != moved) + return moved; + } + } + return null; + } + + /// Resize array, if necessary, to be able to hold given index + private void grow(int index) + { + int newLength = _queue.Length; + if (index < newLength) + return; + if (index == Int32.MaxValue) + throw new InvalidOperationException("Cannot grow queue to accomdate index " + index + ". Doing so would result in a memory overflow."); + while (newLength <= index) + { + if (newLength >= Int32.MaxValue/2) + { + newLength = Int32.MaxValue; + } + else + { + newLength <<= 2; + } + } + Object[] newQueue = new Object[newLength]; + Array.Copy(_queue, 0, newQueue, 0, _queue.Length); + _queue = newQueue; + } + + #endregion + + #region Public Methods + /// + /// Gets the Capacity of this queue. Will equal + /// + public override int Capacity + { + get + { + return _queue.Length; + } + } + + /// + /// Returns the queue count. + /// + public override int Count + { + get { return _priorityQueueSize; } + } + + /// + /// Inserts the specified element into this queue if it is possible to do + /// so immediately without violating capacity restrictions. + /// + /// + ///

    + /// When using a capacity-restricted queue, this method is generally + /// preferable to , + /// which can fail to insert an element only by throwing an exception. + ///

    + ///
    + /// + /// The element to add. + /// + /// + /// if the element was added to this queue. + /// + /// + /// if the specified element cannot be compared + /// with elements currently in the priority queue according + /// to the priority queue's ordering. + /// + /// + /// If the element cannot be added at this time due to capacity restrictions. + /// + /// + /// If the supplied is + /// and this queue does not permit + /// elements. + /// + /// + /// If some property of the supplied prevents + /// it from being added to this queue. + /// + public override bool Offer(Object objectToAdd) + { + if (objectToAdd == null) + throw new ArgumentNullException("objectToAdd"); + _queueModificationCount++; + ++_priorityQueueSize; + + if (_priorityQueueSize >= _queue.Length) + { + grow(_priorityQueueSize); + } + _queue[_priorityQueueSize] = objectToAdd; + fixUp(_priorityQueueSize); + return true; + } + + /// + /// Retrieves, but does not remove, the head of this queue, + /// or returns if this queue is empty. + /// + /// + /// The head of this queue, or if this queue is empty. + /// + public override Object Peek() + { + if (_priorityQueueSize == 0) + return null; + return _queue[1]; + } + + // Collection Methods - the first two override to update docs + /// + /// Inserts the specified element into this queue if it is possible to do so + /// immediately without violating capacity restrictions, returning + /// upon success and throwing an + /// if no space is + /// currently available. + /// + /// + /// The element to add. + /// + /// + /// if successful. + /// + /// + /// If the element cannot be added at this time due to capacity restrictions. + /// + /// + /// If the specified element is and this queue does not + /// permit elements. + /// + /// + /// If some property of the supplied prevents + /// it from being added to this queue. + /// + /// + /// if the specified element cannot be compared + /// with elements currently in the priority queue according + /// to the priority queue's ordering. + /// + public override bool Add(Object objectToAdd) + { + return Offer(objectToAdd); + } + + /// + /// Removes a single instance of the specified element from this + /// queue, if it is present. + /// + public virtual bool Remove(Object objectToRemove) + { + if (objectToRemove == null) + { + return false; + } + + if (_comparator == null) + { + for (int i = 1; i <= _priorityQueueSize; i++) + { + if (((IComparable) _queue[i]).CompareTo(objectToRemove) == 0) + { + removeAt(i); + return true; + } + } + } + else + { + for (int i = 1; i <= _priorityQueueSize; i++) + { + if (_comparator.Compare(_queue[i], objectToRemove) == 0) + { + removeAt(i); + return true; + } + } + } + return false; + } + + /// + /// Returns an over the elements in this queue. + /// The enumeratoar does not return the elements in any particular order. + /// + /// an enumerator over the elements in this queue. + public override IEnumerator GetEnumerator() + { + return new PriorityQueueEnumerator(this); + } + + + /// + /// Removes all elements from the priority queue. + /// The queue will be empty after this call returns. + /// + public override void Clear() + { + _queueModificationCount++; + + for (int i = 1; i <= _priorityQueueSize; i++) + _queue[i] = null; + + _priorityQueueSize = 0; + } + + /// + /// Retrieves and removes the head of this queue, + /// or returns if this queue is empty. + /// + /// + /// The head of this queue, or if this queue is empty. + /// + public override Object Poll() + { + if (_priorityQueueSize == 0) + return null; + _queueModificationCount++; + + Object result = _queue[1]; + _queue[1] = _queue[_priorityQueueSize]; + _queue[_priorityQueueSize--] = null; + if (_priorityQueueSize > 1) + fixDown(1); + return result; + } + /// + /// Queries the queue to see if it contains the specified + /// + /// element to look for. + /// if the queue contains the , + /// otherwise. + public bool Contains( object element ) + { + if (element == null) + { + for (int num1 = 0; num1 < Capacity; num1++) + { + if (_queue[num1] == null) + { + return true; + } + } + return false; + } + for (int num2 = 0; num2 < Capacity; num2++) + { + if (element.Equals(_queue[num2])) + { + return true; + } + } + return false; + } + + + /// Returns the comparator used to order this collection, or + /// if this collection is sorted according to its elements natural ordering + /// (using ). + /// + /// + /// the comparator used to order this collection, or + /// if this collection is sorted according to its elements natural ordering. + /// + public virtual IComparer Comparator() + { + return _comparator; + } + #endregion + + #region ISerializable Implementation + /// + /// Save the state of the instance to a stream (that + /// is, serialize it). + /// + /// The length of the array backing the instance is + /// emitted (int), followed by all of its elements (each an + /// ) in the proper order. + /// + /// the stream + /// the context + public virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext context) + { + Type thisType = this.GetType(); + MemberInfo[] mi = FormatterServices.GetSerializableMembers(thisType, context); + for (int i = 0; i < mi.Length; i++) + { + serializationInfo.AddValue(mi[i].Name, ((FieldInfo) mi[i]).GetValue(this)); + } + + // Write out array length + serializationInfo.AddValue("Spring.Collections.PriorityQueuedata1", _queue.Length); + + // Write out all elements in the proper order. + for (int i = 1; i <= _priorityQueueSize; i++) + { + serializationInfo.AddValue("Spring.Collections.PriorityQueueData" + i, _queue[i]); + } + } + + /// + /// Reconstitute the instance from a stream (that is, + /// deserialize it). + /// + /// the stream + /// the context + protected PriorityQueue(SerializationInfo serializationInfo, StreamingContext context) + { + Type thisType = this.GetType(); + MemberInfo[] mi = FormatterServices.GetSerializableMembers(thisType, context); + for (int i = 0; i < mi.Length; i++) + { + FieldInfo fi = (FieldInfo) mi[i]; + fi.SetValue(this, serializationInfo.GetValue(fi.Name, fi.FieldType)); + } + int arrayLength = serializationInfo.GetInt32("Spring.Collections.PriorityQueuedata1"); + _queue = new Object[arrayLength]; + + for (int i = 1; i <= _priorityQueueSize; i++) + { + _queue[i] = serializationInfo.GetValue("Spring.Collections.PriorityQueueData" + i, typeof (Object)); + } + } + #endregion + + #region ICollection Implementation + + /// + ///Copies the elements of the to an , starting at a particular index. + /// + ///The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + ///The zero-based index in array at which copying begins. + ///array is null. + ///index is less than zero. + ///array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source is greater than the available space from index to the end of the destination array. + ///The type of the source cannot be cast automatically to the type of the destination array. 2 + public override void CopyTo(Array array,Int32 index) + { + if ( _priorityQueueSize > array.Length ) throw new ArgumentException("Destination array too small.", "array"); + if ( index > array.Length - 1 ) throw new ArgumentException("Starting index outside bounds of target array.", "index"); + if ( index + _priorityQueueSize > array.Length ) throw new IndexOutOfRangeException("Destination array not long enough to begin copying at index " + index + "."); + + for ( int queueElementCount = 1; queueElementCount <= _priorityQueueSize; queueElementCount++) + { + array.SetValue(_queue[queueElementCount], index ); + index++; + } + Array.Sort(array); + } + + /// + ///Copies the elements of the to an , starting at index 0. + /// + ///The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + ///array is null. + ///index is less than zero. + ///array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source is greater than the available space from index to the end of the destination array. + ///The type of the source cannot be cast automatically to the type of the destination array. 2 + public void CopyTo(Array array) + { + CopyTo(array, 0); + } + + /// + ///Gets an object that can be used to synchronize access to the . + /// + /// + ///An object that can be used to synchronize access to the . + /// + public override Object SyncRoot + { + get { return null; } + + } + + /// + ///Gets a value indicating whether access to the is synchronized (thread safe). + /// + /// + ///true if access to the is synchronized (thread safe); otherwise, false. + /// + public override Boolean IsSynchronized + { + get { return false; } + + } + + /// + /// Returns if there are no elements in the , otherwise. + /// + public override bool IsEmpty + { + get { return _priorityQueueSize == 0; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/Set.cs b/src/Spring/Spring.Core/Collections/Set.cs new file mode 100644 index 00000000..ff6ca5eb --- /dev/null +++ b/src/Spring/Spring.Core/Collections/Set.cs @@ -0,0 +1,568 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Collections +{ + /// + /// A collection that contains no duplicate elements. + /// + /// + /// $Id: Set.cs,v 1.7 2007/03/16 04:01:28 aseovic Exp $ + [Serializable] + public abstract class Set : ISet + { + /// + /// Performs a "union" of the two sets, where all the elements + /// in both sets are present. + /// + /// A collection of elements. + /// + /// A new containing the union of + /// this with the specified + /// collection. Neither of the input objects is modified by the union. + /// + /// + public virtual ISet Union(ISet setOne) + { + ISet resultSet = (ISet) this.Clone(); + if (setOne != null) + { + resultSet.AddAll(setOne); + } + return resultSet; + } + + /// + /// Performs a "union" of two sets, where all the elements in both are + /// present. + /// + /// + ///

    + /// That is, the element is included if it is in either + /// or . The return + /// value is a clone of one of the sets ( + /// if it is not ) with elements of the other set + /// added in. Neither of the input sets is modified by the operation. + ///

    + ///
    + /// A set of elements. + /// A set of elements. + /// + /// A set containing the union of the input sets; + /// if both sets are . + /// + public static ISet Union(ISet setOne, ISet anotherSet) + { + if (setOne == null && anotherSet == null) + { + return null; + } + else if (setOne == null) + { + return (ISet) anotherSet.Clone(); + } + else if (anotherSet == null) + { + return (ISet) setOne.Clone(); + } + else + { + return setOne.Union(anotherSet); + } + } + + /// + /// Performs a "union" of two sets, where all the elements in both are + /// present. + /// + /// A set of elements. + /// A set of elements. + /// + /// A set containing the union of the input sets; + /// if both sets are . + /// + /// + public static Set operator |(Set setOne, Set anotherSet) + { + return (Set) Union(setOne, anotherSet); + } + + /// + /// Performs an "intersection" of the two sets, where only the elements + /// that are present in both sets remain. + /// + /// A set of elements. + /// + /// The intersection of this set with . + /// + /// + public virtual ISet Intersect(ISet setOne) + { + ISet resultSet = (ISet) this.Clone(); + if (setOne != null) + { + resultSet.RetainAll(setOne); + } + else + { + resultSet.Clear(); + } + return resultSet; + } + + /// + /// Performs an "intersection" of the two sets, where only the elements + /// that are present in both sets remain. + /// + /// + ///

    + /// That is, the element is included only if it exists in both + /// and . Neither input + /// object is modified by the operation. The result object is a + /// clone of one of the input objects ( + /// if it is not ) containing the elements from + /// the intersect operation. + ///

    + ///
    + /// A set of elements. + /// A set of elements. + /// + /// The intersection of the two input sets; if + /// both sets are . + /// + public static ISet Intersect(ISet setOne, ISet anotherSet) + { + if (setOne == null && anotherSet == null) + { + return null; + } + else if (setOne == null) + { + return anotherSet.Intersect(setOne); + } + else + { + return setOne.Intersect(anotherSet); + } + } + + /// + /// Performs an "intersection" of the two sets, where only the elements + /// that are present in both sets remain. + /// + /// A set of elements. + /// A set of elements. + /// + /// The intersection of the two input sets; if + /// both sets are . + /// + /// + public static Set operator &(Set setOne, Set anotherSet) + { + return (Set) Intersect(setOne, anotherSet); + } + + /// + /// Performs a "minus" of this set from the + /// set. + /// + /// A set of elements. + /// + /// A set containing the elements from this set with the elements in + /// removed. + /// + /// + public virtual ISet Minus(ISet setOne) + { + ISet resultSet = (ISet) this.Clone(); + if (setOne != null) + { + resultSet.RemoveAll(setOne); + } + return resultSet; + } + + /// + /// Performs a "minus" of set from set + /// . + /// + /// + ///

    + /// This returns a set of all the elements in set + /// , removing the elements that are also in + /// set . The original sets are not modified + /// during this operation. The result set is a clone of set + /// containing the elements from the operation. + ///

    + ///
    + /// A set of elements. + /// A set of elements. + /// + /// A set containing + /// - elements. + /// if is + /// . + /// + public static ISet Minus(ISet setOne, ISet anotherSet) + { + if (setOne == null) + { + return null; + } + else + { + return setOne.Minus(anotherSet); + } + } + + /// + /// Performs a "minus" of set from set + /// . + /// + /// A set of elements. + /// A set of elements. + /// + /// A set containing + /// - elements. + /// if is + /// . + /// + /// + public static Set operator -(Set setOne, Set anotherSet) + { + return (Set) Minus(setOne, anotherSet); + } + + + /// + /// Performs an "exclusive-or" of the two sets, keeping only those + /// elements that are in one of the sets, but not in both. + /// + /// A set of elements. + /// + /// A set containing the result of + /// ^ this. + /// + /// + public virtual ISet ExclusiveOr(ISet setOne) + { + ISet resultSet = (ISet) this.Clone(); + foreach (object element in setOne) + { + if (resultSet.Contains(element)) + { + resultSet.Remove(element); + } + else + { + resultSet.Add(element); + } + } + return resultSet; + } + + /// + /// Performs an "exclusive-or" of the two sets, keeping only those + /// elements that are in one of the sets, but not in both. + /// + /// + ///

    + /// The original sets are not modified during this operation. The + /// result set is a clone of one of the sets ( + /// if it is not ) + /// containing the elements from the exclusive-or operation. + ///

    + ///
    + /// A set of elements. + /// A set of elements. + /// + /// A set containing the result of + /// ^ . + /// if both sets are . + /// + public static ISet ExclusiveOr(ISet setOne, ISet anotherSet) + { + if (setOne == null && anotherSet == null) + { + return null; + } + else if (setOne == null) + { + return (Set) anotherSet.Clone(); + } + else if (anotherSet == null) + { + return (Set) setOne.Clone(); + } + else + { + return setOne.ExclusiveOr(anotherSet); + } + } + + /// + /// Performs an "exclusive-or" of the two sets, keeping only those + /// elements that are in one of the sets, but not in both. + /// + /// A set of elements. + /// A set of elements. + /// + /// A set containing the result of + /// ^ . + /// if both sets are . + /// + /// + public static Set operator ^(Set setOne, Set anotherSet) + { + return (Set) ExclusiveOr(setOne, anotherSet); + } + + /// + /// Adds the specified element to this set if it is not already present. + /// + /// The object to add to the set. + /// + /// is the object was added, + /// if the object was already present. + /// + public abstract bool Add(object element); + + /// + /// Adds all the elements in the specified collection to the set if + /// they are not already present. + /// + /// A collection of objects to add to the set. + /// + /// is the set changed as a result of this + /// operation. + /// + public abstract bool AddAll(ICollection collection); + + /// + /// Removes all objects from this set. + /// + public abstract void Clear(); + + /// + /// Returns if this set contains the specified + /// element. + /// + /// The element to look for. + /// + /// if this set contains the specified element. + /// + public abstract bool Contains(object element); + + /// + /// Returns if the set contains all the + /// elements in the specified collection. + /// + /// A collection of objects. + /// + /// if the set contains all the elements in the + /// specified collection. + /// + public abstract bool ContainsAll(ICollection collection); + + /// + /// Returns if this set contains no elements. + /// + public abstract bool IsEmpty { get; } + + /// + /// Removes the specified element from the set. + /// + /// The element to be removed. + /// + /// if the set contained the specified element. + /// + public abstract bool Remove(object element); + + /// + /// Remove all the specified elements from this set, if they exist in + /// this set. + /// + /// A collection of elements to remove. + /// + /// if the set was modified as a result of this + /// operation. + /// + public abstract bool RemoveAll(ICollection collection); + + /// + /// Retains only the elements in this set that are contained in the + /// specified collection. + /// + /// + /// The collection that defines the set of elements to be retained. + /// + /// + /// if this set changed as a result of this + /// operation. + /// + public abstract bool RetainAll(ICollection collection); + + /// + /// Returns a clone of the + /// instance. + /// + /// + ///

    + /// This will work for derived + /// classes if the derived class implements a constructor that takes no + /// arguments. + ///

    + ///
    + /// A clone of this object. + public virtual object Clone() + { + Set newSet = (Set) Activator.CreateInstance(this.GetType()); + newSet.AddAll(this); + return newSet; + } + + /// + /// Copies the elements in the to + /// an array. + /// + /// + ///

    + /// The type of array needs to be compatible with the objects in the + /// , obviously. + ///

    + ///
    + /// + /// An array that will be the target of the copy operation. + /// + /// + /// The zero-based index where copying will start. + /// + public abstract void CopyTo(Array array, int index); + + /// + /// The number of elements currently contained in this collection. + /// + public abstract int Count { get; } + + /// + /// Returns if the + /// is synchronized across + /// threads. + /// + /// + ///

    + /// Note that enumeration is inherently not thread-safe. Use the + /// to lock the object during enumeration. + ///

    + ///
    + public abstract bool IsSynchronized { get; } + + /// + /// An object that can be used to synchronize this collection to make + /// it thread-safe. + /// + /// + ///

    + /// When implementing this, if your object uses a base object, like an + /// , or anything that has + /// a SyncRoot, return that object instead of "this". + ///

    + ///
    + /// + /// An object that can be used to synchronize this collection to make + /// it thread-safe. + /// + public abstract object SyncRoot { get; } + + /// + /// Gets an enumerator for the elements in the + /// . + /// + /// + /// An over the elements + /// in the . + /// + public abstract IEnumerator GetEnumerator(); + + /// + /// This method will test the + /// against another for + /// "equality". + /// + /// + ///

    + /// In this case, "equality" means that the two sets contain the same + /// elements. The "==" and "!=" operators are not overridden by design. + /// If you wish to check for "equivalent" + /// instances, use + /// Equals(). If you wish to check to see if two references are + /// actually the same object, use "==" and "!=". + ///

    + ///
    + /// + /// A object to compare to. + /// + /// + /// if the two sets contain the same elements. + /// + public override bool Equals(object obj) + { + Set theOtherSet = obj as Set; + if (theOtherSet == null || theOtherSet.Count != Count) + { + return false; + } + else + { + foreach (object element in theOtherSet) + { + if (!this.Contains(element)) + { + return false; + } + } + return true; + } + } + + /// + /// Gets the hashcode for the object. + /// + public override int GetHashCode() + { + int hashCode = 0, count = 0; + foreach (object element in this) + { + hashCode += element.GetHashCode(); + count++; + } + return count + hashCode; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/SortedSet.cs b/src/Spring/Spring.Core/Collections/SortedSet.cs new file mode 100644 index 00000000..55bd6558 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/SortedSet.cs @@ -0,0 +1,79 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Collections +{ + /// + /// Implements an based on a sorted + /// tree. + /// + /// + ///

    + /// This gives good performance for operations on very large data-sets, + /// though not as good - asymptotically - as a + /// . However, iteration occurs + /// in order. + ///

    + ///

    + /// Elements that you put into this type of collection must implement + /// , and they must actually be comparable. + /// You can't mix and + /// values, for example. + ///

    + ///

    + /// This implementation does + /// not support elements that are . + ///

    + ///
    + /// + /// $Id: SortedSet.cs,v 1.6 2007/03/16 04:01:29 aseovic Exp $ + [Serializable] + public class SortedSet : DictionarySet + { + /// + /// Creates a new set instance based on a sorted tree. + /// + public SortedSet() + { + InternalDictionary = new SortedList(); + } + + /// + /// Creates a new set instance based on a sorted tree and initializes + /// it based on a collection of elements. + /// + /// + /// A collection of elements that defines the initial set contents. + /// + public SortedSet(ICollection initialValues) : this() + { + this.AddAll(initialValues); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/SynchronizedDictionaryEnumerator.cs b/src/Spring/Spring.Core/Collections/SynchronizedDictionaryEnumerator.cs new file mode 100644 index 00000000..f6061e71 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/SynchronizedDictionaryEnumerator.cs @@ -0,0 +1,76 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.Collections +{ + /// + /// Synchronized that should be returned by synchronized + /// dictionary implementations in order to ensure that the enumeration is thread safe. + /// + /// Aleksandar Seovic + /// $Id: SynchronizedDictionaryEnumerator.cs,v 1.1 2006/05/03 01:13:41 aseovic Exp $ + internal class SynchronizedDictionaryEnumerator : SynchronizedEnumerator, IDictionaryEnumerator + { + public SynchronizedDictionaryEnumerator(object syncRoot, IDictionaryEnumerator enumerator) + : base(syncRoot, enumerator) + { + } + + protected IDictionaryEnumerator Enumerator + { + get { return (IDictionaryEnumerator) enumerator; } + } + + public object Key + { + get + { + lock (syncRoot) + { + return Enumerator.Key; + } + } + } + + public object Value + { + get + { + lock (syncRoot) + { + return Enumerator.Value; + } + } + } + + public DictionaryEntry Entry + { + get + { + lock (syncRoot) + { + return Enumerator.Entry; + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/SynchronizedEnumerator.cs b/src/Spring/Spring.Core/Collections/SynchronizedEnumerator.cs new file mode 100644 index 00000000..5060853b --- /dev/null +++ b/src/Spring/Spring.Core/Collections/SynchronizedEnumerator.cs @@ -0,0 +1,69 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.Collections +{ + /// + /// Synchronized that should be returned by synchronized + /// collections in order to ensure that the enumeration is thread safe. + /// + /// Aleksandar Seovic + /// $Id: SynchronizedEnumerator.cs,v 1.1 2006/05/03 01:13:41 aseovic Exp $ + internal class SynchronizedEnumerator : IEnumerator + { + protected object syncRoot; + protected IEnumerator enumerator; + + public SynchronizedEnumerator(object syncRoot, IEnumerator enumerator) + { + this.syncRoot = syncRoot; + this.enumerator = enumerator; + } + + public bool MoveNext() + { + lock (syncRoot) + { + return enumerator.MoveNext(); + } + } + + public void Reset() + { + lock (syncRoot) + { + enumerator.Reset(); + } + } + + public object Current + { + get + { + lock (syncRoot) + { + return enumerator.Current; + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/SynchronizedHashtable.cs b/src/Spring/Spring.Core/Collections/SynchronizedHashtable.cs new file mode 100644 index 00000000..4b754c36 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/SynchronizedHashtable.cs @@ -0,0 +1,367 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using Spring.Util; + +namespace Spring.Collections +{ + /// + /// Synchronized that, unlike hashtable created + /// using method, synchronizes + /// reads from the underlying hashtable in addition to writes. + /// + /// + ///

    + /// In addition to synchronizing reads, this implementation also fixes + /// IEnumerator/ICollection issue described at + /// http://msdn.microsoft.com/netframework/programming/breakingchanges/runtime/clr.aspx + /// (search for SynchronizedHashtable for issue description), by implementing + /// interface explicitly, and returns thread safe enumerator + /// implementations as well. + ///

    + ///

    + /// This class should be used whenever a truly synchronized + /// is needed. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: SynchronizedHashtable.cs,v 1.2 2007/08/27 13:57:17 oakinger Exp $ + [Serializable] + public class SynchronizedHashtable : IDictionary, ICollection, IEnumerable, ICloneable + { + private readonly Hashtable _table; + + #region Constructors + + /// + /// Initializes a new instance of + /// + public SynchronizedHashtable() + { + this._table = new Hashtable(); + } + + /// + /// Initializes a new instance of , copying inital entries from . + /// + public SynchronizedHashtable(IDictionary dictionary) + { + AssertUtils.ArgumentNotNull(dictionary, "dictionary"); + this._table = new Hashtable(dictionary); + } + + #endregion + + #region Properties + + /// + ///Gets a value indicating whether the object is read-only. + /// + /// + ///true if the object is read-only; otherwise, false. + /// + public bool IsReadOnly + { + get + { + lock (SyncRoot) + { + return _table.IsReadOnly; + } + } + } + + /// + ///Gets a value indicating whether the object has a fixed size. + /// + /// + ///true if the object has a fixed size; otherwise, false. + /// + public bool IsFixedSize + { + get + { + lock (SyncRoot) + { + return _table.IsFixedSize; + } + } + } + + /// + ///Gets a value indicating whether access to the is synchronized (thread safe). + /// + /// + ///true if access to the is synchronized (thread safe); otherwise, false. + /// + public bool IsSynchronized + { + get { return true; } + } + + /// + ///Gets an object containing the keys of the object. + /// + /// + ///An object containing the keys of the object. + /// + public ICollection Keys + { + get + { + lock (SyncRoot) + { + return _table.Keys; + } + } + } + + /// + ///Gets an object containing the values in the object. + /// + /// + ///An object containing the values in the object. + /// + public ICollection Values + { + get + { + lock (SyncRoot) + { + return _table.Values; + } + } + } + + /// + ///Gets an object that can be used to synchronize access to the . + /// + /// + ///An object that can be used to synchronize access to the . + /// + public object SyncRoot + { + get { return _table.SyncRoot; } + } + + /// + ///Gets the number of elements contained in the . + /// + /// + ///The number of elements contained in the . + /// + public int Count + { + get + { + lock (SyncRoot) + { + return _table.Count; + } + } + } + + #endregion + + #region Methods + + /// + ///Adds an element with the provided key and value to the object. + /// + ///The to use as the value of the element to add. + ///The to use as the key of the element to add. + ///An element with the same key already exists in the object. + ///key is null. + ///The is read-only.-or- The has a fixed size. 2 + public void Add(object key, object value) + { + lock (SyncRoot) + { + _table.Add(key, value); + } + } + + /// + ///Removes all elements from the object. + /// + ///The object is read-only. 2 + public void Clear() + { + lock (SyncRoot) + { + _table.Clear(); + } + } + + /// + ///Creates a new object that is a copy of the current instance. + /// + /// + ///A new object that is a copy of this instance. + /// + public object Clone() + { + lock (SyncRoot) + { + return new SynchronizedHashtable(this); + } + } + + /// + ///Determines whether the object contains an element with the specified key. + /// + /// + ///true if the contains an element with the key; otherwise, false. + /// + ///The key to locate in the object. + ///key is null. 2 + public bool Contains(object key) + { + lock (SyncRoot) + { + return _table.Contains(key); + } + } + + /// + /// Returns, whether this contains an entry with the specified . + /// + ///The key to look for + ///, if this contains an entry with this + public bool ContainsKey(object key) + { + lock (SyncRoot) + { + return _table.ContainsKey(key); + } + } + + /// + /// Returns, whether this contains an entry with the specified . + /// + ///The valúe to look for + ///, if this contains an entry with this + public bool ContainsValue(object value) + { + lock (SyncRoot) + { + return _table.ContainsValue(value); + } + } + + /// + ///Copies the elements of the to an , starting at a particular index. + /// + ///The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + ///The zero-based index in array at which copying begins. + ///array is null. + ///The type of the source cannot be cast automatically to the type of the destination array. + ///index is less than zero. + ///array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source is greater than the available space from index to the end of the destination array. 2 + public void CopyTo(Array array, int index) + { + lock (SyncRoot) + { + _table.CopyTo(array, index); + } + } + + /// + ///Returns an object for the object. + /// + /// + ///An object for the object. + /// + public IDictionaryEnumerator GetEnumerator() + { + lock (SyncRoot) + { + return new SynchronizedDictionaryEnumerator(SyncRoot, _table.GetEnumerator()); + } + } + + /// + ///Removes the element with the specified key from the object. + /// + ///The key of the element to remove. + ///The object is read-only.-or- The has a fixed size. + ///key is null. 2 + public void Remove(object key) + { + lock (SyncRoot) + { + _table.Remove(key); + } + } + + #endregion + + #region IEnumerable implementation + + /// + ///Returns an enumerator that iterates through a collection. + /// + /// + ///An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + lock (SyncRoot) + { + return new SynchronizedEnumerator(SyncRoot, ((IEnumerable) _table).GetEnumerator()); + } + } + + #endregion + + #region Indexer + + /// + ///Gets or sets the element with the specified key. + /// + /// + ///The element with the specified key. + /// + ///The key of the element to get or set. + ///The property is set and the object is read-only.-or- The property is set, key does not exist in the collection, and the has a fixed size. + ///key is null. 2 + public object this[object key] + { + get + { + lock (SyncRoot) + { + return _table[key]; + } + } + set + { + lock (SyncRoot) + { + _table[key] = value; + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Collections/SynchronizedSet.cs b/src/Spring/Spring.Core/Collections/SynchronizedSet.cs new file mode 100644 index 00000000..cfc216c5 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/SynchronizedSet.cs @@ -0,0 +1,331 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Collections +{ + /// + /// Implements a thread-safe wrapper. + /// + /// + ///

    + /// The implementation is extremely conservative, serializing critical + /// sections to prevent possible deadlocks, and locking on everything. The + /// one exception is for enumeration, which is inherently not thread-safe. + /// For this, you have to lock the SyncRoot object for the + /// duration of the enumeration. + ///

    + ///
    + /// + /// $Id: SynchronizedSet.cs,v 1.6 2007/03/16 04:01:29 aseovic Exp $ + [Serializable] + public sealed class SynchronizedSet : Set + { + private ISet _mBasisSet; + private object _mSyncRoot; + + /// + /// Constructs a thread-safe + /// wrapper. + /// + /// + /// The object that this object + /// will wrap. + /// + /// + /// If the supplied ecposes a + /// SyncRoot value. + /// + public SynchronizedSet(ISet basisSet) + { + _mBasisSet = basisSet; + _mSyncRoot = basisSet.SyncRoot; + if (_mSyncRoot == null) + { + throw new NullReferenceException( + "The Set you specified returned a null SyncRoot."); + } + } + + /// + /// Adds the specified element to this set if it is not already present. + /// + /// The object to add to the set. + /// + /// is the object was added, + /// if the object was already present. + /// + public override sealed bool Add(object element) + { + lock (_mSyncRoot) + { + return _mBasisSet.Add(element); + } + } + + /// + /// Adds all the elements in the specified collection to the set if + /// they are not already present. + /// + /// A collection of objects to add to the set. + /// + /// is the set changed as a result of this + /// operation. + /// + public override sealed bool AddAll(ICollection collection) + { + if(collection == null) + { + return false; + } + Set temp; + lock (collection.SyncRoot) + { + temp = new HybridSet(collection); + } + lock (_mSyncRoot) + { + return _mBasisSet.AddAll(temp); + } + } + + /// + /// Removes all objects from this set. + /// + public override sealed void Clear() + { + lock (_mSyncRoot) + { + _mBasisSet.Clear(); + } + } + + /// + /// Returns if this set contains the specified + /// element. + /// + /// The element to look for. + /// + /// if this set contains the specified element. + /// + public override sealed bool Contains(object element) + { + lock (_mSyncRoot) + { + return _mBasisSet.Contains(element); + } + } + + /// + /// Returns if the set contains all the + /// elements in the specified collection. + /// + /// A collection of objects. + /// + /// if the set contains all the elements in the + /// specified collection; also if the + /// supplied is . + /// + public override sealed bool ContainsAll(ICollection collection) + { + if(collection == null) + { + return false; + } + Set temp; + lock (collection.SyncRoot) + { + temp = new HybridSet(collection); + } + lock (_mSyncRoot) + { + return _mBasisSet.ContainsAll(temp); + } + } + + /// + /// Returns if this set contains no elements. + /// + public override sealed bool IsEmpty + { + get + { + lock (_mSyncRoot) + { + return _mBasisSet.IsEmpty; + } + } + } + + /// + /// Removes the specified element from the set. + /// + /// The element to be removed. + /// + /// if the set contained the specified element. + /// + public override sealed bool Remove(object element) + { + lock (_mSyncRoot) + { + return _mBasisSet.Remove(element); + } + } + + /// + /// Remove all the specified elements from this set, if they exist in + /// this set. + /// + /// A collection of elements to remove. + /// + /// if the set was modified as a result of this + /// operation. + /// + public override sealed bool RemoveAll(ICollection collection) + { + Set temp; + lock (collection.SyncRoot) + { + temp = new HybridSet(collection); + } + lock (_mSyncRoot) + { + return _mBasisSet.RemoveAll(temp); + } + } + + /// + /// Retains only the elements in this set that are contained in the + /// specified collection. + /// + /// + /// The collection that defines the set of elements to be retained. + /// + /// + /// if this set changed as a result of this + /// operation. + /// + public override sealed bool RetainAll(ICollection c) + { + Set temp; + lock (c.SyncRoot) + { + temp = new HybridSet(c); + } + lock (_mSyncRoot) + { + return _mBasisSet.RetainAll(temp); + } + } + + /// + /// Copies the elements in the to + /// an array. + /// + /// + ///

    + /// The type of array needs to be compatible with the objects in the + /// , obviously. + ///

    + ///
    + /// + /// An array that will be the target of the copy operation. + /// + /// + /// The zero-based index where copying will start. + /// + public override sealed void CopyTo(Array array, int index) + { + lock (_mSyncRoot) + { + _mBasisSet.CopyTo(array, index); + } + } + + /// + /// The number of elements currently contained in this collection. + /// + public override sealed int Count + { + get + { + lock (_mSyncRoot) + { + return _mBasisSet.Count; + } + } + } + + /// + /// Returns if the + /// is synchronized across + /// threads. + /// + /// + public override sealed bool IsSynchronized + { + get { return true; } + } + + /// + /// An object that can be used to synchronize this collection to make + /// it thread-safe. + /// + /// + /// An object that can be used to synchronize this collection to make + /// it thread-safe. + /// + /// + public override sealed object SyncRoot + { + get { return _mSyncRoot; } + } + + /// + /// Gets an enumerator for the elements in the + /// . + /// + /// + /// An over the elements + /// in the . + /// + public override sealed IEnumerator GetEnumerator() + { + return _mBasisSet.GetEnumerator(); + } + + /// + /// Returns a clone of the instance. + /// + /// A clone of this object. + public override object Clone() + { + return new SynchronizedSet((ISet) _mBasisSet.Clone()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/ApplicationContextException.cs b/src/Spring/Spring.Core/Context/ApplicationContextException.cs new file mode 100644 index 00000000..d57d1bdb --- /dev/null +++ b/src/Spring/Spring.Core/Context/ApplicationContextException.cs @@ -0,0 +1,87 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using Spring.Objects; + +#endregion + +namespace Spring.Context +{ + /// Exception thrown during application context initialization. + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: ApplicationContextException.cs,v 1.5 2006/04/09 07:18:38 markpollack Exp $ + [Serializable] + public class ApplicationContextException : FatalObjectException + { + /// + /// Creates a new instance of the + /// class. + /// + public ApplicationContextException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ApplicationContextException( SerializationInfo info, StreamingContext context ) + : base( info, context ) {} + + /// + /// Creates a new instance of the + /// class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + public ApplicationContextException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the + /// class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ApplicationContextException(string message, Exception rootCause) + : base(message, rootCause) + { + } + } +} diff --git a/src/Spring/Spring.Core/Context/ApplicationEventArgs.cs b/src/Spring/Spring.Core/Context/ApplicationEventArgs.cs new file mode 100644 index 00000000..99e51ecc --- /dev/null +++ b/src/Spring/Spring.Core/Context/ApplicationEventArgs.cs @@ -0,0 +1,71 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Context +{ + /// + /// Encapsulates the data associated with an event raised by an + /// . + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// Griffin Caprio (.NET) + /// + /// $Id: ApplicationEventArgs.cs,v 1.5 2006/04/09 07:18:38 markpollack Exp $ + [Serializable] + public class ApplicationEventArgs : EventArgs + { + private const long TicksAtEpoch = 621355968000000000; + private DateTime _timestamp; + + /// + /// Creates a new instance of the + /// class. + /// + public ApplicationEventArgs() + { + _timestamp = DateTime.Now; + } + + /// + /// The date and time when the event occured. + /// + /// + /// The date and time when the event occured. + /// + public DateTime TimeStamp + { + get { return _timestamp; } + } + + /// + /// The system time in milliseconds when the event happened. + /// + /// + /// The system time in milliseconds when the event happened. + /// + public long EventTimeMilliseconds + { + get { return ( _timestamp.Ticks - TicksAtEpoch ) / 10000; } + } + } +} diff --git a/src/Spring/Spring.Core/Context/EventListenerAttribute.cs b/src/Spring/Spring.Core/Context/EventListenerAttribute.cs new file mode 100644 index 00000000..86c8d0b4 --- /dev/null +++ b/src/Spring/Spring.Core/Context/EventListenerAttribute.cs @@ -0,0 +1,44 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Context +{ + /// + /// Marks an interface as being an application event listener. + /// + /// Griffin Caprio + /// $Id: EventListenerAttribute.cs,v 1.5 2006/04/09 07:18:38 markpollack Exp $ + /// + [AttributeUsage(AttributeTargets.Interface)] + public sealed class EventListenerAttribute : Attribute + { + /// + /// Creates a new instance of the + /// class. + /// + public EventListenerAttribute() {} + } +} diff --git a/src/Spring/Spring.Core/Context/Events/ConsoleListener.cs b/src/Spring/Spring.Core/Context/Events/ConsoleListener.cs new file mode 100644 index 00000000..fb050fc3 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Events/ConsoleListener.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Context.Events +{ + /// + /// Simple listener that logs application events to the console. + /// + /// + ///

    + /// Intended for use during debugging only. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: ConsoleListener.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ + /// + public sealed class ConsoleListener : IApplicationEventListener + { + /// + /// Creates a new instance of the + /// class. + /// + public ConsoleListener() + { + } + + /// + /// Handle an application event. + /// + /// + /// The source of the event. + /// + /// + /// The event that is to be handled. + /// + public void HandleApplicationEvent(object sender, ApplicationEventArgs e) + { + Console.WriteLine("Source : " + sender); + Console.WriteLine("Event fired : " + e.TimeStamp); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Events/ContextEventArgs.cs b/src/Spring/Spring.Core/Context/Events/ContextEventArgs.cs new file mode 100644 index 00000000..5f06af9a --- /dev/null +++ b/src/Spring/Spring.Core/Context/Events/ContextEventArgs.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Globalization; + +namespace Spring.Context.Events +{ + /// + /// Event object sent to listeners registered with an + /// to inform them of + /// context lifecycle events. + /// + /// Griffin Caprio (.NET) + /// $Id: ContextEventArgs.cs,v 1.8 2007/07/31 08:18:11 markpollack Exp $ + /// + /// + /// + public class ContextEventArgs : ApplicationEventArgs + { + /// + /// The various context event types. + /// + public enum ContextEvent + { + /// + /// The event type when the context is refreshed or created. + /// + Refreshed, + + /// + /// The event type when the context is closed. + /// + Closed + } ; + + private ContextEvent _contextEvent; + + /// + /// Creates a new instance of the ContextEventArgs class to represent the + /// supplied context event. + /// + /// The type of context event. + public ContextEventArgs(ContextEvent e) + { + _contextEvent = e; + } + + /// + /// The event type. + /// + public ContextEvent Event + { + get { return _contextEvent; } + } + + /// + /// Returns a string representation of this object. + /// + /// A string representation of this object. + public override string ToString() + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} [{1}]", GetType().Name, Event); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/IApplicationContext.cs b/src/Spring/Spring.Core/Context/IApplicationContext.cs new file mode 100644 index 00000000..fc745fb1 --- /dev/null +++ b/src/Spring/Spring.Core/Context/IApplicationContext.cs @@ -0,0 +1,164 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Core.IO; +using Spring.Objects.Events; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Context +{ + /// + /// The central interface to Spring.NET's IoC container. + /// + /// + ///

    + /// implementations + /// provide: + /// + /// + /// + /// Object factory functionality inherited from the + /// + /// and + /// interfaces. + /// + /// + /// + /// + /// The ability to resolve messages, supporting internationalization. + /// Inherited from the + /// interface. + /// + /// + /// + /// + /// The ability to load file resources in a generic fashion. + /// Inherited from the + /// interface. + /// + /// + /// + /// + /// Acts an an event registry for supporting loosely coupled eventing + /// between objecs. Inherited from the + /// interface. + /// + /// + /// + /// + /// The ability to raise events related to the context lifecycle. Inherited + /// from the + /// interface. + /// + /// + /// + /// + /// Inheritance from a parent context. Definitions in a descendant context + /// will always take priority. + /// + /// + /// + ///

    + ///

    + /// In addition to standard object factory lifecycle capabilities, + /// implementations need + /// to detect + /// , + /// , and + /// objects and supply + /// their attendant dependencies accordingly. + ///

    + ///

    + /// This interface is the central client interface in Spring.NET's IoC + /// container implementation. As such it does inherit a quite sizeable + /// number of interfaces; implementations are strongly encouraged to use + /// composition to satisfy each of the inherited interfaces (where + /// appropriate of course). + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// + /// + /// + /// + /// $Id: IApplicationContext.cs,v 1.15 2007/08/08 17:46:37 bbaia Exp $ + public interface IApplicationContext + : IListableObjectFactory, IHierarchicalObjectFactory, IMessageSource, + IApplicationEventPublisher, IResourceLoader, IEventRegistry, IDisposable + { + /// + /// Raised in response to an application context event. + /// + event ApplicationEventHandler ContextEvent; + + /// + /// Returns the date and time this context was loaded. + /// + /// + ///

    + /// This is to be set immediately after an + /// has been + /// instantiated and its configuration has been loaded. Implementations + /// are permitted to update this value if the context is reset or + /// refreshed in some way. + ///

    + ///
    + /// + /// The representing when this context + /// was loaded. + /// + /// + DateTime StartupDate { get; } + + /// + /// Gets the parent context, or if there is no + /// parent context. + /// + /// + ///

    + /// If the parent context is , then this context + /// is the root of any context hierarchy. + ///

    + ///
    + /// + /// The parent context, or if there is no + /// parent. + /// + IApplicationContext ParentContext { get; } + + /// + /// Gets and sets a name for this context. + /// + /// + /// A name for this context. + /// + string Name { get; set; } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/IApplicationContextAware.cs b/src/Spring/Spring.Core/Context/IApplicationContextAware.cs new file mode 100644 index 00000000..f82fe311 --- /dev/null +++ b/src/Spring/Spring.Core/Context/IApplicationContextAware.cs @@ -0,0 +1,99 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Context +{ + /// + /// To be implemented by any object that wishes to be notified + /// of the that it runs in. + /// + /// + ///

    + /// Implementing this interface makes sense when an object requires access + /// to a set of collaborating objects. Note that configuration via object + /// references is preferable to implementing this interface just for object + /// lookup purposes. + ///

    + ///

    + /// This interface can also be implemented if an object needs access to + /// file resources, i.e. wants to call + /// , or access to + /// the . However, it is + /// preferable to implement the more specific + /// + /// interface to receive a reference to the + /// object in that scenario. + ///

    + ///

    + /// Note that dependencies can also + /// be exposed as object properties of the + /// type, populated via strings with + /// automatic type conversion performed by an object factory. This obviates + /// the need for implementing any callback interface just for the purpose + /// of accessing a specific file resource. + ///

    + ///

    + /// + /// is a convenience implementation of this interface for your + /// application objects. + ///

    + ///

    + /// For a list of all object lifecycle methods, see the overview for the + /// interface. + ///

    + ///
    + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: IApplicationContextAware.cs,v 1.8 2007/08/08 17:46:37 bbaia Exp $ + /// + /// + /// + /// $Id: IApplicationContextAware.cs,v 1.8 2007/08/08 17:46:37 bbaia Exp $ + public interface IApplicationContextAware + { + /// + /// Set the that this + /// object runs in. + /// + /// + ///

    + /// Normally this call will be used to initialize the object. + ///

    + ///

    + /// Invoked after population of normal object properties but before an + /// init callback such as + /// 's + /// + /// or a custom init-method. Invoked after the setting of any + /// 's + /// + /// property. + ///

    + ///
    + /// + /// In the case of application context initialization errors. + /// + /// + /// If thrown by any application context methods. + /// + /// + IApplicationContext ApplicationContext { set; get; } + } +} diff --git a/src/Spring/Spring.Core/Context/IApplicationEventListener.cs b/src/Spring/Spring.Core/Context/IApplicationEventListener.cs new file mode 100644 index 00000000..d6d65803 --- /dev/null +++ b/src/Spring/Spring.Core/Context/IApplicationEventListener.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Context +{ + /// + /// The callback for application events. + /// + public delegate void ApplicationEventHandler(object sender, ApplicationEventArgs e); + + /// + /// A listener for application events. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: IApplicationEventListener.cs,v 1.2 2006/04/09 07:18:38 markpollack Exp $ + [EventListener] + public interface IApplicationEventListener + { + /// + /// Handle an application event. + /// + /// + /// The source of the event. + /// + /// + /// The event that is to be handled. + /// + void HandleApplicationEvent(object sender, ApplicationEventArgs e); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/IApplicationEventPublisher.cs b/src/Spring/Spring.Core/Context/IApplicationEventPublisher.cs new file mode 100644 index 00000000..c8fc9941 --- /dev/null +++ b/src/Spring/Spring.Core/Context/IApplicationEventPublisher.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Context { + + /// + /// Encapsulates event publication functionality. + /// + /// + ///

    + /// Serves as a super-interface for the + /// interface. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: IApplicationEventPublisher.cs,v 1.2 2006/04/09 07:18:38 markpollack Exp $ + public interface IApplicationEventPublisher + { + /// + /// Publishes an application context event. + /// + /// + /// The source of the event. May be . + /// + /// + /// The event that is to be raised. + /// + void PublishEvent(object sender, ApplicationEventArgs e); + } +} diff --git a/src/Spring/Spring.Core/Context/IConfigurableApplicationContext.cs b/src/Spring/Spring.Core/Context/IConfigurableApplicationContext.cs new file mode 100644 index 00000000..f9fc53fb --- /dev/null +++ b/src/Spring/Spring.Core/Context/IConfigurableApplicationContext.cs @@ -0,0 +1,132 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Context +{ + /// + /// Provides the means to configure an application context in addition to + /// the methods exposed on the + /// interface. + /// + /// + ///

    + /// This interface is to be implemented by most (if not all) + /// implementations. + ///

    + ///

    + /// Configuration and lifecycle methods are encapsulated here to avoid + /// making them obvious to + /// client code. + ///

    + ///

    + /// Calling will close this + /// application context, releasing all resources and locks that the + /// implementation might hold. This includes disposing all cached + /// singleton objects. + ///

    + /// + /// does not invoke the + /// attendant on any parent + /// context. + /// + ///
    + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: IConfigurableApplicationContext.cs,v 1.10 2006/04/09 07:18:38 markpollack Exp $ + /// + /// + public interface IConfigurableApplicationContext : IApplicationContext + { + /// + /// Return the internal object factory of this application context. + /// + /// + ///

    + /// Can be used to access specific functionality of the factory. + ///

    + /// + /// This is just guaranteed to return an instance that is not + /// after the context has been refreshed + /// at least once. + /// + /// + /// Do not use this to post-process the object factory; singletons + /// will already have been instantiated. Use an + /// + /// to intercept the object factory setup process before objects even + /// get touched. + /// + ///
    + /// + IConfigurableListableObjectFactory ObjectFactory { get; } + + /// + /// Add an + /// + /// that will get applied to the internal object factory of this + /// application context on refresh, before any of the object + /// definitions are evaluated. + /// + /// + ///

    + /// To be invoked during context configuration. + ///

    + ///
    + /// + /// The factory processor to register. + /// + /// + void AddObjectFactoryPostProcessor( + IObjectFactoryPostProcessor objectFactoryPostProcessor); + + /// + /// Load or refresh the persistent representation of the configuration, + /// which might an XML file, properties file, or relational database schema. + /// + /// + /// If the configuration cannot be loaded. + /// + /// + /// If the object factory could not be initialized. + /// + void Refresh(); + + /// + /// Sets the parent of this application context. + /// + /// + /// + /// The parent should not be changed: it should only be set + /// outside a constructor if it isn't available when an instance of + /// this class is created. + /// + /// + /// + /// The parent context. + /// + new IApplicationContext ParentContext { get; set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/IHierarchicalMessageSource.cs b/src/Spring/Spring.Core/Context/IHierarchicalMessageSource.cs new file mode 100644 index 00000000..e28c1654 --- /dev/null +++ b/src/Spring/Spring.Core/Context/IHierarchicalMessageSource.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Context +{ + /// + /// Sub-interface of to be + /// implemented by objects that can resolve messages hierarchically. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: IHierarchicalMessageSource.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ + /// + public interface IHierarchicalMessageSource : IMessageSource + { + /// + /// The parent message source used to try and resolve messages that + /// this object can't resolve. + /// + /// + ///

    + /// If the value of this property is then no + /// further resolution is possible. + ///

    + ///
    + IMessageSource ParentMessageSource { get; set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/IMessageSource.cs b/src/Spring/Spring.Core/Context/IMessageSource.cs new file mode 100644 index 00000000..b87cf616 --- /dev/null +++ b/src/Spring/Spring.Core/Context/IMessageSource.cs @@ -0,0 +1,308 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Globalization; + +namespace Spring.Context { + /// + /// Describes an object that can resolve messages. + /// + /// + ///

    + /// This enables the parameterization and internationalization of messages. + ///

    + ///

    + /// Spring.NET provides one out-of-the-box implementation for production + /// use: + ///

      + ///
    • .
    • + ///
    + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// Aleksandar Seovic (.NET) + /// $Id: IMessageSource.cs,v 1.13 2007/07/02 21:24:39 markpollack Exp $ + /// + public interface IMessageSource { + /// + /// Resolve the message identified by the supplied + /// . + /// + /// + ///

    + /// If the lookup is not successful, implementations are permitted to + /// take one of two actions. + ///

    + /// + /// + /// Throw an exception. + /// + /// + /// + /// Return the supplied as is. + /// + /// + /// + ///
    + /// The name of the message to resolve. + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + string GetMessage(string name); + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// + ///

    + /// If the lookup is not successful, implementations are permitted to + /// take one of two actions. + ///

    + /// + /// + /// Throw an exception. + /// + /// + /// + /// Return the supplied as is. + /// + /// + /// + ///
    + /// The name of the message to resolve. + /// + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + string GetMessage(string name, params object[] arguments); + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// + /// Note that the fallback behavior based on CultureInfo seem to + /// have a bug that is fixed by installed .NET 1.1 Service Pack 1. + ///

    + /// If the lookup is not successful, implementations are permitted to + /// take one of two actions. + ///

    + /// + /// + /// Throw an exception. + /// + /// + /// + /// Return the supplied as is. + /// + /// + /// + ///
    + /// The name of the message to resolve. + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + string GetMessage(string name, CultureInfo culture); + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// + /// Note that the fallback behavior based on CultureInfo seem to + /// have a bug that is fixed by installed .NET 1.1 Service Pack 1. + ///

    + /// If the lookup is not successful, implementations are permitted to + /// take one of two actions. + ///

    + /// + /// + /// Throw an exception. + /// + /// + /// + /// Return the supplied as is. + /// + /// + /// + ///
    + /// The name of the message to resolve. + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + string GetMessage(string name, CultureInfo culture, params object[] arguments); + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// + /// Note that the fallback behavior based on CultureInfo seem to + /// have a bug that is fixed by installed .NET 1.1 Service Pack 1. + ///

    + /// If the lookup is not successful, implementations are permitted to + /// take one of two actions. + ///

    + /// + /// + /// Throw an exception. + /// + /// + /// + /// Return the supplied as is. + /// + /// + /// + ///
    + /// The name of the message to resolve. + /// The default message if name is not found. + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + string GetMessage(string name, string defaultMessage, CultureInfo culture, params object[] arguments); + + /// + /// Resolve the message using all of the attributes contained within + /// the supplied + /// argument. + /// + /// + /// The value object storing those attributes that are required to + /// properly resolve a message. + /// + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If the message could not be resolved. + /// + string GetMessage(IMessageSourceResolvable resolvable, CultureInfo culture); + + /// + /// Gets a localized resource object identified by the supplied + /// . + /// + /// + ///

    + /// This method must use the + /// + /// value to obtain a resource. + ///

    + ///

    + /// Examples of resources that may be resolved by this method include + /// (but are not limited to) objects such as icons and bitmaps. + ///

    + ///
    + /// + /// The name of the resource object to resolve. + /// + /// + /// The resolved object, or if not found. + /// + object GetResourceObject(string name); + + /// + /// Gets a localized resource object identified by the supplied + /// . + /// + /// + ///

    + /// Examples of resources that may be resolved by this method include + /// (but are not limited to) objects such as icons and bitmaps. + ///

    + ///
    + /// + /// The name of the resource object to resolve. + /// + /// + /// The with which the + /// resource is associated. + /// + /// + /// The resolved object, or if not found. + /// + object GetResourceObject(string name, CultureInfo culture); + + /// + /// Applies resources to object properties. + /// + /// + ///

    + /// Resource key names are of the form objectName.propertyName. + ///

    + ///
    + /// + /// An object that contains the property values to be applied. + /// + /// + /// The base name of the object to use for key lookup. + /// + /// + /// The with which the + /// resource is associated. + /// + void ApplyResources(object value, string objectName, CultureInfo culture); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/IMessageSourceAware.cs b/src/Spring/Spring.Core/Context/IMessageSourceAware.cs new file mode 100644 index 00000000..9407c0b2 --- /dev/null +++ b/src/Spring/Spring.Core/Context/IMessageSourceAware.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Context { + + /// + /// To be implemented by any object that wishes to be notified + /// of the associated with it. + /// + /// + ///

    + /// In the current implementation, the + /// will typically be the + /// associated that + /// spawned the implementing object. + ///

    + ///

    + /// The can usually also be + /// passed on as an object reference to arbitrary object properties or + /// constructor arguments, because a + /// is typically defined as an + /// object with the well known name "messageSource" in the + /// associated application context. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: IMessageSourceAware.cs,v 1.4 2006/04/09 07:18:38 markpollack Exp $ + /// + public interface IMessageSourceAware + { + /// + /// Sets the associated + /// with this object. + /// + /// + ///

    + /// Invoked after population of normal object properties but + /// before an initializing callback such as the + /// + /// method of the + /// interface + /// or a custom init-method. + ///

    + ///

    + /// It is also invoked before the + /// + /// property of any + /// + /// implementation. + ///

    + ///
    + /// + /// The associated + /// with this object. + /// + IMessageSource MessageSource + { + set; + } + } +} diff --git a/src/Spring/Spring.Core/Context/IMessageSourceResolvable.cs b/src/Spring/Spring.Core/Context/IMessageSourceResolvable.cs new file mode 100644 index 00000000..01473015 --- /dev/null +++ b/src/Spring/Spring.Core/Context/IMessageSourceResolvable.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; + +#endregion + +namespace Spring.Context +{ + /// + /// Describes objects that are suitable for message resolution in a + /// . + /// + /// + ///

    + /// Spring.NET's own validation error classes implement this interface. + ///

    + ///
    + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: IMessageSourceResolvable.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ + /// + /// + public interface IMessageSourceResolvable + { + /// + /// Return the codes to be used to resolve this message, in the order + /// that they are to be tried. + /// + /// + ///

    + /// The last code will therefore be the default one. + ///

    + ///
    + /// + /// A array of codes which are associated + /// with this message. + /// + string[] GetCodes(); + + /// + /// Return the array of arguments to be used to resolve this message. + /// + /// + /// An array of objects to be used as parameters to replace + /// placeholders within the message text. + /// + object[] GetArguments(); + + /// + /// Return the default message to be used to resolve this message. + /// + /// + /// The default message, or if there is no + /// default. + /// + string DefaultMessage { get; } + } +} diff --git a/src/Spring/Spring.Core/Context/IResourceLoaderAware.cs b/src/Spring/Spring.Core/Context/IResourceLoaderAware.cs new file mode 100644 index 00000000..f8006c7b --- /dev/null +++ b/src/Spring/Spring.Core/Context/IResourceLoaderAware.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Core.IO; + +#endregion + +namespace Spring.Context +{ + /// + /// Interface to be implemented by any object that wishes to be notified + /// of the (typically the + /// ) that it runs in. + /// + /// + ///

    + /// Note that dependencies can also + /// be exposed as object properties of type + /// , populated via strings with + /// automatic type conversion by the object factory. This obviates the + /// need for implementing any callback interface just for the purpose of + /// accessing a specific resource. + ///

    + ///

    + /// You typically need an + /// when your application object has to access a variety of file resources + /// whose names are calculated. A good strategy is to make the object use + /// a default resource loader but still implement the + /// interface to allow + /// for overriding when running in an + /// . + ///

    + ///
    + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: IResourceLoaderAware.cs,v 1.8 2007/08/08 17:46:37 bbaia Exp $ + /// + /// + /// + public interface IResourceLoaderAware + { + /// + /// Gets and sets the + /// that this object runs in. + /// + /// + ///

    + /// Invoked after population of normal objects properties but + /// before an init callback such as + /// 's + /// + /// or a custom init-method. Invoked before setting + /// 's + /// + /// property. + ///

    + ///
    + IResourceLoader ResourceLoader + { + set; + get; + } + } +} diff --git a/src/Spring/Spring.Core/Context/NoSuchMessageException.cs b/src/Spring/Spring.Core/Context/NoSuchMessageException.cs new file mode 100644 index 00000000..b2eb9026 --- /dev/null +++ b/src/Spring/Spring.Core/Context/NoSuchMessageException.cs @@ -0,0 +1,115 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Context +{ + /// + /// Thrown when a message cannot be resolved. + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// + /// + /// $Id: NoSuchMessageException.cs,v 1.8 2007/07/17 14:51:13 oakinger Exp $ + [Serializable] + public class NoSuchMessageException : ApplicationException + { + /// + /// Creates a new instance of the + /// class. + /// + public NoSuchMessageException() + { + } + + /// + /// Creates a new instance of the + /// class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public NoSuchMessageException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being + /// thrown. + /// + /// + /// The + /// that contains contextual information about the source or + /// destination. + /// + protected NoSuchMessageException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The code that could not be resolved for given culture. + /// + /// + /// The that was used + /// to search for the code. + /// + public NoSuchMessageException(string code, CultureInfo culture) + : base(string.Format("No message found under code '{0}' for locale '{1}'.", + code, culture)) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The code that could not be resolved for the current UI culture. + /// + public NoSuchMessageException(string code) + : this(code, CultureInfo.CurrentUICulture) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs new file mode 100644 index 00000000..ed7ab560 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs @@ -0,0 +1,1754 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Diagnostics; +using System.Globalization; +using Common.Logging; +using Spring.Context.Events; +using Spring.Core; +using Spring.Core.IO; +using Spring.Objects; +using Spring.Objects.Events; +using Spring.Objects.Events.Support; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Partial implementation of the + /// interface. + /// + /// + ///

    + /// Does not mandate the type of storage used for configuration, but does + /// implement common functionality. Uses the Template Method design + /// pattern, requiring concrete subclasses to implement + /// methods. + ///

    + ///

    + /// In contrast to a plain vanilla + /// , an + /// is supposed + /// to detect special objects defined in its object factory: therefore, + /// this class automatically registers + /// s, + /// s + /// and s that are + /// defined as objects in the context. + ///

    + ///

    + /// An may be also supplied as + /// an object in the context, with the special, well-known-name of + /// "messageSource". Else, message resolution is delegated to the + /// parent context. + ///

    + ///
    + /// Rod Johnson + /// Juergan Hoeller + /// Griffin Caprio (.NET) + /// $Id: AbstractApplicationContext.cs,v 1.74 2007/08/27 09:38:28 oakinger Exp $ + /// + /// + public abstract class AbstractApplicationContext + : ConfigurableResourceLoader, IConfigurableApplicationContext + { + #region Constants + + /// + /// Name of the .Net config section that contains Spring.Net context definition. + /// + public const string ContextSectionName = "spring/context"; + + /// + /// Default name of the root context. + /// + public const string DefaultRootContextName = "spring.root"; + + #endregion + + #region Fields + + private const long TicksAtEpoch = 621355968000000000; + + /// + /// The special, well-known-name of the default + /// in the context. + /// + /// + ///

    + /// If no can be found + /// in the context using this lookup key, then message resolution + /// will be delegated to the parent context (if any). + ///

    + ///
    + public static readonly string MessageSourceObjectName = "messageSource"; + + /// + /// The special, well-known-name of the default + /// in the context. + /// + /// + ///

    + /// If no can be found + /// in the context using this lookup key, then a default + /// will be used. + ///

    + ///
    + public static readonly string EventRegistryObjectName = "eventRegistry"; + + /// + /// The instance for this class. + /// + private static readonly ILog log = LogManager.GetLogger(typeof(AbstractApplicationContext)); + + /// + /// The instance we delegate + /// our implementation of said interface to. + /// + private IMessageSource _messageSource; + + /// + /// The instance we + /// delegate our implementation of said interface to. + /// + private IEventRegistry _eventRegistry; + + private IApplicationContext _parentApplicationContext; + private readonly IList _objectFactoryPostProcessors; + private IList _defaultObjectPostProcessors; + private string _name; + private DateTime _startupDate; + private readonly bool _caseSensitive; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// with no parent context. + /// + /// + ///

    + /// This is an class, and as such exposes + /// no public constructors. + ///

    + ///
    + protected AbstractApplicationContext() : this(null, true, null) + { + } + + /// + /// Creates a new instance of the + /// with no parent context. + /// + /// + ///

    + /// This is an class, and as such exposes + /// no public constructors. + ///

    + ///
    + /// Flag specifying whether to make this context case sensitive or not. + protected AbstractApplicationContext(bool caseSensitive) : this(null, caseSensitive, null) + { + } + + /// + /// Creates a new instance of the + /// with the supplied parent context. + /// + /// + ///

    + /// This is an class, and as such exposes + /// no public constructors. + ///

    + ///
    + /// The application context name. + /// Flag specifying whether to make this context case sensitive or not. + /// The parent application context. + protected AbstractApplicationContext(string name, bool caseSensitive, + IApplicationContext parentApplicationContext) + { + _name = (StringUtils.IsNullOrEmpty(name)) ? DefaultRootContextName : name; + _caseSensitive = caseSensitive; + _parentApplicationContext = parentApplicationContext; + _objectFactoryPostProcessors = new ArrayList(); + _defaultObjectPostProcessors = new ArrayList(); + AddDefaultObjectPostProcessor(new ObjectPostProcessorChecker()); + AddDefaultObjectPostProcessor(new ApplicationContextAwareProcessor(this)); + } + + /// + /// Adds the given to the list of standard + /// processors being added to the underlying + /// + /// + /// Each time is called on this context, the context ensures, that + /// all default s are registered with the underlying . + /// + /// The instance. + protected void AddDefaultObjectPostProcessor(IObjectPostProcessor defaultObjectPostProcessor) + { + _defaultObjectPostProcessors.Add(defaultObjectPostProcessor); + } + + /// + /// Closes this context and disposes of any resources (such as + /// singleton objects in the wrapped + /// ). + /// + public virtual void Dispose() + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "Closing application context [{0}].", + Name)); + } + + #endregion + + new DefensiveEventRaiser().Raise( + ContextEvent, this, + new ContextEventArgs(ContextEventArgs.ContextEvent.Closed)); + ObjectFactory.Dispose(); + } + + #endregion + + #region Abstract Methods + + /// + /// Subclasses must implement this method to perform the actual + /// configuration loading. + /// + /// + ///

    + /// This method is invoked by + /// , + /// before any other initialization occurs. + ///

    + ///
    + /// + /// In the case of errors encountered while refreshing the object factory. + /// + protected abstract void RefreshObjectFactory(); + + #endregion + + /// + /// An object that can be used to synchronize access to the + /// + public object SyncRoot + { + get { return this; } + } + + /// + /// The timestamp when this context was first loaded. + /// + /// + /// The timestamp (milliseconds) when this context was first loaded. + /// + public long StartupDateMilliseconds + { + get { return (StartupDate.Ticks - TicksAtEpoch)/10000; } + } + + + /// + /// Gets a flag indicating whether context should be case sensitive. + /// + /// true if object lookups are case sensitive; otherwise, false. + protected bool CaseSensitive + { + get { return _caseSensitive; } + } + + /// + /// The for this context. + /// + /// + /// If the context has not been initialized yet. + /// + public IMessageSource MessageSource + { + get + { + if (_messageSource == null) + { + throw new InvalidOperationException( + "MessageSource not initialized - call 'Refresh()' " + + "before accessing messages via the context: " + this); + } + return _messageSource; + } + } + + /// + /// The for this context. + /// + /// + /// If the context has not been initialized yet. + /// + public IEventRegistry EventRegistry + { + get + { + if (_eventRegistry == null) + { + throw new InvalidOperationException( + "EventRegistry not initialized - call 'Refresh()' " + + "before accessing the event registry via the context: " + this); + } + return _eventRegistry; + } + } + + /// + /// Returns the internal object factory of the parent context if it implements + /// ; else, + /// returns the parent context itself. + /// + /// + /// The parent context's object factory, or the parent itself. + /// + protected IObjectFactory GetInternalParentObjectFactory() + { + IConfigurableApplicationContext configContext + = _parentApplicationContext as IConfigurableApplicationContext; + if (configContext != null) + { + return ((IConfigurableApplicationContext) + _parentApplicationContext).ObjectFactory; + } + else + { + return _parentApplicationContext; + } + } + + /// + /// Raises an application context event. + /// + /// + /// Any arguments to the event. May be . + /// + protected virtual void OnContextEvent(ApplicationEventArgs e) + { + OnContextEvent(this, e); + } + + /// + /// Raises an application context event. + /// + /// + /// The source of the event. + /// + /// + /// Any arguments to the event. May be . + /// + protected virtual void OnContextEvent(object source, ApplicationEventArgs e) + { + new DefensiveEventRaiser().Raise(ContextEvent, source, e); + } + + /// + /// Modify the application context's internal object factory after its standard + /// initialization. + /// + /// + ///

    + /// All object definitions will have been loaded, but no objects + /// will have been instantiated yet. This allows for the registration + /// of special + /// s + /// in certain + /// implementations. + ///

    + ///
    + /// + /// The object factory used by the application context. + /// + /// + /// In the case of errors. + /// . + protected virtual void PostProcessObjectFactory( + IConfigurableListableObjectFactory objectFactory) + { + } + + /// + /// Template method which can be overridden to add context-specific + /// refresh work. + /// + /// + ///

    + /// Called on initialization of special objects, before instantiation + /// of singletons. + ///

    + ///
    + protected virtual void OnRefresh() + { + } + + /// + /// Instantiate and invoke all registered + /// + /// objects, respecting any explicit ordering. + /// + /// + /// + /// Must be called before singleton instantiation. + /// + /// + /// In the case of errors. + private void InvokeObjectFactoryPostProcessors() + { + // do NOT include IFactoryObjects; they (typically) need to be instantiated + // to determine the Type of object that they create, and if they are instantiated + // then we won't be able to do any factory post processin' on 'em... + string[] factoryProcessorNames + = GetObjectNamesForType(typeof(IObjectFactoryPostProcessor), true, false); + ArrayList orderedFactoryProcessors = new ArrayList(); + IList nonOrderedFactoryProcessorNames = new ArrayList(); + for (int i = 0; i < factoryProcessorNames.Length; ++i) + { + string processorName = factoryProcessorNames[i]; + object processor = GetObject(processorName); + if (typeof(IOrdered).IsAssignableFrom(GetType(processorName))) + { + orderedFactoryProcessors.Add(processor); + } + else + { + nonOrderedFactoryProcessorNames.Add(processor); + } + } + // first, invoke those IObjectFactoryPostProcessors that implement IOrdered... + orderedFactoryProcessors.Sort(new OrderComparator()); + ProcessObjectFactoryPostProcessors(orderedFactoryProcessors); + // and then the unordered ones... + ProcessObjectFactoryPostProcessors(nonOrderedFactoryProcessorNames); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "processed {0} IFactoryObjectPostProcessors defined in application context [{1}].", + factoryProcessorNames.Length, + Name)); + } + + #endregion + } + + private void ProcessObjectFactoryPostProcessors(IList orderedFactoryProcessors) + { + foreach (IObjectFactoryPostProcessor processor in orderedFactoryProcessors) + { + processor.PostProcessObjectFactory(ObjectFactory); + } + } + + private void RegisterObjectPostProcessors(IConfigurableListableObjectFactory objectFactory) + { + RegisterObjectPostProcessorChecker(objectFactory); + IDictionary dict = GetObjectsOfType(typeof(IObjectPostProcessor), true, false); + ArrayList objectProcessors = new ArrayList(dict.Values); + objectProcessors.Sort(new OrderComparator()); + foreach (IObjectPostProcessor objectPostProcessor in objectProcessors) + { + ObjectFactory.AddObjectPostProcessor(objectPostProcessor); + } + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "processed {0} IObjectPostProcessors defined in application context [{1}].", + objectProcessors.Count, + Name)); + } + } + + /// + /// Register an IObjectPostProcessorChecker that logs an info + /// message when an object is created during IObjectPostProcessor + /// instantiation, i.e. when an object is not eligible for being + /// processed by all IObjectPostProcessors. + /// + private void RegisterObjectPostProcessorChecker(IConfigurableListableObjectFactory objectFactory) + { + int objectPostProcessorCount + = ObjectFactory.ObjectPostProcessorCount + 1 + + GetObjectNamesForType(typeof(IObjectPostProcessor), true, false).Length; +// ObjectFactory.AddObjectPostProcessor( +// new ObjectPostProcessorChecker(objectFactory, objectPostProcessorCount)); + ((ObjectPostProcessorChecker) _defaultObjectPostProcessors[0]).Reset(objectFactory, objectPostProcessorCount); + } + + /// + /// Initializes the default event registry for this context. + /// + private void InitEventRegistry() + { + if (ContainsObject(EventRegistryObjectName)) + { + object candidateRegistry = GetObject(EventRegistryObjectName); + if (candidateRegistry is IEventRegistry) + { + _eventRegistry = (IEventRegistry) candidateRegistry; + + #region Instrumentation + + log.Debug(StringUtils.Surround( + "Using IEventRegistry [", EventRegistry, "]")); + + #endregion + } + else + { + _eventRegistry = new EventRegistry(); + + #region Instrumentation + + if (log.IsWarnEnabled) + { + log.Warn(string.Format( + "Found object in context named '{0}' : this name " + + "is typically reserved for IEventRegistry objects. " + + "Falling back to default '{1}'.", + EventRegistryObjectName, EventRegistry)); + } + + #endregion + } + } + else + { + _eventRegistry = new EventRegistry(); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + "No IEventRegistry found with name '{0}' : using default '{1}'.", + EventRegistryObjectName, EventRegistry)); + } + + #endregion + } + ICollection interestedParties + = GetObjectsOfType(typeof(IEventRegistryAware), true, false).Values; + foreach (IEventRegistryAware party in interestedParties) + { + party.EventRegistry = EventRegistry; + } + EventRegistry.PublishEvents(this); + } + + /// + /// Returns the internal message source of the parent context if said + /// parent context is an , else + /// simply the parent context itself. + /// + /// + /// The internal message source of the parent context if said + /// parent context is an , else + /// simply the parent context itself. + /// + protected virtual IMessageSource GetInternalParentMessageSource() + { + AbstractApplicationContext parent + = ParentContext as AbstractApplicationContext; + return parent == null ? ParentContext : parent._messageSource; + } + + /// + /// Initializes the default message source for this context. + /// + /// + ///

    + /// Uses any parent context's message source if one is not available + /// in this context. + ///

    + ///
    + private void InitMessageSource() + { + if (ContainsObject(MessageSourceObjectName)) + { + object candidateSource = GetObject(MessageSourceObjectName); + if (candidateSource is IMessageSource) + { + _messageSource + = (IMessageSource) GetObject(MessageSourceObjectName); + + // make IMessageSource aware of any parent IMessageSource... + if (ParentContext != null) + { + IHierarchicalMessageSource hierSource + = MessageSource as IHierarchicalMessageSource; + if (hierSource != null) + { + IMessageSource parentMessageSource + = GetInternalParentMessageSource(); + hierSource.ParentMessageSource = parentMessageSource; + } + } + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(StringUtils.Surround( + "Using MessageSource [", MessageSource, "]")); + } + + #endregion + } + else + { + _messageSource = new DelegatingMessageSource( + GetInternalParentMessageSource()); + + #region Instrumentation + + if (log.IsWarnEnabled) + { + log.Warn(string.Format( + "Found object in context named '{0}' : this name " + + "is typically reserved for IMessageSource objects. " + + "Falling back to default '{1}'.", + MessageSourceObjectName, MessageSource)); + } + + #endregion + } + } + else if (ParentContext != null) + { + _messageSource = new DelegatingMessageSource( + GetInternalParentMessageSource()); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + "No message source found in the current context: using parent context's message source '{0}'.", + MessageSource)); + } + + #endregion + } + else + { + _messageSource = new StaticMessageSource(); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + "No IMessageSource found with name '{0}' : using default '{1}'.", + MessageSourceObjectName, MessageSource)); + } + + #endregion + } + } + + private void RefreshApplicationEventListeners() + { + ICollection listeners + = GetObjectsOfType( + typeof(IApplicationEventListener), true, false).Values; + foreach (IApplicationEventListener applicationListener in listeners) + { + EventRegistry.Subscribe(applicationListener); + } + } + + /// + /// Returns the list of the + /// s + /// that will be applied to the objects created with this factory. + /// + /// + ///

    + /// The elements of this list are instances of implementations of the + /// + /// interface. + ///

    + ///
    + /// + /// The list of the + /// s + /// that will be applied to the objects created with this factory. + /// + private IList ObjectFactoryPostProcessors + { + get { return _objectFactoryPostProcessors; } + } + + #region IConfigurableApplicationContext Members + + /// + /// Return the internal object factory of this application context. + /// + public abstract IConfigurableListableObjectFactory ObjectFactory { get; } + + /// + /// Add a new + /// that will get applied to the internal object factory of this application context + /// on refresh, before any of the object definitions are evaluated. + /// + /// + /// The factory processor to register. + /// + public void AddObjectFactoryPostProcessor( + IObjectFactoryPostProcessor objectFactoryPostProcessor) + { + _objectFactoryPostProcessors.Add(objectFactoryPostProcessor); + } + + /// + /// Load or refresh the persistent representation of the configuration, + /// which might an XML file, properties file, or relational database schema. + /// + /// + /// If the configuration cannot be loaded. + /// + /// + /// If the object factory could not be initialized. + /// + public virtual void Refresh() + { + lock (SyncRoot) + { + + /* + #region Instrumentation + + if (log.IsDebugEnabled) + { + StackTrace stackTrace = new StackTrace(1, true); + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "Refreshing application context [{0}]. Called from:{1}", + Name, stackTrace)); + } + + #endregion + */ + + _startupDate = DateTime.Now; + + RefreshObjectFactory(); + IConfigurableListableObjectFactory objectFactory = ObjectFactory; + + EnsureKnownObjectPostProcessors(objectFactory); + objectFactory.IgnoreDependencyType(typeof(IResourceLoader)); + objectFactory.IgnoreDependencyType(typeof(IApplicationContext)); + + PostProcessObjectFactory(objectFactory); + foreach (IObjectFactoryPostProcessor factoryProcessor in ObjectFactoryPostProcessors) + { + factoryProcessor.PostProcessObjectFactory(objectFactory); + } + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "{0} objects defined in application context [{1}].", + ObjectDefinitionCount == 0 ? "No" : ObjectDefinitionCount.ToString(), + Name)); + } + + #endregion + + InvokeObjectFactoryPostProcessors(); + RegisterObjectPostProcessors(objectFactory); + InitEventRegistry(); + InitMessageSource(); + OnRefresh(); + RefreshApplicationEventListeners(); + + objectFactory.PreInstantiateSingletons(); + + new DefensiveEventRaiser().Raise( + ContextEvent, this, + new ContextEventArgs(ContextEventArgs.ContextEvent.Refreshed)); + } + } + + /// + /// Ensures, that predefined ObjectPostProcessors are registered with this ObjectFactory + /// + /// + protected void EnsureKnownObjectPostProcessors(IConfigurableListableObjectFactory objectFactory) + { + // index 0 contains the ObjectPostProcessorChecker that is handled separately! + for (int i = 1; i < _defaultObjectPostProcessors.Count; i++) + { + objectFactory.AddObjectPostProcessor((IObjectPostProcessor) this._defaultObjectPostProcessors[i]); + } + } + + /// + /// Gets the parent context, or if there is no + /// parent context. + /// + /// + /// The parent context, or if there is no + /// parent. + /// + /// + public virtual IApplicationContext ParentContext + { + get { return _parentApplicationContext; } + set { _parentApplicationContext = value; } + } + + #endregion + + #region IApplicationContext Members + + /// + /// Raised in response to an implementation-dependant application + /// context event. + /// + public event ApplicationEventHandler ContextEvent; + + /// + /// The date and time this context was first loaded. + /// + /// + /// The representing when this context + /// was first loaded. + /// + public DateTime StartupDate + { + get { return _startupDate; } + } + + /// + /// A name for this context. + /// + /// + /// A name for this context. + /// + public string Name + { + get { return _name; } + set { _name = value; } + } + + + + #endregion + + #region IListableObjectFactory Members + + /// + /// Return the names of objects matching the given + /// (including subclasses), judging from the object definitions. + /// + /// + /// The (class or interface) to match, or + /// for all object names. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + /// + public string[] GetObjectNamesForType(Type type) + { + return ObjectFactory.GetObjectNamesForType(type); + } + + /// + /// Return the names of objects matching the given + /// (including subclasses), judging from the object definitions. + /// + /// + /// The (class or interface) to match, or + /// for all object names. + /// + /// + /// Whether to include prototype objects too or just singletons (also applies to + /// s). + /// + /// + /// Whether to include s too + /// or just normal objects. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + /// + public string[] GetObjectNamesForType( + Type type, bool includePrototypes, bool includeFactoryObjects) + { + return ObjectFactory.GetObjectNamesForType(type, includePrototypes, includeFactoryObjects); + } + + /// + /// Return the names of all objects defined in this factory. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + /// + public string[] GetObjectDefinitionNames() + { + return ObjectFactory.GetObjectDefinitionNames(); + } + + /// + /// Return the registered + /// for the + /// given object, allowing access to its property values and constructor + /// argument values. + /// + /// The name of the object. + /// + /// The registered + /// . + /// + /// + /// If there is no object with the given name. + /// + /// + /// In the case of errors. + /// + public virtual IObjectDefinition GetObjectDefinition(string name) + { + return ObjectFactory.GetObjectDefinition(name); + } + + + /// + /// Return the registered + /// for the + /// given object, allowing access to its property values and constructor + /// argument values. + /// + /// The name of the object. + /// Whether to search parent object factories. + /// + /// The registered + /// . + /// + /// + /// If there is no object with the given name. + /// + /// + /// In the case of errors. + /// + public IObjectDefinition GetObjectDefinition(string name, bool includeAncestors) + { + return ObjectFactory.GetObjectDefinition(name, includeAncestors); + } + + /// + /// Return the object instances that match the given object + /// (including subclasses), judging from either object + /// definitions or the value of + /// in the case of + /// s. + /// + /// + /// The (class or interface) to match. + /// + /// + /// A of the matching objects, + /// containing the object names as keys and the corresponding object instances + /// as values. + /// + /// + /// If the objects could not be created. + /// + /// + public IDictionary GetObjectsOfType(Type type) + { + return GetObjectsOfType(type, true, true); + } + + /// + /// Return the object instances that match the given object + /// (including subclasses), judging from either object + /// definitions or the value of + /// in the case of + /// s. + /// + /// + /// The (class or interface) to match. + /// + /// + /// Whether to include prototype objects too or just singletons (also applies to + /// s). + /// + /// + /// Whether to include s too + /// or just normal objects. + /// + /// + /// A of the matching objects, + /// containing the object names as keys and the corresponding object instances + /// as values. + /// + /// + /// If the objects could not be created. + /// + /// + public IDictionary GetObjectsOfType( + Type type, bool includePrototypes, bool includeFactoryObjects) + { + return ObjectFactory.GetObjectsOfType(type, includePrototypes, includeFactoryObjects); + } + + /// + /// Return the number of objects defined in the factory. + /// + /// + /// The number of objects defined in the factory. + /// + /// + public int ObjectDefinitionCount + { + get { return ObjectFactory.ObjectDefinitionCount; } + } + + /// + /// Check if this object factory contains an object definition with the given name. + /// + /// The name of the object to look for. + /// + /// True if this object factory contains an object definition with the given name. + /// + /// + public bool ContainsObjectDefinition(string name) + { + return ObjectFactory.ContainsObjectDefinition(name); + } + + #endregion + + #region IObjectFactory Members + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// The name of the object to return. + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + public object this[string name] + { + get { return ObjectFactory.GetObject(name); } + } + + /// + /// Does this object factory contain an object with the given name? + /// + /// The name of the object to query. + /// + /// if an object with the given name is defined. + /// + /// + public bool ContainsObject(string name) + { + return ObjectFactory.ContainsObject(name); + } + + /// + /// Return the aliases for the given object name, if defined. + /// + /// The object name to check for aliases. + /// The aliases, or an empty array if none. + /// + /// If there's no such object definition. + /// + /// + public string[] GetAliases(string name) + { + return ObjectFactory.GetAliases(name); + } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// The name of the object to return. + /// + /// the object may match. Can be an interface or + /// superclass of the actual class. For example, if the value is the + /// class, this method will succeed whatever the + /// class of the returned instance. + /// + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + /// If the object is not of the required type. + /// + /// + public object GetObject(string name, Type requiredType) + { + return ObjectFactory.GetObject(name, requiredType); + } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// The name of the object to return. + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + public object GetObject(string name) + { + return ObjectFactory.GetObject(name); + } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// + ///

    + /// This method allows an object factory to be used as a replacement for the + /// Singleton or Prototype design pattern. + ///

    + ///

    + /// Note that callers should retain references to returned objects. There is no + /// guarantee that this method will be implemented to be efficient. For example, + /// it may be synchronized, or may need to run an RDBMS query. + ///

    + ///

    + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + ///

    + ///
    + /// The name of the object to return. + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a static factory method. If there is no factory method and the + /// arguments are not null, then match the argument values by type and + /// call the object's constructor. + /// + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + /// If the supplied is . + /// + public object GetObject(string name, object[] arguments) + { + return ObjectFactory.GetObject(name, arguments); + } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// The name of the object to return. + /// + /// The the object may match. Can be an interface or + /// superclass of the actual class. For example, if the value is the + /// class, this method will succeed whatever the + /// class of the returned instance. + /// + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a factory method. If there is no factory method and the + /// supplied array is not , then + /// match the argument values by type and call the object's constructor. + /// + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + /// If the object is not of the required type. + /// + /// + /// If the supplied is . + /// + /// + public object GetObject(string name, Type requiredType, object[] arguments) + { + return ObjectFactory.GetObject(name, requiredType, arguments); + } + + /// + /// Is this object a singleton? + /// + /// The name of the object to query. + /// True if the named object is a singleton. + /// + /// If there's no such object definition. + /// + /// + public bool IsSingleton(string name) + { + return ObjectFactory.IsSingleton(name); + } + + /// + /// Determines whether the specified object name is prototype. That is, will GetObject + /// always return independent instances? + /// + /// The name of the object to query + /// + /// true if the specified object name will always deliver independent instances; otherwise, false. + /// + /// This method returning false does not clearly indicate a singleton object. + /// It indicated non-independent instances, which may correspond to a scoped object as + /// well. use the IsSingleton property to explicitly check for a shared + /// singleton instance. + /// Translates aliases back to the corresponding canonical object name. Will ask the + /// parent factory if the object can not be found in this factory instance. + /// + /// + /// if there is no object with the given name. + public bool IsPrototype(string name) + { + return ObjectFactory.IsPrototype(name); + } + + + /// + /// Determines whether the object with the given name matches the specified type. + /// + /// More specifically, check whether a GetObject call for the given name + /// would return an object that is assignable to the specified target type. + /// Translates aliases back to the corresponding canonical bean name. + /// Will ask the parent factory if the bean cannot be found in this factory instance. + /// + /// The name of the object to query. + /// Type of the target to match against. + /// + /// true if the object type matches; otherwise, false + /// if it doesn't match or cannot be determined yet. + /// + /// Ff there is no object with the given name + /// + public bool IsTypeMatch(string name, Type targetType) + { + return ObjectFactory.IsTypeMatch(name, targetType); + } + + /// + /// Determine the of the object with the + /// given name. + /// + /// The name of the object to query. + /// + /// The of the object, or + /// if not determinable. + /// + /// + public Type GetType(string name) + { + return ObjectFactory.GetType(name); + } + + /// + /// Injects dependencies into the supplied instance + /// using the named object definition. + /// + /// + /// The object instance that is to be so configured. + /// + /// + /// The name of the object definition expressing the dependencies that are to + /// be injected into the supplied instance. + /// + /// + public object ConfigureObject(object target, string name) + { + return ObjectFactory.ConfigureObject(target, name); + } + + /// + /// Injects dependencies into the supplied instance + /// using the supplied . + /// + /// + /// The object instance that is to be so configured. + /// + /// + /// The name of the object definition expressing the dependencies that are to + /// be injected into the supplied instance. + /// + /// + /// An object definition that should be used to configure object. + /// + /// + public object ConfigureObject(object target, string name, IObjectDefinition definition) + { + return ObjectFactory.ConfigureObject(target, name, definition); + } + + #endregion + + #region IHierarchicalObjectFactory Members + + /// + /// Return the parent object factory, or if there is none. + /// + /// + /// The parent object factory, or if there is none. + /// + /// + public IObjectFactory ParentObjectFactory + { + get { return _parentApplicationContext; } + } + + #endregion + + #region IMessageSource Members + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If no message could be resolved. + /// + /// + /// If the supplied is . + /// + /// + public string GetMessage( + string name, CultureInfo culture, params object[] arguments) + { + return MessageSource.GetMessage(name, culture, arguments); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// The default message. + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If no message could be resolved. + /// + /// + /// If the supplied is . + /// + /// + public string GetMessage(string name, string defaultMessage, CultureInfo culture, params object[] arguments) + { + return MessageSource.GetMessage(name, defaultMessage, culture, arguments); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// + /// The resolved message if the lookup was successful. + /// + /// + /// If no message could be resolved. + /// + /// + public string GetMessage(string name) + { + return MessageSource.GetMessage(name); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// + /// The resolved message if the lookup was successful. + /// + /// + /// If no message could be resolved. + /// + /// + /// If the supplied is . + /// + /// + public string GetMessage(string name, params object[] arguments) + { + return MessageSource.GetMessage(name, arguments); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If no message could be resolved. + /// + /// + /// If the supplied is . + /// + /// + public string GetMessage(string name, CultureInfo culture) + { + return MessageSource.GetMessage(name, culture); + } + + /// + /// Resolve the message using all of the attributes contained within + /// the supplied + /// argument. + /// + /// + /// The value object storing those attributes that are required to + /// properly resolve a message. + /// + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If the message could not be resolved. + /// + /// + public string GetMessage(IMessageSourceResolvable resolvable, CultureInfo culture) + { + return MessageSource.GetMessage(resolvable, culture); + } + + /// + /// Gets a localized resource object identified by the supplied + /// . + /// + /// + /// The name of the resource object to resolve. + /// + /// + /// The with which the + /// resource is associated. + /// + /// + /// The resolved object, or if not found. + /// + /// + object IMessageSource.GetResourceObject(string name, CultureInfo culture) + { + return GetResourceObject(name, culture); + } + + /// + /// Gets a localized resource object identified by the supplied + /// . + /// + /// + /// The name of the resource object to resolve. + /// + /// + /// The resolved object, or if not found. + /// + /// + object IMessageSource.GetResourceObject(string name) + { + return GetResourceObject(name); + } + + /// + /// Gets a localized resource object identified by the supplied + /// . + /// + /// + /// The name of the resource object to resolve. + /// + /// + /// The with which the + /// resource is associated. + /// + /// + /// The resolved object, or if not found. + /// + /// + public object GetResourceObject(string name, CultureInfo culture) + { + return MessageSource.GetResourceObject(name, culture); + } + + /// + /// Gets a localized resource object identified by the supplied + /// . + /// + /// + /// The name of the resource object to resolve. + /// + /// + /// The resolved object, or if not found. + /// + /// + public object GetResourceObject(string name) + { + return MessageSource.GetResourceObject(name); + } + + /// + /// Applies resources to object properties. + /// + /// + /// An object that contains the property values to be applied. + /// + /// + /// The base name of the object to use for key lookup. + /// + /// + /// The with which the + /// resource is associated. + /// + /// + public void ApplyResources(object value, string objectName, CultureInfo culture) + { + MessageSource.ApplyResources(value, objectName, culture); + } + + #endregion + + #region IEventRegistry Members + + /// + /// Publishes all events of the source object. + /// + /// + /// The source object containing events to publish. + /// + /// + public void PublishEvents(object sourceObject) + { + _eventRegistry.PublishEvents(sourceObject); + } + + /// + /// Subscribes to all events published, if the subscriber + /// implements compatible handler methods. + /// + /// The subscriber to use. + /// + public void Subscribe(object subscriber) + { + _eventRegistry.Subscribe(subscriber); + } + + /// + /// Subscribes to published events of a all objects of a given + /// , if the subscriber implements + /// compatible handler methods. + /// + /// The subscriber to use. + /// + /// The target to subscribe to. + /// + /// + public void Subscribe(object subscriber, Type targetSourceType) + { + _eventRegistry.Subscribe(subscriber, targetSourceType); + } + + #endregion + + /// + /// Publishes an application context event. + /// + /// + ///

    + /// + ///

    + ///
    + /// + /// The source of the event. May be . + /// + /// + /// The event that is to be raised. + /// + /// + public void PublishEvent(object sender, ApplicationEventArgs e) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "Publishing event in context [{0}] : {1}", + Name, e)); + } + + #endregion + + OnContextEvent(sender, e); + + if (ParentContext != null) + { + ParentContext.PublishEvent(sender, e); + } + } + + #region IPostProcessor implementation + + private sealed class ObjectPostProcessorChecker : IObjectPostProcessor + { + private int _objectPostProcessorTargetCount; + private IConfigurableListableObjectFactory _objectFactory; + + + public ObjectPostProcessorChecker() + { + } + +// public ObjectPostProcessorChecker( +// IConfigurableListableObjectFactory objectFactory, int objectPostProcessorTargetCount) +// { +// _objectFactory = objectFactory; +// _objectPostProcessorTargetCount = objectPostProcessorTargetCount; +// } + + public void Reset(IConfigurableListableObjectFactory objectFactory, int objectPostProcessorTargetCount) + { + _objectFactory = objectFactory; + _objectPostProcessorTargetCount = objectPostProcessorTargetCount; + } + + public object PostProcessBeforeInitialization(object obj, string name) + { + return obj; + } + + public object PostProcessAfterInitialization(object obj, string objectName) + { + if (_objectFactory.ObjectPostProcessorCount < _objectPostProcessorTargetCount) + { + #region Instrumentation + + if (log.IsInfoEnabled) + { + log.Info(string.Format( + "Object '{0}' is not eligible for being processed by all " + + "IObjectPostProcessors (for example: not eligible for auto-proxying).", objectName)); + } + + #endregion + } + return obj; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/AbstractMessageSource.cs b/src/Spring/Spring.Core/Context/Support/AbstractMessageSource.cs new file mode 100644 index 00000000..0c426817 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/AbstractMessageSource.cs @@ -0,0 +1,617 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using Common.Logging; + +namespace Spring.Context.Support +{ + /// + /// Abstract implementation of the interface, + /// implementing common handling of message variants, making it easy + /// to implement a specific strategy for a concrete . + /// + /// + ///

    Subclasses must implement the abstract ResolveObject + /// method.

    + ///

    Note: By default, message texts are only parsed through + /// String.Format if arguments have been passed in for the message. In case + /// of no arguments, message texts will be returned as-is. As a consequence, + /// you should only use String.Format escaping for messages with actual + /// arguments, and keep all other messages unescaped. + ///

    + ///

    Supports not only IMessageSourceResolvables as primary messages + /// but also resolution of message arguments that are in turn + /// IMessageSourceResolvables themselves. + ///

    + ///

    This class does not implement caching of messages per code, thus + /// subclasses can dynamically change messages over time. Subclasses are + /// encouraged to cache their messages in a modification-aware fashion, + /// allowing for hot deployment of updated messages. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// Harald Radi (.NET) + /// $Id: AbstractMessageSource.cs,v 1.23 2007/08/28 14:16:15 oakinger Exp $ + /// + /// + /// + public abstract class AbstractMessageSource : IHierarchicalMessageSource + { + #region Fields + + /// + /// holds the logger instance shared with subclasses. + /// + protected readonly ILog log; + + private IMessageSource parentMessageSource; + private bool useCodeAsDefaultMessage = false; + + #endregion + + #region Constructor + + /// + /// Initializes this instance. + /// + protected AbstractMessageSource() + { + log = LogManager.GetLogger(GetType()); + } + + #endregion + + #region Properties + + + /// Gets or Sets a value indicating whether to use the message code as + /// default message instead of throwing a NoSuchMessageException. + /// Useful for development and debugging. Default is "false". + /// + /// + ///

    Note: In case of a IMessageSourceResolvable with multiple codes + /// (like a FieldError) and a MessageSource that has a parent MessageSource, + /// do not activate "UseCodeAsDefaultMessage" in the parent: + /// Else, you'll get the first code returned as message by the parent, + /// without attempts to check further codes.

    + ///

    To be able to work with "UseCodeAsDefaultMessage" turned on in the parent, + /// AbstractMessageSource contains special checks + /// to delegate to the internal GetMessageInternal method if available. + /// In general, it is recommended to just use "UseCodeAsDefaultMessage" during + /// development and not rely on it in production in the first place, though.

    + ///

    Alternatively, consider overriding the GetDefaultMessage + /// method to return a custom fallback message for an unresolvable code.

    + ///
    + /// + /// true if use the message code as default message instead of + /// throwing a NoSuchMessageException; otherwise, false. + /// + public bool UseCodeAsDefaultMessage + { + get { return useCodeAsDefaultMessage; } + set { useCodeAsDefaultMessage = value; } + } + + #endregion + + #region IHierarchicalMessageSource Members + + /// + /// The parent message source used to try and resolve messages that + /// this object can't resolve. + /// + /// + /// + ///

    + /// If the value of this property is then no + /// further resolution is possible. + ///

    + ///
    + public IMessageSource ParentMessageSource + { + get { return parentMessageSource; } + set { parentMessageSource = value; } + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If the lookup is not successful throw NoSuchMessageException + /// + public string GetMessage(string name) + { + return GetMessage(name, CultureInfo.CurrentUICulture, null); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// The that represents + /// the culture for which the resource is localized. + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// Note that the fallback behavior based on CultureInfo seem to + /// have a bug that is fixed by installed .NET 1.1 Service Pack 1. + ///

    + /// If the lookup is not successful, implementations are permitted to + /// take one of two actions. + ///

    + /// If the lookup is not successful throw NoSuchMessageException + ///
    + public string GetMessage(string name, CultureInfo culture) + { + return GetMessage(name, culture, null); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If the lookup is not successful throw NoSuchMessageException + /// + public string GetMessage(string name, params object[] arguments) + { + return GetMessage(name, CultureInfo.CurrentUICulture, arguments); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// The that represents + /// the culture for which the resource is localized. + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// Note that the fallback behavior based on CultureInfo seem to + /// have a bug that is fixed by installed .NET 1.1 Service Pack 1. + ///

    + /// If the lookup is not successful throw NoSuchMessageException. + ///

    + ///
    + public string GetMessage(string name, CultureInfo culture, params object[] arguments) + { + string msg = GetMessageInternal(name, arguments, culture); + if (msg != null) return msg; + string fallback = GetDefaultMessage(name); + if (fallback != null) return fallback; + throw new NoSuchMessageException(name, culture); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// The default message if name is not found. + /// The that represents + /// the culture for which the resource is localized. + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// Note that the fallback behavior based on CultureInfo seem to + /// have a bug that is fixed by installed .NET 1.1 Service Pack 1. + ///

    + /// If the lookup is not successful throw NoSuchMessageException + ///

    + ///
    + public string GetMessage(string name, string defaultMessage, CultureInfo culture, params object[] arguments) + { + string msg = GetMessageInternal(name, arguments, culture); + if (msg != null) return msg; + if (defaultMessage == null) + { + string fallback = GetDefaultMessage(name); + if (fallback != null) return fallback; + } + return RenderDefaultMessage(defaultMessage, arguments, culture); + } + + /// + /// Resolve the message using all of the attributes contained within + /// the supplied + /// argument. + /// + /// The value object storing those attributes that are required to + /// properly resolve a message. + /// The that represents + /// the culture for which the resource is localized. + /// + /// The resolved message if the lookup was successful. + /// + /// + /// If the message could not be resolved. + /// + public string GetMessage(IMessageSourceResolvable resolvable, CultureInfo culture) + { + string[] codes = resolvable.GetCodes(); + if (codes == null) codes = new string[0]; + for (int i = 0; i < codes.Length; i++) + { + string msg = GetMessageInternal(codes[i], resolvable.GetArguments(), culture); + if (msg != null) return msg; + } + if (resolvable.DefaultMessage != null) + return RenderDefaultMessage(resolvable.DefaultMessage, resolvable.GetArguments(), culture); + if (codes.Length > 0) + { + string fallback = GetDefaultMessage(codes[0]); + if (fallback != null) return fallback; + } + throw new NoSuchMessageException(codes.Length > 0 ? codes[codes.Length - 1] : null, culture); + } + + /// + /// Gets a localized resource object identified by the supplied + /// . + /// + /// + /// The name of the resource object to resolve. + /// + /// + /// The resolved object, or if not found. + /// + /// + public object GetResourceObject(string name) + { + object resource = GetResourceInternal(name, CultureInfo.CurrentUICulture); + if (resource != null) return resource; + if (ParentMessageSource != null) + return ParentMessageSource.GetResourceObject(name, CultureInfo.CurrentUICulture); + return null; + } + + /// + /// Gets a localized resource object identified by the supplied + /// . + /// + /// + /// Note that the fallback behavior based on CultureInfo seem to + /// have a bug that is fixed by installed .NET 1.1 Service Pack 1. + /// + /// + /// The name of the resource object to resolve. + /// + /// + /// The with which the + /// resource is associated. + /// + /// + /// The resolved object, or if not found. If + /// the resource name resolves to null, then in .NET 1.1 the return + /// value will be String.Empty whereas in .NET 2.0 it will return + /// null. + /// + /// + public object GetResourceObject(string name, CultureInfo culture) + { + object resource = GetResourceInternal(name, culture); + if (resource != null) return resource; + if (ParentMessageSource != null) return ParentMessageSource.GetResourceObject(name, culture); + return null; + } + + /// + /// Applies resources to object properties. + /// + /// + /// An object that contains the property values to be applied. + /// + /// + /// The base name of the object to use for key lookup. + /// + /// + /// The with which the + /// resource is associated. + /// + /// + public void ApplyResources( + object value, string objectName, CultureInfo culture) + { + ApplyResourcesInternal(value, objectName, culture); + if (ParentMessageSource != null) ParentMessageSource.ApplyResources(value, objectName, culture); + } + + #endregion + + #region Protected Methods + + /// Resolve the given code and arguments as message in the given culture, + /// returning null if not found. Does not fall back to the code + /// as default message. Invoked by GetMessage methods. + /// + /// The code to lookup up, such as 'calculator.noRateSet'. + /// array of arguments that will be filled in for params + /// within the message. + /// The with which the + /// resource is associated. + /// + /// The resolved message if the lookup was successful. + /// + protected string GetMessageInternal(string code, object[] args, CultureInfo culture) + { + if (code == null) return null; + if (culture == null) culture = CultureInfo.CurrentUICulture; + + if ((args != null && args.Length > 0)) + { + // Resolve arguments eagerly, for the case where the message + // is defined in a parent MessageSource but resolvable arguments + // are defined in the child MessageSource. + args = ResolveArguments(args, culture); + } + + string message = ResolveMessage(code, culture); + + if (message != null) return FormatMessage(message, args, culture); + + // Not found -> check parent, if any. + return GetMessageFromParent(code, args, culture); + } + + + /// + /// Try to retrieve the given message from the parent MessageSource, if any. + /// + /// The code to lookup up, such as 'calculator.noRateSet'. + /// array of arguments that will be filled in for params + /// within the message. + /// The with which the + /// resource is associated. + /// + /// The resolved message if the lookup was successful. + /// + protected string GetMessageFromParent(string code, object[] args, CultureInfo culture) + { + if (ParentMessageSource != null) + { + AbstractMessageSource parent = ParentMessageSource as AbstractMessageSource; + if (parent != null) + { + // Call internal method to avoid getting the default code back + // in case of "useCodeAsDefaultMessage" being activated. + return parent.GetMessageInternal(code, args, culture); + } + else + { + // Check parent MessageSource, returning null if not found there. + return ParentMessageSource.GetMessage(code, null, culture, args); + } + } + // Not found in parent either. + return null; + } + + + /// + /// Return a fallback default message for the given code, if any. + /// + /// + /// Default is to return the code itself if "UseCodeAsDefaultMessage" + /// is activated, or return no fallback else. In case of no fallback, + /// the caller will usually receive a NoSuchMessageException from GetMessage + /// + /// The code to lookup up, such as 'calculator.noRateSet'. + /// The default message to use, or null if none. + protected virtual string GetDefaultMessage(string code) + { + if (UseCodeAsDefaultMessage) return code; + return null; + } + + + + /// + /// Renders the default message string. The default message is passed in as specified by the + /// caller and can be rendered into a fully formatted default message shown to the user. + /// + /// Default implementation passed he String for String.Format resolving any + /// argument placeholders found in them. Subclasses may override this method to plug + /// in custom processing of default messages. + /// + /// The default message. + /// The array of agruments that will be filled in for parameter + /// placeholders within the message, or null if none. + /// The with which the + /// resource is associated. + /// The rendered default message (with resolved arguments) + protected virtual string RenderDefaultMessage(string defaultMessage, object[] args, CultureInfo culture) + { + return FormatMessage(defaultMessage, args, culture); + } + + /// + /// Format the given default message String resolving any + /// agrument placeholders found in them. + /// + /// The message to format. + /// The array of agruments that will be filled in for parameter + /// placeholders within the message, or null if none. + /// The with which the + /// resource is associated. + /// The formatted message (with resolved arguments) + protected virtual string FormatMessage(string msg, object[] args, CultureInfo culture) + { + if (msg == null || ((args == null || args.Length == 0))) return msg; + return String.Format(culture, msg, args); + } + + + /// + /// Search through the given array of objects, find any + /// MessageSourceResolvable objects and resolve them. + /// + /// + /// Allows for messages to have MessageSourceResolvables as arguments. + /// + /// + /// The array of arguments for a message. + /// The with which the + /// resource is associated. + /// An array of arguments with any IMessageSourceResolvables resolved + protected virtual object[] ResolveArguments(object[] args, CultureInfo culture) + { + if (args == null) return new object[0]; + object[] resolvedArgs = new object[args.Length]; + + for (int i = 0; i < args.Length; i++) + { + IMessageSourceResolvable resolvable = args[i] as IMessageSourceResolvable; + if (resolvable != null) resolvedArgs[i] = GetMessage(resolvable, culture); + else resolvedArgs[i] = args[i]; + } + + return resolvedArgs; + } + + /// + /// Gets the specified resource (e.g. Icon or Bitmap). + /// + /// The name of the resource to resolve. + /// + /// The to resolve the + /// code for. + /// + /// The resource if found. otherwise. + protected object GetResourceInternal(string name, CultureInfo cultureInfo) + { + if (cultureInfo == null) cultureInfo = CultureInfo.CurrentUICulture; + if (name == null) return null; + return ResolveObject(name, cultureInfo); + } + + /// + /// Applies resources from the given name on the specified object. + /// + /// + /// An object that contains the property values to be applied. + /// + /// + /// The base name of the object to use for key lookup. + /// + /// + /// The with which the + /// resource is associated. + /// + protected void ApplyResourcesInternal(object value, string objectName, CultureInfo cultureInfo) + { + if (cultureInfo == null) cultureInfo = CultureInfo.CurrentUICulture; + ApplyResourcesToObject(value, objectName, cultureInfo); + } + + #endregion + + #region Protected Abstract Methods + + /// + /// Subclasses must implement this method to resolve a message. + /// + /// The code to lookup up, such as 'calculator.noRateSet'. + /// The with which the + /// resource is associated. + /// The resolved message from the backing store of message data. + protected abstract string ResolveMessage(string code, CultureInfo cultureInfo); + + /// + /// Resolves an object (typically an icon or bitmap). + /// + /// + ///

    + /// Subclasses must implement this method to resolve an object. + ///

    + ///
    + /// The code of the object to resolve. + /// + /// The to resolve the + /// code for. + /// + /// + /// The resolved object or if not found. + /// + protected abstract object ResolveObject(string code, CultureInfo cultureInfo); + + /// + /// Applies resources to object properties. + /// + /// + ///

    + /// Subclasses must implement this method to apply resources + /// to an arbitrary object. + ///

    + ///
    + /// + /// An object that contains the property values to be applied. + /// + /// + /// The base name of the object to use for key lookup. + /// + /// + /// The with which the + /// resource is associated. + /// + protected abstract void ApplyResourcesToObject(object value, string objectName, CultureInfo cultureInfo); + + + #endregion + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/AbstractXmlApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/AbstractXmlApplicationContext.cs new file mode 100644 index 00000000..75025375 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/AbstractXmlApplicationContext.cs @@ -0,0 +1,293 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using Common.Logging; +using Spring.Objects; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Convenient abstract superclass for + /// implementations that + /// draw their configuration from XML documents containing object + /// definitions as understood by an + /// . + /// + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: AbstractXmlApplicationContext.cs,v 1.20 2007/07/20 11:37:55 oakinger Exp $ + public abstract class AbstractXmlApplicationContext : AbstractApplicationContext + { + /// + /// The instance for this class. + /// + private static readonly ILog log = LogManager.GetLogger(typeof(AbstractXmlApplicationContext)); + + private DefaultListableObjectFactory _objectFactory; + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes + /// no public constructors. + ///

    + ///
    + protected AbstractXmlApplicationContext() : this(null, true, null) + {} + + /// + /// Creates a new instance of the + /// class + /// with the given parent context. + /// + /// + ///

    + /// This is an class, and as such exposes + /// no public constructors. + ///

    + ///
    + /// The application context name. + /// Flag specifying whether to make this context case sensitive or not. + /// The parent context. + protected AbstractXmlApplicationContext(string name, bool caseSensitive, + IApplicationContext parentContext) : base(name, caseSensitive, parentContext) + {} + + /// + /// An array of resource locations, referring to the XML object + /// definition files that this context is to be built with. + /// + /// + ///

    + /// Examples of the format of the various strings that would be + /// returned by accessing this property can be found in the overview + /// documentation of with the + /// class. + ///

    + ///
    + /// + /// An array of resource locations, or if none. + /// + protected abstract string[] ConfigurationLocations { get; } + + /// + /// Instantiates and populates the underlying + /// with the object + /// definitions yielded up by the + /// method. + /// + /// + /// In the case of errors encountered while refreshing the object factory. + /// + /// + /// In the case of errors encountered reading any of the resources + /// yielded by the method. + /// + /// + protected override void RefreshObjectFactory() + { + // Shut down previous object factory, if any. + IConfigurableListableObjectFactory oldObjectFactory = null; + oldObjectFactory = _objectFactory; + + if (oldObjectFactory != null) + { + _objectFactory = null; + oldObjectFactory.Dispose(); + } + + try + { + DefaultListableObjectFactory objectFactory = CreateObjectFactory(); + LoadObjectDefinitions(objectFactory); + + _objectFactory = objectFactory; + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + "Refreshed ObjectFactory for application context '{0}'.", + Name)); + } + + #endregion + } + catch (IOException ex) + { + throw new ApplicationContextException( + string.Format( + "I/O error parsing XML resource for application context '{0}'.", + Name), ex); + } + catch (UriFormatException ex) + { + throw new ApplicationContextException( + string.Format( + "Error parsing resource locations [{0}] for application context '{1}'.", + StringUtils.ArrayToCommaDelimitedString(ConfigurationLocations), + Name), ex); + } + } + + + /// + /// Initialize the object definition reader used for loading the object + /// definitions of this context. + /// + /// + ///

    + /// The default implementation of this method is a no-op; i.e. it does + /// nothing. Can be overridden in subclasses to provide custom + /// initialization of the supplied + /// ; for example, a derived + /// class may want to turn off XML validation. + ///

    + ///
    + /// + /// The object definition reader used by this context. + /// + protected virtual void InitObjectDefinitionReader( + XmlObjectDefinitionReader objectDefinitionReader) + {} + + /// + /// Load the object definitions with the given + /// . + /// + /// + ///

    + /// The lifecycle of the object factory is handled by + /// ; + /// therefore this method is just supposed to load and / or register + /// object definitions. + ///

    + ///
    + /// + /// The reader containing object definitions. + /// + /// In case of object registration errors. + /// + /// + /// In the case of errors encountered reading any of the resources + /// yielded by the method. + /// + protected virtual void LoadObjectDefinitions( + XmlObjectDefinitionReader objectDefinitionReader) + { + string[] locations = ConfigurationLocations; + if (locations != null) + { + objectDefinitionReader.LoadObjectDefinitions(ConfigurationLocations); + } + } + + + /// + /// Loads the object definitions into the given object factory, typically through + /// delegating to one or more object definition readers. + /// + /// The object factory to lead object definitions into + /// + /// + protected virtual void LoadObjectDefinitions(DefaultListableObjectFactory objectFactory) + { + //Create a new XmlObjectDefinitionReader for the given ObjectFactory + XmlObjectDefinitionReader objectDefinitionReader = new XmlObjectDefinitionReader(objectFactory); + + // Configure the bean definition reader with this context's + // resource loading environment. + objectDefinitionReader.ResourceLoader = this; + + // Allow a subclass to provide custom initialization of the reader, + // then proceed with actually loading the object definitions. + InitObjectDefinitionReader(objectDefinitionReader); + LoadObjectDefinitions(objectDefinitionReader); + } + + /// + /// Customizes the internal object factory used by this context. + /// + /// Called for each attempt. + ///

    + /// The default implementation is empty. Can be overriden in subclassses to customize + /// DefaultListableBeanFatory's standard settings. + ///

    + /// The newly created object factory for this context + protected virtual void CustomizeObjectFactory(DefaultListableObjectFactory objectFactory) + { + + } + + /// + /// Create an internal object factory for this context. + /// + /// + ///

    + /// Called for each attempt. + /// This default implementation creates a + /// + /// with the internal object factory of this context's parent serving + /// as the parent object factory. Can be overridden in subclasse,s + /// for example to customize DefaultListableBeanFactory's settings. + ///

    + ///
    + /// The object factory for this context. + protected virtual DefaultListableObjectFactory CreateObjectFactory() + { + return new DefaultListableObjectFactory(CaseSensitive, GetInternalParentObjectFactory()); + } + + /// + /// Subclasses must return their internal object factory here. + /// + /// + /// The internal object factory for the application context. + /// + /// + public override IConfigurableListableObjectFactory ObjectFactory + { + get + { + lock(SyncRoot) + { + return _objectFactory; + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/ApplicationContextAwareProcessor.cs b/src/Spring/Spring.Core/Context/Support/ApplicationContextAwareProcessor.cs new file mode 100644 index 00000000..3d8a45ac --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/ApplicationContextAwareProcessor.cs @@ -0,0 +1,149 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Remoting; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// + /// implementation that passes the application context to object that + /// implement the + /// , + /// , and + /// interfaces. + /// + /// + ///

    + /// If an object's class implements more than one of the + /// , + /// , and + /// interfaces, then the + /// order in which the interfaces are satisfied is as follows... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///

    + ///

    + /// Application contexts will automatically register this with their + /// underlying object factory. Applications should thus never need to use + /// this class directly. + ///

    + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: ApplicationContextAwareProcessor.cs,v 1.8 2007/08/22 08:49:26 markpollack Exp $ + public class ApplicationContextAwareProcessor : IObjectPostProcessor + { + private IApplicationContext _applicationContext; + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The that this + /// instance will work with. + /// + public ApplicationContextAwareProcessor( + IApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + /// + /// Apply this + /// to the given new object instance before any object + /// initialization callbacks. + /// + /// + /// The new object instance. + /// + /// + /// The name of the object. + /// + /// + /// The the object instance to use, either the original or a wrapped one. + /// + /// + /// In case of errors. + /// + /// + public object PostProcessAfterInitialization(object obj, string objectName) + { + return obj; + } + + /// + /// Apply this to the + /// given new object instance after any object initialization + /// callbacks. + /// + /// + /// The new object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// In case of errors. + /// + /// + public object PostProcessBeforeInitialization(object obj, string name) + { + if(!RemotingServices.IsTransparentProxy(obj)) + { + if (typeof (IResourceLoaderAware).IsInstanceOfType(obj)) + { + ((IResourceLoaderAware) obj).ResourceLoader + = _applicationContext; + } + if (typeof (IMessageSourceAware).IsInstanceOfType(obj)) + { + ((IMessageSourceAware) obj).MessageSource + = _applicationContext; + } + if (typeof (IApplicationContextAware).IsInstanceOfType(obj)) + { + ((IApplicationContextAware) obj).ApplicationContext + = _applicationContext; + } + } + return obj; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/ApplicationObjectSupport.cs b/src/Spring/Spring.Core/Context/Support/ApplicationObjectSupport.cs new file mode 100644 index 00000000..f6807649 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/ApplicationObjectSupport.cs @@ -0,0 +1,210 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using Spring.Objects; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Convenient superclass for application objects that want to be aware of + /// the application context, e.g. for custom lookup of collaborating object + /// or for context-specific resource access. + /// + /// + ///

    + /// It saves the application context reference and provides an + /// initialization callback method. Furthermore, it offers numerous + /// convenience methods for message lookup. + ///

    + ///

    + /// There is no requirement to subclass this class: it just makes things + /// a little easier if you need access to the context, e.g. for access to + /// file resources or to the message source. Note that many application + /// objects do not need to be aware of the application context at all, + /// as they can receive collaborating objects via object references. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: ApplicationObjectSupport.cs,v 1.8 2007/07/17 14:51:14 oakinger Exp $ + public abstract class ApplicationObjectSupport : IApplicationContextAware + { + private IApplicationContext _applicationContext; + private MessageSourceAccessor _messageSourceAccessor; + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + protected ApplicationObjectSupport() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + /// + /// The that this + /// object runs in. + /// + protected ApplicationObjectSupport( + IApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + /// + /// The context class that any context passed to the + /// + /// must be an instance of. + /// + /// + /// The + /// . + /// + protected virtual Type RequiredType + { + get { return typeof (IApplicationContext); } + } + + /// + /// Intializes the wrapped + /// . + /// + /// + ///

    + /// This is a template method that subclasses can override for custom + /// initialization behavior. + ///

    + ///

    + /// Gets called by the + /// + /// instance directly after setting the context instance. + ///

    + /// + /// Does not get called on reinitialization of the context. + /// + ///
    + /// + /// In the case of any initialization errors. + /// + /// + /// If thrown by application context methods. + /// + protected virtual void InitApplicationContext() + { + } + + /// + /// Return a for the + /// application context used by this object, for easy message access. + /// + public MessageSourceAccessor MessageSourceAccessor + { + get { return _messageSourceAccessor; } + } + + #region IApplicationContextAware Members + + /// + /// Set the that this + /// object runs in. + /// + /// + /// When passed an unexpected + /// implementation + /// instance that is not compatible with the + /// defined by the value of the + /// . + /// property. Also, thrown when trying to re-initialize with a + /// different than was + /// originally used. + /// + /// + /// If thrown by any application context methods. + /// + /// + /// + public IApplicationContext ApplicationContext + { + get { return _applicationContext; } + set + { + if (_applicationContext == null) + { + if (! isValueOfRequiredType(value)) + { + throw new ApplicationContextException( + "Invalid application context: needs to by of type '" + RequiredType.DeclaringType + "'"); + } + _applicationContext = value; + _messageSourceAccessor = new MessageSourceAccessor(value); + InitApplicationContext(); + } + else + { + if (_applicationContext != value) + { + throw new ApplicationContextException("Cannot reinitialize with different application context"); + } + } + } + } + + private bool isValueOfRequiredType(IApplicationContext value) + { + if (value.GetType() == RequiredType) + { + return true; + } + Type[] implementedTypes = value.GetType().GetInterfaces(); + for (int i = 0; i < implementedTypes.Length; i++) + { + if (implementedTypes[i] == RequiredType) + { + return true; + } + } + return false; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/ContextHandler.cs b/src/Spring/Spring.Core/Context/Support/ContextHandler.cs new file mode 100644 index 00000000..3d613f4c --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/ContextHandler.cs @@ -0,0 +1,642 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Configuration; +using System.Reflection; +using System.Xml; + +using Common.Logging; +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Objects; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Creates an instance + /// using context definitions supplied in a custom configuration and + /// configures the with that instance. + /// + /// + /// Implementations of the + /// interface must provide the following two constructors: + /// + /// + /// + /// A constructor that takes a string array of resource locations. + /// + /// + /// + /// + /// A constructor that takes a reference to a parent application context + /// and a string array of resource locations (and in that order). + /// + /// + /// + ///

    + /// Note that if the type attribute is not present in the declaration + /// of a particular context, then a default + /// + /// is assumed. This default + /// + /// is currently the + /// ; please note the exact + /// of this default is an + /// implementation detail, that, while unlikely, may do so in the future. + /// to + ///

    + ///
    + /// + ///

    + /// This is an example of specifying a context that reads its resources from + /// an embedded Spring.NET XML object configuration file... + ///

    + /// + /// + /// + /// + ///
    + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///

    + /// This is an example of specifying a context that reads its resources from + /// a custom configuration section within the same application / web + /// configuration file and uses case insensitive object lookups. + ///

    + ///

    + /// Please note that you must adhere to the naming + /// of the various sections (i.e. '<sectionGroup name="spring">' and + /// '<section name="context">'. + ///

    + /// + /// + /// + /// + ///
    + ///
    + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///

    + /// And this is an example of specifying a hierarchy of contexts. The + /// hierarchy in this case is only a simple parent->child hierarchy, but + /// hopefully it illustrates the nesting of context configurations. This + /// nesting of contexts can be arbitrarily deep, and is one way... child + /// contexts know about their parent contexts, but parent contexts do not + /// know how many child contexts they have (if any), or have references + /// to any such child contexts. + ///

    + /// + /// + /// + /// + ///
    + ///
    + /// + ///
    + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Mark Pollack + /// Aleksandar Seovic + /// Rick Evans + /// $Id: ContextHandler.cs,v 1.36 2008/03/13 20:07:33 bbaia Exp $ + /// + public class ContextHandler : IConfigurationSectionHandler + { + private readonly ILog Log = LogManager.GetLogger(typeof(ContextHandler)); + + /// + /// The of + /// created if no type attribute is specified on a context element. + /// + /// + protected virtual Type DefaultApplicationContextType + { + get { return typeof (XmlApplicationContext); } + } + + /// + /// Get the context's case-sensitivity to use if none is specified + /// + /// + ///

    + /// Derived handlers may override this property to change their default case-sensitivity. + ///

    + ///

    + /// Defaults to 'true'. + ///

    + ///
    + protected virtual bool DefaultCaseSensitivity + { + get { return true; } + } + + /// + /// Creates an instance + /// using the context definitions supplied in a custom + /// configuration section. + /// + /// + ///

    + /// This instance is + /// also used to configure the . + ///

    + ///
    + /// + /// The configuration settings in a corresponding parent + /// configuration section. + /// + /// + /// The configuration context when called from the ASP.NET + /// configuration system. Otherwise, this parameter is reserved and + /// is . + /// + /// + /// The for the section. + /// + /// + /// An instance + /// populated with the object definitions supplied in the configuration + /// section. + /// + public object Create(object parent, object configContext, XmlNode section) + { + XmlElement contextElement = section as XmlElement; + + #region Sanity Checks + + if (contextElement == null) + { + throw ConfigurationUtils.CreateConfigurationException( + "Context configuration section must be an XmlElement."); + } + + // sanity check on parent + if ( (parent != null) && !(parent is IApplicationContext) ) + { + throw ConfigurationUtils.CreateConfigurationException( + String.Format("Parent context must be of type IApplicationContext, but was '{0}'", parent.GetType().FullName)); + } + + #endregion + + // determine name of context to be created + string contextName = GetContextName(configContext, contextElement); + if (!StringUtils.HasLength(contextName)) + { + contextName = AbstractApplicationContext.DefaultRootContextName; + } + + #region Instrumentation + if (Log.IsDebugEnabled) Log.Debug(string.Format("creating context '{0}'", contextName ) ); + #endregion + + IApplicationContext context = null; + try + { + IApplicationContext parentContext = parent as IApplicationContext; + + // determine context type + Type contextType = GetContextType(contextElement, parentContext); + + // determine case-sensitivity + bool caseSensitive = GetCaseSensitivity(contextElement); + + // get resource-list + string[] resources = GetResources(contextElement); + + // finally create the context instance + context = InstantiateContext(parentContext, configContext, contextName, contextType, caseSensitive, resources); + + // get and create child context definitions + XmlNode[] childContexts = GetChildContexts(contextElement); + CreateChildContexts(context, configContext, childContexts); + + if (Log.IsDebugEnabled) Log.Debug( string.Format("context '{0}' created for name '{1}'", context, contextName) ); + } + catch (Exception ex) + { + if (!ConfigurationUtils.IsConfigurationException(ex)) + { + throw ConfigurationUtils.CreateConfigurationException( + String.Format("Error creating context '{0}': {1}", + contextName, ReflectionUtils.GetExplicitBaseException(ex).Message), ex); + } + throw; + } + return context; + } + + /// + /// Create all child-contexts in the given for the given context. + /// + /// The parent context to use + /// The current configContext + /// The list of child context elements + protected virtual void CreateChildContexts(IApplicationContext parentContext, object configContext, XmlNode[] childContexts) + { + // create child contexts for 'the most recently created context'... + foreach (XmlNode childContext in childContexts) + { + this.Create(parentContext, configContext, childContext); + } + } + + /// + /// Instantiates a new context. + /// + protected virtual IApplicationContext InstantiateContext(IApplicationContext parentContext, object configContext, string contextName, Type contextType, bool caseSensitive, string[] resources) + { + IApplicationContext context; + ContextInstantiator instantiator; + + if (parentContext == null) + { + instantiator = new RootContextInstantiator(contextType, contextName, caseSensitive, resources); + } + else + { + instantiator = new DescendantContextInstantiator(parentContext, contextType, contextName, caseSensitive, resources); + } + + if (IsLazy) + { + // TODO + } + context = instantiator.InstantiateContext(); + return context; + } + + /// + /// Gets the context's name specified in the name attribute of the context element. + /// + /// The current configContext + /// The context element + protected virtual string GetContextName(object configContext, XmlElement contextElement) + { + string contextName; + contextName = contextElement.GetAttribute(ContextSchema.NameAttribute); + return contextName; + } + + /// + /// Extracts the context-type from the context element. + /// If none is specified, returns the parent's type. + /// + private Type GetContextType(XmlElement contextElement, IApplicationContext parentContext) + { + Type contextType; + if (parentContext != null) + { + // set default context type to parent's type (allows for type inheritance) + contextType = GetConfiguredContextType(contextElement, parentContext.GetType()); + } + else + { + contextType = GetConfiguredContextType(contextElement, this.DefaultApplicationContextType); + } + return contextType; + } + + /// + /// Extracts the case-sensitivity attribute from the context element + /// + private bool GetCaseSensitivity(XmlElement contextElement) + { + bool caseSensitive = DefaultCaseSensitivity; + + string caseSensitiveAttr = contextElement.GetAttribute(ContextSchema.CaseSensitiveAttribute); + if (StringUtils.HasText(caseSensitiveAttr)) + { + caseSensitive = Boolean.Parse(caseSensitiveAttr); + } + return caseSensitive; + } + + /// + /// Gets the context specified in the type + /// attribute of the context element. + /// + /// + ///

    + /// If this attribute is not defined it defaults to the + /// type. + ///

    + ///
    + /// + /// If the context type does not implement the + /// interface. + /// + private Type GetConfiguredContextType(XmlElement contextElement, Type defaultContextType) + { + string typeName = contextElement.GetAttribute(ContextSchema.TypeAttribute); + + if (StringUtils.IsNullOrEmpty(typeName)) + { + return defaultContextType; + } + else + { + Type type = TypeResolutionUtils.ResolveType(typeName); + if (typeof(IApplicationContext).IsAssignableFrom(type)) + { + return type; + } + else + { + throw new TypeMismatchException( type.Name + " does not implement IApplicationContext."); + } + } + } + + /// + /// Returns if the context should be lazily + /// initialized. + /// + private bool IsLazy + { + get { return false; } + } + + /// + /// Returns the array of resources containing object definitions for + /// this context. + /// + private string[] GetResources( XmlElement contextElement ) + { + ArrayList resourceNodes = new ArrayList(contextElement.ChildNodes.Count); + foreach (XmlNode possibleResourceNode in contextElement.ChildNodes) + { + XmlElement possibleResourceElement = possibleResourceNode as XmlElement; + if(possibleResourceElement != null && + possibleResourceElement.LocalName == ContextSchema.ResourceElement) + { + string resourceName = possibleResourceElement.GetAttribute(ContextSchema.URIAttribute); + if(StringUtils.HasText(resourceName)) + { + resourceNodes.Add(resourceName); + } + } + } + return (string[]) resourceNodes.ToArray(typeof(string)); + } + + /// + /// Returns the array of child contexts for this context. + /// + private XmlNode[] GetChildContexts(XmlElement contextElement) + { + ArrayList contextNodes = new ArrayList(contextElement.ChildNodes.Count); + foreach (XmlNode possibleContextNode in contextElement.ChildNodes) + { + XmlElement possibleContextElement = possibleContextNode as XmlElement; + if (possibleContextElement != null && + possibleContextElement.LocalName == ContextSchema.ContextElement) + { + contextNodes.Add(possibleContextElement); + } + } + return (XmlNode[])contextNodes.ToArray(typeof(XmlNode)); + } + + #region Inner Class : ContextInstantiator + + private abstract class ContextInstantiator + { + protected ContextInstantiator( + Type contextType, string contextName, bool caseSensitive, string[] resources) + { + _contextType = contextType; + _contextName = contextName; + _caseSensitive = caseSensitive; + _resources = resources; + } + + public IApplicationContext InstantiateContext() + { + ConstructorInfo ctor = GetContextConstructor(); + if (ctor == null) + { + string errorMessage = "No constructor with string[] argument found for context type [" + ContextType.Name + "]"; + throw ConfigurationUtils.CreateConfigurationException(errorMessage); + } + IApplicationContext context = InvokeContextConstructor(ctor); + ContextRegistry.RegisterContext(context); + return context; + } + + protected abstract ConstructorInfo GetContextConstructor(); + + protected abstract IApplicationContext InvokeContextConstructor( + ConstructorInfo ctor); + + protected Type ContextType + { + get { return _contextType; } + } + + protected string ContextName + { + get { return _contextName; } + } + + protected bool CaseSensitive + { + get { return _caseSensitive; } + } + + protected string[] Resources + { + get { return _resources; } + } + + private Type _contextType; + private string _contextName; + private bool _caseSensitive; + private string[] _resources; + } + + #endregion + + #region Inner Class : RootContextInstantiator + + private sealed class RootContextInstantiator : ContextInstantiator + { + public RootContextInstantiator( + Type contextType, string contextName, bool caseSensitive, string[] resources) + : base(contextType, contextName, caseSensitive, resources) + { + } + + protected override ConstructorInfo GetContextConstructor() + { + return ContextType.GetConstructor(new Type[] {typeof(string), typeof(bool), typeof(string[])}); + } + + protected override IApplicationContext InvokeContextConstructor( + ConstructorInfo ctor) + { + return (IApplicationContext) ObjectUtils.InstantiateType( + ctor, new object[] {ContextName, CaseSensitive, Resources}); + } + } + + #endregion + + #region Inner Class : DescendantContextInstantiator + + private sealed class DescendantContextInstantiator : ContextInstantiator + { + public DescendantContextInstantiator( + IApplicationContext parentContext, Type contextType, + string contextName, bool caseSensitive, string[] resources) + : base(contextType, contextName, caseSensitive, resources) + { + this.parentContext = parentContext; + } + + protected override ConstructorInfo GetContextConstructor() + { + return ContextType.GetConstructor( + new Type[] {typeof(string), typeof(bool), typeof(IApplicationContext), typeof(string[])}); + } + + protected override IApplicationContext InvokeContextConstructor( + ConstructorInfo ctor) + { + return (IApplicationContext) ObjectUtils.InstantiateType( + ctor, new object[] {ContextName, CaseSensitive, this.parentContext, Resources}); + } + + private IApplicationContext parentContext; + } + + #endregion + + #region Context Schema Constants + + /// + /// Constants defining the structure and values associated with the + /// schema for laying out Spring.NET contexts in XML. + /// + private sealed class ContextSchema + { + /// + /// Defines a single + /// . + /// + public const string ContextElement = "context"; + + /// + /// Specifies a context name. + /// + public const string NameAttribute = "name"; + + /// + /// Specifies if context should be case sensitive or not. Default is true. + /// + public const string CaseSensitiveAttribute = "caseSensitive"; + + /// + /// Specifies a . + /// + /// + ///

    + /// Does not have to be fully assembly qualified, but its generally regarded + /// as better form if the names of one's objects + /// are specified explicitly. + ///

    + ///
    + public const string TypeAttribute = "type"; + + /// + /// Specifies whether context should be lazy initialized. + /// + public const string LazyAttribute = "lazy"; + + /// + /// Defines an + /// + public const string ResourceElement = "resource"; + + /// + /// Specifies the URI for an + /// . + /// + public const string URIAttribute = "uri"; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/ContextRegistry.cs b/src/Spring/Spring.Core/Context/Support/ContextRegistry.cs new file mode 100644 index 00000000..c4f6d47e --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/ContextRegistry.cs @@ -0,0 +1,286 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Configuration; +using Common.Logging; + +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Provides access to a central registry of + /// s. + /// + /// + ///

    + /// A singleton implementation to access one or more application contexts. Application + /// context instances are cached. + ///

    + ///

    Note that the use of this class or similar is unnecessary except (sometimes) for + /// a small amount of glue code. Excessive usage will lead to code that is more tightly + /// coupled, and harder to modify or test. Consider refactoring your code to use standard + /// Dependency Injection techniques or implement the interface IApplicationContextAware to + /// obtain a reference to an application context.

    + ///
    + /// Mark Pollack + /// Aleksandar Seovic + /// + /// $Id: ContextRegistry.cs,v 1.28 2008/03/21 10:49:37 oakinger Exp $ + public sealed class ContextRegistry + { + /// + /// The shared instance for this class (and derived classes). + /// + private static readonly ILog log = LogManager.GetLogger(typeof(ContextRegistry)); + + private static readonly object syncRoot = new Object(); + private static readonly ContextRegistry instance = new ContextRegistry(); + private static string rootContextName = null; + + private IDictionary contextMap = CollectionsUtil.CreateCaseInsensitiveHashtable(); + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the ContextRegistry class. + /// + /// + ///

    + /// Explicit static constructor to tell C# compiler + /// not to mark type as beforefieldinit. + ///

    + ///
    + static ContextRegistry() + {} + + // CLOVER:ON + + #endregion + + /// + /// This event is fired, if ContextRegistry.Clear() is called.
    + /// Clients may register to get informed + ///
    + /// + /// This event is fired while still holding a lock on the Registry.
    + /// 'sender' parameter is sent as typeof(ContextRegistry), EventArgs are not used + ///
    + public static event EventHandler Cleared; + + /// + /// Gets an object that should be used to synchronize access to ContextRegistry + /// from the calling code. + /// + public static object SyncRoot + { + get { return syncRoot; } + } + + /// + /// Registers an instance of an + /// . + /// + /// + ///

    + /// This is usually called via a + /// inside a .NET + /// application configuration file. + ///

    + ///
    + /// The application context to be registered. + /// + /// If a context has previously been registered using the same name + /// + public static void RegisterContext(IApplicationContext context) + { + lock (syncRoot) + { + if (instance.contextMap.Contains(context.Name)) + { + IApplicationContext ctx = (IApplicationContext)instance.contextMap[context.Name]; + throw new ApplicationContextException( + string.Format("Existing context '{0}' already registered under name '{1}'.", + ctx, context.Name)); + } + instance.contextMap[context.Name] = context; + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(String.Format( + "Registering context '{0}' under name '{1}'.", context, context.Name)); + } + + #endregion + + if (rootContextName == null) + { + rootContextName = context.Name; + } + } + } + + /// + /// Returns the root application context. + /// + /// + ///

    + /// The first call to GetContext will create the context + /// as specified in the .NET application configuration file + /// under the location spring/context. + ///

    + ///
    + /// The root application context. + public static IApplicationContext GetContext() + { + lock (syncRoot) + { + InitializeContextIfNeeded(); + return GetContext(rootContextName); + } + } + + /// + /// Returns context based on specified name. + /// + /// + ///

    + /// The first call to GetContext will create the context + /// as specified in the .NET application configuration file + /// under the location spring/context. + ///

    + ///
    + /// The context name. + /// The specified context, or null, if context with that name doesn't exists. + /// + /// If the context name is null or empty + /// + public static IApplicationContext GetContext(string name) + { + if (StringUtils.IsNullOrEmpty(name)) + { + throw new ArgumentException( + "The context name passed to the GetContext method cannot be null or empty."); + } + else + { + lock (syncRoot) + { + InitializeContextIfNeeded(); + IApplicationContext ctx = (IApplicationContext)instance.contextMap[name]; + + #region Instrumentation + + if (log.IsDebugEnabled) + { + if (ctx == null) + { + log.Debug(String.Format( + "No context registered under name '{0}'.", name)); + } + else + { + log.Debug(String.Format( + "Returning context '{0}' registered under name '{1}'.", ctx, name)); + } + } + + #endregion + + return ctx; + } + } + } + + /// + /// Removes all registered + /// s from this + /// registry. + /// + /// + /// Raises the event while still holding a lock on + /// + public static void Clear() + { + lock (syncRoot) + { + foreach (IApplicationContext ctx in instance.contextMap.Values) + { + ctx.Dispose(); + } + instance.contextMap.Clear(); + rootContextName = null; + ConfigurationUtils.RefreshSection(AbstractApplicationContext.ContextSectionName); + if (Cleared != null) + { + Cleared(typeof(ContextRegistry), EventArgs.Empty); + } + } + } + + /// + /// Allows to check, if a context is already registered + /// + /// The context name. + /// true, if the context is already registered. false otherwise + public static bool IsContextRegistered( string name ) + { + lock (instance) + { + return (instance.contextMap[name] != null); + } + } + + private static bool rootContextCurrentlyInCreation; + + private static void InitializeContextIfNeeded() + { + if (rootContextName == null) + { + if (rootContextCurrentlyInCreation) + { + throw new InvalidOperationException("root context is currently in creation. You must not call ContextRegistry.GetContext() from e.g. constructors of your singleton objects"); + } + + rootContextCurrentlyInCreation = true; + try + { + ConfigurationUtils.GetSection(AbstractApplicationContext.ContextSectionName); + } + finally + { + rootContextCurrentlyInCreation = false; + } + } + } + } +} + diff --git a/src/Spring/Spring.Core/Context/Support/DefaultMessageSourceResolvable.cs b/src/Spring/Spring.Core/Context/Support/DefaultMessageSourceResolvable.cs new file mode 100644 index 00000000..d91942a8 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/DefaultMessageSourceResolvable.cs @@ -0,0 +1,227 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Default implementation of the + /// interface. + /// + /// + ///

    + /// Provides easy ways to store all the necessary values needed to resolve + /// messages from an . + ///

    + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: DefaultMessageSourceResolvable.cs,v 1.4 2007/07/02 21:24:39 markpollack Exp $ + /// + [Serializable] + public class DefaultMessageSourceResolvable : IMessageSourceResolvable + { + private string[] codes; + private object[] arguments; + private string defaultMessage; + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class + /// using a single code. + /// + /// The message code to be resolved. + public DefaultMessageSourceResolvable(string code) + : this(new string[] {code}, StringUtils.EmptyStrings, string.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The codes to be used to resolve this message + public DefaultMessageSourceResolvable(string[] codes) + : this(codes, StringUtils.EmptyStrings, string.Empty) + { + } + + /// + /// Creates a new instance of the + /// class + /// using multiple codes. + /// + /// The message codes to be resolved. + /// + /// The arguments used to resolve the supplied . + /// + public DefaultMessageSourceResolvable(string[] codes, object[] arguments) + : this(codes, arguments, string.Empty) + { + } + + /// + /// Creates a new instance of the + /// class + /// using multiple codes and a default message. + /// + /// The message codes to be resolved. + /// + /// The arguments used to resolve the supplied . + /// + /// + /// The default message used if no code could be resolved. + /// + public DefaultMessageSourceResolvable( + string[] codes, object[] arguments, string defaultMessage) + { + this.codes = codes; + this.arguments = arguments; + this.defaultMessage = defaultMessage; + } + + /// + /// Creates a new instance of the + /// class + /// from another resolvable. + /// + /// + ///

    + /// This is the copy constructor for the + /// class. + ///

    + ///
    + /// + /// The to be copied. + /// + /// + /// If the supplied is . + /// + public DefaultMessageSourceResolvable(IMessageSourceResolvable resolvable) + : this(resolvable.GetCodes(), resolvable.GetArguments(), resolvable.DefaultMessage) + { + } + + #endregion + + /// + /// Return the default code for this resolvable. + /// + /// + /// The default code of this resolvable; this will be the last code in + /// the codes array, or if this instance has no + /// codes. + /// + /// + public string LastCode + { + get + { + if (codes != null && codes.Length > 0) + { + return codes[codes.Length - 1]; + } + else + { + return null; + } + } + } + + /// + /// Returns a representation of this + /// . + /// + /// + /// A representation of this + /// . + /// + public override string ToString() + { + return Accept(new MessageSourceResolvableVisitor()); + } + + /// + /// Calls the visit method on the supplied + /// to output a version of this class. + /// + /// The visitor to use. + /// + /// A representation of this + /// . + /// + public string Accept(MessageSourceResolvableVisitor visitor) + { + return visitor.VisitMessageSourceResolvableString(this); + } + + #region IMessageSourceResolvable Members + + /// + /// Return the codes to be used to resolve this message, in the order + /// that they are to be tried. + /// + /// + /// A array of codes which are associated + /// with this message. + /// + /// + public string[] GetCodes() + { + return codes; + } + + /// + /// Return the array of arguments to be used to resolve this message. + /// + /// + /// An array of objects to be used as parameters to replace + /// placeholders within the message text. + /// + /// + public object[] GetArguments() + { + return arguments; + } + + /// + /// Return the default message to be used to resolve this message. + /// + /// + /// The default message, or if there is no + /// default. + /// + /// + public string DefaultMessage + { + get { return defaultMessage; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/DefaultSectionHandler.cs b/src/Spring/Spring.Core/Context/Support/DefaultSectionHandler.cs new file mode 100644 index 00000000..4fccf4c2 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/DefaultSectionHandler.cs @@ -0,0 +1,65 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Configuration; +using System.Xml; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Default section handler that can handle any configuration section. + /// + /// + ///

    + /// Simply returns the configuration section as an . + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: DefaultSectionHandler.cs,v 1.4 2006/04/09 07:18:38 markpollack Exp $ + public class DefaultSectionHandler : IConfigurationSectionHandler + { + #region Methods + /// + /// Returns the configuration section as an + /// + /// + /// The configuration settings in a corresponding parent + /// configuration section. + /// + /// + /// The configuration context when called from the ASP.NET + /// configuration system. Otherwise, this parameter is reserved and + /// is a null reference. + /// + /// + /// The for the section. + /// + /// Config section as XmlElement. + public object Create(object parent, object configContext, XmlNode section) + { + return section as XmlElement; + } + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/DelegatingMessageSource.cs b/src/Spring/Spring.Core/Context/Support/DelegatingMessageSource.cs new file mode 100644 index 00000000..da5a3f8a --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/DelegatingMessageSource.cs @@ -0,0 +1,387 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Globalization; + +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Empty implementation that + /// simply delegates all method calls to it's parent + /// . + /// + /// + ///

    + /// If no parent is available, + /// no messages will be resolved (and a + /// will be thrown). + ///

    + ///

    + /// Used as placeholder by the + /// class, + /// if the context definition doesn't define its own + /// . Not intended for direct use + /// in applications. + ///

    + ///
    + /// Juergan Hoeller + /// Rick Evans (.NET) + /// $Id: DelegatingMessageSource.cs,v 1.5 2007/07/02 21:24:39 markpollack Exp $ + /// + public class DelegatingMessageSource : IHierarchicalMessageSource + { + #region Fields + + private IMessageSource _parentMessageSource; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public DelegatingMessageSource() + {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The parent message source used to try and resolve messages that + /// this object can't resolve. + /// + public DelegatingMessageSource(IMessageSource parentMessageSource) + { + ParentMessageSource = parentMessageSource; + } + + #endregion + + #region Properties + + /// + /// The parent message source used to try and resolve messages that + /// this object can't resolve. + /// + /// + public IMessageSource ParentMessageSource + { + get + { + if (_parentMessageSource == null) + { + _parentMessageSource = new SpecialCaseNullMessageSource(); + } + return _parentMessageSource; + } + set { _parentMessageSource = value; } + } + + #endregion + + #region Methods + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If the message could not be resolved. + /// + /// + public string GetMessage(string name) + { + return ParentMessageSource.GetMessage(name); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If the message could not be resolved. + /// + /// + public string GetMessage(string name, params object[] arguments) + { + return ParentMessageSource.GetMessage(name, arguments); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If the message could not be resolved. + /// + /// + public string GetMessage(string name, CultureInfo culture) + { + return ParentMessageSource.GetMessage(name, culture); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If the message could not be resolved. + /// + /// + public string GetMessage(string name, CultureInfo culture, params object[] arguments) + { + return ParentMessageSource.GetMessage(name, culture, arguments); + } + + /// + /// Resolve the message identified by the supplied + /// . + /// + /// The name of the message to resolve. + /// The default message. + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The array of arguments that will be filled in for parameters within + /// the message, or if there are no parameters + /// within the message. Parameters within a message should be + /// referenced using the same syntax as the format string for the + /// method. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If the message could not be resolved. + /// + /// + public string GetMessage(string name, string defaultMessage, CultureInfo culture, params object[] arguments) + { + return ParentMessageSource.GetMessage(name, defaultMessage, culture, arguments); + } + + /// + /// Resolve the message using all of the attributes contained within + /// the supplied + /// argument. + /// + /// + /// The value object storing those attributes that are required to + /// properly resolve a message. + /// + /// + /// The that represents + /// the culture for which the resource is localized. + /// + /// + /// The resolved message if the lookup was successful (see above for + /// the return value in the case of an unsuccessful lookup). + /// + /// + /// If the message could not be resolved. + /// + /// + /// If the message could not be resolved. + /// + /// + public string GetMessage(IMessageSourceResolvable resolvable, CultureInfo culture) + { + return ParentMessageSource.GetMessage(resolvable, culture); + } + + /// + /// Gets a localized resource object identified by the supplied + /// . + /// + /// + /// The name of the resource object to resolve. + /// + /// + /// The resolved object, or if not found. + /// + /// + public object GetResourceObject(string name) + { + return ParentMessageSource.GetResourceObject(name); + } + + /// + /// Gets a localized resource object identified by the supplied + /// . + /// + /// + /// The name of the resource object to resolve. + /// + /// + /// The with which the + /// resource is associated. + /// + /// + /// The resolved object, or if not found. + /// + /// + public object GetResourceObject(string name, CultureInfo culture) + { + return ParentMessageSource.GetResourceObject(name, culture); + } + + /// + /// Applies resources to object properties. + /// + /// + /// An object that contains the property values to be applied. + /// + /// + /// The base name of the object to use for key lookup. + /// + /// + /// The with which the + /// resource is associated. + /// + /// + public void ApplyResources(object value, string objectName, CultureInfo culture) + { + ParentMessageSource.ApplyResources(value, objectName, culture); + } + + #endregion + + #region Inner Class : SpecialCaseNullMessageSource + + private sealed class SpecialCaseNullMessageSource : IMessageSource + { + public string GetMessage(string name) + { + return GetMessage(name, (object[]) null); + } + + public string GetMessage(string name, params object[] arguments) + { + return GetMessage(name, CultureInfo.CurrentUICulture, null); + } + + public string GetMessage(string name, CultureInfo culture) + { + return GetMessage(name, culture, null); + } + + public string GetMessage(string name, CultureInfo culture, params object[] arguments) + { + throw new NoSuchMessageException(name, culture); + } + + public string GetMessage(string name, string defaultMessage, CultureInfo culture, params object[] arguments) + { + throw new NoSuchMessageException(name, culture); + } + + + public string GetMessage(IMessageSourceResolvable resolvable, CultureInfo culture) + { + if (StringUtils.HasText(resolvable.DefaultMessage)) + { + return resolvable.DefaultMessage; + } + string[] codes = resolvable.GetCodes(); + string code = (codes != null && codes.Length > 0 ? codes[0] : string.Empty); + throw new NoSuchMessageException(code, culture); + } + + public object GetResourceObject(string name) + { + return GetResourceObject(name, CultureInfo.CurrentUICulture); + } + + public object GetResourceObject(string name, CultureInfo culture) + { + throw new ApplicationContextException( + string.Format( + "Cannot lookup the named resource '{0}' for locale '{1}' " + + ": no IMessageSource in context.", + name, culture)); + } + + public void ApplyResources(object value, string objectName, CultureInfo culture) + { + throw new ApplicationContextException( + string.Format( + "Cannot apply [{0}] resource to object '{1}' for locale '{2}' " + + ": no IMessageSource in context.", + value, objectName, culture)); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/GenericApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/GenericApplicationContext.cs new file mode 100644 index 00000000..935382fa --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/GenericApplicationContext.cs @@ -0,0 +1,259 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Core.IO; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Util; + +namespace Spring.Context.Support +{ + /// + /// Generic ApplicationContext implementation that holds a single internal + /// instance and does not + /// assume a specific object definition format. + /// + /// + /// Implements the interface in order + /// to allow for aplying any object definition readers to it. + /// Typical usage is to register a variety of object definitions via the + /// interface and then call + /// to initialize those + /// objects with application context semantics (handling + /// , auto-detecting + /// ObjectFactoryPostProcessors, etc). + /// + /// In contrast to other IApplicationContext implementations that create a new internal + /// IObjectFactory instance for each refresh, the internal IObjectFactory of this context + /// is available right from the start, to be able to register object definitions on it. + /// may only be called once + /// Usage examples + /// + /// GenericApplicationContext ctx = new GenericApplicationContext(); + /// + /// + /// + /// Mark Pollack + /// $Id: GenericApplicationContext.cs,v 1.5 2008/02/17 13:34:44 markpollack Exp $ + public class GenericApplicationContext : AbstractApplicationContext, IObjectDefinitionRegistry + { + private DefaultListableObjectFactory objectFactory; + + private bool refreshed = false; + + + /// + /// Initializes a new instance of the class. + /// + public GenericApplicationContext() + { + objectFactory = new DefaultListableObjectFactory(); + } + + /// + /// Initializes a new instance of the class. + /// + /// if set to true names in the context are case sensitive. + public GenericApplicationContext(bool caseSensitive) + { + objectFactory = new DefaultListableObjectFactory(caseSensitive); + } + + + /// + /// Initializes a new instance of the class. + /// + /// The object factory instance to use for this context. + public GenericApplicationContext(DefaultListableObjectFactory objectFactory) + { + AssertUtils.ArgumentNotNull(objectFactory, "objectFactory", "ObjectFactory must not be null"); + this.objectFactory = objectFactory; + } + + + /// + /// Initializes a new instance of the class. + /// + /// The parent application context. + public GenericApplicationContext(IApplicationContext parent) + { + objectFactory = new DefaultListableObjectFactory(); + ParentContext = parent; + } + + + /// + /// Initializes a new instance of the class. + /// + /// The name of the application context. + /// if set to true names in the context are case sensitive. + /// The parent application context. + public GenericApplicationContext(string name, bool caseSensitive, IApplicationContext parent) : this(caseSensitive) + { + Name = name; + ParentContext = parent; + } + + + /// + /// Initializes a new instance of the class. + /// + /// The object factory to use for this context + /// The parent applicaiton context. + public GenericApplicationContext(DefaultListableObjectFactory objectFactory, IApplicationContext parent) : this(objectFactory) + { + ParentContext = parent; + } + + + + /// + /// Gets the parent context, or if there is no + /// parent context. Set the parent of this application context also setting + /// the parent of the interanl ObjectFactory accordingly. + /// + /// The parent context + /// + /// The parent context, or if there is no + /// parent. + /// + /// + public override IApplicationContext ParentContext + { + get + { + return base.ParentContext; + } + set { + base.ParentContext = value; + objectFactory.ParentObjectFactory = GetInternalParentObjectFactory(); + } + } + + + /// + /// Do nothing operation. We hold a single internal ObjectFactory and rely on callers + /// to register objects throug our public methods (or the ObjectFactory's). + /// + /// + /// In the case of errors encountered while refreshing the object factory. + /// + protected override void RefreshObjectFactory() + { + if (refreshed) + { + throw new InvalidOperationException( + "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once"); + } + + refreshed = true; + } + + /// + /// Return the internal object factory of this application context. + /// + /// + public override IConfigurableListableObjectFactory ObjectFactory + { + get { return objectFactory; } + } + + /// + /// Gets the underlying object factory of this context, available for + /// registering object definitions. + /// + /// You need to call Refresh to initialize the + /// objects factory and its contained objects with application context + /// semantics (autodecting IObjectFactoryPostProcessors, etc). + /// The internal object factory (as DefaultListableObjectFactory). + public DefaultListableObjectFactory DefaultListableObjectFactory + { + get { return objectFactory; } + } + + + + #region IObjectDefinitionRegistry Members + + /// + /// Returns the + /// + /// for the given object name. + /// + /// The name of the object to find a definition for. + /// + /// The for + /// the given name (never null). + /// + /// + /// If the object definition cannot be resolved. + /// + /// + /// In case of errors. + /// + public override IObjectDefinition GetObjectDefinition(string name) + { + return objectFactory.GetObjectDefinition(name); + } + + /// + /// Register a new object definition with this registry. + /// Must support + /// + /// and . + /// + /// The name of the object instance to register. + /// The definition of the object instance to register. + /// + ///

    + /// Must support + /// and + /// . + ///

    + ///
    + /// + /// If the object definition is invalid. + /// + public void RegisterObjectDefinition(string name, IObjectDefinition definition) + { + objectFactory.RegisterObjectDefinition(name, definition); + } + + /// + /// Given a object name, create an alias. We typically use this method to + /// support names that are illegal within XML ids (used for object names). + /// + /// The name of the object. + /// The alias that will behave the same as the object name. + /// + /// If there is no object with the given name. + /// + /// + /// If the alias is already in use. + /// + public void RegisterAlias(string name, string theAlias) + { + objectFactory.RegisterAlias(name, theAlias); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/MessageSourceAccessor.cs b/src/Spring/Spring.Core/Context/Support/MessageSourceAccessor.cs new file mode 100644 index 00000000..b44f50c8 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/MessageSourceAccessor.cs @@ -0,0 +1,188 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Helper class for easy access to messages from an + /// , providing various + /// overloaded GetMessage methods. + /// + /// + ///

    + /// Available from + /// , but also + /// reusable as a standalone helper to delegate to in application objects. + ///

    + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: MessageSourceAccessor.cs,v 1.8 2007/08/23 14:30:50 oakinger Exp $ + /// + /// + public class MessageSourceAccessor + { + private IMessageSource _messageSource; + private CultureInfo _defaultCultureInfo; + + /// + /// Creates a new instance of the + /// class + /// that uses the current + /// for all locale specific lookups. + /// + /// + /// The to use to locate messages. + /// + public MessageSourceAccessor(IMessageSource messageSource) + : this(messageSource, CultureInfo.CurrentUICulture) + { + } + + /// + /// Creates a new instance of the + /// class + /// + /// + /// The to use to locate + /// messages. + /// + /// + /// The to use for + /// locale specific messages. + /// + public MessageSourceAccessor( + IMessageSource messageSource, CultureInfo cultureInfo) + { + _messageSource = messageSource; + _defaultCultureInfo = cultureInfo; + } + + /// + /// Retrieve the message for the given code and the default + /// . + /// + /// The code of the message. + /// The message. + public string GetMessage(string code) + { + return _messageSource.GetMessage(code, _defaultCultureInfo); + } + + /// + /// Retrieve the message for the given code and the given + /// . + /// + /// The code of the message. + /// + /// The to use for + /// lookups. + /// + /// The message. + public string GetMessage(string code, CultureInfo cultureInfo) + { + return _messageSource.GetMessage(code, cultureInfo); + } + + /// + /// Retrieve the message for the given code and the default + /// . + /// + /// The code of the message. + /// + /// The arguments for the message, or if none. + /// + /// The message. + /// + /// If the message could not be found. + /// + public string GetMessage(string code, params object[] args) + { + return _messageSource.GetMessage(code, _defaultCultureInfo, args); + } + + /// + /// Retrieve the message for the given code and the given + /// . + /// + /// The code of the message. + /// + /// The to use for + /// lookups. + /// + /// + /// The arguments for the message, or if none. + /// + /// The message. + /// + /// If the message could not be found. + /// + public string GetMessage( + string code, CultureInfo cultureInfo, params object[] args) + { + return _messageSource.GetMessage(code, cultureInfo, args); + } + + /// + /// Retrieve a mesage using the given + /// . + /// + /// + /// The . + /// + /// The message. + /// + /// If the message could not be found. + /// + public string GetMessage(IMessageSourceResolvable resolvable) + { + return GetMessage(resolvable, _defaultCultureInfo); + } + + /// + /// Retrieve a mesage using the given + /// in the given + /// . + /// + /// + /// The . + /// + /// + /// The to use for + /// lookups. + /// + /// The message + /// + /// If the message could not be found. + /// + public string GetMessage( + IMessageSourceResolvable resolvable, CultureInfo cultureInfo) + { + return _messageSource.GetMessage(resolvable, cultureInfo); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/MessageSourceResolvableVisitor.cs b/src/Spring/Spring.Core/Context/Support/MessageSourceResolvableVisitor.cs new file mode 100644 index 00000000..fb579548 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/MessageSourceResolvableVisitor.cs @@ -0,0 +1,97 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Visitor class to represent + /// instances. + /// + /// + ///

    + /// Used in the first instance to supply stringified versions of + /// instances. + ///

    + ///

    + /// Other methods can be added here to return different representations, + /// including XML, CSV, etc.. + ///

    + ///
    + /// Griffin Caprio (.NET) + /// $Id: MessageSourceResolvableVisitor.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ + public class MessageSourceResolvableVisitor + { + /// + /// Creates a new instance of the + /// class. + /// + public MessageSourceResolvableVisitor() + { + } + + /// + /// Outputs the supplied + /// as a nicely formatted . + /// + /// + /// The to output. + /// + public string VisitMessageSourceResolvableString( + IMessageSourceResolvable resolvable) + { + StringBuilder builder = new StringBuilder(); + builder.Append("codes=["); + builder.Append(StringUtils.ArrayToDelimitedString(resolvable.GetCodes(), ",")); + builder.Append("]; arguments=["); + if (resolvable.GetArguments() == null) + { + builder.Append("null"); + } + else + { + object[] arguments = resolvable.GetArguments(); + for (int i = 0; i < arguments.Length; i++) + { + builder.Append("("); + builder.Append(arguments[i].GetType().Name); + builder.Append(")["); + builder.Append(arguments[i]); + builder.Append("]"); + if (i < arguments.Length - 1) + { + builder.Append(", "); + } + } + } + builder.Append("]; defaultMessage=["); + builder.Append(resolvable.DefaultMessage); + builder.Append("]"); + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/NamespaceParsersSectionHandler.cs b/src/Spring/Spring.Core/Context/Support/NamespaceParsersSectionHandler.cs new file mode 100644 index 00000000..0e7c7326 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/NamespaceParsersSectionHandler.cs @@ -0,0 +1,139 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Configuration; +using System.Globalization; +using System.Xml; + +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Objects; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Configuration section handler for the (recommended, Spring.NET standard) parsers + /// config section. + /// + /// + ///

    + /// Spring.NET allows the registration of custom configuration parsers that + /// can be used to create simplified configuration schemas that better + /// describe object definitions. + ///

    + ///

    + /// For example, Spring.NET uses this facility internally in order to + /// define simplified schemas for various AOP, Data and Services definitions. + ///

    + ///
    + /// + ///

    + /// The following example shows how to configure both this section handler + /// and how to define custom configuration parsers within a Spring.NET + /// config section. + ///

    + /// + /// + /// + /// + ///
    + /// + /// + /// + /// + /// + /// + /// ... + /// + /// ... + /// + /// + /// + /// + /// Aleksandar Seovic + /// $Id: NamespaceParsersSectionHandler.cs,v 1.1 2007/08/08 01:53:43 bbaia Exp $ + /// + public class NamespaceParsersSectionHandler : IConfigurationSectionHandler + { + private const string ParserElementName = "parser"; + private const string TypeAttributeName = "type"; + private const string NamespaceAttributeName = "namespace"; + private const string SchemaLocationAttributeName = "schemaLocation"; + + /// + /// Registers parsers specified in the (recommended, Spring.NET standard) + /// parsers config section with the . + /// + /// + /// The configuration settings in a corresponding parent + /// configuration section. + /// + /// + /// The configuration context when called from the ASP.NET + /// configuration system. Otherwise, this parameter is reserved and + /// is . + /// + /// + /// The for the section. + /// + /// + /// This method always returns , because parsers + /// are registered as a side-effect of this object's execution and there + /// is thus no need to return anything. + /// + public object Create(object parent, object configContext, XmlNode section) + { + if (section != null) + { + XmlNodeList parsers = ((XmlElement)section).GetElementsByTagName(ParserElementName); + foreach (XmlElement parserElement in parsers) + { + string parserTypeName = GetRequiredAttributeValue(parserElement, TypeAttributeName, section); + string xmlNamespace = parserElement.GetAttribute(NamespaceAttributeName); + string schemaLocation = parserElement.GetAttribute(SchemaLocationAttributeName); + + Type parserType = TypeResolutionUtils.ResolveType(parserTypeName); + NamespaceParserRegistry.RegisterParser(parserType, xmlNamespace, schemaLocation); + } + } + return null; + } + + private static string GetRequiredAttributeValue( + XmlElement aliasElement, string requiredAttributeName, XmlNode section) + { + XmlAttribute attribute = aliasElement.GetAttributeNode(requiredAttributeName); + if (attribute == null) + { + string errorMessage = string.Format(CultureInfo.InvariantCulture, + "The '{0}' attribute is required for the element.", requiredAttributeName); + throw ConfigurationUtils.CreateConfigurationException(errorMessage, section); + } + return attribute.Value; + } + } +} diff --git a/src/Spring/Spring.Core/Context/Support/NullMessageSource.cs b/src/Spring/Spring.Core/Context/Support/NullMessageSource.cs new file mode 100644 index 00000000..e9b691d7 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/NullMessageSource.cs @@ -0,0 +1,117 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Globalization; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// An that doesn't do a whole lot. + /// + /// + ///

    + /// is an implementation of + /// the NullObject pattern. It should be used in those situations where a + /// needs to be passed (say to a + /// method) but where the resolution of messages is not required. + ///

    + ///

    + /// There should not (typically) be a need to instantiate instances of this class; + /// does not maintan any state + /// and the instance is + /// thus safe to pass around. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: NullMessageSource.cs,v 1.5 2007/08/27 09:38:29 oakinger Exp $ + public sealed class NullMessageSource : AbstractMessageSource + { + /// + /// The canonical instance of the + /// class. + /// + public static readonly NullMessageSource Null = new NullMessageSource(); + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// Consider using + /// instead. + ///

    + ///
    + public NullMessageSource() + {} + + /// + /// Simply returns the supplied message as-is. + /// + /// The code of the message to resolve. + /// + /// The to resolve the + /// code for. + /// + /// + /// The supplied message as-is. + /// + protected override string ResolveMessage(string code, CultureInfo cultureInfo) + { + return code; + } + + /// + /// Always returns . + /// + /// The code of the object to resolve. + /// + /// The to resolve the + /// code for. + /// + /// + /// (always). + /// + protected override object ResolveObject(string code, CultureInfo cultureInfo) + { + return null; + } + + /// + /// Does nothing. + /// + /// + /// An object that contains the property values to be applied. + /// + /// + /// The base name of the object to use for key lookup. + /// + /// + /// The with which the + /// resource is associated. + /// + protected override void ApplyResourcesToObject( + object value, string objectName, CultureInfo cultureInfo) + {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/ResourceHandlersSectionHandler.cs b/src/Spring/Spring.Core/Context/Support/ResourceHandlersSectionHandler.cs new file mode 100644 index 00000000..872efe18 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/ResourceHandlersSectionHandler.cs @@ -0,0 +1,137 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +#if ! NET_COMPACT_FRAMEWORK +#endif +using System.Configuration; +using System.Globalization; +using System.Xml; + +using Spring.Core.IO; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Handler for Spring.NET resourceHandlers config section. + /// + /// + ///

    + /// Spring allows registration of custom resource handlers that can be used to load + /// object definitions from. + ///

    + ///

    + /// For example, if you wanted to store your object definitions in a database instead + /// of in the config file, you could write a custom implementation + /// and register it with Spring using 'db' as a protocol name. + ///

    + ///

    + /// Afterwards, you would simply specify resource URI within the context config element + /// using your custom resource handler. + ///

    + ///
    + /// + ///

    + /// The following example shows how to configure both this section handler, + /// how to define custom resource within Spring config section, and how to load + /// object definitions using custom resource handler: + ///

    + /// + /// + /// + /// + ///
    + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Aleksandar Seovic + /// $Id: ResourceHandlersSectionHandler.cs,v 1.3 2007/08/08 17:46:37 bbaia Exp $ + /// + public class ResourceHandlersSectionHandler : IConfigurationSectionHandler + { + private const string HandlerElement = "handler"; + private const string ProtocolAttribute = "protocol"; + private const string TypeAttribute = "type"; + + /// + /// Registers resource handlers that are specified in + /// the resources config section with the . + /// + /// + /// The configuration settings in a corresponding parent + /// configuration section. Ignored. + /// + /// + /// The configuration context when called from the ASP.NET + /// configuration system. Otherwise, this parameter is reserved and + /// is . + /// + /// + /// The for the section. + /// + /// + /// This method always returns null, because resource handlers are registered + /// as a sideffect of its execution and there is no need to return anything. + /// + public object Create(object parent, object configContext, XmlNode section) + { + if (section != null) + { + XmlNodeList resourceHandlers = ((XmlElement) section).GetElementsByTagName(HandlerElement); + + foreach (XmlElement handler in resourceHandlers) + { + string protocolName = GetRequiredAttributeValue(handler, ProtocolAttribute, section); + string typeName = GetRequiredAttributeValue(handler, TypeAttribute, section); + + ResourceHandlerRegistry.RegisterResourceHandler(protocolName, typeName); + } + } + return null; + } + + private static string GetRequiredAttributeValue(XmlElement resourceElement, string requiredAttributeName, XmlNode section) + { + XmlAttribute attribute = resourceElement.GetAttributeNode(requiredAttributeName); + if (attribute == null) + { + string errorMessage = + string.Format(CultureInfo.InvariantCulture, "The '{0}' attribute is required for the resource handler definition.", + requiredAttributeName); + throw ConfigurationUtils.CreateConfigurationException(errorMessage, section); + } + return attribute.Value; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/ResourceSetMessageSource.cs b/src/Spring/Spring.Core/Context/Support/ResourceSetMessageSource.cs new file mode 100644 index 00000000..ac705f94 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/ResourceSetMessageSource.cs @@ -0,0 +1,238 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.Globalization; +using System.Resources; +using System.Text; + +using Spring.Objects.Factory; +using Spring.Core.TypeConversion; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// An implementation that + /// accesses resources from .resx / .resource files. + /// + /// Note that for the method + /// GetResourceObject if the resource name resolves to null, then in + /// .NET 1.1 the return value will be String.Empty whereas + /// in .NET 2.0 it will return null. + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// Aleksandar Seovic (.NET) + /// $Id: ResourceSetMessageSource.cs,v 1.23 2007/07/31 18:15:50 bbaia Exp $ + public class ResourceSetMessageSource : AbstractMessageSource, IInitializingObject + { + #region Fields + + private Hashtable _cachedResources; + private IList _resourceManagers; + + #endregion + + /// + /// Creates a new instance of the + /// class. + /// + public ResourceSetMessageSource() + { + _cachedResources = new Hashtable(); + _resourceManagers = new ArrayList(); + } + + /// + /// The collection of s + /// in this . + /// + public IList ResourceManagers + { + get { return _resourceManagers; } + set { _resourceManagers = value; } + } + + /// + /// Resolves a given code by searching through each assembly name in + /// the base names array. + /// + /// The code to resolve. + /// + /// The to use for lookups. + /// + /// The message from the resource set. + protected override string ResolveMessage( + string code, CultureInfo cultureInfo) + { + string message = null; + for (int i = 0; message == null & i < _resourceManagers.Count; i++) + { + message = ResolveObject((ResourceManager) _resourceManagers[i], code, cultureInfo) as string; + } + return message; + } + + /// + /// Resolves a given code by searching through each assembly name in the array. + /// + /// The code to resolve. + /// + /// The to use for lookups. + /// + /// The object from the resource set. + protected override object ResolveObject(string code, CultureInfo cultureInfo) + { + object obj = null; + + for (int i = 0; obj == null & i < _resourceManagers.Count; i++) + { + obj = ResolveObject((ResourceManager) _resourceManagers[i], code, cultureInfo); + } + return obj; + } + + // *** NOTE Don't use cref for ComponentResourceManager as it doesn't + // exist on 1.0 + // + + /// + /// Uses a System.ComponentModel.ComponentResourceManager + /// to apply resources to object properties. + /// Resource key names are of the form objectName.propertyName + /// + /// + /// This feature is not currently supported on version 1.0 of the .NET platform. + /// + /// + /// An object that contains the property values to be applied. + /// + /// + /// The base name of the object to use for the key lookup. + /// + /// + /// The to use for lookups. + /// If , uses the + /// value. + /// + /// + /// This feature is not currently supported on version 1.0 of the .NET platform. + /// + protected override void ApplyResourcesToObject(object value, string objectName, CultureInfo culture) + { +#if !NET_1_0 + if(value != null) + { + ComponentResourceManager crm = new ComponentResourceManager(value.GetType()); + crm.ApplyResources(value, objectName, culture); + } +#else + throw new NotSupportedException("Operation not supported in .NET 1.0 Release."); +#endif + } + + /// + /// Resolves a code into an object given a base name. + /// + /// The to search. + /// The code to resolve. + /// + /// The to use for lookups. + /// + /// The object from the resource file. + protected object ResolveObject(ResourceManager resourceManager, string code, CultureInfo cultureInfo) + { + string cacheKey = code + "." + cultureInfo.Name; + object resource = _cachedResources[cacheKey]; + + if (resource == null) + { + resource = resourceManager.GetObject(code, cultureInfo); + if (resource != null) + { + lock (_cachedResources) + { + _cachedResources[cacheKey] = resource; + } + } + } + + return resource; + } + + /// + /// Returns a representation of the + /// . + /// + /// A representation of the + /// . + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append(GetType().Name); + sb.Append(" with ResourceManagers of base names = ["); + foreach (ResourceManager rm in ResourceManagers) + { + sb.Append(rm.BaseName); + sb.Append(","); + } + string s = sb.ToString(); + return s.TrimEnd(new char[] {','}) + "]"; + } + + /// + /// Invoked by an + /// after it has set all object properties supplied. + /// + /// + ///

    + /// The list may contain objects of type or + /// . types + /// are converted to instances using the notation + /// resourcename, assembly partial name. + ///

    + ///
    + /// + /// If the conversion from a to a + /// can't be performed. + /// + public void AfterPropertiesSet() + { + ResourceManagerConverter cvt = new ResourceManagerConverter(); + for (int i = 0; i < _resourceManagers.Count; i++) + { + object o = _resourceManagers[i]; + if (o is String) + { + _resourceManagers[i] = cvt.ConvertFrom((string) o); + } + else if (!(o is ResourceManager)) + { + throw new ArgumentException("Only Types of string and ResourceManager are allowed. Type " + o.GetType() + " was set instead."); + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/StaticApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/StaticApplicationContext.cs new file mode 100644 index 00000000..53cea4f0 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/StaticApplicationContext.cs @@ -0,0 +1,102 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using Spring.Objects; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// that allows concrete registration of + /// objects and messages in code, rather than from external configuration sources. + /// + /// + ///

    + /// Mainly useful for testing. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: StaticApplicationContext.cs,v 1.9 2007/07/10 05:48:03 markpollack Exp $ + public class StaticApplicationContext : GenericApplicationContext + { + /// + /// Creates a new instance of the StaticApplicationContext class. + /// + public StaticApplicationContext() : this( null ) {} + + /// + /// Creates a new instance of the StaticApplicationContext class. + /// + /// The parent application context. + public StaticApplicationContext( IApplicationContext parentContext ) + : base( null, true, parentContext ) + { + RegisterSingleton( MessageSourceObjectName, typeof( StaticMessageSource ), null ); + } + + /// + /// Do nothing: we rely on callers to update our public methods. + /// + protected override void RefreshObjectFactory() {} + + /// + /// Register a singleton object with the default object factory. + /// + /// The name of the object. + /// The of the object. + /// The property values for the singleton instance. + public void RegisterSingleton( string name, Type classType, MutablePropertyValues propertyValues ) + { + DefaultListableObjectFactory.RegisterObjectDefinition( name, new RootObjectDefinition( classType, propertyValues ) ); + } + + /// + /// Registers a prototype object with the default object factory. + /// + /// The name of the prototype object. + /// The of the prototype object. + /// The property values for the prototype instance. + public void RegisterPrototype( string name, Type classType, MutablePropertyValues propertyValues ) + { + DefaultListableObjectFactory.RegisterObjectDefinition(name, new RootObjectDefinition(classType, propertyValues, false)); + } + + /// + /// Associate the given message with the given code. + /// + /// The lookup code. + /// + /// The that the message should be found within. + /// + /// The message associated with the lookup code. + public void AddMessage( string code, CultureInfo cultureInfo, string defaultMessage ) + { + StaticMessageSource messageSource = (StaticMessageSource) GetObject( MessageSourceObjectName ); + messageSource.AddMessage( code, cultureInfo, defaultMessage ); + } + } +} diff --git a/src/Spring/Spring.Core/Context/Support/StaticMessageSource.cs b/src/Spring/Spring.Core/Context/Support/StaticMessageSource.cs new file mode 100644 index 00000000..d56380b8 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/StaticMessageSource.cs @@ -0,0 +1,202 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.ComponentModel; +using System.Globalization; +using System.Text; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Simple implementation of + /// that allows messages to be held in an object and added programmatically. + /// + /// + ///

    + /// Mainly useful for testing. + ///

    + ///

    + /// This supports internationalization. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// + /// $Id: StaticMessageSource.cs,v 1.16 2007/08/27 13:57:27 oakinger Exp $ + public class StaticMessageSource : AbstractMessageSource + { + private Hashtable _messages; + private Hashtable _objects; + + /// + /// Creates a new instance of the + /// class. + /// + public StaticMessageSource() + { + _messages = new Hashtable(); + _objects = new Hashtable(); + } + + /// + /// Returns a format string. + /// + /// The code of the message to resolve. + /// + /// The to resolve the + /// code for. + /// + /// + /// A format string or if not found. + /// + /// + protected override string ResolveMessage(string code, CultureInfo cultureInfo) + { + return (string) _messages[GetLookupKey(code, cultureInfo)]; + } + + /// + /// Resolves an object (typically an icon or bitmap). + /// + /// The code of the object to resolve. + /// + /// The to resolve the + /// code for. + /// + /// + /// The resolved object or if not found. + /// + /// + protected override object ResolveObject(string code, CultureInfo cultureInfo) + { + return _objects[GetLookupKey(code, cultureInfo)]; + } + + + // *** NOTE Don't use cref for ComponentResourceManager as it doesn't + // exist on 1.0 + // + + /// + /// Applies resources to object properties. + /// + /// + ///

    + /// Uses a System.ComponentModel.ComponentResourceManager + /// internally to apply resources to object properties. Resource key + /// names are of the form objectName.propertyName. + ///

    + ///

    + /// This feature is not currently supported on version 1.0 of the .NET platform. + ///

    + ///
    + /// + /// An object that contains the property values to be applied. + /// + /// + /// The base name of the object to use for key lookup. + /// + /// + /// The with which the + /// resource is associated. + /// + /// + /// This feature is not currently supported on version 1.0 of the .NET platform. + /// + /// + protected override void ApplyResourcesToObject(object value, string objectName, CultureInfo cultureInfo) + { +#if !NET_1_0 + if(value != null) + { + new ComponentResourceManager(value.GetType()).ApplyResources(value, objectName, cultureInfo); + } +#else + throw new System.NotSupportedException("Operation not supported in .NET 1.0 Release."); +#endif + } + + /// + /// Associate the supplied with the + /// supplied . + /// + /// The lookup code. + /// + /// The to resolve the + /// code for. + /// + /// + /// The message format associated with this lookup code. + /// + public void AddMessage( + string code, CultureInfo culture, string messageFormat) + { + _messages.Add(GetLookupKey(code, culture), messageFormat); + } + + /// + /// Associate the supplied with the + /// supplied . + /// + /// The lookup code. + /// + /// The to resolve the + /// code for. + /// + /// + /// The object associated with this lookup code. + /// + public void AddObject(string code, CultureInfo cultureInfo, object value) + { + _objects.Add(GetLookupKey(code, cultureInfo), value); + } + + /// + /// Returns a representation of this + /// message source. + /// + /// + /// A containing all of this message + /// source's messages. + /// + public override string ToString() + { + StringBuilder output = new StringBuilder(); + output.Append(GetType().Name); + output.Append(" : "); + foreach (string code in _messages.Keys) + { + output.AppendFormat("['{0}' : '{1}']", code, _messages[code]); + } + return output.ToString(); + } + + private static string GetLookupKey(string code, CultureInfo culture) + { + return new StringBuilder(code).Append("_").Append(culture).ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/TypeAliasesSectionHandler.cs b/src/Spring/Spring.Core/Context/Support/TypeAliasesSectionHandler.cs new file mode 100644 index 00000000..9ed6e7da --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/TypeAliasesSectionHandler.cs @@ -0,0 +1,130 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Configuration; +using System.Globalization; +using System.Xml; + +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Configuration section handler for the Spring.NET typeAliases + /// config section. + /// + /// + ///

    + /// Type aliases can be used instead of fully qualified type names anywhere + /// a type name is expected in a Spring.NET configuration file. + ///

    + ///

    + /// This includes type names specified within an object definition, as well + /// as values of the properties or constructor arguments that expect + /// instances. + ///

    + ///
    + /// + ///

    + /// The following example shows how to configure both this section handler and + /// how to define type aliases within a Spring.NET config section: + ///

    + /// + /// + /// + /// + ///
    + /// + /// + /// + /// + /// + /// + /// ... + /// + /// ... + /// + /// + /// + /// + /// Aleksandar Seovic + /// $Id: TypeAliasesSectionHandler.cs,v 1.8 2007/07/31 18:15:50 bbaia Exp $ + /// + public class TypeAliasesSectionHandler : IConfigurationSectionHandler + { + private const string AliasElementName = "alias"; + private const string NameAttributeName = "name"; + private const string TypeAttributeName = "type"; + + /// + /// Populates using values specified in + /// the typeAliases config section. + /// + /// + /// The configuration settings in a corresponding parent + /// configuration section. + /// + /// + /// The configuration context when called from the ASP.NET + /// configuration system. Otherwise, this parameter is reserved and + /// is . + /// + /// + /// The for the section. + /// + /// + /// This method always returns , because the + /// is populated as a side-effect of this + /// object's execution and thus there is no need to return anything. + /// + public object Create(object parent, object configContext, XmlNode section) + { + if (section != null) + { + XmlNodeList aliases = ((XmlElement) section).GetElementsByTagName(AliasElementName); + foreach (XmlElement aliasElement in aliases) + { + string alias = GetRequiredAttributeValue(aliasElement, NameAttributeName, section); + string typeName = GetRequiredAttributeValue(aliasElement, TypeAttributeName, section); + TypeRegistry.RegisterType(alias, typeName); + } + } + return null; + } + + private static string GetRequiredAttributeValue( + XmlElement aliasElement, string requiredAttributeName, XmlNode section) + { + XmlAttribute attribute = aliasElement.GetAttributeNode(requiredAttributeName); + if (attribute == null) + { + string errorMessage = string.Format(CultureInfo.InvariantCulture, + "The '{0}' attribute is required for the (type) element.", requiredAttributeName); + throw ConfigurationUtils.CreateConfigurationException(errorMessage, section); + } + return attribute.Value; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/TypeConvertersSectionHandler.cs b/src/Spring/Spring.Core/Context/Support/TypeConvertersSectionHandler.cs new file mode 100644 index 00000000..941d7f87 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/TypeConvertersSectionHandler.cs @@ -0,0 +1,148 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.ComponentModel; +using System.Configuration; +using System.Globalization; +using System.Xml; + +using Spring.Core.TypeConversion; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Configuration section handler for the Spring.NET typeConverters + /// config section. + /// + /// + ///

    + /// Type converters are used to convert objects from one type into another + /// when injecting property values, evaluating expressions, performing data + /// binding, etc. + ///

    + ///

    + /// They are a very powerful mechanism as they allow Spring.NET to automatically + /// convert string-based property values from the configuration file into the appropriate + /// type based on the target property's type or to convert string values submitted + /// via a web form into a type that is used by your data model when Spring.NET data + /// binding is used. Because they offer such tremendous help, you should always provide + /// a type converter implementation for your custom types that you want to be able to use + /// for injected properties or for data binding. + ///

    + ///

    + /// The standard .NET mechanism for specifying type converter for a particular type is + /// to decorate the type with a , passing the type + /// of the -derived class as a parameter. + ///

    + ///

    + /// This mechanism will still work and is a preferred way of defining type converters if + /// you control the source code for the type that you want to define a converter for. However, + /// this configuration section allows you to specify converters for the types that you don't + /// control and it also allows you to override some of the standard type converters, such as + /// the ones that are defined for some of the types in the .NET Base Class Library. + ///

    + ///
    + /// + ///

    + /// The following example shows how to configure both this section handler and + /// how to define type converters within a Spring.NET config section: + ///

    + /// + /// + /// + /// + ///
    + /// + /// + /// + /// + /// + /// + /// ... + /// + /// ... + /// + /// + /// + /// + /// Aleksandar Seovic + /// $Id: TypeConvertersSectionHandler.cs,v 1.6 2007/07/31 18:15:50 bbaia Exp $ + /// + public class TypeConvertersSectionHandler : IConfigurationSectionHandler + { + private const string ConverterElementName = "converter"; + private const string ForAttributeName = "for"; + private const string TypeAttributeName = "type"; + + /// + /// Populates using values specified in + /// the typeConverters config section. + /// + /// + /// The configuration settings in a corresponding parent + /// configuration section. + /// + /// + /// The configuration context when called from the ASP.NET + /// configuration system. Otherwise, this parameter is reserved and + /// is . + /// + /// + /// The for the section. + /// + /// + /// This method always returns , because the + /// is populated as a side-effect of + /// its execution and thus there is no need to return anything. + /// + public object Create(object parent, object configContext, XmlNode section) + { + if (section != null) + { + XmlNodeList converters = ((XmlElement) section).GetElementsByTagName(ConverterElementName); + foreach (XmlElement aliasElement in converters) + { + string forType = GetRequiredAttributeValue(aliasElement, ForAttributeName, section); + string converterType = GetRequiredAttributeValue(aliasElement, TypeAttributeName, section); + TypeConverterRegistry.RegisterConverter(forType, converterType); + } + } + return null; + } + + private static string GetRequiredAttributeValue( + XmlElement aliasElement, string requiredAttributeName, XmlNode section) + { + XmlAttribute attribute = aliasElement.GetAttributeNode(requiredAttributeName); + if (attribute == null) + { + string errorMessage = string.Format(CultureInfo.InvariantCulture, + "The '{0}' attribute is required for the element.", requiredAttributeName); + throw ConfigurationUtils.CreateConfigurationException(errorMessage, section); + } + return attribute.Value; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/XmlApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/XmlApplicationContext.cs new file mode 100644 index 00000000..7dd00abc --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/XmlApplicationContext.cs @@ -0,0 +1,235 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Context.Support +{ + /// + /// An implementation that + /// reads context definitions from XML based resources. + /// + /// + ///

    + /// Currently, the resources that are supported are the file, + /// http, ftp, config and assembly resource + /// types. + ///

    + ///

    + /// You can provide custom implementations of the + /// interface and and register them + /// with any that inherits + /// from the + /// + /// interface. + ///

    + /// + /// In case of multiple config locations, later object definitions will + /// override ones defined in previously loaded resources. This can be + /// leveraged to deliberately override certain object definitions via an + /// extra XML file. + /// + ///
    + /// + ///

    + /// Find below some examples of instantiating an + /// using a + /// variety of different XML resources. + ///

    + /// + /// // an XmlApplicationContext that reads its object definitions from an + /// // XML file that has been embedded in an assembly... + /// IApplicationContext context = new XmlApplicationContext + /// ( + /// "assembly://AssemblyName/NameSpace/ResourceName" + /// ); + /// + /// // an XmlApplicationContext that reads its object definitions from a + /// // number of disparate XML resources... + /// IApplicationContext context = new XmlApplicationContext + /// ( + /// // from an XML file that has been embedded in an assembly... + /// "assembly://AssemblyName/NameSpace/ResourceName", + /// // and from a (relative) filesystem-based resource... + /// "file://Objects/services.xml", + /// // and from an App.config / Web.config resource... + /// "config://spring/objects" + /// ); + /// + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: XmlApplicationContext.cs,v 1.18 2007/08/08 17:46:37 bbaia Exp $ + /// + /// + /// + public class XmlApplicationContext : AbstractXmlApplicationContext + { + private string[] _configurationLocations; + + /// + /// Creates a new instance of the + /// class, + /// loading the definitions from the supplied XML resource locations. + /// + /// The created context will be case sensitive. + /// + /// Any number of XML based object definition resource locations. + /// + public XmlApplicationContext(params string[] configurationLocations) + : this(true, null, true, null, configurationLocations) + { } + + /// + /// Creates a new instance of the + /// class, + /// loading the definitions from the supplied XML resource locations. + /// + /// Flag specifying whether to make this context case sensitive or not. + /// + /// Any number of XML based object definition resource locations. + /// + public XmlApplicationContext(bool caseSensitive, + params string[] configurationLocations) + : this(true, null, caseSensitive, null, configurationLocations) + { } + + /// + /// Creates a new instance of the + /// class, + /// loading the definitions from the supplied XML resource locations. + /// + /// The application context name. + /// Flag specifying whether to make this context case sensitive or not. + /// + /// Any number of XML based object definition resource locations. + /// + public XmlApplicationContext(string name, bool caseSensitive, + params string[] configurationLocations) + : this(true, name, caseSensitive, null, configurationLocations) + { } + + /// + /// Creates a new instance of the + /// class, + /// loading the definitions from the supplied XML resource locations, + /// with the given . + /// + /// + /// The parent context (may be ). + /// + /// + /// Any number of XML based object definition resource locations. + /// + public XmlApplicationContext( + IApplicationContext parentContext, + params string[] configurationLocations) + : this(true, null, true, parentContext, configurationLocations) + { } + + /// + /// Creates a new instance of the + /// class, + /// loading the definitions from the supplied XML resource locations, + /// with the given . + /// + /// Flag specifying whether to make this context case sensitive or not. + /// + /// The parent context (may be ). + /// + /// + /// Any number of XML based object definition resource locations. + /// + public XmlApplicationContext( + bool caseSensitive, + IApplicationContext parentContext, + params string[] configurationLocations) + : this(true, null, caseSensitive, parentContext, configurationLocations) + { } + + /// + /// Creates a new instance of the + /// class, + /// loading the definitions from the supplied XML resource locations, + /// with the given . + /// + /// The application context name. + /// Flag specifying whether to make this context case sensitive or not. + /// + /// The parent context (may be ). + /// + /// + /// Any number of XML based object definition resource locations. + /// + public XmlApplicationContext( + string name, + bool caseSensitive, + IApplicationContext parentContext, + params string[] configurationLocations) + : this(true, name, caseSensitive, parentContext, configurationLocations) + {} + + /// + /// Creates a new instance of the + /// class, + /// loading the definitions from the supplied XML resource locations, + /// with the given . + /// + /// + /// This constructor is meant to be used by derived classes. By passing =false, it is + /// the responsibility of the deriving class to call to initialize the context instance. + /// + /// if true, is called automatically. + /// The application context name. + /// Flag specifying whether to make this context case sensitive or not. + /// + /// The parent context (may be ). + /// + /// + /// Any number of XML based object definition resource locations. + /// + public XmlApplicationContext( + bool refresh, + string name, + bool caseSensitive, + IApplicationContext parentContext, + params string[] configurationLocations) + : base(name, caseSensitive, parentContext ) + { + _configurationLocations = configurationLocations; + if (refresh) + { + Refresh(); + } + } + + /// + /// An array of resource locations, referring to the XML object + /// definition files that this context is to be built with. + /// + /// + /// An array of resource locations, or if none. + /// + /// + protected override string[] ConfigurationLocations + { + get { return _configurationLocations; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/CannotLoadObjectTypeException.cs b/src/Spring/Spring.Core/Core/CannotLoadObjectTypeException.cs new file mode 100644 index 00000000..99d9a92a --- /dev/null +++ b/src/Spring/Spring.Core/Core/CannotLoadObjectTypeException.cs @@ -0,0 +1,180 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; +using Spring.Util; + +#endregion + +namespace Spring.Core +{ + /// + /// Exception thrown when the ObjectFactory cannot load the specified type of a given object. + /// + /// Mark Pollack + /// $Id: CannotLoadObjectTypeException.cs,v 1.4 2007/08/01 23:24:11 bbaia Exp $ + [Serializable] + public class CannotLoadObjectTypeException : FatalReflectionException + { + #region Constructor (s) / Destructor + + /// + /// Initializes a new instance of the class. + /// + public CannotLoadObjectTypeException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public CannotLoadObjectTypeException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public CannotLoadObjectTypeException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + + /// + /// Initializes a new instance of the class. + /// + /// The resource description that the object definition came from. + /// Name of the object requested + /// Name of the object type. + /// The root cause. + public CannotLoadObjectTypeException(string resourceDescription, string objectName, string objectTypeName, Exception rootCause ) + : base("Cannot resolve type [" + objectTypeName + "] for object with name '" + objectName + "' defined in " + resourceDescription, rootCause) + { + this.resourceDescription = resourceDescription; + this.objectName = objectName; + this.objectTypeName = objectTypeName; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected CannotLoadObjectTypeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + + resourceDescription = info.GetString("ResourceDescription"); + objectName = info.GetString("ObjectName"); + objectTypeName = info.GetString("ObjectTypeName"); + } + + + #endregion + + #region Properties + + /// + /// Gets he name of the object we are trying to load. + /// + /// The name of the object. + public string ObjectName + { + get { return objectName; } + } + + /// + /// Gets the name of the object type we are trying to load. + /// + /// The name of the object type. + public string ObjectTypeName + { + get { return objectTypeName; } + } + + /// + /// Gets the resource description that the object definition came from + /// + /// The resource description. + public string ResourceDescription + { + get { return resourceDescription; } + } + + #endregion + + #region Methods + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("ResourceDescription", ResourceDescription); + info.AddValue("ObjectName", ObjectName); + info.AddValue("ObjectTypeName", ObjectTypeName); + } + + #endregion + + #region Fields + + private string resourceDescription; + private string objectName; + private string objectTypeName; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/ComposedCriteria.cs b/src/Spring/Spring.Core/Core/ComposedCriteria.cs new file mode 100644 index 00000000..30f1d6d1 --- /dev/null +++ b/src/Spring/Spring.Core/Core/ComposedCriteria.cs @@ -0,0 +1,116 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using Spring.Core; + +#endregion + +namespace Spring.Core +{ + /// + /// A implementation that represents + /// a composed collection of instances. + /// + public class ComposedCriteria : ICriteria + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public ComposedCriteria() : this(null) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A user-defined (child) criteria that will be composed into this instance. + /// + public ComposedCriteria(ICriteria criteria) + { + _criteria = new ArrayList(); + Add(criteria); + } + + #endregion + + #region Methods + + /// + /// Does the supplied satisfy the criteria encapsulated by + /// this instance? + /// + /// The data to be checked by this criteria instance. + /// + /// True if the supplied satisfies the criteria encapsulated + /// by this instance; false if not or the supplied is null. + /// + public virtual bool IsSatisfied(object datum) + { + foreach (ICriteria criteria in _criteria) + { + if (!criteria.IsSatisfied(datum)) + { + return false; + } + } + return true; + } + + /// + /// Adds the supplied into the criteria + /// composed within this instance. + /// + /// + /// The to be added. + /// + public void Add(ICriteria criteria) + { + if (criteria != null) + { + _criteria.Add(criteria); + } + } + + #endregion + + /// + /// The list of composing this + /// instance. + /// + protected IList Criteria + { + get { return _criteria; } + } + + #region Fields + + private IList _criteria; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/ControlFlowFactory.cs b/src/Spring/Spring.Core/Core/ControlFlowFactory.cs new file mode 100644 index 00000000..34af262f --- /dev/null +++ b/src/Spring/Spring.Core/Core/ControlFlowFactory.cs @@ -0,0 +1,151 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Diagnostics; +using System.Reflection; + +#endregion + +namespace Spring.Core +{ + /// + /// Factory class to conceal any default implementation. + /// + /// Rod Johnson + /// Simon White (.NET) + /// $Id: ControlFlowFactory.cs,v 1.9 2007/07/31 01:35:07 markpollack Exp $ + public abstract class ControlFlowFactory + { + /// + /// Creates a new instance of the + /// implementation provided by this factory. + /// + /// + /// A new instance of the + /// implementation provided by this factory. + /// + public static IControlFlow CreateControlFlow() + { + return new DefaultControlFlow(); + } + + private class DefaultControlFlow : IControlFlow + { + private StackTrace _stackTrace; + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public DefaultControlFlow() + { + _stackTrace = new StackTrace(); + } + + #endregion + + #region IControlFlow Members + + /// + /// Detects whether the caller is under the supplied , + /// according to the current stacktrace. + /// + /// + bool IControlFlow.Under(Type type) + { + return IsMatch(new MethodsDeclaredTypeCriteria(type)); + } + + /// + /// Detects whether the caller is under the supplied + /// and , according to the current stacktrace. + /// + /// + ///

    + /// Matches the whole method name. + ///

    + ///
    + /// + bool IControlFlow.Under(Type type, string methodName) + { + ComposedCriteria criteria = new ComposedCriteria(); + criteria.Add(new MethodsDeclaredTypeCriteria(type)); + criteria.Add(new RegularExpressionMethodNameCriteria(methodName)); + return IsMatch(criteria); + } + + /// + /// Does the current stack trace contain the supplied ? + /// + /// + ///

    + /// This leaves it up to the caller to decide what matches, but is obviously less of + /// an abstraction because the caller must know the exact format of the underlying + /// stack trace. + ///

    + ///
    + /// + bool IControlFlow.UnderToken(string token) + { + return _stackTrace.ToString().IndexOf(token) != -1; + } + + private bool IsMatch(ICriteria criteria) + { + for (int i = 0; i < _stackTrace.FrameCount; i++) + { + MethodBase method = _stackTrace.GetFrame(i).GetMethod(); + if(criteria.IsSatisfied(method)) + { + return true; + } + } + return false; + } + + private sealed class MethodsDeclaredTypeCriteria : ICriteria + { + public MethodsDeclaredTypeCriteria(Type typeToMatch) + { + _typeToMatch = typeToMatch; + } + + public bool IsSatisfied(object datum) + { + MethodBase method = datum as MethodBase; + if(method != null && method.DeclaringType != null) + { + return method.DeclaringType.Equals(_typeToMatch); + } + return false; + } + + private Type _typeToMatch; + } + + #endregion + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/Conventions.cs b/src/Spring/Spring.Core/Core/Conventions.cs new file mode 100644 index 00000000..a983fbd8 --- /dev/null +++ b/src/Spring/Spring.Core/Core/Conventions.cs @@ -0,0 +1,71 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Util; + +namespace Spring.Core +{ + /// + /// Provides methods to support various naming and other conventions used throughout the framework. + /// Mainly for internal use within the framework. + /// + /// Rob Harrop + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: Conventions.cs,v 1.1 2007/05/26 00:42:36 markpollack Exp $ + public sealed class Conventions + { + /// Convert Strings in attribute name format (lowercase, hyphens separating words) + /// into property name format (camel-cased). For example, transaction-manager is + /// converted into transactionManager. + /// + public static string AttributeNameToPropertyName(string attributeName) + { + AssertUtils.ArgumentNotNull(attributeName, "attributeName"); + if (attributeName.IndexOf("-") == -1) + { + return attributeName; + } + char[] chars = attributeName.ToCharArray(); + char[] result = new char[chars.Length - 1]; // not completely accurate but good guess + int currPos = 0; + bool upperCaseNext = false; + for (int i = 0; i < chars.Length; i++) + { + char c = chars[i]; + if (c == '-') + { + upperCaseNext = true; + } + else if (upperCaseNext) + { + result[currPos++] = Char.ToUpper(c); + upperCaseNext = false; + } + else + { + result[currPos++] = c; + } + } + return new String(result, 0, currPos); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/CriteriaMemberFilter.cs b/src/Spring/Spring.Core/Core/CriteriaMemberFilter.cs new file mode 100644 index 00000000..a7b57e4c --- /dev/null +++ b/src/Spring/Spring.Core/Core/CriteriaMemberFilter.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Reflection; + +#endregion + +namespace Spring.Core +{ + /// + /// Convenience class that exposes a signature that matches the + /// delegate. + /// + /// + ///

    + /// Useful when filtering members via the + /// mechanism. + ///

    + ///
    + /// Rick Evans + /// $Id: CriteriaMemberFilter.cs,v 1.1 2007/07/31 21:43:52 markpollack Exp $ + public class CriteriaMemberFilter + { + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the + /// class. + /// + public CriteriaMemberFilter () + { + } + #endregion + + #region Methods + /// + /// Returns true if the supplied instance + /// satisfies the supplied (which must be an + /// implementation). + /// + /// + /// The instance that will be checked to see if + /// it matches the supplied . + /// + /// + /// The criteria against which to filter the supplied + /// instance. + /// + /// + /// True if the supplied instance + /// satisfies the supplied (which must be an + /// implementation); false if not or the + /// supplied is not an + /// implementation or is null. + /// + public virtual bool FilterMemberByCriteria (MemberInfo member, object filterCriteria) + { + ICriteria criteria = filterCriteria as ICriteria; + return criteria.IsSatisfied (member); + } + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IControlFlow.cs b/src/Spring/Spring.Core/Core/IControlFlow.cs new file mode 100644 index 00000000..f1b464f2 --- /dev/null +++ b/src/Spring/Spring.Core/Core/IControlFlow.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Core +{ + /// + /// Interface to be implemented by objects that can return information about + /// the current call stack. + /// + /// + ///

    + /// Useful in AOP (as an expression of the AspectJ cflow concept) but not AOP-specific. + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.Net) + /// $Id: IControlFlow.cs,v 1.4 2006/04/09 07:18:38 markpollack Exp $ + public interface IControlFlow + { + /// + /// Detects whether the caller is under the supplied , + /// according to the current stacktrace. + /// + /// + /// The to look for. + /// + /// + /// if the caller is under the supplied . + /// + bool Under(Type type); + + /// + /// Detects whether the caller is under the supplied + /// and , according to the current stacktrace. + /// + /// + /// The to look for. + /// + /// The name of the method to look for. + /// + /// if the caller is under the supplied + /// and . + /// + bool Under(Type type, string methodName); + + /// + /// Does the current stack trace contain the supplied ? + /// + /// The token to match against. + /// + /// if the current stack trace contains the supplied + /// . + /// + bool UnderToken(string token); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/ICriteria.cs b/src/Spring/Spring.Core/Core/ICriteria.cs new file mode 100644 index 00000000..81bdb297 --- /dev/null +++ b/src/Spring/Spring.Core/Core/ICriteria.cs @@ -0,0 +1,51 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +namespace Spring.Core +{ + /// + /// The criteria for an arbitrary filter. + /// + /// Rick Evans + /// $Id: ICriteria.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ + public interface ICriteria + { + /// + /// Does the supplied satisfy the criteria + /// encapsulated by this instance? + /// + /// + /// The datum to be checked by this criteria instance. + /// + /// + /// if the supplied + /// satisfies the criteria encapsulated by this instance; + /// if not, or the supplied + /// is . + /// + bool IsSatisfied (object datum); + } +} diff --git a/src/Spring/Spring.Core/Core/IErrorCoded.cs b/src/Spring/Spring.Core/Core/IErrorCoded.cs new file mode 100644 index 00000000..611c1dc0 --- /dev/null +++ b/src/Spring/Spring.Core/Core/IErrorCoded.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +namespace Spring.Core +{ + /// + /// Interface that can be implemented by exceptions etc that are error coded. + /// + /// + ///

    + /// The error code is a , rather than a number, so it can + /// be given user-readable values, such as "object.failureDescription". + ///

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.Net) + /// $Id: IErrorCoded.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ + public interface IErrorCoded + { + /// + /// Return the error code associated with this failure. + /// + /// + ///

    + /// The GUI can render this anyway it pleases, allowing for I18n etc. + ///

    + ///
    + /// + /// The error code associated with this failure, + /// or the empty string instance if not error-coded. + /// + string ErrorCode + { + get; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IO/AbstractResource.cs b/src/Spring/Spring.Core/Core/IO/AbstractResource.cs new file mode 100644 index 00000000..a5afc244 --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/AbstractResource.cs @@ -0,0 +1,764 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Text; +using Spring.Util; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Convenience base class for + /// implementations, pre-implementing typical behavior. + /// + /// + ///

    + /// The method will + /// check whether a or + /// can be opened; + /// will always return + /// ; + /// and + /// throw an exception; + /// and will + /// return the value of the + /// property. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// Aleksandar Seovic (.NET) + /// $Id: AbstractResource.cs,v 1.25 2007/12/06 22:08:47 markpollack Exp $ + /// + public abstract class AbstractResource : IResource + { + /// + /// The default special character that denotes the base (home, or root) + /// path. + /// + /// + ///

    + /// Will be resolved (by those + /// implementations that support it) to the home (or root) path for + /// the specific implementation. + ///

    + ///

    + /// For example, in the case of a web application this will (probably) + /// resolve to the virtual directory of said web application. + ///

    + ///
    + protected const string DefaultBasePathPlaceHolder = "~"; + + private string protocol; + private string basePathPlaceHolder = DefaultBasePathPlaceHolder; + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + protected AbstractResource() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + /// + /// A string representation of the resource. + /// + /// + /// If the supplied is + /// or contains only whitespace character(s). + /// + protected AbstractResource(string resourceName) + { + AssertUtils.ArgumentHasText(resourceName, "resourceName"); + protocol = ConfigurableResourceLoader.GetProtocol(resourceName); + } + + #endregion + + #region Properties + + /// + /// The special character that denotes the base (home, or root) + /// path. + /// + /// + ///

    + /// Will be resolved (by those + /// implementations that support it) to the home (or root) path for + /// the specific implementation. + ///

    + ///

    + /// For example, in the case of a web application this will (probably) + /// resolve to the virtual directory of said web application. + ///

    + ///
    + /// + public string BasePathPlaceHolder + { + get { return basePathPlaceHolder; } + set { basePathPlaceHolder = value; } + } + + /// + /// Return an for this resource. + /// + /// + /// An . + /// + /// + /// If the stream could not be opened. + /// + /// + public abstract Stream InputStream { get; } + + /// + /// Returns a description for this resource. + /// + /// + /// A description for this resource. + /// + /// + public abstract string Description { get; } + + /// + /// Returns the protocol associated with this resource (if any). + /// + /// + ///

    + /// The value of this property may be if no + /// protocol is associated with the resource type (for example if the + /// resource is a memory stream). + ///

    + ///
    + /// + /// The protocol associated with this resource (if any). + /// + public string Protocol + { + get { return protocol; } + } + + /// + /// Does this resource represent a handle with an open stream? + /// + /// + ///

    + /// This, the default implementation, always returns + /// . + ///

    + ///
    + /// + /// if this resource represents a handle with an + /// open stream. + /// + /// + public virtual bool IsOpen + { + get { return false; } + } + + /// + /// Returns the handle for this resource. + /// + /// + ///

    + /// This, the default implementation, always throws a + /// , assuming that the + /// resource cannot be exposed as a . + ///

    + ///
    + /// + /// The handle for this resource. + /// + /// + /// This, the default implementation, always throws a + /// . + /// + /// + public virtual Uri Uri + { + get + { + throw new FileNotFoundException( + Description + " cannot be resolved to a Uri."); + } + } + + /// + /// Returns a handle for this resource. + /// + /// + ///

    + /// This, the default implementation, always throws a + /// , assuming that the + /// resource cannot be resolved to an absolute file path. + ///

    + ///
    + /// + /// The handle for this resource. + /// + /// + /// This implementation always throws a + /// . + /// + /// + /// + public virtual FileInfo File + { + get + { + throw new FileNotFoundException( + Description + " cannot be resolved to an absolute file path."); + } + } + + /// + /// Does this resource actually exist in physical form? + /// + /// + ///

    + /// This implementation checks whether a + /// can be opened, falling back to whether a + /// can be opened. + ///

    + ///

    + /// This will cover both directories and content resources. + ///

    + ///

    + /// This implementation will also return if + /// permission to the (file's) path is denied. + ///

    + ///
    + /// + /// if this resource actually exists in physical + /// form (for example on a filesystem). + /// + /// + /// + public virtual bool Exists + { + get + { + try + { + return File.Exists; + } + catch (IOException) + { + try + { + Stream inputStream = InputStream; + inputStream.Close(); + return true; + } catch (Exception) + { + return false; + } + } + } + } + + #endregion + + #region Methods + + /// + /// Strips any protocol name from the supplied + /// . + /// + /// + ///

    + /// If the supplied does not + /// have any protocol associated with it, then the supplied + /// will be returned as-is. + ///

    + ///
    + /// + /// + /// GetResourceNameWithoutProtocol("http://www.mycompany.com/resource.txt"); + /// // returns www.mycompany.com/resource.txt + /// + /// + /// + /// The name of the resource. + /// + /// + /// The name of the resource without the protocol name. + /// + protected static string GetResourceNameWithoutProtocol(string resourceName) + { + int pos = resourceName.IndexOf( + ConfigurableResourceLoader.ProtocolSeparator); + if (pos == -1) + { + return resourceName; + } + else + { + return resourceName.Substring(pos + ConfigurableResourceLoader.ProtocolSeparator.Length); + } + } + + /// + /// Resolves the supplied to its value + /// sans any leading protocol. + /// + /// + /// The name of the resource. + /// + /// + /// The name of the resource without the protocol name. + /// + /// + protected virtual string ResolveResourceNameWithoutProtocol(string resourceName) + { + return ResolveBasePathPlaceHolder( + GetResourceNameWithoutProtocol(resourceName), BasePathPlaceHolder); + } + + /// + /// Resolves the presence of the + /// value + /// in the supplied into a path. + /// + /// + ///

    + /// The default implementation simply returns the supplied + /// as is. + ///

    + ///
    + /// + /// The name of the resource. + /// + /// + /// The string that is a placeholder for a base path. + /// + /// + /// The name of the resource with any + /// value having been resolved into an actual path. + /// + protected virtual string ResolveBasePathPlaceHolder( + string resourceName, string basePathPlaceHolder) + { + return resourceName; + } + + /// + /// This implementation returns the + /// of this resource. + /// + /// + public override string ToString() + { + return Description; + } + + /// + /// Determines whether the specified is + /// equal to the current . + /// + /// + ///

    + /// This implementation compares values. + ///

    + ///
    + /// + public override bool Equals(object obj) + { + return obj is IResource + && ((IResource)obj).Description.Equals(Description); + } + + /// + /// Serves as a hash function for a particular type, suitable for use + /// in hashing algorithms and data structures like a hash table. + /// + /// + ///

    + /// This implementation returns the hashcode of the + /// property. + ///

    + ///
    + /// + public override int GetHashCode() + { + return Description.GetHashCode(); + } + + #endregion + + #region Relative Resource Support + + /// + /// Factory Method. Create a new instance of the current resource type using the given resourceName + /// + protected virtual IResource CreateResourceInstance( string resourceName ) + { + return null; + } + + /// + /// The ResourceLoader to be used for resolving relative resources + /// + protected virtual IResourceLoader GetResourceLoader() + { + return new ConfigurableResourceLoader(); + } + + /// + /// Does this support relative + /// resource retrieval? + /// + /// + ///

    + /// This property is generally to be consulted prior to attempting + /// to attempting to access a resource that is relative to this + /// resource (via a call to + /// ). + ///

    + ///

    + /// This, the default implementation, always returns + /// . + ///

    + ///
    + /// + /// if this + /// supports relative resource + /// retrieval. + /// + protected virtual bool SupportsRelativeResources + { + get { return false; } + } + + /// + /// Gets the root location of the resource. + /// + /// + ///

    + /// Where root resource can be taken to mean that part of the resource + /// descriptor that doesn't change when a relative resource is looked + /// up. Examples of such a root location would include a drive letter, + /// a web server name, an assembly name, etc. + ///

    + ///
    + /// + /// The root location of the resource. + /// + /// + /// This, the default implementation, always throws a + /// . + /// + protected virtual string RootLocation + { + get { throw new NotSupportedException(); } + } + + /// + /// Gets the current path of the resource. + /// + /// + ///

    + /// An example value of this property would be the name of the + /// directory containing a filesystem based resource. + ///

    + ///
    + /// + /// The current path of the resource. + /// + /// + /// This, the default implementation, always throws a + /// . + /// + protected virtual string ResourcePath + { + get { throw new NotSupportedException(); } + } + + /// + /// Gets those characters that are valid path separators for the + /// resource type. + /// + /// + ///

    + /// An example value of this property would be the + /// and + /// values for a + /// filesystem based resource. + ///

    + ///

    + /// Any derived classes that override this method are expected to + /// return a new array for each access of this property. + ///

    + ///
    + /// + /// Those characters that are valid path separators for the resource + /// type. + /// + /// + /// This, the default implementation, always throws a + /// . + /// + protected virtual char[] PathSeparatorChars + { + get { throw new NotSupportedException(); } + } + + /// + /// Does the supplied relative ? + /// + /// + /// The name of the resource to test. + /// + /// + /// if resource name is relative; + /// otherwise . + /// + protected virtual bool IsRelativeResource(string resourceName) + { + return false; + } + + /// + /// Creates a new resource that is relative to this resource based on the + /// supplied . + /// + /// + ///

    + /// This method can accept either a fully qualified resource name or a + /// relative resource name as it's parameter. + ///

    + ///

    + /// A fully qualified resource is one that has a protocol prefix and + /// all elements of the resource name. All other resources are treated + /// as relative to this resource, and the following rules are used to + /// locate a relative resource: + ///

    + /// + /// + /// If the starts with '..', + /// the current resource path is navigated backwards before the + /// is concatenated to the current + /// of + /// this resource. + /// + /// + /// If the starts with '/', the + /// current resource path is ignored and a new resource name is + /// appended to the + /// of + /// this resource. + /// + /// + /// If the starts with '.' or a + /// letter, a new path is appended to the current + /// of + /// this resource. + /// + /// + ///
    + /// + /// The name of the resource to create. + /// + /// The relative resource. + /// + /// If the process of resolving the relative resource yielded an + /// invalid URI. + /// + /// + /// If this resource does not support the resolution of relative + /// resources (as determined by the value of the + /// + /// property). + /// + /// + public virtual IResource CreateRelative(string resourceName) + { + AssertUtils.ArgumentNotNull(resourceName, "relativePath"); + + // try to create fully qualified resource... + IResourceLoader loader = GetResourceLoader(); + + if (ConfigurableResourceLoader.HasProtocol(resourceName)) + { + IResource resource = loader.GetResource(resourceName); + if (resource != null) + { + return resource; + } + } + if (!SupportsRelativeResources) + { + throw new NotSupportedException(GetType().Name + + " does not support relative resources. Please use fully qualified resource name."); + } + + StringBuilder fullResourceName = new StringBuilder(256); + if (Protocol != null && Protocol != String.Empty) + { + fullResourceName.Append(Protocol).Append(ConfigurableResourceLoader.ProtocolSeparator); + } + + if (!IsRelativeResource(resourceName)) + { + fullResourceName.Append(resourceName); + } + else + { + string targetResource; + string resourcePath; + int n = resourceName.LastIndexOfAny(new char[] { '/', '\\' }); + if (n >= 0) + { + targetResource = resourceName.Substring(n + 1); + resourcePath = CalculateResourcePath(resourceName.Substring(0, n + 1)); + } + else // only resource name is specified, so current path should be used + { + targetResource = resourceName; + resourcePath = ResourcePath; + } + + fullResourceName.Append(RootLocation.TrimEnd('\\','/')); + if (resourcePath != null && resourcePath != String.Empty) + { + fullResourceName.Append('/').Append(resourcePath); + } + fullResourceName.Append('/').Append(targetResource); + } + + string resultResourceName = fullResourceName.ToString(); + + if (!ConfigurableResourceLoader.HasProtocol( resultResourceName )) + { + // give derived resource classes a chance to create an instance on their own + IResource resultResource = CreateResourceInstance( resultResourceName ); + if (resultResource != null) return resultResource; + } + + // create resource instance using default loader + return loader.GetResource(resultResourceName); + } + + /// + /// Calculates a new resource path based on the supplied + /// . + /// + /// + /// The relative path to evaluate. + /// + /// The newly calculated resource path. + private string CalculateResourcePath(string relativePath ) + { + StringBuilder path = new StringBuilder(256); + if (relativePath.StartsWith("..")) // back level navigation + { + string[] pathElements = ResourcePath.Split(PathSeparatorChars); + int upWalks = UpWalks(relativePath); + if (upWalks > pathElements.Length) + { + throw new UriFormatException("Too many back levels."); + } + char separator = PathSeparatorChars[0]; + for (int i = 0; i < pathElements.Length - upWalks; i++) + { + path.Append(pathElements[i]).Append(separator); + } + string[] relativeParts = relativePath.Split('/', '\\'); + for (int i = upWalks; i < relativeParts.Length - 1; i++) + { + path.Append(relativeParts[i]).Append(separator); + } + if (path.Length > 0) + { + path.Length -= 1; + } + return path.ToString(); + } + else if (relativePath.StartsWith("/")) // relative to root + { + if (relativePath.Length > 1) + { + return relativePath.Substring(1, relativePath.Length - 2); + } + else + { + return null; + } + } + else // relative to current namespace... + { + if (ResourcePath != null && ResourcePath != String.Empty) + { + path.Append(ResourcePath.TrimEnd(PathSeparatorChars)).Append(PathSeparatorChars[0]); + } + if (relativePath.StartsWith("./")) + { + if (relativePath.Length > 2) + { + path.Append(relativePath.Substring(2, relativePath.Length - 3)); + } + } + else + { + path.Append(relativePath.Substring(0, relativePath.Length - 1)); + } + + return path.ToString(); + } + } + + private int UpWalks(string path) + { + string[] parts = path.Split('/', '\\'); + int count = 0; + for (; count < parts.Length && parts[count].Equals(".."); count++) + { + ; + } + return count; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IO/AssemblyResource.cs b/src/Spring/Spring.Core/Core/IO/AssemblyResource.cs new file mode 100644 index 00000000..d10283be --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/AssemblyResource.cs @@ -0,0 +1,272 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.IO; +using System.Reflection; +using Common.Logging; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// An implementation for + /// resources stored within assemblies. + /// + /// + ///

    + /// This implementation expects any resource name passed to the + /// constructor to adhere to the following format: + ///

    + ///

    + /// assembly://assemblyName/namespace/resourceName + ///

    + ///
    + /// Aleksandar Seovic (.NET) + /// Federico Spinazzi (.NET) + /// $Id: AssemblyResource.cs,v 1.16 2007/08/08 17:46:55 bbaia Exp $ + public class AssemblyResource : AbstractResource + { + #region Fields + + private Assembly _assembly; + private string[] _resources; + private string _resourceName; + private string _fullResourceName; + private string _resourceNamespace; + private string _resourceAssemblyName; + private static readonly ILog log = LogManager.GetLogger(typeof(AssemblyResource)); + + #endregion + + #region Constructors + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The name of the assembly resource. + /// + /// + /// If the supplied did not conform + /// to the expected format. + /// + /// + /// If the assembly specified in the supplied + /// was loaded twice with two + /// different evidences. + /// + /// + /// If the assembly specified in the supplied + /// could not be found. + /// + /// + /// If the caller does not have the required permission to load + /// the assembly specified in the supplied + /// . + /// + /// + public AssemblyResource(string resourceName) : base(resourceName) + { + string[] info = GetResourceNameWithoutProtocol(resourceName).Split('/'); + if (info.Length != 3) + { + throw new UriFormatException( + "Invalid resource name. Name has to be in " + + "'assembly://' format."); + } +#if NET_2_0 + this._assembly = Assembly.Load(info[0]); +#else + this._assembly = Assembly.LoadWithPartialName(info[0]); +#endif + if (this._assembly == null) + { + throw new FileNotFoundException("Unable to load assembly [" + info[0] + "]"); + } + this._fullResourceName = resourceName; + this._resourceAssemblyName = info[0]; + this._resourceNamespace = info[1]; + this._resourceName = String.Format("{0}.{1}", info[1], info[2]); + } + + #endregion + + #region Properties + + /// + /// Return an for this resource. + /// + /// + /// An . + /// + /// + /// If the stream could not be opened. + /// + /// + /// If the caller does not have the required permission to load + /// the underlying assembly's manifest. + /// + /// + /// + public override Stream InputStream + { + get + { + Stream stream = _assembly.GetManifestResourceStream(_resourceName); + if (stream == null) + { + log.Error("Could not load resource with name = [" + _resourceName + + "] from assembly + " + _assembly); + log.Error("URI specified = [" + this._fullResourceName + "] Spring.NET URI syntax is 'assembly://assemblyName/namespace/resourceName'."); + log.Error("Resource name often has the default namespace prefixed, e.g. 'assembly://MyAssembly/MyNamespace/MyNamespace.MyResource.txt'."); + } + return stream; + } + } + + /// + /// Does the embedded resource specified in the value passed to the + /// constructor exist? + /// + /// + /// if this resource actually exists in physical + /// form (for example on a filesystem). + /// + /// + /// + /// + public override bool Exists + { + get + { + if (_resources == null) + { + _resources = _assembly.GetManifestResourceNames(); + Array.Sort(_resources); + } + return (Array.BinarySearch(_resources, _resourceName) >= 0); + } + } + + /// + /// Does this support relative + /// resource retrieval? + /// + /// + ///

    + /// This implementation does support relative resource retrieval, and + /// so will always return . + ///

    + ///
    + /// + /// if this + /// supports relative resource + /// retrieval. + /// + /// + protected override bool SupportsRelativeResources + { + get { return true; } + } + + /// + /// Gets the root location of the resource (the assembly name in this + /// case). + /// + /// + /// The root location of the resource. + /// + /// + protected override string RootLocation + { + get { return _resourceAssemblyName; } + } + + /// + /// Gets the current path of the resource (the namespace in which the + /// target resource was embedded in this case). + /// + /// + /// The current path of the resource. + /// + /// + protected override string ResourcePath + { + get { return _resourceNamespace; } + } + + /// + /// Gets those characters that are valid path separators for the + /// resource type. + /// + /// + /// Those characters that are valid path separators for the resource + /// type. + /// + /// + protected override char[] PathSeparatorChars + { + get { return new char[] {'.'}; } + } + + /// + /// Returns a description for this resource. + /// + /// + /// A description for this resource. + /// + /// + public override string Description + { + get + { + return string.Format( + CultureInfo.InvariantCulture, + "assembly [{0}], resource [{1}]", _assembly.FullName, _resourceName); + } + } + + #endregion + + /// + /// Does the supplied relative ? + /// + /// + /// The name of the resource to test. + /// + /// + /// if resource name is relative; + /// otherwise . + /// + protected override bool IsRelativeResource(string resourceName) + { + return (resourceName.StartsWith("./") || + resourceName.StartsWith("/") || + resourceName.StartsWith("../") || + resourceName.Split('/').Length != 3); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IO/ConfigSectionResource.cs b/src/Spring/Spring.Core/Core/IO/ConfigSectionResource.cs new file mode 100644 index 00000000..4fd3bd7f --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/ConfigSectionResource.cs @@ -0,0 +1,204 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Text; +using System.Xml; + +using Spring.Util; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Used when retrieving information from the standard .NET configuration + /// files (App.config / Web.config). + /// + /// + ///

    + /// If created with the name of a configuration section, then all methods + /// aside from the description return , + /// , or throw an exception. If created with an + /// , then the + /// property + /// will return a corresponding to parse. + ///

    + ///
    + /// Mark Pollack + /// Rick Evans + /// $Id: ConfigSectionResource.cs,v 1.27 2007/12/05 00:57:31 bbaia Exp $ + public class ConfigSectionResource : AbstractResource + { + private XmlElement configElement; + private string sectionName; + + #region Constructor (s) / Destructor + + /// + /// Creates new instance of the + /// class. + /// + /// + /// The actual XML configuration section. + /// + /// + /// If the supplied is . + /// + public ConfigSectionResource(XmlElement configSection) + { + AssertUtils.ArgumentNotNull(configSection, "configSection"); + sectionName = configSection.Name; + configElement = configSection; + } + + /// + /// Creates new instance of the + /// class. + /// + /// + /// The name of the configuration section. + /// + /// + /// If the supplied is + /// or contains only whitespace character(s). + /// + public ConfigSectionResource(string resourceName) : base(resourceName) + { + AssertUtils.ArgumentHasText(resourceName, "resourceName"); + sectionName = GetResourceNameWithoutProtocol(resourceName); + configElement = (XmlElement) ConfigurationUtils.GetSection(sectionName); + } + + #endregion + + #region IResource Members + + /// + /// Returns the handle for this resource. + /// + /// + ///

    + /// This implementation always returns . + ///

    + ///
    + /// + /// . + /// + /// + public override Uri Uri + { + get { return null; } + } + + /// + /// Returns a handle for this resource. + /// + /// + ///

    + /// This implementation always returns . + ///

    + ///
    + /// + /// . + /// + /// + public override FileInfo File + { + get { return null; } + } + + /// + /// Returns a description for this resource (the name of the + /// configuration section in this case). + /// + /// + /// A description for this resource. + /// + /// + public override string Description + { + get + { + return StringUtils.Surround("config [", sectionName, "]"); + } + } + + /// + /// Does this resource actually exist in physical form? + /// + /// + ///

    + /// This implementation always returns . + ///

    + ///
    + /// + /// + /// + /// + /// + public override bool Exists + { + get { return false; } + } + + #endregion + + #region IInputStreamSource Members + + /// + /// Return an for this resource. + /// + /// + /// An . + /// + /// + /// If the stream could not be opened. + /// + /// + public override Stream InputStream + { + get { return new MemoryStream(Encoding.UTF8.GetBytes(configElement.OuterXml)); } + } + + #endregion + + #region Properties + + /// + /// Exposes the actual for the + /// configuration section. + /// + /// + ///

    + /// Introduced to accomodate line info tracking during parsing. + ///

    + ///
    + internal XmlElement ConfigElement + { + get { return configElement; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IO/ConfigurableResourceLoader.cs b/src/Spring/Spring.Core/Core/IO/ConfigurableResourceLoader.cs new file mode 100644 index 00000000..b577fe1e --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/ConfigurableResourceLoader.cs @@ -0,0 +1,197 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; +using Spring.Reflection.Dynamic; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Configurable implementation of the + /// interface. + /// + /// + ///

    + /// This implementation + /// supports the configuration of resource access protocols and the + /// corresponding .NET types that know how to handle those protocols. + ///

    + ///

    + /// Basic protocol-to-resource type mappings are also defined by this class, + /// while others can be added either internally, by application contexts + /// extending this class, or externally, by the end user configuring the + /// context. + ///

    + ///

    + /// Only one resource type can be defined for each protocol, but multiple + /// protocols can map to the same resource type (for example, the + /// "http" and "ftp" protocols both map to the + /// type. The protocols that are + /// mapped by default can be found in the following list. + ///

    + ///

    + /// + /// + /// assembly + /// + /// + /// config + /// + /// + /// file + /// + /// + /// http + /// + /// + /// https + /// + /// + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ConfigurableResourceLoader.cs,v 1.25 2007/08/08 17:46:55 bbaia Exp $ + /// + /// + /// + public class ConfigurableResourceLoader : IResourceLoader + { + /// + /// The separator between the protocol name and the resource name. + /// + public const string ProtocolSeparator = "://"; + + /// + /// Creates a new instance of the + /// class. + /// + public ConfigurableResourceLoader() + { } + + /// + /// Creates a new instance of the + /// class using the specified default protocol for unqualified resources. + /// + public ConfigurableResourceLoader(string defaultProtocol) + { + AssertUtils.ArgumentNotNull(defaultProtocol, "defaultProtocol"); + this.defaultProtocol = defaultProtocol; + } + + /// + /// The default protocol to use for unqualified resources. + /// + /// + ///

    + /// The initial value is "file". + ///

    + ///
    + public string DefaultResourceProtocol + { + get { return defaultProtocol; } + set { defaultProtocol = value; } + } + + /// + /// Returns a that has been + /// mapped to the protocol of the supplied . + /// + /// The name of the resource. + /// + /// A new instance for the + /// supplied . + /// + /// + /// If a + /// mapping does not exist for the supplied . + /// + /// + /// In the case of any errors arising from the instantiation of the + /// returned instance. + /// + /// + public IResource GetResource(string resourceName) + { + string protocol = GetProtocol(resourceName); + if (protocol == null) + { + protocol = DefaultResourceProtocol; + resourceName = protocol + ProtocolSeparator + resourceName; + } + + IDynamicConstructor handler = ResourceHandlerRegistry.GetResourceHandler(protocol); + if (handler == null) + { + throw new UriFormatException("Resource handler for the '" + protocol + "' protocol is not defined."); + } + + return (IResource) handler.Invoke(new object[] { resourceName }); + } + + /// + /// Checks that the supplied starts + /// with one of the protocol names currently mapped by this + /// instance. + /// + /// The name of the resource. + /// + /// if the supplied + /// starts with one of the known + /// protocols; if not, or if the supplied + /// is itself . + /// + public static bool HasProtocol(string resourceName) + { + string protocol = GetProtocol(resourceName); + return protocol != null && ResourceHandlerRegistry.IsHandlerRegistered(protocol); + } + + /// + /// Extracts the protocol name from the supplied + /// . + /// + /// The name of the resource. + /// + /// The extracted protocol name or if the + /// supplied is unqualified (or + /// is itself ). + /// + internal static string GetProtocol(string resourceName) + { + if (resourceName == null) + { + return null; + } + int pos = resourceName.IndexOf(ProtocolSeparator); + return pos == -1 ? null : resourceName.Substring(0, pos); + } + + #region Fields + + private string defaultProtocol = "file"; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IO/FileSystemResource.cs b/src/Spring/Spring.Core/Core/IO/FileSystemResource.cs new file mode 100644 index 00000000..4d96f082 --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/FileSystemResource.cs @@ -0,0 +1,394 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.IO; +using Spring.Util; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// A backed resource. + /// + /// + ///

    + /// Supports resolution as both a and a + /// . + ///

    + ///

    + /// Also supports the use of the ~ character. If the ~ character + /// is the first character in a resource path (sans protocol), the ~ + /// character will be replaced with the value of the + /// System.AppDomain.CurrentDomain.BaseDirectory property (an example of + /// this can be seen in the examples below). + ///

    + ///
    + /// + ///

    + /// Consider the example of an application that is running (has been launched + /// from) the C:\App\ directory. The following resource paths will map + /// to the following resources on the filesystem... + ///

    + /// + /// strings.txt C:\App\strings.txt + /// ~/strings.txt C:\App\strings.txt + /// file://~/strings.txt C:\App\strings.txt + /// file://~/../strings.txt C:\strings.txt + /// ../strings.txt C:\strings.txt + /// ~/../strings.txt C:\strings.txt + /// + /// // note that only a leading ~ character is resolved to the executing directory... + /// stri~ngs.txt C:\App\stri~ngs.txt + /// + ///
    + /// Juergen Hoeller + /// Leonardo Susatyo (.NET) + /// Aleksandar Seovic (.NET) + /// $Id: FileSystemResource.cs,v 1.23 2007/08/08 17:46:55 bbaia Exp $ + public class FileSystemResource : AbstractResource + { + private FileInfo fileHandle; + private string rootLocation; + private string resourcePath; + + #region Constructors + + /// + /// Creates a new instance of the + /// class. + /// + protected FileSystemResource() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The name of the file system resource. + /// + /// + /// If the supplied is + /// or contains only whitespace character(s). + /// + public FileSystemResource(string resourceName) + : this(resourceName, false) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The name of the file system resource. + /// + /// + /// Supresses initialization of this instance. Used from derived classes. + /// + /// + /// If the supplied is + /// or contains only whitespace character(s). + /// + protected FileSystemResource(string resourceName, bool suppressInitialize) + : base(resourceName) + { + if (!suppressInitialize) + { + Initialize( resourceName ); + } + } + + #endregion + + #region Properties + + /// + /// Returns the underlying handle for + /// this resource. + /// + /// + /// The handle for this resource. + /// + /// + public override FileInfo File + { + get { return fileHandle; } + } + + /// + /// Does this support relative + /// resource retrieval? + /// + /// + ///

    + /// This implementation does support relative resource retrieval, and + /// so will always return . + ///

    + ///
    + /// + /// if this + /// supports relative resource + /// retrieval. + /// + /// + protected override bool SupportsRelativeResources + { + get { return true; } + } + + /// + /// Gets the root location of the resource (a drive or UNC file share + /// name in this case). + /// + /// + /// The root location of the resource. + /// + /// + protected override string RootLocation + { + get { return this.rootLocation; } + } + + /// + /// Gets the current path of the resource. + /// + /// + /// The current path of the resource. + /// + /// + protected override string ResourcePath + { + get { return this.resourcePath; } + } + + /// + /// Gets those characters that are valid path separators for the + /// resource type. + /// + /// + /// Those characters that are valid path separators for the resource + /// type. + /// + /// + /// + protected override char[] PathSeparatorChars + { + get { return new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; } + } + + /// + /// Return an for this resource. + /// + /// + /// An . + /// + /// + /// If the stream could not be opened. + /// + /// + /// If the underlying file could not be found. + /// + /// + public override Stream InputStream + { + get + { + if (Uri.IsFile) + { + try + { + return new FileStream(Uri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + } + catch(DirectoryNotFoundException) + { + // ignore difference between File & Directory exception in this case + } + } + throw new FileNotFoundException(Description + + " cannot be resolved to local file path" + + " - resource does not use 'file:' protocol."); + } + } + + /// + /// Returns a description for this resource. + /// + /// + /// A description for this resource. + /// + /// + public override string Description + { + get + { + return string.Format( + CultureInfo.InvariantCulture, + "file [{0}]", fileHandle.FullName); + } + } + + /// + /// Returns the handle for this resource. + /// + /// + /// The handle for this resource. + /// + /// + /// If the resource is not available or cannot be exposed as a + /// . + /// + /// + public override Uri Uri + { + get { return new UriBuilder("file", null, 0, fileHandle.FullName).Uri; } + } + + #endregion + + /// + /// Initializes this instance. + /// + /// + protected void Initialize( string resourceName ) + { + this.fileHandle = ResolveFileHandle(resourceName); + this.rootLocation = ResolveRootLocation(resourceName); + this.resourcePath = ResolveResourcePath(resourceName); + } + + /// + /// Resolves the handle + /// for the supplied . + /// + /// + /// The name of the file system resource. + /// + /// + /// The handle for this resource. + /// + protected virtual FileInfo ResolveFileHandle(string resourceName) + { + return new FileInfo(ResolveResourceNameWithoutProtocol(resourceName)); + } + + /// + /// Resolves the root location for the supplied . + /// + /// + /// The name of the file system resource. + /// + /// + /// The root location of the resource. + /// + protected virtual string ResolveRootLocation(string resourceName) + { + string root = this.fileHandle.Directory.Root.ToString(); + if (root.Length > 0 && + (root.EndsWith(Path.DirectorySeparatorChar.ToString()) || + root.EndsWith(Path.AltDirectorySeparatorChar.ToString()))) + { + root = root.Substring(0, root.Length - 1); + } + + return root; + } + + /// + /// Resolves the path for the supplied . + /// + /// + /// The name of the file system resource. + /// + /// + /// The current path of the resource. + /// + protected virtual string ResolveResourcePath(string resourceName) + { + string path = this.fileHandle.DirectoryName; + if (path.Equals(this.fileHandle.Directory.Root.ToString())) + { + path = null; + } + else + { + path = path.Substring(this.rootLocation.Length + 1); + } + + return path; + } + + /// + /// Resolves the presence of the + /// value + /// in the supplied into a path. + /// + /// + /// The name of the resource. + /// + /// + /// The string that is a placeholder for a base path. + /// + /// + /// The name of the resource with any + /// value having been resolved into an actual path. + /// + protected override string ResolveBasePathPlaceHolder( + string resourceName, string basePathPlaceHolder) + { + // Remove extra slashes used to indicate that resource is local (handle the case "/C:/path1/...") + if (resourceName[0] == '/' && resourceName[2] == ':') + { + resourceName = resourceName.Substring(1); + } + + if (StringUtils.HasText(resourceName) + && resourceName.TrimStart().StartsWith(basePathPlaceHolder)) + { + return resourceName.Replace(basePathPlaceHolder, AppDomain.CurrentDomain.BaseDirectory).TrimStart(); + } + return resourceName; + } + + /// + /// Does the supplied relative ? + /// + /// + /// The name of the resource to test. + /// + /// + /// if resource name is relative; + /// otherwise . + /// + protected override bool IsRelativeResource(string resourceName) + { + return (! + (resourceName.StartsWith(@"\\") || // UNC file share + resourceName.IndexOf(':') >= 0 || // drive + resourceName.StartsWith(BasePathPlaceHolder))); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IO/IInputStreamSource.cs b/src/Spring/Spring.Core/Core/IO/IInputStreamSource.cs new file mode 100644 index 00000000..64a9e5be --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/IInputStreamSource.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Simple interface for objects that are sources for + /// s. + /// + /// + ///

    + /// This is the base interface for the abstraction encapsulated by + /// Spring.NET's interface. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: IInputStreamSource.cs,v 1.10 2007/08/08 17:46:55 bbaia Exp $ + /// + public interface IInputStreamSource + { + /// + /// Return an for this resource. + /// + /// + /// + /// Clients of this interface must be aware that every access of this + /// property will create a fresh ; + /// it is the responsibility of the calling code to close any such + /// . + /// + /// + /// + /// An . + /// + /// + /// If the stream could not be opened. + /// + Stream InputStream + { + get; + } + } +} diff --git a/src/Spring/Spring.Core/Core/IO/IResource.cs b/src/Spring/Spring.Core/Core/IO/IResource.cs new file mode 100644 index 00000000..6c459191 --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/IResource.cs @@ -0,0 +1,195 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.IO; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// The central abstraction for Spring.NET's access to resources such as + /// s. + /// + /// + ///

    + /// This interface encapsulates a resource descriptor that abstracts away + /// from the underlying type of resource; possible resource types include + /// files, memory streams, and databases (this list is not exhaustive). + ///

    + ///

    + /// A can definitely be opened and accessed + /// for every such resource; if the resource exists in a physical form (for + /// example, the resource is not an in-memory stream or one that has been + /// extracted from an assembly or ZIP file), a or + /// can also be accessed. The actual + /// behavior is implementation-specific. + ///

    + ///

    + /// This interface, when used in tandem with the + /// interface, forms the backbone of + /// Spring.NET's resource handling. Third party extensions or libraries + /// that want to integrate external resources with Spring.NET's IoC + /// container are encouraged expose such resources via this abstraction. + ///

    + ///

    + /// Interfaces cannot obviously mandate implementation, but derived classes + /// are strongly encouraged to expose a constructor that takes a + /// single as it's sole argument (see example). + /// Exposing such a constructor will make your custom + /// implementation integrate nicely + /// with the class. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: IResource.cs,v 1.12 2007/08/08 17:46:55 bbaia Exp $ + /// + /// + [TypeConverter(typeof(ResourceConverter))] + public interface IResource : IInputStreamSource + { + /// + /// Does this resource represent a handle with an open stream? + /// + /// + ///

    + /// If , the + /// cannot be read multiple times, and must be read and then closed to + /// avoid resource leaks. + ///

    + ///

    + /// Will be for all usual resource descriptors. + ///

    + ///
    + /// + /// if this resource represents a handle with an + /// open stream. + /// + /// + bool IsOpen { get; } + + /// + /// Returns the handle for this resource. + /// + /// + ///

    + /// For safety, always check the value of the + /// property prior to + /// accessing this property; resources that cannot be exposed as + /// a will typically return + /// from a call to the + /// property. + ///

    + ///
    + /// + /// The handle for this resource. + /// + /// + /// If the resource is not available or cannot be exposed as a + /// . + /// + /// + /// + Uri Uri { get; } + + /// + /// Returns a handle for this resource. + /// + /// + ///

    + /// For safety, always check the value of the + /// property prior to + /// accessing this property; resources that cannot be exposed as + /// a will typically return + /// from a call to the + /// property. + ///

    + ///
    + /// + /// The handle for this resource. + /// + /// + /// If the resource is not available on a filesystem, or cannot be + /// exposed as a handle. + /// + /// + /// + FileInfo File { get; } + + /// + /// Returns a description for this resource. + /// + /// + ///

    + /// The description is typically used for diagnostics and other such + /// logging when working with the resource. + ///

    + ///

    + /// Implementations are also encouraged to return this value from their + /// method. + ///

    + ///
    + /// + /// A description for this resource. + /// + string Description { get; } + + /// + /// Does this resource actually exist in physical form? + /// + /// + ///

    + /// An example of a resource that physically exists would be a + /// file on a local filesystem. An example of a resource that does not + /// physically exist would be an in-memory stream. + ///

    + ///
    + /// + /// if this resource actually exists in physical + /// form (for example on a filesystem). + /// + /// + /// + bool Exists { get; } + + /// + /// Creates a resource relative to this resource. + /// + /// + /// The path (always resolved as relative to this resource). + /// + /// + /// The relative resource. + /// + /// + /// If the relative resource could not be created from the supplied + /// path. + /// + /// + /// If the resource does not support the notion of a relative path. + /// + IResource CreateRelative(string relativePath); + } +} diff --git a/src/Spring/Spring.Core/Core/IO/IResourceLoader.cs b/src/Spring/Spring.Core/Core/IO/IResourceLoader.cs new file mode 100644 index 00000000..8c7c0140 --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/IResourceLoader.cs @@ -0,0 +1,94 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Describes an object that can load + /// s. + /// + /// + ///

    + /// An implementation is + /// generally required to support the functionality described by this + /// interface. + ///

    + ///

    + /// The class is a + /// standalone implementation that is usable outside an + /// ; the aforementioned + /// class is also used by the + /// class. + ///

    + ///
    + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: IResourceLoader.cs,v 1.12 2007/08/08 17:46:55 bbaia Exp $ + /// + /// + /// + public interface IResourceLoader + { + /// + /// Return an handle for the + /// specified resource. + /// + /// + ///

    + /// The handle should always be a reusable resource descriptor; this + /// allows one to make repeated calls to the underlying + /// . + ///

    + ///

    + ///

      + ///
    • + /// Must support fully qualified URLs, e.g. "file:C:/test.dat". + ///
    • + ///
    • + /// Should support relative file paths, e.g. "test.dat" (this will be + /// implementation-specific, typically provided by an + /// implementation). + ///
    • + ///
    + ///

    + /// + /// An handle does not imply an + /// existing resource; you need to check the value of an + /// 's + /// property to determine + /// conclusively whether or not the resource actually exists. + /// + ///
    + /// The resource location. + /// + /// An appropriate handle. + /// + /// + /// + /// + IResource GetResource(string location); + } +} diff --git a/src/Spring/Spring.Core/Core/IO/InputStreamResource.cs b/src/Spring/Spring.Core/Core/IO/InputStreamResource.cs new file mode 100644 index 00000000..fc051b95 --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/InputStreamResource.cs @@ -0,0 +1,145 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using Spring.Util; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// adapter implementation for a + /// . + /// + /// + ///

    + /// Should only be used if no other + /// implementation is applicable. + ///

    + ///

    + /// In contrast to other + /// implementations, this is an adapter for an already opened + /// resource - the + /// therefore always returns . Do not use this class + /// if you need to keep the resource descriptor somewhere, or if you need + /// to read a stream multiple times. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: InputStreamResource.cs,v 1.10 2007/12/06 22:08:47 markpollack Exp $ + public class InputStreamResource : AbstractResource + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The input to use. + /// + /// + /// Where the input comes from. + /// + /// + /// If the supplied is + /// . + /// + public InputStreamResource(Stream inputStream, string description) + { + AssertUtils.ArgumentNotNull(inputStream, "inputStream"); + + _inputStream = inputStream; + _description = description == null ? string.Empty : description; + } + + #endregion + + #region Properties + + /// + /// The input to use. + /// + /// + /// If the underlying has already + /// been read. + /// + public override Stream InputStream + { + get + { + if (_inputStream == null) + { + throw new InvalidOperationException( + "InputStream has already been read - " + + "do not use InputStreamResource if a stream " + + "needs to be read multiple times"); + } + Stream result = _inputStream; + _inputStream = null; + return result; + } + } + + /// + /// Returns a description for this resource. + /// + /// + /// A description for this resource. + /// + /// + public override string Description + { + get { return _description; } + } + + /// + /// This implementation always returns true + /// + public override bool IsOpen + { + get { return true; } + } + + + + /// + /// This implemementation always returns true + /// + public override bool Exists + { + get { return true; } + } + + #endregion + + #region Fields + + private Stream _inputStream; + private string _description; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IO/ResourceConverter.cs b/src/Spring/Spring.Core/Core/IO/ResourceConverter.cs new file mode 100644 index 00000000..45a72155 --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/ResourceConverter.cs @@ -0,0 +1,215 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.Globalization; +using Common.Logging; +using Spring.Util; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Custom type converter for instances. + /// + /// + ///

    + /// A resource path may contain placeholder variables of the form ${...} + /// that will be expended to environment variables. + ///

    + ///

    + /// Currently only supports conversion from a + /// instance. + ///

    + ///
    + /// + ///

    + /// On Win9x boxes, this resource path, ${userprofile}\objects.xml will + /// be expanded at runtime with the value of the 'userprofile' environment + /// variable substituted for the '${userprofile}' portion of the path. + ///

    + /// + /// // assuming a user called Rick, running on a plain vanilla Windows XP setup... + /// // this resource path... + /// + /// ${userprofile}\objects.xml + /// + /// // will become (after expansion)... + /// + /// C:\Documents and Settings\Rick\objects.xml + /// + ///
    + /// Mark Pollack + /// $Id: ResourceConverter.cs,v 1.14 2007/08/08 17:46:55 bbaia Exp $ + /// + /// + public class ResourceConverter : TypeConverter + { + private ILog _log = LogManager.GetLogger(typeof (ResourceConverter)); + private IResourceLoader _resourceLoader; + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public ResourceConverter() + { + _resourceLoader = new ConfigurableResourceLoader(); + } + + /// + /// Creates a new instance of the + /// class using the specified resourceLoader. + /// + /// the underlying IResourceLoader to be used to resolve resources + public ResourceConverter( IResourceLoader resourceLoader ) + { + AssertUtils.ArgumentNotNull( resourceLoader, "resourceLoader" ); + _resourceLoader = resourceLoader; + } + #endregion + + /// + /// Returns whether this converter can convert an object of one + /// to a + /// + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// + /// if the conversion is possible. + /// + public override bool CanConvertFrom( + ITypeDescriptorContext context, + Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// Convert from a string value to a + /// instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// An if successful. + /// + /// + /// If the resource name objectained form the supplied + /// is malformed. + /// + /// + /// In the case of any errors arising from the instantiation of the + /// returned instance. + /// + public override object ConvertFrom( + ITypeDescriptorContext context, + CultureInfo culture, object value) + { + string resource = value as string; + if (resource != null) + { + return GetResourceLoader().GetResource(ResolvePath(resource)); + } + return base.ConvertFrom(context, culture, value); + } + + /// + /// Resolve the given path, replacing placeholder values with + /// corresponding property values if necessary. + /// + /// + ///

    + /// This implementation resolves environment variables only. + ///

    + ///
    + /// The original resource path. + /// The resolved resource path. + protected virtual string ResolvePath(string path) + { + // quite inefficient, but cost is only ever paid once at startup... + IList expressions = StringUtils.GetAntExpressions(path); + foreach (string expression in expressions) + { + string environmentValue + = Environment.GetEnvironmentVariable(expression); + if (environmentValue != null) + { + path = StringUtils.SetAntExpression( + path, expression, environmentValue); + } + else + { + #region Instrumentation + + if (_log.IsWarnEnabled) + { + _log.Warn(string.Format( + CultureInfo.InvariantCulture, + "Could not resolve placeholder '{0}' in resource path " + + "'{1}' as an environment variable.", expression, path)); + } + + #endregion + } + } + return path; + } + + /// + /// Return the used to + /// resolve the string. + /// + /// + /// The used to resolve + /// the string. + /// + protected internal virtual IResourceLoader GetResourceLoader() + { + return _resourceLoader; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IO/ResourceHandlerRegistry.cs b/src/Spring/Spring.Core/Core/IO/ResourceHandlerRegistry.cs new file mode 100644 index 00000000..053d6154 --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/ResourceHandlerRegistry.cs @@ -0,0 +1,264 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Reflection; + +using Spring.Context.Support; +using Spring.Core.TypeResolution; +using Spring.Util; +using Spring.Reflection.Dynamic; + +namespace Spring.Core.IO +{ + /// + /// Registry class that allows users to register and retrieve protocol handlers. + /// + /// + /// + /// Resource handler is an implementation of interface + /// that should be used to process resources with the specified protocol. + /// + /// + /// They are used throughout the framework to access resources from various + /// sources. For example, application context loads object definitions from the resources + /// that are processed using one of the registered resource handlers. + /// + /// Following resource handlers are registered by default: + /// + /// + /// Protocol + /// Handler Type + /// Description + /// + /// + /// config + /// + /// Resolves the resources by loading specified configuration section from the standard .NET config file. + /// + /// + /// file + /// + /// Resolves filesystem resources. + /// + /// + /// http + /// + /// Resolves remote web resources. + /// + /// + /// https + /// + /// Resolves remote web resources via HTTPS. + /// + /// + /// ftp + /// + /// Resolves ftp resources. + /// + /// + /// assembly + /// + /// Resolves resources that are embedded into an assembly. + /// + /// + /// web + /// Spring.Core.IO.WebResource, Spring.Web* + /// Resolves resources relative to the web application's virtual directory. + /// + /// + /// * only available in web applications. + /// + /// Users can create and register their own protocol handlers by implementing interface + /// and mapping custom protocol name to that implementation. See for details + /// on how to register custom protocol handler. + /// + /// + /// Aleksandar Seovic + /// $Id: ResourceHandlerRegistry.cs,v 1.7 2007/08/08 17:46:55 bbaia Exp $ + public class ResourceHandlerRegistry + { + /// + /// Name of the .Net config section that contains definitions + /// for custom resource handlers. + /// + private const string ResourcesSectionName = "spring/resourceHandlers"; + + private static IDictionary resourceHandlers = new Hashtable(); + + /// + /// Registers standard and user-configured resource handlers. + /// + static ResourceHandlerRegistry() + { + lock (resourceHandlers.SyncRoot) + { + resourceHandlers["config"] = GetResourceConstructor(typeof(ConfigSectionResource)); + resourceHandlers["file"] = GetResourceConstructor(typeof(FileSystemResource)); + resourceHandlers["http"] = GetResourceConstructor(typeof(UrlResource)); + resourceHandlers["https"] = GetResourceConstructor(typeof(UrlResource)); +#if NET_2_0 + resourceHandlers["ftp"] = GetResourceConstructor(typeof(UrlResource)); +#endif + resourceHandlers["assembly"] = GetResourceConstructor(typeof(AssemblyResource)); + + // register custom resource handlers + ConfigurationUtils.GetSection(ResourcesSectionName); + } + } + + /// + /// Returns resource handler for the specified protocol name. + /// + /// + /// + /// This method returns object that should be used + /// to create an instance of the -derived type by passing + /// resource location as a parameter. + /// + /// + /// Name of the protocol to get the handler for. + /// Resource handler constructor for the specified protocol name. + /// If is null. + public static IDynamicConstructor GetResourceHandler(string protocolName) + { + AssertUtils.ArgumentNotNull(protocolName, "protocolName"); + return (IDynamicConstructor) resourceHandlers[protocolName]; + } + + /// + /// Returns true if a handler is registered for the specified protocol, + /// false otherwise. + /// + /// Name of the protocol. + /// + /// true if a handler is registered for the specified protocol, false otherwise. + /// + /// If is null. + public static bool IsHandlerRegistered(string protocolName) + { + return resourceHandlers.Contains(protocolName); + } + + /// + /// Registers resource handler and maps it to the specified protocol name. + /// + /// + ///

    + /// If the mapping already exists, the existing mapping will be + /// silently overwritten with the new mapping. + ///

    + ///
    + /// + /// The protocol to add (or override). + /// + /// + /// The type name of the concrete implementation of the + /// interface that will handle + /// the specified protocol. + /// + /// + /// If the supplied is + /// or contains only whitespace character(s); or + /// if the supplied is + /// . + /// + /// + /// If the supplied is not a + /// that derives from the + /// interface; or (having passed + /// this first check), the supplied + /// does not expose a constructor that takes a single + /// parameter. + /// + public static void RegisterResourceHandler(string protocolName, string handlerTypeName) + { + AssertUtils.ArgumentHasText(protocolName, "protocolName"); + AssertUtils.ArgumentHasText(handlerTypeName, "handlerTypeName"); + + Type handlerType = TypeResolutionUtils.ResolveType(handlerTypeName); + RegisterResourceHandler(protocolName, handlerType); + } + + /// + /// Registers resource handler and maps it to the specified protocol name. + /// + /// + ///

    + /// If the mapping already exists, the existing mapping will be + /// silently overwritten with the new mapping. + ///

    + ///
    + /// + /// The protocol to add (or override). + /// + /// + /// The concrete implementation of the + /// interface that will handle + /// the specified protocol. + /// + /// + /// If the supplied is + /// or contains only whitespace character(s); or + /// if the supplied is + /// . + /// + /// + /// If the supplied is not a + /// that derives from the + /// interface; or (having passed + /// this first check), the supplied + /// does not expose a constructor that takes a single + /// parameter. + /// + public static void RegisterResourceHandler(string protocolName, Type handlerType) + { + #region Sanity Checks + + AssertUtils.ArgumentHasText(protocolName, "protocolName"); + AssertUtils.ArgumentNotNull(handlerType, "handlerType"); + if (!typeof(IResource).IsAssignableFrom(handlerType)) + { + throw new ArgumentException( + string.Format("[{0}] does not implement [{1}] interface (it must).", handlerType.FullName, typeof(IResource).FullName)); + } + + #endregion + + lock (resourceHandlers.SyncRoot) + { + IDynamicConstructor ctor = GetResourceConstructor(handlerType); + resourceHandlers[protocolName] = ctor; + } + } + + private static IDynamicConstructor GetResourceConstructor(Type handlerType) + { + ConstructorInfo ctor = handlerType.GetConstructor(new Type[] {typeof(string)}); + if (ctor == null) + { + throw new ArgumentException( + string.Format("[{0}] does not have a constructor that takes a single string as an argument (it must).", handlerType.FullName)); + } + return DynamicConstructor.Create(ctor); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IO/UrlResource.cs b/src/Spring/Spring.Core/Core/IO/UrlResource.cs new file mode 100644 index 00000000..fad9ae5d --- /dev/null +++ b/src/Spring/Spring.Core/Core/IO/UrlResource.cs @@ -0,0 +1,269 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Net; +using Spring.Util; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// A backed resource + /// on top of + /// + /// + ///

    + /// Obviously supports resolution as a , and also + /// as a in the case of the "file:" + /// protocol. + ///

    + ///
    + /// + ///

    + /// Some examples of the strings that can be used to initialize a new + /// instance of the class + /// include... + /// + /// + /// file:///Config/objects.xml + /// + /// + /// http://www.mycompany.com/services.txt + /// + /// + ///

    + ///
    + /// Juergen Hoeller + /// Leonardo Susatyo (.NET) + /// Aleksandar Seovic (.NET) + /// $Id: UrlResource.cs,v 1.15 2007/08/08 17:46:55 bbaia Exp $ + /// + /// + /// + public class UrlResource : AbstractResource + { + private Uri _uri; + private WebRequest _webRequest; + private string _rootLocation; + private string _resourcePath; + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// Some examples of the values that the + /// can typically be expected to hold include... + /// + /// + /// file:///Config/objects.xml + /// + /// + /// http://www.mycompany.com/services.txt + /// + /// + ///

    + ///
    + /// + /// A string representation of the resource. + /// + public UrlResource(string resourceName) : base(resourceName) + { + this._uri = new Uri(resourceName); + _rootLocation = _uri.Host; + if (!_uri.IsDefaultPort) + { + _rootLocation += ":" + _uri.Port; + } + _resourcePath = _uri.AbsolutePath; + int n = _resourcePath.LastIndexOf('/'); + if (n > 0) + { + _resourcePath = _resourcePath.Substring(1, n - 1); + } + else + { + _resourcePath = null; + } + _webRequest = WebRequest.Create(_uri); + } + + /// + /// Returns the instance + /// used for the resource resolution. + /// + /// + /// A instance. + /// + /// + /// + public WebRequest WebRequest + { + get { return _webRequest; } + } + + /// + /// Return an for this resource. + /// + /// + /// An . + /// + /// + /// If the stream could not be opened. + /// + /// + public override Stream InputStream + { + get { return _webRequest.GetResponse().GetResponseStream(); } + } + + /// + /// Returns the handle for this resource. + /// + /// + /// The handle for this resource. + /// + /// + /// If the resource is not available or cannot be exposed as a + /// . + /// + /// + public override Uri Uri + { + get { return _uri; } + } + + /// + /// Returns a handle for this resource. + /// + /// + /// The handle for this resource. + /// + /// + /// If the resource is not available on a filesystem. + /// + /// + public override FileInfo File + { + get + { + if (_uri.IsFile) + { + return new FileInfo(_uri.AbsolutePath); + } + throw new FileNotFoundException(Description + + " cannot be resolved to absolute file path - " + + "resource does not use 'file:' protocol." ); + } + } + + /// + /// Does this support relative + /// resource retrieval? + /// + /// + ///

    + /// This implementation does support relative resource retrieval, and + /// so will always return . + ///

    + ///
    + /// + /// if this + /// supports relative resource + /// retrieval. + /// + /// + protected override bool SupportsRelativeResources + { + get { return true; } + } + + /// + /// Gets the root location of the resource. + /// + /// + /// The root location of the resource. + /// + /// + protected override string RootLocation + { + get { return _rootLocation; } + } + + /// + /// Gets the current path of the resource. + /// + /// + /// The current path of the resource. + /// + /// + protected override string ResourcePath + { + get { return _resourcePath; } + } + + /// + /// Gets those characters that are valid path separators for the + /// resource type. + /// + /// + /// Those characters that are valid path separators for the resource + /// type. + /// + /// + protected override char[] PathSeparatorChars + { + get { return new char[] {'/'}; } + } + + /// + /// Returns a description for this resource. + /// + /// + /// A description for this resource. + /// + /// + public override string Description + { + get { return StringUtils.Surround("URL [", Uri, "]"); } + } + + /// + /// Does the supplied relative ? + /// + /// + /// The name of the resource to test. + /// + /// + /// if resource name is relative; + /// otherwise . + /// + protected override bool IsRelativeResource(string resourceName) + { + return true; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/IOrdered.cs b/src/Spring/Spring.Core/Core/IOrdered.cs new file mode 100644 index 00000000..b7dadb69 --- /dev/null +++ b/src/Spring/Spring.Core/Core/IOrdered.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +namespace Spring.Core +{ + /// + /// Interface that can be implemented by objects that should be orderable, e.g. in an + /// . + /// + /// + ///

    + /// The actual order can be interpreted as prioritization, the first object (with the + /// lowest order value) having the highest priority. + ///

    + ///
    + /// Juergen Hoeller + /// Aleksandar Seovic (.Net) + /// $Id: IOrdered.cs,v 1.6 2007/05/26 00:42:36 markpollack Exp $ + public interface IOrdered + { + + /// + /// Return the order value of this object, where a higher value means greater in + /// terms of sorting. + /// + /// + ///

    + /// Normally starting with 0 or 1, with indicating + /// greatest. Same order values will result in arbitrary positions for the affected + /// objects. + ///

    + ///

    + /// Higher value can be interpreted as lower priority, consequently the first object + /// has highest priority. + ///

    + ///
    + /// The order value. + int Order + { + get; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/InvalidPropertyException.cs b/src/Spring/Spring.Core/Core/InvalidPropertyException.cs new file mode 100644 index 00000000..17cae32f --- /dev/null +++ b/src/Spring/Spring.Core/Core/InvalidPropertyException.cs @@ -0,0 +1,214 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; +using System.Security.Permissions; +using Spring.Util; + +#endregion + +namespace Spring.Core +{ + /// + /// Thrown in response to referring to an invalid property (most often via reflection). + /// + /// Rick Evans + /// $Id: InvalidPropertyException.cs,v 1.2 2007/07/31 03:47:22 markpollack Exp $ + [Serializable] + public class InvalidPropertyException : FatalReflectionException + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public InvalidPropertyException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public InvalidPropertyException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The that is (or rather was) the source of the + /// offending property. + /// + /// + /// The name of the offending property. + /// + public InvalidPropertyException(Type type, string propertyName) + : this(type, propertyName, string.Format( + CultureInfo.InvariantCulture, + "Invalid property '{0}' in class [{1}].", propertyName, type.FullName)) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The that is (or rather was) the source of the + /// offending property. + /// + /// + /// The name of the offending property. + /// + /// + /// A message about the exception. + /// + public InvalidPropertyException(Type type, string propertyName, string message) + : base(message) + { + offendingObjectType = type; + offendingPropertyName = propertyName; + } + + /// + /// Creates a new instance of the InvalidPropertyException class. + /// + /// + /// The that is (or rather was) the source of the + /// offending property. + /// + /// + /// The name of the offending property. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public InvalidPropertyException ( + Type offendingType, string offendingProperty, string message, Exception rootCause) + : base (message, rootCause) + { + offendingObjectType = offendingType; + offendingPropertyName = offendingProperty; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public InvalidPropertyException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected InvalidPropertyException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + offendingObjectType = info.GetValue("ObjectType", typeof (Type)) as Type; + offendingPropertyName = info.GetString("OffendingPropertyName"); + } + + #endregion + + #region Properties + + /// + /// The that is (or rather was) the source of the + /// offending property. + /// + public Type ObjectType + { + get { return offendingObjectType; } + } + + /// + /// The name of the offending property. + /// + public string OffendingPropertyName + { + get { return offendingPropertyName; } + } + + #endregion + + #region Methods + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] + public override void GetObjectData( + SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("ObjectType", ObjectType, typeof (Type)); + info.AddValue("OffendingPropertyName", OffendingPropertyName); + } + + #endregion + + #region Fields + + private Type offendingObjectType; + private string offendingPropertyName; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/MethodGenericArgumentsCountCriteria.cs b/src/Spring/Spring.Core/Core/MethodGenericArgumentsCountCriteria.cs new file mode 100644 index 00000000..fb8b4b2c --- /dev/null +++ b/src/Spring/Spring.Core/Core/MethodGenericArgumentsCountCriteria.cs @@ -0,0 +1,141 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Reflection; + +using Spring.Core; + +#endregion + +namespace Spring.Core +{ + /// + /// Criteria that is satisfied if the number of generic arguments to a given + /// matches an arbitrary number. + /// + /// + ///

    + /// This class supports checking the generic arguments count of both + /// generic methods and constructors. + ///

    + ///
    + /// Bruno Baia + /// $Id: MethodGenericArgumentsCountCriteria.cs,v 1.1 2007/08/04 01:04:33 bbaia Exp $ + public class MethodGenericArgumentsCountCriteria : ICriteria + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This constructor sets the + /// + /// property to zero (0). + ///

    + ///
    + public MethodGenericArgumentsCountCriteria() : this(0) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The number of generic arguments that a + /// must have to satisfy this criteria. + /// + /// + /// If the supplied is less + /// than zero. + /// + public MethodGenericArgumentsCountCriteria(int expectedGenericArgumentCount) + { + ExpectedGenericArgumentCount = expectedGenericArgumentCount; + } + + #endregion + + #region Properties + + /// + /// The number of generic arguments that a + /// must have to satisfy this criteria. + /// + /// + /// If the supplied value is less than zero. + /// + public int ExpectedGenericArgumentCount + { + get { return _count; } + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException( + "value", value, "Cannot specify a generic argument count of less than zero."); + } + _count = value; + } + } + + #endregion + + #region Methods + + /// + /// Does the supplied satisfy the criteria encapsulated by + /// this instance? + /// + /// The datum to be checked by this criteria instance. + /// + /// True if the supplied satisfies the criteria encapsulated + /// by this instance; false if not or the supplied is null. + /// + public bool IsSatisfied(object datum) + { + bool satisfied = false; + MethodBase method = datum as MethodBase; + if (method != null) + { + satisfied = method.GetGenericArguments().Length == ExpectedGenericArgumentCount; + } + return satisfied; + } + + #endregion + + #region Fields + + private int _count; + + #endregion + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/MethodInvocationException.cs b/src/Spring/Spring.Core/Core/MethodInvocationException.cs new file mode 100644 index 00000000..d9f4f5b3 --- /dev/null +++ b/src/Spring/Spring.Core/Core/MethodInvocationException.cs @@ -0,0 +1,120 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using Spring.Core; + +#endregion + +namespace Spring.Core +{ + /// + /// Thrown when a method (typically a property getter or setter invoked via reflection) + /// throws an exception, analogous to a . + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: MethodInvocationException.cs,v 1.1 2007/07/31 00:08:42 markpollack Exp $ + /// + [Serializable] + public class MethodInvocationException : PropertyAccessException + { + /// + /// The error code string for this exception. + /// + override public string ErrorCode + { + get + { + return "methodInvocation"; + } + } + + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the MethodInvocationException class. + /// + public MethodInvocationException () + { + } + + /// + /// Creates a new instance of the MethodInvocationException class. + /// + /// + /// A message about the exception. + /// + public MethodInvocationException (string message) + : base (message) + { + } + + /// + /// Creates a new instance of the MethodInvocationException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public MethodInvocationException (string message, Exception rootCause) + : base (message, rootCause) + { + } + + /// + /// Constructor to use when an exception results from a + /// . + /// + /// + /// The raised by the invoked property. + /// + /// + /// The that + /// resulted in an exception. + /// + public MethodInvocationException (Exception ex, PropertyChangeEventArgs argument) : + base ("Property '" + argument.PropertyName + "' threw exception.", argument, ex) + { + } + + /// + /// Creates a new instance of the MethodInvocationException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected MethodInvocationException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + } + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/MethodNameMatchCriteria.cs b/src/Spring/Spring.Core/Core/MethodNameMatchCriteria.cs new file mode 100644 index 00000000..50f6c7d0 --- /dev/null +++ b/src/Spring/Spring.Core/Core/MethodNameMatchCriteria.cs @@ -0,0 +1,132 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Core +{ + /// + /// Criteria that is satisfied if the method Name of an + /// instance matches a + /// supplied string pattern. + /// + /// + /// + /// Supports the following simple pattern styles: + /// "xxx*", "*xxx" and "*xxx*" matches, as well as direct equality. + /// + /// + /// Bruno Baia + /// $Id: MethodNameMatchCriteria.cs,v 1.1 2007/08/04 01:04:34 bbaia Exp $ + public class MethodNameMatchCriteria : ICriteria + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This constructor sets the + /// + /// property to * (any method name). + ///

    + ///
    + public MethodNameMatchCriteria() : this("*") + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// The pattern that names + /// must match against in order to satisfy this criteria. + /// + /// If the supplied is null or resolve to an empty string. + /// + public MethodNameMatchCriteria(string pattern) + { + Pattern = pattern; + } + + #endregion + + #region Properties + + /// + /// The number of parameters that a + /// must have to satisfy this criteria. + /// + /// + /// If the supplied value is null or resolve to an empty string. + /// + public string Pattern + { + get { return pattern; } + set + { + AssertUtils.ArgumentHasText(value, "value"); + pattern = value; + } + } + + #endregion + + #region Methods + + /// + /// Does the supplied satisfy the criteria encapsulated by + /// this instance? + /// + /// The datum to be checked by this criteria instance. + /// + /// True if the supplied satisfies the criteria encapsulated + /// by this instance; false if not or the supplied is null. + /// + public bool IsSatisfied(object datum) + { + bool satisfied = false; + MethodBase method = datum as MethodBase; + if (method != null) + { + satisfied = PatternMatchUtils.SimpleMatch(pattern.ToLower(), method.Name.ToLower()); + } + return satisfied; + } + + #endregion + + #region Fields + + private string pattern; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/MethodParametersCountCriteria.cs b/src/Spring/Spring.Core/Core/MethodParametersCountCriteria.cs new file mode 100644 index 00000000..837f800a --- /dev/null +++ b/src/Spring/Spring.Core/Core/MethodParametersCountCriteria.cs @@ -0,0 +1,148 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Core; + +#endregion + +namespace Spring.Core +{ + /// + /// Criteria that is satisfied if the number of parameters to a given + /// matches an arbitrary number. + /// + /// + ///

    + /// This class supports checking the parameter count of both methods and + /// constructors. + ///

    + ///

    + /// Default parameters, etc need to taken into account. + ///

    + ///
    + /// Rick Evans + /// $Id: MethodParametersCountCriteria.cs,v 1.2 2007/09/20 14:20:45 bbaia Exp $ + public class MethodParametersCountCriteria : ICriteria + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This constructor sets the + /// + /// property to zero (0). + ///

    + ///
    + public MethodParametersCountCriteria() : this(0) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The number of parameters that a + /// must have to satisfy this criteria. + /// + /// + /// If the supplied is less + /// than zero. + /// + public MethodParametersCountCriteria(int expectedParameterCount) + { + ExpectedParameterCount = expectedParameterCount; + } + + #endregion + + #region Properties + + /// + /// The number of parameters that a + /// must have to satisfy this criteria. + /// + /// + /// If the supplied value is less than zero. + /// + public int ExpectedParameterCount + { + get { return _count; } + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException( + "value", value, "Cannot specify a parameter count of less than zero."); + } + _count = value; + } + } + + #endregion + + #region Methods + + /// + /// Does the supplied satisfy the criteria encapsulated by + /// this instance? + /// + /// The datum to be checked by this criteria instance. + /// + /// True if the supplied satisfies the criteria encapsulated + /// by this instance; false if not or the supplied is null. + /// + public bool IsSatisfied(object datum) + { + bool satisfied = false; + MethodBase method = datum as MethodBase; + if (method != null) + { + ParameterInfo[] parameters = method.GetParameters(); + if (parameters.Length == ExpectedParameterCount) + { + satisfied = true; + } + else if ((parameters.Length > 0) && (ExpectedParameterCount >= parameters.Length-1)) + { + ParameterInfo lastParameter = parameters[parameters.Length - 1]; + satisfied = lastParameter.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0; + } + } + return satisfied; + } + + #endregion + + #region Fields + + private int _count; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/MethodParametersCriteria.cs b/src/Spring/Spring.Core/Core/MethodParametersCriteria.cs new file mode 100644 index 00000000..8712a08f --- /dev/null +++ b/src/Spring/Spring.Core/Core/MethodParametersCriteria.cs @@ -0,0 +1,180 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Core +{ + /// + /// Criteria that is satisfied if the of each of the + /// parameters of a given matches each + /// of the parameter s of a given + /// . + /// + /// + ///

    + /// If no array is passed to the overloaded constructor, + /// any method that has no parameters will satisfy an instance of this + /// class. The same effect could be achieved by passing the + /// array to the overloaded constructor. + ///

    + ///
    + /// Rick Evans + /// $Id: MethodParametersCriteria.cs,v 1.2 2007/09/20 14:20:46 bbaia Exp $ + public class MethodParametersCriteria : ICriteria + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public MethodParametersCriteria() : this(Type.EmptyTypes) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// If the supplied array is null, then this + /// constructor uses the array. + ///

    + ///
    + /// + /// The array that this criteria will use to + /// check parameter s. + /// + public MethodParametersCriteria(Type[] parameters) + { + _parameters = parameters; + } + + #endregion + + #region Methods + + /// + /// Does the supplied satisfy the criteria encapsulated by + /// this instance? + /// + /// + ///

    + /// This implementation respects the inheritance chain of any parameter + /// s... i.e. methods that have a base type (or + /// interface) that is assignable to the in the + /// same corresponding index of the parameter types will satisfy this + /// criteria instance. + ///

    + ///
    + /// The datum to be checked by this criteria instance. + /// + /// True if the supplied satisfies the criteria encapsulated + /// by this instance; false if not or the supplied is null. + /// + public bool IsSatisfied(object datum) + { + bool satisfied = false; + MethodInfo method = datum as MethodInfo; + if (method != null) + { + bool isParamArray = false; + Type paramArrayType = null; + ParameterInfo[] parametersBeingChecked = method.GetParameters(); + if (parametersBeingChecked.Length > 0) + { + ParameterInfo lastParameter = parametersBeingChecked[parametersBeingChecked.Length - 1]; + isParamArray = lastParameter.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0; + if (isParamArray) + { + paramArrayType = lastParameter.ParameterType.GetElementType(); + } + } + if (parametersBeingChecked != null + && parametersBeingChecked.Length == _parameters.Length) + { + satisfied = true; + for (int i = 0; i < _parameters.Length; ++i) + { + Type sourceType = _parameters[i]; + Type typeBeingChecked = parametersBeingChecked[i].ParameterType; + if (!typeBeingChecked.IsAssignableFrom(sourceType)) + { + if (isParamArray && i == _parameters.Length - 1) + { + if (!paramArrayType.IsAssignableFrom(sourceType)) + { + satisfied = false; + break; + } + } + else + { + satisfied = false; + break; + } + } + } + } + else if (isParamArray && (_parameters.Length >= parametersBeingChecked.Length - 1)) + { + satisfied = true; + for (int i = 0; i < parametersBeingChecked.Length - 1; ++i) + { + Type sourceType = _parameters[i]; + Type typeBeingChecked = parametersBeingChecked[i].ParameterType; + if (!typeBeingChecked.IsAssignableFrom(sourceType)) + { + satisfied = false; + break; + } + } + for (int i = parametersBeingChecked.Length - 1; i < _parameters.Length; ++i) + { + Type sourceType = _parameters[i]; + if (!paramArrayType.IsAssignableFrom(sourceType)) + { + satisfied = false; + break; + } + } + } + } + return satisfied; + } + + #endregion + + #region Fields + + private readonly Type[] _parameters; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/MethodReturnTypeCriteria.cs b/src/Spring/Spring.Core/Core/MethodReturnTypeCriteria.cs new file mode 100644 index 00000000..0fad4d09 --- /dev/null +++ b/src/Spring/Spring.Core/Core/MethodReturnTypeCriteria.cs @@ -0,0 +1,121 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Core; + +#endregion + +namespace Spring.Core +{ + /// + /// Criteria that is satisfied if the return of a given + /// matches a given . + /// + /// Rick Evans + /// $Id: MethodReturnTypeCriteria.cs,v 1.1 2007/07/31 02:03:36 markpollack Exp $ + public class MethodReturnTypeCriteria : ICriteria + { + #region Constants + + /// + /// The return to match against if no + /// is provided explictly. + /// + private static readonly Type DefaultType = typeof (void); + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public MethodReturnTypeCriteria() + : this(DefaultType) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The that the return type of a given + /// must match in order to satisfy + /// this criteria. + /// + public MethodReturnTypeCriteria(Type type) + { + ReturnType = type; + } + + #endregion + + #region Properties + + /// + /// The that the return type of a given + /// must match in order to satisfy + /// this criteria. + /// + public Type ReturnType + { + get { return _type; } + set { _type = value == null ? DefaultType : value; } + } + + #endregion + + #region Methods + + /// + /// Does the supplied satisfy the criteria encapsulated by + /// this instance? + /// + /// The datum to be checked by this criteria instance. + /// + /// True if the supplied satisfies the criteria encapsulated + /// by this instance; false if not or the supplied is null. + /// + public bool IsSatisfied(object datum) + { + bool satisfied = false; + MethodInfo method = datum as MethodInfo; + if (method != null) + { + satisfied = method.ReturnType.Equals(ReturnType); + } + return satisfied; + } + + #endregion + + #region Fields + + private Type _type; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/NotReadablePropertyException.cs b/src/Spring/Spring.Core/Core/NotReadablePropertyException.cs new file mode 100644 index 00000000..60cae8df --- /dev/null +++ b/src/Spring/Spring.Core/Core/NotReadablePropertyException.cs @@ -0,0 +1,116 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Core +{ + /// + /// Thrown in response to a failed attempt to read a property. + /// + /// + ///

    + /// Typically thrown when attempting to read the value of a write-only + /// property via reflection. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: NotReadablePropertyException.cs,v 1.2 2007/07/31 00:26:30 markpollack Exp $ + [Serializable] + public class NotReadablePropertyException : InvalidPropertyException + { + /// + /// Creates a new instance of the + /// class. + /// + public NotReadablePropertyException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public NotReadablePropertyException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The that is (or rather was) the source of the + /// offending property. + /// + /// + /// The name of the offending property. + /// + public NotReadablePropertyException(Type type, string propertyName) + : base(type, propertyName, string.Format( + CultureInfo.InvariantCulture, + "Cannot read the value of the '{0}' property " + + "declared on the [{1}] class : property is read-only.", propertyName, type.FullName)) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public NotReadablePropertyException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected NotReadablePropertyException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/NotWritablePropertyException.cs b/src/Spring/Spring.Core/Core/NotWritablePropertyException.cs new file mode 100644 index 00000000..0f6b012a --- /dev/null +++ b/src/Spring/Spring.Core/Core/NotWritablePropertyException.cs @@ -0,0 +1,148 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Core +{ + /// + /// Thrown in response to a failed attempt to write a property. + /// + /// Mark Pollack (.NET) + /// $Id: NotWritablePropertyException.cs,v 1.1 2007/07/31 00:08:42 markpollack Exp $ + [Serializable] + public class NotWritablePropertyException : InvalidPropertyException + { + /// + /// Creates a new instance of the NotWritablePropertyException class. + /// + public NotWritablePropertyException() + { + } + + /// + /// Creates a new instance of the NotWritablePropertyException class. + /// + /// + /// A message about the exception. + /// + public NotWritablePropertyException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the NotWritablePropertyException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public NotWritablePropertyException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the NotWritablePropertyException class. + /// + /// + /// The that is (or rather was) the source of the + /// offending property. + /// + /// + /// The name of the offending property. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public NotWritablePropertyException( + Type offendingType, string offendingProperty, string message, Exception rootCause) + : base(offendingType, offendingProperty, message, rootCause) + { + } + + /// + /// Creates a new instance of the NotWritablePropertyException class + /// summarizing what property was not writable. + /// + /// + /// The name of the property that is not writable. + /// + /// + /// The in which the property is not writable. + /// + public NotWritablePropertyException(string offendingProperty, Type offendingType) + : base(offendingType, offendingProperty, + string.Format(CultureInfo.InvariantCulture, + "Property '{0}' is not writable in class [{1}].", + offendingProperty, offendingType.FullName)) + { + } + + /// + /// Creates new NotWritablePropertyException with a root cause. + /// + /// + /// The name of the property that is not writable. + /// + /// + /// The in which the property is not writable. + /// + /// + /// The root cause indicating why the property was not writable. + /// + public NotWritablePropertyException(string offendingProperty, Type offendingType, Exception rootCause) + : base(offendingType, offendingProperty, + string.Format(CultureInfo.InvariantCulture, + "Property '{0}' is not writable in class [{1}].", + offendingProperty, + offendingType != null ? offendingType.FullName : "null"), + rootCause) + { + } + + /// + /// Creates a new instance of the NotWritablePropertyException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected NotWritablePropertyException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/NullValueInNestedPathException.cs b/src/Spring/Spring.Core/Core/NullValueInNestedPathException.cs new file mode 100644 index 00000000..76ef1cc5 --- /dev/null +++ b/src/Spring/Spring.Core/Core/NullValueInNestedPathException.cs @@ -0,0 +1,172 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; +using System.Security.Permissions; +using Spring.Util; + +#endregion + +namespace Spring.Core +{ + /// + /// Thrown in response to encountering a value + /// when traversing a nested path expression. + /// + /// $Id: NullValueInNestedPathException.cs,v 1.2 2007/07/31 03:47:22 markpollack Exp $ + [Serializable] + public class NullValueInNestedPathException : FatalReflectionException + { + private string property; + private Type type; + + /// + /// The name of the offending property. + /// + public string PropertyName + { + get { return property; } + } + + /// + /// The of the class where the property was last looked for. + /// + public Type ObjectType + { + get { return type; } + } + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public NullValueInNestedPathException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public NullValueInNestedPathException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public NullValueInNestedPathException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The of the object where the property was not found. + /// + /// The name of the property not found. + public NullValueInNestedPathException(Type type, string theProperty) + : this(type, theProperty, string.Format(CultureInfo.InvariantCulture, + "Value of nested property '{0}' is null in Type [{1}].", theProperty, type)) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The of the object where the property was not found. + /// + /// The name of the property not found. + /// A message about the exception. + public NullValueInNestedPathException(Type type, string theProperty, string message) + : base(message) + { + property = theProperty; + this.type = type; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected NullValueInNestedPathException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + type = info.GetValue("ObjectType", typeof (Type)) as Type; + property = info.GetString("PropertyName"); + } + + #endregion + + #region Methods + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] + public override void GetObjectData( + SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("ObjectType", ObjectType); + info.AddValue("PropertyName", PropertyName); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/OrderComparator.cs b/src/Spring/Spring.Core/Core/OrderComparator.cs new file mode 100644 index 00000000..ccc061e2 --- /dev/null +++ b/src/Spring/Spring.Core/Core/OrderComparator.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Core +{ + /// + /// Comparator implementation for objects, sorting by + /// order value ascending (resp. by priority descending). + /// + /// + ///

    + /// Non- objects are treated as greatest order values, + /// thus ending up at the end of a list, in arbitrary order (just like same order values of + /// objects). + ///

    + ///
    + /// Juergen Hoeller + /// Aleksandar Seovic (.Net) + /// $Id: OrderComparator.cs,v 1.5 2006/04/09 07:18:38 markpollack Exp $ + public class OrderComparator : IComparer + { + /// + /// Compares two objects and returns a value indicating whether one is less than, + /// equal to or greater than the other. + /// + /// + ///

    + /// Uses direct evaluation instead of + /// to avoid unnecessary boxing. + ///

    + ///
    + /// The first object to compare. + /// The second object to compare. + /// + /// -1 if first object is less then second, 1 if it is greater, or 0 if they are equal. + /// + public virtual int Compare(object o1, object o2) + { + IOrdered o1lhs = o1 as IOrdered; + IOrdered o2rhs = o2 as IOrdered; + int lhs = o1lhs != null ? o1lhs.Order : Int32.MaxValue; + int rhs = o2rhs != null ? o2rhs.Order : Int32.MaxValue; + if (lhs < rhs) + { + return - 1; + } + else if (lhs > rhs) + { + return 1; + } + else + { + return 0; + } + } + } +} diff --git a/src/Spring/Spring.Core/Core/PropertyAccessException.cs b/src/Spring/Spring.Core/Core/PropertyAccessException.cs new file mode 100644 index 00000000..275003a7 --- /dev/null +++ b/src/Spring/Spring.Core/Core/PropertyAccessException.cs @@ -0,0 +1,159 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Core +{ + /// + /// Superclass for exceptions related to a property access, such as a + /// mismatch or a target invocation exception. + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: PropertyAccessException.cs,v 1.2 2007/07/31 03:47:22 markpollack Exp $ + [Serializable] + public abstract class PropertyAccessException : ReflectionException, IErrorCoded + { + /// + /// Returns the PropertyChangeEventArgs that resulted in the problem. + /// + public PropertyChangeEventArgs PropertyChangeArgs + { + get { return _propertyChangeEventArgs; } + } + + /// + /// The string error code used to classify the error. + /// + public abstract string ErrorCode { get; } + + private PropertyChangeEventArgs _propertyChangeEventArgs; + + #region Methods + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] + public override void GetObjectData( + SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("PropertyChangeArgs", PropertyChangeArgs); + } + + #endregion + + /// + /// Create a new instance of the PropertyAccessException class. + /// + /// + /// A message about the exception. + /// + /// Describes the change attempted on the property. + protected PropertyAccessException(string message, PropertyChangeEventArgs propertyChangeEvent) : base(message) + { + _propertyChangeEventArgs = propertyChangeEvent; + } + + /// + /// Create a new instance of the PropertyAccessException class. + /// + /// + /// A message about the exception. + /// + /// Describes the change attempted on the property. + /// + /// The root exception that is being wrapped. + /// + protected PropertyAccessException(string message, PropertyChangeEventArgs propertyChangeEvent, Exception rootCause) + : base(message, rootCause) + { + _propertyChangeEventArgs = propertyChangeEvent; + } + + /// + /// Creates a new instance of the PropertyAccessException class. + /// + protected PropertyAccessException() + { + } + + /// + /// Creates a new instance of the PropertyAccessException class. + /// + /// + /// A message about the exception. + /// + protected PropertyAccessException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the PropertyAccessExceptionsException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + protected PropertyAccessException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the PropertyAccessExceptionsException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected PropertyAccessException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + _propertyChangeEventArgs = info.GetValue("PropertyChangeArgs", typeof (object)) as PropertyChangeEventArgs; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/PropertyChangeEventArgs.cs b/src/Spring/Spring.Core/Core/PropertyChangeEventArgs.cs new file mode 100644 index 00000000..a861d711 --- /dev/null +++ b/src/Spring/Spring.Core/Core/PropertyChangeEventArgs.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; + +#endregion + +namespace Spring.Core +{ + /// + /// Provides additional data for the PropertyChanged event. + /// + /// + ///

    + /// Provides some additional properties over and above the name of the + /// property that has changed (which is inherited from the + /// base class). + /// This allows calling code to determine whether or not a property has + /// actually changed (i.e. a PropertyChanged event may have been + /// raised, but the value itself may be equivalent). + ///

    + ///
    + /// + [Serializable] + public class PropertyChangeEventArgs : PropertyChangedEventArgs + { + private object oldValue; + private object newValue; + + /// + /// Create a new instance of the + /// class. + /// + /// + /// The name of the property that was changed. + /// The old value of the property. + /// the new value of the property. + public PropertyChangeEventArgs( + string propertyName, object oldValue, object newValue) : base(propertyName) + { + this.oldValue = oldValue; + this.newValue = newValue; + } + + /// + /// Get the old value for the property. + /// + /// + public object OldValue + { + get { return oldValue; } + } + + /// + /// Get the new value of the property. + /// + /// + public object NewValue + { + get { return newValue; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/RegularExpressionCriteria.cs b/src/Spring/Spring.Core/Core/RegularExpressionCriteria.cs new file mode 100644 index 00000000..38160dad --- /dev/null +++ b/src/Spring/Spring.Core/Core/RegularExpressionCriteria.cs @@ -0,0 +1,143 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Text.RegularExpressions; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Core +{ + /// + /// A base class for all + /// implementations that are regular expression based. + /// + /// Rick Evans + /// $Id: RegularExpressionCriteria.cs,v 1.1 2007/07/31 01:35:07 markpollack Exp $ + public abstract class RegularExpressionCriteria : ICriteria + { + #region Constants + + /// + /// The default pattern... matches absolutely anything. + /// + protected const string MatchAnyThingPattern = ".*"; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + protected RegularExpressionCriteria() : this(RegularExpressionCriteria.MatchAnyThingPattern) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The regular expression pattern to be applied. + /// + protected RegularExpressionCriteria(string pattern) + { + Pattern = pattern; + } + + #endregion + + #region Properties + + /// + /// The regular expression pattern to be applied. + /// + public string Pattern + { + get { return _pattern; } + set + { + _pattern = StringUtils.HasText(value) ? + value : RegularExpressionCriteria.MatchAnyThingPattern; + Expression = new Regex(Pattern, Options); + } + } + + /// + /// The regular expression options to be applied. + /// + public RegexOptions Options + { + get { return _options; } + set { _options = value; } + } + + /// + /// The regular expression to be applied. + /// + public Regex Expression + { + get { return _expression; } + set { _expression = value; } + } + + #endregion + + #region Methods + + /// + /// Does the supplied satisfy the criteria encapsulated by + /// this instance? + /// + /// The datum to be checked by this criteria instance. + /// + /// True if the supplied satisfies the criteria encapsulated + /// by this instance; false if not or the supplied is null. + /// + public abstract bool IsSatisfied(object datum); + + /// + /// Convenience method that calls the + /// + /// on the supplied . + /// + /// The input to match against. + /// True if the matches. + protected bool IsMatch(string input) + { + return Expression.IsMatch(input); + } + + #endregion + + #region Fields + + private string _pattern; + private RegexOptions _options; + private Regex _expression; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/RegularExpressionEventNameCriteria.cs b/src/Spring/Spring.Core/Core/RegularExpressionEventNameCriteria.cs new file mode 100644 index 00000000..8bb72195 --- /dev/null +++ b/src/Spring/Spring.Core/Core/RegularExpressionEventNameCriteria.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Reflection; +using System.Text.RegularExpressions; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Core +{ + /// + /// Criteria that is satisfied if the Name property of an + /// instance matches a + /// supplied regular expression pattern. + /// + /// Rick Evans + /// $Id: RegularExpressionEventNameCriteria.cs,v 1.1 2007/07/31 01:35:07 markpollack Exp $ + public class RegularExpressionEventNameCriteria : RegularExpressionCriteria + { + #region Constants + + /// + /// The default event name pattern... matches pretty much any event name. + /// + private const string MatchAnyEventNamePattern = ".+"; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public RegularExpressionEventNameCriteria() + : this(RegularExpressionEventNameCriteria.MatchAnyEventNamePattern) + { + Options = RegexOptions.IgnoreCase; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The pattern that names + /// must match against in order to satisfy this criteria. + /// + public RegularExpressionEventNameCriteria(string eventNamePattern) + { + Options = RegexOptions.IgnoreCase; + Pattern = StringUtils.HasText(eventNamePattern) ? + eventNamePattern : RegularExpressionEventNameCriteria.MatchAnyEventNamePattern; + } + + #endregion + + #region Methods + + /// + /// Does the supplied satisfy the criteria encapsulated by + /// this instance? + /// + /// The datum to be checked by this criteria instance. + /// + /// True if the supplied satisfies the criteria encapsulated + /// by this instance; false if not or the supplied is null. + /// + public override bool IsSatisfied(object datum) + { + bool satisfied = false; + EventInfo evt = datum as EventInfo; + if (evt != null) + { + satisfied = IsMatch(evt.Name); + } + return satisfied; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/RegularExpressionMethodNameCriteria.cs b/src/Spring/Spring.Core/Core/RegularExpressionMethodNameCriteria.cs new file mode 100644 index 00000000..0c619a71 --- /dev/null +++ b/src/Spring/Spring.Core/Core/RegularExpressionMethodNameCriteria.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Reflection; +using System.Text.RegularExpressions; +using Spring.Objects.Support; +using Spring.Util; + +#endregion + +namespace Spring.Core +{ + /// + /// Criteria that is satisfied if the Name property of an + /// instance matches a + /// supplied regular expression pattern. + /// + /// Rick Evans + /// $Id: RegularExpressionMethodNameCriteria.cs,v 1.1 2007/07/31 01:35:07 markpollack Exp $ + public class RegularExpressionMethodNameCriteria : RegularExpressionCriteria + { + #region Constants + + /// + /// The default method name pattern... matches pretty much any method name. + /// + private const string MatchAnyMethodNamePattern = ".+"; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public RegularExpressionMethodNameCriteria() + : this(RegularExpressionMethodNameCriteria.MatchAnyMethodNamePattern) + { + Options = RegexOptions.IgnoreCase; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The pattern that names + /// must match against in order to satisfy this criteria. + /// + public RegularExpressionMethodNameCriteria(string methodNamePattern) + { + Options = RegexOptions.IgnoreCase; + Pattern = StringUtils.HasText(methodNamePattern) ? + methodNamePattern : RegularExpressionMethodNameCriteria.MatchAnyMethodNamePattern; + } + + #endregion + + #region Methods + + /// + /// Does the supplied satisfy the criteria encapsulated by + /// this instance? + /// + /// The datum to be checked by this criteria instance. + /// + /// True if the supplied satisfies the criteria encapsulated + /// by this instance; false if not or the supplied is null. + /// + public override bool IsSatisfied(object datum) + { + bool satisfied = false; + MethodInfo method = datum as MethodInfo; + if (method != null) + { + satisfied = IsMatch(method.Name); + } + return satisfied; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/CredentialConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/CredentialConverter.cs new file mode 100644 index 00000000..b699da91 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/CredentialConverter.cs @@ -0,0 +1,125 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Net; +using System.ComponentModel; +using System.Globalization; +using System.Text.RegularExpressions; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converts string representation of a credential for Web client authentication + /// into an instance of . + /// + /// + ///

    + /// Find below some examples of the XML formatted strings that this + /// converter will sucessfully convert. + ///

    + /// + /// + /// + /// + /// + /// + ///
    + /// Bruno Baia + /// $Id: CredentialConverter.cs,v 1.2 2007/12/07 15:14:40 bbaia Exp $ + public class CredentialConverter : TypeConverter + { + private readonly static Regex credentialRegex = new Regex( + @"(((?[\w_.]+)\\)?)(?([\w_.]+))((:(?([\w_.]+)))?)", + RegexOptions.Compiled); + + /// + /// Can we convert from the sourcetype + /// to a instance ? + /// + /// + ///

    + /// Currently only supports conversion from a instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// if the conversion is possible. + public override bool CanConvertFrom( + ITypeDescriptorContext context, Type sourceType) + { + return (sourceType == typeof(string)); + } + + /// + /// Convert from a value to an + /// instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A instance if successful. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + string credentials = (string)value; + Match m = credentialRegex.Match(credentials); + + if (!m.Success || m.Value != credentials) + { + throw new ArgumentException(String.Format("The credential '{0}' is not well-formed.", value)); + } + + // Get domain + string domain = m.Groups["domain"].Value; + + // Get user name + string userName = m.Groups["userName"].Value; + + // Get password + string password = m.Groups["password"].Value; + + return new NetworkCredential(userName, password, domain); + } + return base.ConvertFrom(context, culture, value); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/CustomNumberConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/CustomNumberConverter.cs new file mode 100644 index 00000000..f13c4b99 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/CustomNumberConverter.cs @@ -0,0 +1,187 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; + +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// A custom for any + /// primitive numeric type such as , + /// , , etc. + /// + /// + ///

    + /// Can use a given for + /// (locale-specific) parsing and rendering. + ///

    + ///

    + /// This is not meant to be used as a system + /// but rather as a + /// locale-specific number converter within custom controller code, to + /// parse user-entered number strings into number properties of objects, + /// and render them in a UI form. + ///

    + ///
    + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: CustomNumberConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class CustomNumberConverter : TypeConverter + { + private Type _type; + private NumberFormatInfo _nfi; + private bool _allowEmpty; + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The primitive numeric to convert to. + /// + /// + /// The to use for + /// (locale-specific) parsing and rendering + /// + /// + /// Is an empty string allowed to be converted? If + /// , an empty string value will be converted to + /// numeric 0. + /// + /// Id the supplied is not a primitive + /// . + /// + /// + public CustomNumberConverter( + Type type, NumberFormatInfo format, bool allowEmpty) + { + if (!type.IsPrimitive) + { + throw new ArgumentException( + "Property type must be a primitive type."); + } + this._type = type; + this._nfi = format; + this._allowEmpty = allowEmpty; + } + + /// + /// Returns whether this converter can convert an object of one + /// to a + /// + /// + ///

    + /// Currently only supports conversion from a + /// instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// + /// if the conversion is possible. + /// + public override bool CanConvertFrom( + ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// Converts the specified object (a string) to the required primitive + /// type. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// A primitive representation of the string value. + public override object ConvertFrom( + ITypeDescriptorContext context, CultureInfo culture, object val) + { + if (val is string) + { + string value = val as string; + if (!StringUtils.HasText(value) + && _allowEmpty) + { + value = "0"; + } + if (_type.Equals(typeof (Int16))) + { + return Convert.ToInt16(value, _nfi); + } + else if (_type.Equals(typeof (UInt16))) + { + return Convert.ToUInt16(value, _nfi); + } + else if (_type.Equals(typeof (Int32))) + { + return Convert.ToInt32(value, _nfi); + } + else if (_type.Equals(typeof (UInt32))) + { + return Convert.ToUInt32(value, _nfi); + } + else if (_type.Equals(typeof (Int64))) + { + return Convert.ToInt64(value, _nfi); + } + else if (_type.Equals(typeof (UInt64))) + { + return Convert.ToUInt64(value, _nfi); + } + else if (_type.Equals(typeof (Single))) + { + return Convert.ToSingle(value, _nfi); + } + else if (_type.Equals(typeof (Double))) + { + return Convert.ToDouble(value, _nfi); + } + } + return base.ConvertFrom(context, culture, val); + } + } +} diff --git a/src/Spring/Spring.Core/Core/TypeConversion/FileInfoConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/FileInfoConverter.cs new file mode 100644 index 00000000..5533f958 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/FileInfoConverter.cs @@ -0,0 +1,109 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; +using System.IO; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converter for instances. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class FileInfoConverter : TypeConverter + { + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the + /// class. + /// + public FileInfoConverter () {} + #endregion + + #region Methods + /// + /// Returns whether this converter can convert an object of one + /// to a + /// + /// + ///

    + /// Currently only supports conversion from a + /// instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// True if the conversion is possible. + public override bool CanConvertFrom ( + ITypeDescriptorContext context, + Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom (context, sourceType); + } + + /// + /// Convert from a string value to a instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A if successful. + /// + public override object ConvertFrom ( + ITypeDescriptorContext context, + CultureInfo culture, object value) + { + if (value is string) + { + return new FileInfo(value as string); + } + return base.ConvertFrom (context, culture, value); + } + #endregion + + } + +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/NameValueConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/NameValueConverter.cs new file mode 100644 index 00000000..b6556d00 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/NameValueConverter.cs @@ -0,0 +1,159 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Configuration; +using System.Globalization; +using System.Xml; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Custom implementation for + /// objects. + /// + /// + ///

    + /// Handles conversion from an XML formatted string to a + /// object + /// (see below for an example of the expected XML format). + ///

    + ///

    + /// This converter must be registered before it will be available. Standard + /// converters in this namespace are automatically registered by the + /// class. + ///

    + ///
    + /// + ///

    + /// Find below some examples of the XML formatted strings that this + /// converter will sucessfully convert. Note that the name of the top level + /// (document) element is quite arbitrary... it is only the content that + /// matters (and which must be in the format + /// <add key="..." value="..."/>. For your continued sanity + /// though, you may wish to standardize on the top level name of + /// 'dictionary' (although you are of course free to not do so). + ///

    + /// + /// + /// + /// + /// + /// + ///

    + /// The following example uses a different top level (document) element + /// name, but is equivalent to the first example. + ///

    + /// + /// + /// + /// + /// + /// + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: NameValueConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class NameValueConverter : TypeConverter + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public NameValueConverter() + { + } + + #endregion + + /// + /// Returns whether this converter can convert an object of one + /// to a + /// + /// + /// + ///

    + /// Currently only supports conversion from an + /// XML formatted instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// True if the conversion is possible. + public override bool CanConvertFrom( + ITypeDescriptorContext context, + Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// Convert from a string value to a + /// instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A + /// if successful. + /// + public override object ConvertFrom( + ITypeDescriptorContext context, + CultureInfo culture, object value) + { + string text = value as string; + if (text != null) + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(text); + return new NameValueSectionHandler() + .Create(null, null, doc.DocumentElement); + } + return base.ConvertFrom(context, culture, value); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/RGBColorConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/RGBColorConverter.cs new file mode 100644 index 00000000..0356c04c --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/RGBColorConverter.cs @@ -0,0 +1,170 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Drawing; +using System.Globalization; + +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converter for from a comma separated + /// list of RBG values. + /// + /// + ///

    + /// Please note that this class does not implement converting + /// to a comma separated list of RBG values from a + /// . + ///

    + ///
    + /// Federico Spinazzi + /// $Id: RGBColorConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public sealed class RGBColorConverter : TypeConverter + { + private const char ArgbSeparator = ','; + + private const string DefaultAlpha = "255"; + + /// + /// Returns whether this converter can convert an object of one + /// to a + /// . + /// + /// + ///

    + /// Currently only supports conversion from a + /// instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// if the conversion is possible. + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// Converts the specified object (a string) a + /// instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture: currently ignored. + /// + /// + /// The value that is to be converted, in "R,G,B", "A,R,G,B", or + /// symbolic color name (). + /// + /// + /// A representation of the string value. + /// + /// + /// If the input string is not in a supported format, or is not one of the + /// predefined system colors (). + /// + public override object ConvertFrom( + ITypeDescriptorContext context, CultureInfo culture, object value) + { + string input = value as string; + if (StringUtils.HasText(input)) + { + if (input.IndexOf(ArgbSeparator) > -1) + { + string[] colorSplit = input.Split(ArgbSeparator); + if (colorSplit.Length == 3) + { + return FromRgb(colorSplit); + } + else if (colorSplit.Length == 4) + { + return FromArgb(colorSplit); + } + else + { + throw new FormatException( + "Input string is not in the correct format : '" + input + "'."); + } + } + return FromName(input); + } + return base.ConvertFrom(context, culture, value); + } + + private static object FromRgb(string[] rgb) + { + return GetColorFrom(DefaultAlpha, rgb[0], rgb[1], rgb[2]); + } + + private static object FromArgb(string[] argb) + { + return GetColorFrom(argb[0], argb[1], argb[2], argb[3]); + } + + private static Color FromName(string name) + { + try + { + KnownColor color = (KnownColor) Enum.Parse(typeof (KnownColor), name); + return Color.FromKnownColor(color); + } + catch (Exception ex) + { + throw new FormatException( + "Input string is not a known system color : '" + name + "'.", ex); + } + } + + private static Color GetColorFrom(string alpha, string red, string green, string blue) + { + try + { + return Color.FromArgb(Int32.Parse(alpha), Int32.Parse(red), Int32.Parse(green), Int32.Parse(blue)); + } + catch (Exception ex) + { + throw new FormatException( + "Bad color format.", ex); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/RegexConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/RegexConverter.cs new file mode 100644 index 00000000..d85abc37 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/RegexConverter.cs @@ -0,0 +1,92 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Text.RegularExpressions; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converts string representation of a regular expression into an instance of . + /// + /// Aleksandar Seovic + /// $Id: RegexConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class RegexConverter : TypeConverter + { + /// + /// Can we convert from the sourcetype to a ? + /// + /// + ///

    + /// Currently only supports conversion from a instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// if the conversion is possible. + public override bool CanConvertFrom( + ITypeDescriptorContext context, Type sourceType) + { + return (sourceType == typeof(string)); + } + + /// + /// Convert from a value to an + /// instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A if successful. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + return new Regex(value as string); + } + else + { + return base.ConvertFrom(context, culture, value); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/RegistryKeyConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/RegistryKeyConverter.cs new file mode 100644 index 00000000..f9aabd11 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/RegistryKeyConverter.cs @@ -0,0 +1,164 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; +using Microsoft.Win32; + +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converts string representation of the registry key + /// into instance. + /// + /// Aleksandar Seovic + /// $Id: RegistryKeyConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class RegistryKeyConverter : TypeConverter + { + /// + /// Can we convert from a the sourcetype to a ? + /// + /// + ///

    + /// Currently only supports conversion from a instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// if the conversion is possible. + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return (sourceType == typeof(string)); + } + + /// + /// Convert from a value to an + /// instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A array if successful. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + AssertUtils.ArgumentNotNull(value, "value"); + + if (value is string) + { + string keyName = (string) value; + AssertUtils.ArgumentHasText(keyName, "value"); + + string[] keys = StringUtils.Split(keyName, "\\", false, true); + RegistryKey key = GetRootKey(keys[0]); + for (int i = 1; i < keys.Length; i++) + { + // open all sub-keys but the last one as read-only + key = key.OpenSubKey(keys[i], (i == keys.Length - 1)); + if (key == null) + { + throw new ArgumentException("Registry key [" + GetPartialKeyName(keys, i) + "] does not exist."); + } + } + + return key; + } + return base.ConvertFrom(context, culture, value); + } + + /// + /// Generates partial registry key name. + /// + /// + /// Key elements. + /// + /// + /// Index of the last element to use. + /// + /// + /// Friendly key name containing key element from + /// 0 to , inclusive. + /// + private static string GetPartialKeyName(string[] keys, int index) + { + string keyName = ""; + for (int i = 0; i <= index; i++) + { + keyName += (keys[i] + (i < index ? "\\" : "")); + } + return keyName; + } + + /// + /// Returns for the specified + /// root hive name. + /// + /// + /// Root hive name. + /// + /// + /// Registry key for the specified name. + /// + private static RegistryKey GetRootKey(string name) + { + switch (name) + { + case "HKEY_CURRENT_USER": + return Registry.CurrentUser; + case "HKEY_LOCAL_MACHINE": + return Registry.LocalMachine; + case "HKEY_CLASSES_ROOT": + return Registry.ClassesRoot; + case "HKEY_USERS": + return Registry.Users; + case "HKEY_PERFORMANCE_DATA": + return Registry.PerformanceData; + case "HKEY_CURRENT_CONFIG": + return Registry.CurrentConfig; + case "HKEY_DYN_DATA": + return Registry.DynData; + default: + throw new ArgumentException("Invalid root hive name [" + name + "]."); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/ResourceManagerConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/ResourceManagerConverter.cs new file mode 100644 index 00000000..aaee91c2 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/ResourceManagerConverter.cs @@ -0,0 +1,158 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using System.Resources; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converts a two part string, (resource name, assembly name) + /// to a ResourceManager instance. + /// + public class ResourceManagerConverter : TypeConverter + { + /// + /// This constant represents the name of the folder/assembly containing global resources. + /// + public static readonly string APP_GLOBALRESOURCES_ASSEMBLYNAME = "App_GlobalResources"; + + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the + /// class. + /// + public ResourceManagerConverter() + { + } + #endregion + + #region Methods + /// + /// Returns whether this converter can convert an object of one + /// to a + /// + /// + /// + ///

    + /// Currently only supports conversion from a + /// instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// True if the conversion is possible. + public override bool CanConvertFrom ( + ITypeDescriptorContext context, + Type sourceType) + { + if (sourceType == typeof(string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// Convert from a string value to a + /// instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A + /// if successful. + /// + /// If the specified does not denote a valid resource + public override object ConvertFrom ( + ITypeDescriptorContext context, + CultureInfo culture, object value) + { + if (value is string) + { + // convert incoming string into ResourceManager... + string[] resourceManagerDescription = StringUtils.DelimitedListToStringArray((string)value, ","); + if (resourceManagerDescription.Length != 2) + { + throw new ArgumentException ("The string to specify a ResourceManager must be a comma delimited list of length two. i.e. resourcename, assembly parial name."); + } + string resourceName = resourceManagerDescription[0].Trim(); + if (resourceName != null && resourceName.Length == 0) + { + throw new ArgumentException("Empty value set for the resource name in ResourceManager string."); + } + string assemblyName = resourceManagerDescription[1].Trim(); + if (assemblyName != null && assemblyName.Length == 0) + { + throw new ArgumentException("Empty value set for the assembly name in ResourceManager string."); + } +#if NET_2_0 + if (assemblyName == APP_GLOBALRESOURCES_ASSEMBLYNAME) + { + try + { + Type globalResourcesType = TypeResolutionUtils.ResolveType(resourceName); + // look both, NonPublic and Public properties (SPRNET-861) + PropertyInfo resourceManagerProperty = globalResourcesType.GetProperty("ResourceManager", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + return (ResourceManager) resourceManagerProperty.GetValue(globalResourcesType, null); + } + catch (TypeLoadException ex) + { + throw new ArgumentException("Could not load resources '{0}'", resourceName, ex); + } + } + Assembly ass = Assembly.LoadWithPartialName(assemblyName); +#else + Assembly ass = Assembly.LoadWithPartialName(assemblyName); +#endif + if (ass == null) + { + throw new ArgumentException("Could not find assembly with name = '" + assemblyName + "'."); + } + return new ResourceManager(resourceName, ass); + } + return base.ConvertFrom (context, culture, value); + } + #endregion + } +} diff --git a/src/Spring/Spring.Core/Core/TypeConversion/RuntimeTypeConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/RuntimeTypeConverter.cs new file mode 100644 index 00000000..e609cbc2 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/RuntimeTypeConverter.cs @@ -0,0 +1,175 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; + +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + + /// + /// A custom for + /// runtime type references. + /// + /// + ///

    + /// Currently only supports conversion to and from a + /// . + ///

    + ///
    + /// Rick Evans (.NET) + /// $Id: RuntimeTypeConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class RuntimeTypeConverter : TypeConverter + { + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the + /// class. + /// + public RuntimeTypeConverter () {} + #endregion + + #region Methods + /// + /// Returns whether this converter can convert an object of one + /// to the + /// of this converter. + /// + /// + ///

    + /// Currently only supports conversion from a + /// instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// True if the conversion is possible. + public override bool CanConvertFrom ( + ITypeDescriptorContext context, + Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom (context, sourceType); + } + + /// + /// Returns whether this converter can convert the object to the specified + /// . + /// + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert to. + /// + /// True if the conversion is possible. + public override bool CanConvertTo ( + ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof (Type)) + { + return true; + } + return base.CanConvertTo (context, destinationType); + } + + /// + /// Converts the given value to the type of this converter. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// An that represents the converted value. + /// + public override object ConvertFrom ( + ITypeDescriptorContext context, + CultureInfo culture, object value) + { + if (value is string) + { + return TypeResolutionUtils.ResolveType(value as string); + } + return base.ConvertFrom(context, culture, value); + } + + /// + /// Converts the given value object to the specified type, + /// using the specified context and culture information. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// The to convert the + /// parameter to. + /// + /// + /// An that represents the converted value. + /// + public override object ConvertTo ( + ITypeDescriptorContext context, + CultureInfo culture, object value, Type destinationType) + { + if (value is Type + && destinationType == typeof (string)) + { + return ((Type) value).AssemblyQualifiedName; + } + return base.ConvertTo (context, culture, value, destinationType); + } + #endregion + } +} diff --git a/src/Spring/Spring.Core/Core/TypeConversion/StreamConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/StreamConverter.cs new file mode 100644 index 00000000..de8f5b31 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/StreamConverter.cs @@ -0,0 +1,126 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; + +using Spring.Core.IO; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converter for to directly set a + /// property. + /// + /// Jurgen Hoeller + /// Mark Pollack (.NET) + public class StreamConverter : TypeConverter + { + private ResourceConverter resourceConverter; + + #region Constructors + /// + /// Create a new StreamConverter using the default + /// . + /// + public StreamConverter() : this (new ResourceConverter()) + { + } + + /// + /// Create a new StreamConverter using the given + /// . + /// + /// + /// The to use. + public StreamConverter(ResourceConverter resourceConverter) + { + this.resourceConverter = resourceConverter == null + ? new ResourceConverter() : resourceConverter; + } + #endregion + + #region Methods + /// + /// Returns whether this converter can convert an object of one + /// to a + /// + /// + ///

    + /// Currently only supports conversion from a + /// instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// True if the conversion is possible. + public override bool CanConvertFrom ( + ITypeDescriptorContext context, + Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom (context, sourceType); + } + + /// + /// Convert from a string value to a instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A if successful. + /// + public override object ConvertFrom ( + ITypeDescriptorContext context, + CultureInfo culture, object val) + { + if (val is string) + { + IResource resource = (IResource) resourceConverter.ConvertFrom(context, culture, val); + return resource.InputStream; + } + return base.ConvertFrom (context, culture, val); + } + #endregion + } +} diff --git a/src/Spring/Spring.Core/Core/TypeConversion/StringArrayConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/StringArrayConverter.cs new file mode 100644 index 00000000..51e284f6 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/StringArrayConverter.cs @@ -0,0 +1,183 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; + +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converts a separated to a + /// array. + /// + /// + ///

    + /// Defaults to using the , (comma) as the list separator. Note that the value + /// of the current is + /// not used. + ///

    + ///

    + /// If you want to provide your own list separator, you can set the value of the + /// + /// property to the value that you want. Please note that this value will be used + /// for all future conversions in preference to the default list separator. + ///

    + ///

    + /// Please note that the individual elements of a string will be passed + /// through as is (i.e. no conversion or trimming of surrounding + /// whitespace will be performed). + ///

    + ///

    + /// This should be + /// automatically registered with any + /// implementations. + ///

    + ///
    + /// + /// + /// public class StringArrayConverterExample + /// { + /// public static void Main() + /// { + /// StringArrayConverter converter = new StringArrayConverter(); + /// + /// string csvWords = "This,Is,It"; + /// string[] frankBoothWords = converter.ConvertFrom(csvWords); + /// + /// // the 'frankBoothWords' array will have 3 elements, namely + /// // "This", "Is", "It". + /// + /// // please note that extraneous whitespace is NOT trimmed off + /// // in the current implementation... + /// string csv = " Cogito ,ergo ,sum "; + /// string[] descartesWords = converter.ConvertFrom(csv); + /// + /// // the 'descartesWords' array will have 3 elements, namely + /// // " Cogito ", "ergo ", "sum ". + /// // notice how the whitespace has NOT been trimmed. + /// } + /// } + /// + /// + /// + /// $Id: StringArrayConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class StringArrayConverter : TypeConverter + { + private const string DefaultListSeparator = ","; + + private string listSeparator = DefaultListSeparator; + + /// + /// The value that will be used as the list separator when performing + /// conversions. + /// + /// + /// A 'single' string character that will be used as the list separator + /// when performing conversions. + /// + /// + /// If the supplied value is not and is an empty + /// string, or has more than one character. + /// + public string ListSeparator + { + get { return this.listSeparator; } + set + { + if (value != null) + { + if (value.Length != 1) + { + throw new ArgumentException( + "The 'ListSeparator' must be exactly one character in length."); + } + listSeparator = value; + } + else + { + listSeparator = DefaultListSeparator; + } + } + } + + /// + /// Can we convert from a the sourcetype to a array? + /// + /// + ///

    + /// Currently only supports conversion from a instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// if the conversion is possible. + public override bool CanConvertFrom( + ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// Convert from a value to a + /// array. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A array if successful. + /// + public override object ConvertFrom( + ITypeDescriptorContext context, CultureInfo culture, object value) + { + string values = value as string; + if (values != null) + { + return StringUtils.DelimitedListToStringArray(values, this.ListSeparator); + } + return base.ConvertFrom(context, culture, value); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/TimeSpanConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/TimeSpanConverter.cs new file mode 100644 index 00000000..347e4bd9 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/TimeSpanConverter.cs @@ -0,0 +1,110 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converter for instances. + /// + /// Bruno Baia + /// $Id: TimeSpanConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class TimeSpanConverter : System.ComponentModel.TimeSpanConverter + { + #region Constants + + private const string DaySpecifier = "d"; + private const string HourSpecifier = "h"; + private const string MinuteSpecifier = "m"; + private const string SecondSpecifier = "s"; + private const string MillisecondSpecifier = "ms"; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public TimeSpanConverter() { } + + #endregion + + #region Methods + + /// + /// Convert from a string value to a instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A if successful. + /// + public override object ConvertFrom( + ITypeDescriptorContext context, + CultureInfo culture, object value) + { + if (value is string) + { + try + { + string timeSpan = ((string)value).ToLower(); + int specifierLengh = (timeSpan.EndsWith(MillisecondSpecifier)) ? 2 : 1; + int time = int.Parse(timeSpan.Substring(0, timeSpan.Length - specifierLengh)); + + switch (timeSpan.Substring(timeSpan.Length - specifierLengh, specifierLengh)) + { + case MillisecondSpecifier: + return TimeSpan.FromMilliseconds((double)time); + case SecondSpecifier: + return TimeSpan.FromSeconds((double)time); + case MinuteSpecifier: + return TimeSpan.FromMinutes((double)time); + case HourSpecifier: + return TimeSpan.FromHours((double)time); + case DaySpecifier: + return TimeSpan.FromDays((double)time); + } + } + catch { } + } + return base.ConvertFrom(context, culture, value); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs b/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs new file mode 100644 index 00000000..9fd69a6d --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs @@ -0,0 +1,211 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; + +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Utility methods that are used to convert objects from one type into another. + /// + /// Aleksandar Seovic + /// $Id: TypeConversionUtils.cs,v 1.4 2008/03/20 23:58:16 oakinger Exp $ + public class TypeConversionUtils + { + /// + /// Convert the value to the required (if necessary from a string). + /// + /// The proposed change value. + /// + /// The we must convert to. + /// + /// Property name, used for error reporting purposes... + /// + /// If there is an internal error. + /// + /// The new value, possibly the result of type conversion. + public static object ConvertValueIfNecessary(Type requiredType, object newValue, string propertyName) + { + if (newValue != null) + { + // if it is assignable, return the value right away + if (IsAssignableFrom(newValue, requiredType)) + { + return newValue; + } + + // if required type is an array, convert all the elements + if (requiredType != null && requiredType.IsArray) + { + // convert individual elements to array elements + Type componentType = requiredType.GetElementType(); + if (newValue is IList) + { + IList elements = (IList) newValue; + return ToArrayWithTypeConversion(componentType, elements, propertyName); + } + else if (newValue is string) + { + if (requiredType.Equals(typeof(char[]))) + { + return ((string) newValue).ToCharArray(); + } + else + { + string[] elements = StringUtils.CommaDelimitedListToStringArray((string) newValue); + return ToArrayWithTypeConversion(componentType, elements, propertyName); + } + } + else if (!newValue.GetType().IsArray) + { + Array result = Array.CreateInstance(componentType, 1); + object val = ConvertValueIfNecessary(componentType, newValue, propertyName); + result.SetValue(val, 0); + return result; + } + } + + // try to convert using type converter + try + { + TypeConverter typeConverter = TypeConverterRegistry.GetConverter(requiredType); + if (typeConverter != null && typeConverter.CanConvertFrom(newValue.GetType())) + { + try + { + newValue = typeConverter.ConvertFrom(newValue); + } + catch + { + if (newValue is string) + { + newValue = typeConverter.ConvertFromInvariantString((string)newValue); + } + } + } + else + { + typeConverter = TypeConverterRegistry.GetConverter(newValue.GetType()); + if (typeConverter != null && typeConverter.CanConvertTo(requiredType)) + { + newValue = typeConverter.ConvertTo(newValue, requiredType); + } + else + { + // look if it's an enum + if (requiredType != null + && requiredType.IsEnum + && (!(newValue is float) + && (!(newValue is double)))) + { + // convert numeric value into enum's underlying type + Type numericType = Enum.GetUnderlyingType(requiredType); + newValue = Convert.ChangeType(newValue, numericType); + + if (Enum.IsDefined(requiredType, newValue)) + { + newValue = Enum.ToObject(requiredType, newValue); + } + else + { + throw new TypeMismatchException( + CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType); + } + } + else if (newValue is IConvertible) + { + // last resort - try ChangeType + newValue = Convert.ChangeType(newValue, requiredType); + } + else + { + throw new TypeMismatchException( + CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType); + } + } + } + } + catch (Exception ex) + { + throw new TypeMismatchException( + CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType, ex); + } + if (newValue == null) + { + throw new TypeMismatchException( + CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType); + } + } + return newValue; + } + + private static object ToArrayWithTypeConversion(Type componentType, IList elements, string propertyName) + { + Array destination = Array.CreateInstance(componentType, elements.Count); + for (int i = 0; i < elements.Count; ++i) + { + object value = ConvertValueIfNecessary(componentType, elements[i], propertyName + "[" + i + "]"); + destination.SetValue(value, i); + } + return destination; + } + + private static bool IsAssignableFrom(object newValue, Type requiredType) + { + if (newValue is MarshalByRefObject) + { + //TODO see what type of type checking can be done. May need to + //preserve information when proxy was created by SaoServiceExporter. + return true; + } + if (requiredType == null) + { + return false; + } + return requiredType.IsAssignableFrom(newValue.GetType()); + } + + /// + /// Utility method to create a property change event. + /// + /// + /// The full name of the property that has changed. + /// + /// The property old value + /// The property new value + /// + /// A new . + /// + private static PropertyChangeEventArgs CreatePropertyChangeEventArgs(string fullPropertyName, object oldValue, + object newValue) + { + return new PropertyChangeEventArgs(fullPropertyName, oldValue, newValue); + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/TypeConverterRegistry.cs b/src/Spring/Spring.Core/Core/TypeConversion/TypeConverterRegistry.cs new file mode 100644 index 00000000..caaf5cd0 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/TypeConverterRegistry.cs @@ -0,0 +1,167 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Drawing; +using System.IO; +using System.Net; +using System.Resources; +using System.Text.RegularExpressions; +using Microsoft.Win32; + +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Registry class that allows users to register and retrieve type converters. + /// + /// Aleksandar Seovic + /// $Id: TypeConverterRegistry.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class TypeConverterRegistry + { + /// + /// Name of the .Net config section that contains Spring.Net type aliases. + /// + private const string TypeConvertersSectionName = "spring/typeConverters"; + + private static IDictionary converters = new Hashtable(); + + /// + /// Registers standard and configured type converters. + /// + static TypeConverterRegistry() + { + lock (converters.SyncRoot) + { + converters[typeof(string[])] = new StringArrayConverter(); + converters[typeof(Type)] = new RuntimeTypeConverter(); + converters[typeof(Color)] = new RGBColorConverter(); + converters[typeof(Uri)] = new UriConverter(); + converters[typeof(FileInfo)] = new FileInfoConverter(); + converters[typeof(Stream)] = new StreamConverter(); + converters[typeof(NameValueCollection)] = new NameValueConverter(); + converters[typeof(ResourceManager)] = new ResourceManagerConverter(); + converters[typeof(Regex)] = new RegexConverter(); + converters[typeof(TimeSpan)] = new TimeSpanConverter(); + converters[typeof(ICredentials)] = new CredentialConverter(); + converters[typeof(NetworkCredential)] = new CredentialConverter(); + converters[typeof(RegistryKey)] = new RegistryKeyConverter(); + + // register user-defined type converters + ConfigurationUtils.GetSection(TypeConvertersSectionName); + } + } + + /// + /// Returns for the specified type. + /// + /// Type to get the converter for. + /// a type converter for the specified type. + /// If is null. + public static TypeConverter GetConverter(Type type) + { + AssertUtils.ArgumentNotNull(type, "type"); + + TypeConverter converter = (TypeConverter) converters[type]; + if (converter == null) + { + if (type.IsEnum) + { + converter = new EnumConverter(type); + } + else + { + converter = TypeDescriptor.GetConverter(type); + } + } + + return converter; + } + + /// + /// Registers for the specified type. + /// + /// Type to register the converter for. + /// Type converter to register. + /// If either of arguments is null. + public static void RegisterConverter(Type type, TypeConverter converter) + { + AssertUtils.ArgumentNotNull(type, "type"); + AssertUtils.ArgumentNotNull(converter, "converter"); + + lock (converters.SyncRoot) + { + converters[type] = converter; + } + } + + /// + /// Registers for the specified type. + /// + /// + /// This is a convinience method that accepts the names of both + /// type to register converter for and the converter itself, + /// resolves them using , creates an + /// instance of type converter and calls overloaded + /// method. + /// + /// Type name of the type to register the converter for (can be a type alias). + /// Type name of the type converter to register (can be a type alias). + /// If either of arguments is null or empty string. + /// + /// If either of arguments fails to resolve to a valid . + /// + /// + /// If type converter does not derive from or if it cannot be instantiated. + /// + public static void RegisterConverter(string typeName, string converterTypeName) + { + AssertUtils.ArgumentHasText(typeName, "typeName"); + AssertUtils.ArgumentHasText(converterTypeName, "converterTypeName"); + + try + { + Type type = TypeResolutionUtils.ResolveType(typeName); + Type converterType = TypeResolutionUtils.ResolveType(converterTypeName); + if (!typeof(TypeConverter).IsAssignableFrom(converterType)) + { + throw new ArgumentException( + "Type specified as a 'converterTypeName' does not inherit from System.ComponentModel.TypeConverter"); + } + RegisterConverter(type,(TypeConverter) ObjectUtils.InstantiateType(converterType)); + } + catch (FatalReflectionException e) + { + throw new ArgumentException("Failed to create an instance of the specified type converter.", e); + } + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/UniqueKeyConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/UniqueKeyConverter.cs new file mode 100644 index 00000000..5bcd16be --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/UniqueKeyConverter.cs @@ -0,0 +1,162 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Importsusing System; + +using System; +using System.ComponentModel; +using System.Globalization; +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converts between instances of and their string representations. + /// + /// Erich Eichinger + /// $Id: UniqueKeyConverter.cs,v 1.1 2007/08/22 20:17:35 oakinger Exp $ + public class UniqueKeyConverter : TypeConverter + { + /// + /// Can we convert from the sourcetype to a ? + /// + /// + ///

    + /// Currently only supports conversion from a instance. + ///

    + ///
    + /// + /// A that provides a format context. + /// + /// + /// A that represents the you want to convert from. + /// + /// if the conversion is possible. + public override bool CanConvertFrom( + ITypeDescriptorContext context, Type sourceType) + { + return (typeof(string)==sourceType || typeof(UniqueKey)==sourceType); + } + + /// + /// Convert from a value to an instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A if successful, otherwise. + /// + ///The conversion cannot be performed. + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value == null) return null; + + if (!CanConvertFrom(context, value.GetType())) + { + throw new NotSupportedException(string.Format("Conversion from value of type '{0}' is not supported", value.GetType().FullName)); + } + + if (value is string) + { + return new UniqueKey(value as string); + } + else if (value is UniqueKey) + { + return value; + } + + throw new NotSupportedException(string.Format("Cannot convert from value of type '{0}' to '{1}'", value.GetType().FullName, typeof(UniqueKey).FullName)); + } + + /// + ///Returns whether this converter can convert the object to the specified type, using the specified context. + /// + ///An that provides a format context. + ///A that represents the type you want to convert to. + /// + ///true if this converter can perform the conversion; otherwise, false. + /// + /// + /// At the moment only conversion to string is supported. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return (typeof(string)==destinationType || typeof(UniqueKey)==destinationType); + } + + /// + ///Converts the given value object to the specified type, using the specified context and culture information. + /// + /// + /// + ///An that represents the converted value. + /// + /// + ///A . If null is passed, the current culture is assumed. + ///An that provides a format context. + ///The to convert the value parameter to. + ///The to convert. + ///The conversion cannot be performed. + ///The destinationType parameter is null. + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + // check destinationType + if (destinationType == null) + { + throw new ArgumentNullException("destinationType", "destinationType must not be null"); + } + if (!CanConvertTo(context, destinationType)) + { + throw new NotSupportedException(string.Format("conversion to destinationType '{0}' is not supported.", destinationType.FullName)); + } + + // value must either be null or a UniqueKey + if ( (value != null) && (typeof(UniqueKey) != value.GetType()) ) + { + throw new NotSupportedException(string.Format("value is not of type '{0}'", typeof(UniqueKey).FullName)); + } + + if (value == null) return null; + + if (typeof(string)==destinationType) + { + return (value).ToString(); + } + else if (typeof(UniqueKey)==destinationType) + { + return value; + } + + throw new NotSupportedException(string.Format("destinationType must be '{0}' but was '{1}'", typeof(string).FullName, destinationType)); + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeConversion/UriConverter.cs b/src/Spring/Spring.Core/Core/TypeConversion/UriConverter.cs new file mode 100644 index 00000000..9c681263 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeConversion/UriConverter.cs @@ -0,0 +1,114 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Converter for instances. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: UriConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class UriConverter : TypeConverter + { + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the + /// class. + /// + public UriConverter () {} + #endregion + + #region Methods + /// + /// Returns whether this converter can convert an object of one + /// to a + /// + /// + ///

    + /// Currently only supports conversion from a + /// instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// True if the conversion is possible. + public override bool CanConvertFrom ( + ITypeDescriptorContext context, + Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom (context, sourceType); + } + + /// + /// Convert from a string value to a instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A if successful. + /// + public override object ConvertFrom ( + ITypeDescriptorContext context, + CultureInfo culture, object value) + { + if (value is string) + { + try + { + return new Uri(value as string); + } + catch (UriFormatException ex) + { + throw new ArgumentException("Malformed URL: ", ex); + } + } + return base.ConvertFrom (context, culture, value); + } + #endregion + } +} diff --git a/src/Spring/Spring.Core/Core/TypeMismatchException.cs b/src/Spring/Spring.Core/Core/TypeMismatchException.cs new file mode 100644 index 00000000..c5effa58 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeMismatchException.cs @@ -0,0 +1,158 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using System.Text; +using Spring.Core; + +#endregion + +namespace Spring.Core +{ + /// + /// Exception thrown on a mismatch when trying to set a property + /// or resolve an argument to a method invocation. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: TypeMismatchException.cs,v 1.1 2007/07/31 00:08:42 markpollack Exp $ + [Serializable] + public class TypeMismatchException : PropertyAccessException + { + /// + /// The string error code used to classify the exception. + /// + public override string ErrorCode + { + get { return "typeMismatch"; } + } + + /// + /// Creates a new instance of the TypeMismatchException class. + /// + public TypeMismatchException() + { + } + + /// + /// Creates a new instance of the TypeMismatchException class. + /// + /// + /// A message about the exception. + /// + public TypeMismatchException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the TypeMismatchException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public TypeMismatchException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the TypeMismatchException class describing the + /// property and required type that could not used to set a property on the target object. + /// + /// + /// The description of the property that was to be changed. + /// + /// The target conversion type. + public TypeMismatchException( + PropertyChangeEventArgs propertyChangeEventArgs, Type requiredType) : + base(BuildMessage(propertyChangeEventArgs, requiredType), propertyChangeEventArgs) + { + } + + /// + /// Creates a new instance of the TypeMismatchException class describing the + /// property, required type, and underlying exception that could not be used + /// to set a property on the target object. + /// + /// + /// The description of the property that was to be changed. + /// + /// The target conversion type. + /// The underlying exception. + public TypeMismatchException( + PropertyChangeEventArgs propertyChangeEventArgs, Type requiredType, Exception rootCause) : + base(BuildMessage(propertyChangeEventArgs, requiredType), propertyChangeEventArgs, rootCause) + { + } + + /// + /// Creates a new instance of the TypeMismatchException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected TypeMismatchException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + private static string BuildMessage(PropertyChangeEventArgs propertyChangeEventArgs, Type requiredType) + { + StringBuilder message = new StringBuilder(); + message.Append("Cannot convert property value of type ["); + if (propertyChangeEventArgs != null && propertyChangeEventArgs.NewValue != null) + { + message.Append(propertyChangeEventArgs.NewValue.GetType().FullName); + } + else + { + message.Append("null"); + } + message.Append("] to required type ["); + if (requiredType != null) + { + message.Append(requiredType.FullName); + } + else + { + message.Append("null"); + } + message.Append("] for property '"); + if (propertyChangeEventArgs != null && propertyChangeEventArgs.PropertyName != null) + { + message.Append(propertyChangeEventArgs.PropertyName); + } + message.Append("'."); + return message.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeResolution/CachedTypeResolver.cs b/src/Spring/Spring.Core/Core/TypeResolution/CachedTypeResolver.cs new file mode 100644 index 00000000..fb26323b --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeResolution/CachedTypeResolver.cs @@ -0,0 +1,125 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; + +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Resolves (instantiates) a by it's (possibly + /// assembly qualified) name, and caches the + /// instance against the type name. + /// + /// Rick Evans + /// Bruno Baia + /// Erich Eichinger + /// $Id: CachedTypeResolver.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class CachedTypeResolver : ITypeResolver + { + /// + /// The cache, mapping type names ( instances) against + /// instances. + /// + private IDictionary typeCache = new HybridDictionary(); + + private ITypeResolver typeResolver; + + /// + /// Creates a new instance of the class. + /// + /// + /// The that this instance will delegate + /// actual resolution to if a + /// cannot be found in this instance's cache. + /// + /// + /// If the supplied is . + /// + public CachedTypeResolver(ITypeResolver typeResolver) + { + AssertUtils.ArgumentNotNull(typeResolver, "typeResolver"); + this.typeResolver = typeResolver; + } + + /// + /// Resolves the supplied to a + /// + /// instance. + /// + /// + /// The (possibly partially assembly qualified) name of a + /// . + /// + /// + /// A resolved instance. + /// + /// + /// If the supplied could not be resolved + /// to a . + /// + public Type Resolve(string typeName) + { + if (StringUtils.IsNullOrEmpty(typeName)) + { + throw BuildTypeLoadException(typeName); + } + Type type = null; + try + { + lock (this.typeCache.SyncRoot) + { + type = this.typeCache[typeName] as Type; + if (type == null) + { + type = this.typeResolver.Resolve(typeName); + this.typeCache[typeName] = type; + } + } + } + catch (Exception ex) + { + if (ex is TypeLoadException) + { + throw; + } + throw BuildTypeLoadException(typeName, ex); + } + return type; + } + + private static TypeLoadException BuildTypeLoadException(string typeName) + { + return new TypeLoadException("Could not load type from string value '" + typeName + "'."); + } + + private static TypeLoadException BuildTypeLoadException(string typeName, Exception ex) + { + return new TypeLoadException("Could not load type from string value '" + typeName + "'.", ex); + } + } +} diff --git a/src/Spring/Spring.Core/Core/TypeResolution/GenericArgumentsHolder.cs b/src/Spring/Spring.Core/Core/TypeResolution/GenericArgumentsHolder.cs new file mode 100644 index 00000000..f43e3809 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeResolution/GenericArgumentsHolder.cs @@ -0,0 +1,223 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections; + +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Holder for the generic arguments when using type parameters. + /// + /// + ///

    + /// Type parameters can be applied to classes, interfaces, + /// structures, methods, delegates, etc... + ///

    + ///
    + public class GenericArgumentsHolder + { + #region Constants + + /// + /// The generic arguments prefix. + /// + public const char GenericArgumentsPrefix = '<'; + + /// + /// The generic arguments suffix. + /// + public const char GenericArgumentsSuffix = '>'; + + /// + /// The character that separates a list of generic arguments. + /// + public const char GenericArgumentsSeparator = ','; + + #endregion + + #region Fields + + private string unresolvedGenericTypeName; + private string unresolvedGenericMethodName; + private string[] unresolvedGenericArguments; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the GenericArgumentsHolder class. + /// + /// + /// The string value to parse looking for a generic definition + /// and retrieving its generic arguments. + /// + public GenericArgumentsHolder(string value) + { + ParseGenericArguments(value); + } + + #endregion + + #region Properties + + /// + /// The (unresolved) generic type name portion + /// of the original value when parsing a generic type. + /// + public string GenericTypeName + { + get { return unresolvedGenericTypeName; } + } + + /// + /// The (unresolved) generic method name portion + /// of the original value when parsing a generic method. + /// + public string GenericMethodName + { + get { return unresolvedGenericMethodName; } + } + + /// + /// Is the string value contains generic arguments ? + /// + /// + ///

    + /// A generic argument can be a type parameter or a type argument. + ///

    + ///
    + public bool ContainsGenericArguments + { + get + { + return (unresolvedGenericArguments != null && + unresolvedGenericArguments.Length > 0); + } + } + + /// + /// Is generic arguments only contains type parameters ? + /// + public bool IsGenericDefinition + { + get + { + if (unresolvedGenericArguments == null) + return false; + + foreach (string arg in unresolvedGenericArguments) + { + if (arg.Length > 0) + return false; + } + return true; + } + } + + #endregion + + #region Methods + + /// + /// Returns an array of unresolved generic arguments types. + /// + /// + ///

    + /// A empty string represents a type parameter that + /// did not have been substituted by a specific type. + ///

    + ///
    + /// + /// An array of strings that represents the unresolved generic + /// arguments types or an empty array if not generic. + /// + public string[] GetGenericArguments() + { + if (unresolvedGenericArguments == null) + return StringUtils.EmptyStrings; + + return unresolvedGenericArguments; + } + + private void ParseGenericArguments(string originalString) + { + int argsStartIndex + = originalString.IndexOf(GenericArgumentsPrefix); + if (argsStartIndex < 0) + { + unresolvedGenericTypeName = originalString; + unresolvedGenericMethodName = originalString; + } + else + { + int argsEndIndex = + originalString.LastIndexOf(GenericArgumentsSuffix); + if (argsEndIndex != -1) + { + unresolvedGenericMethodName = originalString.Remove( + argsStartIndex, argsEndIndex - argsStartIndex + 1); + + SplitGenericArguments(originalString.Substring( + argsStartIndex + 1, argsEndIndex - argsStartIndex - 1)); + + unresolvedGenericTypeName = originalString.Replace( + originalString.Substring(argsStartIndex, argsEndIndex - argsStartIndex + 1), + "`" + unresolvedGenericArguments.Length); + } + } + } + + private void SplitGenericArguments(string originalArgs) + { + IList args = new ArrayList(); + + int index = 0; + int cursor = originalArgs.IndexOf(GenericArgumentsSeparator, index); + while (cursor != -1) + { + string arg = originalArgs.Substring(index, cursor - index); + if (arg.Split(GenericArgumentsPrefix).Length == + arg.Split(GenericArgumentsSuffix).Length) + { + args.Add(arg.Trim()); + index = cursor + 1; + } + cursor = originalArgs.IndexOf(GenericArgumentsSeparator, cursor + 1); + } + args.Add(originalArgs.Substring(index, originalArgs.Length - index).Trim()); + + unresolvedGenericArguments = new string[args.Count]; + args.CopyTo(unresolvedGenericArguments, 0); + } + + #endregion + } +} + +#endif diff --git a/src/Spring/Spring.Core/Core/TypeResolution/GenericTypeResolver.cs b/src/Spring/Spring.Core/Core/TypeResolution/GenericTypeResolver.cs new file mode 100644 index 00000000..b5fe9425 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeResolution/GenericTypeResolver.cs @@ -0,0 +1,101 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Collections; +using System.Reflection; + +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Resolves a generic by name. + /// + /// Bruno Baia + /// $Id: GenericTypeResolver.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public class GenericTypeResolver : TypeResolver + { + /// + /// Resolves the supplied generic to a + /// instance. + /// + /// + /// The unresolved (possibly generic) name of a . + /// + /// + /// A resolved instance. + /// + /// + /// If the supplied could not be resolved + /// to a . + /// + public override Type Resolve(string typeName) + { + if (StringUtils.IsNullOrEmpty(typeName)) + { + throw BuildTypeLoadException(typeName); + } + GenericArgumentsHolder genericInfo = new GenericArgumentsHolder(typeName); + Type type = null; + try + { + if (genericInfo.ContainsGenericArguments) + { + type = TypeResolutionUtils.ResolveType(genericInfo.GenericTypeName); + if (!genericInfo.IsGenericDefinition) + { + string[] unresolvedGenericArgs = genericInfo.GetGenericArguments(); + Type[] genericArgs = new Type[unresolvedGenericArgs.Length]; + for (int i = 0; i < unresolvedGenericArgs.Length; i++) + { + genericArgs[i] = TypeResolutionUtils.ResolveType(unresolvedGenericArgs[i]); + } + type = type.MakeGenericType(genericArgs); + } + } + } + catch (Exception ex) + { + if (ex is TypeLoadException) + { + throw; + } + throw BuildTypeLoadException(typeName, ex); + } + + if (type == null) + { + type = base.Resolve(typeName); + } + + return type; + } + } +} + +#endif diff --git a/src/Spring/Spring.Core/Core/TypeResolution/ITypeResolver.cs b/src/Spring/Spring.Core/Core/TypeResolution/ITypeResolver.cs new file mode 100644 index 00000000..28b3d639 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeResolution/ITypeResolver.cs @@ -0,0 +1,62 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Resolves a by name. + /// + /// + ///

    + /// The rationale behind the creation of this interface is to centralise + /// the resolution of type names to instances + /// beyond that offered by the plain vanilla + /// method call. + ///

    + ///
    + /// Rick Evans + /// $Id: ITypeResolver.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public interface ITypeResolver + { + /// + /// Resolves the supplied to a + /// + /// instance. + /// + /// + /// The (possibly partially assembly qualified) name of a + /// . + /// + /// + /// A resolved instance. + /// + /// + /// If the supplied could not be resolved + /// to a . + /// + Type Resolve(string typeName); + } +} diff --git a/src/Spring/Spring.Core/Core/TypeResolution/TypeAssemblyHolder.cs b/src/Spring/Spring.Core/Core/TypeResolution/TypeAssemblyHolder.cs new file mode 100644 index 00000000..38fa0f63 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeResolution/TypeAssemblyHolder.cs @@ -0,0 +1,117 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Holds data about a and it's + /// attendant . + /// + public class TypeAssemblyHolder + { + #region Constants + + /// + /// The string that separates a name + /// from the name of it's attendant + /// in an assembly qualified type name. + /// + public const string TypeAssemblySeparator = ","; + + #endregion + + #region Fields + + private string unresolvedAssemblyName; + private string unresolvedTypeName; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the TypeAssemblyHolder class. + /// + /// + /// The unresolved name of a . + /// + public TypeAssemblyHolder(string unresolvedTypeName) + { + SplitTypeAndAssemblyNames(unresolvedTypeName); + } + + #endregion + + #region Properties + + /// + /// The (unresolved) type name portion of the original type name. + /// + public string TypeName + { + get { return unresolvedTypeName; } + } + + /// + /// The (unresolved, possibly partial) name of the attandant assembly. + /// + public string AssemblyName + { + get { return unresolvedAssemblyName; } + } + + /// + /// Is the type name being resolved assembly qualified? + /// + public bool IsAssemblyQualified + { + get { return StringUtils.HasText(AssemblyName); } + } + + #endregion + + #region Methods + + private void SplitTypeAndAssemblyNames(string originalTypeName) + { + int typeAssemblyIndex + = originalTypeName.IndexOf(TypeAssemblySeparator); + if (typeAssemblyIndex < 0) + { + unresolvedTypeName = originalTypeName; + } + else + { + unresolvedTypeName = originalTypeName.Substring( + 0, typeAssemblyIndex).Trim(); + unresolvedAssemblyName = originalTypeName.Substring( + typeAssemblyIndex + 1).Trim(); + } + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Core/TypeResolution/TypeRegistry.cs b/src/Spring/Spring.Core/Core/TypeResolution/TypeRegistry.cs new file mode 100644 index 00000000..1fb1b35a --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeResolution/TypeRegistry.cs @@ -0,0 +1,685 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Provides access to a central registry of aliased s. + /// + /// + ///

    + /// Simplifies configuration by allowing aliases to be used instead of + /// fully qualified type names. + ///

    + ///

    + /// Comes 'pre-loaded' with a number of convenience alias' for the more + /// common types; an example would be the 'int' (or 'Integer' + /// for Visual Basic.NET developers) alias for the + /// type. + ///

    + ///
    + /// Aleksandar Seovic + /// + /// $Id: TypeRegistry.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public sealed class TypeRegistry + { + #region Constants + + /// + /// Name of the .Net config section that contains Spring.Net type aliases. + /// + private const string TypeAliasesSectionName = "spring/typeAliases"; + + /// + /// The alias around the 'int' type. + /// + public const string Int32Alias = "int"; + + /// + /// The alias around the 'Integer' type (Visual Basic.NET style). + /// + public const string Int32AliasVB = "Integer"; + + /// + /// The alias around the 'int[]' array type. + /// + public const string Int32ArrayAlias = "int[]"; + + /// + /// The alias around the 'Integer()' array type (Visual Basic.NET style). + /// + public const string Int32ArrayAliasVB = "Integer()"; + + /// + /// The alias around the 'decimal' type. + /// + public const string DecimalAlias = "decimal"; + + /// + /// The alias around the 'Decimal' type (Visual Basic.NET style). + /// + public const string DecimalAliasVB = "Decimal"; + + /// + /// The alias around the 'decimal[]' array type. + /// + public const string DecimalArrayAlias = "decimal[]"; + + /// + /// The alias around the 'Decimal()' array type (Visual Basic.NET style). + /// + public const string DecimalArrayAliasVB = "Decimal()"; + + /// + /// The alias around the 'char' type. + /// + public const string CharAlias = "char"; + + /// + /// The alias around the 'Char' type (Visual Basic.NET style). + /// + public const string CharAliasVB = "Char"; + + /// + /// The alias around the 'char[]' array type. + /// + public const string CharArrayAlias = "char[]"; + + /// + /// The alias around the 'Char()' array type (Visual Basic.NET style). + /// + public const string CharArrayAliasVB = "Char()"; + + /// + /// The alias around the 'long' type. + /// + public const string Int64Alias = "long"; + + /// + /// The alias around the 'Long' type (Visual Basic.NET style). + /// + public const string Int64AliasVB = "Long"; + + /// + /// The alias around the 'long[]' array type. + /// + public const string Int64ArrayAlias = "long[]"; + + /// + /// The alias around the 'Long()' array type (Visual Basic.NET style). + /// + public const string Int64ArrayAliasVB = "Long()"; + + /// + /// The alias around the 'short' type. + /// + public const string Int16Alias = "short"; + + /// + /// The alias around the 'Short' type (Visual Basic.NET style). + /// + public const string Int16AliasVB = "Short"; + + /// + /// The alias around the 'short[]' array type. + /// + public const string Int16ArrayAlias = "short[]"; + + /// + /// The alias around the 'Short()' array type (Visual Basic.NET style). + /// + public const string Int16ArrayAliasVB = "Short()"; + + /// + /// The alias around the 'unsigned int' type. + /// + public const string UInt32Alias = "uint"; + + /// + /// The alias around the 'unsigned long' type. + /// + public const string UInt64Alias = "ulong"; + + /// + /// The alias around the 'ulong[]' array type. + /// + public const string UInt64ArrayAlias = "ulong[]"; + + /// + /// The alias around the 'uint[]' array type. + /// + public const string UInt32ArrayAlias = "uint[]"; + + /// + /// The alias around the 'unsigned short' type. + /// + public const string UInt16Alias = "ushort"; + + /// + /// The alias around the 'ushort[]' array type. + /// + public const string UInt16ArrayAlias = "ushort[]"; + + /// + /// The alias around the 'double' type. + /// + public const string DoubleAlias = "double"; + + /// + /// The alias around the 'Double' type (Visual Basic.NET style). + /// + public const string DoubleAliasVB = "Double"; + + /// + /// The alias around the 'double[]' array type. + /// + public const string DoubleArrayAlias = "double[]"; + + /// + /// The alias around the 'Double()' array type (Visual Basic.NET style). + /// + public const string DoubleArrayAliasVB = "Double()"; + + /// + /// The alias around the 'float' type. + /// + public const string FloatAlias = "float"; + + /// + /// The alias around the 'Single' type (Visual Basic.NET style). + /// + public const string SingleAlias = "Single"; + + /// + /// The alias around the 'float[]' array type. + /// + public const string FloatArrayAlias = "float[]"; + + /// + /// The alias around the 'Single()' array type (Visual Basic.NET style). + /// + public const string SingleArrayAliasVB = "Single()"; + + /// + /// The alias around the 'DateTime' type. + /// + public const string DateTimeAlias = "DateTime"; + + /// + /// The alias around the 'DateTime' type (C# style). + /// + public const string DateAlias = "date"; + + /// + /// The alias around the 'DateTime' type (Visual Basic.NET style). + /// + public const string DateAliasVB = "Date"; + + /// + /// The alias around the 'DateTime[]' array type. + /// + public const string DateTimeArrayAlias = "DateTime[]"; + + /// + /// The alias around the 'DateTime[]' array type. + /// + public const string DateTimeArrayAliasCSharp = "date[]"; + + /// + /// The alias around the 'DateTime()' array type (Visual Basic.NET style). + /// + public const string DateTimeArrayAliasVB = "DateTime()"; + + /// + /// The alias around the 'bool' type. + /// + public const string BoolAlias = "bool"; + + /// + /// The alias around the 'Boolean' type (Visual Basic.NET style). + /// + public const string BoolAliasVB = "Boolean"; + + /// + /// The alias around the 'bool[]' array type. + /// + public const string BoolArrayAlias = "bool[]"; + + /// + /// The alias around the 'Boolean()' array type (Visual Basic.NET style). + /// + public const string BoolArrayAliasVB = "Boolean()"; + + /// + /// The alias around the 'string' type. + /// + public const string StringAlias = "string"; + + /// + /// The alias around the 'string' type (Visual Basic.NET style). + /// + public const string StringAliasVB = "String"; + + /// + /// The alias around the 'string[]' array type. + /// + public const string StringArrayAlias = "string[]"; + + /// + /// The alias around the 'string[]' array type (Visual Basic.NET style). + /// + public const string StringArrayAliasVB = "String()"; + + /// + /// The alias around the 'object' type. + /// + public const string ObjectAlias = "object"; + + /// + /// The alias around the 'object' type (Visual Basic.NET style). + /// + public const string ObjectAliasVB = "Object"; + + /// + /// The alias around the 'object[]' array type. + /// + public const string ObjectArrayAlias = "object[]"; + + /// + /// The alias around the 'object[]' array type (Visual Basic.NET style). + /// + public const string ObjectArrayAliasVB = "Object()"; + +#if NET_2_0 + /// + /// The alias around the 'int?' type. + /// + public const string NullableInt32Alias = "int?"; + + /// + /// The alias around the 'int?[]' array type. + /// + public const string NullableInt32ArrayAlias = "int?[]"; + + /// + /// The alias around the 'decimal?' type. + /// + public const string NullableDecimalAlias = "decimal?"; + + /// + /// The alias around the 'decimal?[]' array type. + /// + public const string NullableDecimalArrayAlias = "decimal?[]"; + + /// + /// The alias around the 'char?' type. + /// + public const string NullableCharAlias = "char?"; + + /// + /// The alias around the 'char?[]' array type. + /// + public const string NullableCharArrayAlias = "char?[]"; + + /// + /// The alias around the 'long?' type. + /// + public const string NullableInt64Alias = "long?"; + + /// + /// The alias around the 'long?[]' array type. + /// + public const string NullableInt64ArrayAlias = "long?[]"; + + /// + /// The alias around the 'short?' type. + /// + public const string NullableInt16Alias = "short?"; + + /// + /// The alias around the 'short?[]' array type. + /// + public const string NullableInt16ArrayAlias = "short?[]"; + + /// + /// The alias around the 'unsigned int?' type. + /// + public const string NullableUInt32Alias = "uint?"; + + /// + /// The alias around the 'unsigned long?' type. + /// + public const string NullableUInt64Alias = "ulong?"; + + /// + /// The alias around the 'ulong?[]' array type. + /// + public const string NullableUInt64ArrayAlias = "ulong?[]"; + + /// + /// The alias around the 'uint?[]' array type. + /// + public const string NullableUInt32ArrayAlias = "uint?[]"; + + /// + /// The alias around the 'unsigned short?' type. + /// + public const string NullableUInt16Alias = "ushort?"; + + /// + /// The alias around the 'ushort?[]' array type. + /// + public const string NullableUInt16ArrayAlias = "ushort?[]"; + + /// + /// The alias around the 'double?' type. + /// + public const string NullableDoubleAlias = "double?"; + + /// + /// The alias around the 'double?[]' array type. + /// + public const string NullableDoubleArrayAlias = "double?[]"; + + /// + /// The alias around the 'float?' type. + /// + public const string NullableFloatAlias = "float?"; + + /// + /// The alias around the 'float?[]' array type. + /// + public const string NullableFloatArrayAlias = "float?[]"; + + /// + /// The alias around the 'bool?' type. + /// + public const string NullableBoolAlias = "bool?"; + + /// + /// The alias around the 'bool?[]' array type. + /// + public const string NullableBoolArrayAlias = "bool?[]"; +#endif + + #endregion + + #region Fields + + private static IDictionary types = new Hashtable(); + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Registers standard and user-configured type aliases. + /// + static TypeRegistry() + { + lock (types.SyncRoot) + { + types["Int32"] = typeof(Int32); + types[Int32Alias] = typeof(Int32); + types[Int32AliasVB] = typeof(Int32); + types[Int32ArrayAlias] = typeof(Int32[]); + types[Int32ArrayAliasVB] = typeof(Int32[]); + + types["UInt32"] = typeof(UInt32); + types[UInt32Alias] = typeof(UInt32); + types[UInt32ArrayAlias] = typeof(UInt32[]); + + types["Int16"] = typeof(Int16); + types[Int16Alias] = typeof(Int16); + types[Int16AliasVB] = typeof(Int16); + types[Int16ArrayAlias] = typeof(Int16[]); + types[Int16ArrayAliasVB] = typeof(Int16[]); + + types["UInt16"] = typeof(UInt16); + types[UInt16Alias] = typeof(UInt16); + types[UInt16ArrayAlias] = typeof(UInt16[]); + + types["Int64"] = typeof(Int64); + types[Int64Alias] = typeof(Int64); + types[Int64AliasVB] = typeof(Int64); + types[Int64ArrayAlias] = typeof(Int64[]); + types[Int64ArrayAliasVB] = typeof(Int64[]); + + types["UInt64"] = typeof(UInt64); + types[UInt64Alias] = typeof(UInt64); + types[UInt64ArrayAlias] = typeof(UInt64[]); + + types[DoubleAlias] = typeof(double); + types[DoubleAliasVB] = typeof(double); + types[DoubleArrayAlias] = typeof(double[]); + types[DoubleArrayAliasVB] = typeof(double[]); + + types[FloatAlias] = typeof(float); + types[SingleAlias] = typeof(float); + types[FloatArrayAlias] = typeof(float[]); + types[SingleArrayAliasVB] = typeof(float[]); + + types[DateTimeAlias] = typeof(DateTime); + types[DateAlias] = typeof(DateTime); + types[DateAliasVB] = typeof(DateTime); + types[DateTimeArrayAlias] = typeof(DateTime[]); + types[DateTimeArrayAliasCSharp] = typeof(DateTime[]); + types[DateTimeArrayAliasVB] = typeof(DateTime[]); + + types[BoolAlias] = typeof(bool); + types[BoolAliasVB] = typeof(bool); + types[BoolArrayAlias] = typeof(bool[]); + types[BoolArrayAliasVB] = typeof(bool[]); + + types[DecimalAlias] = typeof(decimal); + types[DecimalAliasVB] = typeof(decimal); + types[DecimalArrayAlias] = typeof(decimal[]); + types[DecimalArrayAliasVB] = typeof(decimal[]); + + types[CharAlias] = typeof(char); + types[CharAliasVB] = typeof(char); + types[CharArrayAlias] = typeof(char[]); + types[CharArrayAliasVB] = typeof(char[]); + + types[StringAlias] = typeof(string); + types[StringAliasVB] = typeof(string); + types[StringArrayAlias] = typeof(string[]); + types[StringArrayAliasVB] = typeof(string[]); + + types[ObjectAlias] = typeof(object); + types[ObjectAliasVB] = typeof(object); + types[ObjectArrayAlias] = typeof(object[]); + types[ObjectArrayAliasVB] = typeof(object[]); + +#if NET_2_0 + types[NullableInt32Alias] = typeof(int?); + types[NullableInt32ArrayAlias] = typeof(int?[]); + + types[NullableDecimalAlias] = typeof(decimal?); + types[NullableDecimalArrayAlias] = typeof(decimal?[]); + + types[NullableCharAlias] = typeof(char?); + types[NullableCharArrayAlias] = typeof(char?[]); + + types[NullableInt64Alias] = typeof(long?); + types[NullableInt64ArrayAlias] = typeof(long?[]); + + types[NullableInt16Alias] = typeof(short?); + types[NullableInt16ArrayAlias] = typeof(short?[]); + + types[NullableUInt32Alias] = typeof(uint?); + types[NullableUInt32ArrayAlias] = typeof(uint?[]); + + types[NullableUInt64Alias] = typeof(ulong?); + types[NullableUInt64ArrayAlias] = typeof(ulong?[]); + + types[NullableUInt16Alias] = typeof(ushort?); + types[NullableUInt16ArrayAlias] = typeof(ushort?[]); + + types[NullableDoubleAlias] = typeof(double?); + types[NullableDoubleArrayAlias] = typeof(double?[]); + + types[NullableFloatAlias] = typeof(float?); + types[NullableFloatArrayAlias] = typeof(float?[]); + + types[NullableBoolAlias] = typeof(bool?); + types[NullableBoolArrayAlias] = typeof(bool?[]); +#endif + + // register user-configured type aliases + ConfigurationUtils.GetSection(TypeAliasesSectionName); + } + } + + #endregion + + /// + /// Registers an alias for the specified . + /// + /// + ///

    + /// This overload does eager resolution of the + /// referred to by the parameter. It will throw a + /// if the referred + /// to by the parameter cannot be resolved. + ///

    + ///
    + /// + /// A string that will be used as an alias for the specified + /// . + /// + /// + /// The (possibly partially assembly qualified) name of the + /// to register the alias for. + /// + /// + /// If either of the supplied parameters is or + /// contains only whitespace character(s). + /// + /// + /// If the referred to by the supplied + /// cannot be loaded. + /// + public static void RegisterType(string alias, string typeName) + { + AssertUtils.ArgumentHasText(alias, "alias"); + AssertUtils.ArgumentHasText(typeName, "typeName"); + + Type type = TypeResolutionUtils.ResolveType(typeName); +#if NET_2_0 + if (type.IsGenericTypeDefinition) + alias += ("`" + type.GetGenericArguments().Length); +#endif + RegisterType(alias, type); + } + + /// + /// Registers short type name as an alias for + /// the supplied . + /// + /// + /// The to register. + /// + /// + /// If the supplied is . + /// + public static void RegisterType(Type type) + { + AssertUtils.ArgumentNotNull(type, "type"); + + lock (types.SyncRoot) + { + types[type.Name] = type; + } + } + + /// + /// Registers an alias for the supplied . + /// + /// + /// The alias for the supplied . + /// + /// + /// The to register the supplied under. + /// + /// + /// If the supplied is ; or if + /// the supplied is or + /// contains only whitespace character(s). + /// + public static void RegisterType(string alias, Type type) + { + AssertUtils.ArgumentHasText(alias, "alias"); + AssertUtils.ArgumentNotNull(type, "type"); + + lock (types.SyncRoot) + { + types[alias] = type; + } + } + + /// + /// Resolves the supplied to a . + /// + /// + /// The alias to resolve. + /// + /// + /// The the supplied was + /// associated with, or if no + /// was previously registered for the supplied . + /// + /// + /// If the supplied is or + /// contains only whitespace character(s). + /// + public static Type ResolveType(string alias) + { + AssertUtils.ArgumentHasText(alias, "alias"); + return (Type) types[alias]; + } + + /// + /// Returns a flag specifying whether TypeRegistry contains + /// specified alias or not. + /// + /// + /// Alias to check. + /// + /// + /// true if the specified type alias is registered, + /// false otherwise. + /// + public static bool ContainsAlias(string alias) + { + return types.Contains(alias); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeResolution/TypeResolutionUtils.cs b/src/Spring/Spring.Core/Core/TypeResolution/TypeResolutionUtils.cs new file mode 100644 index 00000000..77c939ab --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeResolution/TypeResolutionUtils.cs @@ -0,0 +1,215 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using System.Text.RegularExpressions; +using System.Reflection; + +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Helper methods with regard to type resolution. + /// + /// + ///

    + /// Not intended to be used directly by applications. + ///

    + ///
    + /// Bruno Baia + /// $Id: TypeResolutionUtils.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + public sealed class TypeResolutionUtils + { + #region Fields + +#if NET_2_0 + private static readonly ITypeResolver internalTypeResolver + = new CachedTypeResolver(new GenericTypeResolver()); +#else + private static readonly ITypeResolver internalTypeResolver + = new CachedTypeResolver(new TypeResolver()); +#endif + + #endregion + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private TypeResolutionUtils() + { + } + + // CLOVER:ON + + #endregion + + #region Methods + + /// + /// Resolves the supplied type name into a + /// instance. + /// + /// + ///

    + /// If you require special resolution, do + /// not use this method, but rather instantiate + /// your own . + ///

    + ///
    + /// + /// The (possibly partially assembly qualified) name of a + /// . + /// + /// + /// A resolved instance. + /// + /// + /// If the type cannot be resolved. + /// + public static Type ResolveType(string typeName) + { + Type type = TypeRegistry.ResolveType(typeName); + if (type == null) + { + type = internalTypeResolver.Resolve(typeName); + } + return type; + } + + /// + /// Resolves a string array of interface names to + /// a array. + /// + /// + /// An array of valid interface names. Each name must include the full + /// interface and assembly name. + /// + /// An array of interface s. + /// + /// If any of the interfaces can't be loaded. + /// + /// + /// If any of the s specified is not an interface. + /// + /// + /// If (or any of its elements ) is + /// . + /// + public static Type[] ResolveInterfaceArray(string[] interfaceNames) + { + AssertUtils.ArgumentNotNull(interfaceNames, "interfaceNames"); + + ArrayList interfaces = new ArrayList(); + for (int i = 0; i < interfaceNames.Length; i++) + { + string interfaceName = interfaceNames[i]; + AssertUtils.ArgumentNotNull(interfaceName, + string.Format(CultureInfo.InvariantCulture, "interfaceNames[{0}]", i)); + Type resolvedInterface = ResolveType(interfaceName); + if (!resolvedInterface.IsInterface) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "[{0}] is a class.", + resolvedInterface.FullName)); + } + interfaces.Add(resolvedInterface); + interfaces.AddRange(resolvedInterface.GetInterfaces()); + } + return (Type[])interfaces.ToArray(typeof(Type)); + } + + #region MethodMatch + + // TODO : Use the future Pointcut expression language instead + + private readonly static Regex methodMatchRegex = new Regex( + @"(?([\w]+\.)*[\w\*]+)(?(\((?[\w\.]+(,[\w\.]+)*)*\))?)", + RegexOptions.Compiled | RegexOptions.ExplicitCapture); + + /// + /// Match a method against the given pattern. + /// + /// the pattern to match against. + /// the method to match. + /// + /// if the method matches the given pattern; otherwise . + /// + /// + /// If the supplied is invalid. + /// + public static bool MethodMatch(string pattern, MethodInfo method) + { + Match m = methodMatchRegex.Match(pattern); + + if (!m.Success) + throw new ArgumentException(String.Format("The pattern [{0}] is not well-formed.", pattern)); + + // Check method name + string methodNamePattern = m.Groups["methodName"].Value; + if (!PatternMatchUtils.SimpleMatch(methodNamePattern, method.Name)) + return false; + + if (m.Groups["parameters"].Value.Length > 0) + { + // Check parameter types + string parameters = m.Groups["parameterTypes"].Value; + string[] paramTypes = + (parameters.Length == 0) + ? new string[0] + : StringUtils.DelimitedListToStringArray(parameters, ","); + ParameterInfo[] paramInfos = method.GetParameters(); + + // Verify parameter count + if (paramTypes.Length != paramInfos.Length) + return false; + + // Match parameter types + for (int i = 0; i < paramInfos.Length; i++) + { + if (paramInfos[i].ParameterType != TypeResolutionUtils.ResolveType(paramTypes[i])) + return false; + } + } + + return true; + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Core/TypeResolution/TypeResolver.cs b/src/Spring/Spring.Core/Core/TypeResolution/TypeResolver.cs new file mode 100644 index 00000000..e73f4827 --- /dev/null +++ b/src/Spring/Spring.Core/Core/TypeResolution/TypeResolver.cs @@ -0,0 +1,169 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; + +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Resolves a by name. + /// + /// Rick Evans + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: TypeResolver.cs,v 1.3 2007/08/27 15:18:24 oakinger Exp $ + public class TypeResolver : ITypeResolver + { + /// + /// Resolves the supplied to a + /// instance. + /// + /// + /// The unresolved (possibly partially assembly qualified) name + /// of a . + /// + /// + /// A resolved instance. + /// + /// + /// If the supplied could not be resolved + /// to a . + /// + public virtual Type Resolve(string typeName) + { + if (StringUtils.IsNullOrEmpty(typeName)) + { + throw BuildTypeLoadException(typeName); + } + TypeAssemblyHolder typeInfo = new TypeAssemblyHolder(typeName); + Type type = null; + try + { + type = (typeInfo.IsAssemblyQualified) ? + LoadTypeDirectlyFromAssembly(typeInfo) : + LoadTypeByIteratingOverAllLoadedAssemblies(typeInfo); + } + catch (Exception ex) + { + if (ex is TypeLoadException) + { + throw; + } + throw BuildTypeLoadException(typeName, ex); + } + if (type == null) + { + throw BuildTypeLoadException(typeName); + } + return type; + } + + /// + /// Uses + /// to load an and then the attendant + /// referred to by the + /// parameter. + /// + /// + ///

    + /// is + /// deprecated in .NET 2.0, but is still used here (even when this class is + /// compiled for .NET 2.0); + /// will + /// still resolve (non-.NET Framework) local assemblies when given only the + /// display name of an assembly (the behaviour for .NET Framework assemblies + /// and strongly named assemblies is documented in the docs for the + /// method). + ///

    + ///
    + /// + /// The assembly and type to be loaded. + /// + /// + /// A , or . + /// + /// + /// + /// + private static Type LoadTypeDirectlyFromAssembly(TypeAssemblyHolder typeInfo) + { + Type type = null; + Assembly assembly = Assembly.LoadWithPartialName(typeInfo.AssemblyName); + if (assembly != null) + { + type = assembly.GetType(typeInfo.TypeName, true, true); + } + return type; + } + + /// + /// Uses + /// to load the attendant referred to by + /// the parameter. + /// + /// + /// The type to be loaded. + /// + /// + /// A , or . + /// + private static Type LoadTypeByIteratingOverAllLoadedAssemblies(TypeAssemblyHolder typeInfo) + { + Type type = null; + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly assembly in assemblies) + { + type = assembly.GetType(typeInfo.TypeName, false, false); + if (type != null) + { + break; + } + } + return type; + } + + /// + /// Creates a new instance + /// from the given + /// + protected static TypeLoadException BuildTypeLoadException(string typeName) + { + return new TypeLoadException("Could not load type from string value '" + typeName + "'."); + } + + /// + /// Creates a new instance + /// from the given with the given inner + /// + protected static TypeLoadException BuildTypeLoadException(string typeName, Exception ex) + { + return new TypeLoadException("Could not load type from string value '" + typeName + "'.", ex); + } + } +} diff --git a/src/Spring/Spring.Core/DataBinding/AbstractBinding.cs b/src/Spring/Spring.Core/DataBinding/AbstractBinding.cs new file mode 100644 index 00000000..0b17df5a --- /dev/null +++ b/src/Spring/Spring.Core/DataBinding/AbstractBinding.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections; +using Spring.Threading; +using Spring.Util; +using Spring.Validation; + +namespace Spring.DataBinding +{ + /// + /// Abstract base class for implementations. + /// + /// Aleksandar Seovic + /// $Id: AbstractBinding.cs,v 1.2 2008/02/05 20:40:25 aseovic Exp $ + public abstract class AbstractBinding : IBinding + { + #region Fields + + // each Binding instance needs its own ID + private readonly string BINDING_ID = Guid.NewGuid().ToString("N"); + + private BindingDirection direction = BindingDirection.Bidirectional; + private ErrorMessage errorMessage; + private string[] errorProviders; + + #endregion + + #region Properties + + /// + /// Gets or sets a flag specifying whether this binding is valid. + /// + /// + /// true if this binding evaluated without errors; + /// false otherwise. + /// + public bool IsValid + { + get + { + object val = LogicalThreadContext.GetData(GetIsValidKey()); + return val == null || (bool)val; + } + set + { + LogicalThreadContext.SetData(GetIsValidKey(), value); + } + } + + /// + /// Gets or sets the . + /// + /// The binding direction. + public BindingDirection Direction + { + get { return direction; } + set { direction = value; } + } + + /// + /// Gets the error message. + /// + /// The error message. + public ErrorMessage ErrorMessage + { + get { return errorMessage; } + } + + /// + /// Gets the error providers. + /// + public string[] ErrorProviders + { + get { return errorProviders; } + } + + #endregion + + #region IBinding Implementation + + /// + /// Binds source object to target object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + public void BindSourceToTarget(object source, object target, IValidationErrors validationErrors) + { + BindSourceToTarget(source, target, validationErrors, null); + } + + /// + /// Binds target object to source object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + public void BindTargetToSource(object source, object target, IValidationErrors validationErrors) + { + BindTargetToSource(source, target, validationErrors, null); + } + + /// + /// Binds source object to target object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + public abstract void BindSourceToTarget(object source, object target, IValidationErrors validationErrors, IDictionary variables); + + /// + /// Binds target object to source object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + public abstract void BindTargetToSource(object source, object target, IValidationErrors validationErrors, IDictionary variables); + + /// + /// Sets error message that should be displayed in the case + /// of a non-fatal binding error. + /// + /// + /// Resource ID of the error message. + /// + /// + /// List of error providers message should be added to. + /// + public void SetErrorMessage(string messageId, params string[] errorProviders) + { + AssertUtils.ArgumentHasText(messageId, "messageId"); + if (errorProviders == null || errorProviders.Length == 0) + { + throw new ArgumentException("At least one error provider has to be specified.", "providers"); + } + + this.errorMessage = new ErrorMessage(messageId, null); + this.errorProviders = errorProviders; + } + + #endregion + + #region Private Methods + + private string GetIsValidKey() + { + return "Binding." + BINDING_ID + ".IsValid"; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/DataBinding/AbstractSimpleBinding.cs b/src/Spring/Spring.Core/DataBinding/AbstractSimpleBinding.cs new file mode 100644 index 00000000..b9335d1f --- /dev/null +++ b/src/Spring/Spring.Core/DataBinding/AbstractSimpleBinding.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections; +using Spring.Globalization; +using Spring.Validation; + +namespace Spring.DataBinding +{ + /// + /// Abstract base class for simple, one-to-one implementations. + /// + /// Aleksandar Seovic + /// $Id: AbstractSimpleBinding.cs,v 1.4 2008/02/05 20:40:25 aseovic Exp $ + public abstract class AbstractSimpleBinding : AbstractBinding + { + #region Fields + + private IFormatter formatter; + + #endregion + + #region Constructor(s) + + /// + /// Initialize a new instance of without any + /// + protected AbstractSimpleBinding() + { + } + + /// + /// Initialize a new instance of with the + /// specified . + /// + protected AbstractSimpleBinding(IFormatter formatter) + { + this.formatter = formatter; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the to use. + /// + /// The formatter to use. + public IFormatter Formatter + { + get { return formatter; } + set { formatter = value; } + } + + #endregion + + #region IBinding Implementation + + /// + /// Binds source object to target object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + public override void BindSourceToTarget(object source, object target, IValidationErrors validationErrors, + IDictionary variables) + { + if (this.IsValid + && + (this.Direction == BindingDirection.Bidirectional || this.Direction == BindingDirection.SourceToTarget)) + { + try + { + DoBindSourceToTarget(source, target, variables); + this.IsValid = true; + } + catch (Exception) + { + this.IsValid = false; + if (this.ErrorMessage != null && validationErrors != null) + { + foreach (string provider in this.ErrorProviders) + { + validationErrors.AddError(provider, this.ErrorMessage); + } + } + else + { + throw; + } + } + } + } + + /// + /// Concrete implementation if source to target binding. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Variables that should be used during expression evaluation. + /// + protected virtual void DoBindSourceToTarget(object source, object target, IDictionary variables) + { + object value = this.GetSourceValue(source, variables); + if (this.Formatter != null && value is string) + { + value = this.Formatter.Parse((string) value); + } + this.SetTargetValue(target, value, variables); + } + + /// + /// Binds target object to source object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + public override void BindTargetToSource(object source, object target, IValidationErrors validationErrors, + IDictionary variables) + { + if (this.IsValid + && + (this.Direction == BindingDirection.Bidirectional || this.Direction == BindingDirection.TargetToSource)) + { + try + { + DoBindTargetToSource(source, target, variables); + this.IsValid = true; + } + catch (Exception) + { + this.IsValid = false; + if (this.ErrorMessage != null && validationErrors != null) + { + foreach (string provider in this.ErrorProviders) + { + validationErrors.AddError(provider, this.ErrorMessage); + } + } + else + { + throw; + } + } + } + } + + /// + /// Concrete implementation of target to source binding. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Variables that should be used during expression evaluation. + /// + protected virtual void DoBindTargetToSource(object source, object target, IDictionary variables) + { + object value = this.GetTargetValue(target, variables); + if (this.Formatter != null) + { + value = this.Formatter.Format(value); + } + this.SetSourceValue(source, value, variables); + } + + #endregion + + #region Abstract Methods + + /// + /// Gets the source value for the binding. + /// + /// + /// Source object to extract value from. + /// + /// + /// Variables for expression evaluation. + /// + /// + /// The source value for the binding. + /// + protected abstract object GetSourceValue(object source, IDictionary variables); + + /// + /// Sets the source value for the binding. + /// + /// + /// The source object to set the value on. + /// + /// + /// The value to set. + /// + /// + /// Variables for expression evaluation. + /// + protected abstract void SetSourceValue(object source, object value, IDictionary variables); + + /// + /// Gets the target value for the binding. + /// + /// + /// Source object to extract value from. + /// + /// + /// Variables for expression evaluation. + /// + /// + /// The target value for the binding. + /// + protected abstract object GetTargetValue(object target, IDictionary variables); + + /// + /// Sets the target value for the binding. + /// + /// + /// The target object to set the value on. + /// + /// + /// The value to set. + /// + /// + /// Variables for expression evaluation. + /// + protected abstract void SetTargetValue(object target, object value, IDictionary variables); + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/DataBinding/BaseBindingContainer.cs b/src/Spring/Spring.Core/DataBinding/BaseBindingContainer.cs new file mode 100644 index 00000000..eaebbbd1 --- /dev/null +++ b/src/Spring/Spring.Core/DataBinding/BaseBindingContainer.cs @@ -0,0 +1,262 @@ +using System.Collections; +using Spring.Globalization; +using Spring.Validation; + +namespace Spring.DataBinding +{ + /// + /// Base implementation of the . + /// + /// Aleksandar Seovic + /// $Id: BaseBindingContainer.cs,v 1.2 2008/02/05 20:40:25 aseovic Exp $ + public class BaseBindingContainer : IBindingContainer + { + #region Fields + + private IList bindings = new ArrayList(); + + #endregion + + #region Constructor(s) + + /// + /// Creates a new instance of . + /// + public BaseBindingContainer() + {} + + #endregion + + #region Properties + + /// + /// Gets a list of bindings for this container. + /// + /// + /// A list of bindings for this container. + /// + protected IList Bindings + { + get { return bindings; } + } + + #endregion + + #region IBindingContainer Implementation + + /// + /// Gets a value indicating whether this instance has bindings. + /// + /// + /// true if this instance has bindings; otherwise, false. + /// + public bool HasBindings + { + get { return bindings.Count > 0; } + } + + /// + /// Adds the binding. + /// + /// + /// Binding definition to add. + /// + /// + /// Added instance. + /// + public IBinding AddBinding(IBinding binding) + { + bindings.Add(binding); + return binding; + } + + /// + /// Adds the binding with a default + /// binding direction of . + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + /// + /// Added instance. + /// + public IBinding AddBinding(string sourceExpression, string targetExpression) + { + return AddBinding(sourceExpression, targetExpression, BindingDirection.Bidirectional, null); + } + + /// + /// Adds the binding. + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + /// + /// Binding direction. + /// + /// + /// Added instance. + /// + public IBinding AddBinding(string sourceExpression, string targetExpression, BindingDirection direction) + { + return AddBinding(sourceExpression, targetExpression, direction, null); + } + + /// + /// Adds the binding with a default + /// binding direction of . + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + /// + /// to use for value formatting and parsing. + /// + /// + /// Added instance. + /// + public IBinding AddBinding(string sourceExpression, string targetExpression, IFormatter formatter) + { + return AddBinding(sourceExpression, targetExpression, BindingDirection.Bidirectional, formatter); + } + + /// + /// Adds the binding. + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + /// + /// Binding direction. + /// + /// + /// to use for value formatting and parsing. + /// + /// + /// Added instance. + /// + public virtual IBinding AddBinding(string sourceExpression, string targetExpression, + BindingDirection direction, IFormatter formatter) + { + SimpleExpressionBinding binding = new SimpleExpressionBinding(sourceExpression, targetExpression); + binding.Direction = direction; + binding.Formatter = formatter; + bindings.Add(binding); + + return binding; + } + + #endregion + + #region IBinding Implementation + + /// + /// Binds source object to target object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + public void BindSourceToTarget(object source, object target, IValidationErrors validationErrors) + { + BindSourceToTarget(source, target, validationErrors, null); + } + + /// + /// Binds source object to target object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + public virtual void BindSourceToTarget(object source, object target, IValidationErrors validationErrors, + IDictionary variables) + { + foreach (IBinding binding in bindings) + { + binding.BindSourceToTarget(source, target, validationErrors); + } + } + + /// + /// Binds target object to source object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + public void BindTargetToSource(object source, object target, IValidationErrors validationErrors) + { + BindTargetToSource(source, target, validationErrors, null); + } + + /// + /// Binds target object to source object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + public virtual void BindTargetToSource(object source, object target, IValidationErrors validationErrors, + IDictionary variables) + { + foreach (IBinding binding in bindings) + { + binding.BindTargetToSource(source, target, validationErrors); + } + } + + /// + /// Sets error message that should be displayed in the case + /// of a non-fatal binding error. + /// + /// + /// Resource ID of the error message. + /// + /// + /// List of error providers message should be added to. + /// + public virtual void SetErrorMessage(string messageId, params string[] errorProviders) + {} + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/DataBinding/BaseBindingManager.cs b/src/Spring/Spring.Core/DataBinding/BaseBindingManager.cs new file mode 100644 index 00000000..b0d6391a --- /dev/null +++ b/src/Spring/Spring.Core/DataBinding/BaseBindingManager.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +using Spring.Globalization; +using Spring.Validation; + +namespace Spring.DataBinding +{ + /// + /// BaseBindingManager keeps track of all registered bindings and + /// represents an entry point for the binding and unbinding process. + /// + /// Aleksandar Seovic + /// $Id: BaseBindingManager.cs,v 1.1 2006/11/21 05:46:57 aseovic Exp $ + public class BaseBindingManager : BaseBindingContainer + { + #region Constructor(s) + + /// + /// Initializes a new instance of the class. + /// + public BaseBindingManager() + {} + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/DataBinding/BindingDirection.cs b/src/Spring/Spring.Core/DataBinding/BindingDirection.cs new file mode 100644 index 00000000..2be758c6 --- /dev/null +++ b/src/Spring/Spring.Core/DataBinding/BindingDirection.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.DataBinding +{ + /// + /// Enumeration that defines possible values for data binding direction. + /// + /// Aleksandar Seovic + /// $Id: BindingDirection.cs,v 1.2 2006/04/09 07:18:39 markpollack Exp $ + [Flags] + public enum BindingDirection + { + /// + /// Specifies that value from the control property should be bound to a data model. + /// + SourceToTarget = 0x0001, + + /// + /// Specifies that value from the data model should be bound to control property. + /// + TargetToSource = 0x0002, + + /// + /// Specifies that binding is bidirectional. + /// + Bidirectional = SourceToTarget | TargetToSource + } +} diff --git a/src/Spring/Spring.Core/DataBinding/IBinding.cs b/src/Spring/Spring.Core/DataBinding/IBinding.cs new file mode 100644 index 00000000..fa0c20e3 --- /dev/null +++ b/src/Spring/Spring.Core/DataBinding/IBinding.cs @@ -0,0 +1,88 @@ +using System.Collections; +using Spring.Validation; + +namespace Spring.DataBinding +{ + /// + /// An interface that defines the methods that have to be implemented by all data bindings. + /// + /// Aleksandar Seovic + /// $Id: IBinding.cs,v 1.3 2008/02/05 20:40:25 aseovic Exp $ + public interface IBinding + { + /// + /// Binds source object to target object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + void BindSourceToTarget(object source, object target, IValidationErrors validationErrors); + + /// + /// Binds source object to target object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + void BindSourceToTarget(object source, object target, IValidationErrors validationErrors, IDictionary variables); + + /// + /// Binds target object to source object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + void BindTargetToSource(object source, object target, IValidationErrors validationErrors); + + /// + /// Binds target object to source object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + void BindTargetToSource(object source, object target, IValidationErrors validationErrors, IDictionary variables); + + /// + /// Sets error message that should be displayed in the case + /// of a non-fatal binding error. + /// + /// + /// Resource ID of the error message. + /// + /// + /// List of error providers message should be added to. + /// + void SetErrorMessage(string messageId, params string[] errorProviders); + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/DataBinding/IBindingContainer.cs b/src/Spring/Spring.Core/DataBinding/IBindingContainer.cs new file mode 100644 index 00000000..f63dc54e --- /dev/null +++ b/src/Spring/Spring.Core/DataBinding/IBindingContainer.cs @@ -0,0 +1,120 @@ +using Spring.Globalization; + +namespace Spring.DataBinding +{ + /// + /// An interface that has to be implemented by all data binding containers. + /// + /// Aleksandar Seovic + /// $Id: IBindingContainer.cs,v 1.1 2006/11/21 05:46:57 aseovic Exp $ + public interface IBindingContainer : IBinding + { + /// + /// Gets a value indicating whether this data binding container + /// has bindings. + /// + /// + /// true if this data binding container has bindings; + /// false otherwise. + /// + bool HasBindings { get; } + + /// + /// Adds the binding. + /// + /// + /// Binding definition to add. + /// + /// + /// Added instance. + /// + IBinding AddBinding(IBinding binding); + + /// + /// Adds the binding with a default + /// binding direction of . + /// + /// + /// This is a convinience method for adding SimpleExpressionBinding, + /// one of the most often used binding types, to the bindings list. + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + /// + /// Added instance. + /// + IBinding AddBinding(string sourceExpression, string targetExpression); + + /// + /// Adds the binding. + /// + /// + /// This is a convinience method for adding SimpleExpressionBinding, + /// one of the most often used binding types, to the bindings list. + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + /// + /// Binding direction. + /// + /// + /// Added instance. + /// + IBinding AddBinding(string sourceExpression, string targetExpression, BindingDirection direction); + + /// + /// Adds the binding with a default + /// binding direction of . + /// + /// + /// This is a convinience method for adding SimpleExpressionBinding, + /// one of the most often used binding types, to the bindings list. + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + /// + /// to use for value formatting and parsing. + /// + /// + /// Added instance. + /// + IBinding AddBinding(string sourceExpression, string targetExpression, IFormatter formatter); + + /// + /// Adds the binding. + /// + /// + /// This is a convinience method for adding SimpleExpressionBinding, + /// one of the most often used binding types, to the bindings list. + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + /// + /// Binding direction. + /// + /// + /// to use for value formatting and parsing. + /// + /// + /// Added instance. + /// + IBinding AddBinding(string sourceExpression, string targetExpression, BindingDirection direction, + IFormatter formatter); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/DataBinding/IDataBound.cs b/src/Spring/Spring.Core/DataBinding/IDataBound.cs new file mode 100644 index 00000000..cdf68c63 --- /dev/null +++ b/src/Spring/Spring.Core/DataBinding/IDataBound.cs @@ -0,0 +1,37 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.DataBinding +{ + /// + /// Interface that should be implemented by data bound objects, such as + /// web pages, user controls, windows forms, etc. + /// + /// Aleksandar Seovic + /// $Id: IDataBound.cs,v 1.3 2006/11/21 05:46:57 aseovic Exp $ + public interface IDataBound + { + /// + /// Gets the binding manager. + /// + /// The binding manager. + IBindingContainer BindingManager { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/DataBinding/ListBinding.cs b/src/Spring/Spring.Core/DataBinding/ListBinding.cs new file mode 100644 index 00000000..30aea12a --- /dev/null +++ b/src/Spring/Spring.Core/DataBinding/ListBinding.cs @@ -0,0 +1,75 @@ +using System.Collections; +using Spring.Expressions; +using Spring.Validation; + +namespace Spring.DataBinding +{ + /// + /// implementation that allows + /// data binding between collections that implement + /// interface. + /// + /// Aleksandar Seovic + /// $Id: ListBinding.cs,v 1.2 2008/02/05 20:40:25 aseovic Exp $ + public class ListBinding : AbstractBinding + { + private IExpression sourceExpression = Expression.Parse("#source = #target"); + private IExpression targetExpression = Expression.Parse("#target = #source"); + + /// + /// Binds source object to target object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + public override void BindSourceToTarget(object source, object target, IValidationErrors validationErrors, + IDictionary variables) + { + if (variables == null) + { + variables = new Hashtable(); + } + variables["source"] = source; + variables["target"] = target; + + targetExpression.GetValue(null, variables); + } + + /// + /// Binds target object to source object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + public override void BindTargetToSource(object source, object target, IValidationErrors validationErrors, + IDictionary variables) + { + if (variables == null) + { + variables = new Hashtable(); + } + variables["source"] = source; + variables["target"] = target; + + sourceExpression.GetValue(null, variables); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/DataBinding/SimpleExpressionBinding.cs b/src/Spring/Spring.Core/DataBinding/SimpleExpressionBinding.cs new file mode 100644 index 00000000..bc7b41eb --- /dev/null +++ b/src/Spring/Spring.Core/DataBinding/SimpleExpressionBinding.cs @@ -0,0 +1,175 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using Spring.Expressions; +using Spring.Globalization; + +namespace Spring.DataBinding +{ + /// + /// Simple, expression-based implementation of that + /// binds source to target one-to-one. + /// + /// Aleksandar Seovic + /// $Id: SimpleExpressionBinding.cs,v 1.3 2007/03/19 16:05:52 oakinger Exp $ + public class SimpleExpressionBinding : AbstractSimpleBinding + { + #region Fields + + private IExpression sourceExpression; + private IExpression targetExpression; + + #endregion + + #region Constructor(s) + + /// + /// Initializes a new instance of the class. + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + public SimpleExpressionBinding(string sourceExpression, string targetExpression) + { + this.sourceExpression = Expression.Parse(sourceExpression); + this.targetExpression = Expression.Parse(targetExpression); + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + /// + /// The formatter to use. + /// + public SimpleExpressionBinding(string sourceExpression, string targetExpression, IFormatter formatter) + :base(formatter) + { + this.sourceExpression = Expression.Parse(sourceExpression); + this.targetExpression = Expression.Parse(targetExpression); + } + + #endregion + + #region Properties + + /// + /// Gets the source expression. + /// + /// The source expression. + public IExpression SourceExpression + { + get { return sourceExpression; } + } + + /// + /// Gets the target expression. + /// + /// The target expression. + public IExpression TargetExpression + { + get { return targetExpression; } + } + + #endregion + + #region Abstract Methods Implementation + + /// + /// Gets the source value for the binding. + /// + /// + /// Source object to extract value from. + /// + /// + /// Variables for expression evaluation. + /// + /// + /// The source value for the binding. + /// + protected override object GetSourceValue(object source, IDictionary variables) + { + return this.SourceExpression.GetValue(source, variables); + } + + /// + /// Sets the source value for the binding. + /// + /// + /// The source object to set the value on. + /// + /// + /// The value to set. + /// + /// + /// Variables for expression evaluation. + /// + protected override void SetSourceValue(object source, object value, IDictionary variables) + { + this.SourceExpression.SetValue(source, variables, value); + } + + /// + /// Gets the target value for the binding. + /// + /// + /// Source object to extract value from. + /// + /// + /// Variables for expression evaluation. + /// + /// + /// The target value for the binding. + /// + protected override object GetTargetValue(object target, IDictionary variables) + { + return this.TargetExpression.GetValue(target, variables); + } + + /// + /// Sets the target value for the binding. + /// + /// + /// The target object to set the value on. + /// + /// + /// The value to set. + /// + /// + /// Variables for expression evaluation. + /// + protected override void SetTargetValue(object target, object value, IDictionary variables) + { + this.TargetExpression.SetValue(target, variables, value); + } + + #endregion + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/ArrayConstructorNode.cs b/src/Spring/Spring.Core/Expressions/ArrayConstructorNode.cs new file mode 100644 index 00000000..2dd3cc45 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/ArrayConstructorNode.cs @@ -0,0 +1,101 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +using antlr.collections; +using Spring.Core.TypeResolution; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents parsed method node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: ArrayConstructorNode.cs,v 1.11 2007/09/07 03:01:21 markpollack Exp $ + [Serializable] + public class ArrayConstructorNode : NodeWithArguments + { + private Type arrayType; + + /// + /// Create a new instance + /// + public ArrayConstructorNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected ArrayConstructorNode(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + /// + /// Creates new instance of the type defined by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if (arrayType == null) + { + lock (this) + { + if (arrayType == null) + { + arrayType = TypeResolutionUtils.ResolveType(getText()); + } + } + } + + AST rankRoot = getFirstChild(); + int dimensions = rankRoot.getNumberOfChildren(); + int[] ranks = new int[dimensions]; + if (dimensions > 0) + { + int i = 0; + AST rankNode = rankRoot.getFirstChild(); + while (rankNode != null) + { + ranks[i++] = (int)((BaseNode)rankNode).GetValueInternal(context, evalContext); + rankNode = rankNode.getNextSibling(); + } + return Array.CreateInstance(arrayType, ranks); + } + else + { + AST valuesRoot = getFirstChild().getNextSibling(); + if (valuesRoot != null) + { + ArrayList values = (ArrayList)((BaseNode)valuesRoot).GetValueInternal(context, evalContext); + return values.ToArray(arrayType); + } + } + + throw new ArgumentException("You have to specify either rank or initializer for an array."); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/AssignNode.cs b/src/Spring/Spring.Core/Expressions/AssignNode.cs new file mode 100644 index 00000000..e16922fc --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/AssignNode.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using antlr.collections; + +namespace Spring.Expressions +{ + /// + /// Represents parsed assignment node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: AssignNode.cs,v 1.9 2007/09/07 03:01:21 markpollack Exp $ + [Serializable] + public class AssignNode : BaseNode + { + /// + /// Create a new instance + /// + public AssignNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected AssignNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Assigns value of the right operand to the left one. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + AST left = getFirstChild(); + AST right = left.getNextSibling(); + + object result; + + if (right.getFirstChild() is LambdaExpressionNode) + { + if (!(left.getFirstChild() is VariableNode)) + { + throw new ArgumentException("Lambda expression can only be assigned to a global variable."); + } + result = right.getFirstChild(); + } + else + { + result = ((BaseNode)right).GetValueInternal(context, evalContext); + } + + ((BaseNode)left).SetValueInternal( context, evalContext, result ); + + return result; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/AttributeNode.cs b/src/Spring/Spring.Core/Expressions/AttributeNode.cs new file mode 100644 index 00000000..e241cc3c --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/AttributeNode.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +using Spring.Core.TypeResolution; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents parsed attribute node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: AttributeNode.cs,v 1.9 2007/09/07 03:01:21 markpollack Exp $ + [Serializable] + public class AttributeNode : ConstructorNode + { + /// + /// Create a new instance + /// + public AttributeNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected AttributeNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Tries to determine attribute type based on the specified + /// attribute type name. + /// + /// + /// Attribute type name to resolve. + /// + /// + /// Resolved attribute type. + /// + /// + /// If type cannot be resolved. + /// + protected override Type GetObjectType(string typeName) + { + Type type; + + try + { + type = base.GetObjectType(typeName); + } + catch (TypeLoadException) + { + if (typeName.EndsWith("Attribute")) + { + throw; + } + type = TypeResolutionUtils.ResolveType(typeName + "Attribute"); + } + + return type; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/BaseNode.cs b/src/Spring/Spring.Core/Expressions/BaseNode.cs new file mode 100644 index 00000000..88d87ac2 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/BaseNode.cs @@ -0,0 +1,267 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Base type for all expression nodes. + /// + /// Aleksandar Seovic + /// $Id: BaseNode.cs,v 1.24 2007/09/07 03:01:21 markpollack Exp $ + //[Serializable] + public abstract class BaseNode : SpringAST, IExpression + { + #region EvaluationContext class + + /// + /// Holds the state during evaluating an expression. + /// + public class EvaluationContext + { + #region Holder classes + + private class ThisContextHolder : IDisposable + { + private readonly EvaluationContext owner; + private readonly object savedThisContext; + + public ThisContextHolder(EvaluationContext owner) + { + this.owner = owner; + this.savedThisContext = owner.ThisContext; + } + + public void Dispose() + { + owner.ThisContext = savedThisContext; + } + } + + private class LocalVariablesHolder : IDisposable + { + private readonly EvaluationContext owner; + private readonly IDictionary savedLocalVariables; + + public LocalVariablesHolder(EvaluationContext owner, IDictionary newLocalVariables) + { + this.owner = owner; + this.savedLocalVariables = owner.LocalVariables; + owner.LocalVariables = newLocalVariables; + } + + public void Dispose() + { + owner.LocalVariables = savedLocalVariables; + } + } + + #endregion + + /// + /// Gets/Sets the root context of the current evaluation + /// + public object RootContext; + /// + /// Gets the type of the + /// + public Type RootContextType { get { return (RootContext == null) ? null : RootContext.GetType(); } } + /// + /// Gets/Sets the current context of the current evaluation + /// + public object ThisContext; + /// + /// Gets/Sets global variables of the current evaluation + /// + public IDictionary Variables; + /// + /// Gets/Sets local variables of the current evaluation + /// + public IDictionary LocalVariables; + + /// + /// Initializes a new EvaluationContext instance. + /// + /// The root context for this evaluation + /// dictionary of global variables used during this evaluation + public EvaluationContext(object rootContext, IDictionary globalVariables) + { + this.RootContext = rootContext; + this.ThisContext = rootContext; + this.Variables = globalVariables; + } + + /// + /// Switches current ThisContext. + /// + public IDisposable SwitchThisContext() + { + return new ThisContextHolder(this); + } + + /// + /// Switches current LocalVariables. + /// + public IDisposable SwitchLocalVariables(IDictionary newLocalVariables) + { + return new LocalVariablesHolder(this, newLocalVariables); + } + } + + #endregion + + /// + /// Create a new instance + /// + public BaseNode() + {} + + /// + /// Create a new instance from SerializationInfo + /// + protected BaseNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns node's value. + /// + /// Node's value. + object IExpression.GetValue() + { + return GetValue(null, null); + } + + /// + /// Returns node's value for the given context. + /// + /// Object to evaluate node against. + /// Node's value. + object IExpression.GetValue(object context) + { + return GetValue(context, null); + } + + /// + /// Returns node's value for the given context. + /// + /// Object to evaluate node against. + /// Expression variables map. + /// Node's value. + object IExpression.GetValue(object context, IDictionary variables) + { + return GetValue(context, variables); + } + + /// + /// This is the entrypoint into evaluating this expression. + /// + private object GetValue(object context, IDictionary variables) + { + EvaluationContext evalContext = new EvaluationContext( context,variables ); + return Get( context, evalContext ); + } + + /// + /// Called internally during expression evaluation + /// + /// Object to evaluate node against. + /// Current expression evaluation context. + /// + protected internal object GetValueInternal(object context, EvaluationContext evalContext) + { + return Get(context, evalContext); + } + + /// + /// Returns node's value for the given context. + /// + /// Node's value. + protected abstract object Get(object context, EvaluationContext evalContext); + + /// + /// Sets node's value for the given context. + /// + /// Object to evaluate node against. + /// New value for this node. + void IExpression.SetValue(object context, object newValue) + { + SetValue(context, null, newValue); + } + + /// + /// Sets node's value for the given context. + /// + /// Object to evaluate node against. + /// Expression variables map. + /// New value for this node. + void IExpression.SetValue(object context, IDictionary variables, object newValue) + { + SetValue( context,variables,newValue ); + } + + /// + /// This is the entrypoint into evaluating this expression. + /// + private void SetValue(object context,IDictionary variables,object newValue) + { + EvaluationContext evalContext = new EvaluationContext( context,variables ); + Set( context, evalContext,newValue ); + } + + /// + /// Called internally during expression evaluation. + /// + protected internal void SetValueInternal(object context, EvaluationContext evalContext, object newValue) + { + Set(context, evalContext, newValue); + } + + /// + /// Sets node's value for the given context. + /// + /// + ///

    + /// This is a default implementation of Set method, which + /// simply throws . + ///

    + ///

    + /// This was done in order to avoid redundant Set method implementations, + /// because most of the node types do not support value setting. + ///

    + ///
    + protected virtual void Set(object context, EvaluationContext evalContext, object newValue) + { + throw new NotSupportedException("You cannot set the value for the node of this type: [" + this.GetType().Name + "]."); + } + + /// + /// Returns a string representation of this node instance. + /// + public override string ToString() + { + return string.Format("{0}[{1}]", this.GetType().Name, base.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/BinaryOperator.cs b/src/Spring/Spring.Core/Expressions/BinaryOperator.cs new file mode 100644 index 00000000..0136a374 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/BinaryOperator.cs @@ -0,0 +1,65 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Base class for unary operators. + /// + /// Aleksandar Seovic + /// $Id: BinaryOperator.cs,v 1.8 2007/09/07 03:01:21 markpollack Exp $ + [Serializable] + public abstract class BinaryOperator : BaseNode + { + /// + /// Create a new instance + /// + public BinaryOperator() + {} + + /// + /// Create a new instance from SerializationInfo + /// + protected BinaryOperator(SerializationInfo info, StreamingContext context) + : base(info, context) + {} + + /// + /// Gets the left operand. + /// + /// The left operand. + public BaseNode Left + { + get { return (BaseNode) this.getFirstChild(); } + } + + /// + /// Gets the right operand. + /// + /// The right operand. + public BaseNode Right + { + get { return (BaseNode) this.getFirstChild().getNextSibling(); } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/BooleanLiteralNode.cs b/src/Spring/Spring.Core/Expressions/BooleanLiteralNode.cs new file mode 100644 index 00000000..77660747 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/BooleanLiteralNode.cs @@ -0,0 +1,74 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed boolean literal node. + /// + /// Aleksandar Seovic + /// $Id: BooleanLiteralNode.cs,v 1.9 2007/09/07 03:01:21 markpollack Exp $ + [Serializable] + public class BooleanLiteralNode : BaseNode + { + private object nodeValue; + + /// + /// Create a new instance + /// + public BooleanLiteralNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected BooleanLiteralNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the boolean literal node. + /// + /// + /// This is the entrypoint into evaluating this expression. + /// + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if (nodeValue == null) + { + lock(this) + { + if (nodeValue == null) + { + nodeValue = Boolean.Parse(this.getText()); + } + } + } + + return nodeValue; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/ConstructorNode.cs b/src/Spring/Spring.Core/Expressions/ConstructorNode.cs new file mode 100644 index 00000000..d43b39a4 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/ConstructorNode.cs @@ -0,0 +1,208 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Reflection; +using System.Runtime.Serialization; + +using Spring.Core.TypeResolution; +using Spring.Util; +using Spring.Reflection.Dynamic; + +namespace Spring.Expressions +{ + /// + /// Represents parsed method node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: ConstructorNode.cs,v 1.16 2007/09/07 03:01:21 markpollack Exp $ + [Serializable] + public class ConstructorNode : NodeWithArguments + { + private SafeConstructor constructor; + private IDictionary namedArgs; + private bool isParamArray = false; + private Type paramArrayType; + private int argumentCount; + + /// + /// Create a new instance + /// + public ConstructorNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected ConstructorNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Creates new instance of the type defined by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object[] argValues = ResolveArguments(evalContext); + IDictionary namedArgValues = ResolveNamedArguments(evalContext); + + if (constructor == null) + { + lock(this) + { + if (constructor == null) + { + constructor = InitializeNode(argValues, namedArgValues); + } + } + } + + object[] paramValues = (isParamArray ? ReflectionUtils.PackageParamArray(argValues, argumentCount, paramArrayType) : argValues); + object instance = constructor.Invoke(paramValues); + if (namedArgValues != null) + { + SetNamedArguments(instance, namedArgValues); + } + + return instance; + } + + /// + /// Determines the type of object that should be instantiated. + /// + /// + /// The type name to resolve. + /// + /// + /// The type of object that should be instantiated. + /// + /// + /// If the type cannot be resolved. + /// + protected virtual Type GetObjectType(string typeName) + { + return TypeResolutionUtils.ResolveType(typeName); + } + + /// + /// Initializes this node by caching necessary constructor and property info. + /// + /// + /// + private SafeConstructor InitializeNode(object[] argValues, IDictionary namedArgValues) + { + SafeConstructor ctor = null; + Type objectType = GetObjectType(this.getText().Trim()); + + // cache constructor info + ConstructorInfo ci = GetBestConstructor(objectType, argValues); + if (ci == null) + { + throw new ArgumentException( + String.Format("Constructor for the type [{0}] with a specified " + + "number and types of arguments does not exist.", + objectType.FullName)); + } + else + { + ParameterInfo[] parameters = ci.GetParameters(); + if (parameters.Length > 0) + { + ParameterInfo lastParameter = parameters[parameters.Length - 1]; + isParamArray = lastParameter.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0; + if (isParamArray) + { + paramArrayType = lastParameter.ParameterType.GetElementType(); + argumentCount = parameters.Length; + } + } + ctor = new SafeConstructor(ci); + } + + // cache named args info + if (namedArgValues != null) + { + namedArgs = new Hashtable(namedArgValues.Count); + foreach (string name in namedArgValues.Keys) + { + this.namedArgs[name] = Expression.ParseProperty(name); + } + } + + return ctor; + } + + /// + /// Sets the named arguments (properties). + /// + /// Instance to set property values on. + /// Argument (property) name to value mappings. + private void SetNamedArguments(object instance, IDictionary namedArgValues) + { + foreach (string name in namedArgValues.Keys) + { + IExpression property = (IExpression) namedArgs[name]; + property.SetValue(instance, namedArgValues[name]); + } + } + + private static ConstructorInfo GetBestConstructor(Type type, object[] argValues) + { + ConstructorInfo[] candidates = GetCandidateConstructors(type, argValues.Length); + if (candidates.Length > 0) + { + return ReflectionUtils.GetConstructorByArgumentValues(candidates, argValues); + } + return null; + } + + private static ConstructorInfo[] GetCandidateConstructors(Type type, int argCount) + { + ConstructorInfo[] ctors = type.GetConstructors(); + ArrayList matches = new ArrayList(); + + foreach (ConstructorInfo ctor in ctors) + { + ParameterInfo[] parameters = ctor.GetParameters(); + if (parameters.Length == argCount) + { + matches.Add(ctor); + } + else if (parameters.Length > 0) + { + ParameterInfo lastParameter = parameters[parameters.Length - 1]; + if (lastParameter.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0) + { + matches.Add(ctor); + } + } + } + + return (ConstructorInfo[]) matches.ToArray(typeof(ConstructorInfo)); + } + + } +} diff --git a/src/Spring/Spring.Core/Expressions/DateLiteralNode.cs b/src/Spring/Spring.Core/Expressions/DateLiteralNode.cs new file mode 100644 index 00000000..90115ecb --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/DateLiteralNode.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using System.Runtime.Serialization; +using antlr.collections; + +namespace Spring.Expressions +{ + /// + /// Represents parsed node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: DateLiteralNode.cs,v 1.7 2007/09/07 03:01:21 markpollack Exp $ + [Serializable] + public class DateLiteralNode : BaseNode + { + private object nodeValue; + + /// + /// Create a new instance + /// + public DateLiteralNode():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected DateLiteralNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns node's value for the given context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if (nodeValue == null) + { + lock (this) + { + if (nodeValue == null) + { + AST dateString = this.getFirstChild(); + if (getNumberOfChildren() == 2) + { + AST dateFormat = dateString.getNextSibling(); + nodeValue = + DateTime.ParseExact(dateString.getText(), dateFormat.getText(), + CultureInfo.InvariantCulture); + } + else + { + nodeValue = DateTime.Parse(dateString.getText()); + } + } + } + } + return nodeValue; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/DefaultNode.cs b/src/Spring/Spring.Core/Expressions/DefaultNode.cs new file mode 100644 index 00000000..cfda1b04 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/DefaultNode.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed default node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: DefaultNode.cs,v 1.3 2007/09/07 03:01:24 markpollack Exp $ + [Serializable] + public class DefaultNode : BinaryOperator + { + /// + /// Create a new instance + /// + public DefaultNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected DefaultNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns left operand if it is not null, or the right operand if it is. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal(context, evalContext); + object right = Right.GetValueInternal(context, evalContext); + + return (left != null ? left : right); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/Expression.cs b/src/Spring/Spring.Core/Expressions/Expression.cs new file mode 100644 index 00000000..a646ae62 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Expression.cs @@ -0,0 +1,285 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using antlr; +using antlr.collections; +using Spring.Core; +using Spring.Util; +using StringUtils=Spring.Util.StringUtils; + +namespace Spring.Expressions +{ + /// + /// Container object for the parsed expression. + /// + /// + ///

    + /// Preparing this object once and reusing it many times for expression + /// evaluation can result in significant performance improvements, as + /// expression parsing and reflection lookups are only performed once. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: Expression.cs,v 1.19 2008/03/20 10:35:54 oakinger Exp $ + [Serializable] + public class Expression : BaseNode + { + /// + /// Contains a list of reserved variable names. + /// You must not use any variable names with the reserved prefix! + /// + public class ReservedVariableNames + { + /// + /// Variable Names using this prefix are reserved for internal framework use + /// + public static readonly string RESERVEDPREFIX = "____spring_"; + + /// + /// variable name of the currently processed object factory, if any + /// + internal static readonly string CurrentObjectFactory = RESERVEDPREFIX + "CurrentObjectFactory"; + } + + private class SpringASTFactory : ASTFactory + { + public SpringASTFactory(Type t) : base(t.FullName) + { + base.defaultASTNodeTypeObject_ = t; + base.typename2creator_ = new Hashtable(32, 0.3f); + base.typename2creator_[t.FullName] = SpringAST.Creator; + } + } + + private class SpringExpressionParser : ExpressionParser + { + public SpringExpressionParser(TokenStream lexer) + : base(lexer) + { + base.astFactory = new SpringASTFactory(typeof(SpringAST)); + base.initialize(); + } + } + + static Expression() + { + // Ensure antlr is loaded (fixes GAC issues)! + Assembly antlrAss = typeof(antlr.LLkParser).Assembly; + } + + /// + /// Initializes a new instance of the class + /// by parsing specified expression string. + /// + /// Expression to parse. + public static IExpression Parse(string expression) + { + if (StringUtils.HasText(expression)) + { + ExpressionLexer lexer = new ExpressionLexer(new StringReader(expression)); + ExpressionParser parser = new SpringExpressionParser(lexer); + + parser.expr(); + + return (IExpression) parser.getAST(); + } + else + { + return new Expression(); + } + } + + /// + /// Registers lambda expression under the specified . + /// + /// Function name to register expression as. + /// Lambda expression to register. + /// Variables dictionary that the function will be registered in. + public static void RegisterFunction(string functionName, string lambdaExpression, IDictionary variables) + { + AssertUtils.ArgumentHasText(functionName, "functionName"); + AssertUtils.ArgumentHasText(lambdaExpression, "lambdaExpression"); + + ExpressionLexer lexer = new ExpressionLexer(new StringReader(lambdaExpression)); + ExpressionParser parser = new SpringExpressionParser(lexer); + + parser.lambda(); + variables[functionName] = parser.getAST(); + } + + /// + /// Initializes a new instance of the class + /// by parsing specified primary expression string. + /// + /// Primary expression to parse. + internal static IExpression ParsePrimary(string expression) + { + if (StringUtils.HasText(expression)) + { + ExpressionLexer lexer = new ExpressionLexer(new StringReader(expression)); + ExpressionParser parser = new SpringExpressionParser(lexer); + + parser.primaryExpression(); + return (IExpression) parser.getAST(); + } + else + { + return new Expression(); + } + } + + /// + /// Initializes a new instance of the class + /// by parsing specified property expression string. + /// + /// Property expression to parse. + internal static IExpression ParseProperty(string expression) + { + if (StringUtils.HasText(expression)) + { + ExpressionLexer lexer = new ExpressionLexer(new StringReader(expression)); + ExpressionParser parser = new SpringExpressionParser(lexer); + + parser.property(); + return (IExpression) parser.getAST(); + } + else + { + return new Expression(); + } + } + + /// + /// Initializes a new instance of the class. + /// + public Expression() + {} + + /// + /// Create a new instance from SerializationInfo + /// + protected Expression(SerializationInfo info, StreamingContext context) + : base(info, context) + {} + + /// + /// Evaluates this expression for the specified root object and returns + /// value of the last node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Value of the last node. + protected override object Get(object context, EvaluationContext evalContext) + { + object result = context; + + if (this.getNumberOfChildren() > 0) + { + AST node = this.getFirstChild(); + while (node != null) + { + result = ((BaseNode)node).GetValueInternal( result, evalContext ); + + node = node.getNextSibling(); + } + } + + return result; + } + + /// + /// Evaluates this expression for the specified root object and sets + /// value of the last node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Value to set last node to. + /// If navigation expression is empty. + protected override void Set(object context, EvaluationContext evalContext, object newValue) + { + object target = context; + + if (this.getNumberOfChildren() > 0) + { + AST node = this.getFirstChild(); + + for (int i = 0; i < this.getNumberOfChildren() - 1; i++) + { + try + { + target = ((BaseNode) node).GetValueInternal(target, evalContext); + node = node.getNextSibling(); + } + catch (NotReadablePropertyException e) + { + throw new NotWritablePropertyException("Cannot read the value of '" + node.getText() + "' property in the expression.", e); + } + } + ((BaseNode) node).SetValueInternal(target, evalContext, newValue); + } + else + { + throw new NotSupportedException("You cannot set the value for an empty expression."); + } + } + + /// + /// Evaluates this expression for the specified root object and returns + /// of the last node, if possible. + /// + /// Context to evaluate expression against. + /// Expression variables map. + /// Value of the last node. + internal PropertyInfo GetPropertyInfo(object context, IDictionary variables) + { + if (this.getNumberOfChildren() > 0) + { + object target = context; + AST node = this.getFirstChild(); + + for (int i = 0; i < this.getNumberOfChildren() - 1; i++) + { + target = ((IExpression) node).GetValue(target, variables); + node = node.getNextSibling(); + } + + if (node is PropertyOrFieldNode) + { + return (PropertyInfo) ((PropertyOrFieldNode) node).GetMemberInfo(target); + } + else if (node is IndexerNode) + { + return ((IndexerNode)node).GetPropertyInfo(target, variables); + } + else + { + throw new FatalReflectionException("Cannot obtain PropertyInfo from an expression that does not resolve to a property or an indexer."); + } + } + + throw new FatalReflectionException("Cannot obtain PropertyInfo for empty property name."); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/Expression.g b/src/Spring/Spring.Core/Expressions/Expression.g new file mode 100644 index 00000000..a549f6f8 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Expression.g @@ -0,0 +1,528 @@ +options +{ + language = "CSharp"; + namespace = "Spring.Expressions"; +} + +class ExpressionParser extends Parser; + +options { + codeGenMakeSwitchThreshold = 3; + codeGenBitsetTestThreshold = 4; + classHeaderPrefix = "internal"; + buildAST=true; + ASTLabelType = "Spring.Expressions.SpringAST"; + k = 2; +} + +tokens { + EXPR; + OPERAND; + FALSE = "false"; + TRUE = "true"; + AND = "and"; + OR = "or"; + IN = "in"; + IS = "is"; + BETWEEN = "between"; + LIKE = "like"; + MATCHES = "matches"; + NULL_LITERAL = "null"; +} + +{ + // CLOVER:OFF + + public override void reportError(RecognitionException ex) + { + //base.reportError(ex); + throw ex; + } + + public override void reportError(string error) + { + //base.reportError(error); + throw new RecognitionException(error); + } + + private string GetRelationalOperatorNodeType(string op) + { + switch (op) + { + case "==" : return "Spring.Expressions.OpEqual"; + case "!=" : return "Spring.Expressions.OpNotEqual"; + case "<" : return "Spring.Expressions.OpLess"; + case "<=" : return "Spring.Expressions.OpLessOrEqual"; + case ">" : return "Spring.Expressions.OpGreater"; + case ">=" : return "Spring.Expressions.OpGreaterOrEqual"; + case "in" : return "Spring.Expressions.OpIn"; + case "is" : return "Spring.Expressions.OpIs"; + case "between" : return "Spring.Expressions.OpBetween"; + case "like" : return "Spring.Expressions.OpLike"; + case "matches" : return "Spring.Expressions.OpMatches"; + default : + throw new ArgumentException("Node type for operator '" + op + "' is not defined."); + } + } +} + +expr : expression EOF!; + +exprList + : LPAREN! expression (SEMI! expression)+ RPAREN! + { #exprList = #([EXPR,"expressionList","Spring.Expressions.ExpressionListNode"], #exprList); } + ; + +expression : logicalOrExpression + ( + (ASSIGN^ logicalOrExpression) + | (DEFAULT^ logicalOrExpression) + | (QMARK^ expression COLON! expression) + )? + ; + +parenExpr + : LPAREN! expression RPAREN!; + +logicalOrExpression : logicalAndExpression (OR^ logicalAndExpression)* ; + +logicalAndExpression : relationalExpression (AND^ relationalExpression)* ; + +relationalExpression + : e1:sumExpr + (op:relationalOperator! e2:sumExpr + {#relationalExpression = #(#[EXPR, op_AST.getText(), GetRelationalOperatorNodeType(op_AST.getText())], #relationalExpression);} + )? + ; + +sumExpr : prodExpr ( + (PLUS^ + | MINUS^ ) prodExpr)* ; + +prodExpr : powExpr ( + (STAR^ + | DIV^ + | MOD^ ) powExpr)* ; + +powExpr : unaryExpression (POWER^ unaryExpression)? ; + +unaryExpression + : (PLUS^ + | MINUS^ + | BANG^ ) unaryExpression + | primaryExpression + ; + +unaryOperator + : PLUS | MINUS | BANG + ; + +primaryExpression : startNode (node)? + { #primaryExpression = #([EXPR,"expression","Spring.Expressions.Expression"], #primaryExpression); }; + +startNode + : + ( (LPAREN expression SEMI) => exprList + | parenExpr + | methodOrProperty + | functionOrVar + | localFunctionOrVar + | reference + | indexer + | literal + | type + | constructor + | projection + | selection + | firstSelection + | lastSelection + | listInitializer + | mapInitializer + | lambda + | attribute + ) + ; + +node : + ( methodOrProperty + | indexer + | projection + | selection + | firstSelection + | lastSelection + | exprList + | DOT! + )+ + ; + +functionOrVar + : (POUND ID LPAREN) => function + | var + ; + +function : POUND! ID^ methodArgs + ; + +var : POUND! ID^ ; + +localFunctionOrVar + : (DOLLAR ID LPAREN) => localFunction + | localVar + ; + +localFunction : DOLLAR! ID^ methodArgs + ; + +localVar : DOLLAR! ID^ ; + +methodOrProperty + : (ID LPAREN)=> + ID^ methodArgs + | property + ; + +methodArgs + : LPAREN! (argument (COMMA! argument)*)? RPAREN! + ; + +property + : ID + ; + +reference + : AT! LPAREN! (cn:contextName! COLON!)? id:qualifiedId! RPAREN! + { #reference = #([EXPR, "ref", "Spring.Context.Support.ReferenceNode"], #cn, #id); } + ; + +indexer + : + LBRACKET^ argument (COMMA! argument)* RBRACKET! + ; + +projection + : + PROJECT^ expression RCURLY! + ; + +selection + : + SELECT^ expression (COMMA! expression)* RCURLY! + ; + +firstSelection + : + SELECT_FIRST^ expression RCURLY! + ; + +lastSelection + : + SELECT_LAST^ expression RCURLY! + ; + +type + : TYPE! tn:qualifiedId! (LBRACKET RBRACKET)? (COMMA qualifiedId)? RPAREN! + { #type = #([EXPR, tn_AST.getText(), "Spring.Expressions.TypeNode"], #type); } + ; + +attribute + : AT! LBRACKET! tn:qualifiedId! (ctorArgs)? RBRACKET! + { #attribute = #([EXPR, tn_AST.getText(), "Spring.Expressions.AttributeNode"], #attribute); } + ; + +lambda + : LAMBDA! (argList)? PIPE! expression RCURLY! + { #lambda = #([EXPR, "lambda", "Spring.Expressions.LambdaExpressionNode"], #lambda); } + ; + +argList : (ID (COMMA! ID)*) + { #argList = #([EXPR, "args"], #argList); } + ; + +constructor + : ("new" qualifiedId LPAREN) => "new"! type:qualifiedId! ctorArgs + { #constructor = #([EXPR, type_AST.getText(), "Spring.Expressions.ConstructorNode"], #constructor); } + | arrayConstructor + ; + +arrayConstructor + : "new"! type:qualifiedId! arrayRank (listInitializer)? + { #arrayConstructor = #([EXPR, type_AST.getText(), "Spring.Expressions.ArrayConstructorNode"], #arrayConstructor); } + ; + +arrayRank + : LBRACKET^ (expression (COMMA! expression)*)? RBRACKET! + ; + +listInitializer + : LCURLY^ expression (COMMA! expression)* RCURLY! + ; + +mapInitializer + : POUND! LCURLY^ mapEntry (COMMA! mapEntry)* RCURLY! + ; + +mapEntry + : expression COLON! expression + { #mapEntry = #([EXPR, "entry", "Spring.Expressions.MapEntryNode"], #mapEntry); } + ; + +ctorArgs : LPAREN! (namedArgument (COMMA! namedArgument)*)? RPAREN!; + +argument : expression; + +namedArgument + : (ID ASSIGN) => ID^ ASSIGN! expression + | argument + ; + +qualifiedId : ID^ (DOT ID)* + ; + +contextName : ID^ (DIV ID)* + ; + +literal + : NULL_LITERAL + | INTEGER_LITERAL + | HEXADECIMAL_INTEGER_LITERAL + | REAL_LITERAL + | STRING_LITERAL + | boolLiteral + | dateLiteral + ; + +boolLiteral + : TRUE + | FALSE + ; + +dateLiteral + : "date"^ + LPAREN! STRING_LITERAL (COMMA! STRING_LITERAL)? RPAREN! + ; + +relationalOperator + : EQUAL + | NOT_EQUAL + | LESS_THAN + | LESS_THAN_OR_EQUAL + | GREATER_THAN + | GREATER_THAN_OR_EQUAL + | IN + | IS + | BETWEEN + | LIKE + | MATCHES + ; + + +class ExpressionLexer extends Lexer; + +options { + charVocabulary = '\u0000' .. '\uFFFE'; + classHeaderPrefix = "internal"; + k = 2; +} + +{ + // CLOVER:OFF +} + +WS : (' ' + | '\t' + | '\n' + | '\r') + { _ttype = Token.SKIP; } + ; + +AT: '@' + ; + +PIPE: '|' + ; + +BANG: '!' + ; + +QMARK: '?' + ; + +DOLLAR: '$' + ; + +POUND: '#' + ; + +LPAREN: '(' + ; + +RPAREN: ')' + ; + +LBRACKET: '[' + ; + +RBRACKET: ']' + ; + +LCURLY: '{' + ; + +RCURLY: '}' + ; + +COMMA : ',' + ; + +SEMI: ';' + ; + +COLON: ':' + ; + +ASSIGN: '=' + ; + +DEFAULT: "??" + ; + +PLUS: '+' + ; + +MINUS: '-' + ; + +DIV: '/' + ; + +STAR: '*' + ; + +MOD: '%' + ; + +POWER: '^' + ; + +EQUAL: "==" + ; + +NOT_EQUAL: "!=" + ; + +LESS_THAN: '<' + ; + +LESS_THAN_OR_EQUAL: "<=" + ; + +GREATER_THAN: '>' + ; + +GREATER_THAN_OR_EQUAL: ">=" + ; + +PROJECT: "!{" + ; + +SELECT: "?{" + ; + +SELECT_FIRST: "^{" + ; + +SELECT_LAST: "${" + ; + +TYPE: "T(" + ; + +LAMBDA: "{|" + ; + +DOT_ESCAPED: "\\." + ; + +STRING_LITERAL + : '\''! (APOS|~'\'')* '\''! + ; + +protected +APOS : '\''! '\'' + ; + +ID +options { + testLiterals = true; +} + : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|DOT_ESCAPED)* + ; + +NUMERIC_LITERAL + + // real + : ('.' DECIMAL_DIGIT) => + '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)? + {$setType(REAL_LITERAL);} + + | ((DECIMAL_DIGIT)+ '.' DECIMAL_DIGIT) => + (DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)? + {$setType(REAL_LITERAL);} + + | ((DECIMAL_DIGIT)+ (EXPONENT_PART)) => + (DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)? + {$setType(REAL_LITERAL);} + + | ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)) => + (DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX) + {$setType(REAL_LITERAL);} + + // integer + | (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)? + {$setType(INTEGER_LITERAL);} + + // just a dot + | '.'{$setType(DOT);} + ; + + +HEXADECIMAL_INTEGER_LITERAL + : "0x" (HEX_DIGIT)+ (INTEGER_TYPE_SUFFIX)? + ; + +protected +DECIMAL_DIGIT + : '0'..'9' + ; +protected +INTEGER_TYPE_SUFFIX + : + ( options {generateAmbigWarnings=false;} + : "UL" | "LU" | "ul" | "lu" + | "UL" | "LU" | "uL" | "lU" + | "U" | "L" | "u" | "l" + ) + ; + +protected +HEX_DIGIT + : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | + 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | + 'a' | 'b' | 'c' | 'd' | 'e' | 'f' + ; + +protected +EXPONENT_PART + : "e" (SIGN)* (DECIMAL_DIGIT)+ + | "E" (SIGN)* (DECIMAL_DIGIT)+ + ; + +protected +SIGN + : '+' | '-' + ; + +protected +REAL_TYPE_SUFFIX + : 'F' | 'f' | 'D' | 'd' | 'M' | 'm' + ; diff --git a/src/Spring/Spring.Core/Expressions/ExpressionConverter.cs b/src/Spring/Spring.Core/Expressions/ExpressionConverter.cs new file mode 100644 index 00000000..3550bbcf --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/ExpressionConverter.cs @@ -0,0 +1,87 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; + +using Spring.Expressions; + +#endregion + +namespace Spring.Expressions +{ + /// + /// Converts string representation of expression into an instance of . + /// + /// Aleksandar Seovic + /// $Id: ExpressionConverter.cs,v 1.1 2007/07/31 02:13:52 markpollack Exp $ + public class ExpressionConverter : TypeConverter + { + /// + /// Can we convert from a the sourcetype to a ? + /// + /// + ///

    + /// Currently only supports conversion from a instance. + ///

    + ///
    + /// + /// A + /// that provides a format context. + /// + /// + /// A that represents the + /// you want to convert from. + /// + /// if the conversion is possible. + public override bool CanConvertFrom( + ITypeDescriptorContext context, Type sourceType) + { + return (sourceType == typeof(string)); + } + + /// + /// Convert from a value to an + /// instance. + /// + /// + /// A + /// that provides a format context. + /// + /// + /// The to use + /// as the current culture. + /// + /// + /// The value that is to be converted. + /// + /// + /// A array if successful. + /// + public override object ConvertFrom( + ITypeDescriptorContext context, CultureInfo culture, object value) + { + return Expression.Parse(value as string); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/ExpressionEvaluator.cs b/src/Spring/Spring.Core/Expressions/ExpressionEvaluator.cs new file mode 100644 index 00000000..e768a04a --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/ExpressionEvaluator.cs @@ -0,0 +1,98 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.Expressions +{ + /// + /// Utility class that enables easy expression evaluation. + /// + /// + ///

    + /// This class allows users to get or set properties, execute methods, and evaluate + /// logical and arithmetic expressions. + ///

    + ///

    + /// Methods in this class parse expression on every invocation. + /// If you plan to reuse the same expression many times, you should prepare + /// the expression once using the static method, + /// and then call to evaluate it. + ///

    + ///

    + /// This can result in significant performance improvements as it avoids expression + /// parsing and node resolution every time it is called. + ///

    + ///

    + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ExpressionEvaluator.cs,v 1.2 2006/04/09 07:18:39 markpollack Exp $ + public class ExpressionEvaluator + { + /// + /// Parses and evaluates specified expression. + /// + /// Root object. + /// Expression to evaluate. + /// Value of the last node in the expression. + public static object GetValue(object root, string expression) + { + return Expression.Parse(expression).GetValue(root, null); + } + + /// + /// Parses and evaluates specified expression. + /// + /// Root object. + /// Expression to evaluate. + /// Expression variables map. + /// Value of the last node in the expression. + public static object GetValue(object root, string expression, IDictionary variables) + { + return Expression.Parse(expression).GetValue(root, variables); + } + + /// + /// Parses and specified expression and sets the value of the + /// last node to the value of the newValue parameter. + /// + /// Root object. + /// Expression to evaluate. + /// Value to set last node to. + public static void SetValue(object root, string expression, object newValue) + { + Expression.Parse(expression).SetValue(root, null, newValue); + } + + /// + /// Parses and specified expression and sets the value of the + /// last node to the value of the newValue parameter. + /// + /// Root object. + /// Expression to evaluate. + /// Expression variables map. + /// Value to set last node to. + public static void SetValue(object root, string expression, IDictionary variables, object newValue) + { + Expression.Parse(expression).SetValue(root, variables, newValue); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/ExpressionListNode.cs b/src/Spring/Spring.Core/Expressions/ExpressionListNode.cs new file mode 100644 index 00000000..a59771b3 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/ExpressionListNode.cs @@ -0,0 +1,69 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using antlr.collections; + +namespace Spring.Expressions +{ + /// + /// Represents parsed expression list node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: ExpressionListNode.cs,v 1.6 2007/09/07 03:01:24 markpollack Exp $ + [Serializable] + public class ExpressionListNode : BaseNode + { + /// + /// Create a new instance + /// + public ExpressionListNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected ExpressionListNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a result of the last expression in a list. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Result of the last expression in a list + protected override object Get(object context, EvaluationContext evalContext) + { + object result = context; + + AST node = this.getFirstChild(); + while (node != null) + { + result = ((BaseNode) node).GetValueInternal(context, evalContext); + node = node.getNextSibling(); + } + return result; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/FunctionNode.cs b/src/Spring/Spring.Core/Expressions/FunctionNode.cs new file mode 100644 index 00000000..b7b4f369 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/FunctionNode.cs @@ -0,0 +1,93 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed function node. + /// + /// Aleksandar Seovic + /// $Id: FunctionNode.cs,v 1.7 2008/03/20 23:58:16 oakinger Exp $ + [Serializable] + public class FunctionNode : NodeWithArguments + { + /// + /// Create a new instance + /// + public FunctionNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected FunctionNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Evaluates function represented by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Result of the function evaluation. + protected override object Get(object context, EvaluationContext evalContext) + { + string name = this.getText(); + LambdaExpressionNode lambda = evalContext.Variables[name] as LambdaExpressionNode; + Delegate function = evalContext.Variables[name] as Delegate; + + if (lambda == null && function == null) + { + throw new InvalidOperationException("Function '" + name + "' is not defined."); + } + + object[] argValues = ResolveArguments(evalContext); + + // delegate? + if (function != null) + { + return function.DynamicInvoke(argValues); + } + + // lambda! + string[] argNames = lambda.ArgumentNames; + + if (argValues.Length != argNames.Length) + { + throw new InvalidOperationException( + "Function '" + name + "' requires " + argNames.Length + " arguments."); + } + + IDictionary arguments = new Hashtable(); + for (int i = 0; i < argValues.Length; i++) + { + arguments[argNames[i]] = argValues[i]; + } + + return lambda.GetValueInternal(context, evalContext, arguments); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/HexLiteralNode.cs b/src/Spring/Spring.Core/Expressions/HexLiteralNode.cs new file mode 100644 index 00000000..6e863d88 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/HexLiteralNode.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed hexadecimal integer literal node. + /// + /// Aleksandar Seovic + /// $Id: HexLiteralNode.cs,v 1.9 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class HexLiteralNode : BaseNode + { + private object nodeValue; + + /// + /// Create a new instance + /// + public HexLiteralNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected HexLiteralNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the hexadecimal integer literal node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if (nodeValue == null) + { + lock (this) + { + if (nodeValue == null) + { + string n = this.getText(); + try + { + nodeValue = Int32.Parse(n.Substring(2), NumberStyles.HexNumber); + } + catch (OverflowException) + { + nodeValue = Int64.Parse(n.Substring(2), NumberStyles.HexNumber); + } + } + } + } + + return nodeValue; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/IExpression.cs b/src/Spring/Spring.Core/Expressions/IExpression.cs new file mode 100644 index 00000000..e5d76d5b --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/IExpression.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.ComponentModel; + +namespace Spring.Expressions +{ + /// + /// Interface that all navigation expression nodes have to implement. + /// + /// Aleksandar Seovic + /// $Id: IExpression.cs,v 1.5 2007/07/31 08:18:20 markpollack Exp $ + [TypeConverter(typeof(ExpressionConverter))] + public interface IExpression + { + /// + /// Returns expression value. + /// + /// Value of the expression. + object GetValue(); + + /// + /// Returns expression value. + /// + /// Object to evaluate expression against. + /// Value of the expression. + object GetValue(object context); + + /// + /// Returns expression value. + /// + /// Object to evaluate expression against. + /// Expression variables map. + /// Value of the expression. + object GetValue(object context, IDictionary variables); + + /// + /// Sets expression value. + /// + /// Object to evaluate expression against. + /// New value for the last node of the expression. + void SetValue(object context, object newValue); + + /// + /// Sets expression value. + /// + /// Object to evaluate expression against. + /// Expression variables map. + /// New value for the last node of the expression. + void SetValue(object context, IDictionary variables, object newValue); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/IndexerNode.cs b/src/Spring/Spring.Core/Expressions/IndexerNode.cs new file mode 100644 index 00000000..d0b32143 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/IndexerNode.cs @@ -0,0 +1,294 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Reflection; +using System.Runtime.Serialization; +using Spring.Core; +using Spring.Util; +using Spring.Reflection.Dynamic; + +namespace Spring.Expressions +{ + /// + /// Represents parsed indexer node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: IndexerNode.cs,v 1.16 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class IndexerNode : NodeWithArguments + { + private const BindingFlags BINDING_FLAGS + = BindingFlags.Public | BindingFlags.NonPublic + | BindingFlags.Instance | BindingFlags.Static + | BindingFlags.IgnoreCase; + + private SafeIndexer indexer; + + /// + /// Create a new instance + /// + public IndexerNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected IndexerNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns node's value for the given context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if (context == null) + { + throw new NullValueInNestedPathException("Cannot retrieve the value of the indexer because the context for its resolution is null."); + } + + try + { + if (context is Array) + { + return GetArrayValue( (Array) context, evalContext ); + } + else if (context is IList) + { + return GetListValue( (IList) context, evalContext ); + } + else if (context is IDictionary) + { + return GetDictionaryValue( (IDictionary) context, evalContext ); + } + else if (context is string) + { + return GetCharacter( (string) context, evalContext ); + } + else + { + return GetGenericIndexer( context, evalContext ); + } + } + catch (TargetInvocationException e) + { + throw new InvalidPropertyException(evalContext.RootContextType, this.ToString(), "Getter for indexer threw an exception.", e); + } + catch (UnauthorizedAccessException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Illegal attempt to get value for the indexer.",e ); + } + catch (IndexOutOfRangeException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Index out of range.",e ); + } + catch (ArgumentOutOfRangeException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Argument out of range.",e ); + } + catch (InvalidCastException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Invalid index type.",e ); + } + catch (ArgumentException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Invalid argument.",e ); + } + } + + /// + /// Sets node's value for the given context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// New value for this node. + protected override void Set(object context, EvaluationContext evalContext, object newValue) + { + if (context == null) + { + throw new NullValueInNestedPathException("Cannot set the value of the indexer because the context for its resolution is null."); + } + + try + { + if (context is Array) + { + SetArrayValue( (Array) context, evalContext,newValue ); + } + else if (context is IList) + { + SetListValue( (IList) context, evalContext,newValue ); + } + else if (context is IDictionary) + { + SetDictionaryValue( (IDictionary) context, evalContext,newValue ); + } + else + { + SetGenericIndexer( context, evalContext,newValue ); + } + } + catch (TargetInvocationException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Setter for indexer threw an exception.",e ); + } + catch (UnauthorizedAccessException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Illegal attempt to set value for the indexer.",e ); + } + catch (IndexOutOfRangeException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Index out of range.",e ); + } + catch (ArgumentOutOfRangeException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Argument out of range.",e ); + } + catch (InvalidCastException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Invalid index type.",e ); + } + catch (ArgumentException e) + { + throw new InvalidPropertyException( evalContext.RootContextType,this.ToString(),"Invalid argument.",e ); + } + + } + + /// + /// Utility method that is needed by ObjectWrapper and AbstractAutowireCapableObjectFactory. + /// + /// Context to resolve property against. + /// Expression variables map. + /// PropertyInfo for this node. + internal PropertyInfo GetPropertyInfo(object context, IDictionary variables) + { + lock (this) + { + EvaluationContext evalContext = new EvaluationContext(context, variables); + InitializeIndexerProperty(context, evalContext); + + return indexer.IndexerProperty; + } + } + + private object GetArrayValue(Array array, EvaluationContext evalContext) + { + int argCount = array.Rank; + AssertArgumentCount(argCount); + + Int32[] indices = new Int32[argCount]; + for (int i = 0; i < argCount; i++) + { + indices[i] = (Int32) ResolveArgument(i, evalContext); + } + return array.GetValue(indices); + } + + private object GetListValue(IList list, EvaluationContext evalContext) + { + AssertArgumentCount(1); + return list[(int) ResolveArgument(0, evalContext)]; + } + + private object GetDictionaryValue(IDictionary dictionary, EvaluationContext evalContext) + { + AssertArgumentCount(1); + return dictionary[ResolveArgument( 0,evalContext )]; + } + + private object GetCharacter(string character, EvaluationContext evalContext) + { + AssertArgumentCount(1); + return character[(int)ResolveArgument( 0,evalContext )]; + } + + private object GetGenericIndexer(object context, EvaluationContext evalContext) + { + object[] indices = InitializeIndexerProperty( context, evalContext ); + return indexer.GetValue(context, indices); + } + + private void SetArrayValue(Array array, EvaluationContext evalContext,object newValue) + { + int argCount = array.Rank; + AssertArgumentCount(argCount); + + Int32[] indices = new Int32[argCount]; + for (int i = 0; i < argCount; i++) + { + indices[i] = (Int32) ResolveArgument(i, evalContext); + } + array.SetValue(newValue, indices); + } + + private void SetListValue(IList list, EvaluationContext evalContext,object newValue) + { + AssertArgumentCount(1); + list[(int) ResolveArgument(0, evalContext)] = newValue; + } + + private void SetDictionaryValue(IDictionary dictionary, EvaluationContext evalContext,object newValue) + { + AssertArgumentCount(1); + dictionary[ResolveArgument( 0,evalContext )] = newValue; + } + + private void SetGenericIndexer(object context, EvaluationContext evalContext,object newValue) + { + object[] indices = InitializeIndexerProperty( context, evalContext ); + indexer.SetValue( context,indices,newValue ); + } + + private object[] InitializeIndexerProperty(object context, EvaluationContext evalContext) + { + object[] indices = ResolveArguments( evalContext ); + + if (indexer == null) + { + lock (this) + { + if (indexer == null) + { + Type[] argTypes = ReflectionUtils.GetTypes(indices); + PropertyInfo indexerProperty = context.GetType().GetProperty("Item", BINDING_FLAGS, null, null, argTypes, null); + if (indexerProperty == null) + { + throw new ArgumentException("Indexer property with specified number and types of arguments does not exist."); + } + else + { + indexer = new SafeIndexer(indexerProperty); + } + } + } + } + + return indices; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/IntLiteralNode.cs b/src/Spring/Spring.Core/Expressions/IntLiteralNode.cs new file mode 100644 index 00000000..856ca77e --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/IntLiteralNode.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed integer literal node. + /// + /// Aleksandar Seovic + /// $Id: IntLiteralNode.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class IntLiteralNode : BaseNode + { + private object nodeValue; + + /// + /// Create a new instance + /// + public IntLiteralNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected IntLiteralNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the integer literal node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if (nodeValue == null) + { + lock (this) + { + if (nodeValue == null) + { + string n = this.getText(); + try + { + nodeValue = Int32.Parse(n); + } + catch (OverflowException) + { + nodeValue = Int64.Parse(n); + } + } + } + } + + return nodeValue; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/LambdaExpressionNode.cs b/src/Spring/Spring.Core/Expressions/LambdaExpressionNode.cs new file mode 100644 index 00000000..a2983f62 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/LambdaExpressionNode.cs @@ -0,0 +1,138 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; +using antlr.collections; + +namespace Spring.Expressions +{ + /// + /// Represents lambda expression. + /// + /// Aleksandar Seovic + /// $Id: LambdaExpressionNode.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class LambdaExpressionNode : BaseNode + { + /// + /// caches argumentNames of this instance + /// + private string[] argumentNames; + + /// + /// caches body expression of this lambda function + /// + private BaseNode bodyExpression; + + /// + /// Create a new instance + /// + public LambdaExpressionNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected LambdaExpressionNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Gets argument names for this lambda expression. + /// + public string[] ArgumentNames + { + get + { + if(bodyExpression == null) + { + InitializeLambda(); + } + return argumentNames; + } + } + + /// + /// Assigns value of the right operand to the left one. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if(bodyExpression == null) + { + InitializeLambda(); + } + + object result = bodyExpression.GetValueInternal(context, evalContext); + return result; + } + + /// + /// Returns Lambda Expression's value for the given context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// A dictionary containing argument map for this lambda expression. + /// Node's value. + public object GetValueInternal(object context, EvaluationContext evalContext, IDictionary arguments) + { + using (evalContext.SwitchLocalVariables(arguments)) + { + object result = base.GetValueInternal(context, evalContext); + return result; + } + } + + private void InitializeLambda() + { + lock (this) + { + if (bodyExpression == null) + { + if (this.getNumberOfChildren() == 1) + { + argumentNames = new string[0]; + bodyExpression = (BaseNode)this.getFirstChild(); + } + else + { + AST argsNode = this.getFirstChild(); + argumentNames = new string[argsNode.getNumberOfChildren()]; + AST argNode = argsNode.getFirstChild(); + int i = 0; + while (argNode != null) + { + argumentNames[i++] = argNode.getText(); + argNode = argNode.getNextSibling(); + } + + bodyExpression = (BaseNode)argsNode.getNextSibling(); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/ListInitializerNode.cs b/src/Spring/Spring.Core/Expressions/ListInitializerNode.cs new file mode 100644 index 00000000..a2dc371a --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/ListInitializerNode.cs @@ -0,0 +1,62 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed list initializer node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: ListInitializerNode.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class ListInitializerNode : NodeWithArguments + { + /// + /// Create a new instance + /// + public ListInitializerNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected ListInitializerNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Creates new instance of the list defined by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object[] values = ResolveArguments(evalContext); + return new ArrayList(values); + } + } +} diff --git a/src/Spring/Spring.Core/Expressions/LocalFunctionNode.cs b/src/Spring/Spring.Core/Expressions/LocalFunctionNode.cs new file mode 100644 index 00000000..deba2fd5 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/LocalFunctionNode.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents local function node. + /// + /// Aleksandar Seovic + /// $Id: LocalFunctionNode.cs,v 1.7 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class LocalFunctionNode : NodeWithArguments + { + /// + /// Create a new instance + /// + public LocalFunctionNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected LocalFunctionNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Evaluates function represented by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Result of the function evaluation. + protected override object Get(object context, EvaluationContext evalContext) + { + string name = this.getText(); + IDictionary locals = evalContext.LocalVariables; + LambdaExpressionNode lambda = locals[name] as LambdaExpressionNode; + + if (lambda == null) + { + throw new InvalidOperationException("Function '" + name + "' is not defined."); + } + + object[] argValues = ResolveArguments(evalContext); + string[] argNames = lambda.ArgumentNames; + + if (argValues.Length != argNames.Length) + { + throw new InvalidOperationException( + "Function '" + name + "' requires " + argNames.Length + " arguments."); + } + + IDictionary arguments = new Hashtable(); + for (int i = 0; i < argValues.Length; i++) + { + arguments[argNames[i]] = argValues[i]; + } + + return lambda.GetValueInternal(context, evalContext, arguments); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/LocalVariableNode.cs b/src/Spring/Spring.Core/Expressions/LocalVariableNode.cs new file mode 100644 index 00000000..e4268ec5 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/LocalVariableNode.cs @@ -0,0 +1,87 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed variable node. + /// + /// Aleksandar Seovic + /// $Id: LocalVariableNode.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class LocalVariableNode : BaseNode + { + //internal const string LOCAL_VARIABLES = "__locals"; + + /// + /// Create a new instance + /// + public LocalVariableNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected LocalVariableNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns value of the local variable represented by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + string varName = this.getText(); + IDictionary locals = evalContext.LocalVariables; + if (locals != null) + { + return locals[varName]; + } + return null; + } + + /// + /// Sets value of the local variable represented by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// New value for this node. + protected override void Set(object context, EvaluationContext evalContext, object newValue) + { + string varName = this.getText(); + IDictionary locals = evalContext.LocalVariables; + if (locals == null) + { + locals = new Hashtable(); + evalContext.LocalVariables = locals; + } + locals[varName] = newValue; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/MapEntryNode.cs b/src/Spring/Spring.Core/Expressions/MapEntryNode.cs new file mode 100644 index 00000000..ea3bd763 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/MapEntryNode.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed map entry node. + /// + /// Aleksandar Seovic + /// $Id: MapEntryNode.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class MapEntryNode : BaseNode + { + /// + /// Creates a new instance of . + /// + public MapEntryNode() + {} + + /// + /// Create a new instance from SerializationInfo + /// + protected MapEntryNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Creates new instance of the map entry defined by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object key = ((BaseNode)this.getFirstChild()).GetValueInternal(context, evalContext); + object value = ((BaseNode)this.getFirstChild().getNextSibling()).GetValueInternal(context, evalContext); + + return new DictionaryEntry(key, value); + } + } +} diff --git a/src/Spring/Spring.Core/Expressions/MapInitializerNode.cs b/src/Spring/Spring.Core/Expressions/MapInitializerNode.cs new file mode 100644 index 00000000..53a322b2 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/MapInitializerNode.cs @@ -0,0 +1,71 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; +using antlr.collections; + +namespace Spring.Expressions +{ + /// + /// Represents parsed map initializer node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: MapInitializerNode.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class MapInitializerNode : BaseNode + { + /// + /// Creates a new instance of . + /// + public MapInitializerNode() + {} + + /// + /// Create a new instance from SerializationInfo + /// + protected MapInitializerNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Creates new instance of the map defined by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + IDictionary entries = new Hashtable(); + AST entryNode = this.getFirstChild(); + while (entryNode != null) + { + DictionaryEntry entry = (DictionaryEntry) + ((MapEntryNode)entryNode).GetValueInternal( evalContext.RootContext, evalContext ); + entries[entry.Key] = entry.Value; + entryNode = entryNode.getNextSibling(); + } + + return entries; + } + } +} diff --git a/src/Spring/Spring.Core/Expressions/MethodNode.cs b/src/Spring/Spring.Core/Expressions/MethodNode.cs new file mode 100644 index 00000000..4e446eab --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/MethodNode.cs @@ -0,0 +1,287 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Reflection; +using System.Runtime.Serialization; +using Spring.Expressions.Processors; +using Spring.Util; +using Spring.Reflection.Dynamic; + +namespace Spring.Expressions +{ + /// + /// Represents parsed method node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: MethodNode.cs,v 1.21 2008/03/20 23:58:16 oakinger Exp $ + [Serializable] + public class MethodNode : NodeWithArguments + { + private const BindingFlags BINDING_FLAGS + = BindingFlags.Public | BindingFlags.NonPublic + | BindingFlags.Instance | BindingFlags.Static + | BindingFlags.IgnoreCase; + + private static readonly IDictionary collectionProcessorMap = new Hashtable(); + + private bool initialized = false; + private bool isParamArray = false; + private Type paramArrayType; + private int argumentCount; + private SafeMethod method; + private int methodHash; + private bool isCollectionProcessor = false; + private ICollectionProcessor collectionProcessor; + + /// + /// Static constructor. Initializes a map of special collection processor methods. + /// + static MethodNode() + { + collectionProcessorMap.Add("count", new CountAggregator()); + collectionProcessorMap.Add("sum", new SumAggregator()); + collectionProcessorMap.Add("max", new MaxAggregator()); + collectionProcessorMap.Add("min", new MinAggregator()); + collectionProcessorMap.Add("average", new AverageAggregator()); + collectionProcessorMap.Add("sort", new SortProcessor()); + collectionProcessorMap.Add("orderBy", new OrderByProcessor()); + collectionProcessorMap.Add("distinct", new DistinctProcessor()); + collectionProcessorMap.Add("nonNull", new NonNullProcessor()); + collectionProcessorMap.Add("convert", new ConversionProcessor()); + collectionProcessorMap.Add("reverse", new ReverseProcessor()); + } + + /// + /// Create a new instance + /// + public MethodNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected MethodNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns node's value for the given context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object[] argValues = ResolveArguments(evalContext); + + ICollectionProcessor localCollectionProcessor = null; + SafeMethod localMethod = null; + + // resolve method, if necessary + lock (this) + { + if (!isCollectionProcessor) + { + if ((context == null || context is ICollection)) + { + string methodName = this.getText(); + + // predefined collection processor? + collectionProcessor = (ICollectionProcessor)collectionProcessorMap[methodName]; + isCollectionProcessor = (collectionProcessor != null); + localCollectionProcessor = collectionProcessor; + + if (!isCollectionProcessor && evalContext.Variables != null) + { + localCollectionProcessor = evalContext.Variables[methodName] as ICollectionProcessor; + } + } + } + else + { + localCollectionProcessor = collectionProcessor; + } + + if (localCollectionProcessor == null) + { + if (context == null) + { + throw new ArgumentNullException("Context for method invocation cannot be null."); + } + + // calculate checksum, if the cached method matches the current context + if (initialized) + { + int calculatedHash = CalculateMethodHash(context.GetType(), argValues); + initialized = (calculatedHash == methodHash); + } + + if (!initialized) + { + string methodName = this.getText(); + Initialize(methodName, argValues, context); + initialized = true; + } + + localMethod = method; + } + } + + // invoke method + if (localCollectionProcessor != null) + { + return localCollectionProcessor.Process((ICollection)context, argValues); + } + else + { + object[] paramValues = (isParamArray ? ReflectionUtils.PackageParamArray(argValues, argumentCount, paramArrayType) : argValues); + return localMethod.Invoke(context, paramValues); + } + } + + private int CalculateMethodHash(Type contextType, object[] argValues) + { + int hash = contextType.GetHashCode(); + for (int i = 0; i < argValues.Length; i++) + { + object arg = argValues[i]; + if (arg != null) hash += s_primes[i] * arg.GetType().GetHashCode(); + } + return hash; + } + + private void Initialize(string methodName, object[] argValues, object context) + { + Type contextType = (context is Type ? context as Type : context.GetType()); + + // check the context type first + MethodInfo mi = GetBestMethod(contextType, methodName, BINDING_FLAGS, argValues); + + // if not found, probe the Type's type + if (mi == null) + { + mi = GetBestMethod(typeof(Type), methodName, BINDING_FLAGS, argValues); + } + + if (mi == null) + { + throw new ArgumentException( + string.Format("Method '{0}' with the specified number and types of arguments does not exist.", + methodName)); + } + else + { + ParameterInfo[] parameters = mi.GetParameters(); + if (parameters.Length > 0) + { + ParameterInfo lastParameter = parameters[parameters.Length - 1]; + isParamArray = lastParameter.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0; + if (isParamArray) + { + paramArrayType = lastParameter.ParameterType.GetElementType(); + argumentCount = parameters.Length; + } + } + + method = new SafeMethod(mi); + methodHash = CalculateMethodHash(contextType, argValues); + } + } + + /// + /// Gets the best method given the name, argument values, for a given type. + /// + /// The type on which to search for the method. + /// Name of the method. + /// The binding flags. + /// The arg values. + /// Best matching method or null if none found. + public static MethodInfo GetBestMethod(Type type, string methodName, BindingFlags bindingFlags, object[] argValues) + { + MethodInfo mi = null; + try + { + mi = type.GetMethod(methodName, bindingFlags | BindingFlags.FlattenHierarchy); + } + catch (AmbiguousMatchException) + { + + MethodInfo[] overloads = GetCandidateMethods(type, methodName, bindingFlags, argValues.Length); + if (overloads.Length > 0) + { + mi = ReflectionUtils.GetMethodByArgumentValues(overloads, argValues); + } + } + return mi; + } + + + + private static MethodInfo[] GetCandidateMethods(Type type, string methodName, BindingFlags bindingFlags, int argCount) + { + MethodInfo[] methods = type.GetMethods(bindingFlags | BindingFlags.FlattenHierarchy); + ArrayList matches = new ArrayList(); + + foreach (MethodInfo method in methods) + { + if (method.Name == methodName) + { + ParameterInfo[] parameters = method.GetParameters(); + if (parameters.Length == argCount) + { + matches.Add(method); + } + else if (parameters.Length > 0) + { + ParameterInfo lastParameter = parameters[parameters.Length - 1]; + if (lastParameter.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0) + { + matches.Add(method); + } + } + } + } + + return (MethodInfo[])matches.ToArray(typeof(MethodInfo)); + } + + // used to calculate signature hash while caring for arg positions + private static readonly int[] s_primes = + { + 17, 19, 23, 29 + , 31, 37, 41, 43, 47, 53, 59, 61, 67, 71 + , 73, 79, 83, 89, 97, 101, 103, 107, 109, 113 + , 127, 131, 137, 139, 149, 151, 157, 163, 167, 173 + , 179, 181, 191, 193, 197, 199, 211, 223, 227, 229 + , 233, 239, 241, 251, 257, 263, 269, 271, 277, 281 + , 283, 293, 307, 311, 313, 317, 331, 337, 347, 349 + , 353, 359, 367, 373, 379, 383, 389, 397, 401, 409 + , 419, 421, 431, 433, 439, 443, 449, 457, 461, 463 + , 467, 479, 487, 491, 499, 503, 509, 521, 523, 541 + , 547, 557, 563, 569, 571, 577, 587, 593, 599, 601 + , 607, 613, 617, 619, 631, 641, 643, 647, 653, 659 + , 661, 673, 677, 683, 691, 701, 709, 719, 727, 733 + }; + } +} diff --git a/src/Spring/Spring.Core/Expressions/NamedArgumentNode.cs b/src/Spring/Spring.Core/Expressions/NamedArgumentNode.cs new file mode 100644 index 00000000..7919a669 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/NamedArgumentNode.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed named argument node in the expression. + /// + /// Aleksandar Seovic + /// $Id: NamedArgumentNode.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class NamedArgumentNode : BaseNode + { + /// + /// Create a new instance + /// + public NamedArgumentNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected NamedArgumentNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns the value of the named argument defined by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + return ((BaseNode) this.getFirstChild()).GetValueInternal(evalContext.RootContext, evalContext); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/NodeWithArguments.cs b/src/Spring/Spring.Core/Expressions/NodeWithArguments.cs new file mode 100644 index 00000000..6734ac1c --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/NodeWithArguments.cs @@ -0,0 +1,176 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; +using antlr.collections; + +namespace Spring.Expressions +{ + /// + /// Base type for nodes that accept arguments. + /// + /// Aleksandar Seovic + /// $Id: NodeWithArguments.cs,v 1.15 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public abstract class NodeWithArguments : BaseNode + { + private BaseNode[] args; + private IDictionary namedArgs; + + /// + /// Create a new instance + /// + public NodeWithArguments() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected NodeWithArguments(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Initializes the node. + /// + private void InitializeNode() + { + if (args == null) + { + lock (this) + { + if (args == null) + { + ArrayList argList = new ArrayList(); + namedArgs = new Hashtable(); + + AST node = this.getFirstChild(); + + while (node != null) + { + if (node.getFirstChild() is LambdaExpressionNode) + { + argList.Add(node.getFirstChild()); + } + else if (node is NamedArgumentNode) + { + namedArgs.Add(node.getText(), node); + } + else + { + argList.Add(node); + } + node = node.getNextSibling(); + } + + args = (BaseNode[])argList.ToArray(typeof(BaseNode)); + } + } + } + } + + /// + /// Asserts the argument count. + /// + /// The required count. + protected void AssertArgumentCount(int requiredCount) + { + InitializeNode(); + if (requiredCount != args.Length) + { + throw new ArgumentException("This expression node requires exactly " + + requiredCount + " argument(s) and " + + args.Length + " were specified."); + } + } + + /// + /// Resolves the arguments. + /// + /// Current expression evaluation context. + /// An array of argument values + protected object[] ResolveArguments(EvaluationContext evalContext) + { + InitializeNode(); + object[] values = new object[args.Length]; + for (int i = 0; i < args.Length; i++) + { + values[i] = ResolveArgument(i, evalContext); + } + return values; + } + + /// + /// Resolves the named arguments. + /// + /// Current expression evaluation context. + /// A dictionary of argument name to value mappings. + protected IDictionary ResolveNamedArguments(EvaluationContext evalContext) + { + InitializeNode(); + if (namedArgs.Count == 0) + { + return null; + } + + IDictionary namesAndValues = new Hashtable(namedArgs.Count); + foreach (string name in namedArgs.Keys) + { + namesAndValues[name] = ResolveNamedArgument(name, evalContext); + } + return namesAndValues; + } + + /// + /// Resolves the argument. + /// + /// Argument position. + /// Current expression evaluation context. + /// Resolved argument value. + protected object ResolveArgument(int position, EvaluationContext evalContext) + { + InitializeNode(); + if (args[position] is LambdaExpressionNode) + { + return args[position]; + } + else + { + return ((BaseNode)args[position]).GetValueInternal(evalContext.ThisContext, evalContext); + } + } + + /// + /// Resolves the named argument. + /// + /// Argument name. + /// Current expression evaluation context. + /// Resolved named argument value. + private object ResolveNamedArgument(string name, EvaluationContext evalContext) + { + return ((BaseNode)namedArgs[name]).GetValueInternal(evalContext.ThisContext, evalContext); + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/NullLiteralNode.cs b/src/Spring/Spring.Core/Expressions/NullLiteralNode.cs new file mode 100644 index 00000000..ad53f007 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/NullLiteralNode.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed null literal node. + /// + /// Aleksandar Seovic + /// $Id: NullLiteralNode.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class NullLiteralNode : BaseNode + { + /// + /// Create a new instance + /// + public NullLiteralNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected NullLiteralNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the null literal node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpADD.cs b/src/Spring/Spring.Core/Expressions/OpADD.cs new file mode 100644 index 00000000..928d8d7f --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpADD.cs @@ -0,0 +1,120 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; +using Spring.Collections; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents arithmetic addition operator. + /// + /// Aleksandar Seovic + /// $Id: OpADD.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpADD : BinaryOperator + { + /// + /// Create a new instance + /// + public OpADD() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpADD(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the arithmetic addition operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal(context, evalContext); + object right = Right.GetValueInternal(context, evalContext); + + if (NumberUtils.IsNumber(left) && NumberUtils.IsNumber(right)) + { + return NumberUtils.Add(left, right); + } + else if (left is DateTime && (right is TimeSpan || right is string || NumberUtils.IsNumber(right))) + { + if (NumberUtils.IsNumber(right)) + { + right = TimeSpan.FromDays(Convert.ToDouble(right)); + } + else if (right is string) + { + right = TimeSpan.Parse((string) right); + } + + return (DateTime) left + (TimeSpan) right; + } + else if (left is String || right is String) + { + return left.ToString() + right.ToString(); + } + else if ((left is IList || left is ISet) && (right is IList || right is ISet)) + { + ISet leftset = new HybridSet(left as ICollection); + ISet rightset = new HybridSet(right as ICollection); + return leftset.Union(rightset); + } + else if (left is IDictionary && right is IDictionary) + { + ISet leftset = new HybridSet(((IDictionary) left).Keys); + ISet rightset = new HybridSet(((IDictionary) right).Keys); + ISet unionset = leftset.Union(rightset); + + IDictionary result = new Hashtable(unionset.Count); + foreach(object key in unionset) + { + if(leftset.Contains(key)) + { + result.Add(key, ((IDictionary)left)[key]); + } + else + { + result.Add(key, ((IDictionary)right)[key]); + } + } + return result; + } + else + { + throw new ArgumentException("Cannot add instances of '" + + left.GetType().FullName + + "' and '" + + right.GetType().FullName + + "'."); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpAND.cs b/src/Spring/Spring.Core/Expressions/OpAND.cs new file mode 100644 index 00000000..cb3ea50b --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpAND.cs @@ -0,0 +1,61 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents logical AND operator. + /// + /// Aleksandar Seovic + /// $Id: OpAND.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpAND : BinaryOperator + { + /// + /// Create a new instance + /// + public OpAND() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpAND(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical AND operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + return Convert.ToBoolean(Left.GetValueInternal(context, evalContext)) + && Convert.ToBoolean(Right.GetValueInternal(context, evalContext)); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpBetween.cs b/src/Spring/Spring.Core/Expressions/OpBetween.cs new file mode 100644 index 00000000..298d6409 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpBetween.cs @@ -0,0 +1,75 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents logical BETWEEN operator. + /// + /// Aleksandar Seovic + /// $Id: OpBetween.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpBetween : BinaryOperator + { + /// + /// Create a new instance + /// + public OpBetween() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpBetween(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical IN operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// + /// true if the left operand is contained within the right operand, false otherwise. + /// + protected override object Get(object context, EvaluationContext evalContext) + { + object value = Left.GetValueInternal(context, evalContext); + IList range = Right.GetValueInternal(context, evalContext) as IList; + + if (range == null || range.Count != 2) + { + throw new ArgumentException("Right operand for the 'between' operator has to be a two-element list."); + } + + object low = range[0]; + object high = range[1]; + + return (CompareUtils.Compare(value, low) >= 0 && CompareUtils.Compare(value, high) <= 0); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpDIVIDE.cs b/src/Spring/Spring.Core/Expressions/OpDIVIDE.cs new file mode 100644 index 00000000..d6f6ef72 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpDIVIDE.cs @@ -0,0 +1,76 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents arithmetic division operator. + /// + /// Aleksandar Seovic + /// $Id: OpDIVIDE.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpDIVIDE : BinaryOperator + { + /// + /// Create a new instance + /// + public OpDIVIDE() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpDIVIDE(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the arithmetic division operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal(context, evalContext); + object right = Right.GetValueInternal(context, evalContext); + + if (NumberUtils.IsNumber(left) && NumberUtils.IsNumber(right)) + { + return NumberUtils.Divide(left, right); + } + else + { + throw new ArgumentException("Cannot divide instances of '" + + left.GetType().FullName + + "' and '" + + right.GetType().FullName + + "'."); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpEqual.cs b/src/Spring/Spring.Core/Expressions/OpEqual.cs new file mode 100644 index 00000000..e740cd1d --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpEqual.cs @@ -0,0 +1,94 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents logical equality operator. + /// + /// Aleksandar Seovic + /// $Id: OpEqual.cs,v 1.12 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpEqual : BinaryOperator + { + /// + /// Create a new instance + /// + public OpEqual() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpEqual(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical equality operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal( context, evalContext ); + object right = Right.GetValueInternal( context, evalContext ); + + if (left == null) + { + return (right == null); + } + else if (right == null) + { + return false; + } + else if (left.GetType() == right.GetType()) + { + if (left is Array) + { + return ArrayUtils.AreEqual(left as Array, right as Array); + } + else + { + return left.Equals(right); + } + } + else if (left.GetType().IsEnum && right is string) + { + return left.Equals(Enum.Parse(left.GetType(), (string) right)); + } + else if (right.GetType().IsEnum && left is string) + { + return right.Equals(Enum.Parse(right.GetType(), (string) left)); + } + else + { + return CompareUtils.Compare(left, right) == 0; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpGreater.cs b/src/Spring/Spring.Core/Expressions/OpGreater.cs new file mode 100644 index 00000000..5ca6a970 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpGreater.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents logical "greater than" operator. + /// + /// Aleksandar Seovic + /// $Id: OpGreater.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpGreater : BinaryOperator + { + /// + /// Create a new instance + /// + public OpGreater():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpGreater(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical "greater than" operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal(context, evalContext); + object right = Right.GetValueInternal(context, evalContext); + + return CompareUtils.Compare(left, right) > 0; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpGreaterOrEqual.cs b/src/Spring/Spring.Core/Expressions/OpGreaterOrEqual.cs new file mode 100644 index 00000000..756c453e --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpGreaterOrEqual.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents logical "greater than or equal" operator. + /// + /// Aleksandar Seovic + /// $Id: OpGreaterOrEqual.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpGreaterOrEqual : BinaryOperator + { + /// + /// Create a new instance + /// + public OpGreaterOrEqual():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpGreaterOrEqual(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical "greater than or equal" operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal( context, evalContext ); + object right = Right.GetValueInternal( context, evalContext ); + + return CompareUtils.Compare(left, right) >= 0; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpIn.cs b/src/Spring/Spring.Core/Expressions/OpIn.cs new file mode 100644 index 00000000..05488fce --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpIn.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents logical IN operator. + /// + /// Aleksandar Seovic + /// $Id: OpIn.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpIn : BinaryOperator + { + /// + /// Create a new instance + /// + public OpIn():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpIn(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical IN operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// + /// true if the left operand is contained within the right operand, false otherwise. + /// + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal( context, evalContext ); + object right = Right.GetValueInternal( context, evalContext ); + + if (right == null) + { + return false; + } + else if (right is IList) + { + return ((IList) right).Contains(left); + } + else if (right is IDictionary) + { + return ((IDictionary) right).Contains(left); + } + else + { + throw new ArgumentException( + "Right hand parameter for 'in' operator has to be an instance of IList or IDictionary."); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpIs.cs b/src/Spring/Spring.Core/Expressions/OpIs.cs new file mode 100644 index 00000000..abecd404 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpIs.cs @@ -0,0 +1,69 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents logical IS operator. + /// + /// Aleksandar Seovic + /// $Id: OpIs.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpIs : BinaryOperator + { + /// + /// Create a new instance + /// + public OpIs():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpIs(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical IS operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// + /// true if the left operand is contained within the right operand, false otherwise. + /// + protected override object Get(object context, EvaluationContext evalContext) + { + object instance = Left.GetValueInternal( context, evalContext ); + Type type = Right.GetValueInternal( context, evalContext ) as Type; + + if (instance == null || type == null) + { + return false; + } + return type.IsAssignableFrom(instance.GetType()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpLess.cs b/src/Spring/Spring.Core/Expressions/OpLess.cs new file mode 100644 index 00000000..80c460ec --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpLess.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents logical "less than" operator. + /// + /// Aleksandar Seovic + /// $Id: OpLess.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpLess : BinaryOperator + { + /// + /// Create a new instance + /// + public OpLess():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpLess(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical "less than" operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal( context, evalContext ); + object right = Right.GetValueInternal( context, evalContext ); + + return CompareUtils.Compare(left, right) < 0; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpLessOrEqual.cs b/src/Spring/Spring.Core/Expressions/OpLessOrEqual.cs new file mode 100644 index 00000000..e93e67d5 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpLessOrEqual.cs @@ -0,0 +1,65 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents logical "less than or equal" operator. + /// + /// Aleksandar Seovic + /// $Id: OpLessOrEqual.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpLessOrEqual : BinaryOperator + { + /// + /// Create a new instance + /// + public OpLessOrEqual():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpLessOrEqual(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical "less than or equal" operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal( context, evalContext ); + object right = Right.GetValueInternal( context, evalContext ); + + return CompareUtils.Compare(left, right) <= 0; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpLike.cs b/src/Spring/Spring.Core/Expressions/OpLike.cs new file mode 100644 index 00000000..b8a92561 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpLike.cs @@ -0,0 +1,73 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +#if NET_2_0 && !MONO +using Microsoft.VisualBasic; +using Microsoft.VisualBasic.CompilerServices; +#endif + +namespace Spring.Expressions +{ + /// + /// Represents VB-style logical LIKE operator. + /// + /// Aleksandar Seovic + /// $Id: OpLike.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpLike : BinaryOperator + { + /// + /// Create a new instance + /// + public OpLike():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpLike(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical LIKE operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// + /// true if the left operand matches the right operand, false otherwise. + /// + protected override object Get(object context, EvaluationContext evalContext) + { +#if NET_2_0 && !MONO + string text = Left.GetValueInternal( context, evalContext ) as string; + string pattern = Right.GetValueInternal( context, evalContext ) as string; + + return LikeOperator.LikeString(text, pattern, CompareMethod.Text); +#else + throw new NotSupportedException("'like' operator is only supported in .NET 2.0 or higher."); +#endif + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpMODULUS.cs b/src/Spring/Spring.Core/Expressions/OpMODULUS.cs new file mode 100644 index 00000000..c65316ca --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpMODULUS.cs @@ -0,0 +1,75 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents arithmetic modulus operator. + /// + /// Aleksandar Seovic + /// $Id: OpMODULUS.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpMODULUS : BinaryOperator + { + /// + /// Create a new instance + /// + public OpMODULUS():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpMODULUS(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the arithmetic modulus operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal( context, evalContext ); + object right = Right.GetValueInternal( context, evalContext ); + + if (NumberUtils.IsNumber(left) && NumberUtils.IsNumber(right)) + { + return NumberUtils.Modulus(left, right); + } + else + { + throw new ArgumentException("Cannot calculate modulus for instances of '" + + left.GetType().FullName + + "' and '" + + right.GetType().FullName + + "'."); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpMULTIPLY.cs b/src/Spring/Spring.Core/Expressions/OpMULTIPLY.cs new file mode 100644 index 00000000..575f48fa --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpMULTIPLY.cs @@ -0,0 +1,126 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; +using Spring.Collections; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents arithmetic multiplication operator. + /// + /// Aleksandar Seovic + /// $Id: OpMULTIPLY.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpMULTIPLY : BinaryOperator + { + /// + /// Create a new instance + /// + public OpMULTIPLY():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpMULTIPLY(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the arithmetic multiplication operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal( context, evalContext ); + object right = Right.GetValueInternal( context, evalContext ); + + if (NumberUtils.IsNumber(left) && NumberUtils.IsNumber(right)) + { + return NumberUtils.Multiply(left, right); + } + else if (left is IList || left is ISet) + { + ISet leftset = new HybridSet(left as ICollection); + ISet rightset; + if (right is IList || right is ISet) + { + rightset = new HybridSet(right as ICollection); + } + else if (right is IDictionary) + { + rightset = new HybridSet(((IDictionary)right).Keys); + } + else + { + throw new ArgumentException("Cannot subtract instances of '" + + left.GetType().FullName + + "' and '" + + right.GetType().FullName + + "'."); + } + return leftset.Intersect(rightset); + } + else if (left is IDictionary) + { + ISet leftset = new HybridSet(((IDictionary)left).Keys); + ISet rightset; + if (right is IList || right is ISet) + { + rightset = new HybridSet(right as ICollection); + } + else if (right is IDictionary) + { + rightset = new HybridSet(((IDictionary)right).Keys); + } + else + { + throw new ArgumentException("Cannot subtract instances of '" + + left.GetType().FullName + + "' and '" + + right.GetType().FullName + + "'."); + } + IDictionary result = new Hashtable(rightset.Count); + foreach (object key in leftset.Intersect(rightset)) + { + result.Add(key, ((IDictionary)left)[key]); + } + return result; + } + else + { + throw new ArgumentException("Cannot multiply instances of '" + + left.GetType().FullName + + "' and '" + + right.GetType().FullName + + "'."); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpMatches.cs b/src/Spring/Spring.Core/Expressions/OpMatches.cs new file mode 100644 index 00000000..7a5d9037 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpMatches.cs @@ -0,0 +1,78 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; + +namespace Spring.Expressions +{ + /// + /// Represents logical MATCHES operator. + /// + /// Aleksandar Seovic + /// $Id: OpMatches.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpMatches : BinaryOperator + { + private Regex regex; + + /// + /// Create a new instance + /// + public OpMatches():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpMatches(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical MATCHES operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// + /// true if the left operand matches the right operand, false otherwise. + /// + protected override object Get(object context, EvaluationContext evalContext) + { + if (regex == null) + { + lock (this) + { + if (regex == null) + { + string pattern = Right.GetValueInternal( context, evalContext ) as string; + regex = new Regex(pattern, RegexOptions.Compiled); + } + } + } + + string text = Left.GetValueInternal( context, evalContext ) as string; + return regex.IsMatch(text); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpNOT.cs b/src/Spring/Spring.Core/Expressions/OpNOT.cs new file mode 100644 index 00000000..f923b7d0 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpNOT.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents logical NOT operator. + /// + /// Aleksandar Seovic + /// $Id: OpNOT.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpNOT : UnaryOperator + { + /// + /// Create a new instance + /// + public OpNOT():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpNOT(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical NOT operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + return !Convert.ToBoolean(Operand.GetValueInternal(context, evalContext)); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpNotEqual.cs b/src/Spring/Spring.Core/Expressions/OpNotEqual.cs new file mode 100644 index 00000000..88907771 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpNotEqual.cs @@ -0,0 +1,86 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents logical inequality operator. + /// + /// Aleksandar Seovic + /// $Id: OpNotEqual.cs,v 1.11 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpNotEqual : BinaryOperator + { + /// + /// Create a new instance + /// + public OpNotEqual():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpNotEqual(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical inequality operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal( context, evalContext ); + object right = Right.GetValueInternal( context, evalContext ); + + if (left == null) + { + return (right != null); + } + else if (right == null) + { + return true; + } + else if (left.GetType() == right.GetType()) + { + if (left is Array) + { + return !ArrayUtils.AreEqual(left as Array, right as Array); + } + else + { + return !left.Equals(right); + } + } + else + { + return CompareUtils.Compare(left, right) != 0; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpOR.cs b/src/Spring/Spring.Core/Expressions/OpOR.cs new file mode 100644 index 00000000..14ce8627 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpOR.cs @@ -0,0 +1,61 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents logical OR operator. + /// + /// Aleksandar Seovic + /// $Id: OpOR.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpOR : BinaryOperator + { + /// + /// Create a new instance + /// + public OpOR():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpOR(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the logical OR operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + return Convert.ToBoolean( Left.GetValueInternal( context, evalContext ) ) + || Convert.ToBoolean( Right.GetValueInternal( context, evalContext ) ); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpPOWER.cs b/src/Spring/Spring.Core/Expressions/OpPOWER.cs new file mode 100644 index 00000000..733578e0 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpPOWER.cs @@ -0,0 +1,75 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents arithmetic exponent operator. + /// + /// Aleksandar Seovic + /// $Id: OpPOWER.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpPOWER : BinaryOperator + { + /// + /// Create a new instance + /// + public OpPOWER():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpPOWER(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the arithmetic exponent operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal( context, evalContext ); + object right = Right.GetValueInternal( context, evalContext ); + + if (NumberUtils.IsNumber(left) && NumberUtils.IsNumber(right)) + { + return NumberUtils.Power(left, right); + } + else + { + throw new ArgumentException("Cannot calculate exponent for the instances of '" + + left.GetType().FullName + + "' and '" + + right.GetType().FullName + + "'."); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpSUBTRACT.cs b/src/Spring/Spring.Core/Expressions/OpSUBTRACT.cs new file mode 100644 index 00000000..1f029d26 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpSUBTRACT.cs @@ -0,0 +1,142 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; +using Spring.Collections; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents arithmetic subtraction operator. + /// + /// Aleksandar Seovic + /// $Id: OpSUBTRACT.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpSUBTRACT : BinaryOperator + { + /// + /// Create a new instance + /// + public OpSUBTRACT():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpSUBTRACT(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the arithmetic subtraction operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object left = Left.GetValueInternal( context, evalContext ); + object right = Right.GetValueInternal( context, evalContext ); + + if (NumberUtils.IsNumber(left) && NumberUtils.IsNumber(right)) + { + return NumberUtils.Subtract(left, right); + } + else if (left is DateTime && (right is TimeSpan || right is string || NumberUtils.IsNumber(right))) + { + if (NumberUtils.IsNumber(right)) + { + right = TimeSpan.FromDays(Convert.ToDouble(right)); + } + else if (right is string) + { + right = TimeSpan.Parse((string) right); + } + return (DateTime) left - (TimeSpan) right; + } + else if (left is DateTime && right is DateTime) + { + return (DateTime) left - (DateTime) right; + } + else if (left is IList || left is ISet) + { + ISet leftset = new HybridSet(left as ICollection); + ISet rightset; + if(right is IList || right is ISet) + { + rightset = new HybridSet(right as ICollection); + } + else if (right is IDictionary) + { + rightset = new HybridSet(((IDictionary) right).Keys); + } + else + { + throw new ArgumentException("Cannot subtract instances of '" + + left.GetType().FullName + + "' and '" + + right.GetType().FullName + + "'."); + } + return leftset.Minus(rightset); + } + else if (left is IDictionary) + { + ISet leftset = new HybridSet(((IDictionary) left).Keys); + ISet rightset; + if (right is IList || right is ISet) + { + rightset = new HybridSet(right as ICollection); + } + else if (right is IDictionary) + { + rightset = new HybridSet(((IDictionary) right).Keys); + } + else + { + throw new ArgumentException("Cannot subtract instances of '" + + left.GetType().FullName + + "' and '" + + right.GetType().FullName + + "'."); + } + IDictionary result = new Hashtable(rightset.Count); + foreach(object key in leftset.Minus(rightset)) + { + result.Add(key, ((IDictionary)left)[key]); + } + return result; + } + else + { + throw new ArgumentException("Cannot subtract instances of '" + + left.GetType().FullName + + "' and '" + + right.GetType().FullName + + "'."); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpUnaryMinus.cs b/src/Spring/Spring.Core/Expressions/OpUnaryMinus.cs new file mode 100644 index 00000000..11121670 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpUnaryMinus.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents unary minus operator. + /// + /// Aleksandar Seovic + /// $Id: OpUnaryMinus.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpUnaryMinus : UnaryOperator + { + /// + /// Create a new instance + /// + public OpUnaryMinus():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpUnaryMinus(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the unary plus operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object n = Operand.GetValueInternal( context, evalContext ); + + if (!NumberUtils.IsNumber(n)) + { + throw new ArgumentException( + "Specified operand is not a number. Only numbers support unary minus operator."); + } + return NumberUtils.Negate(n); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/OpUnaryPlus.cs b/src/Spring/Spring.Core/Expressions/OpUnaryPlus.cs new file mode 100644 index 00000000..d0c0c0c1 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/OpUnaryPlus.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +namespace Spring.Expressions +{ + /// + /// Represents unary plus operator. + /// + /// Aleksandar Seovic + /// $Id: OpUnaryPlus.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class OpUnaryPlus : UnaryOperator + { + /// + /// Create a new instance + /// + public OpUnaryPlus():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected OpUnaryPlus(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the unary plus operator node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + object n = Operand.GetValueInternal( context, evalContext ); + + if (!NumberUtils.IsNumber(n)) + { + throw new ArgumentException( + "Specified operand is not a number. Only numbers support unary plus operator."); + } + return n; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/Parser/ExpressionLexer.cs b/src/Spring/Spring.Core/Expressions/Parser/ExpressionLexer.cs new file mode 100644 index 00000000..f80e88df --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Parser/ExpressionLexer.cs @@ -0,0 +1,1939 @@ +// $ANTLR 2.7.6 (2005-12-22): "Expression.g" -> "ExpressionLexer.cs"$ + +namespace Spring.Expressions +{ + // Generate header specific to lexer CSharp file + using System; + using Stream = System.IO.Stream; + using TextReader = System.IO.TextReader; + using Hashtable = System.Collections.Hashtable; + using Comparer = System.Collections.Comparer; + + using TokenStreamException = antlr.TokenStreamException; + using TokenStreamIOException = antlr.TokenStreamIOException; + using TokenStreamRecognitionException = antlr.TokenStreamRecognitionException; + using CharStreamException = antlr.CharStreamException; + using CharStreamIOException = antlr.CharStreamIOException; + using ANTLRException = antlr.ANTLRException; + using CharScanner = antlr.CharScanner; + using InputBuffer = antlr.InputBuffer; + using ByteBuffer = antlr.ByteBuffer; + using CharBuffer = antlr.CharBuffer; + using Token = antlr.Token; + using IToken = antlr.IToken; + using CommonToken = antlr.CommonToken; + using SemanticException = antlr.SemanticException; + using RecognitionException = antlr.RecognitionException; + using NoViableAltForCharException = antlr.NoViableAltForCharException; + using MismatchedCharException = antlr.MismatchedCharException; + using TokenStream = antlr.TokenStream; + using LexerSharedInputState = antlr.LexerSharedInputState; + using BitSet = antlr.collections.impl.BitSet; + + internal class ExpressionLexer : antlr.CharScanner , TokenStream + { + public const int EOF = 1; + public const int NULL_TREE_LOOKAHEAD = 3; + public const int EXPR = 4; + public const int OPERAND = 5; + public const int FALSE = 6; + public const int TRUE = 7; + public const int AND = 8; + public const int OR = 9; + public const int IN = 10; + public const int IS = 11; + public const int BETWEEN = 12; + public const int LIKE = 13; + public const int MATCHES = 14; + public const int NULL_LITERAL = 15; + public const int LPAREN = 16; + public const int SEMI = 17; + public const int RPAREN = 18; + public const int ASSIGN = 19; + public const int DEFAULT = 20; + public const int QMARK = 21; + public const int COLON = 22; + public const int PLUS = 23; + public const int MINUS = 24; + public const int STAR = 25; + public const int DIV = 26; + public const int MOD = 27; + public const int POWER = 28; + public const int BANG = 29; + public const int DOT = 30; + public const int POUND = 31; + public const int ID = 32; + public const int DOLLAR = 33; + public const int COMMA = 34; + public const int AT = 35; + public const int LBRACKET = 36; + public const int RBRACKET = 37; + public const int PROJECT = 38; + public const int RCURLY = 39; + public const int SELECT = 40; + public const int SELECT_FIRST = 41; + public const int SELECT_LAST = 42; + public const int TYPE = 43; + public const int LAMBDA = 44; + public const int PIPE = 45; + public const int LITERAL_new = 46; + public const int LCURLY = 47; + public const int INTEGER_LITERAL = 48; + public const int HEXADECIMAL_INTEGER_LITERAL = 49; + public const int REAL_LITERAL = 50; + public const int STRING_LITERAL = 51; + public const int LITERAL_date = 52; + public const int EQUAL = 53; + public const int NOT_EQUAL = 54; + public const int LESS_THAN = 55; + public const int LESS_THAN_OR_EQUAL = 56; + public const int GREATER_THAN = 57; + public const int GREATER_THAN_OR_EQUAL = 58; + public const int WS = 59; + public const int DOT_ESCAPED = 60; + public const int APOS = 61; + public const int NUMERIC_LITERAL = 62; + public const int DECIMAL_DIGIT = 63; + public const int INTEGER_TYPE_SUFFIX = 64; + public const int HEX_DIGIT = 65; + public const int EXPONENT_PART = 66; + public const int SIGN = 67; + public const int REAL_TYPE_SUFFIX = 68; + + + // CLOVER:OFF + public ExpressionLexer(Stream ins) : this(new ByteBuffer(ins)) + { + } + + public ExpressionLexer(TextReader r) : this(new CharBuffer(r)) + { + } + + public ExpressionLexer(InputBuffer ib) : this(new LexerSharedInputState(ib)) + { + } + + public ExpressionLexer(LexerSharedInputState state) : base(state) + { + initialize(); + } + private void initialize() + { + caseSensitiveLiterals = true; + setCaseSensitive(true); + literals = new Hashtable(100, (float) 0.4, null, Comparer.Default); + literals.Add("true", 7); + literals.Add("and", 8); + literals.Add("matches", 14); + literals.Add("in", 10); + literals.Add("null", 15); + literals.Add("between", 12); + literals.Add("or", 9); + literals.Add("is", 11); + literals.Add("like", 13); + literals.Add("new", 46); + literals.Add("date", 52); + literals.Add("false", 6); + } + + override public IToken nextToken() //throws TokenStreamException + { + IToken theRetToken = null; +tryAgain: + for (;;) + { + IToken _token = null; + int _ttype = Token.INVALID_TYPE; + resetText(); + try // for char stream error handling + { + try // for lexical error handling + { + switch ( cached_LA1 ) + { + case '\t': case '\n': case '\r': case ' ': + { + mWS(true); + theRetToken = returnToken_; + break; + } + case '@': + { + mAT(true); + theRetToken = returnToken_; + break; + } + case '|': + { + mPIPE(true); + theRetToken = returnToken_; + break; + } + case '#': + { + mPOUND(true); + theRetToken = returnToken_; + break; + } + case '(': + { + mLPAREN(true); + theRetToken = returnToken_; + break; + } + case ')': + { + mRPAREN(true); + theRetToken = returnToken_; + break; + } + case '[': + { + mLBRACKET(true); + theRetToken = returnToken_; + break; + } + case ']': + { + mRBRACKET(true); + theRetToken = returnToken_; + break; + } + case '}': + { + mRCURLY(true); + theRetToken = returnToken_; + break; + } + case ',': + { + mCOMMA(true); + theRetToken = returnToken_; + break; + } + case ';': + { + mSEMI(true); + theRetToken = returnToken_; + break; + } + case ':': + { + mCOLON(true); + theRetToken = returnToken_; + break; + } + case '+': + { + mPLUS(true); + theRetToken = returnToken_; + break; + } + case '-': + { + mMINUS(true); + theRetToken = returnToken_; + break; + } + case '/': + { + mDIV(true); + theRetToken = returnToken_; + break; + } + case '*': + { + mSTAR(true); + theRetToken = returnToken_; + break; + } + case '%': + { + mMOD(true); + theRetToken = returnToken_; + break; + } + case '\\': + { + mDOT_ESCAPED(true); + theRetToken = returnToken_; + break; + } + case '\'': + { + mSTRING_LITERAL(true); + theRetToken = returnToken_; + break; + } + default: + if ((cached_LA1=='?') && (cached_LA2=='?')) + { + mDEFAULT(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='=') && (cached_LA2=='=')) { + mEQUAL(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='!') && (cached_LA2=='=')) { + mNOT_EQUAL(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='<') && (cached_LA2=='=')) { + mLESS_THAN_OR_EQUAL(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='>') && (cached_LA2=='=')) { + mGREATER_THAN_OR_EQUAL(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='!') && (cached_LA2=='{')) { + mPROJECT(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='?') && (cached_LA2=='{')) { + mSELECT(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='^') && (cached_LA2=='{')) { + mSELECT_FIRST(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='$') && (cached_LA2=='{')) { + mSELECT_LAST(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='T') && (cached_LA2=='(')) { + mTYPE(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='{') && (cached_LA2=='|')) { + mLAMBDA(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='0') && (cached_LA2=='x')) { + mHEXADECIMAL_INTEGER_LITERAL(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='!') && (true)) { + mBANG(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='?') && (true)) { + mQMARK(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='$') && (true)) { + mDOLLAR(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='{') && (true)) { + mLCURLY(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='=') && (true)) { + mASSIGN(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='^') && (true)) { + mPOWER(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='<') && (true)) { + mLESS_THAN(true); + theRetToken = returnToken_; + } + else if ((cached_LA1=='>') && (true)) { + mGREATER_THAN(true); + theRetToken = returnToken_; + } + else if ((tokenSet_0_.member(cached_LA1)) && (true)) { + mID(true); + theRetToken = returnToken_; + } + else if ((tokenSet_1_.member(cached_LA1)) && (true)) { + mNUMERIC_LITERAL(true); + theRetToken = returnToken_; + } + else + { + if (cached_LA1==EOF_CHAR) { uponEOF(); returnToken_ = makeToken(Token.EOF_TYPE); } + else {throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());} + } + break; } + if ( null==returnToken_ ) goto tryAgain; // found SKIP token + _ttype = returnToken_.Type; + _ttype = testLiteralsTable(_ttype); + returnToken_.Type = _ttype; + return returnToken_; + } + catch (RecognitionException e) { + throw new TokenStreamRecognitionException(e); + } + } + catch (CharStreamException cse) { + if ( cse is CharStreamIOException ) { + throw new TokenStreamIOException(((CharStreamIOException)cse).io); + } + else { + throw new TokenStreamException(cse.Message); + } + } + } + } + + public void mWS(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = WS; + + { + switch ( cached_LA1 ) + { + case ' ': + { + match(' '); + break; + } + case '\t': + { + match('\t'); + break; + } + case '\n': + { + match('\n'); + break; + } + case '\r': + { + match('\r'); + break; + } + default: + { + throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn()); + } + } + } + if (0==inputState.guessing) + { + _ttype = Token.SKIP; + } + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mAT(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = AT; + + match('@'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mPIPE(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = PIPE; + + match('|'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mBANG(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = BANG; + + match('!'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mQMARK(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = QMARK; + + match('?'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mDOLLAR(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = DOLLAR; + + match('$'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mPOUND(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = POUND; + + match('#'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mLPAREN(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = LPAREN; + + match('('); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mRPAREN(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = RPAREN; + + match(')'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mLBRACKET(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = LBRACKET; + + match('['); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mRBRACKET(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = RBRACKET; + + match(']'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mLCURLY(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = LCURLY; + + match('{'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mRCURLY(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = RCURLY; + + match('}'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mCOMMA(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = COMMA; + + match(','); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mSEMI(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = SEMI; + + match(';'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mCOLON(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = COLON; + + match(':'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mASSIGN(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = ASSIGN; + + match('='); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mDEFAULT(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = DEFAULT; + + match("??"); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mPLUS(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = PLUS; + + match('+'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mMINUS(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = MINUS; + + match('-'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mDIV(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = DIV; + + match('/'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mSTAR(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = STAR; + + match('*'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mMOD(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = MOD; + + match('%'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mPOWER(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = POWER; + + match('^'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mEQUAL(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = EQUAL; + + match("=="); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mNOT_EQUAL(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = NOT_EQUAL; + + match("!="); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mLESS_THAN(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = LESS_THAN; + + match('<'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mLESS_THAN_OR_EQUAL(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = LESS_THAN_OR_EQUAL; + + match("<="); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mGREATER_THAN(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = GREATER_THAN; + + match('>'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mGREATER_THAN_OR_EQUAL(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = GREATER_THAN_OR_EQUAL; + + match(">="); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mPROJECT(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = PROJECT; + + match("!{"); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mSELECT(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = SELECT; + + match("?{"); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mSELECT_FIRST(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = SELECT_FIRST; + + match("^{"); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mSELECT_LAST(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = SELECT_LAST; + + match("${"); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mTYPE(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = TYPE; + + match("T("); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mLAMBDA(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = LAMBDA; + + match("{|"); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mDOT_ESCAPED(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = DOT_ESCAPED; + + match("\\."); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mSTRING_LITERAL(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = STRING_LITERAL; + + int _saveIndex = 0; + _saveIndex = text.Length; + match('\''); + text.Length = _saveIndex; + { // ( ... )* + for (;;) + { + if ((cached_LA1=='\'') && (cached_LA2=='\'')) + { + mAPOS(false); + } + else if ((tokenSet_2_.member(cached_LA1))) { + matchNot('\''); + } + else + { + goto _loop156_breakloop; + } + + } +_loop156_breakloop: ; + } // ( ... )* + _saveIndex = text.Length; + match('\''); + text.Length = _saveIndex; + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + protected void mAPOS(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = APOS; + + int _saveIndex = 0; + _saveIndex = text.Length; + match('\''); + text.Length = _saveIndex; + match('\''); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mID(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = ID; + + { + switch ( cached_LA1 ) + { + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + { + matchRange('a','z'); + break; + } + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + { + matchRange('A','Z'); + break; + } + case '_': + { + match('_'); + break; + } + default: + { + throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn()); + } + } + } + { // ( ... )* + for (;;) + { + switch ( cached_LA1 ) + { + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + { + matchRange('a','z'); + break; + } + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + { + matchRange('A','Z'); + break; + } + case '_': + { + match('_'); + break; + } + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + { + matchRange('0','9'); + break; + } + case '\\': + { + mDOT_ESCAPED(false); + break; + } + default: + { + goto _loop161_breakloop; + } + } + } +_loop161_breakloop: ; + } // ( ... )* + _ttype = testLiteralsTable(_ttype); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mNUMERIC_LITERAL(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = NUMERIC_LITERAL; + + bool synPredMatched164 = false; + if (((cached_LA1=='.') && ((cached_LA2 >= '0' && cached_LA2 <= '9')))) + { + int _m164 = mark(); + synPredMatched164 = true; + inputState.guessing++; + try { + { + match('.'); + mDECIMAL_DIGIT(false); + } + } + catch (RecognitionException) + { + synPredMatched164 = false; + } + rewind(_m164); + inputState.guessing--; + } + if ( synPredMatched164 ) + { + match('.'); + { // ( ... )+ + int _cnt166=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt166 >= 1) { goto _loop166_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt166++; + } +_loop166_breakloop: ; + } // ( ... )+ + { + if ((cached_LA1=='E'||cached_LA1=='e')) + { + mEXPONENT_PART(false); + } + else { + } + + } + { + if ((tokenSet_3_.member(cached_LA1))) + { + mREAL_TYPE_SUFFIX(false); + } + else { + } + + } + if (0==inputState.guessing) + { + _ttype = REAL_LITERAL; + } + } + else { + bool synPredMatched172 = false; + if ((((cached_LA1 >= '0' && cached_LA1 <= '9')) && (tokenSet_1_.member(cached_LA2)))) + { + int _m172 = mark(); + synPredMatched172 = true; + inputState.guessing++; + try { + { + { // ( ... )+ + int _cnt171=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt171 >= 1) { goto _loop171_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt171++; + } +_loop171_breakloop: ; + } // ( ... )+ + match('.'); + mDECIMAL_DIGIT(false); + } + } + catch (RecognitionException) + { + synPredMatched172 = false; + } + rewind(_m172); + inputState.guessing--; + } + if ( synPredMatched172 ) + { + { // ( ... )+ + int _cnt174=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt174 >= 1) { goto _loop174_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt174++; + } +_loop174_breakloop: ; + } // ( ... )+ + match('.'); + { // ( ... )+ + int _cnt176=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt176 >= 1) { goto _loop176_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt176++; + } +_loop176_breakloop: ; + } // ( ... )+ + { + if ((cached_LA1=='E'||cached_LA1=='e')) + { + mEXPONENT_PART(false); + } + else { + } + + } + { + if ((tokenSet_3_.member(cached_LA1))) + { + mREAL_TYPE_SUFFIX(false); + } + else { + } + + } + if (0==inputState.guessing) + { + _ttype = REAL_LITERAL; + } + } + else { + bool synPredMatched183 = false; + if ((((cached_LA1 >= '0' && cached_LA1 <= '9')) && (tokenSet_4_.member(cached_LA2)))) + { + int _m183 = mark(); + synPredMatched183 = true; + inputState.guessing++; + try { + { + { // ( ... )+ + int _cnt181=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt181 >= 1) { goto _loop181_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt181++; + } +_loop181_breakloop: ; + } // ( ... )+ + { + mEXPONENT_PART(false); + } + } + } + catch (RecognitionException) + { + synPredMatched183 = false; + } + rewind(_m183); + inputState.guessing--; + } + if ( synPredMatched183 ) + { + { // ( ... )+ + int _cnt185=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt185 >= 1) { goto _loop185_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt185++; + } +_loop185_breakloop: ; + } // ( ... )+ + { + mEXPONENT_PART(false); + } + { + if ((tokenSet_3_.member(cached_LA1))) + { + mREAL_TYPE_SUFFIX(false); + } + else { + } + + } + if (0==inputState.guessing) + { + _ttype = REAL_LITERAL; + } + } + else { + bool synPredMatched192 = false; + if ((((cached_LA1 >= '0' && cached_LA1 <= '9')) && (tokenSet_5_.member(cached_LA2)))) + { + int _m192 = mark(); + synPredMatched192 = true; + inputState.guessing++; + try { + { + { // ( ... )+ + int _cnt190=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt190 >= 1) { goto _loop190_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt190++; + } +_loop190_breakloop: ; + } // ( ... )+ + { + mREAL_TYPE_SUFFIX(false); + } + } + } + catch (RecognitionException) + { + synPredMatched192 = false; + } + rewind(_m192); + inputState.guessing--; + } + if ( synPredMatched192 ) + { + { // ( ... )+ + int _cnt194=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt194 >= 1) { goto _loop194_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt194++; + } +_loop194_breakloop: ; + } // ( ... )+ + { + mREAL_TYPE_SUFFIX(false); + } + if (0==inputState.guessing) + { + _ttype = REAL_LITERAL; + } + } + else if (((cached_LA1 >= '0' && cached_LA1 <= '9')) && (true)) { + { // ( ... )+ + int _cnt197=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt197 >= 1) { goto _loop197_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt197++; + } +_loop197_breakloop: ; + } // ( ... )+ + { + if ((tokenSet_6_.member(cached_LA1))) + { + mINTEGER_TYPE_SUFFIX(false); + } + else { + } + + } + if (0==inputState.guessing) + { + _ttype = INTEGER_LITERAL; + } + } + else if ((cached_LA1=='.') && (true)) { + match('.'); + if (0==inputState.guessing) + { + _ttype = DOT; + } + } + else + { + throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn()); + } + }}} + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + protected void mDECIMAL_DIGIT(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = DECIMAL_DIGIT; + + matchRange('0','9'); + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + protected void mEXPONENT_PART(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = EXPONENT_PART; + + switch ( cached_LA1 ) + { + case 'e': + { + match("e"); + { // ( ... )* + for (;;) + { + if ((cached_LA1=='+'||cached_LA1=='-')) + { + mSIGN(false); + } + else + { + goto _loop209_breakloop; + } + + } +_loop209_breakloop: ; + } // ( ... )* + { // ( ... )+ + int _cnt211=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt211 >= 1) { goto _loop211_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt211++; + } +_loop211_breakloop: ; + } // ( ... )+ + break; + } + case 'E': + { + match("E"); + { // ( ... )* + for (;;) + { + if ((cached_LA1=='+'||cached_LA1=='-')) + { + mSIGN(false); + } + else + { + goto _loop213_breakloop; + } + + } +_loop213_breakloop: ; + } // ( ... )* + { // ( ... )+ + int _cnt215=0; + for (;;) + { + if (((cached_LA1 >= '0' && cached_LA1 <= '9'))) + { + mDECIMAL_DIGIT(false); + } + else + { + if (_cnt215 >= 1) { goto _loop215_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt215++; + } +_loop215_breakloop: ; + } // ( ... )+ + break; + } + default: + { + throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn()); + } + } + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + protected void mREAL_TYPE_SUFFIX(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = REAL_TYPE_SUFFIX; + + switch ( cached_LA1 ) + { + case 'F': + { + match('F'); + break; + } + case 'f': + { + match('f'); + break; + } + case 'D': + { + match('D'); + break; + } + case 'd': + { + match('d'); + break; + } + case 'M': + { + match('M'); + break; + } + case 'm': + { + match('m'); + break; + } + default: + { + throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn()); + } + } + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + protected void mINTEGER_TYPE_SUFFIX(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = INTEGER_TYPE_SUFFIX; + + { + if ((cached_LA1=='U') && (cached_LA2=='L')) + { + match("UL"); + } + else if ((cached_LA1=='L') && (cached_LA2=='U')) { + match("LU"); + } + else if ((cached_LA1=='u') && (cached_LA2=='l')) { + match("ul"); + } + else if ((cached_LA1=='l') && (cached_LA2=='u')) { + match("lu"); + } + else if ((cached_LA1=='U') && (cached_LA2=='L')) { + match("UL"); + } + else if ((cached_LA1=='L') && (cached_LA2=='U')) { + match("LU"); + } + else if ((cached_LA1=='u') && (cached_LA2=='L')) { + match("uL"); + } + else if ((cached_LA1=='l') && (cached_LA2=='U')) { + match("lU"); + } + else if ((cached_LA1=='U') && (true)) { + match("U"); + } + else if ((cached_LA1=='L') && (true)) { + match("L"); + } + else if ((cached_LA1=='u') && (true)) { + match("u"); + } + else if ((cached_LA1=='l') && (true)) { + match("l"); + } + else + { + throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn()); + } + + } + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + public void mHEXADECIMAL_INTEGER_LITERAL(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = HEXADECIMAL_INTEGER_LITERAL; + + match("0x"); + { // ( ... )+ + int _cnt201=0; + for (;;) + { + if ((tokenSet_7_.member(cached_LA1))) + { + mHEX_DIGIT(false); + } + else + { + if (_cnt201 >= 1) { goto _loop201_breakloop; } else { throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());; } + } + + _cnt201++; + } +_loop201_breakloop: ; + } // ( ... )+ + { + if ((tokenSet_6_.member(cached_LA1))) + { + mINTEGER_TYPE_SUFFIX(false); + } + else { + } + + } + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + protected void mHEX_DIGIT(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = HEX_DIGIT; + + switch ( cached_LA1 ) + { + case '0': + { + match('0'); + break; + } + case '1': + { + match('1'); + break; + } + case '2': + { + match('2'); + break; + } + case '3': + { + match('3'); + break; + } + case '4': + { + match('4'); + break; + } + case '5': + { + match('5'); + break; + } + case '6': + { + match('6'); + break; + } + case '7': + { + match('7'); + break; + } + case '8': + { + match('8'); + break; + } + case '9': + { + match('9'); + break; + } + case 'A': + { + match('A'); + break; + } + case 'B': + { + match('B'); + break; + } + case 'C': + { + match('C'); + break; + } + case 'D': + { + match('D'); + break; + } + case 'E': + { + match('E'); + break; + } + case 'F': + { + match('F'); + break; + } + case 'a': + { + match('a'); + break; + } + case 'b': + { + match('b'); + break; + } + case 'c': + { + match('c'); + break; + } + case 'd': + { + match('d'); + break; + } + case 'e': + { + match('e'); + break; + } + case 'f': + { + match('f'); + break; + } + default: + { + throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn()); + } + } + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + protected void mSIGN(bool _createToken) //throws RecognitionException, CharStreamException, TokenStreamException +{ + int _ttype; IToken _token=null; int _begin=text.Length; + _ttype = SIGN; + + switch ( cached_LA1 ) + { + case '+': + { + match('+'); + break; + } + case '-': + { + match('-'); + break; + } + default: + { + throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn()); + } + } + if (_createToken && (null == _token) && (_ttype != Token.SKIP)) + { + _token = makeToken(_ttype); + _token.setText(text.ToString(_begin, text.Length-_begin)); + } + returnToken_ = _token; + } + + + private static long[] mk_tokenSet_0_() + { + long[] data = new long[1025]; + data[0]=0L; + data[1]=576460745995190270L; + for (int i = 2; i<=1024; i++) { data[i]=0L; } + return data; + } + public static readonly BitSet tokenSet_0_ = new BitSet(mk_tokenSet_0_()); + private static long[] mk_tokenSet_1_() + { + long[] data = new long[1025]; + data[0]=288019269919178752L; + for (int i = 1; i<=1024; i++) { data[i]=0L; } + return data; + } + public static readonly BitSet tokenSet_1_ = new BitSet(mk_tokenSet_1_()); + private static long[] mk_tokenSet_2_() + { + long[] data = new long[2048]; + data[0]=-549755813889L; + for (int i = 1; i<=1022; i++) { data[i]=-1L; } + data[1023]=9223372036854775807L; + for (int i = 1024; i<=2047; i++) { data[i]=0L; } + return data; + } + public static readonly BitSet tokenSet_2_ = new BitSet(mk_tokenSet_2_()); + private static long[] mk_tokenSet_3_() + { + long[] data = new long[1025]; + data[0]=0L; + data[1]=35527969480784L; + for (int i = 2; i<=1024; i++) { data[i]=0L; } + return data; + } + public static readonly BitSet tokenSet_3_ = new BitSet(mk_tokenSet_3_()); + private static long[] mk_tokenSet_4_() + { + long[] data = new long[1025]; + data[0]=287948901175001088L; + data[1]=137438953504L; + for (int i = 2; i<=1024; i++) { data[i]=0L; } + return data; + } + public static readonly BitSet tokenSet_4_ = new BitSet(mk_tokenSet_4_()); + private static long[] mk_tokenSet_5_() + { + long[] data = new long[1025]; + data[0]=287948901175001088L; + data[1]=35527969480784L; + for (int i = 2; i<=1024; i++) { data[i]=0L; } + return data; + } + public static readonly BitSet tokenSet_5_ = new BitSet(mk_tokenSet_5_()); + private static long[] mk_tokenSet_6_() + { + long[] data = new long[1025]; + data[0]=0L; + data[1]=9024791442886656L; + for (int i = 2; i<=1024; i++) { data[i]=0L; } + return data; + } + public static readonly BitSet tokenSet_6_ = new BitSet(mk_tokenSet_6_()); + private static long[] mk_tokenSet_7_() + { + long[] data = new long[1025]; + data[0]=287948901175001088L; + data[1]=541165879422L; + for (int i = 2; i<=1024; i++) { data[i]=0L; } + return data; + } + public static readonly BitSet tokenSet_7_ = new BitSet(mk_tokenSet_7_()); + +} +} diff --git a/src/Spring/Spring.Core/Expressions/Parser/ExpressionParser.cs b/src/Spring/Spring.Core/Expressions/Parser/ExpressionParser.cs new file mode 100644 index 00000000..d7779e60 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Parser/ExpressionParser.cs @@ -0,0 +1,3441 @@ +// $ANTLR 2.7.6 (2005-12-22): "Expression.g" -> "ExpressionParser.cs"$ + +namespace Spring.Expressions +{ + // Generate the header common to all output files. + using System; + + using TokenBuffer = antlr.TokenBuffer; + using TokenStreamException = antlr.TokenStreamException; + using TokenStreamIOException = antlr.TokenStreamIOException; + using ANTLRException = antlr.ANTLRException; + using LLkParser = antlr.LLkParser; + using Token = antlr.Token; + using IToken = antlr.IToken; + using TokenStream = antlr.TokenStream; + using RecognitionException = antlr.RecognitionException; + using NoViableAltException = antlr.NoViableAltException; + using MismatchedTokenException = antlr.MismatchedTokenException; + using SemanticException = antlr.SemanticException; + using ParserSharedInputState = antlr.ParserSharedInputState; + using BitSet = antlr.collections.impl.BitSet; + using AST = antlr.collections.AST; + using ASTPair = antlr.ASTPair; + using ASTFactory = antlr.ASTFactory; + using ASTArray = antlr.collections.impl.ASTArray; + + internal class ExpressionParser : antlr.LLkParser + { + public const int EOF = 1; + public const int NULL_TREE_LOOKAHEAD = 3; + public const int EXPR = 4; + public const int OPERAND = 5; + public const int FALSE = 6; + public const int TRUE = 7; + public const int AND = 8; + public const int OR = 9; + public const int IN = 10; + public const int IS = 11; + public const int BETWEEN = 12; + public const int LIKE = 13; + public const int MATCHES = 14; + public const int NULL_LITERAL = 15; + public const int LPAREN = 16; + public const int SEMI = 17; + public const int RPAREN = 18; + public const int ASSIGN = 19; + public const int DEFAULT = 20; + public const int QMARK = 21; + public const int COLON = 22; + public const int PLUS = 23; + public const int MINUS = 24; + public const int STAR = 25; + public const int DIV = 26; + public const int MOD = 27; + public const int POWER = 28; + public const int BANG = 29; + public const int DOT = 30; + public const int POUND = 31; + public const int ID = 32; + public const int DOLLAR = 33; + public const int COMMA = 34; + public const int AT = 35; + public const int LBRACKET = 36; + public const int RBRACKET = 37; + public const int PROJECT = 38; + public const int RCURLY = 39; + public const int SELECT = 40; + public const int SELECT_FIRST = 41; + public const int SELECT_LAST = 42; + public const int TYPE = 43; + public const int LAMBDA = 44; + public const int PIPE = 45; + public const int LITERAL_new = 46; + public const int LCURLY = 47; + public const int INTEGER_LITERAL = 48; + public const int HEXADECIMAL_INTEGER_LITERAL = 49; + public const int REAL_LITERAL = 50; + public const int STRING_LITERAL = 51; + public const int LITERAL_date = 52; + public const int EQUAL = 53; + public const int NOT_EQUAL = 54; + public const int LESS_THAN = 55; + public const int LESS_THAN_OR_EQUAL = 56; + public const int GREATER_THAN = 57; + public const int GREATER_THAN_OR_EQUAL = 58; + public const int WS = 59; + public const int DOT_ESCAPED = 60; + public const int APOS = 61; + public const int NUMERIC_LITERAL = 62; + public const int DECIMAL_DIGIT = 63; + public const int INTEGER_TYPE_SUFFIX = 64; + public const int HEX_DIGIT = 65; + public const int EXPONENT_PART = 66; + public const int SIGN = 67; + public const int REAL_TYPE_SUFFIX = 68; + + + // CLOVER:OFF + + public override void reportError(RecognitionException ex) + { + //base.reportError(ex); + throw ex; + } + + public override void reportError(string error) + { + //base.reportError(error); + throw new RecognitionException(error); + } + + private string GetRelationalOperatorNodeType(string op) + { + switch (op) + { + case "==" : return "Spring.Expressions.OpEqual"; + case "!=" : return "Spring.Expressions.OpNotEqual"; + case "<" : return "Spring.Expressions.OpLess"; + case "<=" : return "Spring.Expressions.OpLessOrEqual"; + case ">" : return "Spring.Expressions.OpGreater"; + case ">=" : return "Spring.Expressions.OpGreaterOrEqual"; + case "in" : return "Spring.Expressions.OpIn"; + case "is" : return "Spring.Expressions.OpIs"; + case "between" : return "Spring.Expressions.OpBetween"; + case "like" : return "Spring.Expressions.OpLike"; + case "matches" : return "Spring.Expressions.OpMatches"; + default : + throw new ArgumentException("Node type for operator '" + op + "' is not defined."); + } + } + + protected void initialize() + { + tokenNames = tokenNames_; + initializeFactory(); + } + + + protected ExpressionParser(TokenBuffer tokenBuf, int k) : base(tokenBuf, k) + { + initialize(); + } + + public ExpressionParser(TokenBuffer tokenBuf) : this(tokenBuf,2) + { + } + + protected ExpressionParser(TokenStream lexer, int k) : base(lexer,k) + { + initialize(); + } + + public ExpressionParser(TokenStream lexer) : this(lexer,2) + { + } + + public ExpressionParser(ParserSharedInputState state) : base(state,2) + { + initialize(); + } + + public void expr() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST expr_AST = null; + + try { // for error handling + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + match(Token.EOF_TYPE); + expr_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_0_); + } + else + { + throw ex; + } + } + returnAST = expr_AST; + } + + public void expression() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST expression_AST = null; + + try { // for error handling + logicalOrExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { + switch ( LA(1) ) + { + case ASSIGN: + { + { + Spring.Expressions.AssignNode tmp2_AST = null; + tmp2_AST = (Spring.Expressions.AssignNode) astFactory.create(LT(1), "Spring.Expressions.AssignNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp2_AST); + match(ASSIGN); + logicalOrExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + break; + } + case DEFAULT: + { + { + Spring.Expressions.DefaultNode tmp3_AST = null; + tmp3_AST = (Spring.Expressions.DefaultNode) astFactory.create(LT(1), "Spring.Expressions.DefaultNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp3_AST); + match(DEFAULT); + logicalOrExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + break; + } + case QMARK: + { + { + Spring.Expressions.TernaryNode tmp4_AST = null; + tmp4_AST = (Spring.Expressions.TernaryNode) astFactory.create(LT(1), "Spring.Expressions.TernaryNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp4_AST); + match(QMARK); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + match(COLON); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + break; + } + case EOF: + case SEMI: + case RPAREN: + case COLON: + case COMMA: + case RBRACKET: + case RCURLY: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + expression_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_1_); + } + else + { + throw ex; + } + } + returnAST = expression_AST; + } + + public void exprList() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST exprList_AST = null; + + try { // for error handling + match(LPAREN); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )+ + int _cnt4=0; + for (;;) + { + if ((LA(1)==SEMI)) + { + match(SEMI); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + if (_cnt4 >= 1) { goto _loop4_breakloop; } else { throw new NoViableAltException(LT(1), getFilename());; } + } + + _cnt4++; + } +_loop4_breakloop: ; + } // ( ... )+ + match(RPAREN); + if (0==inputState.guessing) + { + exprList_AST = (Spring.Expressions.SpringAST)currentAST.root; + exprList_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,"expressionList","Spring.Expressions.ExpressionListNode"), (AST)exprList_AST); + currentAST.root = exprList_AST; + if ( (null != exprList_AST) && (null != exprList_AST.getFirstChild()) ) + currentAST.child = exprList_AST.getFirstChild(); + else + currentAST.child = exprList_AST; + currentAST.advanceChildToEnd(); + } + exprList_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = exprList_AST; + } + + public void logicalOrExpression() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST logicalOrExpression_AST = null; + + try { // for error handling + logicalAndExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if ((LA(1)==OR)) + { + Spring.Expressions.OpOR tmp9_AST = null; + tmp9_AST = (Spring.Expressions.OpOR) astFactory.create(LT(1), "Spring.Expressions.OpOR"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp9_AST); + match(OR); + logicalAndExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop13_breakloop; + } + + } +_loop13_breakloop: ; + } // ( ... )* + logicalOrExpression_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_3_); + } + else + { + throw ex; + } + } + returnAST = logicalOrExpression_AST; + } + + public void parenExpr() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST parenExpr_AST = null; + + try { // for error handling + match(LPAREN); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + match(RPAREN); + parenExpr_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = parenExpr_AST; + } + + public void logicalAndExpression() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST logicalAndExpression_AST = null; + + try { // for error handling + relationalExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if ((LA(1)==AND)) + { + Spring.Expressions.OpAND tmp12_AST = null; + tmp12_AST = (Spring.Expressions.OpAND) astFactory.create(LT(1), "Spring.Expressions.OpAND"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp12_AST); + match(AND); + relationalExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop16_breakloop; + } + + } +_loop16_breakloop: ; + } // ( ... )* + logicalAndExpression_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_4_); + } + else + { + throw ex; + } + } + returnAST = logicalAndExpression_AST; + } + + public void relationalExpression() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST relationalExpression_AST = null; + Spring.Expressions.SpringAST e1_AST = null; + Spring.Expressions.SpringAST op_AST = null; + Spring.Expressions.SpringAST e2_AST = null; + + try { // for error handling + sumExpr(); + if (0 == inputState.guessing) + { + e1_AST = (Spring.Expressions.SpringAST)returnAST; + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { + if ((tokenSet_5_.member(LA(1)))) + { + relationalOperator(); + if (0 == inputState.guessing) + { + op_AST = (Spring.Expressions.SpringAST)returnAST; + } + sumExpr(); + if (0 == inputState.guessing) + { + e2_AST = (Spring.Expressions.SpringAST)returnAST; + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + if (0==inputState.guessing) + { + relationalExpression_AST = (Spring.Expressions.SpringAST)currentAST.root; + relationalExpression_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,op_AST.getText(),GetRelationalOperatorNodeType(op_AST.getText())), (AST)relationalExpression_AST); + currentAST.root = relationalExpression_AST; + if ( (null != relationalExpression_AST) && (null != relationalExpression_AST.getFirstChild()) ) + currentAST.child = relationalExpression_AST.getFirstChild(); + else + currentAST.child = relationalExpression_AST; + currentAST.advanceChildToEnd(); + } + } + else if ((tokenSet_6_.member(LA(1)))) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + relationalExpression_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_6_); + } + else + { + throw ex; + } + } + returnAST = relationalExpression_AST; + } + + public void sumExpr() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST sumExpr_AST = null; + + try { // for error handling + prodExpr(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if ((LA(1)==PLUS||LA(1)==MINUS)) + { + { + if ((LA(1)==PLUS)) + { + Spring.Expressions.OpADD tmp13_AST = null; + tmp13_AST = (Spring.Expressions.OpADD) astFactory.create(LT(1), "Spring.Expressions.OpADD"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp13_AST); + match(PLUS); + } + else if ((LA(1)==MINUS)) { + Spring.Expressions.OpSUBTRACT tmp14_AST = null; + tmp14_AST = (Spring.Expressions.OpSUBTRACT) astFactory.create(LT(1), "Spring.Expressions.OpSUBTRACT"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp14_AST); + match(MINUS); + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + prodExpr(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop22_breakloop; + } + + } +_loop22_breakloop: ; + } // ( ... )* + sumExpr_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_7_); + } + else + { + throw ex; + } + } + returnAST = sumExpr_AST; + } + + public void relationalOperator() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST relationalOperator_AST = null; + + try { // for error handling + switch ( LA(1) ) + { + case EQUAL: + { + Spring.Expressions.SpringAST tmp15_AST = null; + tmp15_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp15_AST); + match(EQUAL); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case NOT_EQUAL: + { + Spring.Expressions.SpringAST tmp16_AST = null; + tmp16_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp16_AST); + match(NOT_EQUAL); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case LESS_THAN: + { + Spring.Expressions.SpringAST tmp17_AST = null; + tmp17_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp17_AST); + match(LESS_THAN); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case LESS_THAN_OR_EQUAL: + { + Spring.Expressions.SpringAST tmp18_AST = null; + tmp18_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp18_AST); + match(LESS_THAN_OR_EQUAL); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case GREATER_THAN: + { + Spring.Expressions.SpringAST tmp19_AST = null; + tmp19_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp19_AST); + match(GREATER_THAN); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case GREATER_THAN_OR_EQUAL: + { + Spring.Expressions.SpringAST tmp20_AST = null; + tmp20_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp20_AST); + match(GREATER_THAN_OR_EQUAL); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case IN: + { + Spring.Expressions.SpringAST tmp21_AST = null; + tmp21_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp21_AST); + match(IN); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case IS: + { + Spring.Expressions.SpringAST tmp22_AST = null; + tmp22_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp22_AST); + match(IS); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case BETWEEN: + { + Spring.Expressions.SpringAST tmp23_AST = null; + tmp23_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp23_AST); + match(BETWEEN); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case LIKE: + { + Spring.Expressions.SpringAST tmp24_AST = null; + tmp24_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp24_AST); + match(LIKE); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case MATCHES: + { + Spring.Expressions.SpringAST tmp25_AST = null; + tmp25_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp25_AST); + match(MATCHES); + relationalOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_8_); + } + else + { + throw ex; + } + } + returnAST = relationalOperator_AST; + } + + public void prodExpr() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST prodExpr_AST = null; + + try { // for error handling + powExpr(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if (((LA(1) >= STAR && LA(1) <= MOD))) + { + { + switch ( LA(1) ) + { + case STAR: + { + Spring.Expressions.OpMULTIPLY tmp26_AST = null; + tmp26_AST = (Spring.Expressions.OpMULTIPLY) astFactory.create(LT(1), "Spring.Expressions.OpMULTIPLY"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp26_AST); + match(STAR); + break; + } + case DIV: + { + Spring.Expressions.OpDIVIDE tmp27_AST = null; + tmp27_AST = (Spring.Expressions.OpDIVIDE) astFactory.create(LT(1), "Spring.Expressions.OpDIVIDE"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp27_AST); + match(DIV); + break; + } + case MOD: + { + Spring.Expressions.OpMODULUS tmp28_AST = null; + tmp28_AST = (Spring.Expressions.OpMODULUS) astFactory.create(LT(1), "Spring.Expressions.OpMODULUS"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp28_AST); + match(MOD); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + powExpr(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop26_breakloop; + } + + } +_loop26_breakloop: ; + } // ( ... )* + prodExpr_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_9_); + } + else + { + throw ex; + } + } + returnAST = prodExpr_AST; + } + + public void powExpr() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST powExpr_AST = null; + + try { // for error handling + unaryExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { + if ((LA(1)==POWER)) + { + Spring.Expressions.OpPOWER tmp29_AST = null; + tmp29_AST = (Spring.Expressions.OpPOWER) astFactory.create(LT(1), "Spring.Expressions.OpPOWER"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp29_AST); + match(POWER); + unaryExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((tokenSet_10_.member(LA(1)))) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + powExpr_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_10_); + } + else + { + throw ex; + } + } + returnAST = powExpr_AST; + } + + public void unaryExpression() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST unaryExpression_AST = null; + + try { // for error handling + if ((LA(1)==PLUS||LA(1)==MINUS||LA(1)==BANG)) + { + { + switch ( LA(1) ) + { + case PLUS: + { + Spring.Expressions.OpUnaryPlus tmp30_AST = null; + tmp30_AST = (Spring.Expressions.OpUnaryPlus) astFactory.create(LT(1), "Spring.Expressions.OpUnaryPlus"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp30_AST); + match(PLUS); + break; + } + case MINUS: + { + Spring.Expressions.OpUnaryMinus tmp31_AST = null; + tmp31_AST = (Spring.Expressions.OpUnaryMinus) astFactory.create(LT(1), "Spring.Expressions.OpUnaryMinus"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp31_AST); + match(MINUS); + break; + } + case BANG: + { + Spring.Expressions.OpNOT tmp32_AST = null; + tmp32_AST = (Spring.Expressions.OpNOT) astFactory.create(LT(1), "Spring.Expressions.OpNOT"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp32_AST); + match(BANG); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + unaryExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + unaryExpression_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else if ((tokenSet_11_.member(LA(1)))) { + primaryExpression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + unaryExpression_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_12_); + } + else + { + throw ex; + } + } + returnAST = unaryExpression_AST; + } + + public void primaryExpression() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST primaryExpression_AST = null; + + try { // for error handling + startNode(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { + if ((tokenSet_13_.member(LA(1)))) + { + node(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((tokenSet_12_.member(LA(1)))) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if (0==inputState.guessing) + { + primaryExpression_AST = (Spring.Expressions.SpringAST)currentAST.root; + primaryExpression_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,"expression","Spring.Expressions.Expression"), (AST)primaryExpression_AST); + currentAST.root = primaryExpression_AST; + if ( (null != primaryExpression_AST) && (null != primaryExpression_AST.getFirstChild()) ) + currentAST.child = primaryExpression_AST.getFirstChild(); + else + currentAST.child = primaryExpression_AST; + currentAST.advanceChildToEnd(); + } + primaryExpression_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_12_); + } + else + { + throw ex; + } + } + returnAST = primaryExpression_AST; + } + + public void unaryOperator() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST unaryOperator_AST = null; + + try { // for error handling + switch ( LA(1) ) + { + case PLUS: + { + Spring.Expressions.SpringAST tmp33_AST = null; + tmp33_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp33_AST); + match(PLUS); + unaryOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case MINUS: + { + Spring.Expressions.SpringAST tmp34_AST = null; + tmp34_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp34_AST); + match(MINUS); + unaryOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case BANG: + { + Spring.Expressions.SpringAST tmp35_AST = null; + tmp35_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp35_AST); + match(BANG); + unaryOperator_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_0_); + } + else + { + throw ex; + } + } + returnAST = unaryOperator_AST; + } + + public void startNode() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST startNode_AST = null; + + try { // for error handling + { + switch ( LA(1) ) + { + case ID: + { + methodOrProperty(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case DOLLAR: + { + localFunctionOrVar(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case LBRACKET: + { + indexer(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case FALSE: + case TRUE: + case NULL_LITERAL: + case INTEGER_LITERAL: + case HEXADECIMAL_INTEGER_LITERAL: + case REAL_LITERAL: + case STRING_LITERAL: + case LITERAL_date: + { + literal(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case TYPE: + { + type(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case LITERAL_new: + { + constructor(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case PROJECT: + { + projection(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case SELECT: + { + selection(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case SELECT_FIRST: + { + firstSelection(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case SELECT_LAST: + { + lastSelection(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case LCURLY: + { + listInitializer(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case LAMBDA: + { + lambda(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + default: + bool synPredMatched37 = false; + if (((LA(1)==LPAREN) && (tokenSet_8_.member(LA(2))))) + { + int _m37 = mark(); + synPredMatched37 = true; + inputState.guessing++; + try { + { + match(LPAREN); + expression(); + match(SEMI); + } + } + catch (RecognitionException) + { + synPredMatched37 = false; + } + rewind(_m37); + inputState.guessing--; + } + if ( synPredMatched37 ) + { + exprList(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((LA(1)==LPAREN) && (tokenSet_8_.member(LA(2)))) { + parenExpr(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((LA(1)==POUND) && (LA(2)==ID)) { + functionOrVar(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((LA(1)==AT) && (LA(2)==LPAREN)) { + reference(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((LA(1)==POUND) && (LA(2)==LCURLY)) { + mapInitializer(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((LA(1)==AT) && (LA(2)==LBRACKET)) { + attribute(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + break; } + } + startNode_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = startNode_AST; + } + + public void node() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST node_AST = null; + + try { // for error handling + { // ( ... )+ + int _cnt40=0; + for (;;) + { + switch ( LA(1) ) + { + case ID: + { + methodOrProperty(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case LBRACKET: + { + indexer(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case PROJECT: + { + projection(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case SELECT: + { + selection(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case SELECT_FIRST: + { + firstSelection(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case SELECT_LAST: + { + lastSelection(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case LPAREN: + { + exprList(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + break; + } + case DOT: + { + match(DOT); + break; + } + default: + { + if (_cnt40 >= 1) { goto _loop40_breakloop; } else { throw new NoViableAltException(LT(1), getFilename());; } + } + break; } + _cnt40++; + } +_loop40_breakloop: ; + } // ( ... )+ + node_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_12_); + } + else + { + throw ex; + } + } + returnAST = node_AST; + } + + public void methodOrProperty() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST methodOrProperty_AST = null; + + try { // for error handling + bool synPredMatched53 = false; + if (((LA(1)==ID) && (LA(2)==LPAREN))) + { + int _m53 = mark(); + synPredMatched53 = true; + inputState.guessing++; + try { + { + match(ID); + match(LPAREN); + } + } + catch (RecognitionException) + { + synPredMatched53 = false; + } + rewind(_m53); + inputState.guessing--; + } + if ( synPredMatched53 ) + { + Spring.Expressions.MethodNode tmp37_AST = null; + tmp37_AST = (Spring.Expressions.MethodNode) astFactory.create(LT(1), "Spring.Expressions.MethodNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp37_AST); + match(ID); + methodArgs(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + methodOrProperty_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else if ((LA(1)==ID) && (tokenSet_2_.member(LA(2)))) { + property(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + methodOrProperty_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = methodOrProperty_AST; + } + + public void functionOrVar() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST functionOrVar_AST = null; + + try { // for error handling + bool synPredMatched43 = false; + if (((LA(1)==POUND) && (LA(2)==ID))) + { + int _m43 = mark(); + synPredMatched43 = true; + inputState.guessing++; + try { + { + match(POUND); + match(ID); + match(LPAREN); + } + } + catch (RecognitionException) + { + synPredMatched43 = false; + } + rewind(_m43); + inputState.guessing--; + } + if ( synPredMatched43 ) + { + function(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + functionOrVar_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else if ((LA(1)==POUND) && (LA(2)==ID)) { + var(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + functionOrVar_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = functionOrVar_AST; + } + + public void localFunctionOrVar() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST localFunctionOrVar_AST = null; + + try { // for error handling + bool synPredMatched48 = false; + if (((LA(1)==DOLLAR) && (LA(2)==ID))) + { + int _m48 = mark(); + synPredMatched48 = true; + inputState.guessing++; + try { + { + match(DOLLAR); + match(ID); + match(LPAREN); + } + } + catch (RecognitionException) + { + synPredMatched48 = false; + } + rewind(_m48); + inputState.guessing--; + } + if ( synPredMatched48 ) + { + localFunction(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + localFunctionOrVar_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else if ((LA(1)==DOLLAR) && (LA(2)==ID)) { + localVar(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + localFunctionOrVar_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = localFunctionOrVar_AST; + } + + public void reference() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST reference_AST = null; + Spring.Expressions.SpringAST cn_AST = null; + Spring.Expressions.SpringAST id_AST = null; + + try { // for error handling + match(AT); + match(LPAREN); + { + if ((LA(1)==ID) && (LA(2)==COLON||LA(2)==DIV)) + { + contextName(); + if (0 == inputState.guessing) + { + cn_AST = (Spring.Expressions.SpringAST)returnAST; + } + match(COLON); + } + else if ((LA(1)==ID) && (LA(2)==RPAREN||LA(2)==DOT)) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + qualifiedId(); + if (0 == inputState.guessing) + { + id_AST = (Spring.Expressions.SpringAST)returnAST; + } + match(RPAREN); + if (0==inputState.guessing) + { + reference_AST = (Spring.Expressions.SpringAST)currentAST.root; + reference_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,"ref","Spring.Context.Support.ReferenceNode"), (AST)cn_AST, (AST)id_AST); + currentAST.root = reference_AST; + if ( (null != reference_AST) && (null != reference_AST.getFirstChild()) ) + currentAST.child = reference_AST.getFirstChild(); + else + currentAST.child = reference_AST; + currentAST.advanceChildToEnd(); + } + reference_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = reference_AST; + } + + public void indexer() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST indexer_AST = null; + + try { // for error handling + Spring.Expressions.IndexerNode tmp42_AST = null; + tmp42_AST = (Spring.Expressions.IndexerNode) astFactory.create(LT(1), "Spring.Expressions.IndexerNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp42_AST); + match(LBRACKET); + argument(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if ((LA(1)==COMMA)) + { + match(COMMA); + argument(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop63_breakloop; + } + + } +_loop63_breakloop: ; + } // ( ... )* + match(RBRACKET); + indexer_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = indexer_AST; + } + + public void literal() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST literal_AST = null; + + try { // for error handling + switch ( LA(1) ) + { + case NULL_LITERAL: + { + Spring.Expressions.NullLiteralNode tmp45_AST = null; + tmp45_AST = (Spring.Expressions.NullLiteralNode) astFactory.create(LT(1), "Spring.Expressions.NullLiteralNode"); + astFactory.addASTChild(ref currentAST, (AST)tmp45_AST); + match(NULL_LITERAL); + literal_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case INTEGER_LITERAL: + { + Spring.Expressions.IntLiteralNode tmp46_AST = null; + tmp46_AST = (Spring.Expressions.IntLiteralNode) astFactory.create(LT(1), "Spring.Expressions.IntLiteralNode"); + astFactory.addASTChild(ref currentAST, (AST)tmp46_AST); + match(INTEGER_LITERAL); + literal_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case HEXADECIMAL_INTEGER_LITERAL: + { + Spring.Expressions.HexLiteralNode tmp47_AST = null; + tmp47_AST = (Spring.Expressions.HexLiteralNode) astFactory.create(LT(1), "Spring.Expressions.HexLiteralNode"); + astFactory.addASTChild(ref currentAST, (AST)tmp47_AST); + match(HEXADECIMAL_INTEGER_LITERAL); + literal_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case REAL_LITERAL: + { + Spring.Expressions.RealLiteralNode tmp48_AST = null; + tmp48_AST = (Spring.Expressions.RealLiteralNode) astFactory.create(LT(1), "Spring.Expressions.RealLiteralNode"); + astFactory.addASTChild(ref currentAST, (AST)tmp48_AST); + match(REAL_LITERAL); + literal_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case STRING_LITERAL: + { + Spring.Expressions.StringLiteralNode tmp49_AST = null; + tmp49_AST = (Spring.Expressions.StringLiteralNode) astFactory.create(LT(1), "Spring.Expressions.StringLiteralNode"); + astFactory.addASTChild(ref currentAST, (AST)tmp49_AST); + match(STRING_LITERAL); + literal_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case FALSE: + case TRUE: + { + boolLiteral(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + literal_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + case LITERAL_date: + { + dateLiteral(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + literal_AST = (Spring.Expressions.SpringAST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = literal_AST; + } + + public void type() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST type_AST = null; + Spring.Expressions.SpringAST tn_AST = null; + + try { // for error handling + match(TYPE); + qualifiedId(); + if (0 == inputState.guessing) + { + tn_AST = (Spring.Expressions.SpringAST)returnAST; + } + { + if ((LA(1)==LBRACKET)) + { + Spring.Expressions.SpringAST tmp51_AST = null; + tmp51_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp51_AST); + match(LBRACKET); + Spring.Expressions.SpringAST tmp52_AST = null; + tmp52_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp52_AST); + match(RBRACKET); + } + else if ((LA(1)==RPAREN||LA(1)==COMMA)) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + if ((LA(1)==COMMA)) + { + Spring.Expressions.SpringAST tmp53_AST = null; + tmp53_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp53_AST); + match(COMMA); + qualifiedId(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((LA(1)==RPAREN)) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + match(RPAREN); + if (0==inputState.guessing) + { + type_AST = (Spring.Expressions.SpringAST)currentAST.root; + type_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,tn_AST.getText(),"Spring.Expressions.TypeNode"), (AST)type_AST); + currentAST.root = type_AST; + if ( (null != type_AST) && (null != type_AST.getFirstChild()) ) + currentAST.child = type_AST.getFirstChild(); + else + currentAST.child = type_AST; + currentAST.advanceChildToEnd(); + } + type_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = type_AST; + } + + public void constructor() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST constructor_AST = null; + Spring.Expressions.SpringAST type_AST = null; + + try { // for error handling + bool synPredMatched83 = false; + if (((LA(1)==LITERAL_new) && (LA(2)==ID))) + { + int _m83 = mark(); + synPredMatched83 = true; + inputState.guessing++; + try { + { + match(LITERAL_new); + qualifiedId(); + match(LPAREN); + } + } + catch (RecognitionException) + { + synPredMatched83 = false; + } + rewind(_m83); + inputState.guessing--; + } + if ( synPredMatched83 ) + { + match(LITERAL_new); + qualifiedId(); + if (0 == inputState.guessing) + { + type_AST = (Spring.Expressions.SpringAST)returnAST; + } + ctorArgs(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + if (0==inputState.guessing) + { + constructor_AST = (Spring.Expressions.SpringAST)currentAST.root; + constructor_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,type_AST.getText(),"Spring.Expressions.ConstructorNode"), (AST)constructor_AST); + currentAST.root = constructor_AST; + if ( (null != constructor_AST) && (null != constructor_AST.getFirstChild()) ) + currentAST.child = constructor_AST.getFirstChild(); + else + currentAST.child = constructor_AST; + currentAST.advanceChildToEnd(); + } + constructor_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else if ((LA(1)==LITERAL_new) && (LA(2)==ID)) { + arrayConstructor(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + constructor_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = constructor_AST; + } + + public void projection() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST projection_AST = null; + + try { // for error handling + Spring.Expressions.ProjectionNode tmp56_AST = null; + tmp56_AST = (Spring.Expressions.ProjectionNode) astFactory.create(LT(1), "Spring.Expressions.ProjectionNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp56_AST); + match(PROJECT); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + match(RCURLY); + projection_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = projection_AST; + } + + public void selection() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST selection_AST = null; + + try { // for error handling + Spring.Expressions.SelectionNode tmp58_AST = null; + tmp58_AST = (Spring.Expressions.SelectionNode) astFactory.create(LT(1), "Spring.Expressions.SelectionNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp58_AST); + match(SELECT); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if ((LA(1)==COMMA)) + { + match(COMMA); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop67_breakloop; + } + + } +_loop67_breakloop: ; + } // ( ... )* + match(RCURLY); + selection_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = selection_AST; + } + + public void firstSelection() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST firstSelection_AST = null; + + try { // for error handling + Spring.Expressions.SelectionFirstNode tmp61_AST = null; + tmp61_AST = (Spring.Expressions.SelectionFirstNode) astFactory.create(LT(1), "Spring.Expressions.SelectionFirstNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp61_AST); + match(SELECT_FIRST); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + match(RCURLY); + firstSelection_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = firstSelection_AST; + } + + public void lastSelection() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST lastSelection_AST = null; + + try { // for error handling + Spring.Expressions.SelectionLastNode tmp63_AST = null; + tmp63_AST = (Spring.Expressions.SelectionLastNode) astFactory.create(LT(1), "Spring.Expressions.SelectionLastNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp63_AST); + match(SELECT_LAST); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + match(RCURLY); + lastSelection_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = lastSelection_AST; + } + + public void listInitializer() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST listInitializer_AST = null; + + try { // for error handling + Spring.Expressions.ListInitializerNode tmp65_AST = null; + tmp65_AST = (Spring.Expressions.ListInitializerNode) astFactory.create(LT(1), "Spring.Expressions.ListInitializerNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp65_AST); + match(LCURLY); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if ((LA(1)==COMMA)) + { + match(COMMA); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop92_breakloop; + } + + } +_loop92_breakloop: ; + } // ( ... )* + match(RCURLY); + listInitializer_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = listInitializer_AST; + } + + public void mapInitializer() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST mapInitializer_AST = null; + + try { // for error handling + match(POUND); + Spring.Expressions.MapInitializerNode tmp69_AST = null; + tmp69_AST = (Spring.Expressions.MapInitializerNode) astFactory.create(LT(1), "Spring.Expressions.MapInitializerNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp69_AST); + match(LCURLY); + mapEntry(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if ((LA(1)==COMMA)) + { + match(COMMA); + mapEntry(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop95_breakloop; + } + + } +_loop95_breakloop: ; + } // ( ... )* + match(RCURLY); + mapInitializer_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = mapInitializer_AST; + } + + public void lambda() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST lambda_AST = null; + + try { // for error handling + match(LAMBDA); + { + if ((LA(1)==ID)) + { + argList(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((LA(1)==PIPE)) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + match(PIPE); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + match(RCURLY); + if (0==inputState.guessing) + { + lambda_AST = (Spring.Expressions.SpringAST)currentAST.root; + lambda_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,"lambda","Spring.Expressions.LambdaExpressionNode"), (AST)lambda_AST); + currentAST.root = lambda_AST; + if ( (null != lambda_AST) && (null != lambda_AST.getFirstChild()) ) + currentAST.child = lambda_AST.getFirstChild(); + else + currentAST.child = lambda_AST; + currentAST.advanceChildToEnd(); + } + lambda_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = lambda_AST; + } + + public void attribute() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST attribute_AST = null; + Spring.Expressions.SpringAST tn_AST = null; + + try { // for error handling + match(AT); + match(LBRACKET); + qualifiedId(); + if (0 == inputState.guessing) + { + tn_AST = (Spring.Expressions.SpringAST)returnAST; + } + { + if ((LA(1)==LPAREN)) + { + ctorArgs(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((LA(1)==RBRACKET)) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + match(RBRACKET); + if (0==inputState.guessing) + { + attribute_AST = (Spring.Expressions.SpringAST)currentAST.root; + attribute_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,tn_AST.getText(),"Spring.Expressions.AttributeNode"), (AST)attribute_AST); + currentAST.root = attribute_AST; + if ( (null != attribute_AST) && (null != attribute_AST.getFirstChild()) ) + currentAST.child = attribute_AST.getFirstChild(); + else + currentAST.child = attribute_AST; + currentAST.advanceChildToEnd(); + } + attribute_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = attribute_AST; + } + + public void function() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST function_AST = null; + + try { // for error handling + match(POUND); + Spring.Expressions.FunctionNode tmp79_AST = null; + tmp79_AST = (Spring.Expressions.FunctionNode) astFactory.create(LT(1), "Spring.Expressions.FunctionNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp79_AST); + match(ID); + methodArgs(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + function_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = function_AST; + } + + public void var() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST var_AST = null; + + try { // for error handling + match(POUND); + Spring.Expressions.VariableNode tmp81_AST = null; + tmp81_AST = (Spring.Expressions.VariableNode) astFactory.create(LT(1), "Spring.Expressions.VariableNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp81_AST); + match(ID); + var_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = var_AST; + } + + public void methodArgs() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST methodArgs_AST = null; + + try { // for error handling + match(LPAREN); + { + if ((tokenSet_8_.member(LA(1)))) + { + argument(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if ((LA(1)==COMMA)) + { + match(COMMA); + argument(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop57_breakloop; + } + + } +_loop57_breakloop: ; + } // ( ... )* + } + else if ((LA(1)==RPAREN)) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + match(RPAREN); + methodArgs_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = methodArgs_AST; + } + + public void localFunction() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST localFunction_AST = null; + + try { // for error handling + match(DOLLAR); + Spring.Expressions.LocalFunctionNode tmp86_AST = null; + tmp86_AST = (Spring.Expressions.LocalFunctionNode) astFactory.create(LT(1), "Spring.Expressions.LocalFunctionNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp86_AST); + match(ID); + methodArgs(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + localFunction_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = localFunction_AST; + } + + public void localVar() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST localVar_AST = null; + + try { // for error handling + match(DOLLAR); + Spring.Expressions.LocalVariableNode tmp88_AST = null; + tmp88_AST = (Spring.Expressions.LocalVariableNode) astFactory.create(LT(1), "Spring.Expressions.LocalVariableNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp88_AST); + match(ID); + localVar_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = localVar_AST; + } + + public void property() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST property_AST = null; + + try { // for error handling + Spring.Expressions.PropertyOrFieldNode tmp89_AST = null; + tmp89_AST = (Spring.Expressions.PropertyOrFieldNode) astFactory.create(LT(1), "Spring.Expressions.PropertyOrFieldNode"); + astFactory.addASTChild(ref currentAST, (AST)tmp89_AST); + match(ID); + property_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = property_AST; + } + + public void argument() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST argument_AST = null; + + try { // for error handling + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + argument_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_14_); + } + else + { + throw ex; + } + } + returnAST = argument_AST; + } + + public void contextName() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST contextName_AST = null; + + try { // for error handling + Spring.Expressions.QualifiedIdentifier tmp90_AST = null; + tmp90_AST = (Spring.Expressions.QualifiedIdentifier) astFactory.create(LT(1), "Spring.Expressions.QualifiedIdentifier"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp90_AST); + match(ID); + { // ( ... )* + for (;;) + { + if ((LA(1)==DIV)) + { + Spring.Expressions.SpringAST tmp91_AST = null; + tmp91_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp91_AST); + match(DIV); + Spring.Expressions.SpringAST tmp92_AST = null; + tmp92_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp92_AST); + match(ID); + } + else + { + goto _loop110_breakloop; + } + + } +_loop110_breakloop: ; + } // ( ... )* + contextName_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_15_); + } + else + { + throw ex; + } + } + returnAST = contextName_AST; + } + + public void qualifiedId() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST qualifiedId_AST = null; + + try { // for error handling + Spring.Expressions.QualifiedIdentifier tmp93_AST = null; + tmp93_AST = (Spring.Expressions.QualifiedIdentifier) astFactory.create(LT(1), "Spring.Expressions.QualifiedIdentifier"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp93_AST); + match(ID); + { // ( ... )* + for (;;) + { + if ((LA(1)==DOT)) + { + Spring.Expressions.SpringAST tmp94_AST = null; + tmp94_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp94_AST); + match(DOT); + Spring.Expressions.SpringAST tmp95_AST = null; + tmp95_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp95_AST); + match(ID); + } + else + { + goto _loop107_breakloop; + } + + } +_loop107_breakloop: ; + } // ( ... )* + qualifiedId_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_16_); + } + else + { + throw ex; + } + } + returnAST = qualifiedId_AST; + } + + public void ctorArgs() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST ctorArgs_AST = null; + + try { // for error handling + match(LPAREN); + { + if ((tokenSet_8_.member(LA(1)))) + { + namedArgument(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if ((LA(1)==COMMA)) + { + match(COMMA); + namedArgument(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop100_breakloop; + } + + } +_loop100_breakloop: ; + } // ( ... )* + } + else if ((LA(1)==RPAREN)) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + match(RPAREN); + ctorArgs_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = ctorArgs_AST; + } + + public void argList() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST argList_AST = null; + + try { // for error handling + { + Spring.Expressions.SpringAST tmp99_AST = null; + tmp99_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp99_AST); + match(ID); + { // ( ... )* + for (;;) + { + if ((LA(1)==COMMA)) + { + match(COMMA); + Spring.Expressions.SpringAST tmp101_AST = null; + tmp101_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp101_AST); + match(ID); + } + else + { + goto _loop80_breakloop; + } + + } +_loop80_breakloop: ; + } // ( ... )* + } + if (0==inputState.guessing) + { + argList_AST = (Spring.Expressions.SpringAST)currentAST.root; + argList_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,"args"), (AST)argList_AST); + currentAST.root = argList_AST; + if ( (null != argList_AST) && (null != argList_AST.getFirstChild()) ) + currentAST.child = argList_AST.getFirstChild(); + else + currentAST.child = argList_AST; + currentAST.advanceChildToEnd(); + } + argList_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_17_); + } + else + { + throw ex; + } + } + returnAST = argList_AST; + } + + public void arrayConstructor() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST arrayConstructor_AST = null; + Spring.Expressions.SpringAST type_AST = null; + + try { // for error handling + match(LITERAL_new); + qualifiedId(); + if (0 == inputState.guessing) + { + type_AST = (Spring.Expressions.SpringAST)returnAST; + } + arrayRank(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { + if ((LA(1)==LCURLY)) + { + listInitializer(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else if ((tokenSet_2_.member(LA(1)))) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if (0==inputState.guessing) + { + arrayConstructor_AST = (Spring.Expressions.SpringAST)currentAST.root; + arrayConstructor_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,type_AST.getText(),"Spring.Expressions.ArrayConstructorNode"), (AST)arrayConstructor_AST); + currentAST.root = arrayConstructor_AST; + if ( (null != arrayConstructor_AST) && (null != arrayConstructor_AST.getFirstChild()) ) + currentAST.child = arrayConstructor_AST.getFirstChild(); + else + currentAST.child = arrayConstructor_AST; + currentAST.advanceChildToEnd(); + } + arrayConstructor_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = arrayConstructor_AST; + } + + public void arrayRank() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST arrayRank_AST = null; + + try { // for error handling + Spring.Expressions.SpringAST tmp103_AST = null; + tmp103_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.makeASTRoot(ref currentAST, (AST)tmp103_AST); + match(LBRACKET); + { + if ((tokenSet_8_.member(LA(1)))) + { + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + { // ( ... )* + for (;;) + { + if ((LA(1)==COMMA)) + { + match(COMMA); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + } + else + { + goto _loop89_breakloop; + } + + } +_loop89_breakloop: ; + } // ( ... )* + } + else if ((LA(1)==RBRACKET)) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + match(RBRACKET); + arrayRank_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_18_); + } + else + { + throw ex; + } + } + returnAST = arrayRank_AST; + } + + public void mapEntry() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST mapEntry_AST = null; + + try { // for error handling + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + match(COLON); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + if (0==inputState.guessing) + { + mapEntry_AST = (Spring.Expressions.SpringAST)currentAST.root; + mapEntry_AST = (Spring.Expressions.SpringAST) astFactory.make((AST)(Spring.Expressions.SpringAST) astFactory.create(EXPR,"entry","Spring.Expressions.MapEntryNode"), (AST)mapEntry_AST); + currentAST.root = mapEntry_AST; + if ( (null != mapEntry_AST) && (null != mapEntry_AST.getFirstChild()) ) + currentAST.child = mapEntry_AST.getFirstChild(); + else + currentAST.child = mapEntry_AST; + currentAST.advanceChildToEnd(); + } + mapEntry_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_19_); + } + else + { + throw ex; + } + } + returnAST = mapEntry_AST; + } + + public void namedArgument() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST namedArgument_AST = null; + + try { // for error handling + bool synPredMatched104 = false; + if (((LA(1)==ID) && (LA(2)==ASSIGN))) + { + int _m104 = mark(); + synPredMatched104 = true; + inputState.guessing++; + try { + { + match(ID); + match(ASSIGN); + } + } + catch (RecognitionException) + { + synPredMatched104 = false; + } + rewind(_m104); + inputState.guessing--; + } + if ( synPredMatched104 ) + { + Spring.Expressions.NamedArgumentNode tmp107_AST = null; + tmp107_AST = (Spring.Expressions.NamedArgumentNode) astFactory.create(LT(1), "Spring.Expressions.NamedArgumentNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp107_AST); + match(ID); + match(ASSIGN); + expression(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + namedArgument_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else if ((tokenSet_8_.member(LA(1))) && (tokenSet_20_.member(LA(2)))) { + argument(); + if (0 == inputState.guessing) + { + astFactory.addASTChild(ref currentAST, (AST)returnAST); + } + namedArgument_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_21_); + } + else + { + throw ex; + } + } + returnAST = namedArgument_AST; + } + + public void boolLiteral() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST boolLiteral_AST = null; + + try { // for error handling + if ((LA(1)==TRUE)) + { + Spring.Expressions.BooleanLiteralNode tmp109_AST = null; + tmp109_AST = (Spring.Expressions.BooleanLiteralNode) astFactory.create(LT(1), "Spring.Expressions.BooleanLiteralNode"); + astFactory.addASTChild(ref currentAST, (AST)tmp109_AST); + match(TRUE); + boolLiteral_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else if ((LA(1)==FALSE)) { + Spring.Expressions.BooleanLiteralNode tmp110_AST = null; + tmp110_AST = (Spring.Expressions.BooleanLiteralNode) astFactory.create(LT(1), "Spring.Expressions.BooleanLiteralNode"); + astFactory.addASTChild(ref currentAST, (AST)tmp110_AST); + match(FALSE); + boolLiteral_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = boolLiteral_AST; + } + + public void dateLiteral() //throws RecognitionException, TokenStreamException +{ + + returnAST = null; + ASTPair currentAST = new ASTPair(); + Spring.Expressions.SpringAST dateLiteral_AST = null; + + try { // for error handling + Spring.Expressions.DateLiteralNode tmp111_AST = null; + tmp111_AST = (Spring.Expressions.DateLiteralNode) astFactory.create(LT(1), "Spring.Expressions.DateLiteralNode"); + astFactory.makeASTRoot(ref currentAST, (AST)tmp111_AST); + match(LITERAL_date); + match(LPAREN); + Spring.Expressions.SpringAST tmp113_AST = null; + tmp113_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp113_AST); + match(STRING_LITERAL); + { + if ((LA(1)==COMMA)) + { + match(COMMA); + Spring.Expressions.SpringAST tmp115_AST = null; + tmp115_AST = (Spring.Expressions.SpringAST) astFactory.create(LT(1)); + astFactory.addASTChild(ref currentAST, (AST)tmp115_AST); + match(STRING_LITERAL); + } + else if ((LA(1)==RPAREN)) { + } + else + { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + match(RPAREN); + dateLiteral_AST = (Spring.Expressions.SpringAST)currentAST.root; + } + catch (RecognitionException ex) + { + if (0 == inputState.guessing) + { + reportError(ex); + recover(ex,tokenSet_2_); + } + else + { + throw ex; + } + } + returnAST = dateLiteral_AST; + } + + public new Spring.Expressions.SpringAST getAST() + { + return (Spring.Expressions.SpringAST) returnAST; + } + + private void initializeFactory() + { + if (astFactory == null) + { + astFactory = new ASTFactory("Spring.Expressions.SpringAST"); + } + initializeASTFactory( astFactory ); + } + static public void initializeASTFactory( ASTFactory factory ) + { + factory.setMaxNodeType(68); + } + + public static readonly string[] tokenNames_ = new string[] { + @"""<0>""", + @"""EOF""", + @"""<2>""", + @"""NULL_TREE_LOOKAHEAD""", + @"""EXPR""", + @"""OPERAND""", + @"""false""", + @"""true""", + @"""and""", + @"""or""", + @"""in""", + @"""is""", + @"""between""", + @"""like""", + @"""matches""", + @"""null""", + @"""LPAREN""", + @"""SEMI""", + @"""RPAREN""", + @"""ASSIGN""", + @"""DEFAULT""", + @"""QMARK""", + @"""COLON""", + @"""PLUS""", + @"""MINUS""", + @"""STAR""", + @"""DIV""", + @"""MOD""", + @"""POWER""", + @"""BANG""", + @"""DOT""", + @"""POUND""", + @"""ID""", + @"""DOLLAR""", + @"""COMMA""", + @"""AT""", + @"""LBRACKET""", + @"""RBRACKET""", + @"""PROJECT""", + @"""RCURLY""", + @"""SELECT""", + @"""SELECT_FIRST""", + @"""SELECT_LAST""", + @"""TYPE""", + @"""LAMBDA""", + @"""PIPE""", + @"""new""", + @"""LCURLY""", + @"""INTEGER_LITERAL""", + @"""HEXADECIMAL_INTEGER_LITERAL""", + @"""REAL_LITERAL""", + @"""STRING_LITERAL""", + @"""date""", + @"""EQUAL""", + @"""NOT_EQUAL""", + @"""LESS_THAN""", + @"""LESS_THAN_OR_EQUAL""", + @"""GREATER_THAN""", + @"""GREATER_THAN_OR_EQUAL""", + @"""WS""", + @"""DOT_ESCAPED""", + @"""APOS""", + @"""NUMERIC_LITERAL""", + @"""DECIMAL_DIGIT""", + @"""INTEGER_TYPE_SUFFIX""", + @"""HEX_DIGIT""", + @"""EXPONENT_PART""", + @"""SIGN""", + @"""REAL_TYPE_SUFFIX""" + }; + + private static long[] mk_tokenSet_0_() + { + long[] data = { 2L, 0L}; + return data; + } + public static readonly BitSet tokenSet_0_ = new BitSet(mk_tokenSet_0_()); + private static long[] mk_tokenSet_1_() + { + long[] data = { 704379224066L, 0L}; + return data; + } + public static readonly BitSet tokenSet_1_ = new BitSet(mk_tokenSet_1_()); + private static long[] mk_tokenSet_2_() + { + long[] data = { 567462303507644162L, 0L}; + return data; + } + public static readonly BitSet tokenSet_2_ = new BitSet(mk_tokenSet_2_()); + private static long[] mk_tokenSet_3_() + { + long[] data = { 704382894082L, 0L}; + return data; + } + public static readonly BitSet tokenSet_3_ = new BitSet(mk_tokenSet_3_()); + private static long[] mk_tokenSet_4_() + { + long[] data = { 704382894594L, 0L}; + return data; + } + public static readonly BitSet tokenSet_4_ = new BitSet(mk_tokenSet_4_()); + private static long[] mk_tokenSet_5_() + { + long[] data = { 567453553048714240L, 0L}; + return data; + } + public static readonly BitSet tokenSet_5_ = new BitSet(mk_tokenSet_5_()); + private static long[] mk_tokenSet_6_() + { + long[] data = { 704382894850L, 0L}; + return data; + } + public static readonly BitSet tokenSet_6_ = new BitSet(mk_tokenSet_6_()); + private static long[] mk_tokenSet_7_() + { + long[] data = { 567454257431609090L, 0L}; + return data; + } + public static readonly BitSet tokenSet_7_ = new BitSet(mk_tokenSet_7_()); + private static long[] mk_tokenSet_8_() + { + long[] data = { 8971308922667200L, 0L}; + return data; + } + public static readonly BitSet tokenSet_8_ = new BitSet(mk_tokenSet_8_()); + private static long[] mk_tokenSet_9_() + { + long[] data = { 567454257456774914L, 0L}; + return data; + } + public static readonly BitSet tokenSet_9_ = new BitSet(mk_tokenSet_9_()); + private static long[] mk_tokenSet_10_() + { + long[] data = { 567454257691655938L, 0L}; + return data; + } + public static readonly BitSet tokenSet_10_ = new BitSet(mk_tokenSet_10_()); + private static long[] mk_tokenSet_11_() + { + long[] data = { 8971308360630464L, 0L}; + return data; + } + public static readonly BitSet tokenSet_11_ = new BitSet(mk_tokenSet_11_()); + private static long[] mk_tokenSet_12_() + { + long[] data = { 567454257960091394L, 0L}; + return data; + } + public static readonly BitSet tokenSet_12_ = new BitSet(mk_tokenSet_12_()); + private static long[] mk_tokenSet_13_() + { + long[] data = { 8045547552768L, 0L}; + return data; + } + public static readonly BitSet tokenSet_13_ = new BitSet(mk_tokenSet_13_()); + private static long[] mk_tokenSet_14_() + { + long[] data = { 154619084800L, 0L}; + return data; + } + public static readonly BitSet tokenSet_14_ = new BitSet(mk_tokenSet_14_()); + private static long[] mk_tokenSet_15_() + { + long[] data = { 4194304L, 0L}; + return data; + } + public static readonly BitSet tokenSet_15_ = new BitSet(mk_tokenSet_15_()); + private static long[] mk_tokenSet_16_() + { + long[] data = { 223338627072L, 0L}; + return data; + } + public static readonly BitSet tokenSet_16_ = new BitSet(mk_tokenSet_16_()); + private static long[] mk_tokenSet_17_() + { + long[] data = { 35184372088832L, 0L}; + return data; + } + public static readonly BitSet tokenSet_17_ = new BitSet(mk_tokenSet_17_()); + private static long[] mk_tokenSet_18_() + { + long[] data = { 567603040995999490L, 0L}; + return data; + } + public static readonly BitSet tokenSet_18_ = new BitSet(mk_tokenSet_18_()); + private static long[] mk_tokenSet_19_() + { + long[] data = { 566935683072L, 0L}; + return data; + } + public static readonly BitSet tokenSet_19_ = new BitSet(mk_tokenSet_19_()); + private static long[] mk_tokenSet_20_() + { + long[] data = { 576460065104330688L, 0L}; + return data; + } + public static readonly BitSet tokenSet_20_ = new BitSet(mk_tokenSet_20_()); + private static long[] mk_tokenSet_21_() + { + long[] data = { 17180131328L, 0L}; + return data; + } + public static readonly BitSet tokenSet_21_ = new BitSet(mk_tokenSet_21_()); + +} +} diff --git a/src/Spring/Spring.Core/Expressions/Parser/ExpressionParserTokenTypes.cs b/src/Spring/Spring.Core/Expressions/Parser/ExpressionParserTokenTypes.cs new file mode 100644 index 00000000..1f47e89a --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Parser/ExpressionParserTokenTypes.cs @@ -0,0 +1,76 @@ +// $ANTLR 2.7.6 (2005-12-22): "Expression.g" -> "ExpressionLexer.cs"$ + +namespace Spring.Expressions +{ + public class ExpressionParserTokenTypes + { + public const int EOF = 1; + public const int NULL_TREE_LOOKAHEAD = 3; + public const int EXPR = 4; + public const int OPERAND = 5; + public const int FALSE = 6; + public const int TRUE = 7; + public const int AND = 8; + public const int OR = 9; + public const int IN = 10; + public const int IS = 11; + public const int BETWEEN = 12; + public const int LIKE = 13; + public const int MATCHES = 14; + public const int NULL_LITERAL = 15; + public const int LPAREN = 16; + public const int SEMI = 17; + public const int RPAREN = 18; + public const int ASSIGN = 19; + public const int DEFAULT = 20; + public const int QMARK = 21; + public const int COLON = 22; + public const int PLUS = 23; + public const int MINUS = 24; + public const int STAR = 25; + public const int DIV = 26; + public const int MOD = 27; + public const int POWER = 28; + public const int BANG = 29; + public const int DOT = 30; + public const int POUND = 31; + public const int ID = 32; + public const int DOLLAR = 33; + public const int COMMA = 34; + public const int AT = 35; + public const int LBRACKET = 36; + public const int RBRACKET = 37; + public const int PROJECT = 38; + public const int RCURLY = 39; + public const int SELECT = 40; + public const int SELECT_FIRST = 41; + public const int SELECT_LAST = 42; + public const int TYPE = 43; + public const int LAMBDA = 44; + public const int PIPE = 45; + public const int LITERAL_new = 46; + public const int LCURLY = 47; + public const int INTEGER_LITERAL = 48; + public const int HEXADECIMAL_INTEGER_LITERAL = 49; + public const int REAL_LITERAL = 50; + public const int STRING_LITERAL = 51; + public const int LITERAL_date = 52; + public const int EQUAL = 53; + public const int NOT_EQUAL = 54; + public const int LESS_THAN = 55; + public const int LESS_THAN_OR_EQUAL = 56; + public const int GREATER_THAN = 57; + public const int GREATER_THAN_OR_EQUAL = 58; + public const int WS = 59; + public const int DOT_ESCAPED = 60; + public const int APOS = 61; + public const int NUMERIC_LITERAL = 62; + public const int DECIMAL_DIGIT = 63; + public const int INTEGER_TYPE_SUFFIX = 64; + public const int HEX_DIGIT = 65; + public const int EXPONENT_PART = 66; + public const int SIGN = 67; + public const int REAL_TYPE_SUFFIX = 68; + + } +} diff --git a/src/Spring/Spring.Core/Expressions/Parser/ExpressionParserTokenTypes.txt b/src/Spring/Spring.Core/Expressions/Parser/ExpressionParserTokenTypes.txt new file mode 100644 index 00000000..5e36c13e --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Parser/ExpressionParserTokenTypes.txt @@ -0,0 +1,67 @@ +// $ANTLR 2.7.6 (2005-12-22): Expression.g -> ExpressionParserTokenTypes.txt$ +ExpressionParser // output token vocab name +EXPR=4 +OPERAND=5 +FALSE="false"=6 +TRUE="true"=7 +AND="and"=8 +OR="or"=9 +IN="in"=10 +IS="is"=11 +BETWEEN="between"=12 +LIKE="like"=13 +MATCHES="matches"=14 +NULL_LITERAL="null"=15 +LPAREN=16 +SEMI=17 +RPAREN=18 +ASSIGN=19 +DEFAULT=20 +QMARK=21 +COLON=22 +PLUS=23 +MINUS=24 +STAR=25 +DIV=26 +MOD=27 +POWER=28 +BANG=29 +DOT=30 +POUND=31 +ID=32 +DOLLAR=33 +COMMA=34 +AT=35 +LBRACKET=36 +RBRACKET=37 +PROJECT=38 +RCURLY=39 +SELECT=40 +SELECT_FIRST=41 +SELECT_LAST=42 +TYPE=43 +LAMBDA=44 +PIPE=45 +LITERAL_new="new"=46 +LCURLY=47 +INTEGER_LITERAL=48 +HEXADECIMAL_INTEGER_LITERAL=49 +REAL_LITERAL=50 +STRING_LITERAL=51 +LITERAL_date="date"=52 +EQUAL=53 +NOT_EQUAL=54 +LESS_THAN=55 +LESS_THAN_OR_EQUAL=56 +GREATER_THAN=57 +GREATER_THAN_OR_EQUAL=58 +WS=59 +DOT_ESCAPED=60 +APOS=61 +NUMERIC_LITERAL=62 +DECIMAL_DIGIT=63 +INTEGER_TYPE_SUFFIX=64 +HEX_DIGIT=65 +EXPONENT_PART=66 +SIGN=67 +REAL_TYPE_SUFFIX=68 diff --git a/src/Spring/Spring.Core/Expressions/Processors/AverageAggregator.cs b/src/Spring/Spring.Core/Expressions/Processors/AverageAggregator.cs new file mode 100644 index 00000000..36083ab1 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/AverageAggregator.cs @@ -0,0 +1,69 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using Spring.Util; + +namespace Spring.Expressions.Processors +{ + /// + /// Implementation of the average aggregator. + /// + /// Aleksandar Seovic + /// $Id: AverageAggregator.cs,v 1.2 2006/12/04 08:58:33 aseovic Exp $ + public class AverageAggregator : ICollectionProcessor + { + /// + /// Returns the average of the numeric values in the source collection. + /// + /// + /// The source collection to process. + /// + /// + /// Ignored. + /// + /// + /// The average of the numeric values in the source collection. + /// + public object Process(ICollection source, object[] args) + { + int n = 0; + object total = 0d; + foreach (object item in source) + { + if (item != null) + { + if (NumberUtils.IsNumber(item)) + { + total = NumberUtils.Add(total, item); + n++; + } + else + { + throw new ArgumentException("Average can only be calculated for a collection of numeric values."); + } + } + } + + return NumberUtils.Divide(total, n); + } + } +} diff --git a/src/Spring/Spring.Core/Expressions/Processors/ConversionProcessor.cs b/src/Spring/Spring.Core/Expressions/Processors/ConversionProcessor.cs new file mode 100644 index 00000000..a6fd412d --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/ConversionProcessor.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Core.TypeConversion; + +#endregion + +namespace Spring.Expressions.Processors +{ + /// + /// Converts all elements in the input list to a given target type. + /// + /// Erich Eichinger + /// $Id: ConversionProcessor.cs,v 1.1 2008/03/20 23:58:16 oakinger Exp $ + public class ConversionProcessor : ICollectionProcessor + { + /// + /// Processes a list of source items and returns a result. + /// + /// + /// The source list to process. + /// + /// + /// An optional processor arguments array. + /// + /// + /// The processing result. + /// + public object Process(ICollection source, object[] args) + { + if (source == null + || source.Count == 0) + { + return source; + } + + Type targetType = typeof(double); + if (args == null || args.Length == 0) + { + throw new ArgumentNullException("convert() processor requires a Type value argument."); + } + else if (args.Length == 1) + { + if (args[0] is Type) + { + targetType = (Type)args[0]; + } + else + { + throw new ArgumentException("convert() processor argument must be a Type value."); + } + } + else if (args.Length > 1) + { + throw new ArgumentException("Only a single argument can be specified for a convert() processor."); + } + + ArrayList result = new ArrayList(); + foreach(object val in source) + { + object newVal = TypeConversionUtils.ConvertValueIfNecessary(targetType, val, null); + result.Add(newVal); + } + + return result.ToArray(targetType); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/Processors/CountAggregator.cs b/src/Spring/Spring.Core/Expressions/Processors/CountAggregator.cs new file mode 100644 index 00000000..fe0beb81 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/CountAggregator.cs @@ -0,0 +1,54 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.Expressions.Processors +{ + /// + /// Implementation of the count aggregator. + /// + /// Aleksandar Seovic + /// $Id: CountAggregator.cs,v 1.2 2006/12/04 08:58:33 aseovic Exp $ + public class CountAggregator : ICollectionProcessor + { + /// + /// Returns the number of items in the source collection. + /// + /// + /// The source collection to process. + /// + /// + /// Ignored. + /// + /// + /// The number of items in the source collection, + /// or zero if the collection is empty or null. + /// + public object Process(ICollection source, object[] args) + { + if (source == null) + { + return 0; + } + return source.Count; + } + } +} diff --git a/src/Spring/Spring.Core/Expressions/Processors/DistinctProcessor.cs b/src/Spring/Spring.Core/Expressions/Processors/DistinctProcessor.cs new file mode 100644 index 00000000..01107a6d --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/DistinctProcessor.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using Spring.Collections; + +namespace Spring.Expressions.Processors +{ + /// + /// Implementation of the distinct processor. + /// + /// Aleksandar Seovic + /// $Id: DistinctProcessor.cs,v 1.1 2006/12/04 08:58:33 aseovic Exp $ + public class DistinctProcessor : ICollectionProcessor + { + /// + /// Returns distinct items from the collection. + /// + /// + /// The source collection to process. + /// + /// + /// 0: boolean flag specifying whether to include null + /// in the results or not. Default is false, which means that + /// null values will not be included in the results. + /// + /// + /// A collection containing distinct source collection elements. + /// + /// + /// If there is more than one argument, or if the single optional argument + /// is not Boolean. + /// + public object Process(ICollection source, object[] args) + { + if (source == null) + { + return null; + } + + bool includeNulls = false; + if (args.Length == 1) + { + if (args[0] is bool) + { + includeNulls = (bool) args[0]; + } + else + { + throw new ArgumentException("distinct() processor argument must be a boolean value."); + } + } + else if (args.Length > 1) + { + throw new ArgumentException("Only a single argument can be specified for a distinct() processor."); + } + + HybridSet set = new HybridSet(source); + if (!includeNulls) + { + set.Remove(null); + } + return set; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/Processors/ICollectionProcessor.cs b/src/Spring/Spring.Core/Expressions/Processors/ICollectionProcessor.cs new file mode 100644 index 00000000..b7a2563d --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/ICollectionProcessor.cs @@ -0,0 +1,45 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.Expressions.Processors +{ + /// + /// Defines an interface that should be implemented + /// by all collection processors and aggregators. + /// + public interface ICollectionProcessor + { + /// + /// Processes a list of source items and returns a result. + /// + /// + /// The source list to process. + /// + /// + /// An optional processor arguments array. + /// + /// + /// The processing result. + /// + object Process(ICollection source, object[] args); + } +} diff --git a/src/Spring/Spring.Core/Expressions/Processors/MaxAggregator.cs b/src/Spring/Spring.Core/Expressions/Processors/MaxAggregator.cs new file mode 100644 index 00000000..4040e12a --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/MaxAggregator.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using Spring.Util; + +namespace Spring.Expressions.Processors +{ + /// + /// Implementation of the maximum aggregator. + /// + /// Aleksandar Seovic + /// $Id: MaxAggregator.cs,v 1.2 2006/12/04 08:58:33 aseovic Exp $ + public class MaxAggregator : ICollectionProcessor + { + /// + /// Returns the largest item in the source collection. + /// + /// + /// The source collection to process. + /// + /// + /// Ignored. + /// + /// + /// The largest item in the source collection. + /// + public object Process(ICollection source, object[] args) + { + object maxItem = null; + foreach (object item in source) + { + if (CompareUtils.Compare(maxItem, item) < 0) + { + maxItem = item; + } + } + return maxItem; + } + } +} diff --git a/src/Spring/Spring.Core/Expressions/Processors/MinAggregator.cs b/src/Spring/Spring.Core/Expressions/Processors/MinAggregator.cs new file mode 100644 index 00000000..e606c568 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/MinAggregator.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using Spring.Util; + +namespace Spring.Expressions.Processors +{ + /// + /// Implementation of the minimum aggregator. + /// + /// Aleksandar Seovic + /// $Id: MinAggregator.cs,v 1.2 2006/12/04 08:58:33 aseovic Exp $ + public class MinAggregator : ICollectionProcessor + { + /// + /// Returns the smallest item in the source collection. + /// + /// + /// The source collection to process. + /// + /// + /// Ignored. + /// + /// + /// The smallest item in the source collection. + /// + public object Process(ICollection source, object[] args) + { + object minItem = null; + foreach (object item in source) + { + if ((minItem == null && item != null) || (CompareUtils.Compare(minItem, item) > 0)) + { + minItem = item; + } + } + return minItem; + } + } +} diff --git a/src/Spring/Spring.Core/Expressions/Processors/NonNullProcessor.cs b/src/Spring/Spring.Core/Expressions/Processors/NonNullProcessor.cs new file mode 100644 index 00000000..7fd37c8b --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/NonNullProcessor.cs @@ -0,0 +1,62 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.Expressions.Processors +{ + /// + /// Implementation of the non-null processor. + /// + /// Aleksandar Seovic + /// $Id: NonNullProcessor.cs,v 1.2 2007/07/31 21:43:52 markpollack Exp $ + public class NonNullProcessor : ICollectionProcessor + { + /// + /// Returns non-null items from the collection. + /// + /// + /// The source collection to process. + /// + /// + /// Ignored. + /// + /// + /// A collection containing non-null source collection elements. + /// + public object Process(ICollection source, object[] args) + { + if (source == null) + { + return null; + } + + ArrayList list = new ArrayList(); + foreach (object item in source) + { + if (item != null) + { + list.Add(item); + } + } + return list.ToArray(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/Processors/OrderByProcessor.cs b/src/Spring/Spring.Core/Expressions/Processors/OrderByProcessor.cs new file mode 100644 index 00000000..57bce703 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/OrderByProcessor.cs @@ -0,0 +1,166 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using Spring.Util; + +namespace Spring.Expressions.Processors +{ + /// + /// Implementation of the 'order by' processor. + /// + /// Aleksandar Seovic + /// Erich Eichinger + /// $Id: OrderByProcessor.cs,v 1.2 2008/03/20 23:58:16 oakinger Exp $ + public class OrderByProcessor : ICollectionProcessor + { + #region Comparer Helper Implementations + + private class SimpleExpressionComparer : IComparer + { + private readonly IExpression _expression; + + public SimpleExpressionComparer(IExpression expression) + { + _expression = expression; + } + + public int Compare(object x, object y) + { + x = _expression.GetValue(x); + y = _expression.GetValue(y); + + if (x==y) return 0; + + if (x != null) return ((IComparable) x).CompareTo(y); + + return ((IComparable)y).CompareTo(x)*-1; + } + } + + private class LambdaComparer : IComparer + { + private readonly Hashtable _variables; + private readonly IExpression _fn; + + public LambdaComparer(LambdaExpressionNode lambdaExpression) + { + FunctionNode functionNode = new FunctionNode(); + functionNode.Text = "compare"; + VariableNode x = new VariableNode(); + x.Text = "x"; + VariableNode y = new VariableNode(); + y.Text = "y"; + + functionNode.addChild(x); + functionNode.addChild(y); + + _fn = functionNode; + _variables = new Hashtable(); + _variables.Add( "compare", lambdaExpression ); + } + + public int Compare(object x, object y) + { + _variables["x"] = x; + _variables["y"] = y; + return (int) _fn.GetValue(null, _variables); + } + } + + private class DelegateComparer : IComparer + { + private readonly Delegate _fnCompare; + + public DelegateComparer(Delegate fnCompare) + { + _fnCompare = fnCompare; + } + + public int Compare(object x, object y) + { + return (int)_fnCompare.DynamicInvoke(new object[] { x, y }); + } + } + + #endregion + + /// + /// Sorts the source collection using custom sort criteria. + /// + /// + /// Please not that your compare function needs to take care about + /// proper conversion of types to be comparable! + /// + /// + /// The source collection to sort. + /// + /// + /// Sort criteria to use. + /// + /// + /// A sorted array containing collection elements. + /// + public object Process(ICollection source, object[] args) + { + if (source == null || source.Count == 0) + { + return source; + } + + if (args == null || args.Length != 1) + { + throw new ArgumentException("compare expression is a required argument for orderBy"); + } + + object arg = args[0]; + IComparer comparer = null; + if (arg is string) + { + IExpression expCompare = Expression.Parse((string) arg); + comparer = new SimpleExpressionComparer(expCompare); + } + else if (arg is IComparer) + { + comparer = (IComparer) arg; + } + else if (arg is LambdaExpressionNode) + { + LambdaExpressionNode fnCompare = (LambdaExpressionNode)arg; + if (fnCompare.ArgumentNames.Length != 2) + { + throw new ArgumentException("compare function must accept 2 arguments"); + } + comparer = new LambdaComparer(fnCompare); + } + else if (arg is Delegate) + { + comparer = new DelegateComparer((Delegate) arg); + } + + AssertUtils.ArgumentNotNull(comparer, "comparer", "orderBy(comparer) argument 'comparer' does not evaluate to a supported type"); + + ArrayList list = new ArrayList(source); + list.Sort(comparer); + return list; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/Processors/ReverseProcessor.cs b/src/Spring/Spring.Core/Expressions/Processors/ReverseProcessor.cs new file mode 100644 index 00000000..40919670 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/ReverseProcessor.cs @@ -0,0 +1,62 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Expressions.Processors +{ + /// + /// Reverts order of elements in the list + /// + /// Erich Eichinger + /// $Id: ReverseProcessor.cs,v 1.1 2008/03/20 23:58:16 oakinger Exp $ + public class ReverseProcessor : ICollectionProcessor + { + /// + /// Processes a list of source items and returns a result. + /// + /// + /// The source list to process. + /// + /// + /// An optional processor arguments array. + /// + /// + /// The processing result. + /// + public object Process(ICollection source, object[] args) + { + if (source == null || source.Count == 0) + { + return source; + } + + ArrayList list = new ArrayList(source); + list.Reverse(); + + return list; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/Processors/SortProcessor.cs b/src/Spring/Spring.Core/Expressions/Processors/SortProcessor.cs new file mode 100644 index 00000000..857db6f3 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/Processors/SortProcessor.cs @@ -0,0 +1,96 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Collections; + +#endregion + +namespace Spring.Expressions.Processors +{ + /// + /// Implementation of the sort processor. + /// + /// Aleksandar Seovic + /// $Id: SortProcessor.cs,v 1.4 2008/03/20 23:58:16 oakinger Exp $ + public class SortProcessor : ICollectionProcessor + { + /// + /// Sorts the source collection. + /// + /// + /// Please not that this processor requires that collection elements + /// are of a uniform type and that they implement + /// interface. + ///

    + /// If you want to perform custom sorting based on element properties + /// you should consider using instead. + /// + /// + /// The source collection to sort. + /// + /// + /// Ignored. + /// + /// + /// An array containing sorted collection elements. + /// + /// + /// If collection is not empty and it is + /// neither nor . + /// + public object Process(ICollection source, object[] args) + { + if (source == null || source.Count == 0) + { + return source; + } + + bool sortAscending = true; + if (args != null && args.Length == 1 && args[0] is bool) + { + sortAscending = (bool) args[0]; + } + + ArrayList list = new ArrayList(source); + list.Sort(); + if (!sortAscending) + { + list.Reverse(); + } + + Type elementType = DetermineElementType(list); + return list.ToArray(elementType); + } + + private Type DetermineElementType(IList list) + { + for(int i=0;i + /// Implementation of the sum aggregator. + /// + /// Aleksandar Seovic + /// $Id: SumAggregator.cs,v 1.2 2006/12/04 08:58:33 aseovic Exp $ + public class SumAggregator : ICollectionProcessor + { + ///

    + /// Returns the sum of the numeric values in the source collection. + /// + /// + /// The source collection to process. + /// + /// + /// Ignored. + /// + /// + /// The sum of the numeric values in the source collection. + /// + public object Process(ICollection source, object[] args) + { + object total = 0d; + foreach (object item in source) + { + if (item != null) + { + if (NumberUtils.IsNumber(item)) + { + total = NumberUtils.Add(total, item); + } + else + { + throw new ArgumentException("Sum can only be calculated for a collection of numeric values."); + } + } + } + + return total; + } + } +} diff --git a/src/Spring/Spring.Core/Expressions/ProjectionNode.cs b/src/Spring/Spring.Core/Expressions/ProjectionNode.cs new file mode 100644 index 00000000..ec153876 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/ProjectionNode.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed projection node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: ProjectionNode.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class ProjectionNode : BaseNode + { + /// + /// Create a new instance + /// + public ProjectionNode():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected ProjectionNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a containing results of evaluation + /// of projection expression against each node in the context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + IEnumerable enumerable = context as IEnumerable; + if(enumerable == null) + { + throw new ArgumentException( + "Projection can only be used on an instance of the type that implements IEnumerable."); + } + + BaseNode expression = (BaseNode) this.getFirstChild(); + IList projectedList = new ArrayList(); + using (evalContext.SwitchThisContext()) + { + foreach(object o in enumerable) + { + evalContext.ThisContext = o; + projectedList.Add(expression.GetValueInternal(o, evalContext)); + } + } + return projectedList; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/PropertyOrFieldNode.cs b/src/Spring/Spring.Core/Expressions/PropertyOrFieldNode.cs new file mode 100644 index 00000000..b2c8bb08 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/PropertyOrFieldNode.cs @@ -0,0 +1,725 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Runtime.Remoting; +using System.Runtime.Serialization; + +using Spring.Collections; +using Spring.Core; +using Spring.Core.TypeConversion; +using Spring.Core.TypeResolution; +using Spring.Util; +using Spring.Reflection.Dynamic; + +#endregion + +namespace Spring.Expressions +{ + /// + /// Represents node that navigates to object's property or public field. + /// + /// Aleksandar Seovic + /// $Id: PropertyOrFieldNode.cs,v 1.28 2007/09/14 13:48:53 oakinger Exp $ + [Serializable] + public class PropertyOrFieldNode : BaseNode + { + //private static readonly Common.Logging.ILog Log = Common.Logging.LogManager.GetLogger(typeof(PropertyOrFieldNode)); + + private const BindingFlags BINDING_FLAGS = + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | + BindingFlags.IgnoreCase; + + private string memberName; + private IValueAccessor accessor; + + /// + /// Create a new instance + /// + public PropertyOrFieldNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected PropertyOrFieldNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Initializes the node. + /// + /// The parent. + private void InitializeNode(object context) + { + Type contextType = (context == null || context is Type ? context as Type : context.GetType()); + + if (accessor == null || accessor.RequiresRefresh(contextType)) + { + memberName = this.getText(); + + // clear cached member info if context type has changed (for example, when ASP.NET page is recompiled) + if (accessor != null && accessor.RequiresRefresh(contextType)) + { + accessor = null; + } + + // initialize this node if necessary + if (contextType != null && accessor == null) + { + // try to initialize node as enum value first + if (contextType.IsEnum) + { + try + { + accessor = new EnumValueAccessor(Enum.Parse(contextType, memberName, true)); + } + catch (ArgumentException) + { + // ArgumentException will be thrown if specified member name is not a valid + // enum value for the context type. We should just ignore it and continue processing, + // because the specified member could be a property of a Type class (i.e. EnumType.FullName) + } + } + + // then try to initialize node as property or field value + if (accessor == null) + { + // check the context type first + accessor = GetPropertyOrFieldAccessor(contextType, memberName, BINDING_FLAGS); + + // if not found, probe the Type type + if (accessor == null && context is Type) + { + accessor = GetPropertyOrFieldAccessor(typeof(Type), memberName, BINDING_FLAGS); + } + } + } + + // if there is still no match, try to initialize node as type accessor + if (accessor == null) + { + try + { + accessor = new TypeValueAccessor(TypeResolutionUtils.ResolveType(memberName)); + } + catch (TypeLoadException) + { + if (context == null) + { + throw new NullValueInNestedPathException("Cannot initialize property or field node '" + + memberName + + "' because the specified context is null."); + } + else + { + throw new InvalidPropertyException(contextType, memberName, + "'" + memberName + + "' node cannot be resolved for the specified context [" + + context + "]."); + } + } + } + } + } + + /// + /// Attempts to resolve property or field. + /// + /// + /// Type to search for a property or a field. + /// + /// + /// Property or field name. + /// + /// + /// Binding flags to use. + /// + /// + /// Resolved property or field accessor, or null + /// if specified cannot be resolved. + /// + private static IValueAccessor GetPropertyOrFieldAccessor(Type contextType, string memberName, BindingFlags bindingFlags) + { + try + { + PropertyInfo pi = contextType.GetProperty(memberName, bindingFlags); + if (pi == null) + { + FieldInfo fi = contextType.GetField(memberName, bindingFlags); + if (fi != null) + { + return new FieldValueAccessor(fi); + } + } + else + { + return new PropertyValueAccessor(pi); + } + } + catch (AmbiguousMatchException) + { + PropertyInfo pi = null; + + // search type hierarchy + while (contextType != typeof(object)) + { + pi = contextType.GetProperty(memberName, bindingFlags | BindingFlags.DeclaredOnly); + if (pi == null) + { + FieldInfo fi = contextType.GetField(memberName, bindingFlags | BindingFlags.DeclaredOnly); + if (fi != null) + { + return new FieldValueAccessor(fi); + } + } + else + { + return new PropertyValueAccessor(pi); + } + contextType = contextType.BaseType; + } + } + + return null; + } + + /// + /// Returns node's value for the given context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + lock (this) + { + InitializeNode(context); + + if (context == null && accessor.RequiresContext) + { + throw new NullValueInNestedPathException( + "Cannot retrieve the value of a field or property '" + this.memberName + + "', because context for its resolution is null."); + } + if (IsProperty || IsField) + { + return GetPropertyOrFieldValue(context, evalContext); + } + else + { + return accessor.Get(context); + } + } + } + + /// + /// Sets node's value for the given context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// New value for this node. + protected override void Set(object context, EvaluationContext evalContext, object newValue) + { + lock (this) + { + InitializeNode(context); + + if (context == null && accessor.RequiresContext) + { + throw new NullValueInNestedPathException( + "Cannot set the value of a field or property '" + this.memberName + + "', because context for its resolution is null."); + } + if (IsProperty || IsField) + { + SetPropertyOrFieldValue(context, evalContext, newValue); + } + else + { + accessor.Set(context, newValue); + } + } + } + + /// + /// Gets a value indicating whether this node represents a property. + /// + /// + /// true if this node is a property; otherwise, false. + /// + private bool IsProperty + { + get { return accessor is PropertyValueAccessor; } + } + + /// + /// Gets a value indicating whether this node represents a field. + /// + /// + /// true if this node is a field; otherwise, false. + /// + private bool IsField + { + get { return accessor is FieldValueAccessor; } + } + + /// + /// Retrieves property or field value. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Property or field value. + private object GetPropertyOrFieldValue(object context, EvaluationContext evalContext) + { + try + { + return accessor.Get(context); + } + catch (InvalidOperationException) + { + throw new NotReadablePropertyException(evalContext.RootContextType, this.memberName); + } + catch (TargetInvocationException e) + { + throw new InvalidPropertyException(evalContext.RootContextType, this.memberName, + "Getter for property '" + this.memberName + "' threw an exception.", + e); + } + catch (UnauthorizedAccessException e) + { + throw new InvalidPropertyException(evalContext.RootContextType, this.memberName, + "Illegal attempt to get value for the property '" + this.memberName + + "'.", e); + } + } + + /// + /// Sets property value, doing any type conversions that are necessary along the way. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// New value for this node. + private void SetPropertyOrFieldValue(object context, EvaluationContext evalContext, object newValue) + { + bool isWriteable = accessor.IsWriteable; + Type targetType = accessor.TargetType; + + try + { + if (!isWriteable) + { + if (!AddToCollections(context, evalContext, newValue)) + { + throw new NotWritablePropertyException( + "Can't change the value of the read-only property or field '" + this.memberName + "'."); + } + } + else if (targetType.IsPrimitive && (newValue == null || String.Empty.Equals(newValue))) + { + throw new ArgumentException("Invalid value [" + newValue + "] for property or field '" + + this.memberName + "' of primitive type [" + + targetType + "]"); + } + else if (newValue == null || targetType.IsAssignableFrom(newValue.GetType())) + { + SetPropertyOrFieldValueInternal(context, newValue); + } + else if (!RemotingServices.IsTransparentProxy(newValue) && + (newValue is IList || newValue is IDictionary || newValue is ISet)) + { + if (!AddToCollections(context, evalContext, newValue)) + { + object tmpValue = + TypeConversionUtils.ConvertValueIfNecessary(targetType, newValue, this.memberName); + SetPropertyOrFieldValueInternal(context, tmpValue); + } + } + else + { + object tmpValue = TypeConversionUtils.ConvertValueIfNecessary(targetType, newValue, this.memberName); + SetPropertyOrFieldValueInternal(context, tmpValue); + } + } + catch (TargetInvocationException ex) + { + PropertyChangeEventArgs propertyChangeEvent = + new PropertyChangeEventArgs(this.memberName, null, newValue); + if (ex.GetBaseException() is InvalidCastException) + { + throw new TypeMismatchException(propertyChangeEvent, targetType, ex.GetBaseException()); + } + else + { + throw new MethodInvocationException(ex.GetBaseException(), propertyChangeEvent); + } + } + catch (UnauthorizedAccessException ex) + { + throw new FatalReflectionException("Illegal attempt to set property '" + this.memberName + "'", ex); + } + catch (NotWritablePropertyException) + { + throw; + } + catch (NotReadablePropertyException) + { + throw; + } + catch (ArgumentException ex) + { + PropertyChangeEventArgs propertyChangeEvent = + new PropertyChangeEventArgs(this.memberName, null, newValue); + throw new TypeMismatchException(propertyChangeEvent, targetType, ex); + } + } + + /// + /// Sets property or field value using either dynamic or standard reflection. + /// + /// Object to evaluate node against. + /// New value for this node, converted to appropriate type. + private void SetPropertyOrFieldValueInternal(object context, object newValue) + { + accessor.Set(context, newValue); + } + + /// + /// In the case of read only collections or custom collections that are not assignable from + /// IList, try to add to the collection. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// New value for this node. + /// true if was able add to IList, IDictionary, or ISet + private bool AddToCollections(object context, EvaluationContext evalContext, object newValue) + { + // short-circuit if accessor is not readable + if (!this.accessor.IsReadable) + { + return false; + } + + bool added = false; + + // try adding values if property is a list... + if (newValue is IList && !RemotingServices.IsTransparentProxy(newValue)) + { + IList currentValue = (IList)Get(context, evalContext); + if (currentValue != null && !currentValue.IsFixedSize && !currentValue.IsReadOnly) + { + foreach (object el in (IList)newValue) + { + currentValue.Add(el); + } + added = true; + } + } + // try adding values if property is a dictionary... + else if (newValue is IDictionary && !RemotingServices.IsTransparentProxy(newValue)) + { + IDictionary currentValue = (IDictionary)Get(context, evalContext); + if (currentValue != null && !currentValue.IsFixedSize && !currentValue.IsReadOnly) + { + foreach (DictionaryEntry entry in (IDictionary)newValue) + { + currentValue[entry.Key] = entry.Value; + } + added = true; + } + } + // try adding values if property is a set... + else if (newValue is ISet && !RemotingServices.IsTransparentProxy(newValue)) + { + ISet currentValue = (ISet)Get(context, evalContext); + if (currentValue != null) + { + currentValue.AddAll((ICollection)newValue); + added = true; + } + } + return added; + } + + /// + /// Utility method that is needed by ObjectWrapper and AbstractAutowireCapableObjectFactory. + /// We try as hard as we can, but there are instances when we won't be able to obtain PropertyInfo... + /// + /// Context to resolve property against. + /// PropertyInfo for this node. + internal MemberInfo GetMemberInfo(object context) + { + lock (this) + { + InitializeNode(context); + } + return accessor.MemberInfo; + + //if (IsProperty) + //{ + // return (((PropertyValueAccessor) accessor).MemberInfo); + //} + //else + //{ + // throw new FatalObjectException( + // "Cannot obtain PropertyInfo from an expression that does not resolve to a property."); + //} + } + + #region IValueAccessor interface + + private interface IValueAccessor + { + object Get(object context); + void Set(object context, object value); + + bool IsReadable { get; } + bool IsWriteable { get; } + bool RequiresContext { get; } + Type TargetType { get; } + MemberInfo MemberInfo { get; } + bool RequiresRefresh(Type contextType); + } + + #endregion + + #region BaseValueAccessor implementation + + private abstract class BaseValueAccessor : IValueAccessor + { + public abstract object Get(object context); + + public abstract void Set(object context, object value); + + public virtual bool IsReadable + { + get { return true; } + } + + public virtual bool IsWriteable + { + get { return false; } + } + + public virtual bool RequiresContext + { + get { return false; } + } + + public virtual Type TargetType + { + get { throw new NotSupportedException(); } + } + + public virtual MemberInfo MemberInfo + { + get { throw new NotSupportedException(); } + } + + public virtual bool RequiresRefresh(Type contextType) + { + return false; + } + } + + #endregion + + #region PropertyValueAccessor implementation + + private class PropertyValueAccessor : BaseValueAccessor + { + private SafeProperty property; + private string name; + private bool isReadable; + private bool isWriteable; + private Type targetType; + private Type contextType; + + public PropertyValueAccessor(PropertyInfo propertyInfo) + { + this.name = propertyInfo.Name; + this.isReadable = propertyInfo.CanRead; + this.isWriteable = propertyInfo.CanWrite; + this.targetType = propertyInfo.PropertyType; + this.contextType = propertyInfo.DeclaringType; + this.property = new SafeProperty(propertyInfo); + } + + public override object Get(object context) + { + if (!isReadable) + { + throw new NotReadablePropertyException("Cannot get a non-readable property [" + name + "]"); + } + return property.GetValue(context); + } + + public override void Set(object context, object value) + { + if (!isWriteable) + { + throw new NotWritablePropertyException("Cannot set a read-only property [" + name + "]"); + } + property.SetValue(context, value); + } + + public override bool IsReadable + { + get { return isReadable; } + } + + public override bool IsWriteable + { + get { return isWriteable; } + } + + public override bool RequiresContext + { + get { return true; } + } + + public override Type TargetType + { + get { return targetType; } + } + + public override MemberInfo MemberInfo + { + get { return property.PropertyInfo; } + } + + public override bool RequiresRefresh(Type contextType) + { + return this.contextType != contextType; + } + } + + #endregion + + #region FieldValueAccessor implementation + + private class FieldValueAccessor : BaseValueAccessor + { + private SafeField field; + private bool isWriteable; + private Type targetType; + private Type contextType; + + public FieldValueAccessor(FieldInfo fieldInfo) + { + this.field = new SafeField(fieldInfo); + this.isWriteable = !(fieldInfo.IsInitOnly || fieldInfo.IsLiteral); + this.targetType = fieldInfo.FieldType; + this.contextType = fieldInfo.DeclaringType; + } + + public override object Get(object context) + { + return field.GetValue(context); + } + + public override void Set(object context, object value) + { + field.SetValue(context, value); + } + + public override bool IsWriteable + { + get { return isWriteable; } + } + + public override bool RequiresContext + { + get { return true; } + } + + public override Type TargetType + { + get { return targetType; } + } + + public override MemberInfo MemberInfo + { + get { return field.FieldInfo; } + } + + public override bool RequiresRefresh(Type contextType) + { + return this.contextType != contextType; + } + } + + #endregion + + #region EnumValueAccessor implementation + + private class EnumValueAccessor : BaseValueAccessor + { + private object enumValue; + + public EnumValueAccessor(object enumValue) + { + this.enumValue = enumValue; + } + + public override object Get(object context) + { + return enumValue; + } + + public override void Set(object context, object value) + { + throw new NotSupportedException("Cannot set the value of an enum."); + } + } + + #endregion + + #region TypeValueAccessor implementation + + private class TypeValueAccessor : BaseValueAccessor + { + private Type type; + + public TypeValueAccessor(Type type) + { + this.type = type; + } + + public override object Get(object context) + { + return type; + } + + public override void Set(object context, object value) + { + throw new NotSupportedException("Cannot set the value of a type."); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/QualifiedIdentifier.cs b/src/Spring/Spring.Core/Expressions/QualifiedIdentifier.cs new file mode 100644 index 00000000..94a11604 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/QualifiedIdentifier.cs @@ -0,0 +1,98 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using antlr.collections; + +namespace Spring.Expressions +{ + /// + /// Represents parsed named argument node in the expression. + /// + /// Aleksandar Seovic + /// $Id: QualifiedIdentifier.cs,v 1.7 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class QualifiedIdentifier : BaseNode + { + private const string ESCAPE_CHAR = "\\"; + + private string identifier; + + /// + /// Create a new instance + /// + public QualifiedIdentifier():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected QualifiedIdentifier(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns the value of the named argument defined by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if (identifier == null) + { + lock (this) + { + if (identifier == null) + { + identifier = base.getText(); + if (identifier != null) + { + identifier = identifier.Replace(ESCAPE_CHAR,""); // remove all occurrences of escape char + } + AST node = this.getFirstChild(); + while (node != null) + { + identifier += node.getText(); + node = node.getNextSibling(); + } + } + } + } + + return identifier; + } + + /// + /// Overrides getText to allow easy way to get fully + /// qualified identifier. + /// + /// + /// Fully qualified identifier as a string. + /// + public override string getText() + { + return (string) Get(null, null); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/RealLiteralNode.cs b/src/Spring/Spring.Core/Expressions/RealLiteralNode.cs new file mode 100644 index 00000000..86468131 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/RealLiteralNode.cs @@ -0,0 +1,95 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed real literal node. + /// + /// Aleksandar Seovic + /// $Id: RealLiteralNode.cs,v 1.11 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class RealLiteralNode : BaseNode + { + private object nodeValue; + + /// + /// Create a new instance + /// + public RealLiteralNode():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected RealLiteralNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the real literal node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if (nodeValue == null) + { + lock (this) + { + if (nodeValue == null) + { + string n = this.getText(); + char lastChar = n.ToLower()[n.Length - 1]; + if (Char.IsDigit(lastChar)) + { + nodeValue = Double.Parse(n, NumberFormatInfo.InvariantInfo); + } + else + { + n = n.Substring(0, n.Length - 1); + if (lastChar == 'm') + { + nodeValue = Decimal.Parse(n, NumberFormatInfo.InvariantInfo); + } + else if (lastChar == 'f') + { + nodeValue = Single.Parse(n, NumberFormatInfo.InvariantInfo); + } + else + { + nodeValue = Double.Parse(n, NumberFormatInfo.InvariantInfo); + } + } + } + } + } + + return nodeValue; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/ReferenceNode.cs b/src/Spring/Spring.Core/Expressions/ReferenceNode.cs new file mode 100644 index 00000000..b5440338 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/ReferenceNode.cs @@ -0,0 +1,96 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using Spring.Expressions; +using Spring.Objects.Factory; + +namespace Spring.Context.Support +{ + /// + /// Represents a reference to a Spring-managed object. + /// + /// Aleksandar Seovic + /// $Id: ReferenceNode.cs,v 1.13 2008/03/20 10:35:54 oakinger Exp $ + [Serializable] + public class ReferenceNode : BaseNode + { + /// + /// Create a new instance + /// + public ReferenceNode():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected ReferenceNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the integer literal node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + IApplicationContext ctx; + string objectName; + + if (this.getNumberOfChildren() == 2) + { + string contextName = this.getFirstChild().getText(); + objectName = this.getFirstChild().getNextSibling().getText(); + ctx = ContextRegistry.GetContext(contextName); + if (ctx == null) + { + throw new ArgumentException(string.Format("Context '{0}' is not registered.", contextName)); + } + } + else + { + objectName = this.getFirstChild().getText(); + IObjectFactory currentObjectFactory = (evalContext.Variables != null) + ? (IObjectFactory)evalContext.Variables[Expression.ReservedVariableNames.CurrentObjectFactory] + : null; + + // this is a local reference within an object factory + if (currentObjectFactory != null) + { + return currentObjectFactory.GetObject(objectName); + } + + // else lookup in default context + ctx = ContextRegistry.GetContext(); + if (ctx == null) + { + throw new ArgumentException("No context registered."); + } + } + + return ctx.GetObject(objectName); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/SelectionFirstNode.cs b/src/Spring/Spring.Core/Expressions/SelectionFirstNode.cs new file mode 100644 index 00000000..31273dde --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/SelectionFirstNode.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed selection node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: SelectionFirstNode.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class SelectionFirstNode : BaseNode + { + /// + /// Create a new instance + /// + public SelectionFirstNode():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected SelectionFirstNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns the first context item that matches selection expression. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + IEnumerable enumerable = context as IEnumerable; + if (enumerable == null) + { + throw new ArgumentException( + "Selection can only be used on an instance of the type that implements IEnumerable."); + } + + BaseNode expression = (BaseNode) this.getFirstChild(); + using (evalContext.SwitchThisContext()) + { + foreach (object o in enumerable) + { + evalContext.ThisContext = o; + bool isMatch = (bool) expression.GetValueInternal(o, evalContext); + if (isMatch) + { + return o; + } + } + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/SelectionLastNode.cs b/src/Spring/Spring.Core/Expressions/SelectionLastNode.cs new file mode 100644 index 00000000..e0aee460 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/SelectionLastNode.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed selection node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: SelectionLastNode.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class SelectionLastNode : BaseNode + { + /// + /// Create a new instance + /// + public SelectionLastNode():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected SelectionLastNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns the last context item that matches selection expression. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + IList list = context as IList; + + if (list == null) + { + throw new ArgumentException( + "Selection can only be used on an instance of the type that implements IList."); + } + + using (evalContext.SwitchThisContext()) + { + BaseNode expression = (BaseNode) this.getFirstChild(); + for (int i = list.Count - 1; i >= 0; i--) + { + object listItem = list[i]; + evalContext.ThisContext = listItem; + bool isMatch = (bool)expression.GetValueInternal( listItem, evalContext ); + if (isMatch) + { + return listItem; + } + } + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/SelectionNode.cs b/src/Spring/Spring.Core/Expressions/SelectionNode.cs new file mode 100644 index 00000000..ec3c2a9a --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/SelectionNode.cs @@ -0,0 +1,105 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed selection node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: SelectionNode.cs,v 1.6 2008/03/25 22:33:04 oakinger Exp $ + [Serializable] + public class SelectionNode : BaseNode + { + /// + /// Create a new instance + /// + public SelectionNode() + : base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected SelectionNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a containing results of evaluation + /// of selection expression against each node in the context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + IEnumerable enumerable = context as IEnumerable; + if (enumerable == null) + { + throw new ArgumentException( + "Selection can only be used on an instance of the type that implements IEnumerable."); + } + + BaseNode expression = (BaseNode)this.getFirstChild(); + BaseNode minIndexExpression = (BaseNode)expression.getNextSibling(); + BaseNode maxIndexExpression = (minIndexExpression == null) ? null : (BaseNode)minIndexExpression.getNextSibling(); + + int minIndex = (int)((minIndexExpression == null) + ? Int32.MinValue + : minIndexExpression.GetValueInternal(context, evalContext)); + int maxIndex = (int)((maxIndexExpression == null) + ? Int32.MaxValue + : maxIndexExpression.GetValueInternal(context, evalContext)); + + IList selectionList = new ArrayList(); + + using (evalContext.SwitchThisContext()) + { + int found = 0; + foreach (object o in enumerable) + { + evalContext.ThisContext = o; + bool isMatch = (bool)expression.GetValueInternal(o, evalContext); + if (isMatch) + { + if (minIndex <= found && found <= maxIndex) + { + selectionList.Add(o); + } + found++; + + if (found>maxIndex) + { + break; // don't look any further + } + } + } + } + return selectionList; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/SpringAST.cs b/src/Spring/Spring.Core/Expressions/SpringAST.cs new file mode 100644 index 00000000..46a5d4ce --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/SpringAST.cs @@ -0,0 +1,152 @@ +using System; +using System.Runtime.Serialization; +using antlr; +using antlr.collections; + +namespace Spring.Expressions +{ + /// + /// For internal purposes only. Use for expression node implementations. + /// + /// + /// This class is only required to enable serialization of parsed Spring expressions since antlr.CommonAST + /// unfortunately is not marked as [Serializable].
    + ///
    + /// Note:Since SpringAST implements , deriving classes + /// have to explicitely override if they need to persist additional + /// data during serialization. + ///
    + [Serializable] + public class SpringAST : antlr.BaseAST, ISerializable + { + #region Global SpringAST Factory + + internal class SpringASTCreator : antlr.ASTNodeCreator + { + public override antlr.collections.AST Create() + { + return new SpringAST(); + } + + public override string ASTNodeTypeName + { + get { return typeof(SpringAST).FullName; } + } + } + + /// + /// The global SpringAST node factory + /// + internal static readonly SpringASTCreator Creator = new SpringASTCreator(); + + #endregion + + #region Members + + private string text; + private int ttype; + + #endregion + + /// + /// Create an instance + /// + public SpringAST() + {} + + /// + /// Create an instance from a token + /// + public SpringAST(IToken token) + { + initialize(token); + } + + /// + /// initialize this instance from an AST + /// + public override void initialize(AST t) + { + this.setText(t.getText()); + this.Type = t.Type; + } + + /// + /// initialize this instance from an IToken + /// + public override void initialize(IToken tok) + { + this.setText(tok.getText()); + this.Type = tok.Type; + } + + /// + /// initialize this instance from a token type number and a text + /// + public override void initialize(int t, string txt) + { + this.Type = t; + this.setText(txt); + } + + /// + /// gets or sets the token type of this node + /// + public override int Type + { + get { return this.ttype; } + set { this.ttype = value; } + } + + /// + /// gets or sets the text of this node + /// + public string Text + { + get { return this.getText(); } + set { this.setText(value); } + } + + /// + /// sets the text of this node + /// + public override void setText(string txt) + { + this.text = txt; + } + + /// + /// gets the text of this node + /// + public override string getText() + { + return this.text; + } + + #region ISerializable Implementation + + /// + /// Create a new instance from SerializationInfo + /// + protected SpringAST(SerializationInfo info, StreamingContext context) + { + base.down = (BaseAST)info.GetValue("down", typeof(BaseAST)); + base.right = (BaseAST)info.GetValue("right", typeof(BaseAST)); + this.ttype = info.GetInt32("ttype"); + this.text = info.GetString("text"); + } + + /// + /// populate SerializationInfo from this instance + /// + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("down", base.down, typeof(SpringAST)); + info.AddValue("right", base.right, typeof(SpringAST)); + info.AddValue("ttype", this.Type, typeof(int)); + info.AddValue("text", this.Text, typeof(string)); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/StringLiteralNode.cs b/src/Spring/Spring.Core/Expressions/StringLiteralNode.cs new file mode 100644 index 00000000..4c0394a6 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/StringLiteralNode.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed string literal node. + /// + /// Aleksandar Seovic + /// $Id: StringLiteralNode.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class StringLiteralNode : BaseNode + { + /// + /// Create a new instance + /// + public StringLiteralNode():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected StringLiteralNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the string literal node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + return this.getText(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/TernaryNode.cs b/src/Spring/Spring.Core/Expressions/TernaryNode.cs new file mode 100644 index 00000000..39d90af3 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/TernaryNode.cs @@ -0,0 +1,91 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; +using antlr.collections; + +namespace Spring.Expressions +{ + /// + /// Represents ternary expression node. + /// + /// Aleksandar Seovic + /// $Id: TernaryNode.cs,v 1.9 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class TernaryNode : BaseNode + { + private bool initialized = false; + private BaseNode condition; + private BaseNode trueExp; + private BaseNode falseExp; + + /// + /// Create a new instance + /// + public TernaryNode():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected TernaryNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns a value for the string literal node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if (!initialized) + { + lock (this) + { + if (!initialized) + { + AST node = this.getFirstChild(); + condition = (BaseNode) node; + node = node.getNextSibling(); + trueExp = (BaseNode) node; + node = node.getNextSibling(); + falseExp = (BaseNode) node; + + initialized = true; + } + } + } + + if (Convert.ToBoolean(condition.GetValueInternal(context, evalContext))) + { + return trueExp.GetValueInternal(context, evalContext); + } + else + { + return falseExp.GetValueInternal(context, evalContext); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/TypeNode.cs b/src/Spring/Spring.Core/Expressions/TypeNode.cs new file mode 100644 index 00000000..a1c82055 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/TypeNode.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +using antlr.collections; +using Spring.Core.TypeResolution; + +namespace Spring.Expressions +{ + /// + /// Represents parsed type node in the navigation expression. + /// + /// Aleksandar Seovic + /// $Id: TypeNode.cs,v 1.11 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class TypeNode : BaseNode + { + private Type type; + + /// + /// Create a new instance + /// + public TypeNode():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected TypeNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns node's value for the given context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + if (type == null) + { + lock (this) + { + if (type == null) + { + string typeName = this.getText(); + AST node = this.getFirstChild(); + while (node != null) + { + typeName += node.getText(); + node = node.getNextSibling(); + } + + type = TypeResolutionUtils.ResolveType(typeName); + } + } + } + + return type; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/UnaryOperator.cs b/src/Spring/Spring.Core/Expressions/UnaryOperator.cs new file mode 100644 index 00000000..b13ef61e --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/UnaryOperator.cs @@ -0,0 +1,57 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Base class for unary operators. + /// + /// Aleksandar Seovic + /// $Id: UnaryOperator.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + //[Serializable] + public abstract class UnaryOperator : BaseNode + { + /// + /// Create a new instance + /// + public UnaryOperator():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected UnaryOperator(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Gets the operand. + /// + /// The operand. + public BaseNode Operand + { + get { return (BaseNode) this.getFirstChild(); } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Expressions/VariableNode.cs b/src/Spring/Spring.Core/Expressions/VariableNode.cs new file mode 100644 index 00000000..2a7db702 --- /dev/null +++ b/src/Spring/Spring.Core/Expressions/VariableNode.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Expressions +{ + /// + /// Represents parsed variable node. + /// + /// Aleksandar Seovic + /// $Id: VariableNode.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + [Serializable] + public class VariableNode : BaseNode + { + /// + /// Create a new instance + /// + public VariableNode():base() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected VariableNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Returns value of the variable represented by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + string varName = this.getText(); + if (varName == "this") + { + return evalContext.ThisContext; + } + else if (varName == "root") + { + return evalContext.RootContext; + } + return evalContext.Variables[varName]; + } + + /// + /// Sets value of the variable represented by this node. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// New value for this node. + protected override void Set(object context, EvaluationContext evalContext, object newValue) + { + string varName = this.getText(); + if (varName == "this" || varName == "root") + { + throw new ArgumentException("You cannot assign a value to intrinsic variable '" + varName + "'."); + } + if (evalContext.Variables == null) + { + throw new InvalidOperationException( + "You need to provide variables dictionary to expression evaluation engine in order to be able to set variable values."); + } + evalContext.Variables[varName] = newValue; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/AbstractLocalizer.cs b/src/Spring/Spring.Core/Globalization/AbstractLocalizer.cs new file mode 100644 index 00000000..d472a870 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/AbstractLocalizer.cs @@ -0,0 +1,124 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Globalization; +using System.Threading; + +using Spring.Context; +using Spring.Util; + +namespace Spring.Globalization +{ + /// + /// Abstract base class that all localizers should extend + /// + /// + ///

    + /// This class contains the bulk of the localizer logic, including implementation + /// of the ApplyResources methods that are defined in + /// interface. + ///

    + ///

    + /// All specific localizers need to do is inherit this class and implement + /// GetResources method that will return a list of + /// objects that should be applied to a specified target. + ///

    + ///

    + /// Custom implementations can use whatever type of resource storage they want, + /// such as standard .NET resource sets, custom XML files, database, etc. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: AbstractLocalizer.cs,v 1.5 2007/07/24 17:26:25 oakinger Exp $ + public abstract class AbstractLocalizer : ILocalizer + { + private IResourceCache resourceCache = new NullResourceCache(); + + /// + /// Gets or sets the resource cache instance. + /// + /// The resource cache instance. + public IResourceCache ResourceCache + { + get { return resourceCache; } + set { resourceCache = value; } + } + + /// + /// Applies resources of the specified culture to the specified target object. + /// + /// Target object to apply resources to. + /// instance to retrieve resources from. + /// Resource culture to use for resource lookup. + public void ApplyResources(object target, IMessageSource messageSource, CultureInfo culture) + { + AssertUtils.ArgumentNotNull(target, "target"); + AssertUtils.ArgumentNotNull(culture, "culture"); + + IList resources = GetResources(target, messageSource, culture); + foreach (Resource resource in resources) + { + resource.Target.SetValue(target, null, resource.Value); + } + } + + /// + /// Applies resources to the specified target object, using current thread's uiCulture to resolve resources. + /// + /// Target object to apply resources to. + /// instance to retrieve resources from. + public void ApplyResources(object target, IMessageSource messageSource) + { + AssertUtils.ArgumentNotNull(target, "target"); + ApplyResources(target, messageSource, Thread.CurrentThread.CurrentUICulture); + } + + /// + /// Returns a list of instances that should be applied to the target. + /// + /// Target to get a list of resources for. + /// instance to retrieve resources from. + /// Resource locale. + /// A list of resources to apply. + private IList GetResources(object target, IMessageSource messageSource, CultureInfo culture) + { + IList resources = resourceCache.GetResources(target, culture); + + if (resources == null) + { + resources = LoadResources(target, messageSource, culture); + resourceCache.PutResources(target, culture, resources); + } + + return resources; + } + + /// + /// Loads resources from the storage and creates a list of instances that should be applied to the target. + /// + /// Target to get a list of resources for. + /// instance to retrieve resources from. + /// Resource locale. + /// A list of resources to apply. + protected abstract IList LoadResources(object target, IMessageSource messageSource, CultureInfo culture); + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/AbstractResourceCache.cs b/src/Spring/Spring.Core/Globalization/AbstractResourceCache.cs new file mode 100644 index 00000000..306d46ef --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/AbstractResourceCache.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Globalization; + +namespace Spring.Globalization +{ + /// + /// Abstract base class that all resource cache implementations should extend. + /// + /// Aleksandar Seovic + /// $Id: AbstractResourceCache.cs,v 1.3 2006/04/09 07:18:47 markpollack Exp $ + public abstract class AbstractResourceCache : IResourceCache + { + /// + /// Gets the list of resources from the cache. + /// + /// Target to get a list of resources for. + /// Resource culture. + /// A list of cached resources for the specified target object and culture. + public IList GetResources(object target, CultureInfo culture) + { + return GetResources(CreateCacheKey(target, culture)); + } + + /// + /// Puts the list of resources in the cache. + /// + /// Target to cache a list of resources for. + /// Resource culture. + /// A list of resources to cache. + /// A list of cached resources for the specified target object and culture. + public void PutResources(object target, CultureInfo culture, IList resources) + { + PutResources(CreateCacheKey(target, culture), resources); + } + + /// + /// Crates resource cache key for the specified target object and culture. + /// + /// Target object to apply resources to. + /// Resource culture to use for resource lookup. + protected virtual string CreateCacheKey(object target, CultureInfo culture) + { + return target.GetType().FullName + "." + culture.Name + ".resources"; + } + + /// + /// Gets the list of resources from cache. + /// + /// Cache key to use for lookup. + /// A list of cached resources for the specified target object and culture. + protected abstract IList GetResources(string cacheKey); + + /// + /// Puts the list of resources in the cache. + /// + /// Cache key to use for the specified resources. + /// A list of resources to cache. + /// A list of cached resources for the specified target object and culture. + protected abstract void PutResources(string cacheKey, IList resources); + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/Formatters/BooleanFormatter.cs b/src/Spring/Spring.Core/Globalization/Formatters/BooleanFormatter.cs new file mode 100644 index 00000000..e6ddbd43 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Formatters/BooleanFormatter.cs @@ -0,0 +1,146 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; + +using Spring.Util; + +namespace Spring.Globalization.Formatters +{ + /// + /// Implementation of that can be used to + /// format and parse boolean values. + /// + /// Erich Eichinger + /// $Id: BooleanFormatter.cs,v 1.1 2007/06/01 08:59:31 oakinger Exp $ + public class BooleanFormatter:IFormatter + { + private string trueString; + private string falseString; + private bool ignoreCase; + + #region Constructors + + /// + /// Initializes a new instance of the class + /// using default values + /// + public BooleanFormatter() + : this(true,bool.TrueString,bool.FalseString) + { } + + /// + /// Initializes a new instance of the class + /// + public BooleanFormatter(bool ignoreCase,string trueString,string falseString) + { + this.trueString = trueString; + this.falseString = falseString; + this.ignoreCase = ignoreCase; + } + + #endregion + + #region Properties + + /// + /// Set/Get value to control casesensitivity of + /// + /// + /// Defaults to true + /// + public bool IgnoreCase + { + get { return this.ignoreCase; } + set { this.ignoreCase = value; } + } + + /// + /// Set/Get value to recognize as boolean "true" value + /// + /// + /// Defaults to + /// + public string TrueString + { + get { return this.trueString; } + set { this.trueString = value; } + } + + /// + /// Set/Get value to recognize as boolean "false" value + /// + /// + /// Defaults to + /// + public string FalseString + { + get { return this.falseString; } + set { this.falseString = value; } + } + + #endregion + + /// + /// Formats the specified boolean value. + /// + /// The value to format. + /// Formatted boolean value. + /// If is null. + /// If is not of type . + public string Format(object value) + { + AssertUtils.ArgumentNotNull( value,"value" ); + if(!(value is bool)) + { + throw new ArgumentException( "BooleanFormatter can only be used to format instances of [System.Boolean]" ); + } + return ((bool) value) ? this.trueString : this.falseString; + } + + /// + /// Parses the specified boolean value according to settings of and + /// + /// The boolean value to parse. + /// Parsed boolean value as a . + /// If does not match or . + public object Parse(string value) + { + if(!StringUtils.HasText( value )) + { + return false; + } + + if ( 0 == string.Compare( value, this.trueString, this.ignoreCase )) + { + return true; + } + else if(0 == string.Compare( value,this.falseString,this.ignoreCase )) + { + return false; + } + else + { + throw new ArgumentException( string.Format("input value '{0}' is not recognized as a boolean", value) ); + } + } + } +} diff --git a/src/Spring/Spring.Core/Globalization/Formatters/CurrencyFormatter.cs b/src/Spring/Spring.Core/Globalization/Formatters/CurrencyFormatter.cs new file mode 100644 index 00000000..a7e47d42 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Formatters/CurrencyFormatter.cs @@ -0,0 +1,209 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; + +using Spring.Util; + +namespace Spring.Globalization.Formatters +{ + /// + /// Implementation of that can be used to + /// format and parse currency values. + /// + /// + /// + /// CurrencyFormatter uses currency related properties of the + /// to format and parse currency values. + /// + /// + /// If you use one of the constructors that accept culture as a parameter + /// to create an instance of CurrencyFormatter, default NumberFormatInfo + /// for the specified culture will be used. + /// + /// + /// You can also use properties exposed by the CurrencyFormatter in order + /// to override some of the default currency formatting parameters. + /// + /// + /// Aleksandar Seovic + /// $Id: CurrencyFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public class CurrencyFormatter : IFormatter + { + private NumberFormatInfo formatInfo; + + #region Constructors + + /// + /// Initializes a new instance of the class + /// using default for the current thread's culture. + /// + public CurrencyFormatter() : this(CultureInfo.CurrentCulture) + {} + + /// + /// Initializes a new instance of the class + /// using default for the specified culture. + /// + /// The culture name. + public CurrencyFormatter(string cultureName) : this(CultureInfo.CreateSpecificCulture(cultureName)) + {} + + /// + /// Initializes a new instance of the class + /// using default for the specified culture. + /// + /// The culture. + public CurrencyFormatter(CultureInfo culture) + { + formatInfo = culture.NumberFormat; + } + + /// + /// Initializes a new instance of the class + /// using specified . + /// + /// + /// The instance that defines how + /// currency values are formatted. + /// + public CurrencyFormatter(NumberFormatInfo formatInfo) + { + this.formatInfo = formatInfo; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the currency decimal digits. + /// + /// The currency decimal digits. + /// + public int DecimalDigits + { + get { return formatInfo.CurrencyDecimalDigits; } + set { formatInfo.CurrencyDecimalDigits = value; } + } + + /// + /// Gets or sets the currency decimal separator. + /// + /// The currency decimal separator. + /// + public string DecimalSeparator + { + get { return formatInfo.CurrencyDecimalSeparator; } + set { formatInfo.CurrencyDecimalSeparator = value; } + } + + /// + /// Gets or sets the currency group sizes. + /// + /// The currency group sizes. + /// + public int[] GroupSizes + { + get { return formatInfo.CurrencyGroupSizes; } + set { formatInfo.CurrencyGroupSizes = value; } + } + + /// + /// Gets or sets the currency group separator. + /// + /// The currency group separator. + /// + public string GroupSeparator + { + get { return formatInfo.CurrencyGroupSeparator; } + set { formatInfo.CurrencyGroupSeparator = value; } + } + + /// + /// Gets or sets the currency symbol. + /// + /// The currency symbol. + /// + public string CurrencySymbol + { + get { return formatInfo.CurrencySymbol; } + set { formatInfo.CurrencySymbol = value; } + } + + /// + /// Gets or sets the currency negative pattern. + /// + /// The currency negative pattern. + /// + public int NegativePattern + { + get { return formatInfo.CurrencyNegativePattern; } + set { formatInfo.CurrencyNegativePattern = value; } + } + + /// + /// Gets or sets the currency positive pattern. + /// + /// The currency positive pattern. + /// + public int PositivePattern + { + get { return formatInfo.CurrencyPositivePattern; } + set { formatInfo.CurrencyPositivePattern = value; } + } + + #endregion + + /// + /// Formats the specified currency value. + /// + /// The value to format. + /// Formatted currency . + /// If is null. + /// If is not a number. + public string Format(object value) + { + AssertUtils.ArgumentNotNull(value, "value"); + if (!NumberUtils.IsNumber(value)) + { + throw new ArgumentException("CurrencyFormatter can only be used to format numbers."); + } + + return String.Format(formatInfo, "{0:C}", value); + } + + /// + /// Parses the specified currency value. + /// + /// The currency value to parse. + /// Parsed currency value as a . + public object Parse(string value) + { + if (!StringUtils.HasText(value)) + { + return 0d; + } + + return Double.Parse(value, NumberStyles.Currency, formatInfo); + } + } +} diff --git a/src/Spring/Spring.Core/Globalization/Formatters/DateTimeFormatter.cs b/src/Spring/Spring.Core/Globalization/Formatters/DateTimeFormatter.cs new file mode 100644 index 00000000..1ea2c588 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Formatters/DateTimeFormatter.cs @@ -0,0 +1,120 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; + +using Spring.Util; + +namespace Spring.Globalization.Formatters +{ + /// + /// Implementation of that can be used to + /// format and parse values. + /// + /// + /// + /// DateTimeFormatter uses properties of the + /// to format and parse values. + /// + /// + /// If you use one of the constructors that accept culture as a parameter + /// to create an instance of DateTimeFormatter, default DateTimeFormatInfo + /// for the specified culture will be used. + /// + /// + /// You can also use properties exposed by the DateTimeFormatter in order + /// to override some of the default formatting parameters. + /// + /// + /// Aleksandar Seovic + /// $Id: DateTimeFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public class DateTimeFormatter : IFormatter + { + private DateTimeFormatInfo formatInfo; + private string format; + + #region Constructors + + /// + /// Initializes a new instance of the class + /// using default for the current thread's culture. + /// + /// Date/time format string. + public DateTimeFormatter(string format) : this(format, CultureInfo.CurrentCulture) + {} + + /// + /// Initializes a new instance of the class + /// using default for the specified culture. + /// + /// Date/time format string. + /// The culture name. + public DateTimeFormatter(string format, string cultureName) : this(format, CultureInfo.CreateSpecificCulture(cultureName)) + {} + + /// + /// Initializes a new instance of the class + /// using default for the specified culture. + /// + /// Date/time format string. + /// The culture. + public DateTimeFormatter(string format, CultureInfo culture) + { + this.format = format; + this.formatInfo = culture.DateTimeFormat; + } + + #endregion + + /// + /// Formats the specified value. + /// + /// The value to format. + /// Formatted value. + /// If is null. + /// If is not an instance of . + public string Format(object value) + { + AssertUtils.ArgumentNotNull(value, "value"); + if (!(value is DateTime)) + { + throw new ArgumentException("DateTimeFormatter can only be used to format instances of [System.DateTime]."); + } + + return ((DateTime) value).ToString(format, formatInfo); + } + + /// + /// Parses the specified value. + /// + /// The string to parse. + /// Parsed value. + public object Parse(string value) + { + if (!StringUtils.HasText(value)) + { + return DateTime.MinValue; + } + + return DateTime.ParseExact(value, format, formatInfo, DateTimeStyles.AllowWhiteSpaces); + } + } +} diff --git a/src/Spring/Spring.Core/Globalization/Formatters/FilteringFormatter.cs b/src/Spring/Spring.Core/Globalization/Formatters/FilteringFormatter.cs new file mode 100644 index 00000000..e54af5f1 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Formatters/FilteringFormatter.cs @@ -0,0 +1,101 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Util; + +#endregion + +namespace Spring.Globalization.Formatters +{ + /// + /// Provides base functionality for filtering values before they actually get parsed/formatted. + /// + /// Erich Eichinger + /// $Id: FilteringFormatter.cs,v 1.1 2008/03/20 13:19:47 oakinger Exp $ + public abstract class FilteringFormatter : IFormatter + { + private readonly IFormatter _underlyingFormatter; + + /// + /// Creates a new instance of this FilteringFormatter. + /// + ///an optional underlying formatter + /// + /// If no underlying formatter is specified, the values + /// get passed through "as-is" after being filtered + /// + public FilteringFormatter(IFormatter underlyingFormatter) + { + _underlyingFormatter = underlyingFormatter; + } + + /// + /// Parses the specified value. + /// + /// The value to parse. + /// Parsed . + public object Parse(string value) + { + value = FilterValueToParse(value); + + if (_underlyingFormatter != null) + { + return _underlyingFormatter.Parse(value); + } + + return value; + } + + /// + /// Formats the specified value. + /// + /// The value to format. + /// Formatted . + public string Format(object value) + { + value = FilterValueToFormat(value); + + if (_underlyingFormatter != null) + { + return _underlyingFormatter.Format(value); + } + + return (value != null) ? value.ToString() : null; + } + + /// + /// Allows to rewrite a value before it gets parsed by the underlying formatter + /// + protected virtual string FilterValueToParse(string value) + { + return value; + } + + /// + /// Allows to change a value before it gets formatted by the underlying formatter + /// + protected virtual object FilterValueToFormat(object value) + { + return value; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/Formatters/FloatFormatter.cs b/src/Spring/Spring.Core/Globalization/Formatters/FloatFormatter.cs new file mode 100644 index 00000000..d55df1b6 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Formatters/FloatFormatter.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; + +using Spring.Util; + +namespace Spring.Globalization.Formatters +{ + /// + /// Implementation of that can be used to + /// format and parse floating point numbers. + /// + /// + /// + /// This formatter allows you to format and parse numbers that conform + /// to number style (leading and trailing + /// white space, leading sign, decimal point, exponent). + /// + /// + /// Aleksandar Seovic + /// $Id: FloatFormatter.cs,v 1.4 2006/04/09 07:18:47 markpollack Exp $ + public class FloatFormatter : IFormatter + { + /// + /// Default format string. + /// + public const string DefaultFormat = "{0:F}"; + + private string format; + private NumberFormatInfo numberInfo; + + #region Constructors + + /// + /// Initializes a new instance of the class, + /// using default format string of '{0:F}' and current thread's culture. + /// + public FloatFormatter() : this(DefaultFormat, CultureInfo.CurrentCulture) + {} + + /// + /// Initializes a new instance of the class, + /// using specified format string and current thread's culture. + /// + /// The format string. + public FloatFormatter(string format) : this(format, CultureInfo.CurrentCulture) + {} + + /// + /// Initializes a new instance of the class, + /// using default format string of '{0:F}' and specified culture. + /// + /// The culture. + public FloatFormatter(CultureInfo culture) : this(DefaultFormat, culture) + {} + + /// + /// Initializes a new instance of the class, + /// using specified format string and current thread's culture. + /// + /// The format string. + /// The culture name. + public FloatFormatter(string format, string cultureName) : this(format, CultureInfo.CreateSpecificCulture(cultureName)) + {} + + /// + /// Initializes a new instance of the class, + /// using specified format string and culture. + /// + /// The format string. + /// The culture. + public FloatFormatter(string format, CultureInfo culture) + { + this.format = format; + this.numberInfo = culture.NumberFormat; + } + + #endregion + + /// + /// Formats the specified float value. + /// + /// The value to format. + /// Formatted floating point number. + /// If is null. + /// If is not a number. + public string Format(object value) + { + AssertUtils.ArgumentNotNull(value, "value"); + if (!NumberUtils.IsNumber(value)) + { + throw new ArgumentException("FloatFormatter can only be used to format numbers."); + } + + return String.Format(numberInfo, format, value); + } + + /// + /// Parses the specified float value. + /// + /// The float value to parse. + /// Parsed float value as a . + public object Parse(string value) + { + if (!StringUtils.HasText(value)) + { + return 0d; + } + return Double.Parse(value, NumberStyles.Float, numberInfo); + } + } +} diff --git a/src/Spring/Spring.Core/Globalization/Formatters/HasTextFilteringFormatter.cs b/src/Spring/Spring.Core/Globalization/Formatters/HasTextFilteringFormatter.cs new file mode 100644 index 00000000..296bb688 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Formatters/HasTextFilteringFormatter.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Util; + +#endregion + +namespace Spring.Globalization.Formatters +{ + /// + /// Replaces input strings with a given default value, + /// if they are null or contain whitespaces only, + /// + /// Erich Eichinger + /// $Id: HasTextFilteringFormatter.cs,v 1.1 2008/03/20 13:19:47 oakinger Exp $ + public class HasTextFilteringFormatter : FilteringFormatter + { + private readonly string _defaultValue; + + /// + /// Creates a new instance of this HasTextFilteringFormatter using null as default value. + /// + ///an optional underlying formatter + /// + /// If no underlying formatter is specified, the values + /// get passed through "as-is" after being filtered + /// + public HasTextFilteringFormatter(IFormatter underlyingFormatter) + : this(null, underlyingFormatter) + { + } + + /// + /// Creates a new instance of this HasTextFilteringFormatter. + /// + /// the default value to be returned, if input text doesn't contain text + ///an optional underlying formatter + /// + /// If no underlying formatter is specified, the values + /// get passed through "as-is" after being filtered + /// + public HasTextFilteringFormatter(string defaultValue, IFormatter underlyingFormatter) + : base(underlyingFormatter) + { + _defaultValue = defaultValue; + } + + /// + /// If value contains no text, it will be replaced by a defaultValue. + /// + protected override string FilterValueToParse(string value) + { + if (!StringUtils.HasText(value)) + { + value = _defaultValue; + } + return value; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/Formatters/IntegerFormatter.cs b/src/Spring/Spring.Core/Globalization/Formatters/IntegerFormatter.cs new file mode 100644 index 00000000..12a91aff --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Formatters/IntegerFormatter.cs @@ -0,0 +1,97 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; + +using Spring.Util; + +namespace Spring.Globalization.Formatters +{ + /// + /// Implementation of that can be used to + /// format and parse integer numbers. + /// + /// + /// + /// This formatter allows you to format and parse numbers that conform + /// to number style (leading and trailing + /// white space, leading sign). + /// + /// + /// Aleksandar Seovic + /// $Id: IntegerFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public class IntegerFormatter : IFormatter + { + private string format = "{0:D}"; + + #region Constructors + + /// + /// Initializes a new instance of the class, + /// using default format string of '{0:D}'. + /// + public IntegerFormatter() + {} + + /// + /// Initializes a new instance of the class, + /// using specified format string. + /// + public IntegerFormatter(string format) + { + this.format = format; + } + + #endregion + + /// + /// Formats the specified integer value. + /// + /// The value to format. + /// Formatted integer number. + /// If is null. + /// If is not an integer number. + public string Format(object value) + { + AssertUtils.ArgumentNotNull(value, "value"); + if (!NumberUtils.IsInteger(value)) + { + throw new ArgumentException("IntegerFormatter can only be used to format integer numbers."); + } + + return String.Format(format, value); + } + + /// + /// Parses the specified integer value. + /// + /// The integer value to parse. + /// Parsed number value as a . + public object Parse(string value) + { + if (!StringUtils.HasText(value)) + { + return 0; + } + return Int32.Parse(value, NumberStyles.Integer); + } + } +} diff --git a/src/Spring/Spring.Core/Globalization/Formatters/NullFormatter.cs b/src/Spring/Spring.Core/Globalization/Formatters/NullFormatter.cs new file mode 100644 index 00000000..b11c5c52 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Formatters/NullFormatter.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Globalization.Formatters +{ + /// + /// Implementation of that simply calls . + /// + /// + /// This formatter is a no-operation implementation. + /// + /// Erich Eichinger + /// $Id: NullFormatter.cs,v 1.1 2007/05/19 00:23:18 oakinger Exp $ + public class NullFormatter : IFormatter + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public NullFormatter() + { + } + + #endregion + + /// + /// Converts the passed value to a string by calling . + /// + /// The value to convert. + /// to string converted value. + public string Format(object value) + { + return value != null ? value.ToString() : null; + } + + /// + /// Returns the passed string "as is". + /// + /// The value to return. + /// The value passed into this method. + public object Parse(string value) + { + return value; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/Formatters/NumberFormatter.cs b/src/Spring/Spring.Core/Globalization/Formatters/NumberFormatter.cs new file mode 100644 index 00000000..6fd0f2b6 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Formatters/NumberFormatter.cs @@ -0,0 +1,199 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; + +using Spring.Util; + +namespace Spring.Globalization.Formatters +{ + /// + /// Implementation of that can be used to + /// format and parse numbers. + /// + /// + /// + /// NumberFormatter uses number-related properties of the + /// to format and parse numbers. + /// + /// + /// This formatter works with both integer and decimal numbers and allows + /// you to format and parse numbers that conform to + /// number style (leading and trailing white space and/or sign, thousands separator, + /// decimal point) + /// + /// + /// If you use one of the constructors that accept culture as a parameter + /// to create an instance of NumberFormatter, default NumberFormatInfo + /// for the specified culture will be used. + /// + /// + /// You can also use properties exposed by the NumberFormatter in order + /// to override some of the default number formatting parameters. + /// + /// + /// Aleksandar Seovic + /// $Id: NumberFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public class NumberFormatter : IFormatter + { + private NumberFormatInfo formatInfo; + + #region Constructors + + /// + /// Initializes a new instance of the class + /// using default for the current thread's culture. + /// + public NumberFormatter() : this(CultureInfo.CurrentCulture) + {} + + /// + /// Initializes a new instance of the class + /// using default for the specified culture. + /// + /// The culture name. + public NumberFormatter(string cultureName) : this(CultureInfo.CreateSpecificCulture(cultureName)) + {} + + /// + /// Initializes a new instance of the class + /// using default for the specified culture. + /// + /// The culture. + public NumberFormatter(CultureInfo culture) + { + formatInfo = culture.NumberFormat; + } + + /// + /// Initializes a new instance of the class + /// using specified . + /// + /// + /// The instance that defines how + /// numbers are formatted and parsed. + /// + public NumberFormatter(NumberFormatInfo formatInfo) + { + this.formatInfo = formatInfo; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the number of decimal digits. + /// + /// The number of decimal digits. + /// + public int DecimalDigits + { + get { return formatInfo.NumberDecimalDigits; } + set { formatInfo.NumberDecimalDigits = value; } + } + + /// + /// Gets or sets the decimal separator. + /// + /// The decimal separator. + /// + public string DecimalSeparator + { + get { return formatInfo.NumberDecimalSeparator; } + set { formatInfo.NumberDecimalSeparator = value; } + } + + /// + /// Gets or sets the number group sizes. + /// + /// The number group sizes. + /// + public int[] GroupSizes + { + get { return formatInfo.NumberGroupSizes; } + set { formatInfo.NumberGroupSizes = value; } + } + + /// + /// Gets or sets the number group separator. + /// + /// The number group separator. + /// + public string GroupSeparator + { + get { return formatInfo.NumberGroupSeparator; } + set { formatInfo.NumberGroupSeparator = value; } + } + + /// + /// Gets or sets the negative pattern. + /// + /// The number negative pattern. + /// + public int NegativePattern + { + get { return formatInfo.NumberNegativePattern; } + set { formatInfo.NumberNegativePattern = value; } + } + + #endregion + + /// + /// Formats the specified number value. + /// + /// The value to format. + /// Formatted number . + /// If is null. + /// If is not a number. + public string Format(object value) + { + AssertUtils.ArgumentNotNull(value, "value"); + if (!NumberUtils.IsNumber(value)) + { + throw new ArgumentException("NumberFormatter can only be used to format numbers."); + } + + return String.Format(formatInfo, "{0:N}", value); + } + + /// + /// Parses the specified number value. + /// + /// The number value to parse. + /// Parsed number value as a . + public object Parse(string value) + { + if (!StringUtils.HasText(value)) + { + return 0d; + } + + NumberStyles numberStyle = NumberStyles.Number; + if (formatInfo.NumberNegativePattern == 0) + { + numberStyle |= NumberStyles.AllowParentheses; + } + + return Double.Parse(value, numberStyle, formatInfo); + } + } +} diff --git a/src/Spring/Spring.Core/Globalization/Formatters/PercentFormatter.cs b/src/Spring/Spring.Core/Globalization/Formatters/PercentFormatter.cs new file mode 100644 index 00000000..6c678c9a --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Formatters/PercentFormatter.cs @@ -0,0 +1,233 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; + +using Spring.Util; + +namespace Spring.Globalization.Formatters +{ + /// + /// Implementation of that can be used to + /// format and parse numbers. + /// + /// + /// + /// PercentFormatter uses percent-related properties of the + /// to format and parse percentages. + /// + /// + /// If you use one of the constructors that accept culture as a parameter + /// to create an instance of PercentFormatter, default NumberFormatInfo + /// for the specified culture will be used. + /// + /// + /// You can also use properties exposed by the PercentFormatter in order + /// to override some of the default number formatting parameters. + /// + /// + /// Aleksandar Seovic + /// $Id: PercentFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public class PercentFormatter : IFormatter + { + private static int[] positivePatterns = new int[] {3, 1, 0}; + private static int[] negativePatterns = new int[] {8, 5, 1}; + + private NumberFormatInfo formatInfo; + + #region Constructors + + /// + /// Initializes a new instance of the class + /// using default for the current thread's culture. + /// + public PercentFormatter() : this(CultureInfo.CurrentCulture) + {} + + /// + /// Initializes a new instance of the class + /// using default for the specified culture. + /// + /// The culture name. + public PercentFormatter(string cultureName) : this(CultureInfo.CreateSpecificCulture(cultureName)) + {} + + /// + /// Initializes a new instance of the class + /// using default for the specified culture. + /// + /// The culture. + public PercentFormatter(CultureInfo culture) + { + formatInfo = culture.NumberFormat; + } + + /// + /// Initializes a new instance of the class + /// using specified . + /// + /// + /// The instance that defines how + /// numbers are formatted and parsed. + /// + public PercentFormatter(NumberFormatInfo formatInfo) + { + this.formatInfo = formatInfo; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the number of decimal digits. + /// + /// The number of decimal digits. + /// + public int DecimalDigits + { + get { return formatInfo.PercentDecimalDigits; } + set { formatInfo.PercentDecimalDigits = value; } + } + + /// + /// Gets or sets the decimal separator. + /// + /// The decimal separator. + /// + public string DecimalSeparator + { + get { return formatInfo.PercentDecimalSeparator; } + set { formatInfo.PercentDecimalSeparator = value; } + } + + /// + /// Gets or sets the percent group sizes. + /// + /// The percent group sizes. + /// + public int[] GroupSizes + { + get { return formatInfo.PercentGroupSizes; } + set { formatInfo.PercentGroupSizes = value; } + } + + /// + /// Gets or sets the percent group separator. + /// + /// The percent group separator. + /// + public string GroupSeparator + { + get { return formatInfo.PercentGroupSeparator; } + set { formatInfo.PercentGroupSeparator = value; } + } + + /// + /// Gets or sets the negative pattern. + /// + /// The percent negative pattern. + /// + public int NegativePattern + { + get { return formatInfo.PercentNegativePattern; } + set { formatInfo.PercentNegativePattern = value; } + } + + /// + /// Gets or sets the positive pattern. + /// + /// The percent positive pattern. + /// + public int PositivePattern + { + get { return formatInfo.PercentPositivePattern; } + set { formatInfo.PercentPositivePattern = value; } + } + + /// + /// Gets or sets the percent symbol. + /// + /// The percent symbol. + /// + public string PercentSymbol + { + get { return formatInfo.PercentSymbol; } + set { formatInfo.PercentSymbol = value; } + } + + /// + /// Gets or sets the per mille symbol. + /// + /// The per mille symbol. + /// + public string PerMilleSymbol + { + get { return formatInfo.PerMilleSymbol; } + set { formatInfo.PerMilleSymbol = value; } + } + + #endregion + + /// + /// Formats the specified percentage value. + /// + /// The value to format. + /// Formatted percentage. + /// If is null. + /// If is not a number. + public string Format(object value) + { + AssertUtils.ArgumentNotNull(value, "value"); + if (!NumberUtils.IsNumber(value)) + { + throw new ArgumentException("PercentFormatter can only be used to format numbers."); + } + + return String.Format(formatInfo, "{0:P}", value); + } + + /// + /// Parses the specified percentage value. + /// + /// The percentage value to parse. + /// Parsed percentage value as a . + public object Parse(string value) + { + if (!StringUtils.HasText(value)) + { + return 0d; + } + + // there is no percentage parser in .NET, so we use currency parser to achieve the goal + NumberFormatInfo fi = (NumberFormatInfo) formatInfo.Clone(); + fi.CurrencyDecimalDigits = formatInfo.PercentDecimalDigits; + fi.CurrencyDecimalSeparator = formatInfo.PercentDecimalSeparator; + fi.CurrencyGroupSeparator = formatInfo.PercentGroupSeparator; + fi.CurrencyGroupSizes = formatInfo.PercentGroupSizes; + fi.CurrencyNegativePattern = negativePatterns[formatInfo.PercentNegativePattern]; + fi.CurrencyPositivePattern = positivePatterns[formatInfo.PercentPositivePattern]; + fi.CurrencySymbol = formatInfo.PercentSymbol; + + return Double.Parse(value, NumberStyles.Currency, fi) / 100; + } + } +} diff --git a/src/Spring/Spring.Core/Globalization/ICultureResolver.cs b/src/Spring/Spring.Core/Globalization/ICultureResolver.cs new file mode 100644 index 00000000..d86facb2 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/ICultureResolver.cs @@ -0,0 +1,71 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Globalization; + +#endregion + +namespace Spring.Globalization +{ + /// + /// Strategy interface for + /// resolution. + /// + /// Aleksandar Seovic + /// $Id: ICultureResolver.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public interface ICultureResolver + { + /// + /// Resolves the + /// from some context. + /// + /// + ///

    + /// The 'context' is determined by the appropriate implementation class. + /// An example of such a context might be a thread local bound + /// , or a + /// sourced from an HTTP + /// session. + ///

    + ///
    + /// + /// The that should be used + /// by the caller. + /// + CultureInfo ResolveCulture(); + + /// + /// Sets the . + /// + /// + ///

    + /// This is an optional operation and does not need to be implemented + /// such that it actually does anything useful (i.e. it can be a no-op). + ///

    + ///
    + /// + /// The new or + /// to clear the current . + /// + void SetCulture(CultureInfo culture); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/IFormatter.cs b/src/Spring/Spring.Core/Globalization/IFormatter.cs new file mode 100644 index 00000000..a51a0eec --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/IFormatter.cs @@ -0,0 +1,51 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Globalization +{ + /// + /// Interface that should be implemented by all formatters. + /// + /// + /// + /// Formatters assume that source value is a string, and make no assumptions + /// about the target value's type, which means that Parse method can return + /// object of any type. + /// + /// + /// Aleksandar Seovic + /// $Id: IFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public interface IFormatter + { + /// + /// Formats the specified value. + /// + /// The value to format. + /// Formatted . + string Format(object value); + + /// + /// Parses the specified value. + /// + /// The value to parse. + /// Parsed . + object Parse(string value); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/ILocalizer.cs b/src/Spring/Spring.Core/Globalization/ILocalizer.cs new file mode 100644 index 00000000..7e41875a --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/ILocalizer.cs @@ -0,0 +1,61 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Globalization; + +using Spring.Context; + +namespace Spring.Globalization +{ + /// + /// Defines an interface that localizers have to implement. + /// + /// + ///

    + /// Localizers are used to automatically apply resources to object's members + /// using reflection. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ILocalizer.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public interface ILocalizer + { + /// + /// Gets or sets the resource cache instance. + /// + /// The resource cache instance. + IResourceCache ResourceCache { get; set; } + + /// + /// Applies resources of the specified culture to the specified target object. + /// + /// Target object to apply resources to. + /// instance to retrieve resources from. + /// Resource culture to use for resource lookup. + void ApplyResources(object target, IMessageSource messageSource, CultureInfo culture); + + /// + /// Applies resources to the specified target object, using current thread's culture to resolve resources. + /// + /// Target object to apply resources to. + /// instance to retrieve resources from. + void ApplyResources(object target, IMessageSource messageSource); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/IResourceCache.cs b/src/Spring/Spring.Core/Globalization/IResourceCache.cs new file mode 100644 index 00000000..5a171804 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/IResourceCache.cs @@ -0,0 +1,50 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Globalization; + +namespace Spring.Globalization +{ + /// + /// Defines an interface that resource cache adapters have to implement. + /// + /// Aleksandar Seovic + /// $Id: IResourceCache.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public interface IResourceCache + { + /// + /// Gets the list of resources from cache. + /// + /// Target to get a list of resources for. + /// Resource culture. + /// A list of cached resources for the specified target object and culture. + IList GetResources(object target, CultureInfo culture); + + /// + /// Puts the list of resources in the cache. + /// + /// Target to cache a list of resources for. + /// Resource culture. + /// A list of resources to cache. + /// A list of cached resources for the specified target object and culture. + void PutResources(object target, CultureInfo culture, IList resources); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/Localizers/ResourceSetLocalizer.cs b/src/Spring/Spring.Core/Globalization/Localizers/ResourceSetLocalizer.cs new file mode 100644 index 00000000..f1e13f4c --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Localizers/ResourceSetLocalizer.cs @@ -0,0 +1,112 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Globalization; +using System.Resources; +using Common.Logging; +using Spring.Context; +using Spring.Context.Support; +using Spring.Expressions; + +namespace Spring.Globalization.Localizers +{ + /// + /// Loads a list of resources that should be applied from the .NET . + /// + /// + ///

    + /// This implementation will iterate over all resource managers + /// within the message source and return a list of all the resources whose name starts with '$this'. + ///

    + ///

    + /// All other resources will be ignored, but you can retrieve them by calling one of + /// GetMessage methods on the message source directly. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ResourceSetLocalizer.cs,v 1.11 2007/07/24 17:26:25 oakinger Exp $ + public class ResourceSetLocalizer : AbstractLocalizer + { + private static readonly ILog log = LogManager.GetLogger(typeof(ResourceSetLocalizer)); + + private static readonly IList ignoreList = + new string[] {"$this.DefaultModifiers", "$this.TrayAutoArrange", "$this.TrayLargeIcon"}; + + /// + /// Loads resources from the storage and creates a list of instances that should be applied to the target. + /// + /// + /// This feature is not currently supported on version 1.0 of the .NET platform. + /// + /// Target to get a list of resources for. + /// instance to retrieve resources from. + /// Resource locale. + /// A list of resources to apply. + protected override IList LoadResources(object target, IMessageSource messageSource, CultureInfo culture) + { +#if ! NET_1_0 + IList resources; + resources = new ArrayList(); + + if (messageSource is ResourceSetMessageSource) + { + for (int i = 0; i < ((ResourceSetMessageSource) messageSource).ResourceManagers.Count; i++) + { + ResourceManager rm = ((ResourceSetMessageSource) messageSource).ResourceManagers[i] as ResourceManager; + ResourceSet invariantResources = null; + try + { + invariantResources = rm.GetResourceSet(CultureInfo.InvariantCulture, true, true); + } + catch (MissingManifestResourceException mmrex) + { + // ignore but log missing ResourceSet + log.Debug("No ResourceSet available for invariant culture", mmrex); + } + + if (invariantResources != null) + { + foreach (DictionaryEntry resource in invariantResources) + { + string resourceName = (string)resource.Key; + if (resourceName.StartsWith("$this") && !ignoreList.Contains(resourceName)) + { + // redirect resource resolution if necessary + object resourceValue = rm.GetObject(resourceName, culture); + if (resourceValue is String && ((String)resourceValue).StartsWith("$messageSource")) + { + resourceValue = messageSource.GetResourceObject(((String)resourceValue).Substring(15), culture); + } + resources.Add(new Resource(Expression.ParsePrimary(resourceName.Substring(6)), resourceValue)); + } + } + } + } + } + return resources; +#else + throw new NotSupportedException("Operation not supported in .NET 1.0 Release."); +#endif + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/NullResourceCache.cs b/src/Spring/Spring.Core/Globalization/NullResourceCache.cs new file mode 100644 index 00000000..d7f58477 --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/NullResourceCache.cs @@ -0,0 +1,50 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.Globalization +{ + /// + /// Resource cache implementation that doesn't cache resources. + /// + /// Aleksandar Seovic + /// $Id: NullResourceCache.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public class NullResourceCache : AbstractResourceCache + { + /// + /// Gets the list of resources from cache. + /// + /// Cache key to use for lookup. + /// Always returns null. + protected override IList GetResources(string cacheKey) + { + return null; + } + + /// + /// Puts the list of resources in the cache. + /// + /// Cache key to use for the specified resources. + /// A list of resources to cache. + protected override void PutResources(string cacheKey, IList resources) + {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/Resolvers/DefaultCultureResolver.cs b/src/Spring/Spring.Core/Globalization/Resolvers/DefaultCultureResolver.cs new file mode 100644 index 00000000..df2a064c --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Resolvers/DefaultCultureResolver.cs @@ -0,0 +1,123 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Globalization; +using System.Threading; + +#endregion + +namespace Spring.Globalization.Resolvers +{ + /// + /// implementation + /// that simply returns the + /// value of the + /// + /// property (if said property value is not ), or the + /// of the current thread if it is + /// . + /// + /// Aleksandar Seovic + /// $Id: DefaultCultureResolver.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public class DefaultCultureResolver : ICultureResolver + { + private CultureInfo defaultCulture; + + /// + /// The default . + /// + /// + /// The default . + /// + public CultureInfo DefaultCulture + { + get { return defaultCulture; } + set { defaultCulture = value; } + } + + /// + /// Returns the default . + /// + /// + ///

    + /// It tries to get the + /// from the value of the + /// + /// property and falls back to the of the + /// current thread if the + /// + /// is . + ///

    + ///
    + /// + /// The default + /// + protected virtual CultureInfo GetDefaultLocale() + { + if (defaultCulture != null) + { + return defaultCulture; + } + else + { + return Thread.CurrentThread.CurrentUICulture; + } + } + + /// + /// Resolves the + /// from some context. + /// + /// + ///

    + /// The 'context' in this implementation is the + /// value of the + /// + /// property (if said property value is not ), or the + /// of the current thread if it is + /// . + ///

    + ///
    + /// + /// The that should be used + /// by the caller. + /// + public virtual CultureInfo ResolveCulture() + { + return GetDefaultLocale(); + } + + /// + /// Sets the . + /// + /// + /// The new or + /// to clear the current . + /// + /// + /// + public virtual void SetCulture(CultureInfo culture) + { + defaultCulture = culture; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Globalization/Resource.cs b/src/Spring/Spring.Core/Globalization/Resource.cs new file mode 100644 index 00000000..98fc094f --- /dev/null +++ b/src/Spring/Spring.Core/Globalization/Resource.cs @@ -0,0 +1,67 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using Spring.Expressions; + +namespace Spring.Globalization +{ + /// + /// Holds mapping between control property and it's value + /// as read from the resource file. + /// + /// Aleksandar Seovic + /// $Id: Resource.cs,v 1.3 2006/04/09 07:18:47 markpollack Exp $ + [Serializable] + public class Resource + { + private IExpression target; + private object resourceValue; + + /// + /// Creates instance of resource mapper. + /// + /// Target property. + /// Resource value. + public Resource(IExpression target, object resourceValue) + { + this.target = target; + this.resourceValue = resourceValue; + } + + /// + /// Gets parsed target property expression. See + /// for more information on object navigation expressions. + /// + public IExpression Target + { + get { return target; } + } + + /// + /// Value of the resource that target property should be set to. + /// + public object Value + { + get { return resourceValue; } + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Events/IEventRegistry.cs b/src/Spring/Spring.Core/Objects/Events/IEventRegistry.cs new file mode 100644 index 00000000..382ce538 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Events/IEventRegistry.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Objects.Events +{ + /// + /// A registry that manages subscriptions to and the + /// publishing of events. + /// + /// Griffin Caprio + /// $Id: IEventRegistry.cs,v 1.5 2006/04/09 07:18:47 markpollack Exp $ + public interface IEventRegistry + { + /// + /// Publishes all events of the source object. + /// + /// + /// The source object containing events to publish. + /// + void PublishEvents(object sourceObject); + + /// + /// Subscribes to all events published, if the subscriber + /// implements compatible handler methods. + /// + /// The subscriber to use. + void Subscribe(object subscriber); + + /// + /// Subscribes to the published events of all objects of a given + /// , if the subscriber implements + /// compatible handler methods. + /// + /// The subscriber to use. + /// + /// The target to subscribe to. + /// + void Subscribe(object subscriber, Type targetSourceType); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Events/IEventRegistryAware.cs b/src/Spring/Spring.Core/Objects/Events/IEventRegistryAware.cs new file mode 100644 index 00000000..2037464e --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Events/IEventRegistryAware.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Events +{ + /// + /// To be implemented by any object that wishes to receive a reference to + /// an . + /// + /// + ///

    + /// This interface only applies to objects that have been instantiated + /// within the context of an + /// . This interface does + /// not typically need to be implemented by application code, but is rather + /// used by classes internal to Spring.NET. + ///

    + ///
    + /// Mark Pollack + /// Rick Evans + /// $Id: IEventRegistryAware.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + public interface IEventRegistryAware + { + /// + /// Set the + /// associated with the + /// that created this + /// object. + /// + /// + ///

    + /// This property will be set by the relevant + /// after all of this + /// object's dependencies have been resolved. This object can use the + /// supplied + /// immediately to publish or subscribe to one or more events. + ///

    + ///
    + IEventRegistry EventRegistry { set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Events/Support/EventManipulationUtils.cs b/src/Spring/Spring.Core/Objects/Events/Support/EventManipulationUtils.cs new file mode 100644 index 00000000..8eca6c13 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Events/Support/EventManipulationUtils.cs @@ -0,0 +1,120 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Core; +using Spring.Objects.Support; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Events.Support +{ + /// + /// Utility class to aid in the manipulation of events and delegates. + /// + /// Griffin Caprio + /// $Id: EventManipulationUtils.cs,v 1.3 2007/07/31 01:35:12 markpollack Exp $ + public sealed class EventManipulationUtils + { + /// + /// Returns a new instance of the requested . + /// + /// + ///

    + /// Often used to wire subscribers to event publishers. + ///

    + ///
    + /// + /// The of delegate to create. + /// + /// + /// The target subscriber object that contains the delegate implementation. + /// + /// + /// referencing the delegate method on the subscriber. + /// + /// + /// A delegate handler that can be added to an events list of handlers, or called directly. + /// + public static Delegate GetHandlerDelegate( + Type delegateType, object targetSubscriber, MethodInfo targetSubscriberDelegateMethod) + { + return Delegate.CreateDelegate( + delegateType, targetSubscriber, targetSubscriberDelegateMethod.Name); + } + + /// + /// Queries the input type for a signature matching the input + /// signature. + /// + /// + /// Typically used to query a potential subscriber to see if they implement an event handler. + /// + /// to match against + /// to query + /// + /// matching input + /// signature, or if there is no match. + /// + public static MethodInfo GetMethodInfoMatchingSignature( + MethodInfo invoke, Type subscriberType) + { + ComposedCriteria criteria = new ComposedCriteria(); + criteria.Add(new MethodReturnTypeCriteria(invoke.ReturnType)); + criteria.Add(new MethodParametersCountCriteria(invoke.GetParameters().Length)); + criteria.Add(new MethodParametersCriteria(ReflectionUtils.GetParameterTypes(invoke))); + + MemberInfo[] methods = subscriberType.FindMembers( + MemberTypes.Method, ReflectionUtils.AllMembersCaseInsensitiveFlags, + new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria), + criteria); + if (methods != null + && methods.Length > 0) + { + return methods[0] as MethodInfo; + } + return null; + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the EventManipulationUtilities class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly visible constructors. + ///

    + ///
    + private EventManipulationUtils() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Events/Support/EventRegistry.cs b/src/Spring/Spring.Core/Objects/Events/Support/EventRegistry.cs new file mode 100644 index 00000000..edf21d49 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Events/Support/EventRegistry.cs @@ -0,0 +1,125 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; + +#endregion + +namespace Spring.Objects.Events.Support +{ + /// + /// Default implementation of the + /// interface. + /// + /// Griffin Caprio + /// $Id: EventRegistry.cs,v 1.7 2006/04/09 07:18:47 markpollack Exp $ + public class EventRegistry : IEventRegistry + { + private IList _publishers; + + /// + /// Creates a new instance of the EventRegistry class. + /// + public EventRegistry() + { + _publishers = new ArrayList(); + } + + /// + /// The list of event publishers. + /// + /// The list of event publishers. + protected IList Publishers + { + get { return _publishers; } + } + + /// + /// Adds the input object to the list of publishers. + /// + /// + /// This publishes all events of the source object to any object + /// wishing to subscribe + /// + /// The source object to publish. + public virtual void PublishEvents(object source) + { + Publishers.Add(source); + } + + /// + /// Subscribes to all events published, if the subscriber implements + /// compatible handler methods. + /// + /// The subscriber to use. + public virtual void Subscribe(object subscriber) + { + Subscribe(subscriber, null); + } + + /// + /// Subscribes to published events of all objects of a given type, if the + /// subscriber implements compatible handler methods. + /// + /// The subscriber to use. + /// + /// The target to subscribe to. + /// + public virtual void Subscribe(object subscriber, Type sourceType) + { + Type currentSubscriberType = subscriber.GetType(); + foreach (object currentPublisher in _publishers) + { + if (null == sourceType + || sourceType.IsAssignableFrom(currentPublisher.GetType())) + { + wireSubscriberToPublisher( + currentPublisher, currentSubscriberType, subscriber); + } + } + } + + private static void wireSubscriberToPublisher( + object currentPublisher, Type currentSubscriberType, object subscriber) + { + Type currentPublisherType = currentPublisher.GetType(); + EventInfo[] events = currentPublisherType.GetEvents(); + foreach (EventInfo currentEvent in events) + { + Type eventHandlerType = currentEvent.EventHandlerType; + MethodInfo invoke = eventHandlerType.GetMethod("Invoke"); + MethodInfo eventHandler + = EventManipulationUtils.GetMethodInfoMatchingSignature( + invoke, currentSubscriberType); + if (eventHandler != null) + { + currentEvent.AddEventHandler( + currentPublisher, + EventManipulationUtils.GetHandlerDelegate( + eventHandlerType, subscriber, eventHandler)); + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Attributes/RequiredAttribute.cs b/src/Spring/Spring.Core/Objects/Factory/Attributes/RequiredAttribute.cs new file mode 100644 index 00000000..29a66e32 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Attributes/RequiredAttribute.cs @@ -0,0 +1,40 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Objects.Factory.Attributes +{ + /// + /// Marks a property as being 'required': that is, the setter property + /// must be configured to be dependency-injected with a value. + /// + /// Consult the SDK documentation for , + /// which, by default, checks for the presence of this annotation. + /// + /// Rob Harrop + /// Mark Pollack + /// $Id: RequiredAttribute.cs,v 1.1 2008/04/02 18:02:24 markpollack Exp $ + [AttributeUsage(AttributeTargets.Property, Inherited = true)] + public class RequiredAttribute : Attribute + { + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Attributes/RequiredAttributeObjectPostProcessor.cs b/src/Spring/Spring.Core/Objects/Factory/Attributes/RequiredAttributeObjectPostProcessor.cs new file mode 100644 index 00000000..e76c1e99 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Attributes/RequiredAttributeObjectPostProcessor.cs @@ -0,0 +1,186 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +using System; +using System.Collections; +using System.Reflection; +using System.Text; +using Spring.Collections; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Attributes; +using Spring.Objects.Factory.Config; +using Spring.Util; + +namespace Spring.Objects.Factory.Attributes +{ + /// + /// A implementation that enforces required properties to have been configured. + /// Required properties are detected through an attribute, by default, Spring's + /// attribute. + /// + /// + /// The motivation for the existence of this IObjectPostProcessor is to allow + /// developers to annotate the setter properties of their own classes with an + /// arbitrary attribute to indicate that the container must check + /// for the configuration of a dependency injected value. This neatly pushes + /// responsibility for such checking onto the container (where it arguably belongs), + /// and obviates the need (in part) for a developer to code a method that + /// simply checks that all required properties have actually been set. + /// + /// Please note that an 'init' method may still need to implemented (and may + /// still be desirable), because all that this class does is enforce that a + /// 'required' property has actually been configured with a value. It does + /// not check anything else... In particular, it does not check that a + /// configured value is not null. + /// + /// + /// Rob Harrop + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: RequiredAttributeObjectPostProcessor.cs,v 1.2 2008/04/08 19:29:07 markpollack Exp $ + public class RequiredAttributeObjectPostProcessor : InstantiationAwareObjectPostProcessorAdapter + { + private Type requiredAttributeType = typeof (RequiredAttribute); + + /// + /// Cache for validated object names, skipping re-validation for the same object + /// + private ISet validatedObjectNames = new SynchronizedSet(new HashedSet()); + + /// + /// Sets the type of the required attribute, to be used on a property setter + /// + /// + /// The default required attribute type is the Spring-provided attribute. + /// This setter property exists so that developers can provide their own + /// (non-Spring-specific) annotation type to indicate that a property value is required. + /// + /// The type of the required attribute. + public Type RequiredAttributeType + { + set + { + AssertUtils.ArgumentNotNull(value, "RequiredAttributeType", "RequiredAttributeType property must not be null"); + if (!typeof(Attribute).IsAssignableFrom(value)) + { + throw new ArgumentException( + string.Format("[{0}] does not inherit from System.Attribute (it must).", value.FullName)); + } + requiredAttributeType = value; + } + get + { + return requiredAttributeType; + } + } + + + /// + /// Post-process the given property values before the factory applies them + /// to the given object. Checks for the attribute specified by this PostProcessor's RequiredAttributeType. + /// + /// The property values that the factory is about to apply (never null). + /// The relevant property infos for the target object (with ignored + /// dependency types - which the factory handles specifically - already filtered out) + /// The object instance created, but whose properties have not yet + /// been set. + /// Name of the object. + /// + /// The actual property values to apply to the given object (can be the + /// passed-in PropertyValues instances or null to skip property population. + /// + /// If a required property value has not been specified + /// in the configuration metadata. + public override IPropertyValues PostProcessPropertyValues(IPropertyValues pvs, PropertyInfo[] pis, object objectInstance, + string objectName) + { + if (!validatedObjectNames.Contains(objectName)) + { + ArrayList invalidProperties = new ArrayList(); + + foreach (PropertyInfo pi in pis) + { + if (IsRequiredProperty(pi) && !pvs.Contains(pi.Name)) + { + invalidProperties.Add(pi.Name); + } + } + if (invalidProperties.Count != 0) + { + throw new ObjectInitializationException( + BuildExceptionMessage((string[]) invalidProperties.ToArray(typeof (string)), objectName)); + } + validatedObjectNames.Add(objectName); + } + return pvs; + } + + /// + /// Determines whether the supplied property is required to have a value, that is to be dependency injected. + /// + /// + /// This implementation looks for the existence of a "required" attribute on the supplied PropertyInfo and that + /// the property has a setter method. + /// + /// The target PropertyInfo + /// + /// true if the supplied property has been marked as being required;; otherwise, false if + /// not or if the supplied property does not have a setter method + /// + protected virtual bool IsRequiredProperty(PropertyInfo pi) + { + return (pi.GetSetMethod() != null && pi.GetCustomAttributes(RequiredAttributeType, true).Length > 0); + } + + /// + /// Builds an exception message for the given list of invalid properties. + /// + /// The list of names of invalid properties. + /// Name of the object. + /// The exception message + private string BuildExceptionMessage(string[] invalidProperties, ICloneable objectName) + { + int size = invalidProperties.Length; + StringBuilder sb = new StringBuilder(); + sb.Append(size == 1 ? "Property" : "Properties"); + for (int i=0; i < size; i++) + { + string propName = invalidProperties[i]; + if (i > 0) + { + if (i == (size -1 )) + { + sb.Append(" and"); + } + else + { + sb.Append(", "); + } + } + sb.Append(" '").Append(propName).Append("'"); + } + sb.Append(" required for object '").Append(objectName).Append("'"); + return sb.ToString(); + + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/AbstractConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/AbstractConfigurer.cs new file mode 100644 index 00000000..e7642b6f --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/AbstractConfigurer.cs @@ -0,0 +1,123 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using Spring.Core; +using Spring.Core.TypeResolution; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Base class that provides common functionality needed for several IObjectFactoryPostProcessor + /// implementations + /// + /// Mark Pollack + /// $Id: AbstractConfigurer.cs,v 1.3 2007/07/31 18:16:49 bbaia Exp $ + [Serializable] + public abstract class AbstractConfigurer : IOrdered, IObjectFactoryPostProcessor + { + private int order = Int32.MaxValue; // default: same as non-Ordered + + /// + /// Return the order value of this object, with a higher value meaning + /// greater in terms of sorting. + /// + /// The order value. + /// + public int Order + { + get { return order; } + set { order = value; } + } + + /// + /// Modify the application context's internal object factory after its + /// standard initialization. + /// + /// The object factory used by the application context. + /// + ///

    + /// All object definitions will have been loaded, but no objects will have + /// been instantiated yet. This allows for overriding or adding properties + /// even to eager-initializing objects. + ///

    + ///
    + /// + /// In case of errors. + /// + public abstract void PostProcessObjectFactory( + IConfigurableListableObjectFactory factory); + + /// + /// Resolves the supplied into a + /// instance. + /// + /// The object that is to be resolved into a + /// instance. + /// The error context source. + /// The error context string. + /// A resolved . + /// + ///

    + /// This (default) implementation supports resolving + /// s and s. + /// Only override this method if you want to key your type alias + /// on something other than s + /// and s. + ///

    + ///
    + /// + /// If the supplied is , + /// or the supplied cannot be resolved. + /// + protected virtual Type ResolveRequiredType(object value, string errorContextSource, string errorContext) + { + Type requiredType = value as Type; + if (requiredType == null) + { + string typeName = value as string; + if (typeName != null) + { + try + { + requiredType = TypeResolutionUtils.ResolveType(typeName); + } + catch (TypeLoadException ex) + { + throw new ObjectInitializationException( + string.Format( + "Could not load required type [{0}] for {1}.", + typeName, errorContext), ex); + } + } + else + { + throw new ObjectInitializationException( + string.Format( + "Invalid value '{0}' for {1} - " + + "must be a System.String or System.Type.", + value, errorContext)); + } + } + return requiredType; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/AbstractFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/AbstractFactoryObject.cs new file mode 100644 index 00000000..e5f71ce4 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/AbstractFactoryObject.cs @@ -0,0 +1,163 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Simple template superclass for + /// implementations that allows for the creation of a singleton or a prototype + /// instance (depending on a flag). + /// + /// + /// If the value of the + /// + /// property is (this is the default), this class + /// will create a single instance of it's object upon initialization and + /// subsequently return the singleton instance; else, this class will + /// create a new instance each time (prototype mode). Subclasses must + /// implement the + /// + /// template method to actually create objects. + /// + /// Juergen Hoeller + /// Keith Donald + /// Simon White (.NET) + /// $Id: AbstractFactoryObject.cs,v 1.13 2007/03/16 04:01:34 aseovic Exp $ + [Serializable] + public abstract class AbstractFactoryObject : IFactoryObject, IInitializingObject, IDisposable + { + private bool isSingleton = true; + private object singletonInstance; + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + /// + ///

    + /// Please note that changing the value of this property after + /// this factory object instance has been created by an enclosing + /// Spring.NET IoC container really is a programming error. This + /// property should really only be set once, prior to the invocation + /// of the + /// + /// callback method. + ///

    + ///
    + /// + public bool IsSingleton + { + get { return this.isSingleton; } + set { this.isSingleton = value; } + } + + /// + /// Return the of object that this + /// creates, or + /// if not known in advance. + /// + /// + public abstract Type ObjectType { get; } + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + /// + public virtual void AfterPropertiesSet() + { + if (this.isSingleton && this.singletonInstance == null) + { + this.singletonInstance = CreateInstance(); + } + } + + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + /// An instance (possibly shared or independent) of the object managed by + /// this factory. + /// + /// + public object GetObject() + { + if (this.isSingleton) + { + return this.singletonInstance; + } + else + { + return CreateInstance(); + } + } + + /// + /// Template method that subclasses must override to construct + /// the object returned by this factory. + /// + /// + /// Invoked once immediately after the initialization of this + /// in the case of + /// a singleton; else, on each call to the + /// + /// method. + /// + /// + /// If an exception occured during object creation. + /// + /// + /// A distinct instance of the object created by this factory. + /// + protected abstract object CreateInstance(); + + /// + /// Performs cleanup on any cached singleton object. + /// + /// + ///

    + /// Only makes sense in the context of a singleton object. + ///

    + ///
    + /// + /// + public virtual void Dispose() + { + if (this.isSingleton && this.singletonInstance != null) + { + IDisposable disposableSingletonInstance = this.singletonInstance as IDisposable; + if (disposableSingletonInstance != null) + { + disposableSingletonInstance.Dispose(); + } + } + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/AutoWiringMode.cs b/src/Spring/Spring.Core/Objects/Factory/Config/AutoWiringMode.cs new file mode 100644 index 00000000..ef9a0403 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/AutoWiringMode.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Objects.Factory.Config +{ + + /// + /// The various autowiring modes. + /// + /// Rick Evans + /// $Id: AutoWiringMode.cs,v 1.8 2007/03/16 04:01:34 aseovic Exp $ + [Serializable] + public enum AutoWiringMode + { + /// + /// Do not autowire. + /// + No = 0, + + /// + /// Autowire by name. + /// + ByName = 1, + + /// + /// Autowire by . + /// + ByType = 2, + + /// + /// Autowiring by constructor. + /// + Constructor = 3, + + /// + /// The autowiring strategy is to be determined by introspection + /// of the object's . + /// + AutoDetect = 4 + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/CommandLineArgsVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/CommandLineArgsVariableSource.cs new file mode 100644 index 00000000..57a8fe39 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/CommandLineArgsVariableSource.cs @@ -0,0 +1,128 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Collections.Specialized; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Implementation of that + /// resolves variable name against command line arguments. + /// + /// Aleksandar Seovic + /// $Id: CommandLineArgsVariableSource.cs,v 1.3 2007/08/02 22:18:32 markpollack Exp $ + [Serializable] + public class CommandLineArgsVariableSource : IVariableSource + { + private const string DEFAULT_ARG_PREFIX = "/"; + private const string DEFAULT_VALUE_SEPARATOR = ":"; + + private string argumentPrefix = DEFAULT_ARG_PREFIX; + private string valueSeparator = DEFAULT_VALUE_SEPARATOR; + + private string[] commandLineArgs; + private IDictionary arguments; + + /// + /// Default constructor. + /// Initializes command line arguments from the environment. + /// + public CommandLineArgsVariableSource() + { + this.commandLineArgs = Environment.GetCommandLineArgs(); + } + + /// + /// Constructor that allows arguments to be passed externally. + /// Useful for testing. + /// + public CommandLineArgsVariableSource(string[] commandLineArgs) + { + this.commandLineArgs = commandLineArgs; + } + + /// + /// Gets or sets a prefix that should be used to + /// identify arguments to extract values from. + /// + /// + /// A prefix that should be used to identify arguments + /// to extract values from. Defaults to slash ("/"). + /// + public string ArgumentPrefix + { + get { return argumentPrefix; } + set { argumentPrefix = value; } + } + + /// + /// Gets or sets a character that should be used to + /// separate argument name from its value. + /// + /// + /// A character that should be used to separate argument + /// name from its value. Defaults to colon (":"). + /// + public string ValueSeparator + { + get { return valueSeparator; } + set { valueSeparator = value; } + } + + /// + /// Resolves variable value for the specified variable name. + /// + /// + /// The name of the variable to resolve. + /// + /// + /// The variable value if able to resolve, null otherwise. + /// + public string ResolveVariable(string name) + { + if (arguments == null) + { + InitArguments(); + } + return (string) this.arguments[name]; + } + + /// + /// Initializes command line arguments dictionary. + /// + private void InitArguments() + { + this.arguments = CollectionsUtil.CreateCaseInsensitiveHashtable(commandLineArgs.Length); + + foreach (string arg in commandLineArgs) + { + int separatorIndex = arg.IndexOf(valueSeparator); + if (arg.StartsWith(argumentPrefix) && separatorIndex > argumentPrefix.Length) + { + string argName = arg.Substring(argumentPrefix.Length, separatorIndex - argumentPrefix.Length); + string argValue = arg.Substring(separatorIndex + valueSeparator.Length); + this.arguments[argName] = argValue; + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ConfigSectionVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ConfigSectionVariableSource.cs new file mode 100644 index 00000000..108f2b7c --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ConfigSectionVariableSource.cs @@ -0,0 +1,136 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections.Specialized; +using System.Configuration; +using Spring.Util; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Implementation of that + /// resolves variable name against name-value sections in + /// the standard .NET configuration file. + /// + /// Aleksandar Seovic + /// $Id: ConfigSectionVariableSource.cs,v 1.4 2007/08/22 20:16:27 oakinger Exp $ + [Serializable] + public class ConfigSectionVariableSource : IVariableSource + { + private string[] sectionNames; + private NameValueCollection variables; + + /// + /// Initializes a new instance of + /// + public ConfigSectionVariableSource() + { + } + + /// + /// Initializes a new instance of from the given + /// + public ConfigSectionVariableSource(string sectionName) + { + this.SectionName = sectionName; + } + + /// + /// Initializes a new instance of from the given + /// + public ConfigSectionVariableSource(string[] sectionNames) + { + this.SectionNames = sectionNames; + } + + /// + /// Gets or sets a list of section names variables should be loaded from. + /// + /// + /// All sections specified need to be handled by the + /// in order to be processed successfully. + /// + /// + /// A list of section names variables should be loaded from. + /// + public string[] SectionNames + { + get { return sectionNames; } + set { sectionNames = value; } + } + + /// + /// Convinience property. Gets or sets a single section + /// to read properties from. + /// + /// + /// The section specified needs to be handled by the + /// in order to be processed successfully. + /// + /// + /// A section to read properties from. + /// + public string SectionName + { + set { sectionNames = new string[] { value }; } + } + + /// + /// Resolves variable value for the specified variable name. + /// + /// + /// The name of the variable to resolve. + /// + /// + /// The variable value if able to resolve, null otherwise. + /// + public string ResolveVariable(string name) + { + if (variables == null) + { + InitVariables(); + } + return variables.Get(name); + } + + /// + /// Initializes properties based on the specified + /// property file locations. + /// + private void InitVariables() + { + variables = new NameValueCollection(); + foreach (string sectionName in sectionNames) + { + object section = ConfigurationUtils.GetSection(sectionName); + if (section is NameValueCollection) + { + variables.Add((NameValueCollection) section); + } + else + { + throw new ArgumentException("Section [" + sectionName + + "] is not handled by the NameValueSectionHandler."); + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ConfigurationReader.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ConfigurationReader.cs new file mode 100644 index 00000000..73146a93 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ConfigurationReader.cs @@ -0,0 +1,347 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using System.Configuration; +using System.IO; +using System.Xml; + +using Common.Logging; +using Spring.Core; +using Spring.Core.IO; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Various utility methods for .NET style .config files. + /// + /// + ///

    + /// Currently supports reading custom configuration sections and returning them as + /// objects. + ///

    + ///
    + /// Simon White + /// Mark Pollack + /// $Id: ConfigurationReader.cs,v 1.18 2007/08/08 17:47:13 bbaia Exp $ + public sealed class ConfigurationReader + { + private const string ConfigSectionTypeAttribute = "type"; + private const string ConfigurationElement = "configuration"; + private const string ConfigSectionsElement = "configSections"; + private const string ConfigSectionElement = "section"; + private const string ConfigSectionNameAttribute = "name"; + + private static readonly ILog _log = LogManager.GetLogger(typeof (ConfigurationReader)); + + /// + /// Reads the specified configuration section into a + /// . + /// + /// The resource to read. + /// The section name. + /// + /// A newly populated + /// . + /// + /// + /// If any errors are encountered while attempting to open a stream + /// from the supplied . + /// + /// + /// If any errors are encountered while loading or reading (this only applies to + /// v1.1 and greater of the .NET Framework) the actual XML. + /// + /// + /// If any errors are encountered while loading or reading (this only applies to + /// v1.0 of the .NET Framework). + /// + /// + /// If the configuration section was otherwise invalid. + /// + public static NameValueCollection Read(IResource resource, string configSection) + { + return ConfigurationReader.Read(resource, configSection, new NameValueCollection()); + } + + /// + /// Reads the specified configuration section into the supplied + /// . + /// + /// The resource to read. + /// The section name. + /// + /// The collection that is to be populated. May be + /// . + /// + /// + /// A newly populated + /// . + /// + /// + /// If any errors are encountered while attempting to open a stream + /// from the supplied . + /// + /// + /// If any errors are encountered while loading or reading (this only applies to + /// v1.1 and greater of the .NET Framework) the actual XML. + /// + /// + /// If any errors are encountered while loading or reading (this only applies to + /// v1.0 of the .NET Framework). + /// + /// + /// If the configuration section was otherwise invalid. + /// + public static NameValueCollection Read( + IResource resource, string configSection, NameValueCollection properties) + { + return ConfigurationReader.Read(resource, configSection, properties, true); + } + + /// + /// Reads the specified configuration section into the supplied + /// . + /// + /// The resource to read. + /// The section name. + /// + /// The collection that is to be populated. May be + /// . + /// + /// + /// If a key already exists, is its value to be appended to the current + /// value or replaced? + /// + /// + /// The populated + /// . + /// + /// + /// If any errors are encountered while attempting to open a stream + /// from the supplied . + /// + /// + /// If any errors are encountered while loading or reading (this only applies to + /// v1.1 and greater of the .NET Framework) the actual XML. + /// + /// + /// If any errors are encountered while loading or reading (this only applies to + /// v1.0 of the .NET Framework). + /// + /// + /// If the configuration section was otherwise invalid. + /// + public static NameValueCollection Read( + IResource resource, string configSection, NameValueCollection properties, bool overrideValues) + { + if (properties == null) + { + properties = new NameValueCollection(); + } + Stream stream = null; + try + { + XmlDocument doc = new XmlDocument(); + stream = resource.InputStream; + doc.Load(stream); + NameValueCollection newProperties = ReadFromXmlDocument(doc, configSection); + if(newProperties != null) + { + PopulateProperties(overrideValues, properties, newProperties); + } + } + finally + { + if (stream != null) + { + try + { + stream.Close(); + } + catch (IOException ex) + { + #region Instrumentation + + if (_log.IsWarnEnabled) + { + _log.Warn("Could not close stream from resource " + resource.Description, ex); + } + + #endregion + } + } + } + return properties; + } + + /// + /// Read from the specified configuration from the supplied XML + /// into a + /// . + /// + /// + /// + /// Does not support section grouping. The supplied XML + /// must already be loaded. + /// + /// + /// + /// The to read from. + /// + /// + /// The configuration section name to read. + /// + /// + /// A newly populated + /// . + /// + /// + /// If any errors are encountered while reading (this only applies to + /// v1.1 and greater of the .NET Framework). + /// + /// + /// If any errors are encountered while reading (this only applies to + /// v1.0 of the .NET Framework). + /// + /// + /// If the configuration section was otherwise invalid. + /// + public static NameValueCollection ReadFromXmlDocument(XmlDocument document, + string configSectionName) + { + // find the config section declaration (if one exists)... + XmlNode xmlConfig = document.SelectSingleNode( + string.Format("//{0}//{1}//{2}[@{3}='{4}']", + ConfigurationElement, ConfigSectionsElement, + ConfigSectionElement, ConfigSectionNameAttribute, configSectionName)); + + // create appropriate configuration section handler... + NameValueSectionHandler handler = null; + if (xmlConfig == null) + { + // none specified, so use the default... + handler = new NameValueSectionHandler(); + } + else + { + XmlAttribute xmlConfigType = xmlConfig.Attributes[ConfigSectionTypeAttribute]; + Type cshType = TypeResolutionUtils.ResolveType(xmlConfigType.Value); + object o = ObjectUtils.InstantiateType(cshType); + handler = o as NameValueSectionHandler; + if (handler == null) + { + throw ConfigurationUtils.CreateConfigurationException("Configuration section '" + configSectionName + "' not of type NameValueCollection."); + } + + } + XmlNode collectionNode = document.SelectSingleNode( + string.Format("//{0}//{1}", ConfigurationElement, configSectionName)); + if (collectionNode == null) + { + throw ConfigurationUtils.CreateConfigurationException("Cannot read properties; config section '" + configSectionName + "' not found."); + } + else + { + return (NameValueCollection) handler.Create(null, null, collectionNode); + } + } + + /// + /// Populates the supplied with values from + /// a .NET application configuration file. + /// + /// + /// The + /// to add any key-value pairs to. + /// + /// + /// The configuration section name in the a .NET application configuration + /// file. + /// + /// + /// If a key already exists, is its value to be appended to the current + /// value or replaced? + /// + /// + /// if the supplied + /// was found. + /// + public static bool PopulateFromAppConfig( + NameValueCollection properties, string configSectionName, bool overrideValues) + { + bool sectionFound = false; + + NameValueCollection newProperties + = ConfigurationUtils.GetSection(configSectionName) as NameValueCollection; + + if (newProperties != null) + { + sectionFound = true; + PopulateProperties(overrideValues, properties, newProperties); + } + return sectionFound; + } + + private static void PopulateProperties( + bool overrideValues, NameValueCollection properties, NameValueCollection newProperties) + { + if (!overrideValues) + { + properties.Add(newProperties); + } + else + { + foreach (string key in newProperties.AllKeys) + { + properties.Set(key, newProperties.Get(key)); + } + } + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the ConfigurationReader class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly visible + /// constructors. + ///

    + ///
    + private ConfigurationReader() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ConnectionStringsVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ConnectionStringsVariableSource.cs new file mode 100644 index 00000000..cc9bb027 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ConnectionStringsVariableSource.cs @@ -0,0 +1,96 @@ +#if NET_2_0 +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections.Specialized; +using System.Configuration; +using Spring.Util; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Implementation of that + /// resolves variable name connection strings defined in + /// the standard .NET configuration file. + /// + /// + ///

    + /// When the <connectionStrings> configuration section is processed by this class, + /// two variables are defined for each connection string: one for connection string and + /// the second one for the provider name.

    + ///

    + /// Variable names are generated by appending '.connectionString' and '.providerName' + /// literals to the value of the name attribute of the connection string element. + /// For example:

    + ///
    +    /// 
    +    ///    
    +    /// 
    +    /// 
    + ///

    + /// will result in two variables being created: myConn.connectionString and myConn.providerName. + /// You can reference these variables within your object definitions, just like any other variable.

    + ///
    + /// Aleksandar Seovic + /// $Id: ConnectionStringsVariableSource.cs,v 1.4 2007/08/02 22:18:32 markpollack Exp $ + [Serializable] + public class ConnectionStringsVariableSource : IVariableSource + { + private NameValueCollection variables; + + /// + /// Resolves variable value for the specified variable name. + /// + /// + /// The name of the variable to resolve. + /// + /// + /// The variable value if able to resolve, null otherwise. + /// + public string ResolveVariable(string name) + { + if (variables == null) + { + InitVariables(); + } + return variables.Get(name); + } + + /// + /// Initializes properties based on the specified + /// property file locations. + /// + private void InitVariables() + { + variables = new NameValueCollection(); + ConnectionStringSettingsCollection settings = ConfigurationManager.ConnectionStrings; + foreach (ConnectionStringSettings setting in settings) + { + string providerName = setting.ProviderName; + + variables.Add(setting.Name + ".connectionString", setting.ConnectionString); + variables.Add(setting.Name + ".providerName", + StringUtils.HasText(providerName) ? providerName : "System.Data.SqlClient"); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ConstructorArgumentValues.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ConstructorArgumentValues.cs new file mode 100644 index 00000000..5ac02f1c --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ConstructorArgumentValues.cs @@ -0,0 +1,659 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using Spring.Collections; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Holder for constructor argument values for an object. + /// + /// + ///

    + /// Supports values for a specific index or parameter name (case + /// insensitive) in the constructor argument list, and generic matches by + /// . + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: ConstructorArgumentValues.cs,v 1.16 2008/03/03 09:29:07 bbaia Exp $ + /// + [Serializable] + public class ConstructorArgumentValues + { + /// + /// Can be used as an argument filler for the + /// + /// overload when one is not looking for an argument by index. + /// + public const int NoIndex = -1289; // yes, the number really is wholly arbitrary... + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + public ConstructorArgumentValues() + { + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The + /// to be used to populate this instance. + /// + public ConstructorArgumentValues(ConstructorArgumentValues other) + { + AddAll(other); + } + + #endregion + + #region Fields + + private CultureInfo enUSCultureInfo = new CultureInfo("en-US", false); + private IDictionary _indexedArgumentValues = new Hashtable(); + private IList _genericArgumentValues = new LinkedList(); + private IDictionary _namedArgumentValues = new Hashtable(); + + #endregion + + #region Properties + + /// + /// Return the map of indexed argument values. + /// + /// + /// An with + /// indices as keys and + /// s + /// as values. + /// + public virtual IDictionary IndexedArgumentValues + { + get { return _indexedArgumentValues; } + } + + /// + /// Return the map of named argument values. + /// + /// + /// An with + /// named arguments as keys and + /// s + /// as values. + /// + public virtual IDictionary NamedArgumentValues + { + get { return _namedArgumentValues; } + } + + /// + /// Return the set of generic argument values. + /// + /// + /// A of + /// s. + /// + public virtual IList GenericArgumentValues + { + get { return _genericArgumentValues; } + + } + + /// + /// Return the number of arguments held in this instance. + /// + public virtual int ArgumentCount + { + get + { + return IndexedArgumentValues.Count + + GenericArgumentValues.Count + + NamedArgumentValues.Count; + } + + } + + /// + /// Returns true if this holder does not contain any argument values, + /// neither indexed ones nor generic ones. + /// + public virtual bool Empty + { + get + { + return IndexedArgumentValues.Count == 0 + && GenericArgumentValues.Count == 0 + && NamedArgumentValues.Count == 0; + } + } + + #endregion + + #region Methods + + /// + /// Copy all given argument values into this object. + /// + /// + /// The + /// to be used to populate this instance. + /// + public void AddAll(ConstructorArgumentValues other) + { + if (other != null) + { + foreach (object o in other.GenericArgumentValues) + { + GenericArgumentValues.Add(o); + } + foreach (DictionaryEntry entry in other.IndexedArgumentValues) + { + IndexedArgumentValues.Add(entry.Key, entry.Value); + } + foreach (DictionaryEntry entry in other.NamedArgumentValues) + { + NamedArgumentValues.Add(entry.Key, entry.Value); + } + } + } + + /// + /// Add argument value for the given index in the constructor argument list. + /// + /// + /// The index in the constructor argument list. + /// + /// + /// The argument value. + /// + public virtual void AddIndexedArgumentValue(int index, object value) + { + IndexedArgumentValues[index] = new ValueHolder(value); + } + + /// + /// Add argument value for the given index in the constructor argument list. + /// + /// The index in the constructor argument list. + /// The argument value. + /// + /// The of the argument + /// . + /// + public virtual void AddIndexedArgumentValue(int index, object value, string type) + { + IndexedArgumentValues[index] = new ValueHolder(value, type); + } + + /// + /// Add argument value for the given name in the constructor argument list. + /// + /// The name in the constructor argument list. + /// The argument value. + /// + /// If the supplied is + /// or is composed wholly of whitespace. + /// + public virtual void AddNamedArgumentValue(string name, object value) + { + AssertUtils.ArgumentHasText(name, "name"); + NamedArgumentValues[GetCanonicalNamedArgument(name)] = new ValueHolder(value); + } + + /// + /// Get argument value for the given index in the constructor argument list. + /// + /// The index in the constructor argument list. + /// + /// The required of the argument. + /// + /// + /// The + /// + /// for the argument, or if none set. + /// + public virtual ValueHolder GetIndexedArgumentValue(int index, Type requiredType) + { + ValueHolder valueHolder = (ValueHolder) IndexedArgumentValues[index]; + if (valueHolder != null) + { + if (valueHolder.Type == null + || requiredType.FullName.Equals(valueHolder.Type) + || requiredType.AssemblyQualifiedName.Equals(valueHolder.Type)) + { + return valueHolder; + } + } + return null; + } + + /// + /// Get argument value for the given name in the constructor argument list. + /// + /// The name in the constructor argument list. + /// + /// The + /// + /// for the argument, or if none set. + /// + public virtual ValueHolder GetNamedArgumentValue(string name) + { + ValueHolder valueHolder = null; + if (name != null && ContainsNamedArgument(name)) + { + valueHolder = (ValueHolder)NamedArgumentValues[GetCanonicalNamedArgument(name)]; + } + return valueHolder; + } + + /// + /// Does this set of constructor arguments contain a named argument matching the + /// supplied name? + /// + /// + /// + /// The comparison is performed in a case-insensitive fashion. + /// + /// + /// The named argument to look up. + /// + /// if this set of constructor arguments + /// contains a named argument matching the supplied + /// name. + /// + public bool ContainsNamedArgument(string argument) + { + return NamedArgumentValues.Contains(GetCanonicalNamedArgument(argument)); + } + + /// + /// Add generic argument value to be matched by type. + /// + /// + /// The argument value. + /// + public virtual void AddGenericArgumentValue(object value) + { + GenericArgumentValues.Add(new ValueHolder(value)); + } + + /// + /// Add generic argument value to be matched by type. + /// + /// The argument value. + /// + /// The of the argument + /// . + /// + public virtual void AddGenericArgumentValue(object value, string type) + { + GenericArgumentValues.Add(new ValueHolder(value, type)); + } + + /// + /// Look for a generic argument value that matches the given + /// . + /// + /// + /// The to match. + /// + /// + /// The + /// + /// for the argument, or if none set. + /// + public virtual ValueHolder GetGenericArgumentValue(Type requiredType) + { + return GetGenericArgumentValue(requiredType, null); + } + + /// + /// Look for a generic argument value that matches the given + /// . + /// + /// + /// The to match. + /// + /// + /// A of + /// + /// objects that have already been used in the current resolution + /// process and should therefore not be returned again; this allows one + /// to return the next generic argument match in the case of multiple + /// generic argument values of the same type. + /// + /// + /// The + /// + /// for the argument, or if none set. + /// + public virtual ValueHolder GetGenericArgumentValue( + Type requiredType, ISet usedValues) + { + foreach (ValueHolder valueHolder in GenericArgumentValues) + { + if (usedValues == null || !usedValues.Contains(valueHolder)) + { + if (requiredType != null) + { + if (StringUtils.HasText(valueHolder.Type)) + { + if (valueHolder.Type.Equals(requiredType.FullName) + || valueHolder.Type.Equals(requiredType.AssemblyQualifiedName)) + { + return valueHolder; + } + } + else if (requiredType.IsInstanceOfType(valueHolder.Value) + || (requiredType.IsArray + && typeof (IList).IsInstanceOfType(valueHolder.Value))) + { + return valueHolder; + } + } + // if the value holder is (pretty much) untyped, that's ok to return... + else if (StringUtils.IsNullOrEmpty(valueHolder.Type)) + { + return valueHolder; + } + } + } + return null; + } + + /// + /// Look for an argument value that either corresponds to the given index + /// in the constructor argument list or generically matches by + /// . + /// + /// + /// The index in the constructor argument list. + /// + /// + /// The to match. + /// + /// + /// The + /// + /// for the argument, or if none is set. + /// + public virtual ValueHolder GetArgumentValue(int index, Type requiredType) + { + return GetArgumentValue(index, string.Empty, requiredType, null); + } + + /// + /// Look for an argument value that either corresponds to the given index + /// in the constructor argument list or generically matches by + /// . + /// + /// + /// The index in the constructor argument list. + /// + /// + /// The to match. + /// + /// + /// A of + /// + /// objects that have already been used in the current resolution + /// process and should therefore not be returned again; this allows one + /// to return the next generic argument match in the case of multiple + /// generic argument values of the same type. + /// + /// + /// The + /// + /// for the argument, or if none is set. + /// + public virtual ValueHolder GetArgumentValue(int index, Type requiredType, ISet usedValues) + { + return GetArgumentValue(index, string.Empty, requiredType, usedValues); + } + + /// + /// Look for an argument value that either corresponds to the given index + /// in the constructor argument list or generically matches by + /// . + /// + /// + /// The name of the argument in the constructor argument list. May be + /// , in which case generic matching by + /// is assumed. + /// + /// + /// The to match. + /// + /// + /// The + /// + /// for the argument, or if none is set. + /// + public virtual ValueHolder GetArgumentValue(string name, Type requiredType) + { + return GetArgumentValue(NoIndex, name, requiredType, null); + } + + /// + /// Look for an argument value that either corresponds to the given index + /// in the constructor argument list or generically matches by + /// . + /// + /// + /// The name of the argument in the constructor argument list. May be + /// , in which case generic matching by + /// is assumed. + /// + /// + /// The to match. + /// + /// + /// A of + /// + /// objects that have already been used in the current resolution + /// process and should therefore not be returned again; this allows one + /// to return the next generic argument match in the case of multiple + /// generic argument values of the same type. + /// + /// + /// The + /// + /// for the argument, or if none is set. + /// + public virtual ValueHolder GetArgumentValue( + string name, Type requiredType, ISet usedValues) + { + return GetArgumentValue(NoIndex, name, requiredType, usedValues); + } + + /// + /// Look for an argument value that either corresponds to the given index + /// in the constructor argument list, or to the named argument, or + /// generically matches by . + /// + /// + /// The index of the argument in the constructor argument list. May be + /// negative, to denote the fact that we are not looking for an + /// argument by index (see + /// . + /// + /// + /// The name of the argument in the constructor argument list. May be + /// . + /// + /// + /// The to match. + /// + /// + /// A of + /// + /// objects that have already been used in the current resolution + /// process and should therefore not be returned again; this allows one + /// to return the next generic argument match in the case of multiple + /// generic argument values of the same type. + /// + /// + /// The + /// + /// for the argument, or if none is set. + /// + public virtual ValueHolder GetArgumentValue( + int index, string name, Type requiredType, ISet usedValues) + { + ValueHolder valueHolder = null; + if(index != NoIndex) + { + valueHolder = GetIndexedArgumentValue(index, requiredType); + } + if (valueHolder == null) + { + valueHolder = GetNamedArgumentValue(name); + if (valueHolder == null) + { + valueHolder = GetGenericArgumentValue(requiredType, usedValues); + } + } + return valueHolder; + } + + private string GetCanonicalNamedArgument(string argument) + { + return argument != null ? argument.ToLower(enUSCultureInfo) : argument; + } + + #endregion + + #region Inner Class : ValueHolder + + /// + /// Holder for a constructor argument value, with an optional + /// attribute indicating the target + /// of the actual constructor argument. + /// + [Serializable] + public class ValueHolder + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the ValueHolder class. + /// + /// + /// The value of the constructor argument. + /// + internal ValueHolder(object value) + { + Value = value; + } + + /// + /// Creates a new instance of the ValueHolder class. + /// + /// + /// The value of the constructor argument. + /// + /// + /// The of the argument + /// . Can also be one of the common + /// aliases (int, bool, + /// float, etc). + /// + internal ValueHolder(object value, string typeName) + { + Value = value; + this.typeName = typeName; + } + + #endregion + + #region Methods + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, + "'{0}' [{1}]", Value, Type); + } + + #endregion + + #region Properties + + /// + /// Gets and sets the value for the constructor argument. + /// + /// + ///

    + /// Only necessary for manipulating a registered value, for example in + /// s. + ///

    + ///
    + public object Value + { + get { return _ctorValue; } + set { _ctorValue = value; } + } + + /// + /// Return the of the constructor + /// argument. + /// + public string Type + { + get { return typeName; } + } + + #endregion + + #region Fields + + private object _ctorValue; + private string typeName; + + #endregion + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/CustomConverterConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/CustomConverterConfigurer.cs new file mode 100644 index 00000000..22e44910 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/CustomConverterConfigurer.cs @@ -0,0 +1,304 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.Globalization; +using Spring.Core; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// + /// implementation that allows for convenient registration of custom + /// s. + /// + /// + /// + /// The use of this class is typically not required; the .NET + /// mechanism of associating a + /// with a + /// via the use of the + /// is the + /// recommended (and standard) way. This class primarily exists to cover + /// those cases where third party classes to which one does not have the + /// source need to be exposed to the type conversion mechanism. + /// + ///

    + /// Because the + /// + /// class implements the + /// + /// interface, instances of this class that have been exposed in the + /// scope of an + /// will + /// automatically be picked up by the application context and made + /// available to the IoC container whenever type conversion is required. If + /// one is using a + /// + /// object definition within the scope of an + /// , no such automatic + /// pickup of the + /// + /// is performed (custom converters will have to be added manually using the + /// + /// method). For most application scenarios, one will get better + /// mileage using the + /// abstraction. + ///

    + ///
    + /// + ///

    + /// The following examples all assume XML based configuration, and use + /// inner object definitions to define the custom + /// objects (nominally to + /// avoid polluting the object name space, but also because the + /// configuration simply reads better that way). + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///

    + /// The following example illustrates a complete (albeit naieve) use case + /// for this class, including a custom + /// implementation, said + /// converters domain class, and the XML configuration that hooks the + /// converter in place and makes it available to a Spring.NET container for + /// use during object resolution. + ///

    + ///

    + /// The domain class is a simple data-only object that contains the data + /// required to send an email message (such as the host and user account + /// name). A developer would prefer to use a string of the form + /// UserName=administrator,Password=r1l0k1l3y,Host=localhost to + /// configure the mail settings and just let the container take care of the + /// conversion. + ///

    + /// + /// namespace ExampleNamespace + /// { + /// public sealed class MailSettings + /// { + /// private string _userName; + /// private string _password; + /// private string _host; + /// + /// public string Host + /// { + /// get { return _host; } + /// set { _host = value; } + /// } + /// + /// public string UserName + /// { + /// get { return _userName; } + /// set { _userName = value; } + /// } + /// + /// public string Password + /// { + /// get { return _password; } + /// set { _password = value; } + /// } + /// } + /// + /// public sealed class MailSettingsConverter : TypeConverter + /// { + /// public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + /// { + /// if (typeof (string) == sourceType) + /// { + /// return true; + /// } + /// return base.CanConvertFrom(context, sourceType); + /// } + /// + /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + /// { + /// string text = value as string; + /// if(text != null) + /// { + /// MailSettings mailSettings = new MailSettings(); + /// string[] tokens = text.Split(','); + /// for (int i = 0; i < tokens.Length; ++i) + /// { + /// string token = tokens[i]; + /// string[] settings = token.Split('='); + /// typeof(MailSettings).GetProperty(settings[0]) + /// .SetValue(mailSettings, settings[1], null); + /// } + /// return mailSettings; + /// } + /// return base.ConvertFrom(context, culture, value); + /// } + /// } + /// + /// // a very naieve class that uses the MailSettings class... + /// public sealed class ExceptionLogger + /// { + /// private MailSettings _mailSettings; + /// + /// public MailSettings MailSettings { + /// { + /// set { _mailSettings = value; } + /// } + /// + /// public void Log(object value) + /// { + /// Exception ex = value as Exception; + /// if(ex != null) + /// { + /// // use _mailSettings instance... + /// } + /// } + /// } + /// } + /// + ///

    + /// The attendant XML configuration for the above classes would be... + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: CustomConverterConfigurer.cs,v 1.14 2007/05/11 02:32:14 markpollack Exp $ + /// + /// + /// + [Serializable] + public class CustomConverterConfigurer : AbstractConfigurer + { + private IDictionary _customConverters; + + /// + /// The custom converters to register. + /// + /// + ///

    + /// The uses the type name + /// of the class that requires conversion as the key, and an + /// instance of the + /// that will effect + /// the conversion. Alternatively, the actual + /// of the class that requires conversion + /// can be used as the key. + ///

    + ///
    + /// + ///

    + /// + /// IDictionary converters = new Hashtable(); + /// converters.Add( "System.Date", new MyCustomDateConverter() ); + /// // a System.Type instance can also be used as the key... + /// converters.Add( typeof(Color), new MyCustomRBGColorConverter() ); + /// + ///

    + ///
    + public IDictionary CustomConverters + { + set { this._customConverters = value; } + } + + /// + /// Registers any custom converters with the supplied + /// . + /// + /// + /// The object factory to register the converters with. + /// + /// + /// In case of errors. + /// + public override void PostProcessObjectFactory( + IConfigurableListableObjectFactory factory) + { + if (_customConverters != null) + { + foreach (DictionaryEntry entry in _customConverters) + { + Type requiredType = ResolveRequiredType(entry.Key, "key", "custom type converter"); + TypeConverter converter = ResolveConverter(entry.Value); + factory.RegisterCustomConverter(requiredType, converter); + } + } + } + + /// + /// Resolves the supplied into a + /// instance. + /// + /// + /// The object that is to be resolved into a + /// instance. + /// + /// + /// A resolved instance. + /// + /// + /// If the supplied is , + /// or the supplied cannot be resolved. + /// + protected virtual TypeConverter ResolveConverter(object value) + { + TypeConverter converter = value as TypeConverter; + if (converter == null) + { + throw new ObjectInitializationException( + "Mapped value for custom converter is not a " + + "[System.ComponentModel.TypeConverter] instance."); + } + return converter; + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/DelegateFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/DelegateFactoryObject.cs new file mode 100644 index 00000000..7f6d7627 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/DelegateFactoryObject.cs @@ -0,0 +1,179 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// implementation that + /// creates delegates. + /// + /// + ///

    + /// Supports the creation of s for both + /// instance and methods. + ///

    + ///
    + /// Rick Evans + /// $Id: DelegateFactoryObject.cs,v 1.6 2007/03/16 04:01:35 aseovic Exp $ + [Serializable] + public class DelegateFactoryObject : AbstractFactoryObject + { + /// + /// Callback method called once all factory properties have been set. + /// + /// + /// In the event of misconfiguration (such as failure to set an essential + /// property) or if initialization fails. + /// + /// + public override void AfterPropertiesSet() + { + if (DelegateType == null) + { + throw new ArgumentException( + "The 'DelegateType' property is required."); + } + if (!typeof (Delegate).IsAssignableFrom(DelegateType)) + { + throw new ArgumentException( + "The 'DelegateType' property must (obviously) be a Type derived from [System.Delegate]."); + } + if (TargetType == null && TargetObject == null) + { + throw new ArgumentException( + "Exactly one of either the 'TargetType' or 'TargetObject' properties is required."); + } + if (TargetType != null && TargetObject != null) + { + throw new ArgumentException( + "Exactly one of either the 'TargetType' or 'TargetObject' properties is required (not both)."); + } + if (StringUtils.IsNullOrEmpty(MethodName)) + { + throw new ArgumentException( + "The 'MethodName' property is required."); + } + base.AfterPropertiesSet(); + } + + /// + /// Creates the delegate. + /// + /// + /// If an exception occured during object creation. + /// + /// The object returned by this factory. + /// + protected override object CreateInstance() + { + Delegate instance = null; + if (TargetType != null) + { + instance = Delegate.CreateDelegate(DelegateType, TargetType, MethodName); + } + else + { + instance = Delegate.CreateDelegate(DelegateType, TargetObject, MethodName); + } + return instance; + } + + #region Properties + + /// + /// The of + /// created by this factory. + /// + /// + ///

    + /// Returns the + /// if accessed prior to the method + /// being called. + ///

    + ///
    + public override Type ObjectType + { + get + { + return (DelegateType != null) + ? DelegateType + : typeof (Delegate); + } + } + + /// + /// The of the + /// created by this factory. + /// + public Type DelegateType + { + get { return _delegateType; } + set { _delegateType = value; } + } + + /// + /// The name of the method that is to be invoked by the created + /// delegate. + /// + public string MethodName + { + get { return _methodName; } + set { _methodName = value; } + } + + /// + /// The target if the + /// refers to a method. + /// + public Type TargetType + { + get { return _targetType; } + set { _targetType = value; } + } + + /// + /// The target object if the + /// refers to an instance method. + /// + public object TargetObject + { + get { return _targetObject; } + set { _targetObject = value; } + } + + #endregion + + #region Fields + + private Type _delegateType; + private string _methodName; + private Type _targetType; + private object _targetObject; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/DictionaryFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/DictionaryFactoryObject.cs new file mode 100644 index 00000000..b427b9d0 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/DictionaryFactoryObject.cs @@ -0,0 +1,132 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Globalization; +using Spring.Core; +using Spring.Util; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Simple factory for shared instances. + /// + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: DictionaryFactoryObject.cs,v 1.7 2007/07/31 03:47:39 markpollack Exp $ + [Serializable] + public class DictionaryFactoryObject : AbstractFactoryObject + { + private IDictionary _sourceDictionary; + private Type _targetDictionaryType = typeof (Hashtable); + + /// + /// Set the source . + /// + /// + ///

    + /// This value will be used to populate the + /// returned by this factory. + ///

    + ///
    + public IDictionary SourceDictionary + { + set { this._sourceDictionary = value; } + } + + /// + /// Set the of the + /// implementation to use. + /// + /// + ///

    + /// The default is the . + ///

    + ///
    + /// + /// If the value is . + /// + /// + /// If the value is an . + /// + /// + /// If the value is an interface. + /// + public Type TargetDictionaryType + { + set + { + AssertUtils.ArgumentNotNull(value, "value"); + if (!typeof (IDictionary).IsAssignableFrom(value)) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "The Type passed to the TargetDictionaryType property must implement the '{0}' interface.", + ObjectType.FullName)); + } + if (value.IsInterface) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "The Type passed to the TargetDictionaryType property cannot be an interface; it must be a concrete class that implements the '{0}' interface.", + ObjectType.FullName)); + } + if (value.IsAbstract) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "The Type passed to the TargetDictionaryType property cannot be abstract (MustInherit in VisualBasic.NET); it must be a concrete class that implements the '{0}' interface.", + ObjectType.FullName)); + } + this._targetDictionaryType = value; + } + } + + /// + /// The of objects created by this factory. + /// + /// + /// Always returns the . + /// + public override Type ObjectType + { + get { return typeof (IDictionary); } + } + + /// + /// Constructs a new instance of the target dictionary. + /// + /// The new instance. + protected override object CreateInstance() + { + if (this._sourceDictionary == null) + { + throw new ArgumentException("The 'SourceDictionary' property cannot be null (Nothing in Visual Basic.NET)."); + } + IDictionary result = (IDictionary) ObjectUtils.InstantiateType(this._targetDictionaryType); + foreach (DictionaryEntry de in _sourceDictionary) + { + result[de.Key] = de.Value; + } + return result; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/EnvironmentVariableMode.cs b/src/Spring/Spring.Core/Objects/Factory/Config/EnvironmentVariableMode.cs new file mode 100644 index 00000000..eb81471c --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/EnvironmentVariableMode.cs @@ -0,0 +1,52 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Specifies how instances of the + /// + /// class must apply environment variables when replacing values. + /// + /// Mark Pollack + /// $Id: EnvironmentVariableMode.cs,v 1.6 2007/03/16 04:01:35 aseovic Exp $ + [Serializable] + public enum EnvironmentVariableMode + { + /// + /// Never replace environment variables. + /// + Never = 1, + + /// + /// If properties are not specified via a resource, + /// then resolve using environment variables. + /// + Fallback = 2, + + /// + /// Apply environment variables first before applying properties from a + /// resource. + /// + Override = 3 + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/EnvironmentVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/EnvironmentVariableSource.cs new file mode 100644 index 00000000..dcf59a82 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/EnvironmentVariableSource.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Implementation of that + /// resolves variable name against environment variables. + /// + /// Aleksandar Seovic + /// $Id: EnvironmentVariableSource.cs,v 1.3 2007/08/02 22:18:32 markpollack Exp $ + [Serializable] + public class EnvironmentVariableSource : IVariableSource + { + /// + /// Resolves variable value for the specified variable name. + /// + /// + /// The name of the variable to resolve. + /// + /// + /// The variable value if able to resolve, null otherwise. + /// + public string ResolveVariable(string name) + { + return Environment.GetEnvironmentVariable(name); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/EventValues.cs b/src/Spring/Spring.Core/Objects/Factory/Config/EventValues.cs new file mode 100644 index 00000000..9555be8f --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/EventValues.cs @@ -0,0 +1,151 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Holder for event handler values for an object. + /// + /// Rick Evans (.NET) + /// $Id: EventValues.cs,v 1.8 2007/03/16 04:01:38 aseovic Exp $ + [Serializable] + public class EventValues + { + #region Constants + /// + /// The empty array of s. + /// + private static readonly IEventHandlerValue [] EmptyHandlers + = new IEventHandlerValue [] {}; + #endregion + + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the + /// class. + /// + public EventValues() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// to be used to populate this instance. + /// + public EventValues(EventValues other) + { + AddAll (other); + } + #endregion + + #region Properties + /// + /// The mapping of event names to an + /// of + /// s. + /// + protected IDictionary EventHandlers + { + get + { + return _eventHandlers; + } + } + + /// + /// Gets the of events + /// that have handlers associated with them. + /// + public ICollection Events + { + get + { + return EventHandlers.Keys; + } + } + + /// + /// Gets the of + /// s for the supplied + /// event name. + /// + public ICollection this [string eventName] + { + get + { + return EventHandlers.Contains (eventName) ? + EventHandlers [eventName] as ICollection : + EventValues.EmptyHandlers; + } + } + #endregion + + #region Methods + /// + /// Copy all given argument values into this object. + /// + /// + /// The + /// to be used to populate this instance. + /// + public void AddAll (EventValues other) + { + if (other != null) + { + foreach (IList handlers in other.EventHandlers.Values) + { + foreach (IEventHandlerValue handler in handlers) + { + AddHandler (handler); + } + } + } + } + + /// + /// Adds the supplied handler to the collection of event handlers. + /// + /// The handler to be added. + public void AddHandler (IEventHandlerValue handler) + { + IList handlers = EventHandlers [handler.EventName] as IList; + if (handlers == null) + { + handlers = new ArrayList (); + EventHandlers [handler.EventName] = handlers; + } + handlers.Add (handler); + } + #endregion + + #region Fields + private IDictionary _eventHandlers = new Hashtable(); + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ExpressionHolder.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ExpressionHolder.cs new file mode 100644 index 00000000..2ae29e8f --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ExpressionHolder.cs @@ -0,0 +1,110 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using Spring.Expressions; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Immutable placeholder class used for the value of a + /// object when it's a reference + /// to a Spring that should be evaluated at runtime. + /// + /// Aleksandar Seovic + /// $Id: ExpressionHolder.cs,v 1.5 2007/08/21 19:28:33 markpollack Exp $ + [Serializable] + public class ExpressionHolder + { + #region Fields + + private IExpression expression; + private string expressionString; + private MutablePropertyValues properties; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// The expression to resolve. + public ExpressionHolder(string expression) + { + expressionString = expression; + this.expression = Spring.Expressions.Expression.Parse(expression); + } + + #endregion + + #region Properties + + /// + /// Gets or sets the expression string. Setting the expression string will cause + /// the expression to be parsed. + /// + /// The expression string. + public string ExpressionString + { + get { return expressionString; } + } + + /// + /// Return the expression. + /// + public IExpression Expression + { + get { return expression; } + } + + /// + /// Properties for this expression node. + /// + public MutablePropertyValues Properties + { + get { return properties; } + set { properties = value; } + } + + #endregion + + #region Methods + + /// + /// Returns a string representation of this instance. + /// + /// A string representation of this instance. + public override string ToString() + { + return string.Format( + CultureInfo.InvariantCulture, "<{0}>", expressionString); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/FieldRetrievingFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/FieldRetrievingFactoryObject.cs new file mode 100644 index 00000000..645cc7a8 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/FieldRetrievingFactoryObject.cs @@ -0,0 +1,311 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Reflection; +using System.Text; + +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// implementation that + /// retrieves a static or non-static public field value. + /// + /// + ///

    + /// Typically used for retrieving public constants. + ///

    + ///
    + /// + ///

    + /// The following example retrieves the field value... + ///

    + /// + /// + /// + /// + /// + /// + ///

    + /// The previous example could also have been written using the convenience + /// + /// property, like so... + ///

    + /// + /// + /// + /// + /// + ///

    + /// This class also implements the + /// interface + /// (). + /// If the id (or name) of one's + /// + /// object definition is set to the + /// of the field to be retrieved, then the id (or + /// name) of one's object definition will be used for the name of the + /// field lookup. See below for an example of this + /// concise style of definition. + ///

    + /// + /// + /// + /// + /// + /// + /// + ///

    + /// The usage for retrieving instance fields is similar. No example is shown + /// because public instance fields are generally bad practice; but if + /// you have some legacy code that exposes public instance fields, or if you + /// just really like coding public instance fields, then you can use this + /// implementation to + /// retrieve such field values. + ///

    + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: FieldRetrievingFactoryObject.cs,v 1.8 2007/07/31 18:16:49 bbaia Exp $ + [Serializable] + public class FieldRetrievingFactoryObject : IFactoryObject, IInitializingObject, IObjectNameAware + { + private string targetField; + private object targetObject; + private Type targetType; + private FieldInfo field; + private string objectName; + private string staticField; + + /// + /// The of the + /// field to be retrieved. + /// + public string StaticField + { + set { this.staticField = value; } + } + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// + /// The name of the object in the factory. + /// + /// + ///

    + /// In the context of the + /// + /// class, the + /// + /// value will be interepreted as the value of the + /// + /// property if no value has been explicitly assigned to the + /// + /// property. This allows for concise object definitions with just an id or name; + /// see the class documentation for + /// + /// for an example of this style of usage. + ///

    + ///
    + public string ObjectName + { + set { this.objectName = value; } + } + + /// + /// The name of the field the value of which is to be retrieved. + /// + /// + ///

    + /// If the + /// + /// has been set (and is not ), then the value of this property + /// refers to an instance field name; it otherwise refers to a + /// field name. + ///

    + ///
    + public string TargetField + { + get { return targetField; } + set { targetField = value; } + } + + /// + /// The object instance on which the field is defined. + /// + public object TargetObject + { + get { return targetObject; } + set { targetObject = value; } + } + + /// + /// The on which the field is defined. + /// + public Type TargetType + { + get { return targetType; } + set { targetType = value; } + } + + /// + /// The of object that this + /// creates, or + /// if not known in advance. + /// + public Type ObjectType + { + get { return (this.field == null) ? null : this.field.FieldType; } + } + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + public bool IsSingleton + { + get { return true; } + } + + /// + /// Invoked by an + /// after it has set all object properties supplied + /// (and satisfied + /// and ApplicationContextAware). + /// + /// + ///

    + /// This method allows the object instance to perform initialization only + /// possible when all object properties have been set and to throw an + /// exception in the event of misconfiguration. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as failure to set an essential + /// property) or if initialization fails. + /// + public void AfterPropertiesSet() + { + if (TargetType != null && TargetObject != null) + { + throw new ArgumentException( + "Only one of the TargetType or TargetObject properties can be set, not both."); + } + if (TargetType == null && TargetObject == null) + { + if (TargetField != null) + { + throw new ArgumentException( + "Specify the TargetType or TargetObject property in combination with the TargetField property."); + } + + // if no other property specified, use the object name for the static field expression... + if (StringUtils.IsNullOrEmpty(this.staticField)) + { + this.staticField = this.objectName; + } + ParseAndSetFromStaticFieldValue(); + } + if (TargetField == null) + { + throw new ArgumentException("The TargetField property is required."); + } + BindingFlags fieldFlags = BindingFlags.Public | BindingFlags.IgnoreCase; + if (TargetObject == null) + { + // a static field... + fieldFlags |= BindingFlags.Static; + } + else + { + // an instance field... + fieldFlags |= BindingFlags.Instance; + TargetType = TargetObject.GetType(); + } + this.field = targetType.GetField(TargetField, fieldFlags); + if (this.field == null) + { + throw new ObjectDefinitionStoreException( + string.Format( + CultureInfo.InvariantCulture, + "No such field '{0}' on [{1}].", TargetField, + TargetObject == null ? TargetType : TargetObject)); + } + } + + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + /// An instance (possibly shared or independent) of the object managed by + /// this factory. + /// + /// + public object GetObject() + { + if (TargetObject != null) + { + return this.field.GetValue(TargetObject); + } + else + { + return this.field.GetValue(null); + } + } + + private void ParseAndSetFromStaticFieldValue() + { + TypeAssemblyHolder info = new TypeAssemblyHolder(this.staticField); + // the info.TypeName should now contains the Type name, followed by a + // period, followed by the name of the field + int lastDotIndex = info.TypeName.LastIndexOf('.'); + if (lastDotIndex == -1 + || lastDotIndex == info.TypeName.Length) + { + throw new ArgumentException( + "The value passed to the StaticField property must be a fully " + + "qualified Type plus field name: " + + "e.g. 'Example.MyExampleClass.MyField, MyAssembly'"); + } + string typeName = info.TypeName.Substring(0, lastDotIndex); + string fieldName = info.TypeName.Substring(lastDotIndex + 1); + StringBuilder buffer = new StringBuilder(typeName); + if (info.IsAssemblyQualified) + { + buffer.Append(TypeAssemblyHolder.TypeAssemblySeparator); + buffer.Append(info.AssemblyName); + } + TargetType = TypeResolutionUtils.ResolveType(buffer.ToString()); + TargetField = fieldName; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IAutowireCapableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IAutowireCapableObjectFactory.cs new file mode 100644 index 00000000..74924aff --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/IAutowireCapableObjectFactory.cs @@ -0,0 +1,134 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Extension of the + /// interface to be implemented by object factories that are capable of + /// autowiring and expose this functionality for existing object instances. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + public interface IAutowireCapableObjectFactory : IObjectFactory + { + /// + /// Create a new object instance of the given class with the specified + /// autowire strategy. + /// + /// + /// The of the object to instantiate. + /// + /// + /// The desired autowiring mode. + /// + /// + /// Whether to perform a dependency check for objects (not applicable to + /// autowiring a constructor, thus ignored there). + /// + /// The new object instance. + /// + /// If the wiring fails. + /// + /// + object Autowire ( + Type type, AutoWiringMode autowireMode, bool dependencyCheck); + + /// + /// Autowire the object properties of the given object instance by name or + /// . + /// + /// + /// The existing object instance. + /// + /// + /// The desired autowiring mode. + /// + /// + /// Whether to perform a dependency check for the object. + /// + /// + /// If the wiring fails. + /// + /// + void AutowireObjectProperties ( + object instance, AutoWiringMode autowireMode, bool dependencyCheck); + + /// + /// Apply s + /// to the given existing object instance, invoking their + /// + /// methods. + /// + /// + ///

    + /// The returned object instance may be a wrapper around the original. + ///

    + ///
    + /// + /// The existing object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// If any post-processing failed. + /// + /// + object ApplyObjectPostProcessorsBeforeInitialization ( + object instance, string name); + + /// + /// Apply s + /// to the given existing object instance, invoking their + /// + /// methods. + /// + /// + ///

    + /// The returned object instance may be a wrapper around the original. + ///

    + ///
    + /// + /// The existing object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// If any post-processing failed. + /// + /// + object ApplyObjectPostProcessorsAfterInitialization ( + object instance, string name); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableFactoryObject.cs new file mode 100644 index 00000000..a84e3e12 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableFactoryObject.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Extension of the interface + /// that injects dependencies into the object managed by the factory. + /// + /// Bruno Baia + /// $Id: IConfigurableFactoryObject.cs,v 1.1 2007/07/29 19:39:27 markpollack Exp $ + public interface IConfigurableFactoryObject : IFactoryObject + { + /// + /// Gets the template object definition that should be used + /// to configure the instance of the object managed by this factory. + /// + IObjectDefinition ProductTemplate { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableListableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableListableObjectFactory.cs new file mode 100644 index 00000000..e0d16c2d --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableListableObjectFactory.cs @@ -0,0 +1,123 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// SPI interface to be implemented by most if not all listable object factories. + /// + /// + ///

    + /// Allows for framework-internal plug'n'play, e.g. in + /// . + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + public interface IConfigurableListableObjectFactory + : IListableObjectFactory, + IConfigurableObjectFactory, + IAutowireCapableObjectFactory + { + /// + /// Return the registered + /// for the + /// given object, allowing access to its property values and constructor + /// argument values. + /// + /// The name of the object. + /// + /// The registered + /// . + /// + /// + /// If there is no object with the given name. + /// + /// + /// In the case of errors. + /// + IObjectDefinition GetObjectDefinition(string name); + + /// + /// Return the registered + /// for the + /// given object, allowing access to its property values and constructor + /// argument values. + /// + /// The name of the object. + /// Whether to search parent object factories. + /// + /// The registered + /// . + /// + /// + /// If there is no object with the given name. + /// + /// + /// In the case of errors. + /// + IObjectDefinition GetObjectDefinition(string name, bool includeAncestors); + + + /// + /// Injects dependencies into the supplied instance + /// using the supplied . + /// + /// + /// The object instance that is to be so configured. + /// + /// + /// The name of the object definition expressing the dependencies that are to + /// be injected into the supplied instance. + /// + /// + /// An object definition that should be used to configure object. + /// + /// + object ConfigureObject(object target, string name, IObjectDefinition definition); + + /// + /// Ensure that all non-lazy-init singletons are instantiated, also + /// considering s. + /// + /// + ///

    + /// Typically invoked at the end of factory setup, if desired. + ///

    + ///

    + /// As this is a startup method, it should destroy already created singletons if + /// it fails, to avoid dangling resources. In other words, after invocation + /// of that method, either all or no singletons at all should be + /// instantiated. + ///

    + ///
    + /// + /// If one of the singleton objects could not be created. + /// + void PreInstantiateSingletons (); + + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableObjectFactory.cs new file mode 100644 index 00000000..9ac4faab --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableObjectFactory.cs @@ -0,0 +1,235 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Configuration interface to be implemented by most if not all object + /// factories. + /// + /// + ///

    + /// Provides the means to configure an object factory in addition to the + /// object factory client methods in the + /// interface. + ///

    + ///

    + /// Allows for framework-internal plug'n'play even when needing access to object + /// factory configuration methods. + ///

    + ///

    + /// When disposed, it will destroy all cached singletons in this factory. Call + /// when you want to shutdown + /// the factory. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: IConfigurableObjectFactory.cs,v 1.14 2007/07/16 21:06:21 markpollack Exp $ + public interface IConfigurableObjectFactory : IHierarchicalObjectFactory + { + /// + /// Set the parent of this object factory. + /// + /// + ///

    + /// Note that the parent shouldn't be changed: it should only be set outside + /// a constructor if it isn't available when an object of this class is + /// created. + ///

    + ///
    + new IObjectFactory ParentObjectFactory { set; } + + /// + /// Ignore the given dependency type for autowiring. + /// + /// + ///

    + /// To be invoked during factory configuration. + ///

    + ///

    + /// This will typically be used for dependencies that are resolved + /// in other ways, like + /// through . + ///

    + ///
    + /// + /// The to be ignored. + /// + void IgnoreDependencyType(Type type); + + + /// + /// Determines whether the specified object name is currently in creation.. + /// + /// Name of the object. + /// + /// true if the specified object name is currently in creation; otherwise, false. + /// + bool IsCurrentlyInCreation(string objectName); + + /// + /// Add a new + /// that will get applied to objects created by this factory. + /// + /// + ///

    + /// To be invoked during factory configuration. + ///

    + ///
    + /// + /// The + /// to register. + /// + void AddObjectPostProcessor(IObjectPostProcessor processor); + + /// + /// Returns the current number of registered + /// s. + /// + /// + /// The current number of registered + /// s. + /// + int ObjectPostProcessorCount + { + get; + } + + /// + /// Given an object name, create an alias. + /// + /// + ///

    + /// This is typically used to support names that are illegal within + /// XML ids (which are used for object names). + ///

    + ///

    + /// Typically invoked during factory configuration, but can also be + /// used for runtime registration of aliases. Therefore, a factory + /// implementation should synchronize alias access. + ///

    + ///
    + /// The name of the object. + /// + /// + /// The alias that will behave the same as the object name. + /// + /// + /// If there is no object with the given name. + /// + /// + /// If the alias is already in use. + /// + void RegisterAlias(string name, string theAlias); + + /// + /// Register the given existing object as singleton in the object factory, + /// under the given object name. + /// + /// + ///

    + /// Typically invoked during factory configuration, but can also be + /// used for runtime registration of singletons. Therefore, a factory + /// implementation should synchronize singleton access; it will have + /// to do this anyway if it supports lazy initialization of singletons. + ///

    + ///
    + /// + /// The name of the object. + /// + /// The existing object. + /// + /// If the singleton could not be registered. + /// + void RegisterSingleton(string name, object singleton); + + /// + /// Register the given custom + /// for all properties of the given . + /// + /// + ///

    + /// To be invoked during factory configuration. + ///

    + ///
    + /// + /// The required of the property. + /// + /// + /// The to register. + /// + void RegisterCustomConverter(Type requiredType, TypeConverter converter); + + /// + /// Does this object factory contains a singleton instance with the + /// supplied ? + /// + /// + ///

    + /// Only checks already instantiated singletons; does not return + /// for singleton object definitions that have + /// not been instantiated yet. + ///

    + ///

    + /// The main purpose of this method is to check manually registered + /// singletons (). This + /// method can also be used to check whether a singleton defined by an + /// object definition has already been created. + ///

    + ///

    + /// To check whether an object factory contains an object definition + /// with a given name, use the + /// + /// method. Calling both + /// + /// and definitively answers + /// the question of whether a specific object factory contains a + /// singleton object with the given name. + ///

    + ///

    + /// Use the + /// + /// method for general checks as to whether a factory knows about an + /// object with a given name (regrdless of whether the object in + /// question is a manually registed singleton instance or created by + /// an object definition)... this also has the happy bonus of also + /// checking any ancestor factories. + ///

    + ///
    + /// + /// The name of the (singleton) object to look for. + /// + /// + /// if this object factory contains a singleton + /// instance with the given . + /// + /// + /// + bool ContainsSingleton(string name); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IDestructionAwareObjectPostProcessor.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IDestructionAwareObjectPostProcessor.cs new file mode 100644 index 00000000..8e72247e --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/IDestructionAwareObjectPostProcessor.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Subinterface of + /// that adds + /// a before-destruction callback. + /// + /// + /// The typical usage will be to invoke custom destruction callbacks on + /// specific object types, matching corresponding initialization callbacks. + /// + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: IDestructionAwareObjectPostProcessor.cs,v 1.4 2006/04/09 07:18:47 markpollack Exp $ + public interface IDestructionAwareObjectPostProcessor : IObjectPostProcessor + { + /// + /// Apply this + /// to the + /// given new object instance before its destruction. Can invoke custom + /// destruction callbacks. + /// + /// The new object instance. + /// The name of the object. + /// + /// In case of errors. + /// + void PostProcessBeforeDestruction (object instance, string name); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IInstantiationAwareObjectPostProcessor.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IInstantiationAwareObjectPostProcessor.cs new file mode 100644 index 00000000..fca758e3 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/IInstantiationAwareObjectPostProcessor.cs @@ -0,0 +1,125 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using Spring.Objects.Factory.Support; + +namespace Spring.Objects.Factory.Config { + + /// + /// Subinterface of + /// + /// that adds a before-instantiation callback. + /// + /// + ///

    + /// Typical use cases might include being used to suppress the default + /// instantiation of specific target objects, perhaps in favour of creating + /// proxies with special Spring.Aop.ITargetSources (pooling targets, + /// lazily initializing targets, etc). + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: IInstantiationAwareObjectPostProcessor.cs,v 1.4 2007/08/22 08:49:39 markpollack Exp $ + public interface IInstantiationAwareObjectPostProcessor : IObjectPostProcessor + { + /// + /// Apply this + /// + /// before the target object gets instantiated. + /// + /// + ///

    + /// The returned object may be a proxy to use instead of the target + /// object, effectively suppressing the default instantiation of the + /// target object. + ///

    + ///

    + /// If the object is returned by this method is not + /// , the object creation process will be + /// short-circuited. The returned object will not be processed any + /// further; in particular, no further + /// + /// callbacks will be applied to it. This mechanism is mainly intended + /// for exposing a proxy instead of an actual target object. + ///

    + ///

    + /// This callback will only be applied to object definitions with an + /// object class. In particular, it will not be applied to + /// objects with a "factory-method" (i.e. objects that are to be + /// instantiated via a layer of indirection anyway). + ///

    + ///
    + /// + /// The of the target object that is to be + /// instantiated. + /// + /// + /// The name of the target object. + /// + /// + /// The object to expose instead of a default instance of the target + /// object. + /// + /// + /// In the case of any errors. + /// + /// + /// + object PostProcessBeforeInstantiation(Type objectType, string objectName); + + + /// + /// Perform operations after the object has been instantiated, via a constructor or factory method, + /// but before Spring property population (from explicit properties or autowiring) occurs. + /// + /// The object instance created, but whose properties have not yet been set + /// Name of the object. + /// true if properties should be set on the object; false if property population + /// should be skipped. Normal implementations should return true. Returning false will also + /// prevent any subsequent InstantiationAwareObjectPostProcessor instances from being + /// invoked on this object instance. + bool PostProcessAfterInstantiation(object objectInstance, string objectName); + + /// + /// Post-process the given property values before the factory applies them + /// to the given object. + /// + /// Allows for checking whether all dependencies have been + /// satisfied, for example based on a "Required" annotation on bean property setters. + /// Also allows for replacing the property values to apply, typically through + /// creating a new MutablePropertyValues instance based on the original PropertyValues, + /// adding or removing specific values. + /// + /// + /// The property values that the factory is about to apply (never null). + /// he relevant property infos for the target object (with ignored + /// dependency types - which the factory handles specifically - already filtered out) + /// The object instance created, but whose properties have not yet + /// been set. + /// Name of the object. + /// The actual property values to apply to the given object (can be the + /// passed-in PropertyValues instances0 or null to skip property population. + IPropertyValues PostProcessPropertyValues(IPropertyValues pvs, PropertyInfo[] pis, object objectInstance, + string objectName); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IObjectDefinition.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IObjectDefinition.cs new file mode 100644 index 00000000..a570193e --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/IObjectDefinition.cs @@ -0,0 +1,208 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Describes an object instance, which has property values, constructor + /// argument values, and further information supplied by concrete implementations. + /// + /// + ///

    + /// This is just a minimal interface: the main intention is to allow + /// + /// (like PropertyPlaceholderConfigurer) to access and modify property values. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: IObjectDefinition.cs,v 1.14 2007/07/30 17:52:22 markpollack Exp $ + public interface IObjectDefinition + { + /// + /// Return the property values to be applied to a new instance of the object. + /// + MutablePropertyValues PropertyValues { get; } + + /// + /// Return the constructor argument values for this object. + /// + ConstructorArgumentValues ConstructorArgumentValues { get; } + + /// + /// Return the event handlers for any events exposed by this object. + /// + EventValues EventHandlerValues { get; } + + /// + /// Return a description of the resource that this object definition + /// came from (for the purpose of showing context in case of errors). + /// + string ResourceDescription { get; } + + /// + /// Is this object definition a "template", i.e. not meant to be instantiated + /// itself but rather just serving as an object definition for configuration + /// templates used by . + /// + /// + /// if this object definition is a "template". + /// + bool IsTemplate { get; } + + /// + /// Is this object definition "abstract", i.e. not meant to be instantiated + /// itself but rather just serving as parent for concrete child object + /// definitions. + /// + /// + /// if this object definition is "abstract". + /// + bool IsAbstract { get; } + + /// + /// Return whether this a Singleton, with a single, shared instance + /// returned on all calls. + /// + /// + ///

    + /// If , an object factory will apply the Prototype + /// design pattern, with each caller requesting an instance getting an + /// independent instance. How this is defined will depend on the + /// object factory implementation. Singletons are the commoner type. + ///

    + ///
    + bool IsSingleton { get; } + + /// + /// Is this object lazily initialized? + /// + ///

    + /// Only applicable to a singleton object. + ///

    + ///

    + /// If , it will get instantiated on startup by object factories + /// that perform eager initialization of singletons. + ///

    + ///
    + bool IsLazyInit { get; } + + /// + /// Returns the of the object definition (if any). + /// + /// + /// A resolved object . + /// + /// + /// If the of the object definition is not a + /// resolved or . + /// + Type ObjectType { get; } + + /// + /// Returns the of the + /// of the object definition. + /// + /// Note that this does not have to be the actual type name used at runtime, + /// in case of a child definition overrding/inheriting the the type name from its + /// parent. It can be modifed during object factory post-processing, typically + /// replacing the original class name with a parsed variant of it. + /// Hence, do not consider this to be the definitive bean type at runtime + /// but rather only use it for parsing purposes at the individual object + /// definition level. + /// + string ObjectTypeName { get; set;} + + /// + /// The autowire mode as specified in the object definition. + /// + /// + ///

    + /// This determines whether any automagical detection and setting of + /// object references will happen. Default is + /// , + /// which means there's no autowire. + ///

    + ///
    + AutoWiringMode AutowireMode { get; } + + /// + /// The object names that this object depends on. + /// + /// + ///

    + /// The object factory will guarantee that these objects get initialized + /// before. + ///

    + ///

    + /// Note that dependencies are normally expressed through object properties + /// or constructor arguments. This property should just be necessary for + /// other kinds of dependencies like statics (*ugh*) or database + /// preparation on startup. + ///

    + ///
    + string[] DependsOn { get; } + + /// + /// The name of the initializer method. + /// + /// + ///

    + /// The default is , in which case there is no initializer method. + ///

    + ///
    + string InitMethodName { get; } + + /// + /// Return the name of the destroy method. + /// + /// + ///

    + /// The default is , in which case there is no destroy method. + ///

    + ///
    + string DestroyMethodName { get; } + + /// + /// The name of the factory method to use (if any). + /// + /// + ///

    + /// This method will be invoked with constructor arguments, or with no + /// arguments if none are specified. The static method will be invoked on + /// the specified . + ///

    + ///
    + string FactoryMethodName { get; } + + /// + /// The name of the factory object to use (if any). + /// + string FactoryObjectName { get; } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IObjectFactoryPostProcessor.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IObjectFactoryPostProcessor.cs new file mode 100644 index 00000000..f9d00094 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/IObjectFactoryPostProcessor.cs @@ -0,0 +1,72 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Allows for custom modification of an application context's object + /// definitions, adapting the object property values of the context's + /// underlying object factory. + /// + /// + ///

    + /// Application contexts can auto-detect + /// IObjectFactoryPostProcessor objects in their object definitions and + /// apply them before any other objects get created. + ///

    + ///

    + /// Useful for custom config files targeted at system administrators that + /// override object properties configured in the application context. + ///

    + ///

    + /// See PropertyResourceConfigurer and its concrete implementations for + /// out-of-the-box solutions that address such configuration needs. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.Net) + public interface IObjectFactoryPostProcessor + { + /// + /// Modify the application context's internal object factory after its + /// standard initialization. + /// + /// + ///

    + /// All object definitions will have been loaded, but no objects will have + /// been instantiated yet. This allows for overriding or adding properties + /// even to eager-initializing objects. + ///

    + ///
    + /// + /// The object factory used by the application context. + /// + /// + /// In case of errors. + /// + void PostProcessObjectFactory (IConfigurableListableObjectFactory factory); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IObjectPostProcessor.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IObjectPostProcessor.cs new file mode 100644 index 00000000..aef66001 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/IObjectPostProcessor.cs @@ -0,0 +1,99 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Allows for custom modification of new object instances, e.g. + /// checking for marker interfaces or wrapping them with proxies. + /// + /// + ///

    + /// Application contexts can auto-detect + /// + /// objects in their object definitions and apply them before any other + /// objects get created. Plain object factories allow for programmatic + /// registration of post-processors. + ///

    + ///

    + /// Typically, post-processors that populate objects via marker interfaces + /// or the like will implement + /// , + /// and post-processors that wrap objects with proxies will normally implement + /// . + ///

    + ///
    + /// Juergen Hoeller + /// Aleksandar Seovic (.NET) + /// $Id: IObjectPostProcessor.cs,v 1.9 2007/08/22 08:49:39 markpollack Exp $ + /// + public interface IObjectPostProcessor + { + /// + /// Apply this + /// to the given new object instance before any object initialization callbacks. + /// + /// + ///

    + /// The object will already be populated with property values. + /// The returned object instance may be a wrapper around the original. + ///

    + ///
    + /// + /// The new object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// In case of errors. + /// + object PostProcessBeforeInitialization(object instance, string name); + + /// + /// Apply this to the + /// given new object instance after any object initialization callbacks. + /// + /// + ///

    + /// The object will already be populated with property values. The returned object + /// instance may be a wrapper around the original. + ///

    + ///
    + /// + /// The new object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// In case of errors. + /// + object PostProcessAfterInitialization(object instance, string objectName); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IVariableSource.cs new file mode 100644 index 00000000..6494a0c8 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/IVariableSource.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Defines contract that different variable sources have to implement. + /// + /// + ///

    + /// The "variable sources" are objects containing name-value pairs + /// that allow a variable value to be retrieved for the given name.

    + ///

    + /// Out of the box, Spring.NET supports a number of variable sources, + /// that allow users to obtain variable values from .NET config files, + /// Java-style property files, environment, registry, etc.

    + ///

    + /// Users can always write their own variable sources implementations, + /// that will allow them to load variable values from the database or + /// other proprietary data source.

    + ///
    + /// + /// + /// + /// + /// + /// + /// Aleksandar Seovic + /// $Id: IVariableSource.cs,v 1.3 2007/08/02 22:18:32 markpollack Exp $ + public interface IVariableSource + { + /// + /// Resolves variable value for the specified variable name. + /// + /// + /// The name of the variable to resolve. + /// + /// + /// The variable value if able to resolve, null otherwise. + /// + string ResolveVariable(string name); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/InstantiationAwareObjectPostProcessorAdapter.cs b/src/Spring/Spring.Core/Objects/Factory/Config/InstantiationAwareObjectPostProcessorAdapter.cs new file mode 100644 index 00000000..f69ad6b1 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/InstantiationAwareObjectPostProcessorAdapter.cs @@ -0,0 +1,224 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using Spring.Objects.Factory.Support; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Adapter that implements all methods on + /// as no-ops, which will not change normal processing of each object instantiated + /// by the container. Subclasses may override merely those methods that they are + /// actually interested in. + /// + /// + /// Note that this base class is only recommendable if you actually require + /// functionality. If all you need + /// is plain functionality, prefer a straight + /// implementation of that (simpler) interface. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: InstantiationAwareObjectPostProcessorAdapter.cs,v 1.1 2008/04/02 18:02:24 markpollack Exp $ + public abstract class InstantiationAwareObjectPostProcessorAdapter : SmartInstantiationAwareObjectPostProcessor + { + #region SmartInstantiationAwareObjectPostProcessor Members + + /// + /// Predicts the type of the object to be eventually returned from this + /// processors PostProcessBeforeInstantiation callback. + /// + /// The raw Type of the object. + /// Name of the object. + /// The type of the object, or null if not predictable. + /// in case of errors + public virtual Type PredictObjectType(Type objectType, string objectName) + { + return null; + } + + /// + /// Determines the candidate constructors to use for the given object. + /// + /// The raw Type of the object. + /// Name of the object. + /// The candidate constructors, or null if none specified + /// in case of errors + public virtual ConstructorInfo[] DetermineCandidateConstructors(Type objectType, string objectName) + { + return null; + } + + #endregion + + #region IInstantiationAwareObjectPostProcessor Members + + /// + /// Apply this + /// + /// before the target object gets instantiated. + /// + /// + ///

    + /// The returned object may be a proxy to use instead of the target + /// object, effectively suppressing the default instantiation of the + /// target object. + ///

    + ///

    + /// If the object is returned by this method is not + /// , the object creation process will be + /// short-circuited. The returned object will not be processed any + /// further; in particular, no further + /// + /// callbacks will be applied to it. This mechanism is mainly intended + /// for exposing a proxy instead of an actual target object. + ///

    + ///

    + /// This callback will only be applied to object definitions with an + /// object class. In particular, it will not be applied to + /// objects with a "factory-method" (i.e. objects that are to be + /// instantiated via a layer of indirection anyway). + ///

    + ///
    + /// + /// The of the target object that is to be + /// instantiated. + /// + /// + /// The name of the target object. + /// + /// + /// The object to expose instead of a default instance of the target + /// object. + /// + /// + /// In the case of any errors. + /// + /// + /// + public virtual object PostProcessBeforeInstantiation(Type objectType, string objectName) + { + return null; + } + + /// + /// Perform operations after the object has been instantiated, via a constructor or factory method, + /// but before Spring property population (from explicit properties or autowiring) occurs. + /// + /// The object instance created, but whose properties have not yet been set + /// Name of the object. + /// true if properties should be set on the object; false if property population + /// should be skipped. Normal implementations should return true. Returning false will also + /// prevent any subsequent InstantiationAwareObjectPostProcessor instances from being + /// invoked on this object instance. + public virtual bool PostProcessAfterInstantiation(object objectInstance, string objectName) + { + return true; + } + + /// + /// Post-process the given property values before the factory applies them + /// to the given object. + /// + /// Allows for checking whether all dependencies have been + /// satisfied, for example based on a "Required" annotation on bean property setters. + /// Also allows for replacing the property values to apply, typically through + /// creating a new MutablePropertyValues instance based on the original PropertyValues, + /// adding or removing specific values. + /// + /// + /// The property values that the factory is about to apply (never null). + /// he relevant property infos for the target object (with ignored + /// dependency types - which the factory handles specifically - already filtered out) + /// The object instance created, but whose properties have not yet + /// been set. + /// Name of the object. + /// The actual property values to apply to the given object (can be the + /// passed-in PropertyValues instances0 or null to skip property population. + public virtual IPropertyValues PostProcessPropertyValues(IPropertyValues pvs, PropertyInfo[] pis, object objectInstance, + string objectName) + { + return pvs; + } + + #endregion + + #region IObjectPostProcessor Members + + /// + /// Apply this + /// to the given new object instance before any object initialization callbacks. + /// + /// + ///

    + /// The object will already be populated with property values. + /// The returned object instance may be a wrapper around the original. + ///

    + ///
    + /// + /// The new object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// In case of errors. + /// + public virtual object PostProcessBeforeInitialization(object instance, string name) + { + return instance; + } + + /// + /// Apply this to the + /// given new object instance after any object initialization callbacks. + /// + /// + ///

    + /// The object will already be populated with property values. The returned object + /// instance may be a wrapper around the original. + ///

    + ///
    + /// + /// The new object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// In case of errors. + /// + public virtual object PostProcessAfterInitialization(object instance, string objectName) + { + return instance; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ListFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ListFactoryObject.cs new file mode 100644 index 00000000..db1ededc --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ListFactoryObject.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Simple factory for shared instances. + /// + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: ListFactoryObject.cs,v 1.8 2007/07/31 03:47:39 markpollack Exp $ + [Serializable] + public class ListFactoryObject : AbstractFactoryObject + { + private IList _sourceList; + private Type _targetListType = typeof (ArrayList); + + #region Properties + + /// + /// Set the source . + /// + /// + ///

    + /// This value will be used to populate the + /// returned by this factory. + ///

    + ///
    + public IList SourceList + { + set { this._sourceList = value; } + } + + /// + /// Set the of the + /// implementation to use. + /// + /// + ///

    + /// The default is the . + ///

    + ///
    + public Type TargetListType + { + set + { + AssertUtils.ArgumentNotNull(value, "value"); + if (!typeof (IList).IsAssignableFrom(value)) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "The Type passed to the TargetListType property must implement the '{0}' interface.", + ObjectType.FullName)); + } + if (value.IsInterface) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "The Type passed to the TargetListType property cannot be an interface; it must be a concrete class that implements the '{0}' interface.", + ObjectType.FullName)); + } + if (value.IsAbstract) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "The Type passed to the TargetListType property cannot be abstract (MustInherit in VisualBasic.NET); it must be a concrete class that implements the '{0}' interface.", + ObjectType.FullName)); + } + this._targetListType = value; + } + } + + /// + /// The of objects created by this factory. + /// + /// + /// Always returns the . + /// + public override Type ObjectType + { + get { return typeof (IList); } + } + + #endregion + + /// + /// Constructs a new instance of the target dictionary. + /// + /// The new instance. + protected override object CreateInstance() + { + if (this._sourceList == null) + { + throw new ArgumentException("The 'SourceList' property cannot be null (Nothing in Visual Basic.NET)."); + } + IList result = (IList) ObjectUtils.InstantiateType(this._targetListType); + foreach (object obj in this._sourceList) + { + result.Add(obj); + } + return result; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/LogFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/LogFactoryObject.cs new file mode 100644 index 00000000..01901ade --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/LogFactoryObject.cs @@ -0,0 +1,171 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// implementation that + /// creates instances of the class. + /// + /// + ///

    + /// Typically used for retrieving shared + /// instances for common topics (such as the 'DAL', 'BLL', etc). The + /// + /// property determines the name of the + /// Common.Logging logger. + ///

    + ///
    + /// Rick Evans + /// + /// $Id: LogFactoryObject.cs,v 1.3 2007/10/21 18:17:41 markpollack Exp $ + [Serializable] + public class LogFactoryObject : IFactoryObject, IInitializingObject + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + public LogFactoryObject() + { + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The name of the instance served up by + /// this factory. + /// + /// + /// If the supplied is + /// or contains only whitespace character(s). + /// + public LogFactoryObject(string logName) + { + LogName = logName; + } + + #endregion + + /// + /// The name of the instance served up by + /// this factory. + /// + /// + /// The name of the instance served up by + /// this factory. + /// + /// + /// If the supplied to the setter is + /// or contains only whitespace character(s). + /// + public string LogName + { + get { return this.logName; } + set + { + AssertUtils.ArgumentHasText(value, "The 'LogName' property must have a value."); + this.logName = value.Trim(); + } + } + + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + /// An instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + public object GetObject() + { + if (this.log == null) + { + ValidateProperties(); + this.log = LogManager.GetLogger(LogName); + } + return this.log; + } + + /// + /// Return the type of object that this + /// creates, or + /// if not known in advance. + /// + /// + public Type ObjectType + { + get { return typeof(ILog); } + } + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + /// + public bool IsSingleton + { + get { return true; } + } + + /// + /// Invoked by an + /// after it has set all object properties supplied + /// (and satisfied the + /// + /// and + /// interfaces). + /// + /// + /// In the event of misconfiguration (such as failure to set an essential + /// property) or if initialization fails. + /// + /// + public void AfterPropertiesSet() + { + ValidateProperties(); + } + + private void ValidateProperties() + { + if (StringUtils.IsNullOrEmpty(LogName)) + { + throw new ArgumentException("The 'LogName' property has not been set."); + } + } + + private ILog log; + private string logName; + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/MethodInvokingFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/MethodInvokingFactoryObject.cs new file mode 100644 index 00000000..fc85033f --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/MethodInvokingFactoryObject.cs @@ -0,0 +1,271 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Objects.Support; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// An that returns a value + /// that is the result of a or instance method invocation. + /// + /// + ///

    + /// Note that this class generally is expected to be used for accessing factory methods, + /// and as such defaults to operating in singleton mode. The first request to + /// + /// by the owning object factory will cause a method invocation, the return + /// value of which will be cached for all subsequent requests. The + /// property may be set to + /// , to cause this factory to invoke the target method each + /// time it is asked for an object. + ///

    + ///

    + /// A target method may be specified by setting the + /// property to a string representing + /// the method name, with specifying + /// the that the method is defined on. + /// Alternatively, a target instance method may be specified, by setting the + /// property as the target object, and + /// the property as the name of the + /// method to call on that target object. Arguments for the method invocation may be + /// specified by setting the property. + ///

    + ///

    + /// Another (esoteric) use case for this factory object is when one needs to call a method + /// that doesn't return any value (for example, a class method to + /// force some sort of initialization to happen)... this use case is not supported by + /// factory-methods, since a return value is needed to become the object. + ///

    + ///

    + /// + /// This class depends on the + /// + /// method being called after all properties have been set, as per the + /// contract. If you are + /// using this class outside of a Spring.NET IoC container, you must call one of either + /// or + /// yourself to ready the object's internal + /// state, or you will get a nasty . + /// + ///

    + ///
    + /// + ///

    + /// The following example uses an instance of this class to call a + /// factory method... + ///

    + /// + /// + /// + /// + /// + /// + /// + /// 1st + /// 2nd + /// and 3rd arguments + /// + /// + /// + /// + ///

    + /// The following example is similar to the preceding example; the only pertinent difference is the fact that + /// a number of different objects are passed as arguments, demonstrating that not only simple value types + /// are valid as elements of the argument list... + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// 1st + /// + /// + /// + /// + /// + /// + /// http://www.springframework.net/ + /// + /// + /// + /// + /// + ///

    + /// Named parameters are also supported... this next example yields the same results as + /// the preceding example (that did not use named arguments). + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// 1st + /// and 3rd arguments + /// 2nd + /// + /// + /// + /// + ///

    + /// Similarly, the following example uses an instance of this class to call an instance method... + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///

    + /// The above example could also have been written using an anonymous inner object definition... if the + /// object on which the method is to be invoked is not going to be used outside of the factory object + /// definition, then this is the preferred idiom because it limits the scope of the object on which the + /// method is to be invoked to the surrounding factory object. + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Colin Sampaleanu + /// Juergen Hoeller + /// Rick Evans (.NET) + /// Simon White (.NET) + /// $Id: MethodInvokingFactoryObject.cs,v 1.15 2007/03/16 04:01:38 aseovic Exp $ + /// + /// + [Serializable] + public class MethodInvokingFactoryObject : ArgumentConvertingMethodInvoker, IFactoryObject, IInitializingObject + { + private bool singleton = true; + private object singletonObject; + + /// + /// If a singleton should be created, or a new object on each request. + /// Defaults to . + /// + public bool IsSingleton + { + get { return singleton; } + set { singleton = value; } + } + + /// + /// Return the return value of the method + /// that this factory invokes, or if not + /// known in advance. + /// + /// + ///

    + /// If the return value of the method that this factory is to invoke is + /// , then the + /// will be returned (in accordance with the + /// contract that + /// treats a value as a configuration error). + ///

    + ///
    + /// + public Type ObjectType + { + get + { + Type objectType = null; + if (GetPreparedMethod() != null) + { + objectType = GetPreparedMethod().ReturnType; + if (objectType.Equals(typeof (void))) + { + objectType = Void.GetType(); + } + } + return objectType; + } + } + + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + ///

    + /// Returns the return value of the method that is to be invoked. + ///

    + ///

    + /// Will return the same value each time if the + /// + /// property value is . + ///

    + ///
    + /// + /// An instance (possibly shared or independent) of the object managed by + /// this factory. + /// + /// + public object GetObject() + { + if (singleton) + { + if (singletonObject == null) + { + singletonObject = Invoke(); + } + return singletonObject; + } + return Invoke(); + } + + /// + /// Prepares this method invoker. + /// + /// + /// If all required properties are not set. + /// + /// + /// If the specified method could not be found. + /// + /// + public void AfterPropertiesSet() + { + Prepare(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionHolder.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionHolder.cs new file mode 100644 index 00000000..d631eeb7 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionHolder.cs @@ -0,0 +1,137 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Holder for an with + /// name and aliases. + /// + /// + ///

    + /// Recognized by + /// + /// for inner object definitions. Registered by + /// , + /// which also uses it as general holder for a parsed object definition. + ///

    + ///

    + /// Can also be used for programmatic registration of inner object + /// definitions. If you don't care about the functionality offered by the + /// interface and the like, + /// registering + /// or is good enough. + ///

    + ///
    + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: ObjectDefinitionHolder.cs,v 1.2 2007/08/07 22:05:20 markpollack Exp $ + [Serializable] + public class ObjectDefinitionHolder + { + private IObjectDefinition objectDefinition; + private string objectName; + private string[] aliases; + + #region Constructor () / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The object definition to be held by this instance. + /// + /// + /// The name of the object definition. + /// + public ObjectDefinitionHolder(IObjectDefinition definition, string name) + : this(definition, name, StringUtils.EmptyStrings) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The object definition to be held by this instance. + /// + /// The name of the object. + /// + /// Any aliases for the supplied + /// + public ObjectDefinitionHolder( + IObjectDefinition definition, string name, string[] aliases) + { + this.objectDefinition = definition; + this.objectName = name; + this.aliases = aliases == null ? StringUtils.EmptyStrings : aliases; + } + + #endregion + + #region Properties + + /// + /// The held by this + /// instance. + /// + public IObjectDefinition ObjectDefinition + { + get { return objectDefinition; } + } + + /// + /// The name of the object definition. + /// + public string ObjectName + { + get { return objectName; } + } + + /// + /// Any aliases for the object definition. + /// + /// + ///

    + /// Guaranteed to never return ; if the associated + /// + /// does not have any aliases associated with it, then an empty + /// array will be returned. + ///

    + ///
    + public string[] Aliases + { + get { return aliases; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionVisitor.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionVisitor.cs new file mode 100644 index 00000000..007edef0 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionVisitor.cs @@ -0,0 +1,380 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; + +using Spring.Collections; +using Spring.Util; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Visitor class for traversing objects, in particular + /// the property values and constructor arguments contained in them resolving + /// object metadata values. + /// + /// + /// Used by and + /// to parse all string values contained in a ObjectDefinition, resolving any placeholders found. + /// + /// Mark Pollack + /// $Id: ObjectDefinitionVisitor.cs,v 1.9 2008/02/18 00:02:27 bbaia Exp $ + public class ObjectDefinitionVisitor + { + private IVariableSource variableSource; + + + /// + /// Initializes a new instance of the class, + /// applying the specified IVariableSource to all object metadata values. + /// + /// The variable source. + public ObjectDefinitionVisitor(IVariableSource variableSource) + { + this.variableSource = variableSource; + } + + /// + /// Initializes a new instance of the class + /// for subclassing + /// + /// Subclasses should override the ResolveStringValue method + protected ObjectDefinitionVisitor() + { + } + + /// + /// Traverse the given ObjectDefinition object and the MutablePropertyValues + /// and ConstructorArgumentValues contained in them. + /// + /// The object definition to traverse. + public virtual void VisitObjectDefinition(IObjectDefinition definition) + { + VisitObjectTypeName(definition); + VisitPropertyValues(definition); + + ConstructorArgumentValues cas = definition.ConstructorArgumentValues; + if (cas != null) + { + VisitIndexedArgumentValues(cas.IndexedArgumentValues); + VisitNamedArgumentValues(cas.NamedArgumentValues); + VisitGenericArgumentValues(cas.GenericArgumentValues); + } + } + + /// + /// Visits the ObjectDefinition property ObjectTypeName, replacing string values using + /// the specified IVariableSource. + /// + /// The object definition. + protected virtual void VisitObjectTypeName(IObjectDefinition objectDefinition) + { + string objectTypeName = objectDefinition.ObjectTypeName; + if (objectTypeName != null) + { + string resolvedName = ResolveStringValue(objectTypeName).ToString(); + if (!objectTypeName.Equals(resolvedName)) + { + objectDefinition.ObjectTypeName = resolvedName; + } + } + } + + + /// + /// Visits the property values of the ObjectDefinition, replacing string values + /// using the specified IVariableSource. + /// + /// The object definition. + protected virtual void VisitPropertyValues(IObjectDefinition objectDefinition) + { + MutablePropertyValues pvs = objectDefinition.PropertyValues; + if (pvs != null) + { + for (int j = 0; j < pvs.PropertyValues.Length; j++) + { + PropertyValue pv = pvs.PropertyValues[j]; + object newVal = ResolveValue(pv.Value); + if (!ObjectUtils.NullSafeEquals(newVal, pv.Value)) + { + pvs.Add(pv.Name, newVal); + } + } + } + } + + /// + /// Visits the indexed constructor argument values, replacing string values using the + /// specified IVariableSource. + /// + /// The indexed argument values. + protected virtual void VisitIndexedArgumentValues(IDictionary ias) + { + foreach (ConstructorArgumentValues.ValueHolder valueHolder in ias.Values) + { + ConfigureConstructorArgument(valueHolder); + } + } + + /// + /// Visits the named constructor argument values, replacing string values using the + /// specified IVariableSource. + /// + /// The named argument values. + protected virtual void VisitNamedArgumentValues(IDictionary nav) + { + foreach (ConstructorArgumentValues.ValueHolder valueHolder in nav.Values) + { + ConfigureConstructorArgument(valueHolder); + } + } + + /// + /// Visits the generic constructor argument values, replacing string values using + /// the specified IVariableSource. + /// + /// The genreic argument values. + protected virtual void VisitGenericArgumentValues(IList gav) + { + foreach (ConstructorArgumentValues.ValueHolder valueHolder in gav) + { + ConfigureConstructorArgument(valueHolder); + } + } + + /// + /// Configures the constructor argument ValueHolder. + /// + /// The vconstructor alue holder. + protected void ConfigureConstructorArgument(ConstructorArgumentValues.ValueHolder valueHolder) + { + object newVal = ResolveValue(valueHolder.Value); + if (!ObjectUtils.NullSafeEquals(newVal, valueHolder.Value)) + { + valueHolder.Value = newVal; + } + } + + /// + /// Resolves the given value taken from an object definition according to its type + /// + /// the value to resolve + /// the resolved value + protected virtual object ResolveValue(object value) + { + if (value is IObjectDefinition) + { + VisitObjectDefinition((IObjectDefinition)value); + } + else if (value is ObjectDefinitionHolder) + { + VisitObjectDefinition( ((ObjectDefinitionHolder)value).ObjectDefinition); + } + else if (value is RuntimeObjectReference) + { + RuntimeObjectReference ror = (RuntimeObjectReference)value; + //name has to be of string type. + string newObjectName = ResolveStringValue(ror.ObjectName).ToString(); + if (!newObjectName.Equals(ror.ObjectName)) + { + return new RuntimeObjectReference(newObjectName); + } + } + else if (value is ManagedList) + { + VisitManagedList((ManagedList)value); + } + else if (value is ManagedSet) + { + VisitManagedSet((ManagedSet)value); + } + else if (value is ManagedDictionary) + { + VisitManagedDictionary((ManagedDictionary)value); + } + else if (value is NameValueCollection) + { + VisitNameValueCollection((NameValueCollection)value); + } + else if (value is TypedStringValue) + { + TypedStringValue typedStringValue = (TypedStringValue)value; + String stringValue = typedStringValue.Value; + if (stringValue != null) + { + String visitedString = ResolveStringValue(stringValue).ToString(); + typedStringValue.Value = visitedString; + } + } + else if (value is string) + { + return ResolveStringValue((string)value); + } + else if (value is ExpressionHolder) + { + ExpressionHolder holder = (ExpressionHolder)value; + string newExpressionString = ResolveStringValue(holder.ExpressionString).ToString(); + return new ExpressionHolder(newExpressionString); + } + return value; + } + + /// + /// Visits the ManagedList property ElementTypeName and + /// calls for list element. + /// + protected virtual void VisitManagedList(ManagedList listVal) + { + string elementTypeName = listVal.ElementTypeName; + if (elementTypeName != null) + { + string resolvedName = ResolveStringValue(elementTypeName).ToString(); + if (!elementTypeName.Equals(resolvedName)) + { + listVal.ElementTypeName = resolvedName; + } + } + + for (int i = 0; i < listVal.Count; ++i) + { + object oldValue = listVal[i]; + object newValue = ResolveValue(oldValue); + if (!ObjectUtils.NullSafeEquals(newValue, oldValue)) + { + listVal[i] = newValue; + } + } + } + + /// + /// Visits the ManagedSet property ElementTypeName and + /// calls for list element. + /// + protected virtual void VisitManagedSet(ManagedSet setVal) + { + string elementTypeName = setVal.ElementTypeName; + if (elementTypeName != null) + { + string resolvedName = ResolveStringValue(elementTypeName).ToString(); + if (!elementTypeName.Equals(resolvedName)) + { + setVal.ElementTypeName = resolvedName; + } + } + + ISet clone = (ISet)setVal.Clone(); + foreach (object oldValue in clone) + { + object newValue = ResolveValue(oldValue); + if (!ObjectUtils.NullSafeEquals(newValue, oldValue)) + { + setVal.Remove(oldValue); + setVal.Add(newValue); + } + } + } + + /// + /// Visits the ManagedSet properties KeyTypeName and ValueTypeName and + /// calls for dictionary's value element. + /// + protected virtual void VisitManagedDictionary(ManagedDictionary dictVal) + { + string keyTypeName = dictVal.KeyTypeName; + if (keyTypeName != null) + { + string resolvedName = ResolveStringValue(keyTypeName).ToString(); + if (!keyTypeName.Equals(resolvedName)) + { + dictVal.KeyTypeName = resolvedName; + } + } + + string valueTypeName = dictVal.ValueTypeName; + if (valueTypeName != null) + { + string resolvedName = ResolveStringValue(valueTypeName).ToString(); + if (!valueTypeName.Equals(resolvedName)) + { + dictVal.ValueTypeName = resolvedName; + } + } + + Hashtable mods = new Hashtable(); + foreach (DictionaryEntry entry in dictVal) + { + object oldValue = entry.Value; + object newValue = ResolveValue(oldValue); + if (!ObjectUtils.NullSafeEquals(newValue, oldValue)) + { + mods[entry.Key] = newValue; + } + } + foreach (DictionaryEntry entry in mods) + { + dictVal[entry.Key] = entry.Value; + } + } + + /// + /// Visits the elements of a NameValueCollection and calls + /// for value of each element. + /// + protected virtual void VisitNameValueCollection(NameValueCollection collection) + { + foreach (string key in collection.AllKeys) + { + string oldValue = collection[key]; + string newValue = ResolveValue(oldValue) as string; + if (!ObjectUtils.NullSafeEquals(newValue, oldValue)) + { + collection[key] = newValue; + } + } + } + + /// + /// Looks up the value of the given variable name in the configured . + /// + /// The name of the variable to be looked up + /// + /// The value of this variable, as returned from the passed + /// into the constructor + /// + /// If no has been configured. + protected virtual object ResolveStringValue(string variableName) + { + if (variableSource == null) + { + throw new InvalidOperationException("No IVariableSource specified - pass an instance " + + "of this object into the constructor or override the 'ResolveStringValue' method"); + } + return variableSource.ResolveVariable(variableName); + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ObjectFactoryCreatingFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectFactoryCreatingFactoryObject.cs new file mode 100644 index 00000000..3c894ab9 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectFactoryCreatingFactoryObject.cs @@ -0,0 +1,178 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Returns a value that is an + /// that + /// returns an object from an + /// . + /// + /// + ///

    + /// The primary motivation of this class is to avoid having a client object + /// directly calling the + /// + /// method to get a prototype object out of an + /// , which would be a + /// violation of the inversion of control principle. With the use of this + /// class, the client object can be fed an + /// as a property + /// that directly returns one target prototype object. + ///

    + ///

    + /// The object referred to by the value of the + /// + /// property does not have to be a prototype object, but there is little + /// to no point in using this class in conjunction with a singleton object. + ///

    + ///
    + /// + ///

    + /// The following XML configuration snippet illustrates the use of this + /// class... + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Colin Sampaleanu + /// Simon White (.NET) + /// $Id: ObjectFactoryCreatingFactoryObject.cs,v 1.9 2007/03/16 04:01:38 aseovic Exp $ + [Serializable] + public class ObjectFactoryCreatingFactoryObject + : AbstractFactoryObject, IObjectFactoryAware + { + private string _targetObjectName; + private IObjectFactory _objectFactory; + + #region Properties + + /// + /// Sets the name of the target object. + /// + public string TargetObjectName + { + set { _targetObjectName = value; } + } + + /// + /// The target factory that will be used to perform the lookup + /// of the object referred to by the + /// property. + /// + /// + /// The owning + /// (will never be ). + /// + /// + /// In case of initialization errors. + /// + /// + public IObjectFactory ObjectFactory + { + set { this._objectFactory = value; } + } + + /// + /// The of object created by this factory. + /// + public override Type ObjectType + { + get { return typeof (IGenericObjectFactory); } + } + + #endregion + + /// + /// Returns an instance of the object factory. + /// + /// The object factory. + protected override object CreateInstance() + { + return new GenericObjectFactory(this); + } + + /// + /// Invoked by an + /// after it has set all supplied object properties. + /// + /// + /// In the event of misconfiguration (such as failure to set an essential + /// property) or if initialization fails. + /// + /// + public override void AfterPropertiesSet() + { + if(StringUtils.IsNullOrEmpty(_targetObjectName)) + { + throw new ArgumentException("The 'TargetObjectName' property must have a value."); + } + base.AfterPropertiesSet (); + } + + private sealed class GenericObjectFactory : IGenericObjectFactory + { + private ObjectFactoryCreatingFactoryObject _enclosing; + + /// + /// Creates a new instance of the GenericObjectFactory class. + /// + /// + /// The enclosing + /// . + /// + public GenericObjectFactory( + ObjectFactoryCreatingFactoryObject enclosing) + { + _enclosing = enclosing; + } + + /// + /// Returns the object created by the enclosed object factory. + /// + /// The created object. + public object GetObject() + { + return _enclosing._objectFactory.GetObject(_enclosing._targetObjectName); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ObjectReferenceFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectReferenceFactoryObject.cs new file mode 100644 index 00000000..66ac0df3 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectReferenceFactoryObject.cs @@ -0,0 +1,134 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Util; + +namespace Spring.Objects.Factory.Config +{ + /// + /// An implementation + /// that exposes an arbitrary target object under a different name. + /// + /// + ///

    + /// Usually, the target object will reside in a different object + /// definition file, using this + /// to link it in + /// and expose it under a different name. Effectively, this corresponds + /// to an alias for the target object. + ///

    + /// + /// For XML based object definition files, a <alias> + /// tag is available that effectively achieves the same. + /// + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: ObjectReferenceFactoryObject.cs,v 1.3 2007/03/16 04:01:38 aseovic Exp $ + /// + [Serializable] + public sealed class ObjectReferenceFactoryObject + : IFactoryObject, IObjectFactoryAware + { + private string _targetObjectName; + private IObjectFactory _objectFactory; + + /// + /// The name of the target object. + /// + /// + ///

    + /// The target object may potentially be defined in a different object + /// definition file. + ///

    + ///
    + /// The name of the target object. + public string TargetObjectName + { + set { _targetObjectName = value; } + } + + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + /// An instance (possibly shared or independent) of the object managed by + /// this factory. + /// + /// + public object GetObject() + { + return _objectFactory.GetObject(_targetObjectName); + } + + /// + /// Return the type of object that this + /// creates, or + /// if not known in advance. + /// + /// + public Type ObjectType + { + get { return _objectFactory.GetType(_targetObjectName); } + } + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + /// + public bool IsSingleton + { + get { return _objectFactory.IsSingleton(_targetObjectName); } + } + + /// + /// Callback that supplies the owning factory to an object instance. + /// + /// + /// The owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + /// + /// In case of initialization errors. + /// + /// + public IObjectFactory ObjectFactory + { + set + { + // the call to set this property occurs 'after' all properties have been set... + _objectFactory = value; + if (StringUtils.IsNullOrEmpty(_targetObjectName)) + { + throw new ArgumentException( + "The 'TargetObjectName' property is required."); + } + if (!_objectFactory.ContainsObject(_targetObjectName)) + { + throw new NoSuchObjectDefinitionException( + _targetObjectName, _objectFactory.ToString()); + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyFileVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyFileVariableSource.cs new file mode 100644 index 00000000..9dae39c1 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyFileVariableSource.cs @@ -0,0 +1,102 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.IO; +using Spring.Core.IO; +using Spring.Util; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Implementation of that + /// resolves variable name against Java-style property file. + /// + /// + /// Aleksandar Seovic + /// $Id: PropertyFileVariableSource.cs,v 1.5 2007/08/08 17:47:13 bbaia Exp $ + [Serializable] + public class PropertyFileVariableSource : IVariableSource + { + private IResource[] locations; + private Properties properties; + + /// + /// Gets or sets the locations of the property files + /// to read properties from. + /// + /// + /// The locations of the property files + /// to read properties from. + /// + public IResource[] Locations + { + get { return locations; } + set { locations = value; } + } + + /// + /// Convinience property. Gets or sets a single location + /// to read properties from. + /// + /// + /// A location to read properties from. + /// + public IResource Location + { + set { locations = new IResource[] { value} ;} + } + + /// + /// Resolves variable value for the specified variable name. + /// + /// + /// The name of the variable to resolve. + /// + /// + /// The variable value if able to resolve, null otherwise. + /// + public string ResolveVariable(string name) + { + if (properties == null) + { + InitProperties(); + } + return properties.GetProperty(name); + } + + /// + /// Initializes properties based on the specified + /// property file locations. + /// + private void InitProperties() + { + properties = new Properties(); + foreach (IResource location in locations) + { + using (Stream input = location.InputStream) + { + properties.Load(input); + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyOverrideConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyOverrideConfigurer.cs new file mode 100644 index 00000000..116c4bad --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyOverrideConfigurer.cs @@ -0,0 +1,203 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using System.Globalization; +using Common.Logging; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Overrides default values in one or more object definitions. + /// + /// + ///

    + /// Instances of this class override already existing values, and is + /// thus best suited to replacing defaults. If you need to replace + /// placeholder values, consider using the + /// + /// class instead. + ///

    + ///

    + /// In contrast to the + /// + /// class, the original object definition can have default + /// values or no values at all for such object properties. If an overriding + /// configuration file does not have an entry for a certain object property, + /// the default object value is left as is. Also note that it is not + /// immediately obvious to discern which object definitions will be mutated by + /// one or more + /// s + /// simply by looking at the object configuration. + ///

    + ///

    + /// Each line in a referenced configuration file is expected to take the + /// following form... + ///

    + /// + /// + /// + ///

    + /// The name.property key refers to the object name and the + /// property that is to be overridden; and the value is the overridding + /// value that will be inserted into the appropriate object definition's + /// named property. + ///

    + ///

    + /// Please note that in the case of multiple + /// s + /// that define different values for the same object definition value, the + /// last overridden value will win (due to the fact that the values + /// supplied by previous + /// s + /// will be overridden). + ///

    + ///
    + /// + ///

    + /// The following XML context definition defines an object that has a number + /// of properties, all of which have default values... + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + ///

    + /// What follows is a .NET config file snippet for the above example (assuming + /// the need to override one of the default values)... + ///

    + /// + /// + /// + /// + /// + /// + ///
    + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: PropertyOverrideConfigurer.cs,v 1.11 2007/10/10 16:31:39 bbaia Exp $ + /// + /// + /// + [Serializable] + public class PropertyOverrideConfigurer : PropertyResourceConfigurer + { + private ILog _logger = LogManager.GetLogger(typeof (PropertyOverrideConfigurer)); + + /// + /// Apply the given properties to the supplied + /// . + /// + /// + /// The + /// used by the application context. + /// + /// The properties to apply. + /// + /// If an error occured. + /// + protected override void ProcessProperties( + IConfigurableListableObjectFactory factory, NameValueCollection props) + { + foreach (string key in props.AllKeys) + { + ProcessKey(factory, key, props[key]); + } + } + + /// + /// Process the given key as 'name.property' entry. + /// + /// + /// The object factory containing the object definitions that are to be + /// processed. + /// + /// The key. + /// The value. + /// + /// If an error occurs. + /// + /// + /// If the property was not well formed (i.e. not in the format "name.property"). + /// + protected virtual void ProcessKey( + IConfigurableListableObjectFactory factory, string key, string value) + { + int dotIndex = key.IndexOf('.'); + if (dotIndex == -1) + { + throw new FatalObjectException( + string.Format(CultureInfo.InvariantCulture, + "Invalid key '{0}': expected 'objectName.property' form.", key)); + } + string name = key.Substring(0, dotIndex); + string objectProperty = key.Substring(dotIndex + 1); + IObjectDefinition definition = factory.GetObjectDefinition(name); + if(definition != null) + { + PropertyValue pv = definition.PropertyValues.GetPropertyValue(objectProperty); + if (pv != null && pv.Value is RuntimeObjectReference) + { + definition.PropertyValues.Add(objectProperty, new RuntimeObjectReference(value)); + } + else if (pv != null && pv.Value is ExpressionHolder) + { + definition.PropertyValues.Add(objectProperty, new ExpressionHolder(value)); + } + else + { + definition.PropertyValues.Add(objectProperty, value); + } + } + else + { + #region Instrumentation + + if (_logger.IsWarnEnabled) + { + _logger.Warn(string.Format(CultureInfo.InvariantCulture, + "Cannot find object '{0}' when overriding properties; check configuration.", name)); + } + + #endregion + } + + #region Instrumentation + + if (_logger.IsDebugEnabled) + { + _logger.Debug(string.Format(CultureInfo.InvariantCulture, + "Property '{0}' set to '{1}'.", key, value)); + } + + #endregion + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPathFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPathFactoryObject.cs new file mode 100644 index 00000000..e5616d6d --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPathFactoryObject.cs @@ -0,0 +1,337 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Objects.Factory.Config +{ + /// + /// implementation that + /// evaluates a property path on a given target object. + /// + /// + ///

    + /// The target object can be specified directly or via an object name (see + /// example below). + ///

    + ///

    + /// Please note that the + /// is an implementation, and as such has + /// to comply with the contract of the + /// interface; more specifically, this means that the end result of the property lookup path + /// evaluation cannot be ( + /// implementations are not permitted to return ). If the resut of a + /// property lookup path evaluates to , an exception will be thrown. + ///

    + ///
    + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: PropertyPathFactoryObject.cs,v 1.3 2007/03/16 04:01:39 aseovic Exp $ + [Serializable] + public class PropertyPathFactoryObject : IFactoryObject, IObjectNameAware, IObjectFactoryAware + { + private IObjectWrapper targetObjectWrapper; + private string targetObjectName; + private string propertyPath; + private Type resultType; + private string objectName; + private IObjectFactory objectFactory; + + /// + /// The target object that the property path lookup is to be applied to. + /// + /// + ///

    + /// This would most likely be an inner object, but can of course be + /// any object reference. + ///

    + ///
    + /// + /// The target object that the property path lookup is to be applied to. + /// + /// + public object TargetObject + { + set { this.targetObjectWrapper = new ObjectWrapper(value); } + } + + /// + /// The (object) name of the target object that the property path lookup + /// is to be applied to. + /// + /// + ///

    + /// Please note that any leading or trailing whitespace will be + /// trimmed from this name prior to resolution. The implication of this is that + /// one cannot use the + /// class in conjunction with object names that start or end with whitespace. + ///

    + ///
    + /// + /// The (object) name of the target object that the property path lookup + /// is to be applied to. + /// + /// + public string TargetObjectName + { + set + { + if (value != null) + { + value = value.Trim(); + } + this.targetObjectName = value; + } + } + + /// + /// The property (lookup) path to be applied to the target object. + /// + /// + ///

    + /// Please note that any leading or trailing whitespace will be + /// trimmed from this path prior to resolution. Whitespace is not a valid + /// identifier for property names (in part or whole) in CLS-based languages, + /// so this is a not unreasonable action. Please also note that whitespace + /// that is embedded within the property path will be left as-is (which may + /// or may not result in an error being thrown, depending on the context of + /// the whitespace). + ///

    + ///
    + /// + ///

    + /// Examples of such property lookup paths can be seen below; note that + /// property lookup paths can be nested to an arbitrary level. + ///

    + /// + /// name.length + /// accountManager.account['the key'].name + /// accounts[0].name + /// + ///
    + /// + /// The property (lookup) path to be applied to the target object. + /// + public string PropertyPath + { + set + { + if (value != null) + { + value = value.Trim(); + } + this.propertyPath = value; + } + } + + /// + /// The 'expected' of the result from evaluating the + /// property path. + /// + /// + ///

    + /// This is not necessary for directly specified target objects, or + /// singleton target objects, where the can + /// be determined via reflection. Just specify this in case of a + /// prototype target, provided that you need matching by type (for + /// example, for autowiring). + ///

    + ///

    + /// It is permissable to set the value of this property to + /// (which in any case is the default value). + ///

    + ///
    + /// + /// The 'expected' of the result from evaluating the + /// property path. + /// + public Type ResultType + { + set { this.resultType = value; } + } + + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + /// An instance (possibly shared or independent) of the object managed by + /// this factory. + /// + /// + public object GetObject() + { + IObjectWrapper target = this.targetObjectWrapper; + if (target == null) + { + // fetch the prototype object object... + target = new ObjectWrapper(this.objectFactory[this.targetObjectName]); + } + object value = target.GetPropertyValue(this.propertyPath); + if (value == null) + { + throw new FatalObjectException("PropertyPathFactoryObject is not allowed to return null, " + + "but property value for path '" + this.propertyPath + "' is null."); + } + return value; + } + + /// + /// Return the of object that this + /// creates, or + /// if not known in advance. + /// + /// + public Type ObjectType + { + get { return this.resultType; } + } + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + /// + public bool IsSingleton + { + get { return false; } + } + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// + ///

    + /// The object name of this + /// + /// will be interpreted as "objectName.property" pattern, if neither the + /// + /// + /// have been supplied (set). + ///

    + ///

    + /// This allows for concise object definitions with just an id or name. + ///

    + ///
    + /// + /// The name of the object in the factory. + /// + public string ObjectName + { + set { this.objectName = value; } + } + + /// + /// Callback that supplies the owning factory to an object instance. + /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + /// + /// In case of initialization errors. + /// + public IObjectFactory ObjectFactory + { + set + { + this.objectFactory = value; + if (this.targetObjectWrapper != null && this.targetObjectName != null) + { + throw new ArgumentException("Only one of the TargetObjectName or TargetObject properties can be set, not both."); + } + if (this.targetObjectWrapper == null && this.targetObjectName == null) + { + if (this.propertyPath != null) + { + throw new ArgumentException( + "Specify TargetObject or TargetObjectName property in combination with PropertyPath."); + } + // no other properties specified: check object name... + string strippedObjectname = this.objectName.Trim(); + int dotIndex = strippedObjectname.IndexOf('.'); + if (dotIndex <= 0) + { + throw new ArgumentException( + "Neither TargetObject nor TargetObjectName specified, and PropertyPathFactoryObject " + + "object name '" + this.objectName + "' does not follow 'objectName.property' syntax."); + } + this.targetObjectName = strippedObjectname.Substring(0, dotIndex); + this.propertyPath = strippedObjectname.Substring(dotIndex + 1); + } + else if (this.propertyPath == null) + { + throw new ArgumentException("The 'PropertyPath' property has not been set."); + } + if (this.targetObjectWrapper == null + && this.objectFactory.IsSingleton(this.targetObjectName)) + { + // eagerly fetch singleton target object, and determine result type... + this.targetObjectWrapper + = new ObjectWrapper(this.objectFactory.GetObject(this.targetObjectName)); + this.resultType = this.targetObjectWrapper.GetPropertyType(this.propertyPath); + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs new file mode 100644 index 00000000..1064dca0 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs @@ -0,0 +1,414 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Globalization; +using Common.Logging; +using Spring.Collections; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Resolves placeholder values in one or more object definitions. + /// + /// + ///

    + /// The default placeholder syntax follows the NAnt style: ${...}. + /// Instances of this class can be configured in the same way as any other + /// object in a Spring.NET container, and so custom placeholder prefix + /// and suffix values can be set via the + /// and properties. + ///

    + /// + ///

    + /// The following example XML context definition defines an object that has + /// a number of placeholders. The placeholders can easily be distinguished + /// by the presence of the ${} characters. + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + ///

    + /// The associated XML configuration file for the above example containing the + /// values for the placeholders would contain a snippet such as .. + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + ///

    + /// The preceding XML snippet listing the various property keys and their + /// associated values needs to be inserted into the .NET config file of + /// your application (or Web.config file for your ASP.NET web application, + /// as the case may be), like so... + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + ///
    + ///

    + /// + /// checks simple property values, lists, dictionaries, sets, constructor + /// values, object type name, and object names in + /// runtime object references ( + /// ). + /// Furthermore, placeholder values can also cross-reference other + /// placeholders, in the manner of the following example where the + /// rootPath property is cross-referenced by the subPath + /// property. + ///

    + /// + /// + /// + /// + /// + /// + /// + /// + ///

    + /// In contrast to the + /// + /// class, this configurer only permits the replacement of explicit + /// placeholders in object definitions. Therefore, the original definition + /// cannot specify any default values for its object properties, and the + /// placeholder configuration file is expected to contain an entry for each + /// defined placeholder. That is, if an object definition contains a + /// placeholder ${foo}, there should be an associated + /// <add key="foo" value="..."/> entry in the + /// referenced placeholder configuration file. Default property values + /// can be defined via the inherited + /// + /// collection to overcome any perceived limitation of this feature. + ///

    + ///

    + /// If a configurer cannot resolve a placeholder, and the value of the + /// + /// property is currently set to , an + /// + /// will be thrown. If you want to resolve properties from multiple configuration + /// resources, simply specify multiple resources via the + /// + /// property. Finally, please note that you can also define multiple + /// + /// instances, each with their own custom placeholder syntax. + ///

    + ///
    + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: PropertyPlaceholderConfigurer.cs,v 1.23 2007/08/02 22:18:32 markpollack Exp $ + /// + /// + /// + [Serializable] + public class PropertyPlaceholderConfigurer : PropertyResourceConfigurer + { + /// + /// The default placeholder prefix. + /// + public const string DefaultPlaceholderPrefix = "${"; + + /// + /// The default placeholder suffix. + /// + public const string DefaultPlaceholderSuffix = "}"; + + private ILog logger = LogManager.GetLogger(typeof (PropertyPlaceholderConfigurer)); + private bool ignoreUnresolvablePlaceholders = false; + private string placeholderPrefix = DefaultPlaceholderPrefix; + private string placeholderSuffix = DefaultPlaceholderSuffix; + + private EnvironmentVariableMode environmentVariableMode = EnvironmentVariableMode.Fallback; + + + #region Properties + /// + /// The placeholder prefix (the default is ${). + /// + /// + public string PlaceholderPrefix + { + set { placeholderPrefix = value; } + } + + /// + /// The placeholder suffix (the default is }) + /// + /// + public string PlaceholderSuffix + { + set { placeholderSuffix = value; } + } + + /// + /// Indicates whether unresolved placeholders should be ignored. + /// + public bool IgnoreUnresolvablePlaceholders + { + get { return ignoreUnresolvablePlaceholders; } + set { ignoreUnresolvablePlaceholders = value; } + } + + /// + /// Controls how environment variables will be used to + /// replace property placeholders. + /// + /// + ///

    + /// See the overview of the + /// + /// enumeration for the available options. + ///

    + ///
    + public EnvironmentVariableMode EnvironmentVariableMode + { + set { environmentVariableMode = value; } + } + + #endregion + + /// + /// Apply the given properties to the supplied + /// . + /// + /// + /// The + /// used by the application context. + /// + /// The properties to apply. + /// + /// If an error occured. + /// + protected override void ProcessProperties( + IConfigurableListableObjectFactory factory, NameValueCollection props) + { + IVariableSource variableSource = new PlaceholderResolvingStringVariableSource(this, props); + ObjectDefinitionVisitor visitor = new ObjectDefinitionVisitor(variableSource); + + string[] objectDefinitionNames = factory.GetObjectDefinitionNames(); + for (int i = 0; i < objectDefinitionNames.Length; ++i) + { + string name = objectDefinitionNames[i]; + IObjectDefinition definition = factory.GetObjectDefinition(name); + try + { + visitor.VisitObjectDefinition(definition); + } + catch (ObjectDefinitionStoreException ex) + { + throw new ObjectDefinitionStoreException( + definition.ResourceDescription, name, ex.Message); + } + } + } + + + + + + + /// + /// Parse values recursively to be able to resolve cross-references between + /// placeholder values. + /// + /// + /// The map of constructor arguments / property values. + /// + /// The string to be resolved. + /// The placeholders that have already been visited + /// during the current resolution attempt (used to detect circular references + /// between placeholders). Only non-null if we're parsing a nested placeholder. + /// + /// If an error occurs. + /// + /// The resolved string. + public virtual string ParseString( + NameValueCollection properties, string strVal, ISet visitedPlaceholders) + { + int startIndex = strVal.IndexOf(placeholderPrefix); + while (startIndex != -1) + { + int endIndex = strVal.IndexOf( + placeholderSuffix, startIndex + placeholderPrefix.Length); + if (endIndex != -1) + { + int pos = startIndex + placeholderPrefix.Length; + string placeholder = strVal.Substring(pos, endIndex - pos); + if (visitedPlaceholders.Contains(placeholder)) + { + throw new ObjectDefinitionStoreException( + string.Format( + CultureInfo.InvariantCulture, + "Circular placeholder reference '{0}' detected " + + "in property definitions [{1}].", + placeholder, properties)); + } + visitedPlaceholders.Add(placeholder); + string resolvedValue = ResolvePlaceholder(placeholder, properties, environmentVariableMode); + if (resolvedValue != null) + { + resolvedValue = ParseString(properties, resolvedValue, visitedPlaceholders); + + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format( + CultureInfo.InvariantCulture, + "Resolving placeholder '{0}' to '{1}'.", placeholder, resolvedValue)); + } + + #endregion + + strVal = strVal.Substring(0, startIndex) + resolvedValue + strVal.Substring(endIndex + 1); + startIndex = strVal.IndexOf(placeholderPrefix, startIndex + resolvedValue.Length); + } + else if (ignoreUnresolvablePlaceholders) + { + // simply return the unprocessed value... + return strVal; + } + else + { + throw new ObjectDefinitionStoreException(string.Format( + CultureInfo.InvariantCulture, + "Could not resolve placeholder '{0}'.", placeholder)); + } + visitedPlaceholders.Remove(placeholder); + } + else + { + startIndex = -1; + } + } + return strVal; + } + + /// + /// Resolve the given placeholder using the given name value collection, + /// performing an environment variables check according to the given mode. + /// + /// + ///

    + /// The default implementation delegates to + /// + /// before/afer the environment variable check. Subclasses can override + /// this for custom resolution strategies, including customized points + /// for the environment properties check. + ///

    + ///
    + /// The placeholder to resolve + /// + /// The merged name value collection of this configurer. + /// + /// The environment variable mode. + /// + /// The resolved value or if none. + /// + /// + protected virtual string ResolvePlaceholder(string placeholder, + NameValueCollection props, + EnvironmentVariableMode mode) + { + string propertyValue = null; + if (mode == Spring.Objects.Factory.Config.EnvironmentVariableMode.Override) + { + propertyValue = Environment.GetEnvironmentVariable(placeholder); + } + if (propertyValue == null) + { + propertyValue = ResolvePlaceholder(placeholder, props); + } + if (propertyValue == null + && mode == Spring.Objects.Factory.Config.EnvironmentVariableMode.Fallback) + { + propertyValue = Environment.GetEnvironmentVariable(placeholder); + } + return propertyValue; + } + + /// + /// Resolve the given placeholder using the given name value collection. + /// + /// + ///

    + /// This (the default) implementation simply looks up the value of the + /// supplied key. + ///

    + ///

    + /// Subclasses can override this for customized placeholder-to-key + /// mappings or custom resolution strategies, possibly just using the + /// given name value collection as fallback. + ///

    + ///
    + /// The placeholder to resolve. + /// + /// The merged name value collection of this configurer. + /// + /// The resolved value. + protected virtual string ResolvePlaceholder( + string placeholder, NameValueCollection props) + { + return props[placeholder]; + } + } + + #region Helper class + + internal class PlaceholderResolvingStringVariableSource : IVariableSource + { + private PropertyPlaceholderConfigurer ppc; + private NameValueCollection props; + + public PlaceholderResolvingStringVariableSource(PropertyPlaceholderConfigurer outerPPC, NameValueCollection props) + { + ppc = outerPPC; + this.props = props; + } + + public string ResolveVariable(string name) + { + return ppc.ParseString(props, name, new HashedSet()); + } + } + + #endregion +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyResourceConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyResourceConfigurer.cs new file mode 100644 index 00000000..2bbcd427 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyResourceConfigurer.cs @@ -0,0 +1,393 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using System.Globalization; +using Common.Logging; +using Spring.Core; +using Spring.Core.IO; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Allows for the configuration of individual object property values from + /// a .NET .config file. + /// + /// + ///

    + /// Useful for custom .NET .config files targetted at system administrators + /// that override object properties configured in the application context. + ///

    + ///

    + /// Two concrete implementations are provided in the Spring.NET core library: + /// + /// + /// + /// + /// for <add key="placeholderKey" value="..."/> style + /// overriding (pushing values from a .NET .config file into object + /// definitions). + /// + /// + /// + /// + /// + /// for replacing "${...}" placeholders (pulling values from a .NET .config + /// file into object definitions). + /// + /// + /// + ///

    + ///

    + /// Please refer to the API documentation for the concrete implementations + /// listed above for example usage. + ///

    + ///
    + /// Juergen Hoeller + /// Simon White (.NET) + /// + /// + /// $Id: PropertyResourceConfigurer.cs,v 1.19 2007/08/27 14:49:42 oakinger Exp $ + [Serializable] + public abstract class PropertyResourceConfigurer + : IObjectFactoryPostProcessor, IOrdered + { + /// + /// The default configuration section name to use if none is explictly supplied. + /// + /// + public const string DefaultConfigSectionName = "spring-config"; + + #region Fields + + private static readonly ILog _log = LogManager.GetLogger(typeof(PropertyResourceConfigurer)); + + private int _order = Int32.MaxValue; // default: same as non-Ordered + private NameValueCollection _defaultProperties; + private IResource[] _locations; + private string[] _configSections; + private bool _ignoreResourceNotFound = false; + private bool _lastLocationOverrides = true; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + protected PropertyResourceConfigurer() + { + } + + #endregion + + #region Properties + + /// + /// The policy for resolving conflicting property overrides from + /// several resources. + /// + /// + ///

    + /// When merging conflicting property overrides from several resources, + /// should append an override with the same key be appended to the + /// current value, or should the property override from the last resource + /// processed override previous values? + ///

    + ///

    + /// The default value is ; i.e. a property + /// override from the last resource to be processed overrides previous + /// values. + ///

    + ///
    + /// + /// if the property override from the last resource + /// processed overrides previous values. + /// + public bool LastLocationOverrides + { + set { _lastLocationOverrides = value; } + } + + /// + /// Return the order value of this object, where a higher value means greater in + /// terms of sorting. + /// + /// The order value. + /// + public int Order + { + get { return _order; } + set { _order = value; } + } + + /// + /// The default properties to be applied. + /// + /// + ///

    + /// These are to be considered defaults, to be overridden by values + /// loaded from other resources. + ///

    + ///
    + public NameValueCollection Properties + { + get { return _defaultProperties; } + set { _defaultProperties = value; } + } + + /// + /// The location of the .NET .config file that contains the property + /// overrides that are to be applied. + /// + public IResource Location + { + set { _locations = new IResource[] {value}; } + } + + /// + /// The locations of the .NET .config files containing the property + /// overrides that are to be applied. + /// + public IResource[] Locations + { + set { _locations = value; } + } + + /// + /// The configuration sections to look for within the .config files. + /// + /// + /// + public string[] ConfigSections + { + get + { + if (_configSections == null + || _configSections.Length == 0) + { + _configSections = new string[] {DefaultConfigSectionName}; + } + return _configSections; + } + set { _configSections = value; } + } + + /// + /// Should a failure to find a .config file be ignored? + /// + /// + ///

    + /// is only appropriate if the .config file is + /// completely optional. The default is . + ///

    + ///
    + /// + /// if a failure to find a .config file is to be + /// ignored. + /// + public bool IgnoreResourceNotFound + { + set { _ignoreResourceNotFound = value; } + } + + #endregion + + /// + /// Modify the application context's internal object factory after its + /// standard initialization. + /// + /// + /// The object factory used by the application context. + /// + /// + /// In case of errors. + /// + /// + public void PostProcessObjectFactory(IConfigurableListableObjectFactory factory) + { + try + { + NameValueCollection properties = new NameValueCollection(); + InitializeWithDefaultProperties(properties); + LoadProperties(properties); + ProcessProperties(factory, properties); + } + catch (Exception ex) + { + if (typeof(ObjectsException).IsInstanceOfType(ex)) + { + throw; + } + else + { + throw new ObjectsException( + "Errored while postprocessing an object factory.", ex); + } + } + } + + /// + /// Loads properties from the configuration sections + /// specified in into . + /// + /// The instance to be filled with properties. + protected virtual void LoadProperties(NameValueCollection properties) + { + string[] configSections = ConfigSections; + if (_locations != null) + { + ValidateConfigSections(configSections); + bool usingMultipleConfigSections = configSections.Length > 1; + int sectionNameIndex = 0; + foreach (IResource resource in _locations) + { + #region Instrumentation + + if (_log.IsDebugEnabled) + { + _log.Debug(string.Format( + CultureInfo.InvariantCulture, + "Loading configuration from '{0}'.", resource)); + } + + #endregion + + string sectionName = configSections[sectionNameIndex]; + if (resource is ConfigSectionResource) + { + ConfigurationReader.PopulateFromAppConfig( + properties, sectionName, _lastLocationOverrides); + } + else + { + if (resource.Exists) + { + ConfigurationReader.Read( + resource, sectionName, properties, _lastLocationOverrides); + } + else + { + string errorMessage = "Could not load configuration from " + resource; + if (_ignoreResourceNotFound) + { + #region Instrumentation + + if (_log.IsWarnEnabled) + { + _log.Warn(errorMessage); + } + + #endregion + } + else + { + throw new ObjectInitializationException(errorMessage); + } + } + } + if (usingMultipleConfigSections) + { + ++sectionNameIndex; + } + } + } + else + { + foreach (string sectionName in configSections) + { + ConfigurationReader.PopulateFromAppConfig( + properties, sectionName, _lastLocationOverrides); + } + } + } + + /// + /// Apply the given properties to the supplied + /// . + /// + /// + /// The + /// used by the application context. + /// + /// The properties to apply. + /// + /// If an error occured. + /// + protected abstract void ProcessProperties( + IConfigurableListableObjectFactory factory, + NameValueCollection props); + + /// + /// Validates the supplied . + /// + /// + ///

    + /// Basically, if external locations are specified, ensure that either + /// one or a like number of config sections are also specified. + ///

    + ///
    + /// + /// The to be validated. + /// + private void ValidateConfigSections(string[] configSections) + { + if (_locations.Length != configSections.Length) + { + // if only one config section is specified for all locations that's cool... + if (configSections.Length != 1) + { + throw new ObjectInitializationException( + "Invalid number of config sections specified."); + } + } + } + + /// + /// Simply initializes the supplied + /// collection with this instances default + /// (if any). + /// + /// + /// The collection to be so initialized. + /// + private void InitializeWithDefaultProperties(NameValueCollection properties) + { + if (Properties != null) + { + properties.Add(Properties); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyRetrievingFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyRetrievingFactoryObject.cs new file mode 100644 index 00000000..a598f64b --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyRetrievingFactoryObject.cs @@ -0,0 +1,302 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Text; + +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// implementation that + /// retrieves a or non-static public property value. + /// + /// + ///

    + /// Typically used for retrieving public property values. + ///

    + ///
    + /// Rick Evans (.NET) + /// $Id: PropertyRetrievingFactoryObject.cs,v 1.12 2007/07/31 18:16:49 bbaia Exp $ + [Serializable] + public class PropertyRetrievingFactoryObject : AbstractFactoryObject, IInitializingObject + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public PropertyRetrievingFactoryObject() + { + Arguments = new object[] {}; + } + + #endregion + + #region Properties + + /// + /// The of the static property + /// to be retrieved. + /// + public string StaticProperty + { + set + { + AssertUtils.ArgumentNotNull(value, "StaticProperty"); + TypeAssemblyHolder info = new TypeAssemblyHolder(value); + string typeName = info.TypeName; + int indexWherePropertyStarts = 0; + do + { + try + { + indexWherePropertyStarts = typeName.LastIndexOf('.'); + + #region Sanity Check + + if (indexWherePropertyStarts == -1 + || indexWherePropertyStarts == typeName.Length) + { + throw new ArgumentException( + "The value passed to the StaticProperty property must be a fully " + + "qualified Type plus property name: " + + "e.g. 'Example.MyExampleClass.MyProperty, MyAssembly'"); + } + + #endregion + + typeName = typeName.Substring(0, indexWherePropertyStarts); + StringBuilder buffer = new StringBuilder(typeName); + if (info.IsAssemblyQualified) + { + buffer.Append(TypeAssemblyHolder.TypeAssemblySeparator); + buffer.Append(info.AssemblyName); + } + TargetType = TypeResolutionUtils.ResolveType(buffer.ToString()); + } + catch (TypeLoadException) + { + } + } while (TargetType == null); + TargetProperty = info.TypeName.Substring(indexWherePropertyStarts + 1); + } + } + + /// + /// Arguments for the property invocation. + /// + /// + ///

    + /// If this property is not set, or the value passed to the setter invocation + /// is a null or zero-length array, a property with no arguments is assumed. + ///

    + ///
    + public object[] Arguments + { + get { return _arguments; } + set + { + if (value != null) + { + this._arguments = value; + } + } + } + + /// + /// The name of the property the value of which is to be retrieved. + /// + /// + ///

    + /// Refers to either a property or a non-static property, + /// depending on a target object being set. + ///

    + ///
    + public string TargetProperty + { + get { return _targetProperty; } + set { _targetProperty = value; } + } + + /// + /// The object instance on which the property is defined. + /// + public object TargetObject + { + get { return _targetObject; } + set + { + _targetObject = value; + _targetObjectWrapper = new ObjectWrapper(_targetObject); + } + } + + /// + /// The on which the property is defined. + /// + public Type TargetType + { + get { return _targetType; } + set { _targetType = value; } + } + + /// + /// Return the type of object that this + /// creates, or + /// if not known in advance. + /// + public override Type ObjectType + { + get { return (Property == null) ? null : Property.PropertyType; } + } + + private PropertyInfo Property + { + get { return _property; } + set { _property = value; } + } + + #endregion + + #region Methods + + /// + /// Invoked by an + /// after it has set all object properties supplied + /// (and satisfied + /// and ApplicationContextAware). + /// + /// + /// In the event of misconfiguration (such as failure to set an essential + /// property) or if initialization fails. + /// + public override void AfterPropertiesSet() + { + if (TargetType == null + && TargetObject == null) + { + throw new ArgumentException("One of the TargetType or TargetObject properties must be set."); + } + if (TargetProperty == null) + { + throw new ArgumentException("The TargetProperty property is required."); + } + Type targetType = null; + BindingFlags propertyFlags = BindingFlags.Public | BindingFlags.IgnoreCase; + if (TargetObject == null) + { + // a static property... + propertyFlags |= BindingFlags.Static; + targetType = TargetType; + if (TargetProperty.IndexOf(".") == -1) + { + Property = targetType.GetProperty(TargetProperty, propertyFlags); + } + else + { + // $£%#@! a nested static property... recurse to the end property + string property = TargetProperty; + int propertyIndex = property.IndexOf("."); + string startProperty = property.Substring(0, propertyIndex); + Property = targetType.GetProperty(startProperty, propertyFlags); + TargetObject = Property.GetValue(null, new object[] {}); + TargetProperty = property.Substring(propertyIndex + 1); + AfterPropertiesSet(); + } + } + else + { + // an instance property... + propertyFlags |= BindingFlags.Instance; + targetType = TargetObject.GetType(); + + // using the object wrapper does nested property lookup + Property = _targetObjectWrapper.GetPropertyInfo(TargetProperty); + } + if (Property == null) + { + throw new InvalidPropertyException(targetType, TargetProperty); + } + if (!Property.CanRead) + { + throw new NotWritablePropertyException(TargetProperty, targetType); + } + base.AfterPropertiesSet(); + } + + /// + /// Template method that subclasses must override to construct the object + /// returned by this factory. + /// + /// + /// If an exception occured during object creation. + /// + /// The object returned by this factory. + protected override object CreateInstance() + { + object instance = null; + object target = null; + if (TargetObject != null) + { + target = TargetObject; + } + try + { + if (Arguments.Length == 0 && target != null) + { + // using object wrapper supports nested property lookup... + instance = _targetObjectWrapper.GetPropertyValue(_targetProperty); + } + else + { + instance = Property.GetValue(target, Arguments); + } + } + catch (Exception ex) + { + throw new FatalObjectException("Error reading property value.", ex); + } + return instance; + } + + #endregion + + #region Fields + + private string _targetProperty; + private ObjectWrapper _targetObjectWrapper; + private object _targetObject; + private Type _targetType; + private PropertyInfo _property; + private object[] _arguments; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/RegistryVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/RegistryVariableSource.cs new file mode 100644 index 00000000..f35d3238 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/RegistryVariableSource.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections.Specialized; +using Microsoft.Win32; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Implementation of that + /// resolves variable name against registry key. + /// + /// Aleksandar Seovic + /// $Id: RegistryVariableSource.cs,v 1.3 2007/08/03 08:31:24 oakinger Exp $ + public class RegistryVariableSource : IVariableSource + { + private RegistryKey key; + + /// + /// Gets or sets the registry key to obtain variable values from. + /// + /// + /// The registry key to obtain variable values from. + /// + public RegistryKey Key + { + get { return key; } + set { key = value; } + } + + /// + /// Resolves variable value for the specified variable name. + /// + /// + /// The name of the variable to resolve. + /// + /// + /// This implementation resolves REG_SZ as well as REG_MULTI_SZ values. In case of a REG_MULTI_SZ value, + /// strings are concatenated to a comma-separated list following + /// + /// + /// The variable value if able to resolve, null otherwise. + /// + public string ResolveVariable(string name) + { + object res = Key.GetValue(name); + if (res is string) + { + return (string) res; + } + else if (res is string[]) + { + NameValueCollection tmp = new NameValueCollection(); + foreach(string val in (string[])res) + { + tmp.Add(name, val); + } + return tmp.Get(name); + } + else if (res is int) + { + return res.ToString(); + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ResourceHandlerConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ResourceHandlerConfigurer.cs new file mode 100644 index 00000000..50f1b8cb --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ResourceHandlerConfigurer.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Core.IO; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// + /// implementation that allows for convenient registration of custom + /// IResource implementations. + /// + /// + ///

    + /// Because the + /// class implements the + /// + /// interface, instances of this class that have been exposed in the + /// scope of an + /// will + /// automatically be picked up by the application context and made + /// available to the IoC container whenever resolution of IResources is required. + ///

    + ///
    + /// Mark Pollack + /// $Id: ResourceHandlerConfigurer.cs,v 1.3 2007/08/08 17:47:13 bbaia Exp $ + /// + /// + [Serializable] + public class ResourceHandlerConfigurer : AbstractConfigurer + { + private IDictionary resourceHandlers; + + /// + /// The IResource implementations, i.e. resource handlers, to register. + /// + /// + ///

    + /// The has the + /// contains the resource protocol name as the key and type as the value. + /// The key name can either be a string or an object, in which case + /// ToString() will be used to obtain the string name. + /// The value can be the fully qualified name of the IResource + /// implementation, a string, or + /// an actual of the IResource class + /// + ///

    + ///
    + public IDictionary ResourceHandlers + { + set { resourceHandlers = value; } + } + + /// + /// Registers custom IResource implementations. The supplied + /// is not used since IResourse implementations + /// are registered with a global + /// + /// + /// The object factory. + /// + /// + /// In case of errors. + /// + public override void PostProcessObjectFactory( + IConfigurableListableObjectFactory factory) + { + if (resourceHandlers != null) + { + foreach (DictionaryEntry entry in resourceHandlers) + { + string protocolName = entry.Key.ToString(); + Type type = ResolveRequiredType(entry.Value, "value", "custom IResource implementation"); + ResourceHandlerRegistry.RegisterResourceHandler(protocolName, type); + + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ResourceManagerFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ResourceManagerFactoryObject.cs new file mode 100644 index 00000000..03c75d40 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/ResourceManagerFactoryObject.cs @@ -0,0 +1,155 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Reflection; +using System.Resources; + +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// A convenience class to create a + /// given the resource base + /// name and assembly name. + /// + /// + ///

    + /// This is currently the preferred way of injecting resources into view + /// tier components (such as Windows Forms GUIs and ASP.NET ASPX pages). + /// A GUI component (typically a Windows Form) is injected with + /// an instance, and can + /// then proceed to use the various GetXxx() methods on the + /// to retrieve images, + /// strings, custom resources, etc. + ///

    + ///
    + /// Mark Pollack + /// $Id: ResourceManagerFactoryObject.cs,v 1.11 2007/03/16 04:01:39 aseovic Exp $ + /// + /// + /// + [Serializable] + public class ResourceManagerFactoryObject : AbstractFactoryObject + { + private string _baseName; + private string _assemblyName; + + /// + /// The root name of the resources. + /// + /// + ///

    + /// For example, the root name for the resource file named + /// "MyResource.en-US.resources" is "MyResource". + ///

    + /// + /// The namespace is also prefixed before the resource file name. + /// + ///
    + public string BaseName + { + get { return _baseName; } + set { _baseName = value; } + } + + /// + /// The string representation of the assembly that contains the resource. + /// + public string AssemblyName + { + get { return _assemblyName; } + set { _assemblyName = value; } + } + + /// + /// The . + /// + public override Type ObjectType + { + get { return typeof(ResourceManager); } + } + + /// + /// Creates a . + /// + /// + /// If an exception occured during object creation. + /// + /// The object returned by this factory. + /// + /// + protected override object CreateInstance() + { + Assembly assembly; + try + { +#if NET_2_0 + assembly = Assembly.Load(AssemblyName); +#else + assembly = Assembly.LoadWithPartialName(AssemblyName); +#endif + } + catch (FileLoadException) + { + throw new ArgumentException("Not able to load assembly with a given name [" + _assemblyName + "]"); + } + + if (assembly == null) + { + throw new ArgumentException("Not able to load assembly with a given name [" + _assemblyName + "]"); + } + + return new ResourceManager(_baseName, assembly); + } + + /// + /// Invoked by an + /// after it has set all object properties supplied + /// (and satisfied the + /// + /// and + /// interfaces). + /// + /// + /// In the event of misconfiguration (such as failure to set an essential + /// property) or if initialization fails. + /// + /// + public override void AfterPropertiesSet() + { + if (StringUtils.IsNullOrEmpty(BaseName)) + { + throw new ArgumentException("The 'BaseName' property for the resource is required."); + } + if (StringUtils.IsNullOrEmpty(AssemblyName)) + { + throw new ArgumentException("The 'AssemblyName' property for the resource is required."); + } + base.AfterPropertiesSet(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/RuntimeObjectReference.cs b/src/Spring/Spring.Core/Objects/Factory/Config/RuntimeObjectReference.cs new file mode 100644 index 00000000..d8a74092 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/RuntimeObjectReference.cs @@ -0,0 +1,130 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Immutable placeholder class used for the value of a + /// object when it's a reference + /// to another object in this factory to be resolved at runtime. + /// + /// Rod Johnson + /// Rick Evans (.NET) + [Serializable] + public class RuntimeObjectReference + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This does not mark this object as being a reference to + /// another object in any parent factory. + ///

    + ///
    + /// The name of the target object. + public RuntimeObjectReference(string objectName) + : this(objectName, false) + { + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This variant constructor allows a client to specifiy whether or not + /// this object is a reference to another object in a parent factory. + ///

    + ///
    + /// The name of the target object. + /// + /// Whether this object is an explicit reference to an object in a + /// parent factory. + /// + public RuntimeObjectReference(string objectName, bool isToParent) + { + _objectName = objectName; + _isToParent = isToParent; + } + + #endregion + + #region Properties + + /// + /// Return the target object name. + /// + public string ObjectName + { + get { return _objectName; } + } + + /// + /// Is this is an explicit reference to an object in the parent + /// factory? + /// + /// + /// if this is an explicit reference to an + /// object in the parent factory. + /// + public bool IsToParent + { + get { return _isToParent; } + } + + #endregion + + #region Methods + + /// + /// Returns a string representation of this instance. + /// + /// A string representation of this instance. + public override string ToString() + { + return string.Format( + CultureInfo.InvariantCulture, "<{0}>", ObjectName); + } + + #endregion + + #region Fields + + private string _objectName; + + private bool _isToParent; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/SetFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/Config/SetFactoryObject.cs new file mode 100644 index 00000000..d77ce6e9 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/SetFactoryObject.cs @@ -0,0 +1,124 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using Spring.Collections; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Simple factory object for shared instances. + /// + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: SetFactoryObject.cs,v 1.10 2007/07/31 03:47:39 markpollack Exp $ + [Serializable] + public class SetFactoryObject : AbstractFactoryObject + { + private ISet _sourceSet; + private Type _targetSetType = typeof (HybridSet); + + /// + /// Set the source . + /// + /// + ///

    + /// This value will be used to populate the + /// returned by this factory. + ///

    + ///
    + public ISet SourceSet + { + set { this._sourceSet = value; } + } + + /// + /// Set the of the + /// implementation to use. + /// + /// + ///

    + /// The default is the . + ///

    + ///
    + public Type TargetSetType + { + set + { + AssertUtils.ArgumentNotNull(value, "value"); + if (!typeof (ISet).IsAssignableFrom(value)) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "The Type passed to the TargetSetType property must implement the '{0}' interface.", + typeof (ISet).FullName)); + } + if (value.IsInterface) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "The Type passed to the TargetSetType property cannot be an interface; it must be a concrete class that implements the '{0}' interface.", + ObjectType.FullName)); + } + if (value.IsAbstract) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "The Type passed to the TargetSetType property cannot be abstract (MustInherit in VisualBasic.NET); it must be a concrete class that implements the '{0}' interface.", + ObjectType.FullName)); + } + this._targetSetType = value; + } + } + + /// + /// The of objects created by this factory. + /// + /// + /// Always returns the . + /// + public override Type ObjectType + { + get { return typeof (ISet); } + } + + /// + /// Constructs a new instance of the target set. + /// + /// The new instance. + protected override object CreateInstance() + { + if (this._sourceSet == null) + { + throw new ArgumentException("The 'SourceSet' property cannot be null (Nothing in Visual Basic.NET)."); + } + Set result = (Set) ObjectUtils.InstantiateType(this._targetSetType); + result.AddAll(this._sourceSet); + return result; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/SmartInstantiationAwareObjectPostProcessor.cs b/src/Spring/Spring.Core/Objects/Factory/Config/SmartInstantiationAwareObjectPostProcessor.cs new file mode 100644 index 00000000..b056b0ee --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/SmartInstantiationAwareObjectPostProcessor.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Extension of the interface, + /// adding a callback for predicting the eventual type of a processed object. + /// + /// This interface is a special purpose interface, mainly for + /// internal use within the framework. In general, application-provided + /// post-processors should simply implement the plain + /// interface or derive from the + /// class. New methods might be added to this interface even in point releases. + /// + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: SmartInstantiationAwareObjectPostProcessor.cs,v 1.1 2008/04/02 18:02:24 markpollack Exp $ + public interface SmartInstantiationAwareObjectPostProcessor : IInstantiationAwareObjectPostProcessor + { + /// + /// Predicts the type of the object to be eventually returned from this + /// processors callback. + /// + /// The raw Type of the object. + /// Name of the object. + /// The type of the object, or null if not predictable. + /// in case of errors + Type PredictObjectType(Type objectType, string objectName); + + + /// + /// Determines the candidate constructors to use for the given object. + /// + /// The raw Type of the object. + /// Name of the object. + /// The candidate constructors, or null if none specified + /// in case of errors + ConstructorInfo[] DetermineCandidateConstructors(Type objectType, string objectName); + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/SpecialFolderVariableSource.cs b/src/Spring/Spring.Core/Objects/Factory/Config/SpecialFolderVariableSource.cs new file mode 100644 index 00000000..7ee95226 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/SpecialFolderVariableSource.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Implementation of that + /// resolves variable name against special folders (as defined by + /// enumeration). + /// + /// Aleksandar Seovic + /// $Id: SpecialFolderVariableSource.cs,v 1.3 2007/08/02 22:18:32 markpollack Exp $ + [Serializable] + public class SpecialFolderVariableSource : IVariableSource + { + /// + /// Resolves specified special folder to its full path. + /// + /// + /// The name of the special folder to resolve. Should be one of the values + /// defined by the enumeration. + /// + /// + /// The folder path if able to resolve, null otherwise. + /// + public string ResolveVariable(string name) + { + try + { + Environment.SpecialFolder folder = + (Environment.SpecialFolder) Enum.Parse(typeof (Environment.SpecialFolder), name, true); + + return Environment.GetFolderPath(folder); + } + catch (Exception) + { + return null; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/TypeAliasConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/TypeAliasConfigurer.cs new file mode 100644 index 00000000..48fe82ba --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/TypeAliasConfigurer.cs @@ -0,0 +1,106 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Core.TypeResolution; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// + /// implementation that allows for convenient registration of custom + /// type aliases. + /// + /// + /// Type aliases can be used instead of fully qualified type names anywhere + /// a type name is expected in a Spring.NET configuration file. + ///

    + /// Because the + /// class implements the + /// + /// interface, instances of this class that have been exposed in the + /// scope of an + /// will + /// automatically be picked up by the application context and made + /// available to the IoC container whenever resolution of type aliases is required. + ///

    + ///
    + /// Mark Pollack + /// $Id: TypeAliasConfigurer.cs,v 1.6 2007/07/31 18:16:49 bbaia Exp $ + /// + /// + [Serializable] + public class TypeAliasConfigurer : AbstractConfigurer + { + private IDictionary types; + + + /// + /// The type aliases to register. + /// + /// + ///

    + /// The has the + /// contains the alias name as the key and type as the value. + /// The key name can either be a string or an object, in which case + /// ToString() will be used to obtain the string name. + /// the value can be the fully qualified name of the type as a string or + /// an actual of the class that + /// being aliased. + ///

    + ///
    + public IDictionary TypeAliases + { + set { types = value; } + } + + /// + /// Registers any type aliases. The supplied + /// is not used since type aliases + /// are registered with a global + /// + /// + /// The object factory. + /// + /// + /// In case of errors. + /// + public override void PostProcessObjectFactory( + IConfigurableListableObjectFactory factory) + { + if (types != null) + { + foreach (DictionaryEntry entry in types) + { + string alias = entry.Key.ToString(); + Type type = ResolveRequiredType(entry.Value, "value", "custom type alias"); + TypeRegistry.RegisterType(alias, type); + } + } + } + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/TypedStringValue.cs b/src/Spring/Spring.Core/Objects/Factory/Config/TypedStringValue.cs new file mode 100644 index 00000000..4c4ae743 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/TypedStringValue.cs @@ -0,0 +1,143 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Holder for a typed value. + /// + /// + ///

    + /// Can be added to object definitions to explicitly specify + /// a target type for a value, + /// for example for collection + /// elements. + ///

    + ///

    + /// This holder just stores the value and the target + /// . The actual conversion will be performed by + /// the surrounding object factory. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: TypedStringValue.cs,v 1.5 2007/05/29 20:00:20 markpollack Exp $ + [Serializable] + public class TypedStringValue + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + public TypedStringValue() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public TypedStringValue(string value) + { + Value = value; + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The value that is to be converted. + /// + /// + /// The to convert to. + /// + /// + /// If the supplied is + /// . + /// + public TypedStringValue(string value, Type targetType) + { + Value = value; + TargetType = targetType; + } + + #endregion + + /// + /// The value that is to be converted. + /// + /// + ///

    + /// Obviously if the + /// + /// is the , no conversion + /// will actually be performed. + ///

    + ///
    + public string Value + { + get { return theValue; } + set { this.theValue = value; } + } + + /// + /// The to convert to. + /// + /// + /// If the setter is supplied with a value. + /// + public Type TargetType + { + get { return targetType; } + set + { + AssertUtils.ArgumentNotNull(value, "TargetType"); + targetType = value; + } + } + + /// + /// Gets a value indicating whether this instance has target type. + /// + /// + /// true if this instance has target type; otherwise, false. + /// + public bool HasTargetType + { + get { return targetType != null; } + } + + private string theValue; + private Type targetType; + } + +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/VariableAccessor.cs b/src/Spring/Spring.Core/Objects/Factory/Config/VariableAccessor.cs new file mode 100644 index 00000000..f5bef872 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/VariableAccessor.cs @@ -0,0 +1,788 @@ +#region Licence + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Provides methods for type-safe accessing s. + /// + /// Erich Eichinger + /// $Id: VariableAccessor.cs,v 1.2 2007/08/27 14:49:42 oakinger Exp $ + public class VariableAccessor + { + private readonly IVariableSource variableSource; + + /// + /// Initialize a new instance of an + /// + /// The underlying to read values from. + public VariableAccessor(IVariableSource variableSource) + { + this.variableSource = variableSource; + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public float GetFloat(string name, float defaultValue) + { + return GetFloat(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public float GetFloat(string name, float defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return float.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public double GetDouble(string name, double defaultValue) + { + return GetDouble(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public double GetDouble(string name, double defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return double.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public decimal GetDecimal(string name, decimal defaultValue) + { + return GetDecimal(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public decimal GetDecimal(string name, decimal defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return decimal.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public long GetInt64(string name, long defaultValue) + { + return GetInt64(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public long GetInt64(string name, long defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return Int64.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public ulong GetUInt64(string name, ulong defaultValue) + { + return GetUInt64(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public ulong GetUInt64(string name, ulong defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return UInt64.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public int GetInt32(string name, int defaultValue) + { + return GetInt32(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public int GetInt32(string name, int defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return Int32.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public uint GetUInt32(string name, uint defaultValue) + { + return GetUInt32(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public uint GetUInt32(string name, uint defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return UInt32.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public short GetInt16(string name, short defaultValue) + { + return GetInt16(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public short GetInt16(string name, short defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return Int16.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public ushort GetUInt16(string name, ushort defaultValue) + { + return GetUInt16(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public ushort GetUInt16(string name, ushort defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return UInt16.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public byte GetByte(string name, byte defaultValue) + { + return GetByte(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public byte GetByte(string name, byte defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return byte.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public Guid GetGuid(string name, Guid defaultValue) + { + return GetGuid(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public Guid GetGuid(string name, Guid defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return new Guid(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The expected format of the variable's value + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public DateTime GetDateTime(string name, string format, DateTime defaultValue) + { + return GetDateTime(name, format, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The expected format of the variable's value + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public DateTime GetDateTime(string name, string format, DateTime defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + if (format == null) + { + return DateTime.Parse(text); + } + else + { + return DateTime.ParseExact(text, format, CultureInfo.InvariantCulture); + } + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public char GetChar(string name, char defaultValue) + { + return GetChar(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public char GetChar(string name, char defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + if (text.Length != 1) + { + throw new ArgumentException("string '{0}' can't be converted to char", text); + } + return text[0]; + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public bool GetBoolean(string name, bool defaultValue) + { + return GetBoolean(name, defaultValue, true); + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// A that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public bool GetBoolean(string name, bool defaultValue, bool throwOnInvalidValue) + { + try + { + string text = GetString(name, defaultValue.ToString()); + return bool.Parse(text); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns an of type that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// An of type that contains the value of the specified variable + /// or , if returns null. + /// + public Enum GetEnum(string name, Enum defaultValue) + { + return GetEnum(name, defaultValue, true); + } + + /// + /// Returns an of type that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns null. + /// + /// If false, suppresses exceptions if the result + /// of cannot be parsed + /// and returns instead. + /// + /// An of type that contains the value of the specified variable + /// or , if cannot be parsed. + /// + public Enum GetEnum(string name, Enum defaultValue, bool throwOnInvalidValue) + { + try + { + return (Enum)Enum.Parse(defaultValue.GetType(), GetString(name, defaultValue.ToString()), true); + } + catch + { + if (throwOnInvalidValue) + { + throw; + } + else + { + return defaultValue; + } + } + } + + /// + /// Returns a that contains the value of the specified variable. + /// + /// The name of the variable to be read. + /// The value to be returned if returns or . + /// + /// A that contains the value of the specified variable + /// or , if returns null. + /// + public string GetString(string name, string defaultValue) + { + string value = (variableSource == null) ? defaultValue : variableSource.ResolveVariable(name); + + if (!StringUtils.HasLength(value)) + { + return defaultValue; + } + + return value; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/VariablePlaceholderConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/VariablePlaceholderConfigurer.cs new file mode 100644 index 00000000..bf8e83cb --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Config/VariablePlaceholderConfigurer.cs @@ -0,0 +1,316 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Globalization; +using Common.Logging; +using Spring.Collections; +using Spring.Core; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Resolves placeholder values in one or more object definitions + /// + /// + /// The placeholder syntax follows the NAnt style: ${...}. + /// Placeholders values are resolved against a list of + /// s. In case of multiple definitions + /// for the same property placeholder name, the first one in the + /// list is used. + /// Variable substitution is performed on simple property values, + /// lists, dictionaries, sets, constructor + /// values, object type name, and object names in + /// runtime object references ( + /// ). + /// Furthermore, placeholder values can also cross-reference other + /// placeholders, in the manner of the following example where the + /// rootPath property is cross-referenced by the subPath + /// property. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// If a configurer cannot resolve a placeholder, and the value of the + /// + /// property is currently set to , an + /// + /// will be thrown. + /// + /// Mark Pollack + /// $Id: VariablePlaceholderConfigurer.cs,v 1.2 2007/08/02 22:18:32 markpollack Exp $ + public class VariablePlaceholderConfigurer : IObjectFactoryPostProcessor, IOrdered + { + #region Fields + private int order = Int32.MaxValue; // default: same as non-Ordered + + private bool ignoreUnresolvablePlaceholders; + + private IList variableSourceList; + #endregion + + #region Properties + + /// + /// Sets the list of s that will be used to resolve placeholder names. + /// + /// A list of s. + public IList VariableSources + { + set { variableSourceList = value; } + } + + /// + /// Sets that will be used to resolve placeholder names. + /// + /// A instance. + public IVariableSource VariableSource + { + set + { + variableSourceList = new ArrayList(); + variableSourceList.Add(value); + } + } + + /// + /// Indicates whether unresolved placeholders should be ignored. + /// + public bool IgnoreUnresolvablePlaceholders + { + set { ignoreUnresolvablePlaceholders = value; } + } + + #endregion + + #region IObjectFactoryPostProcessor Members + + /// + /// Modify the application context's internal object factory after its + /// standard initialization. + /// + /// The object factory used by the application context. + /// + ///

    + /// All object definitions will have been loaded, but no objects will have + /// been instantiated yet. This allows for overriding or adding properties + /// even to eager-initializing objects. + ///

    + ///
    + /// + /// In case of errors. + /// + public void PostProcessObjectFactory(IConfigurableListableObjectFactory factory) + { + try + { + ProcessProperties(factory); + } + catch (Exception ex) + { + if (typeof (ObjectsException).IsInstanceOfType(ex)) + { + throw; + } + else + { + throw new ObjectsException( + "Errored while postprocessing an object factory.", ex); + } + } + } + + #endregion + + #region IOrdered Members + + /// + /// Return the order value of this object, where a higher value means greater in + /// terms of sorting. + /// + /// The order value. + /// + public int Order + { + get { return order; } + set { order = value; } + } + + #endregion + + /// + /// Apply the property replacement using the specified s for all + /// object in the supplied + /// . + /// + /// + /// The + /// used by the application context. + /// + /// + /// If an error occured. + /// + protected virtual void ProcessProperties(IConfigurableListableObjectFactory factory) + { + IVariableSource compositeVariableSource = + new PlaceholderResolvingCompositeVariableSource(variableSourceList, ignoreUnresolvablePlaceholders); + ObjectDefinitionVisitor visitor = new ObjectDefinitionVisitor(compositeVariableSource); + + string[] objectDefinitionNames = factory.GetObjectDefinitionNames(); + for (int i = 0; i < objectDefinitionNames.Length; ++i) + { + string name = objectDefinitionNames[i]; + IObjectDefinition definition = factory.GetObjectDefinition(name); + try + { + visitor.VisitObjectDefinition(definition); + } + catch (ObjectDefinitionStoreException ex) + { + throw new ObjectDefinitionStoreException( + definition.ResourceDescription, name, ex.Message); + } + } + } + } + + #region Helper class + internal class PlaceholderResolvingCompositeVariableSource : IVariableSource + { + private string placeholderPrefix = "${"; + private string placeholderSuffix = "}"; + private bool ignoreUnresolvablePlaceholders; + + private ILog logger = LogManager.GetLogger(typeof (PlaceholderResolvingCompositeVariableSource)); + + private IList variableSourceList; + + public PlaceholderResolvingCompositeVariableSource(IList variableSourceList, bool ignoreUnresolvablePlaceholders) + { + this.variableSourceList = variableSourceList; + this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders; + } + + #region IVariableSource Members + + public string ResolveVariable(string rawStringValue) + { + return ParseAndResolveVariable(rawStringValue, new HashedSet()); + } + + + //TODO handle resolved values at are not string - identify this case as only 1 placeholder present? + + private string ParseAndResolveVariable(string strVal, ISet visitedPlaceholders) + { + int startIndex = strVal.IndexOf(placeholderPrefix); + while (startIndex != -1) + { + int endIndex = strVal.IndexOf( + placeholderSuffix, startIndex + placeholderPrefix.Length); + if (endIndex != -1) + { + int pos = startIndex + placeholderPrefix.Length; + string placeholder = strVal.Substring(pos, endIndex - pos); + if (visitedPlaceholders.Contains(placeholder)) + { + throw new ObjectDefinitionStoreException( + string.Format( + CultureInfo.InvariantCulture, + "Circular placeholder reference '{0}' detected. ", + placeholder)); + } + visitedPlaceholders.Add(placeholder); + string resolvedValue = ResolvePlaceholderVariable(placeholder); + if (resolvedValue != null) + { + resolvedValue = ParseAndResolveVariable(resolvedValue, visitedPlaceholders); + + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug(string.Format( + CultureInfo.InvariantCulture, + "Resolving placeholder '{0}' to '{1}'.", placeholder, resolvedValue)); + } + + #endregion + + strVal = strVal.Substring(0, startIndex) + resolvedValue + strVal.Substring(endIndex + 1); + startIndex = strVal.IndexOf(placeholderPrefix, startIndex + resolvedValue.Length); + } + else if (ignoreUnresolvablePlaceholders) + { + // simply return the unprocessed value... + return strVal; + } + else + { + throw new ObjectDefinitionStoreException(string.Format( + CultureInfo.InvariantCulture, + "Could not resolve placeholder '{0}'.", placeholder)); + } + visitedPlaceholders.Remove(placeholder); + } + else + { + startIndex = -1; + } + } + return strVal; + } + + private string ResolvePlaceholderVariable(string variableName) + { + foreach (IVariableSource variableSource in variableSourceList) + { + //TODO handle resolved values at are not strings? + + object resolvedValue = variableSource.ResolveVariable(variableName); + if (resolvedValue is string) + { + } + if (resolvedValue != null) + { + if (resolvedValue is string) + { + return resolvedValue as string; + } + else + { + logger.Warn("Placeholder " + variableSource + " resolved to object type [" + resolvedValue.GetType() + "]. Only string type currently supported"); + } + } + } + return null; + } + + #endregion + } + + #endregion +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/FactoryObjectNotInitializedException.cs b/src/Spring/Spring.Core/Objects/Factory/FactoryObjectNotInitializedException.cs new file mode 100644 index 00000000..1dd3f524 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/FactoryObjectNotInitializedException.cs @@ -0,0 +1,124 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Exception thrown if an + /// is not fully + /// initialized, for example if it is involved in a circular reference. + /// + /// + ///

    + /// This is usually indicated by any of the variants of the + /// + /// method returning . + ///

    + ///

    + /// A circular reference with an + /// cannot be solved by eagerly caching singleton instances (as is the + /// case with normal objects. The reason is that every + /// needs to be fully + /// initialized before it can return the created object, while only specific + /// normal objects need to be initialized - that is, if a collaborating object + /// actually invokes them on initialization instead of just storing the reference. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: FactoryObjectNotInitializedException.cs,v 1.4 2007/12/05 00:28:04 bbaia Exp $ + [Serializable] + public class FactoryObjectNotInitializedException : ObjectCreationException + { + /// + /// Creates a new instance of the + /// FactoryObjectNotInitializedException class. + /// + public FactoryObjectNotInitializedException() + { + } + + /// + /// Creates a new instance of the FactoryObjectNotInitializedException class. + /// + /// + /// A message about the exception. + /// + public FactoryObjectNotInitializedException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the FactoryObjectNotInitializedException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public FactoryObjectNotInitializedException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// FactoryObjectCircularReferenceException class. + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// A message about the exception. + /// + public FactoryObjectNotInitializedException(string objectName, string message) + : base(objectName, StringUtils.HasText(message) ? message : + "Circular dependency chain detected for factory object.") + { + } + + /// + /// Creates a new instance of the FactoryObjectCircularReferenceException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected FactoryObjectNotInitializedException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/IFactoryObject.cs b/src/Spring/Spring.Core/Objects/Factory/IFactoryObject.cs new file mode 100644 index 00000000..8721cdab --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/IFactoryObject.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Interface to be implemented by objects used within an + /// that are themselves + /// factories. + /// + /// + ///

    + /// If an object implements this interface, it is used as a factory, + /// not directly as an object. s + /// can support singletons and prototypes + /// ()... + /// please note that an + /// itself can only ever be a singleton. It is a logic error to configure an + /// itself to be a prototype. + ///

    + /// + /// An object that implements this interface cannot be used as a normal object. + /// + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: IFactoryObject.cs,v 1.6 2006/04/09 07:18:48 markpollack Exp $ + public interface IFactoryObject + { + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + /// + /// If this method is being called in the context of an enclosing IoC container and + /// returns , the IoC container will consider this factory + /// object as not being fully initialized and throw a corresponding (and most + /// probably fatal) exception. + /// + /// + /// + /// An instance (possibly shared or independent) of the object managed by + /// this factory. + /// + object GetObject(); + + /// + /// Return the of object that this + /// creates, or + /// if not known in advance. + /// + Type ObjectType { get; } + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + bool IsSingleton { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/IGenericObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/IGenericObjectFactory.cs new file mode 100644 index 00000000..88d52e32 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/IGenericObjectFactory.cs @@ -0,0 +1,52 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Interface defining a factory which can return an object instance + /// (possibly shared or independent) when invoked. + /// + /// + /// This interface is typically used to encapsulate a generic factory + /// which returns a new instance (prototype) on each invocation. + /// It is similar to the , but + /// implementations of the aforementioned interface are normally meant to be defined + /// as instances by the user in an , + /// while implementations of this class are normally meant to be fed as a property to + /// other objects; as such, the + /// method + /// has different exception handling behavior. + /// + /// Colin Sampaleanu + /// Simon White (.NET) + /// $Id: IGenericObjectFactory.cs,v 1.6 2006/04/09 07:18:48 markpollack Exp $ + public interface IGenericObjectFactory + { + /// + /// Return an instance (possibly shared or independent) + /// of the object managed by this factory. + /// + /// + /// An instance of the object (should never be ). + /// + object GetObject(); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/IHierarchicalObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/IHierarchicalObjectFactory.cs new file mode 100644 index 00000000..2074728b --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/IHierarchicalObjectFactory.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Sub-interface implemented by object factories that can be part + /// of a hierarchy. + /// + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: IHierarchicalObjectFactory.cs,v 1.6 2006/04/09 07:18:48 markpollack Exp $ + public interface IHierarchicalObjectFactory : IObjectFactory + { + /// + /// Return the parent object factory, or + /// if this factory does not have a parent. + /// + /// + /// The parent object factory, or + /// if this factory does not have a parent. + /// + IObjectFactory ParentObjectFactory { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/IInitializingObject.cs b/src/Spring/Spring.Core/Objects/Factory/IInitializingObject.cs new file mode 100644 index 00000000..ff0b52bb --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/IInitializingObject.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Defines a simple initialization callback for objects that need to to some + /// post-initialization logic after all of their dependencies have been injected. + /// + /// + ///

    + /// An implementation of the + /// + /// method might perform some additional custom initialization (over and above that + /// performed by the constructor), or merely check that all mandatory properties + /// have been set (this last example is a very typical use case of this interface). + ///

    + /// + /// The use of the + /// interface + /// by non-Spring.NET framework code can be avoided (and is generally + /// discouraged). The Spring.NET container provides support for a generic + /// initialization method given to the object definition in the object + /// configuration store (be it XML, or a database, etc). This requires + /// slightly more configuration (one attribute-value pair in the case of + /// XML configuration), but removes any dependency on Spring.NET from the + /// class definition. + /// + ///
    + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: IInitializingObject.cs,v 1.8 2006/04/09 07:18:48 markpollack Exp $ + /// + public interface IInitializingObject + { + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + void AfterPropertiesSet(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/IListableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/IListableObjectFactory.cs new file mode 100644 index 00000000..f4af15f7 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/IListableObjectFactory.cs @@ -0,0 +1,217 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Extension of the interface + /// to be implemented by object factories that can enumerate all their object instances, + /// rather than attempting object lookup by name one by one as requested by clients. + /// + /// + ///

    + /// implementations that preload + /// all their objects (for example, DOM-based XML factories) may implement this + /// interface. This interface is discussed in + /// "Expert One-on-One J2EE Design and Development", by Rod Johnson. + ///

    + ///

    + /// If this is an , + /// the return values will not take any + /// hierarchy into account, but + /// will relate only to the objects defined in the current factory. + /// Use the helper class to + /// get all objects. + ///

    + ///

    + /// With the exception of + /// , + /// the methods and properties in this interface are not designed for frequent + /// invocation. Implementations may be slow. + ///

    + ///
    + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: IListableObjectFactory.cs,v 1.12 2007/07/29 19:39:27 markpollack Exp $ + public interface IListableObjectFactory : IObjectFactory + { + /// + /// Check if this object factory contains an object definition with the given name. + /// + /// + ///

    + /// Does not consider any hierarchy this factory may participate in. + ///

    + /// + /// Ignores any singleton objects that have been registered by other means + /// than object definitions. + /// + ///
    + /// The name of the object to look for. + /// + /// if this object factory contains an object + /// definition with the given name. + /// + bool ContainsObjectDefinition(string name); + + /// + /// Return the number of objects defined in the factory. + /// + /// + /// The number of objects defined in the factory. + /// + int ObjectDefinitionCount { get; } + + + /// + /// Return the names of all objects defined in this factory. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + string[] GetObjectDefinitionNames(); + + /// + /// Return the names of objects matching the given + /// (including subclasses), judging from the object definitions. + /// + /// + ///

    + /// Does consider objects created by s, + /// or rather it considers the type of objects created by + /// (which means that + /// s will be instantiated). + ///

    + ///

    + /// Does not consider any hierarchy this factory may participate in. + ///

    + ///
    + /// + /// The (class or interface) to match, or + /// for all object names. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + string[] GetObjectNamesForType(Type type); + + /// + /// Return the names of objects matching the given + /// (including subclasses), judging from the object definitions. + /// + /// + ///

    + /// Does consider objects created by s, + /// or rather it considers the type of objects created by + /// (which means that + /// s will be instantiated). + ///

    + ///

    + /// Does not consider any hierarchy this factory may participate in. + ///

    + ///
    + /// + /// The (class or interface) to match, or + /// for all object names. + /// + /// + /// Whether to include prototype objects too or just singletons (also applies to + /// s). + /// + /// + /// Whether to include s too + /// or just normal objects. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + string[] GetObjectNamesForType(Type type, bool includePrototypes, bool includeFactoryObjects); + + /// + /// Return the object instances that match the given object + /// (including subclasses), judging from either object + /// definitions or the value of + /// in the case of + /// s. + /// + /// + ///

    + /// This version of the + /// method matches all kinds of object definitions, be they singletons, prototypes, or + /// s. Typically, the results + /// of this method call will be the same as a call to + /// IListableObjectFactory.GetObjectsOfType(type,true,true) . + ///

    + ///
    + /// + /// The (class or interface) to match. + /// + /// + /// A of the matching objects, + /// containing the object names as keys and the corresponding object instances + /// as values. + /// + /// + /// If the objects could not be created. + /// + IDictionary GetObjectsOfType(Type type); + + /// + /// Return the object instances that match the given object + /// (including subclasses), judging from either object + /// definitions or the value of + /// in the case of + /// s. + /// + /// + /// The (class or interface) to match. + /// + /// + /// Whether to include prototype objects too or just singletons (also applies to + /// s). + /// + /// + /// Whether to include s too + /// or just normal objects. + /// + /// + /// A of the matching objects, + /// containing the object names as keys and the corresponding object instances + /// as values. + /// + /// + /// If the objects could not be created. + /// + IDictionary GetObjectsOfType( + Type type, bool includePrototypes, bool includeFactoryObjects); + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/IObjectDefinitionFactory.cs b/src/Spring/Spring.Core/Objects/Factory/IObjectDefinitionFactory.cs new file mode 100644 index 00000000..1d06f8cf --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/IObjectDefinitionFactory.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Central interface for factories that can create + /// + /// instances. + /// + /// + ///

    + /// Allows for replaceable object definition factories using the Strategy + /// pattern. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: IObjectDefinitionFactory.cs,v 1.10 2007/07/30 18:00:01 markpollack Exp $ + public interface IObjectDefinitionFactory + { + + /// + /// Factory style method for getting concrete + /// + /// instances. + /// + /// + /// The FullName of the of the defined object. + /// + /// The name of the parent object definition (if any). + /// + /// The against which any class names + /// will be resolved into instances. It can be null to register the + /// object class just by name. + /// + /// + /// An + /// + /// instance. + /// + AbstractObjectDefinition CreateObjectDefinition(string typeName, string parent, AppDomain domain); + + + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/IObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/IObjectFactory.cs new file mode 100644 index 00000000..11a43768 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/IObjectFactory.cs @@ -0,0 +1,491 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// The root interface for accessing a Spring.NET IoC container. + /// + /// + ///

    + /// This is the basic client view of a Spring.NET IoC container; further interfaces + /// such as and + /// + /// are available for specific purposes such as enumeration and configuration. + ///

    + ///

    + /// This is the root interface to be implemented by objects that can hold a number + /// of object definitions, each uniquely identified by a + /// name. An independent instance of any of these objects can be obtained + /// (the Prototype design pattern), or a single shared instance can be obtained + /// (a superior alternative to the Singleton design pattern, in which the instance is a + /// singleton in the scope of the factory). Which type of instance + /// will be returned depends on the object factory configuration - the API is the same. + /// The Singleton approach is more useful and hence more common in practice. + ///

    + ///

    + /// The point of this approach is that the IObjectFactory is a central registry of + /// application components, and centralizes the configuring of application components + /// (no more do individual objects need to read properties files, for example). + /// See chapters 4 and 11 of "Expert One-on-One J2EE Design and Development" for a + /// discussion of the benefits of this approach. + ///

    + ///

    + /// Normally an IObjectFactory will load object definitions stored in a configuration + /// source (such as an XML document), and use the + /// namespace to configure the objects. However, an implementation could simply return + /// .NET objects it creates as necessary directly in .NET code. There are no + /// constraints on how the definitions could be stored: LDAP, RDBMS, XML, properties + /// file etc. Implementations are encouraged to support references amongst objects, + /// to either Singletons or Prototypes. + ///

    + ///

    + /// In contrast to the methods in + /// , all of the methods + /// in this interface will also check parent factories if this is an + /// . If an object is + /// not found in this factory instance, the immediate parent is asked. Objects in + /// this factory instance are supposed to override objects of the same name in any + /// parent factory. + ///

    + ///

    + /// Object factories are supposed to support the standard object lifecycle interfaces + /// as far as possible. The maximum set of initialization methods and their standard + /// order is: + ///

    + ///

    + /// + /// + /// + /// 's + /// property. + /// + /// + /// + /// + /// 's + /// property. + /// + /// + /// + /// + /// + /// (only applicable if running within an ). + /// + /// + /// + /// + /// The + /// + /// method of + /// s. + /// + /// + /// + /// + /// 's + /// method. + /// + /// + /// + /// + /// A custom init-method definition. + /// + /// + /// + /// + /// The + /// + /// method of + /// s. + /// + /// + /// + ///

    + ///

    + ///

    + /// On shutdown of an object factory, the following lifecycle methods apply: + ///

    + ///

    + /// + /// + /// + /// 's + /// method. + /// + /// + /// + /// + /// A custom destroy-method definition. + /// + /// + /// + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: IObjectFactory.cs,v 1.17 2007/07/30 15:41:32 markpollack Exp $ + public interface IObjectFactory : IDisposable + { + /// + /// Is this object a singleton? + /// + /// + ///

    + /// That is, will + /// always return the same object? + ///

    + ///

    + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + ///

    + ///
    + /// The name of the object to query. + /// True if the named object is a singleton. + /// + /// If there's no such object definition. + /// + bool IsSingleton(string name); + + + /// + /// Determines whether the specified object name is prototype. That is, will GetObject + /// always return independent instances? + /// + /// This method returning false does not clearly indicate a singleton object. + /// It indicated non-independent instances, which may correspond to a scoped object as + /// well. use the IsSingleton property to explicitly check for a shared + /// singleton instance. + /// Translates aliases back to the corresponding canonical object name. Will ask the + /// parent factory if the object can not be found in this factory instance. + /// + /// + /// + /// The name of the object to query + /// + /// true if the specified object name will always deliver independent instances; otherwise, false. + /// + /// if there is no object with the given name. + bool IsPrototype(string name); + + /// + /// Does this object factory contain an object with the given name? + /// + /// + ///

    + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + ///

    + ///
    + /// The name of the object to query. + /// True if an object with the given name is defined. + bool ContainsObject(string name); + + /// + /// Return the aliases for the given object name, if defined. + /// + /// + ///

    + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + ///

    + ///
    + /// The object name to check for aliases. + /// The aliases, or an empty array if none. + /// + /// If there's no such object definition. + /// + string[] GetAliases(string name); + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// + ///

    + /// This method allows an object factory to be used as a replacement for the + /// Singleton or Prototype design pattern. + ///

    + ///

    + /// Note that callers should retain references to returned objects. There is no + /// guarantee that this method will be implemented to be efficient. For example, + /// it may be synchronized, or may need to run an RDBMS query. + ///

    + ///

    + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + ///

    + ///

    + /// This is the indexer for the + /// interface. + ///

    + ///
    + /// The name of the object to return. + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + object this[string name] { get; } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// + ///

    + /// This method allows an object factory to be used as a replacement for the + /// Singleton or Prototype design pattern. + ///

    + ///

    + /// Note that callers should retain references to returned objects. There is no + /// guarantee that this method will be implemented to be efficient. For example, + /// it may be synchronized, or may need to run an RDBMS query. + ///

    + ///

    + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + ///

    + ///
    + /// The name of the object to return. + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + object GetObject(string name); + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// + ///

    + /// This method allows an object factory to be used as a replacement for the + /// Singleton or Prototype design pattern. + ///

    + ///

    + /// Note that callers should retain references to returned objects. There is no + /// guarantee that this method will be implemented to be efficient. For example, + /// it may be synchronized, or may need to run an RDBMS query. + ///

    + ///

    + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + ///

    + ///
    + /// The name of the object to return. + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a static factory method. If there is no factory method and the + /// arguments are not null, then match the argument values by type and + /// call the object's constructor. + /// + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + /// If the supplied is . + /// + object GetObject(string name, object[] arguments); + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// The name of the object to return. + /// + /// The the object may match. Can be an interface or + /// superclass of the actual class. For example, if the value is the + /// class, this method will succeed whatever the + /// class of the returned instance. + /// + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a factory method. If there is no factory method and the + /// supplied array is not , then + /// match the argument values by type and call the object's constructor. + /// + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + /// If the object is not of the required type. + /// + /// + /// If the supplied is . + /// + /// + object GetObject(string name, Type requiredType, object[] arguments); + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// + ///

    + /// Provides a measure of type safety by throwing an exception if the object is + /// not of the required . + ///

    + ///

    + /// This method allows an object factory to be used as a replacement for the + /// Singleton or Prototype design pattern. + ///

    + ///

    + /// Note that callers should retain references to returned objects. There is no + /// guarantee that this method will be implemented to be efficient. For example, + /// it may be synchronized, or may need to run an RDBMS query. + ///

    + ///

    + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + ///

    + ///
    + /// The name of the object to return. + /// + /// the object may match. Can be an interface or + /// superclass of the actual class. For example, if the value is the + /// class, this method will succeed whatever the + /// class of the returned instance. + /// + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + /// If the object is not of the required type. + /// + object GetObject(string name, Type requiredType); + + /// + /// Determine the type of the object with the given name. + /// + /// + ///

    + /// More specifically, checks the type of object that + /// would return. + /// For an , returns the type + /// of object that the creates. + ///

    + ///
    + /// The name of the object to query. + /// + /// The type of the object or if not determinable. + /// + Type GetType(string name); + + + + /// + /// Determines whether the object with the given name matches the specified type. + /// + /// More specifically, check whether a GetObject call for the given name + /// would return an object that is assignable to the specified target type. + /// Translates aliases back to the corresponding canonical bean name. + /// Will ask the parent factory if the bean cannot be found in this factory instance. + /// + /// The name of the object to query. + /// Type of the target to match against. + /// + /// true if the object type matches; otherwise, false + /// if it doesn't match or cannot be determined yet. + /// + /// Ff there is no object with the given name + /// + bool IsTypeMatch(string name, Type targetType); + + /// + /// Injects dependencies into the supplied instance + /// using the named object definition. + /// + /// + ///

    + /// In addition to being generally useful, typically this method is used to provide + /// dependency injection functionality for objects that are instantiated outwith the + /// control of a developer. A case in point is the way that the current (1.1) + /// ASP.NET classes instantiate web controls... the instantiation takes place within + /// a private method of a compiled page, and thus cannot be hooked into the + /// typical Spring.NET IOC container lifecycle for dependency injection. + ///

    + ///
    + /// + /// The following code snippet assumes that the instantiated factory instance + /// has been configured with an object definition named + /// 'ExampleNamespace.BusinessObject' that has been configured to set the + /// Dao property of any ExampleNamespace.BusinessObject instance + /// to an instance of an appropriate implementation... + /// + /// namespace ExampleNamespace + /// { + /// public class BusinessObject + /// { + /// private IDao _dao; + /// + /// public BusinessObject() {} + /// + /// public IDao Dao + /// { + /// get { return _dao; } + /// set { _dao = value; } + /// } + /// } + /// } + /// + /// with the corresponding driver code looking like so... + /// + /// IObjectFactory factory = GetAnIObjectFactoryImplementation(); + /// BusinessObject instance = new BusinessObject(); + /// factory.ConfigureObject(instance, "object_definition_name"); + /// // at this point the dependencies for the 'instance' object will have been resolved... + /// + /// + /// + /// The object instance that is to be so configured. + /// + /// + /// The name of the object definition expressing the dependencies that are to + /// be injected into the supplied instance. + /// + /// + /// If there is no object definition for the supplied . + /// + /// + /// If any of the target object's dependencies could not be created. + /// + object ConfigureObject(object target, string name); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/IObjectFactoryAware.cs b/src/Spring/Spring.Core/Objects/Factory/IObjectFactoryAware.cs new file mode 100644 index 00000000..f66b7128 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/IObjectFactoryAware.cs @@ -0,0 +1,72 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Interface to be implemented by objects that wish to be aware of their owning + /// . + /// + /// + ///

    + /// For example, objects can look up collaborating objects via the factory. + ///

    + ///

    + /// Note that most objects will choose to receive references to collaborating + /// objects via respective properties and / or an appropriate constructor. + ///

    + ///

    + /// For a list of all object lifecycle methods, see the + /// API documentation. + ///

    + ///
    + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: IObjectFactoryAware.cs,v 1.6 2006/04/09 07:18:48 markpollack Exp $ + public interface IObjectFactoryAware + { + /// + /// Callback that supplies the owning factory to an object instance. + /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + /// + ///

    + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

    + ///
    + /// + /// In case of initialization errors. + /// + IObjectFactory ObjectFactory { set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/IObjectNameAware.cs b/src/Spring/Spring.Core/Objects/Factory/IObjectNameAware.cs new file mode 100644 index 00000000..86077897 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/IObjectNameAware.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +namespace Spring.Objects.Factory +{ + + /// + /// Interface to be implemented by objects that wish to be aware of their object + /// name in an . + /// + /// + ///

    + /// Note that most objects will choose to receive references to collaborating + /// objects via respective properties. + ///

    + ///

    + /// For a list of all object lifecycle methods, see the + /// API documentation. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + public interface IObjectNameAware + { + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// + /// The name of the object in the factory. + /// + /// + ///

    + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

    + ///
    + string ObjectName + { + set; + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/NoSuchObjectDefinitionException.cs b/src/Spring/Spring.Core/Objects/Factory/NoSuchObjectDefinitionException.cs new file mode 100644 index 00000000..1c9a1e9e --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/NoSuchObjectDefinitionException.cs @@ -0,0 +1,195 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; +using System.Security.Permissions; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Exception thrown when an + /// is asked for an object instance name for which it cannot find a definition. + /// + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: NoSuchObjectDefinitionException.cs,v 1.8 2006/04/09 07:18:48 markpollack Exp $ + [Serializable] + public class NoSuchObjectDefinitionException : ObjectsException + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public NoSuchObjectDefinitionException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public NoSuchObjectDefinitionException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public NoSuchObjectDefinitionException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// Name of the missing object. + /// + /// + /// A further, detailed message describing the problem. + /// + public NoSuchObjectDefinitionException(string name, string message) + : base(string.Format( + CultureInfo.CurrentCulture, + "No object named '{0}' is defined : {1}", + name, + StringUtils.HasText(message) ? message : "not found.")) + { + _objectName = name; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The of the missing object. + /// + /// + /// A further, detailed message describing the problem. + /// + public NoSuchObjectDefinitionException(Type type, string message) + : base(string.Format( + CultureInfo.CurrentCulture, + "No unique object of type [{0}] is defined : {1}", + type != null ? type.FullName : "<< no Type specified >>", + StringUtils.HasText(message) ? message : "not found.")) + { + _objectType = type; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected NoSuchObjectDefinitionException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _objectName = info.GetString("ObjectName"); + _objectType = info.GetValue("ObjectType", typeof (Type)) as Type; + } + + #endregion + + #region Methods + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] + public override void GetObjectData( + SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("ObjectName", ObjectName); + info.AddValue("ObjectType", ObjectType); + } + + #endregion + + #region Properties + + /// + /// Return the required of object, if it was a + /// lookup by that failed. + /// + public Type ObjectType + { + get { return _objectType; } + } + + /// + /// Return the name of the missing object, if it was a lookup by name that + /// failed. + /// + public string ObjectName + { + get { return _objectName; } + } + + #endregion + + #region Fields + + private Type _objectType; + private string _objectName; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/ObjectCreationException.cs b/src/Spring/Spring.Core/Objects/Factory/ObjectCreationException.cs new file mode 100644 index 00000000..bcf70314 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/ObjectCreationException.cs @@ -0,0 +1,333 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; +using System.Security.Permissions; +using System.Text; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Thrown when an + /// encounters an error when attempting to create an object from an object + /// definition. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: ObjectCreationException.cs,v 1.15 2007/12/05 00:28:04 bbaia Exp $ + [Serializable] + public class ObjectCreationException : FatalObjectException + { + /// + /// Creates a new instance of the + /// class. + /// + public ObjectCreationException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public ObjectCreationException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + public ObjectCreationException(string objectName, string message) + : this(null, objectName, message, null) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectCreationException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectCreationException( + string objectName, string message, Exception rootCause) + : this(null, objectName, message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The description of the resource associated with the object. + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + public ObjectCreationException( + string resourceDescription, + string objectName, + string message) + : this(resourceDescription, objectName, message, null) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The description of the resource associated with the object. + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectCreationException( + string resourceDescription, + string objectName, + string message, + Exception rootCause) + : base(message, rootCause) + { + _resourceDescription = resourceDescription; + _objectName = objectName; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectCreationException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + _resourceDescription = (string) info.GetValue("_resourceDescription", typeof (string)); + _objectName = (string) info.GetValue("_objectName", typeof (string)); + _callStack = (string) info.GetValue("_callStack", typeof (string)); + } + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] + public override void GetObjectData( + SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("_resourceDescription", ResourceDescription); + info.AddValue("_objectName", ObjectName); + info.AddValue("_callStack", _callStack); + } + + /// + /// The name of the object that triggered the exception (if any). + /// + public string ObjectName + { + get { return _objectName; } + } + + /// + /// The description of the resource associated with the object (if any). + /// + public string ResourceDescription + { + get { return _resourceDescription; } + } + + /// + /// Describes the creation failure trace of this exception. + /// + public override string Message + { + get + { + string message = ObjectCreationException.FormatMessage( + _resourceDescription, + _objectName, + base.Message, + _callStack); + return message; + } + } + + private static string FormatMessage( + string resourceDescription, + string objectName, + string message, + string callStack) + { + if (StringUtils.IsNullOrEmpty(callStack)) + { + return StringUtils.IsNullOrEmpty(resourceDescription) ? + string.Format( + "Error creating object with name '{0}' : {1}", + objectName, + message) + : + string.Format( + "Error creating object with name '{0}' defined in '{1}' : {2}", + objectName, + resourceDescription, + message); + } + else + { + return StringUtils.IsNullOrEmpty(resourceDescription) ? + string.Format( + "Error thrown by a dependency of object '{0}' : {1}{2}", + objectName, + message, + callStack) + : + string.Format( + "Error thrown by a dependency of object '{0}' defined in '{1}' : {2}{3}", + objectName, + resourceDescription, + message, + callStack); + } + } + + internal static ObjectCreationException GetObjectCreationException( + Exception ex, string objectName, string propertyName, + string resourceDescription, string referenceName) + { + ObjectCreationException ocex = ex as ObjectCreationException; + if (ocex != null) + { + StringBuilder newCause = new StringBuilder(); + newCause.AppendFormat("{0} while resolving '{1}' to '{2}' ", + Environment.NewLine, propertyName, ocex._objectName); + if (StringUtils.HasText(ocex._resourceDescription)) + { + newCause.Append("defined in '") + .Append(ocex._resourceDescription) + .Append("'"); + } + if (StringUtils.IsNullOrEmpty(ocex._callStack)) + { + ocex._callStack = newCause.ToString(); + } + else + { + ocex._callStack = newCause.Append(ocex._callStack).ToString(); + } + ocex._objectName = objectName; + ocex._resourceDescription = resourceDescription; + return ocex; + } + else if (ex is PropertyAccessExceptionsException + && ex.InnerException is TypeMismatchException) + { + return new ObjectCreationException( + resourceDescription, + objectName, + string.Format( + CultureInfo.InvariantCulture, + "Invalid type for property '{0}' of object '{1}' in '{2}'.", + propertyName, + objectName, + resourceDescription), + ex); + } + return new ObjectCreationException( + resourceDescription, + objectName, + string.Format( + CultureInfo.InvariantCulture, + "Can't resolve reference to object '{0}' while setting '{1}'.", + referenceName, + propertyName), + ex); + } + + private string _callStack; + private string _resourceDescription = string.Empty; + private string _objectName = string.Empty; + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/ObjectCurrentlyInCreationException.cs b/src/Spring/Spring.Core/Objects/Factory/ObjectCurrentlyInCreationException.cs new file mode 100644 index 00000000..419c2d48 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/ObjectCurrentlyInCreationException.cs @@ -0,0 +1,184 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Thrown in case of a reference to an object that is currently in creation. + /// + /// + ///

    + /// Typically happens when constructor autowiring matches the currently + /// constructed object. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans + /// $Id: ObjectCurrentlyInCreationException.cs,v 1.7 2008/05/29 12:13:27 oakinger Exp $ + [Serializable] + public class ObjectCurrentlyInCreationException : ObjectCreationException + { + /// + /// The default error message text to be used, if none is specified. + /// + public const string DEFAULTMESSAGE = "Requested object is currently in creation: Is there an unresolvable circular reference?"; + + /// + /// Creates a new instance of the + /// class. + /// + public ObjectCurrentlyInCreationException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The name of the object that triggered the exception. + /// + public ObjectCurrentlyInCreationException(string objectName) + : this(null, objectName, null, null) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectCurrentlyInCreationException(string objectName, Exception rootCause) + : this(null, objectName, null, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + public ObjectCurrentlyInCreationException(string objectName, string message) + : this(null, objectName, message, null) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectCurrentlyInCreationException(string objectName, string message, Exception rootCause) + : this(null, objectName, message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The description of the resource associated with the object. + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + public ObjectCurrentlyInCreationException( + string resourceDescription, + string objectName, + string message) + : this(resourceDescription, objectName, message, null) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The description of the resource associated with the object. + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectCurrentlyInCreationException( + string resourceDescription, + string objectName, + string message, + Exception rootCause) + : base(resourceDescription, + objectName, + StringUtils.HasText(message) ? message : DEFAULTMESSAGE, + rootCause) + { + } + + /// + /// Creates a new instance of the ObjectCurrentlyInCreationException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectCurrentlyInCreationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/ObjectDefinitionException.cs b/src/Spring/Spring.Core/Objects/Factory/ObjectDefinitionException.cs new file mode 100644 index 00000000..fcd0fa20 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/ObjectDefinitionException.cs @@ -0,0 +1,134 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; +using System.Security.Permissions; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Exception thrown when an + /// encounters an error when attempting to parse an object + /// definition. + /// + /// Federico Spinazzi (.NET) + [Serializable] + public class ObjectDefinitionException : Exception + { + #region Fields + private string _className; + #endregion + + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the ObjectDefinitionException class. + /// + public ObjectDefinitionException () + { + } + + /// + /// Creates a new instance of the ObjectDefinitionException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectDefinitionException (string message, Exception rootCause) + : base (message, rootCause) + { + } + + /// + /// Creates a new instance of the ObjectDefinitionException class. + /// + /// + /// The value of the xml class attribute thet can be resolved + /// as a type + /// + public ObjectDefinitionException (string name) + { + _className = name; + } + + /// + /// Creates a new instance of the ObjectDefinitionException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectDefinitionException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + _className = info.GetString ("MyClassName"); + } + #endregion + + #region Properties + /// + /// The message about the exception. + /// + public override string Message + { + get + { + return String.Format("The specified name ('{0}') cannot be used to resolve any System.Type instance", _className); + } + } + #endregion + + #region Methods + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission (SecurityAction.Demand,SerializationFormatter=true)] + public override void GetObjectData ( + SerializationInfo info, StreamingContext context) + { + base.GetObjectData (info, context); + info.AddValue ("MyClassName", _className); + } + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/ObjectDefinitionStoreException.cs b/src/Spring/Spring.Core/Objects/Factory/ObjectDefinitionStoreException.cs new file mode 100644 index 00000000..e978f42a --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/ObjectDefinitionStoreException.cs @@ -0,0 +1,260 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; +using System.Security.Permissions; +using Spring.Core.IO; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Thrown when an + /// encounters an internal error, and its definitions are invalid. + /// + /// + ///

    + /// An example of a situation when this exception would be thrown is + /// in the case of an XML document containing object definitions being + /// malformed. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + [Serializable] + public class ObjectDefinitionStoreException : FatalObjectException + { + /// + /// Creates a new instance of the ObjectDefinitionStoreException class. + /// + public ObjectDefinitionStoreException() + { + } + + /// + /// Creates a new instance of the ObjectDefinitionStoreException class. + /// + /// + /// A message about the exception. + /// + public ObjectDefinitionStoreException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the ObjectDefinitionStoreException class. + /// + /// + /// The description of the resource that the object definition came from + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// A message about the exception. + /// + public ObjectDefinitionStoreException( + string resourceDescription, string name, string message) + : this(resourceDescription, name, message, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The description of the resource that the object definition came from + /// + /// The detail message (used as exception message as-is) + /// The root cause. (may be null + public ObjectDefinitionStoreException(string resourceDescription, string msg, Exception cause) + : this(msg, cause) + { + _resourceDescription = resourceDescription; + } + + /// + /// Creates a new instance of the ObjectDefinitionStoreException class. + /// + /// + /// The resource location (e.g. an XML object definition file) associated + /// with the offending object definition. + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + public ObjectDefinitionStoreException( + IResource resourceLocation, + string name, + string message) : this + (resourceLocation == null ? string.Empty : resourceLocation.Description, + name, message) + { + } + + /// + /// Creates a new instance of the ObjectDefinitionStoreException class. + /// + /// + /// The resource location (e.g. an XML object definition file) associated + /// with the offending object definition. + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectDefinitionStoreException( + IResource resourceLocation, + string name, + string message, + Exception rootCause) : this + (resourceLocation == null ? string.Empty : resourceLocation.Description, + name, message, rootCause) + { + } + + /// + /// Creates a new instance of the ObjectDefinitionStoreException class. + /// + /// + /// The description of the resource that the object definition came from + /// + /// + /// A message about the exception. + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectDefinitionStoreException( + string resourceDescription, + string name, + string message, + Exception rootCause) + : base( + string.Format( + "Error registering object with name '{0}' defined in '{1}' : {2}", + name, + resourceDescription, + message), + rootCause) + { + _resourceDescription = resourceDescription; + _objectName = name; + } + + /// + /// Creates a new instance of the ObjectDefinitionStoreException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectDefinitionStoreException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the ObjectDefinitionStoreException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectDefinitionStoreException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + _resourceDescription = info.GetValue ("_resourceDescription", typeof (object)) as string; + _objectName = info.GetValue ("_objectName", typeof (object)) as string; + } + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission (SecurityAction.Demand,SerializationFormatter=true)] + public override void GetObjectData ( + SerializationInfo info, StreamingContext context) + { + base.GetObjectData (info, context); + info.AddValue ("_resourceDescription", ResourceDescription); + info.AddValue ("_objectName", ObjectName); + } + + /// + /// The name of the object that triggered the exception (if any). + /// + public string ObjectName + { + get { return _objectName; } + } + + /// + /// The description of the resource associated with the object (if any). + /// + public string ResourceDescription + { + get { return _resourceDescription; } + } + + /// + /// The description of the resource associated with the object + /// + protected string _resourceDescription = string.Empty; + + /// + /// The name of the object that trigger the exception. + /// + protected string _objectName = string.Empty; + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/ObjectFactoryUtils.cs b/src/Spring/Spring.Core/Objects/Factory/ObjectFactoryUtils.cs new file mode 100644 index 00000000..71afa378 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/ObjectFactoryUtils.cs @@ -0,0 +1,467 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Collections; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Convenience methods operating on object factories, returning object instances, + /// names, or counts. + /// + /// + ///

    + /// The nesting hierarchy of an object factory is taken into account by the various methods + /// exposed by this class. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: ObjectFactoryUtils.cs,v 1.17 2007/07/31 03:47:39 markpollack Exp $ + public sealed class ObjectFactoryUtils + { + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly visible + /// constructors. + ///

    + ///
    + private ObjectFactoryUtils() + { + } + + // CLOVER:ON + + #endregion + + /// + /// Used to dereference an + /// and distinguish it from managed objects created by the factory. + /// + /// + ///

    + /// For example, if the managed object identified as foo is a + /// factory, getting &foo will return the factory, not the + /// instance returned by the factory. + ///

    + ///
    + public const string FactoryObjectPrefix = "&"; + + /// + /// Count all object definitions in any hierarchy in which this + /// factory participates. + /// + /// + ///

    + /// Includes counts of ancestor object factories. + ///

    + ///

    + /// Objects that are "overridden" (specified in a descendant factory + /// with the same name) are counted only once. + ///

    + ///
    + /// The object factory. + /// + /// The count of objects including those defined in ancestor factories. + /// + public static int CountObjectsIncludingAncestors(IListableObjectFactory factory) + { + return ObjectNamesIncludingAncestors(factory).Length; + } + + /// + /// Return all object names in the factory, including ancestor factories. + /// + /// The object factory. + /// The array of object names, or an empty array if none. + public static string[] ObjectNamesIncludingAncestors(IListableObjectFactory factory) + { + Set result = new HashedSet(); + result.AddAll(factory.GetObjectDefinitionNames()); + IListableObjectFactory pof = GetParentFactoryIfAny(factory); + if (pof != null) + { + string[] parentsResult = ObjectNamesIncludingAncestors(pof); + result.AddAll(parentsResult); + } + return ToArrayOfObjectNames(result); + } + + private static string[] ToArrayOfObjectNames(Set result) + { + Array resultArray = Array.CreateInstance(typeof (string), result.Count); + result.CopyTo(resultArray, 0); + return (string[]) resultArray; + } + + /// + /// Get all object names for the given type, including those defined in ancestor + /// factories. + /// + /// + ///

    + /// Will return unique names in case of overridden object definitions. + ///

    + ///

    + /// Does consider objects created by s + /// if is set to true, + /// which means that s will get initialized. + ///

    + ///
    + /// + /// If this isn't also an + /// , + /// this method will return the same as it's own + /// + /// method. + /// + /// + /// The that objects must match. + /// + /// + /// Whether to include prototype objects too or just singletons + /// (also applies to instances). + /// + /// + /// Whether to include instances + /// too or just normal objects. + /// + /// + /// The array of object names, or an empty array if none. + /// + public static string[] ObjectNamesForTypeIncludingAncestors( + IListableObjectFactory factory, Type type, + bool includePrototypes, bool includeFactoryObjects) + { + Set result = new HashedSet(); + result.AddAll(factory.GetObjectNamesForType(type, includePrototypes, includeFactoryObjects)); + IListableObjectFactory pof = GetParentFactoryIfAny(factory); + if (pof != null) + { + string[] parentsResult = ObjectNamesForTypeIncludingAncestors(pof, type, includePrototypes, includeFactoryObjects); + result.AddAll(parentsResult); + } + return ToArrayOfObjectNames(result); + } + + /// + /// Get all object names for the given type, including those defined in ancestor + /// factories. + /// + /// + ///

    + /// Will return unique names in case of overridden object definitions. + ///

    + ///

    + /// Does consider objects created by s, + /// or rather it considers the type of objects created by + /// (which means that + /// s will be instantiated). + ///

    + ///
    + /// + /// If this isn't also an + /// , + /// this method will return the same as it's own + /// + /// method. + /// + /// + /// The that objects must match. + /// + /// + /// The array of object names, or an empty array if none. + /// + public static string[] ObjectNamesForTypeIncludingAncestors( + IListableObjectFactory factory, Type type) + { + Set result = new HashedSet(); + result.AddAll(factory.GetObjectNamesForType(type)); + IListableObjectFactory pof = GetParentFactoryIfAny(factory); + if (pof != null) + { + string[] parentsResult = ObjectNamesForTypeIncludingAncestors(pof, type); + result.AddAll(parentsResult); + } + return ToArrayOfObjectNames(result); + } + + private static IListableObjectFactory GetParentFactoryIfAny(IListableObjectFactory factory) + { + IHierarchicalObjectFactory hierFactory = factory as IHierarchicalObjectFactory; + if (hierFactory != null) + { + return + hierFactory.ParentObjectFactory as IListableObjectFactory; + } + return null; + } + + /// + /// Return all objects of the given type or subtypes, also picking up objects + /// defined in ancestor object factories if the current object factory is an + /// . + /// + /// + ///

    + /// The return list will only contain objects of this type. + /// Useful convenience method when we don't care about object names. + ///

    + ///
    + /// The object factory. + /// The of object to match. + /// + /// Whether to include prototype objects too or just singletons + /// (also applies to instances). + /// + /// + /// Whether to include instances + /// too or just normal objects. + /// + /// + /// If the objects could not be created. + /// + /// + /// The of object instances, or an + /// empty if none. + /// + public static IDictionary ObjectsOfTypeIncludingAncestors( + IListableObjectFactory factory, Type type, + bool includePrototypes, bool includeFactoryObjects) + { + Hashtable result = new Hashtable(); + foreach (DictionaryEntry entry in + factory.GetObjectsOfType(type, includePrototypes, includeFactoryObjects)) + { + result.Add(entry.Key, entry.Value); + } + IListableObjectFactory pof = GetParentFactoryIfAny(factory); + if (pof != null) + { + IDictionary parentResult + = ObjectsOfTypeIncludingAncestors( + pof, type, includePrototypes, includeFactoryObjects); + foreach (object instance in parentResult.Keys) + { + if (!result.ContainsKey(instance)) + { + result.Add(instance, parentResult[instance]); + } + } + } + return result; + } + + /// + /// Return a single object of the given type or subtypes, also picking up objects defined + /// in ancestor object factories if the current object factory is an + /// . + /// + /// + ///

    + /// Useful convenience method when we expect a single object and don't care + /// about the object name. + ///

    + ///
    + /// The object factory. + /// The of object to match. + /// + /// Whether to include prototype objects too or just singletons + /// (also applies to instances). + /// + /// + /// Whether to include instances + /// too or just normal objects. + /// + /// + /// If the object could not be created. + /// + /// + /// If more than one instance of an object was found. + /// + /// + /// A single object of the given type or subtypes. + /// + public static object ObjectOfTypeIncludingAncestors( + IListableObjectFactory factory, Type type, + bool includePrototypes, bool includeFactoryObjects) + { + IDictionary objectsOfType + = ObjectsOfTypeIncludingAncestors( + factory, type, includePrototypes, includeFactoryObjects); + return GrabTheOnlyObject(objectsOfType, type); + } + + private static object GrabTheOnlyObject(IDictionary objectsOfType, Type type) + { + if (objectsOfType.Count == 1) + { + return ObjectUtils.EnumerateFirstElement(objectsOfType.Values); + } + else + { + throw new NoSuchObjectDefinitionException(type, "Expected single object but found " + objectsOfType.Count); + } + } + + /// + /// Return a single object of the given type or subtypes, not looking in + /// ancestor factories. + /// + /// + ///

    + /// Useful convenience method when we expect a single object and don't care + /// about the object name. + ///

    + ///
    + /// The object factory. + /// The of object to match. + /// + /// Whether to include prototype objects too or just singletons + /// (also applies to instances). + /// + /// + /// Whether to include instances + /// too or just normal objects. + /// + /// + /// If the object could not be created. + /// + /// + /// If not exactly one instance of an object was found. + /// + /// + /// A single object of the given type or subtypes. + /// + public static object ObjectOfType(IListableObjectFactory factory, Type type, + bool includePrototypes, bool includeFactoryObjects) + { + IDictionary objectsOfType + = factory.GetObjectsOfType(type, includePrototypes, includeFactoryObjects); + return GrabTheOnlyObject(objectsOfType, type); + } + + /// + /// Return a single object of the given type or subtypes, not looking in + /// ancestor factories. + /// + /// + ///

    + /// Useful convenience method when we expect a single object and don't care + /// about the object name. + /// This version of ObjectOfType automatically includes prototypes and + /// instances. + ///

    + ///
    + /// The object factory. + /// The of object to match. + /// + /// If the object could not be created. + /// + /// + /// If not exactly one instance of an object was found. + /// + /// + /// A single object of the given type or subtypes. + /// + public static object ObjectOfType(IListableObjectFactory factory, Type type) + { + return ObjectOfType(factory, type, true, true); + } + + /// + /// Return the object name, stripping out the factory dereference prefix if necessary. + /// + /// The name of the object. + /// The object name sans any factory dereference prefix. + public static string TransformedObjectName(string name) + { + AssertUtils.ArgumentNotNull(name, "name", "Object name must not be null."); + string objectName = name; + if (ObjectFactoryUtils.IsFactoryDereference(objectName)) + { + objectName = objectName.Substring(ObjectFactoryUtils.FactoryObjectPrefix.Length); + } + return objectName; + } + + /// + /// Given an (object) name, builds a corresponding factory object name such that + /// the return value can be used as a lookup name for a factory object. + /// + /// + /// The name to be used to build the resulting factory object name. + /// + /// + /// The transformed into its factory object name + /// equivalent. + /// + /// + /// + public static string BuildFactoryObjectName(string objectName) + { + return ObjectFactoryUtils.FactoryObjectPrefix + objectName; + } + + /// + /// Is the supplied a factory dereference? + /// + /// + ///

    + /// That is, does the supplied begin with + /// the + /// ? + ///

    + ///
    + /// The name to check. + /// + /// if the supplied is a + /// factory dereference; if not, or the + /// aupplied is or + /// consists solely of the + /// + /// value. + /// + /// + public static bool IsFactoryDereference(string name) + { + return name != null + && name.StartsWith(ObjectFactoryUtils.FactoryObjectPrefix) + && name.Length > ObjectFactoryUtils.FactoryObjectPrefix.Length; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/ObjectInitializationException.cs b/src/Spring/Spring.Core/Objects/Factory/ObjectInitializationException.cs new file mode 100644 index 00000000..d6ea606d --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/ObjectInitializationException.cs @@ -0,0 +1,98 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Exception that an object implementation is suggested to throw if its own + /// factory-aware initialization code fails. + /// thrown by object factory methods + /// themselves should simply be propagated as-is. + /// + /// + ///

    + /// Note that non-factory-aware initialization methods like AfterPropertiesSet () + /// or a custom "init-method" can throw any exception. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + [Serializable] + public class ObjectInitializationException : FatalObjectException + { + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the ObjectInitializationException class. + /// + public ObjectInitializationException () + { + } + + /// + /// Creates a new instance of the ObjectInitializationException class. + /// + /// + /// A message about the exception. + /// + public ObjectInitializationException (string message) + : base (message) + { + } + + /// + /// Creates a new instance of the ObjectInitializationException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectInitializationException (string message, Exception rootCause) + : base (message, rootCause) + { + } + + /// + /// Creates a new instance of the ObjectInitializationException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectInitializationException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + } + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/ObjectIsNotAFactoryException.cs b/src/Spring/Spring.Core/Objects/Factory/ObjectIsNotAFactoryException.cs new file mode 100644 index 00000000..8acd1f33 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/ObjectIsNotAFactoryException.cs @@ -0,0 +1,115 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Thrown in response to an attempt to lookup a factory object, and + /// the object identified by the lookup key is not a factory. + /// + /// + ///

    + /// An object is a factory if it implements (either directly or indirectly + /// via inheritance) the + /// interface. + ///

    + ///
    + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: ObjectIsNotAFactoryException.cs,v 1.5 2006/04/09 07:18:49 markpollack Exp $ + [Serializable] + public class ObjectIsNotAFactoryException : ObjectNotOfRequiredTypeException + { + /// + /// Creates a new instance of the + /// class. + /// + public ObjectIsNotAFactoryException() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public ObjectIsNotAFactoryException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectIsNotAFactoryException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The name of the object that was being retrieved from the factory. + /// + /// + /// The object instance that was retrieved. + /// + public ObjectIsNotAFactoryException(string name, object actualInstance) + : base(name, typeof (IFactoryObject), actualInstance) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectIsNotAFactoryException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/ObjectNotOfRequiredTypeException.cs b/src/Spring/Spring.Core/Objects/Factory/ObjectNotOfRequiredTypeException.cs new file mode 100644 index 00000000..16ecfd47 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/ObjectNotOfRequiredTypeException.cs @@ -0,0 +1,185 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; +using System.Security.Permissions; + +#endregion + +namespace Spring.Objects.Factory { + /// + /// Thrown when an object doesn't match the required . + /// + /// Rod Johnson + /// Rick Evans (.NET) + [Serializable] + public class ObjectNotOfRequiredTypeException : ObjectsException { + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the ObjectNotOfRequiredTypeException class. + /// + public ObjectNotOfRequiredTypeException () { + } + + /// + /// Creates a new instance of the ObjectNotOfRequiredTypeException class. + /// + /// + /// A message about the exception. + /// + public ObjectNotOfRequiredTypeException (string message) + : base (message) { + } + + /// + /// Creates a new instance of the ObjectNotOfRequiredTypeException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectNotOfRequiredTypeException (string message, Exception rootCause) + : base (message, rootCause) { + } + + /// + /// Creates a new instance of the ObjectNotOfRequiredTypeException class. + /// + /// + /// Name of the object requested. + /// + /// + /// The required of the actual object + /// instance that was retrieved. + /// + /// + /// The instance actually returned, whose class did not match the + /// expected . + /// + public ObjectNotOfRequiredTypeException ( + string name, Type requiredType, object actualInstance) + : base ( + string.Format ( + "Object named '{0}' must be of type [{1}], but was actually of type [{2}]", + name, + requiredType.Name, + actualInstance.GetType ().Name)) { + this.name = name; + this.actualInstance = actualInstance; + this.requiredType = requiredType; + } + + /// + /// Creates a new instance of the ObjectNotOfRequiredTypeException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectNotOfRequiredTypeException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + requiredType = info.GetValue ("RequiredType", typeof (Type)) as Type; + actualInstance = info.GetValue ("ActualInstance", typeof (object)); + name = info.GetString ("Name"); + } + #endregion + + #region Properties + /// + /// The actual of the actual object + /// instance that was retrieved. + /// + public Type ActualType { + get { + return ActualInstance.GetType (); + } + } + /// + /// The required of the actual object + /// instance that was retrieved. + /// + public Type RequiredType { + get { + return requiredType; + } + } + + /// + /// The instance actually returned, whose class did not match the + /// expected . + /// + public object ActualInstance { + get { + return actualInstance; + } + } + + /// + /// The name of the object requested. + /// + public string ObjectName { + get { + return name; + } + } + #endregion + + #region Methods + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission (SecurityAction.Demand,SerializationFormatter=true)] + public override void GetObjectData ( + SerializationInfo info, StreamingContext context) { + base.GetObjectData (info, context); + info.AddValue ("RequiredType", requiredType); + info.AddValue ("ActualInstance", actualInstance); + info.AddValue ("Name", name); + } + #endregion + + #region Fields + private Type requiredType; + private object actualInstance; + private string name; + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Parsing/ReaderContext.cs b/src/Spring/Spring.Core/Objects/Factory/Parsing/ReaderContext.cs new file mode 100644 index 00000000..f14ad51f --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Parsing/ReaderContext.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using Spring.Core.IO; + +namespace Spring.Objects.Factory.Parsing +{ + /// + /// Context that gets passed along an object definition reading process, + /// encapsulating all relevant configuraiton as well as state. + /// + /// Rob Harrop + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: ReaderContext.cs,v 1.3 2007/08/08 17:47:13 bbaia Exp $ + public class ReaderContext + { + private IResource resource; + + /// + /// Initializes a new instance of the class. + /// + /// The resource. + public ReaderContext(IResource resource) + { + this.resource = resource; + } + + + /// + /// Gets the resource. + /// + /// The resource. + public IResource Resource + { + get { return resource; } + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractAutowireCapableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractAutowireCapableObjectFactory.cs new file mode 100644 index 00000000..cfc35767 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractAutowireCapableObjectFactory.cs @@ -0,0 +1,2435 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Globalization; +using System.Reflection; + +using Common.Logging; +using Spring.Collections; +using Spring.Core; +using Spring.Core.TypeConversion; +using Spring.Core.TypeResolution; +using Spring.Expressions; +using Spring.Objects; +using Spring.Objects.Factory.Config; +using Spring.Objects.Support; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Abstract superclass + /// that implements default object creation. + /// + /// + ///

    + /// Provides object creation, initialization and wiring, supporting + /// autowiring and constructor resolution. Handles runtime object + /// references, managed collections, and object destruction. + ///

    + ///

    + /// The main template method to be implemented by subclasses is + /// , + /// used for autowiring by type. Note that this class does not implement object + /// definition registry capabilities + /// ( + /// does). + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: AbstractAutowireCapableObjectFactory.cs,v 1.90 2008/05/29 12:13:27 oakinger Exp $ + [Serializable] + public abstract class AbstractAutowireCapableObjectFactory : AbstractObjectFactory, IAutowireCapableObjectFactory + { + #region Constants + + /// + /// The used during the invocation and + /// searching for of methods. + /// + protected const BindingFlags MethodResolutionFlags = + BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase; + + #endregion + + /// + /// The instance for this class. + /// + private readonly ILog log = LogManager.GetLogger(typeof(AbstractAutowireCapableObjectFactory)); + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + /// Flag specifying whether to make this object factory case sensitive or not. + protected AbstractAutowireCapableObjectFactory(bool caseSensitive) + : this(caseSensitive, null) + { } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + /// Flag specifying whether to make this object factory case sensitive or not. + /// The parent object factory, or if none. + protected AbstractAutowireCapableObjectFactory(bool caseSensitive, IObjectFactory parentFactory) + : base(caseSensitive, parentFactory) + { + this.IgnoreDependencyInterface(typeof(IObjectFactoryAware)); + this.IgnoreDependencyInterface(typeof(IObjectNameAware)); + } + + #endregion + + #region Properties + + /// + /// The + /// implementation to be used to instantiate managed objects. + /// + protected IInstantiationStrategy InstantiationStrategy + { + get { return instantiationStrategy; } + set { instantiationStrategy = value; } + } + + #endregion + + #region Methods + + /// + /// Predict the eventual object type (of the processed object instance) for the + /// specified object. + /// + /// Name of the object. + /// The merged object definition to determine the type for. + /// + /// The type of the object, or null if not predictable + /// + protected override Type PredictObjectType(string objectName, RootObjectDefinition mod) + { + Type objectType; + if (StringUtils.HasText(mod.FactoryMethodName)) + { + objectType = GetTypeForFactoryMethod(objectName, mod); + } + else + { + objectType = ResolveObjectType(mod, objectName); + } + return objectType; + } + + /// + /// Determines the of the object defined + /// by the supplied object . + /// + /// + /// The name associated with the supplied object . + /// + /// + /// The + /// that the is to be determined for. + /// + /// + /// The of the object defined by the supplied + /// object ; or if the + /// cannot be determined. + /// + protected override Type GetTypeForFactoryMethod(string objectName, RootObjectDefinition definition) + { + if (StringUtils.HasText(definition.FactoryObjectName) && definition.IsSingleton && !definition.IsLazyInit) + { + return GetObject(objectName).GetType(); + } + + Type factoryType = null; + bool isStatic = true; + + if (StringUtils.HasText(definition.FactoryObjectName)) + { + // check declared factory method return type on factory type... + factoryType = GetType(definition.FactoryObjectName); + isStatic = false; + } + else + { + factoryType = ResolveObjectType(definition, objectName); + } + if (factoryType == null) + { + return null; + } + + // If all factory methods have the same return type, return that type. + // Can't clearly figure out exact method due to type converting / autowiring! + int minNrOfArgs = definition.ConstructorArgumentValues.GenericArgumentValues.Count; + MethodInfo[] candidates = factoryType.GetMethods(); + ISet returnTypes = new HybridSet(); + foreach (MethodInfo factoryMethod in candidates) + { +#if NET_2_0 + GenericArgumentsHolder genericArgsInfo = new GenericArgumentsHolder(definition.FactoryMethodName); + if (factoryMethod.IsStatic == isStatic && factoryMethod.Name.Equals(genericArgsInfo.GenericMethodName) + && ReflectionUtils.GetParameterTypes(factoryMethod).Length >= minNrOfArgs + && factoryMethod.GetGenericArguments().Length == genericArgsInfo.GetGenericArguments().Length) + { + if (genericArgsInfo.ContainsGenericArguments) + { + string[] unresolvedGenericArgs = genericArgsInfo.GetGenericArguments(); + Type[] genericArgs = new Type[unresolvedGenericArgs.Length]; + for (int j = 0; j < unresolvedGenericArgs.Length; j++) + { + genericArgs[j] = TypeResolutionUtils.ResolveType(unresolvedGenericArgs[j]); + } + returnTypes.Add(factoryMethod.MakeGenericMethod(genericArgs).ReturnType); + } + else + { + returnTypes.Add(factoryMethod.ReturnType); + } + } +#else + if (factoryMethod.IsStatic == isStatic && factoryMethod.Name.Equals(definition.FactoryMethodName) + && ReflectionUtils.GetParameterTypes(factoryMethod).Length >= minNrOfArgs) + { + returnTypes.Add(factoryMethod.ReturnType); + } +#endif + } + if (returnTypes.Count == 1) + { + // clear return type found: all factory methods return same type... + return (Type)ObjectUtils.EnumerateFirstElement(returnTypes); + } + else + { + // ambiguous return types found: return null to indicate "not determinable"... + return null; + } + } + + /// + /// Apply the property values of the object definition with the supplied + /// to the supplied . + /// + /// + /// The existing object that the property values for the named object will + /// be applied to. + /// + /// + /// The name of the object definition associated with the property values that are + /// to be applied. + /// + public override void ApplyObjectPropertyValues(object instance, string name) + { + RootObjectDefinition definition = GetMergedObjectDefinition(name, true); + if (definition != null) + { + log.Debug(string.Format("configuring object '{0}' using definition '{1}'", instance, name)); + ApplyPropertyValues(name, definition, new ObjectWrapper(instance), definition.PropertyValues); + } + } + + /// + /// Apply any + /// s. + /// + /// + ///

    + /// The returned instance may be a wrapper around the original. + ///

    + ///
    + /// + /// The of the object that is to be + /// instantiated. + /// + /// + /// The name of the object that is to be instantiated. + /// + /// + /// An instance to use in place of the original instance. + /// + /// + /// In case of errors. + /// + protected object ApplyObjectPostProcessorsBeforeInstantiation(Type objectType, string objectName) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format("Invoking IInstantiationAwareObjectPostProcessors before " + "the instantiation of '{0}'.", objectName)); + } + + #endregion + + foreach (IObjectPostProcessor processor in ObjectPostProcessors) + { + IInstantiationAwareObjectPostProcessor inProc = processor as IInstantiationAwareObjectPostProcessor; + if (inProc != null) + { + object theObject = inProc.PostProcessBeforeInstantiation(objectType, objectName); + if (theObject != null) + { + return theObject; + } + } + } + return null; + } + + /// + /// Apply the given property values, resolving any runtime references + /// to other objects in this object factory. + /// + /// + /// The object name passed for better exception information. + /// + /// + /// The definition of the named object. + /// + /// + /// The wrapping the target object. + /// + /// + /// The new property values. + /// + /// + ///

    + /// Must use deep copy, so that we don't permanently modify this property. + ///

    + ///
    + protected void ApplyPropertyValues(string name, RootObjectDefinition definition, IObjectWrapper wrapper, IPropertyValues properties) + { + if (properties == null || properties.PropertyValues.Length == 0) + { + return; + } + MutablePropertyValues deepCopy = new MutablePropertyValues(properties); + PropertyValue[] copiedProperties = deepCopy.PropertyValues; + for (int i = 0; i < copiedProperties.Length; ++i) + { + PropertyValue copiedProperty = copiedProperties[i]; + object value = ResolveValueIfNecessary(name, definition, copiedProperty.Name, copiedProperty.Value); + PropertyValue propertyValue = new PropertyValue(copiedProperty.Name, value, copiedProperty.Expression); + // update mutable copy... + deepCopy.SetPropertyValueAt(propertyValue, i); + } + // set the (possibly resolved) deep copy properties... + try + { + wrapper.SetPropertyValues(deepCopy); + } + catch (ObjectsException ex) + { + // improve the message by showing the context... + throw new ObjectCreationException(definition.ResourceDescription, name, "Error setting property values: " + ex.Message, ex); + } + } + + /// + /// Return an array of object-type property names that are unsatisfied. + /// + /// + ///

    + /// These are probably unsatisfied references to other objects in the + /// factory. Does not include simple properties like primitives or + /// s. + ///

    + ///
    + /// + /// An array of object-type property names that are unsatisfied. + /// + /// + /// The definition of the named object. + /// + /// + /// The wrapping the target object. + /// + protected string[] UnsatisfiedObjectProperties(RootObjectDefinition definition, IObjectWrapper wrapper) + { + ArrayList result = new ArrayList(); + ISet ignoredTypes = IgnoredDependencyTypes; + PropertyInfo[] properties = wrapper.GetPropertyInfos(); + foreach (PropertyInfo property in properties) + { + string name = property.Name; + if (property.CanWrite && !ignoredTypes.Contains(property.PropertyType) && !result.Contains(name) + && !ObjectUtils.IsSimpleProperty(property.PropertyType)) + { + result.Add(name); + } + } + return (string[])result.ToArray(typeof(string)); + } + + /// + /// Destroy all cached singletons in this factory. + /// + /// + ///

    + /// To be called on shutdown of a factory. + ///

    + ///
    + public override void Dispose() + { + base.Dispose(); + foreach (object o in _disposableInnerObjects) + { + DestroyObject(string.Format(CultureInfo.InvariantCulture, "(Inner object of Type '{0}')", o.GetType().FullName), o); + } + _disposableInnerObjects.Clear(); + } + + /// + /// Populate the object instance in the given + /// with the property values from the + /// object definition. + /// + /// + /// The name of the object. + /// + /// + /// The definition of the named object. + /// + /// + /// The wrapping the target object. + /// + protected void PopulateObject(string name, RootObjectDefinition definition, IObjectWrapper wrapper) + { + // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the + // state of the bean before properties are set. This can be used, for example, + // to support styles of field injection. + bool continueWithPropertyPopulation = true; + + if (HasInstantiationAwareBeanPostProcessors) + { + foreach (IObjectPostProcessor processor in ObjectPostProcessors) + { + IInstantiationAwareObjectPostProcessor inProc = processor as IInstantiationAwareObjectPostProcessor; + if (inProc != null) + { + if (!inProc.PostProcessAfterInstantiation(wrapper.WrappedInstance, name)) + { + continueWithPropertyPopulation = false; + break; + } + } + } + } + if (!continueWithPropertyPopulation) + { + return; + } + + IPropertyValues properties = definition.PropertyValues; + + if (wrapper == null) + { + if (properties.PropertyValues.Length > 0) + { + throw new ObjectCreationException(definition.ResourceDescription, + name, "Cannot apply property values to null instance."); + } + else + { + // skip property population phase for null instance + return; + } + } + + if (definition.ResolvedAutowireMode == AutoWiringMode.ByName || definition.ResolvedAutowireMode == AutoWiringMode.ByType) + { + MutablePropertyValues mpvs = new MutablePropertyValues(properties); + // add property values based on autowire by name if it's applied + if (definition.ResolvedAutowireMode == AutoWiringMode.ByName) + { + AutowireByName(name, definition, wrapper, mpvs); + } + // add property values based on autowire by type if it's applied + if (definition.ResolvedAutowireMode == AutoWiringMode.ByType) + { + AutowireByType(name, definition, wrapper, mpvs); + } + properties = mpvs; + } + //DependencyCheck(name, definition, wrapper, properties); + + + bool hasInstAwareOpps = HasInstantiationAwareBeanPostProcessors; + bool needsDepCheck = (definition.DependencyCheck != DependencyCheckingMode.None); + + + if (hasInstAwareOpps || needsDepCheck) + { + PropertyInfo[] filteredPropInfo = FilterPropertyInfoForDependencyCheck(wrapper); + if (hasInstAwareOpps) + { + foreach (IObjectPostProcessor processor in ObjectPostProcessors) + { + IInstantiationAwareObjectPostProcessor instantiationAwareObjectPostProcessor = + processor as IInstantiationAwareObjectPostProcessor; + if (instantiationAwareObjectPostProcessor != null) + { + properties = + instantiationAwareObjectPostProcessor.PostProcessPropertyValues(properties, filteredPropInfo, wrapper.WrappedInstance, + name); + if (properties == null) + { + return; + } + } + } + } + + if (needsDepCheck) + { + CheckDependencies(name, definition, filteredPropInfo, properties); + } + + } + + ApplyPropertyValues(name, definition, wrapper, properties); + } + + /// + /// Wires up any exposed events in the object instance in the given + /// with any event handler + /// values from the . + /// + /// + /// The name of the object. + /// + /// + /// The definition of the named object. + /// + /// + /// The wrapping the target object. + /// + protected void WireEvents(string name, IConfigurableObjectDefinition definition, IObjectWrapper wrapper) + { + foreach (string eventName in definition.EventHandlerValues.Events) + { + foreach (IEventHandlerValue handlerValue + in definition.EventHandlerValues[eventName]) + { + object handler = null; + if (handlerValue.Source is RuntimeObjectReference) + { + RuntimeObjectReference roref = (RuntimeObjectReference)handlerValue.Source; + handler = ResolveReference(definition, name, eventName, roref); + } + else if (handlerValue.Source is Type) + { + // a static Type event is being wired up; simply pass on the Type + handler = handlerValue.Source; + } + else if (handlerValue.Source is string) + { + // a static Type event is being wired up; we need to resolve the Type + handler = TypeResolutionUtils.ResolveType(handlerValue.Source as string); + } + else + { + throw new FatalObjectException("Currently, only references to other objects and Types are " + "supported as event sources."); + } + handlerValue.Wire(handler, wrapper.WrappedInstance); + } + } + } + + /// + /// Fills in any missing property values with references to + /// other objects in this factory if autowire is set to + /// . + /// + /// + /// The object name to be autowired by . + /// + /// + /// The definition of the named object to update through autowiring. + /// + /// + /// The wrapping the target object (and + /// from which we can rip out information concerning the object). + /// + /// + /// The property values to register wired objects with. + /// + protected void AutowireByName(string name, RootObjectDefinition definition, IObjectWrapper wrapper, MutablePropertyValues properties) + { + string[] propertyNames = UnsatisfiedObjectProperties(definition, wrapper); + foreach (string propertyName in propertyNames) + { + // look for a matching type + if (ContainsObject(propertyName)) + { + object o = GetObject(propertyName); + properties.Add(propertyName, o); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format(CultureInfo.InvariantCulture, + "Added autowiring by name from object name '{0}' via " + "property '{1}' to object named '{1}'.", name, + propertyName)); + } + + #endregion + } + else + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format(CultureInfo.InvariantCulture, + "Not autowiring property '{0}' of object '{1}' by name: " + "no matching object found.", propertyName, name)); + } + + #endregion + } + } + } + + /// + /// Defines "autowire by type" (object properties by type) behavior. + /// + /// + ///

    + /// This is like PicoContainer default, in which there must be exactly one object + /// of the property type in the object factory. This makes object factories simple + /// to configure for small namespaces, but doesn't work as well as standard Spring + /// behavior for bigger applications. + ///

    + ///
    + /// + /// The object name to be autowired by . + /// + /// + /// The definition of the named object to update through autowiring. + /// + /// + /// The wrapping the target object (and + /// from which we can rip out information concerning the object). + /// + /// + /// The property values to register wired objects with. + /// + protected void AutowireByType(string name, RootObjectDefinition definition, IObjectWrapper wrapper, MutablePropertyValues properties) + { + string[] propertyNames = UnsatisfiedObjectProperties(definition, wrapper); + foreach (string propertyName in propertyNames) + { + // look for a matching type + Type requiredType = wrapper.GetPropertyType(propertyName); + IDictionary matchingObjects = FindMatchingObjects(requiredType); + if (matchingObjects != null && matchingObjects.Count == 1) + { + properties.Add(propertyName, ObjectUtils.EnumerateFirstElement(matchingObjects.Values)); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format(CultureInfo.InvariantCulture, + "Autowiring by type from object name '{0}' via property " + "'{1}' to object named '{2}'.", name, + propertyName, ObjectUtils.EnumerateFirstElement(matchingObjects.Keys))); + } + + #endregion + } + else if (matchingObjects != null && matchingObjects.Count > 1) + { + throw new UnsatisfiedDependencyException(string.Empty, name, propertyName, + string.Format(CultureInfo.InvariantCulture, + "There are {0} objects of Type [{1}] for autowire by " + + "type, when there should have been just 1 to be able to " + + "autowire property '{2}' of object '{3}'.", matchingObjects.Count, + requiredType, propertyName, name)); + } + else + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format(CultureInfo.InvariantCulture, "Not autowiring property '{0}' of object '{1}': no matching object found.", + propertyName, name)); + } + + #endregion + } + } + } + + /// + /// Ignore the given dependency type for autowiring + /// + /// + /// This will typically be used by application contexts to register + /// dependencies that are resolved in other ways, like IOjbectFactory through + /// IObjectFactoryAware or IApplicationContext through IApplicationContextAware. + /// By default, IObjectFactoryAware and IObjectName interfaces are ignored. + /// For further types to ignore, invoke this method for each type. + /// + /// . + public void IgnoreDependencyInterface(Type type) + { + ignoredDependencyInterfaces.Add(type); + } + + /// + /// Create an object instance for the given object definition. + /// + /// The name of the object. + /// + /// The object definition for the object that is to be instantiated. + /// + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a static factory method. It is invalid to use a non- arguments value + /// in any other case. + /// + /// + /// A new instance of the object. + /// + /// + /// In case of errors. + /// + /// + ///

    + /// Delegates to the + /// + /// method version with the allowEagerCaching parameter set to true. + ///

    + ///

    + /// The object definition will already have been merged with the parent + /// definition in case of a child definition. + ///

    + ///

    + /// All the other methods in this class invoke this method, although objects + /// may be cached after being instantiated by this method. All object + /// instantiation within this class is performed by this method. + ///

    + ///
    + protected override object CreateObject(string name, RootObjectDefinition definition, object[] arguments) + { + return CreateObject(name, definition, arguments, true); + } + + /// + /// Create an object instance for the given object definition. + /// + /// The name of the object. + /// + /// The object definition for the object that is to be instantiated. + /// + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a static factory method. It is invalid to use a non- arguments value + /// in any other case. + /// + /// + /// Whether eager caching of singletons is allowed... typically true for + /// singlton objects, but never true for inner object definitions. + /// + /// + /// A new instance of the object. + /// + /// + /// In case of errors. + /// + /// + ///

    + /// The object definition will already have been merged with the parent + /// definition in case of a child definition. + ///

    + ///

    + /// All the other methods in this class invoke this method, although objects + /// may be cached after being instantiated by this method. All object + /// instantiation within this class is performed by this method. + ///

    + ///
    + protected virtual object CreateObject(string name, RootObjectDefinition definition, object[] arguments, bool allowEagerCaching) + { + // guarantee the initialization of objects that the current one depends on.. + if (definition.DependsOn != null && definition.DependsOn.Length > 0) + { + foreach (string dependant in definition.DependsOn) + { + GetObject(dependant); + } + } + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format(CultureInfo.InvariantCulture, "Creating instance of Object '{0}' with merged definition [{1}].", name, definition)); + } + + #endregion + + // Make sure object type is actually resolved at this point. + ResolveObjectType(definition, name); + + try + { + definition.PrepareMethodOverrides(); + } + catch (ObjectDefinitionValidationException ex) + { + throw new ObjectDefinitionStoreException(definition.ResourceDescription, name, + "Validation of method overrides failed. " + ex.Message, ex); + } + + // return IObjectDefinition instance itself for an abstract object-definition + if (definition.IsTemplate) + { + return definition; + } + + + + object instance = null; + + + IObjectWrapper instanceWrapper = null; + bool eagerlyCached = false; + try + { + // Give IInstantiationAwareObjectPostProcessors a chance to return a proxy instead of the target instance.... + if (definition.HasObjectType) + { + instance = ApplyObjectPostProcessorsBeforeInstantiation(definition.ObjectType, name); + if (instance != null) + { + return instance; + } + } + + + instanceWrapper = CreateObjectInstance(name, definition, arguments); + instance = instanceWrapper.WrappedInstance; + + // eagerly cache singletons to be able to resolve circular references + // even when triggered by lifecycle interfaces like IObjectFactoryAware. + if (allowEagerCaching && definition.IsSingleton) + { + if (log.IsDebugEnabled) + { + log.Debug("Eagerly caching object '" + name + "' to allow for resolving potential circular references"); + } + AddEagerlyCachedSingleton(name, definition, instance); + eagerlyCached = true; + } + + instance = ConfigureObject(name, definition, instanceWrapper); + } + catch (ObjectCreationException) + { + if (eagerlyCached) + { + RemoveEagerlyCachedSingleton(name, definition); + } + throw; + } + catch (Exception ex) + { + if (eagerlyCached) + { + RemoveEagerlyCachedSingleton(name, definition); + } + throw new ObjectCreationException(definition.ResourceDescription, name, "Initialization of object failed : " + ex.Message, ex); + } + return instance; + } + + /// + /// Add the created, but yet unpopulated singleton to the singleton cache + /// to be able to resolve circular references + /// + /// the name of the object to add to the cache. + /// the definition used to create and populated the object. + /// the raw object instance. + /// + /// Derived classes may override this method to select the right cache based on the object definition. + /// + protected virtual void AddEagerlyCachedSingleton(string objectName, IObjectDefinition objectDefinition, object rawSingletonInstance) + { + base.AddSingleton(objectName, rawSingletonInstance); + } + + /// + /// Remove the specified singleton from the singleton cache that has + /// been added before by a call to + /// + /// the name of the object to remove from the cache. + /// the definition used to create and populated the object. + /// + /// Derived classes may override this method to select the right cache based on the object definition. + /// + protected virtual void RemoveEagerlyCachedSingleton(string objectName, IObjectDefinition objectDefinition) + { + base.RemoveSingleton(objectName); + } + + /// + /// Creates an instance from the passed in + /// using constructor + /// + /// The name of the object to create - used for error messages. + /// The describing the object to be created. + /// optional arguments to pass to the constructor + /// An wrapping the already instantiated object + protected IObjectWrapper CreateObjectInstance(string name, RootObjectDefinition definition, object[] arguments) + { + IObjectWrapper instanceWrapper; + if (StringUtils.HasText(definition.FactoryMethodName)) + { + instanceWrapper = InstantiateUsingFactoryMethod(name, definition, arguments); + } + //Handle case when arguments are passed in explicitly. + else if (arguments != null && arguments.Length > 0) + { + instanceWrapper = AutowireConstructor(name, definition, arguments); + } + else if (definition.ResolvedAutowireMode == AutoWiringMode.Constructor || + definition.HasConstructorArgumentValues) + { + instanceWrapper = AutowireConstructor(name, definition); + } + else + { + instanceWrapper = new ObjectWrapper(InstantiationStrategy.Instantiate(definition, name, this)); + InitObjectWrapper(instanceWrapper); + } + return instanceWrapper; + } + + /// + /// Instantiate an object instance using a named factory method. + /// + /// + ///

    + /// The method may be static, if the + /// parameter specifies a class, rather than a + /// instance, or an + /// instance variable on a factory object itself configured using Dependency + /// Injection. + ///

    + ///

    + /// Implementation requires iterating over the static or instance methods + /// with the name specified in the supplied + /// (the method may be overloaded) and trying to match with the parameters. + /// We don't have the types attached to constructor args, so trial and error + /// is the only way to go here. + ///

    + ///
    + /// + /// The name associated with the supplied . + /// + /// + /// The definition describing the instance that is to be instantiated. + /// + /// + /// Any arguments to the factory method that is to be invoked. + /// + /// + /// The result of the factory method invocation (the instance). + /// + protected virtual IObjectWrapper InstantiateUsingFactoryMethod(string name, RootObjectDefinition definition, object[] arguments) + { + ConstructorArgumentValues cargs = definition.ConstructorArgumentValues; + ConstructorArgumentValues resolvedValues = new ConstructorArgumentValues(); + int expectedArgCount = 0; + + // we don't have arguments passed in programmatically, so we need to resolve the + // arguments specified in the constructor arguments held in the object definition... + if (arguments == null || arguments.Length == 0) + { + expectedArgCount = cargs.ArgumentCount; + ResolveConstructorArguments(name, definition, resolvedValues); + } + else + { + // if we have constructor args, don't need to resolve them... + expectedArgCount = arguments.Length; + } + ObjectWrapper wrapper = new ObjectWrapper(); + InitObjectWrapper(wrapper); + bool isStatic = true; + Type factoryClass = null; + if (StringUtils.HasText(definition.FactoryObjectName)) + { + // it's an instance method on the factory object's class... + factoryClass = GetObject(definition.FactoryObjectName).GetType(); + isStatic = false; + } + else + { + // it's a static factory method on the object class... + factoryClass = definition.ObjectType; + } + +#if NET_2_0 + GenericArgumentsHolder genericArgsInfo = new GenericArgumentsHolder(definition.FactoryMethodName); + + MethodInfo[] factoryMethods = FindMethods(genericArgsInfo.GenericMethodName, expectedArgCount, isStatic, factoryClass); + UnsatisfiedDependencyExceptionData unsatisfiedDependencyExceptionData = null; + // try all matching methods to see if they match the constructor arguments... + for (int i = 0; i < factoryMethods.Length; i++) + { + unsatisfiedDependencyExceptionData = null; + MethodInfo factoryMethod = factoryMethods[i]; + + if (genericArgsInfo.ContainsGenericArguments) + { + string[] unresolvedGenericArgs = genericArgsInfo.GetGenericArguments(); + if (factoryMethod.GetGenericArguments().Length != unresolvedGenericArgs.Length) + continue; + + Type[] genericArgs = new Type[unresolvedGenericArgs.Length]; + for (int j = 0; j < unresolvedGenericArgs.Length; j++) + { + genericArgs[j] = TypeResolutionUtils.ResolveType(unresolvedGenericArgs[j]); + } + factoryMethod = factoryMethod.MakeGenericMethod(genericArgs); + } +#else + MethodInfo[] factoryMethods = FindMethods(definition.FactoryMethodName, expectedArgCount, isStatic, factoryClass); + UnsatisfiedDependencyExceptionData unsatisfiedDependencyExceptionData = null; + // try all matching methods to see if they match the constructor arguments... + foreach(MethodInfo factoryMethod in factoryMethods) + { +#endif + if (arguments == null || arguments.Length == 0) + { + // try to create the required arguments... + arguments = CreateArgumentArray(name, definition, resolvedValues, factoryMethod, out unsatisfiedDependencyExceptionData); + if (arguments == null) + { + // if we failed to match this method, keep + // trying new overloaded factory methods... + continue; + } + } + // if we get here, we found a factory method... + + if (ReflectionUtils.GetMethodByArgumentValues(new MethodInfo[] { factoryMethod }, arguments) == null) + { + continue; + } + + + object objectInstance = InstantiationStrategy.Instantiate(definition, name, this, factoryMethod, arguments); + wrapper.WrappedInstance = objectInstance; + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format(CultureInfo.InvariantCulture, "Object '{0}' instantiated via factory method [{1}].", name, factoryMethod)); + } + + #endregion + + return wrapper; + } + + + + // if we get here, we didn't match any method... + throw new ObjectDefinitionStoreException( + string.Format(CultureInfo.InvariantCulture, "Cannot find matching factory method '{0} on Type [{1}].", definition.FactoryMethodName, + factoryClass)); + } + + /// + /// Returns an array of all of those + /// methods exposed on the + /// that match the supplied criteria. + /// + /// + /// Methods that have this name (can be in the form of a regular expression). + /// + /// + /// Methods that have exactly this many arguments. + /// + /// + /// Methods that are static / instance. + /// + /// + /// The on which the methods (if any) are to be found. + /// + /// + /// An array of all of those + /// methods exposed on the + /// that match the supplied criteria. + /// + private static MethodInfo[] FindMethods(string methodName, int expectedArgumentCount, bool isStatic, Type searchType) + { + ComposedCriteria methodCriteria = new ComposedCriteria(); + methodCriteria.Add(new MethodNameMatchCriteria(methodName)); + methodCriteria.Add(new MethodParametersCountCriteria(expectedArgumentCount)); + BindingFlags methodFlags = BindingFlags.Public | BindingFlags.IgnoreCase | (isStatic ? BindingFlags.Static : BindingFlags.Instance); + MemberInfo[] methods = + searchType.FindMembers(MemberTypes.Method, methodFlags, new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria), + methodCriteria); + return (MethodInfo[])ArrayList.Adapter(methods).ToArray(typeof(MethodInfo)); + } + + /// + /// Create an array of arguments to invoke a constructor or static factory method, + /// given the resolved constructor arguments values. + /// + /// When return value is null the out parameter UnsatisfiedDependencyExceptionData will contain + /// information for use in throwing a UnsatisfiedDependencyException by the caller. This avoids using + /// exceptions for flow control as in the original implementation. + private object[] CreateArgumentArray(string name, RootObjectDefinition definition, + ConstructorArgumentValues resolvedValues, MethodBase methodOrCtor, out UnsatisfiedDependencyExceptionData unsatisfiedDependencyExceptionData) + { + string methodType = (methodOrCtor is ConstructorInfo) ? "constructor" : "factory method"; + unsatisfiedDependencyExceptionData = null; + ParameterInfo[] argTypes = methodOrCtor.GetParameters(); + object[] args = new object[argTypes.Length]; + ISet alreadyUsedValues = new HybridSet(); + for (int j = 0; j < argTypes.Length; ++j) + { + Type parameterType = argTypes[j].ParameterType; + string parameterName = argTypes[j].Name; + ConstructorArgumentValues.ValueHolder valueHolder = null; + if (resolvedValues.GetNamedArgumentValue(parameterName) != null) + { + valueHolder = resolvedValues.GetArgumentValue(parameterName, parameterType, alreadyUsedValues); + } + else + { + valueHolder = resolvedValues.GetArgumentValue(j, parameterType, alreadyUsedValues); + } + if (valueHolder != null) + { + try + { + args[j] = TypeConversionUtils.ConvertValueIfNecessary(parameterType, valueHolder.Value, null); + alreadyUsedValues.Add(valueHolder); + } + catch (TypeMismatchException ex) + { + string errorMessage = String.Format(CultureInfo.InvariantCulture, + "Could not convert {0} argument value [{1}] to required type [{2}] : {3}", + methodType, valueHolder.Value, + parameterType, ex.Message); + unsatisfiedDependencyExceptionData = new UnsatisfiedDependencyExceptionData(j, parameterType, errorMessage); + + + return null; + } + } + else + { + if (definition.ResolvedAutowireMode != AutoWiringMode.Constructor) + { + string errorMessage = String.Format(CultureInfo.InvariantCulture, + "Ambiguous {0} argument types - " + + "Did you specify the correct object references as {0} arguments?", + methodType); + unsatisfiedDependencyExceptionData = new UnsatisfiedDependencyExceptionData(j, parameterType, errorMessage); + + return null; + } + IDictionary matchingObjects = FindMatchingObjects(parameterType); + if (matchingObjects == null || matchingObjects.Count != 1) + { + string errorMessage = String.Format(CultureInfo.InvariantCulture, + "There are '{0}' objects of type [{1}] for autowiring " + + + "{2}. There should have been exactly 1 to be able to " + + + "autowire the '{3}' argument on the {2} of object '{4}'.", + (matchingObjects == null + ? 0 + : matchingObjects.Count), + parameterType, methodType, + parameterName, name); + unsatisfiedDependencyExceptionData = new UnsatisfiedDependencyExceptionData(j, parameterType, errorMessage); + + return null; + } + DictionaryEntry entry = (DictionaryEntry)ObjectUtils.EnumerateFirstElement(matchingObjects); + args[j] = entry.Value; + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format(CultureInfo.InvariantCulture, + "Autowiring '{0}' argument by type from object name '{1}' via {2} to " + + "object named '{3}'.", parameterName, name, methodType, entry.Key)); + } + + #endregion + } + } + + return args; + } + + /// + /// Explicitly construct the object using the supplied constructor arguments. + /// Constructor arguments are matched by type. + /// + /// + /// The name of the object to autowire by type. + /// + /// + /// The object definition to update through autowiring. + /// + /// Array of constructor argument values. + /// + /// An for the new instance. + /// + protected IObjectWrapper AutowireConstructor(string name, RootObjectDefinition definition, object[] args) + { + ConstructorArgumentValues resolvedValues = new ConstructorArgumentValues(); + for (int i = 0; i < args.Length; i++) + { + //This is assigning ctor arguments by type. + resolvedValues.AddGenericArgumentValue(args[i]); + } + return AutowireConstructor(name, definition, resolvedValues); + } + + /// + /// "autowire constructor" (with constructor arguments by type) behaviour. + /// + /// Passes an empty collection of constructor argument values + /// to overloaded method. + /// + /// + /// The name of the object to autowire by type. + /// + /// + /// The object definition to update through autowiring. + /// + /// + /// An for the new instance. + /// + protected IObjectWrapper AutowireConstructor(string name, RootObjectDefinition definition) + { + return AutowireConstructor(name, definition, new ConstructorArgumentValues()); + } + + /// + /// "autowire constructor" (with constructor arguments by type) behaviour. + /// + /// + ///

    + /// Also applied if explicit constructor argument values are specified, + /// matching all remaining arguments with objects from the object factory. + ///

    + ///

    + /// This corresponds to constructor injection: in this mode, a Spring.NET + /// object factory is able to host components that expect constructor-based + /// dependency resolution. + ///

    + ///
    + /// + /// The name of the object to autowire by type. + /// + /// + /// The object definition to update through autowiring. + /// + /// + /// The collection on constructor argument values. + /// + /// + /// An for the new instance. + /// + protected IObjectWrapper AutowireConstructor(string name, RootObjectDefinition definition, ConstructorArgumentValues argumentValues) + { + int minNrOfArgs = ResolveConstructorArguments(name, definition, argumentValues); + ConstructorInfo[] constructors = AutowireUtils.GetConstructors(definition, minNrOfArgs); + if (constructors == null || constructors.Length == 0) + { + throw new ObjectCreationException(definition.ResourceDescription, name, + string.Format(CultureInfo.InvariantCulture, + "'{0}' constructor arguments specified but no matching constructor found " + + "in object '{1}' (hint: specify argument indexes, names, or " + + "types to avoid ambiguities).", minNrOfArgs, name)); + } + ObjectWrapper wrapper = new ObjectWrapper(); + InitObjectWrapper(wrapper); + ConstructorInfo constructorToUse = null; + object[] argsToUse = null; + int weighting = Int32.MaxValue; + UnsatisfiedDependencyExceptionData unsatisfiedDependencyExceptionData = null; + for (int i = 0; i < constructors.Length; ++i) + { + unsatisfiedDependencyExceptionData = null; + ConstructorInfo constructor = constructors[i]; + if (constructorToUse != null && + constructorToUse.GetParameters().Length > constructor.GetParameters().Length) + { + // already found greedy constructor that can be satisfied, so + // don't look any further, there are only less greedy constructors left... + break; + } + + object[] args = CreateArgumentArray(name, definition, argumentValues, constructor, out unsatisfiedDependencyExceptionData); + if (args == null) + { + if (i == constructors.Length - 1 && constructorToUse == null) + { + throw new UnsatisfiedDependencyException(definition.ResourceDescription, + name, + unsatisfiedDependencyExceptionData.ParameterIndex, + unsatisfiedDependencyExceptionData.ParameterType, + unsatisfiedDependencyExceptionData.ErrorMessage); + } + // try next constructor... + continue; + } + + int typeDiffWeight = AutowireUtils.GetTypeDifferenceWeight(constructor.GetParameters(), args); + if (typeDiffWeight < weighting) + { + constructorToUse = constructor; + argsToUse = args; + weighting = typeDiffWeight; + } + } + + if (constructorToUse == null) + { + throw new ObjectCreationException(definition.ResourceDescription, name, "Could not resolve matching constructor."); + } + wrapper.WrappedInstance = InstantiationStrategy.Instantiate(definition, name, this, constructorToUse, argsToUse); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format(CultureInfo.InvariantCulture, "Object '{0}' instantiated via constructor [{1}].", name, constructorToUse)); + } + + #endregion + + return wrapper; + } + + /// + /// Resolves the + /// of the supplied . + /// + /// + ///

    + /// 'Resolve' can be taken to mean that all of the s + /// constructor arguments is resolved into a concrete object that can be plugged + /// into one of the s constructors. Runtime object + /// references to other objects in this (or a parent) factory are resolved, + /// type conversion is performed, etc. + ///

    + ///

    + /// These resolved values are plugged into the supplied + /// object, because we wouldn't want to touch + /// the s constructor arguments in case it (or any of + /// its constructor arguments) is a prototype object definition. + ///

    + ///

    + /// This method is also used for handling invocations of static factory methods. + ///

    + ///
    + /// + /// The name of the object that is being resolved by this factory. + /// + /// + /// The definition associated with the above . + /// + /// + /// Where the resolved constructor arguments will be placed. + /// + /// + /// The minimum number of arguments that any constructor for the supplied + /// must have. + /// + private int ResolveConstructorArguments(string name, RootObjectDefinition definition, ConstructorArgumentValues resolvedValues) + { + int minNrOfArgs = 0; + if (definition.ConstructorArgumentValues != null) + { + minNrOfArgs = definition.ConstructorArgumentValues.ArgumentCount; + foreach (DictionaryEntry de in definition.ConstructorArgumentValues.IndexedArgumentValues) + { + int index = (int)de.Key; + if (index < 0) + { + throw new ObjectCreationException(definition.ResourceDescription, name, "Invalid constructor argument index: " + index); + } + if (index > minNrOfArgs) + { + minNrOfArgs = index + 1; + } + string argName = "constructor argument with index " + index; + ConstructorArgumentValues.ValueHolder valueHolder = (ConstructorArgumentValues.ValueHolder)de.Value; + object resolvedValue = ResolveValueIfNecessary(name, definition, argName, valueHolder.Value); + resolvedValues.AddIndexedArgumentValue(index, resolvedValue, + StringUtils.HasText(valueHolder.Type) + ? TypeResolutionUtils.ResolveType(valueHolder.Type).AssemblyQualifiedName + : null); + } + foreach (ConstructorArgumentValues.ValueHolder valueHolder in definition.ConstructorArgumentValues.GenericArgumentValues) + { + string argName = "constructor argument"; + object resolvedValue = ResolveValueIfNecessary(name, definition, argName, valueHolder.Value); + resolvedValues.AddGenericArgumentValue(resolvedValue, + StringUtils.HasText(valueHolder.Type) + ? TypeResolutionUtils.ResolveType(valueHolder.Type).AssemblyQualifiedName + : null); + } + foreach (DictionaryEntry namedArgumentEntry in definition.ConstructorArgumentValues.NamedArgumentValues) + { + string argumentName = (string)namedArgumentEntry.Key; + string syntheticArgumentName = "constructor argument with name " + argumentName; + ConstructorArgumentValues.ValueHolder valueHolder = (ConstructorArgumentValues.ValueHolder)namedArgumentEntry.Value; + object resolvedValue = ResolveValueIfNecessary(name, definition, syntheticArgumentName, valueHolder.Value); + resolvedValues.AddNamedArgumentValue(argumentName, resolvedValue); + } + } + return minNrOfArgs; + } + + /// + /// Perform a dependency check that all properties exposed have been set, if desired. + /// + /// + ///

    + /// Dependency checks can be objects (collaborating objects), simple (primitives + /// and ), or all (both). + ///

    + ///
    + /// + /// The name of the object. + /// + /// + /// The definition of the named object. + /// + /// + /// The wrapping the target object. + /// + /// + /// The property values to be checked. + /// + /// + /// If all of the checked dependencies were not satisfied. + /// + protected void DependencyCheck(string name, IConfigurableObjectDefinition definition, IObjectWrapper wrapper, IPropertyValues properties) + { + DependencyCheckingMode dependencyCheck = definition.DependencyCheck; + if (dependencyCheck == DependencyCheckingMode.None) + { + return; + } + + PropertyInfo[] filteredPropInfo = FilterPropertyInfoForDependencyCheck(wrapper); + if (HasInstantiationAwareBeanPostProcessors) + { + foreach (IObjectPostProcessor processor in ObjectPostProcessors) + { + IInstantiationAwareObjectPostProcessor inProc = processor as IInstantiationAwareObjectPostProcessor; + if (inProc != null) + { + properties = + inProc.PostProcessPropertyValues(properties, filteredPropInfo, wrapper.WrappedInstance, name); + if (properties == null) + { + return; + } + } + } + } + + + CheckDependencies(name, definition, filteredPropInfo, properties); + } + + private static void CheckDependencies(string name, IConfigurableObjectDefinition definition, PropertyInfo[] filteredPropInfo, IPropertyValues properties) + { + DependencyCheckingMode dependencyCheck = definition.DependencyCheck; + foreach (PropertyInfo property in filteredPropInfo) + { + if (property.CanWrite && properties.GetPropertyValue(property.Name) == null) + { + bool isSimple = ObjectUtils.IsSimpleProperty(property.PropertyType); + bool unsatisfied = (dependencyCheck == DependencyCheckingMode.All) || (isSimple && dependencyCheck == DependencyCheckingMode.Simple) + || (!isSimple && dependencyCheck == DependencyCheckingMode.Objects); + if (unsatisfied) + { + throw new UnsatisfiedDependencyException(definition.ResourceDescription, name, property.Name, + "Set this property value or disable dependency checking for this object."); + } + } + } + } + + /// + /// Extract a filtered set of PropertyInfos from the given IObjectWrapper, excluding + /// ignored dependency types. + /// + /// The object wrapper the object was created with. + /// The filtered PropertyInfos + private PropertyInfo[] FilterPropertyInfoForDependencyCheck(IObjectWrapper wrapper) + { + lock (filteredPropertyDescriptorsCache) + { + PropertyInfo[] filtered = (PropertyInfo[])filteredPropertyDescriptorsCache[wrapper.WrappedType]; + if (filtered == null) + { + + ArrayList list = new ArrayList(wrapper.GetPropertyInfos()); + for (int i = list.Count - 1; i >= 0; i--) + { + PropertyInfo pi = (PropertyInfo)list[i]; + if (IsExcludedFromDependencyCheck(pi)) + { + list.RemoveAt(i); + } + } + + filtered = (PropertyInfo[])list.ToArray(typeof(PropertyInfo)); + filteredPropertyDescriptorsCache.Add(wrapper.WrappedType, filtered); + } + return filtered; + } + + } + + private bool IsExcludedFromDependencyCheck(PropertyInfo pi) + { + bool b1 = !pi.CanWrite; //AutowireUtils.IsExcludedFromDependencyCheck(pi); + bool b2 = IgnoredDependencyTypes.Contains(pi.PropertyType); + bool b3 = AutowireUtils.IsSetterDefinedInInterface(pi, ignoredDependencyInterfaces); + return b1 || b2 || b3; + /* + return AutowireUtils.IsExcludedFromDependencyCheck(pi) || + IgnoredDependencyTypes.Contains(pi.PropertyType) || + AutowireUtils.IsSetterDefinedInInterface(pi, ignoredDependencyInterfaces); + */ + } + + /// + /// Give an object a chance to react now all its properties are set, + /// and a chance to know about its owning object factory (this object). + /// + /// + ///

    + /// This means checking whether the object implements + /// and / or + /// , and invoking the + /// necessary callback(s) if it does. + ///

    + ///

    + /// Custom init methods are resolved in a case-insensitive manner. + ///

    + ///
    + /// + /// The new object instance we may need to initialise. + /// + /// + /// The name the object has in the factory. Used for logging output. + /// + /// + /// The definition of the target object instance. + /// + protected virtual void InvokeInitMethods(object target, string name, IConfigurableObjectDefinition definition) + { + if (ObjectUtils.IsAssignableAndNotTransparentProxy(typeof(IInitializingObject), target)) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format(CultureInfo.InvariantCulture, "Calling AfterPropertiesSet() on object with name '{0}'.", name)); + } + + #endregion + + ((IInitializingObject)target).AfterPropertiesSet(); + } + if (StringUtils.HasText(definition.InitMethodName)) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format(CultureInfo.InvariantCulture, "Calling custom init method '{0} on object with name '{1}'.", + definition.InitMethodName, name)); + } + + #endregion + + try + { + MethodInfo targetMethod = target.GetType().GetMethod(definition.InitMethodName, MethodResolutionFlags, null, Type.EmptyTypes, null); + if (targetMethod == null) + { + throw new ObjectCreationException(definition.ResourceDescription, name, + "Could not find the named initialization method '" + definition.InitMethodName + "'."); + } + targetMethod.Invoke(target, ObjectUtils.EmptyObjects); + } + catch (TargetInvocationException ex) + { + throw new ObjectCreationException(definition.ResourceDescription, name, + "Initialization method '" + definition.InitMethodName + "' threw exception", ex.GetBaseException()); + } + catch (Exception ex) + { + throw new ObjectCreationException(definition.ResourceDescription, name, + "Invocation of initialization method '" + definition.InitMethodName + "' failed", ex); + } + } + } + + /// + /// Invoke the specified custom destroy method on the given object. + /// + /// + ///

    + /// This implementation invokes a no-arg method if found, else checking + /// for a method with a single boolean argument (passing in "true", + /// assuming a "force" parameter), else logging an error. + ///

    + ///

    + /// Can be overridden in subclasses for custom resolution of destroy + /// methods with arguments. + ///

    + ///

    + /// Custom destroy methods are resolved in a case-insensitive manner. + ///

    + ///
    + protected virtual void InvokeCustomDestroyMethod(string name, object target, string destroyMethodName) + { + bool usingForcingVersion = false; + MethodInfo targetMethod = target.GetType().GetMethod(destroyMethodName, MethodResolutionFlags, null, Type.EmptyTypes, null); + if (targetMethod == null) + { + // #%&^! try to find the method with a boolean "force" parameter + targetMethod = target.GetType().GetMethod(destroyMethodName, MethodResolutionFlags, null, new Type[] { typeof(bool) }, null); + if (targetMethod != null) + { + usingForcingVersion = true; + } + } + if (targetMethod == null) + { + #region Instrumentation + + log.Error("Couldn't find a method named '" + destroyMethodName + "' on object with name '" + name + "'"); + + #endregion + } + else + { + object[] args = usingForcingVersion ? new object[] { true } : ObjectUtils.EmptyObjects; + try + { + targetMethod.Invoke(target, args); + } + catch (TargetInvocationException ex) + { + #region Instrumentation + + log.Error("Couldn't invoke destroy method '" + destroyMethodName + "' of object with name '" + name + "'", ex.GetBaseException()); + + #endregion + } + catch (Exception ex) + { + LogExceptionRaisedByCustomDestroyMethodInvocation(destroyMethodName, name, ex); + } + } + } + + private void LogExceptionRaisedByCustomDestroyMethodInvocation(string destroyMethodName, string name, Exception ex) + { + log.Error( + string.Format(CultureInfo.InvariantCulture, "Couldn't invoke destroy method '{0}' of object with name '{1}'.", destroyMethodName, name), + ex); + } + + /// + /// Destroy the target object. + /// + /// + ///

    + /// Must destroy objects that depend on the given object before the object itself. + /// Should not throw any exceptions. + ///

    + ///
    + /// + /// The name of the object. + /// + /// + /// The target object instance to destroyed. + /// + protected override void DestroyObject(string name, object target) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Destroying dependant objects for object '" + name + "'"); + } + + #endregion + + DestroyDependantObjects(name); + if (target is IDisposable) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format(CultureInfo.InvariantCulture, "Calling Dispose () on object with name '{0}'.", name)); + } + + #endregion + + try + { + ((IDisposable)target).Dispose(); + } + catch (Exception ex) + { + #region Instrumentation + + log.Error("Destroy() on object with name '" + name + "' threw an exception.", ex); + + #endregion + } + } + RootObjectDefinition rootDefinition = GetMergedObjectDefinition(name, false); + if (rootDefinition != null && StringUtils.HasText(rootDefinition.DestroyMethodName)) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Calling custom destroy method '" + rootDefinition.DestroyMethodName + "' on object with name '" + name + "'."); + } + + #endregion + + InvokeCustomDestroyMethod(name, target, rootDefinition.DestroyMethodName); + } + } + + /// + /// Destroys all of the objects registered as dependant on the + /// object (definition) identified by the supplied . + /// + /// + /// The name of the root object (definition) that is itself being destroyed. + /// + private void DestroyDependantObjects(string name) + { + string[] dependingObjects = GetDependingObjectNames(name); + foreach (string doName in dependingObjects) + { + DestroySingleton(doName); + } + } + + /// + /// Given a property value, return a value, resolving any references to other + /// objects in the factory if necessary. + /// + /// + ///

    + /// The value could be : + /// + /// + ///

    + /// An , + /// which leads to the creation of a corresponding new object instance. + /// Singleton flags and names of such "inner objects" are always ignored: inner objects + /// are anonymous prototypes. + ///

    + /// + /// + ///

    + /// A , which must + /// be resolved. + ///

    + ///
    + /// + ///

    + /// An . This is a + /// special placeholder collection that may contain + /// s or + /// collections that will need to be resolved. + ///

    + ///
    + /// + ///

    + /// An ordinary object or , in which case it's left alone. + ///

    + ///
    + /// + ///

    + ///
    + /// + /// The name of the object that is having the value of one of its properties resolved. + /// + /// + /// The definition of the named object. + /// + /// + /// The name of the property the value of which is being resolved. + /// + /// + /// The value of the property that is being resolved. + /// + protected object ResolveValueIfNecessary(string name, RootObjectDefinition definition, string argumentName, object argumentValue) + { + object resolvedValue = null; + // we must check the argument value to see whether it requires a runtime + // reference to another object to be resolved. + // if it does, we'll attempt to instantiate the object and set the reference. + if (argumentValue is ObjectDefinitionHolder) + { + // contains an IObjectDefinition with name and aliases... + ObjectDefinitionHolder holder = (ObjectDefinitionHolder)argumentValue; + resolvedValue = ResolveInnerObjectDefinition(name, holder.ObjectName, argumentName, holder.ObjectDefinition, definition.IsSingleton); + } + else if (argumentValue is IObjectDefinition) + { + // resolve plain IObjectDefinition, without contained name: use dummy name... + IObjectDefinition def = (IObjectDefinition)argumentValue; + resolvedValue = ResolveInnerObjectDefinition(name, "(inner object)", argumentName, def, definition.IsSingleton); + + } + else if (argumentValue is RuntimeObjectReference) + { + RuntimeObjectReference roref = (RuntimeObjectReference)argumentValue; + resolvedValue = ResolveReference(definition, name, argumentName, roref); + } + else if (argumentValue is ExpressionHolder) + { + ExpressionHolder expHolder = (ExpressionHolder)argumentValue; + object context = null; + IDictionary variables = null; + + if (expHolder.Properties != null) + { + PropertyValue contextProperty = expHolder.Properties.GetPropertyValue("Context"); + context = contextProperty == null + ? null + : ResolveValueIfNecessary(name, definition, "Context", + contextProperty.Value); + PropertyValue variablesProperty = expHolder.Properties.GetPropertyValue("Variables"); + object vars = (variablesProperty == null + ? null + : ResolveValueIfNecessary(name, definition, "Variables", + variablesProperty.Value)); + if (vars is IDictionary) + { + variables = (IDictionary)vars; + } + else + { + if (vars != null) throw new ArgumentException("'Variables' must resolve to an IDictionary"); + } + } + + if (variables == null) variables = CollectionsUtil.CreateCaseInsensitiveHashtable(); + // add 'this' objectfactory reference to variables + variables.Add(Expression.ReservedVariableNames.CurrentObjectFactory, this); + + resolvedValue = expHolder.Expression.GetValue(context, variables); + } + else if (argumentValue is IManagedCollection) + { + resolvedValue = + ((IManagedCollection)argumentValue).Resolve(name, definition, argumentName, + new ManagedCollectionElementResolver(ResolveValueIfNecessary)); + } + else if (argumentValue is TypedStringValue) + { + TypedStringValue tsv = (TypedStringValue)argumentValue; + try + { + Type resolvedTargetType = ResolveTargetType(tsv); + if (resolvedTargetType != null) + { + resolvedValue = TypeConversionUtils.ConvertValueIfNecessary(tsv.TargetType, tsv.Value, null); + } + else + { + resolvedValue = tsv.Value; + } + } + catch (Exception ex) + { + throw new ObjectCreationException(definition.ResourceDescription, name, + "Error converted typed String value for " + argumentName, ex); + } + + } + else + { + // no need to resolve value... + resolvedValue = argumentValue; + } + return resolvedValue; + } + + /// + /// Resolve the target type of the passed . + /// + /// The who's target type is to be resolved + /// The resolved target type, if any. otherwise. + protected virtual Type ResolveTargetType(TypedStringValue value) + { + if (value.HasTargetType) + { + return value.TargetType; + } + else + { + return null; + } + } + /// + /// Resolves an inner object definition. + /// + /// + /// The name of the object that surrounds this inner object definition. + /// + /// + /// The name of the inner object definition... note: this is a synthetic + /// name assigned by the factory (since it makes no sense for inner object + /// definitions to have names). + /// + /// + /// The name of the property the value of which is being resolved. + /// + /// + /// The definition of the inner object that is to be resolved. + /// + /// + /// if the owner of the property is a singleton. + /// + /// + /// The resolved object as defined by the inner object definition. + /// + protected object ResolveInnerObjectDefinition(string name, string innerObjectName, string argumentName, IObjectDefinition definition, + bool singletonOwner) + { + RootObjectDefinition mod = GetMergedObjectDefinition(innerObjectName, definition); + mod.IsSingleton = singletonOwner; + object instance; + object result; + try + { + instance = CreateObject(innerObjectName, mod, ObjectUtils.EmptyObjects, false); + result = GetObjectForInstance(innerObjectName, instance); + } + catch (ObjectsException ex) + { + throw ObjectCreationException.GetObjectCreationException(ex, name, argumentName, definition.ResourceDescription, innerObjectName); + } + if (singletonOwner && instance is IDisposable) + { + // keep a reference to the inner object instance, to be able to destroy + // it on factory shutdown... + _disposableInnerObjects.Add(instance); + } + return result; + } + + /// + /// Resolve a reference to another object in the factory. + /// + /// + /// The name of the object that is having the value of one of its properties resolved. + /// + /// + /// The definition of the named object. + /// + /// + /// The name of the property the value of which is being resolved. + /// + /// + /// The runtime reference containing the value of the property. + /// + /// A reference to another object in the factory. + protected object ResolveReference(IConfigurableObjectDefinition definition, string name, string argumentName, RuntimeObjectReference reference) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format(CultureInfo.InvariantCulture, "Resolving reference from property '{0}' in object '{1}' to object '{2}'.", + argumentName, name, reference.ObjectName)); + } + + #endregion + + try + { + if (reference.IsToParent) + { + if (null == ParentObjectFactory) + { + throw new ObjectCreationException(definition.ResourceDescription, name, + string.Format( + "Can't resolve reference to '{0}' in parent factory: " + "no parent factory available.", + reference.ObjectName)); + } + return ParentObjectFactory.GetObject(reference.ObjectName); + } + return GetObject(reference.ObjectName); + } + catch (ObjectsException ex) + { + throw ObjectCreationException.GetObjectCreationException(ex, name, argumentName, definition.ResourceDescription, reference.ObjectName); + } + } + + /// + /// Find object instances that match the required . + /// + /// + ///

    + /// Called by autowiring. If a subclass cannot obtain information about object + /// names by , a corresponding exception should be thrown. + ///

    + ///
    + /// + /// The of the objects to look up. + /// + /// + /// An of object names and object + /// instances that match the required , or + /// if none are found. + /// + /// + /// In case of errors. + /// + protected abstract IDictionary FindMatchingObjects(Type requiredType); + + /// + /// Return the names of the objects that depend on the given object. + /// Called by DestroyObject, to be able to destroy depending objects first. + /// + /// + /// The name of the object to find depending objects for. + /// + /// + /// The array of names of depending objects, or the empty string array if none. + /// + /// + /// In case of errors. + /// + protected abstract string[] GetDependingObjectNames(string name); + + /// + /// Injects dependencies into the supplied instance + /// using the named object definition. + /// + /// + /// The object instance that is to be so configured. + /// + /// + /// The name of the object definition expressing the dependencies that are to + /// be injected into the supplied instance. + /// + /// + public override object ConfigureObject(object target, string name) + { + RootObjectDefinition definition = GetMergedObjectDefinition(name, true); + if (definition != null) + { + return ConfigureObject(name, definition, new ObjectWrapper(target)); + } + + return null; + } + + /// + /// Injects dependencies into the supplied instance + /// using the supplied . + /// + /// + /// The object instance that is to be so configured. + /// + /// + /// The name of the object definition expressing the dependencies that are to + /// be injected into the supplied instance. + /// + /// + /// An object definition that should be used to configure object. + /// + /// + public override object ConfigureObject(object target, string name, IObjectDefinition definition) + { + return ConfigureObject(name, new RootObjectDefinition(definition), new ObjectWrapper(target)); + } + + /// + /// Configures object instance by injecting dependencies, satisfying Spring lifecycle + /// interfaces and applying object post-processors. + /// + /// + /// The name of the object definition expressing the dependencies that are to + /// be injected into the supplied instance. + /// + /// + /// An object definition that should be used to configure object. + /// + /// + /// A wrapped object instance that is to be so configured. + /// + /// + protected virtual object ConfigureObject(string name, RootObjectDefinition definition, IObjectWrapper wrapper) + { + object instance = wrapper.WrappedInstance; + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format("configuring object '{0}' using definition '{1}'", instance, name)); + } + + #endregion + + PopulateObject(name, definition, wrapper); + WireEvents(name, definition, wrapper); + + if (ObjectUtils.IsAssignableAndNotTransparentProxy(typeof(IObjectNameAware), instance)) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format(CultureInfo.InvariantCulture, "Setting the name property on the IObjectNameAware object '{0}'.", name)); + } + + #endregion + + ((IObjectNameAware)instance).ObjectName = name; + } + + if (ObjectUtils.IsAssignableAndNotTransparentProxy(typeof(IObjectFactoryAware), instance)) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format(CultureInfo.InvariantCulture, "Setting the ObjectFactory property on the IObjectFactoryAware object '{0}'.", + name)); + } + + #endregion + + ((IObjectFactoryAware)instance).ObjectFactory = this; + } + + instance = ApplyObjectPostProcessorsBeforeInitialization(instance, name); + InvokeInitMethods(instance, name, definition); + instance = ApplyObjectPostProcessorsAfterInitialization(instance, name); + + return instance; + } + + + /// + /// Applies the PostProcessAfterInitialization callback of all + /// registered IObjectPostProcessors, giving them a chance to post-process + /// the object obtained from IFactoryObjects (for example, to auto-proxy them) + /// + /// The instance obtained from the IFactoryObject. + /// Name of the object. + /// The object instance to expose + /// if any post-processing failed. + protected override object PostProcessObjectFromFactoryObject(object instance, string objectName) + { + return ApplyObjectPostProcessorsAfterInitialization(instance, objectName); + } + + #endregion + + #region IAutowireCapableObjectFactory Members + + /// + /// Create a new object instance of the given class with the specified + /// autowire strategy. + /// + /// + /// The of the object to instantiate. + /// + /// + /// The desired autowiring mode. + /// + /// + /// Whether to perform a dependency check for objects (not applicable to + /// autowiring a constructor, thus ignored there). + /// + /// The new object instance. + /// + /// If the wiring fails. + /// + /// + public virtual object Autowire(Type type, AutoWiringMode autowireMode, bool dependencyCheck) + { + RootObjectDefinition rod = new RootObjectDefinition(type, autowireMode, dependencyCheck); + if (rod.ResolvedAutowireMode == AutoWiringMode.Constructor) + { + return AutowireConstructor(type.Name, rod).WrappedInstance; + } + else + { + object obj = InstantiationStrategy.Instantiate(rod, string.Empty, this); + PopulateObject(obj.GetType().Name, rod, new ObjectWrapper(obj)); + return obj; + } + } + + /// + /// Autowire the object properties of the given object instance by name or + /// . + /// + /// + /// The existing object instance. + /// + /// + /// The desired autowiring mode. + /// + /// + /// Whether to perform a dependency check for the object. + /// + /// + /// If the wiring fails. + /// + /// + /// If the supplied is not one of the + /// or + /// + /// values. + /// + /// + public virtual void AutowireObjectProperties(object instance, AutoWiringMode autowireMode, bool dependencyCheck) + { + if (autowireMode != AutoWiringMode.ByName && autowireMode != AutoWiringMode.ByType) + { + throw new ArgumentException("Just AutoWiringMode.ByName and AutoWiringMode.ByType allowed."); + } + RootObjectDefinition rod = new RootObjectDefinition(instance.GetType(), autowireMode, dependencyCheck); + PopulateObject(instance.GetType().Name, rod, new ObjectWrapper(instance)); + } + + /// + /// Apply s + /// to the given existing object instance, invoking their + /// + /// methods. + /// + /// + /// The existing object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// If any post-processing failed. + /// + /// + public virtual object ApplyObjectPostProcessorsBeforeInitialization(object instance, string name) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Invoking IObjectPostProcessors before initialization of object '" + name + "'"); + } + + #endregion + + object result = instance; + foreach (IObjectPostProcessor objectProcessor in ObjectPostProcessors) + { + result = objectProcessor.PostProcessBeforeInitialization(result, name); + if (result == null) + { + throw new ObjectCreationException(name, + string.Format(CultureInfo.InvariantCulture, + "PostProcessBeforeInitialization method of IObjectPostProcessor [{0}] " + + " returned null for object [{1}] with name '{2}'.", objectProcessor, instance, name)); + } + } + return result; + } + + /// + /// Apply s + /// to the given existing object instance, invoking their + /// + /// methods. + /// + /// + /// The existing object instance. + /// + /// + /// The name of the object. + /// + /// + /// The object instance to use, either the original or a wrapped one. + /// + /// + /// If any post-processing failed. + /// + /// + public virtual object ApplyObjectPostProcessorsAfterInitialization(object instance, string name) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Invoking IObjectPostProcessors after initialization of object '" + name + "'"); + } + + #endregion + + object result = instance; + foreach (IObjectPostProcessor objectProcessor in ObjectPostProcessors) + { + result = objectProcessor.PostProcessAfterInitialization(result, name); + if (result == null) + { + throw new ObjectCreationException(name, + string.Format(CultureInfo.InvariantCulture, + "PostProcessAfterInitialization method of IObjectPostProcessor [{0}] " + + " returned null for object [{1}] with name [{2}].", objectProcessor, instance, name)); + } + } + return result; + } + + #endregion + + #region Fields + + /// + /// Set that holds all inner objects created by this factory that implement the IDisposable + /// interface, to be destroyed on call to Dispose. + /// + private ISet _disposableInnerObjects = new SynchronizedSet(new HybridSet()); + + private IInstantiationStrategy instantiationStrategy = new MethodInjectingInstantiationStrategy(); + + /// + /// Cache of unfinished IFactoryObject instances: IFactoryObject name --> IObjectWrapper */ + /// + private IDictionary factoryObjectInstanceCache = new Hashtable(); + + /// + /// Cache of filtered PropertyInfos: object Type -> PropertyInfo array + /// + private IDictionary filteredPropertyDescriptorsCache = new Hashtable(); + + /// + /// Dependency interfaces to ignore on dependency check and autowire, as Set of + /// Class objects. By default, only the IObjectFactoryAware and IObjectNameAware + /// interfaces are ignored. + /// + private ISet ignoredDependencyInterfaces = new HybridSet(); + + #endregion + } + + internal class UnsatisfiedDependencyExceptionData + { + private int parameterIndex; + private Type parameterType; + private string errorMessage; + + public UnsatisfiedDependencyExceptionData(int parameterIndex, Type parameterType, string errorMessage) + { + this.parameterIndex = parameterIndex; + this.parameterType = parameterType; + this.errorMessage = errorMessage; + } + + public int ParameterIndex + { + get { return parameterIndex; } + } + + public Type ParameterType + { + get { return parameterType; } + } + + public string ErrorMessage + { + get { return errorMessage; } + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractMethodReplacer.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractMethodReplacer.cs new file mode 100644 index 00000000..f1815983 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractMethodReplacer.cs @@ -0,0 +1,128 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// An + /// implementation that provides some convenience support for + /// derived classes. + /// + /// + ///

    + /// This class is reserved for internal use within the framework; it is + /// not intended to be used by application developers using Spring.NET. + ///

    + ///
    + /// Rick Evans + /// $Id: AbstractMethodReplacer.cs,v 1.3 2007/07/30 17:52:22 markpollack Exp $ + public abstract class AbstractMethodReplacer : IMethodReplacer + { + private IConfigurableObjectDefinition objectDefinition; + private IObjectFactory objectFactory; + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such has no + /// publicly visible constructors. + ///

    + ///
    + /// + /// The object definition that is the target of the method replacement. + /// + /// + /// The enclosing IoC container with which the above + /// is associated. + /// + /// + /// If either of the supplied arguments is . + /// + protected AbstractMethodReplacer( + IConfigurableObjectDefinition objectDefinition, IObjectFactory objectFactory) + { + AssertUtils.ArgumentNotNull(objectDefinition, "objectDefinition"); + AssertUtils.ArgumentNotNull(objectFactory, "objectFactory"); + this.objectDefinition = objectDefinition; + this.objectFactory = objectFactory; + } + + /// + /// Is ; derived classes must supply an implementation. + /// + /// + /// The instance whose is to be + /// (re)implemented. + /// + /// + /// The method that is to be (re)implemented. + /// + /// The target method's arguments. + /// The result of the object lookup. + public abstract object Implement(object target, MethodInfo method, object[] arguments); + + /// + /// Helper method for subclasses to retrieve the appropriate + /// for the + /// supplied . + /// + /// + /// The to use to retrieve + /// the appropriate + /// . + /// + /// + /// The appropriate + /// . + /// + protected MethodOverride GetOverride(MethodInfo method) + { + return this.objectDefinition.MethodOverrides.GetOverride(method); + } + + /// + /// Helper method for subclasses to lookup an object from an enclosing + /// IoC container. + /// + /// + /// The name of the object that is to be looked up. + /// + /// + /// The named object. + /// + protected object GetObject(string objectName) + { + return this.objectFactory.GetObject(objectName); + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs new file mode 100644 index 00000000..54728959 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs @@ -0,0 +1,747 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Reflection; +using System.Text; +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Common base class for object definitions, factoring out common + /// functionality from + /// and + /// . + /// + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: AbstractObjectDefinition.cs,v 1.31 2007/08/27 13:57:43 oakinger Exp $ + [Serializable] + public abstract class AbstractObjectDefinition : IConfigurableObjectDefinition + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + protected AbstractObjectDefinition() : this(null, null) + { + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + protected AbstractObjectDefinition(ConstructorArgumentValues arguments, MutablePropertyValues properties) + { + constructorArgumentValues = + (arguments != null) ? arguments : new ConstructorArgumentValues(); + propertyValues = + (properties != null) ? properties : new MutablePropertyValues(); + eventHandlerValues = new EventValues(); + DependsOn = StringUtils.EmptyStrings; + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The object definition used to initialise the member fields of this + /// instance. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + protected AbstractObjectDefinition(IObjectDefinition other) + { + AssertUtils.ArgumentNotNull(other, "other"); + AbstractObjectDefinition aod = other as AbstractObjectDefinition; + if (aod != null) + { + if (aod.HasObjectType) + { + ObjectType = other.ObjectType; + } + else + { + ObjectTypeName = other.ObjectTypeName; + } + MethodOverrides = new MethodOverrides(aod.MethodOverrides); + DependencyCheck = aod.DependencyCheck; + } + IsAbstract = other.IsAbstract; + IsSingleton = other.IsSingleton; + IsLazyInit = other.IsLazyInit; + ConstructorArgumentValues + = new ConstructorArgumentValues(other.ConstructorArgumentValues); + PropertyValues = new MutablePropertyValues(other.PropertyValues); + EventHandlerValues = new EventValues(other.EventHandlerValues); + + InitMethodName = other.InitMethodName; + DestroyMethodName = other.DestroyMethodName; + DependsOn = new string[other.DependsOn.Length]; + Array.Copy(other.DependsOn, DependsOn, other.DependsOn.Length); + FactoryMethodName = other.FactoryMethodName; + FactoryObjectName = other.FactoryObjectName; + AutowireMode = other.AutowireMode; + ResourceDescription = other.ResourceDescription; + } + + #endregion + + #region Properties + + /// + /// The property values that are to be applied to the object + /// upon creation. + /// + /// + ///

    + /// Setting the value of this property to + /// will merely result in a new (and empty) + /// + /// collection being assigned to the property value. + ///

    + ///
    + /// + /// The property values (if any) for this object; may be an + /// empty collection but is guaranteed not to be + /// . + /// + public MutablePropertyValues PropertyValues + { + get { return propertyValues; } + set { propertyValues = value == null ? new MutablePropertyValues() : value; } + } + + /// + /// Does this definition have any + /// ? + /// + /// + /// if this definition has at least one + /// . + /// + public bool HasMethodOverrides + { + get { return !MethodOverrides.IsEmpty; } + } + + /// + /// The constructor argument values for this object. + /// + /// + ///

    + /// Setting the value of this property to + /// will merely result in a new (and empty) + /// + /// collection being assigned. + ///

    + ///
    + /// + /// The constructor argument values (if any) for this object; may be an + /// empty collection but is guaranteed not to be + /// . + /// + public ConstructorArgumentValues ConstructorArgumentValues + { + get { return constructorArgumentValues; } + set { constructorArgumentValues = value == null ? new ConstructorArgumentValues() : value; } + } + + /// + /// The event handler values for this object. + /// + /// + ///

    + /// Setting the value of this property to + /// will merely result in a new (and empty) + /// + /// collection being assigned. + ///

    + ///
    + /// + /// The event handler values (if any) for this object; may be an + /// empty collection but is guaranteed not to be + /// . + /// + public EventValues EventHandlerValues + { + get { return eventHandlerValues; } + set { eventHandlerValues = value == null ? new EventValues() : value; } + } + + /// + /// The method overrides (if any) for this object. + /// + /// + ///

    + /// Setting the value of this property to + /// will merely result in a new (and empty) + /// + /// collection being assigned to the property value. + ///

    + ///
    + /// + /// The method overrides (if any) for this object; may be an + /// empty collection but is guaranteed not to be + /// . + /// + public MethodOverrides MethodOverrides + { + get { return methodOverrides; } + set { methodOverrides = value == null ? new MethodOverrides() : value; } + } + + /// + /// Is this definition a singleton, with + /// a single, shared instance returned on all calls to an enclosing + /// container (typically an + /// or + /// ). + /// + /// + ///

    + /// If , an object factory will apply the + /// prototype design pattern, with each caller requesting an + /// instance getting an independent instance. How this is defined + /// will depend on the object factory implementation. singletons + /// are the commoner type. + ///

    + ///
    + /// + public virtual bool IsSingleton + { + get { return isSingleton; } + set + { + isSingleton = value; + isPrototype = !value; + } + } + + /// + /// Gets a value indicating whether this instance is prototype, with an independent instance + /// returned for each call. + /// + /// + /// true if this instance is prototype; otherwise, false. + /// + public virtual bool IsPrototype + { + get { return isPrototype; } + } + + /// + /// Is this object lazily initialized? + /// + ///

    + /// Only applicable to a singleton object. + ///

    + ///

    + /// If , it will get instantiated on startup + /// by object factories that perform eager initialization of + /// singletons. + ///

    + ///
    + public bool IsLazyInit + { + get { return isLazyInit; } + set { isLazyInit = value; } + } + + /// + /// Is this object definition a "template", i.e. not meant to be instantiated + /// itself but rather just serving as an object definition for configuration + /// templates used by . + /// + /// + /// if this object definition is a "template". + /// + public bool IsTemplate + { + get + { + return ( + isAbstract || + (objectType == null && StringUtils.IsNullOrEmpty(factoryObjectName)) + ); + } + } + + /// + /// Is this object definition "abstract", i.e. not meant to be + /// instantiated itself but rather just serving as a parent for concrete + /// child object definitions. + /// + /// + /// if this object definition is "abstract". + /// + public bool IsAbstract + { + get { return isAbstract; } + set { isAbstract = value; } + } + + /// + /// The of the object definition (if any). + /// + /// + /// A resolved object . + /// + /// + /// If the of the object definition is not a + /// resolved or . + /// + /// + public Type ObjectType + { + get + { + if (!HasObjectType) + { + throw new ApplicationException( + "Object definition does not carry a resolved System.Type"); + } + return (Type) objectType; + } + set { objectType = value; } + } + + /// + /// Is the of the object definition a resolved + /// ? + /// + public bool HasObjectType + { + get { return objectType is Type; } + } + + /// + /// Returns the of the + /// of the object definition (if any). + /// + public string ObjectTypeName + { + get + { + if (objectType is Type) + { + return ((Type) objectType).FullName; + } + else + { + return objectType as string; + } + } + set { objectType = value; } + } + + /// + /// A description of the resource that this object definition + /// came from (for the purpose of showing context in case of errors). + /// + public string ResourceDescription + { + get { return resourceDescription; } + set { resourceDescription = value; } + } + + /// + /// The autowire mode as specified in the object definition. + /// + /// + ///

    + /// This determines whether any automagical detection and setting of + /// object references will happen. The default is + /// , + /// which means that no autowiring will be performed. + ///

    + ///
    + public AutoWiringMode AutowireMode + { + get { return autowireMode; } + set { autowireMode = value; } + } + + /// + /// Gets the resolved autowire mode. + /// + /// + ///

    + /// This resolves + /// + /// to one of + /// + /// or + /// . + ///

    + ///
    + public AutoWiringMode ResolvedAutowireMode + { + get + { + if (AutowireMode == AutoWiringMode.AutoDetect) + { + // Work out whether to apply setter autowiring or constructor autowiring. + // If it has a no-arg constructor it's deemed to be setter autowiring, + // otherwise we'll try constructor autowiring. + ConstructorInfo[] constructors = + ObjectType.GetConstructors(); + foreach (ConstructorInfo ctor in constructors) + { + if (ctor.GetParameters().Length == 0) + { + return AutoWiringMode.ByType; + } + } + return AutoWiringMode.Constructor; + } + else + { + return AutowireMode; + } + } + } + + /// + /// The dependency checking mode. + /// + /// + ///

    + /// The default is + /// . + ///

    + ///
    + public DependencyCheckingMode DependencyCheck + { + get { return dependencyCheck; } + set { dependencyCheck = value; } + } + + /// + /// The object names that this object depends on. + /// + /// + ///

    + /// The object factory will guarantee that these objects get initialized + /// before this object definition. + ///

    + /// + /// Dependencies are normally expressed through object properties + /// or constructor arguments. This property should just be necessary for + /// other kinds of dependencies such as statics (*ugh*) or database + /// preparation on startup. + /// + ///
    + public string[] DependsOn + { + get { return dependsOn; } + set { dependsOn = value == null ? StringUtils.EmptyStrings : value; } + } + + /// + /// The name of the initializer method. + /// + /// + ///

    + /// The default value is the constant, + /// in which case there is no initializer method. + ///

    + ///
    + public string InitMethodName + { + get { return initMethodName; } + set { initMethodName = value; } + } + + /// + /// Return the name of the destroy method. + /// + /// + ///

    + /// The default value is the constant, + /// in which case there is no destroy method. + ///

    + ///
    + public string DestroyMethodName + { + get { return destroyMethodName; } + set { destroyMethodName = value; } + } + + /// + /// The name of the factory method to use (if any). + /// + /// + ///

    + /// This method will be invoked with constructor arguments, or with no + /// arguments if none are specified. The + /// method will be invoked on the specified + /// . + ///

    + ///
    + public string FactoryMethodName + { + get { return factoryMethodName; } + set { factoryMethodName = value; } + } + + /// + /// The name of the factory object to use (if any). + /// + public string FactoryObjectName + { + get { return factoryObjectName; } + set { factoryObjectName = value; } + } + + /// + /// Does this object definition have any constructor argument values? + /// + /// + /// if his object definition has at least one + /// element in it's + /// + /// property. + /// + public virtual bool HasConstructorArgumentValues + { + get + { + return ConstructorArgumentValues != null + && !ConstructorArgumentValues.Empty; + } + } + + #endregion + + #region Methods + + /// + /// Resolves the type of the object, resolving it from a specified + /// object type name if necessary. + /// + /// + /// A resolved instance. + /// + /// + /// If the type cannot be resolved. + /// + public Type ResolveObjectType() + { + string typeName = ObjectTypeName; + if (typeName == null) + { + return null; + } + Type resolvedType = TypeResolutionUtils.ResolveType(typeName); + this.ObjectType = resolvedType; + return resolvedType; + } + + /// + /// Validate this object definition. + /// + /// + /// In the case of a validation failure. + /// + public virtual void Validate() + { + if (IsLazyInit && !IsSingleton) + { + throw new ObjectDefinitionValidationException( + "Lazy initialization is only applicable to singleton objects."); + } + if (HasMethodOverrides && StringUtils.HasText(FactoryMethodName)) + { + throw new ObjectDefinitionValidationException( + "Cannot combine static factory method with method overrides: " + + "the static factory method must create the instance."); + } + if (HasObjectType) + { + PrepareMethodOverrides(); + } + } + + /// + /// Validates all + /// + public virtual void PrepareMethodOverrides() + { + // ascertain that the various lookup methods exist... + foreach (MethodOverride mo in MethodOverrides.Overrides) + { + PrepareMethodOverride(mo); + } + } + + /// + /// Validate the supplied . + /// + /// + /// The + /// to be validated. + /// + protected void PrepareMethodOverride(MethodOverride methodOverride) + { + if (!ReflectionUtils.HasAtLeastOneMethodWithName(ObjectType, methodOverride.MethodName)) + { + throw new ObjectDefinitionValidationException( + string.Format( + CultureInfo.InvariantCulture, + "Invalid method override: no method with name '{0}' on class [{1}].", + methodOverride.MethodName, ObjectTypeName)); + } + //TODO investigate setting overloaded at this point using MethodCountForName... + //Test SunnyDayReplaceMethod_WithArgumentAcceptingReplacerWithNoTypeFragmentsSpecified + // will fail if doing this optimization. + } + + /// + /// Override settings in this object definition from the supplied + /// object definition. + /// + /// + /// The object definition used to override the member fields of this instance. + /// + public virtual void OverrideFrom(IObjectDefinition other) + { + AbstractObjectDefinition aod = other as AbstractObjectDefinition; + if (aod != null) + { + if (aod.HasObjectType) + { + ObjectType = other.ObjectType; + } + MethodOverrides.AddAll(aod.MethodOverrides); + DependencyCheck = aod.DependencyCheck; + } + IsAbstract = other.IsAbstract; + IsSingleton = other.IsSingleton; + IsLazyInit = other.IsLazyInit; + ConstructorArgumentValues.AddAll(other.ConstructorArgumentValues); + PropertyValues.AddAll(other.PropertyValues.PropertyValues); + EventHandlerValues.AddAll(other.EventHandlerValues); + if (StringUtils.HasText(other.InitMethodName)) + { + InitMethodName = other.InitMethodName; + } + if (StringUtils.HasText(other.DestroyMethodName)) + { + DestroyMethodName = other.DestroyMethodName; + } + if (StringUtils.HasText(other.FactoryObjectName)) + { + FactoryObjectName = other.FactoryObjectName; + } + if (StringUtils.HasText(other.FactoryMethodName)) + { + FactoryMethodName = other.FactoryMethodName; + } + DependsOn = new string[other.DependsOn.Length]; + Array.Copy(other.DependsOn, DependsOn, other.DependsOn.Length); + AutowireMode = other.AutowireMode; + ResourceDescription = other.ResourceDescription; + } + + /// + /// Returns a that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + StringBuilder buffer = new StringBuilder(); + buffer.Append("Abstract = ").Append(IsAbstract); + buffer.Append("; Singleton = ").Append(IsSingleton); + buffer.Append("; LazyInit = ").Append(IsLazyInit); + buffer.Append("; Autowire = ").Append(AutowireMode); + buffer.Append("; DependencyCheck = ").Append(DependencyCheck); + buffer.Append("; InitMethodName = ").Append(InitMethodName); + buffer.Append("; DestroyMethodName = ").Append(DestroyMethodName); + buffer.Append("; FactoryMethodName = ").Append(FactoryMethodName); + buffer.Append("; FactoryObjectName = ").Append(FactoryObjectName); + if (StringUtils.HasText(ResourceDescription)) + { + buffer.Append("; defined in = ").Append(ResourceDescription); + } + return buffer.ToString(); + } + + #endregion + + #region Fields + + private ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues(); + private MutablePropertyValues propertyValues = new MutablePropertyValues(); + private EventValues eventHandlerValues = new EventValues(); + private MethodOverrides methodOverrides = new MethodOverrides(); + private string resourceDescription = string.Empty; + private bool isSingleton = true; + private bool isPrototype = false; + private bool isLazyInit = false; + private bool isAbstract = false; + private object objectType; + private AutoWiringMode autowireMode = AutoWiringMode.No; + private DependencyCheckingMode dependencyCheck = DependencyCheckingMode.None; + private string[] dependsOn; + private string initMethodName = string.Empty; + private string destroyMethodName = string.Empty; + private string factoryMethodName = string.Empty; + private string factoryObjectName = string.Empty; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinitionReader.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinitionReader.cs new file mode 100644 index 00000000..0f9e7222 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinitionReader.cs @@ -0,0 +1,272 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using Spring.Core.IO; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Abstract base class for object definition readers. + /// + /// + ///

    + /// Provides common properties like the object registry to work on. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: AbstractObjectDefinitionReader.cs,v 1.14 2008/01/10 14:32:24 bbaia Exp $ + public abstract class AbstractObjectDefinitionReader : IObjectDefinitionReader + { + #region Constants + + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (AbstractObjectDefinitionReader)); + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The + /// instance that this reader works on. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + protected AbstractObjectDefinitionReader(IObjectDefinitionRegistry registry) + : this(registry, AppDomain.CurrentDomain) + { + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The + /// instance that this reader works on. + /// + /// + /// The against which any class names + /// will be resolved into instances. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + protected AbstractObjectDefinitionReader( + IObjectDefinitionRegistry registry, + AppDomain domain) + { + AssertUtils.ArgumentNotNull(registry, "registry", "IObjectDefinitionRegistry must not be null"); + _registry = registry; + _domain = domain; + if (registry is IResourceLoader) + { + _resourceLoader = registry as IResourceLoader; + } + else + { + _resourceLoader = new ConfigurableResourceLoader(); + } + } + + #endregion + + #region Properties + + /// + /// Gets the + /// + /// instance that this reader works on. + /// + public IObjectDefinitionRegistry Registry + { + get { return _registry; } + } + + + /// + /// The to use for anonymous + /// objects (wihtout explicit object name specified). + /// + /// + public IObjectNameGenerator ObjectNameGenerator + { + get { return _objectNameGenerator; } + set + { + if (value != null) + { + _objectNameGenerator = value; + } + else + { + _objectNameGenerator = new DefaultObjectNameGenerator(); + } + } + } + + /// + /// The against which any class names + /// will be resolved into instances. + /// + public AppDomain Domain + { + get { return _domain; } + } + + + /// + /// Gets or sets the resource loader to use for resource locations. + /// + /// The resource loader. + public IResourceLoader ResourceLoader + { + get { return _resourceLoader; } + set { _resourceLoader = value; } + } + + #endregion + + #region Methods + + /// + /// Load object definitions from the supplied . + /// + /// + /// The resource for the object definitions that are to be loaded. + /// + /// + /// The number of object definitions that were loaded. + /// + /// + /// In the case of loading or parsing errors. + /// + public abstract int LoadObjectDefinitions(IResource resource); + + /// + /// Load object definitions from the supplied . + /// + /// + /// The resources for the object definitions that are to be loaded. + /// + /// + /// The number of object definitions found + /// + /// + /// In the case of loading or parsing errors. + /// + public int LoadObjectDefinitions(IResource[] resources) + { + AssertUtils.ArgumentNotNull(resources, "resources"); + int counter = 0; + foreach (IResource resource in resources) + { + counter += LoadObjectDefinitions(resource); + } + return counter; + } + + /// + /// Loads the object definitions from the specified resource location. + /// + /// The resource location, to be loaded with the + /// IResourceLoader location . + /// + /// The number of object definitions found + /// + public int LoadObjectDefinitions(string location) + { + if (ResourceLoader == null) + { + throw new ObjectDefinitionStoreException("Cannot import object definitions from location [" + location + + "]:" + + " no ResourceLoader available"); + } + IResource resource; + try + { + resource = ResourceLoader.GetResource(location); + } catch (Exception e) + { + throw new ObjectDefinitionStoreException("Could not resolve resource location [" + location + "]",e); + } + int loadCount = LoadObjectDefinitions(resource); + + if (log.IsDebugEnabled) + { + log.Debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); + } + return loadCount; + } + + + /// + /// Loads the object definitions from the specified resource locations. + /// + /// The the resource locations to be loaded with the + /// IResourceLoader of this object definition reader. + /// + /// The number of object definitions found + /// + public int LoadObjectDefinitions(string[] locations) + { + AssertUtils.ArgumentNotNull(locations, "location"); + int counter = 0; + foreach (string location in locations) + { + counter += LoadObjectDefinitions(location); + } + return counter; + } + + #endregion + + #region Fields + + private IObjectDefinitionRegistry _registry; + private AppDomain _domain; + private IResourceLoader _resourceLoader; + private IObjectNameGenerator _objectNameGenerator = new DefaultObjectNameGenerator(); + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs new file mode 100644 index 00000000..8fd9db1a --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs @@ -0,0 +1,1978 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; + +using Common.Logging; + +using Spring.Collections; +using Spring.Core; +using Spring.Core.TypeConversion; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Abstract superclass for + /// implementations. + /// + /// + ///

    + /// This class provides singleton / prototype determination, singleton caching, + /// object definition aliasing, + /// handling, and object definition merging for child object definitions. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: AbstractObjectFactory.cs,v 1.77 2008/05/29 12:13:27 oakinger Exp $ + [Serializable] + public abstract class AbstractObjectFactory : IConfigurableObjectFactory + { + /// + /// Marker object to be temporarily registered in the singleton cache, + /// while instantiating an object (in order to be able to detect circular references). + /// + private static readonly object CURRENTLY_IN_CREATION = new Object(); + + /// + /// The instance for this class. + /// + private readonly ILog log = LogManager.GetLogger(typeof(AbstractObjectFactory)); + + /// + /// Used as value in hashtable that keeps track of singleton names currently in the + /// process of being created. Would not be necessary if we created a case insensitive implementation of + /// ISet. + /// + private static object emptyObject = new object(); + + + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This constructor implicitly creates an + /// + /// that treats the names of objects in this factory in a case-sensitive fashion. + ///

    + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + protected AbstractObjectFactory() : this(true) + {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + /// + /// if the names of objects in this factory are to be treated in a + /// case-sensitive fashion. + /// + protected AbstractObjectFactory(bool caseSensitive) + { + this.log = LogManager.GetLogger(this.GetType()); + if (caseSensitive) + { + this.aliasMap = new SynchronizedHashtable(); + this.singletonCache = new Hashtable(); + this.singletonsInCreation = new Hashtable(); + } + else + { + this.aliasMap = new SynchronizedHashtable(CollectionsUtil.CreateCaseInsensitiveHashtable()); + this.singletonCache = CollectionsUtil.CreateCaseInsensitiveHashtable(); + this.singletonsInCreation = CollectionsUtil.CreateCaseInsensitiveHashtable(); + } + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + /// + /// if the names of objects in this factory are to be treated in a + /// case-sensitive fashion. + /// + /// + /// Any parent object factory; may be . + /// + protected AbstractObjectFactory(bool caseSensitive, IObjectFactory parentFactory) : this(caseSensitive) + { + ParentObjectFactory = parentFactory; + } + + #endregion + + #region Properties + + /// + /// Gets the of + /// s + /// that will be applied to objects created by this factory. + /// + public IList ObjectPostProcessors + { + get { return objectPostProcessors; } + } + + /// + /// Gets the set of classes that will be ignored for autowiring. + /// + /// + ///

    + /// The elements of this are + /// s. + ///

    + ///
    + public ISet IgnoredDependencyTypes + { + get { return ignoreDependencyTypes; } + } + + /// + /// Returns, whether this object factory instance contains objects. + /// + protected bool HasInstantiationAwareBeanPostProcessors + { + get { return hasInstantiationAwareBeanPostProcessors; } + } + + /// + /// Returns, whether this object factory instance contains objects. + /// + protected bool HasDestructionAwareBeanPostProcessors + { + get { return hasDestructionAwareBeanPostProcessors; } + } + + #endregion + + #region Methods + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// The name of the object to return. + /// + /// The the object may match. Can be an interface or + /// superclass of the actual class. For example, if the value is the + /// class, this method will succeed whatever the + /// class of the returned instance. + /// + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a factory method. If there is no factory method and the + /// supplied array is not , then + /// match the argument values by type and call the object's constructor. + /// + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + /// If the object is not of the required type. + /// + /// + /// If the supplied is . + /// + /// + public object GetObject(string name, Type requiredType, object[] arguments) + { + string objectName = TransformedObjectName(name); + object instance = null; + // eagerly check singleton cache for manually registered singletons... + object sharedInstance = GetSingleton(objectName); + + if (sharedInstance != null) + { + #region Instrumentation + + if (IsSingletonCurrentlyInCreation(objectName)) + { + if (log.IsDebugEnabled) + { + log.Debug("Returning eagerly cached instance of singleton object '" + objectName + + "' that is not fully initialized yet - a consequence of a circular reference"); + } + } + else + { + if (log.IsDebugEnabled) + { + log.Debug(string.Format("Returning cached instance of singleton object '{0}'.", objectName)); + } + } + + #endregion + + instance = GetObjectForInstance(name, sharedInstance); + } + else + { + // check if object definition exists + RootObjectDefinition mergedObjectDefinition = null; + mergedObjectDefinition = GetMergedObjectDefinition(objectName, false); + if (mergedObjectDefinition == null) + { + if (ParentObjectFactory != null) + { + return ParentObjectFactory.GetObject(name, requiredType, arguments); + } + throw new NoSuchObjectDefinitionException(name, "Cannot find definition for object [" + name + "]"); + } + + CheckMergedObjectDefinition(mergedObjectDefinition, objectName, requiredType, arguments); + + // return IObjectDefinition instance itself for an abstract object-definition + if (mergedObjectDefinition.IsAbstract) + { + instance = mergedObjectDefinition; + } + else if (mergedObjectDefinition.IsSingleton) + { + // create object instance... + sharedInstance = CreateAndCacheSingletonInstance(objectName, mergedObjectDefinition, arguments); + instance = GetObjectForInstance(name, sharedInstance); + } + else + { + // it's a prototype, so create a new instance... + instance = CreateObject(name, mergedObjectDefinition, arguments); + } + } + // check that any required type matches the type of the actual object instance... + if (requiredType != null && !requiredType.IsAssignableFrom(instance.GetType())) + { + throw new ObjectNotOfRequiredTypeException(name, requiredType, instance); + } + return instance; + } + + + + /// + /// Apply the property values of the object definition with the supplied + /// to the supplied . + /// + /// + ///

    + /// The object definition can either define a fully self-contained object, + /// reusing it's property values, or just property values meant to be used + /// for existing object instances. + ///

    + ///
    + /// + /// The existing object that the property values for the named object will + /// be applied to. + /// + /// + /// The name of the object definition associated with the property values that are + /// to be applied. + /// + /// + /// In case of errors. + /// + public virtual void ApplyObjectPropertyValues(object instance, string name) + { + // explicit no-op... + } + + /// + /// Initializes the given with the + /// custom s registered with + /// this factory. + /// + /// + /// The to initialise. + /// + protected void InitObjectWrapper(IObjectWrapper wrapper) + { + } + + /// + /// Create an object instance for the given object definition. + /// + /// + ///

    + /// The object definition will already have been merged with the parent + /// definition in case of a child definition. + ///

    + ///

    + /// All the other methods in this class invoke this method, although objects + /// may be cached after being instantiated by this method. All object + /// instantiation within this class is performed by this method. + ///

    + ///
    + /// The name of the object. + /// + /// The object definition for the object that is to be instantiated. + /// + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a factory method. If there is no factory method and the + /// supplied array is not , + /// then match the argument values by type and call the object's constructor. + /// + /// + /// A new instance of the object. + /// + /// + /// In case of errors. + /// + protected abstract object CreateObject(string name, RootObjectDefinition definition, object[] arguments); + + /// + /// Destroy the target object. + /// + /// + ///

    + /// Must destroy objects that depend on the given object before the object itself, + /// nor throw an exception. + ///

    + ///
    + /// + /// The name of the object. + /// + /// + /// The target object instance to destroyed. + /// + protected abstract void DestroyObject(string name, object target); + + /// + /// Does this object factory contain an object definition with the + /// supplied ? + /// + /// + ///

    + /// Does not consider any hierarchy this factory may participate in. + /// Invoked by + /// + /// when no cached singleton instance is found. + ///

    + ///
    + /// + /// The name of the object to look for. + /// + /// + /// if this object factory contains an object + /// definition with the supplied . + /// + public abstract bool ContainsObjectDefinition(string name); + + /// + /// Adds the supplied (object) to this factory's + /// singleton cache. + /// + /// + ///

    + /// To be called for eager registration of singletons, e.g. to be able to + /// resolve circular references. + ///

    + /// + /// If a singleton has already been registered under the same name as + /// the supplied , then the old singleton will + /// be replaced. + /// + ///
    + /// The name of the object. + /// The singleton object. + /// + /// If the argument is + /// or consists wholly of whitespace characters; or if the + /// is . + /// + protected virtual void AddSingleton(string name, object singleton) + { + AssertUtils.ArgumentHasText(name, "The object name must not be empty."); + AssertUtils.ArgumentNotNull(singleton, "singleton"); + lock (singletonCache) + { + singletonCache[name] = singleton; + } + } + + /// + /// Return the object name, stripping out the factory dereference prefix if + /// necessary, and resolving aliases to canonical names. + /// + /// + /// The transformed name of the object. + /// + protected string TransformedObjectName(string name) + { + string objectName = ObjectFactoryUtils.TransformedObjectName(name); + // handle aliasing... + lock (aliasMap) + { + string canonicalName = (string) aliasMap[objectName]; + return canonicalName != null ? canonicalName : objectName; + } + } + + /// + /// Ensures, that the given name is prefixed with + /// if it incidentially already starts with this prefix. This avoids troubles when dereferencing + /// the object name during + /// + protected string OriginalObjectName(string name) + { + string objectName = TransformedObjectName(name); + if (name.StartsWith(ObjectFactoryUtils.FactoryObjectPrefix)) + { + objectName = ObjectFactoryUtils.FactoryObjectPrefix + objectName; + } + return objectName; + } + + /// + /// Determines whether the specified name is defined as an alias as opposed + /// to the name of an actual object definition. + /// + /// The object name to check. + /// + /// true if the specified name is alias; otherwise, false. + /// + protected bool IsAlias(string name) + { + lock (aliasMap) + { + return aliasMap.Contains(name); + } + } + + /// + /// Return a , + /// even by traversing parent if the parameter is a child definition. + /// + /// + /// The name of the object. + /// + /// + /// Are ancestors to be included in the merge? + /// + /// + ///

    + /// Will ask the parent object factory if not found in this instance. + ///

    + ///
    + /// + /// A merged + /// with overridden properties. + /// + public virtual RootObjectDefinition GetMergedObjectDefinition(string name, bool includingAncestors) + { + return GetMergedObjectDefinition(name, GetObjectDefinition(name, includingAncestors)); + } + + /// + /// Return a , + /// even by traversing parent if the parameter is a child definition. + /// + /// + /// A merged + /// with overridden properties. + /// + protected virtual RootObjectDefinition GetMergedObjectDefinition(string name, IObjectDefinition definition) + { + if (definition == null) + { + return null; + } + else if (definition is RootObjectDefinition) + { + return (RootObjectDefinition) definition; + } + else if (definition is ChildObjectDefinition) + { + ChildObjectDefinition childDefinition = (ChildObjectDefinition) definition; + RootObjectDefinition parentDefinition = null; + if (!name.Equals(childDefinition.ParentName)) + { + parentDefinition = + GetMergedObjectDefinition(TransformedObjectName(childDefinition.ParentName), true); + } + else + { + if (ParentObjectFactory is AbstractObjectFactory) + { + parentDefinition = + ((AbstractObjectFactory) ParentObjectFactory).GetMergedObjectDefinition( + childDefinition.ParentName, true); + } + } + if (parentDefinition == null) + { + throw new NoSuchObjectDefinitionException(childDefinition.ParentName, + string.Format( + "Parent name '{0}' is equal to object name '{1}' - " + + + "cannot be resolved without an AbstractObjectFactory parent.", + childDefinition.ParentName, name)); + } + + RootObjectDefinition rootDefinition = CreateRootObjectDefinition(parentDefinition); + rootDefinition.OverrideFrom(childDefinition); + return rootDefinition; + } + else + { + throw new ObjectDefinitionStoreException(definition.ResourceDescription, name, + "Definition is neither a RootObjectDefinition nor a ChildObjectDefinition."); + } + } +/* + /// + /// Merges the object definitions. + /// + /// Object definition name. + /// The parent definition. + /// The child definition. + /// Merged object definition. + protected virtual RootObjectDefinition MergeObjectDefinitions(string name, IObjectDefinition parentDefinition, + IObjectDefinition childDefinition) + { + RootObjectDefinition rootDefinition = CreateRootObjectDefinition(parentDefinition); + rootDefinition.OverrideFrom(childDefinition); + return rootDefinition; + } +*/ + /// + /// Creates the root object definition. + /// + /// The template definition to base root definition on. + /// Root object definition. + protected virtual RootObjectDefinition CreateRootObjectDefinition(IObjectDefinition templateDefinition) + { + return new RootObjectDefinition(templateDefinition); + } + + /// + /// Return the registered + /// for the + /// given object, allowing access to its property values and constructor + /// argument values. + /// + /// The name of the object. + /// + /// The registered + /// . + /// + /// + /// If there is no object with the given name. + /// + /// + /// In the case of errors. + /// + public abstract IObjectDefinition GetObjectDefinition(string name); + + /// + /// Return the registered + /// for the + /// given object, allowing access to its property values and constructor + /// argument values. + /// + /// The name of the object. + /// Whether to search parent object factories. + /// + /// The registered + /// . + /// + /// + /// If there is no object with the given name. + /// + /// + /// In the case of errors. + /// + public abstract IObjectDefinition GetObjectDefinition(string name, bool includeAncestors); + + /// + /// Gets the type for the given FactoryObject. + /// + /// The factory object instance to check. + /// the FactoryObject's object type + protected virtual Type GetTypeForFactoryObject(IFactoryObject factoryObject) + { + try + { + return factoryObject.ObjectType; + } + catch (Exception ex) + { + log.Warn("FactoryObject threw exception from ObjectType, despite the contract saying " + + "that it should return null if the type of its object cannot be determined yet", ex); + return null; + } + } + + /// + /// Gets the object type for the given FactoryObject definition, as far as possible. + /// Only called if there is no singleton instance registered for the target object already. + /// + /// + /// The default implementation creates the FactoryObject via GetObject + /// to call its ObjectType property. Subclasses are encouraged to optimize + /// this, typically by just instantiating the FactoryObject but not populating it yet, + /// trying whether its ObjectType property already returns a type. + /// If no type found, a full FactoryObject creation as performed by this implementation + /// should be used as fallback. + /// + /// Name of the object. + /// The merged object definition for the object. + /// The type for the object if determinable, or null otherwise + protected virtual Type GetTypeForFactoryObject(string objectName, RootObjectDefinition mod) + { + if (!mod.IsSingleton) + { + return null; + } + try + { + IFactoryObject factoryObject = GetFactoryObject(objectName); + return GetTypeForFactoryObject(factoryObject); + } catch (ObjectCreationException ex) + { + // Can only happen when getting a FactoryObject. + log.Debug("Ignoring object creation exception on FactoryObject type check", ex); + return null; + } + } + + /// + /// Predict the eventual object type (of the processed object instance) for the + /// specified object. + /// + /// + /// Does not need to handle FactoryObjects specifically, since it is only + /// supposed to operate on the raw object type. + /// This implementation is simplistic in that it is not able to + /// handle factory methods and InstantiationAwareBeanPostProcessors. + /// It only predicts the object type correctly for a standard object. + /// To be overridden in subclasses, applying more sophisticated type detection. + /// + /// Name of the object. + /// The merged object definition to determine the type for. + /// The type of the object, or null if not predictable + protected virtual Type PredictObjectType(string objectName, RootObjectDefinition mod) + { + if (StringUtils.HasText(mod.FactoryObjectName)) + { + return null; + } + return ResolveObjectType(mod, objectName); + } + + /// + /// Get the object for the given object instance, either the object + /// instance itself or its created object in case of an + /// . + /// + /// + /// The name that may include the factory dereference prefix. + /// + /// The object instance. + /// + /// The singleton instance of the object. + /// + protected virtual object GetObjectForInstance(string name, object instance) + { + //string objectName = TransformedObjectName(name); + + // don't let calling code try to dereference the + // object factory if the object isn't a factory + if (IsFactoryDereference(name) && !(instance is IFactoryObject)) + { + throw new ObjectIsNotAFactoryException(TransformedObjectName(name), instance); + } + + // now we have the object instance, which may be a normal object + // or an IFactoryObject. If it's an IFactoryObject, we use it to + // create an object instance, unless the caller actually wants + // a reference to the factory. + if (ObjectUtils.IsAssignableAndNotTransparentProxy(typeof(IFactoryObject), instance)) + { + if (!IsFactoryDereference(name)) + { + + // return object instance from factory... + IFactoryObject factory = (IFactoryObject) instance; + string objectName = TransformedObjectName(name); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format("Object with name '{0}' is a factory object.", objectName)); + } + + #endregion + + RootObjectDefinition rod = + (ContainsObjectDefinition(objectName) ? GetMergedObjectDefinition(objectName,true) : null); + instance = GetObjectFromFactoryObject(factory, objectName, rod); + + if (instance == null) + { + throw new FactoryObjectNotInitializedException(TransformedObjectName(name), + "Factory object returned null object - " + + "possible cause: not fully initialized due to " + + "circular object reference."); + } + } + else + { + // the user wants the factory itself... + if (log.IsDebugEnabled) + { + log.Debug( + string.Format("Calling code asked for IFactoryObject instance for name '{0}'.", + TransformedObjectName(name))); + } + } + } + return instance; + } + + /// + /// Obtain an object to expose from the given IFactoryObject. + /// + /// The IFactoryObject instance. + /// Name of the object. + /// The merged object definition. + /// The object obtained from the IFactoryObject + /// If IFactoryObject object creation failed. + private object GetObjectFromFactoryObject(IFactoryObject factory, string objectName, RootObjectDefinition rod) + { + object instance; + + try + { + instance = factory.GetObject(); + } + catch (FactoryObjectNotInitializedException ex) + { + throw new ObjectCurrentlyInCreationException( + rod.ResourceDescription, objectName, ex); + } + catch (Exception ex) + { + throw new ObjectCreationException(rod.ResourceDescription, objectName, + "FactoryObject threw exception on object creation.", ex); + } + + // Do not accept a null value for a FactoryBean that's not fully + // initialized yet: Many FactoryBeans just return null then. + if (instance == null && IsSingletonCurrentlyInCreation(objectName)) { + throw new ObjectCurrentlyInCreationException(rod.ResourceDescription, objectName, + "FactoryObject which is currently in creation returned null from GetObject."); + } + + if (factory is IConfigurableFactoryObject) + { + IConfigurableFactoryObject configurableFactory = (IConfigurableFactoryObject)factory; + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format("Factory object with name '{0}' is configurable.", TransformedObjectName(objectName))); + } + + #endregion + + if (configurableFactory.ProductTemplate != null) + { + instance = ConfigureObject(instance, + String.Format("{0}.ProductTemplate", objectName), + configurableFactory.ProductTemplate); + } + } + + if (instance != null) + { + try + { + instance = PostProcessObjectFromFactoryObject(instance, objectName); + } + catch (Exception ex) { + throw new ObjectCreationException(rod.ResourceDescription, objectName, + "Post-processing of the FactoryObject's object failed.", ex); + } + } + + return instance; + } + + /// + /// Post-process the given object that has been obtained from the FactoryObject. + /// The resulting object will be exposed for object references. + /// + /// The default implementation simply returns the given object + /// as-is. Subclasses may override this, for example, to apply + /// post-processors. + /// The instance obtained from the IFactoryObject. + /// Name of the object. + /// The object instance to expose + /// if any post-processing failed. + protected virtual object PostProcessObjectFromFactoryObject(object instance, string objectName) + { + return instance; + } + + /// + /// Convenience method to pull an + /// from this factory. + /// + /// + /// The name of the factory object to be retrieved. If this name is not a valid + /// name, it will be converted + /// into one. + /// + /// + /// The associated with the + /// supplied . + /// + protected IFactoryObject GetFactoryObject(string objectName) + { + if (!ObjectFactoryUtils.IsFactoryDereference(objectName)) + { + objectName = ObjectFactoryUtils.BuildFactoryObjectName(objectName); + } + return (IFactoryObject) GetObject(objectName); + } + + /// + /// Is the supplied a factory object dereference? + /// + protected bool IsFactoryDereference(string name) + { + return ObjectFactoryUtils.IsFactoryDereference(name); + } + + /// + /// Determines whether the type of the given object definition matches the + /// specified target type. + /// + /// Allows for lazy load of the actual object type, provided that the + /// type match can be determined otherwise. + /// The default implementation simply delegates to the standard + /// ResolveObjectType method. Subclasses may override this to use + /// a differnt strategy. + /// + /// Name of the object (for error handling purposes). + /// The merged object definition to determine the type for. + /// Type to match against (never null). + /// + /// true if object definition matches tye specified target type; otherwise, false. + /// + /// if we failed to load the type." + protected bool IsObjectTypeMatch(string objectName, RootObjectDefinition rod, Type targetType) + { + Type objectType = ResolveObjectType(rod, objectName); + return (objectType != null && targetType.IsAssignableFrom(objectType)); + } + + /// + /// Resolves the type of the object for the specified object definition resolving + /// an object type name to a Type (if necessary) and storing the resolved Type + /// in the object definition for further use. + /// + /// The merged object definition to dertermine the type for. + /// Name of the object (for error handling purposes). + /// + protected Type ResolveObjectType(RootObjectDefinition rod, string objectName) + { + try + { + if (rod.HasObjectType) + { + return rod.ObjectType; + } + return rod.ResolveObjectType(); + } catch (TypeLoadException e) + { + throw new CannotLoadObjectTypeException(rod.ResourceDescription, objectName, rod.ObjectTypeName, e); + } + } + + /// + /// Is the object (definition) with the supplied an + /// ? + /// + /// The name of the object to be checked. + /// + /// the object (definition) with the supplied + /// an ? + /// + protected bool IsFactoryObject(string name) + { + string objectName = TransformedObjectName(name); + object objectInstance = GetSingleton(objectName); + //TODO investigate + if (IsSingletonCurrentlyInCreation(name)) + { + throw new ObjectCurrentlyInCreationException(objectName); + } + + if (objectInstance != null) + { + return (objectInstance is IFactoryObject); + } + else + { + RootObjectDefinition definition = GetMergedObjectDefinition(objectName, false); + if (definition != null) + { + return (definition.HasObjectType && typeof(IFactoryObject).IsAssignableFrom(definition.ObjectType)); + } + else + { + if (parentObjectFactory != null) + { + return ((AbstractObjectFactory) parentObjectFactory).IsFactoryObject(name); + } + else + { + throw new NoSuchObjectDefinitionException(objectName, + "Cannot find definition for object [" + objectName + + "]"); + } + } + } + } + + /// + /// Remove the object identified by the supplied + /// from this factory's singleton cache. + /// + /// + /// The name of the object that is to be removed from the singleton + /// cache. + /// + /// + /// If the argument is or + /// consists wholly of whitespace characters. + /// + protected void RemoveSingleton(string name) + { + AssertUtils.ArgumentHasText(name, "name"); + lock (singletonCache) + { + this.singletonCache.Remove(name); + } + } + + /// + /// Return the names of objects in the singleton cache that match the given + /// object type (including subclasses). + /// + /// + /// The class or interface to match, or for all object names. + /// + /// + ///

    + /// Will not consider s + /// as the type of their created objects is not known before instantiation. + ///

    + ///

    + /// Does not consider any hierarchy this factory may participate in. + ///

    + ///
    + /// + /// The names of objects in the singleton cache that match the given + /// object type (including subclasses), or an empty array if none. + /// + public virtual string[] GetSingletonNames(Type type) + { + lock (singletonCache) + { + ArrayList matches = new ArrayList(); + foreach (string name in singletonCache.Keys) + { + object singletonObject = singletonCache[name]; + if (singletonObject != null && type.IsAssignableFrom(singletonObject.GetType()) + && !matches.Contains(name)) + { + matches.Add(name); + } + } + return (string[]) matches.ToArray(typeof(string)); + } + } + + /// + /// Determines whether the object with the given name matches the specified type. + /// + /// More specifically, check whether a GetObject call for the given name + /// would return an object that is assignable to the specified target type. + /// Translates aliases back to the corresponding canonical bean name. + /// Will ask the parent factory if the bean cannot be found in this factory instance. + /// + /// The name of the object to query. + /// Type of the target to match against. + /// + /// true if the object type matches; otherwise, false + /// if it doesn't match or cannot be determined yet. + /// + /// Ff there is no object with the given name + /// + public bool IsTypeMatch(string name, Type targetType) + { + string objectName = TransformedObjectName(name); + Type typeToMatch = (targetType != null ? targetType : typeof (object)); + + //Check manually registered singletons. + object objectInstance = GetSingleton(objectName); + if (objectInstance != null) + { + if (objectInstance is IFactoryObject) + { + if (!IsFactoryDereference(name)) + { + Type type = GetTypeForFactoryObject((IFactoryObject)objectInstance); + return (type != null && typeToMatch.IsAssignableFrom(type)); + } + else + { + return typeToMatch.IsAssignableFrom(objectInstance.GetType()); + } + } + else + { + return !IsFactoryDereference(name) && typeToMatch.IsAssignableFrom(objectInstance.GetType()); + } + } + else + { + // No singleton instance found -> check object definition + IObjectFactory parentFactory = ParentObjectFactory; + if (parentFactory != null && !ContainsObjectDefinition(name)) + { + // No object definition found in this factory -> delegate to parent + return parentFactory.IsTypeMatch(OriginalObjectName(name), targetType); + } + + RootObjectDefinition mod = GetMergedObjectDefinition(objectName, false); + Type objectType = PredictObjectType(objectName, mod); + + if (objectType == null) + { + return false; + } + + // Check object class whether we're dealing with a FactoryObject + if (typeof(IFactoryObject).IsAssignableFrom(objectType)) + { + if (!IsFactoryDereference(name)) + { + // If it's a FactoryObject, we want to look at what it creates, not the factory class. + Type type = GetTypeForFactoryObject(objectName, mod); + return (type != null && typeToMatch.IsAssignableFrom(type)); + } + else + { + return typeToMatch.IsAssignableFrom(objectType); + } + } + else + { + return !IsFactoryDereference(name) && typeToMatch.IsAssignableFrom(objectType); + } + } + } + + /// + /// Determines the of the object with the + /// supplied . + /// + /// + ///

    + /// More specifically, checks the of object that + /// would return. + /// For an , returns the + /// of object that the + /// creates. + ///

    + ///

    + /// Please note that (prototype) objects created via a factory method or + /// objects are handled + /// slightly differently, in that we don't want to needlessly create + /// instances of such objects just to determine the + /// of object that they create. + ///

    + ///
    + /// The name of the object to query. + /// + /// The of the object or + /// if not determinable. + /// + public virtual Type GetType(string name) + { + string objectName = TransformedObjectName(name); + + // check manually registered singletons... + object objectInstance = GetSingleton(objectName); + + if (objectInstance != null) + { + IFactoryObject factoryObject = objectInstance as IFactoryObject; + if (factoryObject != null & !IsFactoryDereference(objectName)) + { + return GetTypeForFactoryObject(factoryObject); + } + else + { + return objectInstance.GetType(); + } + } + else + { + // No singleton instance found -> check bean definition. + IObjectFactory parentFactory = ParentObjectFactory; + if (parentFactory != null && !ContainsObjectDefinition(objectName)) + { + // No bean definition found in this factory -> delegate to parent. + return parentFactory.GetType(this.OriginalObjectName(name)); + } + + RootObjectDefinition mod = this.GetMergedObjectDefinition(objectName, false); + Type objectType = PredictObjectType(objectName, mod); + + if (objectType != null && typeof (IFactoryObject).IsAssignableFrom(objectType)) + { + if (!IsFactoryDereference(name)) + { + // If it's a FactoryBean, we want to look at what it creates, not the factory class. + return GetTypeForFactoryObject(objectName, mod); + } + else + { + return objectType; + } + } + else + { + return (!IsFactoryDereference(name) ? objectType : null); + } + } + } + + /// + /// Determines the of the object defined + /// by the supplied object . + /// + /// + ///

    + /// This, the default, implementation returns + /// to indicate that the type cannot be determined. Subclasses are + /// encouraged to try to determine the actual return + /// here, matching their strategy of resolving + /// factory methods in the + /// + /// implementation. + ///

    + ///
    + /// + /// The name associated with the supplied object . + /// + /// + /// The + /// that the is to be determined for. + /// + /// + /// The of the object defined by the supplied + /// object ; or if the + /// cannot be determined. + /// + protected virtual Type GetTypeForFactoryMethod(string objectName, RootObjectDefinition definition) + { + return null; + } + + /// + /// Returns the names of the objects in the singleton cache. + /// + /// + ///

    + /// Does not consider any hierarchy this factory may participate in. + ///

    + ///
    + /// The names of the objects in the singleton cache. + public virtual string[] GetSingletonNames() + { + lock (singletonCache) + { + return (string[]) new ArrayList(singletonCache.Keys).ToArray(typeof(string)); + } + } + + /// + /// Returns the number of objects in the singleton cache. + /// + /// + ///

    + /// Does not consider any hierarchy this factory may participate in. + ///

    + ///
    + /// The number of objects in the singleton cache. + public virtual int GetSingletonCount() + { + lock (singletonCache) + { + return singletonCache.Count; + } + } + + /// + /// Destroys the named singleton object. + /// + /// + ///

    + /// Delegates to + /// + /// if a corresponding singleton instance is found. + ///

    + ///
    + /// + /// The name of the singleton object that is to be destroyed. + /// + /// + protected virtual void DestroySingleton(string name) + { + lock (singletonCache) + { + object tempObject = singletonCache[name]; + singletonCache.Remove(name); + + object singletonInstance = tempObject; + if (singletonInstance != null) + { + DestroyObject(name, singletonInstance); + } + } + } + + /// + /// Check the supplied merged object definition for any possible + /// validation errors. + /// + /// + /// The object definition to be checked for validation errors. + /// + /// + /// The name of the object associated with the supplied object definition. + /// + /// + /// The the object may match. Can be an interface or + /// superclass of the actual class. For example, if the value is the + /// class, this method will succeed whatever the + /// class of the returned instance. + /// + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a factory method. If there is no factory method and the + /// supplied array is not , then + /// match the argument values by type and call the object's constructor. + /// + /// + /// In the case of object validation errors. + /// + protected void CheckMergedObjectDefinition(RootObjectDefinition mergedObjectDefinition, String objectName, + Type requiredType, params object[] arguments) + { + // check if required type can match according to the object definition; + // this is only possible at this early stage for conventional objects! + if (mergedObjectDefinition.HasObjectType) + { + Type objectType = mergedObjectDefinition.ObjectType; + if (requiredType != null && StringUtils.IsNullOrEmpty(mergedObjectDefinition.FactoryMethodName) + && !typeof(IFactoryObject).IsAssignableFrom(objectType) + && !requiredType.IsAssignableFrom(objectType)) + { + throw new ObjectNotOfRequiredTypeException(objectName, requiredType, objectType); + } + } + // check validity of the usage of the args parameter; this can + // only be used for prototypes constructed via a factory method... + if (arguments != null && arguments.Length > 0) + { + if (mergedObjectDefinition.IsSingleton) + { + throw new ObjectDefinitionStoreException("Cannot specify arguments in the GetObject () method when " + + "referring to a singleton object definition."); + } + //MLP lets skip this check for now. + /* + else if (StringUtils.IsNullOrEmpty(mergedObjectDefinition.FactoryMethodName)) + { + throw new ObjectDefinitionStoreException( + "Can only specify arguments in the GetObject () method in " + + "conjunction with a factory method."); + } + */ + } + } + + /// + /// Gets the temporary object that is placed + /// into the singleton cache during object resolution. + /// + protected object TemporarySingletonPlaceHolder + { + get { return CURRENTLY_IN_CREATION; } + } + + #endregion + + #region Fields + + /// + /// Parent object factory, for object inheritance support + /// + private IObjectFactory parentObjectFactory; + + /// + /// Dependency types to ignore on dependency check and autowire, as Set of + /// Type objects: for example, string. Default is none. + /// + private ISet ignoreDependencyTypes = new HybridSet(); + + + /// + /// ObjectPostProcessors to apply in CreateObject + /// + private IList objectPostProcessors = new ArrayList(); + + /// + /// Indicates whether any IInstantiationAwareBeanPostProcessors have been registered + /// + private bool hasInstantiationAwareBeanPostProcessors; + + /// + /// Indicates whether any IDestructionAwareBeanPostProcessors have been registered + /// + private bool hasDestructionAwareBeanPostProcessors; + + private IDictionary aliasMap; + private IDictionary singletonCache; + private IDictionary singletonsInCreation; + + #endregion + + #region IHierarchicalObjectFactory Members + + /// + /// The parent object factory, or if there is none. + /// + /// + /// The parent object factory, or if there is none. + /// + public IObjectFactory ParentObjectFactory + { + get { return parentObjectFactory; } + set { parentObjectFactory = value; } + } + + #endregion + + #region IObjectFactory Members + + /// + /// Is this object a singleton? + /// + /// + public bool IsSingleton(string name) + { + string objectName = TransformedObjectName(name); + object objectInstance = this.GetSingleton(objectName); + if (objectInstance != null) + { + IFactoryObject factoryObject = objectInstance as IFactoryObject; + if (factoryObject != null) + { + return IsFactoryDereference(name) || factoryObject.IsSingleton; + } + else + { + return !IsFactoryDereference(name); + } + } + else + { + // No singleton instance found -> check object definition + IObjectFactory pof = ParentObjectFactory; + if (pof != null && !ContainsObjectDefinition(objectName)) + { + // No object definition found in this factory -> delegate to parent + return pof.IsSingleton(OriginalObjectName(name)); + } + RootObjectDefinition od = GetMergedObjectDefinition(objectName, false); + + // In case of IFactoryObject, return singleton status of created object if not a dereference + if (od.IsSingleton) + { + if (IsObjectTypeMatch(objectName, od, typeof(IFactoryObject))) + { + if (IsFactoryDereference(name)) + { + return true; + } + IFactoryObject factoryObject = + (IFactoryObject) GetObject(ObjectFactoryUtils.BuildFactoryObjectName(objectName)); + return factoryObject.IsSingleton; + } + else + { + return !IsFactoryDereference(name); + } + } + else + { + return false; + } + } + } + + /// + /// Determines whether the specified object name is prototype. That is, will GetObject + /// always return independent instances? + /// + /// The name of the object to query + /// + /// true if the specified object name will always deliver independent instances; otherwise, false. + /// + /// This method returning false does not clearly indicate a singleton object. + /// It indicated non-independent instances, which may correspond to a scoped object as + /// well. use the IsSingleton property to explicitly check for a shared + /// singleton instance. + /// Translates aliases back to the corresponding canonical object name. Will ask the + /// parent factory if the object can not be found in this factory instance. + /// + /// + /// if there is no object with the given name. + public bool IsPrototype(string name) + { + string objectName = TransformedObjectName(name); + IObjectFactory parentFactory = ParentObjectFactory; + if (parentFactory != null && !this.ContainsObjectDefinition(objectName)) + { + // No object definition found in this factory -> delegate to parent + return parentFactory.IsPrototype(OriginalObjectName(name)); + } + + RootObjectDefinition od = GetMergedObjectDefinition(objectName, false); + + // In case of FactoryObject, return singleton status of created object if not a dereference + if (od.IsPrototype) + { + return (!IsFactoryDereference(name) || IsObjectTypeMatch(objectName, od, typeof(IFactoryObject))); + } + else + { + // not a prototype, however factory object may still produce a prototype object + if (IsFactoryDereference(name) && IsObjectTypeMatch(objectName, od, typeof (IFactoryObject))) + { + IFactoryObject factoryObject = GetFactoryObject(objectName); + return (!factoryObject.IsSingleton); + } + else + { + return false; + } + } + } + + /// + /// Does this object factory contain an object with the given name? + /// + /// + /// This method does not (and it should not) check if the specified + /// object exists in one of the parent object factories. If it did, + /// message sources and event registries within application context + /// hierarchy would have circular references, which would cause stack + /// overflows during message lookup, for example. (A. Seovic) + /// + /// . + public bool ContainsObject(string name) + { + string objectName = TransformedObjectName(name); + lock (singletonCache) + { + if (singletonCache.Contains(objectName)) + { + return true; + } + } + if (ContainsObjectDefinition(objectName)) + { + return true; + } + else + { + return false; + } + } + + /// + /// Return the aliases for the given object name, if defined. + /// + /// . + public string[] GetAliases(string name) + { + string objectName = TransformedObjectName(name); + // check if object actually exists in this object factory... + bool isInSingletonCache = false; + lock(singletonCache) + { + isInSingletonCache = singletonCache.Contains(objectName); + } + if (isInSingletonCache || ContainsObjectDefinition(objectName)) + { + // if found, gather aliases... + ArrayList matches = new ArrayList(); + lock (aliasMap) + { + foreach (DictionaryEntry aliasEntry in aliasMap) + { + if (aliasEntry.Value.Equals(objectName)) + { + matches.Add(aliasEntry.Key); + } + } + } + return (string[]) matches.ToArray(typeof(string)); + } + else + { + // not found, so check parent... + if (ParentObjectFactory != null) + { + return ParentObjectFactory.GetAliases(objectName); + } + throw new NoSuchObjectDefinitionException(objectName, ToString()); + } + } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// . + public object this[string name] + { + get { return GetObject(name); } + } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// . + public object GetObject(string name) + { + return GetObject(name, typeof(object), ObjectUtils.EmptyObjects); + } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// + ///

    + /// This method allows an object factory to be used as a replacement for the + /// Singleton or Prototype design pattern. + ///

    + ///

    + /// Note that callers should retain references to returned objects. There is no + /// guarantee that this method will be implemented to be efficient. For example, + /// it may be synchronized, or may need to run an RDBMS query. + ///

    + ///

    + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + ///

    + ///
    + /// The name of the object to return. + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a static factory method. If there is no factory method and the + /// arguments are not null, then match the argument values by type and + /// call the object's constructor. + /// + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + /// If the supplied is . + /// + public object GetObject(string name, object[] arguments) + { + object instance = null; + string objectName = TransformedObjectName(name); + + // check if object definition exists + RootObjectDefinition mergedObjectDefinition = null; + mergedObjectDefinition = GetMergedObjectDefinition(objectName, false); + if (mergedObjectDefinition == null) + { + if (ParentObjectFactory != null) + { + return ParentObjectFactory.GetObject(name, arguments); + } + throw new NoSuchObjectDefinitionException(name, "Cannot find definition for object [" + name + "]"); + } + + // Override constructor values and configure as a prototype + RootObjectDefinition tmpObjectDefinition = new RootObjectDefinition(mergedObjectDefinition); + tmpObjectDefinition.ConstructorArgumentValues = null; + tmpObjectDefinition.IsSingleton = false; + + // create a new instance... + instance = CreateObject(name, tmpObjectDefinition, arguments); + + return GetObjectForInstance(name, instance); + } + + /// + /// Tries to find a cached object for the specified name. + /// + /// Teh object name to look for. + /// The cached object if found, otherwise. + protected virtual object GetSingleton(string objectName) + { + lock (singletonCache) + { + return singletonCache[objectName]; + } + } + + /// + /// Creates a singleton instance for the specified object name and definition. + /// + /// + /// The object name (will be used as the key in the singleton cache key). + /// + /// The object definition. + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a static factory method. If there is no factory method and the + /// arguments are not null, then match the argument values by type and + /// call the object's constructor. + /// + /// The created object instance. + protected virtual object CreateAndCacheSingletonInstance(string objectName, + RootObjectDefinition objectDefinition, + object[] arguments) + { + lock (singletonCache) + { + object sharedInstance = singletonCache[objectName]; + if (sharedInstance == null) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Creating shared instance of singleton object '" + objectName + "'"); + } + + #endregion + + BeforeSingletonCreation(objectName); + try + { + sharedInstance = CreateObject(objectName, objectDefinition, arguments); + } + finally + { + AfterSingletonCreation(objectName); + } + AddSingleton(objectName, sharedInstance); + } + return sharedInstance; + } + } + + private void AfterSingletonCreation(string name) + { + if (!singletonsInCreation.Contains(name)) + { + throw new InvalidOperationException("Singleton " + name + " isn't currently in creation."); + } + singletonsInCreation.Remove(name); + } + + private void BeforeSingletonCreation(string name) + { + if (this.singletonsInCreation.Contains(name)) + { + throw new ObjectCurrentlyInCreationException(name); + } + singletonsInCreation.Add(name, emptyObject); + } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// + public object GetObject(string name, Type requiredType) + { + return GetObject(name, requiredType, ObjectUtils.EmptyObjects); + } + + /// + /// Injects dependencies into the supplied instance + /// using the named object definition. + /// + /// + public abstract object ConfigureObject(object target, string name); + + /// + /// Injects dependencies into the supplied instance + /// using the supplied . + /// + /// + public abstract object ConfigureObject(object target, string name, IObjectDefinition definition); + + #endregion + + #region IConfigurableObjectFactory Members + + /// + /// Destroy all cached singletons in this factory. + /// + public virtual void Dispose() + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format("Destroying singletons in factory [{0}].", this)); + } + + #endregion + + lock (singletonCache) + { + // copy the keys into a new set, 'cos we are going to modifying the + // original collection (_singletonCache) as we destroy each singleton. + ISet keys = new HashedSet(singletonCache.Keys); + foreach (string name in keys) + { + DestroySingleton(name); + } + } + } + + /// + /// Ignore the given dependency type for autowiring + /// + /// . + public void IgnoreDependencyType(Type type) + { + IgnoredDependencyTypes.Add(type); + } + + /// + /// Determines whether the specified object name is currently in creation.. + /// + /// Name of the object. + /// + /// true if the specified object name is currently in creation; otherwise, false. + /// + public bool IsCurrentlyInCreation(string objectName) + { + return IsSingletonCurrentlyInCreation(objectName) || IsPrototypeCurrentlyInCreation(objectName); + } + + private bool IsPrototypeCurrentlyInCreation(string name) + { + //TODO + return false; + } + + private bool IsSingletonCurrentlyInCreation(string name) + { + if (this.singletonsInCreation.Contains(name)) + { + return true; + } + else + { + return false; + } + } + + /// + /// Add a new + /// that will get applied to objects created by this factory. + /// + /// + /// The + /// to register. + /// + /// . + public void AddObjectPostProcessor(IObjectPostProcessor objectPostProcessor) + { + // ensure the same instance doesn't get registered twice + if (!ObjectPostProcessors.Contains(objectPostProcessor)) + { + ObjectPostProcessors.Add(objectPostProcessor); + } + if (typeof (IInstantiationAwareObjectPostProcessor).IsInstanceOfType(objectPostProcessor)) + { + hasInstantiationAwareBeanPostProcessors = true; + } + if (typeof (IDestructionAwareObjectPostProcessor).IsInstanceOfType(objectPostProcessor)) + { + hasDestructionAwareBeanPostProcessors = true; + } + } + + /// + /// Returns the current number of registered + /// s. + /// + /// + /// The current number of registered + /// s. + /// + /// . + public int ObjectPostProcessorCount + { + get { return ObjectPostProcessors.Count; } + } + + /// + /// Given an object name, create an alias. + /// + /// . + public void RegisterAlias(string name, string alias) + { + #region Sanity Checks + + AssertUtils.ArgumentHasText(name, "The object name must not be empty."); + AssertUtils.ArgumentHasText(alias, "The alias must not be empty."); + + #endregion + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format("Registering alias '{0}' for object with name '{1}'.", alias, name)); + } + + #endregion + + lock (aliasMap) + { + object registeredName = aliasMap[alias]; + if (registeredName != null) + { + throw new ObjectDefinitionStoreException( + string.Format( + "Cannot register alias '{0}' for object with name '{1}': it's already registered for object name '{2}'.", + alias, name, registeredName)); + } + aliasMap[alias] = name; + } + } + + /// + /// Register the given existing object as singleton in the object factory, + /// under the given object name. + /// + /// . + public void RegisterSingleton(string name, object singletonObject) + { + AssertUtils.ArgumentHasText(name, "name", "The singleton object cannot be registered under an empty name."); + lock (singletonCache) + { + object oldObject = singletonCache[name]; + if (oldObject != null) + { + throw new ObjectDefinitionStoreException( + string.Format( + "Could not register object [{0}] under object name '{1}': there's already object [{2}] bound.", + singletonObject, name, oldObject)); + } + AddSingleton(name, singletonObject); + } + } + + /// + /// Register the given custom + /// for all properties of the given . + /// + /// . + public void RegisterCustomConverter(Type requiredType, TypeConverter converter) + { + AssertUtils.ArgumentNotNull(requiredType, "requiredType"); + TypeConverterRegistry.RegisterConverter(requiredType, converter); + } + + /// + /// Does this object factory contains a singleton instance with the + /// supplied ? + /// + /// + public bool ContainsSingleton(string name) + { + AssertUtils.ArgumentHasText(name, "name"); + lock (singletonCache) + { + return singletonCache.Contains(name); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AutowireUtils.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AutowireUtils.cs new file mode 100644 index 00000000..73005c0a --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/AutowireUtils.cs @@ -0,0 +1,286 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using Spring.Collections; +using Spring.Core; +using Spring.Objects.Factory.Config; +using Spring.Objects.Support; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Utility class that contains various methods useful for the implementation of + /// autowire-capable object factories. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: AutowireUtils.cs,v 1.7 2008/04/02 18:02:24 markpollack Exp $ + public sealed class AutowireUtils + { + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the AutowireUtils class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + private AutowireUtils() + { + } + + // CLOVER:ON + + #endregion + + /// + /// Gets those s + /// that are applicable for autowiring the supplied . + /// + /// + /// The + /// (definition) that is being autowired by constructor. + /// + /// + /// The absolute minimum number of arguments that any returned constructor + /// must have. If this parameter is equal to zero (0), then all constructors + /// are valid (regardless of their argument count), including any default + /// constructor. + /// + /// + /// Those s + /// that are applicable for autowiring the supplied . + /// + public static ConstructorInfo[] GetConstructors( + IObjectDefinition definition, int minimumArgumentCount) + { + const BindingFlags flags = + BindingFlags.Public | BindingFlags.NonPublic + | BindingFlags.Instance | BindingFlags.DeclaredOnly; + ConstructorInfo[] constructors = null; + if (minimumArgumentCount > 0) + { + MemberInfo[] ctors = definition.ObjectType.FindMembers( + MemberTypes.Constructor, + flags, + new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria), + new MinimumArgumentCountCriteria(minimumArgumentCount)); + constructors = (ConstructorInfo[]) ArrayList.Adapter(ctors).ToArray(typeof (ConstructorInfo)); + } + else + { + constructors = definition.ObjectType.GetConstructors(flags); + } + AutowireUtils.SortConstructors(constructors); + return constructors; + } + + /// + /// Determine a weight that represents the class hierarchy difference between types and + /// arguments. + /// + /// + ///

    + /// A direct match, i.e. type MyInteger -> arg of class MyInteger, does not increase + /// the result - all direct matches means weight zero (0). A match between the argument type + /// and a MyInteger instance argument would increase the weight by + /// 1, due to the superclass () being one (1) steps up in the + /// class hierarchy being the last one that still matches the required type. + ///

    + ///

    + /// Therefore, with an argument of type , a + /// constructor taking a argument would be + /// preferred to a constructor taking an argument + /// which would be preferred to a constructor taking an + /// argument which would in turn be preferred + /// to a constructor taking an argument. + ///

    + ///

    + /// All argument weights get accumulated. + ///

    + ///
    + /// + /// The argument s to match. + /// + /// The arguments to match. + /// The accumulated weight for all arguments. + public static int GetTypeDifferenceWeight(ParameterInfo[] argTypes, object[] args) + { + if (argTypes.Length != args.Length) + { + throw new ArgumentException("Cannot calculate the type difference weight for argument types and arguments with differing lengths."); + } + int result = 0; + for (int i = 0; i < argTypes.Length; i++) + { + Type theParameterType = argTypes[i].ParameterType; + if (!ObjectUtils.IsAssignable(theParameterType, args[i])) + { + return Int32.MaxValue; + } + if (args[i] != null + && !(args[i].GetType().Equals(theParameterType))) + { + Type superType = args[i].GetType().BaseType; + while (superType != null) + { + if (theParameterType.IsAssignableFrom(superType)) + { + ++result; + superType = superType.BaseType; + } + else + { + superType = null; + } + } + } + } + return result; + } + + /// + /// Determines whether the given object property is excluded from dependency checks. + /// + /// The PropertyInfo of the object property. + /// + /// true if is excluded from dependency check; otherwise, false. + /// + public static Boolean IsExcludedFromDependencyCheck(PropertyInfo pi) + { + return (pi.GetSetMethod() == null) ? false : true; + } + + /// + /// Sorts the supplied , preferring + /// public constructors and "greedy" ones (that have lots of arguments). + /// + /// + ///

    + /// The result will contain public constructors first, with a decreasing number + /// of arguments, then non-public constructors, again with a decreasing number + /// of arguments. + ///

    + ///
    + /// + /// The array to be sorted. + /// + public static void SortConstructors(ConstructorInfo[] constructors) + { + if (constructors != null + && constructors.Length > 0) + { + Array.Sort(constructors, new ConstructorComparer()); + } + } + + #region Inner Class : ConstructorComparer + + private sealed class ConstructorComparer : IComparer + { + public int Compare(object lhs, object rhs) + { + ConstructorInfo lhsCtor = (ConstructorInfo) lhs; + ConstructorInfo rhsCtor = (ConstructorInfo) rhs; + if (lhsCtor.IsPublic != rhsCtor.IsPublic) + { + return (lhsCtor.IsPublic ? -1 : 1); + } + int lhsParams = lhsCtor.GetParameters().Length; + int rhsParams = rhsCtor.GetParameters().Length; + + if (lhsParams < rhsParams) + { + return 1; + } + else if (lhsParams > rhsParams) + { + return -1; + } + else + { + return 0; + } + } + } + + #endregion + + #region Inner Class : MinimumArgumentCountCriteria + + private sealed class MinimumArgumentCountCriteria : ICriteria + { + public MinimumArgumentCountCriteria(int minimumArgumentCount) + { + _minimumArgumentCount = minimumArgumentCount; + } + + public bool IsSatisfied(object datum) + { + bool satisfied = false; + satisfied = ((MethodBase) datum).GetParameters().Length >= _minimumArgumentCount; + return satisfied; + } + + private int _minimumArgumentCount; + } + + #endregion + + /// + /// Determines whether the setter property is defined in any of the given interfaces. + /// + /// The PropertyInfo of the object property + /// The ISet of interfaces. + /// + /// true if setter property is defined in interface; otherwise, false. + /// + public static bool IsSetterDefinedInInterface(PropertyInfo propertyInfo, ISet interfaces) + { + MethodInfo setter = propertyInfo.GetSetMethod(); + if (setter != null) + { + Type targetType = setter.DeclaringType; + foreach (Type interfaceType in interfaces) + { + if (interfaceType.IsAssignableFrom(targetType) && + ReflectionUtils.GetMethod(interfaceType, setter.Name, ReflectionUtils.GetParameterTypes(setter)) != null) + { + return true; + } + } + } + return false; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/ChildObjectDefinition.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ChildObjectDefinition.cs new file mode 100644 index 00000000..917b43ee --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/ChildObjectDefinition.cs @@ -0,0 +1,255 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Object definition for definitions that inherit settings from their + /// parent (object definition). + /// + /// + ///

    + /// Will use the + /// of the parent object definition if none is specified, but can also + /// override it. In the latter case, the child's + /// + /// must be compatible with the parent, i.e. accept the parent's property values + /// and constructor argument values (if any). + ///

    + ///

    + /// A will + /// inherit all of the , + /// , and + /// from it's parent + /// object definition, with the option to add new values. If the + /// , + /// , + /// and / or + /// + /// properties are specified, they will override the corresponding parent settings. + ///

    + ///

    + /// The remaining settings will always be taken from the child definition: + /// , + /// , + /// , + /// , + /// and + /// + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// + [Serializable] + public class ChildObjectDefinition : AbstractObjectDefinition + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The name of the parent object. + /// + public ChildObjectDefinition(string parentName) + { + this.parentName = parentName; + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The name of the parent object. + /// + /// + /// The additional property values (if any) of the child. + /// + public ChildObjectDefinition(string parentName, MutablePropertyValues properties) + : base(null, properties) + { + this.parentName = parentName; + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The name of the parent object. + /// + /// + /// The + /// to be applied to a new instance of the object. + /// + /// + /// The additional property values (if any) of the child. + /// + public ChildObjectDefinition( + string parentName, ConstructorArgumentValues arguments, MutablePropertyValues properties) + : base(arguments, properties) + { + this.parentName = parentName; + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The name of the parent object. + /// + /// + /// The class of the object to instantiate. + /// + /// + /// The + /// to be applied to a new instance of the object. + /// + /// + /// The additional property values (if any) of the child. + /// + public ChildObjectDefinition( + string parentName, Type type, ConstructorArgumentValues arguments, MutablePropertyValues properties) + : base(arguments, properties) + { + this.parentName = parentName; + ObjectType = type; + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The name of the parent object. + /// + /// + /// The of the object to + /// instantiate. + /// + /// + /// The + /// to be applied to a new instance of the object. + /// + /// + /// The additional property values (if any) of the child. + /// + public ChildObjectDefinition( + string parentName, string typeName, ConstructorArgumentValues arguments, MutablePropertyValues properties) + : base(arguments, properties) + { + this.parentName = parentName; + ObjectTypeName = typeName; + } + + #endregion + + #region Properties + + /// + /// The name of the parent object definition. + /// + /// + ///

    + /// This value is required. + ///

    + ///
    + /// + /// The name of the parent object definition. + /// + public string ParentName + { + get { return parentName; } + } + + #endregion + + #region Methods + + /// + /// Validate this object definition. + /// + /// + ///

    + /// A common cause of validation failures is a missing value for the + /// + /// property; by + /// their very nature require that the + /// + /// be set. + ///

    + ///
    + /// + /// In the case of a validation failure. + /// + public override void Validate() + { + base.Validate(); + if (StringUtils.IsNullOrEmpty(parentName)) + { + throw new ObjectDefinitionValidationException( + "The 'ParentName' property must be set in ChildObjectDefinition."); + } + } + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + StringBuilder buffer = new StringBuilder(); + buffer.Append(GetType().Name).Append(" with parent '"); + buffer.Append(ParentName).Append("' : ").Append(base.ToString()); + return buffer.ToString(); + } + + #endregion + + #region Fields + + private string parentName; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs new file mode 100644 index 00000000..8b34d12b --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs @@ -0,0 +1,893 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Globalization; +using Common.Logging; +using Spring.Core; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Concrete implementation of the + /// and + /// + /// interfaces. + /// + /// + ///

    + /// This class is a full-fledged object factory based on object definitions + /// that is usable straight out of the box. + ///

    + ///

    + /// Can be used as an object factory in and of itself, or as a superclass + /// for custom object factory implementations. Note that readers for + /// specific object definition formats are typically implemented separately + /// rather than as object factory subclasses. + ///

    + ///

    + /// For an alternative implementation of the + /// interface, + /// have a look at the + /// + /// class, which manages existing object instances rather than creating new + /// ones based on object definitions. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// + /// $Id: DefaultListableObjectFactory.cs,v 1.42 2007/10/10 19:17:07 bbaia Exp $ + [Serializable] + public class DefaultListableObjectFactory : + AbstractAutowireCapableObjectFactory, + IConfigurableListableObjectFactory, + IObjectDefinitionRegistry + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public DefaultListableObjectFactory() : this(true, null) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// Flag specifying whether to make this object factory case sensitive or not. + public DefaultListableObjectFactory(bool caseSensitive) : this(caseSensitive, null) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// The parent object factory. + public DefaultListableObjectFactory(IObjectFactory parentFactory) + : this(true, parentFactory) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// Flag specifying whether to make this object factory case sensitive or not. + /// The parent object factory. + public DefaultListableObjectFactory(bool caseSensitive, IObjectFactory parentFactory) + : base(caseSensitive, parentFactory) + { + if (caseSensitive) + { + objectDefinitionMap = new Hashtable(); + } + else + { + objectDefinitionMap = CollectionsUtil.CreateCaseInsensitiveHashtable(); + } + } + + #endregion + + #region Properties + + /// + /// Should object definitions registered under the same name as an + /// existing object definition be allowed? + /// + /// + ///

    + /// If , then the new object definition will + /// replace (override) the existing object definition. If + /// , an exception will be thrown when + /// an attempt is made to register an object definition under the same + /// name as an already existing object definition. + ///

    + ///

    + /// The default is . + ///

    + ///
    + /// + /// is the registration of an object definition + /// under the same name as an existing object definition is allowed. + /// + public bool AllowObjectDefinitionOverriding + { + get { return allowObjectDefinitionOverriding; } + set { allowObjectDefinitionOverriding = value; } + } + + #endregion + + #region Methods + + /// + /// Find object instances that match the . + /// + /// + ///

    + /// Called by autowiring. If a subclass cannot obtain information about object + /// names by , a corresponding exception should be thrown. + ///

    + ///
    + /// + /// The type of the objects to look up. + /// + /// + /// An of object names and object + /// instances that match the , or + /// if none is found. + /// + /// + /// In case of errors. + /// + protected override IDictionary FindMatchingObjects(Type requiredType) + { + return ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors( + this, requiredType, true, true); + } + + /// + /// Return the names of the objects that depend on the given object. + /// + /// + ///

    + /// Called by the + /// + /// so that dependant objects are able to be disposed of first. + ///

    + ///
    + /// + /// The name of the object to find depending objects for. + /// + /// + /// The array of names of depending objects, or the empty string array if none. + /// + /// + /// In case of errors. + /// + protected override string[] GetDependingObjectNames(string objectName) + { + ArrayList dependingObjectNames = new ArrayList(); + string[] allObjectDefinitionNames = GetObjectDefinitionNames(); + foreach (string name in allObjectDefinitionNames) + { + if (ContainsObjectDefinition(name)) + { + RootObjectDefinition rod + = GetMergedObjectDefinition(name, false); + if (rod.DependsOn != null) + { + IList dependsOn = new ArrayList(rod.DependsOn); + if (dependsOn.Contains(objectName)) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "Found depending object '{0}' for object '{1}'.", + name, objectName)); + } + + #endregion + + dependingObjectNames.Add(name); + } + } + } + } + return (string[]) dependingObjectNames.ToArray(typeof(string)); + } + + /// + /// Check whether the specified object matches the supplied . + /// + /// The name of the object to check. + /// + /// The to check for. + /// + /// + /// if the object matches the supplied , + /// or if the supplied is . + /// + private bool IsObjectTypeMatch(string objectName, Type type) + { + if (type == null) + { + return true; + } + Type objectType = GetType(objectName); + return (objectType != null && type.IsAssignableFrom(objectType)); + } + + private bool IsObjectDefinitionTypeMatch(string name, Type checkedType) + { + if (checkedType == null) + { + return true; + } + RootObjectDefinition rod = GetMergedObjectDefinition(name, false); + return (rod.HasObjectType && checkedType.IsAssignableFrom(rod.ObjectType)); + } +/* + /// + /// Merges the object definitions. + /// + /// Object definition name. + /// The parent definition. + /// The child definition. + /// Merged object definition. + protected override RootObjectDefinition MergeObjectDefinitions(string name, IObjectDefinition parentDefinition, + IObjectDefinition childDefinition) + { + RootObjectDefinition rootDefinition = base.MergeObjectDefinitions(name, parentDefinition, childDefinition); + RegisterObjectDefinition(name, rootDefinition); + return rootDefinition; + } +*/ + #endregion + + #region Fields + + /// + /// The instance for this class. + /// + private readonly ILog log = LogManager.GetLogger(typeof(DefaultListableObjectFactory)); + + /// + /// Whether to allow re-registration of a different definition with the + /// same name. + /// + private bool allowObjectDefinitionOverriding = true; + + /// + /// The mapping of object definition objects, keyed by object name. + /// + private readonly IDictionary objectDefinitionMap; + + /// + /// List of object definition names, in registration order. + /// + private readonly IList objectDefinitionNames = new ArrayList(); + + #endregion + + #region IObjectDefinitionRegistry Members + + /// + /// Return the number of objects defined in this registry. + /// + /// + /// The number of objects defined in this registry. + /// + /// + public int ObjectDefinitionCount + { + get { return objectDefinitionMap.Count; } + } + + /// + /// Check if this registry contains a object definition with the given + /// name. + /// + /// + /// The name of the object to look for. + /// + /// + /// if this object factory contains an object + /// definition with the given name. + /// + /// + public override bool ContainsObjectDefinition(string name) + { + return objectDefinitionMap.Contains(name); + } + + /// + /// Register a new object definition with this registry. + /// + /// + /// The name of the object instance to register. + /// + /// + /// The definition of the object instance to register. + /// + /// + /// If the object definition is invalid. + /// + /// + public void RegisterObjectDefinition( + string name, IObjectDefinition objectDefinition) + { + if (objectDefinition is AbstractObjectDefinition) + { + try + { + ((AbstractObjectDefinition) objectDefinition).Validate(); + } + catch (ObjectDefinitionValidationException ex) + { + throw new ObjectDefinitionStoreException( + objectDefinition.ResourceDescription, + name, + "Validation of object definition failed.", + ex); + } + } + object oldObjectDefinition = objectDefinitionMap[name]; + if (oldObjectDefinition != null) + { + if (!AllowObjectDefinitionOverriding) + { + throw new ObjectDefinitionStoreException( + string.Format( + "Cannot register object definition [{0}] for object '{1}': there's already [{2}] bound.", + objectDefinition, name, oldObjectDefinition)); + } + else + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + "Overriding object definition for object '{0}': replacing [{1}] with [{2}].", + name, oldObjectDefinition, objectDefinition)); + } + + #endregion + } + } + else + { + objectDefinitionNames.Add(name); + } + objectDefinitionMap[name] = objectDefinition; + } + + #endregion + + #region IConfigurableListableObjectFactory Members + + /// + /// Ensure that all non-lazy-init singletons are instantiated, also + /// considering s. + /// + /// + /// If one of the singleton objects could not be created. + /// + /// + public void PreInstantiateSingletons() + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Pre-instantiating singletons in factory [" + this + "]"); + } + + #endregion + + try + { + int definitionCount = objectDefinitionNames.Count; + for (int i = 0; i < definitionCount; i++) + { + string name = (string) objectDefinitionNames[i]; + if (!ContainsSingleton(name) && ContainsObjectDefinition(name)) + { + RootObjectDefinition definition + = GetMergedObjectDefinition(name, false); + if (!definition.IsAbstract + && definition.IsSingleton + && !definition.IsLazyInit) + { + Type objectType = ResolveObjectType(definition, name); + if (objectType != null + && typeof(IFactoryObject).IsAssignableFrom(definition.ObjectType)) + { + IFactoryObject factoryObject = (IFactoryObject) GetObject( + ObjectFactoryUtils. + BuildFactoryObjectName(name)); + if (factoryObject.IsSingleton) + { + GetObject(name); + } + } + else + { + GetObject(name); + } + } + } + } + } + catch (ObjectsException) + { + // destroy already created singletons to avoid dangling resources... + try + { + Dispose(); + } + catch (Exception ex) + { + log.Error( + "PreInstantiateSingletons failed but couldn't destroy any already-created singletons.", + ex); + } + throw; + } + } + + /// + /// Return the registered + /// for the + /// given object, allowing access to its property values and constructor + /// argument values. + /// + /// The name of the object. + /// + /// The registered , + /// or null, if specified object definitions does not exist. + /// + /// + /// If is null or empty string. + /// + /// + public override IObjectDefinition GetObjectDefinition(string name) + { + return GetObjectDefinition(name, false); + } + + /// + /// Return the registered + /// for the + /// given object, allowing access to its property values and constructor + /// argument values. + /// + /// The name of the object. + /// Whether to search parent object factories. + /// + /// The registered , + /// or null, if specified object definitions does not exist. + /// + /// + /// If is null or empty string. + /// + /// + public override IObjectDefinition GetObjectDefinition(string name, bool includeAncestors) + { + if (StringUtils.IsNullOrEmpty(name)) + { + throw new ArgumentException( + "Cannot get an object definition with a null or zero length object name string."); + } + + name = TransformedObjectName(name); + IObjectDefinition definition = (IObjectDefinition) objectDefinitionMap[name]; + if (definition == null) + { + if (!includeAncestors || ParentObjectFactory == null) + { + return null; + } + else if (ParentObjectFactory is AbstractObjectFactory) + { + definition = + ((AbstractObjectFactory) ParentObjectFactory).GetObjectDefinition(name, includeAncestors); + } + } + return definition; + } + + #endregion + + #region IListableObjectFactory Members + + /// + /// Return the names of all objects defined in this factory. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + /// + public string[] GetObjectDefinitionNames() + { + return (string[]) ((ArrayList) objectDefinitionNames).ToArray(typeof(string)); + } + + /// + /// Return the names of objects matching the given + /// (including subclasses), judging from the object definitions. + /// + /// + /// The (class or interface) to match, or + /// for all object names. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + /// + public string[] GetObjectDefinitionNames(Type type) + { + ArrayList matches = new ArrayList(); + foreach (string name in objectDefinitionNames) + { + if (IsObjectDefinitionTypeMatch(name, type)) + { + matches.Add(name); + } + } + return (string[]) matches.ToArray(typeof(string)); + } + + /// + /// Return the names of objects matching the given + /// (including subclasses), judging from the object definitions. + /// + /// + /// The (class or interface) to match, or + /// for all object names. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + /// + public string[] GetObjectNamesForType(Type type) + { + return GetObjectNamesForType(type, true, true); + } + + /// + /// Return the names of objects matching the given + /// (including subclasses), judging from the object definitions. + /// + /// + /// The (class or interface) to match, or + /// for all object names. + /// + /// + /// Whether to include prototype objects too or just singletons (also applies to + /// s). + /// + /// + /// Whether to include s too + /// or just normal objects. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + /// + public string[] GetObjectNamesForType( + Type type, bool includePrototypes, bool includeFactoryObjects) + { + IList objectNames = DoGetObjectNamesForType(type, includePrototypes, includeFactoryObjects); + return (string[]) ArrayList.Adapter(objectNames).ToArray(typeof(string)); + } + + /// + /// Return the object instances that match the given object + /// (including subclasses), judging from either object + /// definitions or the value of + /// in the case of + /// s. + /// + /// + /// The (class or interface) to match. + /// + /// + /// A of the matching objects, + /// containing the object names as keys and the corresponding object instances + /// as values. + /// + /// + /// If the objects could not be created. + /// + /// + public IDictionary GetObjectsOfType(Type type) + { + return GetObjectsOfType(type, true, true); + } + + /// + /// Return the object instances that match the given object + /// (including subclasses). + /// + /// + /// The (class or interface) to match. + /// + /// + /// Whether to include prototype objects too or just singletons (also applies to + /// s). + /// + /// + /// Whether to include s too + /// or just normal objects. + /// + /// + /// An of the matching objects, + /// containing the object names as keys and the corresponding object instances + /// as values. + /// + /// + /// If any of the objects could not be created. + /// + /// + public IDictionary GetObjectsOfType( + Type type, bool includePrototypes, bool includeFactoryObjects) + { + IDictionary result = new Hashtable(); + IList objectNames = DoGetObjectNamesForType(type, includePrototypes, includeFactoryObjects); + foreach (string objectName in objectNames) + { + try + { + result.Add(objectName, GetObject(objectName)); + } + catch (ObjectCreationException ex) + { + if (ex.InnerException != null + && ex.GetBaseException().GetType().Equals(typeof(ObjectCurrentlyInCreationException))) + { + // ignoring this is ok... it indicates a circular reference when autowiring + // constructors; we want to find matches other than the currently + // created object itself... + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "Ignoring match to currently created object '{0}'.", + objectName), ex); + } + } + else + { + throw; + } + } + } + return result; + } + + // TODO: (ee) What's this method for? It's never used. +/* + protected IList DoGetObjectNamesForTypeNew(Type type, bool includePrototypes, bool includeFactoryObjects) + { + IList result = new ArrayList(); + string[] objectNames = GetObjectDefinitionNames(); + foreach (string objectNam in objectNames) + { + string objectName = objectNam; + if (!IsAlias(objectName)) + { + RootObjectDefinition mod = GetMergedObjectDefinition(objectName, false); + // Only check object definition if it is complete + if (!mod.IsAbstract && + (mod.HasObjectType || !mod.IsLazyInit)) + { + // In case of FactoryObject, match object created by FactoryObject + } + try + { + bool isFactoryObject = IsObjectTypeMatch(objectName, mod, typeof(IFactoryObject)); + bool matchFound = (includePrototypes || mod.IsSingleton) && IsTypeMatch(objectName, type); + if (!matchFound && isFactoryObject) + { + objectName = ObjectFactoryUtils.BuildFactoryObjectName(objectName); + matchFound = (includePrototypes || mod.IsSingleton) && IsTypeMatch(objectName, type); + } + if (matchFound) + { + result.Add(objectName); + } + } + catch (CannotLoadObjectTypeException ex) + { + // Probably contains a placeholder; lets ignore it for type matching purposes. + if (log.IsDebugEnabled) + { + log.Debug("Ignoring object class loading failure for object '" + objectName + "'", ex); + } + } + } + } + // check singletons too, to catch manually registered singletons... + string[] singletonNames = GetSingletonNames(); + foreach (string objectNam in singletonNames) + { + string objectName = objectNam; + // only check if manually registered... + if (!ContainsObjectDefinition(objectName)) + { + // in the case of an IFactoryObject, match the object created by the IFactoryObject... + if (IsFactoryObject(objectName)) + { + if ((includePrototypes || IsSingleton(objectName)) && IsTypeMatch(objectName, type)) + { + result.Add(objectName); + continue; + } + objectName = ObjectFactoryUtils.BuildFactoryObjectName(objectName); + } + if (IsTypeMatch(objectName, type)) + { + result.Add(objectName); + } + } + } + return result; + } +*/ + + /// + /// Return the object instances that match the given object + /// (including subclasses). + /// + /// + /// The (class or interface) to match. + /// + /// + /// Whether to include prototype objects too or just singletons (also applies to + /// s). + /// + /// + /// Whether to include s too + /// or just normal objects. + /// + /// + /// An of the matching objects, + /// containing the object names as keys and the corresponding object instances + /// as values. + /// + /// + /// If any of the objects could not be created. + /// + /// + protected IList DoGetObjectNamesForType( + Type type, bool includePrototypes, bool includeFactoryObjects) + { + bool isFactoryType = (type != null && typeof(IFactoryObject).IsAssignableFrom(type)); + IList result = new ArrayList(); + if (type != null) + { + string[] objectNames = GetObjectDefinitionNames(); + foreach (string objectName in objectNames) + { + RootObjectDefinition rod = GetMergedObjectDefinition(objectName, false); + // only check complete object definitions... + if (!rod.IsAbstract && rod.HasObjectType) + { + // return the return type of an object created via a factory method... + if (StringUtils.HasText(rod.FactoryMethodName)) + { + Type methodType = GetTypeForFactoryMethod(objectName, rod); + if (methodType != null + && type.IsAssignableFrom(methodType)) + { + result.Add(objectName); + } + } + // in the case of an IFactoryObject, match the object created by the IFactoryObject... + else if (typeof(IFactoryObject).IsAssignableFrom(rod.ObjectType) && !isFactoryType) + { + if (includeFactoryObjects && (includePrototypes || IsSingleton(objectName)) && + IsObjectTypeMatch(objectName, type)) + { + result.Add(objectName); + } + } + else + { + string factoryObjectName = objectName; + // if type to match is an IFactoryObject, match the IFactoryObject itself; + // else, match the object instance... + if (isFactoryType) + { + factoryObjectName = ObjectFactoryUtils.BuildFactoryObjectName(objectName); + } + if ((includePrototypes || rod.IsSingleton) && //MLP + (type.IsAssignableFrom(rod.ObjectType))) + { + result.Add(factoryObjectName); + } + } + } + } + + // check singletons too, to catch manually registered singletons... + string[] singletonNames = GetSingletonNames(); + foreach (string objectName in singletonNames) + { + // only check if manually registered... + if (!ContainsObjectDefinition(objectName)) + { + // in the case of an IFactoryObject, match the object created by the IFactoryObject... + if (IsFactoryObject(objectName) && !isFactoryType) + { + if (includeFactoryObjects && (includePrototypes || IsSingleton(objectName)) && + IsObjectTypeMatch(objectName, type)) + { + result.Add(objectName); + } + } + else + { + string factoryObjectName = objectName; + // if type to match is an IFactoryObject, match the IFactoryObject itself... + // else, match the object instance... + if (isFactoryType) + { + factoryObjectName = ObjectFactoryUtils.BuildFactoryObjectName(objectName); + } + if (IsObjectTypeMatch(factoryObjectName, type)) + { + result.Add(factoryObjectName); + } + } + } + } + } + return result; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/DefaultObjectDefinitionFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Support/DefaultObjectDefinitionFactory.cs new file mode 100644 index 00000000..e1e914c8 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/DefaultObjectDefinitionFactory.cs @@ -0,0 +1,114 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Default implementation of the + /// + /// interface. + /// + /// + ///

    + /// Does not support per + /// loading. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: DefaultObjectDefinitionFactory.cs,v 1.13 2007/08/01 12:29:43 bbaia Exp $ + [Serializable] + public class DefaultObjectDefinitionFactory : IObjectDefinitionFactory + { + #region IObjectDefinitionFactory Members + + /// + /// Factory style method for getting concrete + /// + /// instances. + /// + /// /// If no parent is specified, a RootObjectDefinition is created, otherwise a + /// ChildObjectDefinition. + /// The of the defined object. + /// The name of the parent object definition (if any). + /// The against which any class names + /// will be resolved into instances. + /// + /// An + /// + /// instance. + /// + public virtual AbstractObjectDefinition CreateObjectDefinition(string typeName, string parent, AppDomain domain) + { + Type objectType = null; + if (StringUtils.HasText(typeName) && domain != null) + { + try + { + objectType = TypeResolutionUtils.ResolveType(typeName); + } + // try later.... + catch { } + } + if (StringUtils.IsNullOrEmpty(parent)) + { + if (objectType != null) + { + return new RootObjectDefinition(objectType); + + } + else + { + RootObjectDefinition rootObjectDefinition = new RootObjectDefinition(); + rootObjectDefinition.ObjectTypeName = typeName; + return rootObjectDefinition; + } + } + else + { + if (objectType != null) + { + ChildObjectDefinition childObjectDefinition = new ChildObjectDefinition(parent); + childObjectDefinition.ObjectType = objectType; + return childObjectDefinition; + } + else + { + ChildObjectDefinition childObjectDefinition = new ChildObjectDefinition(parent); + childObjectDefinition.ObjectTypeName = typeName; + return childObjectDefinition; + } + } + } + + #endregion + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/DefaultObjectNameGenerator.cs b/src/Spring/Spring.Core/Objects/Factory/Support/DefaultObjectNameGenerator.cs new file mode 100644 index 00000000..87a3cb75 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/DefaultObjectNameGenerator.cs @@ -0,0 +1,62 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Objects.Factory.Config; + +namespace Spring.Objects.Factory.Support +{ + /// + /// Default implementation of the interface, deleagting to + /// . + /// + /// Note that this implementation is only able to handle + /// subclasses such as + /// and + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: DefaultObjectNameGenerator.cs,v 1.1 2007/05/23 20:16:13 markpollack Exp $ + public class DefaultObjectNameGenerator : IObjectNameGenerator + { + #region IObjectNameGenerator Members + + /// + /// Generates an object name for the given object definition. + /// + /// The object definition to generate a name for. + /// The object definitions registry that the given definition is + /// supposed to be registerd with + /// the generated object name + public string GenerateObjectName(IObjectDefinition definition, IObjectDefinitionRegistry registry) + { + IConfigurableObjectDefinition objectDef = definition as IConfigurableObjectDefinition; + if (objectDef == null) + { + throw new ArgumentException( + "DefaultObjectNameGenerator is only able to handle IConfigurableObjectDefinition subclasses: " + + definition); + } + return ObjectDefinitionReaderUtils.GenerateObjectName(objectDef, registry); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/DelegatingMethodReplacer.cs b/src/Spring/Spring.Core/Objects/Factory/Support/DelegatingMethodReplacer.cs new file mode 100644 index 00000000..1a4815ab --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/DelegatingMethodReplacer.cs @@ -0,0 +1,91 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// An + /// implementation that delegates to an + /// that is + /// obtained as the result of a lookup in an associated IoC container. + /// + /// + ///

    + /// This class is reserved for internal use within the framework; it is + /// not intended to be used by application developers using Spring.NET. + ///

    + ///
    + /// Rick Evans + /// $Id: DelegatingMethodReplacer.cs,v 1.3 2007/07/30 17:52:22 markpollack Exp $ + public sealed class DelegatingMethodReplacer : AbstractMethodReplacer + { + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The object definition that is the target of the method replacement. + /// + /// + /// The enclosing IoC container with which the above + /// is associated. + /// + /// + /// If either of the supplied arguments is . + /// + public DelegatingMethodReplacer(IConfigurableObjectDefinition objectDefinition, IObjectFactory objectFactory) + : base(objectDefinition, objectFactory) + { + } + + /// + /// Reimplements the supplied by delegating to + /// another + /// looked up in an enclosing IoC container. + /// + /// + /// The instance whose is to be + /// (re)implemented. + /// + /// + /// The method that is to be (re)implemented. + /// + /// The target method's arguments. + /// + /// The result of the delegated call to the looked up + /// . + /// + public override object Implement(object target, MethodInfo method, object[] arguments) + { + ReplacedMethodOverride ovr = (ReplacedMethodOverride) GetOverride(method); + IMethodReplacer methodReplacement = (IMethodReplacer) GetObject(ovr.MethodReplacerObjectName); + return methodReplacement.Implement(target, method, arguments); + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/DependencyCheckingMode.cs b/src/Spring/Spring.Core/Objects/Factory/Support/DependencyCheckingMode.cs new file mode 100644 index 00000000..2a9acdc9 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/DependencyCheckingMode.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +using System; + +namespace Spring.Objects.Factory.Support { + + /// + /// The various modes of dependency checking. + /// + /// Rick Evans (.NET) + [Serializable] + public enum DependencyCheckingMode + { + /// + /// DO not do any dependency checking. + /// + None = 0, + + /// + /// Check object references. + /// + Objects = 1, + + /// + /// Just check primitive (string, int, etc) values. + /// + Simple = 2, + + /// + /// Check everything. + /// + All = 3 + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/IConfigurableObjectDefinition.cs b/src/Spring/Spring.Core/Objects/Factory/Support/IConfigurableObjectDefinition.cs new file mode 100644 index 00000000..5afc8051 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/IConfigurableObjectDefinition.cs @@ -0,0 +1,197 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Describes a configurable object instance, which has property values, + /// constructor argument values, and further information supplied by concrete + /// implementations. + /// + /// Rick Evans + /// $Id: IConfigurableObjectDefinition.cs,v 1.1 2007/07/30 17:52:22 markpollack Exp $ + public interface IConfigurableObjectDefinition : IObjectDefinition + { + /// + /// Return the property values to be applied to a new instance of the object. + /// + new MutablePropertyValues PropertyValues { get; set; } + + /// + /// Return the constructor argument values for this object. + /// + new ConstructorArgumentValues ConstructorArgumentValues { get; set; } + + /// + /// The method overrides (if any) for this object. + /// + /// + /// The method overrides (if any) for this object; may be an + /// empty collection but is guaranteed not to be + /// . + /// + MethodOverrides MethodOverrides { get; set; } + + /// + /// Return the event handlers for any events exposed by this object. + /// + new EventValues EventHandlerValues { get; set; } + + /// + /// Return a description of the resource that this object definition + /// came from (for the purpose of showing context in case of errors). + /// + new string ResourceDescription { get; set; } + + /// + /// Is this object definition "abstract", i.e. not meant to be instantiated + /// itself but rather just serving as parent for concrete child object + /// definitions. + /// + /// + /// if this object definition is "abstract". + /// + new bool IsAbstract { get; set; } + + /// + /// Returns the of the object definition (if any). + /// + /// + /// A resolved object . + /// + /// + /// If the of the object definition is not a + /// resolved or . + /// + new Type ObjectType { get; set; } + + /// + /// Returns the of the + /// of the object definition (if any). + /// + new string ObjectTypeName { get; set; } + + /// + /// Return whether this a Singleton, with a single, shared instance + /// returned on all calls. + /// + /// + ///

    + /// If , an object factory will apply the Prototype + /// design pattern, with each caller requesting an instance getting an + /// independent instance. How this is defined will depend on the + /// object factory implementation. Singletons are the commoner type. + ///

    + ///
    + new bool IsSingleton { get; set; } + + /// + /// Is this object lazily initialized? + /// + ///

    + /// Only applicable to a singleton object. + ///

    + ///

    + /// If , it will get instantiated on startup by object factories + /// that perform eager initialization of singletons. + ///

    + ///
    + new bool IsLazyInit { get; set; } + + /// + /// The autowire mode as specified in the object definition. + /// + /// + ///

    + /// This determines whether any automagical detection and setting of + /// object references will happen. Default is + /// , + /// which means there's no autowire. + ///

    + ///
    + new AutoWiringMode AutowireMode { get; set; } + + /// + /// The dependency check code. + /// + DependencyCheckingMode DependencyCheck { get; set; } + + /// + /// The object names that this object depends on. + /// + /// + ///

    + /// The object factory will guarantee that these objects get initialized + /// before. + ///

    + ///

    + /// Note that dependencies are normally expressed through object properties + /// or constructor arguments. This property should just be necessary for + /// other kinds of dependencies like statics (*ugh*) or database + /// preparation on startup. + ///

    + ///
    + new string[] DependsOn { get; set; } + + /// + /// The name of the initializer method. + /// + /// + ///

    + /// The default is , in which case there is no initializer method. + ///

    + ///
    + new string InitMethodName { get; set; } + + /// + /// Return the name of the destroy method. + /// + /// + ///

    + /// The default is , in which case there is no destroy method. + ///

    + ///
    + new string DestroyMethodName { get; set; } + + /// + /// The name of the factory method to use (if any). + /// + /// + ///

    + /// This method will be invoked with constructor arguments, or with no + /// arguments if none are specified. The static method will be invoked on + /// the specified . + ///

    + ///
    + new string FactoryMethodName { get; set; } + + /// + /// The name of the factory object to use (if any). + /// + new string FactoryObjectName { get; set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/IInstantiationStrategy.cs b/src/Spring/Spring.Core/Objects/Factory/Support/IInstantiationStrategy.cs new file mode 100644 index 00000000..756f02f9 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/IInstantiationStrategy.cs @@ -0,0 +1,119 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Reflection; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Responsible for creating instances corresponding to a + /// . + /// + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: IInstantiationStrategy.cs,v 1.6 2006/04/09 07:18:49 markpollack Exp $ + public interface IInstantiationStrategy + { + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied . + /// + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the null + /// or zero length string if we're autowiring an object that doesn't belong + /// to the supplied . + /// + /// + /// The owning + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + object Instantiate( + RootObjectDefinition definition, string name, IObjectFactory factory); + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied . + /// + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the null + /// or zero length string if we're autowiring an object that doesn't belong + /// to the supplied . + /// + /// + /// The owning + /// + /// + /// The to be used to instantiate + /// the object. + /// + /// + /// Any arguments to the supplied . May be null. + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + object Instantiate( + RootObjectDefinition definition, string name, IObjectFactory factory, + ConstructorInfo constructor, object[] arguments); + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied . + /// + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the null + /// or zero length string if we're autowiring an object that doesn't belong + /// to the supplied . + /// + /// + /// The owning + /// + /// + /// The to be used to get the object. + /// + /// + /// Any arguments to the supplied . May be null. + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + object Instantiate( + RootObjectDefinition definition, string name, IObjectFactory factory, + MethodInfo factoryMethod, object[] arguments); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/IManagedCollection.cs b/src/Spring/Spring.Core/Objects/Factory/Support/IManagedCollection.cs new file mode 100644 index 00000000..fe86b352 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/IManagedCollection.cs @@ -0,0 +1,165 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Denotes a special placeholder collection that may contain + /// s or + /// other placeholder objects that will need to be resolved. + /// + /// + ///

    + /// 'A special placeholder collection' means that the elements of this + /// collection can be placeholders for objects that will be resolved later by + /// a Spring.NET IoC container, i.e. the elements themselves will be + /// resolved at runtime by the enclosing IoC container. + ///

    + ///

    + /// The core Spring.NET library already provides three implementations of this interface + /// straight out of the box; they are... + ///

    + /// + /// + /// + /// . + /// + /// + /// + /// + /// . + /// + /// + /// + /// + /// . + /// + /// + /// + ///

    + /// If you have a custom collection class (i.e. a class that either implements the + /// directly or derives from a class that does) + /// that you would like to expose as a special placeholder collection (i.e. one that can + /// have s as elements + /// that will be resolved at runtime by an appropriate Spring.NET IoC container, just + /// implement this interface. + ///

    + ///
    + /// + ///

    + /// Lets say one has a Bag class (i.e. a collection that supports bag style semantics). + ///

    + /// + /// using System; + /// + /// using Spring.Objects.Factory.Support; + /// + /// namespace MyNamespace + /// { + /// public sealed class Bag : ICollection + /// { + /// // ICollection implementation elided for clarity... + /// + /// public void Add(object o) + /// { + /// // implementation elided for clarity... + /// } + /// } + /// + /// public class ManagedBag : Bag, IManagedCollection + /// { + /// public ICollection Resolve( + /// string objectName, RootObjectDefinition definition, + /// string propertyName, ManagedCollectionElementResolver resolver) + /// { + /// Bag newBag = new Bag(); + /// string elementName = propertyName + "[bag-element]"; + /// foreach(object element in this) + /// { + /// object resolvedElement = resolver(objectName, definition, elementName, element); + /// newBag.Add(resolvedElement); + /// } + /// return newBag; + /// } + /// } + /// } + /// + ///
    + /// Rick Evans + /// $Id: IManagedCollection.cs,v 1.5 2007/03/16 03:05:37 bbaia Exp $ + public interface IManagedCollection : ICollection + { + /// + /// Resolves this managed collection at runtime. + /// + /// + /// The name of the top level object that is having the value of one of it's + /// collection properties resolved. + /// + /// + /// The definition of the named top level object. + /// + /// + /// The name of the property the value of which is being resolved. + /// + /// + /// The callback that will actually do the donkey work of resolving + /// this managed collection. + /// + /// A fully resolved collection. + ICollection Resolve(string objectName, RootObjectDefinition definition, + string propertyName, ManagedCollectionElementResolver resolver); + } + + /// + /// Resolves a single element value of a managed collection. + /// + /// + ///

    + /// If the does not need to be resolved or + /// converted to an appropriate , the + /// will be returned as-is. + ///

    + ///
    + /// + /// The name of the top level object that is having the value of one of it's + /// collection properties resolved. + /// + /// + /// The definition of the named top level object. + /// + /// + /// The name of the property the value of which is being resolved. + /// + /// + /// That element of a managed collection that may need to be resolved + /// to a concrete value. + /// + /// A fully resolved element. + public delegate object ManagedCollectionElementResolver( + string name, RootObjectDefinition definition, string argumentName, object element); +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/IMethodReplacer.cs b/src/Spring/Spring.Core/Objects/Factory/Support/IMethodReplacer.cs new file mode 100644 index 00000000..e61a5634 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/IMethodReplacer.cs @@ -0,0 +1,71 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Permits the (re)implementation of an arbitrary method on a Spring.NET + /// IoC container managed object. + /// + /// + ///

    + /// Encapsulates the notion of the Method-Injection form of Dependency + /// Injection. + ///

    + ///

    + /// Methods that are dependency injected with implementations of this + /// interface may be (but need not be) , in which + /// case the container will create a concrete subclass of the + /// class prior to instantiation. + ///

    + ///

    + /// Do not use this mechanism as a means of AOP. See the reference + /// manual for examples of appropriate usages of this interface. + ///

    + ///
    + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: IMethodReplacer.cs,v 1.2 2006/04/09 07:18:49 markpollack Exp $ + public interface IMethodReplacer + { + /// + /// Reimplement the supplied . + /// + /// + /// The instance whose is to be + /// (re)implemented. + /// + /// + /// The method that is to be (re)implemented. + /// + /// The target method's arguments. + /// + /// The result of the (re)implementation of the method call. + /// + object Implement(object target, MethodInfo method, object[] arguments); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionReader.cs b/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionReader.cs new file mode 100644 index 00000000..4295649f --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionReader.cs @@ -0,0 +1,127 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Core.IO; + +#endregion + +namespace Spring.Objects.Factory.Support { + + /// + /// Simple interface for object definition readers. + /// + /// Juergen Hoeller + /// Rick Evans + /// $Id: IObjectDefinitionReader.cs,v 1.8 2007/08/08 17:47:13 bbaia Exp $ + public interface IObjectDefinitionReader + { + /// + /// Gets the + /// + /// instance that this reader works on. + /// + IObjectDefinitionRegistry Registry + { + get; + } + + /// + /// The against which any class names + /// will be resolved into instances. + /// + AppDomain Domain + { + get; + } + + /// + /// The to use for anonymous + /// objects (wihtout explicit object name specified). + /// + IObjectNameGenerator ObjectNameGenerator + { + get; + } + + /// + /// Gets the resource loader to use for resource locations. + /// + /// There is also a method + /// available for loading object definitions from a resource location. This is + /// a convenience to avoid explicit ResourceLoader handling. + /// The resource loader. + IResourceLoader ResourceLoader + { + get; + } + + /// + /// Load object definitions from the supplied . + /// + /// + /// The resource for the object definitions that are to be loaded. + /// + /// + /// The number of object definitions found + /// + /// + /// In the case of loading or parsing errors. + /// + int LoadObjectDefinitions (IResource resource); + + /// + /// Load object definitions from the supplied . + /// + /// + /// The resources for the object definitions that are to be loaded. + /// + /// + /// The number of object definitions found + /// + /// + /// In the case of loading or parsing errors. + /// + int LoadObjectDefinitions(IResource[] resources); + + + /// + /// Loads the object definitions from the specified resource location. + /// + /// The resource location, to be loaded with the + /// IResourceLoader location . + /// + /// The number of object definitions found + /// + int LoadObjectDefinitions(string location); + + /// + /// Loads the object definitions from the specified resource locations. + /// + /// The the resource locations to be loaded with the + /// IResourceLoader of this object definition reader. + /// + /// The number of object definitions found + /// + int LoadObjectDefinitions(string[] locations); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistry.cs b/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistry.cs new file mode 100644 index 00000000..24098cfc --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistry.cs @@ -0,0 +1,161 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Interface for registries that hold object definitions, i.e. + /// + /// and + /// + /// instances. + /// + /// + ///

    + /// Typically implemented by object factories that work with the + /// + /// hierarchy internally. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + public interface IObjectDefinitionRegistry + { + /// + /// Return the number of objects defined in the registry. + /// + /// + /// The number of objects defined in the registry. + /// + int ObjectDefinitionCount + { + get; + } + + /// + /// Return the names of all objects defined in this registry. + /// + /// + /// The names of all objects defined in this registry, or an empty array + /// if none defined + /// + string [] GetObjectDefinitionNames (); + + /// + /// Check if this registry contains a object definition with the given name. + /// + /// + /// The name of the object to look for. + /// + /// + /// True if this object factory contains an object definition with the + /// given name. + /// + bool ContainsObjectDefinition (string name); + + /// + /// Returns the + /// + /// for the given object name. + /// + /// + /// The name of the object to find a definition for. + /// + /// + /// The for + /// the given name (never null). + /// + /// + /// If the object definition cannot be resolved. + /// + /// + /// In case of errors. + /// + IObjectDefinition GetObjectDefinition (string name); + + /// + /// Register a new object definition with this registry. + /// Must support + /// + /// and . + /// + /// + /// The name of the object instance to register. + /// + /// + /// The definition of the object instance to register. + /// + /// + ///

    + /// Must support + /// and + /// . + ///

    + ///
    + /// + /// If the object definition is invalid. + /// + void RegisterObjectDefinition (string name, IObjectDefinition definition); + + /// + /// Return the aliases for the given object name, if defined. + /// + /// the object name to check for aliases + /// + /// + ///

    + /// Will ask the parent factory if the object cannot be found in this + /// factory instance. + ///

    + ///
    + /// + /// The aliases, or an empty array if none. + /// + /// + /// If there's no such object definition. + /// + string [] GetAliases (string name); + + /// + /// Given a object name, create an alias. We typically use this method to + /// support names that are illegal within XML ids (used for object names). + /// + /// + /// The name of the object. + /// + /// + /// The alias that will behave the same as the object name. + /// + /// + /// If there is no object with the given name. + /// + /// + /// If the alias is already in use. + /// + void RegisterAlias (string name, string theAlias); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/IObjectNameGenerator.cs b/src/Spring/Spring.Core/Objects/Factory/Support/IObjectNameGenerator.cs new file mode 100644 index 00000000..5ac7b471 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/IObjectNameGenerator.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using Spring.Objects.Factory.Config; + +namespace Spring.Objects.Factory.Support +{ + + /// + /// Strategy interface for generating object names for object definitions + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: IObjectNameGenerator.cs,v 1.1 2007/05/23 20:16:13 markpollack Exp $ + public interface IObjectNameGenerator + { + /// + /// Generates an object name for the given object definition. + /// + /// The object definition to generate a name for. + /// The object definitions registry that the given definition is + /// supposed to be registerd with + /// the generated object name + string GenerateObjectName(IObjectDefinition definition, IObjectDefinitionRegistry registry); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/LookupMethodOverride.cs b/src/Spring/Spring.Core/Objects/Factory/Support/LookupMethodOverride.cs new file mode 100644 index 00000000..f1662ea8 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/LookupMethodOverride.cs @@ -0,0 +1,116 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Text; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Represents an override of a method that looks up an object in the same IoC context. + /// + /// + ///

    + /// Methods eligible for lookup override must not have arguments. + ///

    + ///
    + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: LookupMethodOverride.cs,v 1.3 2007/03/16 04:01:41 aseovic Exp $ + [Serializable] + public sealed class LookupMethodOverride : MethodOverride + { + private readonly string objectName; + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// Methods eligible for lookup override must not have arguments. + ///

    + ///
    + /// + /// The name of the method that is to be overridden. + /// + /// + /// The name of the object in the current IoC context that the + /// dependency injected method must return. + /// + /// + /// If either of the supplied arguments is or + /// contains only whitespace character(s). + /// + public LookupMethodOverride(string methodName, string objectName) + : base(methodName) + { + AssertUtils.ArgumentHasText(objectName, "objectName"); + this.objectName = objectName; + } + + /// + /// The name of the object in the current IoC context that the + /// dependency injected method must return. + /// + public string ObjectName + { + get { return objectName; } + } + + /// + /// Does this + /// match the supplied ? + /// + /// The method to be checked. + /// + /// if this override matches the supplied . + /// + /// + /// If the supplied is . + /// + public override bool Matches(MethodInfo method) + { + AssertUtils.ArgumentNotNull(method, "method"); + return MethodName == method.Name || method.Name.EndsWith(MethodName); + } + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + return new StringBuilder(GetType().Name).Append(" for method '") + .Append(MethodName).Append("'; will return object '").Append(this.objectName) + .Append("'.").ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/LookupMethodReplacer.cs b/src/Spring/Spring.Core/Objects/Factory/Support/LookupMethodReplacer.cs new file mode 100644 index 00000000..f17e677f --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/LookupMethodReplacer.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// An + /// implementation that simply returns the result of a lookup in an + /// associated IoC container. + /// + /// + ///

    + /// This class is Spring.NET's implementation of Dependency Lookup via + /// Method Injection. + ///

    + ///

    + /// This class is reserved for internal use within the framework; it is + /// not intended to be used by application developers using Spring.NET. + ///

    + ///
    + /// Rick Evans + /// $Id: LookupMethodReplacer.cs,v 1.3 2007/07/30 17:52:22 markpollack Exp $ + public sealed class LookupMethodReplacer : AbstractMethodReplacer + { + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The object definition that is the target of the method replacement. + /// + /// + /// The enclosing IoC container with which the above + /// is associated. + /// + /// + /// If either of the supplied arguments is . + /// + public LookupMethodReplacer(IConfigurableObjectDefinition objectDefinition, IObjectFactory objectFactory) + : base(objectDefinition, objectFactory) + { + } + + /// + /// Reimplements the supplied by returning the + /// result of an object lookup in an enclosing IoC container. + /// + /// + /// The instance whose is to be + /// (re)implemented. + /// + /// + /// The method that is to be (re)implemented. + /// + /// The target method's arguments. + /// + /// The result of the object lookup. + /// + public override object Implement(object target, MethodInfo method, object[] arguments) + { + return GetObject(((LookupMethodOverride) GetOverride(method)).ObjectName); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/ManagedDictionary.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ManagedDictionary.cs new file mode 100644 index 00000000..017e1e8a --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/ManagedDictionary.cs @@ -0,0 +1,171 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +#if NET_2_0 +using System.Collections.Generic; +#endif +using System.Collections.Specialized; +using System.Globalization; + +using Spring.Core; +using Spring.Core.TypeConversion; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Tag subclass used to hold a dictionary of managed elements. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: ManagedDictionary.cs,v 1.14 2007/11/26 14:15:54 bbaia Exp $ + [Serializable] + public class ManagedDictionary : Hashtable, IManagedCollection + { + private string keyTypeName; + private string valueTypeName; + + /// + /// Gets or sets the unresolved name for the + /// of the keys of this managed dictionary. + /// + /// The unresolved name for the type of the keys of this managed dictionary. + public string KeyTypeName + { + get { return this.keyTypeName; } + set { this.keyTypeName = value; } + } + + /// + /// Gets or sets the unresolved name for the + /// of the values of this managed dictionary. + /// + /// The unresolved name for the type of the values of this managed dictionary. + public string ValueTypeName + { + get { return this.valueTypeName; } + set { this.valueTypeName = value; } + } + + /// + /// Resolves this managed collection at runtime. + /// + /// + /// The name of the top level object that is having the value of one of it's + /// collection properties resolved. + /// + /// + /// The definition of the named top level object. + /// + /// + /// The name of the property the value of which is being resolved. + /// + /// + /// The callback that will actually do the donkey work of resolving + /// this managed collection. + /// + /// A fully resolved collection. + public ICollection Resolve( + string objectName, RootObjectDefinition definition, + string propertyName, ManagedCollectionElementResolver resolver) + { + IDictionary dictionary; + + Type keyType = null; + if (StringUtils.HasText(this.keyTypeName)) + { + keyType = TypeResolutionUtils.ResolveType(this.keyTypeName); + } + + Type valueType = null; + if (StringUtils.HasText(this.valueTypeName)) + { + valueType = TypeResolutionUtils.ResolveType(this.valueTypeName); + } +#if NET_2_0 + if ((keyType == null) && (valueType == null)) + { + dictionary = new HybridDictionary(); + } + else + { + Type type = typeof(Dictionary<,>); + Type[] genericArgs = new Type[2] { + (keyType == null) ? typeof(object) : keyType, + (valueType == null) ? typeof(object) : valueType }; + type = type.MakeGenericType(genericArgs); + + dictionary = (IDictionary)ObjectUtils.InstantiateType(type); + } +#else + dictionary = new HybridDictionary(); +#endif + foreach (object key in this.Keys) + { + string elementName = string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", propertyName, key); + object resolvedKey = resolver(objectName, definition, elementName, key); + object resolvedValue = resolver(objectName, definition, elementName, this[key]); + + if (keyType != null) + { + try + { + resolvedKey = TypeConversionUtils.ConvertValueIfNecessary(keyType, resolvedKey, propertyName); + } + catch (TypeMismatchException) + { + throw new TypeMismatchException( + String.Format( + "Unable to convert managed dictionary key '{0}' from [{1}] into [{2}] during initialization" + + " of property '{3}' for object '{4}'. Do you have an appropriate type converter registered?", + resolvedKey, resolvedKey.GetType(), keyType, propertyName, objectName)); + } + } + + if (valueType != null) + { + try + { + resolvedValue = TypeConversionUtils.ConvertValueIfNecessary(valueType, resolvedValue, propertyName + "[" + resolvedKey + "]"); + } + catch (TypeMismatchException) + { + throw new TypeMismatchException( + String.Format( + "Unable to convert managed dictionary value '{0}' from [{1}] into [{2}] during initialization" + + " of property '{3}' for object '{4}'. Do you have an appropriate type converter registered?", + resolvedValue, resolvedValue.GetType(), valueType, propertyName, objectName)); + } + } + + dictionary.Add(resolvedKey, resolvedValue); + } + + return dictionary; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/ManagedList.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ManagedList.cs new file mode 100644 index 00000000..2315f496 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/ManagedList.cs @@ -0,0 +1,134 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +#if NET_2_0 +using System.Collections.Generic; +#endif +using System.Globalization; + +using Spring.Core; +using Spring.Core.TypeConversion; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Tag subclass used to hold a list of managed elements. + /// + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: ManagedList.cs,v 1.15 2007/11/26 14:15:54 bbaia Exp $ + [Serializable] + public class ManagedList : ArrayList, IManagedCollection + { + private string elementTypeName; + + /// + /// Gets or sets the unresolved name for the + /// of the elements of this managed list. + /// + /// The unresolved name for the type of the elements of this managed list. + public string ElementTypeName + { + get { return this.elementTypeName; } + set { this.elementTypeName = value; } + } + + /// + /// Resolves this managed collection at runtime. + /// + /// + /// The name of the top level object that is having the value of one of it's + /// collection properties resolved. + /// + /// + /// The definition of the named top level object. + /// + /// + /// The name of the property the value of which is being resolved. + /// + /// + /// The callback that will actually do the donkey work of resolving + /// this managed collection. + /// + /// A fully resolved collection. + public ICollection Resolve(string objectName, RootObjectDefinition definition, string propertyName, ManagedCollectionElementResolver resolver) + { + IList list; + + Type elementType = null; + if (StringUtils.HasText(this.elementTypeName)) + { + elementType = TypeResolutionUtils.ResolveType(this.elementTypeName); + } +#if NET_2_0 + if (elementType == null) + { + list = new ArrayList(); + } + else + { + // CLOVER:ON + Type type = typeof(List<>); + Type[] genericArgs = new Type[1] { elementType }; + type = type.MakeGenericType(genericArgs); + + list = (IList)ObjectUtils.InstantiateType(type); + // CLOVER:OFF + } +#else + list = new ArrayList(); +#endif + for (int i = 0; i < Count; ++i) + { + object element = this[i]; + object resolvedElement = + resolver(objectName, definition, String.Format(CultureInfo.InvariantCulture, "{0}[{1}]", propertyName, i), element); + + if (elementType != null) + { + try + { + resolvedElement = TypeConversionUtils.ConvertValueIfNecessary(elementType, resolvedElement, propertyName + "[" + i + "]"); + } + catch (TypeMismatchException) + { + throw new TypeMismatchException( + String.Format( + "Unable to convert managed list element '{0}' from [{1}] into [{2}] during initialization" + + " of property '{3}' for object '{4}'. Do you have an appropriate type converter registered?", + resolvedElement, resolvedElement.GetType(), elementType, propertyName, objectName)); + } + } + + list.Add(resolvedElement); + } + + return list; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/ManagedSet.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ManagedSet.cs new file mode 100644 index 00000000..db493212 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/ManagedSet.cs @@ -0,0 +1,112 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using Spring.Collections; +using Spring.Core; +using Spring.Core.TypeConversion; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Tag subclass used to hold a set of managed elements. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + [Serializable] + public class ManagedSet : HybridSet, IManagedCollection + { + private string elementTypeName; + + /// + /// Gets or sets the unresolved name for the + /// of the elements of this managed set. + /// + /// The unresolved name for the type of the elements of this managed set. + public string ElementTypeName + { + get { return this.elementTypeName; } + set { this.elementTypeName = value; } + } + + /// + /// Resolves this managed collection at runtime. + /// + /// + /// The name of the top level object that is having the value of one of it's + /// collection properties resolved. + /// + /// + /// The definition of the named top level object. + /// + /// + /// The name of the property the value of which is being resolved. + /// + /// + /// The callback that will actually do the donkey work of resolving + /// this managed collection. + /// + /// A fully resolved collection. + public ICollection Resolve(string objectName, RootObjectDefinition definition, string propertyName, ManagedCollectionElementResolver resolver) + { + ISet set = new HybridSet(); + + Type elementType = null; + if (StringUtils.HasText(this.elementTypeName)) + { + elementType = TypeResolutionUtils.ResolveType(this.elementTypeName); + } + + string elementName = propertyName + "[(set-element)]"; + foreach (object element in this) + { + object resolvedElement = resolver(objectName, definition, elementName, element); + + if (elementType != null) + { + try + { + resolvedElement = TypeConversionUtils.ConvertValueIfNecessary(elementType, resolvedElement, propertyName); + } + catch (TypeMismatchException) + { + throw new TypeMismatchException( + String.Format( + "Unable to convert managed set element '{0}' from [{1}] into [{2}] during initialization" + + " of property '{3}' for object '{4}'. Do you have an appropriate type converter registered?", + resolvedElement, resolvedElement.GetType(), elementType, propertyName, objectName)); + } + } + + set.Add(resolvedElement); + } + + return set; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/MethodInjectingInstantiationStrategy.cs b/src/Spring/Spring.Core/Objects/Factory/Support/MethodInjectingInstantiationStrategy.cs new file mode 100644 index 00000000..0b2a43a5 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/MethodInjectingInstantiationStrategy.cs @@ -0,0 +1,617 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using Spring.Collections; +using Spring.Core; +using Spring.Util; +using Spring.Reflection.Dynamic; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// An + /// implementation that supports method injection. + /// + /// + ///

    + /// Classes that want to take advantage of method injection must meet some + /// stringent criteria. Every method that is to be method injected + /// must be defined as either or + /// . An + /// will be thrown if these criteria are not met. + ///

    + ///
    + /// Rick Evans + /// $Id: MethodInjectingInstantiationStrategy.cs,v 1.13 2008/05/02 16:44:44 markpollack Exp $ + [Serializable] + public class MethodInjectingInstantiationStrategy : SimpleInstantiationStrategy + { + /// + /// The name of the dynamic assembly that holds dynamically created code + /// + private const string DYNAMIC_ASSEMBLY_NAME = "Spring.MethodInjected"; + + /// + /// A cache of generated instances, keyed on + /// the object name for which the was generated. + /// + private IDictionary typeCache = new Hashtable(); + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied , + /// injecting methods as appropriate. + /// + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the + /// or zero length string if we're autowiring an + /// object that doesn't belong to the supplied + /// . + /// + /// + /// The owning + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + /// + protected override object InstantiateWithMethodInjection( + RootObjectDefinition definition, string objectName, IObjectFactory factory) + { + return DoInstantiate(definition, objectName, factory, Type.EmptyTypes, ObjectUtils.EmptyObjects); + } + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied , + /// injecting methods as appropriate. + /// + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the + /// or zero length string if we're autowiring an + /// object that doesn't belong to the supplied + /// . + /// + /// + /// The owning + /// + /// + /// The to be used to instantiate + /// the object. + /// + /// + /// Any arguments to the supplied . May be null. + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + /// + protected override object InstantiateWithMethodInjection( + RootObjectDefinition definition, string objectName, IObjectFactory factory, ConstructorInfo constructor, object[] arguments) + { + return DoInstantiate(definition, objectName, factory, ReflectionUtils.GetParameterTypes(constructor), arguments); + } + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied , + /// injecting methods as appropriate. + /// + /// + ///

    + /// This method dynamically generates a subclass that supports method + /// injection for the supplied . It then + /// instantiates an new instance of said type using the constructor + /// identified by the supplied , + /// passing the supplied to said + /// constructor. It then manually injects (generic) method replacement + /// and method lookup instances (of + /// ) into + /// the new instance: those methods that are 'method-injected' will + /// then delegate to the approriate + /// + /// instance to effect the actual method injection. + ///

    + ///
    + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the + /// or zero length string if we're autowiring an + /// object that doesn't belong to the supplied + /// . + /// + /// + /// The owning + /// + /// + /// The parameter s to use to find the + /// appropriate constructor to invoke. + /// + /// + /// The aguments that are to be passed to the appropriate constructor + /// when the object is being instantiated. + /// + /// + /// A new instance of the defined by the + /// supplied . + /// + private object DoInstantiate( + RootObjectDefinition definition, string objectName, IObjectFactory factory, Type[] ctorParameterTypes, object[] arguments) + { + Type type = GetGeneratedType(objectName, definition); + object instance = type.GetConstructor(ctorParameterTypes).Invoke(arguments); + IObjectWrapper wrapper = new ObjectWrapper(instance); + wrapper.SetPropertyValue( + MethodInjectingTypeBuilder.MethodReplacementPropertyName, + new DelegatingMethodReplacer(definition, factory)); + wrapper.SetPropertyValue( + MethodInjectingTypeBuilder.MethodLookupPropertyName, + new LookupMethodReplacer(definition, factory)); + return instance; + } + + private Type GetGeneratedType(string objectName, RootObjectDefinition definition) + { + lock (typeCache.SyncRoot) + { + Type generatedType = (Type) typeCache[objectName]; + if (generatedType == null) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format(CultureInfo.InvariantCulture, + "Generating a subclass of the [{0}] class for the '{1}' " + + "object definition for the purposes of method injection.", + definition.ObjectType, objectName)); + } + + #endregion + + ModuleBuilder module = DynamicCodeManager.GetModuleBuilder(DYNAMIC_ASSEMBLY_NAME); + generatedType = new MethodInjectingTypeBuilder(module, definition).BuildType(); + typeCache[objectName] = generatedType; + } + return generatedType; + } + } + + #region Inner Class : MethodInjectingTypeBuilder + + /// + /// A factory that generates subclasses of those + /// classes that have been configured for the Method-Injection form of + /// Dependency Injection. + /// + /// + ///

    + /// This class is designed as for one-shot usage; i.e. it must + /// be used to generate exactly one method injected subclass and + /// then discarded (it maintains state in instance fields). + ///

    + ///
    + private sealed class MethodInjectingTypeBuilder + { + /// + /// The name of the generated + /// property (for method replacement). + /// + /// + ///

    + /// Exists so that clients of this class can use this name to set properties reflectively + /// on the dynamically generated subclass. + ///

    + ///
    + internal const string MethodReplacementPropertyName = "MethodReplacement"; + + /// + /// The name of the generated + /// property (for method lookup). + /// + /// + ///

    + /// Exists so that clients of this class can use this name to set properties reflectively + /// on the dynamically generated subclass. + ///

    + ///
    + internal const string MethodLookupPropertyName = "MethodLookup"; + + private RootObjectDefinition objectDefinition; + private FieldBuilder methodReplacementField; + private FieldBuilder methodLookupField; + private ModuleBuilder module; + + private readonly MethodInfo MethodReplacerImplementMethod + = typeof (IMethodReplacer).GetMethod("Implement", new Type[] {typeof (object), typeof (MethodInfo), typeof (object[])}); + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The in which + /// the generated is to be defined. + /// + /// + /// The object definition that is the target of the method injection. + /// + /// + /// If either of the supplied arguments is . + /// + public MethodInjectingTypeBuilder(ModuleBuilder module, RootObjectDefinition objectDefinition) + { + AssertUtils.ArgumentNotNull(module, "module"); + AssertUtils.ArgumentNotNull(objectDefinition, "objectDefinition"); + this.module = module; + this.objectDefinition = objectDefinition; + } + + /// + /// Builds a suitable for Method-Injection. + /// + /// + /// A suitable for Method-Injection. + /// + public Type BuildType() + { + TypeBuilder typeBuilder = DefineType(); + DefineFields(typeBuilder); + DefineConstructors(typeBuilder); + DefineProperties(typeBuilder); + DefineMethods(typeBuilder); + return typeBuilder.CreateType(); + } + + private Type BaseType + { + get { return this.objectDefinition.ObjectType; } + } + + private TypeBuilder DefineProperties(TypeBuilder typeBuilder) + { + DefineWritePropertyForMethodReplacement(typeBuilder, MethodReplacementPropertyName, this.methodReplacementField); + DefineWritePropertyForMethodReplacement(typeBuilder, MethodLookupPropertyName, this.methodLookupField); + return typeBuilder; + } + + private TypeBuilder DefineType() + { + // Generates unique type name + string generatedSubclassName = String.Format("{0}_{1}", + BaseType.FullName, Guid.NewGuid().ToString("N")); + return this.module.DefineType( + generatedSubclassName, TypeAttributes.BeforeFieldInit | TypeAttributes.Public, BaseType); + } + + private TypeBuilder DefineFields(TypeBuilder typeBuilder) + { + methodReplacementField = typeBuilder.DefineField("methodReplacement", typeof (IMethodReplacer), FieldAttributes.Private); + methodLookupField = typeBuilder.DefineField("methodLookup", typeof (IMethodReplacer), FieldAttributes.Private); + return typeBuilder; + } + + private TypeBuilder DefineConstructors(TypeBuilder typeBuilder) + { + ConstructorInfo[] constructors = BaseType.GetConstructors( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + for (int i = 0; i < constructors.Length; ++i) + { + ConstructorInfo constructor = constructors[i]; + if (constructor.IsPublic || constructor.IsFamily) + { + MethodAttributes attributes = MethodAttributes.Public | + MethodAttributes.HideBySig | MethodAttributes.SpecialName | + MethodAttributes.RTSpecialName; + ConstructorBuilder cb = typeBuilder.DefineConstructor(attributes, + constructor.CallingConvention, + ReflectionUtils.GetParameterTypes(constructor.GetParameters())); + ILGenerator il = cb.GetILGenerator(); + int paramCount = constructor.GetParameters().Length; + il.Emit(OpCodes.Ldarg_0); + for (int j = 1; j <= paramCount; ++j) + { + il.Emit(OpCodes.Ldarg_S, j); + } + il.Emit(OpCodes.Call, constructor); + il.Emit(OpCodes.Ret); + } + } + return typeBuilder; + } + + /// + /// Defines overrides for those methods that are configured with an appropriate + /// . + /// + /// + /// The overarching that is defining + /// the generated . + /// + private TypeBuilder DefineMethods(TypeBuilder typeBuilder) + { + MethodInfo[] methods = BaseType.GetMethods( + BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); + for (int i = 0; i < methods.Length; ++i) + { + MethodInfo method = methods[i]; + MethodOverride methodOverride + = this.objectDefinition.MethodOverrides.GetOverride(method); + if (methodOverride != null) + { + if (!method.IsVirtual || method.IsFinal) + { + throw new ObjectCreationException( + "A replaced method must be marked as either abstract or virtual."); + } + FieldBuilder field = null; + if (methodOverride is ReplacedMethodOverride) + { + field = this.methodReplacementField; + } + else + { + // lookup methods cannot have any arguments... + if (method.GetParameters().Length > 0) + { + throw new ObjectCreationException( + "The signature of a lookup method cannot have any arguments."); + } + // lookup methods cannot return void... + if (method.ReturnType == typeof (void)) + { + throw new ObjectCreationException( + "A lookup method cannot be declared with a void return type."); + } + field = this.methodLookupField; + } + DefineReplacedMethod(typeBuilder, method, field); + } + } + return typeBuilder; + } + + /// + /// Override the supplied with the logic + /// encapsulated by the + /// + /// defined by the supplied . + /// + /// + /// The builder for the subclass that is being generated. + /// + /// + /// The method on the superclass that is to be overridden. + /// + /// + /// The field defining the + /// + /// that the overridden method will delegate to to do the 'actual' + /// method injection logic. + /// + private void DefineReplacedMethod(TypeBuilder typeBuilder, MethodInfo method, FieldBuilder field) + { + ParameterInfo[] methodParameters = method.GetParameters(); + MethodBuilder methodBuilder + = typeBuilder.DefineMethod(method.Name, + CalculateMethodAttributes(method), + method.CallingConvention, + method.ReturnType, + ReflectionUtils.GetParameterTypes(methodParameters)); + DefineOverrideMethodParameters(methodParameters, methodBuilder); + ILGenerator il = methodBuilder.GetILGenerator(); + LocalBuilder returnValue = DefineReturnValueIfAny(method, il); + // prepare the invocation of the 'Implement' method for the 'field' (an IMethodReplacer)... + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, field); + PushArguments(methodParameters, il); + // invoke the 'Implement' method of the IMethodReplacer in the 'field'... + il.Emit(OpCodes.Callvirt, MethodReplacerImplementMethod); + SetupTheReturnValueIfAny(returnValue, il); + il.Emit(OpCodes.Ret); + } + + /// + /// Defines the parameters to the method that is being overridden. + /// + /// + ///

    + /// Since we are simply overridding a method (in this method + /// injection context), all we do here is simply copy the + /// parameters (since we want a method with the exact same parameters). + ///

    + ///
    + /// + /// The parameters to the original method that is being overridden. + /// + /// + /// The builder we are using to define the new overridden method. + /// + private static void DefineOverrideMethodParameters( + ParameterInfo[] methodParameters, MethodBuilder methodBuilder) + { + for (int i = 0; i < methodParameters.Length; ++i) + { + ParameterInfo parameter = methodParameters[i]; + methodBuilder.DefineParameter(i + 1, parameter.Attributes, parameter.Name); + } + } + + /// + /// Generates the MSIL for actually returning a return value if the + /// supplied is not + /// . + /// + /// + /// The definition of the return value; if , it + /// means that no return value is to required (a void + /// return type). + /// + /// + /// The to emit + /// the MSIL to. + /// + private static void SetupTheReturnValueIfAny(LocalBuilder returnValue, ILGenerator il) + { + if (returnValue != null) + { + il.Emit(OpCodes.Castclass, returnValue.LocalType); + il.Emit(OpCodes.Stloc, returnValue); + il.Emit(OpCodes.Ldloc, returnValue); + } + else + { + il.Emit(OpCodes.Pop); + } + } + + /// + /// Generates the MSIL for a return value if the supplied + /// returns a value. + /// + /// + /// The method to be checked. + /// + /// + /// The to emit + /// the MSIL to. + /// + /// + /// The return value, or if the method does not + /// return a value (has a void return type). + /// + private static LocalBuilder DefineReturnValueIfAny(MethodInfo method, ILGenerator il) + { + LocalBuilder returnValue = null; + if (method.ReturnType != typeof (void)) + { + returnValue = il.DeclareLocal(method.ReturnType); + } + return returnValue; + } + + /// + /// Pushes (sets up) the arguments for a call to the + /// + /// method of an appropriate + /// . + /// + /// + /// The parameters to the original method (will be bundled + /// up into a generic object[] and passed as the third + /// argument to the + /// + /// invocation. + /// + /// + /// The to emit + /// the MSIL to. + /// + private static void PushArguments(ParameterInfo[] methodParameters, ILGenerator il) + { + // push 'this' (1st arg)... + il.Emit(OpCodes.Ldarg_0); + // push the currently executing method (2nd arg)... + il.Emit(OpCodes.Call, typeof (MethodBase).GetMethod("GetCurrentMethod", BindingFlags.Static | BindingFlags.Public)); + il.Emit(OpCodes.Castclass, typeof (MethodInfo)); + // push the arguments to the currently executing method as an object [] (3rd arg)... + il.Emit(OpCodes.Ldc_I4, methodParameters.Length); + LocalBuilder args = il.DeclareLocal(typeof (object[])); + il.Emit(OpCodes.Newarr, typeof (object)); + il.Emit(OpCodes.Stloc, args); + for (int i = 0; i < methodParameters.Length; ++i) + { + il.Emit(OpCodes.Ldloc, args); + il.Emit(OpCodes.Ldc_I4, i); + il.Emit(OpCodes.Ldarg_S, i + 1); + ParameterInfo parameter = methodParameters[i]; + if (parameter.ParameterType.IsEnum || parameter.ParameterType.IsValueType) + { + il.Emit(OpCodes.Box, parameter.ParameterType); + } + il.Emit(OpCodes.Stelem_Ref); + } + il.Emit(OpCodes.Ldloc, args); + } + + /// + /// Simply generates the IL for a write only property for the + /// . + /// + /// + /// The in which the property is defined. + /// + /// + /// The name of the (to be) generated property. + /// + /// + /// The (instance) field that the property is to 'set'. + /// + private void DefineWritePropertyForMethodReplacement( + TypeBuilder typeBuilder, string propertyName, FieldBuilder field) + { + MethodBuilder setMethodBuilder = typeBuilder.DefineMethod( + "set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName, + typeof (void), new Type[] {typeof (IMethodReplacer)}); + ILGenerator il = setMethodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Stfld, field); + il.Emit(OpCodes.Ret); + PropertyBuilder propertyBuilder = typeBuilder.DefineProperty( + propertyName, PropertyAttributes.None, typeof (IMethodReplacer), Type.EmptyTypes); + propertyBuilder.SetSetMethod(setMethodBuilder); + } + + private MethodAttributes CalculateMethodAttributes(MethodInfo method) + { + MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.ReuseSlot + | MethodAttributes.HideBySig | MethodAttributes.Virtual; + if (method.IsSpecialName) + { + return attributes | MethodAttributes.SpecialName; + } + return attributes; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/MethodOverride.cs b/src/Spring/Spring.Core/Objects/Factory/Support/MethodOverride.cs new file mode 100644 index 00000000..3710c8e5 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/MethodOverride.cs @@ -0,0 +1,123 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Represents the override of a method on a managed object by the IoC container. + /// + /// + ///

    + /// Note that the override mechanism is not intended as a generic means of + /// inserting crosscutting code: use AOP for that. + ///

    + ///
    + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: MethodOverride.cs,v 1.9 2007/08/22 08:52:03 markpollack Exp $ + [Serializable] + public abstract class MethodOverride + { + private readonly string methodName; + private bool isOverloaded = true; + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no + /// public constructors. + ///

    + ///
    + /// + /// The name of the method that is to be overridden. + /// + /// + /// If the supplied is or + /// contains only whitespace character(s). + /// + protected MethodOverride(string methodName) + { + AssertUtils.ArgumentHasText(methodName, "methodName"); + this.methodName = methodName.Trim(); + } + + /// + /// The name of the method that is to be overridden. + /// + public string MethodName + { + get { return methodName; } + } + + /// + /// Is the method that is ot be injected + /// () + /// to be considered as overloaded? + /// + /// + ///

    + /// If (the default), then argument type matching + /// will be performed (because one would not want to override the wrong + /// method). + ///

    + ///

    + /// Setting the value of this property to can be used + /// to optimize runtime performance (ever so slightly). + ///

    + ///
    + public bool IsOverloaded + { + get { return isOverloaded; } + set { isOverloaded = value; } + } + + /// + /// Does this + /// match the supplied ? + /// + /// + ///

    + /// By 'match' one means does this particular + /// + /// instance apply to the supplied ? + ///

    + ///

    + /// This allows for argument list checking as well as method name checking. + ///

    + ///
    + /// The method to be checked. + /// + /// if this override matches the supplied + /// . + /// + public abstract bool Matches(MethodInfo method); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/MethodOverrides.cs b/src/Spring/Spring.Core/Objects/Factory/Support/MethodOverrides.cs new file mode 100644 index 00000000..f177105e --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/MethodOverrides.cs @@ -0,0 +1,200 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using Spring.Collections; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// A collection (with set semantics) of method overrides, determining which, if any, + /// methods on a managed object the Spring.NET IoC container will override at runtime. + /// + /// Rod Johnson + /// Rick Evans + /// $Id: MethodOverrides.cs,v 1.8 2007/03/16 04:01:42 aseovic Exp $ + [Serializable] + public class MethodOverrides : IEnumerable + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public MethodOverrides() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// Deep copy constructoe. + ///

    + ///
    + /// + /// The instance supplying initial overrides for this new instance. + /// + public MethodOverrides(MethodOverrides other) + { + AddAll(other); + } + + #endregion + + #region Properties + + /// + /// The collection of method overrides. + /// + public ISet Overrides + { + get { return _overrides; } + } + + /// + /// Returns true if this instance contains no overrides. + /// + public bool IsEmpty + { + get { return Overrides.IsEmpty; } + } + + #endregion + + #region Methods + + /// + /// Copy all given method overrides into this object. + /// + /// + /// The overrides to be copied into this object. + /// + public void AddAll(MethodOverrides other) + { + if (other != null) + { + Overrides.AddAll(other.Overrides); + _overloadedMethodNames.AddAll(other._overloadedMethodNames); + } + } + + /// + /// Adds the supplied to the overrides contained + /// within this instance. + /// + /// + /// The to be + /// added. + /// + public void Add(MethodOverride theOverride) + { + Overrides.Add(theOverride); + } + + /// + /// Adds the supplied to the overloaded method names + /// contained within this instance. + /// + /// + /// The overloaded method name to be added. + /// + public void AddOverloadedMethodName(string methodName) + { + _overloadedMethodNames.Add(methodName); + } + + /// + /// Returns true if the supplied is present within + /// the overloaded method names contained within this instance. + /// + /// + /// The overloaded method name to be checked. + /// + /// + /// True if the supplied is present within + /// the overloaded method names contained within this instance. + /// + public bool IsOverloadedMethodName(string methodName) + { + return _overloadedMethodNames.Contains(methodName); + } + + /// + /// Return the override for the given method, if any. + /// + /// + /// The method to check for overrides for. + /// + /// + /// the override for the given method, if any. + /// + public MethodOverride GetOverride(MethodInfo method) + { + foreach (MethodOverride ovr in Overrides) + { + if (ovr.Matches(method)) + { + return ovr; + } + } + return null; + } + + /// + /// Returns an that can iterate + /// through a collection. + /// + /// + ///

    + /// The returned is the + /// exposed by the + /// + /// property. + ///

    + ///
    + /// + /// An that can iterate through a + /// collection. + /// + public IEnumerator GetEnumerator() + { + return Overrides.GetEnumerator(); + } + + #endregion + + #region Fields + + private ISet _overrides = new HybridSet(); + private ISet _overloadedMethodNames = new HybridSet(); + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionBuilder.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionBuilder.cs new file mode 100644 index 00000000..034e386a --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionBuilder.cs @@ -0,0 +1,374 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports +using System; +using System.Collections; +using Spring.Objects.Factory.Config; +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Programmatic means of constructing a using the builder pattern. Intended primarily + /// for use when implementing custom namespace parsers. + /// + /// Set methods are used instead of properties, so that chaining of methods can be used to create + /// 'one-liner'definitions that set multiple properties at one. + /// Rod Johnson + /// Rob Harrop + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: ObjectDefinitionBuilder.cs,v 1.4 2007/05/29 20:00:20 markpollack Exp $ + public class ObjectDefinitionBuilder + { + #region Fields + private AbstractObjectDefinition objectDefinition; + + private IObjectDefinitionFactory objectDefinitionFactory; + + private int constructorArgIndex; + + #endregion + + #region Constructor(s) + + /// + /// Initializes a new instance of the class, private + /// to force use of factory methods. + /// + private ObjectDefinitionBuilder() + { + } + + #endregion + + #region Factory Methods + + + /// + /// Create a new ObjectDefinitionBuilder used to construct a root object definition. + /// + /// The object definition factory. + /// The type name of the object. + /// A new ObjectDefinitionBuilder instance. + public static ObjectDefinitionBuilder RootObjectDefinition(IObjectDefinitionFactory objectDefinitionFactory, + string objectTypeName) + { + return RootObjectDefinition(objectDefinitionFactory, objectTypeName, null); + } + + /// + /// Create a new ObjectDefinitionBuilder used to construct a root object definition. + /// + /// The object definition factory. + /// Name of the object type. + /// Name of the factory method. + /// A new ObjectDefinitionBuilder instance. + public static ObjectDefinitionBuilder RootObjectDefinition(IObjectDefinitionFactory objectDefinitionFactory, + string objectTypeName, + string factoryMethodName) + { + ObjectDefinitionBuilder builder = new ObjectDefinitionBuilder(); + + builder.objectDefinitionFactory = objectDefinitionFactory; + + // Pass in null for parent name and also AppDomain to force object definition to be register by name and not type. + builder.objectDefinition = + objectDefinitionFactory.CreateObjectDefinition(objectTypeName, null, null); + + builder.objectDefinition.FactoryMethodName = factoryMethodName; + + return builder; + + } + + /// + /// Create a new ObjectDefinitionBuilder used to construct a root object definition. + /// + /// The object definition factory. + /// Type of the object. + /// A new ObjectDefinitionBuilder instance. + public static ObjectDefinitionBuilder RootObjectDefinition(IObjectDefinitionFactory objectDefinitionFactory, + Type objectType) + { + return RootObjectDefinition(objectDefinitionFactory, objectType, null); + } + + /// + /// Create a new ObjectDefinitionBuilder used to construct a root object definition. + /// + /// The object definition factory. + /// Type of the object. + /// Name of the factory method. + /// A new ObjectDefinitionBuilder instance. + public static ObjectDefinitionBuilder RootObjectDefinition(IObjectDefinitionFactory objectDefinitionFactory, + Type objectType, string factoryMethodName) + { + ObjectDefinitionBuilder builder = new ObjectDefinitionBuilder(); + + builder.objectDefinitionFactory = objectDefinitionFactory; + + builder.objectDefinition = + objectDefinitionFactory.CreateObjectDefinition(objectType.FullName, null, AppDomain.CurrentDomain); + + builder.objectDefinition.ObjectType = objectType; + builder.objectDefinition.FactoryMethodName = factoryMethodName; + return builder; + } + + /// + /// Create a new ObjectDefinitionBuilder used to construct a child object definition.. + /// + /// The object definition factory. + /// Name of the parent object. + /// + public static ObjectDefinitionBuilder ChildObjectDefinition(IObjectDefinitionFactory objectDefinitionFactory, + string parentObjectName) + { + ObjectDefinitionBuilder builder = new ObjectDefinitionBuilder(); + + builder.objectDefinitionFactory = objectDefinitionFactory; + + builder.objectDefinition = + objectDefinitionFactory.CreateObjectDefinition(null, parentObjectName, AppDomain.CurrentDomain); + + return builder; + } + + #endregion + + + #region Properties + + /// + /// Gets the current object definition in its raw (unvalidated) form. + /// + /// The raw object definition. + public AbstractObjectDefinition RawObjectDefinition + { + get { return objectDefinition; } + + } + + /// + /// Validate and gets the object definition. + /// + /// The object definition. + public AbstractObjectDefinition ObjectDefinition + { + get + { + objectDefinition.Validate(); + return objectDefinition; + } + } + + + #endregion + + #region Methods + //TODO add expression support. + + /// + /// Adds the property value under the given name. + /// + /// The name. + /// The value. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder AddPropertyValue(string name, object value) + { + objectDefinition.PropertyValues.Add(new PropertyValue(name, value)); + return this; + } + + /// + /// Adds a reference to the specified object name under the property specified. + /// + /// The name. + /// Name of the object. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder AddPropertyReference(string name, string objectName) + { + objectDefinition.PropertyValues.Add(new PropertyValue(name, new RuntimeObjectReference(objectName))); + return this; + } + + + /// + /// Adds an index constructor arg value. The current index is tracked internally and all addtions are + /// at the present point + /// + /// The constructor arg value. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder AddConstructorArg(object value) + { + objectDefinition.ConstructorArgumentValues.AddIndexedArgumentValue(constructorArgIndex++,value); + return this; + } + + /// + /// Adds a reference to the named object as a constructor argument. + /// + /// Name of the object. + /// + public ObjectDefinitionBuilder AddConstructorArgReference(string objectName) + { + return AddConstructorArg(new RuntimeObjectReference(objectName)); + } + + + /// + /// Sets the name of the factory method to use for this definition. + /// + /// The factory method. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder SetFactoryMethod(string factoryMethod) + { + objectDefinition.FactoryMethodName = factoryMethod; + return this; + } + + /// + /// Sets the name of the factory object to use for this definition. + /// + /// The factory object. + /// The factory method. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder SetFactoryObject(string factoryObject, string factoryMethod) + { + objectDefinition.FactoryObjectName = factoryObject; + objectDefinition.FactoryMethodName = factoryMethod; + return this; + } + + /// + /// Sets whether or not this definition describes a singleton object. + /// + /// if set to true [singleton]. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder SetSingleton(bool singleton) + { + objectDefinition.IsSingleton = singleton; + return this; + } + + /// + /// Sets whether objects or not this definition is abstract. + /// + /// if set to true [flag]. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder SetAbstract(bool flag) + { + objectDefinition.IsAbstract = flag; + return this; + } + + /// + /// Sets whether objects for this definition should be lazily initialized or not. + /// + /// if set to true [lazy]. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder SetLazyInit(bool lazy) + { + objectDefinition.IsLazyInit = lazy; + return this; + } + + /// + /// Sets the autowire mode for this definition. + /// + /// The autowire mode. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder SetAutowireMode(AutoWiringMode autowireMode) + { + objectDefinition.AutowireMode = autowireMode; + return this; + } + + /// + /// Sets the dependency check mode for this definition. + /// + /// The dependency check. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder SetDependencyCheck(DependencyCheckingMode dependencyCheck) + { + objectDefinition.DependencyCheck = dependencyCheck; + return this; + } + + + /// + /// Sets the name of the destroy method for this definition. + /// + /// Name of the method. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder SetDestroyMethodName(string methodName) + { + objectDefinition.DestroyMethodName = methodName; + return this; + } + + /// + /// Sets the name of the init method for this definition. + /// + /// Name of the method. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder SetInitMethodName(string methodName) + { + objectDefinition.InitMethodName = methodName; + return this; + } + + /// + /// Sets the resource description for this definition. + /// + /// The resource description. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder SetResourceDescription(string resourceDescription) + { + objectDefinition.ResourceDescription = resourceDescription; + return this; + } + + /// + /// Adds the specified object name to the list of objects that this definition depends on. + /// + /// Name of the object. + /// The current ObjectDefinitionBuilder. + public ObjectDefinitionBuilder AddDependsOn(string objectName) + { + if (objectDefinition.DependsOn == null) + { + objectDefinition.DependsOn = new string[] {objectName}; + } + else + { + ArrayList arrayList = new ArrayList(); + arrayList.AddRange(objectDefinition.DependsOn); + arrayList.AddRange(new string[]{ objectName}); + objectDefinition.DependsOn = (string[])arrayList.ToArray(typeof(string)); + } + return this; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionReaderUtils.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionReaderUtils.cs new file mode 100644 index 00000000..8bc1d134 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionReaderUtils.cs @@ -0,0 +1,257 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text; +using System.Text.RegularExpressions; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Xml; +using Spring.Objects.Support; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Utility methods that are useful for + /// + /// implementations. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + /// + /// $Id: ObjectDefinitionReaderUtils.cs,v 1.13 2007/08/07 22:05:20 markpollack Exp $ + public sealed class ObjectDefinitionReaderUtils + { + /// + /// The string used as a separator in the generation of synthetic id's + /// for those object definitions explicitly that aren't assigned one. + /// + /// + ///

    + /// If a name or parent object definition + /// name is not unique, "#1", "#2" etc will be appended, until such + /// time that the name becomes unique. + ///

    + ///
    + public const string GeneratedObjectIdSeparator = "#"; + + /// + /// Registers the supplied with the + /// supplied . + /// + /// + ///

    + /// This is a convenience method that registers the + /// + /// of the supplied under the + /// + /// property value of said . If the + /// supplied has any + /// , + /// then those aliases will also be registered with the supplied + /// . + ///

    + ///
    + /// + /// The object definition holder containing the + /// that + /// is to be registered. + /// + /// + /// The registry that the supplied + /// is to be registered with. + /// + /// + /// If either of the supplied arguments is . + /// + /// + /// If the could not be registered + /// with the . + /// + public static void RegisterObjectDefinition( + ObjectDefinitionHolder objectDefinition, IObjectDefinitionRegistry registry) + { + AssertUtils.ArgumentNotNull(objectDefinition, "objectDefinition"); + AssertUtils.ArgumentNotNull(registry, "registry"); + + registry.RegisterObjectDefinition(objectDefinition.ObjectName, objectDefinition.ObjectDefinition); + string[] aliases = objectDefinition.Aliases; + for (int i = 0; i < aliases.Length; ++i) + { + string alias = aliases[i]; + registry.RegisterAlias(objectDefinition.ObjectName, alias); + } + } + + /// + /// Generates an object definition name for the supplied + /// that is guaranteed to be unique + /// within the scope of the supplied . + /// + /// + /// The + /// that requires a generated name. + /// + /// + /// The + /// + /// that the supplied is to be + /// registered with (needed so that the uniqueness of any generated + /// name can be guaranteed). + /// + /// + /// An object definition name for the supplied + /// that is guaranteed to be unique + /// within the scope of the supplied and + /// never . + /// + /// + /// If either of the or + /// arguments is . + /// + /// + /// If a unique name cannot be generated. + /// + public static string GenerateObjectName( + IConfigurableObjectDefinition objectDefinition, IObjectDefinitionRegistry registry) + { + AssertUtils.ArgumentNotNull(objectDefinition, "objectDefinition"); + AssertUtils.ArgumentNotNull(registry, "registry"); + + string starterName = objectDefinition.ObjectTypeName; + if (StringUtils.IsNullOrEmpty(starterName)) + { + if (objectDefinition is ChildObjectDefinition) + { + starterName = ((ChildObjectDefinition) objectDefinition).ParentName + "$child"; + } + else if (objectDefinition.FactoryObjectName != null) + { + starterName = objectDefinition.FactoryObjectName + "$created"; + } + } + if (StringUtils.IsNullOrEmpty(starterName)) + { + throw new ObjectDefinitionStoreException( + objectDefinition.ResourceDescription, String.Empty, + "Unnamed object definition specifies neither 'Type' nor 'Parent' " + + "nor 'FactoryObject' property values so a unique name cannot be generated."); + } + String generatedName = starterName; + int counter = 0; + while (registry.ContainsObjectDefinition(generatedName)) + { + generatedName = new StringBuilder(starterName) + .Append(GeneratedObjectIdSeparator).Append(++counter).ToString(); + } + return generatedName; + } + + /// + /// Factory method for getting concrete + /// instances. + /// + /// + /// The name of the event handler method. This may be straight text, a regular + /// expression, , or empty. + /// + /// + /// The name of the event being wired. This too may be straight text, a regular + /// expression, , or empty. + /// + /// + /// A concrete + /// instance. + /// + public static IEventHandlerValue CreateEventHandlerValue( + string methodName, string eventName) + { + bool weAreAutowiring = false; + if (StringUtils.HasText(eventName)) + { + // does the value contain regular expression characters? mmm, totally trent... + if (Regex.IsMatch(eventName, @"[\*\.\[\]\{\},\(\)\$\^\+]+")) + { + // wildcarded event name + weAreAutowiring = true; + } + } + else + { + // we're definitely autowiring based on the event name + weAreAutowiring = true; + } + if (!weAreAutowiring) + { + if (StringUtils.HasText(methodName)) + { + // does the value contain the string ${event}? + if (methodName.IndexOf("${event}") >= 0) + { + // wildcarded method name + weAreAutowiring = true; + } + } + else + { + // we're definitely autowiring based on the method name + weAreAutowiring = true; + } + } + IEventHandlerValue myHandler; + if (weAreAutowiring) + { + myHandler = new AutoWiringEventHandlerValue(); + } + else + { + myHandler = new InstanceEventHandlerValue(); + } + myHandler.EventName = eventName; + myHandler.MethodName = methodName; + return myHandler; + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private ObjectDefinitionReaderUtils() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionValidationException.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionValidationException.cs new file mode 100644 index 00000000..e52c19c8 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionValidationException.cs @@ -0,0 +1,93 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Thrown when the validation of an object definition failed. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + [Serializable] + public class ObjectDefinitionValidationException : FatalObjectException + { + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the + /// + /// class. + /// + public ObjectDefinitionValidationException () + { + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// The detail message. + public ObjectDefinitionValidationException (string message) + : base (message) + { + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The detail message. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectDefinitionValidationException (string message, Exception rootCause) + : base (message, rootCause) + { + } + + /// + /// Creates a new instance of the ObjectDefinitionValidationException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectDefinitionValidationException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + } + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/PropertiesObjectDefinitionReader.cs b/src/Spring/Spring.Core/Objects/Factory/Support/PropertiesObjectDefinitionReader.cs new file mode 100644 index 00000000..c1da557b --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/PropertiesObjectDefinitionReader.cs @@ -0,0 +1,517 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Resources; + +using Spring.Core.IO; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Object definition reader for a simple properties format. + /// + /// + /// Provides object definition registration methods for + /// and + /// instances. Typically applied to a + /// . + /// + /// Rod Johnson + /// Juergen Hoeller + /// Simon White (.NET) + public class PropertiesObjectDefinitionReader : AbstractObjectDefinitionReader + { + /// + /// Value of a T/F attribute that represents true. + /// Anything else represents false. Case seNsItive. + /// + public const string TrueValue = "true"; + + /// + /// Separator between object name and property name. + /// + public const string Separator = "."; + + /// + /// Prefix for the class property of a root object definition. + /// + public const string ClassKey = "class"; + + /// + /// Special string added to distinguish if the object will be + /// a singleton. + /// + /// + ///

    + /// Default is true. + ///

    + ///
    + /// + ///

    + /// owner.(singleton)=true + ///

    + ///
    + public const string SingletonKey = "(singleton)"; + + /// + /// Special string added to distinguish if the object will be + /// lazily initialised. + /// + /// + ///

    + /// Default is false. + ///

    + ///
    + /// + ///

    + /// owner.(lazy-init)=true + ///

    + ///
    + public const string LazyInitKey = "(lazy-init)"; + + /// + /// Reserved "property" to indicate the parent of a child object definition. + /// + public const string ParentKey = "parent"; + + /// + /// Property suffix for references to other objects in the current + /// : e.g. + /// owner.dog(ref)=fido. + /// + /// + ///

    + /// Whether this is a reference to a singleton or a prototype + /// will depend on the definition of the target object. + ///

    + ///
    + public const string RefSuffix = "(ref)"; + + /// + /// Prefix before values referencing other objects. + /// + public const string RefPrefix = "*"; + + private string _defaultParentObject = string.Empty; + + private IObjectDefinitionFactory _objectDefinitionFactory = new DefaultObjectDefinitionFactory(); + + /// + /// Name of default parent object + /// + public string DefaultParentObject + { + get { return _defaultParentObject; } + set { this._defaultParentObject = value; } + } + + /// + /// Gets or sets object definition factory to use. + /// + public IObjectDefinitionFactory ObjectDefinitionFactory + { + get { return _objectDefinitionFactory; } + set { _objectDefinitionFactory = value; } + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The + /// instance that this reader works on. + /// + public PropertiesObjectDefinitionReader(IObjectDefinitionRegistry registry) + : base(registry) + {} + + /// + /// Load object definitions from the supplied . + /// + /// + /// The resource for the object definitions that are to be loaded. + /// + /// + /// The number of object definitions that were loaded. + /// + /// + /// In the case of loading or parsing errors. + /// + public override int LoadObjectDefinitions(IResource resource) + { + return LoadObjectDefinitions(resource, string.Empty); + } + + /// + /// Load object definitions from the specified properties file. + /// + /// + /// The resource descriptor for the properties file. + /// + /// + /// The match or filter for object definition names, e.g. 'objects.' + /// + /// in case of loading or parsing errors + /// the number of object definitions found + public int LoadObjectDefinitions(IResource resource, string prefix) + { + Properties props = new Properties(); + try + { + Stream str = resource.InputStream; + try + { + props.Load(str); + } + finally + { + str.Close(); + } + return RegisterObjectDefinitions(props, prefix, resource.Description); + } + catch (IOException ex) + { + throw new ObjectDefinitionStoreException("IOException parsing properties from " + resource, ex); + } + } + + /// + /// Register object definitions contained in a + /// , using all property keys (i.e. + /// not filtering by prefix). + /// + /// + /// The containing object definitions. + /// + /// + /// In case of loading or parsing errors. + /// + /// The number of object definitions registered. + public int RegisterObjectDefinitions(ResourceSet rs) + { + return RegisterObjectDefinitions(rs, string.Empty); + } + + /// + /// Register object definitions contained in a + /// . + /// + /// + ///

    + /// Similar syntax as for an . + /// This method is useful to enable standard .NET internationalization support. + ///

    + ///
    + /// + /// The containing object definitions. + /// + /// + /// The match or filter for object definition names, e.g. 'objects.' + /// + /// + /// In case of loading or parsing errors. + /// + /// The number of object definitions registered. + public int RegisterObjectDefinitions(ResourceSet rs, string prefix) + { +#if ! NET_1_0 + // Simply create a map and call overloaded method + IDictionary id = new Hashtable(); + foreach (DictionaryEntry de in rs) + { + id.Add(de.Key, de.Value); + } + return RegisterObjectDefinitions(id, prefix); +#else + throw new NotSupportedException("Operation not supported on NET 1.0"); +#endif + } + + /// + /// Register object definitions contained in an + /// , using all property keys + /// (i.e. not filtering by prefix). + /// + /// + /// The containing object definitions. + /// + /// + /// In case of loading or parsing errors. + /// + /// The number of object definitions registered. + public int RegisterObjectDefinitions(IDictionary id) + { + return RegisterObjectDefinitions(id, string.Empty); + } + + /// + /// Registers object definitions contained in an + /// using all property keys ( i.e. not filtering by prefix ) + /// + /// The containing + /// object definitions. + /// + /// + /// In case of loading or parsing errors. + /// + /// The number of object definitions registered. + public int RegisterObjectDefinitions(NameValueCollection nameValueCollection) + { + IDictionary id = new Hashtable(); + foreach (DictionaryEntry de in nameValueCollection) + { + id.Add(de.Key, de.Value); + } + + return RegisterObjectDefinitions(id); + } + + /// + /// Register object definitions contained in a + /// . + /// + /// + ///

    + /// Ignores ineligible properties. + ///

    + ///
    + /// IDictionary name -> property (String or Object). Property values + /// will be strings if coming from a Properties file etc. Property names + /// (keys) must be strings. Type keys must be strings. + /// + /// + /// The match or filter within the keys in the map: e.g. 'objects.' + /// + /// + /// In case of loading or parsing errors. + /// + /// The number of object definitions found. + public int RegisterObjectDefinitions(IDictionary id, string prefix) + { + return RegisterObjectDefinitions(id, prefix, "(no description)"); + } + + /// + /// Register object definitions contained in a + /// . + /// + /// + ///

    + /// Ignores ineligible properties. + ///

    + ///
    + /// IDictionary name -> property (String or Object). Property values + /// will be strings if coming from a Properties file etc. Property names + /// (keys) must be strings. Type keys must be strings. + /// + /// + /// The match or filter within the keys in the map: e.g. 'objects.' + /// + /// + /// The description of the resource that the + /// came from (for logging purposes). + /// + /// + /// In case of loading or parsing errors. + /// + /// The number of object definitions found. + public int RegisterObjectDefinitions( + IDictionary id, string prefix, string resourceDescription) + { + if (prefix == null) + { + prefix = string.Empty; + } + int objectCount = 0; + foreach (string key in id.Keys) + { + if (key.StartsWith(prefix)) + { + // Key is of form prefix.property + string nameAndProperty = key.Substring(prefix.Length); + int sepIndx = nameAndProperty.IndexOf(Separator); + if (sepIndx != -1) + { + string name = nameAndProperty.Substring(0, sepIndx); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Found object name '" + name + "'"); + } + + #endregion + + if (!Registry.ContainsObjectDefinition(name)) + { + ++objectCount; + } + RegisterObjectDefinition(name, id, prefix + name, resourceDescription); + } + else + { + // Ignore it: it wasn't a valid object name and property, + // although it did start with the required prefix + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Invalid object name and property [" + nameAndProperty + "]"); + } + + #endregion + } + } // if the key started with the prefix we're looking for + } // while there are more keys + return objectCount; + } + + /// + /// Get all property values, given a prefix (which will be stripped) + /// and add the object they define to the factory with the given name + /// + /// The name of the object to define. + /// + /// The containing string pairs. + /// + /// The prefix of each entry, which will be stripped. + /// + /// The description of the resource that the + /// came from (for logging purposes). + /// + /// + /// In case of loading or parsing errors. + /// + protected void RegisterObjectDefinition( + string name, IDictionary id, string prefix, string resourceDescription) + { + string typeName = null; + string parent = null; + bool singleton = true; + bool lazyInit = false; + + MutablePropertyValues pvs = new MutablePropertyValues(); + foreach (string key in id.Keys) + { + if (key.StartsWith(prefix + Separator)) + { + string property = key.Substring(prefix.Length + Separator.Length); + if (property.Equals(ClassKey)) + { + typeName = (string) id[key]; + } + else if (property.Equals(SingletonKey)) + { + string val = (string) id[key]; + singleton = (val == null) || val.Equals(TrueValue); + } + else if (property.Equals(LazyInitKey)) + { + string val = (string) id[key]; + lazyInit = val.Equals(TrueValue); + } + else if (property.Equals(ParentKey)) + { + parent = (string) id[key]; + } + else if (property.EndsWith(RefSuffix)) + { + // This isn't a real property, but a reference to another prototype + // Extract property name: property is of form dog(ref) + property = property.Substring(0, property.Length - RefSuffix.Length); + string reference = (String) id[key]; + + // It doesn't matter if the referenced object hasn't yet been registered: + // this will ensure that the reference is resolved at runtime + // Default is not to use singleton + object val = new RuntimeObjectReference(reference); + pvs.Add(new PropertyValue(property, val)); + } + else + { + // normal object property + object val = id[key]; + if (val is String) + { + string strVal = (string) val; + // if it starts with a reference prefix... + if (strVal.StartsWith(RefPrefix)) + { + // expand reference + string targetName = strVal.Substring(1); + if (targetName.StartsWith(RefPrefix)) + { + // escaped prefix -> use plain value + val = targetName; + } + else + { + val = new RuntimeObjectReference(targetName); + } + } + } + pvs.Add(new PropertyValue(property, val)); + } + } + } + if (log.IsDebugEnabled) + { + log.Debug(pvs.ToString()); + } + if (parent == null) + { + log.Debug(this.DefaultParentObject); + parent = this.DefaultParentObject; + } + if (typeName == null && parent == null) + { + throw new ObjectDefinitionStoreException(resourceDescription, name, + "Either 'type' or 'parent' is required"); + } + try + { + IConfigurableObjectDefinition objectDefinition = ObjectDefinitionFactory.CreateObjectDefinition(typeName, parent, Domain); + objectDefinition.PropertyValues = pvs; + objectDefinition.IsSingleton = singleton; + objectDefinition.IsLazyInit = lazyInit; + Registry.RegisterObjectDefinition(name, objectDefinition); + } + catch (Exception ex) + { + throw new ObjectDefinitionStoreException( + resourceDescription, name, "Unable to load type [" + typeName + "]", ex); + } + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/ReplacedMethodOverride.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ReplacedMethodOverride.cs new file mode 100644 index 00000000..771bae93 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/ReplacedMethodOverride.cs @@ -0,0 +1,160 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using System.Reflection; +using System.Text; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Represents the replacement of a method on a managed object by the IoC + /// container. + /// + /// + ///

    + /// Note that this mechanism is not intended as a generic means of + /// inserting crosscutting code: use AOP for that. + ///

    + ///
    + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: ReplacedMethodOverride.cs,v 1.3 2007/03/16 04:01:42 aseovic Exp $ + [Serializable] + public sealed class ReplacedMethodOverride : MethodOverride + { + private readonly string methodReplacerObjectName; + private StringCollection typeIdentifiers = new StringCollection(); + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The name of the method that is to be overridden. + /// + /// + /// The object name of the + /// instance in the surrounding IoC container. + /// + /// + /// If either of the supplied arguments is or + /// contains only whitespace character(s). + /// + public ReplacedMethodOverride(string methodName, string methodReplacerObjectName) + : base(methodName) + { + AssertUtils.ArgumentHasText(methodReplacerObjectName, "methodReplacerObjectName"); + this.methodReplacerObjectName = methodReplacerObjectName; + } + + /// + /// Add a fragment of a instance's + /// such as 'Exception or System.Excep to identify an argument + /// for a dependency injected method. + /// + /// + /// A (sub) string of a instance's . + /// + /// + /// If the supplied is or + /// contains only whitespace character(s). + /// + /// + public void AddTypeIdentifier(string identifier) + { + AssertUtils.ArgumentHasText(identifier, "identifier"); + this.typeIdentifiers.Add(identifier); + } + + /// + /// The object name of the + /// instance in the surrounding IoC container. + /// + public string MethodReplacerObjectName + { + get { return methodReplacerObjectName; } + } + + /// + /// Does this + /// match the supplied ? + /// + /// The method to be checked. + /// + /// if this override matches the supplied . + /// + /// + /// If the supplied is . + /// + public override bool Matches(MethodInfo method) + { + AssertUtils.ArgumentNotNull(method, "method"); + if (MethodName != method.Name && !(method.Name.EndsWith(MethodName))) + { + // can't ever match... + return false; + } + if (!IsOverloaded) + { + // no need to worry about overloading... + return true; + } + // if we get to here, we need to insist on precise argument matching... + ParameterInfo[] parameters = method.GetParameters(); + if (this.typeIdentifiers.Count != parameters.Length) + { + return false; + } + for (int i = 0; i < typeIdentifiers.Count; ++i) + { + string identifier = this.typeIdentifiers[i]; + ParameterInfo parameter = parameters[i]; + if (parameter.ParameterType.FullName.IndexOf(identifier) == -1) + { + // this parameter cannot match, so neither can the whole override... + return false; + } + } + return true; + } + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + return new StringBuilder(GetType().Name).Append(" for method '") + .Append(MethodName).Append("'; will call object '").Append(this.methodReplacerObjectName) + .Append("'.").ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/RootObjectDefinition.cs b/src/Spring/Spring.Core/Objects/Factory/Support/RootObjectDefinition.cs new file mode 100644 index 00000000..1eec0f07 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/RootObjectDefinition.cs @@ -0,0 +1,293 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// A plain-vanilla object definition. + /// + /// + ///

    + /// This is the most common type of object definition; + /// instances + /// do not derive from a parent + /// , and usually + /// (but not always - see below) have an + /// + /// and (optionally) some + /// and + /// . + ///

    + ///

    + /// Note that + /// instances do not have to specify an + /// : + /// This can be useful for deriving + /// instances + /// from such definitions, each with it's own + /// , + /// inheriting common property values and other settings from the parent. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: RootObjectDefinition.cs,v 1.20 2007/03/16 04:01:43 aseovic Exp $ + /// + [Serializable] + public class RootObjectDefinition : AbstractObjectDefinition + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public RootObjectDefinition() + {} + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The of the object to instantiate. + /// + public RootObjectDefinition(Type type) + { + ObjectType = type; + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The of the object to instantiate. + /// + /// + /// if this object definition defines a singleton object. + /// + public RootObjectDefinition(Type type, bool singleton) + { + ObjectType = type; + IsSingleton = singleton; + } + + /// + /// Creates a new instance of the + /// class + /// for a singleton, providing property values and constructor arguments. + /// + /// + /// The of the object to instantiate. + /// + /// + /// The + /// to be applied to a new instance of the object. + /// + /// + /// The to be applied to + /// a new instance of the object. + /// + public RootObjectDefinition( + Type type, ConstructorArgumentValues arguments, MutablePropertyValues properties) + : base(arguments, properties) + { + ObjectType = type; + } + + + /// + /// Creates a new instance of the + /// class + /// for a singleton using the supplied + /// . + /// + /// + /// The of the object to instantiate. + /// + /// + /// The autowiring mode. + /// + public RootObjectDefinition(Type type, AutoWiringMode autowireMode) + { + ObjectType = type; + AutowireMode = autowireMode; + } + + /// + /// Creates a new instance of the + /// class + /// for a singleton using the supplied + /// . + /// + /// + /// The of the object to instantiate. + /// + /// + /// The autowiring mode. + /// + /// + /// Whether to perform a dependency check for objects (not + /// applicable to autowiring a constructor, thus ignored there) + /// + public RootObjectDefinition( + Type type, AutoWiringMode autowireMode, bool dependencyCheck) + { + ObjectType = type; + AutowireMode = autowireMode; + if (dependencyCheck + && ResolvedAutowireMode != AutoWiringMode.Constructor) + { + DependencyCheck = DependencyCheckingMode.Objects; + } + } + + /// + /// Creates a new instance of the + /// class + /// with the given singleton status, providing property values. + /// + /// + /// The of the object to instantiate. + /// + /// + /// The to be applied to + /// a new instance of the object. + /// + public RootObjectDefinition( + Type type, MutablePropertyValues properties) : base(null, properties) + { + ObjectType = type; + } + + /// + /// Creates a new instance of the + /// class + /// with the given singleton status, providing property values. + /// + /// + /// The of the object to instantiate. + /// + /// + /// The to be applied to + /// a new instance of the object. + /// + /// + /// if this object definition defines a singleton object. + /// + public RootObjectDefinition( + Type type, MutablePropertyValues properties, bool singleton) : base(null, properties) + { + ObjectType = type; + IsSingleton = singleton; + } + + /// + /// Creates a new instance of the + /// class + /// for a singleton, providing property values and constructor arguments. + /// + /// + ///

    + /// Takes an object class name to avoid eager loading of the object class. + ///

    + ///
    + /// + /// The assembly qualified of the object to instantiate. + /// + /// + /// The to be applied to + /// a new instance of the object. + /// + /// + /// The + /// to be applied to a new instance of the object. + /// + public RootObjectDefinition( + string typeName, ConstructorArgumentValues arguments, MutablePropertyValues properties) + : base(arguments, properties) + { + ObjectTypeName = typeName; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// Deep copy constructor. + ///

    + ///
    + /// + /// The definition that is to be copied. + /// + public RootObjectDefinition(IObjectDefinition other) : base(other) + {} + + #endregion + + /// + /// Validate this object definition. + /// + /// + /// In the case of a validation failure. + /// + public override void Validate() + { + base.Validate(); + if (HasObjectType) + { + if (typeof(IFactoryObject).IsAssignableFrom(ObjectType) + && !IsSingleton) + { + throw new ObjectDefinitionValidationException( + "IFactoryObject must be defined as a singleton - " + + "IFactoryObjects themselves are not allowed to be prototypes."); + } + } + } + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + return String.Format("{0} : {1}", GetType().Name, base.ToString()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/SimpleInstantiationStrategy.cs b/src/Spring/Spring.Core/Objects/Factory/Support/SimpleInstantiationStrategy.cs new file mode 100644 index 00000000..24546a17 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/SimpleInstantiationStrategy.cs @@ -0,0 +1,308 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Reflection; + +using Common.Logging; +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Simple object instantiation strategy for use in + /// implementations. + /// + /// + ///

    + /// Does not support method injection, although it provides hooks for subclasses + /// to override to add method injection support, for example by overriding methods. + ///

    + ///
    + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: SimpleInstantiationStrategy.cs,v 1.18 2008/04/07 00:30:34 bbaia Exp $ + /// + [Serializable] + public class SimpleInstantiationStrategy : IInstantiationStrategy + { + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (SimpleInstantiationStrategy)); + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied . + /// + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the null + /// or zero length string if we're autowiring an object that doesn't belong + /// to the supplied . + /// + /// + /// The owning + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + public virtual object Instantiate( + RootObjectDefinition definition, string name, IObjectFactory factory) + { + AssertUtils.ArgumentNotNull(definition, "definition"); + AssertUtils.ArgumentNotNull(factory, "factory"); + if (definition.HasMethodOverrides) + { + return InstantiateWithMethodInjection(definition, name, factory); + } + else + { + Type objectType = definition.HasObjectType + ? definition.ObjectType + : TypeResolutionUtils.ResolveType(definition.ObjectTypeName); + ConstructorInfo constructor = GetZeroArgConstructorInfo(objectType); + return ObjectUtils.InstantiateType(constructor, ObjectUtils.EmptyObjects); + } + } + + /// + /// Gets the zero arg ConstructorInfo object, if the type offers such functionality. + /// + /// The type. + /// Zero argument ConstructorInfo + /// + /// If the type does not have a zero-arg constructor. + /// + private ConstructorInfo GetZeroArgConstructorInfo(Type type) + { + const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.Instance | BindingFlags.DeclaredOnly; + + ConstructorInfo constructor = type.GetConstructor(flags, null, Type.EmptyTypes, null); + if (constructor == null) + { + throw new FatalReflectionException(string.Format( + CultureInfo.InvariantCulture, "Cannot instantiate a class that does not have a no-argument constructor [{0}].", type)); + } + return constructor; + } + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied . + /// + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the null + /// or zero length string if we're autowiring an object that doesn't belong + /// to the supplied . + /// + /// + /// The owning + /// + /// + /// The to be used to instantiate + /// the object. + /// + /// + /// Any arguments to the supplied . May be null. + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + public virtual object Instantiate( + RootObjectDefinition definition, string name, IObjectFactory factory, + ConstructorInfo constructor, object[] arguments) + { + if (definition.HasMethodOverrides) + { + return InstantiateWithMethodInjection(definition, name, factory, constructor, arguments); + } + else + { + return ObjectUtils.InstantiateType(constructor, arguments); + } + } + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied . + /// + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the null + /// or zero length string if we're autowiring an object that doesn't belong + /// to the supplied . + /// + /// + /// The owning + /// + /// + /// The to be used to get the object. + /// + /// + /// Any arguments to the supplied . May be null. + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + public virtual object Instantiate( + RootObjectDefinition definition, string name, IObjectFactory factory, + MethodInfo factoryMethod, object[] arguments) + { + object instance = null; + object target = null; + if (StringUtils.HasText(definition.FactoryObjectName)) + { + target = factory[definition.FactoryObjectName]; + } + try + { + // the target will be null if using a static factory method + instance = factoryMethod.Invoke(target, arguments); + } + catch (TargetInvocationException ex) + { + string msg = string.Format( + CultureInfo.InvariantCulture, + "Factory method '{0}' threw an Exception.", factoryMethod); + + #region Instrumentation + + if (log.IsWarnEnabled) + { + log.Warn(msg, ex.InnerException); + } + + #endregion + + throw new ObjectDefinitionStoreException(msg, ex.InnerException); + } + catch (Exception ex) + { + throw new ObjectDefinitionStoreException(string.Format( + CultureInfo.InvariantCulture, + "Factory method '{0}' threw an Exception.", factoryMethod), ex); + } + return instance; + } + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied , + /// injecting methods as appropriate. + /// + /// + ///

    + /// The default implementation of this method is to throw a + /// . + ///

    + ///

    + /// Derived classes can override this method if they can instantiate an object + /// with the Method Injection specified in the supplied + /// . Instantiation should use a no-arg constructor. + ///

    + ///
    + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be a + /// or zero length string if we're autowiring an object that + /// doesn't belong to the supplied . + /// + /// + /// The owning + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + protected virtual object InstantiateWithMethodInjection( + RootObjectDefinition definition, string objectName, IObjectFactory factory) + { + throw new InvalidOperationException("Method Injection not supported in SimpleInstantiationStrategy"); + } + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied , + /// injecting methods as appropriate. + /// + /// + ///

    + /// The default implementation of this method is to throw a + /// . + ///

    + ///

    + /// Derived classes can override this method if they can instantiate an object + /// with the Method Injection specified in the supplied + /// . Instantiation should use the supplied + /// and attendant . + ///

    + ///
    + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the null + /// or zero length string if we're autowiring an object that doesn't belong + /// to the supplied . + /// + /// + /// The owning + /// + /// + /// The to be used to instantiate + /// the object. + /// + /// + /// Any arguments to the supplied . May be null. + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + protected virtual object InstantiateWithMethodInjection( + RootObjectDefinition definition, string objectName, IObjectFactory factory, + ConstructorInfo constructor, object[] arguments) + { + throw new InvalidOperationException("Method Injection not supported in SimpleInstantiationStrategy"); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/StaticListableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Support/StaticListableObjectFactory.cs new file mode 100644 index 00000000..dae2bfad --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/StaticListableObjectFactory.cs @@ -0,0 +1,692 @@ +#region License +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Static factory that permits the registration of existing singleton instances. + /// + /// + ///

    + /// Does not have support for prototype objects, aliases, and post startup object + /// configuration. + ///

    + ///

    + /// Serves as a simple example implementation of the + /// interface, that manages existing object instances as opposed to creating new ones + /// based on object definitions. + ///

    + ///

    + /// The + /// method is not supported by this class; this class deals exclusively with + /// existing singleton instances, thus the methods mentioned previously make little sense in this context. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: StaticListableObjectFactory.cs,v 1.24 2007/12/05 00:28:05 bbaia Exp $ + [Serializable] + public class StaticListableObjectFactory : IListableObjectFactory + { + /// + /// Map from object name to object instance. + /// + private Hashtable objects = new Hashtable(); + + /// + /// Return the number of objects defined in the factory. + /// + /// + /// The number of objects defined in the factory. + /// + public int ObjectDefinitionCount + { + get { return objects.Count; } + } + + /// + /// Return an instance of the given object name. + /// + /// The name of the object to return. + /// The instance of the object. + /// + public object this[string name] + { + get { return GetObject(name); } + } + + /// + /// Return an instance of the given object name. + /// + /// The name of the object to return. + /// The instance of the object. + /// + /// is not currently supported. + /// + /// + public object GetObject(string name) + { + object instance = objects[name]; + if (instance is IFactoryObject) + { + if (instance is IConfigurableFactoryObject) + { + throw new NotSupportedException(); + } + try + { + return ((IFactoryObject) instance).GetObject(); + } + catch (Exception ex) + { + throw new ObjectCreationException(name, + "IFactoryObject threw an exception on object creation", ex); + } + } + if (instance == null) + { + throw new NoSuchObjectDefinitionException(name, GrabDefinedObjectsString()); + } + return instance; + } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// + ///

    + /// This method allows an object factory to be used as a replacement for the + /// Singleton or Prototype design pattern. + ///

    + ///

    + /// Note that callers should retain references to returned objects. There is no + /// guarantee that this method will be implemented to be efficient. For example, + /// it may be synchronized, or may need to run an RDBMS query. + ///

    + ///

    + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + ///

    + ///
    + /// The name of the object to return. + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a static factory method. If there is no factory method and the + /// arguments are not null, then match the argument values by type and + /// call the object's constructor. + /// + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + /// If the supplied is . + /// + public object GetObject(string name, object[] arguments) + { + throw new NotSupportedException("StaticListableObjectFactory does not support this method."); + } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// The name of the object to return. + /// + /// The the object may match. Can be an interface or + /// superclass of the actual class. For example, if the value is the + /// class, this method will succeed whatever the + /// class of the returned instance. + /// + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a factory method. If there is no factory method and the + /// supplied array is not , then + /// match the argument values by type and call the object's constructor. + /// + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If the object could not be created. + /// + /// + /// If the object is not of the required type. + /// + /// + /// If the supplied is . + /// + /// + public object GetObject(string name, Type requiredType, object[] arguments) + { + throw new NotSupportedException("StaticListableObjectFactory does not support this method."); + } + + /// + /// Return an instance of the given object name. + /// + /// The name of the object to return. + /// + /// the object may match. Can be an interface or + /// superclass of the actual class. For example, if the value is the + /// class, this method will succeed whatever the + /// class of the returned instance. + /// + /// The instance of the object. + /// + public object GetObject(string name, Type requiredType) + { + object instance = GetObject(name); + if (!requiredType.IsAssignableFrom(instance.GetType())) + { + throw new ObjectNotOfRequiredTypeException(name, requiredType, instance); + } + return instance; + } + + /// + /// Does this object factory contain an object with the given name? + /// + /// The name of the object to query. + /// True if an object with the given name is defined. + public bool ContainsObject(string name) + { + return objects.ContainsKey(name); + } + + /// + /// Is this object a singleton? + /// + /// + ///

    + /// That is, will + /// or + /// always return the same object? + ///

    + ///
    + /// The name of the object to query. + /// True if the named object is a singleton. + /// + /// If there's no such object definition. + /// + public bool IsSingleton(string name) + { + bool isSingleton = true; + object instance = GetObject(name); + // in case of IFactoryObject, return singleton status of created object + if (instance is IFactoryObject) + { + isSingleton = ((IFactoryObject) instance).IsSingleton; + } + return isSingleton; + } + + + /// + /// Determines whether the specified object name is prototype. That is, will GetObject + /// always return independent instances? + /// + /// This method returning false does not clearly indicate a singleton object. + /// It indicated non-independent instances, which may correspond to a scoped object as + /// well. use the IsSingleton property to explicitly check for a shared + /// singleton instance. + /// Translates aliases back to the corresponding canonical object name. Will ask the + /// parent factory if the object can not be found in this factory instance. + /// + /// + /// + /// The name of the object to query + /// + /// true if the specified object name will always deliver independent instances; otherwise, false. + /// + /// if there is no object with the given name. + public bool IsPrototype(string name) + { + bool isPrototype = true; + object instance = GetObject(name); + if (instance is IFactoryObject) + { + isPrototype = !((IFactoryObject) instance).IsSingleton; + } + return isPrototype; + + } + + /// + /// Determine the type of the object with the given name. + /// + /// + ///

    + /// More specifically, checks the type of object that + /// would return. + /// For an , returns the type + /// of object that the creates. + ///

    + ///
    + /// The name of the object to query. + /// + /// The of the object or if + /// not determinable. + /// + public Type GetType(string name) + { + string objectName = ObjectFactoryUtils.TransformedObjectName(name); + object instance = objects[objectName]; + if (instance == null) + { + throw new NoSuchObjectDefinitionException(name, GrabDefinedObjectsString()); + } + if (instance is IFactoryObject && !ObjectFactoryUtils.IsFactoryDereference(name)) + { + return ((IFactoryObject) instance).ObjectType; + } + return instance.GetType(); + } + + + /// + /// Determines whether the object with the given name matches the specified type. + /// + /// The name of the object to query. + /// Type of the target to match against. + /// + /// true if the object type matches; otherwise, false + /// if it doesn't match or cannot be determined yet. + /// + /// Ff there is no object with the given name + /// + public bool IsTypeMatch(string name, Type targetType) + { + Type type = GetType(name); + return (targetType == null || (type != null && targetType.IsAssignableFrom(type))); + } + + private string GrabDefinedObjectsString() + { + return "Defined objects are [" + + StringUtils.CollectionToDelimitedString(objects.Keys, ",") + "]"; + } + + /// + /// Return the aliases for the given object name, if defined. + /// + /// The object name to check for aliases. + /// The aliases, or an empty array if none. + /// + /// If there's no such object definition. + /// + public string[] GetAliases(string name) + { + return StringUtils.EmptyStrings; + } + + /// + /// Not supported. + /// + /// The name of the object. + /// + /// The registered + /// . + /// + /// + /// Always, as object definitions are not supported by this + /// implementation. + /// + public IObjectDefinition GetObjectDefinition(string name) + { + throw new NotSupportedException("StaticListableObjectFactory does not contain object definitions."); + } + + /// + /// Return the registered + /// for the + /// given object, allowing access to its property values and constructor + /// argument values. + /// + /// The name of the object. + /// Whether to search parent object factories. + /// + /// The registered + /// . + /// + /// + /// If there is no object with the given name. + /// + /// + /// In the case of errors. + /// + public IObjectDefinition GetObjectDefinition(string name, bool includeAncestors) + { + throw new NotSupportedException("StaticListableObjectFactory does not contain object definitions."); + } + + + /// + /// Return the names of all objects defined in this factory. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + public string[] GetObjectDefinitionNames() + { + ArrayList names = new ArrayList(objects.Keys); + return (string[]) names.ToArray(typeof (string)); + } + + /// + /// Return the names of objects matching the given + /// (including subclasses), judging from the object definitions. + /// + /// + /// The (class or interface) to match, or + /// for all object names. + /// + /// + ///

    + /// Will not consider s, + /// as the type of their created objects is not known before instantiation. + ///

    + ///
    + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + public string[] GetObjectDefinitionNames(Type type) + { + ArrayList matches = new ArrayList(); + foreach (string name in objects.Keys) + { + Type t = objects[name].GetType(); + if (type.IsAssignableFrom(t)) + { + matches.Add(name); + } + } + return (string[]) matches.ToArray(typeof (string)); + } + + /// + /// Return the names of objects matching the given + /// (including subclasses), judging from the object definitions. + /// + /// + /// The (class or interface) to match, or + /// for all object names. + /// + /// + ///

    + /// Does consider objects created by s, + /// or rather it considers the type of objects created by + /// (which means that + /// s will be instantiated). + ///

    + ///

    + /// Does not consider any hierarchy this factory may participate in. + ///

    + ///
    + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + public string[] GetObjectNamesForType(Type type) + { + return GetObjectNamesForType(type, true, true); + } + + /// + /// Return the names of objects matching the given + /// (including subclasses), judging from the object definitions. + /// + /// + ///

    + /// Since this implementation of the + /// + /// interface does not support the notion of ptototype objects, the + /// parameter is ignored. + ///

    + ///
    + /// + /// The (class or interface) to match, or + /// for all object names. + /// + /// + /// Whether to include prototype objects too or just singletons (also applies to + /// s). Ignored. + /// + /// + /// Whether to include s too + /// or just normal objects. + /// + /// + /// The names of all objects defined in this factory, or an empty array if none + /// are defined. + /// + /// + public string[] GetObjectNamesForType( + Type type, bool includePrototypes, bool includeFactoryObjects) + { + bool isFactoryType = (type != null && typeof(IFactoryObject).IsAssignableFrom(type)); + IList matches = new ArrayList(); + foreach (string name in objects.Keys) + { + object instance = objects[name]; + if (instance is IFactoryObject && !isFactoryType) + { + if(includeFactoryObjects) + { + Type objectType = ((IFactoryObject) instance).ObjectType; + if (objectType != null && type.IsAssignableFrom(objectType)) + { + matches.Add(name); + } + } + } + else + { + if (type.IsInstanceOfType(instance)) + { + matches.Add(name); + } + } + } + return (string[]) ArrayList.Adapter(matches).ToArray(typeof(string)); + } + + /// + /// Tests whether this object factory contains an object definition for the + /// specified object name. + /// + /// The object name to query. + /// + /// True if an object defintion is contained within this object factory. + /// + public bool ContainsObjectDefinition(string name) + { + return objects.ContainsKey(name); + } + + /// + /// Return the object instances that match the given object + /// (including subclasses), judging from either object + /// definitions or the value of + /// in the case of + /// s. + /// + /// + ///

    + /// This version of the + /// method matches all kinds of object definitions, be they singletons, prototypes, or + /// s. Typically, the results + /// of this method call will be the same as a call to + /// IListableObjectFactory.GetObjectsOfType(type,true,true) . + ///

    + ///
    + /// + /// The (class or interface) to match. + /// + /// + /// A of the matching objects, + /// containing the object names as keys and the corresponding object instances + /// as values. + /// + /// + /// If the objects could not be created. + /// + public IDictionary GetObjectsOfType(Type type) + { + return GetObjectsOfType(type, true, true); + } + + /// + /// Return the object instances that match the given object + /// (including subclasses), judging from either object + /// definitions or the value of + /// in the case of + /// s. + /// + /// + /// The (class or interface) to match. + /// + /// + /// Whether to include prototype objects too or just singletons (also applies to + /// s). + /// + /// + /// Whether to include s too + /// or just normal objects. + /// + /// + /// A of the matching objects, + /// containing the object names as keys and the corresponding object instances + /// as values. + /// + /// + /// If the objects could not be created. + /// + public IDictionary GetObjectsOfType(Type type, bool includePrototypes, bool includeFactoryObjects) + { + bool isFactoryType = (type != null && typeof(IFactoryObject).IsAssignableFrom(type)); + IDictionary matches = new Hashtable(); + foreach (string name in objects.Keys) + { + object instance = objects[name]; + if (instance is IFactoryObject && includeFactoryObjects) + { + IFactoryObject factory = (IFactoryObject) instance; + Type objectType = factory.ObjectType; + if ((objectType == null && factory.IsSingleton) || + ((factory.IsSingleton || includePrototypes) && + objectType != null && type.IsAssignableFrom(objectType))) + { + object createdObject = GetObject(name); + if (type.IsInstanceOfType(createdObject)) + { + matches[name] = createdObject; + } + } + } + else if (type.IsAssignableFrom(instance.GetType())) + { + if (isFactoryType) + { + matches[ObjectFactoryUtils.BuildFactoryObjectName(name)] = instance; + } + else + { + matches[name] = instance; + } + } + } + return matches; + } + + /// + /// Add a new singleton object. + /// + /// + /// The name to be associated with the object name. + /// + /// The singleton object. + public void AddObject(string name, object instance) + { + objects[name] = instance; + } + + /// + /// Injects dependencies into the supplied instance + /// using the named object definition. + /// + /// + /// The object instance that is to be so configured. + /// + /// + /// The name of the object definition expressing the dependencies that are to + /// be injected into the supplied instance. + /// + /// + /// This feature is not currently supported. + /// + /// + public object ConfigureObject(object target, string name) + { + throw new NotSupportedException(); + } + + /// + /// Injects dependencies into the supplied instance + /// using the supplied . + /// + /// + /// The object instance that is to be so configured. + /// + /// + /// The name of the object definition expressing the dependencies that are to + /// be injected into the supplied instance. + /// + /// + /// An object definition that should be used to configure object. + /// + /// + public object ConfigureObject(object target, string name, IObjectDefinition definition) + { + throw new NotSupportedException(); + } + + /// + /// Defines a method to release allocated unmanaged resources. + /// + public virtual void Dispose() + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/UnsatisfiedDependencyException.cs b/src/Spring/Spring.Core/Objects/Factory/UnsatisfiedDependencyException.cs new file mode 100644 index 00000000..a32dcd70 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/UnsatisfiedDependencyException.cs @@ -0,0 +1,160 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Exception thrown when an object depends on other objects or simple properties + /// that were not specified in the object factory definition, although dependency + /// checking was enabled. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: UnsatisfiedDependencyException.cs,v 1.8 2006/04/09 07:18:49 markpollack Exp $ + [Serializable] + public class UnsatisfiedDependencyException : ObjectCreationException + { + /// + /// Creates a new instance of the UnsatisfiedDependencyException class. + /// + public UnsatisfiedDependencyException() + { + } + + /// + /// Creates a new instance of the UnsatisfiedDependencyException class. + /// + /// + /// A message about the exception. + /// + public UnsatisfiedDependencyException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the UnsatisfiedDependencyException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public UnsatisfiedDependencyException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the UnsatisfiedDependencyException class. + /// + /// + /// The description of the resource associated with the object. + /// + /// + /// The name of the object that has the unsatisfied dependency. + /// + /// + /// The constructor argument index at which the dependency is + /// unsatisfied. + /// + /// + /// The of the constructor argument at + /// which the dependency is unsatisfied. + /// + /// + /// A message about the exception. + /// + public UnsatisfiedDependencyException( + string resourceDescription, + string name, + int argumentIndex, + Type argumentType, + string message) + : base( + resourceDescription, + name, + string.Format( + "Unsatisfied dependency expressed through constructor argument with index {0} of type [{1}] : {2}", + argumentIndex, + argumentType, + message)) + { + } + + /// + /// Creates a new instance of the UnsatisfiedDependencyException class. + /// + /// + /// The description of the resource associated with the object. + /// + /// + /// The name of the object that has the unsatisfied dependency. + /// + /// + /// The name identifying the property on which the dependency is + /// unsatisfied. + /// + /// + /// A message about the exception. + /// + public UnsatisfiedDependencyException( + string resourceDescription, + string name, + string propertyName, + string message) + : base( + resourceDescription, + name, + string.Format( + "Unsatisfied dependency expressed through object property '{0}': {1}", + propertyName, + message)) + { + } + + /// + /// Creates a new instance of the UnsatisfiedDependencyException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected UnsatisfiedDependencyException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/AbstractObjectDefinitionParser.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/AbstractObjectDefinitionParser.cs new file mode 100644 index 00000000..2d15e7d0 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/AbstractObjectDefinitionParser.cs @@ -0,0 +1,212 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports +using System.Xml; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Util; +#endregion + +namespace Spring.Objects.Factory.Xml +{ + + + /// + /// Abstract implementation providing + /// a number of convenience methods and a + /// template method + /// that subclasses must override to provide the actual parsing logic. + /// + /// + /// Use this implementation when you want + /// to parse some arbitrarily complex XML into one or more + /// ObjectDefinitions. If you just want to parse some + /// XML into a single IObjectDefinition, you may wish to consider + /// the simpler convenience extensions of this class, namely + /// and + /// + /// + /// Rob Harrop + /// Juergen Hoeller + /// Rick Evans + /// Mark Pollack (.NET) + /// $Id: AbstractObjectDefinitionParser.cs,v 1.4 2007/05/26 19:25:54 markpollack Exp $ + public abstract class AbstractObjectDefinitionParser : IObjectDefinitionParser + { + /// + /// Constant for the ID attribute + /// + public static readonly string ID_ATTRIBUTE = "id"; + + #region Properties + + /// + /// Gets a value indicating whether an ID should be generated instead of read + /// from the passed in XmlElement. + /// + /// Note that this flag is about always generating an ID; the parser + /// won't even check for an "id" attribute in this case. + /// + /// true if should generate id; otherwise, false. + protected virtual bool ShouldGenerateId + { + get { return false; } + } + + /// + /// Gets a value indicating whether an ID should be generated instead if the + /// passed in XmlElement does not specify an "id" attribute explicitly. + /// + /// Disabled by default; subclasses can override this to enable ID generation + /// as fallback: The parser will first check for an "id" attribute in this case, + /// only falling back to a generated ID if no value was specified. + /// + /// true if should generate id if no value was specified; otherwise, false. + /// + protected virtual bool ShouldGenerateIdAsFallback + { + get { return false; } + } + + #endregion + + #region IObjectDefinitionParser Members + + + /// + /// Parse the specified XmlElement and register the resulting + /// ObjectDefinitions with the IObjectDefinitionRegistry + /// embedded in the supplied + /// + /// The element to be parsed. + /// TThe object encapsulating the current state of the parsing process. + /// Provides access to a IObjectDefinitionRegistry + /// The primary object definition. + /// + ///

    + /// This method is never invoked if the parser is namespace aware + /// and was called to process the root node. + ///

    + ///
    + public IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + AbstractObjectDefinition definition = ParseInternal(element, parserContext); + + if (!parserContext.IsNested) + { + string id = null; + try + { + id = ResolveId(element, definition, parserContext); + if (!StringUtils.HasText(id)) + { + parserContext.ReaderContext.ReportException(element, "null", + "Id is required for element '" + element.LocalName + "' when used as a top-level tag", null); + } + ObjectDefinitionHolder holder = new ObjectDefinitionHolder(definition, id); + RegisterObjectDefinition(holder, parserContext.Registry); + } + catch (ObjectDefinitionStoreException ex) + { + parserContext.ReaderContext.ReportException(element, id, ex.Message); + return null; + } + } + return definition; + + + } + + #endregion + + #region Methods + + /// + /// Resolves the ID for the supplied . + /// + /// + /// When using generation, a name is generated automatically. + /// Otherwise, the ID is extracted from the "id" attribute, potentially with a + /// fallback to a generated id. + /// + /// The element that the object definition has been built from. + /// The object definition to be registered. + /// The the object encapsulating the current state of the parsing process; + /// provides access to a + /// the resolved id + /// + /// if no unique name could be generated for the given object definition + /// + protected virtual string ResolveId(XmlElement element, AbstractObjectDefinition definition, ParserContext parserContext) + { + + if (ShouldGenerateId) { + return parserContext.ReaderContext.GenerateObjectName(definition); + } + else { + string id = element.GetAttribute(ID_ATTRIBUTE); + if (!StringUtils.HasText(id) && ShouldGenerateIdAsFallback) { + id = parserContext.ReaderContext.GenerateObjectName(definition); + } + return id; + } + } + + /// + /// Registers the supplied with the supplied + /// . + /// + /// Subclasses can override this method to control whether or not the supplied + /// is actually even registered, or to + /// register even more objects. + /// + /// The default implementation registers the supplied + /// with the supplied only if the IsNested + /// parameter is false, because one typically does not want inner objects + /// to be registered as top level objects. + /// + /// + /// + /// The object definition to be registered. + /// The registry that the bean is to be registered with. + protected virtual void RegisterObjectDefinition(ObjectDefinitionHolder definition, IObjectDefinitionRegistry registry) + { + ObjectDefinitionReaderUtils.RegisterObjectDefinition(definition, registry); + } + + #endregion + + + #region Abstract Methods + + /// + /// Central template method to actually parse the supplied XmlElement + /// into one or more IObjectDefinitions. + /// + /// The element that is to be parsed into one or more s + /// The the object encapsulating the current state of the parsing process; + /// provides access to a + /// The primary IObjectDefinition resulting from the parsing of the supplied XmlElement + protected abstract AbstractObjectDefinition ParseInternal(XmlElement element, ParserContext parserContext); + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/AbstractSimpleObjectDefinitionParser.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/AbstractSimpleObjectDefinitionParser.cs new file mode 100644 index 00000000..7e45a7d6 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/AbstractSimpleObjectDefinitionParser.cs @@ -0,0 +1,37 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Convenient base class for when there exists a one-to-one mapping + /// between attribute names on the element that is to be parsed and + /// the property names on the Type being configured. + /// + /// + /// + /// + /// Mark Pollack + /// $Id: AbstractSimpleObjectDefinitionParser.cs,v 1.1 2007/05/26 19:25:54 markpollack Exp $ + public class AbstractSimpleObjectDefinitionParser : AbstractSingleObjectDefinitionParser + { + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/AbstractSingleObjectDefinitionParser.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/AbstractSingleObjectDefinitionParser.cs new file mode 100644 index 00000000..56fba856 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/AbstractSingleObjectDefinitionParser.cs @@ -0,0 +1,143 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Xml; +using Spring.Objects.Factory.Support; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Base Type for those implementations that + /// need to parse and define just a single IObjectDefinition. + /// + /// + /// Extend this parser Type when you want to create a single object definition + /// from an arbitrarily complex XML element. You may wish to consider extending + /// the when you want to create a + /// single Object definition from a relatively simple custom XML element. + /// The resulting ObjectDefinition will be automatically registered + /// with the ObjectDefinitionRegistry. Your job simply is to parse the + /// custom XML element into a single ObjectDefinition + /// + /// Rob Harrop + /// Juergen Hoeller + /// Rick Evans + /// Mark Pollack (.NET) + /// $Id: AbstractSingleObjectDefinitionParser.cs,v 1.3 2007/05/29 14:42:20 markpollack Exp $ + public class AbstractSingleObjectDefinitionParser : AbstractObjectDefinitionParser + { + #region Methods + + /// + /// Central template method to actually parse the supplied XmlElement + /// into one or more IObjectDefinitions. + /// + /// The element that is to be parsed into one or more s + /// The the object encapsulating the current state of the parsing process; + /// provides access to a + /// + /// The primary IObjectDefinition resulting from the parsing of the supplied XmlElement + /// + protected override AbstractObjectDefinition ParseInternal(XmlElement element, ParserContext parserContext) + { + ObjectDefinitionBuilder builder; + Type objectType = GetObjectType(element); + if (objectType != null) + { + builder = + ObjectDefinitionBuilder.RootObjectDefinition(parserContext.ReaderContext.ObjectDefinitionFactory, + objectType); + } + else + { + throw new NotSupportedException("Need to refactor IObjectDefinitionFactory to not resolve object type names"); + } + if (parserContext.IsNested) + { + // Inner object definition must receive same singleton status as containing object. + builder.SetSingleton(parserContext.ContainingObjectDefinition.IsSingleton); + } + if (parserContext.IsDefaultLazyInit) + { + // Default-lazy-init applies to custom object definitions as well. + builder.SetLazyInit(true); + } + DoParse(element, parserContext, builder); + return builder.ObjectDefinition; + + } + + /// + /// Gets the type of the object corresponding to the supplied XmlElement. + /// + /// Note that, for application classes, it is generally preferable to override + /// GetObjectTypeName instad, in order to avoid a direct + /// dependence on the object implementation class. The ObjectDefinitionParser + /// and its IXmlObjectDefinitionParser (namespace parser) can be used within an + /// IDE add-in then, even if the application classses are not available in the add-ins + /// AppDomain. + /// + /// The element. + /// The Type of the class that is being defined via parsing the supplied + /// Element. + protected virtual Type GetObjectType(XmlElement element) + { + return null; + } + + /// + /// Gets the name of the object type name (FullName) corresponding to the supplied XmlElement. + /// + /// The element. + /// The type name of the object that is being defined via parsing the supplied + /// XmlElement. + protected virtual string GetObjectTypeName(XmlElement element) + { + return null; + } + + /// + /// Parse the supplied XmlElement and populate the supplied ObjectDefinitionBuilder as required. + /// + /// The default implementation delegates to the DoParse version without + /// ParameterContext argument. + /// The element. + /// The parser context. + /// The builder used to define the IObjectDefinition. + protected virtual void DoParse(XmlElement element, ParserContext parserContext, ObjectDefinitionBuilder builder) + { + DoParse(element, builder); + } + + /// + /// Parse the supplied XmlElement and populate the supplied ObjectDefinitionBuilder as required. + /// + /// The default implementation does nothing. + /// The element. + /// The builder used to define the IObjectDefinition. + protected virtual void DoParse(XmlElement element, ObjectDefinitionBuilder builder) + { + + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/DefaultObjectDefinitionDocumentReader.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/DefaultObjectDefinitionDocumentReader.cs new file mode 100644 index 00000000..794f2837 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/DefaultObjectDefinitionDocumentReader.cs @@ -0,0 +1,341 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.IO; +using System.Xml; +using Common.Logging; +using Spring.Core.IO; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// XML resource reader. + /// + /// + ///

    + /// Navigates through an XML resource and invokes parsers registered + /// with the . + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: DefaultObjectDefinitionDocumentReader.cs,v 1.9 2007/08/27 14:49:43 oakinger Exp $ + public class DefaultObjectDefinitionDocumentReader : IObjectDefinitionDocumentReader + { + #region Constants + + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof(DefaultObjectDefinitionDocumentReader)); + + #endregion + + #region Fields + + private XmlReaderContext readerContext; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the DefaultObjectDefinitionDocumentReader class. + /// + public DefaultObjectDefinitionDocumentReader() + { + } + + #endregion + + #region Properties + + /// + /// Gets the reader context. + /// + /// The reader context. + public XmlReaderContext ReaderContext + { + get { return readerContext; } + } + + #endregion + + #region Methods + + /// + /// Read object definitions from the given DOM element, and register + /// them with the given object registry. + /// + /// The DOM element containing object definitions, usually the + /// root (document) element. + /// The current context of the reader. Includes + /// the resource being parsed + /// + /// The number of object definitions that were loaded. + /// + /// + /// In case of parsing errors. + /// + public void RegisterObjectDefinitions(XmlDocument doc, XmlReaderContext readerContext) + { + //int objectDefinitionCounter = 0; + + this.readerContext = readerContext; + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Loading object definitions."); + } + + #endregion + + XmlElement root = doc.DocumentElement; + + ObjectDefinitionParserHelper parserHelper = CreateHelper(readerContext, root); + + + PreProcessXml(root); + + ParseObjectDefinitions(root, parserHelper); + + PostProcessXml(root); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + "Found {0} <{1}> elements defining objects.", + readerContext.Registry.ObjectDefinitionCount, + ObjectDefinitionConstants.ObjectElement)); + } + + #endregion + } + + /// + /// Parses object definitions starting at the given + /// using the passed . + /// + /// The root element to start parsing from. + /// The instance to use. + protected virtual void ParseObjectDefinitions(XmlElement root, ObjectDefinitionParserHelper helper) + { + foreach (XmlNode node in root.ChildNodes) + { + if (node.NodeType == XmlNodeType.Element) + { + XmlElement element = (XmlElement) node; + INamespaceParser parser = GetNamespaceParser(element, helper); + ParserContext parserContext = new ParserContext(helper.ReaderContext, helper); + parser.ParseElement(element, parserContext); + } + } + } + + /// + /// Parses the default element. + /// + /// The element. + /// The helper. + private void ParseDefaultElement(XmlElement element, ObjectDefinitionParserHelper helper) + { + if (element.LocalName == ObjectDefinitionConstants.ImportElement) + { + ImportObjectDefinitionResource(element); + } + else if (element.LocalName == ObjectDefinitionConstants.AliasElement) + { + ParseAlias(element, ReaderContext.Registry); + } + else if (element.LocalName == ObjectDefinitionConstants.ObjectElement) + { + RegisterObjectDefinition(element, helper); + } + } + + /// + /// Loads external XML object definitions from the resource described by the supplied + /// . + /// + /// The XML element describing the resource. + /// + /// If the resource could not be imported. + /// + protected virtual void ImportObjectDefinitionResource(XmlElement resource) + { + string location = resource.GetAttribute(ObjectDefinitionConstants.ImportResourceAttribute); + try + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "Attempting to import object definitions from '{0}'.", location)); + } + + #endregion + + IResource importResource = ReaderContext.Resource.CreateRelative(location); + ReaderContext.Reader.LoadObjectDefinitions(importResource); + } + catch (IOException ex) + { + ReaderContext.ReportException(resource, null, string.Format( + CultureInfo.InvariantCulture, + "Invalid relative resource location '{0}' to import object definitions from.", + location), ex); + } + } + + /// + /// Parses the given alias element, registering the alias with the registry. + /// + /// The alias element. + /// The registry. + protected virtual void ParseAlias(XmlElement aliasElement, IObjectDefinitionRegistry registry) + { + string name = aliasElement.GetAttribute(ObjectDefinitionConstants.NameAttribute); + string alias = aliasElement.GetAttribute(ObjectDefinitionConstants.AliasAttribute); + registry.RegisterAlias(name, alias); + } + + /// + /// Parse an object definition and register it with the object factory.. + /// + /// The element containing the object definition. + /// The helper. + /// + protected virtual void RegisterObjectDefinition(XmlElement element, ObjectDefinitionParserHelper helper) + { + ObjectDefinitionHolder holder = null; + try + { + INamespaceParser parser = GetNamespaceParser(element, helper); + + //holder = ParseObjectDefinition(element, parserContext); + //holder = helper.ParseObjectDefinitionElement(element); + if (holder == null) + { + return; + } + } + catch (Exception ex) + { + throw new ObjectDefinitionStoreException( + string.Format("Failed parsing object definition '{0}'", element.OuterXml), ex); + } + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + CultureInfo.InvariantCulture, + "Registering object definition with id '{0}'.", holder.ObjectName)); + } + + #endregion + + ObjectDefinitionReaderUtils.RegisterObjectDefinition(holder, ReaderContext.Registry); + } + + /// + /// + /// Allow the XML to be extensible by processing any custom element types last, + /// after we finished processing the objct definitions. This method is a natural + /// extension point for any other custom post-processing of the XML. + /// + /// The default implementation is empty. Subclasses can override this method to + /// convert custom elements into standard Spring object definitions, for example. + /// Implementors have access to the parser's object definition reader and the + /// underlying XML resource, through the corresponding properties. + /// + /// + /// The root. + protected virtual void PostProcessXml(XmlElement root) + { + } + + /// + /// Allow the XML to be extensible by processing any custom element types first, + /// before we start to process the object definitions. + /// + /// This method is a natural + /// extension point for any other custom pre-processing of the XML. + ///

    The default implementation is empty. Subclasses can override this method to + /// convert custom elements into standard Spring object definitions, for example. + /// Implementors have access to the parser's object definition reader and the + /// underlying XML resource, through the corresponding properties. + ///

    + ///
    + /// The root element of the XML document. + protected virtual void PreProcessXml(XmlElement root) + { + } + + /// + /// Creates an instance for the given and element. + /// + /// the to create the + /// the root to start reading from + /// a new instance + protected virtual ObjectDefinitionParserHelper CreateHelper(XmlReaderContext readerContext, XmlElement root) + { + ObjectDefinitionParserHelper helper = new ObjectDefinitionParserHelper(readerContext); + helper.InitDefaults(root); + return helper; + } + + private INamespaceParser GetNamespaceParser(XmlElement element, ObjectDefinitionParserHelper helper) + { + INamespaceParser parser = NamespaceParserRegistry.GetParser(element.NamespaceURI); + if (parser == null) + { + helper.ReaderContext.ReportException(element, null, GetNoParserForNamespaceMessage(element.NamespaceURI)); + } + return parser; + } + + private string GetNoParserForNamespaceMessage(string namespaceURI) + { + return "There is no parser registered for namespace '" + namespaceURI + "'"; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/DocumentDefaultsDefinition.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/DocumentDefaultsDefinition.cs new file mode 100644 index 00000000..bfe2372c --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/DocumentDefaultsDefinition.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Simple class that holds the defaults specified at the <objects> + /// level in a standard Spring XML object definition document: + /// default-lazy-init, default-autowire, etc. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class DocumentDefaultsDefinition + { + private string autowire; + private string dependencyCheck; + private string lazyInit; + + /// + /// Gets or sets the autowire setting for the document that's currently parsed. + /// + /// The autowire. + public string Autowire + { + get { return autowire; } + set { autowire = value; } + } + + /// + /// Gets or sets the dependency-check setting for the document that's currently parsed + /// + /// The dependency check. + public string DependencyCheck + { + get { return dependencyCheck; } + set { dependencyCheck = value; } + } + + /// + /// Gets or sets the lazy-init flag for the document that's currently parsed. + /// + /// The lazy init. + public string LazyInit + { + get { return lazyInit; } + set { lazyInit = value; } + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/INamespaceParser.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/INamespaceParser.cs new file mode 100644 index 00000000..478696d1 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/INamespaceParser.cs @@ -0,0 +1,100 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Xml; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + + /// + /// Strategy interface for parsing XML object definitions. + /// + /// + ///

    + /// Used by + /// for actually parsing a DOM document or + /// fragment. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// Sandu Turcan (.NET) + /// $Id: INamespaceParser.cs,v 1.1 2007/08/07 21:23:37 markpollack Exp $ + public interface INamespaceParser + { + + /// + /// Invoked by after construction but before any + /// elements have been parsed. + /// + void Init(); + + + /// + /// Parse the specified element and register any resulting + /// IObjectDefinitions with the IObjectDefinitionRegistry that is + /// embedded in the supplied ParserContext. + /// + /// + /// Implementations should return the primary IObjectDefinition + /// that results from the parse phase if they wish to used nested + /// inside (for example) a <property> tag. + /// Implementations may return null if they will not + /// be used in a nested scenario. + /// + /// + /// The element to be parsed into one or more IObjectDefinitions + /// The object encapsulating the current state of the parsing + /// process. + /// + /// The primary IObjectDefinition (can be null as explained above) + /// + IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext); + + + /// + /// Parse the specified XmlNode and decorate the supplied ObjectDefinitionHolder, + /// returning the decorated definition. + /// + /// The XmlNode may either be an XmlAttribute or an XmlElement, depending on + /// whether a custom attribute or element is being parsed. + /// Implementations may choose to return a completely new definition, + /// which will replace the original definition in the resulting IApplicationContext/IObjectFactory. + /// + /// The supplied ParserContext can be used to register any additional objects needed to support + /// the main definition. + /// + /// The source element or attribute that is to be parsed. + /// The current object definition. + /// The object encapsulating the current state of the parsing + /// process. + /// The decorated definition (to be registered in the IApplicationContext/IObjectFactory), + /// or simply the original object definition if no decoration is required. A null value is strickly + /// speaking invalid, but will leniently treated like the case where the original object definition + /// gets returned. + ObjectDefinitionHolder Decorate(XmlNode node, ObjectDefinitionHolder definition, ParserContext parserContext); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/IObjectDefinitionDocumentReader.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/IObjectDefinitionDocumentReader.cs new file mode 100644 index 00000000..c7b098ac --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/IObjectDefinitionDocumentReader.cs @@ -0,0 +1,57 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Xml; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// SPI for parsing an XML document that contains Spring object definitions. + /// Used by for actually parsing a DOM + /// document. + /// + /// Instantiated per document to parse: Implementations can hold state in + /// instance variables during the execution of the RegisterObjectDefinitions + /// method, for example global settings that are defined for all object definitions + /// in the document. + /// + /// Juergen Hoeller + /// Rob Harrop + /// Mark Pollack (.NET) + /// + public interface IObjectDefinitionDocumentReader + { + /// + /// Read object definitions from the given DOM element, and register + /// them with the given object registry. + /// + /// The DOM element containing object definitions, usually the + /// root (document) element. + /// The current context of the reader. Includes + /// the resource being parsed + /// + /// The number of object definitions that were loaded. + /// + /// + /// In case of parsing errors. + /// + void RegisterObjectDefinitions(XmlDocument doc, XmlReaderContext readerContext); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/IObjectDefinitionParser.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/IObjectDefinitionParser.cs new file mode 100644 index 00000000..af799065 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/IObjectDefinitionParser.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion +using System.Xml; +using Spring.Objects.Factory.Config; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Interface used to handle custom, top-level tags. + /// + /// Implementations are free to turn the metadata in the custom tag into as + /// many as required. + /// + /// Rob Harrop + /// Mark Pollack (.NET) + /// $Id: IObjectDefinitionParser.cs,v 1.3 2007/05/25 03:23:21 markpollack Exp $ + public interface IObjectDefinitionParser + { + /// + /// Parse the specified XmlElement and register the resulting + /// ObjectDefinitions with the IObjectDefinitionRegistry + /// embedded in the supplied + /// + /// + ///

    + /// This method is never invoked if the parser is namespace aware + /// and was called to process the root node. + ///

    + ///
    + /// + /// The element to be parsed. + /// + /// + /// TThe object encapsulating the current state of the parsing process. + /// Provides access to a IObjectDefinitionRegistry + /// + /// + /// The primary object definition. + /// + IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserAttribute.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserAttribute.cs new file mode 100644 index 00000000..d389175b --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserAttribute.cs @@ -0,0 +1,100 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Attribute that should be used to specify the default namespace + /// and schema location for a custom namespace parser. + /// + /// + /// If + /// + /// Aleksandar Seovic + /// $Id: NamespaceParserAttribute.cs,v 1.1 2007/08/08 00:34:06 bbaia Exp $ + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] + public class NamespaceParserAttribute : Attribute + { + private string ns; + private string schemaLocation; + private Type schemaLocationAssemblyHint; + + /// + /// Creates a new instance of . + /// + public NamespaceParserAttribute() + {} + + /// + /// Gets or sets the default namespace for the configuration parser. + /// + /// + /// The default namespace for the configuration parser. + /// + public string Namespace + { + get { return ns; } + set { ns = value; } + } + + /// + /// Gets or sets the default schema location for the configuration parser. + /// + /// + /// The default schema location for the configuration parser. + /// + /// + /// If the property is set, the will always resolve to an assembly-resource + /// and the set will be interpreted relative to this assembly. + /// + public string SchemaLocation + { + get + { + if (schemaLocationAssemblyHint != null) + { + return "assembly://" + schemaLocationAssemblyHint.Assembly.FullName + schemaLocation; + } + return schemaLocation; + } + set { schemaLocation = value; } + } + + /// + /// Gets or sets a type from the assembly containing the schema + /// + /// + /// If this property is set, the will always resolve to an assembly-resource + /// and the will be interpreted relative to this assembly. + /// + public Type SchemaLocationAssemblyHint + { + get { return schemaLocationAssemblyHint; } + set { schemaLocationAssemblyHint = value; } + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserRegistry.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserRegistry.cs new file mode 100644 index 00000000..9716a8d0 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserRegistry.cs @@ -0,0 +1,287 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Xml; +using System.Xml.Schema; +using Spring.Core; +using Spring.Core.IO; +using Spring.Core.TypeResolution; +using Spring.Util; +using Spring.Validation; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Provides a resolution mechanism for configuration parsers. + /// + /// + ///

    + /// The uses this registry + /// class to find the parser handling a specific namespace. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: NamespaceParserRegistry.cs,v 1.7 2007/09/17 01:12:48 bbaia Exp $ + public class NamespaceParserRegistry + { + /// + /// Name of the .Net config section that contains definitions + /// for custom config parsers. + /// + private const string ConfigParsersSectionName = "spring/parsers"; + + #region Fields + + private static IDictionary parsers = new HybridDictionary(); + +#if !NET_2_0 + private static XmlSchemaCollection schemas = new XmlSchemaCollection(); +#else + private static XmlSchemaSet schemas = new XmlSchemaSet(); +#endif + + #endregion + + /// + /// Creates a new instance of the NamespaceParserRegistry class. + /// + static NamespaceParserRegistry() + { + lock (parsers.SyncRoot) + lock (schemas) + { + //TODO - externalize default list of parsers. + RegisterParser(new ObjectsNamespaceParser()); + //This is done simple as a means to avoid cyclic dependencies with Factory.Xml + //which implementations of parsers typically use. + RegisterParser(ObjectUtils.InstantiateType(typeof(NamespaceParserRegistry).Assembly, + "Spring.Validation.Config.ValidationNamespaceParser") as INamespaceParser); + + // register custom config parsers + ConfigurationUtils.GetSection(ConfigParsersSectionName); + } + } + + /// + /// Returns a parser for the given namespace. + /// + /// + /// The namespace for which to lookup the parser implementation. + /// + /// + /// A parser for a given , or + /// if no parser was found. + /// + public static INamespaceParser GetParser(string namespaceURI) + { + return (INamespaceParser) parsers[namespaceURI]; + } + + /// + /// Returns a schema collection containing validation schemas for all registered parsers. + /// + /// + /// A schema collection containing validation schemas for all registered parsers. + /// +#if !NET_2_0 + public static XmlSchemaCollection GetSchemas() +#else + public static XmlSchemaSet GetSchemas() +#endif + { + return schemas; + } + + /// + /// Pegisters parser, using default namespace and schema location + /// as defined by the . + /// + /// + /// The of the parser that will be activated + /// when an element in its default namespace is encountered. + /// + /// + /// If is . + /// + public static void RegisterParser(Type parserType) + { + RegisterParser(parserType, null, null); + } + + /// + /// Associates a parser with a namespace. + /// + /// + /// + /// Parsers registered with the same as that + /// of a parser that has previously been registered will overwrite the existing + /// parser. + /// + /// + /// + /// The of the parser that will be activated + /// when the attendant is + /// encountered. + /// + /// + /// The namespace with which to associate instance of the parser. + /// + /// + /// The location of the XML schema that should be used for validation + /// of the XML elements that belong to the specified namespace + /// (can be any valid Spring.NET resource URI). + /// + /// + /// If the is not a + /// that implements the + /// interface. + /// + /// + /// If is . + /// + public static void RegisterParser(Type parserType, string namespaceUri, string schemaLocation) + { + AssertUtils.ArgumentNotNull(parserType, "parserType"); + + if (!(typeof(INamespaceParser)).IsAssignableFrom(parserType)) + { + throw new ArgumentException(string.Format("The [{0}] Type must implement the IXmlObjectDefinitionParser interface.", + parserType.Name), "parserType"); + } + + RegisterParser((INamespaceParser) ObjectUtils.InstantiateType(parserType), namespaceUri, schemaLocation); + } + + /// + /// Pegisters parser, using default namespace and schema location + /// as defined by the . + /// + /// + /// The parser instance. + /// + /// + /// If is . + /// + public static void RegisterParser(INamespaceParser parser) + { + RegisterParser(parser, null, null); + } + + /// + /// Associates a parser with a namespace. + /// + /// + /// + /// Parsers registered with the same as that + /// of a parser that has previously been registered will overwrite the existing + /// parser. + /// + /// + /// + /// The namespace with which to associate instance of the parser. + /// + /// + /// The parser instance. + /// + /// + /// The location of the XML schema that should be used for validation + /// of the XML elements that belong to the specified namespace + /// (can be any valid Spring.NET resource URI). + /// + /// + /// If is , or if + /// is not specified and parser class + /// does not have default value defined using . + /// + public static void RegisterParser(INamespaceParser parser, string namespaceUri, string schemaLocation) + { + AssertUtils.ArgumentNotNull(parser, "parser"); + + // determine and use defaults for the namespace and schema location, if necessary + if (StringUtils.IsNullOrEmpty(namespaceUri) || StringUtils.IsNullOrEmpty(schemaLocation)) + { + NamespaceParserAttribute defaults = GetDefaults(parser); + if (defaults == null) + { + throw new ArgumentNullException( + "Either default or an explicit namespace value must be specified for a configuration parser."); + } + if (StringUtils.IsNullOrEmpty(namespaceUri)) + { + namespaceUri = defaults.Namespace; + } + if (StringUtils.IsNullOrEmpty(schemaLocation)) + { + schemaLocation = defaults.SchemaLocation; + } + } + + // initialize the parser + parser.Init(); + + // register parser + lock (parsers.SyncRoot) + lock (schemas) + { + parsers[namespaceUri] = parser; + if (StringUtils.HasText(schemaLocation) && !schemas.Contains(namespaceUri)) + { + IResourceLoader resourceLoader = new ConfigurableResourceLoader(); + IResource schema = resourceLoader.GetResource(schemaLocation); + try { + schemas.Add(namespaceUri, new XmlTextReader(schema.InputStream)); + } catch (Exception e) + { + throw new ArgumentException("Could not load schema from resource = " + schema, e); + } + + } + } + } + + /// + /// Returns default values for the parser namespace and schema location as + /// defined by the . + /// + /// + /// A parser instance. + /// + /// + /// A instance containing + /// default values for the parser namsepace and schema location + /// + private static NamespaceParserAttribute GetDefaults(INamespaceParser parser) + { + object[] attrs = parser.GetType().GetCustomAttributes(typeof(NamespaceParserAttribute), true); + if (attrs.Length > 0) + { + return (NamespaceParserAttribute)attrs[0]; + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserSupport.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserSupport.cs new file mode 100644 index 00000000..81aab2aa --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/NamespaceParserSupport.cs @@ -0,0 +1,120 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Xml; +using Spring.Objects.Factory.Config; +using Spring.Util; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Support class for implementing custom namespace parsers. + /// + /// Parsing of individual elements is done via a ObjectDefintionParser. + /// Provides the RegisterObjectDefinitionParser for registering a ObjectDefintionParser + /// to handle a specific element. + /// Rob Harrop + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: NamespaceParserSupport.cs,v 1.9 2008/02/20 14:29:22 bbaia Exp $ + public abstract class NamespaceParserSupport : INamespaceParser + { + + private readonly IDictionary objectParsers = new Hashtable(); + + #region IXmlObjectDefinitionParser Members + + /// + /// Invoked by after construction but before any + /// elements have been parsed. + /// + public abstract void Init(); + + /// + /// Parses an element under the root node, typically + /// an object definition or import statement. + /// + /// + /// The element to be parsed. + /// + /// + /// The parser context. + /// + /// + /// The number of object defintions created from this element. + /// + public virtual IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + return FindParserForElement(element, parserContext).ParseElement(element, parserContext); + } + + + /// + /// Parse the specified XmlNode and decorate the supplied ObjectDefinitionHolder, + /// returning the decorated definition. + /// + /// The XmlNode may either be an XmlAttribute or an XmlElement, depending on + /// whether a custom attribute or element is being parsed. + /// Implementations may choose to return a completely new definition, + /// which will replace the original definition in the resulting IApplicationContext/IObjectFactory. + /// + /// The supplied ParserContext can be used to register any additional objects needed to support + /// the main definition. + /// + /// The source element or attribute that is to be parsed. + /// The current object definition. + /// The object encapsulating the current state of the parsing + /// process. + /// The decorated definition (to be registered in the IApplicationContext/IObjectFactory), + /// or simply the original object definition if no decoration is required. A null value is strickly + /// speaking invalid, but will leniently treated like the case where the original object definition + /// gets returned. + public ObjectDefinitionHolder Decorate(XmlNode node, ObjectDefinitionHolder definition, + ParserContext parserContext) + { + return null; + } + + private IObjectDefinitionParser FindParserForElement(XmlElement element, ParserContext parserContext) + { + IObjectDefinitionParser parser = objectParsers[element.LocalName] as IObjectDefinitionParser; + if (parser == null) + { + parserContext.ReaderContext.ReportException(element, "unknown object name", "Cannot locate IObjectDefinitionParser for element [" + + element.LocalName + "]"); + } + return parser; + + } + + /// + /// Register the specified for the given + /// + protected virtual void RegisterObjectDefinitionParser(string elementName, IObjectDefinitionParser parser) + { + AssertUtils.ArgumentNotNull(elementName, "elementName"); + AssertUtils.ArgumentNotNull(parser, "parser"); + objectParsers[elementName] = parser; + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs new file mode 100644 index 00000000..4f7a2dc0 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs @@ -0,0 +1,772 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Constants defining the structure and values associated with the + /// Spring.NET XML object definition format. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: ObjectDefinitionConstants.cs,v 1.8 2006/11/17 20:41:39 aseovic Exp $ + public sealed class ObjectDefinitionConstants + { + /// + /// Value of a boolean attribute that represents + /// . + /// + /// + ///

    + /// Anything else represents . + ///

    + ///
    + public const string TrueValue = "true"; + + /// + /// Signifies that a default value is to be applied. + /// + public const string DefaultValue = "default"; + + /// + /// Defines an external XML object definition resource. + /// + public const string ImportElement = "import"; + + /// + /// Specifies the relative path to an external XML object definition + /// resource. + /// + public const string ImportResourceAttribute = "resource"; + + /// + /// Defines an alias for an object definition. + /// + public const string AliasElement = "alias"; + + /// + /// Specifies the alias of an object definition. + /// + public const string AliasAttribute = "alias"; + + /// + /// Specifies the default lazy initialization mode. + /// + public const string DefaultLazyInitAttribute = "default-lazy-init"; + + /// + /// Specifies the default dependency checking mode. + /// + public const string DefaultDependencyCheckAttribute + = "default-dependency-check"; + + /// + /// Specifies the default autowire mode. + /// + public const string DefaultAutowireAttribute = "default-autowire"; + + /// + /// Defines a single named object. + /// + public const string ObjectElement = "object"; + + /// + /// Element containing informative text describing the purpose of the + /// enclosing element. + /// + /// + ///

    + /// Always optional. + ///

    + ///

    + /// Used primarily for user documentation of XML object definition + /// documents. + ///

    + ///
    + public const string DescriptionElement = "description"; + + /// + /// Specifies a . + /// + /// + ///

    + /// Does not have to be fully assembly qualified, but it is recommended + /// that the names of one's objects are + /// specified explicitly. + ///

    + ///
    + public const string TypeAttribute = "type"; + + /// + /// The name or alias of the parent object definition that a child + /// object definition inherits from. + /// + public const string ParentAttribute = "parent"; + + /// + /// Objects can be identified by an id, to enable reference checking. + /// + /// + ///

    + /// There are constraints on a valid XML id: if you want to reference + /// your object in .NET code using a name that's illegal as an XML id, + /// use the optional "name" attribute + /// (). + /// If neither given, the objects name is + /// used as id. + ///

    + ///
    + public const string IdAttribute = "id"; + + /// + /// Can be used to create one or more aliases illegal in an id. + /// + /// + ///

    + /// Multiple aliases can be separated by any number of spaces, + /// semicolons, or commas + /// (). + ///

    + ///

    + /// Always optional. + ///

    + ///
    + public const string NameAttribute = "name"; + + /// + /// Is this object a "singleton" (one shared instance, which will + /// be returned by all calls to + /// with the id), or a + /// "prototype" (independent instance resulting from each call to + /// ). + /// + /// + ///

    + /// Singletons are most commonly used, and are ideal for multi-threaded + /// service objects. + ///

    + ///
    + /// + public const string SingletonAttribute = "singleton"; + + /// + /// Controls object scope. Only applicable to ASP.NET web applications. + /// + /// + ///

    + /// Scope can be defined as either application, session or request. It + /// defines when "singleton" instances are initialized, but has no + /// effect on prototype definitions. + ///

    + ///
    + public const string ScopeAttribute = "scope"; + + /// + /// The names of the objects that this object depends on being + /// initialized. + /// + /// + ///

    + /// The object factory will guarantee that these objects + /// get initialized before this object definition. + ///

    + /// + /// Dependencies are normally expressed through object properties or + /// constructor arguments. This property should just be necessary for + /// other kinds of dependencies such as statics (*ugh*) or database + /// preparation on startup. + /// + ///
    + public const string DependsOnAttribute = "depends-on"; + + /// + /// Optional attribute for the name of the custom initialization method + /// to invoke after setting object properties. + /// + /// + ///

    + /// The method must have no arguments. + ///

    + ///
    + public const string InitMethodAttribute = "init-method"; + + /// + /// Optional attribute for the name of the custom destroy method to + /// invoke on object factory shutdown. + /// + /// + ///

    + /// Valid destroy methods have either of the following signatures... + /// + /// void MethodName() + /// void MethodName(bool force) + /// + ///

    + /// + /// Only invoked on singleton objects! + /// + ///
    + public const string DestroyMethodAttribute = "destroy-method"; + + /// + /// A constructor argument : the constructor-arg tag can have an + /// optional type attribute, to specify the exact type of the + /// constructor argument + /// + /// + ///

    + /// Only needed to avoid ambiguities, e.g. in case of 2 single + /// argument constructors that can both be converted from a + /// . + ///

    + ///
    + public const string ConstructorArgElement = "constructor-arg"; + + /// + /// The constructor-arg tag can have an optional index attribute, + /// to specify the exact index in the constructor argument list. + /// + /// + ///

    + /// Only needed to avoid ambiguities, e.g. in case of 2 arguments of + /// the same type. + ///

    + ///
    + public const string IndexAttribute = "index"; + + /// + /// The constructor-arg tag can have an optional named parameter + /// attribute, to specify a named parameter in the constructor + /// argument list. + /// + public const string ArgumentNameAttribute = "name"; + + /// + /// Is this object "abstract", i.e. not meant to be instantiated itself + /// but rather just serving as parent for concrete child object + /// definitions? + /// + /// + ///

    + /// Default is . Specify + /// to tell the object factory to not try to instantiate that + /// particular object in any case. + ///

    + ///
    + public const string AbstractAttribute = "abstract"; + + /// + /// A property definition : object definitions can have zero or more + /// properties. + /// + /// + ///

    + /// Spring.NET supports primitives, references to other objects in the + /// same or related factories, lists, dictionaries, and name value + /// collections. + ///

    + ///
    + public const string PropertyElement = "property"; + + /// + /// A reference to another managed object or static + /// . + /// + public const string RefElement = "ref"; + + /// + /// ID refs must specify a name of the target object. + /// + public const string IdRefElement = "idref"; + + /// + /// A reference to the name of another managed object in the same + /// context. + /// + public const string ObjectRefAttribute = "object"; + + /// + /// A reference to the name of another managed object in the same + /// context. + /// + /// + ///

    + /// Local references, using the "local" attribute, have to use object + /// ids; they can be checked by a parser, thus should be preferred for + /// references within the same object factory XML file. + ///

    + ///
    + public const string LocalRefAttribute = "local"; + + /// + /// Alternative to type attribute for factory-method usage. + /// + /// + ///

    + /// If this is specified, no type attribute should be used. This should + /// be set to the name of an object in the current or ancestor + /// factories that contains the relevant factory method. This allows + /// the factory itself to be configured using Dependency Injection, and + /// an instance (rather than static) method to be used. + ///

    + ///
    + public const string FactoryObjectAttribute = "factory-object"; + + /// + /// Optional attribute specifying the name of a factory method to use + /// to create this object. + /// + /// + ///

    + /// Use constructor-arg elements to specify arguments to the factory + /// method, if it takes arguments. Autowiring does not apply to + /// factory methods. + ///

    + ///

    + /// If the "type" attribute is present, the factory method will be a + /// static method on the type specified by the "type" attribute on + /// this object definition. Often this will be the same type as that + /// of the constructed object - for example, when the factory method + /// is used as an alternative to a constructor. However, it may be on + /// a different type. In that case, the created object will *not* be + /// of the type specified in the "type" attribute. This is analogous + /// to behaviour. + ///

    + ///

    + /// If the "factory-object" attribute is present, the "type" attribute + /// is not used, and the factory method will be an instance method on + /// the object returned from a + /// + /// call with the specified object name. The factory object may be + /// defined as a singleton or a prototype. + ///

    + ///

    + /// The factory method can have any number of arguments. Use indexed + /// constructor-arg elements in conjunction with the factory-method + /// attribute. + ///

    + ///

    + /// Setter Injection can be used in conjunction with a factory method. + /// Method Injection cannot, as the factory method returns an instance, + /// which will be used when the container creates the object. + ///

    + ///
    + public const string FactoryMethodAttribute = "factory-method"; + + /// + /// A list can contain multiple inner object, ref, collection, or + /// value elements. + /// + /// + ///

    + /// Lists are untyped, pending generics support, although references + /// will be strongly typed. + ///

    + ///

    + /// A list can also map to an array type. The necessary conversion is + /// automatically performed by the + /// . + ///

    + ///
    + public const string ListElement = "list"; + + /// + /// A set can contain multiple inner object, ref, collection, or value + /// elements. + /// + /// + ///

    + /// Sets are untyped, pending generics support, although references + /// will be strongly typed. + ///

    + ///
    + public const string SetElement = "set"; + + /// + /// A Spring.NET map is a mapping from a string key to object (a .NET + /// ). + /// + /// + ///

    + /// Dictionaries may be empty. + ///

    + ///
    + public const string DictionaryElement = "dictionary"; + + /// + /// A lookup key (for a dictionary or name / value collection). + /// + public const string KeyAttribute = "key"; + + /// + /// A lookup key (for a dictionary or name / value collection). + /// + public const string KeyElement = "key"; + + /// + /// Contains a string representation of a value. + /// + /// + ///

    + /// This is used by name-value, ctor argument, and property elements. + ///

    + ///
    + public const string ValueAttribute = "value"; + + /// + /// Contains delimiters that should be used to split delimited string values. + /// + /// + ///

    + /// This is used by name-value element. + ///

    + ///
    + public const string DelimitersAttribute = "delimiters"; + + /// + /// A reference to another objects. + /// + /// + ///

    + /// Used as a convenience shortcut on property and constructor-arg + /// elements to refer to other objects. + ///

    + ///
    + public const string RefAttribute = "ref"; + + /// + /// Contains a string representation of an expression. + /// + /// + ///

    + /// This is used by ctor argument and property elements. + ///

    + ///
    + public const string ExpressionAttribute = "expression"; + + /// + /// A map entry can be an inner object, ref, collection, or value. + /// + /// + ///

    + /// The name of the property is given by the "key" attribute. + ///

    + ///
    + public const string EntryElement = "entry"; + + /// + /// Contains a string representation of a property value. + /// + /// + ///

    + /// The property may be a string, or may be converted to the + /// required using the + /// + /// machinery. This makes it possible for application developers to + /// write custom + /// implementations that can convert strings to objects. + ///

    + /// + /// This is recommended for simple objects only. Configure more complex + /// objects by setting properties to references to other objects. + /// + ///
    + public const string ValueElement = "value"; + + /// + /// Contains a string representation of an expression. + /// + public const string ExpressionElement = "expression"; + + /// + /// Denotes value. + /// + /// + ///

    + /// Necessary because an empty "value" tag will resolve to an empty + /// , which will not be resolved to + /// value unless a special + /// does so. + ///

    + ///
    + public const string NullElement = "null"; + + /// + /// 'name-values' elements differ from dictionary elements in that + /// values must be strings. + /// + /// + ///

    + /// May be empty. + ///

    + ///
    + public const string NameValuesElement = "name-values"; + + /// + /// Element content is the string value of the property. + /// + /// + ///

    + /// The "key" attribute is the name of the property. + ///

    + ///
    + public const string AddElement = "add"; + + /// + /// The lazy initialization mode for an individual object definition. + /// + public const string LazyInitAttribute = "lazy-init"; + + /// + /// The dependency checking mode for an individual object definition. + /// + public const string DependencyCheckAttribute = "dependency-check"; + + /// + /// Defines a subscription to one or more events published by one or + /// more event sources. + /// + public const string ListenerElement = "listener"; + + /// + /// The name of an event handling method. + /// + /// + ///

    + /// Defaults to On${event}. + /// Note : this default will probably change before the first 1.0 + /// release. + ///

    + ///
    + public const string ListenerMethodAttribute = "method"; + + /// + /// The name of an event. + /// + public const string ListenerEventAttribute = "event"; + + /// + /// The autowiring mode for an individual object definition. + /// + public const string AutowireAttribute = "autowire"; + + /// + /// Shortcut alternative to specifying a key element in a + /// dictionary entry element with <ref object="..."/>. + /// + public const string DictionaryKeyRefShortcutAttribute = "key-ref"; + + /// + /// Shortcut alternative to specifying a value element in a + /// dictionary entry element with <ref object="..."/>. + /// + public const string DictionaryValueRefShortcutAttribute = "value-ref"; + + /// + /// The string of characters that delimit object names. + /// + public const string ObjectNameDelimiters = ",; "; + + /// + /// A lookup method causes the IoC container to override a given method and return + /// the object with the name given in the attendant object attribute. + /// + /// + ///

    + /// This is a form of Method Injection. + ///

    + ///

    + /// It's particularly useful as an alternative to implementing the + /// interface, + /// in order to be able to make + /// + /// calls for non-singleton instances at runtime. In this case, Method Injection + /// is a less invasive alternative. + ///

    + ///
    + public const string LookupMethodElement = "lookup-method"; + + /// + /// The name of a lookup method. This method must take no arguments. + /// + public const string LookupMethodNameAttribute = "name"; + + /// + /// The name of the object in the IoC container that the lookup method + /// must resolve to. + /// + /// + ///

    + /// Often this object will be a prototype, in which case the lookup method + /// will return a distinct instance on every invocation. This is useful + /// for single-threaded objects. + ///

    + ///
    + public const string LookupMethodObjectNameAttribute = "object"; + + /// + /// A replaced method causes the IoC container to override a given method + /// with an (arbitrary) implementation at runtime. + /// + /// + ///

    + /// This (again) is a form of Method Injection. + ///

    + ///
    + public const string ReplacedMethodElement = "replaced-method"; + + /// + /// Name of the method whose implementation should be replaced by the + /// IoC container. + /// + /// + ///

    + /// If this method is not overloaded, there's no need to use arg-type + /// subelements. + ///

    + ///

    + /// If this method is overloaded, arg-type subelements must be + /// used for all override definitions for the method. + ///

    + ///
    + public const string ReplacedMethodNameAttribute = "name"; + + /// + /// The object name of an implementation of the + /// interface. + /// + /// + ///

    + /// This may be a singleton or prototype. If it's a prototype, a new + /// instance will be used for each method replacement. Singleton usage + /// is the norm. + ///

    + ///
    + public const string ReplacedMethodReplacerNameAttribute = "replacer"; + + /// + /// Subelement of replaced-method identifying an argument for a + /// replaced method in the event of method overloading. + /// + /// + public const string ReplacedMethodArgumentTypeElement = "arg-type"; + + /// + /// Specification of the of an overloaded method + /// argument as a . + /// + /// + ///

    + /// For convenience, this may be a substring of the FQN. E.g. all the following would match + /// : + ///

    + ///

    + /// + /// + /// System.String + /// + /// + /// string + /// + /// + /// str + /// + /// + ///

    + ///
    + /// + public const string ReplacedMethodArgumentTypeMatchAttribute = "match"; + + /// + /// Check everything. + /// + public static readonly string DependencyCheckAllAttributeValue + = Enum.GetName(typeof (DependencyCheckingMode), DependencyCheckingMode.All); + + /// + /// Just check primitive (string, int, etc) values. + /// + public static readonly string DependencyCheckSimpleAttributeValue + = Enum.GetName(typeof (DependencyCheckingMode), DependencyCheckingMode.Simple); + + /// + /// Check object references. + /// + public static readonly string DependencyCheckObjectsAttributeValue + = Enum.GetName(typeof (DependencyCheckingMode), DependencyCheckingMode.Objects); + + /// + /// Autowire by name. + /// + public static readonly string AutowireByNameValue + = Enum.GetName(typeof (AutoWiringMode), AutoWiringMode.ByName); + + /// + /// Autowire by . + /// + public static readonly string AutowireByTypeValue + = Enum.GetName(typeof (AutoWiringMode), AutoWiringMode.ByType); + + /// + /// Autowiring by constructor. + /// + public static readonly string AutowireConstructorValue + = Enum.GetName(typeof (AutoWiringMode), AutoWiringMode.Constructor); + + /// + /// The autowiring strategy is to be determined by introspection + /// of the object's . + /// + public static readonly string AutowireAutoDetectValue + = Enum.GetName(typeof (AutoWiringMode), AutoWiringMode.AutoDetect); + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is a utility class, and as such has no publicly visible + /// constructors. + ///

    + ///
    + private ObjectDefinitionConstants() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionParserHelper.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionParserHelper.cs new file mode 100644 index 00000000..3696697f --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionParserHelper.cs @@ -0,0 +1,210 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using System.Xml; +using Common.Logging; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Util; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Sateful class used to parse XML object definitions. + /// + /// Not all parsing code has been refactored into this class. + /// Rob Harrop + /// Juergen Hoeller + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: ObjectDefinitionParserHelper.cs,v 1.7 2007/08/07 22:05:20 markpollack Exp $ + public class ObjectDefinitionParserHelper + { + + #region Fields + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof(ObjectDefinitionParserHelper)); + + private DocumentDefaultsDefinition defaults; + + private XmlReaderContext readerContext; + + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The reader context. + public ObjectDefinitionParserHelper(XmlReaderContext readerContext) + { + AssertUtils.ArgumentNotNull(readerContext, "readerContext"); + this.readerContext = readerContext; + } + + /// + /// Gets the defaults definition object, or null if the + /// default have not yet been initialized. + /// + /// The defaults. + public DocumentDefaultsDefinition Defaults + { + get { return defaults; } + } + + + /// + /// Gets the reader context. + /// + /// The reader context. + public XmlReaderContext ReaderContext + { + get { return readerContext; } + } + + /// + /// Initialize the default lazy-init, dependency check, and autowire settings. + /// + /// The root element + public void InitDefaults(XmlElement root) + { + DocumentDefaultsDefinition ddd = new DocumentDefaultsDefinition(); + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Loading object definitions..."); + } + + #endregion + + ddd.LazyInit = root.GetAttribute(ObjectDefinitionConstants.DefaultLazyInitAttribute); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + "Default lazy init '{0}'.", + ddd.LazyInit)); + } + + #endregion + + ddd.DependencyCheck = root.GetAttribute(ObjectDefinitionConstants.DefaultDependencyCheckAttribute); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + "Default dependency check '{0}'.", + ddd.DependencyCheck)); + } + + #endregion + + ddd.Autowire = root.GetAttribute(ObjectDefinitionConstants.DefaultAutowireAttribute); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + "Default autowire '{0}'.", + ddd.Autowire)); + } + + #endregion + + defaults = ddd; + } + + + /// + /// Determines whether the Spring object namespace is equal to the the specified namespace URI. + /// + /// The namespace URI. + /// + /// true if is the default Spring namespace; otherwise, false. + /// + public bool IsDefaultNamespace(string namespaceUri) + { + return + (!StringUtils.HasLength(namespaceUri) || ObjectsNamespaceParser.Namespace.Equals(namespaceUri)); + } + + + /// + /// Decorates the object definition if required. + /// + /// The element. + /// The holder. + /// + public ObjectDefinitionHolder DecorateObjectDefinitionIfRequired(XmlElement element, ObjectDefinitionHolder holder) + { + + //TODO decoration processing. + return holder; + } + + + /// + /// Determines whether the string represents a 'true' boolean value. + /// + /// The value. + /// + /// true if is 'true' string value; otherwise, false. + /// + public bool IsTrueStringValue(string value) + { + return ObjectDefinitionConstants.TrueValue.Equals(value.ToLower(CultureInfo.CurrentCulture)); + } + + /// + /// Convenience method to create a builder for a root object definition. + /// + /// Name of the object type. + /// A builder for a root object definition. + public ObjectDefinitionBuilder CreateRootObjectDefinitionBuilder(string objectTypeName) + { + return ObjectDefinitionBuilder.RootObjectDefinition(this.readerContext.ObjectDefinitionFactory, objectTypeName); + } + + /// + /// Convenience method to create a builder for a root object definition. + /// + /// Type of the object. + /// a builder for a root object definition + public ObjectDefinitionBuilder CreateRootObjectDefinitionBuilder(Type objectType) + { + return ObjectDefinitionBuilder.RootObjectDefinition(this.readerContext.ObjectDefinitionFactory, objectType); + } + + + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectFactorySectionHandler.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectFactorySectionHandler.cs new file mode 100644 index 00000000..47cf97c1 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectFactorySectionHandler.cs @@ -0,0 +1,92 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Configuration; +using System.Xml; + +using Spring.Core.IO; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Creates an instance + /// populated with the object definitions supplied in the configuration + /// section. + /// + /// + ///

    + /// Applications will typically want to use an + /// , and instantiate it + /// via the use of the + /// class (which is similar in functionality to this class). This class is + /// provided for those times when only an + /// is required. + ///

    + /// Creates an instance of the class XmlObjectFactory + ///
    + /// + ///

    + /// + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: ObjectFactorySectionHandler.cs,v 1.3 2007/08/08 17:47:13 bbaia Exp $ + public class ObjectFactorySectionHandler : IConfigurationSectionHandler + { + /// + /// Creates a new instance of the + /// class. + /// + public ObjectFactorySectionHandler() + {} + + /// + /// Creates a + /// instance populated with the object definitions supplied in the + /// configuration section. + /// + /// + /// The configuration settings in a corresponding parent configuration + /// section. + /// + /// + /// The configuration context when called from the ASP.NET + /// configuration system. Otherwise, this parameter is reserved and + /// is . + /// + /// + /// The for the section. + /// + /// + /// A instance + /// populated with the object definitions supplied in the configuration + /// section. + /// + public object Create( + object parent, object configContext, XmlNode section) + { + return new XmlObjectFactory(new ConfigSectionResource(section as XmlElement), true); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs new file mode 100644 index 00000000..221da168 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs @@ -0,0 +1,1421 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Globalization; +using System.IO; +using System.Text; +using System.Xml; + +using Common.Logging; + +using Spring.Collections; +using Spring.Core; +using Spring.Core.IO; +using Spring.Core.TypeResolution; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Default implementation of the + /// interface. + /// + /// + ///

    + /// Parses object definitions according to the standard Spring.NET schema. + ///

    + ///

    + /// This schema is typically located at + /// http://www.springframework.net/xsd/spring-objects.xsd. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: ObjectsNamespaceParser.cs,v 1.7 2007/11/26 14:15:54 bbaia Exp $ + [ + NamespaceParser( + Namespace = "http://www.springframework.net", + SchemaLocationAssemblyHint = typeof(ObjectsNamespaceParser), + SchemaLocation = "/Spring.Objects.Factory.Xml/spring-objects-1.1.xsd" + ) + ] + public class ObjectsNamespaceParser : INamespaceParser + { + /// + /// The namespace URI for the standard Spring.NET object definition schema. + /// + public const string Namespace = "http://www.springframework.net"; + + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof(ObjectsNamespaceParser)); + + #region IXmlObjectDefinitionParser Members + + /// + /// Invoked by after construction but before any + /// elements have been parsed. + /// + /// This is a NoOp + public void Init() + { + + } + + #endregion + + + /// + /// Parse the specified element and register any resulting + /// IObjectDefinitions with the IObjectDefinitionRegistry that is + /// embedded in the supplied ParserContext. + /// + /// The element to be parsed into one or more IObjectDefinitions + /// The object encapsulating the current state of the parsing + /// process. + /// + /// The primary IObjectDefinition (can be null as explained above) + /// + /// + /// Implementations should return the primary IObjectDefinition + /// that results from the parse phase if they wish to used nested + /// inside (for example) a <property> tag. + /// Implementations may return null if they will not + /// be used in a nested scenario. + /// + /// + public virtual IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + + if (element.LocalName == ObjectDefinitionConstants.ImportElement) + { + ImportObjectDefinitionResource(element, parserContext); + } + else if (element.LocalName == ObjectDefinitionConstants.AliasElement) + { + ParseAlias(element, parserContext.ReaderContext.Registry); + } + else if (element.LocalName == ObjectDefinitionConstants.ObjectElement) + { + RegisterObjectDefinition(element, parserContext); + } + + return null; + } + + + /// + /// Parse the specified XmlNode and decorate the supplied ObjectDefinitionHolder, + /// returning the decorated definition. + /// + /// The XmlNode may either be an XmlAttribute or an XmlElement, depending on + /// whether a custom attribute or element is being parsed. + /// Implementations may choose to return a completely new definition, + /// which will replace the original definition in the resulting IApplicationContext/IObjectFactory. + /// + /// The supplied ParserContext can be used to register any additional objects needed to support + /// the main definition. + /// + /// The source element or attribute that is to be parsed. + /// The current object definition. + /// The object encapsulating the current state of the parsing + /// process. + /// The decorated definition (to be registered in the IApplicationContext/IObjectFactory), + /// or simply the original object definition if no decoration is required. A null value is strickly + /// speaking invalid, but will leniently treated like the case where the original object definition + /// gets returned. + public ObjectDefinitionHolder Decorate(XmlNode node, ObjectDefinitionHolder definition, + ParserContext parserContext) + { + return null; + } + + private void ParseAlias(XmlElement aliasElement, IObjectDefinitionRegistry registry) + { + string name = aliasElement.GetAttribute(ObjectDefinitionConstants.NameAttribute); + string alias = aliasElement.GetAttribute(ObjectDefinitionConstants.AliasAttribute); + registry.RegisterAlias(name, alias); + } + + + + + /// + /// Loads external XML object definitions from the resource described by the supplied + /// . + /// + /// The XML element describing the resource. + /// The parser context. + /// + /// If the resource could not be imported. + /// + protected virtual void ImportObjectDefinitionResource(XmlElement resource, ParserContext parserContext) + { + string location = resource.GetAttribute(ObjectDefinitionConstants.ImportResourceAttribute); + try + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "Attempting to import object definitions from '{0}'.", location)); + } + + #endregion + + IResource importResource = parserContext.ReaderContext.Resource.CreateRelative(location); + parserContext.ReaderContext.Reader.LoadObjectDefinitions(importResource); + } + catch (IOException ex) + { + parserContext.ReaderContext.ReportException(resource, null, string.Format( + CultureInfo.InvariantCulture, + "Invalid relative resource location '{0}' to import object definitions from.", + location), ex); + } + } + + + /// Parses an event listener definition. + /// + /// The name associated with the object that the event handler is being defined on. + /// + /// The events being populated. + /// + /// The element containing the event listener definition. + /// + /// + /// The namespace-aware parser. + /// + protected virtual void ParseEventListenerDefinition( + string name, EventValues events, XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + // get an appropriate IEventHandlerValue instance based upon the + // attribute values of the listener element... + IEventHandlerValue myHandler = ObjectDefinitionReaderUtils.CreateEventHandlerValue( + element.GetAttribute(ObjectDefinitionConstants.ListenerMethodAttribute), + element.GetAttribute(ObjectDefinitionConstants.ListenerEventAttribute)); + + // and then get the source of the event (another managed object instance + // or a Type reference (i.e. a static event exposed on a class)... + XmlElement sourceElement = this.SelectSingleNode(element, ObjectDefinitionConstants.RefElement) as XmlElement; + + XmlAttribute sourceAtt = sourceElement.Attributes[0]; + if (StringUtils.IsNullOrEmpty(sourceAtt.Value)) + { + parserHelper.ReaderContext.ReportFatalException(sourceElement, string.Format( + CultureInfo.InvariantCulture, + "The single attribute of the <{0}/> element cannot be empty. Specify the " + + "object id (alias) or the full, assembly qualified Type name that is the " + + "source of the event.", + ObjectDefinitionConstants.RefElement)); + return; + } + switch (sourceAtt.LocalName) + { + case ObjectDefinitionConstants.LocalRefAttribute: + case ObjectDefinitionConstants.ObjectRefAttribute: + // we're wiring up to an event exposed on another managed object (instance) + RuntimeObjectReference ror = new RuntimeObjectReference(sourceAtt.Value); + myHandler.Source = ror; + break; + case ObjectDefinitionConstants.TypeAttribute: + // we're wiring up to a static event exposed on a Type (class) + myHandler.Source = parserHelper.ReaderContext.Reader.Domain == null ? + (object) sourceAtt.Value : + (object)TypeResolutionUtils.ResolveType(sourceAtt.Value); + break; + } + events.AddHandler(myHandler); + } + + + + /// + /// Parse an object definition and register it with the object factory.. + /// + /// The element containing the object definition. + /// The parser context. + /// + protected void RegisterObjectDefinition(XmlElement element, ParserContext parserContext) + { + ObjectDefinitionHolder holder = null; + try + { + holder = ParseObjectDefinition(element, parserContext); + if (holder == null) + { + return; + } + } + catch (Exception ex) + { + throw new ObjectDefinitionStoreException(string.Format("Failed parsing object definition '{0}'", element.OuterXml), ex); + } + + + holder = parserContext.ParserHelper.DecorateObjectDefinitionIfRequired(element, holder); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + CultureInfo.InvariantCulture, + "Registering object definition with id '{0}'.", holder.ObjectName)); + } + + #endregion + + ObjectDefinitionReaderUtils.RegisterObjectDefinition(holder, parserContext.ReaderContext.Registry); + } + + + /// + /// Parse a standard object definition into a + /// , + /// including object name and aliases. + /// + /// The element containing the object definition. + /// The parser context. + /// + /// The object (definition) wrapped within an + /// + /// instance. + /// + /// + ///

    + /// Object elements specify their canonical name via the "id" attribute + /// and their aliases as a delimited "name" attribute. + ///

    + ///

    + /// If no "id" is specified, uses the first name in the "name" attribute + /// as the canonical name, registering all others as aliases. + ///

    + ///
    + protected ObjectDefinitionHolder ParseObjectDefinition(XmlElement element, ParserContext parserContext) + { + string id = element.GetAttribute(ObjectDefinitionConstants.IdAttribute); + string name = element.GetAttribute(ObjectDefinitionConstants.NameAttribute); + ArrayList aliases = new ArrayList(); + if (StringUtils.HasText(name)) + { + aliases.AddRange(GetObjectNames(name)); + } + // if we ain't got an id, check if object is page definition or assign any existing (first) alias... + if (StringUtils.IsNullOrEmpty(id)) + { + id = CalculateId(element, aliases); + } + + + IConfigurableObjectDefinition definition = ParseObjectDefinition(element, id, parserContext.ParserHelper); + if (StringUtils.IsNullOrEmpty(id)) + { + id = ObjectDefinitionReaderUtils.GenerateObjectName(definition, parserContext.Registry); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + "Neither XML '{0}' nor '{1}' specified - using object " + + "class name [{2}] as the id.", + id, ObjectDefinitionConstants.IdAttribute, ObjectDefinitionConstants.NameAttribute)); + } + + #endregion + } + string[] aliasesArray = (string[]) aliases.ToArray(typeof(string)); + return new ObjectDefinitionHolder(definition, id, aliasesArray); + } + + /// + /// Calculates an id for an object definition. + /// + /// + ///

    + /// Called when an object definition has not been explicitly defined + /// with an id. + ///

    + ///
    + /// + /// The element containing the object definition. + /// + /// + /// The list of names defined for the object; may be + /// or even empty. + /// + /// + /// A calculated object definition id. + /// + protected virtual string CalculateId(XmlElement element, ArrayList aliases) + { + string id = null; + if (aliases.Count > 0) + { + string firstAlias = aliases[0] as string; + aliases.RemoveAt(0); + id = firstAlias; + } + + #region Instrumentation + + if (log.IsDebugEnabled) + { + StringBuilder buffer = new StringBuilder(); + foreach (string alias in aliases) + { + buffer.Append(alias).Append(","); + } + log.Debug(string.Format("No XML 'id' specified - using '{0}' as the id and '{1}' as aliases.", + id, buffer.ToString())); + } + + #endregion + + return id; + } + + /// + /// Parse a standard object definition. + /// + /// The element containing the object definition. + /// The id of the object definition. + /// parsing state holder + /// The object (definition). + protected virtual IConfigurableObjectDefinition ParseObjectDefinition( + XmlElement element, string id, ObjectDefinitionParserHelper parserHelper) + { + string typeName = null; + try + { + if (element.HasAttribute(ObjectDefinitionConstants.TypeAttribute)) + { + typeName = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute); + if (StringUtils.IsNullOrEmpty(typeName)) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, id, + "The 'type' attribute does not need to be present, but if it is it must not be empty: got '" + typeName + "'."); + } + } + string parent = element.GetAttribute(ObjectDefinitionConstants.ParentAttribute); + + + AbstractObjectDefinition od + = parserHelper.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( + typeName, parent, parserHelper.ReaderContext.Reader.Domain); + + + MutablePropertyValues pvs = GetPropertyValueSubElements(id, element, parserHelper); + ConstructorArgumentValues arguments + = GetConstructorArgSubElements(id, element, parserHelper); + EventValues events = GetEventHandlerSubElements(id, element, parserHelper); + MethodOverrides methodOverrides = GetMethodOverrideSubElements(id, element, parserHelper); + + bool isPage = StringUtils.HasText(typeName) && typeName!= null && typeName.ToLower().EndsWith(".aspx"); + if (!isPage) + { + od.ConstructorArgumentValues = arguments; + } + + od.PropertyValues = pvs; + od.MethodOverrides = methodOverrides; + od.EventHandlerValues = events; + if (element.HasAttribute(ObjectDefinitionConstants.DependsOnAttribute)) + { + string dependsOn = element.GetAttribute(ObjectDefinitionConstants.DependsOnAttribute); + od.DependsOn = GetObjectNames(dependsOn); + } + od.FactoryMethodName = element.GetAttribute(ObjectDefinitionConstants.FactoryMethodAttribute); + od.FactoryObjectName = element.GetAttribute(ObjectDefinitionConstants.FactoryObjectAttribute); + string dependencyCheck = element.GetAttribute(ObjectDefinitionConstants.DependencyCheckAttribute); + if (ObjectDefinitionConstants.DefaultValue.Equals(dependencyCheck)) + { + dependencyCheck = parserHelper.Defaults.DependencyCheck; + } + od.DependencyCheck = GetDependencyCheck(dependencyCheck); + string autowire = element.GetAttribute(ObjectDefinitionConstants.AutowireAttribute); + if (ObjectDefinitionConstants.DefaultValue.Equals(autowire)) + { + autowire = parserHelper.Defaults.Autowire; + } + od.AutowireMode = GetAutowireMode(autowire); + string initMethodName = element.GetAttribute(ObjectDefinitionConstants.InitMethodAttribute); + if (StringUtils.HasText(initMethodName)) + { + od.InitMethodName = initMethodName; + } + string destroyMethodName = element.GetAttribute(ObjectDefinitionConstants.DestroyMethodAttribute); + if (StringUtils.HasText(destroyMethodName)) + { + od.DestroyMethodName = destroyMethodName; + } + if (element.HasAttribute(ObjectDefinitionConstants.SingletonAttribute)) + { + od.IsSingleton = IsTrueStringValue(element.GetAttribute(ObjectDefinitionConstants.SingletonAttribute).ToLower(CultureInfo.CurrentCulture)); + } + string lazyInit = element.GetAttribute(ObjectDefinitionConstants.LazyInitAttribute); + if (ObjectDefinitionConstants.DefaultValue.Equals(lazyInit) && od.IsSingleton) + { + // just apply default to singletons, as lazy-init has no meaning for prototypes... + lazyInit = parserHelper.Defaults.LazyInit; + } + od.IsLazyInit = IsTrueStringValue(lazyInit); + + // try to get the line info + string resourceDescription = parserHelper.ReaderContext.Resource.Description; + if (StringUtils.HasText(resourceDescription)) + { + int line = ConfigurationUtils.GetLineNumber(element); + if (line > 0) + { + resourceDescription += " line " + line; + } + } + od.ResourceDescription = resourceDescription; + + string isAbstract = element.GetAttribute(ObjectDefinitionConstants.AbstractAttribute); + if (StringUtils.HasText(isAbstract)) + { + od.IsAbstract = IsTrueStringValue(isAbstract); + } + return od; + } + catch (TypeLoadException ex) + { + parserHelper.ReaderContext.ReportException( + element, + id, + string.Format( + "Object class [{0}] not found.", + typeName), + ex); + } + catch (ApplicationException ex) + { + parserHelper.ReaderContext.ReportException(element, id, string.Empty, ex); + } + return null; + } + + /// + /// Parse method override argument subelements of the given object element. + /// + protected MethodOverrides GetMethodOverrideSubElements( + string name, XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + MethodOverrides overrides = new MethodOverrides(); + foreach (XmlNode node in this.SelectNodes(element, ObjectDefinitionConstants.LookupMethodElement)) + { + ParseLookupMethodElement(name, overrides, (XmlElement) node, parserHelper); + } + foreach (XmlNode node in this.SelectNodes(element, ObjectDefinitionConstants.ReplacedMethodElement)) + { + ParseReplacedMethodElement(name, overrides, (XmlElement) node, parserHelper); + } + return overrides; + } + + /// + /// Parse element and add parsed element to + /// + protected void ParseLookupMethodElement( + string name, MethodOverrides overrides, XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + string methodName = element.GetAttribute(ObjectDefinitionConstants.LookupMethodNameAttribute); + string targetObjectName = element.GetAttribute(ObjectDefinitionConstants.LookupMethodObjectNameAttribute); + if (StringUtils.IsNullOrEmpty(methodName)) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + string.Format("The '{0}' attribute is required for the '{1}' element.", + ObjectDefinitionConstants.LookupMethodNameAttribute, ObjectDefinitionConstants.LookupMethodElement)); + } + if (StringUtils.IsNullOrEmpty(targetObjectName)) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + string.Format("The '{0}' attribute is required for the '{1}' element.", + ObjectDefinitionConstants.LookupMethodObjectNameAttribute, ObjectDefinitionConstants.LookupMethodElement)); + } + overrides.Add(new LookupMethodOverride(methodName, targetObjectName)); + } + + /// + /// Parse element and add parsed element to + /// + protected void ParseReplacedMethodElement( + string name, MethodOverrides overrides, XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + string methodName = element.GetAttribute(ObjectDefinitionConstants.ReplacedMethodNameAttribute); + string targetReplacerObjectName = element.GetAttribute(ObjectDefinitionConstants.ReplacedMethodReplacerNameAttribute); + if (StringUtils.IsNullOrEmpty(methodName)) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + string.Format("The '{0}' attribute is required for the '{1}' element.", + ObjectDefinitionConstants.ReplacedMethodNameAttribute, ObjectDefinitionConstants.ReplacedMethodElement)); + } + if (StringUtils.IsNullOrEmpty(targetReplacerObjectName)) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + string.Format("The '{0}' attribute is required for the '{1}' element.", + ObjectDefinitionConstants.ReplacedMethodReplacerNameAttribute, ObjectDefinitionConstants.ReplacedMethodElement)); + } + ReplacedMethodOverride theOverride = new ReplacedMethodOverride(methodName, targetReplacerObjectName); + foreach (XmlNode node in this.SelectNodes(element, ObjectDefinitionConstants.ReplacedMethodArgumentTypeElement)) + { + XmlElement argElement = (XmlElement) node; + string match = argElement.GetAttribute(ObjectDefinitionConstants.ReplacedMethodArgumentTypeMatchAttribute); + if (StringUtils.IsNullOrEmpty(match)) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + string.Format("The '{0}' attribute is required for the '{1}' element.", + ObjectDefinitionConstants.ReplacedMethodArgumentTypeMatchAttribute, ObjectDefinitionConstants.ReplacedMethodArgumentTypeElement)); + } + theOverride.AddTypeIdentifier(match); + } + overrides.Add(theOverride); + } + + /// + /// Parse constructor argument subelements of the given object element. + /// + protected ConstructorArgumentValues GetConstructorArgSubElements( + string name, XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + ConstructorArgumentValues arguments = new ConstructorArgumentValues(); + foreach (XmlNode node in this.SelectNodes(element, ObjectDefinitionConstants.ConstructorArgElement)) + { + ParseConstructorArgElement(name, arguments, (XmlElement) node, parserHelper); + } + return arguments; + } + + /// + /// Parse event handler subelements of the given object element. + /// + protected EventValues GetEventHandlerSubElements( + string name, XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + EventValues events = new EventValues(); + foreach (XmlNode node in this.SelectNodes(element, ObjectDefinitionConstants.ListenerElement)) + { + ParseEventListenerDefinition(name, events, (XmlElement) node, parserHelper); + } + return events; + } + + /// + /// Parse property value subelements of the given object element. + /// + /// + /// The name of the object (definition) associated with the property element (s) + /// + /// + /// The element containing the top level object definition. + /// + /// + /// The namespace-aware parser. + /// + /// + /// The property (s) associated with the object (definition). + /// + protected virtual MutablePropertyValues GetPropertyValueSubElements( + string name, XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + MutablePropertyValues properties = new MutablePropertyValues(); + foreach (XmlNode node in this.SelectNodes(element, ObjectDefinitionConstants.PropertyElement)) + { + ParsePropertyElement(name, properties, (XmlElement) node, parserHelper); + } + return properties; + } + + /// + /// Parse a constructor-arg element. + /// + /// + /// The name of the object (definition) associated with the ctor arg. + /// + /// + /// The list of constructor args associated with the object (definition). + /// + /// + /// The name of the element containing the ctor arg definition. + /// + /// + /// The namespace-aware parser. + /// + protected virtual void ParseConstructorArgElement( + string name, ConstructorArgumentValues arguments, XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + object val = GetPropertyValue(element, name, parserHelper); + string indexAttr = element.GetAttribute(ObjectDefinitionConstants.IndexAttribute); + string typeAttr = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute); + string nameAttr = element.GetAttribute(ObjectDefinitionConstants.ArgumentNameAttribute); + + // only one of the 'index' or 'name' attributes can be present + if (StringUtils.HasText(indexAttr) + && StringUtils.HasText(nameAttr)) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + "Only one of the 'index' or 'name' attributes can be present per constructor argument."); + } + if (StringUtils.HasText(indexAttr)) + { + try + { + int index = int.Parse(indexAttr, CultureInfo.CurrentCulture); + if (index < 0) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + "'index' cannot be lower than 0"); + } + if (StringUtils.HasText(typeAttr)) + { + arguments.AddIndexedArgumentValue(index, val, typeAttr); + } + else + { + arguments.AddIndexedArgumentValue(index, val); + } + } + catch (FormatException) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + "Attribute 'index' of tag 'constructor-arg' must be an integer value."); + } + } + else if (StringUtils.HasText(nameAttr)) + { + if (StringUtils.HasText(typeAttr)) + { + if (log.IsWarnEnabled) + { + log.Warn("The 'type' attribute is redundant when the 'name' attribute has been used on a constructor argument element."); + } + } + arguments.AddNamedArgumentValue(nameAttr, val); + } + else + { + if (StringUtils.HasText(typeAttr)) + { + arguments.AddGenericArgumentValue(val, typeAttr); + } + else + { + arguments.AddGenericArgumentValue(val); + } + } + } + + /// + /// Parse a property element. + /// + /// + /// The name of the object (definition) associated with the property. + /// + /// + /// The list of properties associated with the object (definition). + /// + /// + /// The name of the element containing the property definition. + /// + /// + /// The namespace-aware parser. + /// + protected void ParsePropertyElement( + string name, MutablePropertyValues properties, XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + string propertyName = element.GetAttribute(ObjectDefinitionConstants.NameAttribute); + if (StringUtils.IsNullOrEmpty(propertyName)) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, + name, + "The 'property' element must have a 'name' attribute"); + } + object val = GetPropertyValue(element, name, parserHelper); + properties.Add(new PropertyValue(propertyName, val)); + } + + /// + /// Get the value of a property element (may be a list). + /// + ///

    + /// Please note that even though this method is named GetPropertyValue, + /// it is called by both the property and constructor argument element + /// handlers. + ///

    + ///
    + /// The property element. + /// + /// The name of the object associated with the property. + /// + /// + /// The namespace-aware parser. + /// + protected virtual object GetPropertyValue( + XmlElement element, string name, ObjectDefinitionParserHelper parserHelper) + { + XmlAttribute inlineValueAtt = element.Attributes[ObjectDefinitionConstants.ValueAttribute]; + if (inlineValueAtt != null) + { + return inlineValueAtt.Value; + } + XmlAttribute inlineRefAtt = element.Attributes[ObjectDefinitionConstants.RefAttribute]; + if (inlineRefAtt != null) + { + return new RuntimeObjectReference(inlineRefAtt.Value); + } + XmlAttribute inlineExpressionAtt = element.Attributes[ObjectDefinitionConstants.ExpressionAttribute]; + if (inlineExpressionAtt != null) + { + return new ExpressionHolder(inlineExpressionAtt.Value); + } + + // should only have one element child: value, ref, collection... + XmlNodeList nodes = element.ChildNodes; + XmlElement valueRefOrCollectionElement = null; + for (int i = 0; i < nodes.Count; ++i) + { + XmlElement candidateEle = nodes.Item(i) as XmlElement; + if (candidateEle != null) + { + if (ObjectDefinitionConstants.DescriptionElement.Equals(candidateEle.Name)) + { + // keep going: we don't use this value for now... + } + else + { + // child element is what we're looking for... + valueRefOrCollectionElement = candidateEle; + } + } + } + if (valueRefOrCollectionElement == null) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, + name, + "The '' element must have a subelement such as 'value' or 'ref'."); + } + return ParsePropertySubElement(valueRefOrCollectionElement, name, parserHelper); + } + + /// + /// Parse a value, ref or collection subelement of a property element. + /// + /// + /// Subelement of property element; we don't know which yet. + /// + /// + /// The name of the object (definition) associated with the top level property. + /// + /// + /// The namespace-aware parser. + /// + protected virtual object ParsePropertySubElement( + XmlElement element, string name, ObjectDefinitionParserHelper parserHelper) + { + if (element.Name.Equals(ObjectDefinitionConstants.ObjectElement)) + { + return ParseObjectDefinition(element, "(inner object definition)", parserHelper); + } + else if (element.Name.Equals(ObjectDefinitionConstants.RefElement)) + { + return GetReference(element, parserHelper, name); + } + else if (element.Name.Equals(ObjectDefinitionConstants.IdRefElement)) + { + return GetObjectReference(element, parserHelper, name); + } + else if (element.Name.Equals(ObjectDefinitionConstants.ListElement)) + { + return GetList(element, name, parserHelper); + } + else if (element.Name.Equals(ObjectDefinitionConstants.SetElement)) + { + return GetSet(element, name, parserHelper); + } + else if (element.Name.Equals(ObjectDefinitionConstants.DictionaryElement)) + { + return GetDictionary(element, name, parserHelper); + } + else if (element.Name.Equals(ObjectDefinitionConstants.NameValuesElement)) + { + return GetNameValues(element, name); + } + else if (element.Name.Equals(ObjectDefinitionConstants.ValueElement)) + { + return GetValue(element, name); + } + else if (element.Name.Equals(ObjectDefinitionConstants.ExpressionElement)) + { + return GetExpression(element, name, parserHelper); + } + else if (element.Name.Equals(ObjectDefinitionConstants.NullElement)) + { + // it's a distinguished null value... + return null; + } + else + { + // it may match another Parser + INamespaceParser otherParser = GetParser(element.NamespaceURI); + if (otherParser != null) + { + // The other parser uses nestings tags and thus returns the definition + // of the parsed object. + return otherParser.ParseElement(element, new ParserContext(parserHelper.ReaderContext, parserHelper)); + } + } + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, + name, + "Unknown subelement of : <" + element.Name + ">"); + } + + private static INamespaceParser GetParser(string nspace) + { + // finds the configuration parser for the given namespace + try + { + return NamespaceParserRegistry.GetParser(nspace); + } + catch (Exception) + { + // The parser for the given namespace is not found + return null; + } + } + + private static object GetObjectReference(XmlElement element, ObjectDefinitionParserHelper parserHelper, string name) + { + // a generic reference to any name of any object + string objectRef = element.GetAttribute(ObjectDefinitionConstants.ObjectRefAttribute); + if (StringUtils.IsNullOrEmpty(objectRef)) + { + // a reference to the id of another object in the same XML file + objectRef = element.GetAttribute(ObjectDefinitionConstants.LocalRefAttribute); + if (StringUtils.IsNullOrEmpty(objectRef)) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, + name, + "Either 'object' or 'local' is required for an idref"); + } + } + return objectRef; + } + + private object GetReference(XmlElement element, ObjectDefinitionParserHelper parserHelper, string name) + { + // is it a generic reference to any name of any object? + string objectRef = element.GetAttribute(ObjectDefinitionConstants.ObjectRefAttribute); + if (StringUtils.IsNullOrEmpty(objectRef)) + { + // is it a reference to the id of another object in the same XML file? + objectRef = element.GetAttribute(ObjectDefinitionConstants.LocalRefAttribute); + if (StringUtils.IsNullOrEmpty(objectRef)) + { + // is it a reference to the id of another object in a parent context? + objectRef = element.GetAttribute(ObjectDefinitionConstants.ParentAttribute); + if (StringUtils.IsNullOrEmpty(objectRef)) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, + name, + "Either 'object' or 'local' is required for a reference"); + } + return new RuntimeObjectReference(objectRef, true); + } + } + return new RuntimeObjectReference(objectRef); + } + + private object GetValue(XmlElement element, string name) + { + string valueType = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute); + if (StringUtils.IsNullOrEmpty(valueType)) + { + return GetTextValue(element, name); + } + else + { + Type resolvedValueType = TypeResolutionUtils.ResolveType(valueType); + if (resolvedValueType == typeof(string)) + { + return GetTextValue(element, name); + } + else + { + return new TypedStringValue(GetTextValue(element, name), resolvedValueType); + } + } + } + + private object GetExpression(XmlElement element, string name, ObjectDefinitionParserHelper parserHelper) + { + string expression = element.GetAttribute(ObjectDefinitionConstants.ValueAttribute); + ExpressionHolder holder = new ExpressionHolder(expression); + holder.Properties = GetPropertyValueSubElements(name, element, parserHelper); + return holder; + } + + /// + /// Gets a list definition. + /// + /// + /// The element describing the list definition. + /// + /// + /// The name of the object (definition) associated with the list definition. + /// + /// + /// The namespace-aware parser. + /// + /// The list definition. + protected virtual IList GetList(XmlElement element, string name, ObjectDefinitionParserHelper parserHelper) + { + ManagedList list = new ManagedList(); + + string elementTypeName = element.GetAttribute("element-type"); + if (StringUtils.HasText(elementTypeName)) + { + list.ElementTypeName = elementTypeName; + } + + foreach (XmlNode node in element.ChildNodes) + { + XmlElement ele = node as XmlElement; + if (ele != null) + { + list.Add(ParsePropertySubElement(ele, name, parserHelper)); + } + } + return list; + } + + /// + /// Gets a set definition. + /// + /// + /// The element describing the set definition. + /// + /// + /// The name of the object (definition) associated with the set definition. + /// + /// + /// The namespace-aware parser. + /// + /// The set definition. + protected Set GetSet(XmlElement element, string name, ObjectDefinitionParserHelper parserHelper) + { + ManagedSet theSet = new ManagedSet(); + string elementTypeName = element.GetAttribute("element-type"); + if (StringUtils.HasText(elementTypeName)) + { + theSet.ElementTypeName = elementTypeName; + } + foreach (XmlNode node in element.ChildNodes) + { + XmlElement ele = node as XmlElement; + if (ele != null) + { + object sub = ParsePropertySubElement(ele, name, parserHelper); + theSet.Add(sub); + } + } + return theSet; + } + + /// + /// Gets a dictionary definition. + /// + /// + /// The element describing the dictionary definition. + /// + /// + /// The name of the object (definition) associated with the dictionary definition. + /// + /// + /// The namespace-aware parser. + /// + /// The dictionary definition. + protected IDictionary GetDictionary(XmlElement element, string name, ObjectDefinitionParserHelper parserHelper) + { + ManagedDictionary dictionary = new ManagedDictionary(); + string keyTypeName = element.GetAttribute("key-type"); + string valueTypeName = element.GetAttribute("value-type"); + if (StringUtils.HasText(keyTypeName)) + { + dictionary.KeyTypeName = keyTypeName; + } + if (StringUtils.HasText(valueTypeName)) + { + dictionary.ValueTypeName = valueTypeName; + } + + XmlNodeList entryElements = SelectNodes(element, ObjectDefinitionConstants.EntryElement); + foreach (XmlElement entryEle in entryElements) + { + #region Key + + object key = null; + + XmlAttribute keyAtt = entryEle.Attributes[ObjectDefinitionConstants.KeyAttribute]; + if (keyAtt != null) + { + key = keyAtt.Value; + } + else + { + // ok, we're not using the 'key' attribute; lets check for the ref shortcut... + XmlAttribute keyRefAtt = entryEle.Attributes[ObjectDefinitionConstants.DictionaryKeyRefShortcutAttribute]; + if (keyRefAtt != null) + { + key = new RuntimeObjectReference(keyRefAtt.Value); + } + else + { + // so check for the 'key' element... + XmlNode keyNode = SelectSingleNode(entryEle, ObjectDefinitionConstants.KeyElement); + if (keyNode == null) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + string.Format("One of either the '{0}' element, or the the '{1}' or '{2}' attributes " + + "is required for the <{3}/> element.", + ObjectDefinitionConstants.KeyElement, + ObjectDefinitionConstants.KeyAttribute, + ObjectDefinitionConstants.DictionaryKeyRefShortcutAttribute, + ObjectDefinitionConstants.EntryElement)); + } + XmlElement keyElement = (XmlElement) keyNode; + XmlNodeList keyNodes = keyElement.GetElementsByTagName("*"); + if (keyNodes == null || keyNodes.Count == 0) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + string.Format("Malformed <{0}/> element... the value of the key must be " + + "specified as a child value-style element.", + ObjectDefinitionConstants.KeyElement)); + } + key = ParsePropertySubElement((XmlElement) keyNodes.Item(0), name, parserHelper); + } + } + + #endregion + + #region Value + + XmlAttribute inlineValueAtt = entryEle.Attributes[ObjectDefinitionConstants.ValueAttribute]; + if (inlineValueAtt != null) + { + // ok, we're using the value attribute shortcut... + dictionary[key] = inlineValueAtt.Value; + } + else if (entryEle.Attributes[ObjectDefinitionConstants.DictionaryValueRefShortcutAttribute] != null) + { + // ok, we're using the value-ref attribute shortcut... + XmlAttribute inlineValueRefAtt = entryEle.Attributes[ObjectDefinitionConstants.DictionaryValueRefShortcutAttribute]; + RuntimeObjectReference ror = new RuntimeObjectReference(inlineValueRefAtt.Value); + dictionary[key] = ror; + } + else if (entryEle.Attributes[ObjectDefinitionConstants.ExpressionAttribute] != null) + { + // ok, we're using the expression attribute shortcut... + XmlAttribute inlineExpressionAtt = entryEle.Attributes[ObjectDefinitionConstants.ExpressionAttribute]; + ExpressionHolder expHolder = new ExpressionHolder(inlineExpressionAtt.Value); + dictionary[key] = expHolder; + } + else + { + XmlNode keyNode = SelectSingleNode(entryEle, ObjectDefinitionConstants.KeyElement); + if (keyNode != null) + { + entryEle.RemoveChild(keyNode); + } + // ok, we're using the original full-on value element... + XmlNodeList valueElements = entryEle.GetElementsByTagName("*"); + if (valueElements == null || valueElements.Count == 0) + { + throw new ObjectDefinitionStoreException( + parserHelper.ReaderContext.Resource, name, + string.Format("One of either the '{0}' or '{1}' attributes, or a value-style element " + + "is required for the <{2}/> element.", + ObjectDefinitionConstants.ValueAttribute, ObjectDefinitionConstants.DictionaryValueRefShortcutAttribute, ObjectDefinitionConstants.EntryElement)); + } + dictionary[key] = ParsePropertySubElement((XmlElement)valueElements.Item(0), name, parserHelper); + } + + #endregion + } + return dictionary; + } + + /// + /// Selects sub-elements with a given + /// name. + /// + /// + ///

    + /// Uses a namespace manager if necessary. + ///

    + ///
    + /// + /// The element to be searched in. + /// + /// + /// The name of the child nodes to look for. + /// + /// + /// The child s of the supplied + /// with the supplied + /// . + /// + protected XmlNodeList SelectNodes(XmlElement element, string childElementName) + { + XmlNamespaceManager nsManager = new XmlNamespaceManager(new NameTable()); + nsManager.AddNamespace(GetNamespacePrefix(element), element.NamespaceURI); + return element.SelectNodes(GetNamespacePrefix(element) + ":" + childElementName, nsManager); + } + + /// + /// Selects a single sub-element with a given + /// name. + /// + /// + ///

    + /// Uses a namespace manager if necessary. + ///

    + ///
    + /// + /// The element to be searched in. + /// + /// + /// The name of the child node to look for. + /// + /// + /// The first child of the supplied + /// with the supplied + /// . + /// + protected XmlNode SelectSingleNode(XmlElement element, string childElementName) + { + XmlNamespaceManager nsManager = new XmlNamespaceManager(new NameTable()); + nsManager.AddNamespace(GetNamespacePrefix(element), element.NamespaceURI); + return element.SelectSingleNode(GetNamespacePrefix(element) + ":" + childElementName, nsManager); + } + + /// + /// Gets a name value collection mapping definition. + /// + /// + /// The element describing the name value collection mapping definition. + /// + /// + /// The name of the object (definition) associated with the + /// name value collection mapping definition. + /// + /// The name value collection definition. + protected NameValueCollection GetNameValues(XmlElement element, string name) + { + NameValueCollection nvc = new NameValueCollection(); + XmlNodeList addElements = element.GetElementsByTagName(ObjectDefinitionConstants.AddElement); + foreach (XmlElement addElement in addElements) + { + string key = addElement.GetAttribute(ObjectDefinitionConstants.KeyAttribute); + string value = addElement.GetAttribute(ObjectDefinitionConstants.ValueAttribute); + string delimiters = addElement.GetAttribute(ObjectDefinitionConstants.DelimitersAttribute); + + if (StringUtils.HasText(delimiters)) + { + string[] values = value.Split(delimiters.ToCharArray()); + foreach (string v in values) + { + nvc.Add(key, v); + } + } + else + { + nvc[key] = value; + } + } + return nvc; + } + + /// + /// Returns the text of the supplied , + /// or the empty string value if said is empty. + /// + /// + ///

    + /// If the supplied is , + /// then the empty string value will be returned. + ///

    + ///
    + protected string GetTextValue(XmlElement element, string name) + { + if (element == null || StringUtils.IsNullOrEmpty(element.InnerText)) + { + return String.Empty; + } + return element.InnerText; + } + + /// + /// Strips the dependency check value out of the supplied string. + /// + /// + ///

    + /// If the supplied is an invalid dependency + /// checking mode, the invalid value will be logged and this method will + /// return the value. + /// No exception will be raised. + ///

    + ///
    + /// + /// The string containing the dependency check value. + /// + /// The dependency check value. + /// + protected DependencyCheckingMode GetDependencyCheck(string value) + { + DependencyCheckingMode code = DependencyCheckingMode.None; + if (StringUtils.HasText(value)) + { + try + { + code = (DependencyCheckingMode) Enum.Parse( + typeof(DependencyCheckingMode), value, true); + } + catch (ArgumentException ex) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format("Error while parsing dependency checking mode : '{0}' is an invalid value.", + value), ex); + } + + #endregion + } + } + return code; + } + + /// + /// Strips the autowiring mode out of the supplied string. + /// + /// + ///

    + /// If the supplied is an invalid autowiring mode, + /// the invalid value will be logged and this method will return the + /// value. No exception will be raised. + ///

    + ///
    + /// + /// The string containing the autowiring mode definition. + /// + /// The autowiring mode. + /// + protected AutoWiringMode GetAutowireMode(string value) + { + AutoWiringMode mode = AutoWiringMode.No; + if (StringUtils.HasText(value)) + { + try + { + mode = (AutoWiringMode) Enum.Parse( + typeof(AutoWiringMode), value, true); + } + catch (ArgumentException ex) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format("Error while parsing autowire mode : '{0}' is an invalid value.", + value), ex); + } + + #endregion + } + } + return mode; + } + + /// + /// Given a string containing delimited object names, returns + /// a string array split on the object name delimeter. + /// + /// + /// The string containing delimited object names. + /// + /// + /// A string array split on the object name delimeter. + /// + /// + private string[] GetObjectNames(string value) + { + return StringUtils.Split( + value, ObjectDefinitionConstants.ObjectNameDelimiters, true, true); + } + + private static bool IsTrueStringValue(string value) + { + return ObjectDefinitionConstants.TrueValue.Equals(value); + } + + private string GetNamespacePrefix(XmlElement element) + { + return StringUtils.HasText(element.Prefix) ? element.Prefix : "spring"; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/ParserContext.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/ParserContext.cs new file mode 100644 index 00000000..ed05a8a7 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ParserContext.cs @@ -0,0 +1,128 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Context that gets passed along an object definition parsing process, encapsulating + /// all relevant configuraiton as well as state. + /// + public class ParserContext + { + private XmlReaderContext readerContext; + + private ObjectDefinitionParserHelper parserHelper; + + private IObjectDefinition containingObjectDefinition; + + private Stack containingComponents = new Stack(); + + + /// + /// Initializes a new instance of the class. + /// + /// The reader context. + /// The parser helper. + public ParserContext(XmlReaderContext readerContext, ObjectDefinitionParserHelper parserHelper) + { + this.readerContext = readerContext; + this.parserHelper = parserHelper; + } + + + /// + /// Initializes a new instance of the class. + /// + /// The reader context. + /// The parser helper. + /// The containing object definition. + public ParserContext(XmlReaderContext readerContext, ObjectDefinitionParserHelper parserHelper, IObjectDefinition containingObjectDefinition) + { + this.readerContext = readerContext; + this.parserHelper = parserHelper; + this.containingObjectDefinition = containingObjectDefinition; + } + + + /// + /// Gets the reader context. + /// + /// The reader context. + public XmlReaderContext ReaderContext + { + get { return readerContext; } + } + + /// + /// Gets the registry. + /// + /// The registry. + public IObjectDefinitionRegistry Registry + { + get { return readerContext.Registry; } + } + + + /// + /// Gets the parser helper. + /// + /// The parser helper. + public ObjectDefinitionParserHelper ParserHelper + { + get { return parserHelper; } + } + + + /// + /// Gets the containing object definition. + /// + /// The containing object definition. + public IObjectDefinition ContainingObjectDefinition + { + get { return containingObjectDefinition; } + } + + /// + /// Gets a value indicating whether this instance is nested. + /// + /// true if this instance is nested; otherwise, false. + public bool IsNested + { + get { return containingObjectDefinition != null; } + } + + /// + /// Gets a value indicating whether this instance is default lazy init. + /// + /// + /// true if this instance is default lazy init; otherwise, false. + /// + public bool IsDefaultLazyInit + { + get { return ObjectDefinitionConstants.TrueValue.Equals(ParserHelper.Defaults.LazyInit); } + } + + + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectDefinitionReader.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectDefinitionReader.cs new file mode 100644 index 00000000..b2bd456f --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectDefinitionReader.cs @@ -0,0 +1,329 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Xml; +using System.Xml.Schema; +using Spring.Core.IO; +using Spring.Objects.Factory.Support; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Object definition reader for Spring's default XML object definition format. + /// + /// + ///

    + /// Typically applied to a + /// instance. + ///

    + ///

    + /// This class registers each object definition with the given object factory superclass, + /// and relies on the latter's implementation of the + /// interface. + ///

    + ///

    + /// It supports singletons, prototypes, and references to either of these kinds of object. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: XmlObjectDefinitionReader.cs,v 1.33 2008/02/04 22:44:03 markpollack Exp $ + public class XmlObjectDefinitionReader : AbstractObjectDefinitionReader + { + #region Fields + + [NonSerialized] + private XmlResolver resolver; + + private Type documentReaderType = typeof (DefaultObjectDefinitionDocumentReader); + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// instance that this reader works on. + /// + public XmlObjectDefinitionReader(IObjectDefinitionRegistry registry) + : this(registry, new XmlUrlResolver()) + {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// instance that this reader works on. + /// + /// + /// The to be used for parsing. + /// + public XmlObjectDefinitionReader(IObjectDefinitionRegistry registry, XmlResolver resolver) : base(registry) + { + Resolver = resolver; + } + + #endregion + + #region Properties + + /// + /// The to be used for parsing. + /// + public XmlResolver Resolver + { + get { return resolver; } + set { resolver = value; } + } + + + /// + /// Sets the IObjectDefinitionDocumentReader implementation to use, responsible for + /// the actual reading of the XML object definition document.stype of the document reader. + /// + /// The type of the document reader. + public Type DocumentReaderType + { + set + { + if (value == null || !typeof(IObjectDefinitionDocumentReader).IsAssignableFrom(value)) + { + throw new ArgumentException( + "DocumentReaderType must be an implementation of the IObjectDefinitionReader interface."); + } + documentReaderType = value; + } + } + + #endregion + + #region Methods + + /// + /// Load object definitions from the supplied XML . + /// + /// + /// The XML resource for the object definitions that are to be loaded. + /// + /// + /// The number of object definitions that were loaded. + /// + /// + /// In the case of loading or parsing errors. + /// + public override int LoadObjectDefinitions(IResource resource) + { + if (resource == null) + { + throw new ObjectDefinitionStoreException + ("Resource cannot be null: expected an XML resource."); + } + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Loading XML object definitions from " + resource); + } + + #endregion + + try + { + Stream stream = resource.InputStream; + if (stream == null) + { + throw new ObjectDefinitionStoreException( + "InputStream is null from Resource = [" + resource + "]"); + } + try + { + return DoLoadObjectDefinitions(stream, resource); + } + finally + { + #region Close stream + try + { + stream.Close(); + } + catch (IOException ex) + { + #region Instrumentation + + if (log.IsWarnEnabled) + { + log.Warn("Could not close stream.", ex); + } + + #endregion + } + #endregion + } + } + catch (IOException ex) + { + throw new ObjectDefinitionStoreException( + "IOException parsing XML document from " + resource.Description, ex); + } + + } + + /// + /// Actually load object definitions from the specified XML file. + /// + /// The input stream to read from. + /// The resource for the XML data. + /// + protected virtual int DoLoadObjectDefinitions(Stream stream, IResource resource) + { + try + { + XmlReader reader = null; + + if (SystemUtils.MonoRuntime) + { + reader = XmlUtils.CreateReader(stream); + } + else + { + reader = + XmlUtils.CreateValidatingReader(stream, Resolver, NamespaceParserRegistry.GetSchemas(), + new ValidationEventHandler(HandleValidation)); + } + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Using the following XmlReader implementation : " + reader.GetType()); + } + + #endregion + + XmlDocument doc = new XmlDocument(); + doc.Load(reader); + return RegisterObjectDefinitions(doc, resource); + } + catch (XmlException ex) + { + throw new XmlObjectDefinitionStoreException(resource.Description, + "Line " + ex.LineNumber + " in XML document from " + + resource + " is invalid. " + ex.Message, ex); + } + catch (Exception ex) + { + throw new ObjectDefinitionStoreException("Unexpected exception parsing XML document from " + resource.Description + "Inner exception message= " + ex.Message, ex); + } + } + + /// + /// Validation callback for a validating XML reader. + /// + /// The source of the event. + /// Any data pertinent to the event. + private void HandleValidation(object sender, ValidationEventArgs args) + { + if (args.Severity == XmlSeverityType.Error) + { +#if !NET_1_0 + throw new XmlException(args.Message, args.Exception, args.Exception.LineNumber, args.Exception.LinePosition); +#else + throw new XmlException(args.Message, args.Exception); +#endif + } + else + { + #region Instrumentation + + if (log.IsWarnEnabled) + { + log.Warn( + "Ignored XML validation warning: " + args.Message, + args.Exception); + } + + #endregion + } + } + + /// + /// Register the object definitions contained in the given DOM document. + /// + /// The DOM document. + /// + /// The original resource from where the + /// was read. + /// + /// + /// The number of object definitions that were registered. + /// + /// + /// In case of parsing errors. + /// + public int RegisterObjectDefinitions( + XmlDocument doc, IResource resource) + { + IObjectDefinitionDocumentReader documentReader = CreateObjectDefinitionDocumentReader(); + + //TODO make void return and get object count from registry. + int countBefore = Registry.ObjectDefinitionCount; + documentReader.RegisterObjectDefinitions(doc, CreateReaderContext(resource)); + return Registry.ObjectDefinitionCount - countBefore; + } + + /// + /// Creates the to use for actually + /// reading object definitions from an XML document. + /// + /// Default implementation instantiates the specified 'documentReaderType'. + /// + protected virtual IObjectDefinitionDocumentReader CreateObjectDefinitionDocumentReader() + { + return (IObjectDefinitionDocumentReader) ObjectUtils.InstantiateType(documentReaderType); + } + + /// + /// Creates the to be passed along + /// during the object definition reading process. + /// + /// The underlying that is currently processed. + /// A new + protected virtual XmlReaderContext CreateReaderContext(IResource resource) + { + return new XmlReaderContext(resource, this); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectDefinitionStoreException.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectDefinitionStoreException.cs new file mode 100644 index 00000000..26b92f36 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectDefinitionStoreException.cs @@ -0,0 +1,121 @@ + + +using System; +using System.Runtime.Serialization; +using System.Xml; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// XML-specific ObjectDefinitionStoreException subclass that wraps a XmlException, which + /// contains information about the error location. + /// + [Serializable] + public class XmlObjectDefinitionStoreException : ObjectDefinitionStoreException + { + + #region Required for Standards Compliance + /// + /// Initializes a new instance of the class. + /// + public XmlObjectDefinitionStoreException() + { + } + + /// + /// Creates a new instance of the XmlObjectDefinitionStoreException class. + /// + /// + /// A message about the exception. + /// + public XmlObjectDefinitionStoreException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the XmlObjectDefinitionStoreException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public XmlObjectDefinitionStoreException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the XmlObjectDefinitionStoreException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected XmlObjectDefinitionStoreException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The description of the resource that the object definition came from + /// The detail message (used as exception message as-is). + /// The XmlException root cause. + public XmlObjectDefinitionStoreException(string resourceDescription, string msg, XmlException cause) + : base(resourceDescription, msg, cause) + { + + } + + /// + /// Gets the line number in the XML resource that failed. + /// + /// The line number if available (in case of a XmlException); -1 else. + public int LineNumber + { + get + { + XmlException cause = InnerException as XmlException; + if (cause != null) + { + return (cause.LineNumber); + } + else + { + return -1; + } + } + } + + /// + /// Gets the line position in the XML resource that failed. + /// + /// The line position if available (in case of a XmlException); -1 else. + public int LinePosition + { + get + { + XmlException cause = InnerException as XmlException; + if (cause != null) + { + return (cause.LinePosition); + } + else + { + return -1; + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectFactory.cs new file mode 100644 index 00000000..b2f25884 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlObjectFactory.cs @@ -0,0 +1,150 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Core.IO; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Convenience extension of + /// + /// that reads object definitions from an XML document or element. + /// + /// + ///

    + /// Delegates to + /// + /// underneath; effectively equivalent to using a + /// for a + /// . + ///

    + /// + /// objects doesn't need to be the root element of + /// the XML document: this class will parse all object definition elements in the + /// XML stream. + /// + ///

    + /// This class registers each object definition with the + /// + /// superclass, and relies on the latter's implementation of the + /// interface. It supports + /// singletons, prototypes and references to either of these kinds of object. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: XmlObjectFactory.cs,v 1.13 2007/08/08 17:47:13 bbaia Exp $ + /// + [Serializable] + public class XmlObjectFactory : DefaultListableObjectFactory + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the class, + /// with the given resource, which must be parsable using DOM. + /// + /// + /// The XML resource to load object definitions from. + /// + /// + /// In the case of loading or parsing errors. + /// + public XmlObjectFactory(IResource resource) : this(resource, true, null) + { + } + + /// + /// Creates a new instance of the class, + /// with the given resource, which must be parsable using DOM. + /// + /// + /// The XML resource to load object definitions from. + /// + /// Flag specifying whether to make this object factory case sensitive or not. + /// + /// In the case of loading or parsing errors. + /// + public XmlObjectFactory(IResource resource, bool caseSensitive) : this(resource, caseSensitive, null) + { + } + + /// + /// Creates a new instance of the class, + /// with the given resource, which must be parsable using DOM, and the + /// given parent factory. + /// + /// + /// The XML resource to load object definitions from. + /// + /// The parent object factory (may be ). + /// + /// In the case of loading or parsing errors. + /// + public XmlObjectFactory( + IResource resource, IObjectFactory parentFactory) + : this(resource, true, parentFactory) + {} + + /// + /// Creates a new instance of the class, + /// with the given resource, which must be parsable using DOM, and the + /// given parent factory. + /// + /// + /// The XML resource to load object definitions from. + /// + /// Flag specifying whether to make this object factory case sensitive or not. + /// The parent object factory (may be ). + /// + /// In the case of loading or parsing errors. + /// + public XmlObjectFactory( + IResource resource, bool caseSensitive, IObjectFactory parentFactory) + : base(caseSensitive, parentFactory) + { + ObjectDefinitionReader.LoadObjectDefinitions(resource); + } + + #endregion + + #region Properties + + /// + /// Gets object definition reader to use. + /// + protected virtual IObjectDefinitionReader ObjectDefinitionReader + { + get + { + return new XmlObjectDefinitionReader(this); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/XmlReaderContext.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlReaderContext.cs new file mode 100644 index 00000000..2cc7a2a0 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/XmlReaderContext.cs @@ -0,0 +1,239 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Xml; +using Spring.Core.IO; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Parsing; +using Spring.Objects.Factory.Support; +using Spring.Util; + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Extension of specific to use with an + /// XmlObjectDefinitionReader. + /// + /// In future will contain access to IXmlParserRegistry + /// $Id: XmlReaderContext.cs,v 1.6 2007/08/27 13:57:43 oakinger Exp $ + public class XmlReaderContext : ReaderContext + { + + //TODO: Should have a ref to NamespaceParserRegistry, i.e. NamespaceHandlerResolver here.... + + private IObjectDefinitionReader reader; + + private IObjectDefinitionFactory objectDefinitionFactory = new DefaultObjectDefinitionFactory(); + + /// + /// The maximum length of any XML fragment displayed in the error message + /// reporting. + /// + /// + ///

    + /// Hopefully this will display enough context so that a user + /// can pinpoint the cause of the error. + ///

    + ///
    + private const int MaxXmlErrorFragmentLength = 255; + + /// + /// Initializes a new instance of the class. + /// + /// The resource. + /// The reader. + public XmlReaderContext(IResource resource, IObjectDefinitionReader reader) : base(resource) + { + this.reader = reader; + + } + + + /// + /// Gets the reader. + /// + /// The reader. + public IObjectDefinitionReader Reader + { + get { return reader; } + } + + /// + /// Gets the resource loader. + /// + /// The resource loader. + public IResourceLoader ResourceLoader + { + get { return reader.ResourceLoader; } + } + + /// + /// Gets the registry. + /// + /// The registry. + public IObjectDefinitionRegistry Registry + { + get + { + return reader.Registry; + } + } + + + /// + /// Gets or sets the object definition factory. + /// + /// The object definition factory. + public IObjectDefinitionFactory ObjectDefinitionFactory + { + get { return objectDefinitionFactory; } + set { objectDefinitionFactory = value; } + } + + + + /// + /// Generates the name of the object. + /// + /// The object definition. + /// the generated object name + public string GenerateObjectName(IObjectDefinition objectDefinition) + { + return reader.ObjectNameGenerator.GenerateObjectName(objectDefinition, Registry); + } + + /// + /// Registers the name of the with generated. + /// + /// The object definition. + /// the generated object name + public string RegisterWithGeneratedName(IObjectDefinition objectDefinition) + { + string generatedName = GenerateObjectName(objectDefinition); + Registry.RegisterObjectDefinition(generatedName, objectDefinition); + return generatedName; + } + + /// + /// Reports a parse error by loading a + /// with helpful contextual + /// information and throwing said exception. + /// + /// + ///

    + /// Derived classes can of course override this method in order to implement + /// validators capable of displaying a full list of errors found in the + /// definition. + ///

    + ///
    + /// + /// The node that triggered the parse error. + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// A message about the exception. + /// + /// + /// Always throws an instance of this exception class, that will + /// contain helpful contextual infomation about the parse error. + /// + /// + public void ReportException(XmlNode node, string name, string message) + { + ReportException(node, name, message, null); + } + + /// + /// Reports a parse error by loading a + /// with helpful contextual + /// information and throwing said exception. + /// + /// + ///

    + /// Derived classes can of course override this method in order to implement + /// validators capable of displaying a full list of errors found in the + /// definition. + ///

    + ///
    + /// + /// The node that triggered the parse error. + /// + /// + /// The name of the object that triggered the exception. + /// + /// + /// A message about the error. + /// + /// + /// The root cause of the parse error (if any - may be ). + /// + /// + /// Always throws an instance of this exception class, that will + /// contain helpful contextual infomation about the parse error. + /// + public virtual void ReportException( + XmlNode node, string name, string message, Exception cause) + { + string xmlFragment; + + if (node is XmlAttribute) + { + xmlFragment = ((XmlAttribute)node).OwnerElement.OuterXml; + } + else + { + xmlFragment = node.OuterXml; + } + if (xmlFragment.Length > MaxXmlErrorFragmentLength) + { + xmlFragment = xmlFragment.Substring(0, MaxXmlErrorFragmentLength) + "..."; + } + + string resourceDescription = Resource.Description; + int line = ConfigurationUtils.GetLineNumber(node); + if (line > 0) + { + string atLine = " at line " + line; + resourceDescription += atLine; + } + throw new ObjectDefinitionStoreException( + resourceDescription, name, message + Environment.NewLine + xmlFragment, cause); + } + + /// + /// This method can be overwritten in order to implement validators + /// capable of displaying a full list of errors found in the definition. + /// + /// + /// The node that triggered the parse error. + /// + /// + /// A message about the exception. + /// + public virtual void ReportFatalException(XmlNode node, string message) + { + throw new FatalObjectException(message); + } + + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/spring-objects-1.1.xsd b/src/Spring/Spring.Core/Objects/Factory/Xml/spring-objects-1.1.xsd new file mode 100644 index 00000000..71178b7a --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/spring-objects-1.1.xsd @@ -0,0 +1,544 @@ + + + + + Spring Objects XML Schema Definition + Based on Spring Beans DTD, authored by Rod Johnson & Juergen Hoeller + + Author: Griffin Caprio + + This defines a simple and consistent way of creating a namespace + of managed objects configured by a Spring XmlObjectFactory. + This document type is used by most Spring functionality, including + web application contexts, which are based on object factories. + + Each object element in this document defines an object. + Typically the object type (System.Type is specified, along with plain vanilla + object properties. + + Object instances can be "singletons" (shared instances) or "prototypes" + (independent instances). + + References among objects are supported, i.e. setting an object property + to refer to another object in the same factory or an ancestor factory. + + As alternative to object references, "inner object definitions" can be used. + Singleton flags and names of such "inner object" are always ignored: + Inner object are anonymous prototypes. + + There is also support for lists, dictionaries, and sets. + + + + + + + + + + + + + + + + + + + + + Defines a base type for any required string. Defines a string with a minimum length of 0 + + + + + + + + + Element containing informative text describing the purpose of the enclosing + element. Always optional. + Used primarily for user documentation of XML object definition documents. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Defines constructor argument. + + + + + + + + + + + + + + + + + + + Defines property. + + + + + + + + + + + Defines a single named object. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The document root. At least one object definition is required. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/spring-objects-1.1.xsx b/src/Spring/Spring.Core/Objects/Factory/Xml/spring-objects-1.1.xsx new file mode 100644 index 00000000..d79bfd40 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/spring-objects-1.1.xsx @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + <_x0028_group1_x0029__XmlChoice left="7243" top="43376" width="5292" height="3757" selected="0" zOrder="15" index="1" expanded="1"> + + + + + <_x0028_group1_x0029__XmlChoice left="19095" top="39111" width="5292" height="3757" selected="0" zOrder="25" index="1" expanded="0" /> + + + <_x0028_group1_x0029__XmlChoice left="19095" top="43376" width="5292" height="3757" selected="0" zOrder="29" index="1" expanded="0" /> + + + + + + + + + + + + + + + + + + + + + <_x0028_group1_x0029__XmlChoice left="7243" top="90291" width="5292" height="3757" selected="0" zOrder="53" index="1" expanded="0" /> + + + + <_x0028_group1_x0029__XmlChoice left="7243" top="98821" width="5292" height="3757" selected="0" zOrder="57" index="1" expanded="0" /> + + + <_x0028_group1_x0029__XmlChoice left="7243" top="103086" width="5292" height="3757" selected="0" zOrder="60" index="1" expanded="0" /> + + + + + + + + + + + + + <_x0028_scope_x0029__XmlSimpleType left="13169" top="128676" width="5292" height="3757" selected="0" zOrder="79" index="0" expanded="1" /> + + + <_x0028_lazy-init_x0029__XmlSimpleType left="13169" top="132941" width="5292" height="3757" selected="0" zOrder="83" index="0" expanded="1" /> + + + <_x0028_autowire_x0029__XmlSimpleType left="13169" top="137206" width="5292" height="3757" selected="0" zOrder="87" index="0" expanded="1" /> + + + <_x0028_dependency-check_x0029__XmlSimpleType left="13169" top="141471" width="5292" height="3757" selected="0" zOrder="91" index="0" expanded="1" /> + + + + <_x0028_group1_x0029__XmlChoice left="7243" top="150001" width="5292" height="3757" selected="0" zOrder="94" index="1" expanded="1"> + + + + + + <_x0028_default-dependency-check_x0029__XmlSimpleType left="13169" top="158531" width="5292" height="3757" selected="0" zOrder="104" index="0" expanded="1" /> + + + <_x0028_default-autowire_x0029__XmlSimpleType left="13169" top="162796" width="5292" height="3757" selected="0" zOrder="108" index="0" expanded="1" /> + + + \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/spring-tool-1.1.xsd b/src/Spring/Spring.Core/Objects/Factory/Xml/spring-tool-1.1.xsd new file mode 100644 index 00000000..009b5891 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/spring-tool-1.1.xsd @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Indicates that an annotated type exports an application visible component. + + + + + + The type of the exported component. May be null if the type is not known until runtime. + + + + + + + Defines an XPath query that can be executed against the node annotated with this + type to determine the identifier of any exported component. + + + + + + diff --git a/src/Spring/Spring.Core/Objects/FatalObjectException.cs b/src/Spring/Spring.Core/Objects/FatalObjectException.cs new file mode 100644 index 00000000..d2b51009 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/FatalObjectException.cs @@ -0,0 +1,91 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Objects +{ + /// + /// Thrown on an unrecoverable problem encountered in the + /// objects namespace or sub-namespaces, e.g. bad class or field. + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: FatalObjectException.cs,v 1.7 2006/04/09 07:18:49 markpollack Exp $ + /// + [Serializable] + public class FatalObjectException : ObjectsException + { + /// + /// Creates a new instance of the FatalObjectException class. + /// + public FatalObjectException() + { + } + + /// + /// Creates a new instance of the FatalObjectException class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + public FatalObjectException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the FatalObjectException class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public FatalObjectException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the FatalObjectException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected FatalObjectException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/IEventHandlerValue.cs b/src/Spring/Spring.Core/Objects/IEventHandlerValue.cs new file mode 100644 index 00000000..7b1ff97e --- /dev/null +++ b/src/Spring/Spring.Core/Objects/IEventHandlerValue.cs @@ -0,0 +1,72 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +namespace Spring.Objects +{ + /// + /// Describes an event handler. + /// + /// Rick Evans + /// $Id: IEventHandlerValue.cs,v 1.6 2006/04/09 07:18:49 markpollack Exp $ + public interface IEventHandlerValue + { + /// + /// The source of the event. + /// + object Source + { + get; + set; + } + + /// + /// The name of the method that is going to handle the event. + /// + string MethodName + { + get; + set; + } + + /// + /// The name of the event that is being wired up. + /// + string EventName + { + get; + set; + } + + /// + /// Wires up the specified handler to the named event on the + /// supplied event source. + /// + /// + /// The object (an object instance, a , etc) + /// exposing the named event. + /// + /// + /// The handler for the event (an object instance, a + /// , etc). + /// + void Wire (object source, object handler); + } +} diff --git a/src/Spring/Spring.Core/Objects/IObjectWrapper.cs b/src/Spring/Spring.Core/Objects/IObjectWrapper.cs new file mode 100644 index 00000000..3081fc97 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/IObjectWrapper.cs @@ -0,0 +1,208 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Core; + +#endregion + +namespace Spring.Objects +{ + /// + /// The central interface of Spring.NET's low-level object infrastructure. + /// + /// + ///

    + /// Typically not directly used by application code but rather implicitly + /// via an . + ///

    + ///

    + /// Implementing classes have the ability to get and set property values + /// (individually or in bulk), get property descriptors and query the + /// readability and writability of properties. + ///

    + ///

    + /// This interface supports nested properties enabling the setting + /// of properties on subproperties to an unlimited depth. + ///

    + ///

    + /// If a property update causes an exception, a + /// will be thrown. Bulk + /// updates continue after exceptions are encountered, throwing an exception + /// wrapping all exceptions encountered during the update. + ///

    + ///

    + /// implementations can be used + /// repeatedly, with their "target" or wrapped object changed. + ///

    + ///
    + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: IObjectWrapper.cs,v 1.15 2007/07/31 21:43:52 markpollack Exp $ + /// + public interface IObjectWrapper + { + /// + /// The object wrapped by the wrapper (cannot be ). + /// + /// + ///

    + /// Implementations are required to allow the type of the wrapped + /// object to change. + ///

    + ///
    + /// The object wrapped by this wrapper. + object WrappedInstance { get; set; } + + /// + /// Convenience method to return the + /// of the wrapped object. + /// + /// The of the wrapped object. + Type WrappedType { get; } + + /// Get the value of a property. + /// + /// The name of the property to get the value of. May be nested. + /// + /// The value of the property. + /// + /// if the property isn't readable, or if the getting the value throws + /// an exception. + /// + object GetPropertyValue(string theProperty); + + /// + /// Get the for a particular + /// property. + /// + /// + /// The property to be retrieved. + /// + /// + /// The for the particular + /// property. + /// + PropertyInfo GetPropertyInfo(string theProperty); + + /// + /// Get the for a particular property. + /// + /// + /// The property the of which is to be retrieved. + /// + /// + /// The for a particular property.. + /// + Type GetPropertyType(string theProperty); + + /// + /// Get all of the instances for + /// all of the properties of the wrapped object. + /// + /// + /// An array of instances. + /// + PropertyInfo[] GetPropertyInfos(); + + /// + /// Set a property value. + /// + /// + ///

    + /// This is the preferred way to update an individual property. + ///

    + ///
    + /// The new property value. + void SetPropertyValue(PropertyValue propertyValue); + + /// + /// Set a property value. + /// + /// + ///

    + /// This method is provided for convenience only. The + /// + /// method is more powerful. + ///

    + ///
    + /// + /// The name of the property to set value of. + /// + /// The new property value. + void SetPropertyValue(string theProperty, object propertyValue); + + /// Set a number of property values in bulk. + /// + ///

    + /// This is the preferred way to perform a bulk update. + ///

    + ///

    + /// Note that performing a bulk update differs from performing a single update, + /// in that an implementation of this class will continue to update properties + /// if a recoverable error (such as a vetoed property change or a type + /// mismatch, but not an invalid property name or the like) is + /// encountered, throwing a + /// containing + /// all the individual errors. This exception can be examined later to see all + /// binding errors. Properties that were successfully updated stay changed. + ///

    + ///

    + /// Does not allow the setting of unknown fields. Equivalent to + /// + /// with an argument of false for the second parameter. + ///

    + ///
    + /// + /// The collection of instances to + /// set on the wrapped object. + /// + void SetPropertyValues(IPropertyValues values); + + /// + /// Set a number of property values in bulk with full control over behavior. + /// + /// + ///

    + /// Note that performing a bulk update differs from performing a single update, + /// in that an implementation of this class will continue to update properties + /// if a recoverable error (such as a vetoed property change or a type + /// mismatch, but not an invalid property name or the like) is + /// encountered, throwing a + /// containing + /// all the individual errors. This exception can be examined later to see all + /// binding errors. Properties that were successfully updated stay changed. + ///

    + ///

    Does not allow the setting of unknown fields. + ///

    + ///
    + /// + /// The to set on the target object + /// + /// + /// Should we ignore unknown values (not found in the object!?) + /// + void SetPropertyValues(IPropertyValues values, bool ignoreUnknown); + + } +} diff --git a/src/Spring/Spring.Core/Objects/IPropertyValues.cs b/src/Spring/Spring.Core/Objects/IPropertyValues.cs new file mode 100644 index 00000000..da6e032b --- /dev/null +++ b/src/Spring/Spring.Core/Objects/IPropertyValues.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +#endregion + +namespace Spring.Objects +{ + /// + /// A collection style container for + /// instances. + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: IPropertyValues.cs,v 1.7 2006/04/09 07:18:49 markpollack Exp $ + public interface IPropertyValues : IEnumerable + { + /// + /// Return an array of the objects + /// held in this object. + /// + /// An array of the objects held + /// in this object. + /// + PropertyValue [] PropertyValues + { + get; + } + + /// + /// Return the instance with the + /// given name. + /// + /// The name to search for. + /// the , or null if a + /// the with the supplied + /// did not exist in this collection. + /// + PropertyValue GetPropertyValue(string propertyName); + + /// + /// Is there a instance for this + /// property name? + /// + /// The name to search for. + /// + /// True if there is a instance for + /// the supplied . + /// + bool Contains(string propertyName); + + /// + /// Return the difference (changes, additions, but not removals) of + /// property values between the supplied argument and the values + /// contained in the collection. + /// + /// + ///

    + /// Subclasses should also override Equals. + ///

    + ///
    + /// The old property values. + /// + /// An containing any changes, or + /// an empty instance if there were + /// no changes. + /// + IPropertyValues ChangesSince (IPropertyValues old); + } +} diff --git a/src/Spring/Spring.Core/Objects/MutablePropertyValues.cs b/src/Spring/Spring.Core/Objects/MutablePropertyValues.cs new file mode 100644 index 00000000..296813f7 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/MutablePropertyValues.cs @@ -0,0 +1,348 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using System.Text; +using Spring.Util; + +#endregion + +namespace Spring.Objects +{ + /// + /// Default implementation of the + /// interface. + /// + /// + ///

    + /// Allows simple manipulation of properties, and provides constructors to + /// support deep copy and construction from a number of collection types such as + /// and + /// . + ///

    + ///
    + /// Rod Johnson + /// Mark Pollack (.NET) + /// Rick Evans (.NET) + /// $Id: MutablePropertyValues.cs,v 1.15 2007/03/16 04:01:29 aseovic Exp $ + [Serializable] + public class MutablePropertyValues : IPropertyValues + { + #region Fields + + /// + /// The list of objects. + /// + private IList propertyValuesList = new ArrayList(); + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// The returned instance is initially empty... + /// s can be added with the various + /// overloaded , + /// , + /// , + /// and + /// methods. + ///

    + ///
    + /// + /// + public MutablePropertyValues () + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// Deep copy constructor. Guarantees + /// references are independent, although it can't deep copy objects currently + /// referenced by individual objects. + ///

    + ///
    + public MutablePropertyValues (IPropertyValues other) + { + if (other != null) + { + AddAll (other.PropertyValues); + } + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The with property values + /// keyed by property name, which must be a . + /// + public MutablePropertyValues (IDictionary map) + { + AddAll (map); + } + + #endregion + + #region Properties + + /// + /// Property to retrieve the array of property values. + /// + public PropertyValue[] PropertyValues + { + get { return (PropertyValue[]) ArrayList.Adapter (propertyValuesList).ToArray (typeof (PropertyValue)); } + } + + #endregion + + #region Methods + + /// + /// Overloaded version of Add that takes a property name and a property value. + /// + /// + /// The name of the property. + /// + /// + /// The value of the property. + /// + public void Add (string propertyName, object propertyValue) + { + Add (new PropertyValue (propertyName, propertyValue)); + } + + /// + /// Add the supplied object, + /// replacing any existing one for the respective property. + /// + /// + /// The object to add. + /// + public void Add (PropertyValue pv) + { + for (int i = 0; i < propertyValuesList.Count; ++i) + { + PropertyValue currentPv = (PropertyValue) propertyValuesList [i]; + if (currentPv.Name.Equals (pv.Name)) + { + propertyValuesList[i] = pv; + return ; + } + } + propertyValuesList.Add (pv); + } + + /// + /// Add all property values from the given + /// . + /// + /// + /// The map of property values, the keys of which must be + /// s. + /// + public void AddAll (IDictionary map) + { + if (map != null) + { + foreach (string key in map.Keys) + { + Add (new PropertyValue (key, map [key])); + } + } + } + + /// + /// Add all property values from the given + /// . + /// + /// + /// The list of s to be added. + /// + public void AddAll (IList values) + { + if (values != null) + { + foreach (PropertyValue value in values) + { + Add (value); + } + } + } + + /// + /// Remove the given , if contained. + /// + /// + /// The to remove. + /// + public void Remove (PropertyValue pv) + { + propertyValuesList.Remove (pv); + } + + /// + /// Removes the named , if contained. + /// + /// + /// The name of the property. + /// + public void Remove (string propertyName) + { + Remove (GetPropertyValue (propertyName)); + } + + /// + /// Modify a object held in this object. Indexed from 0. + /// + public void SetPropertyValueAt (PropertyValue pv, int i) + { + propertyValuesList [i] = pv; + } + + /// + /// Return the property value given the name. + /// + /// + /// The property name is checked in a case-insensitive fashion. + /// + /// + /// The name of the property. + /// + /// + /// The property value. + /// + public PropertyValue GetPropertyValue (string propertyName) + { + string propertyNameLowered = propertyName.ToLower (CultureInfo.CurrentCulture); + foreach (PropertyValue pv in propertyValuesList) + { + if (pv.Name.ToLower(CultureInfo.CurrentCulture).Equals (propertyNameLowered)) + { + return pv; + } + } + return null; + } + + /// + /// Does the container of properties contain one of this name. + /// + /// The name of the property to search for. + /// + /// True if the property is contained in this collection, false otherwise. + /// + public bool Contains (string propertyName) + { + return GetPropertyValue (propertyName) != null; + } + + /// + /// Return the difference (changes, additions, but not removals) of + /// property values between the supplied argument and the values + /// contained in the collection. + /// + /// Another property values collection. + /// + /// The collection of property values that are different than the supplied one. + /// + public IPropertyValues ChangesSince (IPropertyValues old) + { + MutablePropertyValues changes = new MutablePropertyValues (); + if (old == this) + { + return changes; + } + // for each property value in this (the newer set) + foreach (PropertyValue newProperty in propertyValuesList) + { + PropertyValue oldProperty = old.GetPropertyValue (newProperty.Name); + if (oldProperty == null) + { + // if there wasn't an old one, add it + changes.Add (newProperty); + } + else if (!oldProperty.Equals (newProperty)) + { + // it's changed + changes.Add (newProperty); + } + } + return changes; + } + + /// + /// Returns an that can iterate + /// through a collection. + /// + /// + ///

    + /// The returned is the + /// exposed by the + /// + /// property. + ///

    + ///
    + /// + /// An that can iterate through a + /// collection. + /// + public IEnumerator GetEnumerator () + { + return PropertyValues.GetEnumerator (); + } + + // CLOVER:OFF + + /// + /// Convert the object to a string representation. + /// + /// + /// A string representation of the object. + /// + public override string ToString () + { + PropertyValue[] pvs = PropertyValues; + StringBuilder sb + = new StringBuilder ( + "MutablePropertyValues: length=").Append (pvs.Length).Append ("; "); + sb.Append (StringUtils.ArrayToDelimitedString (pvs, ",")); + return sb.ToString (); + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/ObjectWrapper.cs b/src/Spring/Spring.Core/Objects/ObjectWrapper.cs new file mode 100644 index 00000000..53409a96 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/ObjectWrapper.cs @@ -0,0 +1,547 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.Reflection; +using System.Text; + +using antlr; +using Common.Logging; +using Spring.Core; +using Spring.Expressions; +using Spring.Util; +using StringUtils=Spring.Util.StringUtils; + +#endregion + +namespace Spring.Objects +{ + /// + /// Default implementation of the + /// interface that should be sufficient for all normal uses. + /// + /// + ///

    + /// will convert + /// and array + /// values to the corresponding target arrays, if necessary. Custom + /// s that deal with + /// s or arrays can be written against a + /// comma delimited as + /// arrays are converted in such a format if the array itself is not assignable. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Jean-Pierre Pawlak + /// Mark Pollack (.NET) + /// Aleksandar Seovic(.NET) + /// $Id: ObjectWrapper.cs,v 1.72 2007/07/31 08:18:20 markpollack Exp $ + [Serializable] + public class ObjectWrapper : IObjectWrapper + { + private ILog Log = LogManager.GetLogger(typeof(ObjectWrapper)); + + #region Fields + + /// The wrapped object. + private object wrappedObject; + + /// + /// The ILog instance for this class. We'll create a lot of these objects, + /// so we don't want a new instance every time. + /// + private static readonly ILog log = LogManager.GetLogger(typeof(ObjectWrapper)); + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// The wrapped target instance will need to be set afterwards. + ///

    + ///
    + /// + public ObjectWrapper() + {} + + /// + /// Creates a new instance of the class. + /// + /// + /// The object wrapped by this . + /// + /// + /// If the supplied is . + /// + public ObjectWrapper(object instance) + { + WrappedInstance = instance; + } + + /// + /// Creates a new instance of the class, + /// instantiating a new instance of the specified and using + /// it as the . + /// + /// + ///

    + /// Please note that the passed as the + /// argument must have a no-argument constructor. + /// If it does not, an exception will be thrown when this class attempts + /// to instantiate the supplied using it's + /// (non-existent) constructor. + ///

    + ///
    + /// + /// The to instantiate and wrap. + /// + /// + /// If the is , or if the + /// invocation of the s default (no-arg) constructor + /// fails (due to invalid arguments, insufficient permissions, etc). + /// + public ObjectWrapper(Type type) : this(true) + { + try + { + WrappedInstance = ObjectUtils.InstantiateType(type); + } catch (FatalReflectionException e) + { + throw new FatalObjectException(e.Message, e); + } + } + + #endregion + + #region Properties + + /// + /// The object wrapped by this . + /// + /// + /// If the object cannot be changed; or an attempt is made to set the + /// value of this property to . + /// + public object WrappedInstance + { + get { return wrappedObject; } + set + { + if (value == null) + { + throw new FatalObjectException("Wraped instance cannot be null."); + } + this.wrappedObject = value; + } + } + + /// + /// Convenience method to return the of the wrapped object. + /// + /// + ///

    + /// Do not use this (convenience) method prior to setting the + /// property. + ///

    + ///
    + /// + /// The of the wrapped object. + /// + /// + /// If the property + /// is . + /// + public Type WrappedType + { + get { return WrappedInstance.GetType(); } + } + + #endregion + + #region Methods + + /// Gets the value of a property. + /// + /// The name of the property to get the value of. + /// + /// The value of the property. + /// + /// If there is no such property, if the property isn't readable, or + /// if getting the property value throws an exception. + /// + public virtual object GetPropertyValue(string propertyName) + { + try + { + IExpression propertyExpression = GetPropertyExpression(propertyName); + return GetPropertyValue(propertyExpression); + } + catch (RecognitionException e) + { + throw new InvalidPropertyException("Failed to parse property name '" + propertyName + "'.", e); + } + catch (TokenStreamRecognitionException e) + { + throw new InvalidPropertyException("Failed to parse property name '" + propertyName + "'.", e); + } + } + + /// Gets the value of a property. + /// + /// The property expression that should be used to retrieve the property value. + /// + /// The value of the property. + /// + /// If there is no such property, if the property isn't readable, or + /// if getting the property value throws an exception. + /// + public virtual object GetPropertyValue(IExpression propertyExpression) + { + return propertyExpression.GetValue(this.wrappedObject); + } + + /// + /// Sets a property value. + /// + /// + ///

    + /// This method is provided for convenience only. The + /// + /// method is more powerful. + ///

    + ///
    + /// + /// The name of the property to set value of. + /// + /// The new value. + public virtual void SetPropertyValue(string propertyName, object val) + { + try + { + IExpression propertyExpression = GetPropertyExpression(propertyName); + SetPropertyValue(propertyExpression, val); + } + catch (RecognitionException e) + { + throw new InvalidPropertyException("Failed to parse property name '" + propertyName + "'.", e); + } + catch (TokenStreamRecognitionException e) + { + throw new InvalidPropertyException("Failed to parse property name '" + propertyName + "'.", e); + } + } + + /// + /// Sets a property value. + /// + /// + /// The property expression that should be used to set the property value. + /// + /// The new value. + public virtual void SetPropertyValue(IExpression propertyExpression, object val) + { + propertyExpression.SetValue(this.wrappedObject, val); + } + + /// + /// Sets a property value. + /// + /// + ///

    + /// This is the preferred way to update an individual property. + ///

    + ///
    + /// + /// The object containing new property value. + /// + public virtual void SetPropertyValue(PropertyValue pv) + { + SetPropertyValue(pv.Expression, pv.Value); + } + + /// Set a number of property values in bulk. + /// + ///

    + /// Does not allow unknown fields. Equivalent to + /// + /// with and for + /// arguments. + ///

    + ///
    + /// + /// The to set on the target + /// object. + /// + /// + /// If an error is encountered while setting a property. + /// + /// + /// On a mismatch while setting a property, insufficient permissions, etc. + /// + /// + public virtual void SetPropertyValues(IPropertyValues pvs) + { + SetPropertyValues(pvs, false); + } + + /// + /// Perform a bulk update with full control over behavior. + /// + /// + ///

    + /// This method may throw a reflection-based exception, if there is a critical + /// failure such as no matching field... less serious exceptions will be accumulated + /// and thrown as a single . + ///

    + ///
    + /// + /// The s to set on the target object. + /// + /// + /// Should we ignore unknown values (not found in the object!?). + /// + /// + /// If an error is encountered while setting a property (only thrown if the + /// parameter is set to ). + /// + /// + /// On a mismatch while setting a property, insufficient permissions, etc. + /// + /// + public virtual void SetPropertyValues(IPropertyValues propertyValues, bool ignoreUnknown) + { + ArrayList propertyAccessExceptions = new ArrayList(); + foreach (PropertyValue pv in propertyValues) + { + try + { + SetPropertyValue(pv); + } + catch (NotWritablePropertyException ex) + { + if (!ignoreUnknown) + { + Log.Error(string.Format("Failed setting property '{0}'", pv.Name), ex); + throw; + } + } + catch (InvalidPropertyException ex) + { + if (!ignoreUnknown) + { + Log.Error(string.Format("Failed setting property '{0}'", pv.Name), ex); + throw; + } + } + catch (TypeMismatchException ex) // otherwise, just ignore it and continue... + { + Log.Error(string.Format("Failed setting property '{0}'", pv.Name), ex); + propertyAccessExceptions.Add(ex); + } + catch (MethodInvocationException ex) + { + Log.Error(string.Format("Failed setting property '{0}'", pv.Name), ex); + propertyAccessExceptions.Add(ex); + } + catch (Exception ex) + { + Log.Error(string.Format("Failed setting property '{0}' on instance of type '{1}'", pv.Name, this.WrappedType.FullName), ex); + throw; + } + } + + // if we encountered individual exceptions, throw the composite exception... + if (propertyAccessExceptions.Count > 0) + { + throw new PropertyAccessExceptionsException(this, + (PropertyAccessException[]) propertyAccessExceptions.ToArray(typeof(PropertyAccessException))); + } + } + + /// + /// Returns PropertyInfo for the specified property + /// + /// The name of the property to search for. + /// The for the specified property. + /// If cannot be determined. + public PropertyInfo GetPropertyInfo(string propertyName) + { + return (PropertyInfo) this.GetPropertyOrFieldInfo(propertyName); + } + + /// + /// Get the for a particular property. + /// + /// + /// The property the of which is to be retrieved. + /// + /// + /// The for a particular property.. + /// + public Type GetPropertyType(string propertyName) + { + MemberInfo memberInfo = this.GetPropertyOrFieldInfo(propertyName); + switch(memberInfo.MemberType) + { + case MemberTypes.Property: + return ((PropertyInfo) memberInfo).PropertyType; + case MemberTypes.Field: + return ((FieldInfo) memberInfo).FieldType; + default: + throw new FatalObjectException("'" + propertyName + "' is not a valid property expression."); + } + } + + /// + /// Returns MemberInfo for the specified property or field + /// + /// The name of the property or field to search for. + /// The or for the specified property or field. + /// If does not resolve to a property or field. + private MemberInfo GetPropertyOrFieldInfo(string propertyOrFieldName) + { + if(StringUtils.IsNullOrEmpty(propertyOrFieldName)) + { + throw new FatalObjectException("Can't find property or field info for null or zero length property name."); + } + + try + { + IExpression propertyExpression = GetPropertyExpression(propertyOrFieldName); + if(propertyExpression is PropertyOrFieldNode) + { + return ((PropertyOrFieldNode)propertyExpression).GetMemberInfo(this.wrappedObject); + } + else if(propertyExpression is IndexerNode) + { + return ((IndexerNode)propertyExpression).GetPropertyInfo(this.wrappedObject, null); + } + else if(propertyExpression is Expression) + { + return ((Expression)propertyExpression).GetPropertyInfo(this.wrappedObject, null); + } + else + { + throw new FatalObjectException("'" + propertyOrFieldName + "' is not a valid property or field expression."); + } + } + catch(RecognitionException e) + { + throw new FatalObjectException("Failed to parse property or field name '" + propertyOrFieldName + "'.", e); + } + catch(TokenStreamRecognitionException e) + { + throw new FatalObjectException("Failed to parse property or field name '" + propertyOrFieldName + "'.", e); + } + } + + + /// + /// Get the properties of the wrapped object. + /// + /// + /// An array of s. + /// + public PropertyInfo[] GetPropertyInfos() + { + return WrappedType.GetProperties(); + } + + /// + /// Return the collection of property descriptors. + /// + public PropertyDescriptorCollection PropertyDescriptors + { + get { return TypeDescriptor.GetProperties(WrappedInstance); } + } + + /// + /// This method is expensive! Only call for diagnostics and debugging reasons, + /// not in production. + /// + /// + /// A string describing the state of this object. + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + try + { + sb.Append("ObjectWrapper: wrapping class ["); + sb.Append(WrappedType.FullName); + sb.Append("]; "); + foreach (PropertyDescriptor p in PropertyDescriptors) + { + object val = GetPropertyValue(p.Name); + string valStr = (val != null) ? val.ToString() : "null"; + sb.Append(p.Name).Append("={").Append(valStr).Append("}"); + } + } + catch (Exception ex) + { + sb.Append("Exception encountered: ").Append(ex.ToString()); + } + return sb.ToString(); + } + + #endregion + + #region Helper methods + + /// + /// Attempts to parse property expression first and falls back to full expression + /// if that fails. Performance optimization. + /// + /// Property expression to parse. + /// Parsed proeprty expression. + internal static IExpression GetPropertyExpression(string propertyName) + { + IExpression propertyExpression = null; + if (propertyName.IndexOfAny(new char[] { '.', '[', '(', ' ', '{' }) < 0) + { + try + { + propertyExpression = Expression.ParseProperty(propertyName); + } + catch (Exception) + { + propertyExpression = Expression.ParsePrimary(propertyName); + } + } + else + { + propertyExpression = Expression.ParsePrimary(propertyName); + } + + return propertyExpression; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/ObjectsException.cs b/src/Spring/Spring.Core/Objects/ObjectsException.cs new file mode 100644 index 00000000..0e490690 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/ObjectsException.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Objects +{ + /// + /// Superclass for all exceptions thrown in the Objects namespace and sub-namespaces. + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: ObjectsException.cs,v 1.8 2006/04/09 07:18:49 markpollack Exp $ + /// + [Serializable] + public class ObjectsException : ApplicationException + { + #region Constructor (s) / Destructor + /// Creates a new instance of the ObjectsException class. + public ObjectsException() + { + } + + /// + /// Creates a new instance of the ObjectsException class. with the specified message. + /// + /// + /// A message about the exception. + /// + public ObjectsException (string message) : base(message) + { + } + + /// + /// Creates a new instance of the ObjectsException class with the specified message + /// and root cause. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ObjectsException (string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the ObjectsException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectsException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + } + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/PropertyAccessExceptionsException.cs b/src/Spring/Spring.Core/Objects/PropertyAccessExceptionsException.cs new file mode 100644 index 00000000..6de65983 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/PropertyAccessExceptionsException.cs @@ -0,0 +1,259 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; +using System.Text; +using Spring.Core; + +#endregion + +namespace Spring.Objects +{ + /// + /// Combined exception, composed of individual binding + /// s. + /// + /// + ///

    + /// An object of this class is created at the beginning of the binding + /// process, and errors added to it as necessary. + ///

    + ///

    + /// The binding process continues when it encounters application-level + /// s, applying those changes + /// that can be applied and storing rejected changes in an instance of this class. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: PropertyAccessExceptionsException.cs,v 1.15 2007/07/31 00:26:30 markpollack Exp $ + [Serializable] + public class PropertyAccessExceptionsException : ObjectsException + { + #region Constants + + private static PropertyAccessException[] EmptyPropertyAccessExceptions + = new PropertyAccessException[] {}; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the PropertyAccessExceptionsException class. + /// + public PropertyAccessExceptionsException() + { + } + + /// + /// Creates a new instance of the PropertyAccessExceptionsException class. + /// + /// + /// A message about the exception. + /// + public PropertyAccessExceptionsException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the PropertyAccessExceptionsException class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public PropertyAccessExceptionsException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Create new empty PropertyAccessExceptionsException. + /// We'll add errors to it as we attempt to bind properties. + /// + public PropertyAccessExceptionsException( + IObjectWrapper objectWrapper, + PropertyAccessException[] propertyAccessExceptions) + : base(string.Empty) + { + _objectWrapper = objectWrapper; + _propertyAccessExceptions + = propertyAccessExceptions == null ? + EmptyPropertyAccessExceptions : + propertyAccessExceptions; + } + + /// + /// Creates a new instance of the PropertyAccessExceptionsException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected PropertyAccessExceptionsException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + #endregion + + /// + /// Return the that generated + /// this exception. + /// + public IObjectWrapper ObjectWrapper + { + get { return _objectWrapper; } + } + + /// + /// Return the object we're binding to. + /// + public object BindObject + { + get { return ObjectWrapper.WrappedInstance; } + } + + /// + /// If this returns zero (0), no errors were encountered during binding. + /// + public int ExceptionCount + { + get { return PropertyAccessExceptions.Length; } + } + + /// + /// Return an array of the s + /// stored in this object. + /// + /// + ///

    + /// Will return the empty array (not ) if there were no errors. + ///

    + ///
    + public virtual PropertyAccessException[] PropertyAccessExceptions + { + get { return _propertyAccessExceptions; } + } + + /// + /// Describe the group of exceptions. + /// + public override string Message + { + get + { + StringBuilder sb = new StringBuilder(); + sb.Append("PropertyAccessExceptionsException (") + .Append(ExceptionCount) + .Append(" errors)") + .Append("; nested PropertyAccessExceptions are: \n"); + for (int i = 0; i < PropertyAccessExceptions.Length; ++i) + { + PropertyAccessException pae = PropertyAccessExceptions[i]; + sb.Append("["); + sb.Append(pae.GetType().FullName); + sb.Append(": "); + sb.Append(pae.Message); + if (pae.InnerException != null) + { + sb.Append(", Inner Exception: "); + sb.Append(pae.InnerException.ToString()); + } + sb.Append(']'); + if (i < PropertyAccessExceptions.Length - 1) + { + sb.Append(", "); + } + } + return sb.ToString(); + } + } + + /// + /// Populates a with + /// the data needed to serialize the target object. + /// + /// + /// The to populate + /// with data. + /// + /// + /// The destination (see ) + /// for this serialization. + /// + [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] + public override void GetObjectData( + SerializationInfo info, StreamingContext context) + { + // TODO serialize me + base.GetObjectData(info, context); + } + + /// + /// The IObjectWrapper wrapping the target object at the root of the exception. + /// + private IObjectWrapper _objectWrapper; + + /// The list of PropertyAccessException objects. + private PropertyAccessException[] _propertyAccessExceptions + = EmptyPropertyAccessExceptions; + + /// + /// Return the + /// for the supplied , or + /// if there isn't one. + /// + public PropertyAccessException GetPropertyAccessException( + string propertyName) + { + foreach (PropertyAccessException pae in PropertyAccessExceptions) + { + if (propertyName.Equals(pae.PropertyChangeArgs.PropertyName)) + { + return pae; + } + } + return null; + } + + /// + /// Describe the number of exceptions contained in this container class. + /// + /// A description of the instance contents. + public override string ToString() + { + return Message; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/PropertyValue.cs b/src/Spring/Spring.Core/Objects/PropertyValue.cs new file mode 100644 index 00000000..4d013d34 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/PropertyValue.cs @@ -0,0 +1,199 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; + +using antlr; +using Spring.Core; +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Objects +{ + /// + /// Holds information and value for an individual property. + /// + /// + ///

    + /// Using an object here, rather than just storing all properties in a + /// map keyed by property name, allows for more flexibility, and the + /// ability to handle indexed properties in a special way if necessary. + ///

    + ///

    + /// Note that the value doesn't need to be the final required + /// : an + /// implementation must + /// handle any necessary conversion, as this object doesn't know anything + /// about the objects it will be applied to. + ///

    + ///
    + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: PropertyValue.cs,v 1.15 2007/07/31 00:08:42 markpollack Exp $ + [Serializable] + public class PropertyValue + { + private string propertyName; + private IExpression propertyExpression; + private object propertyValue; + + /// + /// Creates a new instance of the + /// class. + /// + /// The name of the property. + /// + /// The value of the property (possibly before type conversion). + /// + /// + /// If the supplied is or + /// contains only whitespace character(s). + /// + public PropertyValue(string name, object val) + { + AssertUtils.ArgumentHasText(name, "name"); + + propertyName = name; + propertyValue = val; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// The name of the property. + /// + /// The value of the property (possibly before type conversion). + /// + /// Pre-parsed property name. + /// + /// If the supplied or + /// is , or if the name contains only whitespace characters. + /// + public PropertyValue(string name, object val, IExpression expression) + { + AssertUtils.ArgumentHasText(name, "name"); + + propertyName = name; + propertyExpression = expression; + propertyValue = val; + } + + /// The name of the property. + /// The name of the property. + public string Name + { + get { return propertyName; } + } + + /// + /// Parsed property expression. + /// + public IExpression Expression + { + get + { + if (propertyExpression == null) + { + try + { + propertyExpression = ObjectWrapper.GetPropertyExpression(propertyName); + } + catch (RecognitionException e) + { + throw new InvalidPropertyException("Failed to parse property name '" + propertyName + "'.", e); + } + catch (TokenStreamRecognitionException e) + { + throw new InvalidPropertyException("Failed to parse property name '" + propertyName + "'.", e); + } + } + return propertyExpression; + } + } + + /// + /// Return the value of the property. + /// + /// + ///

    + /// Note that type conversion will not have occurred here. + /// It is the responsibility of the + /// implementation to + /// perform type conversion. + ///

    + ///
    + /// The (possibly unresolved) value of the property. + public object Value + { + get { return propertyValue; } + } + + /// + /// Print a string representation of the property. + /// + /// A string representation of the property. + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "PropertyValue: name='{0}'; value=[{1}].", propertyName, propertyValue); + } + + /// + /// Determines whether the supplied + /// is equal to the current . + /// + /// The other instance. + /// + /// if they are equal in content. + /// + public override bool Equals(object other) + { + if (this == other) + { + return true; + } + if (!(other is PropertyValue)) + { + return false; + } + PropertyValue otherPv = (PropertyValue) other; + return + (propertyName.Equals(otherPv.propertyName) + && ((propertyValue == null && otherPv.propertyValue == null) || propertyValue.Equals(otherPv.propertyValue))); + } + + /// + /// Serves as a hash function for a particular type, suitable for use + /// in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return propertyName.GetHashCode() * 29 + (propertyValue != null ? propertyValue.GetHashCode() : 0); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Support/AbstractEventHandlerValue.cs b/src/Spring/Spring.Core/Objects/Support/AbstractEventHandlerValue.cs new file mode 100644 index 00000000..feb03e9a --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Support/AbstractEventHandlerValue.cs @@ -0,0 +1,153 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Globalization; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Base class implementation for classes that describe an event handler. + /// + /// Rick Evans + /// $Id: AbstractEventHandlerValue.cs,v 1.7 2006/04/09 07:19:00 markpollack Exp $ + public abstract class AbstractEventHandlerValue : IEventHandlerValue + { + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + protected AbstractEventHandlerValue() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The object (possibly unresolved) that is exposing the event. + /// + /// + /// The name of the method on the handler that is going to handle the event. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + protected AbstractEventHandlerValue (object source, string methodName) + { + _source = source; + _methodName = methodName; + } + #endregion + + #region Properties + /// + /// The source of the event (may be unresolved, as in the case + /// of a + /// value). + /// + public virtual object Source + { + get + { + return _source; + } + set + { + _source = value; + } + } + + /// + /// The name of the method that is going to handle the event. + /// + public virtual string MethodName + { + get + { + return _methodName; + } + set + { + _methodName = StringUtils.HasText (value) ? value.Trim () : string.Empty; + } + } + + /// + /// The name of the event that is being wired up. + /// + public virtual string EventName + { + get + { + return _eventName; + } + set + { + _eventName = StringUtils.HasText (value) ? value.Trim () : string.Empty; + } + } + #endregion + + #region Methods + /// + /// Wires up the specified handler to the named event on the + /// supplied event source. + /// + /// + /// The object (an object instance, a , etc) + /// exposing the named event. + /// + /// + /// The handler for the event (an object instance, a + /// , etc). + /// + public abstract void Wire (object source, object handler); + + /// + /// Returns a stringified representation of this object. + /// + /// A stringified representation of this object. + public override string ToString() + { + return string.Format ( + CultureInfo.InvariantCulture, + "{0} [Source = '{1}', Method = '{2}']", GetType ().FullName, Source, MethodName); + } + #endregion + + #region Fields + private object _source; + private string _methodName; + private string _eventName; + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Support/AbstractWiringEventHandlerValue.cs b/src/Spring/Spring.Core/Objects/Support/AbstractWiringEventHandlerValue.cs new file mode 100644 index 00000000..8a9c7146 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Support/AbstractWiringEventHandlerValue.cs @@ -0,0 +1,159 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Reflection; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Base class for all + /// implemenations that actually perform event wiring. + /// + /// Rick Evans + /// $Id: AbstractWiringEventHandlerValue.cs,v 1.8 2006/04/09 07:19:00 markpollack Exp $ + public abstract class AbstractWiringEventHandlerValue : AbstractEventHandlerValue + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + protected AbstractWiringEventHandlerValue() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The object (possibly unresolved) that is exposing the event. + /// + /// + /// The name of the method on the handler that is going to handle the event. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + protected AbstractWiringEventHandlerValue(object source, string methodName) + : base(source, methodName) + { + } + + #endregion + + #region Methods + + /// + /// Wires up the specified handler to the named event on the + /// supplied event source. + /// + /// + /// The object (an object instance, a , etc) + /// exposing the named event. + /// + /// + /// The handler for the event (an object instance, a + /// , etc). + /// + public override void Wire(object source, object handler) + { + Type sourceType = ReflectionUtils.TypeOfOrType(source); + EventInfo eventInfo = sourceType.GetEvent(EventName); + Delegate callback = GetHandler(handler, eventInfo); + eventInfo.AddEventHandler(source, callback); + } + + /// + /// Gets the event handler. + /// + /// + /// The instance that is registering for the event notification. + /// + /// + /// Event metadata about the event. + /// + /// + /// The event handler. + /// + protected abstract Delegate GetHandler(object instance, EventInfo info); + + /// + /// Resolves the method metadata that describes the method that is to be used + /// as the argument to a delegate constructor. + /// + /// + /// The exposing the method. + /// + /// + /// The of the delegate (e.g. System.EventHandler). + /// + /// + /// The custom binding flags to use when searching for the method. + /// + /// The method metadata. + /// + /// If the method could not be found. + /// + protected virtual MethodInfo ResolveHandlerMethod( + Type handlerType, Type delegateType, BindingFlags flags) + { + MethodInfo method = handlerType.GetMethod( + MethodName, + flags, + Type.DefaultBinder, + CallingConventions.Standard, + // cache this? for EventHandler types, we're always gonna get the same parameters + new DelegateInfo(delegateType).GetParameterTypes(), + null); + + #region Sanity Check + + if (method == null) + { + throw new FatalObjectException(string.Format( + CultureInfo.InvariantCulture, + "The '{0}' method could not be found on this object [{1}]", + MethodName, handlerType)); + } + + #endregion + + return method; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Support/ArgumentConvertingMethodInvoker.cs b/src/Spring/Spring.Core/Objects/Support/ArgumentConvertingMethodInvoker.cs new file mode 100644 index 00000000..4e7ab959 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Support/ArgumentConvertingMethodInvoker.cs @@ -0,0 +1,120 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; + +using Spring.Core; +using Spring.Core.TypeConversion; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Specialisation of the class that tries + /// to convert the given arguments for the actual target method via an + /// appropriate implementation. + /// + /// Juergen Hoeller + /// Rick Evans + /// $Id: ArgumentConvertingMethodInvoker.cs,v 1.10 2007/07/31 18:17:08 bbaia Exp $ + /// + public class ArgumentConvertingMethodInvoker : MethodInvoker + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public ArgumentConvertingMethodInvoker() + { + } + + #endregion + + #region Properties + + private ObjectWrapper Wrapper + { + get { return _wrapper; } + } + + #endregion + + #region Methods + + /// + /// Prepare the specified method. + /// + /// + ///

    + /// The method can be invoked any number of times afterwards. + ///

    + ///
    + /// + /// If all required properties are not set. + /// + /// + /// If the specified method could not be found. + /// + public override void Prepare() + { + base.Prepare(); + // try to convert the arguments for the chosen method + Type[] requiredTypes = ReflectionUtils.GetParameterTypes(GetPreparedMethod()); + object[] arguments = PreparedArguments; + object[] convertedArguments = new object[arguments.Length]; + for (int i = 0; i < arguments.Length; ++i) + { + convertedArguments[i] = TypeConversionUtils.ConvertValueIfNecessary(requiredTypes[i], arguments[i], null); + } + PreparedArguments = convertedArguments; + } + + /// + /// Register the given custom + /// for all properties of the given . + /// + /// + /// The of property. + /// + /// + /// The to register. + /// + public virtual void RegisterCustomConverter( + Type requiredType, TypeConverter typeConverter) + { + TypeConverterRegistry.RegisterConverter(requiredType, typeConverter); + } + + #endregion + + #region Fields + + private readonly ObjectWrapper _wrapper = new ObjectWrapper(); + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Support/AutoWiringEventHandlerValue.cs b/src/Spring/Spring.Core/Objects/Support/AutoWiringEventHandlerValue.cs new file mode 100644 index 00000000..20df3c63 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Support/AutoWiringEventHandlerValue.cs @@ -0,0 +1,321 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Reflection; +using Common.Logging; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Describes an implementation + /// that autowires events to handler methods. + /// + /// Rick Evans + /// $Id: AutoWiringEventHandlerValue.cs,v 1.11 2006/11/13 07:04:41 markpollack Exp $ + public class AutoWiringEventHandlerValue : AbstractEventHandlerValue + { + #region Constants + + private const string EventNamePlaceHolder = "${event}"; + + private const string DefaultMethodPrefix = "On"; + + private const string DefaultMethodName = DefaultMethodPrefix + EventNamePlaceHolder; + + private static readonly ILog log + = LogManager.GetLogger(typeof (AutoWiringEventHandlerValue)); + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public AutoWiringEventHandlerValue() + { + MethodName = DefaultMethodName; + } + + #endregion + + #region Properties + + /// + /// The name of the method that is going to handle the event. + /// + public override string MethodName + { + get { return base.MethodName; } + set { base.MethodName = StringUtils.HasText(value) ? value.Trim() : DefaultMethodName; } + } + + #endregion + + #region Methods + + /// + /// Wires up the specified handler to the named event on the supplied event source. + /// + /// + /// The object (an object instance, a , etc) + /// exposing the named event. + /// + /// + /// The handler for the event (an object instance, a , + /// etc). + /// + public override void Wire(object source, object handler) + { + AssertUtils.ArgumentNotNull(source, "source"); + AssertUtils.ArgumentNotNull(handler, "handler"); + // simply delegate so that after wiring, state is wiped clean... + AutoWirer wirer = new AutoWirer(source, EventName, handler, MethodName); + wirer.Wire(); + } + + #endregion + + #region Inner Class : AutoWirer + + /// + /// Performs the matching up of handler methods to one or more source events. + /// + /// + ///

    + /// This class merely marshals the matching of handler methods to the events exposed + /// by an event source, and then delegates to a concrete + /// implementation (such as + /// or + /// ) to do the heavy lifting of + /// actually wiring a handler method to an event. + ///

    + ///

    + /// Note : the order in which handler's are wired up to events is non-deterministic. + ///

    + ///
    + private sealed class AutoWirer + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The object exposing the event (s) being wired up. + /// + /// + /// The name of the event that is being wired up. + /// + /// + /// The object exposing the method (s) being wired to the event. + /// + /// + /// The name of the method that is going to handle the event. + /// + public AutoWirer( + object source, string eventName, object handler, string methodName) + { + Source = source; + EventName = eventName; + Handler = handler; + MethodName = methodName; + } + + #endregion + + #region Methods + + /// + /// Wires up events on the source to methods exposed on the handler. + /// + public void Wire() + { + Type sourceType = ReflectionUtils.TypeOfOrType(Source); + // create the criteria for the event search... + ICriteria criteria = new RegularExpressionEventNameCriteria(EventName); + // and grab the events that satisfy the criteria... + BindingFlags eventFlags = BindingFlags.Instance | BindingFlags.Static + | BindingFlags.Public; + MemberInfo[] events = sourceType.FindMembers( + MemberTypes.Event, + eventFlags, + new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria), + criteria); + // and for each event that satisfied the criteria... + foreach (EventInfo evt in events) + { + WireEvent(evt); + } + } + + /// + /// Wires up the supplied event to any handler methods that match the event + /// signature. + /// + /// The event being wired up. + private void WireEvent(EventInfo theEvent) + { + // grab some info (such as the delegate's method signature) about the event + DelegateInfo eventDelegate = new DelegateInfo(theEvent); + // if the method name needs to be customised on a per event basis, do so + string customMethodName = GetMethodNameCustomisedForEvent(theEvent.Name); + + // create the criteria for the handler method search... + ComposedCriteria methodCriteria = new ComposedCriteria(); + // a candidate handlers method name must match the custom method name + methodCriteria.Add(new RegularExpressionMethodNameCriteria(customMethodName)); + // the return Type of a candidate handlers method must be the same as the return type of the event + methodCriteria.Add(new MethodReturnTypeCriteria(eventDelegate.GetReturnType())); + // a candidate handlers method parameters must match the event's parameters + methodCriteria.Add(new MethodParametersCriteria(eventDelegate.GetParameterTypes())); + + // and grab the methods that satisfy the criteria... + BindingFlags methodFlags = BindingFlags.Instance | BindingFlags.Static + | BindingFlags.NonPublic | BindingFlags.Public; + MemberInfo[] methods = HandlerType.FindMembers( + MemberTypes.Method, + methodFlags, + new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria), + methodCriteria); + + // and for each method that satisfied the criteria... + foreach (MethodInfo method in methods) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format( + CultureInfo.InvariantCulture, + "Wiring up this method '{0}' to this event '{1}'", + method.Name, + theEvent.Name)); + } + + #endregion + + IEventHandlerValue myHandler = method.IsStatic ? + (IEventHandlerValue) new StaticEventHandlerValue() : + (IEventHandlerValue) new InstanceEventHandlerValue(); + myHandler.EventName = theEvent.Name; + myHandler.MethodName = method.Name; + myHandler.Wire(Source, Handler); + } + } + + /// + /// Only replaces the first occurrence of the placeholder. + /// + /// The event whose name is going to be used. + /// + /// The method name customised for the name of the supplied event. + /// + private string GetMethodNameCustomisedForEvent(string eventName) + { + string methodName = MethodName; + if (MethodName.IndexOf(EventNamePlaceHolder) >= 0) + { + methodName = MethodName.Replace(EventNamePlaceHolder, eventName); + } + return methodName; + } + + #endregion + + #region Properties + + /// + /// The object exposing the event (s) being wired up. + /// + private object Source + { + get { return _source; } + set { _source = value; } + } + + /// + /// The object exposing the method (s) being wired to an event source. + /// + private object Handler + { + get { return _handler; } + set { _handler = value; } + } + + /// + /// The of the object that is handling any events. + /// + private Type HandlerType + { + get + { + if (_handlerType == null) + { + _handlerType = ReflectionUtils.TypeOfOrType(Handler); + } + return _handlerType; + } + } + + /// + /// The name of the method that is going to handle the event. + /// + private string MethodName + { + get { return _methodName; } + set { _methodName = value; } + } + + /// + /// The name of the event that is being wired up. + /// + private string EventName + { + get { return _eventName; } + set { _eventName = value; } + } + + #endregion + + #region Fields + + private object _source; + private object _handler; + private string _methodName; + private string _eventName; + private Type _handlerType; + + #endregion + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Support/ISortDefinition.cs b/src/Spring/Spring.Core/Objects/Support/ISortDefinition.cs new file mode 100644 index 00000000..c996cbba --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Support/ISortDefinition.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Definition for sorting object instances by a property. + /// + /// Juergen Hoeller + /// Simon White (.NET) + public interface ISortDefinition + { + /// + /// The name of the property to sort by. + /// + string Property + { + get; + } + + /// + /// Whether upper and lower case in string values should be ignored. + /// + /// + /// True if the sorting should be performed in a case-insensitive fashion. + /// + bool IgnoreCase + { + get; + } + + /// + /// If the sorting should be ascending or descending. + /// + /// + /// True if the sorting should be in the ascending order. + /// + bool Ascending + { + get; + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Support/InstanceEventHandlerValue.cs b/src/Spring/Spring.Core/Objects/Support/InstanceEventHandlerValue.cs new file mode 100644 index 00000000..869bf081 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Support/InstanceEventHandlerValue.cs @@ -0,0 +1,116 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Describes an event handler for an object instance. + /// + /// Rick Evans + /// $Id: InstanceEventHandlerValue.cs,v 1.7 2006/04/09 07:19:00 markpollack Exp $ + public class InstanceEventHandlerValue : AbstractWiringEventHandlerValue + { + #region Constants + + private static readonly BindingFlags InstanceMethodFlags = + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.IgnoreCase | BindingFlags.Static; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public InstanceEventHandlerValue() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The object (possibly unresolved) that is exposing the event. + /// + /// + /// The name of the method on the handler that is going to handle the event. + /// + public InstanceEventHandlerValue(object source, string methodName) + : base(source, methodName) + { + } + + #endregion + + #region Methods + + /// + /// Gets the event handler. + /// + /// + /// The instance that is registering for the event notification. + /// + /// + /// Event metadata about the event. + /// + /// + /// The event handler. + /// + protected override Delegate GetHandler(object instance, EventInfo info) + { + MethodInfo methodMeta = ResolveHandlerMethod( + ReflectionUtils.TypeOfOrType(instance), + info.EventHandlerType, + InstanceEventHandlerValue.InstanceMethodFlags); + Delegate callback = null; + if (methodMeta.IsStatic) + { + // case insensitive binding to a static method on an (irrelevant) instance + callback = Delegate.CreateDelegate( + info.EventHandlerType, + methodMeta); + } + else + { + // case insensitive binding to an instance method on an instance + callback = + Delegate.CreateDelegate( + info.EventHandlerType, + instance, + MethodName, + true); + } + return callback; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Support/MethodInvoker.cs b/src/Spring/Spring.Core/Objects/Support/MethodInvoker.cs new file mode 100644 index 00000000..e35523ed --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Support/MethodInvoker.cs @@ -0,0 +1,555 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using System.Reflection; + +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Objects; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Helper class allowing one to declaratively specify a method call for later invocation. + /// + /// + ///

    + /// Typically not used directly but via its subclasses such as + /// . + ///

    + ///

    + /// Usage: specify either the and + /// or the + /// and + /// properties respectively, and + /// (optionally) any arguments to the method. Then call the + /// method to prepare the invoker. + /// Once prepared, the invoker can be invoked any number of times. + ///

    + ///
    + /// + ///

    + /// The following example uses the class to invoke the + /// ToString() method on the Foo class using a mixture of both named and unnamed + /// arguments. + ///

    + /// + /// public class Foo + /// { + /// public string ToString(string name, int age, string address) + /// { + /// return string.Format("{0}, {1} years old, {2}", name, age, address); + /// } + /// + /// public static void Main() + /// { + /// Foo foo = new Foo(); + /// MethodInvoker invoker = new MethodInvoker(); + /// invoker.Arguments = new object [] {"Kaneda", "18 Kaosu Gardens, Nakatani Drive, Okinanawa"}; + /// invoker.AddNamedArgument("age", 29); + /// invoker.Prepare(); + /// // at this point, the arguments that will be passed to the method invocation + /// // will have been resolved into the following ordered array : {"Kaneda", 29, "18 Kaosu Gardens, Nakatani Drive, Okinanawa"} + /// string details = (string) invoker.Invoke(); + /// Console.WriteLine (details); + /// // will print out 'Kaneda, 29 years old, 18 Kaosu Gardens, Nakatani Drive, Okinanawa' + /// } + /// } + /// + ///
    + /// Colin Sampaleanu + /// Juergen Hoeller + /// Simon White (.NET) + /// $Id: MethodInvoker.cs,v 1.7 2007/08/04 01:05:15 bbaia Exp $ + public class MethodInvoker + { + #region Fields + + /// + /// The value returned from the invocation of a method that returns void. + /// + public static readonly Missing Void = Missing.Value; + + private Type _targetType; + private object _targetObject; + private string _targetMethod; + private object[] _arguments; + private IDictionary _namedArguments; + private object[] _preparedArguments; + + /// + /// The method that will be invoked. + /// + private MethodInfo _methodObject; + + /// + /// The used to search for + /// the method to be invoked. + /// + private const BindingFlags MethodSearchingFlags = + BindingFlags.Instance | + BindingFlags.Static | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.IgnoreCase; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the class. + /// + public MethodInvoker() + { + Arguments = new object[] {}; + NamedArguments = new Hashtable(); + PreparedArguments = new object[] {}; + } + + #endregion + + #region Properties + + /// + /// The target on which to call the target method. + /// + /// + ///

    + /// Only necessary when the target method is ; + /// else, a target object needs to be specified. + ///

    + ///
    + public Type TargetType + { + get { return _targetType; } + set { this._targetType = value; } + } + + /// + /// The target object on which to call the target method. + /// + /// + ///

    + /// Only necessary when the target method is not ; + /// else, a target class is sufficient. + ///

    + ///
    + public object TargetObject + { + get { return _targetObject; } + set { this._targetObject = value; } + } + + /// + /// The name of the method to be invoked. + /// + /// + ///

    + /// Refers to either a method + /// or a non- method, depending on + /// whether or not a target object has been set. + ///

    + ///
    + /// + public string TargetMethod + { + get { return _targetMethod; } + set { this._targetMethod = value; } + } + + /// + /// Arguments for the method invocation. + /// + /// + ///

    + /// Ordering is significant... the order of the arguments in this + /// property must match the ordering of the various parameters on the target + /// method. There does however exist a small possibility for confusion when + /// the arguments in this property are supplied in addition to one or more named + /// arguments. In this case, each named argument is slotted into the index position + /// corresponding to the named argument... once once all named arguments have been + /// resolved, the arguments in this property are slotted into any remaining (empty) + /// slots in the method parameter list (see the example in the overview of the + /// class if this is not clear). + ///

    + ///

    + /// If this property is not set, or the value passed to the setter invocation + /// is or a zero-length array, a method with no (un-named) arguments is assumed. + ///

    + ///
    + /// + public object[] Arguments + { + get { return _arguments; } + set + { + if (value != null) + { + this._arguments = value; + } + else + { + this._arguments = new object[] {}; + } + } + } + + /// + /// The resolved arguments for the method invocation. + /// + /// + /// + /// This property is not set until the target method has been resolved via a call to the + /// method). It is a combination of the + /// named and plain vanilla arguments properties, and it is this object array that + /// will actually be passed to the invocation of the target method. + /// + ///

    + /// Setting the value of this property to results in basically clearing out any + /// previously prepared arguments... another call to the + /// method will then be required to prepare the arguments again (or the prepared arguments + /// can be set explicitly if so desired). + ///

    + ///
    + /// + /// + protected object[] PreparedArguments + { + get { return _preparedArguments; } + set + { + if (value != null) + { + this._preparedArguments = value; + } + else + { + this._preparedArguments = new object[] {}; + } + } + } + + /// + /// Named arguments for the method invocation. + /// + /// + ///

    + /// The keys of this dictionary are the () names of the + /// method arguments, and the () values are the actual + /// argument values themselves. + ///

    + ///

    + /// If this property is not set, or the value passed to the setter invocation + /// is a reference, a method with no named arguments is assumed. + ///

    + ///
    + /// + public IDictionary NamedArguments + { + get { return _namedArguments; } + set + { + if (value != null) + { + this._namedArguments = value; + } + else + { + this._namedArguments.Clear(); + } + } + } + + #endregion + + #region Methods + + /// + /// Prepare the specified method. + /// + /// + ///

    + /// The method can be invoked any number of times afterwards. + ///

    + ///
    + /// + /// If all required properties are not set, or a matching argument could not be found + /// for a named argument (typically down to a typo). + /// + /// + /// If the specified method could not be found. + /// + public virtual void Prepare() + { + if (_targetMethod == null) + { + throw new ArgumentException("The 'TargetMethod' property is required."); + } + if (_targetType == null && _targetObject == null) + { + throw new ArgumentException("One of either the 'TargetType' or 'TargetObject' properties is required."); + } + _methodObject = FindTheMethodToInvoke(); + if (TargetObject == null && !_methodObject.IsStatic) + { + throw new ArgumentException( + "The target method cannot be an instance method without a corresponding target instance on which to invoke it."); + } + PrepareArguments(); + } + + private void PrepareArguments() + { + _preparedArguments = new object[ArgumentCount]; + // ok, lets prepare any named arguments first... + if (NamedArguments.Count > 0) + { + // lets slot in all of the named arguments first... + ParameterInfo[] parameters = _methodObject.GetParameters(); + // lets figure out the index og each of the method parameters... + IDictionary argumentNamesToIndexes = new Hashtable(); + for (int i = 0; i < parameters.Length; ++i) + { + ParameterInfo parameter = parameters[i]; + argumentNamesToIndexes[parameter.Name.ToLower(CultureInfo.InvariantCulture)] = i; + } + int THE_ARGUMENT_IS_PREPARED = -12; + foreach (DictionaryEntry namedArgument in NamedArguments) + { + string argumentName = ((string) namedArgument.Key).ToLower(CultureInfo.InvariantCulture); + object argumentValue = namedArgument.Value; + if (!argumentNamesToIndexes.Contains(argumentName)) + { + // whoa (Nelly); the named argument does not exist on the method... + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "The named argument '{0}' could not be found on the '{1}' method of class [{2}].", + argumentName, _methodObject.Name, _methodObject.DeclaringType.FullName)); + + } + // look up the index of where in the prepared args array we're gonna stick the named argument value + int namedArgumentsIndex = (int) argumentNamesToIndexes[argumentName]; + PreparedArguments[namedArgumentsIndex] = argumentValue; + // we've prepped this index position, so mark it as so... + argumentNamesToIndexes[argumentName] = THE_ARGUMENT_IS_PREPARED; + } + // and then fill in any remaining blanks with the plain vanilla arguments... + int plainVanillaIndex = 0; + int[] sortedIndexes = (int[]) new ArrayList(argumentNamesToIndexes.Values).ToArray(typeof (int)); + Array.Sort(sortedIndexes); + foreach (int argumentIndex in sortedIndexes) + { + // have we previously prepped a named argument at this index position? + if (argumentIndex == THE_ARGUMENT_IS_PREPARED) + { + continue; + } + // lets stick a plain vanilla argument in at this index position (in the order that they have been supplied)... + PreparedArguments[argumentIndex] = Arguments[plainVanillaIndex++]; + } + } + else + { + PreparedArguments = Arguments; + } + } + + /// + /// Searches for and returns the method that is to be invoked. + /// + /// + /// The return value of this method call will subsequently be returned from the + /// . + /// + /// The method that is to be invoked. + /// + /// If no method could be found. + /// + /// + /// If more than one method was found. + /// + protected virtual MethodInfo FindTheMethodToInvoke() + { + MethodInfo theMethod = null; + Type targetType = (TargetObject != null) ? TargetObject.GetType() : TargetType; +#if NET_2_0 + GenericArgumentsHolder genericInfo = new GenericArgumentsHolder(TargetMethod); +#endif + // if we don't have any named arguments, we can try to get the exact method first... + if (NamedArguments.Count == 0) + { + ComposedCriteria searchCriteria = new ComposedCriteria(); +#if NET_2_0 + searchCriteria.Add(new MethodNameMatchCriteria(genericInfo.GenericMethodName)); + searchCriteria.Add(new MethodParametersCountCriteria(ArgumentCount)); + searchCriteria.Add(new MethodGenericArgumentsCountCriteria( + genericInfo.GetGenericArguments().Length)); +#else + searchCriteria.Add(new MethodNameMatchCriteria(TargetMethod)); + searchCriteria.Add(new MethodParametersCountCriteria(ArgumentCount)); +#endif + searchCriteria.Add(new MethodParametersCriteria(ReflectionUtils.GetTypes(Arguments))); + + MemberInfo[] matchingMethods = targetType.FindMembers( + MemberTypes.Method, + MethodSearchingFlags, + new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria), + searchCriteria); + + if (matchingMethods != null && matchingMethods.Length == 1) + { + theMethod = matchingMethods[0] as MethodInfo; + } + } + if (theMethod == null) + { + // search for a method with a matching signature... + ComposedCriteria searchCriteria = new ComposedCriteria(); +#if NET_2_0 + searchCriteria.Add(new MethodNameMatchCriteria(genericInfo.GenericMethodName)); + searchCriteria.Add(new MethodParametersCountCriteria(ArgumentCount)); + searchCriteria.Add(new MethodGenericArgumentsCountCriteria( + genericInfo.GetGenericArguments().Length)); +#else + searchCriteria.Add(new MethodNameMatchCriteria(TargetMethod)); + searchCriteria.Add(new MethodParametersCountCriteria(ArgumentCount)); +#endif + MemberInfo[] matchingMethods = targetType.FindMembers( + MemberTypes.Method, + MethodSearchingFlags, + new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria), + searchCriteria); + + if (matchingMethods == null + || matchingMethods.Length == 0) + { + throw new MissingMethodException(targetType.Name, TargetMethod); + } + if (matchingMethods.Length > 1) + { + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "Unable to determine which exact method to call; found '{0}' matches.", + matchingMethods.Length)); + } + theMethod = matchingMethods[0] as MethodInfo; + } +#if NET_2_0 + if (genericInfo.ContainsGenericArguments) + { + string[] unresolvedGenericArgs = genericInfo.GetGenericArguments(); + Type[] genericArgs = new Type[unresolvedGenericArgs.Length]; + for (int j = 0; j < unresolvedGenericArgs.Length; j++) + { + genericArgs[j] = TypeResolutionUtils.ResolveType(unresolvedGenericArgs[j]); + } + theMethod = theMethod.MakeGenericMethod(genericArgs); + } +#endif + return theMethod; + } + + /// + /// Adds the named argument to this instances mapping of argument names to argument values. + /// + /// + /// The name of an argument on the method that is to be invoked. + /// + /// + /// The value of the named argument on the method that is to be invoked. + /// + public void AddNamedArgument(string argumentName, object argument) + { + if (NamedArguments.Contains(argumentName)) + { + NamedArguments.Remove(argumentName); + } + NamedArguments.Add(argumentName, argument); + } + + private int ArgumentCount + { + get { return Arguments.Length + NamedArguments.Count; } + } + + /// + /// Returns the prepared object that + /// will be invoked. + /// + /// + ///

    + /// A possible use case is to determine the return of the method. + ///

    + ///
    + /// + /// The prepared object that + /// will be invoked. + /// + public MethodInfo GetPreparedMethod() + { + return this._methodObject; + } + + /// + /// Invoke the specified method. + /// + /// + ///

    + /// The invoker needs to have been prepared beforehand (via a call to the + /// method). + ///

    + ///
    + /// + /// The object returned by the method invocation, or + /// if the method returns void. + /// + /// + /// If at least one of the arguments passed to this + /// was incompatible with the signature of the invoked method. + /// + public virtual object Invoke() + { + object result = null; + try + { + result = this._methodObject.Invoke(TargetObject, PreparedArguments); + } + catch (ArgumentException ex) + { + throw new MethodInvocationException( + string.Format(CultureInfo.InvariantCulture, + "At least one of the arguments passed to this {0} was " + + "incompatible with the signature of the invoked method.", GetType().Name), ex); + } + return (result == null ? Void : result); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Support/MutableSortDefinition.cs b/src/Spring/Spring.Core/Objects/Support/MutableSortDefinition.cs new file mode 100644 index 00000000..452b3db3 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Support/MutableSortDefinition.cs @@ -0,0 +1,217 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Mutable implementation of the + /// interface that + /// supports toggling the ascending value on setting the same property again. + /// + /// Juergen Hoeller + /// Jean-Pierre Pawlak + /// Simon White (.NET) + [Serializable] + public class MutableSortDefinition : ISortDefinition + { + private string _property = string.Empty; + private bool _ignoreCase = true; + private bool _ascending = true; + private bool _toggleAscendingOnProperty = false; + + #region Properties + private bool ToggleAscendingOnProperty + { + get + { + return _toggleAscendingOnProperty; + } + set + { + this._toggleAscendingOnProperty = value; + } + } + #endregion + + #region ISortDefinition Properties + /// + /// The name of the property to sort by. + /// + public string Property + { + get + { + return _property; + } + set + { + if (!StringUtils.HasText (value)) + { + _property = string.Empty; + } + else + { + // implicit toggling of ascending? + if (ToggleAscendingOnProperty) + { + if (value.Equals(_property)) + { + _ascending = !_ascending; + } + } + _property = value; + } + } + } + + /// + /// Whether upper and lower case in string values should be ignored. + /// + /// + /// True if the sorting should be performed in a case-insensitive fashion. + /// + public bool IgnoreCase + { + get + { + return _ignoreCase; + } + } + + /// + /// If the sorting should be ascending or descending. + /// + /// + /// True if the sorting should be in the ascending order. + /// + public bool Ascending + { + get + { + return _ascending; + } + } + #endregion + + #region Constructors + /// + /// Creates a new instance of the + /// class. + /// + public MutableSortDefinition () + { + } + + /// + /// Creates a new instance of the + /// class using + /// the specified . + /// + /// + /// The to use + /// as a source for initial property values. + /// + public MutableSortDefinition (ISortDefinition source) + { + this._property = source.Property; + this._ignoreCase = source.IgnoreCase; + this._ascending = source.Ascending; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The name of the property to sort by. + /// + /// + /// Whether upper and lower case in string values should be ignored. + /// + /// + /// Whether or not the sorting should be ascending or descending. + /// + public MutableSortDefinition(string name, bool ignoreCase, bool ascending) + { + this._property = name; + this._ignoreCase = ignoreCase; + this._ascending = ascending; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// Whether or not the + /// + /// property should be toggled if the same name is set on the + /// + /// property. + /// + public MutableSortDefinition (bool toggleAscendingOnSameProperty) + { + this.ToggleAscendingOnProperty = toggleAscendingOnSameProperty; + } + #endregion + + #region Methods + /// + /// Overrides the default method + /// + /// + /// The object to test against this instance for equality. + /// + /// + /// True if the supplied is equal to this instance. + /// + public override bool Equals(object obj) + { + if (!(obj is ISortDefinition)) + { + return false; + } + ISortDefinition sd = (ISortDefinition) obj; + return (this.Property.Equals(sd.Property) && + this.Ascending == sd.Ascending && this.IgnoreCase == sd.IgnoreCase); + } + + /// + /// Overrides the default method. + /// + /// The hashcode for this instance. + public override int GetHashCode() + { + int result = 0; + result = this.Property.GetHashCode(); + result = 29 * result + (this.IgnoreCase ? 1 : 0); + result = 29 * result + (this.Ascending ? 1 : 0); + return result; + } + #endregion + } +} diff --git a/src/Spring/Spring.Core/Objects/Support/PropertyComparator.cs b/src/Spring/Spring.Core/Objects/Support/PropertyComparator.cs new file mode 100644 index 00000000..69f12f27 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Support/PropertyComparator.cs @@ -0,0 +1,198 @@ +#region License + +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using Common.Logging; +using Spring.Core; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Performs a comparison of two objects, using the specified object property via + /// an . + /// + /// Juergen Hoeller + /// Jean-Pierre Pawlak + /// Simon White (.NET) + /// $Id: PropertyComparator.cs,v 1.12 2007/07/31 00:09:03 markpollack Exp $ + public class PropertyComparator : IComparer + { + private static readonly ILog logger + = LogManager.GetLogger(typeof (PropertyComparator)); + + private ISortDefinition sortDefinition; + private readonly IDictionary cachedObjectWrappers = new Hashtable(); + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The to use for any + /// sorting. + /// + /// + /// If the supplied is . + /// + public PropertyComparator(ISortDefinition definition) + { + AssertUtils.ArgumentNotNull(definition, "definition"); + this.sortDefinition = definition; + } + + /// + /// Gets the to + /// use for any sorting. + /// + /// + /// The to use for + /// any sorting. + /// + public ISortDefinition SortDefinition + { + get { return this.sortDefinition; } + } + + /// + /// Compares two objects and returns a value indicating whether one is less + /// than, equal to or greater than the other. + /// + /// The first object to compare. + /// The second object to compare. + /// + public virtual int Compare(object o1, object o2) + { + object v1 = GetPropertyValue(o1); + object v2 = GetPropertyValue(o2); + if (this.sortDefinition.IgnoreCase + && (v1 is string) + && (v2 is string)) + { + v1 = ((string) v1).ToLower(CultureInfo.CurrentCulture); + v2 = ((string) v2).ToLower(CultureInfo.CurrentCulture); + } + int result = 0; + try + { + if (v1 != null) + { + if (v2 != null) + { + result = ((IComparable) v1).CompareTo(v2); + } + else + { + result = -1; + } + } + else + { + if (v2 != null) + { + result = 1; + } + else + { + result = 0; + } + } + } + catch (Exception ex) + { + #region Instrumentation + + if (logger.IsWarnEnabled) + { + logger.Warn("Could not sort objects [" + o1 + "] and [" + o2 + "]", + ex); + } + + #endregion + + return 0; + } + return (this.sortDefinition.Ascending ? result : -result); + } + + /// + /// Get the 's property + /// value for the given object. + /// + /// The object to get the property value for. + /// The property value. + private object GetPropertyValue(object obj) + { + object propertyValue = null; + if (obj != null) + { + IObjectWrapper ow = (IObjectWrapper) this.cachedObjectWrappers[obj]; + if (ow == null) + { + ow = new ObjectWrapper(obj); + this.cachedObjectWrappers.Add(obj, ow); + } + try + { + propertyValue = ow.GetPropertyValue(this.sortDefinition.Property); + } + catch (InvalidPropertyException ex) + { + // the property doesn't exist in the first place, so let exception through... + throw ex; + } + catch (ObjectsException ex) + { + // if a nested property cannot be read, simply return null... + if (logger.IsDebugEnabled) + { + logger.Debug("Could not access property - treating as null for sorting.", ex); + } + } + } + return propertyValue; + } + + /// + /// Sort the given according to the + /// given sort definition. + /// + /// + /// The to be sorted. + /// + /// The parameters to sort by. + /// + /// In the case of a missing property name. + /// + /// + /// If the supplied is . + /// + public static void Sort(IList source, ISortDefinition sortDefinition) + { + ArrayList.Adapter(source).Sort(new PropertyComparator(sortDefinition)); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Objects/Support/StaticEventHandlerValue.cs b/src/Spring/Spring.Core/Objects/Support/StaticEventHandlerValue.cs new file mode 100644 index 00000000..3c85ab6d --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Support/StaticEventHandlerValue.cs @@ -0,0 +1,98 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Reflection; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Describes an event handler for a static class method. + /// + /// Rick Evans + /// $Id: StaticEventHandlerValue.cs,v 1.5 2006/04/09 07:19:00 markpollack Exp $ + public class StaticEventHandlerValue : AbstractWiringEventHandlerValue + { + #region Constants + private static readonly BindingFlags StaticMethodFlags = + BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.IgnoreCase; + #endregion + + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the + /// class. + /// + public StaticEventHandlerValue () + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The object (possibly unresolved) that is exposing the event. + /// + /// + /// The name of the method on the handler that is going to handle the event. + /// + public StaticEventHandlerValue (object source, string methodName) + : base (source, methodName) {} + #endregion + + #region Methods + /// + /// Gets the event handler. + /// + /// + /// The instance that is registering for the event notification. + /// + /// + /// Event metadata about the event. + /// + /// + /// The event handler. + /// + protected override Delegate GetHandler(object instance, EventInfo info) + { + Type type = instance as Type; + if (type == null) + { + throw new FatalObjectException (string.Format ( + CultureInfo.InvariantCulture, + "Only 'System.Type' instances can be wired by instances of " + + "the StaticEventHandlerValue class; got '{0}'", instance)); + } + MethodInfo method = ResolveHandlerMethod ( + type, + info.EventHandlerType, + StaticEventHandlerValue.StaticMethodFlags); + return Delegate.CreateDelegate (info.EventHandlerType, method); + } + #endregion + } +} diff --git a/src/Spring/Spring.Core/Pool/IObjectPool.cs b/src/Spring/Spring.Core/Pool/IObjectPool.cs new file mode 100644 index 00000000..07ccc58c --- /dev/null +++ b/src/Spring/Spring.Core/Pool/IObjectPool.cs @@ -0,0 +1,150 @@ +#region License + +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +using System; + +namespace Spring.Pool +{ + /// + /// A simple pooling interface for managing and monitoring a pool + /// of objects. + /// + /// + ///

    + /// Based on the Jakarta Commons Pool API. + ///

    + ///
    + /// Federico Spinazzi + /// $Id: IObjectPool.cs,v 1.5 2005/08/13 18:04:54 springboy Exp $ + /// + public interface IObjectPool + { + /// + /// Obtain an instance from the pool. + /// + /// + ///

    + /// By contract, clients must return the borrowed + /// instance using + /// or a related method as defined in an implementation or + /// sub-interface. + ///

    + ///
    + /// An instance from the pool. + /// + /// In case the pool is unusable. + /// + /// + object BorrowObject(); + + /// + /// Return an instance to the pool. + /// + /// + ///

    + /// By contract, the object must have been obtained using + /// + /// or a related method as defined in an implementation or sub-interface. + ///

    + ///
    + /// The instance to be returned to the pool. + /// + void ReturnObject(object target); + + /// + /// Create an object using the factory set by + /// the property + /// or other implementation dependent mechanism + /// and place it into the pool. + /// + /// + ///

    + /// This is an optional operation. AddObject is useful for "pre-loading" a + /// pool with idle objects. + ///

    + ///
    + /// + /// If the implementation does not support the operation. + /// + void AddObject(); + + /// + /// Close the pool and free any resources associated with it. + /// + void Close(); + + /// + /// Clear objects sitting idle in the pool, releasing any + /// associated resources. + /// + /// + ///

    + /// This is an optional operation. + ///

    + ///
    + /// + /// If the implementation does not support the operation. + /// + void Clear(); + + /// + /// Gets the number of instances currently borrowed from the pool. + /// + /// + ///

    + /// This is an optional operation. + ///

    + ///
    + /// + /// If the implementation does not support the operation. + /// + int NumActive { get; } + + /// + /// Gets the number of instances currently idle in the pool. + /// + /// + ///

    + /// This is an optional operation. + ///

    + ///

    + /// This may be considered an approximation of the number of objects + /// that can be borrowed without creating any new instances. + ///

    + ///
    + /// + /// If the implementation does not support the operation. + /// + int NumIdle { get; } + + /// + /// Set the factory used to create new instances. + /// + /// + ///

    + /// This is an optional operation. + ///

    + ///
    + /// + /// If the implementation does not support the operation. + /// + IPoolableObjectFactory PoolableObjectFactory { set; } + } +} diff --git a/src/Spring/Spring.Core/Pool/IPoolableObjectFactory.cs b/src/Spring/Spring.Core/Pool/IPoolableObjectFactory.cs new file mode 100644 index 00000000..500e1605 --- /dev/null +++ b/src/Spring/Spring.Core/Pool/IPoolableObjectFactory.cs @@ -0,0 +1,125 @@ +#region License + +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +namespace Spring.Pool +{ + /// + /// Defines lifecycle methods for objects that are to be used in an + /// implementation. + /// + /// + ///

    + /// The following methods summarize the contract between an + /// and an + /// an . + ///

    + /// + /// + /// + /// is called whenever a new instance is needed. + /// + /// + /// + /// is invoked on every instance before it is returned from + /// the pool. + /// + /// + /// + /// is invoked on every instance when it is returned to the pool. + /// + /// + /// + /// is invoked on every instance when it is being dropped from the + /// pool (see + /// + /// + /// + ///

    + /// Based on the Jakarta Commons Pool API. + ///

    + ///
    + /// Federico Spinazzi + /// $Id: IPoolableObjectFactory.cs,v 1.5 2005/11/18 17:29:32 gcaprio Exp $ + /// + public interface IPoolableObjectFactory + { + /// + /// Creates an instance that can be returned by the pool. + /// + /// + /// An instance that can be returned by the pool. + /// + object MakeObject(); + + /// + /// Destroys an instance no longer needed by the pool. + /// + /// + ///

    + /// Invoked on every instance when it is being "dropped" + /// from the pool (whether due to the return value from a call to the + /// + /// method, or for reasons specific to the pool implementation.) + ///

    + ///
    + /// The instance to be destroyed. + void DestroyObject(object obj); + + /// + /// Ensures that the instance is safe to be returned by the pool. + /// Returns false if this object should be destroyed. + /// + /// + ///

    + /// Invoked in an implementation-specific fashion to determine if an + /// instance is still valid to be returned by the pool. + /// It will only be invoked on an "activated" instance. + ///

    + ///
    + /// The instance to validate. + /// + /// if this object is not valid and + /// should be dropped from the pool, otherwise . + /// + bool ValidateObject(object obj); + + /// + /// Reinitialize an instance to be returned by the pool. + /// + /// + ///

    + /// Invoked on every instance before it is returned from the pool. + ///

    + ///
    + /// The instance to be activated. + void ActivateObject(object obj); + + /// + /// Uninitialize an instance to be returned to the pool. + /// + /// + ///

    + /// Invoked on every instance when it is returned to the pool. + ///

    + ///
    + /// The instance returned to the pool. + void PassivateObject(object obj); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Pool/PoolException.cs b/src/Spring/Spring.Core/Pool/PoolException.cs new file mode 100644 index 00000000..8d6c3467 --- /dev/null +++ b/src/Spring/Spring.Core/Pool/PoolException.cs @@ -0,0 +1,81 @@ +#region License +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Pool +{ + /// + /// Base class for all pooling exceptions. + /// + /// Federico Spinazzi + /// $Id: PoolException.cs,v 1.4 2005/08/07 04:59:47 markpollack Exp $ + [Serializable] + public class PoolException : Exception + { + /// + /// Creates a new instance of the + /// class. + /// + public PoolException () + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public PoolException (string message) : base (message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public PoolException (string message, Exception innerException) : base (message, innerException) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected PoolException (SerializationInfo info, StreamingContext context) : base (info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Pool/Support/SimplePool.cs b/src/Spring/Spring.Core/Pool/Support/SimplePool.cs new file mode 100644 index 00000000..34175bd9 --- /dev/null +++ b/src/Spring/Spring.Core/Pool/Support/SimplePool.cs @@ -0,0 +1,294 @@ +#region License + +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Util; +using Spring.Threading; + +#endregion + +namespace Spring.Pool.Support +{ + /// + /// A simple pool implementation + /// + /// + ///

    + /// Based on the implementation found in Concurrent Programming in Java, + /// 2nd ed., by Doug Lea. + ///

    + ///
    + /// Doug Lea + /// Federico Spinazzi + /// Mark Pollack + /// $Id: SimplePool.cs,v 1.4 2006/09/15 19:06:07 markpollack Exp $ + public class SimplePool : IObjectPool + { + private readonly IPoolableObjectFactory factory; + private bool closed; + private IList free = new ArrayList(); + private IList busy = new ArrayList(); // linear search !! + + /// + /// Set of permits + /// + internal readonly Semaphore available; + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The factory used to instantiate and manage the lifecycle of pooled objects. + /// + /// The initial size of the pool. + /// + /// If the supplied is . + /// + /// + /// If the supplied is less than or equal to zero. + /// + public SimplePool(IPoolableObjectFactory factory, int size) + { + AssertUtils.ArgumentNotNull(factory, "factory"); + this.available = new Semaphore(size); + this.factory = factory; + InitItems(size); + } + + /// + /// Obtain an instance from the pool. + /// + /// + /// In case the pool is unusable. + /// + /// + /// + public object BorrowObject() + { + available.Acquire(); + return DoBorrow(); + } + + /// + /// Return an instance to the pool. + /// + /// The instance to be returned to the pool. + /// + /// + public void ReturnObject(object target) + { + if (DoReturn(target)) + { + available.Release(); + } + } + + /// + /// Create an object using the factory set by + /// the property + /// or other implementation dependent mechanism + /// and place it into the pool. + /// + /// + ///

    + /// This implementation always throws a + /// . + ///

    + ///
    + /// + /// If the implementation does not support the operation. + /// + public void AddObject() + { + throw new NotSupportedException(); + } + + /// + /// Synchronized borrow logic. + /// + /// + protected object DoBorrow() + { + lock (this) + { + while (free.Count > 0) + { + int i = free.Count - 1; + object o = free[i]; + free.RemoveAt(i); + factory.ActivateObject(o); + if (factory.ValidateObject(o)) + { + busy.Add(o); + return o; + } + } + if (!closed) + { + throw new PoolException("No more valid objects in pool."); + } + else + { + throw new PoolException("Pool was closed and is unusable."); + } + } + } + + /// + /// Synchronized release logic. + /// + /// + /// The object to release to the pool. + /// + /// + /// if the object was not a busy one. + /// + protected bool DoReturn(object target) + { + lock (this) + { + if (busy.Contains(target)) + { + busy.Remove(target); + factory.PassivateObject(target); + free.Add(target); + return true; + } + return false; + } + } + + /// + /// Instantiates the supplied number of instances and adds + /// them to the pool. + /// + /// + /// The initial number of objects to build. + /// + /// + /// If the supplied number of is + /// less than or equal to zero. + /// + protected void InitItems(int initialInstances) + { + if(initialInstances <= 0) + { + throw new ArgumentException("Cannot pool a negative number of instances.", "initialInstances"); + } + for (int i = 0; i < initialInstances; ++i) + { + free.Add(factory.MakeObject()); + } + } + + /// + /// Close the pool and free any resources associated with it. + /// + public void Close() + { + lock (this) + { + for (IEnumerator e = busy.GetEnumerator(); + e.MoveNext(); + e = busy.GetEnumerator()) + { + ReturnObject(e.Current); + } + foreach (object o in free) + { + factory.DestroyObject(o); + } + MakeNotUsable(); + } + } + + /// + /// Clear objects sitting idle in the pool, releasing any + /// associated resources. + /// + /// + ///

    + /// This implementation always throws a + /// . + ///

    + ///
    + /// + /// If the implementation does not support the operation. + /// + public void Clear() + { + throw new NotSupportedException(); + } + + /// + /// Change the state of the pool to unusable. + /// + private void MakeNotUsable() + { + free = busy = new ArrayList(); + closed = true; + } + + /// + /// Gets the number of instances currently borrowed from the pool. + /// + /// + /// If the implementation does not support the operation. + /// + /// + public int NumActive + { + get { return this.busy.Count; } + } + + /// + /// Gets the number of instances currently idle in the pool. + /// + /// + /// If the implementation does not support the operation. + /// + /// + public int NumIdle + { + get { return this.free.Count; } + } + + /// + /// Set the factory used to create new instances. + /// + /// + ///

    + /// This implementation always throws a + /// . + ///

    + ///
    + /// + /// If the implementation does not support the operation. + /// + public IPoolableObjectFactory PoolableObjectFactory + { + set { throw new NotSupportedException(); } + } + } +} diff --git a/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs b/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs new file mode 100644 index 00000000..288ea069 --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs @@ -0,0 +1,349 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Util; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Base class for method builders that contains common functionalities. + /// + /// Bruno Baia + /// $Id: AbstractProxyMethodBuilder.cs,v 1.7 2008/02/06 18:26:42 bbaia Exp $ + public abstract class AbstractProxyMethodBuilder : IProxyMethodBuilder + { + #region Fields + + /// + /// The type builder to use. + /// + protected TypeBuilder typeBuilder; + + /// + /// The implementation to use. + /// + protected IProxyTypeGenerator proxyGenerator; + + /// + /// Indicates whether interfaces should be implemented explicitly. + /// + protected bool explicitImplementation; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the method builder. + /// + /// The type builder to use. + /// + /// The implementation to use. + /// + /// + /// if the interface is to be + /// implemented explicitly; otherwise . + /// + public AbstractProxyMethodBuilder(TypeBuilder typeBuilder, + IProxyTypeGenerator proxyGenerator, bool explicitImplementation) + { + this.typeBuilder = typeBuilder; + this.proxyGenerator = proxyGenerator; + this.explicitImplementation = explicitImplementation; + } + + #endregion + + #region IProxyMethodBuilder Members + + /// + /// Dynamically builds proxy method. + /// + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + /// + /// The for the proxy method. + /// + public virtual MethodBuilder BuildProxyMethod(MethodInfo method, MethodInfo interfaceMethod) + { + MethodBuilder methodBuilder = + DefineMethod(method, interfaceMethod, explicitImplementation); + + ILGenerator il = methodBuilder.GetILGenerator(); + + GenerateMethod(il, method, interfaceMethod); + + il.Emit(OpCodes.Ret); + + if (interfaceMethod != null) + { + typeBuilder.DefineMethodOverride(methodBuilder, interfaceMethod); + } + + return methodBuilder; + } + + #endregion + + #region Protected Methods + + /// + /// Generates the IL instructions that pushes + /// the proxy instance on stack. + /// + /// The IL generator to use. + protected virtual void PushProxy(ILGenerator il) + { + proxyGenerator.PushProxy(il); + } + + /// + /// Generates the IL instructions that pushes + /// the target instance on which calls should be delegated to. + /// + /// The IL generator to use. + protected virtual void PushTarget(ILGenerator il) + { + proxyGenerator.PushTarget(il); + } + + /// + /// Defines proxy method for the target object. + /// + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + /// + /// if the supplied is to be + /// implemented explicitly; otherwise . + /// + /// + /// The for the proxy method. + /// + protected virtual MethodBuilder DefineMethod( + MethodInfo method, MethodInfo intfMethod, bool explicitImplementation) + { + MethodBuilder methodBuilder; + string name; + MethodAttributes attributes; + + if (intfMethod == null) + { + name = method.Name; + attributes = MethodAttributes.Public | MethodAttributes.ReuseSlot + | MethodAttributes.HideBySig | MethodAttributes.Virtual; + } + else + { + attributes = MethodAttributes.HideBySig + | MethodAttributes.NewSlot | MethodAttributes.Virtual + | MethodAttributes.Final; + + if (explicitImplementation || method.Name.IndexOf('.') != -1) + { + name = String.Format("{0}.{1}", + intfMethod.DeclaringType.FullName, intfMethod.Name); + attributes |= MethodAttributes.Private; + } + else + { + name = intfMethod.Name; + attributes |= MethodAttributes.Public; + } + } + + if ((intfMethod != null && intfMethod.IsSpecialName) || method.IsSpecialName) + { + attributes |= MethodAttributes.SpecialName; + } + + methodBuilder = typeBuilder.DefineMethod(name, attributes, + method.CallingConvention, method.ReturnType, + ReflectionUtils.GetParameterTypes(method.GetParameters())); + +#if NET_2_0 + DefineGenericParameters(methodBuilder, method); +#endif + DefineParameters(methodBuilder, method); + + return methodBuilder; + } + + /// + /// Defines method parameters based on proxied method metadata. + /// + /// + /// The to use. + /// + /// The method to proxy. + protected void DefineParameters(MethodBuilder methodBuilder, MethodInfo method) + { + int n = 1; + foreach (ParameterInfo param in method.GetParameters()) + { + ParameterBuilder pb = methodBuilder.DefineParameter(n, param.Attributes, param.Name); + n++; + } + } + +#if NET_2_0 + /// + /// Defines generic method parameters based on proxied method metadata. + /// + /// + /// The to use. + /// + /// The method to proxy. + protected void DefineGenericParameters(MethodBuilder methodBuilder, MethodInfo method) + { + if (method.IsGenericMethodDefinition) + { + Type[] genericArguments = method.GetGenericArguments(); + + // define generic parameters + GenericTypeParameterBuilder[] gtpBuilders = + methodBuilder.DefineGenericParameters(ReflectionUtils.GetGenericParameterNames(genericArguments)); + + // define constraints for each generic parameter + for (int i = 0; i < genericArguments.Length; i++) + { + gtpBuilders[i].SetGenericParameterAttributes(genericArguments[i].GenericParameterAttributes); + + Type[] constraints = genericArguments[i].GetGenericParameterConstraints(); + System.Collections.Generic.List interfaces = new System.Collections.Generic.List(constraints.Length); + foreach (Type constraint in constraints) + { + if (constraint.IsClass) + gtpBuilders[i].SetBaseTypeConstraint(constraint); + else + interfaces.Add(constraint); + } + gtpBuilders[i].SetInterfaceConstraints(interfaces.ToArray()); + } + } + } +#endif + + /// + /// Generates the proxy method. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected abstract void GenerateMethod( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod); + + /// + /// Calls target method directly. + /// + /// The IL generator to use. + /// + /// The interface definition of the method, if applicable. + /// + protected virtual void CallDirectTargetMethod( + ILGenerator il, MethodInfo interfaceMethod) + { + // setup target object for call + PushTarget(il); + + // cast to type method is on + il.Emit(OpCodes.Castclass, interfaceMethod.DeclaringType); + + // setup parameters for call + ParameterInfo[] paramArray = interfaceMethod.GetParameters(); + for (int i = 0; i < paramArray.Length; i++) + { + il.Emit(OpCodes.Ldarg_S, i + 1); + } + + // call method + il.EmitCall(OpCodes.Callvirt, interfaceMethod, null); + } + + /// + /// Calls base method directly. + /// + /// The IL generator to use. + /// The method to proxy. + protected virtual void CallDirectBaseMethod(ILGenerator il, MethodInfo method) + { + // setup proxy instance for call + PushProxy(il); + + // setup parameters for call + ParameterInfo[] paramArray = method.GetParameters(); + for (int i = 0; i < paramArray.Length; i++) + { + il.Emit(OpCodes.Ldarg_S, i + 1); + } + + // call method + il.EmitCall(OpCodes.Call, method, null); + } + + /// + /// Replaces a raw reference with a reference to a proxy. + /// + /// + ///

    + /// If the target object returns reference to itself -- 'this' -- + /// we need to treat it as a special case and return a reference + /// to a proxy object instead. + ///

    + ///
    + /// The IL generator to use. + /// The location of the return value. + protected virtual void ProcessReturnValue(ILGenerator il, LocalBuilder returnValue) + { + Label jmpMethodReturn = il.DefineLabel(); + + // check if target method returned 'this', reference to target + PushTarget(il); + il.Emit(OpCodes.Ldloc, returnValue); + il.Emit(OpCodes.Bne_Un_S, jmpMethodReturn); + + // check if proxy can be casted to the return type + PushProxy(il); + il.Emit(OpCodes.Isinst, returnValue.LocalType); + il.Emit(OpCodes.Brfalse_S, jmpMethodReturn); + + // if it did, return reference to this proxy instead + PushProxy(il); + il.Emit(OpCodes.Stloc, returnValue); + + il.MarkLabel(jmpMethodReturn); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Proxy/AbstractProxyTypeBuilder.cs b/src/Spring/Spring.Core/Proxy/AbstractProxyTypeBuilder.cs new file mode 100644 index 00000000..469cf539 --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/AbstractProxyTypeBuilder.cs @@ -0,0 +1,996 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; + +using Common.Logging; +using Spring.Core.TypeResolution; +using Spring.Util; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Base class for proxy builders that can be used + /// to create a proxy for any class. + /// + /// + ///

    + /// This class provides a set of template + /// methods that derived classes can override to provide custom behaviour + /// appropriate to the type of proxy that is being generated (one of + /// inheritance or composition-based proxying). + ///

    + ///
    + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: AbstractProxyTypeBuilder.cs,v 1.33 2007/12/07 17:59:20 bbaia Exp $ + public abstract class AbstractProxyTypeBuilder : IProxyTypeBuilder, IProxyTypeGenerator + { + #region Fields + + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = LogManager.GetLogger(typeof(AbstractProxyTypeBuilder)); + + private const string DEFAULT_PROXY_TYPE_NAME = "Proxy"; + + private string _name; + private Type _targetType; + private Type _baseType = typeof (object); + private Type[] _interfaces; + private bool _proxyTargetAttributes = true; + private IList _typeAttributes = new ArrayList(); + private IDictionary _memberAttributes = new Hashtable(); + + #endregion + + #region IProxyTypeBuilder Members + + /// + /// Creates the proxy type. + /// + /// The generated proxy class. + public abstract Type BuildProxyType(); + + /// + /// The name of the proxy . + /// + /// The name of the proxy . + public string Name + { + get + { + if (StringUtils.IsNullOrEmpty(_name)) + { + _name = DEFAULT_PROXY_TYPE_NAME; + } + return _name; + } + set { _name = value; } + } + + /// + /// The of the target object. + /// + public Type TargetType + { + get { return _targetType; } + set { _targetType = value; } + } + + /// + /// The of the class that the proxy must + /// inherit from. + /// + /// + ///

    + /// The default value of this property is the + /// . + ///

    + ///
    + public Type BaseType + { + get { return _baseType; } + set { _baseType = value; } + } + + /// + /// Gets or sets the list of interfaces proxy should implement. + /// + /// + /// The default value of this property is all the interfaces + /// implemented or inherited by the target type. + /// + public Type[] Interfaces + { + get + { + if (_interfaces == null) + { + _interfaces = GetProxiableInterfaces(TargetType.GetInterfaces()); + } + return _interfaces; + } + set { _interfaces = value; } + } + + /// + /// Should we proxy target attributes? + /// + /// + public bool ProxyTargetAttributes + { + get { return _proxyTargetAttributes; } + set { _proxyTargetAttributes = value; } + } + + /// + /// The list of custom s that the proxy + /// class must be decorated with. + /// + /// + public IList TypeAttributes + { + get { return _typeAttributes; } + set { _typeAttributes = value; } + } + + /// + /// The custom s that the proxy + /// members must be decorated with. + /// + /// + public IDictionary MemberAttributes + { + get { return _memberAttributes; } + set { _memberAttributes = value; } + } + + #endregion + + #region IProxyTypeGenerator Members + + /// + /// Generates the IL instructions that pushes + /// the proxy instance on stack. + /// + /// The IL generator to use. + public virtual void PushProxy(ILGenerator il) + { + il.Emit(OpCodes.Ldarg_0); + } + + /// + /// Generates the IL instructions that pushes + /// the target instance on which calls should be delegated to. + /// + /// The IL generator to use. + public abstract void PushTarget(ILGenerator il); + + #endregion + + #region Protected Methods + + #region Type generation + + /// + /// Creates an appropriate type builder. + /// + /// The name to use for the proxy type name. + /// The type to extends if provided. + /// The type builder to use. + protected virtual TypeBuilder CreateTypeBuilder(string name, Type baseType) + { + // Generates unique type name + string typeName = String.Format("{0}_{1}", + name, Guid.NewGuid().ToString("N")); + + return DynamicProxyManager.CreateTypeBuilder(typeName, baseType); + } + + #endregion + + #region Attributes generation + + /// + /// Applies attributes to the proxy class. + /// + /// The type builder to use. + /// The proxied class. + /// + /// + protected virtual void ApplyTypeAttributes(TypeBuilder typeBuilder, Type targetType) + { + foreach (object attr in GetTypeAttributes(targetType)) + { + if (attr is CustomAttributeBuilder) + { + typeBuilder.SetCustomAttribute((CustomAttributeBuilder)attr); + } +#if NET_2_0 + else if (attr is CustomAttributeData) + { + typeBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute((CustomAttributeData)attr)); + } +#endif + else if (attr is Attribute) + { + typeBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute((Attribute)attr)); + } + } + } + + /// + /// Applies attributes to the proxied method. + /// + /// The method builder to use. + /// The proxied method. + /// + /// + protected virtual void ApplyMethodAttributes(MethodBuilder methodBuilder, MethodInfo targetMethod) + { + foreach (object attr in GetMethodAttributes(targetMethod)) + { + if (attr is CustomAttributeBuilder) + { + methodBuilder.SetCustomAttribute((CustomAttributeBuilder)attr); + } +#if NET_2_0 + else if (attr is CustomAttributeData) + { + methodBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute((CustomAttributeData)attr)); + } +#endif + else if (attr is Attribute) + { + methodBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute((Attribute)attr)); + } + } + +#if NET_2_0 + ApplyMethodReturnTypeAttributes(methodBuilder, targetMethod); +#endif + ApplyMethodParameterAttributes(methodBuilder, targetMethod); + } + +#if NET_2_0 + /// + /// Applies attributes to the proxied method's return type. + /// + /// The method builder to use. + /// The proxied method. + /// + protected virtual void ApplyMethodReturnTypeAttributes(MethodBuilder methodBuilder, MethodInfo targetMethod) + { + foreach (object attr in GetMethodReturnTypeAttributes(targetMethod)) + { + ParameterBuilder parameterBuilder = methodBuilder.DefineParameter(0, ParameterAttributes.Retval, null); + if (attr is CustomAttributeBuilder) + { + parameterBuilder.SetCustomAttribute((CustomAttributeBuilder)attr); + } + else if (attr is CustomAttributeData) + { + parameterBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute((CustomAttributeData)attr)); + } + else if (attr is Attribute) + { + parameterBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute((Attribute)attr)); + } + } + } +#endif + + /// + /// Applies attributes to proxied method's parameters. + /// + /// The method builder to use. + /// The proxied method. + /// + protected virtual void ApplyMethodParameterAttributes(MethodBuilder methodBuilder, MethodInfo targetMethod) + { + foreach (ParameterInfo paramInfo in targetMethod.GetParameters()) + { + foreach (object attr in GetMethodParameterAttributes(targetMethod, paramInfo)) + { + ParameterBuilder parameterBuilder = methodBuilder.DefineParameter( + (paramInfo.Position + 1), paramInfo.Attributes, paramInfo.Name); + if (attr is CustomAttributeBuilder) + { + parameterBuilder.SetCustomAttribute((CustomAttributeBuilder)attr); + } +#if NET_2_0 + else if (attr is CustomAttributeData) + { + parameterBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute((CustomAttributeData)attr)); + } +#endif + else if (attr is Attribute) + { + parameterBuilder.SetCustomAttribute( + ReflectionUtils.CreateCustomAttribute((Attribute)attr)); + } + } + } + } + + /// + /// Calculates and returns the list of attributes that apply to the + /// specified type. + /// + /// The type to find attributes for. + /// + /// A list of custom attributes that should be applied to type. + /// + /// + /// + protected virtual IList GetTypeAttributes(Type type) + { + ArrayList attributes = new ArrayList(); + + if (this.ProxyTargetAttributes) + { + // add attributes that apply to the target type +#if NET_2_0 + object[] attrs = type.GetCustomAttributes(false); + try + { + System.Collections.Generic.IList attrsData = + CustomAttributeData.GetCustomAttributes(type); + + if (attrs.Length != attrsData.Count) + { + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94803 + attributes.AddRange(attrs); + } + else + { + foreach (CustomAttributeData cad in attrsData) + { + attributes.Add(cad); + } + } + } + catch (ArgumentException) + { + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=296032 + attributes.AddRange(attrs); + } + catch (CustomAttributeFormatException) + { + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=161522 + attributes.AddRange(attrs); + } +#else + attributes.AddRange(type.GetCustomAttributes(false)); +#endif + } + + // add attributes defined by configuration + attributes.AddRange(TypeAttributes); + + return attributes; + } + + /// + /// Calculates and returns the list of attributes that apply to the + /// specified method. + /// + /// The method to find attributes for. + /// + /// A list of custom attributes that should be applied to method. + /// + /// + /// + protected virtual IList GetMethodAttributes(MethodInfo method) + { + ArrayList attributes = new ArrayList(); + + if (this.ProxyTargetAttributes) + { + // add attributes that apply to the target method +#if NET_2_0 + object[] attrs = method.GetCustomAttributes(false); + try + { + System.Collections.Generic.IList attrsData = + CustomAttributeData.GetCustomAttributes(method); + + if (attrs.Length != attrsData.Count) + { + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94803 + attributes.AddRange(attrs); + } + else + { + foreach (CustomAttributeData cad in attrsData) + { + attributes.Add(cad); + } + } + } + catch (ArgumentException) + { + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=296032 + attributes.AddRange(attrs); + } +#else + attributes.AddRange(method.GetCustomAttributes(false)); +#endif + } + + // add attributes defined by configuration + foreach (DictionaryEntry entry in MemberAttributes) + { + if (TypeResolutionUtils.MethodMatch((string)entry.Key, method)) + { + if (entry.Value is Attribute) + { + attributes.Add(entry.Value); + } + else if (entry.Value is IList) + { + attributes.AddRange(entry.Value as IList); + } + } + } + + return attributes; + } + +#if NET_2_0 + /// + /// Calculates and returns the list of attributes that apply to the + /// specified method's return type. + /// + /// The method to find attributes for. + /// + /// A list of custom attributes that should be applied to method's return type. + /// + /// + protected virtual IList GetMethodReturnTypeAttributes(MethodInfo method) + { + ArrayList attributes = new ArrayList(); + + if (this.ProxyTargetAttributes) + { + // add attributes that apply to the target method' return type + object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false); + try + { + System.Collections.Generic.IList attrsData = + CustomAttributeData.GetCustomAttributes(method.ReturnParameter); + + if (attrs.Length != attrsData.Count) + { + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94803 + attributes.AddRange(attrs); + } + else + { + foreach (CustomAttributeData cad in attrsData) + { + attributes.Add(cad); + } + } + } + catch (ArgumentException) + { + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=296032 + attributes.AddRange(attrs); + } + } + + // TODO: add attributes defined by configuration + + return attributes; + } +#endif + + /// + /// Calculates and returns the list of attributes that apply to the + /// specified method's parameters. + /// + /// The method to find attributes for. + /// The method's parameter to find attributes for. + /// + /// A list of custom attributes that should be applied to the specified method's parameter. + /// + /// + protected virtual IList GetMethodParameterAttributes(MethodInfo method, ParameterInfo paramInfo) + { + ArrayList attributes = new ArrayList(); + + if (this.ProxyTargetAttributes) + { + // add attributes that apply to the target method's parameter +#if NET_2_0 + object[] attrs = paramInfo.GetCustomAttributes(false); + try + { + System.Collections.Generic.IList attrsData = + CustomAttributeData.GetCustomAttributes(paramInfo); + + if (attrs.Length != attrsData.Count) + { + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94803 + attributes.AddRange(attrs); + } + else + { + foreach (CustomAttributeData cad in attrsData) + { + attributes.Add(cad); + } + } + } + catch (ArgumentException) + { + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=296032 + attributes.AddRange(attrs); + } +#else + attributes.AddRange(paramInfo.GetCustomAttributes(false)); +#endif + } + + // TODO: add attributes defined by configuration + + return attributes; + } + + /// + /// Check that the specified object is matching the passed attribute type. + /// + /// + ///

    + /// The specified object can be of different type : + ///

    + /// + /// + /// + /// + /// + /// System.Reflection.CustomAttributeData (Only with .NET 2.0) + /// + /// + /// + /// + /// + ///
    + /// The object instance to check. + /// The attribute type to test against. + /// + /// if the object instance matches the attribute type; + /// otherwise . + /// + protected virtual bool IsAttributeMatchingType(object attr, Type attrType) + { + if (attr is Attribute) + { + return (attrType == attr.GetType()); + } +#if NET_2_0 + else if (attr is CustomAttributeData) + { + return (attrType == ((CustomAttributeData)attr).Constructor.DeclaringType); + } +#endif + else if (attr is CustomAttributeBuilder) + { + return (attrType == ((ConstructorInfo)CustomAttributeConstructorField.GetValue(attr)).DeclaringType); + } + return false; + } + + private static readonly FieldInfo CustomAttributeConstructorField = + typeof(CustomAttributeBuilder).GetField("m_con", BindingFlags.Instance | BindingFlags.NonPublic); + + #endregion + + #region Constructors generation + + /// + /// Defines the types of the parameters for the specified constructor. + /// + /// The constructor to use. + /// The types for constructor's parameters. + protected virtual Type[] DefineConstructorParameters(ConstructorInfo constructor) + { + return ReflectionUtils.GetParameterTypes(constructor.GetParameters()); + } + + /// + /// Implements constructors for the proxy class. + /// + /// + /// The builder to use. + /// + protected virtual void ImplementConstructors(TypeBuilder typeBuilder) + { + ConstructorInfo[] constructors = TargetType.GetConstructors( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + foreach (ConstructorInfo constructor in constructors) + { + if (constructor.IsPublic || constructor.IsFamily) + { + ConstructorBuilder cb = typeBuilder.DefineConstructor( + constructor.Attributes, + constructor.CallingConvention, + DefineConstructorParameters(constructor)); + + ILGenerator il = cb.GetILGenerator(); + GenerateConstructor(cb, il, constructor); + il.Emit(OpCodes.Ret); + } + } + } + + /// + /// Generates the proxy constructor. + /// + /// The constructor builder to use. + /// The IL generator to use. + /// The constructor to use. + protected virtual void GenerateConstructor( + ConstructorBuilder builder, ILGenerator il, ConstructorInfo constructor) + { + } + + #endregion + + #region Members generation + + /// + /// Implements an interface. + /// + /// + /// Generates proxy methods that belongs to the interface + /// using the specified . + /// + /// The type builder to use. + /// + /// The implementation to use + /// + /// The interface to implement. + /// + /// The of the target object. + /// + protected virtual void ImplementInterface(TypeBuilder typeBuilder, + IProxyMethodBuilder proxyMethodBuilder, Type intf, Type targetType) + { + ImplementInterface(typeBuilder, proxyMethodBuilder, intf, targetType, true); + } + + /// + /// Implements an interface. + /// + /// + /// Generates proxy methods that belongs to the interface + /// using the specified . + /// + /// The type builder to use. + /// + /// The implementation to use + /// + /// The interface to implement. + /// + /// The of the target object. + /// + /// + /// if target virtual methods should not be proxied; + /// otherwise . + /// + protected virtual void ImplementInterface(TypeBuilder typeBuilder, + IProxyMethodBuilder proxyMethodBuilder, Type intf, + Type targetType, bool proxyVirtualMethods) + { + IDictionary methodMap = new Hashtable(); + + InterfaceMapping mapping = GetInterfaceMapping(targetType, intf); + + typeBuilder.AddInterfaceImplementation(intf); + + for (int i = 0; i < mapping.InterfaceMethods.Length; i++) + { + if (!proxyVirtualMethods && + !mapping.TargetMethods[i].DeclaringType.IsInterface && + mapping.TargetMethods[i].IsVirtual && + !mapping.TargetMethods[i].IsFinal) + continue; + + MethodBuilder methodBuilder = proxyMethodBuilder.BuildProxyMethod( + mapping.TargetMethods[i], mapping.InterfaceMethods[i]); + + ApplyMethodAttributes(methodBuilder, mapping.TargetMethods[i]); + + methodMap[mapping.InterfaceMethods[i].Name] = methodBuilder; + } + foreach (PropertyInfo property in intf.GetProperties()) + { + ImplementProperty(typeBuilder, intf, property, methodMap); + } + foreach (EventInfo evt in intf.GetEvents()) + { + ImplementEvent(typeBuilder, intf, evt, methodMap); + } + } + + /// + /// Gets the mapping of the interface to proxy + /// into the actual methods on the target type + /// that does not need to implement that interface. + /// + /// + ///

    + /// If the target type does not implement the interface, + /// we return the interfaces methods as the target methods for many reasons : + ///

      + ///
    • + /// The target object can change for an object that implements the interface. + /// (See 'Spring.Aop.Framework.DynamicProxy.IAdvisedProxyMethodBuilder' + /// implementation in the Spring AOP framework for an example) + ///
    • + ///
    • + /// Allow Transparent proxies to be proxied. + /// (See Spring Remoting framework for an example) + ///
    • + ///
    • + /// Allow null target to be proxied. + /// (See Spring AOP framework which avoid calls to the target object + /// by intercepting all methods. Think "dynamic mock") + /// (See 'Spring.Web.Services.WebServiceProxyFactory' implementation for another example) + ///
    • + ///
    + ///

    + ///
    + /// + /// The of the target object. + /// + /// The interface to implement. + /// + /// An interface mapping for the interface to proxy. + /// + protected virtual InterfaceMapping GetInterfaceMapping( + Type targetType, Type intf) + { + InterfaceMapping mapping; + + if (intf.IsAssignableFrom(targetType)) + { + // target type implements the interface + mapping = targetType.GetInterfaceMap(intf); + } + else + { + // target type does not implement the interface + mapping.TargetType = targetType; + mapping.InterfaceType = intf; + mapping.InterfaceMethods = intf.GetMethods(); + mapping.TargetMethods = mapping.InterfaceMethods; + } + + return mapping; + } + + /// + /// Inherit from a type. + /// + /// + /// Generates proxy methods for base virtual methods + /// using the specified . + /// + /// + /// The builder to use for code generation. + /// + /// + /// The implementation to use to override base virtual methods. + /// + /// The to inherit from. + protected virtual void InheritType(TypeBuilder typeBuilder, + IProxyMethodBuilder proxyMethodBuilder, Type type) + { + InheritType(typeBuilder, proxyMethodBuilder, type, false); + } + + /// + /// Inherit from a type. + /// + /// + /// Generates proxy methods for base virtual methods + /// using the specified . + /// + /// + /// The builder to use for code generation. + /// + /// + /// The implementation to use to override base virtual methods. + /// + /// The to inherit from. + /// + /// if only members declared at the level + /// of the supplied 's hierarchy should be proxied; + /// otherwise . + /// + protected virtual void InheritType(TypeBuilder typeBuilder, + IProxyMethodBuilder proxyMethodBuilder, Type type, bool declaredMembersOnly) + { + IDictionary methodMap = new Hashtable(); + IList finalMethods = new ArrayList(); + + BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; + + if (declaredMembersOnly) + { + bindingFlags |= BindingFlags.DeclaredOnly; + } + + // override virtual methods + MethodInfo[] methods = type.GetMethods(bindingFlags); + foreach (MethodInfo method in methods) + { + if (method.IsVirtual && !method.IsFinal) + { + MethodBuilder methodBuilder = proxyMethodBuilder.BuildProxyMethod(method, null); + ApplyMethodAttributes(methodBuilder, method); + methodMap[method.Name] = methodBuilder; + } + } + // override virtual properties + foreach (PropertyInfo property in type.GetProperties(bindingFlags)) + { + ImplementProperty(typeBuilder, type, property, methodMap); + } + // override virtual events + foreach (EventInfo evt in type.GetEvents(bindingFlags)) + { + ImplementEvent(typeBuilder, type, evt, methodMap); + } + } + + /// + /// Implements the specified . + /// + /// The type builder to use. + /// The type the property is defined on. + /// The property to proxy. + /// The implemented methods map. + protected virtual void ImplementProperty( + TypeBuilder typeBuilder, Type type, PropertyInfo property, IDictionary methodMap) + { + MethodBuilder getMethod = methodMap["get_" + property.Name] as MethodBuilder; + MethodBuilder setMethod = methodMap["set_" + property.Name] as MethodBuilder; + + if (getMethod != null || setMethod != null) + { + string propertyName = (type.IsInterface && + ((getMethod != null && getMethod.IsPrivate) || (setMethod != null && setMethod.IsPrivate))) + ? type.FullName + "." + property.Name + : property.Name; + PropertyBuilder pb = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, + property.PropertyType, Type.EmptyTypes); + + // set get/set methods + if (property.CanRead && getMethod != null) + { + pb.SetGetMethod(getMethod); + } + if (property.CanWrite && setMethod != null) + { + pb.SetSetMethod(setMethod); + } + } + } + + /// + /// Implements the specified event. + /// + /// The type builder to use. + /// The type the event is defined on. + /// The event to proxy. + /// The implemented methods map. + protected virtual void ImplementEvent( + TypeBuilder typeBuilder, Type type, EventInfo evt, IDictionary methodMap) + { + MethodBuilder addOnMethod = methodMap["add_" + evt.Name] as MethodBuilder; + MethodBuilder removeOnMethod = methodMap["remove_" + evt.Name] as MethodBuilder; + + if (addOnMethod != null && removeOnMethod != null) + { + string eventName = (addOnMethod.IsPrivate) + ? addOnMethod.DeclaringType.FullName + "." + evt.Name + : evt.Name; + + EventBuilder eb = typeBuilder.DefineEvent( + eventName, EventAttributes.None, evt.EventHandlerType); + + // set add/remove methods + eb.SetAddOnMethod(addOnMethod); + eb.SetRemoveOnMethod(removeOnMethod); + } + } + + #endregion + + /// + /// Returns an array of s that represent + /// the proxiable interfaces. + /// + /// + /// An interface is proxiable if it's not marked with the + /// . + /// + /// + /// The array of interfaces from which + /// we want to get the proxiable interfaces. + /// + /// + /// An array containing the interface s. + /// + protected virtual Type[] GetProxiableInterfaces(Type[] interfaces) + { + ArrayList proxiableInterfaces = new ArrayList(); + + foreach(Type intf in interfaces) + { + if (!Attribute.IsDefined(intf, typeof(ProxyIgnoreAttribute), false) && + !IsSpecialInterface(intf) && + ReflectionUtils.IsTypeVisible(intf, DynamicProxyManager.ASSEMBLY_NAME)) + { + proxiableInterfaces.Add(intf); + } + } + + return (Type[]) proxiableInterfaces.ToArray(typeof(Type)); + } + + /// + /// Checks if specified interface is of a special type + /// that should never be proxied (i.e. ISerializable). + /// + /// Interface type to check. + /// + /// true if it is, false otherwise. + /// + private bool IsSpecialInterface(Type intf) + { + return intf == typeof(ISerializable); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Proxy/BaseProxyMethodBuilder.cs b/src/Spring/Spring.Core/Proxy/BaseProxyMethodBuilder.cs new file mode 100644 index 00000000..75b5bc58 --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/BaseProxyMethodBuilder.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Util; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Implementation of IProxyMethodBuilder that delegates method calls to the base class. + /// + /// Bruno Baia + /// $Id: BaseProxyMethodBuilder.cs,v 1.1 2006/08/10 18:45:55 bbaia Exp $ + public class BaseProxyMethodBuilder : AbstractProxyMethodBuilder + { + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the method builder. + /// + /// The type builder to use. + /// + /// The implementation to use. + /// + /// + /// if the interface is to be + /// implemented explicitly; otherwise . + /// + public BaseProxyMethodBuilder(TypeBuilder typeBuilder, + IProxyTypeGenerator proxyGenerator, bool explicitImplementation) + : base(typeBuilder, proxyGenerator, explicitImplementation) + { + } + + #endregion + + #region Protected Methods + + /// + /// Generates the proxy method. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected override void GenerateMethod( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + CallDirectBaseMethod(il, method); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Proxy/CompositionProxyTypeBuilder.cs b/src/Spring/Spring.Core/Proxy/CompositionProxyTypeBuilder.cs new file mode 100644 index 00000000..0ff70f9e --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/CompositionProxyTypeBuilder.cs @@ -0,0 +1,192 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Util; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Builds a proxy type using composition. + /// + /// + /// + /// In order for this builder to work, the target must implement + /// one or more interfaces. + /// + /// + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: CompositionProxyTypeBuilder.cs,v 1.12 2007/03/27 02:27:43 bbaia Exp $ + public class CompositionProxyTypeBuilder : AbstractProxyTypeBuilder + { + #region Fields + + private bool explicitInterfaceImplementation = false; + + /// + /// Target instance calls should be delegated to. + /// + protected FieldBuilder targetInstance; + + #endregion + + #region Properties + + /// + /// Gets or sets a value indicating whether interfaces should be implemented explicitly. + /// + /// + /// if they should be; otherwise, . + /// + public bool ExplicitInterfaceImplementation + { + get { return explicitInterfaceImplementation; } + set { explicitInterfaceImplementation = value; } + } + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public CompositionProxyTypeBuilder() + { + Name = "CompositionProxy"; + } + + #endregion + + #region IProxyTypeBuilder Members + + /// + /// Creates a proxy that delegates calls to an instance of the + /// target object. + /// + /// + ///

    + /// Only interfaces can be proxied using composition, so the target + /// must implement one or more interfaces. + ///

    + ///
    + /// The generated proxy class. + /// + /// If the + /// does not implement any interfaces. + /// + public override Type BuildProxyType() + { + if (Interfaces == null || Interfaces.Length == 0) + { + throw new ArgumentException( + "Composition proxy target must implement at least one interface."); + } + + TypeBuilder typeBuilder = CreateTypeBuilder(Name, BaseType); + + // apply custom attributes to the proxy type. + ApplyTypeAttributes(typeBuilder, TargetType); + + // declare fields + DeclareTargetInstanceField(typeBuilder); + + // create constructors + ImplementConstructors(typeBuilder); + + // implement interfaces + foreach (Type intf in Interfaces) + { + ImplementInterface(typeBuilder, + new TargetProxyMethodBuilder(typeBuilder, this, explicitInterfaceImplementation), + intf, TargetType); + } + + return typeBuilder.CreateType(); + } + + #endregion + + #region IProxyTypeGenerator Members + + /// + /// Generates the IL instructions that pushes + /// the target instance on which calls should be delegated to. + /// + /// The IL generator to use. + public override void PushTarget(ILGenerator il) + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, targetInstance); + } + + #endregion + + #region Protected Methods + + /// + /// Deaclares a field that holds the target object instance. + /// + /// + /// The builder to use for code generation. + /// + protected virtual void DeclareTargetInstanceField(TypeBuilder builder) + { + targetInstance = builder.DefineField("__proxyTarget", TargetType, FieldAttributes.Private); + } + + /// + /// Generates the proxy constructor. + /// + /// + ///

    + /// This implementation creates instance of the target object for delegation + /// using constructor arguments. + ///

    + ///
    + /// The constructor builder to use. + /// The IL generator to use. + /// The constructor to delegate the creation to. + protected override void GenerateConstructor( + ConstructorBuilder builder, ILGenerator il, ConstructorInfo constructor) + { + int paramCount = constructor.GetParameters().Length; + il.Emit(OpCodes.Ldarg_0); + for (int i = 1; i <= paramCount; i++) + { + il.Emit(OpCodes.Ldarg_S, i); + } + il.Emit(OpCodes.Newobj, TargetType.GetConstructor( + ReflectionUtils.GetParameterTypes(constructor.GetParameters()))); + il.Emit(OpCodes.Stfld, targetInstance); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Proxy/DynamicProxyManager.cs b/src/Spring/Spring.Core/Proxy/DynamicProxyManager.cs new file mode 100644 index 00000000..ede6f5dd --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/DynamicProxyManager.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using Spring.Util; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Allows easy access to existing and creation of new dynamic proxies. + /// + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: DynamicProxyManager.cs,v 1.6 2007/05/30 17:32:05 oakinger Exp $ + public sealed class DynamicProxyManager + { + #region Fields + + /// + /// The name of the assembly that defines proxy types created. + /// + public const string ASSEMBLY_NAME = "Spring.Proxy"; + + /// + /// The attributes of the proxy type to generate. + /// + private const TypeAttributes TYPE_ATTRIBUTES = TypeAttributes.BeforeFieldInit | TypeAttributes.Public; + + #endregion + + #region Public Methods + + /// + /// Creates an appropriate type builder. + /// + /// The proxy type name. + /// The type to extends if provided. + /// The type builder to use. + public static TypeBuilder CreateTypeBuilder(string typeName, Type baseType) + { + ModuleBuilder module = DynamicCodeManager.GetModuleBuilder(ASSEMBLY_NAME); + + if (baseType == null) + { + return module.DefineType(typeName, TYPE_ATTRIBUTES); + } + else + { + return module.DefineType(typeName, TYPE_ATTRIBUTES, baseType); + } + } + + /// + /// Saves dynamically generated assembly to disk. + /// Can only be called in DEBUG_DYNAMIC mode, per ConditionalAttribute rules. + /// + [Conditional("DEBUG_DYNAMIC")] + public static void SaveAssembly() + { + DynamicCodeManager.SaveAssembly( ASSEMBLY_NAME ); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Proxy/IProxyMethodBuilder.cs b/src/Spring/Spring.Core/Proxy/IProxyMethodBuilder.cs new file mode 100644 index 00000000..1619674f --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/IProxyMethodBuilder.cs @@ -0,0 +1,51 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Defines interface that proxy method builders have to implement. + /// + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: IProxyMethodBuilder.cs,v 1.1 2006/08/10 18:45:55 bbaia Exp $ + public interface IProxyMethodBuilder + { + /// + /// Dynamically builds proxy method. + /// + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + /// + /// The for the proxy method. + /// + MethodBuilder BuildProxyMethod(MethodInfo method, MethodInfo intfMethod); + } +} diff --git a/src/Spring/Spring.Core/Proxy/IProxyTypeBuilder.cs b/src/Spring/Spring.Core/Proxy/IProxyTypeBuilder.cs new file mode 100644 index 00000000..afc6b50a --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/IProxyTypeBuilder.cs @@ -0,0 +1,173 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Describes the operations for a generic proxy type builder that can be + /// used to create a proxy type for any class. + /// + /// Aleksandar Seovic + /// $Id: IProxyTypeBuilder.cs,v 1.8 2007/06/20 15:59:54 bbaia Exp $ + public interface IProxyTypeBuilder + { + /// + /// Creates the proxy type. + /// + /// The generated proxy class. + Type BuildProxyType(); + + /// + /// The name of the proxy . + /// + /// The name of the proxy . + string Name { get; set; } + + /// + /// The of the target object. + /// + Type TargetType { get; set; } + + /// + /// The of the class that the proxy must + /// inherit from. + /// + Type BaseType { get; set; } + + /// + /// Gets or sets the list of interfaces proxy should implement. + /// + Type[] Interfaces { get; set; } + + /// + /// Should we proxy target attributes? + /// + /// + /// by default. + /// Target type attributes, method attributes, method's return type attributes + /// and method's parameter attributes are copied to the proxy. + /// + bool ProxyTargetAttributes { get; set; } + + /// + /// The list of custom s that the proxy + /// class must be decorated with. + /// + /// + ///

    + /// Note that the list is composed of instances of the actual + /// s that are to be applied, not the + /// s of the s. + ///

    + ///
    + /// + ///

    + /// The following code snippets show examples of how to decorate the + /// the proxied class with one or more s. + ///

    + /// + /// // get a concrete implementation of an IProxyTypeBuilder... + /// IProxyTypeBuilder builder = ... ; + /// builder.TargetType = typeof( ... ); + /// + /// IDictionary typeAtts = new Hashtable(); + /// builder.TypeAttributes = typeAtts; + /// + /// // applies a single Attribute to the proxied class... + /// typeAtts = new Attribute[] { new MyCustomAttribute() }); + /// + /// // applies a number of Attributes to the proxied class... + /// typeAtts = new Attribute[] + /// { + /// new MyCustomAttribute(), + /// new AnotherAttribute(), + /// }); + /// + ///
    + IList TypeAttributes { get; set; } + + /// + /// The custom s that the proxy + /// members must be decorated with. + /// + /// + ///

    + /// This dictionary must use simple s for keys + /// (denoting the member names that the attributes are to be applied to), + /// with the corresponding values being + /// s. + ///

    + ///

    + /// The key may be wildcarded using the '*' character... if so, + /// then those proxy members that match against the key will be + /// decorated with the attendant list of + /// s. This naturally implies that using + /// the '*' character as a key will result in the attendant list + /// of s being applied to every member of + /// the proxied class. + ///

    + ///
    + /// + ///

    + /// The following code snippets show examples of how to decorate the + /// members of a proxied class with one or more + /// s. + ///

    + /// + /// // get a concrete implementation of an IProxyTypeBuilder... + /// IProxyTypeBuilder builder = ... ; + /// builder.TargetType = typeof( ... ); + /// + /// IDictionary memAtts = new Hashtable(); + /// builder.MemberAttributes = memAtts; + /// + /// // applies a single Attribute to all members of the proxied class... + /// memAtts ["*"] = new Attribute[] { new MyCustomAttribute() }); + /// + /// // applies a number of Attributes to all members of the proxied class... + /// memAtts ["*"] = new Attribute[] + /// { + /// new MyCustomAttribute(), + /// new AnotherAttribute(), + /// }); + /// + /// // applies a single Attribute to those members of the proxied class + /// // that have identifiers starting with 'Do' ... + /// memAtts ["Do*"] = new Attribute[] { new MyCustomAttribute() }); + /// + /// // applies a number of Attributes to those members of the proxied class + /// // that have identifiers starting with 'Do' ... + /// memAtts ["Do*"] = new Attribute[] + /// { + /// new MyCustomAttribute(), + /// new AnotherAttribute(), + /// }); + /// + ///
    + IDictionary MemberAttributes { get; set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Proxy/IProxyTypeGenerator.cs b/src/Spring/Spring.Core/Proxy/IProxyTypeGenerator.cs new file mode 100644 index 00000000..995c4478 --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/IProxyTypeGenerator.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection.Emit; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Describes the operations that generates IL instructions + /// used to build the proxy type. + /// + /// Bruno Baia + /// $Id: IProxyTypeGenerator.cs,v 1.3 2006/11/12 01:37:47 bbaia Exp $ + public interface IProxyTypeGenerator + { + // TODO : Why not ? +/* + /// + /// Gets the used to build the proxy type. + /// + TypeBuilder ProxyTypeBuilder { get; } +*/ + /// + /// Generates the IL instructions that pushes + /// the proxy instance on stack. + /// + /// The IL generator to use. + void PushProxy(ILGenerator il); + + /// + /// Generates the IL instructions that pushes + /// the target instance on which calls should be delegated to. + /// + /// The IL generator to use. + void PushTarget(ILGenerator il); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Proxy/InheritanceProxyTypeBuilder.cs b/src/Spring/Spring.Core/Proxy/InheritanceProxyTypeBuilder.cs new file mode 100644 index 00000000..eb00f213 --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/InheritanceProxyTypeBuilder.cs @@ -0,0 +1,175 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Builds a proxy type using inheritance. + /// + /// + /// + /// In order for this builder to work, target methods have to be either + /// , or belong to an interface. + /// + /// + /// Aleksandar Seovic + /// Bruno Baia + /// $Id: InheritanceProxyTypeBuilder.cs,v 1.14 2007/12/04 18:38:02 bbaia Exp $ + public class InheritanceProxyTypeBuilder : AbstractProxyTypeBuilder + { + #region Fields + + private bool declaredMembersOnly = false; + + #endregion + + #region Properties + + /// + /// Gets or sets a value indicating whether inherited members should be proxied. + /// + /// + /// if they should be; otherwise, . + /// + public bool DeclaredMembersOnly + { + get { return declaredMembersOnly; } + set { declaredMembersOnly = value; } + } + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public InheritanceProxyTypeBuilder() + { + Name = "InheritanceProxy"; + //ProxyTargetAttributes = false; + } + + #endregion + + #region IProxyTypeBuilder Members + + /// + /// Creates a proxy that inherits the proxied object's class. + /// + /// + ///

    + /// Only (non-final) methods can be proxied, + /// unless they are members of one of the interfaces that target class + /// implements. In that case, methods will be proxied using explicit + /// interface implementation, which means that client code will have + /// to cast the proxy to a specific interface in order to invoke the + /// methods. + ///

    + ///
    + /// The generated proxy class. + public override Type BuildProxyType() + { + BaseType = TargetType; + if (BaseType.IsSealed) + { + throw new ArgumentException("Inheritance proxy cannot be created for a sealed class [" + BaseType.FullName + "]"); + } + + TypeBuilder typeBuilder = CreateTypeBuilder(Name, BaseType); + + // apply custom attributes to the proxy type. + ApplyTypeAttributes(typeBuilder, BaseType); + + // create constructors + ImplementConstructors(typeBuilder); + + // proxy only final methods that are members of one of the interfaces + foreach (Type intf in Interfaces) + { + ImplementInterface(typeBuilder, + new BaseProxyMethodBuilder(typeBuilder, this, true), + intf, TargetType, false); + } + + // proxy base virtual methods + InheritType(typeBuilder, + new BaseProxyMethodBuilder(typeBuilder, this, false), + BaseType, declaredMembersOnly); + + return typeBuilder.CreateType(); + } + + #endregion + + #region IProxyTypeGenerator Members + + /// + /// Generates the IL instructions that pushes + /// the target instance on which calls should be delegated to. + /// + /// The IL generator to use. + public override void PushTarget(ILGenerator il) + { + PushProxy(il); + } + + #endregion + + #region Protected Methods + + /// + /// Generates the proxy constructor. + /// + /// + ///

    + /// This implementation delegates the call to a base class constructor. + ///

    + ///
    + /// The constructor builder to use. + /// The IL generator to use. + /// + /// The base class constructor to delegate the call to. + /// + protected override void GenerateConstructor( + ConstructorBuilder builder, ILGenerator il, ConstructorInfo constructor) + { + int paramCount = constructor.GetParameters().Length; + il.Emit(OpCodes.Ldarg_0); + for (int i = 1; i <= paramCount; i++) + { + il.Emit(OpCodes.Ldarg_S, i); + } + il.Emit(OpCodes.Call, constructor); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Proxy/ProxyIgnoreAttribute.cs b/src/Spring/Spring.Core/Proxy/ProxyIgnoreAttribute.cs new file mode 100644 index 00000000..79c294c8 --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/ProxyIgnoreAttribute.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Proxy +{ + /// + /// This attribute can be used to mark interfaces that should not be proxied + /// + /// Bruno Baia + /// $Id: ProxyIgnoreAttribute.cs,v 1.1 2006/11/16 02:30:50 bbaia Exp $ + [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + [Serializable] + public sealed class ProxyIgnoreAttribute : Attribute + { + /// + /// Creates a new instance of the + /// class. + /// + public ProxyIgnoreAttribute() + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Proxy/TargetProxyMethodBuilder.cs b/src/Spring/Spring.Core/Proxy/TargetProxyMethodBuilder.cs new file mode 100644 index 00000000..e85a62c7 --- /dev/null +++ b/src/Spring/Spring.Core/Proxy/TargetProxyMethodBuilder.cs @@ -0,0 +1,97 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Util; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Implementation of IProxyMethodBuilder that delegates method calls to target object. + /// + /// Bruno Baia + /// $Id: TargetProxyMethodBuilder.cs,v 1.1 2006/08/10 18:45:55 bbaia Exp $ + public class TargetProxyMethodBuilder : AbstractProxyMethodBuilder + { + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the method builder. + /// + /// The type builder to use. + /// + /// The implementation to use. + /// + /// + /// if the interface is to be + /// implemented explicitly; otherwise . + /// + public TargetProxyMethodBuilder( + TypeBuilder typeBuilder, IProxyTypeGenerator proxyGenerator, bool explicitImplementation) + : base(typeBuilder, proxyGenerator, explicitImplementation) + { + } + + #endregion + + #region Protected Methods + + /// + /// Generates the proxy method. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected override void GenerateMethod( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + LocalBuilder returnValue = null; + if (method.ReturnType != typeof(void)) + { + returnValue = il.DeclareLocal(method.ReturnType); + } + + CallDirectTargetMethod(il, interfaceMethod); + + // unboxing is not necessary because we called the method directly + if (returnValue != null) + { + il.Emit(OpCodes.Stloc, returnValue); + + if (!method.ReturnType.IsValueType) + { + ProcessReturnValue(il, returnValue); + } + il.Emit(OpCodes.Ldloc, returnValue); + } + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/BaseDynamicMember.cs b/src/Spring/Spring.Core/Reflection/Dynamic/BaseDynamicMember.cs new file mode 100644 index 00000000..e592f377 --- /dev/null +++ b/src/Spring/Spring.Core/Reflection/Dynamic/BaseDynamicMember.cs @@ -0,0 +1,147 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + /// + /// Base class for dynamic members. + /// + /// Aleksandar Seovic + /// $Id: BaseDynamicMember.cs,v 1.1 2007/08/08 04:05:37 bbaia Exp $ + public class BaseDynamicMember + { + /// + /// Method attributes constant. + /// + protected const MethodAttributes METHOD_ATTRIBUTES = + MethodAttributes.Public | MethodAttributes.HideBySig + | MethodAttributes.NewSlot | MethodAttributes.Virtual + | MethodAttributes.Final; + + private static ConstructorInfo invalidOperationException = + typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) }); + + /// + /// Sets up target instance for invocation. + /// + /// IL generator to use. + /// Type of target instance. + protected static void SetupTargetInstance(ILGenerator il, Type targetType) + { + il.Emit(OpCodes.Ldarg_1); + if (targetType.IsValueType) + { + LocalBuilder target = il.DeclareLocal(targetType); +#if NET_2_0 + il.Emit(OpCodes.Unbox_Any, targetType); +#else + il.Emit(OpCodes.Unbox, targetType); + il.Emit(OpCodes.Ldobj, targetType); +#endif + il.Emit(OpCodes.Stloc, target); + il.Emit(OpCodes.Ldloca, target); + } + else + { + il.Emit(OpCodes.Castclass, targetType); + } + } + + /// + /// Sets up invocation argument. + /// + /// IL generator to use. + /// Argument type. + /// Argument position. + protected static void SetupArgument(ILGenerator il, Type argumentType, int argumentPosition) + { + il.Emit(OpCodes.Ldarg, argumentPosition); + if (argumentType.IsValueType) + { +#if NET_2_0 + il.Emit(OpCodes.Unbox_Any, argumentType); +#else + il.Emit(OpCodes.Unbox, argumentType); + il.Emit(OpCodes.Ldobj, argumentType); +#endif + } + else + { + il.Emit(OpCodes.Castclass, argumentType); + } + } + + /// + /// Generates method invocation code. + /// + /// IL generator to use. + /// Flag specifying whether method is static. + /// Flag specifying whether method is on the value type. + /// Method to invoke. + protected static void InvokeMethod(ILGenerator il, bool isStatic, bool isValueType, MethodInfo method) + { + if (isStatic || isValueType) + { + il.EmitCall(OpCodes.Call, method, null); + } + else + { + il.EmitCall(OpCodes.Callvirt, method, null); + } + } + + /// + /// Generates code to process return value if necessary. + /// + /// IL generator to use. + /// Type of the return value. + protected static void ProcessReturnValue(ILGenerator il, Type returnValueType) + { + if (returnValueType == typeof(void)) + { + il.Emit(OpCodes.Ldnull); + } + else if (returnValueType.IsValueType) + { + il.Emit(OpCodes.Box, returnValueType); + } + } + + /// + /// Generates code that throws . + /// + /// IL generator to use. + /// Error message to use. + protected static void ThrowInvalidOperationException(ILGenerator il, string message) + { + il.Emit(OpCodes.Ldstr, message); + il.Emit(OpCodes.Newobj, invalidOperationException); + il.Emit(OpCodes.Throw); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicConstructor.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicConstructor.cs new file mode 100644 index 00000000..64adb46c --- /dev/null +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicConstructor.cs @@ -0,0 +1,199 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; +using Spring.Util; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + #region IDynamicConstructor interface + + /// + /// Defines constructors that dynamic constructor class has to implement. + /// + public interface IDynamicConstructor + { + /// + /// Invokes dynamic constructor. + /// + /// + /// Constructor arguments. + /// + /// + /// A constructor value. + /// + object Invoke(object[] arguments); + } + + #endregion + + #region Safe wrapper + + /// + /// Safe wrapper for the dynamic constructor. + /// + /// + /// will attempt to use dynamic + /// constructor if possible, but it will fall back to standard + /// reflection if necessary. + /// + public class SafeConstructor : IDynamicConstructor + { + private ConstructorInfo constructor; + private IDynamicConstructor dynamicConstructor; + private bool isOptimized = false; + + /// + /// Creates a new instance of the safe constructor wrapper. + /// + /// Constructor to wrap. + public SafeConstructor(ConstructorInfo constructor) + { + this.constructor = constructor; + if (constructor.IsPublic && + ReflectionUtils.IsTypeVisible(constructor.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME)) + { + this.dynamicConstructor = DynamicConstructor.Create(constructor); + this.isOptimized = true; + } + } + + /// + /// Invokes dynamic constructor. + /// + /// + /// Constructor arguments. + /// + /// + /// A constructor value. + /// + public object Invoke(object[] arguments) + { + if (isOptimized) + { + // try dynamic constructor first but fall back to standard reflection + // if arguments are causing InvalidCastExceptions + try + { + return dynamicConstructor.Invoke(arguments); + } + catch (InvalidCastException) + { + isOptimized = false; + return constructor.Invoke(arguments); + } + } + else + { + return constructor.Invoke(arguments); + } + } + } + + #endregion + + /// + /// Factory class for dynamic constructors. + /// + /// Aleksandar Seovic + /// $Id: DynamicConstructor.cs,v 1.1 2007/08/08 04:05:37 bbaia Exp $ + public class DynamicConstructor : BaseDynamicMember + { + private static readonly CreateConstructorCallback s_createCallback = new CreateConstructorCallback(CreateInternal); + + #region Create Constructor + + /// + /// Creates dynamic constructor instance for the specified . + /// + /// Constructor info to create dynamic constructor for. + /// Dynamic constructor for the specified . + public static IDynamicConstructor Create(ConstructorInfo constructor) + { + AssertUtils.ArgumentNotNull(constructor, "You cannot create a dynamic constructor for a null value."); + + IDynamicConstructor dynamicConstructor = DynamicReflectionManager.GetDynamicConstructor(constructor, s_createCallback); + return dynamicConstructor; + } + + private static IDynamicConstructor CreateInternal(ConstructorInfo constructor) + { + IDynamicConstructor dynamicConstructor = null; + + TypeBuilder tb = DynamicReflectionManager.CreateTypeBuilder("Ctor_" + constructor.DeclaringType.Name); + tb.AddInterfaceImplementation(typeof(IDynamicConstructor)); + + GenerateInvoke(tb, constructor); + + Type dynamicConstructorType = tb.CreateType(); + ConstructorInfo ctor = dynamicConstructorType.GetConstructor(Type.EmptyTypes); + dynamicConstructor = (IDynamicConstructor) ctor.Invoke(ObjectUtils.EmptyObjects); + + return dynamicConstructor; + } + + private static void GenerateInvoke(TypeBuilder tb, ConstructorInfo constructor) + { + MethodBuilder invokeMethod = + tb.DefineMethod("Invoke", METHOD_ATTRIBUTES, typeof(object), new Type[] {typeof(object[])}); + invokeMethod.DefineParameter(1, ParameterAttributes.None, "args"); + + ILGenerator il = invokeMethod.GetILGenerator(); + + Type[] argTypes = ReflectionUtils.GetParameterTypes(constructor); + for (int i = 0; i < argTypes.Length; i++) + { + SetupConstructorArgument(il, argTypes[i], i); + } + + il.Emit(OpCodes.Newobj, constructor); + ProcessReturnValue(il, constructor.DeclaringType); + il.Emit(OpCodes.Ret); + } + + private static void SetupConstructorArgument(ILGenerator il, Type argumentType, int argumentPosition) + { + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldc_I4, argumentPosition); + il.Emit(OpCodes.Ldelem_Ref); + if (argumentType.IsValueType) + { +#if NET_2_0 + il.Emit(OpCodes.Unbox_Any, argumentType); +#else + il.Emit(OpCodes.Unbox, argumentType); + il.Emit(OpCodes.Ldobj, argumentType); +#endif + } + else + { + il.Emit(OpCodes.Castclass, argumentType); + } + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicField.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicField.cs new file mode 100644 index 00000000..18596835 --- /dev/null +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicField.cs @@ -0,0 +1,487 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using Spring.Util; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + #region IDynamicField interface + + /// + /// Defines methods that dynamic field class has to implement. + /// + public interface IDynamicField + { + /// + /// Gets the value of the dynamic field for the specified target object. + /// + /// + /// Target object to get field value from. + /// + /// + /// A field value. + /// + object GetValue(object target); + + /// + /// Gets the value of the dynamic field for the specified target object. + /// + /// + /// Target object to set field value on. + /// + /// + /// A new field value. + /// + void SetValue(object target, object value); + } + + #endregion + + #region Safe wrapper + + /// + /// Safe wrapper for the dynamic field. + /// + /// + /// will attempt to use dynamic + /// field if possible, but it will fall back to standard + /// reflection if necessary. + /// + public class SafeField : IDynamicField + { + private readonly FieldInfo fieldInfo; +#if NET_2_0 + #region Cache + + private static readonly IDictionary fieldCache = new Hashtable(); + + /// + /// Holds cached Getter/Setter delegates for a Field + /// + private class DynamicFieldCacheEntry + { + public readonly GetterDelegate Getter; + public readonly SetterDelegate Setter; + + public DynamicFieldCacheEntry(GetterDelegate getter, SetterDelegate setter) + { + Getter = getter; + Setter = setter; + } + } + + /// + /// Obtains cached fieldInfo or creates a new entry, if none is found. + /// + private static DynamicFieldCacheEntry GetOrCreateDynamicField(FieldInfo field) + { + DynamicFieldCacheEntry fieldInfo = (DynamicFieldCacheEntry)fieldCache[field]; + if (fieldInfo == null) + { + fieldInfo = new DynamicFieldCacheEntry(DynamicReflectionManager.CreateFieldGetter(field), DynamicReflectionManager.CreateFieldSetter(field)); + lock (fieldCache) + { + fieldCache[field] = fieldInfo; + } + } + return fieldInfo; + } + + #endregion + + private readonly GetterDelegate getter; + private readonly SetterDelegate setter; + + /// + /// Creates a new instance of the safe field wrapper. + /// + /// Field to wrap. + public SafeField(FieldInfo field) + { + AssertUtils.ArgumentNotNull(field, "You cannot create a dynamic field for a null value."); + + fieldInfo = field; + DynamicFieldCacheEntry fi = GetOrCreateDynamicField(field); + getter = fi.Getter; + setter = fi.Setter; + } + + /// + /// Gets the value of the dynamic field for the specified target object. + /// + /// + /// Target object to get field value from. + /// + /// + /// A field value. + /// + public object GetValue(object target) + { + return getter(target); + } + + /// + /// Gets the value of the dynamic field for the specified target object. + /// + /// + /// Target object to set field value on. + /// + /// + /// A new field value. + /// + public void SetValue(object target, object value) + { + setter(target, value); + } + +#else + /// + /// Returns a implementation + /// by determining the fastest possible dynamic access strategy + /// + /// the field to be wrapped + /// an instance for accessing the + /// field represented by the given + public static IDynamicField CreateFrom(FieldInfo field) + { + AssertUtils.ArgumentNotNull(field, "You cannot create a dynamic field for a null value."); + + IDynamicField dynamicField; + + if (field.IsPublic && + ReflectionUtils.IsTypeVisible(field.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME)) + { + dynamicField = DynamicField.Create(field); + } + else + { + dynamicField = new SafeField(field); + } + + return dynamicField; + } + + private readonly IDynamicField dynamicField; + private readonly bool isOptimizedGet = false; + private bool isOptimizedSet = false; + private readonly bool canSet; + + /// + /// Creates a new instance of the safe field wrapper. + /// + /// Field to wrap. + public SafeField(FieldInfo field) + { + AssertUtils.ArgumentNotNull(field, "You cannot create a dynamic field for a null value."); + + this.fieldInfo = field; + this.canSet = (!fieldInfo.IsLiteral + && !fieldInfo.IsInitOnly + && !(fieldInfo.DeclaringType.IsValueType && !fieldInfo.IsStatic)); + + if (fieldInfo.IsPublic && + ReflectionUtils.IsTypeVisible(fieldInfo.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME)) + { + dynamicField = DynamicField.Create(fieldInfo); + isOptimizedGet = isOptimizedSet = true; + } + } + + /// + /// Gets the value of the dynamic field for the specified target object. + /// + /// + /// Target object to get field value from. + /// + /// + /// A field value. + /// + public object GetValue(object target) + { + if (isOptimizedGet) + { + return dynamicField.GetValue(target); + } + else + { + return fieldInfo.GetValue(target); + } + } + + /// + /// Gets the value of the dynamic field for the specified target object. + /// + /// + /// Target object to set field value on. + /// + /// + /// A new field value. + /// + public void SetValue(object target, object value) + { + if (isOptimizedSet) + { + try + { + dynamicField.SetValue(target, value); + } + catch (InvalidCastException) + { + isOptimizedSet = false; + if (!canSet) + { + throw new InvalidOperationException("Cannot set value of a read-only field or a constant."); + } + + fieldInfo.SetValue(target, value); + } + } + else + { + if (!canSet) + { + throw new InvalidOperationException("Cannot set value of a read-only field or a constant."); + } + fieldInfo.SetValue(target, value); + } + } +#endif + internal FieldInfo FieldInfo + { + get { return fieldInfo; } + } + } + + #endregion + +#if NET_2_0 + /// + /// Factory class for dynamic fields. + /// + /// Aleksandar Seovic + /// $Id: DynamicField.cs,v 1.4 2008/05/16 10:02:39 oakinger Exp $ + public class DynamicField : BaseDynamicMember + { + /// + /// Creates dynamic field instance for the specified . + /// + /// Field info to create dynamic field for. + /// Dynamic field for the specified . + public static IDynamicField Create(FieldInfo field) + { + AssertUtils.ArgumentNotNull(field, "You cannot create a dynamic field for a null value."); + + IDynamicField dynamicField = new SafeField(field); + return dynamicField; + } + } +#else + /// + /// Factory class for dynamic fields. + /// + /// Aleksandar Seovic + /// $Id: DynamicField.cs,v 1.4 2008/05/16 10:02:39 oakinger Exp $ + public class DynamicField : BaseDynamicMember + { + private static readonly CreateFieldCallback s_createCallback = new CreateFieldCallback(CreateInternal); + + #region Create Method + + /// + /// Creates dynamic field instance for the specified . + /// + /// Field info to create dynamic field for. + /// Dynamic field for the specified . + public static IDynamicField Create(FieldInfo field) + { + AssertUtils.ArgumentNotNull(field, "You cannot create a dynamic field for a null value."); + + IDynamicField dynamicField = DynamicReflectionManager.GetDynamicField(field, s_createCallback); + return dynamicField; + } + + private static IDynamicField CreateInternal(FieldInfo field) + { + IDynamicField dynamicField = null; + + TypeBuilder tb = DynamicReflectionManager.CreateTypeBuilder("Field_" + field.Name); + tb.AddInterfaceImplementation(typeof(IDynamicField)); + + GenerateGetValue(tb, field); + GenerateSetValue(tb, field); + + Type dynamicFieldType = tb.CreateType(); + ConstructorInfo ctor = dynamicFieldType.GetConstructor(Type.EmptyTypes); + dynamicField = (IDynamicField)ctor.Invoke(ObjectUtils.EmptyObjects); + + return dynamicField; + } + + private static void GenerateGetValue(TypeBuilder tb, FieldInfo field) + { + MethodBuilder getValueMethod = + tb.DefineMethod("GetValue", METHOD_ATTRIBUTES, typeof(object), new Type[] { typeof(object) }); + getValueMethod.DefineParameter(1, ParameterAttributes.None, "target"); + + ILGenerator il = getValueMethod.GetILGenerator(); + if (field.IsLiteral) + { + EmitConstant(il, field.GetValue(field.FieldType)); + } + else if (field.IsStatic) + { + il.Emit(OpCodes.Ldsfld, field); + } + else + { + SetupTargetInstance(il, field.DeclaringType); + il.Emit(OpCodes.Ldfld, field); + } + ProcessReturnValue(il, field.FieldType); + il.Emit(OpCodes.Ret); + } + + private static void EmitConstant(ILGenerator il, object value) + { + if (value is String) + { + il.Emit(OpCodes.Ldstr, (string)value); + return; + } + + if (value is bool) + { + if ((bool)value) + { + il.Emit(OpCodes.Ldc_I4_1); + } + else + { + il.Emit(OpCodes.Ldc_I4_0); + } + return; + } + + if (value is Char) + { + il.Emit(OpCodes.Ldc_I4, (Char)value); + il.Emit(OpCodes.Conv_I2); + return; + } + + if (value is byte) + { + il.Emit(OpCodes.Ldc_I4, (byte)value); + il.Emit(OpCodes.Conv_I1); + } + else if (value is Int16) + { + il.Emit(OpCodes.Ldc_I4, (Int16)value); + il.Emit(OpCodes.Conv_I2); + } + else if (value is Int32) + { + il.Emit(OpCodes.Ldc_I4, (Int32)value); + } + else if (value is Int64) + { + il.Emit(OpCodes.Ldc_I8, (Int64)value); + } + else if (value is UInt16) + { + il.Emit(OpCodes.Ldc_I4, (UInt16)value); + il.Emit(OpCodes.Conv_U2); + } + else if (value is UInt32) + { + il.Emit(OpCodes.Ldc_I4, (UInt32)value); + il.Emit(OpCodes.Conv_U4); + } + else if (value is UInt64) + { + il.Emit(OpCodes.Ldc_I8, (UInt64)value); + il.Emit(OpCodes.Conv_U8); + } + else if (value is Single) + { + il.Emit(OpCodes.Ldc_R4, (Single)value); + } + else if (value is Double) + { + il.Emit(OpCodes.Ldc_R8, (Double)value); + } + } + + private static void GenerateSetValue(TypeBuilder tb, FieldInfo field) + { + MethodBuilder setValueMethod = + tb.DefineMethod("SetValue", METHOD_ATTRIBUTES, typeof(void), + new Type[] { typeof(object), typeof(object) }); + setValueMethod.DefineParameter(1, ParameterAttributes.None, "target"); + setValueMethod.DefineParameter(2, ParameterAttributes.None, "value"); + + ILGenerator il = setValueMethod.GetILGenerator(); + if (!(field.IsInitOnly || field.IsLiteral)) + { + bool isValueType = field.DeclaringType.IsValueType; + if (isValueType && !field.IsStatic) + { + ThrowInvalidOperationException(il, "Cannot set field value on a value type due to boxing."); + } + else + { + bool isStatic = field.IsStatic; + + if (!isStatic) + { + SetupTargetInstance(il, field.DeclaringType); + } + + SetupArgument(il, field.FieldType, 2); + if (!isStatic) + { + il.Emit(OpCodes.Stfld, field); + } + else + { + il.Emit(OpCodes.Stsfld, field); + } + il.Emit(OpCodes.Ret); + } + } + else + { + ThrowInvalidOperationException(il, "Cannot set value of a read-only field or a constant."); + } + } + + #endregion + } +#endif // NET_2_0 +} + diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicIndexer.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicIndexer.cs new file mode 100644 index 00000000..f55f68c4 --- /dev/null +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicIndexer.cs @@ -0,0 +1,519 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; +using Spring.Util; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + #region IDynamicIndexer interface + + /// + /// Defines methods that dynamic indexer class has to implement. + /// + public interface IDynamicIndexer + { + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get the indexer value from. + /// + /// + /// Indexer argument. + /// + /// + /// A indexer value. + /// + object GetValue(object target, int index); + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get the indexer value from. + /// + /// + /// Indexer argument. + /// + /// + /// A indexer value. + /// + object GetValue(object target, object index); + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get the indexer value from. + /// + /// + /// Indexer arguments. + /// + /// + /// A indexer value. + /// + object GetValue(object target, object[] index); + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set the indexer value on. + /// + /// + /// Indexer argument. + /// + /// + /// A new indexer value. + /// + void SetValue(object target, int index, object value); + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set the indexer value on. + /// + /// + /// Indexer argument. + /// + /// + /// A new indexer value. + /// + void SetValue(object target, object index, object value); + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set the indexer value on. + /// + /// + /// Indexer arguments. + /// + /// + /// A new indexer value. + /// + void SetValue(object target, object[] index, object value); + } + + #endregion + + #region Safe wrapper + + /// + /// Safe wrapper for the dynamic indexer. + /// + /// + /// will attempt to use dynamic + /// indexer if possible, but it will fall back to standard + /// reflection if necessary. + /// + public class SafeIndexer : IDynamicIndexer + { + private PropertyInfo indexerProperty; + private IDynamicIndexer dynamicIndexer; + private bool isOptimizedGet = false; + private bool isOptimizedSet = false; + + /// + /// Creates a new instance of the safe indexer wrapper. + /// + /// Indexer to wrap. + public SafeIndexer(PropertyInfo indexer) + { + this.indexerProperty = indexer; + + if (indexer.CanRead + && indexer.GetGetMethod() != null + && ReflectionUtils.IsTypeVisible(indexer.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME)) + { + dynamicIndexer = DynamicIndexer.Create(indexer); + isOptimizedGet = true; + } + if (indexer.CanWrite + && indexer.GetSetMethod() != null + && ReflectionUtils.IsTypeVisible(indexer.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME)) + { + if (dynamicIndexer == null) + { + dynamicIndexer = DynamicIndexer.Create(indexer); + } + isOptimizedSet = true; + } + } + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get indexer value from. + /// + /// + /// Indexer arguments. + /// + /// + /// A indexer value. + /// + public object GetValue(object target, int index) + { + if (isOptimizedGet) + { + return dynamicIndexer.GetValue(target, index); + } + else + { + return indexerProperty.GetValue(target, new object[] { index }); + } + } + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get the indexer value from. + /// + /// + /// Indexer argument. + /// + /// + /// A indexer value. + /// + public object GetValue(object target, object index) + { + if (isOptimizedGet) + { + return dynamicIndexer.GetValue(target, index); + } + else + { + return indexerProperty.GetValue(target, new object[] { index }); + } + } + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get indexer value from. + /// + /// + /// Indexer arguments. + /// + /// + /// A indexer value. + /// + public object GetValue(object target, object[] index) + { + if (isOptimizedGet) + { + return dynamicIndexer.GetValue(target, index); + } + else + { + return indexerProperty.GetValue(target, index); + } + } + + /// + /// Sets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set indexer value on. + /// + /// + /// Indexer arguments. + /// + /// + /// A new indexer value. + /// + public void SetValue(object target, int index, object value) + { + if (isOptimizedSet) + { + try + { + dynamicIndexer.SetValue(target, index, value); + } + catch (InvalidCastException) + { + isOptimizedSet = false; + indexerProperty.SetValue(target, value, new object[] { index }); + } + } + else + { + indexerProperty.SetValue(target, value, new object[] { index }); + } + } + + /// + /// Sets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set indexer value on. + /// + /// + /// Indexer arguments. + /// + /// + /// A new indexer value. + /// + public void SetValue(object target, object index, object value) + { + if (isOptimizedSet) + { + try + { + dynamicIndexer.SetValue(target, index, value); + } + catch (InvalidCastException) + { + isOptimizedSet = false; + indexerProperty.SetValue(target, value, new object[] { index }); + } + } + else + { + indexerProperty.SetValue(target, value, new object[] { index }); + } + } + + /// + /// Sets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set indexer value on. + /// + /// + /// Indexer arguments. + /// + /// + /// A new indexer value. + /// + public void SetValue(object target, object[] index, object value) + { + if (isOptimizedSet) + { + try + { + dynamicIndexer.SetValue(target, index, value); + } + catch (InvalidCastException) + { + isOptimizedSet = false; + indexerProperty.SetValue(target, value, index); + } + } + else + { + indexerProperty.SetValue(target, value, index); + } + } + + /// + /// Internal PropertyInfo accessor. + /// + internal PropertyInfo IndexerProperty + { + get { return indexerProperty; } + } + } + + #endregion + + /// + /// Factory class for dynamic indexers. + /// + /// Aleksandar Seovic + /// $Id: DynamicIndexer.cs,v 1.1 2007/08/08 04:05:37 bbaia Exp $ + public class DynamicIndexer : BaseDynamicMember + { + private static readonly CreateIndexerCallback s_createCallback = new CreateIndexerCallback(CreateInternal); + + #region Create Method + + /// + /// Creates dynamic indexer instance for the specified . + /// + /// Indexer info to create dynamic indexer for. + /// Dynamic indexer for the specified . + public static IDynamicIndexer Create(PropertyInfo indexer) + { + AssertUtils.ArgumentNotNull(indexer, "You cannot create a dynamic indexer for a null value."); + + IDynamicIndexer dynamicIndexer = DynamicReflectionManager.GetDynamicIndexer(indexer, s_createCallback); + return dynamicIndexer; + } + + private static IDynamicIndexer CreateInternal(PropertyInfo indexer) + { + IDynamicIndexer dynamicIndexer = null; + + TypeBuilder tb = DynamicReflectionManager.CreateTypeBuilder("Indexer_" + indexer.DeclaringType.Name); + tb.AddInterfaceImplementation(typeof(IDynamicIndexer)); + + GenerateGetValue(tb, indexer, typeof(int)); + GenerateGetValue(tb, indexer, typeof(object)); + GenerateGetValue(tb, indexer, typeof(object[])); + GenerateSetValue(tb, indexer, typeof(int)); + GenerateSetValue(tb, indexer, typeof(object)); + GenerateSetValue(tb, indexer, typeof(object[])); + + Type dynamicIndexerType = tb.CreateType(); + ConstructorInfo ctor = dynamicIndexerType.GetConstructor(Type.EmptyTypes); + dynamicIndexer = (IDynamicIndexer) ctor.Invoke(ObjectUtils.EmptyObjects); + + return dynamicIndexer; + } + + private static void GenerateGetValue(TypeBuilder tb, PropertyInfo indexer, Type indexType) + { + MethodBuilder getValueMethod = + tb.DefineMethod("GetValue", METHOD_ATTRIBUTES, typeof(object), new Type[] { typeof(object), indexType }); + getValueMethod.DefineParameter(1, ParameterAttributes.None, "target"); + getValueMethod.DefineParameter(2, ParameterAttributes.None, "index"); + + ILGenerator il = getValueMethod.GetILGenerator(); + if (indexer.CanRead) + { + MethodInfo getMethod = indexer.GetGetMethod(); + bool isValueType = indexer.DeclaringType.IsValueType; + bool isStatic = getMethod.IsStatic; + + if (!isStatic) + { + SetupTargetInstance(il, indexer.DeclaringType); + } + + if (indexType != typeof(object) && indexType != typeof(object[])) + { + il.Emit(OpCodes.Ldarg_2); + } + else if (indexType == typeof(object)) + { + Type argumentType = ReflectionUtils.GetParameterTypes(getMethod)[0]; + SetupIndexerArgument(il, argumentType, 0, false); + } + else + { + Type[] argTypes = ReflectionUtils.GetParameterTypes(getMethod); + for (int i = 0; i < argTypes.Length; i++) + { + SetupIndexerArgument(il, argTypes[i], i, true); + } + } + InvokeMethod(il, isStatic, isValueType, getMethod); + ProcessReturnValue(il, indexer.PropertyType); + il.Emit(OpCodes.Ret); + } + else + { + ThrowInvalidOperationException(il, "Cannot get value of a non-readable indexer"); + } + } + + private static void GenerateSetValue(TypeBuilder tb, PropertyInfo indexer, Type indexType) + { + MethodBuilder setValueMethod = + tb.DefineMethod("SetValue", METHOD_ATTRIBUTES, typeof(void), + new Type[] { typeof(object), indexType, typeof(object) }); + setValueMethod.DefineParameter(1, ParameterAttributes.None, "target"); + setValueMethod.DefineParameter(2, ParameterAttributes.None, "value"); + + ILGenerator il = setValueMethod.GetILGenerator(); + if (indexer.CanWrite) + { + bool isValueType = indexer.DeclaringType.IsValueType; + if (isValueType) + { + ThrowInvalidOperationException(il, "Cannot set indexer value on a value type due to boxing."); + } + else + { + MethodInfo setMethod = indexer.GetSetMethod(); + bool isStatic = setMethod.IsStatic; + + if (!isStatic) + { + SetupTargetInstance(il, indexer.DeclaringType); + } + + if (indexType != typeof(object) && indexType != typeof(object[])) + { + il.Emit(OpCodes.Ldarg_2); + } + else if (indexType == typeof(object)) + { + Type argumentType = ReflectionUtils.GetParameterTypes(setMethod)[0]; + SetupIndexerArgument(il, argumentType, 0, false); + } + else + { + Type[] argTypes = ReflectionUtils.GetParameterTypes(setMethod); + for (int i = 0; i < argTypes.Length - 1; i++) + { + SetupIndexerArgument(il, argTypes[i], i, true); + } + } + SetupArgument(il, indexer.PropertyType, 3); + InvokeMethod(il, isStatic, isValueType, setMethod); + il.Emit(OpCodes.Ret); + } + } + else + { + ThrowInvalidOperationException(il, "Cannot set value of a read-only indexer"); + } + } + + private static void SetupIndexerArgument(ILGenerator il, Type argumentType, int argumentPosition, bool isObjectArray) + { + il.Emit(OpCodes.Ldarg_2); + if (isObjectArray) + { + il.Emit(OpCodes.Ldc_I4, argumentPosition); + il.Emit(OpCodes.Ldelem_Ref); + } + if (argumentType.IsValueType) + { +#if NET_2_0 + il.Emit(OpCodes.Unbox_Any, argumentType); +#else + il.Emit(OpCodes.Unbox, argumentType); + il.Emit(OpCodes.Ldobj, argumentType); +#endif + } + else + { + il.Emit(OpCodes.Castclass, argumentType); + } + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs new file mode 100644 index 00000000..ee58a323 --- /dev/null +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs @@ -0,0 +1,291 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using Spring.Util; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + #region IDynamicMethod interface + + /// + /// Defines methods that dynamic method class has to implement. + /// + public interface IDynamicMethod + { + /// + /// Invokes dynamic method on the specified target object. + /// + /// + /// Target object to invoke method on. + /// + /// + /// Method arguments. + /// + /// + /// A method return value. + /// + object Invoke(object target, object[] arguments); + } + + #endregion + + #region Safe wrapper + + /// + /// Safe wrapper for the dynamic method. + /// + /// + /// will attempt to use dynamic + /// method if possible, but it will fall back to standard + /// reflection if necessary. + /// + public class SafeMethod : IDynamicMethod + { + private MethodInfo method; + private IDynamicMethod dynamicMethod; + private bool isOptimized = false; + + /// + /// Creates a new instance of the safe method wrapper. + /// + /// Method to wrap. + public SafeMethod(MethodInfo method) + { + this.method = method; + if (method.IsPublic && + ReflectionUtils.IsTypeVisible(method.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME)) + { + this.dynamicMethod = DynamicMethod.Create(method); + this.isOptimized = true; + } + } + + /// + /// Gets the class, that declares this method + /// + public Type DeclaringType + { + get { return method.DeclaringType; } + } + + /// + /// Invokes dynamic method. + /// + /// + /// Target object to invoke method on. + /// + /// + /// Method arguments. + /// + /// + /// A method return value. + /// + public object Invoke(object target, object[] arguments) + { + if (isOptimized) + { + // try dynamic method first but fall back to standard reflection + // if arguments are causing InvalidCastExceptions + try + { + return dynamicMethod.Invoke(target, arguments); + } + catch (InvalidCastException e) + { + // Only attempt if DynamicReflection code itself threw the exception. + if (!ExceptionFromDynamicReflection(e)) + { + throw; + } + isOptimized = false; + return method.Invoke(target, arguments); + } + } + else + { + return method.Invoke(target, arguments); + } + } + + private bool ExceptionFromDynamicReflection(InvalidCastException e) + { + return e.TargetSite.DeclaringType.FullName.IndexOf(DynamicReflectionManager.ASSEMBLY_NAME) >= 0; + } + } + + #endregion + + /// + /// Factory class for dynamic methods. + /// + /// Aleksandar Seovic + /// $Id: DynamicMethod.cs,v 1.2 2008/03/06 20:20:37 oakinger Exp $ + public class DynamicMethod : BaseDynamicMember + { + private static readonly CreateMethodCallback s_createMethodCallback = new CreateMethodCallback(CreateInternal); + + #region Create Method + + /// + /// Creates dynamic method instance for the specified . + /// + /// Method info to create dynamic method for. + /// Dynamic method for the specified . + public static IDynamicMethod Create(MethodInfo method) + { + AssertUtils.ArgumentNotNull(method, "You cannot create a dynamic method for a null value."); + + IDynamicMethod dynamicMethod = DynamicReflectionManager.GetDynamicMethod(method, s_createMethodCallback); + return dynamicMethod; + } + + private static IDynamicMethod CreateInternal(MethodInfo method) + { + IDynamicMethod dynamicMethod = null; + + TypeBuilder tb = DynamicReflectionManager.CreateTypeBuilder("Method_" + method.Name); + tb.AddInterfaceImplementation(typeof(IDynamicMethod)); + + GenerateInvoke(tb, method); + + Type dynamicMethodType = tb.CreateType(); + ConstructorInfo ctor = dynamicMethodType.GetConstructor(Type.EmptyTypes); + dynamicMethod = (IDynamicMethod) ctor.Invoke(ObjectUtils.EmptyObjects); + + return dynamicMethod; + } + + private static void GenerateInvoke(TypeBuilder tb, MethodInfo method) + { + MethodBuilder invokeMethod = + tb.DefineMethod("Invoke", METHOD_ATTRIBUTES, typeof(object), new Type[] {typeof(object), typeof(object[])}); + invokeMethod.DefineParameter(1, ParameterAttributes.None, "target"); + invokeMethod.DefineParameter(2, ParameterAttributes.None, "args"); + + ILGenerator il = invokeMethod.GetILGenerator(); + bool isValueType = method.DeclaringType.IsValueType; + bool isStatic = method.IsStatic; + + IDictionary outArgs = new Hashtable(); + ParameterInfo[] args = method.GetParameters(); + for (int i = 0; i < args.Length; i++) + { + if (IsOutputOrRefArgument(args[i])) + { + SetupOutputArgument(il, args[i], outArgs); + } + } + + if (!isStatic) + { + SetupTargetInstance(il, method.DeclaringType); + } + + for (int i = 0; i < args.Length; i++) + { + SetupMethodArgument(il, args[i], outArgs); + } + InvokeMethod(il, isStatic, isValueType, method); + for (int i = 0; i < args.Length; i++) + { + if (IsOutputOrRefArgument(args[i])) + { + ProcessOutputArgument(il, args[i], outArgs); + } + } + ProcessReturnValue(il, method.ReturnType); + il.Emit(OpCodes.Ret); + } + + private static bool IsOutputOrRefArgument(ParameterInfo argInfo) + { + return argInfo.IsOut || argInfo.ParameterType.Name.EndsWith("&"); + } + + private static void SetupOutputArgument(ILGenerator il, ParameterInfo argInfo, IDictionary outArgs) + { + Type argType = argInfo.ParameterType.GetElementType(); + + LocalBuilder lb = il.DeclareLocal(argType); + if (!argInfo.IsOut) + { + PushArgumentValue(il, argType, argInfo.Position); + il.Emit(OpCodes.Stloc, lb); + } + outArgs[argInfo.Position] = lb; + } + + private static void ProcessOutputArgument(ILGenerator il, ParameterInfo argInfo, IDictionary outArgs) + { + Type argType = argInfo.ParameterType.GetElementType(); + + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Ldc_I4, argInfo.Position); + il.Emit(OpCodes.Ldloc, (LocalBuilder)outArgs[argInfo.Position]); + if (argType.IsValueType) + { + il.Emit(OpCodes.Box, argType); + } + il.Emit(OpCodes.Stelem_Ref); + } + + private static void SetupMethodArgument(ILGenerator il, ParameterInfo argInfo, IDictionary outArgs) + { + if (IsOutputOrRefArgument(argInfo)) + { + il.Emit(OpCodes.Ldloca_S, (LocalBuilder)outArgs[argInfo.Position]); + } + else + { + PushArgumentValue(il, argInfo.ParameterType, argInfo.Position); + } + } + + private static void PushArgumentValue(ILGenerator il, Type argumentType, int argumentPosition) + { + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Ldc_I4, argumentPosition); + il.Emit(OpCodes.Ldelem_Ref); + if (argumentType.IsValueType) + { +#if NET_2_0 + il.Emit(OpCodes.Unbox_Any, argumentType); +#else + il.Emit(OpCodes.Unbox, argumentType); + il.Emit(OpCodes.Ldobj, argumentType); +#endif + } + else + { + il.Emit(OpCodes.Castclass, argumentType); + } + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicProperty.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicProperty.cs new file mode 100644 index 00000000..be833b55 --- /dev/null +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicProperty.cs @@ -0,0 +1,449 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +using Common.Logging; +using Spring.Util; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + #region IDynamicProperty interface + + /// + /// Defines methods that dynamic property class has to implement. + /// + public interface IDynamicProperty + { + /// + /// Gets the value of the dynamic property for the specified target object. + /// + /// + /// Target object to get property value from. + /// + /// + /// A property value. + /// + object GetValue(object target); + + /// + /// Gets the value of the dynamic property for the specified target object. + /// + /// + /// Target object to set property value on. + /// + /// + /// A new property value. + /// + void SetValue(object target, object value); + } + + #endregion + + #region Safe wrapper + + /// + /// Safe wrapper for the dynamic property. + /// + /// + /// will attempt to use dynamic + /// property if possible, but it will fall back to standard + /// reflection if necessary. + /// + public class SafeProperty : IDynamicProperty + { + private static readonly ILog Log = LogManager.GetLogger(typeof(SafeProperty)); + + private readonly PropertyInfo propertyInfo; + +#if NET_2_0 + + #region Cache + + private static readonly IDictionary propertyCache = new Hashtable(); + + /// + /// Holds cached Getter/Setter delegates for a Property + /// + private class DynamicPropertyCacheEntry + { + public readonly GetterDelegate Getter; + public readonly SetterDelegate Setter; + + public DynamicPropertyCacheEntry(GetterDelegate getter, SetterDelegate setter) + { + Getter = getter; + Setter = setter; + } + } + + /// + /// Obtains cached property info or creates a new entry, if none is found. + /// + private static DynamicPropertyCacheEntry GetOrCreateDynamicProperty(PropertyInfo property) + { + DynamicPropertyCacheEntry propertyInfo = (DynamicPropertyCacheEntry)propertyCache[property]; + if (propertyInfo == null) + { + propertyInfo = new DynamicPropertyCacheEntry(DynamicReflectionManager.CreatePropertyGetter(property), DynamicReflectionManager.CreatePropertySetter(property)); + lock (propertyCache) + { + propertyCache[property] = propertyInfo; + } + } + return propertyInfo; + } + + #endregion + + private readonly GetterDelegate getter; + private readonly SetterDelegate setter; + + /// + /// Creates a new instance of the safe property wrapper. + /// + /// Property to wrap. + public SafeProperty(PropertyInfo propertyInfo) + { + AssertUtils.ArgumentNotNull(propertyInfo, "You cannot create a dynamic property for a null value."); + + this.propertyInfo = propertyInfo; + DynamicPropertyCacheEntry pi = GetOrCreateDynamicProperty(propertyInfo); + getter = pi.Getter; + setter = pi.Setter; + } + + /// + /// Gets the value of the dynamic property for the specified target object. + /// + /// + /// Target object to get property value from. + /// + /// + /// A property value. + /// + public object GetValue(object target) + { + return getter(target); + } + + /// + /// Gets the value of the dynamic property for the specified target object. + /// + /// + /// Target object to set property value on. + /// + /// + /// A new property value. + /// + public void SetValue(object target, object value) + { + setter(target, value); + } + +#else + private readonly IDynamicProperty dynamicProperty; + private readonly bool isOptimizedGet = false; + private bool isOptimizedSet = false; + private readonly bool canSet; + + /// + /// Creates a new instance of the safe property wrapper. + /// + /// Property to wrap. + public SafeProperty(PropertyInfo property) + { + this.propertyInfo = property; + + if (property.CanRead + && property.GetGetMethod() != null + && ReflectionUtils.IsTypeVisible(property.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME)) + { + dynamicProperty = DynamicProperty.Create(property); + isOptimizedGet = true; + } + + canSet = property.CanWrite && !(propertyInfo.DeclaringType.IsValueType && !propertyInfo.GetSetMethod(true).IsStatic); + if (property.GetSetMethod() != null + && ReflectionUtils.IsTypeVisible(property.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME)) + { + if (dynamicProperty == null) + { + dynamicProperty = DynamicProperty.Create(property); + } + isOptimizedSet = true; + } + } + + /// + /// Gets the value of the dynamic property for the specified target object. + /// + /// + /// Target object to get property value from. + /// + /// + /// A property value. + /// + public object GetValue(object target) + { + if (isOptimizedGet) + { + return dynamicProperty.GetValue(target); + } + else + { + return propertyInfo.GetValue(target, null); + } + } + + /// + /// Gets the value of the dynamic property for the specified target object. + /// + /// + /// Target object to set property value on. + /// + /// + /// A new property value. + /// + public void SetValue(object target, object value) + { + try + { + if (isOptimizedSet) + { + try + { + dynamicProperty.SetValue(target, value); + } + catch (InvalidCastException) + { + isOptimizedSet = false; + propertyInfo.SetValue(target, value, null); + } + } + else + { + if (!canSet) + { + throw (propertyInfo.DeclaringType.IsValueType) + ? new InvalidOperationException("Cannot set property value on a value type due to boxing.") + : new InvalidOperationException("Cannot set value of a read-only property."); + } + + propertyInfo.SetValue(target, value, null); + } + } + catch (Exception ex) + { + Log.Error( + string.Format("Failed setting value '{0}' on property '{2}.{1}' on instance of type '{3}'", + (value != null ? value.GetType().FullName : ""), + propertyInfo.Name, + propertyInfo.DeclaringType.FullName, + (target != null ? target.GetType().FullName : "")), ex); + throw; + } + } +#endif + /// + /// Internal PropertyInfo accessor. + /// + internal PropertyInfo PropertyInfo + { + get { return propertyInfo; } + } + } + + #endregion + +#if NET_2_0 + /// + /// Factory class for dynamic properties. + /// + /// Aleksandar Seovic + /// $Id: DynamicProperty.cs,v 1.3 2008/05/17 11:05:26 oakinger Exp $ + public class DynamicProperty : BaseDynamicMember + { + /// + /// Creates safe dynamic property instance for the specified . + /// + /// + ///

    This factory method will create a dynamic property with a "safe" wrapper.

    + ///

    Safe wrapper will attempt to use generated dynamic property if possible, + /// but it will fall back to standard reflection if necessary.

    + ///
    + /// Property info to create dynamic property for. + /// Safe dynamic property for the specified . + /// + public static IDynamicProperty CreateSafe(PropertyInfo property) + { + return new SafeProperty(property); + } + + /// + /// Creates dynamic property instance for the specified . + /// + /// Property info to create dynamic property for. + /// Dynamic property for the specified . + public static IDynamicProperty Create(PropertyInfo property) + { + AssertUtils.ArgumentNotNull(property, "You cannot create a dynamic property for a null value."); + + IDynamicProperty dynamicProperty = new SafeProperty(property); + return dynamicProperty; + } + } +#else + /// + /// Factory class for dynamic properties. + /// + /// Aleksandar Seovic + /// $Id: DynamicProperty.cs,v 1.3 2008/05/17 11:05:26 oakinger Exp $ + public class DynamicProperty : BaseDynamicMember + { + private readonly static CreatePropertyCallback s_createPropertyCallback = new CreatePropertyCallback(CreateInternal); + + #region Create Method + + /// + /// Creates safe dynamic property instance for the specified . + /// + /// + ///

    This factory method will create a dynamic property with a "safe" wrapper.

    + ///

    Safe wrapper will attempt to use generated dynamic property if possible, + /// but it will fall back to standard reflection if necessary.

    + ///
    + /// Property info to create dynamic property for. + /// Safe dynamic property for the specified . + /// + public static IDynamicProperty CreateSafe(PropertyInfo property) + { + return new SafeProperty(property); + } + + /// + /// Creates dynamic property instance for the specified . + /// + /// Property info to create dynamic property for. + /// Dynamic property for the specified . + public static IDynamicProperty Create(PropertyInfo property) + { + AssertUtils.ArgumentNotNull(property, "You cannot create a dynamic property for a null value."); + + IDynamicProperty dynamicProperty = DynamicReflectionManager.GetDynamicProperty(property, s_createPropertyCallback); + return dynamicProperty; + } + + private static IDynamicProperty CreateInternal(PropertyInfo property) + { + IDynamicProperty dynamicProperty = null; + + TypeBuilder tb = DynamicReflectionManager.CreateTypeBuilder("Property_" + property.Name); + tb.AddInterfaceImplementation(typeof(IDynamicProperty)); + + GenerateGetValue(tb, property); + GenerateSetValue(tb, property); + + Type dynamicPropertyType = tb.CreateType(); + ConstructorInfo ctor = dynamicPropertyType.GetConstructor(Type.EmptyTypes); + dynamicProperty = (IDynamicProperty)ctor.Invoke(ObjectUtils.EmptyObjects); + + return dynamicProperty; + } + + private static void GenerateGetValue(TypeBuilder tb, PropertyInfo property) + { + MethodBuilder getValueMethod = + tb.DefineMethod("GetValue", METHOD_ATTRIBUTES, typeof(object), new Type[] { typeof(object) }); + getValueMethod.DefineParameter(1, ParameterAttributes.None, "target"); + + ILGenerator il = getValueMethod.GetILGenerator(); + if (property.CanRead) + { + MethodInfo getMethod = property.GetGetMethod(true); + bool isValueType = property.DeclaringType.IsValueType; + bool isStatic = getMethod.IsStatic; + + if (!isStatic) + { + SetupTargetInstance(il, property.DeclaringType); + } + + InvokeMethod(il, isStatic, isValueType, getMethod); + ProcessReturnValue(il, property.PropertyType); + il.Emit(OpCodes.Ret); + } + else + { + ThrowInvalidOperationException(il, "Cannot get value of a non-readable property"); + } + } + + private static void GenerateSetValue(TypeBuilder tb, PropertyInfo property) + { + MethodBuilder setValueMethod = + tb.DefineMethod("SetValue", METHOD_ATTRIBUTES, typeof(void), + new Type[] { typeof(object), typeof(object) }); + setValueMethod.DefineParameter(1, ParameterAttributes.None, "target"); + setValueMethod.DefineParameter(2, ParameterAttributes.None, "value"); + + ILGenerator il = setValueMethod.GetILGenerator(); + if (property.CanWrite) + { + bool isValueType = property.DeclaringType.IsValueType; + // we can't set nonstatic properties on value types (due to boxing) + bool canSet = !(isValueType && !property.GetSetMethod(true).IsStatic); + if (!canSet) + { + ThrowInvalidOperationException(il, "Cannot set property value on a value type due to boxing."); + } + else + { + MethodInfo setMethod = property.GetSetMethod(true); + bool isStatic = setMethod.IsStatic; + + if (!isStatic) + { + SetupTargetInstance(il, property.DeclaringType); + } + + SetupArgument(il, property.PropertyType, 2); + InvokeMethod(il, isStatic, property.DeclaringType.IsValueType, setMethod); + il.Emit(OpCodes.Ret); + } + } + else + { + ThrowInvalidOperationException(il, "Cannot set value of a read-only property"); + } + } + + #endregion + } + +#endif +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs new file mode 100644 index 00000000..4f83b80a --- /dev/null +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs @@ -0,0 +1,557 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using Spring.Util; + +#if NET_2_0 +using NetDynamicMethod = System.Reflection.Emit.DynamicMethod; +#endif + +#endregion + +namespace Spring.Reflection.Dynamic +{ + /// + /// Represents a Get method + /// + /// the target instance when calling an instance method + /// the value return by the Get method + public delegate object GetterDelegate(object target); + + /// + /// Represents a Set method + /// + /// the target instance when calling an instance method + /// the value to be set + public delegate void SetterDelegate(object target, object value); + + /// + /// Represents a method + /// + /// the target instance when calling an instance method + /// arguments to be passed to the method + /// the value return by the method. null when calling a void method + public delegate object FunctionDelegate(object target, params object[] args); + + /// + /// Represents a callback method used to create an from a instance. + /// + internal delegate IDynamicProperty CreatePropertyCallback(PropertyInfo property); + /// + /// Represents a callback method used to create an from a instance. + /// + internal delegate IDynamicField CreateFieldCallback(FieldInfo property); + /// + /// Represents a callback method used to create an from a instance. + /// + internal delegate IDynamicMethod CreateMethodCallback(MethodInfo method); + /// + /// Represents a callback method used to create an from a instance. + /// + internal delegate IDynamicConstructor CreateConstructorCallback(ConstructorInfo constructor); + /// + /// Represents a callback method used to create an from a instance. + /// + internal delegate IDynamicIndexer CreateIndexerCallback(PropertyInfo indexer); + + /// + /// Allows easy access to existing and creation of new dynamic relection members. + /// + /// Aleksandar Seovic + /// $Id: DynamicReflectionManager.cs,v 1.5 2008/05/17 11:05:26 oakinger Exp $ + public sealed class DynamicReflectionManager + { + #region Fields + + /// + /// The name of the assembly that defines reflection types created. + /// + public const string ASSEMBLY_NAME = "Spring.DynamicReflection"; + + /// + /// The attributes of the reflection type to generate. + /// + private const TypeAttributes TYPE_ATTRIBUTES = TypeAttributes.BeforeFieldInit | TypeAttributes.Public; + + /// + /// Cache for dynamic property types. + /// + private readonly static IDictionary propertyCache = new Hashtable(); + + /// + /// Cache for dynamic field types. + /// + private readonly static IDictionary fieldCache = new Hashtable(); + + /// + /// Cache for dynamic indexer types. + /// + private readonly static IDictionary indexerCache = new Hashtable(); + + /// + /// Cache for dynamic method types. + /// + private readonly static IDictionary methodCache = new Hashtable(); + + /// + /// Cache for dynamic constructor types. + /// + private readonly static IDictionary constructorCache = new Hashtable(); + + #endregion + + #region Public Methods + + /// + /// Creates an appropriate type builder. + /// + /// + /// The base name to use for the reflection type name. + /// + /// The type builder to use. + internal static TypeBuilder CreateTypeBuilder(string name) + { + // Generates type name + string typeName = String.Format("{0}.{1}_{2}", + ASSEMBLY_NAME, name, Guid.NewGuid().ToString("N")); + + ModuleBuilder module = DynamicCodeManager.GetModuleBuilder(ASSEMBLY_NAME); + return module.DefineType(typeName, TYPE_ATTRIBUTES); + } + + /// + /// Returns dynamic property if one exists. + /// + /// Property to look up. + /// callback function that will be called to create the dynamic property + /// An for the given property info. + internal static IDynamicProperty GetDynamicProperty(PropertyInfo property, CreatePropertyCallback createCallback) + { + lock (propertyCache.SyncRoot) + { + IDynamicProperty dynamicProperty = (IDynamicProperty)propertyCache[property]; + if (dynamicProperty == null) + { + dynamicProperty = createCallback(property); + propertyCache[property] = dynamicProperty; + } + return dynamicProperty; + } + } + + /// + /// Returns dynamic field if one exists. + /// + /// Field to look up. + /// callback function that will be called to create the dynamic field + /// An for the given field info. + internal static IDynamicField GetDynamicField(FieldInfo field, CreateFieldCallback createCallback) + { + lock (fieldCache.SyncRoot) + { + IDynamicField dynamicField = (IDynamicField)fieldCache[field]; + if (dynamicField == null) + { + dynamicField = createCallback(field); + fieldCache[field] = dynamicField; + } + return dynamicField; + } + } + + /// + /// Returns dynamic indexer if one exists. + /// + /// Indexer to look up. + /// callback function that will be called to create the dynamic indexer + /// An for the given indexer. + internal static IDynamicIndexer GetDynamicIndexer(PropertyInfo indexer, CreateIndexerCallback createCallback) + { + lock (indexerCache.SyncRoot) + { + IDynamicIndexer dynamicIndexer = (IDynamicIndexer)indexerCache[indexer]; + if (dynamicIndexer == null) + { + dynamicIndexer = createCallback(indexer); + indexerCache[indexer] = dynamicIndexer; + } + return dynamicIndexer; + } + } + + /// + /// Returns dynamic method if one exists. + /// + /// Method to look up. + /// callback function that will be called to create the dynamic method + /// An for the given method. + internal static IDynamicMethod GetDynamicMethod(MethodInfo method, CreateMethodCallback createCallback) + { + lock (methodCache.SyncRoot) + { + IDynamicMethod dynamicMethod = (IDynamicMethod)methodCache[method]; + if (dynamicMethod == null) + { + dynamicMethod = createCallback(method); + methodCache[method] = dynamicMethod; + } + return dynamicMethod; + } + } + + /// + /// Returns dynamic constructor if one exists. + /// + /// Constructor to look up. + /// callback function that will be called to create the dynamic constructor + /// An for the given constructor. + internal static IDynamicConstructor GetDynamicConstructor(ConstructorInfo constructor, CreateConstructorCallback createCallback) + { + lock (constructorCache.SyncRoot) + { + IDynamicConstructor dynamicConstructor = (IDynamicConstructor)constructorCache[constructor]; + if (dynamicConstructor == null) + { + dynamicConstructor = createCallback(constructor); + constructorCache[constructor] = dynamicConstructor; + } + return dynamicConstructor; + } + } + + /// + /// Saves dynamically generated assembly to disk. + /// Can only be called in DEBUG mode, per ConditionalAttribute rules. + /// + [Conditional("DEBUG_DYNAMIC")] + public static void SaveAssembly() + { + DynamicCodeManager.SaveAssembly(ASSEMBLY_NAME); + } + + #endregion + +#if NET_2_0 + private static readonly ConstructorInfo NewInvalidOperationException = + typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) }); + + /// + /// Create a new Get method delegate for the specified field using + /// + /// the field to create the delegate for + /// a delegate that can be used to read the field + public static GetterDelegate CreateFieldGetter(FieldInfo fieldInfo) + { + AssertUtils.ArgumentNotNull(fieldInfo, "You cannot create a delegate for a null value."); + + System.Reflection.Emit.DynamicMethod dmGetter = new System.Reflection.Emit.DynamicMethod("getter", typeof(object), new Type[] { typeof(object) }, fieldInfo.DeclaringType.Module, true); + ILGenerator il = dmGetter.GetILGenerator(); + if (fieldInfo.IsLiteral) + { + object value = fieldInfo.GetValue(null); + EmitConstant(il, value); + } + else if (fieldInfo.IsStatic) + { + il.Emit(OpCodes.Ldsfld, fieldInfo); + } + else + { + EmitTarget(il, fieldInfo.DeclaringType); + il.Emit(OpCodes.Ldfld, fieldInfo); + } + + if (fieldInfo.FieldType.IsValueType) + { + il.Emit(OpCodes.Box, fieldInfo.FieldType); + } + il.Emit(OpCodes.Ret); + return (GetterDelegate)dmGetter.CreateDelegate(typeof(GetterDelegate)); + } + + /// + /// Create a new Set method delegate for the specified field using + /// + /// the field to create the delegate for + /// a delegate that can be used to read the field. + /// + /// If the field's returns true, the returned method + /// will throw an when called. + /// + public static SetterDelegate CreateFieldSetter(FieldInfo fieldInfo) + { + AssertUtils.ArgumentNotNull(fieldInfo, "You cannot create a delegate for a null value."); + + System.Reflection.Emit.DynamicMethod dmSetter = new System.Reflection.Emit.DynamicMethod("setter", null, new Type[] { typeof(object), typeof(object) }, fieldInfo.DeclaringType.Module, true); + ILGenerator il = dmSetter.GetILGenerator(); + + if (!fieldInfo.IsLiteral + && !fieldInfo.IsInitOnly + && !(fieldInfo.DeclaringType.IsValueType && !fieldInfo.IsStatic)) + { + if (!fieldInfo.IsStatic) + { + EmitTarget(il, fieldInfo.DeclaringType); + } + il.Emit(OpCodes.Ldarg_1); + if (fieldInfo.FieldType.IsValueType) + { + il.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType); + } + if (fieldInfo.IsStatic) + { + il.Emit(OpCodes.Stsfld, fieldInfo); + } + else + { + il.Emit(OpCodes.Stfld, fieldInfo); + } + il.Emit(OpCodes.Ret); + } + else + { + EmitThrowInvalidOperationException(il, string.Format("Cannot write to constant field '{0}.{1}'", fieldInfo.DeclaringType.FullName, fieldInfo.Name)); + } + return (SetterDelegate)dmSetter.CreateDelegate(typeof(SetterDelegate)); + } + + /// + /// Create a new Get method delegate for the specified property using + /// + /// the property to create the delegate for + /// a delegate that can be used to read the property. + /// + /// If the property's returns false, the returned method + /// will throw an when called. + /// + public static GetterDelegate CreatePropertyGetter(PropertyInfo propertyInfo) + { + AssertUtils.ArgumentNotNull(propertyInfo, "You cannot create a delegate for a null value."); + + NetDynamicMethod dm = new NetDynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object) }, propertyInfo.DeclaringType.Module, true); + ILGenerator il = dm.GetILGenerator(); + + if (propertyInfo.CanRead) + { + MethodInfo method = propertyInfo.GetGetMethod(true); + if (!method.IsStatic) + { + EmitTarget(il, method.DeclaringType); + } + EmitCall(il, method); + if (propertyInfo.PropertyType.IsValueType) + { + il.Emit(OpCodes.Box, propertyInfo.PropertyType); + } + il.Emit(OpCodes.Ret); + } + else + { + EmitThrowInvalidOperationException(il, string.Format("Cannot read from write-only property '{0}.{1}'", propertyInfo.DeclaringType.FullName, propertyInfo.Name)); + } + return (GetterDelegate)dm.CreateDelegate(typeof(GetterDelegate)); + } + + /// + /// Create a new Set method delegate for the specified property using + /// + /// the property to create the delegate for + /// a delegate that can be used to write the property. + /// + /// If the property's returns false, the returned method + /// will throw an when called. + /// + public static SetterDelegate CreatePropertySetter(PropertyInfo propertyInfo) + { + AssertUtils.ArgumentNotNull(propertyInfo, "You cannot create a delegate for a null value."); + + NetDynamicMethod dm = new NetDynamicMethod(string.Empty, null, new Type[] { typeof(object), typeof(object) }, propertyInfo.DeclaringType.Module, true); + ILGenerator il = dm.GetILGenerator(); + + if (propertyInfo.CanWrite + && !(propertyInfo.DeclaringType.IsValueType && !propertyInfo.GetSetMethod(true).IsStatic)) + { + MethodInfo method = propertyInfo.GetSetMethod(true); + if (!method.IsStatic) + { + EmitTarget(il, method.DeclaringType); + } + + il.Emit(OpCodes.Ldarg_1); + if (propertyInfo.PropertyType.IsValueType) + { + il.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); + } + EmitCall(il, method); + il.Emit(OpCodes.Ret); + } + else + { + EmitThrowInvalidOperationException(il, string.Format("Cannot write to read-only property '{0}.{1}'", propertyInfo.DeclaringType.FullName, propertyInfo.Name)); + } + + return (SetterDelegate)dm.CreateDelegate(typeof(SetterDelegate)); + } + + /// + /// Create a new method delegate for the specified method using + /// + /// the method to create the delegate for + /// a delegate that can be used to invoke the method. + private static FunctionDelegate CreateFunctionDelegate(MethodInfo method) + { + NetDynamicMethod dm = new NetDynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, method.DeclaringType.Module, true); + ILGenerator il = dm.GetILGenerator(); + ParameterInfo[] parameters = method.GetParameters(); + for (int i = 0; i < parameters.Length; i++) + { + ParameterInfo p = parameters[i]; + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldc_I4, i); + il.Emit(OpCodes.Ldelem_Ref); + if (p.ParameterType.IsValueType) + { + il.Emit(OpCodes.Unbox_Any, p.ParameterType); + } + } + + if (!method.IsStatic) + { + EmitTarget(il, method.DeclaringType); + } + EmitCall(il, method); + + if (method.ReturnType == typeof(void)) + { + il.Emit(OpCodes.Ldnull); + } + il.Emit(OpCodes.Ret); + return (FunctionDelegate)dm.CreateDelegate(typeof(FunctionDelegate)); + } + + private static void EmitTarget(ILGenerator il, Type targetType) + { + il.Emit(OpCodes.Ldarg_0); + if (targetType.IsValueType) + { + LocalBuilder local = il.DeclareLocal(targetType); + il.Emit(OpCodes.Unbox_Any, targetType); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloca_S, 0); + } + } + + private static void EmitCall(ILGenerator il, MethodInfo method) + { + il.Emit((method.IsVirtual) ? OpCodes.Callvirt : OpCodes.Call, method); + } + + private static void EmitConstant(ILGenerator il, object value) + { + if (value is String) + { + il.Emit(OpCodes.Ldstr, (string)value); + return; + } + + if (value is bool) + { + if ((bool)value) + { + il.Emit(OpCodes.Ldc_I4_1); + } + else + { + il.Emit(OpCodes.Ldc_I4_0); + } + return; + } + + if (value is Char) + { + il.Emit(OpCodes.Ldc_I4, (Char)value); + il.Emit(OpCodes.Conv_I2); + return; + } + + if (value is byte) + { + il.Emit(OpCodes.Ldc_I4, (byte)value); + il.Emit(OpCodes.Conv_I1); + } + else if (value is Int16) + { + il.Emit(OpCodes.Ldc_I4, (Int16)value); + il.Emit(OpCodes.Conv_I2); + } + else if (value is Int32) + { + il.Emit(OpCodes.Ldc_I4, (Int32)value); + } + else if (value is Int64) + { + il.Emit(OpCodes.Ldc_I8, (Int64)value); + } + else if (value is UInt16) + { + il.Emit(OpCodes.Ldc_I4, (UInt16)value); + il.Emit(OpCodes.Conv_U2); + } + else if (value is UInt32) + { + il.Emit(OpCodes.Ldc_I4, (UInt32)value); + il.Emit(OpCodes.Conv_U4); + } + else if (value is UInt64) + { + il.Emit(OpCodes.Ldc_I8, (UInt64)value); + il.Emit(OpCodes.Conv_U8); + } + else if (value is Single) + { + il.Emit(OpCodes.Ldc_R4, (Single)value); + } + else if (value is Double) + { + il.Emit(OpCodes.Ldc_R8, (Double)value); + } + } + + /// + /// Generates code that throws . + /// + /// IL generator to use. + /// Error message to use. + private static void EmitThrowInvalidOperationException(ILGenerator il, string message) + { + il.Emit(OpCodes.Ldstr, message); + il.Emit(OpCodes.Newobj, NewInvalidOperationException); + il.Emit(OpCodes.Throw); + } +#endif + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Resources/Strings.resx b/src/Spring/Spring.Core/Resources/Strings.resx new file mode 100644 index 00000000..88d19390 --- /dev/null +++ b/src/Spring/Spring.Core/Resources/Strings.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Spring/Spring.Core/Spring.Core.2002.csproj b/src/Spring/Spring.Core/Spring.Core.2002.csproj new file mode 100644 index 00000000..16bc2540 --- /dev/null +++ b/src/Spring/Spring.Core/Spring.Core.2002.csproj @@ -0,0 +1,2383 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Core/Spring.Core.2003.csproj b/src/Spring/Spring.Core/Spring.Core.2003.csproj new file mode 100644 index 00000000..ed343a7d --- /dev/null +++ b/src/Spring/Spring.Core/Spring.Core.2003.csproj @@ -0,0 +1,2453 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Core/Spring.Core.2005.csproj b/src/Spring/Spring.Core/Spring.Core.2005.csproj new file mode 100644 index 00000000..79034c4a --- /dev/null +++ b/src/Spring/Spring.Core/Spring.Core.2005.csproj @@ -0,0 +1,1066 @@ + + + Local + 8.0.50727 + 2.0 + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Debug + AnyCPU + + + + + Spring.Core + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + ..\..\..\build\VS.Net.2005\Spring.Core\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0;DEBUG_DYNAMIC + Spring.Core.xml + true + 4096 + false + 219, 162, 618 + false + false + false + false + 4 + full + prompt + 1591 + + + ..\..\..\build\VS.Net.2005\Spring.Core\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\antlr.runtime.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + + + + + + + + + CommonAssemblyInfo.cs + Code + + + Code + + + + + + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + Code + + + + + + + + + + + + + + + + + + + Code + + + Code + + + Code + + + + Code + + + + + + + + + + Code + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + + + Code + + + + Code + + + + Code + + + + + + + + + + + + + + + + + + + + + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + Code + + + + Code + + + + + Code + + + + + + + Code + + + Code + + + Code + + + + + Code + + + + + + + Code + + + Code + + + + Designer + + + Designer + + + + spring-validation-1.1.xsd + + + Designer + + + Designer + + + + + + + + rem $(ProjectDir)\..\..\..\tools\antlr-2.7.6\antlr-2.7.6.exe -o $(ProjectDir)\Expressions\Parser $(ProjectDir)\Expressions\Expression.g + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Core/Spring.Core.build b/src/Spring/Spring.Core/Spring.Core.build new file mode 100644 index 00000000..e6c26c8f --- /dev/null +++ b/src/Spring/Spring.Core/Spring.Core.build @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Core/Spring.Core.xml b/src/Spring/Spring.Core/Spring.Core.xml new file mode 100644 index 00000000..2ded4f5d --- /dev/null +++ b/src/Spring/Spring.Core/Spring.Core.xml @@ -0,0 +1,42722 @@ + + + + Spring.Core + + + + + Validates that required value is not empty. + + +

    + This validator uses following rules to determine if target value is valid: + + + + + + + + + + + + + + + + + + + + + + + + + +
    Target Valid Value
    A .Not or an empty string.
    A .Not and not .
    One of the number types.Not zero.
    A .Not or whitespace.
    Any reference type other than .Not .
    +

    +

    + You cannot use this validator to validate any value types other than the ones + specified in the table above. +

    +
    + Aleksandar Seovic + $Id: RequiredValidator.cs,v 1.8 2008/03/21 14:12:02 markpollack Exp $ +
    + + + Base class that defines common properties for all validators. + + +

    + Custom validators should always extend this class instead of + simply implementing interface, in + order to inherit common validator functionality. +

    +
    + Aleksandar Seovic + $Id: BaseValidator.cs,v 1.12 2008/02/05 20:40:26 aseovic Exp $ +
    + + + An object that can validate application-specific objects. + + +

    + The primary motivation for this interface is to enable validation to be + decoupled from the (user) interface and placed in business objects. +

    +

    + Application developers writing their own custom + implementations will + typically not implement this interface directly. In most cases, custom + validators woud be better served deriving from the + class, with the + custom validation ligic being implemented in an override of the + + + template method. +

    +
    + Aleksandar Seovic + $Id: IValidator.cs,v 1.8 2008/02/05 20:40:26 aseovic Exp $ + +
    + + + Validates the specified object. + + The object to validate. + + The instance to add any error + messages to in the case of validation failure. + + + if validation was successful. + + + + + Validates the specified object. + + The object to validate. + Additional context parameters. + + The instance to add any error + messages to in the case of validation failure. + + + if validation was successful. + + + + + Creates a new instance of the class. + + + + + Creates a new instance of the class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Creates a new instance of the class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Validates the specified object. + + The object to validate. + instance to add error messages to. + True if validation was successful, False otherwise. + + + + Validates the specified object. + + The object to validate. + Additional context parameters. + instance to add error messages to. + True if validation was successful, False otherwise. + + + + Validates test object. + + Object to validate. + True if specified object is valid, False otherwise. + + + + Evaluates test expression. + + Root context to use for expression evaluation. + Additional context parameters. + Result of the test expression evaluation, or validation context if test is null. + + + + Evaluates when expression. + + Root context to use for expression evaluation. + Additional context parameters. + True if the condition is true, False otherwise. + + + + Processes the error messages. + + Whether validator is valid or not. + Validation context. + Additional context parameters. + Validation errors container. + + + + Gets or sets the test expression. + + The test expression. + + + + Gets or sets the expression that determines if this validator should be evaluated. + + The expression that determines if this validator should be evaluated. + + + + Gets or sets the validation actions. + + The actions that should be executed after validation. + + + + Creates a new instance of the class. + + + + + Creates a new instance of the class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Creates a new instance of the class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Validates the supplied . + + + In the case of the class, + the test should be a variable expression that will be evaluated and the object + obtained as a result of this evaluation will be tested using the rules described + in the class overview of the + class. + + The object to validate. + + if the supplied is valid. + + + + + implementation that supports grouping of validators. + + +

    + This validator will be valid when one or more of the validators in the Validators + collection are valid. +

    +

    + ValidationErrors property will return a union of all validation error messages + for the contained validators, but only if this validator is not valid (meaning, when none + of the contained validators are valid). +

    +
    + Aleksandar Seovic + $Id: AnyValidatorGroup.cs,v 1.9 2008/02/05 20:40:26 aseovic Exp $ +
    + + + implementation that supports grouping of validators. + + +

    + This validator will be valid only when all of the validators in the Validators + collection are valid. +

    +

    + ValidationErrors property will return a union of all validation error messages + for the contained validators. +

    +
    + Aleksandar Seovic + $Id: ValidatorGroup.cs,v 1.10 2008/02/05 20:40:26 aseovic Exp $ +
    + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The expression that determines if this validator should be evaluated. + + + + Initializes a new instance of the class. + + The expression that determines if this validator should be evaluated. + + + + Validates the specified object. + + The object to validate. + Additional context parameters. + instance to add error messages to. + True if validation was successful, False otherwise. + + + + Doesn't do anything for validator group as there is no single test. + + Object to validate. + True if specified object is valid, False otherwise. + + + + Gets or sets the validators. + + The validators. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The expression that determines if this validator should be evaluated. + + + + Initializes a new instance of the class. + + The expression that determines if this validator should be evaluated. + + + + Validates the specified object. + + The object to validate. + Additional context parameters. + instance to add error messages to. + True if validation was successful, False otherwise. + + + + Various utility methods relating to numbers. + + +

    + Mainly for internal use within the framework. +

    +
    + Aleksandar Seovic + $Id: NumberUtils.cs,v 1.6 2008/03/20 23:58:16 oakinger Exp $ +
    + + + Determines whether the supplied is an integer. + + The object to check. + + if the supplied is an integer. + + + + + Determines whether the supplied is a decimal number. + + The object to check. + + if the supplied is a decimal number. + + + + + Determines whether the supplied is of numeric type. + + The object to check. + + true if the specified object is of numeric type; otherwise, false. + + + + + Determines whether the supplied can be converted to an integer. + + The object to check. + + if the supplied can be converted to an integer. + + + + + Determines whether the supplied can be converted to an integer. + + The object to check. + + if the supplied can be converted to an integer. + + + + + Determines whether the supplied can be converted to a number. + + The object to check. + + true if the specified object is decimal number; otherwise, false. + + + + + Is the supplied equal to zero (0)? + + The number to check. + + id the supplied is equal to zero (0). + + + + + Negates the supplied . + + The number to negate. + The supplied negated. + + If the supplied is not a supported numeric type. + + + + + Adds the specified numbers. + + The first number. + The second number. + + + + Subtracts the specified numbers. + + The first number. + The second number. + + + + Multiplies the specified numbers. + + The first number. + The second number. + + + + Divides the specified numbers. + + The first number. + The second number. + + + + Calculates remainder for the specified numbers. + + The first number (dividend). + The second number (divisor). + + + + Raises first number to the power of the second one. + + The first number. + The second number. + + + + Coerces the types so they can be compared. + + The right. + The left. + + + + Creates a new instance of the class. + + +

    + This is a utility class, and as such exposes no public constructors. +

    +
    +
    + + + Various utility methods relating to the manipulation of arrays. + + Aleksandar Seovic + $Id: ArrayUtils.cs,v 1.15 2007/02/17 08:28:12 bbaia Exp $ + + + + Checks if the given array or collection is null or has no elements. + + + + + + + Tests equality of two single-dimensional arrays by checking each element + for equality. + + The first array to be checked. + The second array to be checked. + True if arrays are the same, false otherwise. + + + + Returns hash code for an array that is generated based on the elements. + + + Hash code returned by this method is guaranteed to be the same for + arrays with equal elements. + + + Array to calculate hash code for. + + + A hash code for the specified array. + + + + + Returns string representation of an array. + + + Array to return as a string. + + + String representation of the specified . + + + + + Support to account for differences between java nad .NET: +
      +
    +
    +
    + + + .NET threads have not a method to check if they have been interrupted. + Moreover, differently from java threads, when entering locked + blocks, Monitor, Sleep, SpinWait and so on, a + will be raised by the runtime. +

    Spring.Threading classes usually call this method before entering a lock block, to mirror java code +

    Usually this is non issue because the same exception will be raised entering the monitor + associated with the lock () +

    +
    + if the thread has been interrupted +
    + + + Normalize the given so that + is is comparable with . + + Date. + + + + + + + + + the difference between millisecodns of the first and second date + + + + Returns the number of nanoseconds for the current value of + + Current number of nanoseconds + + + + Returns the number of nano seconds represented by the + + to use + Number of nano seconds for + + + + Returns a representing the number of nanoseconds passed in via . + + Number of nanoseconds. + representing the number of nanoseconds passed in. + + + + Placeholder for java.lang.System.currentTimeMillis + + The current machine time in milliseconds + + + + Has been interrupted this thread + + + + A TimeoutSync is an adaptor class that transforms all + calls to acquire to instead invoke attempt with a predetermined + timeout value. + + + + + + Acquire/Release protocol, base of many concurrency utilities. + + + +

    objects isolate waiting and notification for particular logical + states, resource availability, events, and the like that are shared + across multiple threads.

    + +

    Use of s sometimes (but by no means always) adds + flexibility and efficiency compared to the use of plain + .Net monitor methods and locking, and are sometimes (but by no means + always) simpler to program with.

    + +

    Used for implementation of a

    +
    + + Doug Lea + Federico Spinazzi (.Net) + $Id: ISync.cs,v 1.6 2006/09/15 19:06:08 markpollack Exp $ +
    + + Wait (possibly forever) until successful passage. + Fail only upon interuption. Interruptions always result in + `clean' failures. On failure, you can be sure that it has not + been acquired, and that no + corresponding release should be performed. Conversely, + a normal return guarantees that the acquire was successful. + + + + + Potentially enable others to pass. +

    + Because release does not raise exceptions, + it can be used in `finally' clauses without requiring extra + embedded try/catch blocks. But keep in mind that + as with any java method, implementations may + still throw unchecked exceptions such as Error or NullPointerException + when faced with uncontinuable errors. However, these should normally + only be caught by higher-level error handlers. +

    +
    +
    + + + Wait at most msecs to pass; report whether passed. +

    + The method has best-effort semantics: + The msecs bound cannot + be guaranteed to be a precise upper bound on wait time in Java. + Implementations generally can only attempt to return as soon as possible + after the specified bound. Also, timers in Java do not stop during garbage + collection, so timeouts can occur just because a GC intervened. + So, msecs arguments should be used in + a coarse-grained manner. Further, + implementations cannot always guarantee that this method + will return at all without blocking indefinitely when used in + unintended ways. For example, deadlocks may be encountered + when called in an unintended context. +

    +
    + the number of milleseconds to wait + An argument less than or equal to zero means not to wait at all. + However, this may still require + access to a synchronization lock, which can impose unbounded + delay if there is a lot of contention among threads. + + true if acquired +
    + + + the adapted sync + + + + + timeout value + + + + Create a TimeoutSync using the given Sync object, and + using the given timeout value for all calls to acquire. + + + + + Try to acquire the sync before the timeout + + In case a time out occurred + + + + + + + + + + + + + + Utility class to use an with the + C# using () {} idiom + + + + + Creates a new trying to the given + + + the to be held + + + + Creates a new trying to the given + + + the to be held + millisecond to try to acquire the lock + + + + Releases the held + + + + + initializes and acquire access to the + + + + + + Defines interface that proxy method builders have to implement. + + Aleksandar Seovic + Bruno Baia + $Id: IProxyMethodBuilder.cs,v 1.1 2006/08/10 18:45:55 bbaia Exp $ + + + + Dynamically builds proxy method. + + The method to proxy. + + The interface definition of the method, if applicable. + + + The for the proxy method. + + + + + Builds a proxy type using composition. + + + + In order for this builder to work, the target must implement + one or more interfaces. + + + Aleksandar Seovic + Bruno Baia + $Id: CompositionProxyTypeBuilder.cs,v 1.12 2007/03/27 02:27:43 bbaia Exp $ + + + + Base class for proxy builders that can be used + to create a proxy for any class. + + +

    + This class provides a set of template + methods that derived classes can override to provide custom behaviour + appropriate to the type of proxy that is being generated (one of + inheritance or composition-based proxying). +

    +
    + Aleksandar Seovic + Bruno Baia + $Id: AbstractProxyTypeBuilder.cs,v 1.33 2007/12/07 17:59:20 bbaia Exp $ +
    + + + Describes the operations for a generic proxy type builder that can be + used to create a proxy type for any class. + + Aleksandar Seovic + $Id: IProxyTypeBuilder.cs,v 1.8 2007/06/20 15:59:54 bbaia Exp $ + + + + Creates the proxy type. + + The generated proxy class. + + + + The name of the proxy . + + The name of the proxy . + + + + The of the target object. + + + + + The of the class that the proxy must + inherit from. + + + + + Gets or sets the list of interfaces proxy should implement. + + + + + Should we proxy target attributes? + + + by default. + Target type attributes, method attributes, method's return type attributes + and method's parameter attributes are copied to the proxy. + + + + + The list of custom s that the proxy + class must be decorated with. + + +

    + Note that the list is composed of instances of the actual + s that are to be applied, not the + s of the s. +

    +
    + +

    + The following code snippets show examples of how to decorate the + the proxied class with one or more s. +

    + + // get a concrete implementation of an IProxyTypeBuilder... + IProxyTypeBuilder builder = ... ; + builder.TargetType = typeof( ... ); + + IDictionary typeAtts = new Hashtable(); + builder.TypeAttributes = typeAtts; + + // applies a single Attribute to the proxied class... + typeAtts = new Attribute[] { new MyCustomAttribute() }); + + // applies a number of Attributes to the proxied class... + typeAtts = new Attribute[] + { + new MyCustomAttribute(), + new AnotherAttribute(), + }); + +
    +
    + + + The custom s that the proxy + members must be decorated with. + + +

    + This dictionary must use simple s for keys + (denoting the member names that the attributes are to be applied to), + with the corresponding values being + s. +

    +

    + The key may be wildcarded using the '*' character... if so, + then those proxy members that match against the key will be + decorated with the attendant list of + s. This naturally implies that using + the '*' character as a key will result in the attendant list + of s being applied to every member of + the proxied class. +

    +
    + +

    + The following code snippets show examples of how to decorate the + members of a proxied class with one or more + s. +

    + + // get a concrete implementation of an IProxyTypeBuilder... + IProxyTypeBuilder builder = ... ; + builder.TargetType = typeof( ... ); + + IDictionary memAtts = new Hashtable(); + builder.MemberAttributes = memAtts; + + // applies a single Attribute to all members of the proxied class... + memAtts ["*"] = new Attribute[] { new MyCustomAttribute() }); + + // applies a number of Attributes to all members of the proxied class... + memAtts ["*"] = new Attribute[] + { + new MyCustomAttribute(), + new AnotherAttribute(), + }); + + // applies a single Attribute to those members of the proxied class + // that have identifiers starting with 'Do' ... + memAtts ["Do*"] = new Attribute[] { new MyCustomAttribute() }); + + // applies a number of Attributes to those members of the proxied class + // that have identifiers starting with 'Do' ... + memAtts ["Do*"] = new Attribute[] + { + new MyCustomAttribute(), + new AnotherAttribute(), + }); + +
    +
    + + + Describes the operations that generates IL instructions + used to build the proxy type. + + Bruno Baia + $Id: IProxyTypeGenerator.cs,v 1.3 2006/11/12 01:37:47 bbaia Exp $ + + + + Generates the IL instructions that pushes + the proxy instance on stack. + + The IL generator to use. + + + + Generates the IL instructions that pushes + the target instance on which calls should be delegated to. + + The IL generator to use. + + + + The shared instance for this class (and derived classes). + + + + + Creates the proxy type. + + The generated proxy class. + + + + Generates the IL instructions that pushes + the proxy instance on stack. + + The IL generator to use. + + + + Generates the IL instructions that pushes + the target instance on which calls should be delegated to. + + The IL generator to use. + + + + Creates an appropriate type builder. + + The name to use for the proxy type name. + The type to extends if provided. + The type builder to use. + + + + Applies attributes to the proxy class. + + The type builder to use. + The proxied class. + + + + + + Applies attributes to the proxied method. + + The method builder to use. + The proxied method. + + + + + + Applies attributes to the proxied method's return type. + + The method builder to use. + The proxied method. + + + + + Applies attributes to proxied method's parameters. + + The method builder to use. + The proxied method. + + + + + Calculates and returns the list of attributes that apply to the + specified type. + + The type to find attributes for. + + A list of custom attributes that should be applied to type. + + + + + + + Calculates and returns the list of attributes that apply to the + specified method. + + The method to find attributes for. + + A list of custom attributes that should be applied to method. + + + + + + + Calculates and returns the list of attributes that apply to the + specified method's return type. + + The method to find attributes for. + + A list of custom attributes that should be applied to method's return type. + + + + + + Calculates and returns the list of attributes that apply to the + specified method's parameters. + + The method to find attributes for. + The method's parameter to find attributes for. + + A list of custom attributes that should be applied to the specified method's parameter. + + + + + + Check that the specified object is matching the passed attribute type. + + +

    + The specified object can be of different type : +

    + + + + + + System.Reflection.CustomAttributeData (Only with .NET 2.0) + + + + + +
    + The object instance to check. + The attribute type to test against. + + if the object instance matches the attribute type; + otherwise . + +
    + + + Defines the types of the parameters for the specified constructor. + + The constructor to use. + The types for constructor's parameters. + + + + Implements constructors for the proxy class. + + + The builder to use. + + + + + Generates the proxy constructor. + + The constructor builder to use. + The IL generator to use. + The constructor to use. + + + + Implements an interface. + + + Generates proxy methods that belongs to the interface + using the specified . + + The type builder to use. + + The implementation to use + + The interface to implement. + + The of the target object. + + + + + Implements an interface. + + + Generates proxy methods that belongs to the interface + using the specified . + + The type builder to use. + + The implementation to use + + The interface to implement. + + The of the target object. + + + if target virtual methods should not be proxied; + otherwise . + + + + + Gets the mapping of the interface to proxy + into the actual methods on the target type + that does not need to implement that interface. + + +

    + If the target type does not implement the interface, + we return the interfaces methods as the target methods for many reasons : +

      +
    • + The target object can change for an object that implements the interface. + (See 'Spring.Aop.Framework.DynamicProxy.IAdvisedProxyMethodBuilder' + implementation in the Spring AOP framework for an example) +
    • +
    • + Allow Transparent proxies to be proxied. + (See Spring Remoting framework for an example) +
    • +
    • + Allow null target to be proxied. + (See Spring AOP framework which avoid calls to the target object + by intercepting all methods. Think "dynamic mock") + (See 'Spring.Web.Services.WebServiceProxyFactory' implementation for another example) +
    • +
    +

    +
    + + The of the target object. + + The interface to implement. + + An interface mapping for the interface to proxy. + +
    + + + Inherit from a type. + + + Generates proxy methods for base virtual methods + using the specified . + + + The builder to use for code generation. + + + The implementation to use to override base virtual methods. + + The to inherit from. + + + + Inherit from a type. + + + Generates proxy methods for base virtual methods + using the specified . + + + The builder to use for code generation. + + + The implementation to use to override base virtual methods. + + The to inherit from. + + if only members declared at the level + of the supplied 's hierarchy should be proxied; + otherwise . + + + + + Implements the specified . + + The type builder to use. + The type the property is defined on. + The property to proxy. + The implemented methods map. + + + + Implements the specified event. + + The type builder to use. + The type the event is defined on. + The event to proxy. + The implemented methods map. + + + + Returns an array of s that represent + the proxiable interfaces. + + + An interface is proxiable if it's not marked with the + . + + + The array of interfaces from which + we want to get the proxiable interfaces. + + + An array containing the interface s. + + + + + Checks if specified interface is of a special type + that should never be proxied (i.e. ISerializable). + + Interface type to check. + + true if it is, false otherwise. + + + + + The name of the proxy . + + The name of the proxy . + + + + The of the target object. + + + + + The of the class that the proxy must + inherit from. + + +

    + The default value of this property is the + . +

    +
    +
    + + + Gets or sets the list of interfaces proxy should implement. + + + The default value of this property is all the interfaces + implemented or inherited by the target type. + + + + + Should we proxy target attributes? + + + + + + The list of custom s that the proxy + class must be decorated with. + + + + + + The custom s that the proxy + members must be decorated with. + + + + + + Target instance calls should be delegated to. + + + + + Creates a new instance of the + class. + + + + + Creates a proxy that delegates calls to an instance of the + target object. + + +

    + Only interfaces can be proxied using composition, so the target + must implement one or more interfaces. +

    +
    + The generated proxy class. + + If the + does not implement any interfaces. + +
    + + + Generates the IL instructions that pushes + the target instance on which calls should be delegated to. + + The IL generator to use. + + + + Deaclares a field that holds the target object instance. + + + The builder to use for code generation. + + + + + Generates the proxy constructor. + + +

    + This implementation creates instance of the target object for delegation + using constructor arguments. +

    +
    + The constructor builder to use. + The IL generator to use. + The constructor to delegate the creation to. +
    + + + Gets or sets a value indicating whether interfaces should be implemented explicitly. + + + if they should be; otherwise, . + + + + + Performs a comparison of two objects, using the specified object property via + an . + + Juergen Hoeller + Jean-Pierre Pawlak + Simon White (.NET) + $Id: PropertyComparator.cs,v 1.12 2007/07/31 00:09:03 markpollack Exp $ + + + + Creates a new instance of the + class. + + + The to use for any + sorting. + + + If the supplied is . + + + + + Compares two objects and returns a value indicating whether one is less + than, equal to or greater than the other. + + The first object to compare. + The second object to compare. + + + + + Get the 's property + value for the given object. + + The object to get the property value for. + The property value. + + + + Sort the given according to the + given sort definition. + + + The to be sorted. + + The parameters to sort by. + + In the case of a missing property name. + + + If the supplied is . + + + + + Gets the to + use for any sorting. + + + The to use for + any sorting. + + + + + Helper class allowing one to declaratively specify a method call for later invocation. + + +

    + Typically not used directly but via its subclasses such as + . +

    +

    + Usage: specify either the and + or the + and + properties respectively, and + (optionally) any arguments to the method. Then call the + method to prepare the invoker. + Once prepared, the invoker can be invoked any number of times. +

    +
    + +

    + The following example uses the class to invoke the + ToString() method on the Foo class using a mixture of both named and unnamed + arguments. +

    + + public class Foo + { + public string ToString(string name, int age, string address) + { + return string.Format("{0}, {1} years old, {2}", name, age, address); + } + + public static void Main() + { + Foo foo = new Foo(); + MethodInvoker invoker = new MethodInvoker(); + invoker.Arguments = new object [] {"Kaneda", "18 Kaosu Gardens, Nakatani Drive, Okinanawa"}; + invoker.AddNamedArgument("age", 29); + invoker.Prepare(); + // at this point, the arguments that will be passed to the method invocation + // will have been resolved into the following ordered array : {"Kaneda", 29, "18 Kaosu Gardens, Nakatani Drive, Okinanawa"} + string details = (string) invoker.Invoke(); + Console.WriteLine (details); + // will print out 'Kaneda, 29 years old, 18 Kaosu Gardens, Nakatani Drive, Okinanawa' + } + } + +
    + Colin Sampaleanu + Juergen Hoeller + Simon White (.NET) + $Id: MethodInvoker.cs,v 1.7 2007/08/04 01:05:15 bbaia Exp $ +
    + + + The used to search for + the method to be invoked. + + + + + The value returned from the invocation of a method that returns void. + + + + + The method that will be invoked. + + + + + Creates a new instance of the class. + + + + + Prepare the specified method. + + +

    + The method can be invoked any number of times afterwards. +

    +
    + + If all required properties are not set, or a matching argument could not be found + for a named argument (typically down to a typo). + + + If the specified method could not be found. + +
    + + + Searches for and returns the method that is to be invoked. + + + The return value of this method call will subsequently be returned from the + . + + The method that is to be invoked. + + If no method could be found. + + + If more than one method was found. + + + + + Adds the named argument to this instances mapping of argument names to argument values. + + + The name of an argument on the method that is to be invoked. + + + The value of the named argument on the method that is to be invoked. + + + + + Returns the prepared object that + will be invoked. + + +

    + A possible use case is to determine the return of the method. +

    +
    + + The prepared object that + will be invoked. + +
    + + + Invoke the specified method. + + +

    + The invoker needs to have been prepared beforehand (via a call to the + method). +

    +
    + + The object returned by the method invocation, or + if the method returns void. + + + If at least one of the arguments passed to this + was incompatible with the signature of the invoked method. + +
    + + + The target on which to call the target method. + + +

    + Only necessary when the target method is ; + else, a target object needs to be specified. +

    +
    +
    + + + The target object on which to call the target method. + + +

    + Only necessary when the target method is not ; + else, a target class is sufficient. +

    +
    +
    + + + The name of the method to be invoked. + + +

    + Refers to either a method + or a non- method, depending on + whether or not a target object has been set. +

    +
    + +
    + + + Arguments for the method invocation. + + +

    + Ordering is significant... the order of the arguments in this + property must match the ordering of the various parameters on the target + method. There does however exist a small possibility for confusion when + the arguments in this property are supplied in addition to one or more named + arguments. In this case, each named argument is slotted into the index position + corresponding to the named argument... once once all named arguments have been + resolved, the arguments in this property are slotted into any remaining (empty) + slots in the method parameter list (see the example in the overview of the + class if this is not clear). +

    +

    + If this property is not set, or the value passed to the setter invocation + is or a zero-length array, a method with no (un-named) arguments is assumed. +

    +
    + +
    + + + The resolved arguments for the method invocation. + + + + This property is not set until the target method has been resolved via a call to the + method). It is a combination of the + named and plain vanilla arguments properties, and it is this object array that + will actually be passed to the invocation of the target method. + +

    + Setting the value of this property to results in basically clearing out any + previously prepared arguments... another call to the + method will then be required to prepare the arguments again (or the prepared arguments + can be set explicitly if so desired). +

    +
    + + +
    + + + Named arguments for the method invocation. + + +

    + The keys of this dictionary are the () names of the + method arguments, and the () values are the actual + argument values themselves. +

    +

    + If this property is not set, or the value passed to the setter invocation + is a reference, a method with no named arguments is assumed. +

    +
    + +
    + + + Specialisation of the class that tries + to convert the given arguments for the actual target method via an + appropriate implementation. + + Juergen Hoeller + Rick Evans + $Id: ArgumentConvertingMethodInvoker.cs,v 1.10 2007/07/31 18:17:08 bbaia Exp $ + + + + + Creates a new instance of the + class. + + + + + Prepare the specified method. + + +

    + The method can be invoked any number of times afterwards. +

    +
    + + If all required properties are not set. + + + If the specified method could not be found. + +
    + + + Register the given custom + for all properties of the given . + + + The of property. + + + The to register. + + + + + Simple interface for object definition readers. + + Juergen Hoeller + Rick Evans + $Id: IObjectDefinitionReader.cs,v 1.8 2007/08/08 17:47:13 bbaia Exp $ + + + + Load object definitions from the supplied . + + + The resource for the object definitions that are to be loaded. + + + The number of object definitions found + + + In the case of loading or parsing errors. + + + + + Load object definitions from the supplied . + + + The resources for the object definitions that are to be loaded. + + + The number of object definitions found + + + In the case of loading or parsing errors. + + + + + Loads the object definitions from the specified resource location. + + The resource location, to be loaded with the + IResourceLoader location . + + The number of object definitions found + + + + + Loads the object definitions from the specified resource locations. + + The the resource locations to be loaded with the + IResourceLoader of this object definition reader. + + The number of object definitions found + + + + + Gets the + + instance that this reader works on. + + + + + The against which any class names + will be resolved into instances. + + + + + The to use for anonymous + objects (wihtout explicit object name specified). + + + + + Gets the resource loader to use for resource locations. + + There is also a method + available for loading object definitions from a resource location. This is + a convenience to avoid explicit ResourceLoader handling. + The resource loader. + + + + Responsible for creating instances corresponding to a + . + + Rod Johnson + Rick Evans (.NET) + $Id: IInstantiationStrategy.cs,v 1.6 2006/04/09 07:18:49 markpollack Exp $ + + + + Instantiate an instance of the object described by the supplied + from the supplied . + + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the null + or zero length string if we're autowiring an object that doesn't belong + to the supplied . + + + The owning + + + An instance of the object described by the supplied + from the supplied . + + + + + Instantiate an instance of the object described by the supplied + from the supplied . + + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the null + or zero length string if we're autowiring an object that doesn't belong + to the supplied . + + + The owning + + + The to be used to instantiate + the object. + + + Any arguments to the supplied . May be null. + + + An instance of the object described by the supplied + from the supplied . + + + + + Instantiate an instance of the object described by the supplied + from the supplied . + + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the null + or zero length string if we're autowiring an object that doesn't belong + to the supplied . + + + The owning + + + The to be used to get the object. + + + Any arguments to the supplied . May be null. + + + An instance of the object described by the supplied + from the supplied . + + + + + Abstract base class for object definition readers. + + +

    + Provides common properties like the object registry to work on. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: AbstractObjectDefinitionReader.cs,v 1.14 2008/01/10 14:32:24 bbaia Exp $ +
    + + + The shared instance for this class (and derived classes). + + + + + Creates a new instance of the + + class. + + + The + instance that this reader works on. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    +
    + + + Creates a new instance of the + + class. + + + The + instance that this reader works on. + + + The against which any class names + will be resolved into instances. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    +
    + + + Load object definitions from the supplied . + + + The resource for the object definitions that are to be loaded. + + + The number of object definitions that were loaded. + + + In the case of loading or parsing errors. + + + + + Load object definitions from the supplied . + + + The resources for the object definitions that are to be loaded. + + + The number of object definitions found + + + In the case of loading or parsing errors. + + + + + Loads the object definitions from the specified resource location. + + The resource location, to be loaded with the + IResourceLoader location . + + The number of object definitions found + + + + + Loads the object definitions from the specified resource locations. + + The the resource locations to be loaded with the + IResourceLoader of this object definition reader. + + The number of object definitions found + + + + + Gets the + + instance that this reader works on. + + + + + The to use for anonymous + objects (wihtout explicit object name specified). + + + + + + The against which any class names + will be resolved into instances. + + + + + Gets or sets the resource loader to use for resource locations. + + The resource loader. + + + + An that returns a value + that is the result of a or instance method invocation. + + +

    + Note that this class generally is expected to be used for accessing factory methods, + and as such defaults to operating in singleton mode. The first request to + + by the owning object factory will cause a method invocation, the return + value of which will be cached for all subsequent requests. The + property may be set to + , to cause this factory to invoke the target method each + time it is asked for an object. +

    +

    + A target method may be specified by setting the + property to a string representing + the method name, with specifying + the that the method is defined on. + Alternatively, a target instance method may be specified, by setting the + property as the target object, and + the property as the name of the + method to call on that target object. Arguments for the method invocation may be + specified by setting the property. +

    +

    + Another (esoteric) use case for this factory object is when one needs to call a method + that doesn't return any value (for example, a class method to + force some sort of initialization to happen)... this use case is not supported by + factory-methods, since a return value is needed to become the object. +

    +

    + + This class depends on the + + method being called after all properties have been set, as per the + contract. If you are + using this class outside of a Spring.NET IoC container, you must call one of either + or + yourself to ready the object's internal + state, or you will get a nasty . + +

    +
    + +

    + The following example uses an instance of this class to call a + factory method... +

    + + + + + + + + 1st + 2nd + and 3rd arguments + + + + +

    + The following example is similar to the preceding example; the only pertinent difference is the fact that + a number of different objects are passed as arguments, demonstrating that not only simple value types + are valid as elements of the argument list... +

    + + + + + + + + + + + 1st + + + + + + + http://www.springframework.net/ + + + + + +

    + Named parameters are also supported... this next example yields the same results as + the preceding example (that did not use named arguments). +

    + + + + + + + + + + 1st + and 3rd arguments + 2nd + + + + +

    + Similarly, the following example uses an instance of this class to call an instance method... +

    + + + + + + + + + +

    + The above example could also have been written using an anonymous inner object definition... if the + object on which the method is to be invoked is not going to be used outside of the factory object + definition, then this is the preferred idiom because it limits the scope of the object on which the + method is to be invoked to the surrounding factory object. +

    + + + + + + + + + + Colin Sampaleanu + Juergen Hoeller + Rick Evans (.NET) + Simon White (.NET) + $Id: MethodInvokingFactoryObject.cs,v 1.15 2007/03/16 04:01:38 aseovic Exp $ + + + + + + Interface to be implemented by objects used within an + that are themselves + factories. + + +

    + If an object implements this interface, it is used as a factory, + not directly as an object. s + can support singletons and prototypes + ()... + please note that an + itself can only ever be a singleton. It is a logic error to configure an + itself to be a prototype. +

    + + An object that implements this interface cannot be used as a normal object. + +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: IFactoryObject.cs,v 1.6 2006/04/09 07:18:48 markpollack Exp $ +
    + + + Return an instance (possibly shared or independent) of the object + managed by this factory. + + + + If this method is being called in the context of an enclosing IoC container and + returns , the IoC container will consider this factory + object as not being fully initialized and throw a corresponding (and most + probably fatal) exception. + + + + An instance (possibly shared or independent) of the object managed by + this factory. + + + + + Return the of object that this + creates, or + if not known in advance. + + + + + Is the object managed by this factory a singleton or a prototype? + + + + + Defines a simple initialization callback for objects that need to to some + post-initialization logic after all of their dependencies have been injected. + + +

    + An implementation of the + + method might perform some additional custom initialization (over and above that + performed by the constructor), or merely check that all mandatory properties + have been set (this last example is a very typical use case of this interface). +

    + + The use of the + interface + by non-Spring.NET framework code can be avoided (and is generally + discouraged). The Spring.NET container provides support for a generic + initialization method given to the object definition in the object + configuration store (be it XML, or a database, etc). This requires + slightly more configuration (one attribute-value pair in the case of + XML configuration), but removes any dependency on Spring.NET from the + class definition. + +
    + Rod Johnson + Rick Evans (.NET) + $Id: IInitializingObject.cs,v 1.8 2006/04/09 07:18:48 markpollack Exp $ + +
    + + + Invoked by an + after it has injected all of an object's dependencies. + + +

    + This method allows the object instance to perform the kind of + initialization only possible when all of it's dependencies have + been injected (set), and to throw an appropriate exception in the + event of misconfiguration. +

    +

    + Please do consult the class level documentation for the + interface for a + description of exactly when this method is invoked. In + particular, it is worth noting that the + + and + callbacks will have been invoked prior to this method being + called. +

    +
    + + In the event of misconfiguration (such as the failure to set a + required property) or if initialization fails. + +
    + + + Return an instance (possibly shared or independent) of the object + managed by this factory. + + +

    + Returns the return value of the method that is to be invoked. +

    +

    + Will return the same value each time if the + + property value is . +

    +
    + + An instance (possibly shared or independent) of the object managed by + this factory. + + +
    + + + Prepares this method invoker. + + + If all required properties are not set. + + + If the specified method could not be found. + + + + + + If a singleton should be created, or a new object on each request. + Defaults to . + + + + + Return the return value of the method + that this factory invokes, or if not + known in advance. + + +

    + If the return value of the method that this factory is to invoke is + , then the + will be returned (in accordance with the + contract that + treats a value as a configuration error). +

    +
    + +
    + + + SPI interface to be implemented by most if not all listable object factories. + + +

    + Allows for framework-internal plug'n'play, e.g. in + . +

    +
    + Juergen Hoeller + Rick Evans (.NET) +
    + + + Extension of the interface + to be implemented by object factories that can enumerate all their object instances, + rather than attempting object lookup by name one by one as requested by clients. + + +

    + implementations that preload + all their objects (for example, DOM-based XML factories) may implement this + interface. This interface is discussed in + "Expert One-on-One J2EE Design and Development", by Rod Johnson. +

    +

    + If this is an , + the return values will not take any + hierarchy into account, but + will relate only to the objects defined in the current factory. + Use the helper class to + get all objects. +

    +

    + With the exception of + , + the methods and properties in this interface are not designed for frequent + invocation. Implementations may be slow. +

    +
    + Rod Johnson + Rick Evans (.NET) + $Id: IListableObjectFactory.cs,v 1.12 2007/07/29 19:39:27 markpollack Exp $ +
    + + + The root interface for accessing a Spring.NET IoC container. + + +

    + This is the basic client view of a Spring.NET IoC container; further interfaces + such as and + + are available for specific purposes such as enumeration and configuration. +

    +

    + This is the root interface to be implemented by objects that can hold a number + of object definitions, each uniquely identified by a + name. An independent instance of any of these objects can be obtained + (the Prototype design pattern), or a single shared instance can be obtained + (a superior alternative to the Singleton design pattern, in which the instance is a + singleton in the scope of the factory). Which type of instance + will be returned depends on the object factory configuration - the API is the same. + The Singleton approach is more useful and hence more common in practice. +

    +

    + The point of this approach is that the IObjectFactory is a central registry of + application components, and centralizes the configuring of application components + (no more do individual objects need to read properties files, for example). + See chapters 4 and 11 of "Expert One-on-One J2EE Design and Development" for a + discussion of the benefits of this approach. +

    +

    + Normally an IObjectFactory will load object definitions stored in a configuration + source (such as an XML document), and use the + namespace to configure the objects. However, an implementation could simply return + .NET objects it creates as necessary directly in .NET code. There are no + constraints on how the definitions could be stored: LDAP, RDBMS, XML, properties + file etc. Implementations are encouraged to support references amongst objects, + to either Singletons or Prototypes. +

    +

    + In contrast to the methods in + , all of the methods + in this interface will also check parent factories if this is an + . If an object is + not found in this factory instance, the immediate parent is asked. Objects in + this factory instance are supposed to override objects of the same name in any + parent factory. +

    +

    + Object factories are supposed to support the standard object lifecycle interfaces + as far as possible. The maximum set of initialization methods and their standard + order is: +

    +

    + + + + 's + property. + + + + + 's + property. + + + + + + (only applicable if running within an ). + + + + + The + + method of + s. + + + + + 's + method. + + + + + A custom init-method definition. + + + + + The + + method of + s. + + + +

    +

    +

    + On shutdown of an object factory, the following lifecycle methods apply: +

    +

    + + + + 's + method. + + + + + A custom destroy-method definition. + + + +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: IObjectFactory.cs,v 1.17 2007/07/30 15:41:32 markpollack Exp $ +
    + + + Is this object a singleton? + + +

    + That is, will + always return the same object? +

    +

    + Will ask the parent factory if the object cannot be found in this factory + instance. +

    +
    + The name of the object to query. + True if the named object is a singleton. + + If there's no such object definition. + +
    + + + Determines whether the specified object name is prototype. That is, will GetObject + always return independent instances? + + This method returning false does not clearly indicate a singleton object. + It indicated non-independent instances, which may correspond to a scoped object as + well. use the IsSingleton property to explicitly check for a shared + singleton instance. + Translates aliases back to the corresponding canonical object name. Will ask the + parent factory if the object can not be found in this factory instance. + + + + The name of the object to query + + true if the specified object name will always deliver independent instances; otherwise, false. + + if there is no object with the given name. + + + + Does this object factory contain an object with the given name? + + +

    + Will ask the parent factory if the object cannot be found in this factory + instance. +

    +
    + The name of the object to query. + True if an object with the given name is defined. +
    + + + Return the aliases for the given object name, if defined. + + +

    + Will ask the parent factory if the object cannot be found in this factory + instance. +

    +
    + The object name to check for aliases. + The aliases, or an empty array if none. + + If there's no such object definition. + +
    + + + Return an instance (possibly shared or independent) of the given object name. + + +

    + This method allows an object factory to be used as a replacement for the + Singleton or Prototype design pattern. +

    +

    + Note that callers should retain references to returned objects. There is no + guarantee that this method will be implemented to be efficient. For example, + it may be synchronized, or may need to run an RDBMS query. +

    +

    + Will ask the parent factory if the object cannot be found in this factory + instance. +

    +
    + The name of the object to return. + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + +
    + + + Return an instance (possibly shared or independent) of the given object name. + + +

    + This method allows an object factory to be used as a replacement for the + Singleton or Prototype design pattern. +

    +

    + Note that callers should retain references to returned objects. There is no + guarantee that this method will be implemented to be efficient. For example, + it may be synchronized, or may need to run an RDBMS query. +

    +

    + Will ask the parent factory if the object cannot be found in this factory + instance. +

    +
    + The name of the object to return. + + The arguments to use if creating a prototype using explicit arguments to + a static factory method. If there is no factory method and the + arguments are not null, then match the argument values by type and + call the object's constructor. + + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + If the supplied is . + +
    + + + Return an instance (possibly shared or independent) of the given object name. + + The name of the object to return. + + The the object may match. Can be an interface or + superclass of the actual class. For example, if the value is the + class, this method will succeed whatever the + class of the returned instance. + + + The arguments to use if creating a prototype using explicit arguments to + a factory method. If there is no factory method and the + supplied array is not , then + match the argument values by type and call the object's constructor. + + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + If the object is not of the required type. + + + If the supplied is . + + + + + + Return an instance (possibly shared or independent) of the given object name. + + +

    + Provides a measure of type safety by throwing an exception if the object is + not of the required . +

    +

    + This method allows an object factory to be used as a replacement for the + Singleton or Prototype design pattern. +

    +

    + Note that callers should retain references to returned objects. There is no + guarantee that this method will be implemented to be efficient. For example, + it may be synchronized, or may need to run an RDBMS query. +

    +

    + Will ask the parent factory if the object cannot be found in this factory + instance. +

    +
    + The name of the object to return. + + the object may match. Can be an interface or + superclass of the actual class. For example, if the value is the + class, this method will succeed whatever the + class of the returned instance. + + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + If the object is not of the required type. + +
    + + + Determine the type of the object with the given name. + + +

    + More specifically, checks the type of object that + would return. + For an , returns the type + of object that the creates. +

    +
    + The name of the object to query. + + The type of the object or if not determinable. + +
    + + + Determines whether the object with the given name matches the specified type. + + More specifically, check whether a GetObject call for the given name + would return an object that is assignable to the specified target type. + Translates aliases back to the corresponding canonical bean name. + Will ask the parent factory if the bean cannot be found in this factory instance. + + The name of the object to query. + Type of the target to match against. + + true if the object type matches; otherwise, false + if it doesn't match or cannot be determined yet. + + Ff there is no object with the given name + + + + + Injects dependencies into the supplied instance + using the named object definition. + + +

    + In addition to being generally useful, typically this method is used to provide + dependency injection functionality for objects that are instantiated outwith the + control of a developer. A case in point is the way that the current (1.1) + ASP.NET classes instantiate web controls... the instantiation takes place within + a private method of a compiled page, and thus cannot be hooked into the + typical Spring.NET IOC container lifecycle for dependency injection. +

    +
    + + The following code snippet assumes that the instantiated factory instance + has been configured with an object definition named + 'ExampleNamespace.BusinessObject' that has been configured to set the + Dao property of any ExampleNamespace.BusinessObject instance + to an instance of an appropriate implementation... + + namespace ExampleNamespace + { + public class BusinessObject + { + private IDao _dao; + + public BusinessObject() {} + + public IDao Dao + { + get { return _dao; } + set { _dao = value; } + } + } + } + + with the corresponding driver code looking like so... + + IObjectFactory factory = GetAnIObjectFactoryImplementation(); + BusinessObject instance = new BusinessObject(); + factory.ConfigureObject(instance, "object_definition_name"); + // at this point the dependencies for the 'instance' object will have been resolved... + + + + The object instance that is to be so configured. + + + The name of the object definition expressing the dependencies that are to + be injected into the supplied instance. + + + If there is no object definition for the supplied . + + + If any of the target object's dependencies could not be created. + +
    + + + Return an instance (possibly shared or independent) of the given object name. + + +

    + This method allows an object factory to be used as a replacement for the + Singleton or Prototype design pattern. +

    +

    + Note that callers should retain references to returned objects. There is no + guarantee that this method will be implemented to be efficient. For example, + it may be synchronized, or may need to run an RDBMS query. +

    +

    + Will ask the parent factory if the object cannot be found in this factory + instance. +

    +

    + This is the indexer for the + interface. +

    +
    + The name of the object to return. + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + +
    + + + Check if this object factory contains an object definition with the given name. + + +

    + Does not consider any hierarchy this factory may participate in. +

    + + Ignores any singleton objects that have been registered by other means + than object definitions. + +
    + The name of the object to look for. + + if this object factory contains an object + definition with the given name. + +
    + + + Return the names of all objects defined in this factory. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + + + + + Return the names of objects matching the given + (including subclasses), judging from the object definitions. + + +

    + Does consider objects created by s, + or rather it considers the type of objects created by + (which means that + s will be instantiated). +

    +

    + Does not consider any hierarchy this factory may participate in. +

    +
    + + The (class or interface) to match, or + for all object names. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + +
    + + + Return the names of objects matching the given + (including subclasses), judging from the object definitions. + + +

    + Does consider objects created by s, + or rather it considers the type of objects created by + (which means that + s will be instantiated). +

    +

    + Does not consider any hierarchy this factory may participate in. +

    +
    + + The (class or interface) to match, or + for all object names. + + + Whether to include prototype objects too or just singletons (also applies to + s). + + + Whether to include s too + or just normal objects. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + +
    + + + Return the object instances that match the given object + (including subclasses), judging from either object + definitions or the value of + in the case of + s. + + +

    + This version of the + method matches all kinds of object definitions, be they singletons, prototypes, or + s. Typically, the results + of this method call will be the same as a call to + IListableObjectFactory.GetObjectsOfType(type,true,true) . +

    +
    + + The (class or interface) to match. + + + A of the matching objects, + containing the object names as keys and the corresponding object instances + as values. + + + If the objects could not be created. + +
    + + + Return the object instances that match the given object + (including subclasses), judging from either object + definitions or the value of + in the case of + s. + + + The (class or interface) to match. + + + Whether to include prototype objects too or just singletons (also applies to + s). + + + Whether to include s too + or just normal objects. + + + A of the matching objects, + containing the object names as keys and the corresponding object instances + as values. + + + If the objects could not be created. + + + + + Return the number of objects defined in the factory. + + + The number of objects defined in the factory. + + + + + Configuration interface to be implemented by most if not all object + factories. + + +

    + Provides the means to configure an object factory in addition to the + object factory client methods in the + interface. +

    +

    + Allows for framework-internal plug'n'play even when needing access to object + factory configuration methods. +

    +

    + When disposed, it will destroy all cached singletons in this factory. Call + when you want to shutdown + the factory. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: IConfigurableObjectFactory.cs,v 1.14 2007/07/16 21:06:21 markpollack Exp $ +
    + + + Sub-interface implemented by object factories that can be part + of a hierarchy. + + Rod Johnson + Rick Evans (.NET) + $Id: IHierarchicalObjectFactory.cs,v 1.6 2006/04/09 07:18:48 markpollack Exp $ + + + + Return the parent object factory, or + if this factory does not have a parent. + + + The parent object factory, or + if this factory does not have a parent. + + + + + Ignore the given dependency type for autowiring. + + +

    + To be invoked during factory configuration. +

    +

    + This will typically be used for dependencies that are resolved + in other ways, like + through . +

    +
    + + The to be ignored. + +
    + + + Determines whether the specified object name is currently in creation.. + + Name of the object. + + true if the specified object name is currently in creation; otherwise, false. + + + + + Add a new + that will get applied to objects created by this factory. + + +

    + To be invoked during factory configuration. +

    +
    + + The + to register. + +
    + + + Given an object name, create an alias. + + +

    + This is typically used to support names that are illegal within + XML ids (which are used for object names). +

    +

    + Typically invoked during factory configuration, but can also be + used for runtime registration of aliases. Therefore, a factory + implementation should synchronize alias access. +

    +
    + The name of the object. + + + The alias that will behave the same as the object name. + + + If there is no object with the given name. + + + If the alias is already in use. + +
    + + + Register the given existing object as singleton in the object factory, + under the given object name. + + +

    + Typically invoked during factory configuration, but can also be + used for runtime registration of singletons. Therefore, a factory + implementation should synchronize singleton access; it will have + to do this anyway if it supports lazy initialization of singletons. +

    +
    + + The name of the object. + + The existing object. + + If the singleton could not be registered. + +
    + + + Register the given custom + for all properties of the given . + + +

    + To be invoked during factory configuration. +

    +
    + + The required of the property. + + + The to register. + +
    + + + Does this object factory contains a singleton instance with the + supplied ? + + +

    + Only checks already instantiated singletons; does not return + for singleton object definitions that have + not been instantiated yet. +

    +

    + The main purpose of this method is to check manually registered + singletons (). This + method can also be used to check whether a singleton defined by an + object definition has already been created. +

    +

    + To check whether an object factory contains an object definition + with a given name, use the + + method. Calling both + + and definitively answers + the question of whether a specific object factory contains a + singleton object with the given name. +

    +

    + Use the + + method for general checks as to whether a factory knows about an + object with a given name (regrdless of whether the object in + question is a manually registed singleton instance or created by + an object definition)... this also has the happy bonus of also + checking any ancestor factories. +

    +
    + + The name of the (singleton) object to look for. + + + if this object factory contains a singleton + instance with the given . + + + +
    + + + Set the parent of this object factory. + + +

    + Note that the parent shouldn't be changed: it should only be set outside + a constructor if it isn't available when an object of this class is + created. +

    +
    +
    + + + Returns the current number of registered + s. + + + The current number of registered + s. + + + + + Extension of the + interface to be implemented by object factories that are capable of + autowiring and expose this functionality for existing object instances. + + Juergen Hoeller + Rick Evans (.NET) + + + + Create a new object instance of the given class with the specified + autowire strategy. + + + The of the object to instantiate. + + + The desired autowiring mode. + + + Whether to perform a dependency check for objects (not applicable to + autowiring a constructor, thus ignored there). + + The new object instance. + + If the wiring fails. + + + + + + Autowire the object properties of the given object instance by name or + . + + + The existing object instance. + + + The desired autowiring mode. + + + Whether to perform a dependency check for the object. + + + If the wiring fails. + + + + + + Apply s + to the given existing object instance, invoking their + + methods. + + +

    + The returned object instance may be a wrapper around the original. +

    +
    + + The existing object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + If any post-processing failed. + + +
    + + + Apply s + to the given existing object instance, invoking their + + methods. + + +

    + The returned object instance may be a wrapper around the original. +

    +
    + + The existing object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + If any post-processing failed. + + +
    + + + Return the registered + for the + given object, allowing access to its property values and constructor + argument values. + + The name of the object. + + The registered + . + + + If there is no object with the given name. + + + In the case of errors. + + + + + Return the registered + for the + given object, allowing access to its property values and constructor + argument values. + + The name of the object. + Whether to search parent object factories. + + The registered + . + + + If there is no object with the given name. + + + In the case of errors. + + + + + Injects dependencies into the supplied instance + using the supplied . + + + The object instance that is to be so configured. + + + The name of the object definition expressing the dependencies that are to + be injected into the supplied instance. + + + An object definition that should be used to configure object. + + + + + + Ensure that all non-lazy-init singletons are instantiated, also + considering s. + + +

    + Typically invoked at the end of factory setup, if desired. +

    +

    + As this is a startup method, it should destroy already created singletons if + it fails, to avoid dangling resources. In other words, after invocation + of that method, either all or no singletons at all should be + instantiated. +

    +
    + + If one of the singleton objects could not be created. + +
    + + + Holder for event handler values for an object. + + Rick Evans (.NET) + $Id: EventValues.cs,v 1.8 2007/03/16 04:01:38 aseovic Exp $ + + + + The empty array of s. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The + to be used to populate this instance. + + + + + Copy all given argument values into this object. + + + The + to be used to populate this instance. + + + + + Adds the supplied handler to the collection of event handlers. + + The handler to be added. + + + + The mapping of event names to an + of + s. + + + + + Gets the of events + that have handlers associated with them. + + + + + Gets the of + s for the supplied + event name. + + + + + Various utility methods for .NET style .config files. + + +

    + Currently supports reading custom configuration sections and returning them as + objects. +

    +
    + Simon White + Mark Pollack + $Id: ConfigurationReader.cs,v 1.18 2007/08/08 17:47:13 bbaia Exp $ +
    + + + Reads the specified configuration section into a + . + + The resource to read. + The section name. + + A newly populated + . + + + If any errors are encountered while attempting to open a stream + from the supplied . + + + If any errors are encountered while loading or reading (this only applies to + v1.1 and greater of the .NET Framework) the actual XML. + + + If any errors are encountered while loading or reading (this only applies to + v1.0 of the .NET Framework). + + + If the configuration section was otherwise invalid. + + + + + Reads the specified configuration section into the supplied + . + + The resource to read. + The section name. + + The collection that is to be populated. May be + . + + + A newly populated + . + + + If any errors are encountered while attempting to open a stream + from the supplied . + + + If any errors are encountered while loading or reading (this only applies to + v1.1 and greater of the .NET Framework) the actual XML. + + + If any errors are encountered while loading or reading (this only applies to + v1.0 of the .NET Framework). + + + If the configuration section was otherwise invalid. + + + + + Reads the specified configuration section into the supplied + . + + The resource to read. + The section name. + + The collection that is to be populated. May be + . + + + If a key already exists, is its value to be appended to the current + value or replaced? + + + The populated + . + + + If any errors are encountered while attempting to open a stream + from the supplied . + + + If any errors are encountered while loading or reading (this only applies to + v1.1 and greater of the .NET Framework) the actual XML. + + + If any errors are encountered while loading or reading (this only applies to + v1.0 of the .NET Framework). + + + If the configuration section was otherwise invalid. + + + + + Read from the specified configuration from the supplied XML + into a + . + + + + Does not support section grouping. The supplied XML + must already be loaded. + + + + The to read from. + + + The configuration section name to read. + + + A newly populated + . + + + If any errors are encountered while reading (this only applies to + v1.1 and greater of the .NET Framework). + + + If any errors are encountered while reading (this only applies to + v1.0 of the .NET Framework). + + + If the configuration section was otherwise invalid. + + + + + Populates the supplied with values from + a .NET application configuration file. + + + The + to add any key-value pairs to. + + + The configuration section name in the a .NET application configuration + file. + + + If a key already exists, is its value to be appended to the current + value or replaced? + + + if the supplied + was found. + + + + + Creates a new instance of the ConfigurationReader class. + + +

    + This is a utility class, and as such has no publicly visible + constructors. +

    +
    +
    + + + Utility class to aid in the manipulation of events and delegates. + + Griffin Caprio + $Id: EventManipulationUtils.cs,v 1.3 2007/07/31 01:35:12 markpollack Exp $ + + + + Returns a new instance of the requested . + + +

    + Often used to wire subscribers to event publishers. +

    +
    + + The of delegate to create. + + + The target subscriber object that contains the delegate implementation. + + + referencing the delegate method on the subscriber. + + + A delegate handler that can be added to an events list of handlers, or called directly. + +
    + + + Queries the input type for a signature matching the input + signature. + + + Typically used to query a potential subscriber to see if they implement an event handler. + + to match against + to query + + matching input + signature, or if there is no match. + + + + + Creates a new instance of the EventManipulationUtilities class. + + +

    + This is a utility class, and as such has no publicly visible constructors. +

    +
    +
    + + + Represents parsed indexer node in the navigation expression. + + Aleksandar Seovic + $Id: IndexerNode.cs,v 1.16 2007/09/07 03:01:26 markpollack Exp $ + + + + Base type for nodes that accept arguments. + + Aleksandar Seovic + $Id: NodeWithArguments.cs,v 1.15 2007/09/07 03:01:26 markpollack Exp $ + + + + Base type for all expression nodes. + + Aleksandar Seovic + $Id: BaseNode.cs,v 1.24 2007/09/07 03:01:21 markpollack Exp $ + + + + For internal purposes only. Use for expression node implementations. + + + This class is only required to enable serialization of parsed Spring expressions since antlr.CommonAST + unfortunately is not marked as [Serializable].
    +
    + Note:Since SpringAST implements , deriving classes + have to explicitely override if they need to persist additional + data during serialization. +
    +
    + + + The global SpringAST node factory + + + + + Create an instance + + + + + Create an instance from a token + + + + + initialize this instance from an AST + + + + + initialize this instance from an IToken + + + + + initialize this instance from a token type number and a text + + + + + sets the text of this node + + + + + gets the text of this node + + + + + Create a new instance from SerializationInfo + + + + + populate SerializationInfo from this instance + + + + + gets or sets the token type of this node + + + + + gets or sets the text of this node + + + + + Interface that all navigation expression nodes have to implement. + + Aleksandar Seovic + $Id: IExpression.cs,v 1.5 2007/07/31 08:18:20 markpollack Exp $ + + + + Returns expression value. + + Value of the expression. + + + + Returns expression value. + + Object to evaluate expression against. + Value of the expression. + + + + Returns expression value. + + Object to evaluate expression against. + Expression variables map. + Value of the expression. + + + + Sets expression value. + + Object to evaluate expression against. + New value for the last node of the expression. + + + + Sets expression value. + + Object to evaluate expression against. + Expression variables map. + New value for the last node of the expression. + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns node's value. + + Node's value. + + + + Returns node's value for the given context. + + Object to evaluate node against. + Node's value. + + + + Returns node's value for the given context. + + Object to evaluate node against. + Expression variables map. + Node's value. + + + + This is the entrypoint into evaluating this expression. + + + + + Called internally during expression evaluation + + Object to evaluate node against. + Current expression evaluation context. + + + + + Returns node's value for the given context. + + Node's value. + + + + Sets node's value for the given context. + + Object to evaluate node against. + New value for this node. + + + + Sets node's value for the given context. + + Object to evaluate node against. + Expression variables map. + New value for this node. + + + + This is the entrypoint into evaluating this expression. + + + + + Called internally during expression evaluation. + + + + + Sets node's value for the given context. + + +

    + This is a default implementation of Set method, which + simply throws . +

    +

    + This was done in order to avoid redundant Set method implementations, + because most of the node types do not support value setting. +

    +
    +
    + + + Returns a string representation of this node instance. + + + + + Holds the state during evaluating an expression. + + + + + Gets/Sets the root context of the current evaluation + + + + + Gets/Sets the current context of the current evaluation + + + + + Gets/Sets global variables of the current evaluation + + + + + Gets/Sets local variables of the current evaluation + + + + + Initializes a new EvaluationContext instance. + + The root context for this evaluation + dictionary of global variables used during this evaluation + + + + Switches current ThisContext. + + + + + Switches current LocalVariables. + + + + + Gets the type of the + + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Initializes the node. + + + + + Asserts the argument count. + + The required count. + + + + Resolves the arguments. + + Current expression evaluation context. + An array of argument values + + + + Resolves the named arguments. + + Current expression evaluation context. + A dictionary of argument name to value mappings. + + + + Resolves the argument. + + Argument position. + Current expression evaluation context. + Resolved argument value. + + + + Resolves the named argument. + + Argument name. + Current expression evaluation context. + Resolved named argument value. + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns node's value for the given context. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Sets node's value for the given context. + + Context to evaluate expressions against. + Current expression evaluation context. + New value for this node. + + + + Utility method that is needed by ObjectWrapper and AbstractAutowireCapableObjectFactory. + + Context to resolve property against. + Expression variables map. + PropertyInfo for this node. + + + + Represents logical MATCHES operator. + + Aleksandar Seovic + $Id: OpMatches.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + + + + Base class for unary operators. + + Aleksandar Seovic + $Id: BinaryOperator.cs,v 1.8 2007/09/07 03:01:21 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Gets the left operand. + + The left operand. + + + + Gets the right operand. + + The right operand. + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical MATCHES operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + + true if the left operand matches the right operand, false otherwise. + + + + + Represents parsed variable node. + + Aleksandar Seovic + $Id: LocalVariableNode.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns value of the local variable represented by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Sets value of the local variable represented by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + New value for this node. + + + + Defines an interface that should be implemented + by all collection processors and aggregators. + + + + + Processes a list of source items and returns a result. + + + The source list to process. + + + An optional processor arguments array. + + + The processing result. + + + + + Reverts order of elements in the list + + Erich Eichinger + $Id: ReverseProcessor.cs,v 1.1 2008/03/20 23:58:16 oakinger Exp $ + + + + Processes a list of source items and returns a result. + + + The source list to process. + + + An optional processor arguments array. + + + The processing result. + + + + + Represents parsed default node in the navigation expression. + + Aleksandar Seovic + $Id: DefaultNode.cs,v 1.3 2007/09/07 03:01:24 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns left operand if it is not null, or the right operand if it is. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Exception thrown on a mismatch when trying to set a property + or resolve an argument to a method invocation. + + Rod Johnson + Juergen Hoeller + Mark Pollack (.NET) + $Id: TypeMismatchException.cs,v 1.1 2007/07/31 00:08:42 markpollack Exp $ + + + + Superclass for exceptions related to a property access, such as a + mismatch or a target invocation exception. + + Rod Johnson + Mark Pollack (.NET) + $Id: PropertyAccessException.cs,v 1.2 2007/07/31 03:47:22 markpollack Exp $ + + + + Superclass for all exceptions thrown in the Objects namespace and sub-namespaces. + + Rod Johnson + Mark Pollack (.NET) + $Id: ReflectionException.cs,v 1.1 2007/07/31 03:47:23 markpollack Exp $ + + + + Creates a new instance of the ObjectsException class. + + + + Creates a new instance of the ObjectsException class. with the specified message. + + + A message about the exception. + + + + + Creates a new instance of the ObjectsException class with the specified message + and root cause. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ObjectsException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Interface that can be implemented by exceptions etc that are error coded. + + +

    + The error code is a , rather than a number, so it can + be given user-readable values, such as "object.failureDescription". +

    +
    + Rod Johnson + Aleksandar Seovic (.Net) + $Id: IErrorCoded.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ +
    + + + Return the error code associated with this failure. + + +

    + The GUI can render this anyway it pleases, allowing for I18n etc. +

    +
    + + The error code associated with this failure, + or the empty string instance if not error-coded. + +
    + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + Create a new instance of the PropertyAccessException class. + + + A message about the exception. + + Describes the change attempted on the property. + + + + Create a new instance of the PropertyAccessException class. + + + A message about the exception. + + Describes the change attempted on the property. + + The root exception that is being wrapped. + + + + + Creates a new instance of the PropertyAccessException class. + + + + + Creates a new instance of the PropertyAccessException class. + + + A message about the exception. + + + + + Creates a new instance of the PropertyAccessExceptionsException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the PropertyAccessExceptionsException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Returns the PropertyChangeEventArgs that resulted in the problem. + + + + + The string error code used to classify the error. + + + + + Creates a new instance of the TypeMismatchException class. + + + + + Creates a new instance of the TypeMismatchException class. + + + A message about the exception. + + + + + Creates a new instance of the TypeMismatchException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the TypeMismatchException class describing the + property and required type that could not used to set a property on the target object. + + + The description of the property that was to be changed. + + The target conversion type. + + + + Creates a new instance of the TypeMismatchException class describing the + property, required type, and underlying exception that could not be used + to set a property on the target object. + + + The description of the property that was to be changed. + + The target conversion type. + The underlying exception. + + + + Creates a new instance of the TypeMismatchException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + The string error code used to classify the exception. + + + + + Criteria that is satisfied if the of each of the + parameters of a given matches each + of the parameter s of a given + . + + +

    + If no array is passed to the overloaded constructor, + any method that has no parameters will satisfy an instance of this + class. The same effect could be achieved by passing the + array to the overloaded constructor. +

    +
    + Rick Evans + $Id: MethodParametersCriteria.cs,v 1.2 2007/09/20 14:20:46 bbaia Exp $ +
    + + + The criteria for an arbitrary filter. + + Rick Evans + $Id: ICriteria.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ + + + + Does the supplied satisfy the criteria + encapsulated by this instance? + + + The datum to be checked by this criteria instance. + + + if the supplied + satisfies the criteria encapsulated by this instance; + if not, or the supplied + is . + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + +

    + If the supplied array is null, then this + constructor uses the array. +

    +
    + + The array that this criteria will use to + check parameter s. + +
    + + + Does the supplied satisfy the criteria encapsulated by + this instance? + + +

    + This implementation respects the inheritance chain of any parameter + s... i.e. methods that have a base type (or + interface) that is assignable to the in the + same corresponding index of the parameter types will satisfy this + criteria instance. +

    +
    + The datum to be checked by this criteria instance. + + True if the supplied satisfies the criteria encapsulated + by this instance; false if not or the supplied is null. + +
    + + + Configuration section handler for the Spring.NET typeAliases + config section. + + +

    + Type aliases can be used instead of fully qualified type names anywhere + a type name is expected in a Spring.NET configuration file. +

    +

    + This includes type names specified within an object definition, as well + as values of the properties or constructor arguments that expect + instances. +

    +
    + +

    + The following example shows how to configure both this section handler and + how to define type aliases within a Spring.NET config section: +

    + + + + +
    + + + + + + + ... + + ... + + + + + Aleksandar Seovic + $Id: TypeAliasesSectionHandler.cs,v 1.8 2007/07/31 18:15:50 bbaia Exp $ + + + + + Populates using values specified in + the typeAliases config section. + + + The configuration settings in a corresponding parent + configuration section. + + + The configuration context when called from the ASP.NET + configuration system. Otherwise, this parameter is reserved and + is . + + + The for the section. + + + This method always returns , because the + is populated as a side-effect of this + object's execution and thus there is no need to return anything. + + + + + Handler for Spring.NET resourceHandlers config section. + + +

    + Spring allows registration of custom resource handlers that can be used to load + object definitions from. +

    +

    + For example, if you wanted to store your object definitions in a database instead + of in the config file, you could write a custom implementation + and register it with Spring using 'db' as a protocol name. +

    +

    + Afterwards, you would simply specify resource URI within the context config element + using your custom resource handler. +

    +
    + +

    + The following example shows how to configure both this section handler, + how to define custom resource within Spring config section, and how to load + object definitions using custom resource handler: +

    + + + + +
    + + + + + + + + + + + + + + Aleksandar Seovic + $Id: ResourceHandlersSectionHandler.cs,v 1.3 2007/08/08 17:46:37 bbaia Exp $ + + + + + Registers resource handlers that are specified in + the resources config section with the . + + + The configuration settings in a corresponding parent + configuration section. Ignored. + + + The configuration context when called from the ASP.NET + configuration system. Otherwise, this parameter is reserved and + is . + + + The for the section. + + + This method always returns null, because resource handlers are registered + as a sideffect of its execution and there is no need to return anything. + + + + + Provides the means to configure an application context in addition to + the methods exposed on the + interface. + + +

    + This interface is to be implemented by most (if not all) + implementations. +

    +

    + Configuration and lifecycle methods are encapsulated here to avoid + making them obvious to + client code. +

    +

    + Calling will close this + application context, releasing all resources and locks that the + implementation might hold. This includes disposing all cached + singleton objects. +

    + + does not invoke the + attendant on any parent + context. + +
    + Juergen Hoeller + Mark Pollack (.NET) + $Id: IConfigurableApplicationContext.cs,v 1.10 2006/04/09 07:18:38 markpollack Exp $ + + +
    + + + The central interface to Spring.NET's IoC container. + + +

    + implementations + provide: + + + + Object factory functionality inherited from the + + and + interfaces. + + + + + The ability to resolve messages, supporting internationalization. + Inherited from the + interface. + + + + + The ability to load file resources in a generic fashion. + Inherited from the + interface. + + + + + Acts an an event registry for supporting loosely coupled eventing + between objecs. Inherited from the + interface. + + + + + The ability to raise events related to the context lifecycle. Inherited + from the + interface. + + + + + Inheritance from a parent context. Definitions in a descendant context + will always take priority. + + + +

    +

    + In addition to standard object factory lifecycle capabilities, + implementations need + to detect + , + , and + objects and supply + their attendant dependencies accordingly. +

    +

    + This interface is the central client interface in Spring.NET's IoC + container implementation. As such it does inherit a quite sizeable + number of interfaces; implementations are strongly encouraged to use + composition to satisfy each of the inherited interfaces (where + appropriate of course). +

    +
    + Rod Johnson + Juergen Hoeller + Mark Pollack (.NET) + + + + + $Id: IApplicationContext.cs,v 1.15 2007/08/08 17:46:37 bbaia Exp $ +
    + + + Describes an object that can resolve messages. + + +

    + This enables the parameterization and internationalization of messages. +

    +

    + Spring.NET provides one out-of-the-box implementation for production + use: +

      +
    • .
    • +
    +

    +
    + Rod Johnson + Juergen Hoeller + Mark Pollack (.NET) + Aleksandar Seovic (.NET) + $Id: IMessageSource.cs,v 1.13 2007/07/02 21:24:39 markpollack Exp $ + +
    + + + Resolve the message identified by the supplied + . + + +

    + If the lookup is not successful, implementations are permitted to + take one of two actions. +

    + + + Throw an exception. + + + + Return the supplied as is. + + + +
    + The name of the message to resolve. + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + +
    + + + Resolve the message identified by the supplied + . + + +

    + If the lookup is not successful, implementations are permitted to + take one of two actions. +

    + + + Throw an exception. + + + + Return the supplied as is. + + + +
    + The name of the message to resolve. + + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + +
    + + + Resolve the message identified by the supplied + . + + + Note that the fallback behavior based on CultureInfo seem to + have a bug that is fixed by installed .NET 1.1 Service Pack 1. +

    + If the lookup is not successful, implementations are permitted to + take one of two actions. +

    + + + Throw an exception. + + + + Return the supplied as is. + + + +
    + The name of the message to resolve. + + The that represents + the culture for which the resource is localized. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + +
    + + + Resolve the message identified by the supplied + . + + + Note that the fallback behavior based on CultureInfo seem to + have a bug that is fixed by installed .NET 1.1 Service Pack 1. +

    + If the lookup is not successful, implementations are permitted to + take one of two actions. +

    + + + Throw an exception. + + + + Return the supplied as is. + + + +
    + The name of the message to resolve. + + The that represents + the culture for which the resource is localized. + + + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + +
    + + + Resolve the message identified by the supplied + . + + + Note that the fallback behavior based on CultureInfo seem to + have a bug that is fixed by installed .NET 1.1 Service Pack 1. +

    + If the lookup is not successful, implementations are permitted to + take one of two actions. +

    + + + Throw an exception. + + + + Return the supplied as is. + + + +
    + The name of the message to resolve. + The default message if name is not found. + + The that represents + the culture for which the resource is localized. + + + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + +
    + + + Resolve the message using all of the attributes contained within + the supplied + argument. + + + The value object storing those attributes that are required to + properly resolve a message. + + + The that represents + the culture for which the resource is localized. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If the message could not be resolved. + + + + + Gets a localized resource object identified by the supplied + . + + +

    + This method must use the + + value to obtain a resource. +

    +

    + Examples of resources that may be resolved by this method include + (but are not limited to) objects such as icons and bitmaps. +

    +
    + + The name of the resource object to resolve. + + + The resolved object, or if not found. + +
    + + + Gets a localized resource object identified by the supplied + . + + +

    + Examples of resources that may be resolved by this method include + (but are not limited to) objects such as icons and bitmaps. +

    +
    + + The name of the resource object to resolve. + + + The with which the + resource is associated. + + + The resolved object, or if not found. + +
    + + + Applies resources to object properties. + + +

    + Resource key names are of the form objectName.propertyName. +

    +
    + + An object that contains the property values to be applied. + + + The base name of the object to use for key lookup. + + + The with which the + resource is associated. + +
    + + + Encapsulates event publication functionality. + + +

    + Serves as a super-interface for the + interface. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: IApplicationEventPublisher.cs,v 1.2 2006/04/09 07:18:38 markpollack Exp $ +
    + + + Publishes an application context event. + + + The source of the event. May be . + + + The event that is to be raised. + + + + + Describes an object that can load + s. + + +

    + An implementation is + generally required to support the functionality described by this + interface. +

    +

    + The class is a + standalone implementation that is usable outside an + ; the aforementioned + class is also used by the + class. +

    +
    + Juergen Hoeller + Mark Pollack (.NET) + $Id: IResourceLoader.cs,v 1.12 2007/08/08 17:46:55 bbaia Exp $ + + + +
    + + + Return an handle for the + specified resource. + + +

    + The handle should always be a reusable resource descriptor; this + allows one to make repeated calls to the underlying + . +

    +

    +

      +
    • + Must support fully qualified URLs, e.g. "file:C:/test.dat". +
    • +
    • + Should support relative file paths, e.g. "test.dat" (this will be + implementation-specific, typically provided by an + implementation). +
    • +
    +

    + + An handle does not imply an + existing resource; you need to check the value of an + 's + property to determine + conclusively whether or not the resource actually exists. + +
    + The resource location. + + An appropriate handle. + + + + +
    + + + A registry that manages subscriptions to and the + publishing of events. + + Griffin Caprio + $Id: IEventRegistry.cs,v 1.5 2006/04/09 07:18:47 markpollack Exp $ + + + + Publishes all events of the source object. + + + The source object containing events to publish. + + + + + Subscribes to all events published, if the subscriber + implements compatible handler methods. + + The subscriber to use. + + + + Subscribes to the published events of all objects of a given + , if the subscriber implements + compatible handler methods. + + The subscriber to use. + + The target to subscribe to. + + + + + Raised in response to an application context event. + + + + + Returns the date and time this context was loaded. + + +

    + This is to be set immediately after an + has been + instantiated and its configuration has been loaded. Implementations + are permitted to update this value if the context is reset or + refreshed in some way. +

    +
    + + The representing when this context + was loaded. + + +
    + + + Gets the parent context, or if there is no + parent context. + + +

    + If the parent context is , then this context + is the root of any context hierarchy. +

    +
    + + The parent context, or if there is no + parent. + +
    + + + Gets and sets a name for this context. + + + A name for this context. + + + + + Add an + + that will get applied to the internal object factory of this + application context on refresh, before any of the object + definitions are evaluated. + + +

    + To be invoked during context configuration. +

    +
    + + The factory processor to register. + + +
    + + + Load or refresh the persistent representation of the configuration, + which might an XML file, properties file, or relational database schema. + + + If the configuration cannot be loaded. + + + If the object factory could not be initialized. + + + + + Return the internal object factory of this application context. + + +

    + Can be used to access specific functionality of the factory. +

    + + This is just guaranteed to return an instance that is not + after the context has been refreshed + at least once. + + + Do not use this to post-process the object factory; singletons + will already have been instantiated. Use an + + to intercept the object factory setup process before objects even + get touched. + +
    + +
    + + + Sets the parent of this application context. + + + + The parent should not be changed: it should only be set + outside a constructor if it isn't available when an instance of + this class is created. + + + + The parent context. + + + + + Implements an immutable (read-only) + wrapper. + + +

    + Although this class is advertised as immutable, it really isn't. + Anyone with access to the wrapped + can still change the data. So + is not implemented for this , as + is the case for all + implementations in this library. This design decision was based on the + efficiency of not having to clone the wrapped + every time you wrap a mutable + . +

    +
    + $Id: ImmutableSet.cs,v 1.6 2007/03/16 04:01:28 aseovic Exp $ +
    + + + A collection that contains no duplicate elements. + + + $Id: Set.cs,v 1.7 2007/03/16 04:01:28 aseovic Exp $ + + + + A collection that contains no duplicate elements. + + +

    + This interface models the mathematical + abstraction. The order of + elements in a set is dependant on (a)the data-structure implementation, and + (b)the implementation of the various + methods, and thus is not + guaranteed. +

    +

    + overrides the + method to test for "equivalency": + whether the two sets contain the same elements. The "==" and "!=" + operators are not overridden by design, since it is often desirable to + compare object references for equality. +

    +

    + Also, the method is not + implemented on any of the set implementations, since none of them are + truly immutable. This is by design, and it is the way almost all + collections in the .NET framework function. So as a general rule, don't + store collection objects inside + instances. You would typically want to use a keyed + instead. +

    +

    + None of the implementations in + this library are guaranteed to be thread-safe in any way unless wrapped + in a . +

    +

    + The following table summarizes the binary operators that are supported + by the class. +

    + + + Operation + Description + Method + + + Union (OR) + + Element included in result if it exists in either A OR + B. + + Union() + + + Intersection (AND) + + Element included in result if it exists in both A AND + B. + + InterSect() + + + Exclusive Or (XOR) + + Element included in result if it exists in one, but not both, + of A and B. + + ExclusiveOr() + + + Minus (n/a) + + Take all the elements in A. Now, if any of them exist in + B, remove them. Note that unlike the other operators, + A - B is not the same as B - A. + + Minus() + + +
    + $Id: ISet.cs,v 1.5 2006/04/09 07:18:37 markpollack Exp $ +
    + + + Performs a "union" of the two sets, where all the elements + in both sets are present. + + +

    + That is, the element is included if it is in either + or this set. Neither this set nor the input + set are modified during the operation. The return value is a + clone of this set with the extra elements added in. +

    +
    + A collection of elements. + + A new containing the union of + this with the specified + collection. Neither of the input objects is modified by the union. + +
    + + + Performs an "intersection" of the two sets, where only the elements + that are present in both sets remain. + + +

    + That is, the element is included if it exists in both sets. The + Intersect() operation does not modify the input sets. It + returns a clone of this set with the appropriate elements + removed. +

    +
    + A set of elements. + + The intersection of this set with . + +
    + + + Performs a "minus" of this set from the + set. + + +

    + This returns a set of all the elements in set + , removing the elements that are also in + this set. The original sets are not modified during this operation. + The result set is a clone of this + containing the elements from + the operation. +

    +
    + A set of elements. + + A set containing the elements from this set with the elements in + removed. + +
    + + + Performs an "exclusive-or" of the two sets, keeping only those + elements that are in one of the sets, but not in both. + + +

    + The original sets are not modified during this operation. The + result set is a clone of this set containing the elements + from the exclusive-or operation. +

    +
    + A set of elements. + + A set containing the result of + ^ this. + +
    + + + Returns if this set contains the specified + element. + + The element to look for. + + if this set contains the specified element. + + + + + Returns if the set contains all the + elements in the specified collection. + + A collection of objects. + + if the set contains all the elements in the + specified collection. + + + + + Adds the specified element to this set if it is not already present. + + The object to add to the set. + + is the object was added, + if the object was already present. + + + + + Adds all the elements in the specified collection to the set if + they are not already present. + + A collection of objects to add to the set. + + is the set changed as a result of this + operation. + + + + + Removes the specified element from the set. + + The element to be removed. + + if the set contained the specified element. + + + + + Remove all the specified elements from this set, if they exist in + this set. + + A collection of elements to remove. + + if the set was modified as a result of this + operation. + + + + + Retains only the elements in this set that are contained in the + specified collection. + + + The collection that defines the set of elements to be retained. + + + if this set changed as a result of this + operation. + + + + + Removes all objects from this set. + + + + + Returns if this set contains no elements. + + + + + Performs a "union" of the two sets, where all the elements + in both sets are present. + + A collection of elements. + + A new containing the union of + this with the specified + collection. Neither of the input objects is modified by the union. + + + + + + Performs a "union" of two sets, where all the elements in both are + present. + + +

    + That is, the element is included if it is in either + or . The return + value is a clone of one of the sets ( + if it is not ) with elements of the other set + added in. Neither of the input sets is modified by the operation. +

    +
    + A set of elements. + A set of elements. + + A set containing the union of the input sets; + if both sets are . + +
    + + + Performs a "union" of two sets, where all the elements in both are + present. + + A set of elements. + A set of elements. + + A set containing the union of the input sets; + if both sets are . + + + + + + Performs an "intersection" of the two sets, where only the elements + that are present in both sets remain. + + A set of elements. + + The intersection of this set with . + + + + + + Performs an "intersection" of the two sets, where only the elements + that are present in both sets remain. + + +

    + That is, the element is included only if it exists in both + and . Neither input + object is modified by the operation. The result object is a + clone of one of the input objects ( + if it is not ) containing the elements from + the intersect operation. +

    +
    + A set of elements. + A set of elements. + + The intersection of the two input sets; if + both sets are . + +
    + + + Performs an "intersection" of the two sets, where only the elements + that are present in both sets remain. + + A set of elements. + A set of elements. + + The intersection of the two input sets; if + both sets are . + + + + + + Performs a "minus" of this set from the + set. + + A set of elements. + + A set containing the elements from this set with the elements in + removed. + + + + + + Performs a "minus" of set from set + . + + +

    + This returns a set of all the elements in set + , removing the elements that are also in + set . The original sets are not modified + during this operation. The result set is a clone of set + containing the elements from the operation. +

    +
    + A set of elements. + A set of elements. + + A set containing + - elements. + if is + . + +
    + + + Performs a "minus" of set from set + . + + A set of elements. + A set of elements. + + A set containing + - elements. + if is + . + + + + + + Performs an "exclusive-or" of the two sets, keeping only those + elements that are in one of the sets, but not in both. + + A set of elements. + + A set containing the result of + ^ this. + + + + + + Performs an "exclusive-or" of the two sets, keeping only those + elements that are in one of the sets, but not in both. + + +

    + The original sets are not modified during this operation. The + result set is a clone of one of the sets ( + if it is not ) + containing the elements from the exclusive-or operation. +

    +
    + A set of elements. + A set of elements. + + A set containing the result of + ^ . + if both sets are . + +
    + + + Performs an "exclusive-or" of the two sets, keeping only those + elements that are in one of the sets, but not in both. + + A set of elements. + A set of elements. + + A set containing the result of + ^ . + if both sets are . + + + + + + Adds the specified element to this set if it is not already present. + + The object to add to the set. + + is the object was added, + if the object was already present. + + + + + Adds all the elements in the specified collection to the set if + they are not already present. + + A collection of objects to add to the set. + + is the set changed as a result of this + operation. + + + + + Removes all objects from this set. + + + + + Returns if this set contains the specified + element. + + The element to look for. + + if this set contains the specified element. + + + + + Returns if the set contains all the + elements in the specified collection. + + A collection of objects. + + if the set contains all the elements in the + specified collection. + + + + + Removes the specified element from the set. + + The element to be removed. + + if the set contained the specified element. + + + + + Remove all the specified elements from this set, if they exist in + this set. + + A collection of elements to remove. + + if the set was modified as a result of this + operation. + + + + + Retains only the elements in this set that are contained in the + specified collection. + + + The collection that defines the set of elements to be retained. + + + if this set changed as a result of this + operation. + + + + + Returns a clone of the + instance. + + +

    + This will work for derived + classes if the derived class implements a constructor that takes no + arguments. +

    +
    + A clone of this object. +
    + + + Copies the elements in the to + an array. + + +

    + The type of array needs to be compatible with the objects in the + , obviously. +

    +
    + + An array that will be the target of the copy operation. + + + The zero-based index where copying will start. + +
    + + + Gets an enumerator for the elements in the + . + + + An over the elements + in the . + + + + + This method will test the + against another for + "equality". + + +

    + In this case, "equality" means that the two sets contain the same + elements. The "==" and "!=" operators are not overridden by design. + If you wish to check for "equivalent" + instances, use + Equals(). If you wish to check to see if two references are + actually the same object, use "==" and "!=". +

    +
    + + A object to compare to. + + + if the two sets contain the same elements. + +
    + + + Gets the hashcode for the object. + + + + + Returns if this set contains no elements. + + + + + The number of elements currently contained in this collection. + + + + + Returns if the + is synchronized across + threads. + + +

    + Note that enumeration is inherently not thread-safe. Use the + to lock the object during enumeration. +

    +
    +
    + + + An object that can be used to synchronize this collection to make + it thread-safe. + + +

    + When implementing this, if your object uses a base object, like an + , or anything that has + a SyncRoot, return that object instead of "this". +

    +
    + + An object that can be used to synchronize this collection to make + it thread-safe. + +
    + + + Constructs an immutable (read-only) + wrapper. + + + The that is to be wrapped. + + + + + Adds the specified element to this set if it is not already present. + + The object to add to the set. + + is the object was added, + if the object was already present. + + + + + + Adds all the elements in the specified collection to the set if + they are not already present. + + A collection of objects to add to the set. + + is the set changed as a result of this + operation. + + + + + + Removes all objects from this set. + + + + + + Returns if this set contains the specified + element. + + The element to look for. + + if this set contains the specified element. + + + + + Returns if the set contains all the + elements in the specified collection. + + A collection of objects. + + if the set contains all the elements in the + specified collection. + + + + + Removes the specified element from the set. + + The element to be removed. + + if the set contained the specified element. + + + + + + Remove all the specified elements from this set, if they exist in + this set. + + A collection of elements to remove. + + if the set was modified as a result of this + operation. + + + + + + Retains only the elements in this set that are contained in the + specified collection. + + + The collection that defines the set of elements to be retained. + + + if this set changed as a result of this + operation. + + + + + + Copies the elements in the to + an array. + + +

    + The type of array needs to be compatible with the objects in the + , obviously. +

    +
    + + An array that will be the target of the copy operation. + + + The zero-based index where copying will start. + +
    + + + Gets an enumerator for the elements in the + . + + + An over the elements + in the . + + + + + Returns a clone of the + instance. + + A clone of this object. + + + + Performs a "union" of the two sets, where all the elements + in both sets are present. + + A collection of elements. + + A new containing the union of + this with the specified + collection. Neither of the input objects is modified by the union. + + + + + + Performs an "intersection" of the two sets, where only the elements + that are present in both sets remain. + + A set of elements. + + The intersection of this set with . + + + + + + Performs a "minus" of this set from the + set. + + A set of elements. + + A set containing the elements from this set with the elements in + removed. + + + + + + Performs an "exclusive-or" of the two sets, keeping only those + elements that are in one of the sets, but not in both. + + A set of elements. + + A set containing the result of + ^ this. + + + + + + Returns if this set contains no elements. + + + + + The number of elements currently contained in this collection. + + + + + Returns if the + is synchronized across + threads. + + +

    + Note that enumeration is inherently not thread-safe. Use the + to lock the object during enumeration. +

    +
    +
    + + + An object that can be used to synchronize this collection to make + it thread-safe. + + + An object that can be used to synchronize this collection to make + it thread-safe. + + + + + is an + class that supports the creation of new + types where the underlying data + store is an instance. + + +

    + You can use any object that implements the + interface to hold set + data. You can define your own, or you can use one of the objects + provided in the framework. The type of + you + choose will affect both the performance and the behavior of the + using it. +

    +

    + This object overrides the method, + but not the method, because + the class is mutable. + Therefore, it is not safe to use as a key value in a dictionary. +

    +

    + To make a typed based on your + own , simply derive a new + class with a constructor that takes no parameters. Some + implmentations cannot be defined + with a default constructor. If this is the case for your class, you + will need to override clone as well. +

    +

    + It is also standard practice that at least one of your constructors + takes an or an + as an argument. +

    +
    + + $Id: DictionarySet.cs,v 1.7 2007/03/16 04:01:27 aseovic Exp $ +
    + + + Adds the specified element to this set if it is not already present. + + The object to add to the set. + + is the object was added, + if the object was already present. + + + + + Adds all the elements in the specified collection to the set if + they are not already present. + + A collection of objects to add to the set. + + is the set changed as a result of this + operation. + + + + + Removes all objects from this set. + + + + + Returns if this set contains the specified + element. + + The element to look for. + + if this set contains the specified element. + + + + + Returns if the set contains all the + elements in the specified collection. + + A collection of objects. + + if the set contains all the elements in the + specified collection; also if the + supplied is . + + + + + Removes the specified element from the set. + + The element to be removed. + + if the set contained the specified element. + + + + + Remove all the specified elements from this set, if they exist in + this set. + + A collection of elements to remove. + + if the set was modified as a result of this + operation. + + + + + Retains only the elements in this set that are contained in the + specified collection. + + + The collection that defines the set of elements to be retained. + + + if this set changed as a result of this + operation. + + + + + Copies the elements in the to + an array. + + +

    + The type of array needs to be compatible with the objects in the + , obviously. +

    +
    + + An array that will be the target of the copy operation. + + + The zero-based index where copying will start. + +
    + + + Gets an enumerator for the elements in the + . + + + An over the elements + in the . + + + + + Provides the storage for elements in the + , stored as the key-set + of the object. + + +

    + Set this object in the constructor if you create your own + class. +

    +
    +
    + + + The placeholder object used as the value for the + instance. + + + There is a single instance of this object globally, used for all + s. + + + + + Returns if this set contains no elements. + + + + + The number of elements currently contained in this collection. + + + + + Returns if the + is synchronized across + threads. + + + + + + An object that can be used to synchronize this collection to make + it thread-safe. + + + An object that can be used to synchronize this collection to make + it thread-safe. + + + + + + Represents a reference to an externally defined validator object + + +

    + This class allows validation groups to reference validators that + are defined outside of the group itself. +

    +

    + It also allows users to narrow the context for the referenced validator + by specifying value for the Context property. +

    +
    + Aleksandar Seovic + $Id: ValidatorReference.cs,v 1.5 2008/02/05 20:40:26 aseovic Exp $ +
    + + + Interface to be implemented by objects that wish to be aware of their owning + . + + +

    + For example, objects can look up collaborating objects via the factory. +

    +

    + Note that most objects will choose to receive references to collaborating + objects via respective properties and / or an appropriate constructor. +

    +

    + For a list of all object lifecycle methods, see the + API documentation. +

    +
    + Rod Johnson + Rick Evans (.NET) + $Id: IObjectFactoryAware.cs,v 1.6 2006/04/09 07:18:48 markpollack Exp $ +
    + + + Callback that supplies the owning factory to an object instance. + + + Owning + (may not be ). The object can immediately + call methods on the factory. + + +

    + Invoked after population of normal object properties but before an init + callback like 's + + method or a custom init-method. +

    +
    + + In case of initialization errors. + +
    + + + Initializes a new instance of the class. + + + + + Validates the specified object. + + The object to validate. + instance to add error messages to. + True if validation was successful, False otherwise. + + + + Validates the specified object. + + The object to validate. + Additional context parameters. + instance to add error messages to. + True if validation was successful, False otherwise. + + + + Gets or sets the name of the referenced validator. + + The name of the referenced validator. + + + + Gets or sets the expression that should be used to narrow validation context. + + The expression that should be used to narrow validation context. + + + + Callback that supplies the owning factory to an object instance. + + + Owning + (may not be ). The object can immediately + call methods on the factory. + + +

    + Invoked after population of normal object properties but before an init + callback like 's + + method or a custom init-method. +

    +
    + + In case of initialization errors. + +
    + + Utility methods for simple pattern matching, in particular for + Spring's typical "xxx*", "*xxx" and "*xxx*" pattern styles. + + Juergen Hoeller + Mark Pollack + $Id: PatternMatchUtils.cs,v 1.4 2007/07/31 18:16:25 bbaia Exp $ + + + Match a String against the given pattern, supporting the following simple + pattern styles: "xxx*", "*xxx" and "*xxx*" matches, as well as direct equality. + + the pattern to match against + + the String to match + + whether the String matches the given pattern + + + + Match a String against the given patterns, supporting the following simple + pattern styles: "xxx*", "*xxx" and "*xxx*" matches, as well as direct equality. + + the patterns to match against + + the String to match + + whether the String matches any of the given patterns + + + + + Thrown on an unrecoverable problem encountered in the + objects namespace or sub-namespaces, e.g. bad class or field. + + Rod Johnson + Mark Pollack (.NET) + $Id: FatalReflectionException.cs,v 1.1 2007/07/31 03:47:23 markpollack Exp $ + + + + + Creates a new instance of the FatalObjectException class. + + + + + Creates a new instance of the FatalObjectException class with the + specified message. + + + A message about the exception. + + + + + Creates a new instance of the FatalObjectException class with the + specified message. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the FatalObjectException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Base class implementation for classes that describe an event handler. + + Rick Evans + $Id: AbstractEventHandlerValue.cs,v 1.7 2006/04/09 07:19:00 markpollack Exp $ + + + + Describes an event handler. + + Rick Evans + $Id: IEventHandlerValue.cs,v 1.6 2006/04/09 07:18:49 markpollack Exp $ + + + + Wires up the specified handler to the named event on the + supplied event source. + + + The object (an object instance, a , etc) + exposing the named event. + + + The handler for the event (an object instance, a + , etc). + + + + + The source of the event. + + + + + The name of the method that is going to handle the event. + + + + + The name of the event that is being wired up. + + + + + Creates a new instance of the + class. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    +
    + + + Creates a new instance of the + class. + + + The object (possibly unresolved) that is exposing the event. + + + The name of the method on the handler that is going to handle the event. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    +
    + + + Wires up the specified handler to the named event on the + supplied event source. + + + The object (an object instance, a , etc) + exposing the named event. + + + The handler for the event (an object instance, a + , etc). + + + + + Returns a stringified representation of this object. + + A stringified representation of this object. + + + + The source of the event (may be unresolved, as in the case + of a + value). + + + + + The name of the method that is going to handle the event. + + + + + The name of the event that is being wired up. + + + + + Tag subclass used to hold a set of managed elements. + + Juergen Hoeller + Rick Evans (.NET) + + + + Implements an that automatically + changes from a list based implementation to a hashtable based + implementation when the size reaches a certain threshold. + + +

    + This is good if you are unsure about whether you data-set will be tiny + or huge. +

    + + Because this uses a dual implementation, iteration order is not + guaranteed! + +
    + + $Id: HybridSet.cs,v 1.6 2007/03/16 04:01:28 aseovic Exp $ +
    + + + Creates a new set instance based on either a list or a hash table, + depending on which will be more efficient based on the data-set + size. + + + + + Creates a new set instance based on either a list or a hash table, + depending on which will be more efficient based on the data-set + size, and initializes it based on a collection of elements. + + + A collection of elements that defines the initial set contents. + + + + + Denotes a special placeholder collection that may contain + s or + other placeholder objects that will need to be resolved. + + +

    + 'A special placeholder collection' means that the elements of this + collection can be placeholders for objects that will be resolved later by + a Spring.NET IoC container, i.e. the elements themselves will be + resolved at runtime by the enclosing IoC container. +

    +

    + The core Spring.NET library already provides three implementations of this interface + straight out of the box; they are... +

    + + + + . + + + + + . + + + + + . + + + +

    + If you have a custom collection class (i.e. a class that either implements the + directly or derives from a class that does) + that you would like to expose as a special placeholder collection (i.e. one that can + have s as elements + that will be resolved at runtime by an appropriate Spring.NET IoC container, just + implement this interface. +

    +
    + +

    + Lets say one has a Bag class (i.e. a collection that supports bag style semantics). +

    + + using System; + + using Spring.Objects.Factory.Support; + + namespace MyNamespace + { + public sealed class Bag : ICollection + { + // ICollection implementation elided for clarity... + + public void Add(object o) + { + // implementation elided for clarity... + } + } + + public class ManagedBag : Bag, IManagedCollection + { + public ICollection Resolve( + string objectName, RootObjectDefinition definition, + string propertyName, ManagedCollectionElementResolver resolver) + { + Bag newBag = new Bag(); + string elementName = propertyName + "[bag-element]"; + foreach(object element in this) + { + object resolvedElement = resolver(objectName, definition, elementName, element); + newBag.Add(resolvedElement); + } + return newBag; + } + } + } + +
    + Rick Evans + $Id: IManagedCollection.cs,v 1.5 2007/03/16 03:05:37 bbaia Exp $ +
    + + + Resolves this managed collection at runtime. + + + The name of the top level object that is having the value of one of it's + collection properties resolved. + + + The definition of the named top level object. + + + The name of the property the value of which is being resolved. + + + The callback that will actually do the donkey work of resolving + this managed collection. + + A fully resolved collection. + + + + Resolves this managed collection at runtime. + + + The name of the top level object that is having the value of one of it's + collection properties resolved. + + + The definition of the named top level object. + + + The name of the property the value of which is being resolved. + + + The callback that will actually do the donkey work of resolving + this managed collection. + + A fully resolved collection. + + + + Gets or sets the unresolved name for the + of the elements of this managed set. + + The unresolved name for the type of the elements of this managed set. + + + + Convenience methods operating on object factories, returning object instances, + names, or counts. + + +

    + The nesting hierarchy of an object factory is taken into account by the various methods + exposed by this class. +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: ObjectFactoryUtils.cs,v 1.17 2007/07/31 03:47:39 markpollack Exp $ +
    + + + Used to dereference an + and distinguish it from managed objects created by the factory. + + +

    + For example, if the managed object identified as foo is a + factory, getting &foo will return the factory, not the + instance returned by the factory. +

    +
    +
    + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such has no publicly visible + constructors. +

    +
    +
    + + + Count all object definitions in any hierarchy in which this + factory participates. + + +

    + Includes counts of ancestor object factories. +

    +

    + Objects that are "overridden" (specified in a descendant factory + with the same name) are counted only once. +

    +
    + The object factory. + + The count of objects including those defined in ancestor factories. + +
    + + + Return all object names in the factory, including ancestor factories. + + The object factory. + The array of object names, or an empty array if none. + + + + Get all object names for the given type, including those defined in ancestor + factories. + + +

    + Will return unique names in case of overridden object definitions. +

    +

    + Does consider objects created by s + if is set to true, + which means that s will get initialized. +

    +
    + + If this isn't also an + , + this method will return the same as it's own + + method. + + + The that objects must match. + + + Whether to include prototype objects too or just singletons + (also applies to instances). + + + Whether to include instances + too or just normal objects. + + + The array of object names, or an empty array if none. + +
    + + + Get all object names for the given type, including those defined in ancestor + factories. + + +

    + Will return unique names in case of overridden object definitions. +

    +

    + Does consider objects created by s, + or rather it considers the type of objects created by + (which means that + s will be instantiated). +

    +
    + + If this isn't also an + , + this method will return the same as it's own + + method. + + + The that objects must match. + + + The array of object names, or an empty array if none. + +
    + + + Return all objects of the given type or subtypes, also picking up objects + defined in ancestor object factories if the current object factory is an + . + + +

    + The return list will only contain objects of this type. + Useful convenience method when we don't care about object names. +

    +
    + The object factory. + The of object to match. + + Whether to include prototype objects too or just singletons + (also applies to instances). + + + Whether to include instances + too or just normal objects. + + + If the objects could not be created. + + + The of object instances, or an + empty if none. + +
    + + + Return a single object of the given type or subtypes, also picking up objects defined + in ancestor object factories if the current object factory is an + . + + +

    + Useful convenience method when we expect a single object and don't care + about the object name. +

    +
    + The object factory. + The of object to match. + + Whether to include prototype objects too or just singletons + (also applies to instances). + + + Whether to include instances + too or just normal objects. + + + If the object could not be created. + + + If more than one instance of an object was found. + + + A single object of the given type or subtypes. + +
    + + + Return a single object of the given type or subtypes, not looking in + ancestor factories. + + +

    + Useful convenience method when we expect a single object and don't care + about the object name. +

    +
    + The object factory. + The of object to match. + + Whether to include prototype objects too or just singletons + (also applies to instances). + + + Whether to include instances + too or just normal objects. + + + If the object could not be created. + + + If not exactly one instance of an object was found. + + + A single object of the given type or subtypes. + +
    + + + Return a single object of the given type or subtypes, not looking in + ancestor factories. + + +

    + Useful convenience method when we expect a single object and don't care + about the object name. + This version of ObjectOfType automatically includes prototypes and + instances. +

    +
    + The object factory. + The of object to match. + + If the object could not be created. + + + If not exactly one instance of an object was found. + + + A single object of the given type or subtypes. + +
    + + + Return the object name, stripping out the factory dereference prefix if necessary. + + The name of the object. + The object name sans any factory dereference prefix. + + + + Given an (object) name, builds a corresponding factory object name such that + the return value can be used as a lookup name for a factory object. + + + The name to be used to build the resulting factory object name. + + + The transformed into its factory object name + equivalent. + + + + + + + Is the supplied a factory dereference? + + +

    + That is, does the supplied begin with + the + ? +

    +
    + The name to check. + + if the supplied is a + factory dereference; if not, or the + aupplied is or + consists solely of the + + value. + + +
    + + + Implementation of that + resolves variable name against special folders (as defined by + enumeration). + + Aleksandar Seovic + $Id: SpecialFolderVariableSource.cs,v 1.3 2007/08/02 22:18:32 markpollack Exp $ + + + + Defines contract that different variable sources have to implement. + + +

    + The "variable sources" are objects containing name-value pairs + that allow a variable value to be retrieved for the given name.

    +

    + Out of the box, Spring.NET supports a number of variable sources, + that allow users to obtain variable values from .NET config files, + Java-style property files, environment, registry, etc.

    +

    + Users can always write their own variable sources implementations, + that will allow them to load variable values from the database or + other proprietary data source.

    +
    + + + + + + + Aleksandar Seovic + $Id: IVariableSource.cs,v 1.3 2007/08/02 22:18:32 markpollack Exp $ +
    + + + Resolves variable value for the specified variable name. + + + The name of the variable to resolve. + + + The variable value if able to resolve, null otherwise. + + + + + Resolves specified special folder to its full path. + + + The name of the special folder to resolve. Should be one of the values + defined by the enumeration. + + + The folder path if able to resolve, null otherwise. + + + + + Implementation of that + resolves variable name against Java-style property file. + + + Aleksandar Seovic + $Id: PropertyFileVariableSource.cs,v 1.5 2007/08/08 17:47:13 bbaia Exp $ + + + + Resolves variable value for the specified variable name. + + + The name of the variable to resolve. + + + The variable value if able to resolve, null otherwise. + + + + + Initializes properties based on the specified + property file locations. + + + + + Gets or sets the locations of the property files + to read properties from. + + + The locations of the property files + to read properties from. + + + + + Convinience property. Gets or sets a single location + to read properties from. + + + A location to read properties from. + + + + + Base class that provides common functionality needed for several IObjectFactoryPostProcessor + implementations + + Mark Pollack + $Id: AbstractConfigurer.cs,v 1.3 2007/07/31 18:16:49 bbaia Exp $ + + + + Interface that can be implemented by objects that should be orderable, e.g. in an + . + + +

    + The actual order can be interpreted as prioritization, the first object (with the + lowest order value) having the highest priority. +

    +
    + Juergen Hoeller + Aleksandar Seovic (.Net) + $Id: IOrdered.cs,v 1.6 2007/05/26 00:42:36 markpollack Exp $ +
    + + + Return the order value of this object, where a higher value means greater in + terms of sorting. + + +

    + Normally starting with 0 or 1, with indicating + greatest. Same order values will result in arbitrary positions for the affected + objects. +

    +

    + Higher value can be interpreted as lower priority, consequently the first object + has highest priority. +

    +
    + The order value. +
    + + + Allows for custom modification of an application context's object + definitions, adapting the object property values of the context's + underlying object factory. + + +

    + Application contexts can auto-detect + IObjectFactoryPostProcessor objects in their object definitions and + apply them before any other objects get created. +

    +

    + Useful for custom config files targeted at system administrators that + override object properties configured in the application context. +

    +

    + See PropertyResourceConfigurer and its concrete implementations for + out-of-the-box solutions that address such configuration needs. +

    +
    + Juergen Hoeller + Rick Evans (.Net) +
    + + + Modify the application context's internal object factory after its + standard initialization. + + +

    + All object definitions will have been loaded, but no objects will have + been instantiated yet. This allows for overriding or adding properties + even to eager-initializing objects. +

    +
    + + The object factory used by the application context. + + + In case of errors. + +
    + + + Modify the application context's internal object factory after its + standard initialization. + + The object factory used by the application context. + +

    + All object definitions will have been loaded, but no objects will have + been instantiated yet. This allows for overriding or adding properties + even to eager-initializing objects. +

    +
    + + In case of errors. + +
    + + + Resolves the supplied into a + instance. + + The object that is to be resolved into a + instance. + The error context source. + The error context string. + A resolved . + +

    + This (default) implementation supports resolving + s and s. + Only override this method if you want to key your type alias + on something other than s + and s. +

    +
    + + If the supplied is , + or the supplied cannot be resolved. + +
    + + + Return the order value of this object, with a higher value meaning + greater in terms of sorting. + + The order value. + + + + + A implementation that enforces required properties to have been configured. + Required properties are detected through an attribute, by default, Spring's + attribute. + + + The motivation for the existence of this IObjectPostProcessor is to allow + developers to annotate the setter properties of their own classes with an + arbitrary attribute to indicate that the container must check + for the configuration of a dependency injected value. This neatly pushes + responsibility for such checking onto the container (where it arguably belongs), + and obviates the need (in part) for a developer to code a method that + simply checks that all required properties have actually been set. + + Please note that an 'init' method may still need to implemented (and may + still be desirable), because all that this class does is enforce that a + 'required' property has actually been configured with a value. It does + not check anything else... In particular, it does not check that a + configured value is not null. + + + Rob Harrop + Juergen Hoeller + Mark Pollack (.NET) + $Id: RequiredAttributeObjectPostProcessor.cs,v 1.2 2008/04/08 19:29:07 markpollack Exp $ + + + + Adapter that implements all methods on + as no-ops, which will not change normal processing of each object instantiated + by the container. Subclasses may override merely those methods that they are + actually interested in. + + + Note that this base class is only recommendable if you actually require + functionality. If all you need + is plain functionality, prefer a straight + implementation of that (simpler) interface. + + Rod Johnson + Juergen Hoeller + Mark Pollack (.NET) + $Id: InstantiationAwareObjectPostProcessorAdapter.cs,v 1.1 2008/04/02 18:02:24 markpollack Exp $ + + + + Extension of the interface, + adding a callback for predicting the eventual type of a processed object. + + This interface is a special purpose interface, mainly for + internal use within the framework. In general, application-provided + post-processors should simply implement the plain + interface or derive from the + class. New methods might be added to this interface even in point releases. + + + Juergen Hoeller + Mark Pollack (.NET) + $Id: SmartInstantiationAwareObjectPostProcessor.cs,v 1.1 2008/04/02 18:02:24 markpollack Exp $ + + + + Subinterface of + + that adds a before-instantiation callback. + + +

    + Typical use cases might include being used to suppress the default + instantiation of specific target objects, perhaps in favour of creating + proxies with special Spring.Aop.ITargetSources (pooling targets, + lazily initializing targets, etc). +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: IInstantiationAwareObjectPostProcessor.cs,v 1.4 2007/08/22 08:49:39 markpollack Exp $ +
    + + + Allows for custom modification of new object instances, e.g. + checking for marker interfaces or wrapping them with proxies. + + +

    + Application contexts can auto-detect + + objects in their object definitions and apply them before any other + objects get created. Plain object factories allow for programmatic + registration of post-processors. +

    +

    + Typically, post-processors that populate objects via marker interfaces + or the like will implement + , + and post-processors that wrap objects with proxies will normally implement + . +

    +
    + Juergen Hoeller + Aleksandar Seovic (.NET) + $Id: IObjectPostProcessor.cs,v 1.9 2007/08/22 08:49:39 markpollack Exp $ + +
    + + + Apply this + to the given new object instance before any object initialization callbacks. + + +

    + The object will already be populated with property values. + The returned object instance may be a wrapper around the original. +

    +
    + + The new object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + In case of errors. + +
    + + + Apply this to the + given new object instance after any object initialization callbacks. + + +

    + The object will already be populated with property values. The returned object + instance may be a wrapper around the original. +

    +
    + + The new object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + In case of errors. + +
    + + + Apply this + + before the target object gets instantiated. + + +

    + The returned object may be a proxy to use instead of the target + object, effectively suppressing the default instantiation of the + target object. +

    +

    + If the object is returned by this method is not + , the object creation process will be + short-circuited. The returned object will not be processed any + further; in particular, no further + + callbacks will be applied to it. This mechanism is mainly intended + for exposing a proxy instead of an actual target object. +

    +

    + This callback will only be applied to object definitions with an + object class. In particular, it will not be applied to + objects with a "factory-method" (i.e. objects that are to be + instantiated via a layer of indirection anyway). +

    +
    + + The of the target object that is to be + instantiated. + + + The name of the target object. + + + The object to expose instead of a default instance of the target + object. + + + In the case of any errors. + + + +
    + + + Perform operations after the object has been instantiated, via a constructor or factory method, + but before Spring property population (from explicit properties or autowiring) occurs. + + The object instance created, but whose properties have not yet been set + Name of the object. + true if properties should be set on the object; false if property population + should be skipped. Normal implementations should return true. Returning false will also + prevent any subsequent InstantiationAwareObjectPostProcessor instances from being + invoked on this object instance. + + + + Post-process the given property values before the factory applies them + to the given object. + + Allows for checking whether all dependencies have been + satisfied, for example based on a "Required" annotation on bean property setters. + Also allows for replacing the property values to apply, typically through + creating a new MutablePropertyValues instance based on the original PropertyValues, + adding or removing specific values. + + + The property values that the factory is about to apply (never null). + he relevant property infos for the target object (with ignored + dependency types - which the factory handles specifically - already filtered out) + The object instance created, but whose properties have not yet + been set. + Name of the object. + The actual property values to apply to the given object (can be the + passed-in PropertyValues instances0 or null to skip property population. + + + + Predicts the type of the object to be eventually returned from this + processors callback. + + The raw Type of the object. + Name of the object. + The type of the object, or null if not predictable. + in case of errors + + + + Determines the candidate constructors to use for the given object. + + The raw Type of the object. + Name of the object. + The candidate constructors, or null if none specified + in case of errors + + + + Predicts the type of the object to be eventually returned from this + processors PostProcessBeforeInstantiation callback. + + The raw Type of the object. + Name of the object. + The type of the object, or null if not predictable. + in case of errors + + + + Determines the candidate constructors to use for the given object. + + The raw Type of the object. + Name of the object. + The candidate constructors, or null if none specified + in case of errors + + + + Apply this + + before the target object gets instantiated. + + +

    + The returned object may be a proxy to use instead of the target + object, effectively suppressing the default instantiation of the + target object. +

    +

    + If the object is returned by this method is not + , the object creation process will be + short-circuited. The returned object will not be processed any + further; in particular, no further + + callbacks will be applied to it. This mechanism is mainly intended + for exposing a proxy instead of an actual target object. +

    +

    + This callback will only be applied to object definitions with an + object class. In particular, it will not be applied to + objects with a "factory-method" (i.e. objects that are to be + instantiated via a layer of indirection anyway). +

    +
    + + The of the target object that is to be + instantiated. + + + The name of the target object. + + + The object to expose instead of a default instance of the target + object. + + + In the case of any errors. + + + +
    + + + Perform operations after the object has been instantiated, via a constructor or factory method, + but before Spring property population (from explicit properties or autowiring) occurs. + + The object instance created, but whose properties have not yet been set + Name of the object. + true if properties should be set on the object; false if property population + should be skipped. Normal implementations should return true. Returning false will also + prevent any subsequent InstantiationAwareObjectPostProcessor instances from being + invoked on this object instance. + + + + Post-process the given property values before the factory applies them + to the given object. + + Allows for checking whether all dependencies have been + satisfied, for example based on a "Required" annotation on bean property setters. + Also allows for replacing the property values to apply, typically through + creating a new MutablePropertyValues instance based on the original PropertyValues, + adding or removing specific values. + + + The property values that the factory is about to apply (never null). + he relevant property infos for the target object (with ignored + dependency types - which the factory handles specifically - already filtered out) + The object instance created, but whose properties have not yet + been set. + Name of the object. + The actual property values to apply to the given object (can be the + passed-in PropertyValues instances0 or null to skip property population. + + + + Apply this + to the given new object instance before any object initialization callbacks. + + +

    + The object will already be populated with property values. + The returned object instance may be a wrapper around the original. +

    +
    + + The new object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + In case of errors. + +
    + + + Apply this to the + given new object instance after any object initialization callbacks. + + +

    + The object will already be populated with property values. The returned object + instance may be a wrapper around the original. +

    +
    + + The new object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + In case of errors. + +
    + + + Cache for validated object names, skipping re-validation for the same object + + + + + Post-process the given property values before the factory applies them + to the given object. Checks for the attribute specified by this PostProcessor's RequiredAttributeType. + + The property values that the factory is about to apply (never null). + The relevant property infos for the target object (with ignored + dependency types - which the factory handles specifically - already filtered out) + The object instance created, but whose properties have not yet + been set. + Name of the object. + + The actual property values to apply to the given object (can be the + passed-in PropertyValues instances or null to skip property population. + + If a required property value has not been specified + in the configuration metadata. + + + + Determines whether the supplied property is required to have a value, that is to be dependency injected. + + + This implementation looks for the existence of a "required" attribute on the supplied PropertyInfo and that + the property has a setter method. + + The target PropertyInfo + + true if the supplied property has been marked as being required;; otherwise, false if + not or if the supplied property does not have a setter method + + + + + Builds an exception message for the given list of invalid properties. + + The list of names of invalid properties. + Name of the object. + The exception message + + + + Sets the type of the required attribute, to be used on a property setter + + + The default required attribute type is the Spring-provided attribute. + This setter property exists so that developers can provide their own + (non-Spring-specific) annotation type to indicate that a property value is required. + + The type of the required attribute. + + + + Implementation of that can be used to + format and parse integer numbers. + + + + This formatter allows you to format and parse numbers that conform + to number style (leading and trailing + white space, leading sign). + + + Aleksandar Seovic + $Id: IntegerFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + + + + Interface that should be implemented by all formatters. + + + + Formatters assume that source value is a string, and make no assumptions + about the target value's type, which means that Parse method can return + object of any type. + + + Aleksandar Seovic + $Id: IFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + + + + Formats the specified value. + + The value to format. + Formatted . + + + + Parses the specified value. + + The value to parse. + Parsed . + + + + Initializes a new instance of the class, + using default format string of '{0:D}'. + + + + + Initializes a new instance of the class, + using specified format string. + + + + + Formats the specified integer value. + + The value to format. + Formatted integer number. + If is null. + If is not an integer number. + + + + Parses the specified integer value. + + The integer value to parse. + Parsed number value as a . + + + + Abstract base class that all localizers should extend + + +

    + This class contains the bulk of the localizer logic, including implementation + of the ApplyResources methods that are defined in + interface. +

    +

    + All specific localizers need to do is inherit this class and implement + GetResources method that will return a list of + objects that should be applied to a specified target. +

    +

    + Custom implementations can use whatever type of resource storage they want, + such as standard .NET resource sets, custom XML files, database, etc. +

    +
    + Aleksandar Seovic + $Id: AbstractLocalizer.cs,v 1.5 2007/07/24 17:26:25 oakinger Exp $ +
    + + + Defines an interface that localizers have to implement. + + +

    + Localizers are used to automatically apply resources to object's members + using reflection. +

    +
    + Aleksandar Seovic + $Id: ILocalizer.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ +
    + + + Applies resources of the specified culture to the specified target object. + + Target object to apply resources to. + instance to retrieve resources from. + Resource culture to use for resource lookup. + + + + Applies resources to the specified target object, using current thread's culture to resolve resources. + + Target object to apply resources to. + instance to retrieve resources from. + + + + Gets or sets the resource cache instance. + + The resource cache instance. + + + + Applies resources of the specified culture to the specified target object. + + Target object to apply resources to. + instance to retrieve resources from. + Resource culture to use for resource lookup. + + + + Applies resources to the specified target object, using current thread's uiCulture to resolve resources. + + Target object to apply resources to. + instance to retrieve resources from. + + + + Returns a list of instances that should be applied to the target. + + Target to get a list of resources for. + instance to retrieve resources from. + Resource locale. + A list of resources to apply. + + + + Loads resources from the storage and creates a list of instances that should be applied to the target. + + Target to get a list of resources for. + instance to retrieve resources from. + Resource locale. + A list of resources to apply. + + + + Gets or sets the resource cache instance. + + The resource cache instance. + + + + Represents logical "less than or equal" operator. + + Aleksandar Seovic + $Id: OpLessOrEqual.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical "less than or equal" operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents logical IN operator. + + Aleksandar Seovic + $Id: OpIn.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical IN operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + + true if the left operand is contained within the right operand, false otherwise. + + + + + Represents parsed selection node in the navigation expression. + + Aleksandar Seovic + $Id: SelectionLastNode.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns the last context item that matches selection expression. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Implementation of the minimum aggregator. + + Aleksandar Seovic + $Id: MinAggregator.cs,v 1.2 2006/12/04 08:58:33 aseovic Exp $ + + + + Returns the smallest item in the source collection. + + + The source collection to process. + + + Ignored. + + + The smallest item in the source collection. + + + + + Base implementation of the . + + Aleksandar Seovic + $Id: BaseBindingContainer.cs,v 1.2 2008/02/05 20:40:25 aseovic Exp $ + + + + An interface that has to be implemented by all data binding containers. + + Aleksandar Seovic + $Id: IBindingContainer.cs,v 1.1 2006/11/21 05:46:57 aseovic Exp $ + + + + An interface that defines the methods that have to be implemented by all data bindings. + + Aleksandar Seovic + $Id: IBinding.cs,v 1.3 2008/02/05 20:40:25 aseovic Exp $ + + + + Binds source object to target object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + + + Binds source object to target object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Binds target object to source object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + + + Binds target object to source object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Sets error message that should be displayed in the case + of a non-fatal binding error. + + + Resource ID of the error message. + + + List of error providers message should be added to. + + + + + Adds the binding. + + + Binding definition to add. + + + Added instance. + + + + + Adds the binding with a default + binding direction of . + + + This is a convinience method for adding SimpleExpressionBinding, + one of the most often used binding types, to the bindings list. + + + The source expression. + + + The target expression. + + + Added instance. + + + + + Adds the binding. + + + This is a convinience method for adding SimpleExpressionBinding, + one of the most often used binding types, to the bindings list. + + + The source expression. + + + The target expression. + + + Binding direction. + + + Added instance. + + + + + Adds the binding with a default + binding direction of . + + + This is a convinience method for adding SimpleExpressionBinding, + one of the most often used binding types, to the bindings list. + + + The source expression. + + + The target expression. + + + to use for value formatting and parsing. + + + Added instance. + + + + + Adds the binding. + + + This is a convinience method for adding SimpleExpressionBinding, + one of the most often used binding types, to the bindings list. + + + The source expression. + + + The target expression. + + + Binding direction. + + + to use for value formatting and parsing. + + + Added instance. + + + + + Gets a value indicating whether this data binding container + has bindings. + + + true if this data binding container has bindings; + false otherwise. + + + + + Creates a new instance of . + + + + + Adds the binding. + + + Binding definition to add. + + + Added instance. + + + + + Adds the binding with a default + binding direction of . + + + The source expression. + + + The target expression. + + + Added instance. + + + + + Adds the binding. + + + The source expression. + + + The target expression. + + + Binding direction. + + + Added instance. + + + + + Adds the binding with a default + binding direction of . + + + The source expression. + + + The target expression. + + + to use for value formatting and parsing. + + + Added instance. + + + + + Adds the binding. + + + The source expression. + + + The target expression. + + + Binding direction. + + + to use for value formatting and parsing. + + + Added instance. + + + + + Binds source object to target object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + + + Binds source object to target object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Binds target object to source object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + + + Binds target object to source object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Sets error message that should be displayed in the case + of a non-fatal binding error. + + + Resource ID of the error message. + + + List of error providers message should be added to. + + + + + Gets a list of bindings for this container. + + + A list of bindings for this container. + + + + + Gets a value indicating whether this instance has bindings. + + + true if this instance has bindings; otherwise, false. + + + + + Converter for to directly set a + property. + + Jurgen Hoeller + Mark Pollack (.NET) + + + + Create a new StreamConverter using the default + . + + + + + Create a new StreamConverter using the given + . + + + The to use. + + + + Returns whether this converter can convert an object of one + to a + + +

    + Currently only supports conversion from a + instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + True if the conversion is possible. +
    + + + Convert from a string value to a instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A if successful. + + + + + Criteria that is satisfied if the number of generic arguments to a given + matches an arbitrary number. + + +

    + This class supports checking the generic arguments count of both + generic methods and constructors. +

    +
    + Bruno Baia + $Id: MethodGenericArgumentsCountCriteria.cs,v 1.1 2007/08/04 01:04:33 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + +

    + This constructor sets the + + property to zero (0). +

    +
    +
    + + + Creates a new instance of the + class. + + + The number of generic arguments that a + must have to satisfy this criteria. + + + If the supplied is less + than zero. + + + + + Does the supplied satisfy the criteria encapsulated by + this instance? + + The datum to be checked by this criteria instance. + + True if the supplied satisfies the criteria encapsulated + by this instance; false if not or the supplied is null. + + + + + The number of generic arguments that a + must have to satisfy this criteria. + + + If the supplied value is less than zero. + + + + + Exception thrown when the ObjectFactory cannot load the specified type of a given object. + + Mark Pollack + $Id: CannotLoadObjectTypeException.cs,v 1.4 2007/08/01 23:24:11 bbaia Exp $ + + + + Initializes a new instance of the class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Initializes a new instance of the class. + + The resource description that the object definition came from. + Name of the object requested + Name of the object type. + The root cause. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + Gets he name of the object we are trying to load. + + The name of the object. + + + + Gets the name of the object type we are trying to load. + + The name of the object type. + + + + Gets the resource description that the object definition came from + + The resource description. + + + + Creates an instance + using context definitions supplied in a custom configuration and + configures the with that instance. + + + Implementations of the + interface must provide the following two constructors: + + + + A constructor that takes a string array of resource locations. + + + + + A constructor that takes a reference to a parent application context + and a string array of resource locations (and in that order). + + + +

    + Note that if the type attribute is not present in the declaration + of a particular context, then a default + + is assumed. This default + + is currently the + ; please note the exact + of this default is an + implementation detail, that, while unlikely, may do so in the future. + to +

    +
    + +

    + This is an example of specifying a context that reads its resources from + an embedded Spring.NET XML object configuration file... +

    + + + + +
    + + + + + + + + + +

    + This is an example of specifying a context that reads its resources from + a custom configuration section within the same application / web + configuration file and uses case insensitive object lookups. +

    +

    + Please note that you must adhere to the naming + of the various sections (i.e. '<sectionGroup name="spring">' and + '<section name="context">'. +

    + + + + +
    +
    + + + + + + + + + + + + +

    + And this is an example of specifying a hierarchy of contexts. The + hierarchy in this case is only a simple parent->child hierarchy, but + hopefully it illustrates the nesting of context configurations. This + nesting of contexts can be arbitrarily deep, and is one way... child + contexts know about their parent contexts, but parent contexts do not + know how many child contexts they have (if any), or have references + to any such child contexts. +

    + + + + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mark Pollack + Aleksandar Seovic + Rick Evans + $Id: ContextHandler.cs,v 1.36 2008/03/13 20:07:33 bbaia Exp $ + + + + + Creates an instance + using the context definitions supplied in a custom + configuration section. + + +

    + This instance is + also used to configure the . +

    +
    + + The configuration settings in a corresponding parent + configuration section. + + + The configuration context when called from the ASP.NET + configuration system. Otherwise, this parameter is reserved and + is . + + + The for the section. + + + An instance + populated with the object definitions supplied in the configuration + section. + +
    + + + Create all child-contexts in the given for the given context. + + The parent context to use + The current configContext + The list of child context elements + + + + Instantiates a new context. + + + + + Gets the context's name specified in the name attribute of the context element. + + The current configContext + The context element + + + + Extracts the context-type from the context element. + If none is specified, returns the parent's type. + + + + + Extracts the case-sensitivity attribute from the context element + + + + + Gets the context specified in the type + attribute of the context element. + + +

    + If this attribute is not defined it defaults to the + type. +

    +
    + + If the context type does not implement the + interface. + +
    + + + Returns the array of resources containing object definitions for + this context. + + + + + Returns the array of child contexts for this context. + + + + + The of + created if no type attribute is specified on a context element. + + + + + + Get the context's case-sensitivity to use if none is specified + + +

    + Derived handlers may override this property to change their default case-sensitivity. +

    +

    + Defaults to 'true'. +

    +
    +
    + + + Returns if the context should be lazily + initialized. + + + + + Constants defining the structure and values associated with the + schema for laying out Spring.NET contexts in XML. + + + + + Defines a single + . + + + + + Specifies a context name. + + + + + Specifies if context should be case sensitive or not. Default is true. + + + + + Specifies a . + + +

    + Does not have to be fully assembly qualified, but its generally regarded + as better form if the names of one's objects + are specified explicitly. +

    +
    +
    + + + Specifies whether context should be lazy initialized. + + + + + Defines an + + + + + Specifies the URI for an + . + + + + + To be implemented by any object that wishes to be notified + of the that it runs in. + + +

    + Implementing this interface makes sense when an object requires access + to a set of collaborating objects. Note that configuration via object + references is preferable to implementing this interface just for object + lookup purposes. +

    +

    + This interface can also be implemented if an object needs access to + file resources, i.e. wants to call + , or access to + the . However, it is + preferable to implement the more specific + + interface to receive a reference to the + object in that scenario. +

    +

    + Note that dependencies can also + be exposed as object properties of the + type, populated via strings with + automatic type conversion performed by an object factory. This obviates + the need for implementing any callback interface just for the purpose + of accessing a specific file resource. +

    +

    + + is a convenience implementation of this interface for your + application objects. +

    +

    + For a list of all object lifecycle methods, see the overview for the + interface. +

    +
    + Rod Johnson + Mark Pollack (.NET) + $Id: IApplicationContextAware.cs,v 1.8 2007/08/08 17:46:37 bbaia Exp $ + + + + $Id: IApplicationContextAware.cs,v 1.8 2007/08/08 17:46:37 bbaia Exp $ +
    + + + Set the that this + object runs in. + + +

    + Normally this call will be used to initialize the object. +

    +

    + Invoked after population of normal object properties but before an + init callback such as + 's + + or a custom init-method. Invoked after the setting of any + 's + + property. +

    +
    + + In the case of application context initialization errors. + + + If thrown by any application context methods. + + +
    + + + Perform email validations. + + +

    + This implementation is not guaranteed to catch all possible errors in an + email address. For example, an address like nobody@noplace.nowhere will + pass validator, even though there is no TLD "nowhere". + + Goran Milosavljevic + + +

    + Creates a new instance of the EmailValidator class. + +
    + + + Creates a new instance of the EmailValidator class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Creates a new instance of the EmailValidator class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Validates the supplied . + + + In the case of the class, + the test should be a string variable that will be evaluated and the object + obtained as a result of this evaluation will be checked if it is + a valid e-mail address. + + The object to validate. + + if the supplied is valid + e-mail address. + + + + + Regular expression used for validation of object passed to this . + + + + + Converts between instances of and their string representations. + + Erich Eichinger + $Id: UniqueKeyConverter.cs,v 1.1 2007/08/22 20:17:35 oakinger Exp $ + + + + Can we convert from the sourcetype to a ? + + +

    + Currently only supports conversion from a instance. +

    +
    + + A that provides a format context. + + + A that represents the you want to convert from. + + if the conversion is possible. +
    + + + Convert from a value to an instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A if successful, otherwise. + + The conversion cannot be performed. + + + + Returns whether this converter can convert the object to the specified type, using the specified context. + + An that provides a format context. + A that represents the type you want to convert to. + + true if this converter can perform the conversion; otherwise, false. + + + At the moment only conversion to string is supported. + + + + + Converts the given value object to the specified type, using the specified context and culture information. + + + + An that represents the converted value. + + + A . If null is passed, the current culture is assumed. + An that provides a format context. + The to convert the value parameter to. + The to convert. + The conversion cannot be performed. + The destinationType parameter is null. + + + + A simple pooling interface for managing and monitoring a pool + of objects. + + +

    + Based on the Jakarta Commons Pool API. +

    +
    + Federico Spinazzi + $Id: IObjectPool.cs,v 1.5 2005/08/13 18:04:54 springboy Exp $ + +
    + + + Obtain an instance from the pool. + + +

    + By contract, clients must return the borrowed + instance using + or a related method as defined in an implementation or + sub-interface. +

    +
    + An instance from the pool. + + In case the pool is unusable. + + +
    + + + Return an instance to the pool. + + +

    + By contract, the object must have been obtained using + + or a related method as defined in an implementation or sub-interface. +

    +
    + The instance to be returned to the pool. + +
    + + + Create an object using the factory set by + the property + or other implementation dependent mechanism + and place it into the pool. + + +

    + This is an optional operation. AddObject is useful for "pre-loading" a + pool with idle objects. +

    +
    + + If the implementation does not support the operation. + +
    + + + Close the pool and free any resources associated with it. + + + + + Clear objects sitting idle in the pool, releasing any + associated resources. + + +

    + This is an optional operation. +

    +
    + + If the implementation does not support the operation. + +
    + + + Gets the number of instances currently borrowed from the pool. + + +

    + This is an optional operation. +

    +
    + + If the implementation does not support the operation. + +
    + + + Gets the number of instances currently idle in the pool. + + +

    + This is an optional operation. +

    +

    + This may be considered an approximation of the number of objects + that can be borrowed without creating any new instances. +

    +
    + + If the implementation does not support the operation. + +
    + + + Set the factory used to create new instances. + + +

    + This is an optional operation. +

    +
    + + If the implementation does not support the operation. + +
    + + + A collection style container for + instances. + + Rod Johnson + Mark Pollack (.NET) + $Id: IPropertyValues.cs,v 1.7 2006/04/09 07:18:49 markpollack Exp $ + + + + Return the instance with the + given name. + + The name to search for. + the , or null if a + the with the supplied + did not exist in this collection. + + + + + Is there a instance for this + property name? + + The name to search for. + + True if there is a instance for + the supplied . + + + + + Return the difference (changes, additions, but not removals) of + property values between the supplied argument and the values + contained in the collection. + + +

    + Subclasses should also override Equals. +

    +
    + The old property values. + + An containing any changes, or + an empty instance if there were + no changes. + +
    + + + Return an array of the objects + held in this object. + + An array of the objects held + in this object. + + + + + Object definition reader for Spring's default XML object definition format. + + +

    + Typically applied to a + instance. +

    +

    + This class registers each object definition with the given object factory superclass, + and relies on the latter's implementation of the + interface. +

    +

    + It supports singletons, prototypes, and references to either of these kinds of object. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: XmlObjectDefinitionReader.cs,v 1.33 2008/02/04 22:44:03 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + The + instance that this reader works on. + + + + + Creates a new instance of the + class. + + + The + instance that this reader works on. + + + The to be used for parsing. + + + + + Load object definitions from the supplied XML . + + + The XML resource for the object definitions that are to be loaded. + + + The number of object definitions that were loaded. + + + In the case of loading or parsing errors. + + + + + Actually load object definitions from the specified XML file. + + The input stream to read from. + The resource for the XML data. + + + + + Validation callback for a validating XML reader. + + The source of the event. + Any data pertinent to the event. + + + + Register the object definitions contained in the given DOM document. + + The DOM document. + + The original resource from where the + was read. + + + The number of object definitions that were registered. + + + In case of parsing errors. + + + + + Creates the to use for actually + reading object definitions from an XML document. + + Default implementation instantiates the specified 'documentReaderType'. + + + + + Creates the to be passed along + during the object definition reading process. + + The underlying that is currently processed. + A new + + + + The to be used for parsing. + + + + + Sets the IObjectDefinitionDocumentReader implementation to use, responsible for + the actual reading of the XML object definition document.stype of the document reader. + + The type of the document reader. + + + + Exception thrown when an object depends on other objects or simple properties + that were not specified in the object factory definition, although dependency + checking was enabled. + + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: UnsatisfiedDependencyException.cs,v 1.8 2006/04/09 07:18:49 markpollack Exp $ + + + + Thrown when an + encounters an error when attempting to create an object from an object + definition. + + Juergen Hoeller + Rick Evans (.NET) + $Id: ObjectCreationException.cs,v 1.15 2007/12/05 00:28:04 bbaia Exp $ + + + + Thrown on an unrecoverable problem encountered in the + objects namespace or sub-namespaces, e.g. bad class or field. + + Rod Johnson + Mark Pollack (.NET) + $Id: FatalObjectException.cs,v 1.7 2006/04/09 07:18:49 markpollack Exp $ + + + + + Superclass for all exceptions thrown in the Objects namespace and sub-namespaces. + + Rod Johnson + Mark Pollack (.NET) + $Id: ObjectsException.cs,v 1.8 2006/04/09 07:18:49 markpollack Exp $ + + + + Creates a new instance of the ObjectsException class. + + + + Creates a new instance of the ObjectsException class. with the specified message. + + + A message about the exception. + + + + + Creates a new instance of the ObjectsException class with the specified message + and root cause. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ObjectsException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Creates a new instance of the FatalObjectException class. + + + + + Creates a new instance of the FatalObjectException class with the + specified message. + + + A message about the exception. + + + + + Creates a new instance of the FatalObjectException class with the + specified message. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the FatalObjectException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The name of the object that triggered the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The name of the object that triggered the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The description of the resource associated with the object. + + + A message about the exception. + + + The name of the object that triggered the exception. + + + + + Creates a new instance of the + class. + + + The description of the resource associated with the object. + + + A message about the exception. + + + The name of the object that triggered the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + The name of the object that triggered the exception (if any). + + + + + The description of the resource associated with the object (if any). + + + + + Describes the creation failure trace of this exception. + + + + + Creates a new instance of the UnsatisfiedDependencyException class. + + + + + Creates a new instance of the UnsatisfiedDependencyException class. + + + A message about the exception. + + + + + Creates a new instance of the UnsatisfiedDependencyException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the UnsatisfiedDependencyException class. + + + The description of the resource associated with the object. + + + The name of the object that has the unsatisfied dependency. + + + The constructor argument index at which the dependency is + unsatisfied. + + + The of the constructor argument at + which the dependency is unsatisfied. + + + A message about the exception. + + + + + Creates a new instance of the UnsatisfiedDependencyException class. + + + The description of the resource associated with the object. + + + The name of the object that has the unsatisfied dependency. + + + The name identifying the property on which the dependency is + unsatisfied. + + + A message about the exception. + + + + + Creates a new instance of the UnsatisfiedDependencyException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Holder for a typed value. + + +

    + Can be added to object definitions to explicitly specify + a target type for a value, + for example for collection + elements. +

    +

    + This holder just stores the value and the target + . The actual conversion will be performed by + the surrounding object factory. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: TypedStringValue.cs,v 1.5 2007/05/29 20:00:20 markpollack Exp $ +
    + + + Creates a new instance of the + + class. + + + + + Initializes a new instance of the class. + + The value. + + + + Creates a new instance of the + + class. + + + The value that is to be converted. + + + The to convert to. + + + If the supplied is + . + + + + + The value that is to be converted. + + +

    + Obviously if the + + is the , no conversion + will actually be performed. +

    +
    +
    + + + The to convert to. + + + If the setter is supplied with a value. + + + + + Gets a value indicating whether this instance has target type. + + + true if this instance has target type; otherwise, false. + + + + + Simple class that holds the defaults specified at the <objects> + level in a standard Spring XML object definition document: + default-lazy-init, default-autowire, etc. + + Juergen Hoeller + Mark Pollack (.NET) + + + + Gets or sets the autowire setting for the document that's currently parsed. + + The autowire. + + + + Gets or sets the dependency-check setting for the document that's currently parsed + + The dependency check. + + + + Gets or sets the lazy-init flag for the document that's currently parsed. + + The lazy init. + + + + Strategy interface for + resolution. + + Aleksandar Seovic + $Id: ICultureResolver.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + + + + Resolves the + from some context. + + +

    + The 'context' is determined by the appropriate implementation class. + An example of such a context might be a thread local bound + , or a + sourced from an HTTP + session. +

    +
    + + The that should be used + by the caller. + +
    + + + Sets the . + + +

    + This is an optional operation and does not need to be implemented + such that it actually does anything useful (i.e. it can be a no-op). +

    +
    + + The new or + to clear the current . + +
    + + + Represents parsed real literal node. + + Aleksandar Seovic + $Id: RealLiteralNode.cs,v 1.11 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the real literal node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents logical NOT operator. + + Aleksandar Seovic + $Id: OpNOT.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Base class for unary operators. + + Aleksandar Seovic + $Id: UnaryOperator.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Gets the operand. + + The operand. + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical NOT operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed hexadecimal integer literal node. + + Aleksandar Seovic + $Id: HexLiteralNode.cs,v 1.9 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the hexadecimal integer literal node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents lambda expression. + + Aleksandar Seovic + $Id: LambdaExpressionNode.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + caches argumentNames of this instance + + + + + caches body expression of this lambda function + + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Assigns value of the right operand to the left one. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Returns Lambda Expression's value for the given context. + + Context to evaluate expressions against. + Current expression evaluation context. + A dictionary containing argument map for this lambda expression. + Node's value. + + + + Gets argument names for this lambda expression. + + + + + Represents parsed assignment node in the navigation expression. + + Aleksandar Seovic + $Id: AssignNode.cs,v 1.9 2007/09/07 03:01:21 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Assigns value of the right operand to the left one. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Abstract base class for simple, one-to-one implementations. + + Aleksandar Seovic + $Id: AbstractSimpleBinding.cs,v 1.4 2008/02/05 20:40:25 aseovic Exp $ + + + + Abstract base class for implementations. + + Aleksandar Seovic + $Id: AbstractBinding.cs,v 1.2 2008/02/05 20:40:25 aseovic Exp $ + + + + Binds source object to target object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + + + Binds target object to source object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + + + Binds source object to target object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Binds target object to source object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Sets error message that should be displayed in the case + of a non-fatal binding error. + + + Resource ID of the error message. + + + List of error providers message should be added to. + + + + + Gets or sets a flag specifying whether this binding is valid. + + + true if this binding evaluated without errors; + false otherwise. + + + + + Gets or sets the . + + The binding direction. + + + + Gets the error message. + + The error message. + + + + Gets the error providers. + + + + + Initialize a new instance of without any + + + + + Initialize a new instance of with the + specified . + + + + + Binds source object to target object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Concrete implementation if source to target binding. + + + The source object. + + + The target object. + + + Variables that should be used during expression evaluation. + + + + + Binds target object to source object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Concrete implementation of target to source binding. + + + The source object. + + + The target object. + + + Variables that should be used during expression evaluation. + + + + + Gets the source value for the binding. + + + Source object to extract value from. + + + Variables for expression evaluation. + + + The source value for the binding. + + + + + Sets the source value for the binding. + + + The source object to set the value on. + + + The value to set. + + + Variables for expression evaluation. + + + + + Gets the target value for the binding. + + + Source object to extract value from. + + + Variables for expression evaluation. + + + The target value for the binding. + + + + + Sets the target value for the binding. + + + The target object to set the value on. + + + The value to set. + + + Variables for expression evaluation. + + + + + Gets or sets the to use. + + The formatter to use. + + + + Helper methods with regard to type resolution. + + +

    + Not intended to be used directly by applications. +

    +
    + Bruno Baia + $Id: TypeResolutionUtils.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ +
    + + + Creates a new instance of the class. + + +

    + This is a utility class, and as such exposes no public constructors. +

    +
    +
    + + + Resolves the supplied type name into a + instance. + + +

    + If you require special resolution, do + not use this method, but rather instantiate + your own . +

    +
    + + The (possibly partially assembly qualified) name of a + . + + + A resolved instance. + + + If the type cannot be resolved. + +
    + + + Resolves a string array of interface names to + a array. + + + An array of valid interface names. Each name must include the full + interface and assembly name. + + An array of interface s. + + If any of the interfaces can't be loaded. + + + If any of the s specified is not an interface. + + + If (or any of its elements ) is + . + + + + + Match a method against the given pattern. + + the pattern to match against. + the method to match. + + if the method matches the given pattern; otherwise . + + + If the supplied is invalid. + + + + + Registry class that allows users to register and retrieve type converters. + + Aleksandar Seovic + $Id: TypeConverterRegistry.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + + + + Name of the .Net config section that contains Spring.Net type aliases. + + + + + Registers standard and configured type converters. + + + + + Returns for the specified type. + + Type to get the converter for. + a type converter for the specified type. + If is null. + + + + Registers for the specified type. + + Type to register the converter for. + Type converter to register. + If either of arguments is null. + + + + Registers for the specified type. + + + This is a convinience method that accepts the names of both + type to register converter for and the converter itself, + resolves them using , creates an + instance of type converter and calls overloaded + method. + + Type name of the type to register the converter for (can be a type alias). + Type name of the type converter to register (can be a type alias). + If either of arguments is null or empty string. + + If either of arguments fails to resolve to a valid . + + + If type converter does not derive from or if it cannot be instantiated. + + + + + Interface to be implemented by objects that can return information about + the current call stack. + + +

    + Useful in AOP (as an expression of the AspectJ cflow concept) but not AOP-specific. +

    +
    + Rod Johnson + Aleksandar Seovic (.Net) + $Id: IControlFlow.cs,v 1.4 2006/04/09 07:18:38 markpollack Exp $ +
    + + + Detects whether the caller is under the supplied , + according to the current stacktrace. + + + The to look for. + + + if the caller is under the supplied . + + + + + Detects whether the caller is under the supplied + and , according to the current stacktrace. + + + The to look for. + + The name of the method to look for. + + if the caller is under the supplied + and . + + + + + Does the current stack trace contain the supplied ? + + The token to match against. + + if the current stack trace contains the supplied + . + + + + + Helper class for easy access to messages from an + , providing various + overloaded GetMessage methods. + + +

    + Available from + , but also + reusable as a standalone helper to delegate to in application objects. +

    +
    + Juergen Hoeller + Griffin Caprio (.NET) + $Id: MessageSourceAccessor.cs,v 1.8 2007/08/23 14:30:50 oakinger Exp $ + + +
    + + + Creates a new instance of the + class + that uses the current + for all locale specific lookups. + + + The to use to locate messages. + + + + + Creates a new instance of the + class + + + The to use to locate + messages. + + + The to use for + locale specific messages. + + + + + Retrieve the message for the given code and the default + . + + The code of the message. + The message. + + + + Retrieve the message for the given code and the given + . + + The code of the message. + + The to use for + lookups. + + The message. + + + + Retrieve the message for the given code and the default + . + + The code of the message. + + The arguments for the message, or if none. + + The message. + + If the message could not be found. + + + + + Retrieve the message for the given code and the given + . + + The code of the message. + + The to use for + lookups. + + + The arguments for the message, or if none. + + The message. + + If the message could not be found. + + + + + Retrieve a mesage using the given + . + + + The . + + The message. + + If the message could not be found. + + + + + Retrieve a mesage using the given + in the given + . + + + The . + + + The to use for + lookups. + + The message + + If the message could not be found. + + + + + Provides access to a central registry of + s. + + +

    + A singleton implementation to access one or more application contexts. Application + context instances are cached. +

    +

    Note that the use of this class or similar is unnecessary except (sometimes) for + a small amount of glue code. Excessive usage will lead to code that is more tightly + coupled, and harder to modify or test. Consider refactoring your code to use standard + Dependency Injection techniques or implement the interface IApplicationContextAware to + obtain a reference to an application context.

    +
    + Mark Pollack + Aleksandar Seovic + + $Id: ContextRegistry.cs,v 1.28 2008/03/21 10:49:37 oakinger Exp $ +
    + + + The shared instance for this class (and derived classes). + + + + + Creates a new instance of the ContextRegistry class. + + +

    + Explicit static constructor to tell C# compiler + not to mark type as beforefieldinit. +

    +
    +
    + + + Registers an instance of an + . + + +

    + This is usually called via a + inside a .NET + application configuration file. +

    +
    + The application context to be registered. + + If a context has previously been registered using the same name + +
    + + + Returns the root application context. + + +

    + The first call to GetContext will create the context + as specified in the .NET application configuration file + under the location spring/context. +

    +
    + The root application context. +
    + + + Returns context based on specified name. + + +

    + The first call to GetContext will create the context + as specified in the .NET application configuration file + under the location spring/context. +

    +
    + The context name. + The specified context, or null, if context with that name doesn't exists. + + If the context name is null or empty + +
    + + + Removes all registered + s from this + registry. + + + Raises the event while still holding a lock on + + + + + Allows to check, if a context is already registered + + The context name. + true, if the context is already registered. false otherwise + + + + This event is fired, if ContextRegistry.Clear() is called.
    + Clients may register to get informed +
    + + This event is fired while still holding a lock on the Registry.
    + 'sender' parameter is sent as typeof(ContextRegistry), EventArgs are not used +
    +
    + + + Gets an object that should be used to synchronize access to ContextRegistry + from the calling code. + + + + + Interface to be implemented by any object that wishes to be notified + of the (typically the + ) that it runs in. + + +

    + Note that dependencies can also + be exposed as object properties of type + , populated via strings with + automatic type conversion by the object factory. This obviates the + need for implementing any callback interface just for the purpose of + accessing a specific resource. +

    +

    + You typically need an + when your application object has to access a variety of file resources + whose names are calculated. A good strategy is to make the object use + a default resource loader but still implement the + interface to allow + for overriding when running in an + . +

    +
    + Juergen Hoeller + Mark Pollack (.NET) + $Id: IResourceLoaderAware.cs,v 1.8 2007/08/08 17:46:37 bbaia Exp $ + + + +
    + + + Gets and sets the + that this object runs in. + + +

    + Invoked after population of normal objects properties but + before an init callback such as + 's + + or a custom init-method. Invoked before setting + 's + + property. +

    +
    +
    + + + Synchronized that should be returned by synchronized + dictionary implementations in order to ensure that the enumeration is thread safe. + + Aleksandar Seovic + $Id: SynchronizedDictionaryEnumerator.cs,v 1.1 2006/05/03 01:13:41 aseovic Exp $ + + + + Synchronized that should be returned by synchronized + collections in order to ensure that the enumeration is thread safe. + + Aleksandar Seovic + $Id: SynchronizedEnumerator.cs,v 1.1 2006/05/03 01:13:41 aseovic Exp $ + + + + A container for validation errors. + + +

    + This class groups validation errors by validator names and allows + access to both the complete errors collection and to the errors for a + certain validator. +

    +
    + Aleksandar Seovic + Goran Milosavljevic + $Id: ValidationErrors.cs,v 1.9 2008/02/05 20:40:26 aseovic Exp $ +
    + + + An interface that validation errors containers have to implement. + + Aleksandar Seovic + $Id: IValidationErrors.cs,v 1.1 2008/02/05 20:40:26 aseovic Exp $ + + + + Adds the supplied to this + instance's collection of errors. + + + The provider that should be used for message grouping; can't be + . + + The error message to add. + + If the supplied or is . + + + + + Merges another instance of into this one. + + +

    + If the supplied is , + then no errors will be added to this instance, and this method will + (silently) return. +

    +
    + + The validation errors to merge; can be . + +
    + + + Gets the list of errors for the supplied error . + + +

    + If there are no errors for the supplied , + an empty will be returned. +

    +
    + Error key that was used to group messages. + + A list of all s for the supplied lookup . + +
    + + + Gets the list of resolved error messages for the supplied lookup . + + +

    + If there are no errors for the supplied lookup , + an empty will be returned. +

    +
    + Error key that was used to group messages. + to resolve messages against. + + A list of resolved error messages for the supplied lookup . + +
    + + + Does this instance contain any validation errors? + + +

    + If this returns , this means that it (obviously) + contains no validation errors. +

    +
    + if this instance is empty. +
    + + + Gets the list of all error providers. + + + + + Default constructor. + + + + + This property is reserved, apply the + + to the class instead. + + + An that describes the + XML representation of the object that is produced by + the + method and consumed by the + + method. + + + + + Generates an object from its XML representation. + + + The stream + from which the object is deserialized. + + + + + Converts an object into its XML representation. + + + The stream + to which the object is serialized. + + + + + Adds the supplied to this + instance's collection of errors. + + + The provider that should be used for message grouping; can't be + . + + The error message to add. + + If the supplied or is . + + + + + Merges another instance of into this one. + + +

    + If the supplied is , + then no errors will be added to this instance, and this method will + (silently) return. +

    +
    + + The validation errors to merge; can be . + +
    + + + Gets the list of errors for the supplied lookup . + + +

    + If there are no errors for the supplied lookup , + an empty will be returned. +

    +
    + Error key that was used to group messages. + + A list of all s for the supplied lookup . + +
    + + + Gets the list of resolved error messages for the supplied lookup . + + +

    + If there are no errors for the supplied lookup , + an empty will be returned. +

    +
    + Error key that was used to group messages. + to resolve messages against. + + A list of resolved error messages for the supplied lookup . + +
    + + + Does this instance contain any validation errors? + + +

    + If this returns , this means that it (obviously) + contains no validation errors. +

    +
    + if this instance is empty. +
    + + + Gets the list of all providers. + + + + + XML utility methods. + + Aleksandar Seovic + $Id: XmlUtils.cs,v 1.5 2007/04/22 00:02:38 oakinger Exp $ + + + + Gets an appropriate implementation + for the supplied . + + The XML that is going to be read. + XML schemas that should be used for validation. + Validation event handler. + + A validating implementation. + + + + + Gets an appropriate implementation + for the supplied . + + The XML that is going to be read. + to be used for resolving external references + XML schemas that should be used for validation. + Validation event handler. + + A validating implementation. + + + + + Gets an appropriate implementation + for the supplied . + + The XML that is going to be read. + + A non-validating implementation. + + + + + Defines methods that dynamic property class has to implement. + + + + + Gets the value of the dynamic property for the specified target object. + + + Target object to get property value from. + + + A property value. + + + + + Gets the value of the dynamic property for the specified target object. + + + Target object to set property value on. + + + A new property value. + + + + + Safe wrapper for the dynamic property. + + + will attempt to use dynamic + property if possible, but it will fall back to standard + reflection if necessary. + + + + + Obtains cached property info or creates a new entry, if none is found. + + + + + Creates a new instance of the safe property wrapper. + + Property to wrap. + + + + Gets the value of the dynamic property for the specified target object. + + + Target object to get property value from. + + + A property value. + + + + + Gets the value of the dynamic property for the specified target object. + + + Target object to set property value on. + + + A new property value. + + + + + Internal PropertyInfo accessor. + + + + + Holds cached Getter/Setter delegates for a Property + + + + + Factory class for dynamic properties. + + Aleksandar Seovic + $Id: DynamicProperty.cs,v 1.3 2008/05/17 11:05:26 oakinger Exp $ + + + + Base class for dynamic members. + + Aleksandar Seovic + $Id: BaseDynamicMember.cs,v 1.1 2007/08/08 04:05:37 bbaia Exp $ + + + + Method attributes constant. + + + + + Sets up target instance for invocation. + + IL generator to use. + Type of target instance. + + + + Sets up invocation argument. + + IL generator to use. + Argument type. + Argument position. + + + + Generates method invocation code. + + IL generator to use. + Flag specifying whether method is static. + Flag specifying whether method is on the value type. + Method to invoke. + + + + Generates code to process return value if necessary. + + IL generator to use. + Type of the return value. + + + + Generates code that throws . + + IL generator to use. + Error message to use. + + + + Creates safe dynamic property instance for the specified . + + +

    This factory method will create a dynamic property with a "safe" wrapper.

    +

    Safe wrapper will attempt to use generated dynamic property if possible, + but it will fall back to standard reflection if necessary.

    +
    + Property info to create dynamic property for. + Safe dynamic property for the specified . + +
    + + + Creates dynamic property instance for the specified . + + Property info to create dynamic property for. + Dynamic property for the specified . + + + + Describes an implementation + that autowires events to handler methods. + + Rick Evans + $Id: AutoWiringEventHandlerValue.cs,v 1.11 2006/11/13 07:04:41 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Wires up the specified handler to the named event on the supplied event source. + + + The object (an object instance, a , etc) + exposing the named event. + + + The handler for the event (an object instance, a , + etc). + + + + + The name of the method that is going to handle the event. + + + + + Performs the matching up of handler methods to one or more source events. + + +

    + This class merely marshals the matching of handler methods to the events exposed + by an event source, and then delegates to a concrete + implementation (such as + or + ) to do the heavy lifting of + actually wiring a handler method to an event. +

    +

    + Note : the order in which handler's are wired up to events is non-deterministic. +

    +
    +
    + + + Creates a new instance of the + class. + + + The object exposing the event (s) being wired up. + + + The name of the event that is being wired up. + + + The object exposing the method (s) being wired to the event. + + + The name of the method that is going to handle the event. + + + + + Wires up events on the source to methods exposed on the handler. + + + + + Wires up the supplied event to any handler methods that match the event + signature. + + The event being wired up. + + + + Only replaces the first occurrence of the placeholder. + + The event whose name is going to be used. + + The method name customised for the name of the supplied event. + + + + + The object exposing the event (s) being wired up. + + + + + The object exposing the method (s) being wired to an event source. + + + + + The of the object that is handling any events. + + + + + The name of the method that is going to handle the event. + + + + + The name of the event that is being wired up. + + + + + A plain-vanilla object definition. + + +

    + This is the most common type of object definition; + instances + do not derive from a parent + , and usually + (but not always - see below) have an + + and (optionally) some + and + . +

    +

    + Note that + instances do not have to specify an + : + This can be useful for deriving + instances + from such definitions, each with it's own + , + inheriting common property values and other settings from the parent. +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: RootObjectDefinition.cs,v 1.20 2007/03/16 04:01:43 aseovic Exp $ + +
    + + + Common base class for object definitions, factoring out common + functionality from + and + . + + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: AbstractObjectDefinition.cs,v 1.31 2007/08/27 13:57:43 oakinger Exp $ + + + + Describes a configurable object instance, which has property values, + constructor argument values, and further information supplied by concrete + implementations. + + Rick Evans + $Id: IConfigurableObjectDefinition.cs,v 1.1 2007/07/30 17:52:22 markpollack Exp $ + + + + Describes an object instance, which has property values, constructor + argument values, and further information supplied by concrete implementations. + + +

    + This is just a minimal interface: the main intention is to allow + + (like PropertyPlaceholderConfigurer) to access and modify property values. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: IObjectDefinition.cs,v 1.14 2007/07/30 17:52:22 markpollack Exp $ +
    + + + Return the property values to be applied to a new instance of the object. + + + + + Return the constructor argument values for this object. + + + + + Return the event handlers for any events exposed by this object. + + + + + Return a description of the resource that this object definition + came from (for the purpose of showing context in case of errors). + + + + + Is this object definition a "template", i.e. not meant to be instantiated + itself but rather just serving as an object definition for configuration + templates used by . + + + if this object definition is a "template". + + + + + Is this object definition "abstract", i.e. not meant to be instantiated + itself but rather just serving as parent for concrete child object + definitions. + + + if this object definition is "abstract". + + + + + Return whether this a Singleton, with a single, shared instance + returned on all calls. + + +

    + If , an object factory will apply the Prototype + design pattern, with each caller requesting an instance getting an + independent instance. How this is defined will depend on the + object factory implementation. Singletons are the commoner type. +

    +
    +
    + + + Is this object lazily initialized? + +

    + Only applicable to a singleton object. +

    +

    + If , it will get instantiated on startup by object factories + that perform eager initialization of singletons. +

    +
    +
    + + + Returns the of the object definition (if any). + + + A resolved object . + + + If the of the object definition is not a + resolved or . + + + + + Returns the of the + of the object definition. + + Note that this does not have to be the actual type name used at runtime, + in case of a child definition overrding/inheriting the the type name from its + parent. It can be modifed during object factory post-processing, typically + replacing the original class name with a parsed variant of it. + Hence, do not consider this to be the definitive bean type at runtime + but rather only use it for parsing purposes at the individual object + definition level. + + + + + The autowire mode as specified in the object definition. + + +

    + This determines whether any automagical detection and setting of + object references will happen. Default is + , + which means there's no autowire. +

    +
    +
    + + + The object names that this object depends on. + + +

    + The object factory will guarantee that these objects get initialized + before. +

    +

    + Note that dependencies are normally expressed through object properties + or constructor arguments. This property should just be necessary for + other kinds of dependencies like statics (*ugh*) or database + preparation on startup. +

    +
    +
    + + + The name of the initializer method. + + +

    + The default is , in which case there is no initializer method. +

    +
    +
    + + + Return the name of the destroy method. + + +

    + The default is , in which case there is no destroy method. +

    +
    +
    + + + The name of the factory method to use (if any). + + +

    + This method will be invoked with constructor arguments, or with no + arguments if none are specified. The static method will be invoked on + the specified . +

    +
    +
    + + + The name of the factory object to use (if any). + + + + + Return the property values to be applied to a new instance of the object. + + + + + Return the constructor argument values for this object. + + + + + The method overrides (if any) for this object. + + + The method overrides (if any) for this object; may be an + empty collection but is guaranteed not to be + . + + + + + Return the event handlers for any events exposed by this object. + + + + + Return a description of the resource that this object definition + came from (for the purpose of showing context in case of errors). + + + + + Is this object definition "abstract", i.e. not meant to be instantiated + itself but rather just serving as parent for concrete child object + definitions. + + + if this object definition is "abstract". + + + + + Returns the of the object definition (if any). + + + A resolved object . + + + If the of the object definition is not a + resolved or . + + + + + Returns the of the + of the object definition (if any). + + + + + Return whether this a Singleton, with a single, shared instance + returned on all calls. + + +

    + If , an object factory will apply the Prototype + design pattern, with each caller requesting an instance getting an + independent instance. How this is defined will depend on the + object factory implementation. Singletons are the commoner type. +

    +
    +
    + + + Is this object lazily initialized? + +

    + Only applicable to a singleton object. +

    +

    + If , it will get instantiated on startup by object factories + that perform eager initialization of singletons. +

    +
    +
    + + + The autowire mode as specified in the object definition. + + +

    + This determines whether any automagical detection and setting of + object references will happen. Default is + , + which means there's no autowire. +

    +
    +
    + + + The dependency check code. + + + + + The object names that this object depends on. + + +

    + The object factory will guarantee that these objects get initialized + before. +

    +

    + Note that dependencies are normally expressed through object properties + or constructor arguments. This property should just be necessary for + other kinds of dependencies like statics (*ugh*) or database + preparation on startup. +

    +
    +
    + + + The name of the initializer method. + + +

    + The default is , in which case there is no initializer method. +

    +
    +
    + + + Return the name of the destroy method. + + +

    + The default is , in which case there is no destroy method. +

    +
    +
    + + + The name of the factory method to use (if any). + + +

    + This method will be invoked with constructor arguments, or with no + arguments if none are specified. The static method will be invoked on + the specified . +

    +
    +
    + + + The name of the factory object to use (if any). + + + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    +
    + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    +
    + + + Creates a new instance of the + + class. + + + The object definition used to initialise the member fields of this + instance. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    +
    + + + Resolves the type of the object, resolving it from a specified + object type name if necessary. + + + A resolved instance. + + + If the type cannot be resolved. + + + + + Validate this object definition. + + + In the case of a validation failure. + + + + + Validates all + + + + + Validate the supplied . + + + The + to be validated. + + + + + Override settings in this object definition from the supplied + object definition. + + + The object definition used to override the member fields of this instance. + + + + + Returns a that represents the current + . + + + A that represents the current + . + + + + + The property values that are to be applied to the object + upon creation. + + +

    + Setting the value of this property to + will merely result in a new (and empty) + + collection being assigned to the property value. +

    +
    + + The property values (if any) for this object; may be an + empty collection but is guaranteed not to be + . + +
    + + + Does this definition have any + ? + + + if this definition has at least one + . + + + + + The constructor argument values for this object. + + +

    + Setting the value of this property to + will merely result in a new (and empty) + + collection being assigned. +

    +
    + + The constructor argument values (if any) for this object; may be an + empty collection but is guaranteed not to be + . + +
    + + + The event handler values for this object. + + +

    + Setting the value of this property to + will merely result in a new (and empty) + + collection being assigned. +

    +
    + + The event handler values (if any) for this object; may be an + empty collection but is guaranteed not to be + . + +
    + + + The method overrides (if any) for this object. + + +

    + Setting the value of this property to + will merely result in a new (and empty) + + collection being assigned to the property value. +

    +
    + + The method overrides (if any) for this object; may be an + empty collection but is guaranteed not to be + . + +
    + + + Is this definition a singleton, with + a single, shared instance returned on all calls to an enclosing + container (typically an + or + ). + + +

    + If , an object factory will apply the + prototype design pattern, with each caller requesting an + instance getting an independent instance. How this is defined + will depend on the object factory implementation. singletons + are the commoner type. +

    +
    + +
    + + + Gets a value indicating whether this instance is prototype, with an independent instance + returned for each call. + + + true if this instance is prototype; otherwise, false. + + + + + Is this object lazily initialized? + +

    + Only applicable to a singleton object. +

    +

    + If , it will get instantiated on startup + by object factories that perform eager initialization of + singletons. +

    +
    +
    + + + Is this object definition a "template", i.e. not meant to be instantiated + itself but rather just serving as an object definition for configuration + templates used by . + + + if this object definition is a "template". + + + + + Is this object definition "abstract", i.e. not meant to be + instantiated itself but rather just serving as a parent for concrete + child object definitions. + + + if this object definition is "abstract". + + + + + The of the object definition (if any). + + + A resolved object . + + + If the of the object definition is not a + resolved or . + + + + + + Is the of the object definition a resolved + ? + + + + + Returns the of the + of the object definition (if any). + + + + + A description of the resource that this object definition + came from (for the purpose of showing context in case of errors). + + + + + The autowire mode as specified in the object definition. + + +

    + This determines whether any automagical detection and setting of + object references will happen. The default is + , + which means that no autowiring will be performed. +

    +
    +
    + + + Gets the resolved autowire mode. + + +

    + This resolves + + to one of + + or + . +

    +
    +
    + + + The dependency checking mode. + + +

    + The default is + . +

    +
    +
    + + + The object names that this object depends on. + + +

    + The object factory will guarantee that these objects get initialized + before this object definition. +

    + + Dependencies are normally expressed through object properties + or constructor arguments. This property should just be necessary for + other kinds of dependencies such as statics (*ugh*) or database + preparation on startup. + +
    +
    + + + The name of the initializer method. + + +

    + The default value is the constant, + in which case there is no initializer method. +

    +
    +
    + + + Return the name of the destroy method. + + +

    + The default value is the constant, + in which case there is no destroy method. +

    +
    +
    + + + The name of the factory method to use (if any). + + +

    + This method will be invoked with constructor arguments, or with no + arguments if none are specified. The + method will be invoked on the specified + . +

    +
    +
    + + + The name of the factory object to use (if any). + + + + + Does this object definition have any constructor argument values? + + + if his object definition has at least one + element in it's + + property. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + + class. + + + The of the object to instantiate. + + + + + Creates a new instance of the + + class. + + + The of the object to instantiate. + + + if this object definition defines a singleton object. + + + + + Creates a new instance of the + class + for a singleton, providing property values and constructor arguments. + + + The of the object to instantiate. + + + The + to be applied to a new instance of the object. + + + The to be applied to + a new instance of the object. + + + + + Creates a new instance of the + class + for a singleton using the supplied + . + + + The of the object to instantiate. + + + The autowiring mode. + + + + + Creates a new instance of the + class + for a singleton using the supplied + . + + + The of the object to instantiate. + + + The autowiring mode. + + + Whether to perform a dependency check for objects (not + applicable to autowiring a constructor, thus ignored there) + + + + + Creates a new instance of the + class + with the given singleton status, providing property values. + + + The of the object to instantiate. + + + The to be applied to + a new instance of the object. + + + + + Creates a new instance of the + class + with the given singleton status, providing property values. + + + The of the object to instantiate. + + + The to be applied to + a new instance of the object. + + + if this object definition defines a singleton object. + + + + + Creates a new instance of the + class + for a singleton, providing property values and constructor arguments. + + +

    + Takes an object class name to avoid eager loading of the object class. +

    +
    + + The assembly qualified of the object to instantiate. + + + The to be applied to + a new instance of the object. + + + The + to be applied to a new instance of the object. + +
    + + + Creates a new instance of the + class. + + +

    + Deep copy constructor. +

    +
    + + The definition that is to be copied. + +
    + + + Validate this object definition. + + + In the case of a validation failure. + + + + + A that represents the current + . + + + A that represents the current + . + + + + + An + implementation that simply returns the result of a lookup in an + associated IoC container. + + +

    + This class is Spring.NET's implementation of Dependency Lookup via + Method Injection. +

    +

    + This class is reserved for internal use within the framework; it is + not intended to be used by application developers using Spring.NET. +

    +
    + Rick Evans + $Id: LookupMethodReplacer.cs,v 1.3 2007/07/30 17:52:22 markpollack Exp $ +
    + + + An + implementation that provides some convenience support for + derived classes. + + +

    + This class is reserved for internal use within the framework; it is + not intended to be used by application developers using Spring.NET. +

    +
    + Rick Evans + $Id: AbstractMethodReplacer.cs,v 1.3 2007/07/30 17:52:22 markpollack Exp $ +
    + + + Permits the (re)implementation of an arbitrary method on a Spring.NET + IoC container managed object. + + +

    + Encapsulates the notion of the Method-Injection form of Dependency + Injection. +

    +

    + Methods that are dependency injected with implementations of this + interface may be (but need not be) , in which + case the container will create a concrete subclass of the + class prior to instantiation. +

    +

    + Do not use this mechanism as a means of AOP. See the reference + manual for examples of appropriate usages of this interface. +

    +
    + Rod Johnson + Rick Evans (.NET) + $Id: IMethodReplacer.cs,v 1.2 2006/04/09 07:18:49 markpollack Exp $ +
    + + + Reimplement the supplied . + + + The instance whose is to be + (re)implemented. + + + The method that is to be (re)implemented. + + The target method's arguments. + + The result of the (re)implementation of the method call. + + + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such has no + publicly visible constructors. +

    +
    + + The object definition that is the target of the method replacement. + + + The enclosing IoC container with which the above + is associated. + + + If either of the supplied arguments is . + +
    + + + Is ; derived classes must supply an implementation. + + + The instance whose is to be + (re)implemented. + + + The method that is to be (re)implemented. + + The target method's arguments. + The result of the object lookup. + + + + Helper method for subclasses to retrieve the appropriate + for the + supplied . + + + The to use to retrieve + the appropriate + . + + + The appropriate + . + + + + + Helper method for subclasses to lookup an object from an enclosing + IoC container. + + + The name of the object that is to be looked up. + + + The named object. + + + + + Creates a new instance of the + class. + + + The object definition that is the target of the method replacement. + + + The enclosing IoC container with which the above + is associated. + + + If either of the supplied arguments is . + + + + + Reimplements the supplied by returning the + result of an object lookup in an enclosing IoC container. + + + The instance whose is to be + (re)implemented. + + + The method that is to be (re)implemented. + + The target method's arguments. + + The result of the object lookup. + + + + + Allows for the configuration of individual object property values from + a .NET .config file. + + +

    + Useful for custom .NET .config files targetted at system administrators + that override object properties configured in the application context. +

    +

    + Two concrete implementations are provided in the Spring.NET core library: + + + + + for <add key="placeholderKey" value="..."/> style + overriding (pushing values from a .NET .config file into object + definitions). + + + + + + for replacing "${...}" placeholders (pulling values from a .NET .config + file into object definitions). + + + +

    +

    + Please refer to the API documentation for the concrete implementations + listed above for example usage. +

    +
    + Juergen Hoeller + Simon White (.NET) + + + $Id: PropertyResourceConfigurer.cs,v 1.19 2007/08/27 14:49:42 oakinger Exp $ +
    + + + The default configuration section name to use if none is explictly supplied. + + + + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    +
    + + + Modify the application context's internal object factory after its + standard initialization. + + + The object factory used by the application context. + + + In case of errors. + + + + + + Loads properties from the configuration sections + specified in into . + + The instance to be filled with properties. + + + + Apply the given properties to the supplied + . + + + The + used by the application context. + + The properties to apply. + + If an error occured. + + + + + Validates the supplied . + + +

    + Basically, if external locations are specified, ensure that either + one or a like number of config sections are also specified. +

    +
    + + The to be validated. + +
    + + + Simply initializes the supplied + collection with this instances default + (if any). + + + The collection to be so initialized. + + + + + The policy for resolving conflicting property overrides from + several resources. + + +

    + When merging conflicting property overrides from several resources, + should append an override with the same key be appended to the + current value, or should the property override from the last resource + processed override previous values? +

    +

    + The default value is ; i.e. a property + override from the last resource to be processed overrides previous + values. +

    +
    + + if the property override from the last resource + processed overrides previous values. + +
    + + + Return the order value of this object, where a higher value means greater in + terms of sorting. + + The order value. + + + + + The default properties to be applied. + + +

    + These are to be considered defaults, to be overridden by values + loaded from other resources. +

    +
    +
    + + + The location of the .NET .config file that contains the property + overrides that are to be applied. + + + + + The locations of the .NET .config files containing the property + overrides that are to be applied. + + + + + The configuration sections to look for within the .config files. + + + + + + + Should a failure to find a .config file be ignored? + + +

    + is only appropriate if the .config file is + completely optional. The default is . +

    +
    + + if a failure to find a .config file is to be + ignored. + +
    + + + An implementation + that exposes an arbitrary target object under a different name. + + +

    + Usually, the target object will reside in a different object + definition file, using this + to link it in + and expose it under a different name. Effectively, this corresponds + to an alias for the target object. +

    + + For XML based object definition files, a <alias> + tag is available that effectively achieves the same. + +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: ObjectReferenceFactoryObject.cs,v 1.3 2007/03/16 04:01:38 aseovic Exp $ + +
    + + + Return an instance (possibly shared or independent) of the object + managed by this factory. + + + An instance (possibly shared or independent) of the object managed by + this factory. + + + + + + The name of the target object. + + +

    + The target object may potentially be defined in a different object + definition file. +

    +
    + The name of the target object. +
    + + + Return the type of object that this + creates, or + if not known in advance. + + + + + + Is the object managed by this factory a singleton or a prototype? + + + + + + Callback that supplies the owning factory to an object instance. + + + The owning + (may not be ). The object can immediately + call methods on the factory. + + + In case of initialization errors. + + + + + + implementation that + creates instances of the class. + + +

    + Typically used for retrieving shared + instances for common topics (such as the 'DAL', 'BLL', etc). The + + property determines the name of the + Common.Logging logger. +

    +
    + Rick Evans + + $Id: LogFactoryObject.cs,v 1.3 2007/10/21 18:17:41 markpollack Exp $ +
    + + + Creates a new instance of the + + class. + + + + + Creates a new instance of the + + class. + + + The name of the instance served up by + this factory. + + + If the supplied is + or contains only whitespace character(s). + + + + + Return an instance (possibly shared or independent) of the object + managed by this factory. + + + An instance (possibly shared or independent) of the object + managed by this factory. + + + + + + Invoked by an + after it has set all object properties supplied + (and satisfied the + + and + interfaces). + + + In the event of misconfiguration (such as failure to set an essential + property) or if initialization fails. + + + + + + The name of the instance served up by + this factory. + + + The name of the instance served up by + this factory. + + + If the supplied to the setter is + or contains only whitespace character(s). + + + + + Return the type of object that this + creates, or + if not known in advance. + + + + + + Is the object managed by this factory a singleton or a prototype? + + + + + + The various autowiring modes. + + Rick Evans + $Id: AutoWiringMode.cs,v 1.8 2007/03/16 04:01:34 aseovic Exp $ + + + + Do not autowire. + + + + + Autowire by name. + + + + + Autowire by . + + + + + Autowiring by constructor. + + + + + The autowiring strategy is to be determined by introspection + of the object's . + + + + + Default implementation of the + interface. + + Griffin Caprio + $Id: EventRegistry.cs,v 1.7 2006/04/09 07:18:47 markpollack Exp $ + + + + Creates a new instance of the EventRegistry class. + + + + + Adds the input object to the list of publishers. + + + This publishes all events of the source object to any object + wishing to subscribe + + The source object to publish. + + + + Subscribes to all events published, if the subscriber implements + compatible handler methods. + + The subscriber to use. + + + + Subscribes to published events of all objects of a given type, if the + subscriber implements compatible handler methods. + + The subscriber to use. + + The target to subscribe to. + + + + + The list of event publishers. + + The list of event publishers. + + + + + implementation that allows for convenient registration of custom + IResource implementations. + + +

    + Because the + class implements the + + interface, instances of this class that have been exposed in the + scope of an + will + automatically be picked up by the application context and made + available to the IoC container whenever resolution of IResources is required. +

    +
    + Mark Pollack + $Id: ResourceHandlerConfigurer.cs,v 1.3 2007/08/08 17:47:13 bbaia Exp $ + + +
    + + + Registers custom IResource implementations. The supplied + is not used since IResourse implementations + are registered with a global + + + The object factory. + + + In case of errors. + + + + + The IResource implementations, i.e. resource handlers, to register. + + +

    + The has the + contains the resource protocol name as the key and type as the value. + The key name can either be a string or an object, in which case + ToString() will be used to obtain the string name. + The value can be the fully qualified name of the IResource + implementation, a string, or + an actual of the IResource class + +

    +
    +
    + + + Defines an interface that resource cache adapters have to implement. + + Aleksandar Seovic + $Id: IResourceCache.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + + + + Gets the list of resources from cache. + + Target to get a list of resources for. + Resource culture. + A list of cached resources for the specified target object and culture. + + + + Puts the list of resources in the cache. + + Target to cache a list of resources for. + Resource culture. + A list of resources to cache. + A list of cached resources for the specified target object and culture. + + + + Represents logical OR operator. + + Aleksandar Seovic + $Id: OpOR.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical OR operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents arithmetic addition operator. + + Aleksandar Seovic + $Id: OpADD.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the arithmetic addition operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed list initializer node in the navigation expression. + + Aleksandar Seovic + $Id: ListInitializerNode.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Creates new instance of the list defined by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Implementation of the distinct processor. + + Aleksandar Seovic + $Id: DistinctProcessor.cs,v 1.1 2006/12/04 08:58:33 aseovic Exp $ + + + + Returns distinct items from the collection. + + + The source collection to process. + + + 0: boolean flag specifying whether to include null + in the results or not. Default is false, which means that + null values will not be included in the results. + + + A collection containing distinct source collection elements. + + + If there is more than one argument, or if the single optional argument + is not Boolean. + + + + + Implementation of the non-null processor. + + Aleksandar Seovic + $Id: NonNullProcessor.cs,v 1.2 2007/07/31 21:43:52 markpollack Exp $ + + + + Returns non-null items from the collection. + + + The source collection to process. + + + Ignored. + + + A collection containing non-null source collection elements. + + + + + Thrown when a method (typically a property getter or setter invoked via reflection) + throws an exception, analogous to a . + + Rod Johnson + Mark Pollack (.NET) + $Id: MethodInvocationException.cs,v 1.1 2007/07/31 00:08:42 markpollack Exp $ + + + + + Creates a new instance of the MethodInvocationException class. + + + + + Creates a new instance of the MethodInvocationException class. + + + A message about the exception. + + + + + Creates a new instance of the MethodInvocationException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Constructor to use when an exception results from a + . + + + The raised by the invoked property. + + + The that + resulted in an exception. + + + + + Creates a new instance of the MethodInvocationException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + The error code string for this exception. + + + + + Convenience base class for + implementations, pre-implementing typical behavior. + + +

    + The method will + check whether a or + can be opened; + will always return + ; + and + throw an exception; + and will + return the value of the + property. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + Aleksandar Seovic (.NET) + $Id: AbstractResource.cs,v 1.25 2007/12/06 22:08:47 markpollack Exp $ + +
    + + + The central abstraction for Spring.NET's access to resources such as + s. + + +

    + This interface encapsulates a resource descriptor that abstracts away + from the underlying type of resource; possible resource types include + files, memory streams, and databases (this list is not exhaustive). +

    +

    + A can definitely be opened and accessed + for every such resource; if the resource exists in a physical form (for + example, the resource is not an in-memory stream or one that has been + extracted from an assembly or ZIP file), a or + can also be accessed. The actual + behavior is implementation-specific. +

    +

    + This interface, when used in tandem with the + interface, forms the backbone of + Spring.NET's resource handling. Third party extensions or libraries + that want to integrate external resources with Spring.NET's IoC + container are encouraged expose such resources via this abstraction. +

    +

    + Interfaces cannot obviously mandate implementation, but derived classes + are strongly encouraged to expose a constructor that takes a + single as it's sole argument (see example). + Exposing such a constructor will make your custom + implementation integrate nicely + with the class. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: IResource.cs,v 1.12 2007/08/08 17:46:55 bbaia Exp $ + + +
    + + + Simple interface for objects that are sources for + s. + + +

    + This is the base interface for the abstraction encapsulated by + Spring.NET's interface. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: IInputStreamSource.cs,v 1.10 2007/08/08 17:46:55 bbaia Exp $ + +
    + + + Return an for this resource. + + + + Clients of this interface must be aware that every access of this + property will create a fresh ; + it is the responsibility of the calling code to close any such + . + + + + An . + + + If the stream could not be opened. + + + + + Creates a resource relative to this resource. + + + The path (always resolved as relative to this resource). + + + The relative resource. + + + If the relative resource could not be created from the supplied + path. + + + If the resource does not support the notion of a relative path. + + + + + Does this resource represent a handle with an open stream? + + +

    + If , the + cannot be read multiple times, and must be read and then closed to + avoid resource leaks. +

    +

    + Will be for all usual resource descriptors. +

    +
    + + if this resource represents a handle with an + open stream. + + +
    + + + Returns the handle for this resource. + + +

    + For safety, always check the value of the + property prior to + accessing this property; resources that cannot be exposed as + a will typically return + from a call to the + property. +

    +
    + + The handle for this resource. + + + If the resource is not available or cannot be exposed as a + . + + + +
    + + + Returns a handle for this resource. + + +

    + For safety, always check the value of the + property prior to + accessing this property; resources that cannot be exposed as + a will typically return + from a call to the + property. +

    +
    + + The handle for this resource. + + + If the resource is not available on a filesystem, or cannot be + exposed as a handle. + + + +
    + + + Returns a description for this resource. + + +

    + The description is typically used for diagnostics and other such + logging when working with the resource. +

    +

    + Implementations are also encouraged to return this value from their + method. +

    +
    + + A description for this resource. + +
    + + + Does this resource actually exist in physical form? + + +

    + An example of a resource that physically exists would be a + file on a local filesystem. An example of a resource that does not + physically exist would be an in-memory stream. +

    +
    + + if this resource actually exists in physical + form (for example on a filesystem). + + + +
    + + + The default special character that denotes the base (home, or root) + path. + + +

    + Will be resolved (by those + implementations that support it) to the home (or root) path for + the specific implementation. +

    +

    + For example, in the case of a web application this will (probably) + resolve to the virtual directory of said web application. +

    +
    +
    + + + Creates a new instance of the + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    +
    + + + Creates a new instance of the + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    + + A string representation of the resource. + + + If the supplied is + or contains only whitespace character(s). + +
    + + + Strips any protocol name from the supplied + . + + +

    + If the supplied does not + have any protocol associated with it, then the supplied + will be returned as-is. +

    +
    + + + GetResourceNameWithoutProtocol("http://www.mycompany.com/resource.txt"); + // returns www.mycompany.com/resource.txt + + + + The name of the resource. + + + The name of the resource without the protocol name. + +
    + + + Resolves the supplied to its value + sans any leading protocol. + + + The name of the resource. + + + The name of the resource without the protocol name. + + + + + + Resolves the presence of the + value + in the supplied into a path. + + +

    + The default implementation simply returns the supplied + as is. +

    +
    + + The name of the resource. + + + The string that is a placeholder for a base path. + + + The name of the resource with any + value having been resolved into an actual path. + +
    + + + This implementation returns the + of this resource. + + + + + + Determines whether the specified is + equal to the current . + + +

    + This implementation compares values. +

    +
    + +
    + + + Serves as a hash function for a particular type, suitable for use + in hashing algorithms and data structures like a hash table. + + +

    + This implementation returns the hashcode of the + property. +

    +
    + +
    + + + Factory Method. Create a new instance of the current resource type using the given resourceName + + + + + The ResourceLoader to be used for resolving relative resources + + + + + Does the supplied relative ? + + + The name of the resource to test. + + + if resource name is relative; + otherwise . + + + + + Creates a new resource that is relative to this resource based on the + supplied . + + +

    + This method can accept either a fully qualified resource name or a + relative resource name as it's parameter. +

    +

    + A fully qualified resource is one that has a protocol prefix and + all elements of the resource name. All other resources are treated + as relative to this resource, and the following rules are used to + locate a relative resource: +

    + + + If the starts with '..', + the current resource path is navigated backwards before the + is concatenated to the current + of + this resource. + + + If the starts with '/', the + current resource path is ignored and a new resource name is + appended to the + of + this resource. + + + If the starts with '.' or a + letter, a new path is appended to the current + of + this resource. + + +
    + + The name of the resource to create. + + The relative resource. + + If the process of resolving the relative resource yielded an + invalid URI. + + + If this resource does not support the resolution of relative + resources (as determined by the value of the + + property). + + +
    + + + Calculates a new resource path based on the supplied + . + + + The relative path to evaluate. + + The newly calculated resource path. + + + + The special character that denotes the base (home, or root) + path. + + +

    + Will be resolved (by those + implementations that support it) to the home (or root) path for + the specific implementation. +

    +

    + For example, in the case of a web application this will (probably) + resolve to the virtual directory of said web application. +

    +
    + +
    + + + Return an for this resource. + + + An . + + + If the stream could not be opened. + + + + + + Returns a description for this resource. + + + A description for this resource. + + + + + + Returns the protocol associated with this resource (if any). + + +

    + The value of this property may be if no + protocol is associated with the resource type (for example if the + resource is a memory stream). +

    +
    + + The protocol associated with this resource (if any). + +
    + + + Does this resource represent a handle with an open stream? + + +

    + This, the default implementation, always returns + . +

    +
    + + if this resource represents a handle with an + open stream. + + +
    + + + Returns the handle for this resource. + + +

    + This, the default implementation, always throws a + , assuming that the + resource cannot be exposed as a . +

    +
    + + The handle for this resource. + + + This, the default implementation, always throws a + . + + +
    + + + Returns a handle for this resource. + + +

    + This, the default implementation, always throws a + , assuming that the + resource cannot be resolved to an absolute file path. +

    +
    + + The handle for this resource. + + + This implementation always throws a + . + + + +
    + + + Does this resource actually exist in physical form? + + +

    + This implementation checks whether a + can be opened, falling back to whether a + can be opened. +

    +

    + This will cover both directories and content resources. +

    +

    + This implementation will also return if + permission to the (file's) path is denied. +

    +
    + + if this resource actually exists in physical + form (for example on a filesystem). + + + +
    + + + Does this support relative + resource retrieval? + + +

    + This property is generally to be consulted prior to attempting + to attempting to access a resource that is relative to this + resource (via a call to + ). +

    +

    + This, the default implementation, always returns + . +

    +
    + + if this + supports relative resource + retrieval. + +
    + + + Gets the root location of the resource. + + +

    + Where root resource can be taken to mean that part of the resource + descriptor that doesn't change when a relative resource is looked + up. Examples of such a root location would include a drive letter, + a web server name, an assembly name, etc. +

    +
    + + The root location of the resource. + + + This, the default implementation, always throws a + . + +
    + + + Gets the current path of the resource. + + +

    + An example value of this property would be the name of the + directory containing a filesystem based resource. +

    +
    + + The current path of the resource. + + + This, the default implementation, always throws a + . + +
    + + + Gets those characters that are valid path separators for the + resource type. + + +

    + An example value of this property would be the + and + values for a + filesystem based resource. +

    +

    + Any derived classes that override this method are expected to + return a new array for each access of this property. +

    +
    + + Those characters that are valid path separators for the resource + type. + + + This, the default implementation, always throws a + . + +
    + + + XML resource reader. + + +

    + Navigates through an XML resource and invokes parsers registered + with the . +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: DefaultObjectDefinitionDocumentReader.cs,v 1.9 2007/08/27 14:49:43 oakinger Exp $ +
    + + + SPI for parsing an XML document that contains Spring object definitions. + Used by for actually parsing a DOM + document. + + Instantiated per document to parse: Implementations can hold state in + instance variables during the execution of the RegisterObjectDefinitions + method, for example global settings that are defined for all object definitions + in the document. + + Juergen Hoeller + Rob Harrop + Mark Pollack (.NET) + + + + + Read object definitions from the given DOM element, and register + them with the given object registry. + + The DOM element containing object definitions, usually the + root (document) element. + The current context of the reader. Includes + the resource being parsed + + The number of object definitions that were loaded. + + + In case of parsing errors. + + + + + The shared instance for this class (and derived classes). + + + + + Creates a new instance of the DefaultObjectDefinitionDocumentReader class. + + + + + Read object definitions from the given DOM element, and register + them with the given object registry. + + The DOM element containing object definitions, usually the + root (document) element. + The current context of the reader. Includes + the resource being parsed + + The number of object definitions that were loaded. + + + In case of parsing errors. + + + + + Parses object definitions starting at the given + using the passed . + + The root element to start parsing from. + The instance to use. + + + + Parses the default element. + + The element. + The helper. + + + + Loads external XML object definitions from the resource described by the supplied + . + + The XML element describing the resource. + + If the resource could not be imported. + + + + + Parses the given alias element, registering the alias with the registry. + + The alias element. + The registry. + + + + Parse an object definition and register it with the object factory.. + + The element containing the object definition. + The helper. + + + + + + Allow the XML to be extensible by processing any custom element types last, + after we finished processing the objct definitions. This method is a natural + extension point for any other custom post-processing of the XML. + + The default implementation is empty. Subclasses can override this method to + convert custom elements into standard Spring object definitions, for example. + Implementors have access to the parser's object definition reader and the + underlying XML resource, through the corresponding properties. + + + The root. + + + + Allow the XML to be extensible by processing any custom element types first, + before we start to process the object definitions. + + This method is a natural + extension point for any other custom pre-processing of the XML. +

    The default implementation is empty. Subclasses can override this method to + convert custom elements into standard Spring object definitions, for example. + Implementors have access to the parser's object definition reader and the + underlying XML resource, through the corresponding properties. +

    +
    + The root element of the XML document. +
    + + + Creates an instance for the given and element. + + the to create the + the root to start reading from + a new instance + + + + Gets the reader context. + + The reader context. + + + + An + implementation that supports method injection. + + +

    + Classes that want to take advantage of method injection must meet some + stringent criteria. Every method that is to be method injected + must be defined as either or + . An + will be thrown if these criteria are not met. +

    +
    + Rick Evans + $Id: MethodInjectingInstantiationStrategy.cs,v 1.13 2008/05/02 16:44:44 markpollack Exp $ +
    + + + Simple object instantiation strategy for use in + implementations. + + +

    + Does not support method injection, although it provides hooks for subclasses + to override to add method injection support, for example by overriding methods. +

    +
    + Rod Johnson + Rick Evans (.NET) + $Id: SimpleInstantiationStrategy.cs,v 1.18 2008/04/07 00:30:34 bbaia Exp $ + +
    + + + The shared instance for this class (and derived classes). + + + + + Instantiate an instance of the object described by the supplied + from the supplied . + + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the null + or zero length string if we're autowiring an object that doesn't belong + to the supplied . + + + The owning + + + An instance of the object described by the supplied + from the supplied . + + + + + Gets the zero arg ConstructorInfo object, if the type offers such functionality. + + The type. + Zero argument ConstructorInfo + + If the type does not have a zero-arg constructor. + + + + + Instantiate an instance of the object described by the supplied + from the supplied . + + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the null + or zero length string if we're autowiring an object that doesn't belong + to the supplied . + + + The owning + + + The to be used to instantiate + the object. + + + Any arguments to the supplied . May be null. + + + An instance of the object described by the supplied + from the supplied . + + + + + Instantiate an instance of the object described by the supplied + from the supplied . + + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the null + or zero length string if we're autowiring an object that doesn't belong + to the supplied . + + + The owning + + + The to be used to get the object. + + + Any arguments to the supplied . May be null. + + + An instance of the object described by the supplied + from the supplied . + + + + + Instantiate an instance of the object described by the supplied + from the supplied , + injecting methods as appropriate. + + +

    + The default implementation of this method is to throw a + . +

    +

    + Derived classes can override this method if they can instantiate an object + with the Method Injection specified in the supplied + . Instantiation should use a no-arg constructor. +

    +
    + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be a + or zero length string if we're autowiring an object that + doesn't belong to the supplied . + + + The owning + + + An instance of the object described by the supplied + from the supplied . + +
    + + + Instantiate an instance of the object described by the supplied + from the supplied , + injecting methods as appropriate. + + +

    + The default implementation of this method is to throw a + . +

    +

    + Derived classes can override this method if they can instantiate an object + with the Method Injection specified in the supplied + . Instantiation should use the supplied + and attendant . +

    +
    + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the null + or zero length string if we're autowiring an object that doesn't belong + to the supplied . + + + The owning + + + The to be used to instantiate + the object. + + + Any arguments to the supplied . May be null. + + + An instance of the object described by the supplied + from the supplied . + +
    + + + The name of the dynamic assembly that holds dynamically created code + + + + + A cache of generated instances, keyed on + the object name for which the was generated. + + + + + Instantiate an instance of the object described by the supplied + from the supplied , + injecting methods as appropriate. + + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the + or zero length string if we're autowiring an + object that doesn't belong to the supplied + . + + + The owning + + + An instance of the object described by the supplied + from the supplied . + + + + + + Instantiate an instance of the object described by the supplied + from the supplied , + injecting methods as appropriate. + + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the + or zero length string if we're autowiring an + object that doesn't belong to the supplied + . + + + The owning + + + The to be used to instantiate + the object. + + + Any arguments to the supplied . May be null. + + + An instance of the object described by the supplied + from the supplied . + + + + + + Instantiate an instance of the object described by the supplied + from the supplied , + injecting methods as appropriate. + + +

    + This method dynamically generates a subclass that supports method + injection for the supplied . It then + instantiates an new instance of said type using the constructor + identified by the supplied , + passing the supplied to said + constructor. It then manually injects (generic) method replacement + and method lookup instances (of + ) into + the new instance: those methods that are 'method-injected' will + then delegate to the approriate + + instance to effect the actual method injection. +

    +
    + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the + or zero length string if we're autowiring an + object that doesn't belong to the supplied + . + + + The owning + + + The parameter s to use to find the + appropriate constructor to invoke. + + + The aguments that are to be passed to the appropriate constructor + when the object is being instantiated. + + + A new instance of the defined by the + supplied . + +
    + + + A factory that generates subclasses of those + classes that have been configured for the Method-Injection form of + Dependency Injection. + + +

    + This class is designed as for one-shot usage; i.e. it must + be used to generate exactly one method injected subclass and + then discarded (it maintains state in instance fields). +

    +
    +
    + + + The name of the generated + property (for method replacement). + + +

    + Exists so that clients of this class can use this name to set properties reflectively + on the dynamically generated subclass. +

    +
    +
    + + + The name of the generated + property (for method lookup). + + +

    + Exists so that clients of this class can use this name to set properties reflectively + on the dynamically generated subclass. +

    +
    +
    + + + Creates a new instance of the + class. + + + The in which + the generated is to be defined. + + + The object definition that is the target of the method injection. + + + If either of the supplied arguments is . + + + + + Builds a suitable for Method-Injection. + + + A suitable for Method-Injection. + + + + + Defines overrides for those methods that are configured with an appropriate + . + + + The overarching that is defining + the generated . + + + + + Override the supplied with the logic + encapsulated by the + + defined by the supplied . + + + The builder for the subclass that is being generated. + + + The method on the superclass that is to be overridden. + + + The field defining the + + that the overridden method will delegate to to do the 'actual' + method injection logic. + + + + + Defines the parameters to the method that is being overridden. + + +

    + Since we are simply overridding a method (in this method + injection context), all we do here is simply copy the + parameters (since we want a method with the exact same parameters). +

    +
    + + The parameters to the original method that is being overridden. + + + The builder we are using to define the new overridden method. + +
    + + + Generates the MSIL for actually returning a return value if the + supplied is not + . + + + The definition of the return value; if , it + means that no return value is to required (a void + return type). + + + The to emit + the MSIL to. + + + + + Generates the MSIL for a return value if the supplied + returns a value. + + + The method to be checked. + + + The to emit + the MSIL to. + + + The return value, or if the method does not + return a value (has a void return type). + + + + + Pushes (sets up) the arguments for a call to the + + method of an appropriate + . + + + The parameters to the original method (will be bundled + up into a generic object[] and passed as the third + argument to the + + invocation. + + + The to emit + the MSIL to. + + + + + Simply generates the IL for a write only property for the + . + + + The in which the property is defined. + + + The name of the (to be) generated property. + + + The (instance) field that the property is to 'set'. + + + + + The various modes of dependency checking. + + Rick Evans (.NET) + + + + DO not do any dependency checking. + + + + + Check object references. + + + + + Just check primitive (string, int, etc) values. + + + + + Check everything. + + + + + Exception thrown when an + is asked for an object instance name for which it cannot find a definition. + + Rod Johnson + Rick Evans (.NET) + $Id: NoSuchObjectDefinitionException.cs,v 1.8 2006/04/09 07:18:48 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + Name of the missing object. + + + A further, detailed message describing the problem. + + + + + Creates a new instance of the + class. + + + The of the missing object. + + + A further, detailed message describing the problem. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + Return the required of object, if it was a + lookup by that failed. + + + + + Return the name of the missing object, if it was a lookup by name that + failed. + + + + + Simple factory for shared instances. + + Juergen Hoeller + Simon White (.NET) + $Id: DictionaryFactoryObject.cs,v 1.7 2007/07/31 03:47:39 markpollack Exp $ + + + + Simple template superclass for + implementations that allows for the creation of a singleton or a prototype + instance (depending on a flag). + + + If the value of the + + property is (this is the default), this class + will create a single instance of it's object upon initialization and + subsequently return the singleton instance; else, this class will + create a new instance each time (prototype mode). Subclasses must + implement the + + template method to actually create objects. + + Juergen Hoeller + Keith Donald + Simon White (.NET) + $Id: AbstractFactoryObject.cs,v 1.13 2007/03/16 04:01:34 aseovic Exp $ + + + + Invoked by an + after it has injected all of an object's dependencies. + + + In the event of misconfiguration (such as the failure to set a + required property) or if initialization fails. + + + + + + Return an instance (possibly shared or independent) of the object + managed by this factory. + + + An instance (possibly shared or independent) of the object managed by + this factory. + + + + + + Template method that subclasses must override to construct + the object returned by this factory. + + + Invoked once immediately after the initialization of this + in the case of + a singleton; else, on each call to the + + method. + + + If an exception occured during object creation. + + + A distinct instance of the object created by this factory. + + + + + Performs cleanup on any cached singleton object. + + +

    + Only makes sense in the context of a singleton object. +

    +
    + + +
    + + + Is the object managed by this factory a singleton or a prototype? + + +

    + Please note that changing the value of this property after + this factory object instance has been created by an enclosing + Spring.NET IoC container really is a programming error. This + property should really only be set once, prior to the invocation + of the + + callback method. +

    +
    + +
    + + + Return the of object that this + creates, or + if not known in advance. + + + + + + Constructs a new instance of the target dictionary. + + The new instance. + + + + Set the source . + + +

    + This value will be used to populate the + returned by this factory. +

    +
    +
    + + + Set the of the + implementation to use. + + +

    + The default is the . +

    +
    + + If the value is . + + + If the value is an . + + + If the value is an interface. + +
    + + + The of objects created by this factory. + + + Always returns the . + + + + + Default implementation of the + interface that should be sufficient for all normal uses. + + +

    + will convert + and array + values to the corresponding target arrays, if necessary. Custom + s that deal with + s or arrays can be written against a + comma delimited as + arrays are converted in such a format if the array itself is not assignable. +

    +
    + Rod Johnson + Juergen Hoeller + Jean-Pierre Pawlak + Mark Pollack (.NET) + Aleksandar Seovic(.NET) + $Id: ObjectWrapper.cs,v 1.72 2007/07/31 08:18:20 markpollack Exp $ +
    + + + The central interface of Spring.NET's low-level object infrastructure. + + +

    + Typically not directly used by application code but rather implicitly + via an . +

    +

    + Implementing classes have the ability to get and set property values + (individually or in bulk), get property descriptors and query the + readability and writability of properties. +

    +

    + This interface supports nested properties enabling the setting + of properties on subproperties to an unlimited depth. +

    +

    + If a property update causes an exception, a + will be thrown. Bulk + updates continue after exceptions are encountered, throwing an exception + wrapping all exceptions encountered during the update. +

    +

    + implementations can be used + repeatedly, with their "target" or wrapped object changed. +

    +
    + Rod Johnson + Mark Pollack (.NET) + $Id: IObjectWrapper.cs,v 1.15 2007/07/31 21:43:52 markpollack Exp $ + +
    + + Get the value of a property. + + The name of the property to get the value of. May be nested. + + The value of the property. + + if the property isn't readable, or if the getting the value throws + an exception. + + + + + Get the for a particular + property. + + + The property to be retrieved. + + + The for the particular + property. + + + + + Get the for a particular property. + + + The property the of which is to be retrieved. + + + The for a particular property.. + + + + + Get all of the instances for + all of the properties of the wrapped object. + + + An array of instances. + + + + + Set a property value. + + +

    + This is the preferred way to update an individual property. +

    +
    + The new property value. +
    + + + Set a property value. + + +

    + This method is provided for convenience only. The + + method is more powerful. +

    +
    + + The name of the property to set value of. + + The new property value. +
    + + Set a number of property values in bulk. + +

    + This is the preferred way to perform a bulk update. +

    +

    + Note that performing a bulk update differs from performing a single update, + in that an implementation of this class will continue to update properties + if a recoverable error (such as a vetoed property change or a type + mismatch, but not an invalid property name or the like) is + encountered, throwing a + containing + all the individual errors. This exception can be examined later to see all + binding errors. Properties that were successfully updated stay changed. +

    +

    + Does not allow the setting of unknown fields. Equivalent to + + with an argument of false for the second parameter. +

    +
    + + The collection of instances to + set on the wrapped object. + +
    + + + Set a number of property values in bulk with full control over behavior. + + +

    + Note that performing a bulk update differs from performing a single update, + in that an implementation of this class will continue to update properties + if a recoverable error (such as a vetoed property change or a type + mismatch, but not an invalid property name or the like) is + encountered, throwing a + containing + all the individual errors. This exception can be examined later to see all + binding errors. Properties that were successfully updated stay changed. +

    +

    Does not allow the setting of unknown fields. +

    +
    + + The to set on the target object + + + Should we ignore unknown values (not found in the object!?) + +
    + + + The object wrapped by the wrapper (cannot be ). + + +

    + Implementations are required to allow the type of the wrapped + object to change. +

    +
    + The object wrapped by this wrapper. +
    + + + Convenience method to return the + of the wrapped object. + + The of the wrapped object. + + + The wrapped object. + + + + The ILog instance for this class. We'll create a lot of these objects, + so we don't want a new instance every time. + + + + + Creates a new instance of the class. + + +

    + The wrapped target instance will need to be set afterwards. +

    +
    + +
    + + + Creates a new instance of the class. + + + The object wrapped by this . + + + If the supplied is . + + + + + Creates a new instance of the class, + instantiating a new instance of the specified and using + it as the . + + +

    + Please note that the passed as the + argument must have a no-argument constructor. + If it does not, an exception will be thrown when this class attempts + to instantiate the supplied using it's + (non-existent) constructor. +

    +
    + + The to instantiate and wrap. + + + If the is , or if the + invocation of the s default (no-arg) constructor + fails (due to invalid arguments, insufficient permissions, etc). + +
    + + Gets the value of a property. + + The name of the property to get the value of. + + The value of the property. + + If there is no such property, if the property isn't readable, or + if getting the property value throws an exception. + + + + Gets the value of a property. + + The property expression that should be used to retrieve the property value. + + The value of the property. + + If there is no such property, if the property isn't readable, or + if getting the property value throws an exception. + + + + + Sets a property value. + + +

    + This method is provided for convenience only. The + + method is more powerful. +

    +
    + + The name of the property to set value of. + + The new value. +
    + + + Sets a property value. + + + The property expression that should be used to set the property value. + + The new value. + + + + Sets a property value. + + +

    + This is the preferred way to update an individual property. +

    +
    + + The object containing new property value. + +
    + + Set a number of property values in bulk. + +

    + Does not allow unknown fields. Equivalent to + + with and for + arguments. +

    +
    + + The to set on the target + object. + + + If an error is encountered while setting a property. + + + On a mismatch while setting a property, insufficient permissions, etc. + + +
    + + + Perform a bulk update with full control over behavior. + + +

    + This method may throw a reflection-based exception, if there is a critical + failure such as no matching field... less serious exceptions will be accumulated + and thrown as a single . +

    +
    + + The s to set on the target object. + + + Should we ignore unknown values (not found in the object!?). + + + If an error is encountered while setting a property (only thrown if the + parameter is set to ). + + + On a mismatch while setting a property, insufficient permissions, etc. + + +
    + + + Returns PropertyInfo for the specified property + + The name of the property to search for. + The for the specified property. + If cannot be determined. + + + + Get the for a particular property. + + + The property the of which is to be retrieved. + + + The for a particular property.. + + + + + Returns MemberInfo for the specified property or field + + The name of the property or field to search for. + The or for the specified property or field. + If does not resolve to a property or field. + + + + Get the properties of the wrapped object. + + + An array of s. + + + + + This method is expensive! Only call for diagnostics and debugging reasons, + not in production. + + + A string describing the state of this object. + + + + + Attempts to parse property expression first and falls back to full expression + if that fails. Performance optimization. + + Property expression to parse. + Parsed proeprty expression. + + + + The object wrapped by this . + + + If the object cannot be changed; or an attempt is made to set the + value of this property to . + + + + + Convenience method to return the of the wrapped object. + + +

    + Do not use this (convenience) method prior to setting the + property. +

    +
    + + The of the wrapped object. + + + If the property + is . + +
    + + + Return the collection of property descriptors. + + + + + Implementation of that can be used to + format and parse floating point numbers. + + + + This formatter allows you to format and parse numbers that conform + to number style (leading and trailing + white space, leading sign, decimal point, exponent). + + + Aleksandar Seovic + $Id: FloatFormatter.cs,v 1.4 2006/04/09 07:18:47 markpollack Exp $ + + + + Default format string. + + + + + Initializes a new instance of the class, + using default format string of '{0:F}' and current thread's culture. + + + + + Initializes a new instance of the class, + using specified format string and current thread's culture. + + The format string. + + + + Initializes a new instance of the class, + using default format string of '{0:F}' and specified culture. + + The culture. + + + + Initializes a new instance of the class, + using specified format string and current thread's culture. + + The format string. + The culture name. + + + + Initializes a new instance of the class, + using specified format string and culture. + + The format string. + The culture. + + + + Formats the specified float value. + + The value to format. + Formatted floating point number. + If is null. + If is not a number. + + + + Parses the specified float value. + + The float value to parse. + Parsed float value as a . + + + + Represents parsed projection node in the navigation expression. + + Aleksandar Seovic + $Id: ProjectionNode.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a containing results of evaluation + of projection expression against each node in the context. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + BaseBindingManager keeps track of all registered bindings and + represents an entry point for the binding and unbinding process. + + Aleksandar Seovic + $Id: BaseBindingManager.cs,v 1.1 2006/11/21 05:46:57 aseovic Exp $ + + + + Initializes a new instance of the class. + + + + + Simple, expression-based implementation of that + binds source to target one-to-one. + + Aleksandar Seovic + $Id: SimpleExpressionBinding.cs,v 1.3 2007/03/19 16:05:52 oakinger Exp $ + + + + Initializes a new instance of the class. + + + The source expression. + + + The target expression. + + + + + Initializes a new instance of the class. + + + The source expression. + + + The target expression. + + + The formatter to use. + + + + + Gets the source value for the binding. + + + Source object to extract value from. + + + Variables for expression evaluation. + + + The source value for the binding. + + + + + Sets the source value for the binding. + + + The source object to set the value on. + + + The value to set. + + + Variables for expression evaluation. + + + + + Gets the target value for the binding. + + + Source object to extract value from. + + + Variables for expression evaluation. + + + The target value for the binding. + + + + + Sets the target value for the binding. + + + The target object to set the value on. + + + The value to set. + + + Variables for expression evaluation. + + + + + Gets the source expression. + + The source expression. + + + + Gets the target expression. + + The target expression. + + + + Resolves (instantiates) a by it's (possibly + assembly qualified) name, and caches the + instance against the type name. + + Rick Evans + Bruno Baia + Erich Eichinger + $Id: CachedTypeResolver.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + + + + Resolves a by name. + + +

    + The rationale behind the creation of this interface is to centralise + the resolution of type names to instances + beyond that offered by the plain vanilla + method call. +

    +
    + Rick Evans + $Id: ITypeResolver.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ +
    + + + Resolves the supplied to a + + instance. + + + The (possibly partially assembly qualified) name of a + . + + + A resolved instance. + + + If the supplied could not be resolved + to a . + + + + + The cache, mapping type names ( instances) against + instances. + + + + + Creates a new instance of the class. + + + The that this instance will delegate + actual resolution to if a + cannot be found in this instance's cache. + + + If the supplied is . + + + + + Resolves the supplied to a + + instance. + + + The (possibly partially assembly qualified) name of a + . + + + A resolved instance. + + + If the supplied could not be resolved + to a . + + + + + Converts a two part string, (resource name, assembly name) + to a ResourceManager instance. + + + + + This constant represents the name of the folder/assembly containing global resources. + + + + + Creates a new instance of the + class. + + + + + Returns whether this converter can convert an object of one + to a + + + +

    + Currently only supports conversion from a + instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + True if the conversion is possible. +
    + + + Convert from a string value to a + instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A + if successful. + + If the specified does not denote a valid resource + + + + Custom implementation for + objects. + + +

    + Handles conversion from an XML formatted string to a + object + (see below for an example of the expected XML format). +

    +

    + This converter must be registered before it will be available. Standard + converters in this namespace are automatically registered by the + class. +

    +
    + +

    + Find below some examples of the XML formatted strings that this + converter will sucessfully convert. Note that the name of the top level + (document) element is quite arbitrary... it is only the content that + matters (and which must be in the format + <add key="..." value="..."/>. For your continued sanity + though, you may wish to standardize on the top level name of + 'dictionary' (although you are of course free to not do so). +

    + + + + + + +

    + The following example uses a different top level (document) element + name, but is equivalent to the first example. +

    + + + + + + +
    + Rod Johnson + Juergen Hoeller + Simon White (.NET) + $Id: NameValueConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + + + + Returns whether this converter can convert an object of one + to a + + + +

    + Currently only supports conversion from an + XML formatted instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + True if the conversion is possible. +
    + + + Convert from a string value to a + instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A + if successful. + + + + + An implementation that + accesses resources from .resx / .resource files. + + Note that for the method + GetResourceObject if the resource name resolves to null, then in + .NET 1.1 the return value will be String.Empty whereas + in .NET 2.0 it will return null. + Griffin Caprio (.NET) + Mark Pollack (.NET) + Aleksandar Seovic (.NET) + $Id: ResourceSetMessageSource.cs,v 1.23 2007/07/31 18:15:50 bbaia Exp $ + + + + Abstract implementation of the interface, + implementing common handling of message variants, making it easy + to implement a specific strategy for a concrete . + + +

    Subclasses must implement the abstract ResolveObject + method.

    +

    Note: By default, message texts are only parsed through + String.Format if arguments have been passed in for the message. In case + of no arguments, message texts will be returned as-is. As a consequence, + you should only use String.Format escaping for messages with actual + arguments, and keep all other messages unescaped. +

    +

    Supports not only IMessageSourceResolvables as primary messages + but also resolution of message arguments that are in turn + IMessageSourceResolvables themselves. +

    +

    This class does not implement caching of messages per code, thus + subclasses can dynamically change messages over time. Subclasses are + encouraged to cache their messages in a modification-aware fashion, + allowing for hot deployment of updated messages. +

    +
    + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + Harald Radi (.NET) + $Id: AbstractMessageSource.cs,v 1.23 2007/08/28 14:16:15 oakinger Exp $ + + + +
    + + + Sub-interface of to be + implemented by objects that can resolve messages hierarchically. + + Rod Johnson + Juergen Hoeller + Mark Pollack (.NET) + $Id: IHierarchicalMessageSource.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ + + + + + The parent message source used to try and resolve messages that + this object can't resolve. + + +

    + If the value of this property is then no + further resolution is possible. +

    +
    +
    + + + holds the logger instance shared with subclasses. + + + + + Initializes this instance. + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If the lookup is not successful throw NoSuchMessageException + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + The that represents + the culture for which the resource is localized. + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + Note that the fallback behavior based on CultureInfo seem to + have a bug that is fixed by installed .NET 1.1 Service Pack 1. +

    + If the lookup is not successful, implementations are permitted to + take one of two actions. +

    + If the lookup is not successful throw NoSuchMessageException +
    +
    + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If the lookup is not successful throw NoSuchMessageException + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + The that represents + the culture for which the resource is localized. + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + Note that the fallback behavior based on CultureInfo seem to + have a bug that is fixed by installed .NET 1.1 Service Pack 1. +

    + If the lookup is not successful throw NoSuchMessageException. +

    +
    +
    + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + The default message if name is not found. + The that represents + the culture for which the resource is localized. + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + Note that the fallback behavior based on CultureInfo seem to + have a bug that is fixed by installed .NET 1.1 Service Pack 1. +

    + If the lookup is not successful throw NoSuchMessageException +

    +
    +
    + + + Resolve the message using all of the attributes contained within + the supplied + argument. + + The value object storing those attributes that are required to + properly resolve a message. + The that represents + the culture for which the resource is localized. + + The resolved message if the lookup was successful. + + + If the message could not be resolved. + + + + + Gets a localized resource object identified by the supplied + . + + + The name of the resource object to resolve. + + + The resolved object, or if not found. + + + + + + Gets a localized resource object identified by the supplied + . + + + Note that the fallback behavior based on CultureInfo seem to + have a bug that is fixed by installed .NET 1.1 Service Pack 1. + + + The name of the resource object to resolve. + + + The with which the + resource is associated. + + + The resolved object, or if not found. If + the resource name resolves to null, then in .NET 1.1 the return + value will be String.Empty whereas in .NET 2.0 it will return + null. + + + + + + Applies resources to object properties. + + + An object that contains the property values to be applied. + + + The base name of the object to use for key lookup. + + + The with which the + resource is associated. + + + + + Resolve the given code and arguments as message in the given culture, + returning null if not found. Does not fall back to the code + as default message. Invoked by GetMessage methods. + + The code to lookup up, such as 'calculator.noRateSet'. + array of arguments that will be filled in for params + within the message. + The with which the + resource is associated. + + The resolved message if the lookup was successful. + + + + + Try to retrieve the given message from the parent MessageSource, if any. + + The code to lookup up, such as 'calculator.noRateSet'. + array of arguments that will be filled in for params + within the message. + The with which the + resource is associated. + + The resolved message if the lookup was successful. + + + + + Return a fallback default message for the given code, if any. + + + Default is to return the code itself if "UseCodeAsDefaultMessage" + is activated, or return no fallback else. In case of no fallback, + the caller will usually receive a NoSuchMessageException from GetMessage + + The code to lookup up, such as 'calculator.noRateSet'. + The default message to use, or null if none. + + + + Renders the default message string. The default message is passed in as specified by the + caller and can be rendered into a fully formatted default message shown to the user. + + Default implementation passed he String for String.Format resolving any + argument placeholders found in them. Subclasses may override this method to plug + in custom processing of default messages. + + The default message. + The array of agruments that will be filled in for parameter + placeholders within the message, or null if none. + The with which the + resource is associated. + The rendered default message (with resolved arguments) + + + + Format the given default message String resolving any + agrument placeholders found in them. + + The message to format. + The array of agruments that will be filled in for parameter + placeholders within the message, or null if none. + The with which the + resource is associated. + The formatted message (with resolved arguments) + + + + Search through the given array of objects, find any + MessageSourceResolvable objects and resolve them. + + + Allows for messages to have MessageSourceResolvables as arguments. + + + The array of arguments for a message. + The with which the + resource is associated. + An array of arguments with any IMessageSourceResolvables resolved + + + + Gets the specified resource (e.g. Icon or Bitmap). + + The name of the resource to resolve. + + The to resolve the + code for. + + The resource if found. otherwise. + + + + Applies resources from the given name on the specified object. + + + An object that contains the property values to be applied. + + + The base name of the object to use for key lookup. + + + The with which the + resource is associated. + + + + + Subclasses must implement this method to resolve a message. + + The code to lookup up, such as 'calculator.noRateSet'. + The with which the + resource is associated. + The resolved message from the backing store of message data. + + + + Resolves an object (typically an icon or bitmap). + + +

    + Subclasses must implement this method to resolve an object. +

    +
    + The code of the object to resolve. + + The to resolve the + code for. + + + The resolved object or if not found. + +
    + + + Applies resources to object properties. + + +

    + Subclasses must implement this method to apply resources + to an arbitrary object. +

    +
    + + An object that contains the property values to be applied. + + + The base name of the object to use for key lookup. + + + The with which the + resource is associated. + +
    + + Gets or Sets a value indicating whether to use the message code as + default message instead of throwing a NoSuchMessageException. + Useful for development and debugging. Default is "false". + + +

    Note: In case of a IMessageSourceResolvable with multiple codes + (like a FieldError) and a MessageSource that has a parent MessageSource, + do not activate "UseCodeAsDefaultMessage" in the parent: + Else, you'll get the first code returned as message by the parent, + without attempts to check further codes.

    +

    To be able to work with "UseCodeAsDefaultMessage" turned on in the parent, + AbstractMessageSource contains special checks + to delegate to the internal GetMessageInternal method if available. + In general, it is recommended to just use "UseCodeAsDefaultMessage" during + development and not rely on it in production in the first place, though.

    +

    Alternatively, consider overriding the GetDefaultMessage + method to return a custom fallback message for an unresolvable code.

    +
    + + true if use the message code as default message instead of + throwing a NoSuchMessageException; otherwise, false. + +
    + + + The parent message source used to try and resolve messages that + this object can't resolve. + + + +

    + If the value of this property is then no + further resolution is possible. +

    +
    +
    + + + Creates a new instance of the + class. + + + + + Resolves a given code by searching through each assembly name in + the base names array. + + The code to resolve. + + The to use for lookups. + + The message from the resource set. + + + + Resolves a given code by searching through each assembly name in the array. + + The code to resolve. + + The to use for lookups. + + The object from the resource set. + + + + Uses a System.ComponentModel.ComponentResourceManager + to apply resources to object properties. + Resource key names are of the form objectName.propertyName + + + This feature is not currently supported on version 1.0 of the .NET platform. + + + An object that contains the property values to be applied. + + + The base name of the object to use for the key lookup. + + + The to use for lookups. + If , uses the + value. + + + This feature is not currently supported on version 1.0 of the .NET platform. + + + + + Resolves a code into an object given a base name. + + The to search. + The code to resolve. + + The to use for lookups. + + The object from the resource file. + + + + Returns a representation of the + . + + A representation of the + . + + + + Invoked by an + after it has set all object properties supplied. + + +

    + The list may contain objects of type or + . types + are converted to instances using the notation + resourcename, assembly partial name. +

    +
    + + If the conversion from a to a + can't be performed. + +
    + + + The collection of s + in this . + + + + + Thrown when a message cannot be resolved. + + Rod Johnson + Mark Pollack (.NET) + + + $Id: NoSuchMessageException.cs,v 1.8 2007/07/17 14:51:13 oakinger Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class with the + specified message. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being + thrown. + + + The + that contains contextual information about the source or + destination. + + + + + Creates a new instance of the + class. + + + The code that could not be resolved for given culture. + + + The that was used + to search for the code. + + + + + Creates a new instance of the + class. + + + The code that could not be resolved for the current UI culture. + + + + + Describes objects that are suitable for message resolution in a + . + + +

    + Spring.NET's own validation error classes implement this interface. +

    +
    + Juergen Hoeller + Mark Pollack (.NET) + $Id: IMessageSourceResolvable.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ + + +
    + + + Return the codes to be used to resolve this message, in the order + that they are to be tried. + + +

    + The last code will therefore be the default one. +

    +
    + + A array of codes which are associated + with this message. + +
    + + + Return the array of arguments to be used to resolve this message. + + + An array of objects to be used as parameters to replace + placeholders within the message text. + + + + + Return the default message to be used to resolve this message. + + + The default message, or if there is no + default. + + + + + Implements by using a hashtable. + + Erich Eichinger + $Id: ThreadStaticStorage.cs,v 1.1 2007/02/02 21:30:34 oakinger Exp $ + + + + Specifies the contract a strategy must be implement to store and + retrieve data that is specific to the executing thread. + + + All implementations of this interface must treat keys case-sensitive. + + Erich Eichinger + $Id: IThreadStorage.cs,v 1.1 2007/02/02 21:30:34 oakinger Exp $ + + + + Retrieves an object with the specified . + + The name of the item. + + The object in the current thread's context associated with the + specified or null if no object has been stored previously + + + + + Stores a given object and associates it with the specified . + + The name with which to associate the new item. + The object to store in the current thread's context. + + + + Empties a data slot with the specified name. + + + If the object with the specified is not found, the method does nothing. + + The name of the object to remove. + + + + Retrieves an object with the specified name. + + The name of the item. + The object in the call context associated with the specified name or null if no object has been stored previously + + + + Stores a given object and associates it with the specified name. + + The name with which to associate the new item. + The object to store in the call context. + + + + Empties a data slot with the specified name. + + The name of the data slot to empty. + + + + Represents a Get method + + the target instance when calling an instance method + the value return by the Get method + + + + Represents a Set method + + the target instance when calling an instance method + the value to be set + + + + Represents a method + + the target instance when calling an instance method + arguments to be passed to the method + the value return by the method. null when calling a void method + + + + Represents a callback method used to create an from a instance. + + + + + Represents a callback method used to create an from a instance. + + + + + Represents a callback method used to create an from a instance. + + + + + Represents a callback method used to create an from a instance. + + + + + Represents a callback method used to create an from a instance. + + + + + Allows easy access to existing and creation of new dynamic relection members. + + Aleksandar Seovic + $Id: DynamicReflectionManager.cs,v 1.5 2008/05/17 11:05:26 oakinger Exp $ + + + + The name of the assembly that defines reflection types created. + + + + + The attributes of the reflection type to generate. + + + + + Cache for dynamic property types. + + + + + Cache for dynamic field types. + + + + + Cache for dynamic indexer types. + + + + + Cache for dynamic method types. + + + + + Cache for dynamic constructor types. + + + + + Creates an appropriate type builder. + + + The base name to use for the reflection type name. + + The type builder to use. + + + + Returns dynamic property if one exists. + + Property to look up. + callback function that will be called to create the dynamic property + An for the given property info. + + + + Returns dynamic field if one exists. + + Field to look up. + callback function that will be called to create the dynamic field + An for the given field info. + + + + Returns dynamic indexer if one exists. + + Indexer to look up. + callback function that will be called to create the dynamic indexer + An for the given indexer. + + + + Returns dynamic method if one exists. + + Method to look up. + callback function that will be called to create the dynamic method + An for the given method. + + + + Returns dynamic constructor if one exists. + + Constructor to look up. + callback function that will be called to create the dynamic constructor + An for the given constructor. + + + + Saves dynamically generated assembly to disk. + Can only be called in DEBUG mode, per ConditionalAttribute rules. + + + + + Create a new Get method delegate for the specified field using + + the field to create the delegate for + a delegate that can be used to read the field + + + + Create a new Set method delegate for the specified field using + + the field to create the delegate for + a delegate that can be used to read the field. + + If the field's returns true, the returned method + will throw an when called. + + + + + Create a new Get method delegate for the specified property using + + the property to create the delegate for + a delegate that can be used to read the property. + + If the property's returns false, the returned method + will throw an when called. + + + + + Create a new Set method delegate for the specified property using + + the property to create the delegate for + a delegate that can be used to write the property. + + If the property's returns false, the returned method + will throw an when called. + + + + + Create a new method delegate for the specified method using + + the method to create the delegate for + a delegate that can be used to invoke the method. + + + + Generates code that throws . + + IL generator to use. + Error message to use. + + + + Allows easy access to existing and creation of new dynamic proxies. + + Aleksandar Seovic + Bruno Baia + $Id: DynamicProxyManager.cs,v 1.6 2007/05/30 17:32:05 oakinger Exp $ + + + + The name of the assembly that defines proxy types created. + + + + + The attributes of the proxy type to generate. + + + + + Creates an appropriate type builder. + + The proxy type name. + The type to extends if provided. + The type builder to use. + + + + Saves dynamically generated assembly to disk. + Can only be called in DEBUG_DYNAMIC mode, per ConditionalAttribute rules. + + + + + Tag subclass used to hold a list of managed elements. + + Rod Johnson + Rick Evans (.NET) + $Id: ManagedList.cs,v 1.15 2007/11/26 14:15:54 bbaia Exp $ + + + + Resolves this managed collection at runtime. + + + The name of the top level object that is having the value of one of it's + collection properties resolved. + + + The definition of the named top level object. + + + The name of the property the value of which is being resolved. + + + The callback that will actually do the donkey work of resolving + this managed collection. + + A fully resolved collection. + + + + Gets or sets the unresolved name for the + of the elements of this managed list. + + The unresolved name for the type of the elements of this managed list. + + + + Returns a value that is an + that + returns an object from an + . + + +

    + The primary motivation of this class is to avoid having a client object + directly calling the + + method to get a prototype object out of an + , which would be a + violation of the inversion of control principle. With the use of this + class, the client object can be fed an + as a property + that directly returns one target prototype object. +

    +

    + The object referred to by the value of the + + property does not have to be a prototype object, but there is little + to no point in using this class in conjunction with a singleton object. +

    +
    + +

    + The following XML configuration snippet illustrates the use of this + class... +

    + + + + + + + + + + + + + + + + Colin Sampaleanu + Simon White (.NET) + $Id: ObjectFactoryCreatingFactoryObject.cs,v 1.9 2007/03/16 04:01:38 aseovic Exp $ + + + + Returns an instance of the object factory. + + The object factory. + + + + Invoked by an + after it has set all supplied object properties. + + + In the event of misconfiguration (such as failure to set an essential + property) or if initialization fails. + + + + + + Sets the name of the target object. + + + + + The target factory that will be used to perform the lookup + of the object referred to by the + property. + + + The owning + (will never be ). + + + In case of initialization errors. + + + + + + The of object created by this factory. + + + + + Interface defining a factory which can return an object instance + (possibly shared or independent) when invoked. + + + This interface is typically used to encapsulate a generic factory + which returns a new instance (prototype) on each invocation. + It is similar to the , but + implementations of the aforementioned interface are normally meant to be defined + as instances by the user in an , + while implementations of this class are normally meant to be fed as a property to + other objects; as such, the + method + has different exception handling behavior. + + Colin Sampaleanu + Simon White (.NET) + $Id: IGenericObjectFactory.cs,v 1.6 2006/04/09 07:18:48 markpollack Exp $ + + + + Return an instance (possibly shared or independent) + of the object managed by this factory. + + + An instance of the object (should never be ). + + + + + Creates a new instance of the GenericObjectFactory class. + + + The enclosing + . + + + + + Returns the object created by the enclosed object factory. + + The created object. + + + + + implementation that allows for convenient registration of custom + s. + + + + The use of this class is typically not required; the .NET + mechanism of associating a + with a + via the use of the + is the + recommended (and standard) way. This class primarily exists to cover + those cases where third party classes to which one does not have the + source need to be exposed to the type conversion mechanism. + +

    + Because the + + class implements the + + interface, instances of this class that have been exposed in the + scope of an + will + automatically be picked up by the application context and made + available to the IoC container whenever type conversion is required. If + one is using a + + object definition within the scope of an + , no such automatic + pickup of the + + is performed (custom converters will have to be added manually using the + + method). For most application scenarios, one will get better + mileage using the + abstraction. +

    +
    + +

    + The following examples all assume XML based configuration, and use + inner object definitions to define the custom + objects (nominally to + avoid polluting the object name space, but also because the + configuration simply reads better that way). +

    + + + + + + + + + + + + + + + + +

    + The following example illustrates a complete (albeit naieve) use case + for this class, including a custom + implementation, said + converters domain class, and the XML configuration that hooks the + converter in place and makes it available to a Spring.NET container for + use during object resolution. +

    +

    + The domain class is a simple data-only object that contains the data + required to send an email message (such as the host and user account + name). A developer would prefer to use a string of the form + UserName=administrator,Password=r1l0k1l3y,Host=localhost to + configure the mail settings and just let the container take care of the + conversion. +

    + + namespace ExampleNamespace + { + public sealed class MailSettings + { + private string _userName; + private string _password; + private string _host; + + public string Host + { + get { return _host; } + set { _host = value; } + } + + public string UserName + { + get { return _userName; } + set { _userName = value; } + } + + public string Password + { + get { return _password; } + set { _password = value; } + } + } + + public sealed class MailSettingsConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (typeof (string) == sourceType) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + string text = value as string; + if(text != null) + { + MailSettings mailSettings = new MailSettings(); + string[] tokens = text.Split(','); + for (int i = 0; i < tokens.Length; ++i) + { + string token = tokens[i]; + string[] settings = token.Split('='); + typeof(MailSettings).GetProperty(settings[0]) + .SetValue(mailSettings, settings[1], null); + } + return mailSettings; + } + return base.ConvertFrom(context, culture, value); + } + } + + // a very naieve class that uses the MailSettings class... + public sealed class ExceptionLogger + { + private MailSettings _mailSettings; + + public MailSettings MailSettings { + { + set { _mailSettings = value; } + } + + public void Log(object value) + { + Exception ex = value as Exception; + if(ex != null) + { + // use _mailSettings instance... + } + } + } + } + +

    + The attendant XML configuration for the above classes would be... +

    + + + + + + + + + + + + + + + + Juergen Hoeller + Simon White (.NET) + $Id: CustomConverterConfigurer.cs,v 1.14 2007/05/11 02:32:14 markpollack Exp $ + + + + + + + Registers any custom converters with the supplied + . + + + The object factory to register the converters with. + + + In case of errors. + + + + + Resolves the supplied into a + instance. + + + The object that is to be resolved into a + instance. + + + A resolved instance. + + + If the supplied is , + or the supplied cannot be resolved. + + + + + The custom converters to register. + + +

    + The uses the type name + of the class that requires conversion as the key, and an + instance of the + that will effect + the conversion. Alternatively, the actual + of the class that requires conversion + can be used as the key. +

    +
    + +

    + + IDictionary converters = new Hashtable(); + converters.Add( "System.Date", new MyCustomDateConverter() ); + // a System.Type instance can also be used as the key... + converters.Add( typeof(Color), new MyCustomRBGColorConverter() ); + +

    +
    +
    + + + Programmatic means of constructing a using the builder pattern. Intended primarily + for use when implementing custom namespace parsers. + + Set methods are used instead of properties, so that chaining of methods can be used to create + 'one-liner'definitions that set multiple properties at one. + Rod Johnson + Rob Harrop + Juergen Hoeller + Mark Pollack (.NET) + $Id: ObjectDefinitionBuilder.cs,v 1.4 2007/05/29 20:00:20 markpollack Exp $ + + + + Initializes a new instance of the class, private + to force use of factory methods. + + + + + Create a new ObjectDefinitionBuilder used to construct a root object definition. + + The object definition factory. + The type name of the object. + A new ObjectDefinitionBuilder instance. + + + + Create a new ObjectDefinitionBuilder used to construct a root object definition. + + The object definition factory. + Name of the object type. + Name of the factory method. + A new ObjectDefinitionBuilder instance. + + + + Create a new ObjectDefinitionBuilder used to construct a root object definition. + + The object definition factory. + Type of the object. + A new ObjectDefinitionBuilder instance. + + + + Create a new ObjectDefinitionBuilder used to construct a root object definition. + + The object definition factory. + Type of the object. + Name of the factory method. + A new ObjectDefinitionBuilder instance. + + + + Create a new ObjectDefinitionBuilder used to construct a child object definition.. + + The object definition factory. + Name of the parent object. + + + + + Adds the property value under the given name. + + The name. + The value. + The current ObjectDefinitionBuilder. + + + + Adds a reference to the specified object name under the property specified. + + The name. + Name of the object. + The current ObjectDefinitionBuilder. + + + + Adds an index constructor arg value. The current index is tracked internally and all addtions are + at the present point + + The constructor arg value. + The current ObjectDefinitionBuilder. + + + + Adds a reference to the named object as a constructor argument. + + Name of the object. + + + + + Sets the name of the factory method to use for this definition. + + The factory method. + The current ObjectDefinitionBuilder. + + + + Sets the name of the factory object to use for this definition. + + The factory object. + The factory method. + The current ObjectDefinitionBuilder. + + + + Sets whether or not this definition describes a singleton object. + + if set to true [singleton]. + The current ObjectDefinitionBuilder. + + + + Sets whether objects or not this definition is abstract. + + if set to true [flag]. + The current ObjectDefinitionBuilder. + + + + Sets whether objects for this definition should be lazily initialized or not. + + if set to true [lazy]. + The current ObjectDefinitionBuilder. + + + + Sets the autowire mode for this definition. + + The autowire mode. + The current ObjectDefinitionBuilder. + + + + Sets the dependency check mode for this definition. + + The dependency check. + The current ObjectDefinitionBuilder. + + + + Sets the name of the destroy method for this definition. + + Name of the method. + The current ObjectDefinitionBuilder. + + + + Sets the name of the init method for this definition. + + Name of the method. + The current ObjectDefinitionBuilder. + + + + Sets the resource description for this definition. + + The resource description. + The current ObjectDefinitionBuilder. + + + + Adds the specified object name to the list of objects that this definition depends on. + + Name of the object. + The current ObjectDefinitionBuilder. + + + + Gets the current object definition in its raw (unvalidated) form. + + The raw object definition. + + + + Validate and gets the object definition. + + The object definition. + + + + Implementation of that + resolves variable name against environment variables. + + Aleksandar Seovic + $Id: EnvironmentVariableSource.cs,v 1.3 2007/08/02 22:18:32 markpollack Exp $ + + + + Resolves variable value for the specified variable name. + + + The name of the variable to resolve. + + + The variable value if able to resolve, null otherwise. + + + + + Implementation of that + resolves variable name against registry key. + + Aleksandar Seovic + $Id: RegistryVariableSource.cs,v 1.3 2007/08/03 08:31:24 oakinger Exp $ + + + + Resolves variable value for the specified variable name. + + + The name of the variable to resolve. + + + This implementation resolves REG_SZ as well as REG_MULTI_SZ values. In case of a REG_MULTI_SZ value, + strings are concatenated to a comma-separated list following + + + The variable value if able to resolve, null otherwise. + + + + + Gets or sets the registry key to obtain variable values from. + + + The registry key to obtain variable values from. + + + + + Implementation of that can be used to + format and parse numbers. + + + + NumberFormatter uses number-related properties of the + to format and parse numbers. + + + This formatter works with both integer and decimal numbers and allows + you to format and parse numbers that conform to + number style (leading and trailing white space and/or sign, thousands separator, + decimal point) + + + If you use one of the constructors that accept culture as a parameter + to create an instance of NumberFormatter, default NumberFormatInfo + for the specified culture will be used. + + + You can also use properties exposed by the NumberFormatter in order + to override some of the default number formatting parameters. + + + Aleksandar Seovic + $Id: NumberFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + + + + Initializes a new instance of the class + using default for the current thread's culture. + + + + + Initializes a new instance of the class + using default for the specified culture. + + The culture name. + + + + Initializes a new instance of the class + using default for the specified culture. + + The culture. + + + + Initializes a new instance of the class + using specified . + + + The instance that defines how + numbers are formatted and parsed. + + + + + Formats the specified number value. + + The value to format. + Formatted number . + If is null. + If is not a number. + + + + Parses the specified number value. + + The number value to parse. + Parsed number value as a . + + + + Gets or sets the number of decimal digits. + + The number of decimal digits. + + + + + Gets or sets the decimal separator. + + The decimal separator. + + + + + Gets or sets the number group sizes. + + The number group sizes. + + + + + Gets or sets the number group separator. + + The number group separator. + + + + + Gets or sets the negative pattern. + + The number negative pattern. + + + + + Implementation of that can be used to + format and parse boolean values. + + Erich Eichinger + $Id: BooleanFormatter.cs,v 1.1 2007/06/01 08:59:31 oakinger Exp $ + + + + Initializes a new instance of the class + using default values + + + + + Initializes a new instance of the class + + + + + Formats the specified boolean value. + + The value to format. + Formatted boolean value. + If is null. + If is not of type . + + + + Parses the specified boolean value according to settings of and + + The boolean value to parse. + Parsed boolean value as a . + If does not match or . + + + + Set/Get value to control casesensitivity of + + + Defaults to true + + + + + Set/Get value to recognize as boolean "true" value + + + Defaults to + + + + + Set/Get value to recognize as boolean "false" value + + + Defaults to + + + + + Represents parsed string literal node. + + Aleksandar Seovic + $Id: StringLiteralNode.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the string literal node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed expression list node in the navigation expression. + + Aleksandar Seovic + $Id: ExpressionListNode.cs,v 1.6 2007/09/07 03:01:24 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a result of the last expression in a list. + + Context to evaluate expressions against. + Current expression evaluation context. + Result of the last expression in a list + + + + Represents parsed selection node in the navigation expression. + + Aleksandar Seovic + $Id: SelectionFirstNode.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns the first context item that matches selection expression. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents logical BETWEEN operator. + + Aleksandar Seovic + $Id: OpBetween.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical IN operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + + true if the left operand is contained within the right operand, false otherwise. + + + + + Implementation of the sum aggregator. + + Aleksandar Seovic + $Id: SumAggregator.cs,v 1.2 2006/12/04 08:58:33 aseovic Exp $ + + + + Returns the sum of the numeric values in the source collection. + + + The source collection to process. + + + Ignored. + + + The sum of the numeric values in the source collection. + + + + + Implementation of the 'order by' processor. + + Aleksandar Seovic + Erich Eichinger + $Id: OrderByProcessor.cs,v 1.2 2008/03/20 23:58:16 oakinger Exp $ + + + + Sorts the source collection using custom sort criteria. + + + Please not that your compare function needs to take care about + proper conversion of types to be comparable! + + + The source collection to sort. + + + Sort criteria to use. + + + A sorted array containing collection elements. + + + + + Converts all elements in the input list to a given target type. + + Erich Eichinger + $Id: ConversionProcessor.cs,v 1.1 2008/03/20 23:58:16 oakinger Exp $ + + + + Processes a list of source items and returns a result. + + + The source list to process. + + + An optional processor arguments array. + + + The processing result. + + + + + Holds data about a and it's + attendant . + + + + + The string that separates a name + from the name of it's attendant + in an assembly qualified type name. + + + + + Creates a new instance of the TypeAssemblyHolder class. + + + The unresolved name of a . + + + + + The (unresolved) type name portion of the original type name. + + + + + The (unresolved, possibly partial) name of the attandant assembly. + + + + + Is the type name being resolved assembly qualified? + + + + + Criteria that is satisfied if the Name property of an + instance matches a + supplied regular expression pattern. + + Rick Evans + $Id: RegularExpressionEventNameCriteria.cs,v 1.1 2007/07/31 01:35:07 markpollack Exp $ + + + + A base class for all + implementations that are regular expression based. + + Rick Evans + $Id: RegularExpressionCriteria.cs,v 1.1 2007/07/31 01:35:07 markpollack Exp $ + + + + The default pattern... matches absolutely anything. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The regular expression pattern to be applied. + + + + + Does the supplied satisfy the criteria encapsulated by + this instance? + + The datum to be checked by this criteria instance. + + True if the supplied satisfies the criteria encapsulated + by this instance; false if not or the supplied is null. + + + + + Convenience method that calls the + + on the supplied . + + The input to match against. + True if the matches. + + + + The regular expression pattern to be applied. + + + + + The regular expression options to be applied. + + + + + The regular expression to be applied. + + + + + The default event name pattern... matches pretty much any event name. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The pattern that names + must match against in order to satisfy this criteria. + + + + + Does the supplied satisfy the criteria encapsulated by + this instance? + + The datum to be checked by this criteria instance. + + True if the supplied satisfies the criteria encapsulated + by this instance; false if not or the supplied is null. + + + + + Criteria that is satisfied if the method Name of an + instance matches a + supplied string pattern. + + + + Supports the following simple pattern styles: + "xxx*", "*xxx" and "*xxx*" matches, as well as direct equality. + + + Bruno Baia + $Id: MethodNameMatchCriteria.cs,v 1.1 2007/08/04 01:04:34 bbaia Exp $ + + + + Creates a new instance of the + class. + + +

    + This constructor sets the + + property to * (any method name). +

    +
    +
    + + + Creates a new instance of the + class. + + The pattern that names + must match against in order to satisfy this criteria. + + If the supplied is null or resolve to an empty string. + + + + + Does the supplied satisfy the criteria encapsulated by + this instance? + + The datum to be checked by this criteria instance. + + True if the supplied satisfies the criteria encapsulated + by this instance; false if not or the supplied is null. + + + + + The number of parameters that a + must have to satisfy this criteria. + + + If the supplied value is null or resolve to an empty string. + + + + + Provides methods to support various naming and other conventions used throughout the framework. + Mainly for internal use within the framework. + + Rob Harrop + Juergen Hoeller + Mark Pollack (.NET) + $Id: Conventions.cs,v 1.1 2007/05/26 00:42:36 markpollack Exp $ + + + Convert Strings in attribute name format (lowercase, hyphens separating words) + into property name format (camel-cased). For example, transaction-manager is + converted into transactionManager. + + + + + An unbounded priority based on a priority + heap. This queue orders elements according to an order specified + at construction time, which is specified either according to their + natural order (see , or according to a + , depending on which constructor is + used. A priority queue does not permit elements. + A priority queue relying on natural ordering also does not + permit insertion of non-comparable objects (doing so will result + . + +

    + The head of this queue is the lowest element + with respect to the specified ordering. If multiple elements are + tied for lowest value, the head is one of those elements -- ties are + broken arbitrarily. + +

    + A priority queue is unbounded, but has an internal + capacity governing the size of an array used to store the + elements on the queue. It is always at least as large as the queue + size. As elements are added to a priority queue, its capacity + grows automatically. The details of the growth policy are not + specified. + +

    + This class and its enumerator implement all of the + optional methods of the and + interfaces. + The enumerator provided in method + is not guaranteed to traverse the elements of the PriorityQueue in any + particular order. + +

    + Note that this implementation is NOT synchronized. + Multiple threads should not access a + instance concurrently if any of the threads modifies the list + structurally. Instead, use the thread-safe PriorityBlockingQueue. +

    + Josh Bloch + Griffin Caprio (.NET) +
    + + + This class provides skeletal implementations of some + operations. + + +

    + The implementations in this class are appropriate when the base + implementation does not allow elements. The methods + , + , and + are based on + the , + , and + methods + respectively but throw exceptions instead of indicating failure via + or returns. +

    + An implementation that extends this class must + minimally define a method + which does + not permit the insertion of elements, along with methods + , and + . Typically, + additional methods will be overridden as well. If these requirements + cannot be met, consider instead subclassing + }. +

    +
    + Doug Lea + Griffin Caprio (.NET) + $Id: AbstractQueue.cs,v 1.8 2007/08/27 09:38:11 oakinger Exp $ +
    + + + A collection designed for holding elements prior to processing. + + +

    + Besides basic operations, + queues provide additional insertion, extraction, and inspection + operations. +

    +

    + Each of these methods exists in two forms: one throws + an exception if the operation fails, the other returns a special + value (either or , depending on the + operation). The latter form of the insert operation is designed + specifically for use with capacity-restricted + implementations; in most implementations, insert operations cannot + fail. +

    +

    + Queues typically, but do not necessarily, order elements in a + FIFO (first-in-first-out) manner. Among the exceptions are + priority queues, which order elements according to a supplied + comparator, or the elements' natural ordering, and LIFO queues (or + stacks) which order the elements LIFO (last-in-first-out). + Whatever the ordering used, the head of the queue is that + element which would be removed by a call to + or + . In a FIFO queue, all new + elements are inserted at the tail of the queue. Other kinds of queues may + use different placement rules. Every implementation + must specify its ordering properties. +

    +

    + The method inserts an + element if possible, otherwise returning . This differs from the + method, which can fail to + add an element only by throwing an exception. The + method is designed for + use when failure is a normal, rather than exceptional occurrence, for example, + in fixed-capacity (or "bounded" queues. +

    +

    + The + methods remove and + return the head of the queue. Exactly which element is removed from the + queue is a function of the queue's ordering policy, which differs from + implementation to implementation. The + and + methods differ only in their + behavior when the queue is empty: the + method throws an exception, + while the method returns + . +

    +

    + The and + methods return, but do + not remove, the head of the queue. +

    +

    + The interface does not define the blocking queue + methods, which are common in concurrent programming. +

    +

    + implementations generally do not allow insertion + of elements, although some implementations, such as + a linked list, do not prohibit the insertion of . + Even in the implementations that permit it, should + not be inserted into a , as is also + used as a special return value by the + method to + indicate that the queue contains no elements. +

    +

    + implementations generally do not define + element-based versions of methods + and , but instead inherit the + identity based versions from the class object, because element-based equality + is not always well-defined for queues with the same elements but different + ordering properties. +

    +

    + Based on the back port of JCP JSR-166. +

    +
    + Doug Lea + Griffin Caprio (.NET) + $Id: IQueue.cs,v 1.7 2006/09/30 18:39:24 gcaprio Exp $ +
    + + + Inserts the specified element into this queue if it is possible to do so + immediately without violating capacity restrictions, returning + upon success and throwing an + if no space is + currently available. + + + The element to add. + + + if successful. + + + If the element cannot be added at this time due to capacity restrictions. + + + If the class of the supplied prevents it + from being added to this queue. + + + If the specified element is and this queue does not + permit elements. + + + If some property of the supplied prevents + it from being added to this queue. + + + + + Inserts the specified element into this queue if it is possible to do + so immediately without violating capacity restrictions. + + +

    + When using a capacity-restricted queue, this method is generally + preferable to , + which can fail to insert an element only by throwing an exception. +

    +
    + + The element to add. + + + if the element was added to this queue. + + + If the element cannot be added at this time due to capacity restrictions. + + + If the supplied is + . + + + If some property of the supplied prevents + it from being added to this queue. + +
    + + + Retrieves and removes the head of this queue. + + +

    + This method differs from + only in that it throws an exception if this queue is empty. +

    +
    + + The head of this queue + + if this queue is empty +
    + + + Retrieves and removes the head of this queue, + or returns if this queue is empty. + + + The head of this queue, or if this queue is empty. + + + + + Retrieves, but does not remove, the head of this queue. + + +

    + This method differs from + only in that it throws an exception if this queue is empty. +

    +
    + + The head of this queue. + + If this queue is empty. +
    + + + Retrieves, but does not remove, the head of this queue, + or returns if this queue is empty. + + + The head of this queue, or if this queue is empty. + + + + + Returns if there are no elements in the , otherwise. + + + + + Creates a new instance of the class. + + +

    + This is an abstract class, and as such has no publicly + visible constructors. +

    +
    +
    + + + Inserts the specified element into this queue if it is possible + to do so immediately without violating capacity restrictions. + + + The element to add. + + + if successful. + + + If the element cannot be added at this time due to capacity restrictions. + + + + + Retrieves and removes the head of this queue. + + +

    + This method differs from + only in that + it throws an exception if this queue is empty. +

    +
    + + The head of this queue + + + If this queue is empty. + +
    + + + Retrieves, but does not remove, the head of this queue. + + +

    + This method differs from + only in that it throws an exception if this queue is empty. +

    +

    + ALso note that this implementation returns the result of + unless the queue + is empty. +

    +
    + The head of this queue. + + If this queue is empty. + +
    + + + Removes all of the elements from this queue. + + +

    + The queue will be empty after this call returns. +

    +

    + This implementation repeatedly invokes + until it + returns . +

    +
    +
    + + + Adds all of the elements in the supplied + to this queue. + + +

    + Attempts to + + of a queue to itself result in . + Further, the behavior of this operation is undefined if the specified + collection is modified while the operation is in progress. +

    +

    + This implementation iterates over the specified collection, + and adds each element returned by the iterator to this queue, in turn. + An exception encountered while trying to add an element (including, + in particular, a element) may result in only some + of the elements having been successfully added when the associated + exception is thrown. +

    +
    + + The collection containing the elements to be added to this queue. + + + if this queue changed as a result of the call. + + + If the supplied or any one of its elements are . + + + If the collection is the current or + the collection size is greater than the queue capacity. + +
    + + + Inserts the specified element into this queue if it is possible to do + so immediately without violating capacity restrictions. + + +

    + When using a capacity-restricted queue, this method is generally + preferable to , + which can fail to insert an element only by throwing an exception. +

    +
    + + The element to add. + + + if the element was added to this queue. + + + If the element cannot be added at this time due to capacity restrictions. + + + If the supplied is + . + + + If some property of the supplied prevents + it from being added to this queue. + +
    + + + Retrieves, but does not remove, the head of this queue, + or returns if this queue is empty. + + + The head of this queue, or if this queue is empty. + + + + + Retrieves and removes the head of this queue, + or returns if this queue is empty. + + + The head of this queue, or if this queue is empty. + + + + + Copies the elements of the to an , starting at a particular index. + + The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + The zero-based index in array at which copying begins. + array is null. + index is less than zero. + array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source is greater than the available space from index to the end of the destination array. + The type of the source cannot be cast automatically to the type of the destination array. 2 + + + + Returns an enumerator that iterates through a collection. + + + An object that can be used to iterate through the collection. + + + + + Returns if there are no elements in the , otherwise. + + + + + Returns the current capacity of this queue. + + + + + Gets the number of elements contained in the . + + + The number of elements contained in the . + + + + + Gets an object that can be used to synchronize access to the . + + + An object that can be used to synchronize access to the . + + + + + Gets a value indicating whether access to the is synchronized (thread safe). + + + true if access to the is synchronized (thread safe); otherwise, false. + + + + + Priority queue represented as a balanced binary heap: the two children + of queue[n] are queue[2*n] and queue[2*n + 1]. The priority queue is + ordered by comparator, or by the elements' natural ordering, if + comparator is null: For each node n in the heap and each descendant d + of n, n <= d. + + The element with the lowest value is in queue[1], assuming the queue is + nonempty. (A one-based array is used in preference to the traditional + zero-based array to simplify parent and child calculations.) + + queue.length must be >= 2, even if size == 0. + + + + The number of elements in the priority queue. + + + + The comparator, or null if priority queue uses elements' + natural ordering. + + + + + The number of times this priority queue has been + structurally modified. + + + + + Creates a with the default initial capacity + (11) that orders its elements according to their natural + ordering (using ). + + + + + Creates a with the specified initial capacity + that orders its elements according to their natural ordering + (using ). + + the initial capacity for this priority queue. + + if is less than 1. + + + + Creates a with the specified initial capacity + that orders its elements according to the specified comparator. + + the initial capacity for this priority queue. + the comparator used to order this priority queue. + If then the order depends on the elements' natural ordering. + + if is less than 1. + + + + Creates a containing the elements in the + specified collection. The priority queue has an initial + capacity of 110% of the size of the specified collection or 1 + if the collection is empty. If the specified collection is an + instance of a , the priority queue will be sorted + according to the same comparator, or according to its elements' + natural order if the collection is sorted according to its + elements' natural order. Otherwise, the priority queue is + ordered according to its elements' natural order. + + the collection whose elements are to be placed into this priority queue. + if elements of cannot be + compared to one another according to the priority queue's ordering + if or any element with it is + + + + + + Common code to initialize underlying queue array across + constructors below. + + + + + Performs an unsigned bitwise right shift with the specified number + + Number to operate on + Ammount of bits to shift + The resulting number from the shift operation + + + + Establishes the heap invariant assuming the heap + satisfies the invariant except possibly for the leaf-node indexed by k + (which may have a nextExecutionTime less than its parent's). + + + This method functions by "promoting" queue[k] up the hierarchy + (by swapping it with its parent) repeatedly until queue[k] + is greater than or equal to its parent. + + + + + Establishes the heap invariant (described above) in the subtree + rooted at k, which is assumed to satisfy the heap invariant except + possibly for node k itself (which may be greater than its children). + + + This method functions by "demoting" queue[k] down the hierarchy + (by swapping it with its smaller child) repeatedly until queue[k] + is less than or equal to its children. + + + + + Establishes the heap invariant in the entire tree, + assuming nothing about the order of the elements prior to the call. + + + + + Returns the of or - 1, + whichever is smaller. + + base size + percentage to return + of + + + + Initially fill elements of the queue array under the + knowledge that it is sorted or is another , in which + case we can just place the elements in the order presented. + + + + + Initially fill elements of the queue array that is not to our knowledge + sorted, so we must rearrange the elements to guarantee the heap + invariant. + + + + + Removes and returns element located at from queue. (Recall that the queue + is one-based, so 1 <= i <= size.) + + + Normally this method leaves the elements at positions from 1 up to i-1, + inclusive, untouched. Under these circumstances, it returns . + Occasionally, in order to maintain the heap invariant, it must move + the last element of the list to some index in the range [2, i-1], + and move the element previously at position (i/2) to position i. + Under these circumstances, this method returns the element that was + previously at the end of the list and is now at some position between + 2 and i-1 inclusive. + + + + Resize array, if necessary, to be able to hold given index + + + + Inserts the specified element into this queue if it is possible to do + so immediately without violating capacity restrictions. + + +

    + When using a capacity-restricted queue, this method is generally + preferable to , + which can fail to insert an element only by throwing an exception. +

    +
    + + The element to add. + + + if the element was added to this queue. + + + if the specified element cannot be compared + with elements currently in the priority queue according + to the priority queue's ordering. + + + If the element cannot be added at this time due to capacity restrictions. + + + If the supplied is + and this queue does not permit + elements. + + + If some property of the supplied prevents + it from being added to this queue. + +
    + + + Retrieves, but does not remove, the head of this queue, + or returns if this queue is empty. + + + The head of this queue, or if this queue is empty. + + + + + Inserts the specified element into this queue if it is possible to do so + immediately without violating capacity restrictions, returning + upon success and throwing an + if no space is + currently available. + + + The element to add. + + + if successful. + + + If the element cannot be added at this time due to capacity restrictions. + + + If the specified element is and this queue does not + permit elements. + + + If some property of the supplied prevents + it from being added to this queue. + + + if the specified element cannot be compared + with elements currently in the priority queue according + to the priority queue's ordering. + + + + + Removes a single instance of the specified element from this + queue, if it is present. + + + + + Returns an over the elements in this queue. + The enumeratoar does not return the elements in any particular order. + + an enumerator over the elements in this queue. + + + + Removes all elements from the priority queue. + The queue will be empty after this call returns. + + + + + Retrieves and removes the head of this queue, + or returns if this queue is empty. + + + The head of this queue, or if this queue is empty. + + + + + Queries the queue to see if it contains the specified + + element to look for. + if the queue contains the , + otherwise. + + + Returns the comparator used to order this collection, or + if this collection is sorted according to its elements natural ordering + (using ). + + + the comparator used to order this collection, or + if this collection is sorted according to its elements natural ordering. + + + + + Save the state of the instance to a stream (that + is, serialize it). + + The length of the array backing the instance is + emitted (int), followed by all of its elements (each an + ) in the proper order. + + the stream + the context + + + + Reconstitute the instance from a stream (that is, + deserialize it). + + the stream + the context + + + + Copies the elements of the to an , starting at a particular index. + + The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + The zero-based index in array at which copying begins. + array is null. + index is less than zero. + array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source is greater than the available space from index to the end of the destination array. + The type of the source cannot be cast automatically to the type of the destination array. 2 + + + + Copies the elements of the to an , starting at index 0. + + The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + array is null. + index is less than zero. + array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source is greater than the available space from index to the end of the destination array. + The type of the source cannot be cast automatically to the type of the destination array. 2 + + + + Gets the Capacity of this queue. Will equal + + + + + Returns the queue count. + + + + + Gets an object that can be used to synchronize access to the . + + + An object that can be used to synchronize access to the . + + + + + Gets a value indicating whether access to the is synchronized (thread safe). + + + true if access to the is synchronized (thread safe); otherwise, false. + + + + + Returns if there are no elements in the , otherwise. + + + + + Index (into queue array) of element to be returned by subsequent call to next. + + + + + This attribute should be used with methods that return an + in order to cache each item separately. + + +

    + This attribute allows application developers to specify that each item + from the collection returned by the method should be cached, + but it will not do any caching by itself. +

    +

    + In order to actually cache the result, an application developer + must apply a Spring.Aspects.Cache.CacheResultAdvice to + all of the members that have this attribute defined. +

    +
    + Aleksandar Seovic + $Id: CacheResultItemsAttribute.cs,v 1.2 2007/03/31 02:59:48 bbaia Exp $ +
    + + + Abstract base class containing shared properties for all cache attributes. + + Aleksandar Seovic + $Id: BaseCacheAttribute.cs,v 1.5 2007/08/29 17:29:10 oakinger Exp $ + + + + The instance used to parse values. + + + + + + + Creates an attribute instance. + + + + + Creates an attribute instance. + + + The name of the cache to use. + + + An expression string that should be evaluated in order to determine + the cache key for the item. + + + + + Gets or sets the name of the cache to use. + + + The name of the cache to use. + + + + + Gets or sets a SpEL expression that should be evaluated in order + to determine the cache key for the item. + + + An expression string that should be evaluated in order to determine + the cache key for the item. + + + + + Gets an expression instance that should be evaluated in order + to determine the cache key for the item. + + + An expression instance that should be evaluated in order to determine + the cache key for the item. + + + + + Gets or sets a SpEL expression that should be evaluated in order + to determine whether the item should be cached. + + + An expression string that should be evaluated in order to determine + whether the item should be cached. + + + + + Gets an expression instance that should be evaluated in order + to determine whether the item should be cached. + + + An expression instance that should be evaluated in order to determine + whether the item should be cached. + + + + + The amount of time an object should remain in the cache. + + + If no TTL is specified, the default TTL defined by the + cache's policy will be applied. + + + The amount of time object should remain in the cache + formatted to be recognizable by . + + + + + The amount of time an object should remain in the cache (in seconds). + + + If no TTL is specified, the default TTL defined by the + cache's policy will be applied. + + + The amount of time object should remain in the cache (in seconds). + + + + + Creates an attribute instance. + + + + + Creates an attribute instance. + + + The name of the cache to use. + + + An expression string that should be evaluated in order to determine + the cache key for the item. + + + + + Evaluates validator test using condition evaluator. + + Aleksandar Seovic + $Id: ConditionValidator.cs,v 1.5 2006/04/09 07:19:04 markpollack Exp $ + + + + Creates a new instance of the class. + + + + + Creates a new instance of the class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Creates a new instance of the class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Evaluates the test using condition evaluator. + + +

    + Test can be any logical expression that is supported by the Spring.NET logical + expression evaluation engine, and can use any variables that can be resolved + by the variable resolver used by the validation engine. +

    +
    + The object to validate. + + if the supplied is valid. + +
    + + + UniqueKey allows for generating keys unique to a type or particular instance and a partial name, + that can e.g. be used as keys in . + + + // shows usage type-scoped keys + UniqueKey classAKey = UniqueKey.GetTypeScoped(typeof(ClassA), "myKey"); + UniqueKey classBKey = UniqueKey.GetTypeScoped(typeof(ClassB), "myKey"); + + HttpContext.Current.Items.Add( classAKey, "some value unqiue for class A having key 'myKey'"); + object value = HttpContext.Current.Items[ UniqueKey.GetTypeScoped(typeof(ClassA), "myKey") ]; + Assert.AreEqual( "some value unique for class A having key 'myKey'", value); + + HttpContext.Current.Items.Add( classBKey, "some value unqiue for class B having key 'myKey'"); + object value = HttpContext.Current.Items[ UniqueKey.GetTypeScoped(typeof(ClassB), "myKey") ]; + Assert.AreEqual( "some value unique for class B having key 'myKey'", value); + + + + + Initialize a new instance of from its string representation. + See and See for details. + + The string representation of the new instance. + + + + Compares this instance to another. + + + + + Compares this instance to another. + + + + + Returns the hash code for this key. + + + + + + Returns a string representation of this key. + + + + + Creates a new key instance unique to the given instance. + + The instance the key shall be unique to + The partial key to be made unique + + + If is of type + + + + Creates a new key instance unique to the given type. + + The type the key shall be unique to + The partial key to be made unique + + + + Returns a key unique for the given instance. + + The instance the key shall be unique to + The partial key to be made unique + A key formatted as typename[instance-id].partialkey + + + + Returns a key unique for the given type. + + The type the key shall be unique to + The partial key to be made unique + A key formatted as typename.partialkey + + + + Miscellaneous generic collection utility methods. + + + Mainly for internal use within the framework. + + Mark Pollack (.NET) + $Id: CollectionUtils.cs,v 1.2 2006/12/02 07:51:28 markpollack Exp $ + + + + Determine whether a given collection only contains + a single unique object + + + + + + + Determines whether the contains the specified . + + The collection to check. + The object to locate in the collection. + if the element is in the collection, otherwise. + + + + Determines whether the collection contains all the elements in the specified collection. + + The collection to check. + Collection whose elements would be checked for containment. + true if the target collection contains all the elements of the specified collection. + + + Thrown by synchronization classes that report + timeouts via exceptions. The exception is treated + as a form (subclass) of InterruptedException. This both + simplifies handling, and conceptually reflects the fact that + timed-out operations are artificially interrupted by timers. + + + + + The approximate time that the operation lasted before + this timeout exception was thrown. + + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class with the + specified message. + + + A message about the exception. + + + + + Creates a new instance of the + class with the + specified message. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Override of GetObjectData to allow for private serialization + + serialization info + streaming context + + + Constructs a TimeoutException with given duration value. + + + + + Constructs a TimeoutException with the + specified duration value and detail message. + + + + + Gets the approximate time that the operation lasted before + this timeout exception was thrown. + + + + + Combined exception, composed of individual binding + s. + + +

    + An object of this class is created at the beginning of the binding + process, and errors added to it as necessary. +

    +

    + The binding process continues when it encounters application-level + s, applying those changes + that can be applied and storing rejected changes in an instance of this class. +

    +
    + Rod Johnson + Juergen Hoeller + Mark Pollack (.NET) + $Id: PropertyAccessExceptionsException.cs,v 1.15 2007/07/31 00:26:30 markpollack Exp $ +
    + + + Creates a new instance of the PropertyAccessExceptionsException class. + + + + + Creates a new instance of the PropertyAccessExceptionsException class. + + + A message about the exception. + + + + + Creates a new instance of the PropertyAccessExceptionsException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Create new empty PropertyAccessExceptionsException. + We'll add errors to it as we attempt to bind properties. + + + + + Creates a new instance of the PropertyAccessExceptionsException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + The IObjectWrapper wrapping the target object at the root of the exception. + + + + The list of PropertyAccessException objects. + + + + Return the + for the supplied , or + if there isn't one. + + + + + Describe the number of exceptions contained in this container class. + + A description of the instance contents. + + + + Return the that generated + this exception. + + + + + Return the object we're binding to. + + + + + If this returns zero (0), no errors were encountered during binding. + + + + + Return an array of the s + stored in this object. + + +

    + Will return the empty array (not ) if there were no errors. +

    +
    +
    + + + Describe the group of exceptions. + + + + + Represents the replacement of a method on a managed object by the IoC + container. + + +

    + Note that this mechanism is not intended as a generic means of + inserting crosscutting code: use AOP for that. +

    +
    + Rod Johnson + Rick Evans (.NET) + $Id: ReplacedMethodOverride.cs,v 1.3 2007/03/16 04:01:42 aseovic Exp $ +
    + + + Represents the override of a method on a managed object by the IoC container. + + +

    + Note that the override mechanism is not intended as a generic means of + inserting crosscutting code: use AOP for that. +

    +
    + Rod Johnson + Rick Evans (.NET) + $Id: MethodOverride.cs,v 1.9 2007/08/22 08:52:03 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    + + The name of the method that is to be overridden. + + + If the supplied is or + contains only whitespace character(s). + +
    + + + Does this + match the supplied ? + + +

    + By 'match' one means does this particular + + instance apply to the supplied ? +

    +

    + This allows for argument list checking as well as method name checking. +

    +
    + The method to be checked. + + if this override matches the supplied + . + +
    + + + The name of the method that is to be overridden. + + + + + Is the method that is ot be injected + () + to be considered as overloaded? + + +

    + If (the default), then argument type matching + will be performed (because one would not want to override the wrong + method). +

    +

    + Setting the value of this property to can be used + to optimize runtime performance (ever so slightly). +

    +
    +
    + + + Creates a new instance of the + class. + + + The name of the method that is to be overridden. + + + The object name of the + instance in the surrounding IoC container. + + + If either of the supplied arguments is or + contains only whitespace character(s). + + + + + Add a fragment of a instance's + such as 'Exception or System.Excep to identify an argument + for a dependency injected method. + + + A (sub) string of a instance's . + + + If the supplied is or + contains only whitespace character(s). + + + + + + Does this + match the supplied ? + + The method to be checked. + + if this override matches the supplied . + + + If the supplied is . + + + + + A that represents the current + . + + + A that represents the current + . + + + + + The object name of the + instance in the surrounding IoC container. + + + + + Utility methods that are useful for + + implementations. + + Juergen Hoeller + Rick Evans (.NET) + + $Id: ObjectDefinitionReaderUtils.cs,v 1.13 2007/08/07 22:05:20 markpollack Exp $ + + + + The string used as a separator in the generation of synthetic id's + for those object definitions explicitly that aren't assigned one. + + +

    + If a name or parent object definition + name is not unique, "#1", "#2" etc will be appended, until such + time that the name becomes unique. +

    +
    +
    + + + Registers the supplied with the + supplied . + + +

    + This is a convenience method that registers the + + of the supplied under the + + property value of said . If the + supplied has any + , + then those aliases will also be registered with the supplied + . +

    +
    + + The object definition holder containing the + that + is to be registered. + + + The registry that the supplied + is to be registered with. + + + If either of the supplied arguments is . + + + If the could not be registered + with the . + +
    + + + Generates an object definition name for the supplied + that is guaranteed to be unique + within the scope of the supplied . + + + The + that requires a generated name. + + + The + + that the supplied is to be + registered with (needed so that the uniqueness of any generated + name can be guaranteed). + + + An object definition name for the supplied + that is guaranteed to be unique + within the scope of the supplied and + never . + + + If either of the or + arguments is . + + + If a unique name cannot be generated. + + + + + Factory method for getting concrete + instances. + + + The name of the event handler method. This may be straight text, a regular + expression, , or empty. + + + The name of the event being wired. This too may be straight text, a regular + expression, , or empty. + + + A concrete + instance. + + + + + Creates a new instance of the + class. + + +

    + This is a utility class, and as such exposes no public constructors. +

    +
    +
    + + + Abstract superclass for + implementations. + + +

    + This class provides singleton / prototype determination, singleton caching, + object definition aliasing, + handling, and object definition merging for child object definitions. +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: AbstractObjectFactory.cs,v 1.76 2008/04/05 13:35:35 oakinger Exp $ +
    + + + Marker object to be temporarily registered in the singleton cache, + while instantiating an object (in order to be able to detect circular references). + + + + + The instance for this class. + + + + + Used as value in hashtable that keeps track of singleton names currently in the + process of being created. Would not be necessary if we created a case insensitive implementation of + ISet. + + + + + Creates a new instance of the + class. + + +

    + This constructor implicitly creates an + + that treats the names of objects in this factory in a case-sensitive fashion. +

    +

    + This is an class, and as such exposes no public constructors. +

    +
    +
    + + + Creates a new instance of the + class. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    + + if the names of objects in this factory are to be treated in a + case-sensitive fashion. + +
    + + + Creates a new instance of the + class. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    + + if the names of objects in this factory are to be treated in a + case-sensitive fashion. + + + Any parent object factory; may be . + +
    + + + Return an instance (possibly shared or independent) of the given object name. + + The name of the object to return. + + The the object may match. Can be an interface or + superclass of the actual class. For example, if the value is the + class, this method will succeed whatever the + class of the returned instance. + + + The arguments to use if creating a prototype using explicit arguments to + a factory method. If there is no factory method and the + supplied array is not , then + match the argument values by type and call the object's constructor. + + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + If the object is not of the required type. + + + If the supplied is . + + + + + + Apply the property values of the object definition with the supplied + to the supplied . + + +

    + The object definition can either define a fully self-contained object, + reusing it's property values, or just property values meant to be used + for existing object instances. +

    +
    + + The existing object that the property values for the named object will + be applied to. + + + The name of the object definition associated with the property values that are + to be applied. + + + In case of errors. + +
    + + + Initializes the given with the + custom s registered with + this factory. + + + The to initialise. + + + + + Create an object instance for the given object definition. + + +

    + The object definition will already have been merged with the parent + definition in case of a child definition. +

    +

    + All the other methods in this class invoke this method, although objects + may be cached after being instantiated by this method. All object + instantiation within this class is performed by this method. +

    +
    + The name of the object. + + The object definition for the object that is to be instantiated. + + + The arguments to use if creating a prototype using explicit arguments to + a factory method. If there is no factory method and the + supplied array is not , + then match the argument values by type and call the object's constructor. + + + A new instance of the object. + + + In case of errors. + +
    + + + Destroy the target object. + + +

    + Must destroy objects that depend on the given object before the object itself, + nor throw an exception. +

    +
    + + The name of the object. + + + The target object instance to destroyed. + +
    + + + Does this object factory contain an object definition with the + supplied ? + + +

    + Does not consider any hierarchy this factory may participate in. + Invoked by + + when no cached singleton instance is found. +

    +
    + + The name of the object to look for. + + + if this object factory contains an object + definition with the supplied . + +
    + + + Adds the supplied (object) to this factory's + singleton cache. + + +

    + To be called for eager registration of singletons, e.g. to be able to + resolve circular references. +

    + + If a singleton has already been registered under the same name as + the supplied , then the old singleton will + be replaced. + +
    + The name of the object. + The singleton object. + + If the argument is + or consists wholly of whitespace characters; or if the + is . + +
    + + + Return the object name, stripping out the factory dereference prefix if + necessary, and resolving aliases to canonical names. + + + The transformed name of the object. + + + + + Ensures, that the given name is prefixed with + if it incidentially already starts with this prefix. This avoids troubles when dereferencing + the object name during + + + + + Determines whether the specified name is defined as an alias as opposed + to the name of an actual object definition. + + The object name to check. + + true if the specified name is alias; otherwise, false. + + + + + Return a , + even by traversing parent if the parameter is a child definition. + + + The name of the object. + + + Are ancestors to be included in the merge? + + +

    + Will ask the parent object factory if not found in this instance. +

    +
    + + A merged + with overridden properties. + +
    + + + Return a , + even by traversing parent if the parameter is a child definition. + + + A merged + with overridden properties. + + + + + Creates the root object definition. + + The template definition to base root definition on. + Root object definition. + + + + Return the registered + for the + given object, allowing access to its property values and constructor + argument values. + + The name of the object. + + The registered + . + + + If there is no object with the given name. + + + In the case of errors. + + + + + Return the registered + for the + given object, allowing access to its property values and constructor + argument values. + + The name of the object. + Whether to search parent object factories. + + The registered + . + + + If there is no object with the given name. + + + In the case of errors. + + + + + Gets the type for the given FactoryObject. + + The factory object instance to check. + the FactoryObject's object type + + + + Gets the object type for the given FactoryObject definition, as far as possible. + Only called if there is no singleton instance registered for the target object already. + + + The default implementation creates the FactoryObject via GetObject + to call its ObjectType property. Subclasses are encouraged to optimize + this, typically by just instantiating the FactoryObject but not populating it yet, + trying whether its ObjectType property already returns a type. + If no type found, a full FactoryObject creation as performed by this implementation + should be used as fallback. + + Name of the object. + The merged object definition for the object. + The type for the object if determinable, or null otherwise + + + + Predict the eventual object type (of the processed object instance) for the + specified object. + + + Does not need to handle FactoryObjects specifically, since it is only + supposed to operate on the raw object type. + This implementation is simplistic in that it is not able to + handle factory methods and InstantiationAwareBeanPostProcessors. + It only predicts the object type correctly for a standard object. + To be overridden in subclasses, applying more sophisticated type detection. + + Name of the object. + The merged object definition to determine the type for. + The type of the object, or null if not predictable + + + + Get the object for the given object instance, either the object + instance itself or its created object in case of an + . + + + The name that may include the factory dereference prefix. + + The object instance. + + The singleton instance of the object. + + + + + Obtain an object to expose from the given IFactoryObject. + + The IFactoryObject instance. + Name of the object. + The merged object definition. + The object obtained from the IFactoryObject + If IFactoryObject object creation failed. + + + + Post-process the given object that has been obtained from the FactoryObject. + The resulting object will be exposed for object references. + + The default implementation simply returns the given object + as-is. Subclasses may override this, for example, to apply + post-processors. + The instance obtained from the IFactoryObject. + Name of the object. + The object instance to expose + if any post-processing failed. + + + + Convenience method to pull an + from this factory. + + + The name of the factory object to be retrieved. If this name is not a valid + name, it will be converted + into one. + + + The associated with the + supplied . + + + + + Is the supplied a factory object dereference? + + + + + Determines whether the type of the given object definition matches the + specified target type. + + Allows for lazy load of the actual object type, provided that the + type match can be determined otherwise. + The default implementation simply delegates to the standard + ResolveObjectType method. Subclasses may override this to use + a differnt strategy. + + Name of the object (for error handling purposes). + The merged object definition to determine the type for. + Type to match against (never null). + + true if object definition matches tye specified target type; otherwise, false. + + if we failed to load the type." + + + + Resolves the type of the object for the specified object definition resolving + an object type name to a Type (if necessary) and storing the resolved Type + in the object definition for further use. + + The merged object definition to dertermine the type for. + Name of the object (for error handling purposes). + + + + + Is the object (definition) with the supplied an + ? + + The name of the object to be checked. + + the object (definition) with the supplied + an ? + + + + + Remove the object identified by the supplied + from this factory's singleton cache. + + + The name of the object that is to be removed from the singleton + cache. + + + If the argument is or + consists wholly of whitespace characters. + + + + + Return the names of objects in the singleton cache that match the given + object type (including subclasses). + + + The class or interface to match, or for all object names. + + +

    + Will not consider s + as the type of their created objects is not known before instantiation. +

    +

    + Does not consider any hierarchy this factory may participate in. +

    +
    + + The names of objects in the singleton cache that match the given + object type (including subclasses), or an empty array if none. + +
    + + + Determines whether the object with the given name matches the specified type. + + More specifically, check whether a GetObject call for the given name + would return an object that is assignable to the specified target type. + Translates aliases back to the corresponding canonical bean name. + Will ask the parent factory if the bean cannot be found in this factory instance. + + The name of the object to query. + Type of the target to match against. + + true if the object type matches; otherwise, false + if it doesn't match or cannot be determined yet. + + Ff there is no object with the given name + + + + + Determines the of the object with the + supplied . + + +

    + More specifically, checks the of object that + would return. + For an , returns the + of object that the + creates. +

    +

    + Please note that (prototype) objects created via a factory method or + objects are handled + slightly differently, in that we don't want to needlessly create + instances of such objects just to determine the + of object that they create. +

    +
    + The name of the object to query. + + The of the object or + if not determinable. + +
    + + + Determines the of the object defined + by the supplied object . + + +

    + This, the default, implementation returns + to indicate that the type cannot be determined. Subclasses are + encouraged to try to determine the actual return + here, matching their strategy of resolving + factory methods in the + + implementation. +

    +
    + + The name associated with the supplied object . + + + The + that the is to be determined for. + + + The of the object defined by the supplied + object ; or if the + cannot be determined. + +
    + + + Returns the names of the objects in the singleton cache. + + +

    + Does not consider any hierarchy this factory may participate in. +

    +
    + The names of the objects in the singleton cache. +
    + + + Returns the number of objects in the singleton cache. + + +

    + Does not consider any hierarchy this factory may participate in. +

    +
    + The number of objects in the singleton cache. +
    + + + Destroys the named singleton object. + + +

    + Delegates to + + if a corresponding singleton instance is found. +

    +
    + + The name of the singleton object that is to be destroyed. + + +
    + + + Check the supplied merged object definition for any possible + validation errors. + + + The object definition to be checked for validation errors. + + + The name of the object associated with the supplied object definition. + + + The the object may match. Can be an interface or + superclass of the actual class. For example, if the value is the + class, this method will succeed whatever the + class of the returned instance. + + + The arguments to use if creating a prototype using explicit arguments to + a factory method. If there is no factory method and the + supplied array is not , then + match the argument values by type and call the object's constructor. + + + In the case of object validation errors. + + + + + Parent object factory, for object inheritance support + + + + + Dependency types to ignore on dependency check and autowire, as Set of + Type objects: for example, string. Default is none. + + + + + ObjectPostProcessors to apply in CreateObject + + + + + Indicates whether any IInstantiationAwareBeanPostProcessors have been registered + + + + + Indicates whether any IDestructionAwareBeanPostProcessors have been registered + + + + + Is this object a singleton? + + + + + + Determines whether the specified object name is prototype. That is, will GetObject + always return independent instances? + + The name of the object to query + + true if the specified object name will always deliver independent instances; otherwise, false. + + This method returning false does not clearly indicate a singleton object. + It indicated non-independent instances, which may correspond to a scoped object as + well. use the IsSingleton property to explicitly check for a shared + singleton instance. + Translates aliases back to the corresponding canonical object name. Will ask the + parent factory if the object can not be found in this factory instance. + + + if there is no object with the given name. + + + + Does this object factory contain an object with the given name? + + + This method does not (and it should not) check if the specified + object exists in one of the parent object factories. If it did, + message sources and event registries within application context + hierarchy would have circular references, which would cause stack + overflows during message lookup, for example. (A. Seovic) + + . + + + + Return the aliases for the given object name, if defined. + + . + + + + Return an instance (possibly shared or independent) of the given object name. + + . + + + + Return an instance (possibly shared or independent) of the given object name. + + +

    + This method allows an object factory to be used as a replacement for the + Singleton or Prototype design pattern. +

    +

    + Note that callers should retain references to returned objects. There is no + guarantee that this method will be implemented to be efficient. For example, + it may be synchronized, or may need to run an RDBMS query. +

    +

    + Will ask the parent factory if the object cannot be found in this factory + instance. +

    +
    + The name of the object to return. + + The arguments to use if creating a prototype using explicit arguments to + a static factory method. If there is no factory method and the + arguments are not null, then match the argument values by type and + call the object's constructor. + + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + If the supplied is . + +
    + + + Tries to find a cached object for the specified name. + + Teh object name to look for. + The cached object if found, otherwise. + + + + Creates a singleton instance for the specified object name and definition. + + + The object name (will be used as the key in the singleton cache key). + + The object definition. + + The arguments to use if creating a prototype using explicit arguments to + a static factory method. If there is no factory method and the + arguments are not null, then match the argument values by type and + call the object's constructor. + + The created object instance. + + + + Return an instance (possibly shared or independent) of the given object name. + + + + + + Injects dependencies into the supplied instance + using the named object definition. + + + + + + Injects dependencies into the supplied instance + using the supplied . + + + + + + Destroy all cached singletons in this factory. + + + + + Ignore the given dependency type for autowiring + + . + + + + Determines whether the specified object name is currently in creation.. + + Name of the object. + + true if the specified object name is currently in creation; otherwise, false. + + + + + Add a new + that will get applied to objects created by this factory. + + + The + to register. + + . + + + + Given an object name, create an alias. + + . + + + + Register the given existing object as singleton in the object factory, + under the given object name. + + . + + + + Register the given custom + for all properties of the given . + + . + + + + Does this object factory contains a singleton instance with the + supplied ? + + + + + + Gets the of + s + that will be applied to objects created by this factory. + + + + + Gets the set of classes that will be ignored for autowiring. + + +

    + The elements of this are + s. +

    +
    +
    + + + Returns, whether this object factory instance contains objects. + + + + + Returns, whether this object factory instance contains objects. + + + + + Gets the temporary object that is placed + into the singleton cache during object resolution. + + + + + The parent object factory, or if there is none. + + + The parent object factory, or if there is none. + + + + + Return an instance (possibly shared or independent) of the given object name. + + . + + + + Returns the current number of registered + s. + + + The current number of registered + s. + + . + + + + Exception thrown when an + encounters an error when attempting to parse an object + definition. + + Federico Spinazzi (.NET) + + + + Creates a new instance of the ObjectDefinitionException class. + + + + + Creates a new instance of the ObjectDefinitionException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ObjectDefinitionException class. + + + The value of the xml class attribute thet can be resolved + as a type + + + + + Creates a new instance of the ObjectDefinitionException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + The message about the exception. + + + + + Thrown in case of a reference to an object that is currently in creation. + + +

    + Typically happens when constructor autowiring matches the currently + constructed object. +

    +
    + Juergen Hoeller + Rick Evans + $Id: ObjectCurrentlyInCreationException.cs,v 1.6 2007/12/05 00:28:04 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The name of the object that triggered the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The name of the object that triggered the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The description of the resource associated with the object. + + + A message about the exception. + + + The name of the object that triggered the exception. + + + + + Creates a new instance of the + class. + + + The description of the resource associated with the object. + + + A message about the exception. + + + The name of the object that triggered the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ObjectCurrentlyInCreationException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Immutable placeholder class used for the value of a + object when it's a reference + to another object in this factory to be resolved at runtime. + + Rod Johnson + Rick Evans (.NET) + + + + Creates a new instance of the + + class. + + +

    + This does not mark this object as being a reference to + another object in any parent factory. +

    +
    + The name of the target object. +
    + + + Creates a new instance of the + + class. + + +

    + This variant constructor allows a client to specifiy whether or not + this object is a reference to another object in a parent factory. +

    +
    + The name of the target object. + + Whether this object is an explicit reference to an object in a + parent factory. + +
    + + + Returns a string representation of this instance. + + A string representation of this instance. + + + + Return the target object name. + + + + + Is this is an explicit reference to an object in the parent + factory? + + + if this is an explicit reference to an + object in the parent factory. + + + + + Default implementation of the + interface. + + +

    + Allows simple manipulation of properties, and provides constructors to + support deep copy and construction from a number of collection types such as + and + . +

    +
    + Rod Johnson + Mark Pollack (.NET) + Rick Evans (.NET) + $Id: MutablePropertyValues.cs,v 1.15 2007/03/16 04:01:29 aseovic Exp $ +
    + + + The list of objects. + + + + + Creates a new instance of the + class. + + +

    + The returned instance is initially empty... + s can be added with the various + overloaded , + , + , + and + methods. +

    +
    + + +
    + + + Creates a new instance of the + class. + + +

    + Deep copy constructor. Guarantees + references are independent, although it can't deep copy objects currently + referenced by individual objects. +

    +
    +
    + + + Creates a new instance of the + class. + + + The with property values + keyed by property name, which must be a . + + + + + Overloaded version of Add that takes a property name and a property value. + + + The name of the property. + + + The value of the property. + + + + + Add the supplied object, + replacing any existing one for the respective property. + + + The object to add. + + + + + Add all property values from the given + . + + + The map of property values, the keys of which must be + s. + + + + + Add all property values from the given + . + + + The list of s to be added. + + + + + Remove the given , if contained. + + + The to remove. + + + + + Removes the named , if contained. + + + The name of the property. + + + + + Modify a object held in this object. Indexed from 0. + + + + + Return the property value given the name. + + + The property name is checked in a case-insensitive fashion. + + + The name of the property. + + + The property value. + + + + + Does the container of properties contain one of this name. + + The name of the property to search for. + + True if the property is contained in this collection, false otherwise. + + + + + Return the difference (changes, additions, but not removals) of + property values between the supplied argument and the values + contained in the collection. + + Another property values collection. + + The collection of property values that are different than the supplied one. + + + + + Returns an that can iterate + through a collection. + + +

    + The returned is the + exposed by the + + property. +

    +
    + + An that can iterate through a + collection. + +
    + + + Convert the object to a string representation. + + + A string representation of the object. + + + + + Property to retrieve the array of property values. + + + + + Holder for an with + name and aliases. + + +

    + Recognized by + + for inner object definitions. Registered by + , + which also uses it as general holder for a parsed object definition. +

    +

    + Can also be used for programmatic registration of inner object + definitions. If you don't care about the functionality offered by the + interface and the like, + registering + or is good enough. +

    +
    + Juergen Hoeller + Simon White (.NET) + $Id: ObjectDefinitionHolder.cs,v 1.2 2007/08/07 22:05:20 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + The object definition to be held by this instance. + + + The name of the object definition. + + + + + Creates a new instance of the + class. + + + The object definition to be held by this instance. + + The name of the object. + + Any aliases for the supplied + + + + + The held by this + instance. + + + + + The name of the object definition. + + + + + Any aliases for the object definition. + + +

    + Guaranteed to never return ; if the associated + + does not have any aliases associated with it, then an empty + array will be returned. +

    +
    +
    + + + Extension of the interface + that injects dependencies into the object managed by the factory. + + Bruno Baia + $Id: IConfigurableFactoryObject.cs,v 1.1 2007/07/29 19:39:27 markpollack Exp $ + + + + Gets the template object definition that should be used + to configure the instance of the object managed by this factory. + + + + + Represents unary plus operator. + + Aleksandar Seovic + $Id: OpUnaryPlus.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the unary plus operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents arithmetic division operator. + + Aleksandar Seovic + $Id: OpDIVIDE.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the arithmetic division operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed method node in the navigation expression. + + Aleksandar Seovic + $Id: MethodNode.cs,v 1.21 2008/03/20 23:58:16 oakinger Exp $ + + + + Static constructor. Initializes a map of special collection processor methods. + + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns node's value for the given context. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Gets the best method given the name, argument values, for a given type. + + The type on which to search for the method. + Name of the method. + The binding flags. + The arg values. + Best matching method or null if none found. + + + + Container object for the parsed expression. + + +

    + Preparing this object once and reusing it many times for expression + evaluation can result in significant performance improvements, as + expression parsing and reflection lookups are only performed once. +

    +
    + Aleksandar Seovic + $Id: Expression.cs,v 1.19 2008/03/20 10:35:54 oakinger Exp $ +
    + + + Initializes a new instance of the class + by parsing specified expression string. + + Expression to parse. + + + + Registers lambda expression under the specified . + + Function name to register expression as. + Lambda expression to register. + Variables dictionary that the function will be registered in. + + + + Initializes a new instance of the class + by parsing specified primary expression string. + + Primary expression to parse. + + + + Initializes a new instance of the class + by parsing specified property expression string. + + Property expression to parse. + + + + Initializes a new instance of the class. + + + + + Create a new instance from SerializationInfo + + + + + Evaluates this expression for the specified root object and returns + value of the last node. + + Context to evaluate expressions against. + Current expression evaluation context. + Value of the last node. + + + + Evaluates this expression for the specified root object and sets + value of the last node. + + Context to evaluate expressions against. + Current expression evaluation context. + Value to set last node to. + If navigation expression is empty. + + + + Evaluates this expression for the specified root object and returns + of the last node, if possible. + + Context to evaluate expression against. + Expression variables map. + Value of the last node. + + + + Contains a list of reserved variable names. + You must not use any variable names with the reserved prefix! + + + + + Variable Names using this prefix are reserved for internal framework use + + + + + variable name of the currently processed object factory, if any + + + + + Represents parsed attribute node in the navigation expression. + + Aleksandar Seovic + $Id: AttributeNode.cs,v 1.9 2007/09/07 03:01:21 markpollack Exp $ + + + + Represents parsed method node in the navigation expression. + + Aleksandar Seovic + $Id: ConstructorNode.cs,v 1.16 2007/09/07 03:01:21 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Creates new instance of the type defined by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Determines the type of object that should be instantiated. + + + The type name to resolve. + + + The type of object that should be instantiated. + + + If the type cannot be resolved. + + + + + Initializes this node by caching necessary constructor and property info. + + + + + + + Sets the named arguments (properties). + + Instance to set property values on. + Argument (property) name to value mappings. + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Tries to determine attribute type based on the specified + attribute type name. + + + Attribute type name to resolve. + + + Resolved attribute type. + + + If type cannot be resolved. + + + + + Converts string representation of expression into an instance of . + + Aleksandar Seovic + $Id: ExpressionConverter.cs,v 1.1 2007/07/31 02:13:52 markpollack Exp $ + + + + Can we convert from a the sourcetype to a ? + + +

    + Currently only supports conversion from a instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + if the conversion is possible. +
    + + + Convert from a value to an + instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A array if successful. + + + + + Utility methods that are used to convert objects from one type into another. + + Aleksandar Seovic + $Id: TypeConversionUtils.cs,v 1.4 2008/03/20 23:58:16 oakinger Exp $ + + + + Convert the value to the required (if necessary from a string). + + The proposed change value. + + The we must convert to. + + Property name, used for error reporting purposes... + + If there is an internal error. + + The new value, possibly the result of type conversion. + + + + Utility method to create a property change event. + + + The full name of the property that has changed. + + The property old value + The property new value + + A new . + + + + + Visitor class to represent + instances. + + +

    + Used in the first instance to supply stringified versions of + instances. +

    +

    + Other methods can be added here to return different representations, + including XML, CSV, etc.. +

    +
    + Griffin Caprio (.NET) + $Id: MessageSourceResolvableVisitor.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Outputs the supplied + as a nicely formatted . + + + The to output. + + + + + Convenient superclass for application objects that want to be aware of + the application context, e.g. for custom lookup of collaborating object + or for context-specific resource access. + + +

    + It saves the application context reference and provides an + initialization callback method. Furthermore, it offers numerous + convenience methods for message lookup. +

    +

    + There is no requirement to subclass this class: it just makes things + a little easier if you need access to the context, e.g. for access to + file resources or to the message source. Note that many application + objects do not need to be aware of the application context at all, + as they can receive collaborating objects via object references. +

    +
    + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + $Id: ApplicationObjectSupport.cs,v 1.8 2007/07/17 14:51:14 oakinger Exp $ +
    + + + Creates a new instance of the + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    +
    + + + Creates a new instance of the + class. + + +

    + This is an class, and as such exposes no + public constructors. +

    +
    + + The that this + object runs in. + +
    + + + Intializes the wrapped + . + + +

    + This is a template method that subclasses can override for custom + initialization behavior. +

    +

    + Gets called by the + + instance directly after setting the context instance. +

    + + Does not get called on reinitialization of the context. + +
    + + In the case of any initialization errors. + + + If thrown by application context methods. + +
    + + + The context class that any context passed to the + + must be an instance of. + + + The + . + + + + + Return a for the + application context used by this object, for easy message access. + + + + + Set the that this + object runs in. + + + When passed an unexpected + implementation + instance that is not compatible with the + defined by the value of the + . + property. Also, thrown when trying to re-initialize with a + different than was + originally used. + + + If thrown by any application context methods. + + + + + + + Convenient abstract superclass for + implementations that + draw their configuration from XML documents containing object + definitions as understood by an + . + + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + $Id: AbstractXmlApplicationContext.cs,v 1.20 2007/07/20 11:37:55 oakinger Exp $ + + + + Partial implementation of the + interface. + + +

    + Does not mandate the type of storage used for configuration, but does + implement common functionality. Uses the Template Method design + pattern, requiring concrete subclasses to implement + methods. +

    +

    + In contrast to a plain vanilla + , an + is supposed + to detect special objects defined in its object factory: therefore, + this class automatically registers + s, + s + and s that are + defined as objects in the context. +

    +

    + An may be also supplied as + an object in the context, with the special, well-known-name of + "messageSource". Else, message resolution is delegated to the + parent context. +

    +
    + Rod Johnson + Juergan Hoeller + Griffin Caprio (.NET) + $Id: AbstractApplicationContext.cs,v 1.74 2007/08/27 09:38:28 oakinger Exp $ + + +
    + + + Configurable implementation of the + interface. + + +

    + This implementation + supports the configuration of resource access protocols and the + corresponding .NET types that know how to handle those protocols. +

    +

    + Basic protocol-to-resource type mappings are also defined by this class, + while others can be added either internally, by application contexts + extending this class, or externally, by the end user configuring the + context. +

    +

    + Only one resource type can be defined for each protocol, but multiple + protocols can map to the same resource type (for example, the + "http" and "ftp" protocols both map to the + type. The protocols that are + mapped by default can be found in the following list. +

    +

    + + + assembly + + + config + + + file + + + http + + + https + + +

    +
    + Aleksandar Seovic + $Id: ConfigurableResourceLoader.cs,v 1.25 2007/08/08 17:46:55 bbaia Exp $ + + + +
    + + + The separator between the protocol name and the resource name. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class using the specified default protocol for unqualified resources. + + + + + Returns a that has been + mapped to the protocol of the supplied . + + The name of the resource. + + A new instance for the + supplied . + + + If a + mapping does not exist for the supplied . + + + In the case of any errors arising from the instantiation of the + returned instance. + + + + + + Checks that the supplied starts + with one of the protocol names currently mapped by this + instance. + + The name of the resource. + + if the supplied + starts with one of the known + protocols; if not, or if the supplied + is itself . + + + + + Extracts the protocol name from the supplied + . + + The name of the resource. + + The extracted protocol name or if the + supplied is unqualified (or + is itself ). + + + + + The default protocol to use for unqualified resources. + + +

    + The initial value is "file". +

    +
    +
    + + + Name of the .Net config section that contains Spring.Net context definition. + + + + + Default name of the root context. + + + + + The special, well-known-name of the default + in the context. + + +

    + If no can be found + in the context using this lookup key, then message resolution + will be delegated to the parent context (if any). +

    +
    +
    + + + The special, well-known-name of the default + in the context. + + +

    + If no can be found + in the context using this lookup key, then a default + will be used. +

    +
    +
    + + + The instance for this class. + + + + + The instance we delegate + our implementation of said interface to. + + + + + The instance we + delegate our implementation of said interface to. + + + + + Creates a new instance of the + with no parent context. + + +

    + This is an class, and as such exposes + no public constructors. +

    +
    +
    + + + Creates a new instance of the + with no parent context. + + +

    + This is an class, and as such exposes + no public constructors. +

    +
    + Flag specifying whether to make this context case sensitive or not. +
    + + + Creates a new instance of the + with the supplied parent context. + + +

    + This is an class, and as such exposes + no public constructors. +

    +
    + The application context name. + Flag specifying whether to make this context case sensitive or not. + The parent application context. +
    + + + Adds the given to the list of standard + processors being added to the underlying + + + Each time is called on this context, the context ensures, that + all default s are registered with the underlying . + + The instance. + + + + Closes this context and disposes of any resources (such as + singleton objects in the wrapped + ). + + + + + Subclasses must implement this method to perform the actual + configuration loading. + + +

    + This method is invoked by + , + before any other initialization occurs. +

    +
    + + In the case of errors encountered while refreshing the object factory. + +
    + + + Returns the internal object factory of the parent context if it implements + ; else, + returns the parent context itself. + + + The parent context's object factory, or the parent itself. + + + + + Raises an application context event. + + + Any arguments to the event. May be . + + + + + Raises an application context event. + + + The source of the event. + + + Any arguments to the event. May be . + + + + + Modify the application context's internal object factory after its standard + initialization. + + +

    + All object definitions will have been loaded, but no objects + will have been instantiated yet. This allows for the registration + of special + s + in certain + implementations. +

    +
    + + The object factory used by the application context. + + + In the case of errors. + . +
    + + + Template method which can be overridden to add context-specific + refresh work. + + +

    + Called on initialization of special objects, before instantiation + of singletons. +

    +
    +
    + + + Instantiate and invoke all registered + + objects, respecting any explicit ordering. + + + + Must be called before singleton instantiation. + + + In the case of errors. + + + + Register an IObjectPostProcessorChecker that logs an info + message when an object is created during IObjectPostProcessor + instantiation, i.e. when an object is not eligible for being + processed by all IObjectPostProcessors. + + + + + Initializes the default event registry for this context. + + + + + Returns the internal message source of the parent context if said + parent context is an , else + simply the parent context itself. + + + The internal message source of the parent context if said + parent context is an , else + simply the parent context itself. + + + + + Initializes the default message source for this context. + + +

    + Uses any parent context's message source if one is not available + in this context. +

    +
    +
    + + + Add a new + that will get applied to the internal object factory of this application context + on refresh, before any of the object definitions are evaluated. + + + The factory processor to register. + + + + + Load or refresh the persistent representation of the configuration, + which might an XML file, properties file, or relational database schema. + + + If the configuration cannot be loaded. + + + If the object factory could not be initialized. + + + + + Ensures, that predefined ObjectPostProcessors are registered with this ObjectFactory + + + + + + Return the names of objects matching the given + (including subclasses), judging from the object definitions. + + + The (class or interface) to match, or + for all object names. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + + + + + + Return the names of objects matching the given + (including subclasses), judging from the object definitions. + + + The (class or interface) to match, or + for all object names. + + + Whether to include prototype objects too or just singletons (also applies to + s). + + + Whether to include s too + or just normal objects. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + + + + + + Return the names of all objects defined in this factory. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + + + + + + Return the registered + for the + given object, allowing access to its property values and constructor + argument values. + + The name of the object. + + The registered + . + + + If there is no object with the given name. + + + In the case of errors. + + + + + Return the registered + for the + given object, allowing access to its property values and constructor + argument values. + + The name of the object. + Whether to search parent object factories. + + The registered + . + + + If there is no object with the given name. + + + In the case of errors. + + + + + Return the object instances that match the given object + (including subclasses), judging from either object + definitions or the value of + in the case of + s. + + + The (class or interface) to match. + + + A of the matching objects, + containing the object names as keys and the corresponding object instances + as values. + + + If the objects could not be created. + + + + + + Return the object instances that match the given object + (including subclasses), judging from either object + definitions or the value of + in the case of + s. + + + The (class or interface) to match. + + + Whether to include prototype objects too or just singletons (also applies to + s). + + + Whether to include s too + or just normal objects. + + + A of the matching objects, + containing the object names as keys and the corresponding object instances + as values. + + + If the objects could not be created. + + + + + + Check if this object factory contains an object definition with the given name. + + The name of the object to look for. + + True if this object factory contains an object definition with the given name. + + + + + + Does this object factory contain an object with the given name? + + The name of the object to query. + + if an object with the given name is defined. + + + + + + Return the aliases for the given object name, if defined. + + The object name to check for aliases. + The aliases, or an empty array if none. + + If there's no such object definition. + + + + + + Return an instance (possibly shared or independent) of the given object name. + + The name of the object to return. + + the object may match. Can be an interface or + superclass of the actual class. For example, if the value is the + class, this method will succeed whatever the + class of the returned instance. + + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + If the object is not of the required type. + + + + + + Return an instance (possibly shared or independent) of the given object name. + + The name of the object to return. + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + + + + Return an instance (possibly shared or independent) of the given object name. + + +

    + This method allows an object factory to be used as a replacement for the + Singleton or Prototype design pattern. +

    +

    + Note that callers should retain references to returned objects. There is no + guarantee that this method will be implemented to be efficient. For example, + it may be synchronized, or may need to run an RDBMS query. +

    +

    + Will ask the parent factory if the object cannot be found in this factory + instance. +

    +
    + The name of the object to return. + + The arguments to use if creating a prototype using explicit arguments to + a static factory method. If there is no factory method and the + arguments are not null, then match the argument values by type and + call the object's constructor. + + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + If the supplied is . + +
    + + + Return an instance (possibly shared or independent) of the given object name. + + The name of the object to return. + + The the object may match. Can be an interface or + superclass of the actual class. For example, if the value is the + class, this method will succeed whatever the + class of the returned instance. + + + The arguments to use if creating a prototype using explicit arguments to + a factory method. If there is no factory method and the + supplied array is not , then + match the argument values by type and call the object's constructor. + + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + If the object is not of the required type. + + + If the supplied is . + + + + + + Is this object a singleton? + + The name of the object to query. + True if the named object is a singleton. + + If there's no such object definition. + + + + + + Determines whether the specified object name is prototype. That is, will GetObject + always return independent instances? + + The name of the object to query + + true if the specified object name will always deliver independent instances; otherwise, false. + + This method returning false does not clearly indicate a singleton object. + It indicated non-independent instances, which may correspond to a scoped object as + well. use the IsSingleton property to explicitly check for a shared + singleton instance. + Translates aliases back to the corresponding canonical object name. Will ask the + parent factory if the object can not be found in this factory instance. + + + if there is no object with the given name. + + + + Determines whether the object with the given name matches the specified type. + + More specifically, check whether a GetObject call for the given name + would return an object that is assignable to the specified target type. + Translates aliases back to the corresponding canonical bean name. + Will ask the parent factory if the bean cannot be found in this factory instance. + + The name of the object to query. + Type of the target to match against. + + true if the object type matches; otherwise, false + if it doesn't match or cannot be determined yet. + + Ff there is no object with the given name + + + + + Determine the of the object with the + given name. + + The name of the object to query. + + The of the object, or + if not determinable. + + + + + + Injects dependencies into the supplied instance + using the named object definition. + + + The object instance that is to be so configured. + + + The name of the object definition expressing the dependencies that are to + be injected into the supplied instance. + + + + + + Injects dependencies into the supplied instance + using the supplied . + + + The object instance that is to be so configured. + + + The name of the object definition expressing the dependencies that are to + be injected into the supplied instance. + + + An object definition that should be used to configure object. + + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + + The that represents + the culture for which the resource is localized. + + + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If no message could be resolved. + + + If the supplied is . + + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + The default message. + + The that represents + the culture for which the resource is localized. + + + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If no message could be resolved. + + + If the supplied is . + + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + + The resolved message if the lookup was successful. + + + If no message could be resolved. + + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + + The resolved message if the lookup was successful. + + + If no message could be resolved. + + + If the supplied is . + + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + + The that represents + the culture for which the resource is localized. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If no message could be resolved. + + + If the supplied is . + + + + + + Resolve the message using all of the attributes contained within + the supplied + argument. + + + The value object storing those attributes that are required to + properly resolve a message. + + + The that represents + the culture for which the resource is localized. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If the message could not be resolved. + + + + + + Gets a localized resource object identified by the supplied + . + + + The name of the resource object to resolve. + + + The with which the + resource is associated. + + + The resolved object, or if not found. + + + + + + Gets a localized resource object identified by the supplied + . + + + The name of the resource object to resolve. + + + The resolved object, or if not found. + + + + + + Gets a localized resource object identified by the supplied + . + + + The name of the resource object to resolve. + + + The with which the + resource is associated. + + + The resolved object, or if not found. + + + + + + Gets a localized resource object identified by the supplied + . + + + The name of the resource object to resolve. + + + The resolved object, or if not found. + + + + + + Applies resources to object properties. + + + An object that contains the property values to be applied. + + + The base name of the object to use for key lookup. + + + The with which the + resource is associated. + + + + + + Publishes all events of the source object. + + + The source object containing events to publish. + + + + + + Subscribes to all events published, if the subscriber + implements compatible handler methods. + + The subscriber to use. + + + + + Subscribes to published events of a all objects of a given + , if the subscriber implements + compatible handler methods. + + The subscriber to use. + + The target to subscribe to. + + + + + + Publishes an application context event. + + +

    + +

    +
    + + The source of the event. May be . + + + The event that is to be raised. + + +
    + + + An object that can be used to synchronize access to the + + + + + The timestamp when this context was first loaded. + + + The timestamp (milliseconds) when this context was first loaded. + + + + + Gets a flag indicating whether context should be case sensitive. + + true if object lookups are case sensitive; otherwise, false. + + + + The for this context. + + + If the context has not been initialized yet. + + + + + The for this context. + + + If the context has not been initialized yet. + + + + + Returns the list of the + s + that will be applied to the objects created with this factory. + + +

    + The elements of this list are instances of implementations of the + + interface. +

    +
    + + The list of the + s + that will be applied to the objects created with this factory. + +
    + + + Return the internal object factory of this application context. + + + + + Gets the parent context, or if there is no + parent context. + + + The parent context, or if there is no + parent. + + + + + + Raised in response to an implementation-dependant application + context event. + + + + + The date and time this context was first loaded. + + + The representing when this context + was first loaded. + + + + + A name for this context. + + + A name for this context. + + + + + Return the number of objects defined in the factory. + + + The number of objects defined in the factory. + + + + + + Return an instance (possibly shared or independent) of the given object name. + + The name of the object to return. + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + + + + Return the parent object factory, or if there is none. + + + The parent object factory, or if there is none. + + + + + + The instance for this class. + + + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such exposes + no public constructors. +

    +
    +
    + + + Creates a new instance of the + class + with the given parent context. + + +

    + This is an class, and as such exposes + no public constructors. +

    +
    + The application context name. + Flag specifying whether to make this context case sensitive or not. + The parent context. +
    + + + Instantiates and populates the underlying + with the object + definitions yielded up by the + method. + + + In the case of errors encountered while refreshing the object factory. + + + In the case of errors encountered reading any of the resources + yielded by the method. + + + + + + Initialize the object definition reader used for loading the object + definitions of this context. + + +

    + The default implementation of this method is a no-op; i.e. it does + nothing. Can be overridden in subclasses to provide custom + initialization of the supplied + ; for example, a derived + class may want to turn off XML validation. +

    +
    + + The object definition reader used by this context. + +
    + + + Load the object definitions with the given + . + + +

    + The lifecycle of the object factory is handled by + ; + therefore this method is just supposed to load and / or register + object definitions. +

    +
    + + The reader containing object definitions. + + In case of object registration errors. + + + In the case of errors encountered reading any of the resources + yielded by the method. + +
    + + + Loads the object definitions into the given object factory, typically through + delegating to one or more object definition readers. + + The object factory to lead object definitions into + + + + + + Customizes the internal object factory used by this context. + + Called for each attempt. +

    + The default implementation is empty. Can be overriden in subclassses to customize + DefaultListableBeanFatory's standard settings. +

    + The newly created object factory for this context +
    + + + Create an internal object factory for this context. + + +

    + Called for each attempt. + This default implementation creates a + + with the internal object factory of this context's parent serving + as the parent object factory. Can be overridden in subclasse,s + for example to customize DefaultListableBeanFactory's settings. +

    +
    + The object factory for this context. +
    + + + An array of resource locations, referring to the XML object + definition files that this context is to be built with. + + +

    + Examples of the format of the various strings that would be + returned by accessing this property can be found in the overview + documentation of with the + class. +

    +
    + + An array of resource locations, or if none. + +
    + + + Subclasses must return their internal object factory here. + + + The internal object factory for the application context. + + + + + + Simple listener that logs application events to the console. + + +

    + Intended for use during debugging only. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: ConsoleListener.cs,v 1.6 2006/04/09 07:18:38 markpollack Exp $ + +
    + + + A listener for application events. + + Rod Johnson + Griffin Caprio (.NET) + $Id: IApplicationEventListener.cs,v 1.2 2006/04/09 07:18:38 markpollack Exp $ + + + + Handle an application event. + + + The source of the event. + + + The event that is to be handled. + + + + + Creates a new instance of the + class. + + + + + Handle an application event. + + + The source of the event. + + + The event that is to be handled. + + + + + Marks an interface as being an application event listener. + + Griffin Caprio + $Id: EventListenerAttribute.cs,v 1.5 2006/04/09 07:18:38 markpollack Exp $ + + + + + Creates a new instance of the + class. + + + + + Thrown when an element is requested from an empty . + + Griffin Caprio + $Id: NoElementsException.cs,v 1.4 2006/04/09 07:18:37 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Creates a new instance of the + class with the + specified message. + + + A message about the exception. + + + + + Creates a new instance of the + class with the + specified message. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + An abstract implementation that can + be used as base class for concrete implementations. + + Aleksandar Seovic + Erich Eichinger + $Id: AbstractCache.cs,v 1.7 2007/10/11 01:29:49 markpollack Exp $ + + + + Defines a contract that all cache implementations have to fulfill. + + Aleksandar Seovic + Erich Eichinger + $Id: ICache.cs,v 1.5 2007/08/27 09:38:11 oakinger Exp $ + + + + Retrieves an item from the cache. + + + Item key. + + + Item for the specified , or null. + + + + + Removes an item from the cache. + + + Item key. + + + + + Removes collection of items from the cache. + + + Collection of keys to remove. + + + + + Removes all items from the cache. + + + + + Inserts an item into the cache. + + + Items inserted using this method have no expiration time + and default cache priority. + + + Item key. + + + Item value. + + + + + Inserts an item into the cache. + + + Items inserted using this method have default cache priority. + + + Item key. + + + Item value. + + + Item's time-to-live. + + + + + Gets the number of items in the cache. + + + + + Gets a collection of all cache item keys. + + + + + Retrieves an item from the cache. + + + Item key. + + + Item for the specified , or null. + + + + + Removes an item from the cache. + + + Item key. + + + + + Removes collection of items from the cache. + + + Collection of keys to remove. + + + + + Removes all items from the cache. + + + + + Inserts an item into the cache. + + + Items inserted using this method use the default + + + Item key. + + + Item value. + + + + + Inserts an item into the cache. + + + If equals , + or is true, this cache + instance's value will be applied. + + + Item key. + + + Item value. + + + Item's time-to-live (TTL). + + + + + Actually does the cache implementation specific insert operation into the cache. + + + Items inserted using this method have default cache priority. + + + Item key. + + + Item value. + + + Item's time-to-live (TTL). + + + + + Gets/Set the Default time-to-live (TTL) for items inserted into this cache. + Used by + + + + + Gets/Sets a value, whether the this cache instance's + shall be applied to all items, regardless of their individual TTL + when is called. + + + + + Gets the number of items in the cache. + + + May be overridden by subclasses for cache-specific efficient implementation. + + + + + Gets a collection of all cache item keys. + + + + + Validates that the object is valid ISBN-10 or ISBN-13 value. + + Goran Milosavljevic + + + + Creates a new instance of the ISBNValidator class. + + + + + Creates a new instance of the ISBNValidator class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Creates a new instance of the ISBNValidator class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Validates the supplied . + + + In the case of the class, + the test should be a string variable that will be evaluated and the object + obtained as a result of this evaluation will be tested using the ISBN-10 or + ISBN-13 validation rules. + + The object to validate. + + if the supplied is valid ISBN. + + + + + Validates against ISBN-10 or ISBN-13 validation + rules. + + + ISBN string to validate. + + + true if is a valid ISBN-10 or ISBN-13 code. + + + + + ISBN-10 consists of 4 groups of numbers separated by either + dashes (-) or spaces. + + + The first group is 1-5 characters, second 1-7, third 1-6, + and fourth is 1 digit or an X. + + + + + ISBN-13 consists of 5 groups of numbers separated by either + dashes (-) or spaces. + + + The first group is 978 or 979, the second group is + 1-5 characters, third 1-7, fourth 1-6, and fifth is 1 digit. + + + + + Represents a single validation error message. + + Aleksandar Seovic + Goran Milosavljevic + $Id: ErrorMessage.cs,v 1.5 2007/02/19 17:56:42 aseovic Exp $ + + + + Default constructor. + + + + + Initializes a new instance of the class. + + Error message resource identifier. + Parameters that should be used for message resolution. + + + + This property is reserved, apply the + + to the class instead. + + + An + that describes the XML representation of the object that + is produced by the + + method and consumed by the + + method. + + + + + + Generates an object from its XML representation. + + + The stream + from which the object is deserialized. + + + + + Converts an object into its XML representation. + + + The stream + to which the object is serialized. + + + + + Resolves the message against specified . + + Message source to resolve this error message against. + Resolved error message. + + + + Gets or sets the resource identifier for this message. + + The resource identifier for this message. + + + + Gets or sets the message parameters. + + The message parameters. + + + + Utility class containing miscellaneous system-level functionality. + + Aleksandar Seovic + $Id: SystemUtils.cs,v 1.5 2007/09/07 02:46:49 markpollack Exp $ + + + + Registers assembly resolver that iterates over the + assemblies loaded into the current + in order to find an assembly that cannot be resolved. + + + This method has to be called if you need to serialize dynamically + generated types in transient assemblies, such as Spring AOP proxies, + because standard .NET serialization engine always tries to load + assembly from the disk. + + + + + Returns true if running on Mono + + Tests for the presence of the type Mono.Runtime + + + + Gets the thread id for the current thread. Use thread name is available, + otherwise use CurrentThread.GetHashCode() for .NET 1.0/1.1 and + CurrentThread.ManagedThreadId otherwise. + + The thread id. + + + + Defines methods that dynamic method class has to implement. + + + + + Invokes dynamic method on the specified target object. + + + Target object to invoke method on. + + + Method arguments. + + + A method return value. + + + + + Safe wrapper for the dynamic method. + + + will attempt to use dynamic + method if possible, but it will fall back to standard + reflection if necessary. + + + + + Creates a new instance of the safe method wrapper. + + Method to wrap. + + + + Invokes dynamic method. + + + Target object to invoke method on. + + + Method arguments. + + + A method return value. + + + + + Gets the class, that declares this method + + + + + Factory class for dynamic methods. + + Aleksandar Seovic + $Id: DynamicMethod.cs,v 1.2 2008/03/06 20:20:37 oakinger Exp $ + + + + Creates dynamic method instance for the specified . + + Method info to create dynamic method for. + Dynamic method for the specified . + + + + Builds a proxy type using inheritance. + + + + In order for this builder to work, target methods have to be either + , or belong to an interface. + + + Aleksandar Seovic + Bruno Baia + $Id: InheritanceProxyTypeBuilder.cs,v 1.14 2007/12/04 18:38:02 bbaia Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a proxy that inherits the proxied object's class. + + +

    + Only (non-final) methods can be proxied, + unless they are members of one of the interfaces that target class + implements. In that case, methods will be proxied using explicit + interface implementation, which means that client code will have + to cast the proxy to a specific interface in order to invoke the + methods. +

    +
    + The generated proxy class. +
    + + + Generates the IL instructions that pushes + the target instance on which calls should be delegated to. + + The IL generator to use. + + + + Generates the proxy constructor. + + +

    + This implementation delegates the call to a base class constructor. +

    +
    + The constructor builder to use. + The IL generator to use. + + The base class constructor to delegate the call to. + +
    + + + Gets or sets a value indicating whether inherited members should be proxied. + + + if they should be; otherwise, . + + + + + A simple pool implementation + + +

    + Based on the implementation found in Concurrent Programming in Java, + 2nd ed., by Doug Lea. +

    +
    + Doug Lea + Federico Spinazzi + Mark Pollack + $Id: SimplePool.cs,v 1.4 2006/09/15 19:06:07 markpollack Exp $ +
    + + + Set of permits + + + + + Creates a new instance of the + class. + + + The factory used to instantiate and manage the lifecycle of pooled objects. + + The initial size of the pool. + + If the supplied is . + + + If the supplied is less than or equal to zero. + + + + + Obtain an instance from the pool. + + + In case the pool is unusable. + + + + + + + Return an instance to the pool. + + The instance to be returned to the pool. + + + + + + Create an object using the factory set by + the property + or other implementation dependent mechanism + and place it into the pool. + + +

    + This implementation always throws a + . +

    +
    + + If the implementation does not support the operation. + +
    + + + Synchronized borrow logic. + + + + + + Synchronized release logic. + + + The object to release to the pool. + + + if the object was not a busy one. + + + + + Instantiates the supplied number of instances and adds + them to the pool. + + + The initial number of objects to build. + + + If the supplied number of is + less than or equal to zero. + + + + + Close the pool and free any resources associated with it. + + + + + Clear objects sitting idle in the pool, releasing any + associated resources. + + +

    + This implementation always throws a + . +

    +
    + + If the implementation does not support the operation. + +
    + + + Change the state of the pool to unusable. + + + + + Gets the number of instances currently borrowed from the pool. + + + If the implementation does not support the operation. + + + + + + Gets the number of instances currently idle in the pool. + + + If the implementation does not support the operation. + + + + + + Set the factory used to create new instances. + + +

    + This implementation always throws a + . +

    +
    + + If the implementation does not support the operation. + +
    + + + Base class for all pooling exceptions. + + Federico Spinazzi + $Id: PoolException.cs,v 1.4 2005/08/07 04:59:47 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Describes an event handler for an object instance. + + Rick Evans + $Id: InstanceEventHandlerValue.cs,v 1.7 2006/04/09 07:19:00 markpollack Exp $ + + + + Base class for all + implemenations that actually perform event wiring. + + Rick Evans + $Id: AbstractWiringEventHandlerValue.cs,v 1.8 2006/04/09 07:19:00 markpollack Exp $ + + + + Creates a new instance of the + class. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    +
    + + + Creates a new instance of the + class. + + + The object (possibly unresolved) that is exposing the event. + + + The name of the method on the handler that is going to handle the event. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    +
    + + + Wires up the specified handler to the named event on the + supplied event source. + + + The object (an object instance, a , etc) + exposing the named event. + + + The handler for the event (an object instance, a + , etc). + + + + + Gets the event handler. + + + The instance that is registering for the event notification. + + + Event metadata about the event. + + + The event handler. + + + + + Resolves the method metadata that describes the method that is to be used + as the argument to a delegate constructor. + + + The exposing the method. + + + The of the delegate (e.g. System.EventHandler). + + + The custom binding flags to use when searching for the method. + + The method metadata. + + If the method could not be found. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The object (possibly unresolved) that is exposing the event. + + + The name of the method on the handler that is going to handle the event. + + + + + Gets the event handler. + + + The instance that is registering for the event notification. + + + Event metadata about the event. + + + The event handler. + + + + + implementation that + evaluates a property path on a given target object. + + +

    + The target object can be specified directly or via an object name (see + example below). +

    +

    + Please note that the + is an implementation, and as such has + to comply with the contract of the + interface; more specifically, this means that the end result of the property lookup path + evaluation cannot be ( + implementations are not permitted to return ). If the resut of a + property lookup path evaluates to , an exception will be thrown. +

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + Juergen Hoeller + Rick Evans (.NET) + $Id: PropertyPathFactoryObject.cs,v 1.3 2007/03/16 04:01:39 aseovic Exp $ + + + + Interface to be implemented by objects that wish to be aware of their object + name in an . + + +

    + Note that most objects will choose to receive references to collaborating + objects via respective properties. +

    +

    + For a list of all object lifecycle methods, see the + API documentation. +

    +
    + Juergen Hoeller + Rick Evans (.NET) +
    + + + Set the name of the object in the object factory that created this object. + + + The name of the object in the factory. + + +

    + Invoked after population of normal object properties but before an init + callback like 's + + method or a custom init-method. +

    +
    +
    + + + Return an instance (possibly shared or independent) of the object + managed by this factory. + + + An instance (possibly shared or independent) of the object managed by + this factory. + + + + + + The target object that the property path lookup is to be applied to. + + +

    + This would most likely be an inner object, but can of course be + any object reference. +

    +
    + + The target object that the property path lookup is to be applied to. + + +
    + + + The (object) name of the target object that the property path lookup + is to be applied to. + + +

    + Please note that any leading or trailing whitespace will be + trimmed from this name prior to resolution. The implication of this is that + one cannot use the + class in conjunction with object names that start or end with whitespace. +

    +
    + + The (object) name of the target object that the property path lookup + is to be applied to. + + +
    + + + The property (lookup) path to be applied to the target object. + + +

    + Please note that any leading or trailing whitespace will be + trimmed from this path prior to resolution. Whitespace is not a valid + identifier for property names (in part or whole) in CLS-based languages, + so this is a not unreasonable action. Please also note that whitespace + that is embedded within the property path will be left as-is (which may + or may not result in an error being thrown, depending on the context of + the whitespace). +

    +
    + +

    + Examples of such property lookup paths can be seen below; note that + property lookup paths can be nested to an arbitrary level. +

    + + name.length + accountManager.account['the key'].name + accounts[0].name + +
    + + The property (lookup) path to be applied to the target object. + +
    + + + The 'expected' of the result from evaluating the + property path. + + +

    + This is not necessary for directly specified target objects, or + singleton target objects, where the can + be determined via reflection. Just specify this in case of a + prototype target, provided that you need matching by type (for + example, for autowiring). +

    +

    + It is permissable to set the value of this property to + (which in any case is the default value). +

    +
    + + The 'expected' of the result from evaluating the + property path. + +
    + + + Return the of object that this + creates, or + if not known in advance. + + + + + + Is the object managed by this factory a singleton or a prototype? + + + + + + Set the name of the object in the object factory that created this object. + + +

    + The object name of this + + will be interpreted as "objectName.property" pattern, if neither the + + + have been supplied (set). +

    +

    + This allows for concise object definitions with just an id or name. +

    +
    + + The name of the object in the factory. + +
    + + + Callback that supplies the owning factory to an object instance. + + + Owning + (may not be ). The object can immediately + call methods on the factory. + + + In case of initialization errors. + + + + + To be implemented by any object that wishes to receive a reference to + an . + + +

    + This interface only applies to objects that have been instantiated + within the context of an + . This interface does + not typically need to be implemented by application code, but is rather + used by classes internal to Spring.NET. +

    +
    + Mark Pollack + Rick Evans + $Id: IEventRegistryAware.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ +
    + + + Set the + associated with the + that created this + object. + + +

    + This property will be set by the relevant + after all of this + object's dependencies have been resolved. This object can use the + supplied + immediately to publish or subscribe to one or more events. +

    +
    +
    + + + Base Type for those implementations that + need to parse and define just a single IObjectDefinition. + + + Extend this parser Type when you want to create a single object definition + from an arbitrarily complex XML element. You may wish to consider extending + the when you want to create a + single Object definition from a relatively simple custom XML element. + The resulting ObjectDefinition will be automatically registered + with the ObjectDefinitionRegistry. Your job simply is to parse the + custom XML element into a single ObjectDefinition + + Rob Harrop + Juergen Hoeller + Rick Evans + Mark Pollack (.NET) + $Id: AbstractSingleObjectDefinitionParser.cs,v 1.3 2007/05/29 14:42:20 markpollack Exp $ + + + + Abstract implementation providing + a number of convenience methods and a + template method + that subclasses must override to provide the actual parsing logic. + + + Use this implementation when you want + to parse some arbitrarily complex XML into one or more + ObjectDefinitions. If you just want to parse some + XML into a single IObjectDefinition, you may wish to consider + the simpler convenience extensions of this class, namely + and + + + Rob Harrop + Juergen Hoeller + Rick Evans + Mark Pollack (.NET) + $Id: AbstractObjectDefinitionParser.cs,v 1.4 2007/05/26 19:25:54 markpollack Exp $ + + + + Interface used to handle custom, top-level tags. + + Implementations are free to turn the metadata in the custom tag into as + many as required. + + Rob Harrop + Mark Pollack (.NET) + $Id: IObjectDefinitionParser.cs,v 1.3 2007/05/25 03:23:21 markpollack Exp $ + + + + Parse the specified XmlElement and register the resulting + ObjectDefinitions with the IObjectDefinitionRegistry + embedded in the supplied + + +

    + This method is never invoked if the parser is namespace aware + and was called to process the root node. +

    +
    + + The element to be parsed. + + + TThe object encapsulating the current state of the parsing process. + Provides access to a IObjectDefinitionRegistry + + + The primary object definition. + +
    + + + Constant for the ID attribute + + + + + Parse the specified XmlElement and register the resulting + ObjectDefinitions with the IObjectDefinitionRegistry + embedded in the supplied + + The element to be parsed. + TThe object encapsulating the current state of the parsing process. + Provides access to a IObjectDefinitionRegistry + The primary object definition. + +

    + This method is never invoked if the parser is namespace aware + and was called to process the root node. +

    +
    +
    + + + Resolves the ID for the supplied . + + + When using generation, a name is generated automatically. + Otherwise, the ID is extracted from the "id" attribute, potentially with a + fallback to a generated id. + + The element that the object definition has been built from. + The object definition to be registered. + The the object encapsulating the current state of the parsing process; + provides access to a + the resolved id + + if no unique name could be generated for the given object definition + + + + + Registers the supplied with the supplied + . + + Subclasses can override this method to control whether or not the supplied + is actually even registered, or to + register even more objects. + + The default implementation registers the supplied + with the supplied only if the IsNested + parameter is false, because one typically does not want inner objects + to be registered as top level objects. + + + + The object definition to be registered. + The registry that the bean is to be registered with. + + + + Central template method to actually parse the supplied XmlElement + into one or more IObjectDefinitions. + + The element that is to be parsed into one or more s + The the object encapsulating the current state of the parsing process; + provides access to a + The primary IObjectDefinition resulting from the parsing of the supplied XmlElement + + + + Gets a value indicating whether an ID should be generated instead of read + from the passed in XmlElement. + + Note that this flag is about always generating an ID; the parser + won't even check for an "id" attribute in this case. + + true if should generate id; otherwise, false. + + + + Gets a value indicating whether an ID should be generated instead if the + passed in XmlElement does not specify an "id" attribute explicitly. + + Disabled by default; subclasses can override this to enable ID generation + as fallback: The parser will first check for an "id" attribute in this case, + only falling back to a generated ID if no value was specified. + + true if should generate id if no value was specified; otherwise, false. + + + + + Central template method to actually parse the supplied XmlElement + into one or more IObjectDefinitions. + + The element that is to be parsed into one or more s + The the object encapsulating the current state of the parsing process; + provides access to a + + The primary IObjectDefinition resulting from the parsing of the supplied XmlElement + + + + + Gets the type of the object corresponding to the supplied XmlElement. + + Note that, for application classes, it is generally preferable to override + GetObjectTypeName instad, in order to avoid a direct + dependence on the object implementation class. The ObjectDefinitionParser + and its IXmlObjectDefinitionParser (namespace parser) can be used within an + IDE add-in then, even if the application classses are not available in the add-ins + AppDomain. + + The element. + The Type of the class that is being defined via parsing the supplied + Element. + + + + Gets the name of the object type name (FullName) corresponding to the supplied XmlElement. + + The element. + The type name of the object that is being defined via parsing the supplied + XmlElement. + + + + Parse the supplied XmlElement and populate the supplied ObjectDefinitionBuilder as required. + + The default implementation delegates to the DoParse version without + ParameterContext argument. + The element. + The parser context. + The builder used to define the IObjectDefinition. + + + + Parse the supplied XmlElement and populate the supplied ObjectDefinitionBuilder as required. + + The default implementation does nothing. + The element. + The builder used to define the IObjectDefinition. + + + + Default implementation of the interface, deleagting to + . + + Note that this implementation is only able to handle + subclasses such as + and + + Juergen Hoeller + Mark Pollack (.NET) + $Id: DefaultObjectNameGenerator.cs,v 1.1 2007/05/23 20:16:13 markpollack Exp $ + + + + Strategy interface for generating object names for object definitions + + Juergen Hoeller + Mark Pollack (.NET) + $Id: IObjectNameGenerator.cs,v 1.1 2007/05/23 20:16:13 markpollack Exp $ + + + + Generates an object name for the given object definition. + + The object definition to generate a name for. + The object definitions registry that the given definition is + supposed to be registerd with + the generated object name + + + + Generates an object name for the given object definition. + + The object definition to generate a name for. + The object definitions registry that the given definition is + supposed to be registerd with + the generated object name + + + + Marks a property as being 'required': that is, the setter property + must be configured to be dependency-injected with a value. + + Consult the SDK documentation for , + which, by default, checks for the presence of this annotation. + + Rob Harrop + Mark Pollack + $Id: RequiredAttribute.cs,v 1.1 2008/04/02 18:02:24 markpollack Exp $ + + + + Abstract base class that all resource cache implementations should extend. + + Aleksandar Seovic + $Id: AbstractResourceCache.cs,v 1.3 2006/04/09 07:18:47 markpollack Exp $ + + + + Gets the list of resources from the cache. + + Target to get a list of resources for. + Resource culture. + A list of cached resources for the specified target object and culture. + + + + Puts the list of resources in the cache. + + Target to cache a list of resources for. + Resource culture. + A list of resources to cache. + A list of cached resources for the specified target object and culture. + + + + Crates resource cache key for the specified target object and culture. + + Target object to apply resources to. + Resource culture to use for resource lookup. + + + + Gets the list of resources from cache. + + Cache key to use for lookup. + A list of cached resources for the specified target object and culture. + + + + Puts the list of resources in the cache. + + Cache key to use for the specified resources. + A list of resources to cache. + A list of cached resources for the specified target object and culture. + + + + Represents arithmetic exponent operator. + + Aleksandar Seovic + $Id: OpPOWER.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the arithmetic exponent operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents logical "greater than or equal" operator. + + Aleksandar Seovic + $Id: OpGreaterOrEqual.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical "greater than or equal" operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed function node. + + Aleksandar Seovic + $Id: FunctionNode.cs,v 1.7 2008/03/20 23:58:16 oakinger Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Evaluates function represented by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Result of the function evaluation. + + + + Resolves a by name. + + Rick Evans + Aleksandar Seovic + Bruno Baia + $Id: TypeResolver.cs,v 1.3 2007/08/27 15:18:24 oakinger Exp $ + + + + Resolves the supplied to a + instance. + + + The unresolved (possibly partially assembly qualified) name + of a . + + + A resolved instance. + + + If the supplied could not be resolved + to a . + + + + + Uses + to load an and then the attendant + referred to by the + parameter. + + +

    + is + deprecated in .NET 2.0, but is still used here (even when this class is + compiled for .NET 2.0); + will + still resolve (non-.NET Framework) local assemblies when given only the + display name of an assembly (the behaviour for .NET Framework assemblies + and strongly named assemblies is documented in the docs for the + method). +

    +
    + + The assembly and type to be loaded. + + + A , or . + + + + +
    + + + Uses + to load the attendant referred to by + the parameter. + + + The type to be loaded. + + + A , or . + + + + + Creates a new instance + from the given + + + + + Creates a new instance + from the given with the given inner + + + + + Converter for from a comma separated + list of RBG values. + + +

    + Please note that this class does not implement converting + to a comma separated list of RBG values from a + . +

    +
    + Federico Spinazzi + $Id: RGBColorConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ +
    + + + Returns whether this converter can convert an object of one + to a + . + + +

    + Currently only supports conversion from a + instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + if the conversion is possible. +
    + + + Converts the specified object (a string) a + instance. + + + A + that provides a format context. + + + The to use + as the current culture: currently ignored. + + + The value that is to be converted, in "R,G,B", "A,R,G,B", or + symbolic color name (). + + + A representation of the string value. + + + If the input string is not in a supported format, or is not one of the + predefined system colors (). + + + + + Criteria that is satisfied if the Name property of an + instance matches a + supplied regular expression pattern. + + Rick Evans + $Id: RegularExpressionMethodNameCriteria.cs,v 1.1 2007/07/31 01:35:07 markpollack Exp $ + + + + The default method name pattern... matches pretty much any method name. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The pattern that names + must match against in order to satisfy this criteria. + + + + + Does the supplied satisfy the criteria encapsulated by + this instance? + + The datum to be checked by this criteria instance. + + True if the supplied satisfies the criteria encapsulated + by this instance; false if not or the supplied is null. + + + + + Criteria that is satisfied if the return of a given + matches a given . + + Rick Evans + $Id: MethodReturnTypeCriteria.cs,v 1.1 2007/07/31 02:03:36 markpollack Exp $ + + + + The return to match against if no + is provided explictly. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The that the return type of a given + must match in order to satisfy + this criteria. + + + + + Does the supplied satisfy the criteria encapsulated by + this instance? + + The datum to be checked by this criteria instance. + + True if the supplied satisfies the criteria encapsulated + by this instance; false if not or the supplied is null. + + + + + The that the return type of a given + must match in order to satisfy + this criteria. + + + + + A backed resource + on top of + + +

    + Obviously supports resolution as a , and also + as a in the case of the "file:" + protocol. +

    +
    + +

    + Some examples of the strings that can be used to initialize a new + instance of the class + include... + + + file:///Config/objects.xml + + + http://www.mycompany.com/services.txt + + +

    +
    + Juergen Hoeller + Leonardo Susatyo (.NET) + Aleksandar Seovic (.NET) + $Id: UrlResource.cs,v 1.15 2007/08/08 17:46:55 bbaia Exp $ + + + +
    + + + Creates a new instance of the + class. + + +

    + Some examples of the values that the + can typically be expected to hold include... + + + file:///Config/objects.xml + + + http://www.mycompany.com/services.txt + + +

    +
    + + A string representation of the resource. + +
    + + + Does the supplied relative ? + + + The name of the resource to test. + + + if resource name is relative; + otherwise . + + + + + Returns the instance + used for the resource resolution. + + + A instance. + + + + + + + Return an for this resource. + + + An . + + + If the stream could not be opened. + + + + + + Returns the handle for this resource. + + + The handle for this resource. + + + If the resource is not available or cannot be exposed as a + . + + + + + + Returns a handle for this resource. + + + The handle for this resource. + + + If the resource is not available on a filesystem. + + + + + + Does this support relative + resource retrieval? + + +

    + This implementation does support relative resource retrieval, and + so will always return . +

    +
    + + if this + supports relative resource + retrieval. + + +
    + + + Gets the root location of the resource. + + + The root location of the resource. + + + + + + Gets the current path of the resource. + + + The current path of the resource. + + + + + + Gets those characters that are valid path separators for the + resource type. + + + Those characters that are valid path separators for the resource + type. + + + + + + Returns a description for this resource. + + + A description for this resource. + + + + + + Simple implementation of + that allows messages to be held in an object and added programmatically. + + +

    + Mainly useful for testing. +

    +

    + This supports internationalization. +

    +
    + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + + $Id: StaticMessageSource.cs,v 1.16 2007/08/27 13:57:27 oakinger Exp $ +
    + + + Creates a new instance of the + class. + + + + + Returns a format string. + + The code of the message to resolve. + + The to resolve the + code for. + + + A format string or if not found. + + + + + + Resolves an object (typically an icon or bitmap). + + The code of the object to resolve. + + The to resolve the + code for. + + + The resolved object or if not found. + + + + + + Applies resources to object properties. + + +

    + Uses a System.ComponentModel.ComponentResourceManager + internally to apply resources to object properties. Resource key + names are of the form objectName.propertyName. +

    +

    + This feature is not currently supported on version 1.0 of the .NET platform. +

    +
    + + An object that contains the property values to be applied. + + + The base name of the object to use for key lookup. + + + The with which the + resource is associated. + + + This feature is not currently supported on version 1.0 of the .NET platform. + + +
    + + + Associate the supplied with the + supplied . + + The lookup code. + + The to resolve the + code for. + + + The message format associated with this lookup code. + + + + + Associate the supplied with the + supplied . + + The lookup code. + + The to resolve the + code for. + + + The object associated with this lookup code. + + + + + Returns a representation of this + message source. + + + A containing all of this message + source's messages. + + + + + Configuration section handler for the Spring.NET typeConverters + config section. + + +

    + Type converters are used to convert objects from one type into another + when injecting property values, evaluating expressions, performing data + binding, etc. +

    +

    + They are a very powerful mechanism as they allow Spring.NET to automatically + convert string-based property values from the configuration file into the appropriate + type based on the target property's type or to convert string values submitted + via a web form into a type that is used by your data model when Spring.NET data + binding is used. Because they offer such tremendous help, you should always provide + a type converter implementation for your custom types that you want to be able to use + for injected properties or for data binding. +

    +

    + The standard .NET mechanism for specifying type converter for a particular type is + to decorate the type with a , passing the type + of the -derived class as a parameter. +

    +

    + This mechanism will still work and is a preferred way of defining type converters if + you control the source code for the type that you want to define a converter for. However, + this configuration section allows you to specify converters for the types that you don't + control and it also allows you to override some of the standard type converters, such as + the ones that are defined for some of the types in the .NET Base Class Library. +

    +
    + +

    + The following example shows how to configure both this section handler and + how to define type converters within a Spring.NET config section: +

    + + + + +
    + + + + + + + ... + + ... + + + + + Aleksandar Seovic + $Id: TypeConvertersSectionHandler.cs,v 1.6 2007/07/31 18:15:50 bbaia Exp $ + + + + + Populates using values specified in + the typeConverters config section. + + + The configuration settings in a corresponding parent + configuration section. + + + The configuration context when called from the ASP.NET + configuration system. Otherwise, this parameter is reserved and + is . + + + The for the section. + + + This method always returns , because the + is populated as a side-effect of + its execution and thus there is no need to return anything. + + + + + The callback for application events. + + + + + Encapsulates the data associated with an event raised by an + . + + Rod Johnson + Mark Pollack (.NET) + Griffin Caprio (.NET) + + $Id: ApplicationEventArgs.cs,v 1.5 2006/04/09 07:18:38 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + The date and time when the event occured. + + + The date and time when the event occured. + + + + + The system time in milliseconds when the event happened. + + + The system time in milliseconds when the event happened. + + + + + Perform credit card validations. + + + By default, all supported card types are allowed. You can specify + which credit card type validator should be used by setting + the value of property to a concrete + instance. + + + + + Creates a new instance of the UrlValidator class. + + + + + Creates a new instance of the UrlValidator class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + Credit Card type validator to use. + + + + Creates a new instance of the UrlValidator class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + Credit Card type validator to use. + + + + Validates the supplied . + + + In the case of the class, + the test should be a string variable that will be evaluated and the object + obtained as a result of this evaluation will be checked if it is + a valid credit card number. + + The object to validate. + + if the supplied is valid + credit card number. + + + + + Checks if the is a valid credit card number. + + + The card number to validate. + + + true if the card number is valid. + + + + + Validates card number with the specified validator. + + + Credit card number to validate. + + + true if credit card number is a valid number of credit card type specified. + + + + + Checks for a valid credit card number. + + + Credit Card Number. + + + true if the card number passes the LuhnCheck. + + + + + Credit card type validator to use. + + + Can be concrete implementations of + interface. The following are available implementations: + , , , + . + + + + + CreditCardType interface defines how validation is performed + for one type/brand of credit card. + + + + + Returns true if the card number matches this type of + credit card. + + + The card number, never null. + + + true if the number matches. + + + + + Visa credit card type validation support. + + + + + Indicates, wheter the given credit card number matches a visa number. + + + + + American Express credit card type validation support. + + + + + Indicates, wheter the given credit card number matches an amex number. + + + + + Discover credit card type validation support. + + + + + Indicates, wheter the given credit card number matches a discover number. + + + + + Mastercard credit card type validation support. + + + + + Indicates, wheter the given credit card number matches a mastercard number. + + + + + Thrown by the validation advice if the method parameters validation fails. + + Aleksandar Seovic + $Id: ValidationException.cs,v 1.1 2008/02/05 20:40:26 aseovic Exp $ + + + + Creates a new instance of the ValidationException class. + + + + + Creates a new instance of the ValidationException class with + specified validation errors. + + + Validation errors. + + + + + Creates a new instance of the ValidationException class with the + specified message. + + + A message about the exception. + + + + + Creates a new instance of the ValidationException class with the + specified message and validation errors. + + + A message about the exception. + + + Validation errors. + + + + + Creates a new instance of the ValidationException class with the + specified message and root cause. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ValidationException class with the + specified message, root cause and validation errors. + + + A message about the exception. + + + The root exception that is being wrapped. + + + Validation errors. + + + + + Creates a new instance of the ValidationException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Implements object serialization. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Gets validation errors. + + Validation errors. + + + + A utility class for raising events in a generic and consistent fashion. + + Rick Evans + $Id: EventUtils.cs,v 1.5 2006/04/09 07:19:00 markpollack Exp $ + + + + Raises the event encapsulated by the supplied + , passing the supplied + to the event. + + The event to be raised. + The arguments to the event. + + + + Invokes the supplied , passing the supplied + to the sink. + + The sink to be invoked. + The arguments to the sink. + + + + Raises events defensively. + + +

    + Raising events defensively means that as the raised event is passed to each handler, + any thrown by a handler will be caught and silently + ignored. +

    +
    + Rick Evans + $Id: EventUtils.cs,v 1.5 2006/04/09 07:19:00 markpollack Exp $ +
    + + + Defensively invokes the supplied , passing the + supplied to the sink. + + The sink to be invoked. + The arguments to the sink. + + + A latch is a boolean condition that is set at most once, ever. + Once a single release is issued, all acquires will pass. +

    + Sample usage. Here are a set of classes that use + a latch as a start signal for a group of worker threads that + are created and started beforehand, and then later enabled. +

    + + class Worker implements IRunnable { + private readonly Latch startSignal; + Worker(Latch l) + { + startSignal = l; + } + + public void Run() { + startSignal.acquire(); + DoWork(); + } + + void DoWork() { ... } + } + + class Driver { // ... + void Main() { + Latch go = new Latch(); + for (int i = 0; i < N; ++i) // make threads + new Thread(new ThreadStart(new Worker(go)).Start(); + DoSomethingElse(); // don't let run yet + go.Release(); // let all threads proceed + } + } + +
    + Doug Lea + Federico Spinazzi (.Net) + $Id: Latch.cs,v 1.11 2006/09/15 19:06:08 markpollack Exp $ +
    + + + can acquire ? + + + + + Method mainly used by clients who are trying to get the latch + + + + Wait at most msecs millisconds for a permit + + + + Enable all current and future acquires to pass + + + + + Implements by using . + + Erich Eichinger + $Id: CallContextStorage.cs,v 1.1 2007/02/02 21:30:34 oakinger Exp $ + + + + Retrieves an object with the specified name. + + The name of the item. + The object in the call context associated with the specified name or null if no object has been stored previously + + + + Stores a given object and associates it with the specified name. + + The name with which to associate the new item. + The object to store in the call context. + + + + Empties a data slot with the specified name. + + The name of the data slot to empty. + + + + Defines methods that dynamic indexer class has to implement. + + + + + Gets the value of the dynamic indexer for the specified target object. + + + Target object to get the indexer value from. + + + Indexer argument. + + + A indexer value. + + + + + Gets the value of the dynamic indexer for the specified target object. + + + Target object to get the indexer value from. + + + Indexer argument. + + + A indexer value. + + + + + Gets the value of the dynamic indexer for the specified target object. + + + Target object to get the indexer value from. + + + Indexer arguments. + + + A indexer value. + + + + + Gets the value of the dynamic indexer for the specified target object. + + + Target object to set the indexer value on. + + + Indexer argument. + + + A new indexer value. + + + + + Gets the value of the dynamic indexer for the specified target object. + + + Target object to set the indexer value on. + + + Indexer argument. + + + A new indexer value. + + + + + Gets the value of the dynamic indexer for the specified target object. + + + Target object to set the indexer value on. + + + Indexer arguments. + + + A new indexer value. + + + + + Safe wrapper for the dynamic indexer. + + + will attempt to use dynamic + indexer if possible, but it will fall back to standard + reflection if necessary. + + + + + Creates a new instance of the safe indexer wrapper. + + Indexer to wrap. + + + + Gets the value of the dynamic indexer for the specified target object. + + + Target object to get indexer value from. + + + Indexer arguments. + + + A indexer value. + + + + + Gets the value of the dynamic indexer for the specified target object. + + + Target object to get the indexer value from. + + + Indexer argument. + + + A indexer value. + + + + + Gets the value of the dynamic indexer for the specified target object. + + + Target object to get indexer value from. + + + Indexer arguments. + + + A indexer value. + + + + + Sets the value of the dynamic indexer for the specified target object. + + + Target object to set indexer value on. + + + Indexer arguments. + + + A new indexer value. + + + + + Sets the value of the dynamic indexer for the specified target object. + + + Target object to set indexer value on. + + + Indexer arguments. + + + A new indexer value. + + + + + Sets the value of the dynamic indexer for the specified target object. + + + Target object to set indexer value on. + + + Indexer arguments. + + + A new indexer value. + + + + + Internal PropertyInfo accessor. + + + + + Factory class for dynamic indexers. + + Aleksandar Seovic + $Id: DynamicIndexer.cs,v 1.1 2007/08/08 04:05:37 bbaia Exp $ + + + + Creates dynamic indexer instance for the specified . + + Indexer info to create dynamic indexer for. + Dynamic indexer for the specified . + + + + Defines lifecycle methods for objects that are to be used in an + implementation. + + +

    + The following methods summarize the contract between an + and an + an . +

    + + + + is called whenever a new instance is needed. + + + + is invoked on every instance before it is returned from + the pool. + + + + is invoked on every instance when it is returned to the pool. + + + + is invoked on every instance when it is being dropped from the + pool (see + + + +

    + Based on the Jakarta Commons Pool API. +

    +
    + Federico Spinazzi + $Id: IPoolableObjectFactory.cs,v 1.5 2005/11/18 17:29:32 gcaprio Exp $ + +
    + + + Creates an instance that can be returned by the pool. + + + An instance that can be returned by the pool. + + + + + Destroys an instance no longer needed by the pool. + + +

    + Invoked on every instance when it is being "dropped" + from the pool (whether due to the return value from a call to the + + method, or for reasons specific to the pool implementation.) +

    +
    + The instance to be destroyed. +
    + + + Ensures that the instance is safe to be returned by the pool. + Returns false if this object should be destroyed. + + +

    + Invoked in an implementation-specific fashion to determine if an + instance is still valid to be returned by the pool. + It will only be invoked on an "activated" instance. +

    +
    + The instance to validate. + + if this object is not valid and + should be dropped from the pool, otherwise . + +
    + + + Reinitialize an instance to be returned by the pool. + + +

    + Invoked on every instance before it is returned from the pool. +

    +
    + The instance to be activated. +
    + + + Uninitialize an instance to be returned to the pool. + + +

    + Invoked on every instance when it is returned to the pool. +

    +
    + The instance returned to the pool. +
    + + + Mutable implementation of the + interface that + supports toggling the ascending value on setting the same property again. + + Juergen Hoeller + Jean-Pierre Pawlak + Simon White (.NET) + + + + Definition for sorting object instances by a property. + + Juergen Hoeller + Simon White (.NET) + + + + The name of the property to sort by. + + + + + Whether upper and lower case in string values should be ignored. + + + True if the sorting should be performed in a case-insensitive fashion. + + + + + If the sorting should be ascending or descending. + + + True if the sorting should be in the ascending order. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class using + the specified . + + + The to use + as a source for initial property values. + + + + + Creates a new instance of the + class. + + + The name of the property to sort by. + + + Whether upper and lower case in string values should be ignored. + + + Whether or not the sorting should be ascending or descending. + + + + + Creates a new instance of the + class. + + + Whether or not the + + property should be toggled if the same name is set on the + + property. + + + + + Overrides the default method + + + The object to test against this instance for equality. + + + True if the supplied is equal to this instance. + + + + + Overrides the default method. + + The hashcode for this instance. + + + + The name of the property to sort by. + + + + + Whether upper and lower case in string values should be ignored. + + + True if the sorting should be performed in a case-insensitive fashion. + + + + + If the sorting should be ascending or descending. + + + True if the sorting should be in the ascending order. + + + + + Thrown when an object doesn't match the required . + + Rod Johnson + Rick Evans (.NET) + + + + Creates a new instance of the ObjectNotOfRequiredTypeException class. + + + + + Creates a new instance of the ObjectNotOfRequiredTypeException class. + + + A message about the exception. + + + + + Creates a new instance of the ObjectNotOfRequiredTypeException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ObjectNotOfRequiredTypeException class. + + + Name of the object requested. + + + The required of the actual object + instance that was retrieved. + + + The instance actually returned, whose class did not match the + expected . + + + + + Creates a new instance of the ObjectNotOfRequiredTypeException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + The actual of the actual object + instance that was retrieved. + + + + + The required of the actual object + instance that was retrieved. + + + + + The instance actually returned, whose class did not match the + expected . + + + + + The name of the object requested. + + + + + implementation that + retrieves a or non-static public property value. + + +

    + Typically used for retrieving public property values. +

    +
    + Rick Evans (.NET) + $Id: PropertyRetrievingFactoryObject.cs,v 1.12 2007/07/31 18:16:49 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + + + + Invoked by an + after it has set all object properties supplied + (and satisfied + and ApplicationContextAware). + + + In the event of misconfiguration (such as failure to set an essential + property) or if initialization fails. + + + + + Template method that subclasses must override to construct the object + returned by this factory. + + + If an exception occured during object creation. + + The object returned by this factory. + + + + The of the static property + to be retrieved. + + + + + Arguments for the property invocation. + + +

    + If this property is not set, or the value passed to the setter invocation + is a null or zero-length array, a property with no arguments is assumed. +

    +
    +
    + + + The name of the property the value of which is to be retrieved. + + +

    + Refers to either a property or a non-static property, + depending on a target object being set. +

    +
    +
    + + + The object instance on which the property is defined. + + + + + The on which the property is defined. + + + + + Return the type of object that this + creates, or + if not known in advance. + + + + + XML-specific ObjectDefinitionStoreException subclass that wraps a XmlException, which + contains information about the error location. + + + + + Thrown when an + encounters an internal error, and its definitions are invalid. + + +

    + An example of a situation when this exception would be thrown is + in the case of an XML document containing object definitions being + malformed. +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) +
    + + + Creates a new instance of the ObjectDefinitionStoreException class. + + + + + Creates a new instance of the ObjectDefinitionStoreException class. + + + A message about the exception. + + + + + Creates a new instance of the ObjectDefinitionStoreException class. + + + The description of the resource that the object definition came from + + + The name of the object that triggered the exception. + + + A message about the exception. + + + + + Initializes a new instance of the class. + + + The description of the resource that the object definition came from + + The detail message (used as exception message as-is) + The root cause. (may be null + + + + Creates a new instance of the ObjectDefinitionStoreException class. + + + The resource location (e.g. an XML object definition file) associated + with the offending object definition. + + + A message about the exception. + + + The name of the object that triggered the exception. + + + + + Creates a new instance of the ObjectDefinitionStoreException class. + + + The resource location (e.g. an XML object definition file) associated + with the offending object definition. + + + A message about the exception. + + + The name of the object that triggered the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ObjectDefinitionStoreException class. + + + The description of the resource that the object definition came from + + + A message about the exception. + + + The name of the object that triggered the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ObjectDefinitionStoreException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ObjectDefinitionStoreException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + The description of the resource associated with the object + + + + + The name of the object that trigger the exception. + + + + + The name of the object that triggered the exception (if any). + + + + + The description of the resource associated with the object (if any). + + + + + Initializes a new instance of the class. + + + + + Creates a new instance of the XmlObjectDefinitionStoreException class. + + + A message about the exception. + + + + + Creates a new instance of the XmlObjectDefinitionStoreException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the XmlObjectDefinitionStoreException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Initializes a new instance of the class. + + The description of the resource that the object definition came from + The detail message (used as exception message as-is). + The XmlException root cause. + + + + Gets the line number in the XML resource that failed. + + The line number if available (in case of a XmlException); -1 else. + + + + Gets the line position in the XML resource that failed. + + The line position if available (in case of a XmlException); -1 else. + + + + Context that gets passed along an object definition parsing process, encapsulating + all relevant configuraiton as well as state. + + + + + Initializes a new instance of the class. + + The reader context. + The parser helper. + + + + Initializes a new instance of the class. + + The reader context. + The parser helper. + The containing object definition. + + + + Gets the reader context. + + The reader context. + + + + Gets the registry. + + The registry. + + + + Gets the parser helper. + + The parser helper. + + + + Gets the containing object definition. + + The containing object definition. + + + + Gets a value indicating whether this instance is nested. + + true if this instance is nested; otherwise, false. + + + + Gets a value indicating whether this instance is default lazy init. + + + true if this instance is default lazy init; otherwise, false. + + + + + + implementation that allows for convenient registration of custom + type aliases. + + + Type aliases can be used instead of fully qualified type names anywhere + a type name is expected in a Spring.NET configuration file. +

    + Because the + class implements the + + interface, instances of this class that have been exposed in the + scope of an + will + automatically be picked up by the application context and made + available to the IoC container whenever resolution of type aliases is required. +

    +
    + Mark Pollack + $Id: TypeAliasConfigurer.cs,v 1.6 2007/07/31 18:16:49 bbaia Exp $ + + +
    + + + Registers any type aliases. The supplied + is not used since type aliases + are registered with a global + + + The object factory. + + + In case of errors. + + + + + The type aliases to register. + + +

    + The has the + contains the alias name as the key and type as the value. + The key name can either be a string or an object, in which case + ToString() will be used to obtain the string name. + the value can be the fully qualified name of the type as a string or + an actual of the class that + being aliased. +

    +
    +
    + + + Implementation of that can be used to + format and parse numbers. + + + + PercentFormatter uses percent-related properties of the + to format and parse percentages. + + + If you use one of the constructors that accept culture as a parameter + to create an instance of PercentFormatter, default NumberFormatInfo + for the specified culture will be used. + + + You can also use properties exposed by the PercentFormatter in order + to override some of the default number formatting parameters. + + + Aleksandar Seovic + $Id: PercentFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + + + + Initializes a new instance of the class + using default for the current thread's culture. + + + + + Initializes a new instance of the class + using default for the specified culture. + + The culture name. + + + + Initializes a new instance of the class + using default for the specified culture. + + The culture. + + + + Initializes a new instance of the class + using specified . + + + The instance that defines how + numbers are formatted and parsed. + + + + + Formats the specified percentage value. + + The value to format. + Formatted percentage. + If is null. + If is not a number. + + + + Parses the specified percentage value. + + The percentage value to parse. + Parsed percentage value as a . + + + + Gets or sets the number of decimal digits. + + The number of decimal digits. + + + + + Gets or sets the decimal separator. + + The decimal separator. + + + + + Gets or sets the percent group sizes. + + The percent group sizes. + + + + + Gets or sets the percent group separator. + + The percent group separator. + + + + + Gets or sets the negative pattern. + + The percent negative pattern. + + + + + Gets or sets the positive pattern. + + The percent positive pattern. + + + + + Gets or sets the percent symbol. + + The percent symbol. + + + + + Gets or sets the per mille symbol. + + The per mille symbol. + + + + + Utility class that enables easy expression evaluation. + + +

    + This class allows users to get or set properties, execute methods, and evaluate + logical and arithmetic expressions. +

    +

    + Methods in this class parse expression on every invocation. + If you plan to reuse the same expression many times, you should prepare + the expression once using the static method, + and then call to evaluate it. +

    +

    + This can result in significant performance improvements as it avoids expression + parsing and node resolution every time it is called. +

    +

    +

    +
    + Aleksandar Seovic + $Id: ExpressionEvaluator.cs,v 1.2 2006/04/09 07:18:39 markpollack Exp $ +
    + + + Parses and evaluates specified expression. + + Root object. + Expression to evaluate. + Value of the last node in the expression. + + + + Parses and evaluates specified expression. + + Root object. + Expression to evaluate. + Expression variables map. + Value of the last node in the expression. + + + + Parses and specified expression and sets the value of the + last node to the value of the newValue parameter. + + Root object. + Expression to evaluate. + Value to set last node to. + + + + Parses and specified expression and sets the value of the + last node to the value of the newValue parameter. + + Root object. + Expression to evaluate. + Expression variables map. + Value to set last node to. + + + + Represents local function node. + + Aleksandar Seovic + $Id: LocalFunctionNode.cs,v 1.7 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Evaluates function represented by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Result of the function evaluation. + + + + Interface that should be implemented by data bound objects, such as + web pages, user controls, windows forms, etc. + + Aleksandar Seovic + $Id: IDataBound.cs,v 1.3 2006/11/21 05:46:57 aseovic Exp $ + + + + Gets the binding manager. + + The binding manager. + + + + Provides access to a central registry of aliased s. + + +

    + Simplifies configuration by allowing aliases to be used instead of + fully qualified type names. +

    +

    + Comes 'pre-loaded' with a number of convenience alias' for the more + common types; an example would be the 'int' (or 'Integer' + for Visual Basic.NET developers) alias for the + type. +

    +
    + Aleksandar Seovic + + $Id: TypeRegistry.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ +
    + + + Name of the .Net config section that contains Spring.Net type aliases. + + + + + The alias around the 'int' type. + + + + + The alias around the 'Integer' type (Visual Basic.NET style). + + + + + The alias around the 'int[]' array type. + + + + + The alias around the 'Integer()' array type (Visual Basic.NET style). + + + + + The alias around the 'decimal' type. + + + + + The alias around the 'Decimal' type (Visual Basic.NET style). + + + + + The alias around the 'decimal[]' array type. + + + + + The alias around the 'Decimal()' array type (Visual Basic.NET style). + + + + + The alias around the 'char' type. + + + + + The alias around the 'Char' type (Visual Basic.NET style). + + + + + The alias around the 'char[]' array type. + + + + + The alias around the 'Char()' array type (Visual Basic.NET style). + + + + + The alias around the 'long' type. + + + + + The alias around the 'Long' type (Visual Basic.NET style). + + + + + The alias around the 'long[]' array type. + + + + + The alias around the 'Long()' array type (Visual Basic.NET style). + + + + + The alias around the 'short' type. + + + + + The alias around the 'Short' type (Visual Basic.NET style). + + + + + The alias around the 'short[]' array type. + + + + + The alias around the 'Short()' array type (Visual Basic.NET style). + + + + + The alias around the 'unsigned int' type. + + + + + The alias around the 'unsigned long' type. + + + + + The alias around the 'ulong[]' array type. + + + + + The alias around the 'uint[]' array type. + + + + + The alias around the 'unsigned short' type. + + + + + The alias around the 'ushort[]' array type. + + + + + The alias around the 'double' type. + + + + + The alias around the 'Double' type (Visual Basic.NET style). + + + + + The alias around the 'double[]' array type. + + + + + The alias around the 'Double()' array type (Visual Basic.NET style). + + + + + The alias around the 'float' type. + + + + + The alias around the 'Single' type (Visual Basic.NET style). + + + + + The alias around the 'float[]' array type. + + + + + The alias around the 'Single()' array type (Visual Basic.NET style). + + + + + The alias around the 'DateTime' type. + + + + + The alias around the 'DateTime' type (C# style). + + + + + The alias around the 'DateTime' type (Visual Basic.NET style). + + + + + The alias around the 'DateTime[]' array type. + + + + + The alias around the 'DateTime[]' array type. + + + + + The alias around the 'DateTime()' array type (Visual Basic.NET style). + + + + + The alias around the 'bool' type. + + + + + The alias around the 'Boolean' type (Visual Basic.NET style). + + + + + The alias around the 'bool[]' array type. + + + + + The alias around the 'Boolean()' array type (Visual Basic.NET style). + + + + + The alias around the 'string' type. + + + + + The alias around the 'string' type (Visual Basic.NET style). + + + + + The alias around the 'string[]' array type. + + + + + The alias around the 'string[]' array type (Visual Basic.NET style). + + + + + The alias around the 'object' type. + + + + + The alias around the 'object' type (Visual Basic.NET style). + + + + + The alias around the 'object[]' array type. + + + + + The alias around the 'object[]' array type (Visual Basic.NET style). + + + + + The alias around the 'int?' type. + + + + + The alias around the 'int?[]' array type. + + + + + The alias around the 'decimal?' type. + + + + + The alias around the 'decimal?[]' array type. + + + + + The alias around the 'char?' type. + + + + + The alias around the 'char?[]' array type. + + + + + The alias around the 'long?' type. + + + + + The alias around the 'long?[]' array type. + + + + + The alias around the 'short?' type. + + + + + The alias around the 'short?[]' array type. + + + + + The alias around the 'unsigned int?' type. + + + + + The alias around the 'unsigned long?' type. + + + + + The alias around the 'ulong?[]' array type. + + + + + The alias around the 'uint?[]' array type. + + + + + The alias around the 'unsigned short?' type. + + + + + The alias around the 'ushort?[]' array type. + + + + + The alias around the 'double?' type. + + + + + The alias around the 'double?[]' array type. + + + + + The alias around the 'float?' type. + + + + + The alias around the 'float?[]' array type. + + + + + The alias around the 'bool?' type. + + + + + The alias around the 'bool?[]' array type. + + + + + Registers standard and user-configured type aliases. + + + + + Registers an alias for the specified . + + +

    + This overload does eager resolution of the + referred to by the parameter. It will throw a + if the referred + to by the parameter cannot be resolved. +

    +
    + + A string that will be used as an alias for the specified + . + + + The (possibly partially assembly qualified) name of the + to register the alias for. + + + If either of the supplied parameters is or + contains only whitespace character(s). + + + If the referred to by the supplied + cannot be loaded. + +
    + + + Registers short type name as an alias for + the supplied . + + + The to register. + + + If the supplied is . + + + + + Registers an alias for the supplied . + + + The alias for the supplied . + + + The to register the supplied under. + + + If the supplied is ; or if + the supplied is or + contains only whitespace character(s). + + + + + Resolves the supplied to a . + + + The alias to resolve. + + + The the supplied was + associated with, or if no + was previously registered for the supplied . + + + If the supplied is or + contains only whitespace character(s). + + + + + Returns a flag specifying whether TypeRegistry contains + specified alias or not. + + + Alias to check. + + + true if the specified type alias is registered, + false otherwise. + + + + + Resolves a generic by name. + + Bruno Baia + $Id: GenericTypeResolver.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + + + + Resolves the supplied generic to a + instance. + + + The unresolved (possibly generic) name of a . + + + A resolved instance. + + + If the supplied could not be resolved + to a . + + + + + Thrown in response to a failed attempt to read a property. + + +

    + Typically thrown when attempting to read the value of a write-only + property via reflection. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: NotReadablePropertyException.cs,v 1.2 2007/07/31 00:26:30 markpollack Exp $ +
    + + + Thrown in response to referring to an invalid property (most often via reflection). + + Rick Evans + $Id: InvalidPropertyException.cs,v 1.2 2007/07/31 03:47:22 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + The that is (or rather was) the source of the + offending property. + + + The name of the offending property. + + + + + Creates a new instance of the + class. + + + The that is (or rather was) the source of the + offending property. + + + The name of the offending property. + + + A message about the exception. + + + + + Creates a new instance of the InvalidPropertyException class. + + + The that is (or rather was) the source of the + offending property. + + + The name of the offending property. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + The that is (or rather was) the source of the + offending property. + + + + + The name of the offending property. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + The that is (or rather was) the source of the + offending property. + + + The name of the offending property. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Convenience class that exposes a signature that matches the + delegate. + + +

    + Useful when filtering members via the + mechanism. +

    +
    + Rick Evans + $Id: CriteriaMemberFilter.cs,v 1.1 2007/07/31 21:43:52 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Returns true if the supplied instance + satisfies the supplied (which must be an + implementation). + + + The instance that will be checked to see if + it matches the supplied . + + + The criteria against which to filter the supplied + instance. + + + True if the supplied instance + satisfies the supplied (which must be an + implementation); false if not or the + supplied is not an + implementation or is null. + + + + + Custom type converter for instances. + + +

    + A resource path may contain placeholder variables of the form ${...} + that will be expended to environment variables. +

    +

    + Currently only supports conversion from a + instance. +

    +
    + +

    + On Win9x boxes, this resource path, ${userprofile}\objects.xml will + be expanded at runtime with the value of the 'userprofile' environment + variable substituted for the '${userprofile}' portion of the path. +

    + + // assuming a user called Rick, running on a plain vanilla Windows XP setup... + // this resource path... + + ${userprofile}\objects.xml + + // will become (after expansion)... + + C:\Documents and Settings\Rick\objects.xml + +
    + Mark Pollack + $Id: ResourceConverter.cs,v 1.14 2007/08/08 17:46:55 bbaia Exp $ + + +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class using the specified resourceLoader. + + the underlying IResourceLoader to be used to resolve resources + + + + Returns whether this converter can convert an object of one + to a + + + A + that provides a format context. + + + A that represents the + you want to convert from. + + + if the conversion is possible. + + + + + Convert from a string value to a + instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + An if successful. + + + If the resource name objectained form the supplied + is malformed. + + + In the case of any errors arising from the instantiation of the + returned instance. + + + + + Resolve the given path, replacing placeholder values with + corresponding property values if necessary. + + +

    + This implementation resolves environment variables only. +

    +
    + The original resource path. + The resolved resource path. +
    + + + Return the used to + resolve the string. + + + The used to resolve + the string. + + + + + A implementation that represents + a composed collection of instances. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A user-defined (child) criteria that will be composed into this instance. + + + + + Does the supplied satisfy the criteria encapsulated by + this instance? + + The data to be checked by this criteria instance. + + True if the supplied satisfies the criteria encapsulated + by this instance; false if not or the supplied is null. + + + + + Adds the supplied into the criteria + composed within this instance. + + + The to be added. + + + + + The list of composing this + instance. + + + + Exception thrown during application context initialization. + Rod Johnson + Mark Pollack (.NET) + $Id: ApplicationContextException.cs,v 1.5 2006/04/09 07:18:38 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Creates a new instance of the + class with the + specified message. + + + A message about the exception. + + + + + Creates a new instance of the + class with the + specified message. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Implements a based on a list. + + +

    + Performance is much better for very small lists than either + or . + However, performance degrades rapidly as the data-set gets bigger. Use a + instead if you are not sure your data-set + will always remain very small. Iteration produces elements in the order they were added. + However, element order is not guaranteed to be maintained by the various + mathematical operators. +

    +
    +
    + + + Creates a new set instance based on a list. + + + + + Creates a new set instance based on a list and initializes it based on a + collection of elements. + + + A collection of elements that defines the initial set contents. + + + + + Validates that the value is valid URL. + + Goran Milosavljevic + + + + Creates a new instance of the UrlValidator class. + + + + + Creates a new instance of the UrlValidator class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Creates a new instance of the UrlValidator class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + + + + Validates the supplied . + + + In the case of the class, + the test should be a string variable that will be evaluated and the object + obtained as a result of this evaluation will be tested using the URL validation rules. + + The object to validate. + + if the supplied is valid. + + + + + Regular expression used for validation of object passed to this . + + + + + Various reflection related methods that are missing from the standard library. + + Rod Johnson + Juergen Hoeller + Aleksandar Seovic (.NET) + Stan Dvoychenko (.NET) + Bruno Baia (.NET) + $Id: ReflectionUtils.cs,v 1.59 2008/05/13 14:22:46 oakinger Exp $ + + + + Convenience value that will + match all private and public, static and instance members on a class + in a case inSenSItivE fashion. + + + + + Returns signature for the specified , method name and argument + s. + + The the method is in. + The method name. + + The argument s. + + The method signature. + + + + Returns method for the specified , method + name and argument + s. + + Searches with BindingFlags + + The target to find the method on. + + The method to find. + + The argument s. May be + if the method has no arguments. + + The target method. + + + + Returns an array of parameter s for the specified method + or constructor. + + The method (or constructor). + An array containing the parameter s. + + If is . + + + + + Returns an array of parameter s for the + specified parameter info array. + + The parameter info array. + An array containing parameter s. + + If is or any of the + elements is . + + + + + Returns an array of s that represent + the names of the generic type parameter. + + The method. + An array containing the parameter names. + + If is . + + + + + Returns an array of s that represent + the names of the generic type parameter. + + The parameter info array. + An array containing parameter names. + + If is or any of the + elements is . + + + + + From a given list of methods, selects the method having an exact match on the given ' types. + + the list of methods to choose from + the arguments to the method + the method matching exactly the passed ' types + + If more than 1 matching methods are found in the list. + + + + + From a given list of methods, selects the method having an exact match on the given ' types. + + the type of method (used for exception reporting only) + the list of methods to choose from + the arguments to the method + the method matching exactly the passed ' types + + If more than 1 matching methods are found in the list. + + + + + From a given list of constructors, selects the constructor having an exact match on the given ' types. + + the list of constructors to choose from + the arguments to the method + the constructor matching exactly the passed ' types + + If more than 1 matching methods are found in the list. + + + + + Packages arguments into argument list containing parameter array as a last argument. + + Argument vaklues to package. + Total number of oarameters. + Type of the param array element. + Packaged arguments. + + + + Convenience method to convert an interface + to a array that contains + all the interfaces inherited and the specified interface. + + The interface to convert. + An array of interface s. + + If the specified is not an interface. + + + If is . + + + + + Is the supplied the default indexer for the + supplied ? + + + The name of the property on the supplied to be checked. + + + The to be checked. + + + if the supplied is the + default indexer for the supplied . + + + If the supplied is . + + + + + Is the supplied declared on one of these interfaces? + + The method to check. + The array of interfaces we want to check. + + if the method is declared on one of these interfaces. + + + If any of the s specified is not an interface. + + + If or any of the specified interfaces is + . + + + + + Returns the default value for the specified + + +

    + Follows the standard .NET conventions for default values where + relevant; for example, all numeric types default to the value + 0. +

    +
    + + The to return default value for. + + + The default value for the specified . + + + If the supplied is an enumerated type that + has no values. + +
    + + + Returns an array consisting of the default values for the supplied + . + + + The array of s to return default values for. + + + An array consisting of the default values for the supplied + . + + + If any of the elements in the supplied + array is an enumerated type that has no values. + + + + + + Checks that the parameter s of the + supplied match the parameter + s of the supplied + . + + The method to be checked. + + The array of parameter s to check against. + + + if the parameter s + match. + + + + + Returns an array containing the s of the + objects in the supplied array. + + + The objects array for which the corresponding s + are needed. + + + An array containing the s of the objects + in the supplied array; this array will be empty (but not + if the supplied + is null or has no elements. + + +

    + [C#]
    + Given an array containing the following objects, + [83, "Foo", new object ()], the + array returned from this method call would consist of the following + elements... + [Int32, String, Object]. +

    +
    +
    + + + Does the given and/or it's superclasses + have at least one or more methods with the given name (with any + argument types)? + + +

    + Includes non-public methods in the methods searched. +

    +
    + + The to be checked. + + + The name of the method to be searched for. Case inSenSItivE. + + + if the given or / and it's + superclasses have at least one or more methods (with any argument types); + if not, or either of the parameters is . + +
    + + + Within , counts the number of overloads for the method with the given (case-insensitive!) + + The type to be searched + the name of the method for which overloads shall be counted + The number of overloads for method within type + + + + Creates a . + + +

    + Note that if a non- + is supplied, any read write properties exposed by the + will be used to overwrite values that may have been passed in via the + . That is, the will be used + to initialize the custom attribute, and then any read-write properties on the + will be plugged in. +

    +
    + + The desired . + + + Any constructor arguments for the attribute (may be + in the case of no arguments). + + + Source attribute to copy properties from (may be ). + + A custom attribute builder. + + If the parameter is . + + + If the parameter is not a + that derives from the class. + + +
    + + + Creates a . + + + The desired . + + + Source attribute to copy properties from (may be ). + + A custom attribute builder. + + + + Creates a . + + + The source attribute to copy properties from. + + A custom attribute builder. + + If the supplied is + . + + + + + Creates a . + + + The desired . + + A custom attribute builder. + + + + Creates a . + + + The desired . + + + Any constructor arguments for the attribute (may be + in the case of no arguments). + + A custom attribute builder. + + + + Creates a . + + + The to create + the custom attribute builder from. + + A custom attribute builder. + + + + Tries to find matching methods in the specified + for each method in the supplied list. + + + The to look for matching methods in. + + The methods to match. + + A flag that specifies whether to throw an exception if a matching + method is not found. + + A list of the matched methods. + + If either of the or + parameters are . + + + + + Returns the of the supplied + . + + +

    + If the is a + instance, the return value of this method call with be the + parameter cast to a + . If the is + anything other than a , the return value + will be the result of invoking the 's + method. +

    +
    + + A or instance. + + + The argument if it is a + or the result of invoking + on the argument if it + is an . + + + If the is . + +
    + + + Unwraps the supplied + and returns the inner exception preserving the stack trace. + + + The to unwrap. + + The unwrapped exception. + + + + Is the supplied can be accessed outside the assembly ? + + The type to check. + + if the type can be accessed outside the assembly; + Otherwise . + + + + + Is the supplied can be accessed + from the supplied friendly assembly ? + + The type to check. + The friendly assembly name. + + if the type can be accessed + from the supplied friendly assembly; Otherwise . + + + + + Gets all of the interfaces implemented by + the specified . + + + The object to get the interfaces of. + + + All of the interfaces implemented by the + . + + + + + Returns the explicit that is the root cause of an exception. + + + If the InnerException property of the current exception is a null reference + or a , returns the current exception. + + The last exception thrown. + + The first explicit exception thrown in a chain of exceptions. + + + + + Copies all fields from one object to another. + + + The types of both objects must be related. This means, that either of the following is true: + + fromObject.GetType() == toObject.GetType() + fromObject.GetType() is derived from toObject.GetType() + toObject.GetType() is derived from fromObject.GetType() + + + The source object + The object, who's fields will be populated with values from the source object + If the object's types are not related + + + + Creates a . + + Bruno Baia + $Id: ReflectionUtils.cs,v 1.59 2008/05/13 14:22:46 oakinger Exp $ + + + + Creates a new instance of the + class. + + The custom attribute type. + + + + Creates a new instance of the + class. + + The custom attribute type. + The custom attribute constructor arguments. + + + + Adds the specified values to the constructor argument list + used to create the custom attribute. + + An array of argument values. + + + + Adds a property value to the custom attribute. + + The property name. + The property value. + + + + Creates the . + + The created . + + + + Strategy interface for parsing XML object definitions. + + +

    + Used by + for actually parsing a DOM document or + fragment. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + Sandu Turcan (.NET) + $Id: INamespaceParser.cs,v 1.1 2007/08/07 21:23:37 markpollack Exp $ +
    + + + Invoked by after construction but before any + elements have been parsed. + + + + + Parse the specified element and register any resulting + IObjectDefinitions with the IObjectDefinitionRegistry that is + embedded in the supplied ParserContext. + + + Implementations should return the primary IObjectDefinition + that results from the parse phase if they wish to used nested + inside (for example) a <property> tag. + Implementations may return null if they will not + be used in a nested scenario. + + + The element to be parsed into one or more IObjectDefinitions + The object encapsulating the current state of the parsing + process. + + The primary IObjectDefinition (can be null as explained above) + + + + + Parse the specified XmlNode and decorate the supplied ObjectDefinitionHolder, + returning the decorated definition. + + The XmlNode may either be an XmlAttribute or an XmlElement, depending on + whether a custom attribute or element is being parsed. + Implementations may choose to return a completely new definition, + which will replace the original definition in the resulting IApplicationContext/IObjectFactory. + + The supplied ParserContext can be used to register any additional objects needed to support + the main definition. + + The source element or attribute that is to be parsed. + The current object definition. + The object encapsulating the current state of the parsing + process. + The decorated definition (to be registered in the IApplicationContext/IObjectFactory), + or simply the original object definition if no decoration is required. A null value is strickly + speaking invalid, but will leniently treated like the case where the original object definition + gets returned. + + + + Object definition reader for a simple properties format. + + + Provides object definition registration methods for + and + instances. Typically applied to a + . + + Rod Johnson + Juergen Hoeller + Simon White (.NET) + + + + Value of a T/F attribute that represents true. + Anything else represents false. Case seNsItive. + + + + + Separator between object name and property name. + + + + + Prefix for the class property of a root object definition. + + + + + Special string added to distinguish if the object will be + a singleton. + + +

    + Default is true. +

    +
    + +

    + owner.(singleton)=true +

    +
    +
    + + + Special string added to distinguish if the object will be + lazily initialised. + + +

    + Default is false. +

    +
    + +

    + owner.(lazy-init)=true +

    +
    +
    + + + Reserved "property" to indicate the parent of a child object definition. + + + + + Property suffix for references to other objects in the current + : e.g. + owner.dog(ref)=fido. + + +

    + Whether this is a reference to a singleton or a prototype + will depend on the definition of the target object. +

    +
    +
    + + + Prefix before values referencing other objects. + + + + + Creates a new instance of the + + class. + + + The + instance that this reader works on. + + + + + Load object definitions from the supplied . + + + The resource for the object definitions that are to be loaded. + + + The number of object definitions that were loaded. + + + In the case of loading or parsing errors. + + + + + Load object definitions from the specified properties file. + + + The resource descriptor for the properties file. + + + The match or filter for object definition names, e.g. 'objects.' + + in case of loading or parsing errors + the number of object definitions found + + + + Register object definitions contained in a + , using all property keys (i.e. + not filtering by prefix). + + + The containing object definitions. + + + In case of loading or parsing errors. + + The number of object definitions registered. + + + + Register object definitions contained in a + . + + +

    + Similar syntax as for an . + This method is useful to enable standard .NET internationalization support. +

    +
    + + The containing object definitions. + + + The match or filter for object definition names, e.g. 'objects.' + + + In case of loading or parsing errors. + + The number of object definitions registered. +
    + + + Register object definitions contained in an + , using all property keys + (i.e. not filtering by prefix). + + + The containing object definitions. + + + In case of loading or parsing errors. + + The number of object definitions registered. + + + + Registers object definitions contained in an + using all property keys ( i.e. not filtering by prefix ) + + The containing + object definitions. + + + In case of loading or parsing errors. + + The number of object definitions registered. + + + + Register object definitions contained in a + . + + +

    + Ignores ineligible properties. +

    +
    + IDictionary name -> property (String or Object). Property values + will be strings if coming from a Properties file etc. Property names + (keys) must be strings. Type keys must be strings. + + + The match or filter within the keys in the map: e.g. 'objects.' + + + In case of loading or parsing errors. + + The number of object definitions found. +
    + + + Register object definitions contained in a + . + + +

    + Ignores ineligible properties. +

    +
    + IDictionary name -> property (String or Object). Property values + will be strings if coming from a Properties file etc. Property names + (keys) must be strings. Type keys must be strings. + + + The match or filter within the keys in the map: e.g. 'objects.' + + + The description of the resource that the + came from (for logging purposes). + + + In case of loading or parsing errors. + + The number of object definitions found. +
    + + + Get all property values, given a prefix (which will be stripped) + and add the object they define to the factory with the given name + + The name of the object to define. + + The containing string pairs. + + The prefix of each entry, which will be stripped. + + The description of the resource that the + came from (for logging purposes). + + + In case of loading or parsing errors. + + + + + Name of default parent object + + + + + Gets or sets object definition factory to use. + + + + + Thrown when the validation of an object definition failed. + + Juergen Hoeller + Rick Evans (.NET) + + + + Creates a new instance of the + + class. + + + + + Creates a new instance of the + + class. + + The detail message. + + + + Creates a new instance of the + + class. + + + The detail message. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ObjectDefinitionValidationException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Utility class that contains various methods useful for the implementation of + autowire-capable object factories. + + Juergen Hoeller + Rick Evans (.NET) + $Id: AutowireUtils.cs,v 1.7 2008/04/02 18:02:24 markpollack Exp $ + + + + Creates a new instance of the AutowireUtils class. + + +

    + This is a utility class, and as such has no publicly + visible constructors. +

    +
    +
    + + + Gets those s + that are applicable for autowiring the supplied . + + + The + (definition) that is being autowired by constructor. + + + The absolute minimum number of arguments that any returned constructor + must have. If this parameter is equal to zero (0), then all constructors + are valid (regardless of their argument count), including any default + constructor. + + + Those s + that are applicable for autowiring the supplied . + + + + + Determine a weight that represents the class hierarchy difference between types and + arguments. + + +

    + A direct match, i.e. type MyInteger -> arg of class MyInteger, does not increase + the result - all direct matches means weight zero (0). A match between the argument type + and a MyInteger instance argument would increase the weight by + 1, due to the superclass () being one (1) steps up in the + class hierarchy being the last one that still matches the required type. +

    +

    + Therefore, with an argument of type , a + constructor taking a argument would be + preferred to a constructor taking an argument + which would be preferred to a constructor taking an + argument which would in turn be preferred + to a constructor taking an argument. +

    +

    + All argument weights get accumulated. +

    +
    + + The argument s to match. + + The arguments to match. + The accumulated weight for all arguments. +
    + + + Determines whether the given object property is excluded from dependency checks. + + The PropertyInfo of the object property. + + true if is excluded from dependency check; otherwise, false. + + + + + Sorts the supplied , preferring + public constructors and "greedy" ones (that have lots of arguments). + + +

    + The result will contain public constructors first, with a decreasing number + of arguments, then non-public constructors, again with a decreasing number + of arguments. +

    +
    + + The array to be sorted. + +
    + + + Determines whether the setter property is defined in any of the given interfaces. + + The PropertyInfo of the object property + The ISet of interfaces. + + true if setter property is defined in interface; otherwise, false. + + + + + Implementation of that + resolves variable name against name-value sections in + the standard .NET configuration file. + + Aleksandar Seovic + $Id: ConfigSectionVariableSource.cs,v 1.4 2007/08/22 20:16:27 oakinger Exp $ + + + + Initializes a new instance of + + + + + Initializes a new instance of from the given + + + + + Initializes a new instance of from the given + + + + + Resolves variable value for the specified variable name. + + + The name of the variable to resolve. + + + The variable value if able to resolve, null otherwise. + + + + + Initializes properties based on the specified + property file locations. + + + + + Gets or sets a list of section names variables should be loaded from. + + + All sections specified need to be handled by the + in order to be processed successfully. + + + A list of section names variables should be loaded from. + + + + + Convinience property. Gets or sets a single section + to read properties from. + + + The section specified needs to be handled by the + in order to be processed successfully. + + + A section to read properties from. + + + + + Implementation of that simply calls . + + + This formatter is a no-operation implementation. + + Erich Eichinger + $Id: NullFormatter.cs,v 1.1 2007/05/19 00:23:18 oakinger Exp $ + + + + Initializes a new instance of the class. + + + + + Converts the passed value to a string by calling . + + The value to convert. + to string converted value. + + + + Returns the passed string "as is". + + The value to return. + The value passed into this method. + + + + Implementation of that can be used to + format and parse values. + + + + DateTimeFormatter uses properties of the + to format and parse values. + + + If you use one of the constructors that accept culture as a parameter + to create an instance of DateTimeFormatter, default DateTimeFormatInfo + for the specified culture will be used. + + + You can also use properties exposed by the DateTimeFormatter in order + to override some of the default formatting parameters. + + + Aleksandar Seovic + $Id: DateTimeFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + + + + Initializes a new instance of the class + using default for the current thread's culture. + + Date/time format string. + + + + Initializes a new instance of the class + using default for the specified culture. + + Date/time format string. + The culture name. + + + + Initializes a new instance of the class + using default for the specified culture. + + Date/time format string. + The culture. + + + + Formats the specified value. + + The value to format. + Formatted value. + If is null. + If is not an instance of . + + + + Parses the specified value. + + The string to parse. + Parsed value. + + + + Represents unary minus operator. + + Aleksandar Seovic + $Id: OpUnaryMinus.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the unary plus operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents logical "less than" operator. + + Aleksandar Seovic + $Id: OpLess.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical "less than" operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed map initializer node in the navigation expression. + + Aleksandar Seovic + $Id: MapInitializerNode.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + + + + Creates a new instance of . + + + + + Create a new instance from SerializationInfo + + + + + Creates new instance of the map defined by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Implementation of the sort processor. + + Aleksandar Seovic + $Id: SortProcessor.cs,v 1.4 2008/03/20 23:58:16 oakinger Exp $ + + + + Sorts the source collection. + + + Please not that this processor requires that collection elements + are of a uniform type and that they implement + interface. +

    + If you want to perform custom sorting based on element properties + you should consider using instead. + + + The source collection to sort. + + + Ignored. + + + An array containing sorted collection elements. + + + If collection is not empty and it is + neither nor . + + + +

    + Converts string representation of the registry key + into instance. + + Aleksandar Seovic + $Id: RegistryKeyConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ +
    + + + Can we convert from a the sourcetype to a ? + + +

    + Currently only supports conversion from a instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + if the conversion is possible. +
    + + + Convert from a value to an + instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A array if successful. + + + + + Generates partial registry key name. + + + Key elements. + + + Index of the last element to use. + + + Friendly key name containing key element from + 0 to , inclusive. + + + + + Returns for the specified + root hive name. + + + Root hive name. + + + Registry key for the specified name. + + + + + Converts string representation of a regular expression into an instance of . + + Aleksandar Seovic + $Id: RegexConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + + + + Can we convert from the sourcetype to a ? + + +

    + Currently only supports conversion from a instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + if the conversion is possible. +
    + + + Convert from a value to an + instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A if successful. + + + + + A custom for any + primitive numeric type such as , + , , etc. + + +

    + Can use a given for + (locale-specific) parsing and rendering. +

    +

    + This is not meant to be used as a system + but rather as a + locale-specific number converter within custom controller code, to + parse user-entered number strings into number properties of objects, + and render them in a UI form. +

    +
    + Juergen Hoeller + Simon White (.NET) + $Id: CustomNumberConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + + The primitive numeric to convert to. + + + The to use for + (locale-specific) parsing and rendering + + + Is an empty string allowed to be converted? If + , an empty string value will be converted to + numeric 0. + + Id the supplied is not a primitive + . + + + + + + Returns whether this converter can convert an object of one + to a + + +

    + Currently only supports conversion from a + instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + + if the conversion is possible. + +
    + + + Converts the specified object (a string) to the required primitive + type. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + A primitive representation of the string value. + + + + Registry class that allows users to register and retrieve protocol handlers. + + + + Resource handler is an implementation of interface + that should be used to process resources with the specified protocol. + + + They are used throughout the framework to access resources from various + sources. For example, application context loads object definitions from the resources + that are processed using one of the registered resource handlers. + + Following resource handlers are registered by default: + + + Protocol + Handler Type + Description + + + config + + Resolves the resources by loading specified configuration section from the standard .NET config file. + + + file + + Resolves filesystem resources. + + + http + + Resolves remote web resources. + + + https + + Resolves remote web resources via HTTPS. + + + ftp + + Resolves ftp resources. + + + assembly + + Resolves resources that are embedded into an assembly. + + + web + Spring.Core.IO.WebResource, Spring.Web* + Resolves resources relative to the web application's virtual directory. + + + * only available in web applications. + + Users can create and register their own protocol handlers by implementing interface + and mapping custom protocol name to that implementation. See for details + on how to register custom protocol handler. + + + Aleksandar Seovic + $Id: ResourceHandlerRegistry.cs,v 1.7 2007/08/08 17:46:55 bbaia Exp $ + + + + Name of the .Net config section that contains definitions + for custom resource handlers. + + + + + Registers standard and user-configured resource handlers. + + + + + Returns resource handler for the specified protocol name. + + + + This method returns object that should be used + to create an instance of the -derived type by passing + resource location as a parameter. + + + Name of the protocol to get the handler for. + Resource handler constructor for the specified protocol name. + If is null. + + + + Returns true if a handler is registered for the specified protocol, + false otherwise. + + Name of the protocol. + + true if a handler is registered for the specified protocol, false otherwise. + + If is null. + + + + Registers resource handler and maps it to the specified protocol name. + + +

    + If the mapping already exists, the existing mapping will be + silently overwritten with the new mapping. +

    +
    + + The protocol to add (or override). + + + The type name of the concrete implementation of the + interface that will handle + the specified protocol. + + + If the supplied is + or contains only whitespace character(s); or + if the supplied is + . + + + If the supplied is not a + that derives from the + interface; or (having passed + this first check), the supplied + does not expose a constructor that takes a single + parameter. + +
    + + + Registers resource handler and maps it to the specified protocol name. + + +

    + If the mapping already exists, the existing mapping will be + silently overwritten with the new mapping. +

    +
    + + The protocol to add (or override). + + + The concrete implementation of the + interface that will handle + the specified protocol. + + + If the supplied is + or contains only whitespace character(s); or + if the supplied is + . + + + If the supplied is not a + that derives from the + interface; or (having passed + this first check), the supplied + does not expose a constructor that takes a single + parameter. + +
    + + + A backed resource. + + +

    + Supports resolution as both a and a + . +

    +

    + Also supports the use of the ~ character. If the ~ character + is the first character in a resource path (sans protocol), the ~ + character will be replaced with the value of the + System.AppDomain.CurrentDomain.BaseDirectory property (an example of + this can be seen in the examples below). +

    +
    + +

    + Consider the example of an application that is running (has been launched + from) the C:\App\ directory. The following resource paths will map + to the following resources on the filesystem... +

    + + strings.txt C:\App\strings.txt + ~/strings.txt C:\App\strings.txt + file://~/strings.txt C:\App\strings.txt + file://~/../strings.txt C:\strings.txt + ../strings.txt C:\strings.txt + ~/../strings.txt C:\strings.txt + + // note that only a leading ~ character is resolved to the executing directory... + stri~ngs.txt C:\App\stri~ngs.txt + +
    + Juergen Hoeller + Leonardo Susatyo (.NET) + Aleksandar Seovic (.NET) + $Id: FileSystemResource.cs,v 1.23 2007/08/08 17:46:55 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The name of the file system resource. + + + If the supplied is + or contains only whitespace character(s). + + + + + Creates a new instance of the + class. + + + The name of the file system resource. + + + Supresses initialization of this instance. Used from derived classes. + + + If the supplied is + or contains only whitespace character(s). + + + + + Initializes this instance. + + + + + + Resolves the handle + for the supplied . + + + The name of the file system resource. + + + The handle for this resource. + + + + + Resolves the root location for the supplied . + + + The name of the file system resource. + + + The root location of the resource. + + + + + Resolves the path for the supplied . + + + The name of the file system resource. + + + The current path of the resource. + + + + + Resolves the presence of the + value + in the supplied into a path. + + + The name of the resource. + + + The string that is a placeholder for a base path. + + + The name of the resource with any + value having been resolved into an actual path. + + + + + Does the supplied relative ? + + + The name of the resource to test. + + + if resource name is relative; + otherwise . + + + + + Returns the underlying handle for + this resource. + + + The handle for this resource. + + + + + + Does this support relative + resource retrieval? + + +

    + This implementation does support relative resource retrieval, and + so will always return . +

    +
    + + if this + supports relative resource + retrieval. + + +
    + + + Gets the root location of the resource (a drive or UNC file share + name in this case). + + + The root location of the resource. + + + + + + Gets the current path of the resource. + + + The current path of the resource. + + + + + + Gets those characters that are valid path separators for the + resource type. + + + Those characters that are valid path separators for the resource + type. + + + + + + + Return an for this resource. + + + An . + + + If the stream could not be opened. + + + If the underlying file could not be found. + + + + + + Returns a description for this resource. + + + A description for this resource. + + + + + + Returns the handle for this resource. + + + The handle for this resource. + + + If the resource is not available or cannot be exposed as a + . + + + + + + Default section handler that can handle any configuration section. + + +

    + Simply returns the configuration section as an . +

    +
    + Aleksandar Seovic + $Id: DefaultSectionHandler.cs,v 1.4 2006/04/09 07:18:38 markpollack Exp $ +
    + + + Returns the configuration section as an + + + The configuration settings in a corresponding parent + configuration section. + + + The configuration context when called from the ASP.NET + configuration system. Otherwise, this parameter is reserved and + is a null reference. + + + The for the section. + + Config section as XmlElement. + + + + + implementation that passes the application context to object that + implement the + , + , and + interfaces. + + +

    + If an object's class implements more than one of the + , + , and + interfaces, then the + order in which the interfaces are satisfied is as follows... + + + + + + + + + + + +

    +

    + Application contexts will automatically register this with their + underlying object factory. Applications should thus never need to use + this class directly. +

    +
    + Juergen Hoeller + Griffin Caprio (.NET) + $Id: ApplicationContextAwareProcessor.cs,v 1.8 2007/08/22 08:49:26 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + The that this + instance will work with. + + + + + Apply this + to the given new object instance before any object + initialization callbacks. + + + The new object instance. + + + The name of the object. + + + The the object instance to use, either the original or a wrapped one. + + + In case of errors. + + + + + + Apply this to the + given new object instance after any object initialization + callbacks. + + + The new object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + In case of errors. + + + + + + To be implemented by any object that wishes to be notified + of the associated with it. + + +

    + In the current implementation, the + will typically be the + associated that + spawned the implementing object. +

    +

    + The can usually also be + passed on as an object reference to arbitrary object properties or + constructor arguments, because a + is typically defined as an + object with the well known name "messageSource" in the + associated application context. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: IMessageSourceAware.cs,v 1.4 2006/04/09 07:18:38 markpollack Exp $ + +
    + + + Sets the associated + with this object. + + +

    + Invoked after population of normal object properties but + before an initializing callback such as the + + method of the + interface + or a custom init-method. +

    +

    + It is also invoked before the + + property of any + + implementation. +

    +
    + + The associated + with this object. + +
    + + + This attribute should be used to mark methods whose argument(s) + need to be cached. + + +

    + This attribute allows application developers to specify that an argument + of the method should be cached, but it will not do any caching by itself. +

    +

    + In order to actually cache the result, an application developer + must apply a Spring.Aspects.Cache.CacheParameterAdvice to + all of the members that have this attribute defined. +

    +

    + You can specify this attribute multiple times on the same method in order to + cache several method parameters. +

    +
    + Aleksandar Seovic + $Id: CacheParameterAttribute.cs,v 1.2 2007/03/31 01:07:26 bbaia Exp $ +
    + + + Creates an attribute instance. + + + + + Creates an attribute instance. + + + The name of the cache to use. + + + An expression string that should be evaluated in order to determine + the cache key for the item. + + + + + Implementation of that adds error message + to the validation errors container. + + Aleksandar Seovic + $Id: ErrorMessageAction.cs,v 1.5 2008/02/05 20:40:25 aseovic Exp $ + + + + Abstract base class that should be extended by all + validation actions. + + +

    + This class implements template Execute method + and defines OnValid and OnInvalid methods that + can be overriden + by specific validation actions. +

    +
    + Aleksandar Seovic + $Id: BaseValidationAction.cs,v 1.6 2008/02/05 20:40:26 aseovic Exp $ +
    + + + An action that should be executed after validator is evaluated. + + +

    + This interface allows us to define the actions that should be executed + after validation in a generic fashion. +

    +

    + For example, addition of error messages to validation errors collection + is performed by one specific implementation of this interface, . +

    +
    + Aleksandar Seovic + $Id: IValidationAction.cs,v 1.4 2008/02/05 20:40:26 aseovic Exp $ +
    + + + Executes the action. + + Whether associated validator is valid or not. + Validation context. + Additional context parameters. + Validation errors container. + + + + Initializes a new instance of the class. + + + + + Executes the action. + + Whether associated validator is valid or not. + Validation context. + Additional context parameters. + Validation errors container. + + + + Called when associated validator is valid. + + Validation context. + Additional context parameters. + Validation errors container. + + + + Called when associated validator is not valid. + + Validation context. + Additional context parameters. + Validation errors container. + + + + Evaluates 'when' expression. + + Root context to use for expression evaluation. + Additional context parameters. + True if the condition is true, False otherwise. + + + + Gets or sets the expression that determines if this validator should be evaluated. + + The expression that determines if this validator should be evaluated. + + + + Initializes a new instance of the class. + + Error message resource identifier. + Names of the error providers this message should be added to. + + + + Called when associated validator is invalid. + + Validation context. + Additional context parameters. + Validation errors container. + + + + Resolves the error message. + + Validation context to resolve message parameters against. + Additional context parameters. + Resolved error message + + + + Resolves the message parameters. + + List of parameters to resolve. + Validation context to resolve parameters against. + Additional context parameters. + Resolved message parameters. + + + + Sets the expressions that should be resolved to error message parameters. + + The expressions that should be resolved to error message parameters. + + + + Discovers the attributes of a + and provides access to the + s metadata. + + Rick Evans + $Id: DelegateInfo.cs,v 1.9 2006/04/09 07:19:00 markpollack Exp $ + + + + The method name associated with a delegate invocation. + + + + + Creates a new instance of the + class. + + + The event used to extract the delegate + from. + + + if the supplied is + . + + + + + Creates a new instance of the + class. + + + The delegate . + + + If the supplied is not a subclass of the + class, or is . + + + + + Checks to see if the method encapsulated by the supplied method + metadata is compatible with the method signature associated with + this delegate type. + + The method to be checked. + + if the method signature is compatible with + the signature of this delegate; if not, or + if the supplied parameter is + . + + + + + Gets the s of the parameters of the + method signature associated with this delegate type. + + +

    + This method will never return ; the returned + array may be empty, but it most certainly + will not be . +

    +
    + + A array of the parameter + s; or the + array if the method signature has no parameters. + +
    + + + Gets the return of the + method signature associated with this delegate type. + + The return . + + + + Gets the metadata about the method signature associated + with this delegate type. + + + The metadata about the method signature associated + with this delegate type. + + + + + Determines whether the supplied + is a type. + + + The to be checked. + + + if the supplied + is a ; + if not or the supplied + is . + + + + + Checks if the signature of the supplied + is compatible with the signature expected by the supplied + . + + The event to be checked against. + + The method signature to check for compatibility. + + + if the signature of the supplied + is compatible with the signature + expected by the supplied ; + if not or either of the supplied + parameters is . + + + + + + The of the delegate. + + + + + Utility class for .NET configuration files management. + + Aleksandar Seovic + $Id: ConfigurationUtils.cs,v 1.5 2008/01/10 16:37:43 bbaia Exp $ + + + + Parses the configuration section. + + +

    + Primary purpose of this method is to allow us to parse and + load configuration sections using the same API regardless + of the .NET framework version. +

    +

    + If Microsoft paid a bit more attention to preserving backwards + compatibility we would not even need it, but... :( +

    +
    + Name of the configuration section. + Object created by a corresponding . +
    + + + Refresh the configuration section. + + +

    + Primary purpose of this method is to allow us to parse and + load configuration sections using the same API regardless + of the .NET framework version. +

    +

    + If Microsoft paid a bit more attention to preserving backwards + compatibility we would not even need it, but... :( +

    +
    + Name of the configuration section. +
    + + + Creates the configuration exception. + + The message to display to the client when the exception is thrown. + The inner exception. + Name of the configuration file. + The line where exception occured. + Configuration exception. + + + + Creates the configuration exception. + + The message to display to the client when the exception is thrown. + Name of the configuration file. + The line where exception occured. + Configuration exception. + + + + Creates the configuration exception. + + The message to display to the client when the exception is thrown. + The inner exception. + XML node where exception occured. + Configuration exception. + + + + Creates the configuration exception. + + The message to display to the client when the exception is thrown. + XML node where exception occured. + Configuration exception. + + + + Creates the configuration exception. + + The message to display to the client when the exception is thrown. + The inner exception. + Configuration exception. + + + + Creates the configuration exception. + + The message to display to the client when the exception is thrown. + Configuration exception. + + + + Creates the configuration exception. + + Configuration exception. + + + + Determines whether the specified exception is configuration exception. + + The exception to check. + + true if the specified exception is configuration exception; otherwise, false. + + + + + Returns the line number of the specified node. + + Node to get the line number for. + The line number of the specified node. + + + + Returns the name of the file specified node is defined in. + + Node to get the file name for. + The name of the file specified node is defined in. + + + + Utility class containing helper methods for object comparison. + + Aleksandar Seovic + $Id: CompareUtils.cs,v 1.2 2007/02/17 08:28:12 bbaia Exp $ + + + Compares two objects. + First object. + Second object. + + 0, if objects are equal; + less than zero, if the first object is smaller than the second one; + greater than zero, if the first object is greater than the second one. + + + + Defines constructors that dynamic constructor class has to implement. + + + + + Invokes dynamic constructor. + + + Constructor arguments. + + + A constructor value. + + + + + Safe wrapper for the dynamic constructor. + + + will attempt to use dynamic + constructor if possible, but it will fall back to standard + reflection if necessary. + + + + + Creates a new instance of the safe constructor wrapper. + + Constructor to wrap. + + + + Invokes dynamic constructor. + + + Constructor arguments. + + + A constructor value. + + + + + Factory class for dynamic constructors. + + Aleksandar Seovic + $Id: DynamicConstructor.cs,v 1.1 2007/08/08 04:05:37 bbaia Exp $ + + + + Creates dynamic constructor instance for the specified . + + Constructor info to create dynamic constructor for. + Dynamic constructor for the specified . + + + + Default implementation of the + interface. + + +

    + Parses object definitions according to the standard Spring.NET schema. +

    +

    + This schema is typically located at + http://www.springframework.net/xsd/spring-objects.xsd. +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: ObjectsNamespaceParser.cs,v 1.7 2007/11/26 14:15:54 bbaia Exp $ +
    + + + The namespace URI for the standard Spring.NET object definition schema. + + + + + The shared instance for this class (and derived classes). + + + + + Invoked by after construction but before any + elements have been parsed. + + This is a NoOp + + + + Parse the specified element and register any resulting + IObjectDefinitions with the IObjectDefinitionRegistry that is + embedded in the supplied ParserContext. + + The element to be parsed into one or more IObjectDefinitions + The object encapsulating the current state of the parsing + process. + + The primary IObjectDefinition (can be null as explained above) + + + Implementations should return the primary IObjectDefinition + that results from the parse phase if they wish to used nested + inside (for example) a <property> tag. + Implementations may return null if they will not + be used in a nested scenario. + + + + + + Parse the specified XmlNode and decorate the supplied ObjectDefinitionHolder, + returning the decorated definition. + + The XmlNode may either be an XmlAttribute or an XmlElement, depending on + whether a custom attribute or element is being parsed. + Implementations may choose to return a completely new definition, + which will replace the original definition in the resulting IApplicationContext/IObjectFactory. + + The supplied ParserContext can be used to register any additional objects needed to support + the main definition. + + The source element or attribute that is to be parsed. + The current object definition. + The object encapsulating the current state of the parsing + process. + The decorated definition (to be registered in the IApplicationContext/IObjectFactory), + or simply the original object definition if no decoration is required. A null value is strickly + speaking invalid, but will leniently treated like the case where the original object definition + gets returned. + + + + Loads external XML object definitions from the resource described by the supplied + . + + The XML element describing the resource. + The parser context. + + If the resource could not be imported. + + + + Parses an event listener definition. + + The name associated with the object that the event handler is being defined on. + + The events being populated. + + The element containing the event listener definition. + + + The namespace-aware parser. + + + + + Parse an object definition and register it with the object factory.. + + The element containing the object definition. + The parser context. + + + + + Parse a standard object definition into a + , + including object name and aliases. + + The element containing the object definition. + The parser context. + + The object (definition) wrapped within an + + instance. + + +

    + Object elements specify their canonical name via the "id" attribute + and their aliases as a delimited "name" attribute. +

    +

    + If no "id" is specified, uses the first name in the "name" attribute + as the canonical name, registering all others as aliases. +

    +
    +
    + + + Calculates an id for an object definition. + + +

    + Called when an object definition has not been explicitly defined + with an id. +

    +
    + + The element containing the object definition. + + + The list of names defined for the object; may be + or even empty. + + + A calculated object definition id. + +
    + + + Parse a standard object definition. + + The element containing the object definition. + The id of the object definition. + parsing state holder + The object (definition). + + + + Parse method override argument subelements of the given object element. + + + + + Parse element and add parsed element to + + + + + Parse element and add parsed element to + + + + + Parse constructor argument subelements of the given object element. + + + + + Parse event handler subelements of the given object element. + + + + + Parse property value subelements of the given object element. + + + The name of the object (definition) associated with the property element (s) + + + The element containing the top level object definition. + + + The namespace-aware parser. + + + The property (s) associated with the object (definition). + + + + + Parse a constructor-arg element. + + + The name of the object (definition) associated with the ctor arg. + + + The list of constructor args associated with the object (definition). + + + The name of the element containing the ctor arg definition. + + + The namespace-aware parser. + + + + + Parse a property element. + + + The name of the object (definition) associated with the property. + + + The list of properties associated with the object (definition). + + + The name of the element containing the property definition. + + + The namespace-aware parser. + + + + + Get the value of a property element (may be a list). + +

    + Please note that even though this method is named GetPropertyValue, + it is called by both the property and constructor argument element + handlers. +

    +
    + The property element. + + The name of the object associated with the property. + + + The namespace-aware parser. + +
    + + + Parse a value, ref or collection subelement of a property element. + + + Subelement of property element; we don't know which yet. + + + The name of the object (definition) associated with the top level property. + + + The namespace-aware parser. + + + + + Gets a list definition. + + + The element describing the list definition. + + + The name of the object (definition) associated with the list definition. + + + The namespace-aware parser. + + The list definition. + + + + Gets a set definition. + + + The element describing the set definition. + + + The name of the object (definition) associated with the set definition. + + + The namespace-aware parser. + + The set definition. + + + + Gets a dictionary definition. + + + The element describing the dictionary definition. + + + The name of the object (definition) associated with the dictionary definition. + + + The namespace-aware parser. + + The dictionary definition. + + + + Selects sub-elements with a given + name. + + +

    + Uses a namespace manager if necessary. +

    +
    + + The element to be searched in. + + + The name of the child nodes to look for. + + + The child s of the supplied + with the supplied + . + +
    + + + Selects a single sub-element with a given + name. + + +

    + Uses a namespace manager if necessary. +

    +
    + + The element to be searched in. + + + The name of the child node to look for. + + + The first child of the supplied + with the supplied + . + +
    + + + Gets a name value collection mapping definition. + + + The element describing the name value collection mapping definition. + + + The name of the object (definition) associated with the + name value collection mapping definition. + + The name value collection definition. + + + + Returns the text of the supplied , + or the empty string value if said is empty. + + +

    + If the supplied is , + then the empty string value will be returned. +

    +
    +
    + + + Strips the dependency check value out of the supplied string. + + +

    + If the supplied is an invalid dependency + checking mode, the invalid value will be logged and this method will + return the value. + No exception will be raised. +

    +
    + + The string containing the dependency check value. + + The dependency check value. + +
    + + + Strips the autowiring mode out of the supplied string. + + +

    + If the supplied is an invalid autowiring mode, + the invalid value will be logged and this method will return the + value. No exception will be raised. +

    +
    + + The string containing the autowiring mode definition. + + The autowiring mode. + +
    + + + Given a string containing delimited object names, returns + a string array split on the object name delimeter. + + + The string containing delimited object names. + + + A string array split on the object name delimeter. + + + + + + Represents an override of a method that looks up an object in the same IoC context. + + +

    + Methods eligible for lookup override must not have arguments. +

    +
    + Rod Johnson + Rick Evans (.NET) + $Id: LookupMethodOverride.cs,v 1.3 2007/03/16 04:01:41 aseovic Exp $ +
    + + + Creates a new instance of the + class. + + +

    + Methods eligible for lookup override must not have arguments. +

    +
    + + The name of the method that is to be overridden. + + + The name of the object in the current IoC context that the + dependency injected method must return. + + + If either of the supplied arguments is or + contains only whitespace character(s). + +
    + + + Does this + match the supplied ? + + The method to be checked. + + if this override matches the supplied . + + + If the supplied is . + + + + + A that represents the current + . + + + A that represents the current + . + + + + + The name of the object in the current IoC context that the + dependency injected method must return. + + + + + Thrown in response to an attempt to lookup a factory object, and + the object identified by the lookup key is not a factory. + + +

    + An object is a factory if it implements (either directly or indirectly + via inheritance) the + interface. +

    +
    + Rod Johnson + Rick Evans (.NET) + $Id: ObjectIsNotAFactoryException.cs,v 1.5 2006/04/09 07:18:49 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The name of the object that was being retrieved from the factory. + + + The object instance that was retrieved. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Resolves placeholder values in one or more object definitions. + + +

    + The default placeholder syntax follows the NAnt style: ${...}. + Instances of this class can be configured in the same way as any other + object in a Spring.NET container, and so custom placeholder prefix + and suffix values can be set via the + and properties. +

    + +

    + The following example XML context definition defines an object that has + a number of placeholders. The placeholders can easily be distinguished + by the presence of the ${} characters. +

    + + + + + + + + +

    + The associated XML configuration file for the above example containing the + values for the placeholders would contain a snippet such as .. +

    + + + + + + + + +

    + The preceding XML snippet listing the various property keys and their + associated values needs to be inserted into the .NET config file of + your application (or Web.config file for your ASP.NET web application, + as the case may be), like so... +

    + + + + + + + + +
    +

    + + checks simple property values, lists, dictionaries, sets, constructor + values, object type name, and object names in + runtime object references ( + ). + Furthermore, placeholder values can also cross-reference other + placeholders, in the manner of the following example where the + rootPath property is cross-referenced by the subPath + property. +

    + + + + + + + + +

    + In contrast to the + + class, this configurer only permits the replacement of explicit + placeholders in object definitions. Therefore, the original definition + cannot specify any default values for its object properties, and the + placeholder configuration file is expected to contain an entry for each + defined placeholder. That is, if an object definition contains a + placeholder ${foo}, there should be an associated + <add key="foo" value="..."/> entry in the + referenced placeholder configuration file. Default property values + can be defined via the inherited + + collection to overcome any perceived limitation of this feature. +

    +

    + If a configurer cannot resolve a placeholder, and the value of the + + property is currently set to , an + + will be thrown. If you want to resolve properties from multiple configuration + resources, simply specify multiple resources via the + + property. Finally, please note that you can also define multiple + + instances, each with their own custom placeholder syntax. +

    +
    + Juergen Hoeller + Simon White (.NET) + $Id: PropertyPlaceholderConfigurer.cs,v 1.23 2007/08/02 22:18:32 markpollack Exp $ + + + +
    + + + The default placeholder prefix. + + + + + The default placeholder suffix. + + + + + Apply the given properties to the supplied + . + + + The + used by the application context. + + The properties to apply. + + If an error occured. + + + + + Parse values recursively to be able to resolve cross-references between + placeholder values. + + + The map of constructor arguments / property values. + + The string to be resolved. + The placeholders that have already been visited + during the current resolution attempt (used to detect circular references + between placeholders). Only non-null if we're parsing a nested placeholder. + + If an error occurs. + + The resolved string. + + + + Resolve the given placeholder using the given name value collection, + performing an environment variables check according to the given mode. + + +

    + The default implementation delegates to + + before/afer the environment variable check. Subclasses can override + this for custom resolution strategies, including customized points + for the environment properties check. +

    +
    + The placeholder to resolve + + The merged name value collection of this configurer. + + The environment variable mode. + + The resolved value or if none. + + +
    + + + Resolve the given placeholder using the given name value collection. + + +

    + This (the default) implementation simply looks up the value of the + supplied key. +

    +

    + Subclasses can override this for customized placeholder-to-key + mappings or custom resolution strategies, possibly just using the + given name value collection as fallback. +

    +
    + The placeholder to resolve. + + The merged name value collection of this configurer. + + The resolved value. +
    + + + The placeholder prefix (the default is ${). + + + + + + The placeholder suffix (the default is }) + + + + + + Indicates whether unresolved placeholders should be ignored. + + + + + Controls how environment variables will be used to + replace property placeholders. + + +

    + See the overview of the + + enumeration for the available options. +

    +
    +
    + + + Subinterface of + that adds + a before-destruction callback. + + + The typical usage will be to invoke custom destruction callbacks on + specific object types, matching corresponding initialization callbacks. + + Juergen Hoeller + Simon White (.NET) + $Id: IDestructionAwareObjectPostProcessor.cs,v 1.4 2006/04/09 07:18:47 markpollack Exp $ + + + + Apply this + to the + given new object instance before its destruction. Can invoke custom + destruction callbacks. + + The new object instance. + The name of the object. + + In case of errors. + + + + + Support class for implementing custom namespace parsers. + + Parsing of individual elements is done via a ObjectDefintionParser. + Provides the RegisterObjectDefinitionParser for registering a ObjectDefintionParser + to handle a specific element. + Rob Harrop + Juergen Hoeller + Mark Pollack (.NET) + $Id: NamespaceParserSupport.cs,v 1.9 2008/02/20 14:29:22 bbaia Exp $ + + + + Invoked by after construction but before any + elements have been parsed. + + + + + Parses an element under the root node, typically + an object definition or import statement. + + + The element to be parsed. + + + The parser context. + + + The number of object defintions created from this element. + + + + + Parse the specified XmlNode and decorate the supplied ObjectDefinitionHolder, + returning the decorated definition. + + The XmlNode may either be an XmlAttribute or an XmlElement, depending on + whether a custom attribute or element is being parsed. + Implementations may choose to return a completely new definition, + which will replace the original definition in the resulting IApplicationContext/IObjectFactory. + + The supplied ParserContext can be used to register any additional objects needed to support + the main definition. + + The source element or attribute that is to be parsed. + The current object definition. + The object encapsulating the current state of the parsing + process. + The decorated definition (to be registered in the IApplicationContext/IObjectFactory), + or simply the original object definition if no decoration is required. A null value is strickly + speaking invalid, but will leniently treated like the case where the original object definition + gets returned. + + + + Register the specified for the given + + + + + Represents ternary expression node. + + Aleksandar Seovic + $Id: TernaryNode.cs,v 1.9 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the string literal node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents node that navigates to object's property or public field. + + Aleksandar Seovic + $Id: PropertyOrFieldNode.cs,v 1.28 2007/09/14 13:48:53 oakinger Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Initializes the node. + + The parent. + + + + Attempts to resolve property or field. + + + Type to search for a property or a field. + + + Property or field name. + + + Binding flags to use. + + + Resolved property or field accessor, or null + if specified cannot be resolved. + + + + + Returns node's value for the given context. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Sets node's value for the given context. + + Context to evaluate expressions against. + Current expression evaluation context. + New value for this node. + + + + Retrieves property or field value. + + Context to evaluate expressions against. + Current expression evaluation context. + Property or field value. + + + + Sets property value, doing any type conversions that are necessary along the way. + + Context to evaluate expressions against. + Current expression evaluation context. + New value for this node. + + + + Sets property or field value using either dynamic or standard reflection. + + Object to evaluate node against. + New value for this node, converted to appropriate type. + + + + In the case of read only collections or custom collections that are not assignable from + IList, try to add to the collection. + + Context to evaluate expressions against. + Current expression evaluation context. + New value for this node. + true if was able add to IList, IDictionary, or ISet + + + + Utility method that is needed by ObjectWrapper and AbstractAutowireCapableObjectFactory. + We try as hard as we can, but there are instances when we won't be able to obtain PropertyInfo... + + Context to resolve property against. + PropertyInfo for this node. + + + + Gets a value indicating whether this node represents a property. + + + true if this node is a property; otherwise, false. + + + + + Gets a value indicating whether this node represents a field. + + + true if this node is a field; otherwise, false. + + + + + Represents arithmetic multiplication operator. + + Aleksandar Seovic + $Id: OpMULTIPLY.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the arithmetic multiplication operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed named argument node in the expression. + + Aleksandar Seovic + $Id: NamedArgumentNode.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns the value of the named argument defined by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed selection node in the navigation expression. + + Aleksandar Seovic + $Id: SelectionNode.cs,v 1.6 2008/03/25 22:33:04 oakinger Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a containing results of evaluation + of selection expression against each node in the context. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Implementation of the maximum aggregator. + + Aleksandar Seovic + $Id: MaxAggregator.cs,v 1.2 2006/12/04 08:58:33 aseovic Exp $ + + + + Returns the largest item in the source collection. + + + The source collection to process. + + + Ignored. + + + The largest item in the source collection. + + + + + Converts a separated to a + array. + + +

    + Defaults to using the , (comma) as the list separator. Note that the value + of the current is + not used. +

    +

    + If you want to provide your own list separator, you can set the value of the + + property to the value that you want. Please note that this value will be used + for all future conversions in preference to the default list separator. +

    +

    + Please note that the individual elements of a string will be passed + through as is (i.e. no conversion or trimming of surrounding + whitespace will be performed). +

    +

    + This should be + automatically registered with any + implementations. +

    +
    + + + public class StringArrayConverterExample + { + public static void Main() + { + StringArrayConverter converter = new StringArrayConverter(); + + string csvWords = "This,Is,It"; + string[] frankBoothWords = converter.ConvertFrom(csvWords); + + // the 'frankBoothWords' array will have 3 elements, namely + // "This", "Is", "It". + + // please note that extraneous whitespace is NOT trimmed off + // in the current implementation... + string csv = " Cogito ,ergo ,sum "; + string[] descartesWords = converter.ConvertFrom(csv); + + // the 'descartesWords' array will have 3 elements, namely + // " Cogito ", "ergo ", "sum ". + // notice how the whitespace has NOT been trimmed. + } + } + + + + $Id: StringArrayConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ +
    + + + Can we convert from a the sourcetype to a array? + + +

    + Currently only supports conversion from a instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + if the conversion is possible. +
    + + + Convert from a value to a + array. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A array if successful. + + + + + The value that will be used as the list separator when performing + conversions. + + + A 'single' string character that will be used as the list separator + when performing conversions. + + + If the supplied value is not and is an empty + string, or has more than one character. + + + + + Configuration section handler for the (recommended, Spring.NET standard) parsers + config section. + + +

    + Spring.NET allows the registration of custom configuration parsers that + can be used to create simplified configuration schemas that better + describe object definitions. +

    +

    + For example, Spring.NET uses this facility internally in order to + define simplified schemas for various AOP, Data and Services definitions. +

    +
    + +

    + The following example shows how to configure both this section handler + and how to define custom configuration parsers within a Spring.NET + config section. +

    + + + + +
    + + + + + + + ... + + ... + + + + + Aleksandar Seovic + $Id: NamespaceParsersSectionHandler.cs,v 1.1 2007/08/08 01:53:43 bbaia Exp $ + + + + + Registers parsers specified in the (recommended, Spring.NET standard) + parsers config section with the . + + + The configuration settings in a corresponding parent + configuration section. + + + The configuration context when called from the ASP.NET + configuration system. Otherwise, this parameter is reserved and + is . + + + The for the section. + + + This method always returns , because parsers + are registered as a side-effect of this object's execution and there + is thus no need to return anything. + + + + + Implements a thread-safe wrapper. + + +

    + The implementation is extremely conservative, serializing critical + sections to prevent possible deadlocks, and locking on everything. The + one exception is for enumeration, which is inherently not thread-safe. + For this, you have to lock the SyncRoot object for the + duration of the enumeration. +

    +
    + + $Id: SynchronizedSet.cs,v 1.6 2007/03/16 04:01:29 aseovic Exp $ +
    + + + Constructs a thread-safe + wrapper. + + + The object that this object + will wrap. + + + If the supplied ecposes a + SyncRoot value. + + + + + Adds the specified element to this set if it is not already present. + + The object to add to the set. + + is the object was added, + if the object was already present. + + + + + Adds all the elements in the specified collection to the set if + they are not already present. + + A collection of objects to add to the set. + + is the set changed as a result of this + operation. + + + + + Removes all objects from this set. + + + + + Returns if this set contains the specified + element. + + The element to look for. + + if this set contains the specified element. + + + + + Returns if the set contains all the + elements in the specified collection. + + A collection of objects. + + if the set contains all the elements in the + specified collection; also if the + supplied is . + + + + + Removes the specified element from the set. + + The element to be removed. + + if the set contained the specified element. + + + + + Remove all the specified elements from this set, if they exist in + this set. + + A collection of elements to remove. + + if the set was modified as a result of this + operation. + + + + + Retains only the elements in this set that are contained in the + specified collection. + + + The collection that defines the set of elements to be retained. + + + if this set changed as a result of this + operation. + + + + + Copies the elements in the to + an array. + + +

    + The type of array needs to be compatible with the objects in the + , obviously. +

    +
    + + An array that will be the target of the copy operation. + + + The zero-based index where copying will start. + +
    + + + Gets an enumerator for the elements in the + . + + + An over the elements + in the . + + + + + Returns a clone of the instance. + + A clone of this object. + + + + Returns if this set contains no elements. + + + + + The number of elements currently contained in this collection. + + + + + Returns if the + is synchronized across + threads. + + + + + + An object that can be used to synchronize this collection to make + it thread-safe. + + + An object that can be used to synchronize this collection to make + it thread-safe. + + + + + + Synchronized that, unlike hashtable created + using method, synchronizes + reads from the underlying hashtable in addition to writes. + + +

    + In addition to synchronizing reads, this implementation also fixes + IEnumerator/ICollection issue described at + http://msdn.microsoft.com/netframework/programming/breakingchanges/runtime/clr.aspx + (search for SynchronizedHashtable for issue description), by implementing + interface explicitly, and returns thread safe enumerator + implementations as well. +

    +

    + This class should be used whenever a truly synchronized + is needed. +

    +
    + Aleksandar Seovic + $Id: SynchronizedHashtable.cs,v 1.2 2007/08/27 13:57:17 oakinger Exp $ +
    + + + Initializes a new instance of + + + + + Initializes a new instance of , copying inital entries from . + + + + + Adds an element with the provided key and value to the object. + + The to use as the value of the element to add. + The to use as the key of the element to add. + An element with the same key already exists in the object. + key is null. + The is read-only.-or- The has a fixed size. 2 + + + + Removes all elements from the object. + + The object is read-only. 2 + + + + Creates a new object that is a copy of the current instance. + + + A new object that is a copy of this instance. + + + + + Determines whether the object contains an element with the specified key. + + + true if the contains an element with the key; otherwise, false. + + The key to locate in the object. + key is null. 2 + + + + Returns, whether this contains an entry with the specified . + + The key to look for + , if this contains an entry with this + + + + Returns, whether this contains an entry with the specified . + + The valúe to look for + , if this contains an entry with this + + + + Copies the elements of the to an , starting at a particular index. + + The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + The zero-based index in array at which copying begins. + array is null. + The type of the source cannot be cast automatically to the type of the destination array. + index is less than zero. + array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source is greater than the available space from index to the end of the destination array. 2 + + + + Returns an object for the object. + + + An object for the object. + + + + + Removes the element with the specified key from the object. + + The key of the element to remove. + The object is read-only.-or- The has a fixed size. + key is null. 2 + + + + Returns an enumerator that iterates through a collection. + + + An object that can be used to iterate through the collection. + + + + + Gets a value indicating whether the object is read-only. + + + true if the object is read-only; otherwise, false. + + + + + Gets a value indicating whether the object has a fixed size. + + + true if the object has a fixed size; otherwise, false. + + + + + Gets a value indicating whether access to the is synchronized (thread safe). + + + true if access to the is synchronized (thread safe); otherwise, false. + + + + + Gets an object containing the keys of the object. + + + An object containing the keys of the object. + + + + + Gets an object containing the values in the object. + + + An object containing the values in the object. + + + + + Gets an object that can be used to synchronize access to the . + + + An object that can be used to synchronize access to the . + + + + + Gets the number of elements contained in the . + + + The number of elements contained in the . + + + + + Gets or sets the element with the specified key. + + + The element with the specified key. + + The key of the element to get or set. + The property is set and the object is read-only.-or- The property is set, key does not exist in the collection, and the has a fixed size. + key is null. 2 + + + + Simple linked list implementation. + + Simon White + $Id: LinkedList.cs,v 1.9 2007/03/16 04:01:28 aseovic Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class that contains all + elements of the specified list. + + + A list of elements that defines the initial contents. + + + + + Removes the object at the specified index. + + The lookup index. + + If the specified is greater than the + number of objects within the list. + + + + + Inserts an object at the specified index. + + The lookup index. + The object to be inserted. + + If the specified is greater than the + number of objects within the list. + + + + + Removes the first instance of the specified object found. + + The object to remove + + + + Returns if this list contains the specified + element. + + The element to look for. + + if this list contains the specified element. + + + + + Removes all objects from the list. + + + + + Returns the index of the first instance of the specified + found. + + The object to search for + + The index of the first instance found, or -1 if the element was not + found. + + + + + Adds the specified object to the end of the list. + + The object to add + The index that the object was added at. + + + + Adds all of the elements of the supplied + list to the end of this list. + + The list of objects to add. + + + + Checks whether the list can be modified. + + + If the list cannot be modified. + + + + + Validates the specified index. + + The lookup index. + + If the index is invalid. + + + + + Returns the node at the specified index. + + The lookup index. + The node at the specified index. + + If the specified is greater than the + number of objects within the list. + + + + + Returns the node (and index) of the first node that contains + the specified value. + + The value to search for. + + The node, or if not found. + + + + + Removes the specified node. + + The node to be removed. + + + + Copies the elements in this list to an array. + + +

    + The type of array needs to be compatible with the objects in this + list, obviously. +

    +
    + + An array that will be the target of the copy operation. + + + The zero-based index where copying will start. + + + If the supplied is . + + + If the supplied is less than zero + or is greater than the length of . + + + If the supplied is of insufficient size. + +
    + + + Gets an enumerator for the elements in the + . + + +

    + Enumerators are fail fast. +

    +
    + + An over the elements + in the . + +
    + + + Is list read only? + + + if the list is read only. + + + + + Returns the node at the specified index. + + +

    + This is the indexer for the + class. +

    +
    + +
    + + + Is the list a fixed size? + + + if the list is a fixed size list. + + + + + Returns if the list is synchronized across + threads. + + + + This implementation always returns . + +

    + Note that enumeration is inherently not thread-safe. Use the + to lock the object during enumeration. +

    +
    +
    + + + The number of objects within the list. + + + + + An object that can be used to synchronize this + to make it thread-safe. + + + An object that can be used to synchronize this + to make it thread-safe. + + + + + implementation that supports validating collections. + + +

    + This validator will be valid only when all of the validators in the Validators + collection are valid for all of the objects in the specified collection. +

    +

    + You can specify if you want to validate all of the collection elements regardless of the errors, by + setting the ValidateAll property to true. +

    +

    + If you set the IncludeElementErrors property to true, + ValidationErrors collection will contain a union of all validation error messages + for the contained validators; + Otherwise it will contain only error messages that were set for this Validator. +

    +
    + Damjan Tomic + Aleksandar Seovic + $Id: CollectionValidator.cs,v 1.5 2008/02/05 20:40:26 aseovic Exp $ +
    + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The bool that determines if all elements of the collection should be evaluated. + regardless of the Errors + + The bool that determines whether Validate method should collect + all error messages returned by the item validators + + + + Initializes a new instance of the class. + + The expression that determines if this validator should be evaluated. + The bool that determines if this all elements of the collection should be evaluated. + regardless of the Errors + + The bool that determines whether Validate method should collect + all error messages returned by the item validators + + + + Initializes a new instance of the class. + + The expression that determines if this validator should be evaluated. + The bool that determines if this all elements of the collection should be evaluated. + regardless of the Errors + + The bool that determines whether Validate method should collect + all error messages returned by the item validators + + + + Validates the specified collection of objects. + If the IncludeElementErrors property was set to true, + collection will contain a union of all validation error messages + for the contained validators; + Otherwise it will contain only error messages that were set for this Validator. + + The collection to validate. + Additional context parameters. + instance to add error messages to. + True if validation was successful, False otherwise. + + + + Gets or sets the value that indicates whether to validate all elements of the collection + regardless of the errors. + + + + + Gets or sets the value that indicates whether to capture all the errors of the specific + elements of the collection + + + + + Gets or sets the expression that should be used to narrow validation context. + + The expression that should be used to narrow validation context. + + + + Use this class for obtaining instances for dynamic code generation. + + +

    + The purpose of this class is to provide a simple abstraction for creating and managing dynamic assemblies. +

    + + Using this factory you can't define several modules within a single dynamic assembly - only a simple one2one relation between assembly/module is used. + +
    + +

    The following excerpt from demonstrates usage:

    + + public class DynamicProxyManager + { + public const string PROXY_ASSEMBLY_NAME = "Spring.Proxy"; + + public static TypeBuilder CreateTypeBuilder(string name, Type baseType) + { + // Generates type name + string typeName = String.Format("{0}.{1}_{2}", PROXY_ASSEMBLY_NAME, name, Guid.NewGuid().ToString("N")); + ModuleBuilder module = DynamicCodeManager.GetModuleBuilder(PROXY_ASSEMBLY_NAME); + return module.DefineType(typeName, PROXY_TYPE_ATTRIBUTES); + } + } + +
    + Erich Eichinger + $Id: DynamicCodeManager.cs,v 1.6 2007/08/08 04:00:16 bbaia Exp $ + + + +
    + + + prevent instantiation + + + + + Returns the for the dynamic module within the specified assembly. + + + If the assembly does not exist yet, it will be created.
    + This factory caches any dynamic assembly it creates - calling GetModule() twice with + the same name will *not* create 2 distinct modules! +
    + The assembly-name of the module to be returned + the that can be used to define new types within the specified assembly +
    + + + Persists the specified dynamic assembly to the file-system + + the name of the dynamic assembly to persist + + Can only be called in DEBUG_DYNAMIC mode, per ConditionalAttribute rules. + + + + + Defines methods that dynamic field class has to implement. + + + + + Gets the value of the dynamic field for the specified target object. + + + Target object to get field value from. + + + A field value. + + + + + Gets the value of the dynamic field for the specified target object. + + + Target object to set field value on. + + + A new field value. + + + + + Safe wrapper for the dynamic field. + + + will attempt to use dynamic + field if possible, but it will fall back to standard + reflection if necessary. + + + + + Obtains cached fieldInfo or creates a new entry, if none is found. + + + + + Creates a new instance of the safe field wrapper. + + Field to wrap. + + + + Gets the value of the dynamic field for the specified target object. + + + Target object to get field value from. + + + A field value. + + + + + Gets the value of the dynamic field for the specified target object. + + + Target object to set field value on. + + + A new field value. + + + + + Holds cached Getter/Setter delegates for a Field + + + + + Factory class for dynamic fields. + + Aleksandar Seovic + $Id: DynamicField.cs,v 1.4 2008/05/16 10:02:39 oakinger Exp $ + + + + Creates dynamic field instance for the specified . + + Field info to create dynamic field for. + Dynamic field for the specified . + + + + Interface for registries that hold object definitions, i.e. + + and + + instances. + + +

    + Typically implemented by object factories that work with the + + hierarchy internally. +

    +
    + Juergen Hoeller + Rick Evans (.NET) +
    + + + Return the names of all objects defined in this registry. + + + The names of all objects defined in this registry, or an empty array + if none defined + + + + + Check if this registry contains a object definition with the given name. + + + The name of the object to look for. + + + True if this object factory contains an object definition with the + given name. + + + + + Returns the + + for the given object name. + + + The name of the object to find a definition for. + + + The for + the given name (never null). + + + If the object definition cannot be resolved. + + + In case of errors. + + + + + Register a new object definition with this registry. + Must support + + and . + + + The name of the object instance to register. + + + The definition of the object instance to register. + + +

    + Must support + and + . +

    +
    + + If the object definition is invalid. + +
    + + + Return the aliases for the given object name, if defined. + + the object name to check for aliases + + +

    + Will ask the parent factory if the object cannot be found in this + factory instance. +

    +
    + + The aliases, or an empty array if none. + + + If there's no such object definition. + +
    + + + Given a object name, create an alias. We typically use this method to + support names that are illegal within XML ids (used for object names). + + + The name of the object. + + + The alias that will behave the same as the object name. + + + If there is no object with the given name. + + + If the alias is already in use. + + + + + Return the number of objects defined in the registry. + + + The number of objects defined in the registry. + + + + + Exception that an object implementation is suggested to throw if its own + factory-aware initialization code fails. + thrown by object factory methods + themselves should simply be propagated as-is. + + +

    + Note that non-factory-aware initialization methods like AfterPropertiesSet () + or a custom "init-method" can throw any exception. +

    +
    + Juergen Hoeller + Rick Evans (.NET) +
    + + + Creates a new instance of the ObjectInitializationException class. + + + + + Creates a new instance of the ObjectInitializationException class. + + + A message about the exception. + + + + + Creates a new instance of the ObjectInitializationException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the ObjectInitializationException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Overrides default values in one or more object definitions. + + +

    + Instances of this class override already existing values, and is + thus best suited to replacing defaults. If you need to replace + placeholder values, consider using the + + class instead. +

    +

    + In contrast to the + + class, the original object definition can have default + values or no values at all for such object properties. If an overriding + configuration file does not have an entry for a certain object property, + the default object value is left as is. Also note that it is not + immediately obvious to discern which object definitions will be mutated by + one or more + s + simply by looking at the object configuration. +

    +

    + Each line in a referenced configuration file is expected to take the + following form... +

    + + + +

    + The name.property key refers to the object name and the + property that is to be overridden; and the value is the overridding + value that will be inserted into the appropriate object definition's + named property. +

    +

    + Please note that in the case of multiple + s + that define different values for the same object definition value, the + last overridden value will win (due to the fact that the values + supplied by previous + s + will be overridden). +

    +
    + +

    + The following XML context definition defines an object that has a number + of properties, all of which have default values... +

    + + + + + + + + +

    + What follows is a .NET config file snippet for the above example (assuming + the need to override one of the default values)... +

    + + + + + + +
    + Juergen Hoeller + Simon White (.NET) + $Id: PropertyOverrideConfigurer.cs,v 1.11 2007/10/10 16:31:39 bbaia Exp $ + + + +
    + + + Apply the given properties to the supplied + . + + + The + used by the application context. + + The properties to apply. + + If an error occured. + + + + + Process the given key as 'name.property' entry. + + + The object factory containing the object definitions that are to be + processed. + + The key. + The value. + + If an error occurs. + + + If the property was not well formed (i.e. not in the format "name.property"). + + + + + Attribute that should be used to specify the default namespace + and schema location for a custom namespace parser. + + + If + + Aleksandar Seovic + $Id: NamespaceParserAttribute.cs,v 1.1 2007/08/08 00:34:06 bbaia Exp $ + + + + Creates a new instance of . + + + + + Gets or sets the default namespace for the configuration parser. + + + The default namespace for the configuration parser. + + + + + Gets or sets the default schema location for the configuration parser. + + + The default schema location for the configuration parser. + + + If the property is set, the will always resolve to an assembly-resource + and the set will be interpreted relative to this assembly. + + + + + Gets or sets a type from the assembly containing the schema + + + If this property is set, the will always resolve to an assembly-resource + and the will be interpreted relative to this assembly. + + + + + Resolves placeholder values in one or more object definitions + + + The placeholder syntax follows the NAnt style: ${...}. + Placeholders values are resolved against a list of + s. In case of multiple definitions + for the same property placeholder name, the first one in the + list is used. + Variable substitution is performed on simple property values, + lists, dictionaries, sets, constructor + values, object type name, and object names in + runtime object references ( + ). + Furthermore, placeholder values can also cross-reference other + placeholders, in the manner of the following example where the + rootPath property is cross-referenced by the subPath + property. + + + + + + + + + + If a configurer cannot resolve a placeholder, and the value of the + + property is currently set to , an + + will be thrown. + + Mark Pollack + $Id: VariablePlaceholderConfigurer.cs,v 1.2 2007/08/02 22:18:32 markpollack Exp $ + + + + Modify the application context's internal object factory after its + standard initialization. + + The object factory used by the application context. + +

    + All object definitions will have been loaded, but no objects will have + been instantiated yet. This allows for overriding or adding properties + even to eager-initializing objects. +

    +
    + + In case of errors. + +
    + + + Apply the property replacement using the specified s for all + object in the supplied + . + + + The + used by the application context. + + + If an error occured. + + + + + Sets the list of s that will be used to resolve placeholder names. + + A list of s. + + + + Sets that will be used to resolve placeholder names. + + A instance. + + + + Indicates whether unresolved placeholders should be ignored. + + + + + Return the order value of this object, where a higher value means greater in + terms of sorting. + + The order value. + + + + + Replaces input strings with a given default value, + if they are null or contain whitespaces only, + + Erich Eichinger + $Id: HasTextFilteringFormatter.cs,v 1.1 2008/03/20 13:19:47 oakinger Exp $ + + + + Provides base functionality for filtering values before they actually get parsed/formatted. + + Erich Eichinger + $Id: FilteringFormatter.cs,v 1.1 2008/03/20 13:19:47 oakinger Exp $ + + + + Creates a new instance of this FilteringFormatter. + + an optional underlying formatter + + If no underlying formatter is specified, the values + get passed through "as-is" after being filtered + + + + + Parses the specified value. + + The value to parse. + Parsed . + + + + Formats the specified value. + + The value to format. + Formatted . + + + + Allows to rewrite a value before it gets parsed by the underlying formatter + + + + + Allows to change a value before it gets formatted by the underlying formatter + + + + + Creates a new instance of this HasTextFilteringFormatter using null as default value. + + an optional underlying formatter + + If no underlying formatter is specified, the values + get passed through "as-is" after being filtered + + + + + Creates a new instance of this HasTextFilteringFormatter. + + the default value to be returned, if input text doesn't contain text + an optional underlying formatter + + If no underlying formatter is specified, the values + get passed through "as-is" after being filtered + + + + + If value contains no text, it will be replaced by a defaultValue. + + + + + Represents arithmetic subtraction operator. + + Aleksandar Seovic + $Id: OpSUBTRACT.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the arithmetic subtraction operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents logical inequality operator. + + Aleksandar Seovic + $Id: OpNotEqual.cs,v 1.11 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical inequality operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents VB-style logical LIKE operator. + + Aleksandar Seovic + $Id: OpLike.cs,v 1.5 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical LIKE operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + + true if the left operand matches the right operand, false otherwise. + + + + + Represents parsed method node in the navigation expression. + + Aleksandar Seovic + $Id: ArrayConstructorNode.cs,v 1.11 2007/09/07 03:01:21 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Creates new instance of the type defined by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Implementation of the average aggregator. + + Aleksandar Seovic + $Id: AverageAggregator.cs,v 1.2 2006/12/04 08:58:33 aseovic Exp $ + + + + Returns the average of the numeric values in the source collection. + + + The source collection to process. + + + Ignored. + + + The average of the numeric values in the source collection. + + + + + Enumeration that defines possible values for data binding direction. + + Aleksandar Seovic + $Id: BindingDirection.cs,v 1.2 2006/04/09 07:18:39 markpollack Exp $ + + + + Specifies that value from the control property should be bound to a data model. + + + + + Specifies that value from the data model should be bound to control property. + + + + + Specifies that binding is bidirectional. + + + + + Provides additional data for the PropertyChanged event. + + +

    + Provides some additional properties over and above the name of the + property that has changed (which is inherited from the + base class). + This allows calling code to determine whether or not a property has + actually changed (i.e. a PropertyChanged event may have been + raised, but the value itself may be equivalent). +

    +
    + +
    + + + Create a new instance of the + class. + + + The name of the property that was changed. + The old value of the property. + the new value of the property. + + + + Get the old value for the property. + + + + + + Get the new value of the property. + + + + + + Thrown in response to a failed attempt to write a property. + + Mark Pollack (.NET) + $Id: NotWritablePropertyException.cs,v 1.1 2007/07/31 00:08:42 markpollack Exp $ + + + + Creates a new instance of the NotWritablePropertyException class. + + + + + Creates a new instance of the NotWritablePropertyException class. + + + A message about the exception. + + + + + Creates a new instance of the NotWritablePropertyException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the NotWritablePropertyException class. + + + The that is (or rather was) the source of the + offending property. + + + The name of the offending property. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the NotWritablePropertyException class + summarizing what property was not writable. + + + The name of the property that is not writable. + + + The in which the property is not writable. + + + + + Creates new NotWritablePropertyException with a root cause. + + + The name of the property that is not writable. + + + The in which the property is not writable. + + + The root cause indicating why the property was not writable. + + + + + Creates a new instance of the NotWritablePropertyException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Generic ApplicationContext implementation that holds a single internal + instance and does not + assume a specific object definition format. + + + Implements the interface in order + to allow for aplying any object definition readers to it. + Typical usage is to register a variety of object definitions via the + interface and then call + to initialize those + objects with application context semantics (handling + , auto-detecting + ObjectFactoryPostProcessors, etc). + + In contrast to other IApplicationContext implementations that create a new internal + IObjectFactory instance for each refresh, the internal IObjectFactory of this context + is available right from the start, to be able to register object definitions on it. + may only be called once + Usage examples + + GenericApplicationContext ctx = new GenericApplicationContext(); + + + + Mark Pollack + $Id: GenericApplicationContext.cs,v 1.5 2008/02/17 13:34:44 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + if set to true names in the context are case sensitive. + + + + Initializes a new instance of the class. + + The object factory instance to use for this context. + + + + Initializes a new instance of the class. + + The parent application context. + + + + Initializes a new instance of the class. + + The name of the application context. + if set to true names in the context are case sensitive. + The parent application context. + + + + Initializes a new instance of the class. + + The object factory to use for this context + The parent applicaiton context. + + + + Do nothing operation. We hold a single internal ObjectFactory and rely on callers + to register objects throug our public methods (or the ObjectFactory's). + + + In the case of errors encountered while refreshing the object factory. + + + + + Returns the + + for the given object name. + + The name of the object to find a definition for. + + The for + the given name (never null). + + + If the object definition cannot be resolved. + + + In case of errors. + + + + + Register a new object definition with this registry. + Must support + + and . + + The name of the object instance to register. + The definition of the object instance to register. + +

    + Must support + and + . +

    +
    + + If the object definition is invalid. + +
    + + + Given a object name, create an alias. We typically use this method to + support names that are illegal within XML ids (used for object names). + + The name of the object. + The alias that will behave the same as the object name. + + If there is no object with the given name. + + + If the alias is already in use. + + + + + Gets the parent context, or if there is no + parent context. Set the parent of this application context also setting + the parent of the interanl ObjectFactory accordingly. + + The parent context + + The parent context, or if there is no + parent. + + + + + + Return the internal object factory of this application context. + + + + + + Gets the underlying object factory of this context, available for + registering object definitions. + + You need to call Refresh to initialize the + objects factory and its contained objects with application context + semantics (autodecting IObjectFactoryPostProcessors, etc). + The internal object factory (as DefaultListableObjectFactory). + + + + Implements an based on a sorted + tree. + + +

    + This gives good performance for operations on very large data-sets, + though not as good - asymptotically - as a + . However, iteration occurs + in order. +

    +

    + Elements that you put into this type of collection must implement + , and they must actually be comparable. + You can't mix and + values, for example. +

    +

    + This implementation does + not support elements that are . +

    +
    + + $Id: SortedSet.cs,v 1.6 2007/03/16 04:01:29 aseovic Exp $ +
    + + + Creates a new set instance based on a sorted tree. + + + + + Creates a new set instance based on a sorted tree and initializes + it based on a collection of elements. + + + A collection of elements that defines the initial set contents. + + + + + This attribute should be used to mark method that should + invalidate one or more cache items when invoked. + + +

    + This attribute allows application developers to specify that some + cache items should be evicted from cache when the method is invoked, + but it will not do any eviction by itself. +

    +

    + In order to actually evict cache items, an application developer + must apply a Spring.Aspects.Cache.InvalidateCacheAdvice to + all of the members that have this attribute defined. +

    +
    + Aleksandar Seovic + $Id: InvalidateCacheAttribute.cs,v 1.2 2007/04/01 15:04:39 bbaia Exp $ +
    + + + Creates an attribute instance. + + + + + Creates an attribute instance. + + + The name of the cache to use. + + + + + Gets or sets the name of the cache to use. + + + The name of the cache to use. + + + + + Gets or sets a SpEL expression that should be evaluated in order + to determine the keys for the items that should be evicted. + + + An expression string that should be evaluated in order + to determine the keys for the items that should be evicted. + + + + + Gets an expression instance that should be evaluated in order + to determine the keys for the items that should be evicted. + + + An expression instance that should be evaluated in order + to determine the keys for the items that should be evicted. + + + + + Gets or sets a SpEL expression that should be evaluated in order + to determine whether items should be evicted. + + + An expression string that should be evaluated in order to determine + whether items should be evicted. + + + + + Gets an expression instance that should be evaluated in order + to determine whether items should be evicted. + + + An expression instance that should be evaluated in order to determine + whether items should be evicted. + + + + + Implementation of that allows you + to define Spring.NET expressions that should be evaluated after + validation. + + Aleksandar Seovic + $Id: ExpressionAction.cs,v 1.6 2008/02/05 20:40:25 aseovic Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + Expression to execute when validator is valid. + Expression to execute when validator is not valid. + + + + Initializes a new instance of the class. + + Expression to execute when validator is valid. + Expression to execute when validator is not valid. + + + + Called when associated validator is valid. + + Validation context. + Additional context parameters. + Validation errors container. + + + + Called when associated validator is invalid. + + Validation context. + Additional context parameters. + Validation errors container. + + + + Gets or sets the expression to execute when validator is valid. + + The expression to execute when validator is valid. + + + + Gets or sets the expression to execute when validator is not valid. + + The expression to execute when validator is not valid. + + + + Base class for method builders that contains common functionalities. + + Bruno Baia + $Id: AbstractProxyMethodBuilder.cs,v 1.7 2008/02/06 18:26:42 bbaia Exp $ + + + + The type builder to use. + + + + + The implementation to use. + + + + + Indicates whether interfaces should be implemented explicitly. + + + + + Creates a new instance of the method builder. + + The type builder to use. + + The implementation to use. + + + if the interface is to be + implemented explicitly; otherwise . + + + + + Dynamically builds proxy method. + + The method to proxy. + + The interface definition of the method, if applicable. + + + The for the proxy method. + + + + + Generates the IL instructions that pushes + the proxy instance on stack. + + The IL generator to use. + + + + Generates the IL instructions that pushes + the target instance on which calls should be delegated to. + + The IL generator to use. + + + + Defines proxy method for the target object. + + The method to proxy. + + The interface definition of the method, if applicable. + + + if the supplied is to be + implemented explicitly; otherwise . + + + The for the proxy method. + + + + + Defines method parameters based on proxied method metadata. + + + The to use. + + The method to proxy. + + + + Defines generic method parameters based on proxied method metadata. + + + The to use. + + The method to proxy. + + + + Generates the proxy method. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + Calls target method directly. + + The IL generator to use. + + The interface definition of the method, if applicable. + + + + + Calls base method directly. + + The IL generator to use. + The method to proxy. + + + + Replaces a raw reference with a reference to a proxy. + + +

    + If the target object returns reference to itself -- 'this' -- + we need to treat it as a special case and return a reference + to a proxy object instead. +

    +
    + The IL generator to use. + The location of the return value. +
    + + + Resolves a single element value of a managed collection. + + +

    + If the does not need to be resolved or + converted to an appropriate , the + will be returned as-is. +

    +
    + + The name of the top level object that is having the value of one of it's + collection properties resolved. + + + The definition of the named top level object. + + + The name of the property the value of which is being resolved. + + + That element of a managed collection that may need to be resolved + to a concrete value. + + A fully resolved element. +
    + + + An + implementation that delegates to an + that is + obtained as the result of a lookup in an associated IoC container. + + +

    + This class is reserved for internal use within the framework; it is + not intended to be used by application developers using Spring.NET. +

    +
    + Rick Evans + $Id: DelegatingMethodReplacer.cs,v 1.3 2007/07/30 17:52:22 markpollack Exp $ +
    + + + Creates a new instance of the + + class. + + + The object definition that is the target of the method replacement. + + + The enclosing IoC container with which the above + is associated. + + + If either of the supplied arguments is . + + + + + Reimplements the supplied by delegating to + another + looked up in an enclosing IoC container. + + + The instance whose is to be + (re)implemented. + + + The method that is to be (re)implemented. + + The target method's arguments. + + The result of the delegated call to the looked up + . + + + + + Central interface for factories that can create + + instances. + + +

    + Allows for replaceable object definition factories using the Strategy + pattern. +

    +
    + Aleksandar Seovic + $Id: IObjectDefinitionFactory.cs,v 1.10 2007/07/30 18:00:01 markpollack Exp $ +
    + + + Factory style method for getting concrete + + instances. + + + The FullName of the of the defined object. + + The name of the parent object definition (if any). + + The against which any class names + will be resolved into instances. It can be null to register the + object class just by name. + + + An + + instance. + + + + + Sateful class used to parse XML object definitions. + + Not all parsing code has been refactored into this class. + Rob Harrop + Juergen Hoeller + Rod Johnson + Mark Pollack (.NET) + $Id: ObjectDefinitionParserHelper.cs,v 1.7 2007/08/07 22:05:20 markpollack Exp $ + + + + The shared instance for this class (and derived classes). + + + + + Initializes a new instance of the class. + + The reader context. + + + + Initialize the default lazy-init, dependency check, and autowire settings. + + The root element + + + + Determines whether the Spring object namespace is equal to the the specified namespace URI. + + The namespace URI. + + true if is the default Spring namespace; otherwise, false. + + + + + Decorates the object definition if required. + + The element. + The holder. + + + + + Determines whether the string represents a 'true' boolean value. + + The value. + + true if is 'true' string value; otherwise, false. + + + + + Convenience method to create a builder for a root object definition. + + Name of the object type. + A builder for a root object definition. + + + + Convenience method to create a builder for a root object definition. + + Type of the object. + a builder for a root object definition + + + + Gets the defaults definition object, or null if the + default have not yet been initialized. + + The defaults. + + + + Gets the reader context. + + The reader context. + + + + Provides methods for type-safe accessing s. + + Erich Eichinger + $Id: VariableAccessor.cs,v 1.2 2007/08/27 14:49:42 oakinger Exp $ + + + + Initialize a new instance of an + + The underlying to read values from. + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The expected format of the variable's value + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The expected format of the variable's value + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + A that contains the value of the specified variable + or , if returns null. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + A that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns an of type that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + An of type that contains the value of the specified variable + or , if returns null. + + + + + Returns an of type that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns null. + + If false, suppresses exceptions if the result + of cannot be parsed + and returns instead. + + An of type that contains the value of the specified variable + or , if cannot be parsed. + + + + + Returns a that contains the value of the specified variable. + + The name of the variable to be read. + The value to be returned if returns or . + + A that contains the value of the specified variable + or , if returns null. + + + + + Implementation of that can be used to + format and parse currency values. + + + + CurrencyFormatter uses currency related properties of the + to format and parse currency values. + + + If you use one of the constructors that accept culture as a parameter + to create an instance of CurrencyFormatter, default NumberFormatInfo + for the specified culture will be used. + + + You can also use properties exposed by the CurrencyFormatter in order + to override some of the default currency formatting parameters. + + + Aleksandar Seovic + $Id: CurrencyFormatter.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + + + + Initializes a new instance of the class + using default for the current thread's culture. + + + + + Initializes a new instance of the class + using default for the specified culture. + + The culture name. + + + + Initializes a new instance of the class + using default for the specified culture. + + The culture. + + + + Initializes a new instance of the class + using specified . + + + The instance that defines how + currency values are formatted. + + + + + Formats the specified currency value. + + The value to format. + Formatted currency . + If is null. + If is not a number. + + + + Parses the specified currency value. + + The currency value to parse. + Parsed currency value as a . + + + + Gets or sets the currency decimal digits. + + The currency decimal digits. + + + + + Gets or sets the currency decimal separator. + + The currency decimal separator. + + + + + Gets or sets the currency group sizes. + + The currency group sizes. + + + + + Gets or sets the currency group separator. + + The currency group separator. + + + + + Gets or sets the currency symbol. + + The currency symbol. + + + + + Gets or sets the currency negative pattern. + + The currency negative pattern. + + + + + Gets or sets the currency positive pattern. + + The currency positive pattern. + + + + + Represents logical "greater than" operator. + + Aleksandar Seovic + $Id: OpGreater.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical "greater than" operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed null literal node. + + Aleksandar Seovic + $Id: NullLiteralNode.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the null literal node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed map entry node. + + Aleksandar Seovic + $Id: MapEntryNode.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + + + + Creates a new instance of . + + + + + Create a new instance from SerializationInfo + + + + + Creates new instance of the map entry defined by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + implementation that allows + data binding between collections that implement + interface. + + Aleksandar Seovic + $Id: ListBinding.cs,v 1.2 2008/02/05 20:40:25 aseovic Exp $ + + + + Binds source object to target object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Binds target object to source object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Converter for instances. + + Juergen Hoeller + Mark Pollack (.NET) + + + + Creates a new instance of the + class. + + + + + Returns whether this converter can convert an object of one + to a + + +

    + Currently only supports conversion from a + instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + True if the conversion is possible. +
    + + + Convert from a string value to a instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A if successful. + + + + + Converts string representation of a credential for Web client authentication + into an instance of . + + +

    + Find below some examples of the XML formatted strings that this + converter will sucessfully convert. +

    + + + + + + +
    + Bruno Baia + $Id: CredentialConverter.cs,v 1.2 2007/12/07 15:14:40 bbaia Exp $ +
    + + + Can we convert from the sourcetype + to a instance ? + + +

    + Currently only supports conversion from a instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + if the conversion is possible. +
    + + + Convert from a value to an + instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A instance if successful. + + + + + Comparator implementation for objects, sorting by + order value ascending (resp. by priority descending). + + +

    + Non- objects are treated as greatest order values, + thus ending up at the end of a list, in arbitrary order (just like same order values of + objects). +

    +
    + Juergen Hoeller + Aleksandar Seovic (.Net) + $Id: OrderComparator.cs,v 1.5 2006/04/09 07:18:38 markpollack Exp $ +
    + + + Compares two objects and returns a value indicating whether one is less than, + equal to or greater than the other. + + +

    + Uses direct evaluation instead of + to avoid unnecessary boxing. +

    +
    + The first object to compare. + The second object to compare. + + -1 if first object is less then second, 1 if it is greater, or 0 if they are equal. + +
    + + + Used when retrieving information from the standard .NET configuration + files (App.config / Web.config). + + +

    + If created with the name of a configuration section, then all methods + aside from the description return , + , or throw an exception. If created with an + , then the + property + will return a corresponding to parse. +

    +
    + Mark Pollack + Rick Evans + $Id: ConfigSectionResource.cs,v 1.27 2007/12/05 00:57:31 bbaia Exp $ +
    + + + Creates new instance of the + class. + + + The actual XML configuration section. + + + If the supplied is . + + + + + Creates new instance of the + class. + + + The name of the configuration section. + + + If the supplied is + or contains only whitespace character(s). + + + + + Returns the handle for this resource. + + +

    + This implementation always returns . +

    +
    + + . + + +
    + + + Returns a handle for this resource. + + +

    + This implementation always returns . +

    +
    + + . + + +
    + + + Returns a description for this resource (the name of the + configuration section in this case). + + + A description for this resource. + + + + + + Does this resource actually exist in physical form? + + +

    + This implementation always returns . +

    +
    + + + + + +
    + + + Return an for this resource. + + + An . + + + If the stream could not be opened. + + + + + + Exposes the actual for the + configuration section. + + +

    + Introduced to accomodate line info tracking during parsing. +

    +
    +
    + + + that allows concrete registration of + objects and messages in code, rather than from external configuration sources. + + +

    + Mainly useful for testing. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: StaticApplicationContext.cs,v 1.9 2007/07/10 05:48:03 markpollack Exp $ +
    + + + Creates a new instance of the StaticApplicationContext class. + + + + + Creates a new instance of the StaticApplicationContext class. + + The parent application context. + + + + Do nothing: we rely on callers to update our public methods. + + + + + Register a singleton object with the default object factory. + + The name of the object. + The of the object. + The property values for the singleton instance. + + + + Registers a prototype object with the default object factory. + + The name of the prototype object. + The of the prototype object. + The property values for the prototype instance. + + + + Associate the given message with the given code. + + The lookup code. + + The that the message should be found within. + + The message associated with the lookup code. + + + + implementation that supports grouping of validators. + + +

    + This validator will be valid when one and only one of the validators in the Validators collection are valid +

    +

    + ValidationErrors property will return a union of all validation error messages + for the contained validators, but only if this validator is not valid (meaning, when none + of the contained validators are valid). +

    +
    + Aleksandar Seovic + $Id: ExclusiveValidatorGroup.cs,v 1.8 2008/02/05 20:40:26 aseovic Exp $ +
    + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The expression that determines if this validator should be evaluated. + + + + Initializes a new instance of the class. + + The expression that determines if this validator should be evaluated. + + + + Validates the specified object. + + The object to validate. + Additional context parameters. + instance to add error messages to. + True if validation was successful, False otherwise. + + + + Implementation of the custom configuration parser for validator definitions. + + Aleksandar Seovic + $Id: ValidationNamespaceParser.cs,v 1.2 2007/08/08 00:34:17 bbaia Exp $ + + + + Initializes a new instance of the class. + + + + + Parse the specified element and register any resulting + IObjectDefinitions with the IObjectDefinitionRegistry that is + embedded in the supplied ParserContext. + + The element to be parsed into one or more IObjectDefinitions + The object encapsulating the current state of the parsing + process. + + The primary IObjectDefinition (can be null as explained above) + + + Implementations should return the primary IObjectDefinition + that results from the parse phase if they wish to used nested + inside (for example) a <property> tag. + Implementations may return null if they will not + be used in a nested scenario. + + + + + + Parses the validator definition. + + Validator's identifier. + The element to parse. + The parser helper. + Validator object definition. + + + + Parses and potentially registers a validator. + + + Only validators that have id attribute specified are registered + as separate object definitions within application context. + + Validator XML element. + The parser helper. + Validator object definition. + + + + Gets the name of the object type for the specified element. + + The element. + The name of the object type. + + + + Creates an error message action based on the specified message element. + + The message element. + The parser helper. + The error message action definition. + + + + Creates a generic action based on the specified element. + + The action definition element. + The parser helper. + Generic validation action definition. + + + + Creates object definition for the validator reference. + + The action definition element. + The parser helper. + Generic validation action definition. + + + + Implementation of IProxyMethodBuilder that delegates method calls to target object. + + Bruno Baia + $Id: TargetProxyMethodBuilder.cs,v 1.1 2006/08/10 18:45:55 bbaia Exp $ + + + + Creates a new instance of the method builder. + + The type builder to use. + + The implementation to use. + + + if the interface is to be + implemented explicitly; otherwise . + + + + + Generates the proxy method. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + This attribute can be used to mark interfaces that should not be proxied + + Bruno Baia + $Id: ProxyIgnoreAttribute.cs,v 1.1 2006/11/16 02:30:50 bbaia Exp $ + + + + Creates a new instance of the + class. + + + + + Describes an event handler for a static class method. + + Rick Evans + $Id: StaticEventHandlerValue.cs,v 1.5 2006/04/09 07:19:00 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The object (possibly unresolved) that is exposing the event. + + + The name of the method on the handler that is going to handle the event. + + + + + Gets the event handler. + + + The instance that is registering for the event notification. + + + Event metadata about the event. + + + The event handler. + + + + + Simple factory for shared instances. + + Juergen Hoeller + Simon White (.NET) + $Id: ListFactoryObject.cs,v 1.8 2007/07/31 03:47:39 markpollack Exp $ + + + + Constructs a new instance of the target dictionary. + + The new instance. + + + + Set the source . + + +

    + This value will be used to populate the + returned by this factory. +

    +
    +
    + + + Set the of the + implementation to use. + + +

    + The default is the . +

    +
    +
    + + + The of objects created by this factory. + + + Always returns the . + + + + + Holder for constructor argument values for an object. + + +

    + Supports values for a specific index or parameter name (case + insensitive) in the constructor argument list, and generic matches by + . +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: ConstructorArgumentValues.cs,v 1.16 2008/03/03 09:29:07 bbaia Exp $ + +
    + + + Can be used as an argument filler for the + + overload when one is not looking for an argument by index. + + + + + Creates a new instance of the + + class. + + + + + Creates a new instance of the + + class. + + + The + to be used to populate this instance. + + + + + Copy all given argument values into this object. + + + The + to be used to populate this instance. + + + + + Add argument value for the given index in the constructor argument list. + + + The index in the constructor argument list. + + + The argument value. + + + + + Add argument value for the given index in the constructor argument list. + + The index in the constructor argument list. + The argument value. + + The of the argument + . + + + + + Add argument value for the given name in the constructor argument list. + + The name in the constructor argument list. + The argument value. + + If the supplied is + or is composed wholly of whitespace. + + + + + Get argument value for the given index in the constructor argument list. + + The index in the constructor argument list. + + The required of the argument. + + + The + + for the argument, or if none set. + + + + + Get argument value for the given name in the constructor argument list. + + The name in the constructor argument list. + + The + + for the argument, or if none set. + + + + + Does this set of constructor arguments contain a named argument matching the + supplied name? + + + + The comparison is performed in a case-insensitive fashion. + + + The named argument to look up. + + if this set of constructor arguments + contains a named argument matching the supplied + name. + + + + + Add generic argument value to be matched by type. + + + The argument value. + + + + + Add generic argument value to be matched by type. + + The argument value. + + The of the argument + . + + + + + Look for a generic argument value that matches the given + . + + + The to match. + + + The + + for the argument, or if none set. + + + + + Look for a generic argument value that matches the given + . + + + The to match. + + + A of + + objects that have already been used in the current resolution + process and should therefore not be returned again; this allows one + to return the next generic argument match in the case of multiple + generic argument values of the same type. + + + The + + for the argument, or if none set. + + + + + Look for an argument value that either corresponds to the given index + in the constructor argument list or generically matches by + . + + + The index in the constructor argument list. + + + The to match. + + + The + + for the argument, or if none is set. + + + + + Look for an argument value that either corresponds to the given index + in the constructor argument list or generically matches by + . + + + The index in the constructor argument list. + + + The to match. + + + A of + + objects that have already been used in the current resolution + process and should therefore not be returned again; this allows one + to return the next generic argument match in the case of multiple + generic argument values of the same type. + + + The + + for the argument, or if none is set. + + + + + Look for an argument value that either corresponds to the given index + in the constructor argument list or generically matches by + . + + + The name of the argument in the constructor argument list. May be + , in which case generic matching by + is assumed. + + + The to match. + + + The + + for the argument, or if none is set. + + + + + Look for an argument value that either corresponds to the given index + in the constructor argument list or generically matches by + . + + + The name of the argument in the constructor argument list. May be + , in which case generic matching by + is assumed. + + + The to match. + + + A of + + objects that have already been used in the current resolution + process and should therefore not be returned again; this allows one + to return the next generic argument match in the case of multiple + generic argument values of the same type. + + + The + + for the argument, or if none is set. + + + + + Look for an argument value that either corresponds to the given index + in the constructor argument list, or to the named argument, or + generically matches by . + + + The index of the argument in the constructor argument list. May be + negative, to denote the fact that we are not looking for an + argument by index (see + . + + + The name of the argument in the constructor argument list. May be + . + + + The to match. + + + A of + + objects that have already been used in the current resolution + process and should therefore not be returned again; this allows one + to return the next generic argument match in the case of multiple + generic argument values of the same type. + + + The + + for the argument, or if none is set. + + + + + Return the map of indexed argument values. + + + An with + indices as keys and + s + as values. + + + + + Return the map of named argument values. + + + An with + named arguments as keys and + s + as values. + + + + + Return the set of generic argument values. + + + A of + s. + + + + + Return the number of arguments held in this instance. + + + + + Returns true if this holder does not contain any argument values, + neither indexed ones nor generic ones. + + + + + Holder for a constructor argument value, with an optional + attribute indicating the target + of the actual constructor argument. + + + + + Creates a new instance of the ValueHolder class. + + + The value of the constructor argument. + + + + + Creates a new instance of the ValueHolder class. + + + The value of the constructor argument. + + + The of the argument + . Can also be one of the common + aliases (int, bool, + float, etc). + + + + + A that represents the current + . + + + A that represents the current + . + + + + + Gets and sets the value for the constructor argument. + + +

    + Only necessary for manipulating a registered value, for example in + s. +

    +
    +
    + + + Return the of the constructor + argument. + + + + + Creates an instance + populated with the object definitions supplied in the configuration + section. + + +

    + Applications will typically want to use an + , and instantiate it + via the use of the + class (which is similar in functionality to this class). This class is + provided for those times when only an + is required. +

    + Creates an instance of the class XmlObjectFactory +
    + +

    + +

    +
    + Mark Pollack (.NET) + $Id: ObjectFactorySectionHandler.cs,v 1.3 2007/08/08 17:47:13 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a + instance populated with the object definitions supplied in the + configuration section. + + + The configuration settings in a corresponding parent configuration + section. + + + The configuration context when called from the ASP.NET + configuration system. Otherwise, this parameter is reserved and + is . + + + The for the section. + + + A instance + populated with the object definitions supplied in the configuration + section. + + + + + Immutable placeholder class used for the value of a + object when it's a reference + to a Spring that should be evaluated at runtime. + + Aleksandar Seovic + $Id: ExpressionHolder.cs,v 1.5 2007/08/21 19:28:33 markpollack Exp $ + + + + Creates a new instance of the + + class. + + The expression to resolve. + + + + Returns a string representation of this instance. + + A string representation of this instance. + + + + Gets or sets the expression string. Setting the expression string will cause + the expression to be parsed. + + The expression string. + + + + Return the expression. + + + + + Properties for this expression node. + + + + + implementation + that simply returns the + value of the + + property (if said property value is not ), or the + of the current thread if it is + . + + Aleksandar Seovic + $Id: DefaultCultureResolver.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + + + + Returns the default . + + +

    + It tries to get the + from the value of the + + property and falls back to the of the + current thread if the + + is . +

    +
    + + The default + +
    + + + Resolves the + from some context. + + +

    + The 'context' in this implementation is the + value of the + + property (if said property value is not ), or the + of the current thread if it is + . +

    +
    + + The that should be used + by the caller. + +
    + + + Sets the . + + + The new or + to clear the current . + + + + + + + The default . + + + The default . + + + + + Resource cache implementation that doesn't cache resources. + + Aleksandar Seovic + $Id: NullResourceCache.cs,v 1.2 2006/04/09 07:18:47 markpollack Exp $ + + + + Gets the list of resources from cache. + + Cache key to use for lookup. + Always returns null. + + + + Puts the list of resources in the cache. + + Cache key to use for the specified resources. + A list of resources to cache. + + + + Represents logical AND operator. + + Aleksandar Seovic + $Id: OpAND.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical AND operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents logical IS operator. + + Aleksandar Seovic + $Id: OpIs.cs,v 1.4 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical IS operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + + true if the left operand is contained within the right operand, false otherwise. + + + + + adapter implementation for a + . + + +

    + Should only be used if no other + implementation is applicable. +

    +

    + In contrast to other + implementations, this is an adapter for an already opened + resource - the + therefore always returns . Do not use this class + if you need to keep the resource descriptor somewhere, or if you need + to read a stream multiple times. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: InputStreamResource.cs,v 1.10 2007/12/06 22:08:47 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + The input to use. + + + Where the input comes from. + + + If the supplied is + . + + + + + The input to use. + + + If the underlying has already + been read. + + + + + Returns a description for this resource. + + + A description for this resource. + + + + + + This implementation always returns true + + + + + This implemementation always returns true + + + + + An implementation for + resources stored within assemblies. + + +

    + This implementation expects any resource name passed to the + constructor to adhere to the following format: +

    +

    + assembly://assemblyName/namespace/resourceName +

    +
    + Aleksandar Seovic (.NET) + Federico Spinazzi (.NET) + $Id: AssemblyResource.cs,v 1.16 2007/08/08 17:46:55 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + + The name of the assembly resource. + + + If the supplied did not conform + to the expected format. + + + If the assembly specified in the supplied + was loaded twice with two + different evidences. + + + If the assembly specified in the supplied + could not be found. + + + If the caller does not have the required permission to load + the assembly specified in the supplied + . + + + + + + Does the supplied relative ? + + + The name of the resource to test. + + + if resource name is relative; + otherwise . + + + + + Return an for this resource. + + + An . + + + If the stream could not be opened. + + + If the caller does not have the required permission to load + the underlying assembly's manifest. + + + + + + + Does the embedded resource specified in the value passed to the + constructor exist? + + + if this resource actually exists in physical + form (for example on a filesystem). + + + + + + + + Does this support relative + resource retrieval? + + +

    + This implementation does support relative resource retrieval, and + so will always return . +

    +
    + + if this + supports relative resource + retrieval. + + +
    + + + Gets the root location of the resource (the assembly name in this + case). + + + The root location of the resource. + + + + + + Gets the current path of the resource (the namespace in which the + target resource was embedded in this case). + + + The current path of the resource. + + + + + + Gets those characters that are valid path separators for the + resource type. + + + Those characters that are valid path separators for the resource + type. + + + + + + Returns a description for this resource. + + + A description for this resource. + + + + + +

    Base class for counting semaphores based on Semaphore implementation + from Doug Lea.

    +
    + + +

    Conceptually, a semaphore + maintains a set of permits. Each acquire() blocks if + necessary until a permit is available, and then takes it.

    + +

    Each release adds a permit. However, no actual permit objects are used; + the Semaphore just keeps a count of the number available + and acts accordingly.

    + +

    A semaphore initialized to 1 can serve as a mutual exclusion lock.

    + + Used for implementation of a +
    + Doug Lea + Federico Spinazzi (.Net) + $Id: Semaphore.cs,v 1.10 2006/09/15 21:30:09 markpollack Exp $ +
    + + + current number of available permits + + + + +

    Create a Semaphore with the given initial number of permits.

    +

    Using a seed of 1 makes the semaphore act as a mutual + exclusion lock.

    + +

    Negative seeds are also allowed, + in which case no acquires will proceed until the number of + releases has pushed the number of permits past 0.

    +
    +
    + + + Release a permit + + + + + Acquire a permit + + + + + Wait at most msecs millisconds for a permit + + number of ms to wait + true if aquired + + + Release N permits. release(n) is + equivalent in effect to: +
    +            for (int i = 0; i < n; ++i) release();
    +            
    + But may be more efficient in some semaphore implementations. +
    + if n is negative. + + +
    + + Return the current number of available permits. + Returns an accurate, but possibly unstable value, + that may change immediately after returning. + + + + + An abstraction to safely store "ThreadStatic" data. + + + By default, is used to store thread-specific data. + You may switch the storage strategy by calling .

    + NOTE: Access to the underlying storage is not synchronized for performance reasons. + You should call only once at application startup! + + Erich Eichinger + $Id: LogicalThreadContext.cs,v 1.3 2007/07/03 22:32:04 markpollack Exp $ + + +

    + Holds the current strategy. + + + Access to this variable is not synchronized on purpose for performance reasons. + Setting a different strategy should happen only once + at application startup. + +
    + + + Set the new strategy. + + + + + Retrieves an object with the specified name. + + The name of the item. + The object in the context associated with the specified name or null if no object has been stored previously + + + + Stores a given object and associates it with the specified name. + + The name with which to associate the new item. + The object to store in the current thread's context. + + + + Empties a data slot with the specified name. + + The name of the data slot to empty. + + + + Provides a resolution mechanism for configuration parsers. + + +

    + The uses this registry + class to find the parser handling a specific namespace. +

    +
    + Aleksandar Seovic + $Id: NamespaceParserRegistry.cs,v 1.7 2007/09/17 01:12:48 bbaia Exp $ +
    + + + Name of the .Net config section that contains definitions + for custom config parsers. + + + + + Creates a new instance of the NamespaceParserRegistry class. + + + + + Returns a parser for the given namespace. + + + The namespace for which to lookup the parser implementation. + + + A parser for a given , or + if no parser was found. + + + + + Returns a schema collection containing validation schemas for all registered parsers. + + + A schema collection containing validation schemas for all registered parsers. + + + + + Pegisters parser, using default namespace and schema location + as defined by the . + + + The of the parser that will be activated + when an element in its default namespace is encountered. + + + If is . + + + + + Associates a parser with a namespace. + + + + Parsers registered with the same as that + of a parser that has previously been registered will overwrite the existing + parser. + + + + The of the parser that will be activated + when the attendant is + encountered. + + + The namespace with which to associate instance of the parser. + + + The location of the XML schema that should be used for validation + of the XML elements that belong to the specified namespace + (can be any valid Spring.NET resource URI). + + + If the is not a + that implements the + interface. + + + If is . + + + + + Pegisters parser, using default namespace and schema location + as defined by the . + + + The parser instance. + + + If is . + + + + + Associates a parser with a namespace. + + + + Parsers registered with the same as that + of a parser that has previously been registered will overwrite the existing + parser. + + + + The namespace with which to associate instance of the parser. + + + The parser instance. + + + The location of the XML schema that should be used for validation + of the XML elements that belong to the specified namespace + (can be any valid Spring.NET resource URI). + + + If is , or if + is not specified and parser class + does not have default value defined using . + + + + + Returns default values for the parser namespace and schema location as + defined by the . + + + A parser instance. + + + A instance containing + default values for the parser namsepace and schema location + + + + + Convenience extension of + + that reads object definitions from an XML document or element. + + +

    + Delegates to + + underneath; effectively equivalent to using a + for a + . +

    + + objects doesn't need to be the root element of + the XML document: this class will parse all object definition elements in the + XML stream. + +

    + This class registers each object definition with the + + superclass, and relies on the latter's implementation of the + interface. It supports + singletons, prototypes and references to either of these kinds of object. +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: XmlObjectFactory.cs,v 1.13 2007/08/08 17:47:13 bbaia Exp $ + +
    + + + Concrete implementation of the + and + + interfaces. + + +

    + This class is a full-fledged object factory based on object definitions + that is usable straight out of the box. +

    +

    + Can be used as an object factory in and of itself, or as a superclass + for custom object factory implementations. Note that readers for + specific object definition formats are typically implemented separately + rather than as object factory subclasses. +

    +

    + For an alternative implementation of the + interface, + have a look at the + + class, which manages existing object instances rather than creating new + ones based on object definitions. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + + $Id: DefaultListableObjectFactory.cs,v 1.42 2007/10/10 19:17:07 bbaia Exp $ +
    + + + Abstract superclass + that implements default object creation. + + +

    + Provides object creation, initialization and wiring, supporting + autowiring and constructor resolution. Handles runtime object + references, managed collections, and object destruction. +

    +

    + The main template method to be implemented by subclasses is + , + used for autowiring by type. Note that this class does not implement object + definition registry capabilities + ( + does). +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: AbstractAutowireCapableObjectFactory.cs,v 1.89 2008/05/05 22:42:17 markpollack Exp $ +
    + + + The used during the invocation and + searching for of methods. + + + + + The instance for this class. + + + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    + Flag specifying whether to make this object factory case sensitive or not. +
    + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    + Flag specifying whether to make this object factory case sensitive or not. + The parent object factory, or if none. +
    + + + Predict the eventual object type (of the processed object instance) for the + specified object. + + Name of the object. + The merged object definition to determine the type for. + + The type of the object, or null if not predictable + + + + + Determines the of the object defined + by the supplied object . + + + The name associated with the supplied object . + + + The + that the is to be determined for. + + + The of the object defined by the supplied + object ; or if the + cannot be determined. + + + + + Apply the property values of the object definition with the supplied + to the supplied . + + + The existing object that the property values for the named object will + be applied to. + + + The name of the object definition associated with the property values that are + to be applied. + + + + + Apply any + s. + + +

    + The returned instance may be a wrapper around the original. +

    +
    + + The of the object that is to be + instantiated. + + + The name of the object that is to be instantiated. + + + An instance to use in place of the original instance. + + + In case of errors. + +
    + + + Apply the given property values, resolving any runtime references + to other objects in this object factory. + + + The object name passed for better exception information. + + + The definition of the named object. + + + The wrapping the target object. + + + The new property values. + + +

    + Must use deep copy, so that we don't permanently modify this property. +

    +
    +
    + + + Return an array of object-type property names that are unsatisfied. + + +

    + These are probably unsatisfied references to other objects in the + factory. Does not include simple properties like primitives or + s. +

    +
    + + An array of object-type property names that are unsatisfied. + + + The definition of the named object. + + + The wrapping the target object. + +
    + + + Destroy all cached singletons in this factory. + + +

    + To be called on shutdown of a factory. +

    +
    +
    + + + Populate the object instance in the given + with the property values from the + object definition. + + + The name of the object. + + + The definition of the named object. + + + The wrapping the target object. + + + + + Wires up any exposed events in the object instance in the given + with any event handler + values from the . + + + The name of the object. + + + The definition of the named object. + + + The wrapping the target object. + + + + + Fills in any missing property values with references to + other objects in this factory if autowire is set to + . + + + The object name to be autowired by . + + + The definition of the named object to update through autowiring. + + + The wrapping the target object (and + from which we can rip out information concerning the object). + + + The property values to register wired objects with. + + + + + Defines "autowire by type" (object properties by type) behavior. + + +

    + This is like PicoContainer default, in which there must be exactly one object + of the property type in the object factory. This makes object factories simple + to configure for small namespaces, but doesn't work as well as standard Spring + behavior for bigger applications. +

    +
    + + The object name to be autowired by . + + + The definition of the named object to update through autowiring. + + + The wrapping the target object (and + from which we can rip out information concerning the object). + + + The property values to register wired objects with. + +
    + + + Ignore the given dependency type for autowiring + + + This will typically be used by application contexts to register + dependencies that are resolved in other ways, like IOjbectFactory through + IObjectFactoryAware or IApplicationContext through IApplicationContextAware. + By default, IObjectFactoryAware and IObjectName interfaces are ignored. + For further types to ignore, invoke this method for each type. + + . + + + + Create an object instance for the given object definition. + + The name of the object. + + The object definition for the object that is to be instantiated. + + + The arguments to use if creating a prototype using explicit arguments to + a static factory method. It is invalid to use a non- arguments value + in any other case. + + + A new instance of the object. + + + In case of errors. + + +

    + Delegates to the + + method version with the allowEagerCaching parameter set to true. +

    +

    + The object definition will already have been merged with the parent + definition in case of a child definition. +

    +

    + All the other methods in this class invoke this method, although objects + may be cached after being instantiated by this method. All object + instantiation within this class is performed by this method. +

    +
    +
    + + + Create an object instance for the given object definition. + + The name of the object. + + The object definition for the object that is to be instantiated. + + + The arguments to use if creating a prototype using explicit arguments to + a static factory method. It is invalid to use a non- arguments value + in any other case. + + + Whether eager caching of singletons is allowed... typically true for + singlton objects, but never true for inner object definitions. + + + A new instance of the object. + + + In case of errors. + + +

    + The object definition will already have been merged with the parent + definition in case of a child definition. +

    +

    + All the other methods in this class invoke this method, although objects + may be cached after being instantiated by this method. All object + instantiation within this class is performed by this method. +

    +
    +
    + + + Creates an instance from the passed in + using constructor + + The name of the object to create - used for error messages. + The describing the object to be created. + optional arguments to pass to the constructor + An wrapping the already instantiated object + + + + Instantiate an object instance using a named factory method. + + +

    + The method may be static, if the + parameter specifies a class, rather than a + instance, or an + instance variable on a factory object itself configured using Dependency + Injection. +

    +

    + Implementation requires iterating over the static or instance methods + with the name specified in the supplied + (the method may be overloaded) and trying to match with the parameters. + We don't have the types attached to constructor args, so trial and error + is the only way to go here. +

    +
    + + The name associated with the supplied . + + + The definition describing the instance that is to be instantiated. + + + Any arguments to the factory method that is to be invoked. + + + The result of the factory method invocation (the instance). + +
    + + + Returns an array of all of those + methods exposed on the + that match the supplied criteria. + + + Methods that have this name (can be in the form of a regular expression). + + + Methods that have exactly this many arguments. + + + Methods that are static / instance. + + + The on which the methods (if any) are to be found. + + + An array of all of those + methods exposed on the + that match the supplied criteria. + + + + + Create an array of arguments to invoke a constructor or static factory method, + given the resolved constructor arguments values. + + When return value is null the out parameter UnsatisfiedDependencyExceptionData will contain + information for use in throwing a UnsatisfiedDependencyException by the caller. This avoids using + exceptions for flow control as in the original implementation. + + + + Explicitly construct the object using the supplied constructor arguments. + Constructor arguments are matched by type. + + + The name of the object to autowire by type. + + + The object definition to update through autowiring. + + Array of constructor argument values. + + An for the new instance. + + + + + "autowire constructor" (with constructor arguments by type) behaviour. + + Passes an empty collection of constructor argument values + to overloaded method. + + + The name of the object to autowire by type. + + + The object definition to update through autowiring. + + + An for the new instance. + + + + + "autowire constructor" (with constructor arguments by type) behaviour. + + +

    + Also applied if explicit constructor argument values are specified, + matching all remaining arguments with objects from the object factory. +

    +

    + This corresponds to constructor injection: in this mode, a Spring.NET + object factory is able to host components that expect constructor-based + dependency resolution. +

    +
    + + The name of the object to autowire by type. + + + The object definition to update through autowiring. + + + The collection on constructor argument values. + + + An for the new instance. + +
    + + + Resolves the + of the supplied . + + +

    + 'Resolve' can be taken to mean that all of the s + constructor arguments is resolved into a concrete object that can be plugged + into one of the s constructors. Runtime object + references to other objects in this (or a parent) factory are resolved, + type conversion is performed, etc. +

    +

    + These resolved values are plugged into the supplied + object, because we wouldn't want to touch + the s constructor arguments in case it (or any of + its constructor arguments) is a prototype object definition. +

    +

    + This method is also used for handling invocations of static factory methods. +

    +
    + + The name of the object that is being resolved by this factory. + + + The definition associated with the above . + + + Where the resolved constructor arguments will be placed. + + + The minimum number of arguments that any constructor for the supplied + must have. + +
    + + + Perform a dependency check that all properties exposed have been set, if desired. + + +

    + Dependency checks can be objects (collaborating objects), simple (primitives + and ), or all (both). +

    +
    + + The name of the object. + + + The definition of the named object. + + + The wrapping the target object. + + + The property values to be checked. + + + If all of the checked dependencies were not satisfied. + +
    + + + Extract a filtered set of PropertyInfos from the given IObjectWrapper, excluding + ignored dependency types. + + The object wrapper the object was created with. + The filtered PropertyInfos + + + + Give an object a chance to react now all its properties are set, + and a chance to know about its owning object factory (this object). + + +

    + This means checking whether the object implements + and / or + , and invoking the + necessary callback(s) if it does. +

    +

    + Custom init methods are resolved in a case-insensitive manner. +

    +
    + + The new object instance we may need to initialise. + + + The name the object has in the factory. Used for logging output. + + + The definition of the target object instance. + +
    + + + Invoke the specified custom destroy method on the given object. + + +

    + This implementation invokes a no-arg method if found, else checking + for a method with a single boolean argument (passing in "true", + assuming a "force" parameter), else logging an error. +

    +

    + Can be overridden in subclasses for custom resolution of destroy + methods with arguments. +

    +

    + Custom destroy methods are resolved in a case-insensitive manner. +

    +
    +
    + + + Destroy the target object. + + +

    + Must destroy objects that depend on the given object before the object itself. + Should not throw any exceptions. +

    +
    + + The name of the object. + + + The target object instance to destroyed. + +
    + + + Destroys all of the objects registered as dependant on the + object (definition) identified by the supplied . + + + The name of the root object (definition) that is itself being destroyed. + + + + + Given a property value, return a value, resolving any references to other + objects in the factory if necessary. + + +

    + The value could be : + + +

    + An , + which leads to the creation of a corresponding new object instance. + Singleton flags and names of such "inner objects" are always ignored: inner objects + are anonymous prototypes. +

    + + +

    + A , which must + be resolved. +

    +
    + +

    + An . This is a + special placeholder collection that may contain + s or + collections that will need to be resolved. +

    +
    + +

    + An ordinary object or , in which case it's left alone. +

    +
    + +

    +
    + + The name of the object that is having the value of one of its properties resolved. + + + The definition of the named object. + + + The name of the property the value of which is being resolved. + + + The value of the property that is being resolved. + +
    + + + Resolve the target type of the passed . + + The who's target type is to be resolved + The resolved target type, if any. otherwise. + + + + Resolves an inner object definition. + + + The name of the object that surrounds this inner object definition. + + + The name of the inner object definition... note: this is a synthetic + name assigned by the factory (since it makes no sense for inner object + definitions to have names). + + + The name of the property the value of which is being resolved. + + + The definition of the inner object that is to be resolved. + + + if the owner of the property is a singleton. + + + The resolved object as defined by the inner object definition. + + + + + Resolve a reference to another object in the factory. + + + The name of the object that is having the value of one of its properties resolved. + + + The definition of the named object. + + + The name of the property the value of which is being resolved. + + + The runtime reference containing the value of the property. + + A reference to another object in the factory. + + + + Find object instances that match the required . + + +

    + Called by autowiring. If a subclass cannot obtain information about object + names by , a corresponding exception should be thrown. +

    +
    + + The of the objects to look up. + + + An of object names and object + instances that match the required , or + if none are found. + + + In case of errors. + +
    + + + Return the names of the objects that depend on the given object. + Called by DestroyObject, to be able to destroy depending objects first. + + + The name of the object to find depending objects for. + + + The array of names of depending objects, or the empty string array if none. + + + In case of errors. + + + + + Injects dependencies into the supplied instance + using the named object definition. + + + The object instance that is to be so configured. + + + The name of the object definition expressing the dependencies that are to + be injected into the supplied instance. + + + + + + Injects dependencies into the supplied instance + using the supplied . + + + The object instance that is to be so configured. + + + The name of the object definition expressing the dependencies that are to + be injected into the supplied instance. + + + An object definition that should be used to configure object. + + + + + + Configures object instance by injecting dependencies, satisfying Spring lifecycle + interfaces and applying object post-processors. + + + The name of the object definition expressing the dependencies that are to + be injected into the supplied instance. + + + An object definition that should be used to configure object. + + + A wrapped object instance that is to be so configured. + + + + + + Applies the PostProcessAfterInitialization callback of all + registered IObjectPostProcessors, giving them a chance to post-process + the object obtained from IFactoryObjects (for example, to auto-proxy them) + + The instance obtained from the IFactoryObject. + Name of the object. + The object instance to expose + if any post-processing failed. + + + + Create a new object instance of the given class with the specified + autowire strategy. + + + The of the object to instantiate. + + + The desired autowiring mode. + + + Whether to perform a dependency check for objects (not applicable to + autowiring a constructor, thus ignored there). + + The new object instance. + + If the wiring fails. + + + + + + Autowire the object properties of the given object instance by name or + . + + + The existing object instance. + + + The desired autowiring mode. + + + Whether to perform a dependency check for the object. + + + If the wiring fails. + + + If the supplied is not one of the + or + + values. + + + + + + Apply s + to the given existing object instance, invoking their + + methods. + + + The existing object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + If any post-processing failed. + + + + + + Apply s + to the given existing object instance, invoking their + + methods. + + + The existing object instance. + + + The name of the object. + + + The object instance to use, either the original or a wrapped one. + + + If any post-processing failed. + + + + + + Set that holds all inner objects created by this factory that implement the IDisposable + interface, to be destroyed on call to Dispose. + + + + + Cache of unfinished IFactoryObject instances: IFactoryObject name --> IObjectWrapper */ + + + + + Cache of filtered PropertyInfos: object Type -> PropertyInfo array + + + + + Dependency interfaces to ignore on dependency check and autowire, as Set of + Class objects. By default, only the IObjectFactoryAware and IObjectNameAware + interfaces are ignored. + + + + + The + implementation to be used to instantiate managed objects. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + Flag specifying whether to make this object factory case sensitive or not. + + + + Creates a new instance of the + class. + + The parent object factory. + + + + Creates a new instance of the + class. + + Flag specifying whether to make this object factory case sensitive or not. + The parent object factory. + + + + Find object instances that match the . + + +

    + Called by autowiring. If a subclass cannot obtain information about object + names by , a corresponding exception should be thrown. +

    +
    + + The type of the objects to look up. + + + An of object names and object + instances that match the , or + if none is found. + + + In case of errors. + +
    + + + Return the names of the objects that depend on the given object. + + +

    + Called by the + + so that dependant objects are able to be disposed of first. +

    +
    + + The name of the object to find depending objects for. + + + The array of names of depending objects, or the empty string array if none. + + + In case of errors. + +
    + + + Check whether the specified object matches the supplied . + + The name of the object to check. + + The to check for. + + + if the object matches the supplied , + or if the supplied is . + + + + + The instance for this class. + + + + + Whether to allow re-registration of a different definition with the + same name. + + + + + The mapping of object definition objects, keyed by object name. + + + + + List of object definition names, in registration order. + + + + + Check if this registry contains a object definition with the given + name. + + + The name of the object to look for. + + + if this object factory contains an object + definition with the given name. + + + + + + Register a new object definition with this registry. + + + The name of the object instance to register. + + + The definition of the object instance to register. + + + If the object definition is invalid. + + + + + + Ensure that all non-lazy-init singletons are instantiated, also + considering s. + + + If one of the singleton objects could not be created. + + + + + + Return the registered + for the + given object, allowing access to its property values and constructor + argument values. + + The name of the object. + + The registered , + or null, if specified object definitions does not exist. + + + If is null or empty string. + + + + + + Return the registered + for the + given object, allowing access to its property values and constructor + argument values. + + The name of the object. + Whether to search parent object factories. + + The registered , + or null, if specified object definitions does not exist. + + + If is null or empty string. + + + + + + Return the names of all objects defined in this factory. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + + + + + + Return the names of objects matching the given + (including subclasses), judging from the object definitions. + + + The (class or interface) to match, or + for all object names. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + + + + + + Return the names of objects matching the given + (including subclasses), judging from the object definitions. + + + The (class or interface) to match, or + for all object names. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + + + + + + Return the names of objects matching the given + (including subclasses), judging from the object definitions. + + + The (class or interface) to match, or + for all object names. + + + Whether to include prototype objects too or just singletons (also applies to + s). + + + Whether to include s too + or just normal objects. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + + + + + + Return the object instances that match the given object + (including subclasses), judging from either object + definitions or the value of + in the case of + s. + + + The (class or interface) to match. + + + A of the matching objects, + containing the object names as keys and the corresponding object instances + as values. + + + If the objects could not be created. + + + + + + Return the object instances that match the given object + (including subclasses). + + + The (class or interface) to match. + + + Whether to include prototype objects too or just singletons (also applies to + s). + + + Whether to include s too + or just normal objects. + + + An of the matching objects, + containing the object names as keys and the corresponding object instances + as values. + + + If any of the objects could not be created. + + + + + + Return the object instances that match the given object + (including subclasses). + + + The (class or interface) to match. + + + Whether to include prototype objects too or just singletons (also applies to + s). + + + Whether to include s too + or just normal objects. + + + An of the matching objects, + containing the object names as keys and the corresponding object instances + as values. + + + If any of the objects could not be created. + + + + + + Should object definitions registered under the same name as an + existing object definition be allowed? + + +

    + If , then the new object definition will + replace (override) the existing object definition. If + , an exception will be thrown when + an attempt is made to register an object definition under the same + name as an already existing object definition. +

    +

    + The default is . +

    +
    + + is the registration of an object definition + under the same name as an existing object definition is allowed. + +
    + + + Return the number of objects defined in this registry. + + + The number of objects defined in this registry. + + + + + + Creates a new instance of the class, + with the given resource, which must be parsable using DOM. + + + The XML resource to load object definitions from. + + + In the case of loading or parsing errors. + + + + + Creates a new instance of the class, + with the given resource, which must be parsable using DOM. + + + The XML resource to load object definitions from. + + Flag specifying whether to make this object factory case sensitive or not. + + In the case of loading or parsing errors. + + + + + Creates a new instance of the class, + with the given resource, which must be parsable using DOM, and the + given parent factory. + + + The XML resource to load object definitions from. + + The parent object factory (may be ). + + In the case of loading or parsing errors. + + + + + Creates a new instance of the class, + with the given resource, which must be parsable using DOM, and the + given parent factory. + + + The XML resource to load object definitions from. + + Flag specifying whether to make this object factory case sensitive or not. + The parent object factory (may be ). + + In the case of loading or parsing errors. + + + + + Gets object definition reader to use. + + + + + Constants defining the structure and values associated with the + Spring.NET XML object definition format. + + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: ObjectDefinitionConstants.cs,v 1.8 2006/11/17 20:41:39 aseovic Exp $ + + + + Value of a boolean attribute that represents + . + + +

    + Anything else represents . +

    +
    +
    + + + Signifies that a default value is to be applied. + + + + + Defines an external XML object definition resource. + + + + + Specifies the relative path to an external XML object definition + resource. + + + + + Defines an alias for an object definition. + + + + + Specifies the alias of an object definition. + + + + + Specifies the default lazy initialization mode. + + + + + Specifies the default dependency checking mode. + + + + + Specifies the default autowire mode. + + + + + Defines a single named object. + + + + + Element containing informative text describing the purpose of the + enclosing element. + + +

    + Always optional. +

    +

    + Used primarily for user documentation of XML object definition + documents. +

    +
    +
    + + + Specifies a . + + +

    + Does not have to be fully assembly qualified, but it is recommended + that the names of one's objects are + specified explicitly. +

    +
    +
    + + + The name or alias of the parent object definition that a child + object definition inherits from. + + + + + Objects can be identified by an id, to enable reference checking. + + +

    + There are constraints on a valid XML id: if you want to reference + your object in .NET code using a name that's illegal as an XML id, + use the optional "name" attribute + (). + If neither given, the objects name is + used as id. +

    +
    +
    + + + Can be used to create one or more aliases illegal in an id. + + +

    + Multiple aliases can be separated by any number of spaces, + semicolons, or commas + (). +

    +

    + Always optional. +

    +
    +
    + + + Is this object a "singleton" (one shared instance, which will + be returned by all calls to + with the id), or a + "prototype" (independent instance resulting from each call to + ). + + +

    + Singletons are most commonly used, and are ideal for multi-threaded + service objects. +

    +
    + +
    + + + Controls object scope. Only applicable to ASP.NET web applications. + + +

    + Scope can be defined as either application, session or request. It + defines when "singleton" instances are initialized, but has no + effect on prototype definitions. +

    +
    +
    + + + The names of the objects that this object depends on being + initialized. + + +

    + The object factory will guarantee that these objects + get initialized before this object definition. +

    + + Dependencies are normally expressed through object properties or + constructor arguments. This property should just be necessary for + other kinds of dependencies such as statics (*ugh*) or database + preparation on startup. + +
    +
    + + + Optional attribute for the name of the custom initialization method + to invoke after setting object properties. + + +

    + The method must have no arguments. +

    +
    +
    + + + Optional attribute for the name of the custom destroy method to + invoke on object factory shutdown. + + +

    + Valid destroy methods have either of the following signatures... + + void MethodName() + void MethodName(bool force) + +

    + + Only invoked on singleton objects! + +
    +
    + + + A constructor argument : the constructor-arg tag can have an + optional type attribute, to specify the exact type of the + constructor argument + + +

    + Only needed to avoid ambiguities, e.g. in case of 2 single + argument constructors that can both be converted from a + . +

    +
    +
    + + + The constructor-arg tag can have an optional index attribute, + to specify the exact index in the constructor argument list. + + +

    + Only needed to avoid ambiguities, e.g. in case of 2 arguments of + the same type. +

    +
    +
    + + + The constructor-arg tag can have an optional named parameter + attribute, to specify a named parameter in the constructor + argument list. + + + + + Is this object "abstract", i.e. not meant to be instantiated itself + but rather just serving as parent for concrete child object + definitions? + + +

    + Default is . Specify + to tell the object factory to not try to instantiate that + particular object in any case. +

    +
    +
    + + + A property definition : object definitions can have zero or more + properties. + + +

    + Spring.NET supports primitives, references to other objects in the + same or related factories, lists, dictionaries, and name value + collections. +

    +
    +
    + + + A reference to another managed object or static + . + + + + + ID refs must specify a name of the target object. + + + + + A reference to the name of another managed object in the same + context. + + + + + A reference to the name of another managed object in the same + context. + + +

    + Local references, using the "local" attribute, have to use object + ids; they can be checked by a parser, thus should be preferred for + references within the same object factory XML file. +

    +
    +
    + + + Alternative to type attribute for factory-method usage. + + +

    + If this is specified, no type attribute should be used. This should + be set to the name of an object in the current or ancestor + factories that contains the relevant factory method. This allows + the factory itself to be configured using Dependency Injection, and + an instance (rather than static) method to be used. +

    +
    +
    + + + Optional attribute specifying the name of a factory method to use + to create this object. + + +

    + Use constructor-arg elements to specify arguments to the factory + method, if it takes arguments. Autowiring does not apply to + factory methods. +

    +

    + If the "type" attribute is present, the factory method will be a + static method on the type specified by the "type" attribute on + this object definition. Often this will be the same type as that + of the constructed object - for example, when the factory method + is used as an alternative to a constructor. However, it may be on + a different type. In that case, the created object will *not* be + of the type specified in the "type" attribute. This is analogous + to behaviour. +

    +

    + If the "factory-object" attribute is present, the "type" attribute + is not used, and the factory method will be an instance method on + the object returned from a + + call with the specified object name. The factory object may be + defined as a singleton or a prototype. +

    +

    + The factory method can have any number of arguments. Use indexed + constructor-arg elements in conjunction with the factory-method + attribute. +

    +

    + Setter Injection can be used in conjunction with a factory method. + Method Injection cannot, as the factory method returns an instance, + which will be used when the container creates the object. +

    +
    +
    + + + A list can contain multiple inner object, ref, collection, or + value elements. + + +

    + Lists are untyped, pending generics support, although references + will be strongly typed. +

    +

    + A list can also map to an array type. The necessary conversion is + automatically performed by the + . +

    +
    +
    + + + A set can contain multiple inner object, ref, collection, or value + elements. + + +

    + Sets are untyped, pending generics support, although references + will be strongly typed. +

    +
    +
    + + + A Spring.NET map is a mapping from a string key to object (a .NET + ). + + +

    + Dictionaries may be empty. +

    +
    +
    + + + A lookup key (for a dictionary or name / value collection). + + + + + A lookup key (for a dictionary or name / value collection). + + + + + Contains a string representation of a value. + + +

    + This is used by name-value, ctor argument, and property elements. +

    +
    +
    + + + Contains delimiters that should be used to split delimited string values. + + +

    + This is used by name-value element. +

    +
    +
    + + + A reference to another objects. + + +

    + Used as a convenience shortcut on property and constructor-arg + elements to refer to other objects. +

    +
    +
    + + + Contains a string representation of an expression. + + +

    + This is used by ctor argument and property elements. +

    +
    +
    + + + A map entry can be an inner object, ref, collection, or value. + + +

    + The name of the property is given by the "key" attribute. +

    +
    +
    + + + Contains a string representation of a property value. + + +

    + The property may be a string, or may be converted to the + required using the + + machinery. This makes it possible for application developers to + write custom + implementations that can convert strings to objects. +

    + + This is recommended for simple objects only. Configure more complex + objects by setting properties to references to other objects. + +
    +
    + + + Contains a string representation of an expression. + + + + + Denotes value. + + +

    + Necessary because an empty "value" tag will resolve to an empty + , which will not be resolved to + value unless a special + does so. +

    +
    +
    + + + 'name-values' elements differ from dictionary elements in that + values must be strings. + + +

    + May be empty. +

    +
    +
    + + + Element content is the string value of the property. + + +

    + The "key" attribute is the name of the property. +

    +
    +
    + + + The lazy initialization mode for an individual object definition. + + + + + The dependency checking mode for an individual object definition. + + + + + Defines a subscription to one or more events published by one or + more event sources. + + + + + The name of an event handling method. + + +

    + Defaults to On${event}. + Note : this default will probably change before the first 1.0 + release. +

    +
    +
    + + + The name of an event. + + + + + The autowiring mode for an individual object definition. + + + + + Shortcut alternative to specifying a key element in a + dictionary entry element with <ref object="..."/>. + + + + + Shortcut alternative to specifying a value element in a + dictionary entry element with <ref object="..."/>. + + + + + The string of characters that delimit object names. + + + + + A lookup method causes the IoC container to override a given method and return + the object with the name given in the attendant object attribute. + + +

    + This is a form of Method Injection. +

    +

    + It's particularly useful as an alternative to implementing the + interface, + in order to be able to make + + calls for non-singleton instances at runtime. In this case, Method Injection + is a less invasive alternative. +

    +
    +
    + + + The name of a lookup method. This method must take no arguments. + + + + + The name of the object in the IoC container that the lookup method + must resolve to. + + +

    + Often this object will be a prototype, in which case the lookup method + will return a distinct instance on every invocation. This is useful + for single-threaded objects. +

    +
    +
    + + + A replaced method causes the IoC container to override a given method + with an (arbitrary) implementation at runtime. + + +

    + This (again) is a form of Method Injection. +

    +
    +
    + + + Name of the method whose implementation should be replaced by the + IoC container. + + +

    + If this method is not overloaded, there's no need to use arg-type + subelements. +

    +

    + If this method is overloaded, arg-type subelements must be + used for all override definitions for the method. +

    +
    +
    + + + The object name of an implementation of the + interface. + + +

    + This may be a singleton or prototype. If it's a prototype, a new + instance will be used for each method replacement. Singleton usage + is the norm. +

    +
    +
    + + + Subelement of replaced-method identifying an argument for a + replaced method in the event of method overloading. + + + + + + Specification of the of an overloaded method + argument as a . + + +

    + For convenience, this may be a substring of the FQN. E.g. all the following would match + : +

    +

    + + + System.String + + + string + + + str + + +

    +
    + +
    + + + Check everything. + + + + + Just check primitive (string, int, etc) values. + + + + + Check object references. + + + + + Autowire by name. + + + + + Autowire by . + + + + + Autowiring by constructor. + + + + + The autowiring strategy is to be determined by introspection + of the object's . + + + + + Creates a new instance of the + + class. + + +

    + This is a utility class, and as such has no publicly visible + constructors. +

    +
    +
    + + + Static factory that permits the registration of existing singleton instances. + + +

    + Does not have support for prototype objects, aliases, and post startup object + configuration. +

    +

    + Serves as a simple example implementation of the + interface, that manages existing object instances as opposed to creating new ones + based on object definitions. +

    +

    + The + method is not supported by this class; this class deals exclusively with + existing singleton instances, thus the methods mentioned previously make little sense in this context. +

    +
    + Rod Johnson + Juergen Hoeller + Simon White (.NET) + $Id: StaticListableObjectFactory.cs,v 1.24 2007/12/05 00:28:05 bbaia Exp $ +
    + + + Map from object name to object instance. + + + + + Return an instance of the given object name. + + The name of the object to return. + The instance of the object. + + is not currently supported. + + + + + + Return an instance (possibly shared or independent) of the given object name. + + +

    + This method allows an object factory to be used as a replacement for the + Singleton or Prototype design pattern. +

    +

    + Note that callers should retain references to returned objects. There is no + guarantee that this method will be implemented to be efficient. For example, + it may be synchronized, or may need to run an RDBMS query. +

    +

    + Will ask the parent factory if the object cannot be found in this factory + instance. +

    +
    + The name of the object to return. + + The arguments to use if creating a prototype using explicit arguments to + a static factory method. If there is no factory method and the + arguments are not null, then match the argument values by type and + call the object's constructor. + + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + If the supplied is . + +
    + + + Return an instance (possibly shared or independent) of the given object name. + + The name of the object to return. + + The the object may match. Can be an interface or + superclass of the actual class. For example, if the value is the + class, this method will succeed whatever the + class of the returned instance. + + + The arguments to use if creating a prototype using explicit arguments to + a factory method. If there is no factory method and the + supplied array is not , then + match the argument values by type and call the object's constructor. + + The instance of the object. + + If there's no such object definition. + + + If the object could not be created. + + + If the object is not of the required type. + + + If the supplied is . + + + + + + Return an instance of the given object name. + + The name of the object to return. + + the object may match. Can be an interface or + superclass of the actual class. For example, if the value is the + class, this method will succeed whatever the + class of the returned instance. + + The instance of the object. + + + + + Does this object factory contain an object with the given name? + + The name of the object to query. + True if an object with the given name is defined. + + + + Is this object a singleton? + + +

    + That is, will + or + always return the same object? +

    +
    + The name of the object to query. + True if the named object is a singleton. + + If there's no such object definition. + +
    + + + Determines whether the specified object name is prototype. That is, will GetObject + always return independent instances? + + This method returning false does not clearly indicate a singleton object. + It indicated non-independent instances, which may correspond to a scoped object as + well. use the IsSingleton property to explicitly check for a shared + singleton instance. + Translates aliases back to the corresponding canonical object name. Will ask the + parent factory if the object can not be found in this factory instance. + + + + The name of the object to query + + true if the specified object name will always deliver independent instances; otherwise, false. + + if there is no object with the given name. + + + + Determine the type of the object with the given name. + + +

    + More specifically, checks the type of object that + would return. + For an , returns the type + of object that the creates. +

    +
    + The name of the object to query. + + The of the object or if + not determinable. + +
    + + + Determines whether the object with the given name matches the specified type. + + The name of the object to query. + Type of the target to match against. + + true if the object type matches; otherwise, false + if it doesn't match or cannot be determined yet. + + Ff there is no object with the given name + + + + + Return the aliases for the given object name, if defined. + + The object name to check for aliases. + The aliases, or an empty array if none. + + If there's no such object definition. + + + + + Not supported. + + The name of the object. + + The registered + . + + + Always, as object definitions are not supported by this + implementation. + + + + + Return the registered + for the + given object, allowing access to its property values and constructor + argument values. + + The name of the object. + Whether to search parent object factories. + + The registered + . + + + If there is no object with the given name. + + + In the case of errors. + + + + + Return the names of all objects defined in this factory. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + + + + + Return the names of objects matching the given + (including subclasses), judging from the object definitions. + + + The (class or interface) to match, or + for all object names. + + +

    + Will not consider s, + as the type of their created objects is not known before instantiation. +

    +
    + + The names of all objects defined in this factory, or an empty array if none + are defined. + +
    + + + Return the names of objects matching the given + (including subclasses), judging from the object definitions. + + + The (class or interface) to match, or + for all object names. + + +

    + Does consider objects created by s, + or rather it considers the type of objects created by + (which means that + s will be instantiated). +

    +

    + Does not consider any hierarchy this factory may participate in. +

    +
    + + The names of all objects defined in this factory, or an empty array if none + are defined. + +
    + + + Return the names of objects matching the given + (including subclasses), judging from the object definitions. + + +

    + Since this implementation of the + + interface does not support the notion of ptototype objects, the + parameter is ignored. +

    +
    + + The (class or interface) to match, or + for all object names. + + + Whether to include prototype objects too or just singletons (also applies to + s). Ignored. + + + Whether to include s too + or just normal objects. + + + The names of all objects defined in this factory, or an empty array if none + are defined. + + +
    + + + Tests whether this object factory contains an object definition for the + specified object name. + + The object name to query. + + True if an object defintion is contained within this object factory. + + + + + Return the object instances that match the given object + (including subclasses), judging from either object + definitions or the value of + in the case of + s. + + +

    + This version of the + method matches all kinds of object definitions, be they singletons, prototypes, or + s. Typically, the results + of this method call will be the same as a call to + IListableObjectFactory.GetObjectsOfType(type,true,true) . +

    +
    + + The (class or interface) to match. + + + A of the matching objects, + containing the object names as keys and the corresponding object instances + as values. + + + If the objects could not be created. + +
    + + + Return the object instances that match the given object + (including subclasses), judging from either object + definitions or the value of + in the case of + s. + + + The (class or interface) to match. + + + Whether to include prototype objects too or just singletons (also applies to + s). + + + Whether to include s too + or just normal objects. + + + A of the matching objects, + containing the object names as keys and the corresponding object instances + as values. + + + If the objects could not be created. + + + + + Add a new singleton object. + + + The name to be associated with the object name. + + The singleton object. + + + + Injects dependencies into the supplied instance + using the named object definition. + + + The object instance that is to be so configured. + + + The name of the object definition expressing the dependencies that are to + be injected into the supplied instance. + + + This feature is not currently supported. + + + + + + Injects dependencies into the supplied instance + using the supplied . + + + The object instance that is to be so configured. + + + The name of the object definition expressing the dependencies that are to + be injected into the supplied instance. + + + An object definition that should be used to configure object. + + + + + + Defines a method to release allocated unmanaged resources. + + + + + Return the number of objects defined in the factory. + + + The number of objects defined in the factory. + + + + + Return an instance of the given object name. + + The name of the object to return. + The instance of the object. + + + + + A collection (with set semantics) of method overrides, determining which, if any, + methods on a managed object the Spring.NET IoC container will override at runtime. + + Rod Johnson + Rick Evans + $Id: MethodOverrides.cs,v 1.8 2007/03/16 04:01:42 aseovic Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + +

    + Deep copy constructoe. +

    +
    + + The instance supplying initial overrides for this new instance. + +
    + + + Copy all given method overrides into this object. + + + The overrides to be copied into this object. + + + + + Adds the supplied to the overrides contained + within this instance. + + + The to be + added. + + + + + Adds the supplied to the overloaded method names + contained within this instance. + + + The overloaded method name to be added. + + + + + Returns true if the supplied is present within + the overloaded method names contained within this instance. + + + The overloaded method name to be checked. + + + True if the supplied is present within + the overloaded method names contained within this instance. + + + + + Return the override for the given method, if any. + + + The method to check for overrides for. + + + the override for the given method, if any. + + + + + Returns an that can iterate + through a collection. + + +

    + The returned is the + exposed by the + + property. +

    +
    + + An that can iterate through a + collection. + +
    + + + The collection of method overrides. + + + + + Returns true if this instance contains no overrides. + + + + + Default implementation of the + + interface. + + +

    + Does not support per + loading. +

    +
    + Aleksandar Seovic + $Id: DefaultObjectDefinitionFactory.cs,v 1.13 2007/08/01 12:29:43 bbaia Exp $ +
    + + + Factory style method for getting concrete + + instances. + + /// If no parent is specified, a RootObjectDefinition is created, otherwise a + ChildObjectDefinition. + The of the defined object. + The name of the parent object definition (if any). + The against which any class names + will be resolved into instances. + + An + + instance. + + + + + Simple factory object for shared instances. + + Juergen Hoeller + Simon White (.NET) + $Id: SetFactoryObject.cs,v 1.10 2007/07/31 03:47:39 markpollack Exp $ + + + + Constructs a new instance of the target set. + + The new instance. + + + + Set the source . + + +

    + This value will be used to populate the + returned by this factory. +

    +
    +
    + + + Set the of the + implementation to use. + + +

    + The default is the . +

    +
    +
    + + + The of objects created by this factory. + + + Always returns the . + + + + + Specifies how instances of the + + class must apply environment variables when replacing values. + + Mark Pollack + $Id: EnvironmentVariableMode.cs,v 1.6 2007/03/16 04:01:35 aseovic Exp $ + + + + Never replace environment variables. + + + + + If properties are not specified via a resource, + then resolve using environment variables. + + + + + Apply environment variables first before applying properties from a + resource. + + + + + Context that gets passed along an object definition reading process, + encapsulating all relevant configuraiton as well as state. + + Rob Harrop + Juergen Hoeller + Mark Pollack (.NET) + $Id: ReaderContext.cs,v 1.3 2007/08/08 17:47:13 bbaia Exp $ + + + + Initializes a new instance of the class. + + The resource. + + + + Gets the resource. + + The resource. + + + + Implementation of that + resolves variable name against command line arguments. + + Aleksandar Seovic + $Id: CommandLineArgsVariableSource.cs,v 1.3 2007/08/02 22:18:32 markpollack Exp $ + + + + Default constructor. + Initializes command line arguments from the environment. + + + + + Constructor that allows arguments to be passed externally. + Useful for testing. + + + + + Resolves variable value for the specified variable name. + + + The name of the variable to resolve. + + + The variable value if able to resolve, null otherwise. + + + + + Initializes command line arguments dictionary. + + + + + Gets or sets a prefix that should be used to + identify arguments to extract values from. + + + A prefix that should be used to identify arguments + to extract values from. Defaults to slash ("/"). + + + + + Gets or sets a character that should be used to + separate argument name from its value. + + + A character that should be used to separate argument + name from its value. Defaults to colon (":"). + + + + + Represents parsed type node in the navigation expression. + + Aleksandar Seovic + $Id: TypeNode.cs,v 1.11 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns node's value for the given context. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents logical equality operator. + + Aleksandar Seovic + $Id: OpEqual.cs,v 1.12 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the logical equality operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Converter for instances. + + Juergen Hoeller + Mark Pollack (.NET) + $Id: UriConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + + + + Creates a new instance of the + class. + + + + + Returns whether this converter can convert an object of one + to a + + +

    + Currently only supports conversion from a + instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + True if the conversion is possible. +
    + + + Convert from a string value to a instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A if successful. + + + + + Event object sent to listeners registered with an + to inform them of + context lifecycle events. + + Griffin Caprio (.NET) + $Id: ContextEventArgs.cs,v 1.8 2007/07/31 08:18:11 markpollack Exp $ + + + + + + + Creates a new instance of the ContextEventArgs class to represent the + supplied context event. + + The type of context event. + + + + Returns a string representation of this object. + + A string representation of this object. + + + + The event type. + + + + + The various context event types. + + + + + The event type when the context is refreshed or created. + + + + + The event type when the context is closed. + + + + + Implements an based on a + hash table. + + +

    + This will give the best lookup, add, and remove performance for very + large data-sets, but iteration will occur in no particular order. +

    +
    + + $Id: HashedSet.cs,v 1.6 2007/03/16 04:01:27 aseovic Exp $ +
    + + + Creates a new instance of the class. + + + + + Creates a new instance of the class, and + initializes it based on a collection of elements. + + + A collection of elements that defines the initial set contents. + + + + + Miscellaneous utility methods. + + +

    + Mainly for internal use within the framework. +

    +
    + Rod Johnson + Juergen Hoeller + Keith Donald + Aleksandar Seovic (.NET) + Mark Pollack (.NET) + Rick Evans (.NET) + $Id: StringUtils.cs,v 1.29 2006/04/09 07:19:00 markpollack Exp $ +
    + + + The string that signals the start of an Ant-style expression. + + + + + The string that signals the end of an Ant-style expression. + + + + + An empty array of instances. + + + + + Creates a new instance of the class. + + +

    + This is a utility class, and as such exposes no public constructors. +

    +
    +
    + + + Tokenize the given into a + array. + + +

    + If is , returns an empty + array. +

    +

    + If is or the empty + , returns a array with one + element: itself. +

    +
    + The to tokenize. + + The delimiter characters, assembled as a . + + + Trim the tokens via . + + + Omit empty tokens from the result array. + An array of the tokens. +
    + + + Convert a CSV list into an array of s. + + A CSV list. + + An array of s, or the empty array + if is . + + + + + Take a which is a delimited list + and convert it to a array. + + +

    + If the supplied is a + or zero-length string, then a single element + array composed of the supplied + will be + eturned. If the supplied + is , then an empty, + zero-length array will be returned. +

    +
    + + The to be parsed. + + + The delimeter (this will not be returned). Note that only the first + character of the supplied is used. + + + An array of the tokens in the list. + +
    + + + Convenience method to return an + as a delimited + (e.g. CSV) . + + + The to parse. + + + The delimiter to use (probably a ','). + + The delimited string representation. + + + + Convenience method to return an + as a CSV + . + + + The to display. + + The delimited string representation. + + + + Convenience method to return an array as a CSV + . + + + The array to parse. Elements may be of any type ( + will be called on each + element). + + + + + Convenience method to return a + array as a delimited (e.g. CSV) . + + + The array to parse. Elements may be of any type ( + will be called on each + element). + + + The delimiter to use (probably a ','). + + + + Checks if a string has length. + + The string to check, may be . + + + if the string has length and is not + . + + + + StringUtils.HasLength(null) = false + StringUtils.HasLength("") = false + StringUtils.HasLength(" ") = true + StringUtils.HasLength("Hello") = true + + + + + + Checks if a has text. + + +

    + More specifically, returns if the string is + not , it's is > + zero (0), and it has at least one non-whitespace character. +

    +
    + + The string to check, may be . + + + if the is not + , + > zero (0), and does not consist + solely of whitespace. + + + + StringUtils.HasText(null) = false + StringUtils.HasText("") = false + StringUtils.HasText(" ") = false + StringUtils.HasText("12345") = true + StringUtils.HasText(" 12345 ") = true + + +
    + + + Checks if a is + or an empty string. + + +

    + More specifically, returns if the string is + , it's is equal + to zero (0), or it is composed entirely of whitespace + characters. +

    +
    + + The string to check, may (obviously) be . + + + if the is + , has a length equal to zero (0), or + is composed entirely of whitespace characters. + + + + StringUtils.IsNullOrEmpty(null) = true + StringUtils.IsNullOrEmpty("") = true + StringUtils.IsNullOrEmpty(" ") = true + StringUtils.IsNullOrEmpty("12345") = false + StringUtils.IsNullOrEmpty(" 12345 ") = false + + +
    + + + Strips first and last character off the string. + + The string to strip. + The stripped string. + + + + Returns a list of Ant-style expressions from the specified text. + + The text to inspect. + + A list of expressions that exist in the specified text. + + + If any of the expressions in the supplied + is empty (${}). + + + + + Replaces Ant-style expression placeholder with expression value. + + +

    + +

    +
    + The string to set the value in. + The name of the expression to set. + The expression value. + + A new string with the expression value set; the + value if the supplied + is , has a length + equal to zero (0), or is composed entirely of whitespace + characters. + +
    + + + Surrounds (prepends and appends) the string value of the supplied + to the supplied . + + +

    + The return value of this method call is always guaranteed to be non + . If every value passed as a parameter to this method is + , the string will be returned. +

    +
    + + The prefix and suffix that respectively will be prepended and + appended to the target . If this value + is not a value, it's attendant + value will be used. + + + The target that is to be surrounded. If this value is not a + value, it's attendant + value will be used. + + The surrounded string. +
    + + + Surrounds (prepends and appends) the string values of the supplied + and to the supplied + . + + +

    + The return value of this method call is always guaranteed to be non + . If every value passed as a parameter to this method is + , the string will be returned. +

    +
    + + The value that will be prepended to the . If this value + is not a value, it's attendant + value will be used. + + + The target that is to be surrounded. If this value is not a + value, it's attendant + value will be used. + + + The value that will be appended to the . If this value + is not a value, it's attendant + value will be used. + + The surrounded string. +
    + + + Converts escaped characters (for example "\t") within a string + to their real character. + + The string to convert. + The converted string. + + + + Support matching of file system paths in a manner similar to that of the + NAnt FileSet. + + +

    + Any (back)slashes are converted to forward slashes. +

    +
    + + + // true + PathMatcher.Match("c:/*.bat", @"c:\autoexec.bat"); + PathMatcher.Match("c:\fo*\*.bat", @"c:/foobar/autoexec.bat"); + PathMatcher.Match("c:\fo?\*.bat", @"c:/foo/autoexec.bat"); + // false + PathMatcher.Match("c:\fo?\*.bat", @"c:/fo/autoexec.bat"); + + + Federico Spinazzi + $Id: PathMatcher.cs,v 1.7 2007/02/23 18:46:41 markpollack Exp $ +
    + + + Determines if a given path matches a NAnt-like pattern. + + + A forward or back-slashed fileset-like pattern. + + A forward or back-slashed full path. + should the match consider the case + + if the path is matched by the pattern; + otherwise . + + + + + Determines if a given path matches a NAnt-like pattern. + + + A forward or back-slashed fileset-like pattern. + + A forward or back-slashed full path. + + if the path is matched by the pattern; + otherwise . + + + + + Replaces back(slashes) with forward slashes. + + + The path or the pattern to modify. + + A forward-slashed string. + + + + Helper method to convert a NAnt-like pattern into the + appropriate pattern for a regular expression. + + The NAnt-like pattern. + A regex-compatible pattern. + + + + Creates a new instance of the class. + + +

    + This is a utility class, and as such exposes no public constructors. +

    +
    +
    + + + Helper methods with regard to objects, types, properties, etc. + + +

    + Not intended to be used directly by applications. +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + $Id: ObjectUtils.cs,v 1.9 2007/12/29 00:31:00 markpollack Exp $ +
    + + + An empty object array. + + + + + Creates a new instance of the class. + + +

    + This is a utility class, and as such exposes no public constructors. +

    +
    +
    + + + Instantiates the type using the assembly specified to load the type. + + This is a convenience in the case of needing to instantiate a type but not + wanting to specify in the string the version, culture and public key token. + The assembly. + Name of the type. + + + If the or is + + + If cannot load the type from the assembly or the call to InstantiateType(Type) fails. + + + + + Convenience method to instantiate a using + its no-arg constructor. + + +

    + As this method doesn't try to instantiate s + by name, it should avoid loading issues. +

    +
    + + The to instantiate* + + A new instance of the . + + If the is + + + If the is an abstract class, an interface, + an open generic type or does not have a public no-argument constructor. + +
    + + + Gets the zero arg ConstructorInfo object, if the type offers such functionality. + + The type. + Zero argument ConstructorInfo + + If the type is an interface, abstract, open generic type, or does not have a zero-arg constructor. + + + + + Determines whether the specified type is instantiable, i.e. not an interface, abstract class or contains + open generic type parameters. + + The type. + + + + Convenience method to instantiate a using + the given constructor. + + +

    + As this method doesn't try to instantiate s + by name, it should avoid loading issues. +

    +
    + + The constructor to use for the instantiation. + + + The arguments to be passed to the constructor. + + A new instance. + + If the is + + + If the 's declaring type is an abstract class, + an interface, an open generic type or does not have a public no-argument constructor. + +
    + + + Checks whether the supplied is not a transparent proxy and is + assignable to the supplied . + + +

    + Neccessary when dealing with server-activated remote objects, because the + object is of the type TransparentProxy and regular is testing for assignable + types does not work. +

    +

    + Transparent proxy instances always return when tested + with the 'is' operator (C#). This method only checks if the object + is assignable to the type if it is not a transparent proxy. +

    +
    + The target to be checked. + The value that should be assigned to the type. + + if the supplied is not a + transparent proxy and is assignable to the supplied . + +
    + + + Determine if the given is assignable from the + given value, assuming setting by reflection. + + +

    + Considers primitive wrapper classes as assignable to the + corresponding primitive types. +

    +

    + For example used in an object factory's constructor resolution. +

    +
    + The target . + The value that should be assigned to the type. + True if the type is assignable from the value. +
    + + + Check if the given represents a + "simple" property, + i.e. a primitive, a , a + , or a corresponding array. + + +

    + Used to determine properties to check for a "simple" dependency-check. +

    +
    + + The to check. + +
    + + + Check if the given class represents a primitive array, + i.e. boolean, byte, char, short, int, long, float, or double. + + + + + Determine if the given objects are equal, returning + if both are respectively + if only one is . + + The first object to compare. + The second object to compare. + + if the given objects are equal. + + + + + Returns the first element in the supplied . + + + The to use to enumerate + elements. + + + The first element in the supplied . + + + If the supplied did not have any elements. + + + + + Returns the first element in the supplied . + + + The to use to enumerate + elements. + + + The first element in the supplied . + + + If the supplied did not have any elements. + + + If the supplied is . + + + + + Returns the element at the specified index using the supplied + . + + + The to use to enumerate + elements until the supplied is reached. + + + The index of the element in the enumeration to return. + + + The element at the specified index using the supplied + . + + + If the supplied was less than zero, or the + supplied did not contain enough elements + to be able to reach the supplied . + + + + + Returns the element at the specified index using the supplied + . + + + The to use to enumerate + elements until the supplied is reached. + + + The index of the element in the enumeration to return. + + + The element at the specified index using the supplied + . + + + If the supplied was less than zero, or the + supplied did not contain enough elements + to be able to reach the supplied . + + + If the supplied is . + + + + + Gets the qualified name of the given method, consisting of + fully qualified interface/class name + "." method name. + + The method. + qualified name of the method. + + + + Assertion utility methods that simplify things such as argument checks. + + +

    + Not intended to be used directly by applications. +

    +
    + Aleksandar Seovic + $Id: AssertUtils.cs,v 1.13 2008/03/14 10:45:08 bbaia Exp $ +
    + + + Checks the value of the supplied and throws an + if it is . + + The object to check. + The argument name. + + If the supplied is . + + + + + Checks the value of the supplied and throws an + if it is . + + The object to check. + The argument name. + + An arbitrary message that will be passed to any thrown + . + + + If the supplied is . + + + + + Checks the value of the supplied string and throws an + if it is or + contains only whitespace character(s). + + The string to check. + The argument name. + + If the supplied is or + contains only whitespace character(s). + + + + + Checks the value of the supplied string and throws an + if it is or + contains only whitespace character(s). + + The string to check. + The argument name. + + An arbitrary message that will be passed to any thrown + . + + + If the supplied is or + contains only whitespace character(s). + + + + + Checks the value of the supplied and throws + an if it is or contains no elements. + + The array or collection to check. + The argument name. + + If the supplied is or + contains no elements. + + + + + Checks the value of the supplied and throws + an if it is or contains no elements. + + The array or collection to check. + The argument name. + An arbitrary message that will be passed to any thrown . + + If the supplied is or + contains no elements. + + + + + Checks whether the specified can be cast + into the . + + + The argument to check. + + + The name of the argument to check. + + + The required type for the argument. + + + An arbitrary message that will be passed to any thrown + . + + + + + Assert a bool expression, throwing InvalidOperationException + if the expression is false. + + a boolean expression. + The exception message to use if the assertion fails + if expression is false + + + + Creates a new instance of the class. + + +

    + This is a utility class, and as such exposes no public constructors. +

    +
    +
    + + + Implementation of IProxyMethodBuilder that delegates method calls to the base class. + + Bruno Baia + $Id: BaseProxyMethodBuilder.cs,v 1.1 2006/08/10 18:45:55 bbaia Exp $ + + + + Creates a new instance of the method builder. + + The type builder to use. + + The implementation to use. + + + if the interface is to be + implemented explicitly; otherwise . + + + + + Generates the proxy method. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + Tag subclass used to hold a dictionary of managed elements. + + Juergen Hoeller + Rick Evans (.NET) + $Id: ManagedDictionary.cs,v 1.14 2007/11/26 14:15:54 bbaia Exp $ + + + + Resolves this managed collection at runtime. + + + The name of the top level object that is having the value of one of it's + collection properties resolved. + + + The definition of the named top level object. + + + The name of the property the value of which is being resolved. + + + The callback that will actually do the donkey work of resolving + this managed collection. + + A fully resolved collection. + + + + Gets or sets the unresolved name for the + of the keys of this managed dictionary. + + The unresolved name for the type of the keys of this managed dictionary. + + + + Gets or sets the unresolved name for the + of the values of this managed dictionary. + + The unresolved name for the type of the values of this managed dictionary. + + + + Object definition for definitions that inherit settings from their + parent (object definition). + + +

    + Will use the + of the parent object definition if none is specified, but can also + override it. In the latter case, the child's + + must be compatible with the parent, i.e. accept the parent's property values + and constructor argument values (if any). +

    +

    + A will + inherit all of the , + , and + from it's parent + object definition, with the option to add new values. If the + , + , + and / or + + properties are specified, they will override the corresponding parent settings. +

    +

    + The remaining settings will always be taken from the child definition: + , + , + , + , + and + +

    +
    + Rod Johnson + Juergen Hoeller + Rick Evans (.NET) + +
    + + + Creates a new instance of the + + class. + + + The name of the parent object. + + + + + Creates a new instance of the + + class. + + + The name of the parent object. + + + The additional property values (if any) of the child. + + + + + Creates a new instance of the + + class. + + + The name of the parent object. + + + The + to be applied to a new instance of the object. + + + The additional property values (if any) of the child. + + + + + Creates a new instance of the + + class. + + + The name of the parent object. + + + The class of the object to instantiate. + + + The + to be applied to a new instance of the object. + + + The additional property values (if any) of the child. + + + + + Creates a new instance of the + + class. + + + The name of the parent object. + + + The of the object to + instantiate. + + + The + to be applied to a new instance of the object. + + + The additional property values (if any) of the child. + + + + + Validate this object definition. + + +

    + A common cause of validation failures is a missing value for the + + property; by + their very nature require that the + + be set. +

    +
    + + In the case of a validation failure. + +
    + + + A that represents the current + . + + + A that represents the current + . + + + + + The name of the parent object definition. + + +

    + This value is required. +

    +
    + + The name of the parent object definition. + +
    + + + Extension of specific to use with an + XmlObjectDefinitionReader. + + In future will contain access to IXmlParserRegistry + $Id: XmlReaderContext.cs,v 1.6 2007/08/27 13:57:43 oakinger Exp $ + + + + The maximum length of any XML fragment displayed in the error message + reporting. + + +

    + Hopefully this will display enough context so that a user + can pinpoint the cause of the error. +

    +
    +
    + + + Initializes a new instance of the class. + + The resource. + The reader. + + + + Generates the name of the object. + + The object definition. + the generated object name + + + + Registers the name of the with generated. + + The object definition. + the generated object name + + + + Reports a parse error by loading a + with helpful contextual + information and throwing said exception. + + +

    + Derived classes can of course override this method in order to implement + validators capable of displaying a full list of errors found in the + definition. +

    +
    + + The node that triggered the parse error. + + + The name of the object that triggered the exception. + + + A message about the exception. + + + Always throws an instance of this exception class, that will + contain helpful contextual infomation about the parse error. + + +
    + + + Reports a parse error by loading a + with helpful contextual + information and throwing said exception. + + +

    + Derived classes can of course override this method in order to implement + validators capable of displaying a full list of errors found in the + definition. +

    +
    + + The node that triggered the parse error. + + + The name of the object that triggered the exception. + + + A message about the error. + + + The root cause of the parse error (if any - may be ). + + + Always throws an instance of this exception class, that will + contain helpful contextual infomation about the parse error. + +
    + + + This method can be overwritten in order to implement validators + capable of displaying a full list of errors found in the definition. + + + The node that triggered the parse error. + + + A message about the exception. + + + + + Gets the reader. + + The reader. + + + + Gets the resource loader. + + The resource loader. + + + + Gets the registry. + + The registry. + + + + Gets or sets the object definition factory. + + The object definition factory. + + + + Convenient base class for when there exists a one-to-one mapping + between attribute names on the element that is to be parsed and + the property names on the Type being configured. + + + + + Mark Pollack + $Id: AbstractSimpleObjectDefinitionParser.cs,v 1.1 2007/05/26 19:25:54 markpollack Exp $ + + + + Implementation of that + resolves variable name connection strings defined in + the standard .NET configuration file. + + +

    + When the <connectionStrings> configuration section is processed by this class, + two variables are defined for each connection string: one for connection string and + the second one for the provider name.

    +

    + Variable names are generated by appending '.connectionString' and '.providerName' + literals to the value of the name attribute of the connection string element. + For example:

    +
    +            
    +               
    +            
    +            
    +

    + will result in two variables being created: myConn.connectionString and myConn.providerName. + You can reference these variables within your object definitions, just like any other variable.

    +
    + Aleksandar Seovic + $Id: ConnectionStringsVariableSource.cs,v 1.4 2007/08/02 22:18:32 markpollack Exp $ +
    + + + Resolves variable value for the specified variable name. + + + The name of the variable to resolve. + + + The variable value if able to resolve, null otherwise. + + + + + Initializes properties based on the specified + property file locations. + + + + + Holds mapping between control property and it's value + as read from the resource file. + + Aleksandar Seovic + $Id: Resource.cs,v 1.3 2006/04/09 07:18:47 markpollack Exp $ + + + + Creates instance of resource mapper. + + Target property. + Resource value. + + + + Gets parsed target property expression. See + for more information on object navigation expressions. + + + + + Value of the resource that target property should be set to. + + + + + Represents a reference to a Spring-managed object. + + Aleksandar Seovic + $Id: ReferenceNode.cs,v 1.13 2008/03/20 10:35:54 oakinger Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the integer literal node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents arithmetic modulus operator. + + Aleksandar Seovic + $Id: OpMODULUS.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the arithmetic modulus operator node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed node in the navigation expression. + + Aleksandar Seovic + $Id: DateLiteralNode.cs,v 1.7 2007/09/07 03:01:21 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns node's value for the given context. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Implementation of the count aggregator. + + Aleksandar Seovic + $Id: CountAggregator.cs,v 1.2 2006/12/04 08:58:33 aseovic Exp $ + + + + Returns the number of items in the source collection. + + + The source collection to process. + + + Ignored. + + + The number of items in the source collection, + or zero if the collection is empty or null. + + + + + Holder for the generic arguments when using type parameters. + + +

    + Type parameters can be applied to classes, interfaces, + structures, methods, delegates, etc... +

    +
    +
    + + + The generic arguments prefix. + + + + + The generic arguments suffix. + + + + + The character that separates a list of generic arguments. + + + + + Creates a new instance of the GenericArgumentsHolder class. + + + The string value to parse looking for a generic definition + and retrieving its generic arguments. + + + + + Returns an array of unresolved generic arguments types. + + +

    + A empty string represents a type parameter that + did not have been substituted by a specific type. +

    +
    + + An array of strings that represents the unresolved generic + arguments types or an empty array if not generic. + +
    + + + The (unresolved) generic type name portion + of the original value when parsing a generic type. + + + + + The (unresolved) generic method name portion + of the original value when parsing a generic method. + + + + + Is the string value contains generic arguments ? + + +

    + A generic argument can be a type parameter or a type argument. +

    +
    +
    + + + Is generic arguments only contains type parameters ? + + + + + Converter for instances. + + Bruno Baia + $Id: TimeSpanConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ + + + + Creates a new instance of the + class. + + + + + Convert from a string value to a instance. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + A if successful. + + + + + A custom for + runtime type references. + + +

    + Currently only supports conversion to and from a + . +

    +
    + Rick Evans (.NET) + $Id: RuntimeTypeConverter.cs,v 1.1 2007/07/31 18:16:08 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + + + + Returns whether this converter can convert an object of one + to the + of this converter. + + +

    + Currently only supports conversion from a + instance. +

    +
    + + A + that provides a format context. + + + A that represents the + you want to convert from. + + True if the conversion is possible. +
    + + + Returns whether this converter can convert the object to the specified + . + + + A + that provides a format context. + + + A that represents the + you want to convert to. + + True if the conversion is possible. + + + + Converts the given value to the type of this converter. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + An that represents the converted value. + + + + + Converts the given value object to the specified type, + using the specified context and culture information. + + + A + that provides a format context. + + + The to use + as the current culture. + + + The value that is to be converted. + + + The to convert the + parameter to. + + + An that represents the converted value. + + + + + Thrown in response to encountering a value + when traversing a nested path expression. + + $Id: NullValueInNestedPathException.cs,v 1.2 2007/07/31 03:47:22 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The of the object where the property was not found. + + The name of the property not found. + + + + Creates a new instance of the + class. + + + The of the object where the property was not found. + + The name of the property not found. + A message about the exception. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Populates a with + the data needed to serialize the target object. + + + The to populate + with data. + + + The destination (see ) + for this serialization. + + + + + The name of the offending property. + + + + + The of the class where the property was last looked for. + + + + + Criteria that is satisfied if the number of parameters to a given + matches an arbitrary number. + + +

    + This class supports checking the parameter count of both methods and + constructors. +

    +

    + Default parameters, etc need to taken into account. +

    +
    + Rick Evans + $Id: MethodParametersCountCriteria.cs,v 1.2 2007/09/20 14:20:45 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + +

    + This constructor sets the + + property to zero (0). +

    +
    +
    + + + Creates a new instance of the + class. + + + The number of parameters that a + must have to satisfy this criteria. + + + If the supplied is less + than zero. + + + + + Does the supplied satisfy the criteria encapsulated by + this instance? + + The datum to be checked by this criteria instance. + + True if the supplied satisfies the criteria encapsulated + by this instance; false if not or the supplied is null. + + + + + The number of parameters that a + must have to satisfy this criteria. + + + If the supplied value is less than zero. + + + + + An implementation that + reads context definitions from XML based resources. + + +

    + Currently, the resources that are supported are the file, + http, ftp, config and assembly resource + types. +

    +

    + You can provide custom implementations of the + interface and and register them + with any that inherits + from the + + interface. +

    + + In case of multiple config locations, later object definitions will + override ones defined in previously loaded resources. This can be + leveraged to deliberately override certain object definitions via an + extra XML file. + +
    + +

    + Find below some examples of instantiating an + using a + variety of different XML resources. +

    + + // an XmlApplicationContext that reads its object definitions from an + // XML file that has been embedded in an assembly... + IApplicationContext context = new XmlApplicationContext + ( + "assembly://AssemblyName/NameSpace/ResourceName" + ); + + // an XmlApplicationContext that reads its object definitions from a + // number of disparate XML resources... + IApplicationContext context = new XmlApplicationContext + ( + // from an XML file that has been embedded in an assembly... + "assembly://AssemblyName/NameSpace/ResourceName", + // and from a (relative) filesystem-based resource... + "file://Objects/services.xml", + // and from an App.config / Web.config resource... + "config://spring/objects" + ); + +
    + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + $Id: XmlApplicationContext.cs,v 1.18 2007/08/08 17:46:37 bbaia Exp $ + + + +
    + + + Creates a new instance of the + class, + loading the definitions from the supplied XML resource locations. + + The created context will be case sensitive. + + Any number of XML based object definition resource locations. + + + + + Creates a new instance of the + class, + loading the definitions from the supplied XML resource locations. + + Flag specifying whether to make this context case sensitive or not. + + Any number of XML based object definition resource locations. + + + + + Creates a new instance of the + class, + loading the definitions from the supplied XML resource locations. + + The application context name. + Flag specifying whether to make this context case sensitive or not. + + Any number of XML based object definition resource locations. + + + + + Creates a new instance of the + class, + loading the definitions from the supplied XML resource locations, + with the given . + + + The parent context (may be ). + + + Any number of XML based object definition resource locations. + + + + + Creates a new instance of the + class, + loading the definitions from the supplied XML resource locations, + with the given . + + Flag specifying whether to make this context case sensitive or not. + + The parent context (may be ). + + + Any number of XML based object definition resource locations. + + + + + Creates a new instance of the + class, + loading the definitions from the supplied XML resource locations, + with the given . + + The application context name. + Flag specifying whether to make this context case sensitive or not. + + The parent context (may be ). + + + Any number of XML based object definition resource locations. + + + + + Creates a new instance of the + class, + loading the definitions from the supplied XML resource locations, + with the given . + + + This constructor is meant to be used by derived classes. By passing =false, it is + the responsibility of the deriving class to call to initialize the context instance. + + if true, is called automatically. + The application context name. + Flag specifying whether to make this context case sensitive or not. + + The parent context (may be ). + + + Any number of XML based object definition resource locations. + + + + + An array of resource locations, referring to the XML object + definition files that this context is to be built with. + + + An array of resource locations, or if none. + + + + + + An that doesn't do a whole lot. + + +

    + is an implementation of + the NullObject pattern. It should be used in those situations where a + needs to be passed (say to a + method) but where the resolution of messages is not required. +

    +

    + There should not (typically) be a need to instantiate instances of this class; + does not maintan any state + and the instance is + thus safe to pass around. +

    +
    + Aleksandar Seovic + $Id: NullMessageSource.cs,v 1.5 2007/08/27 09:38:29 oakinger Exp $ +
    + + + The canonical instance of the + class. + + + + + Creates a new instance of the class. + + +

    + Consider using + instead. +

    +
    +
    + + + Simply returns the supplied message as-is. + + The code of the message to resolve. + + The to resolve the + code for. + + + The supplied message as-is. + + + + + Always returns . + + The code of the object to resolve. + + The to resolve the + code for. + + + (always). + + + + + Does nothing. + + + An object that contains the property values to be applied. + + + The base name of the object to use for key lookup. + + + The with which the + resource is associated. + + + + + Default implementation of the + interface. + + +

    + Provides easy ways to store all the necessary values needed to resolve + messages from an . +

    +
    + Juergen Hoeller + Griffin Caprio (.NET) + $Id: DefaultMessageSourceResolvable.cs,v 1.4 2007/07/02 21:24:39 markpollack Exp $ + +
    + + + Creates a new instance of the + class + using a single code. + + The message code to be resolved. + + + + Initializes a new instance of the class. + + The codes to be used to resolve this message + + + + Creates a new instance of the + class + using multiple codes. + + The message codes to be resolved. + + The arguments used to resolve the supplied . + + + + + Creates a new instance of the + class + using multiple codes and a default message. + + The message codes to be resolved. + + The arguments used to resolve the supplied . + + + The default message used if no code could be resolved. + + + + + Creates a new instance of the + class + from another resolvable. + + +

    + This is the copy constructor for the + class. +

    +
    + + The to be copied. + + + If the supplied is . + +
    + + + Returns a representation of this + . + + + A representation of this + . + + + + + Calls the visit method on the supplied + to output a version of this class. + + The visitor to use. + + A representation of this + . + + + + + Return the codes to be used to resolve this message, in the order + that they are to be tried. + + + A array of codes which are associated + with this message. + + + + + + Return the array of arguments to be used to resolve this message. + + + An array of objects to be used as parameters to replace + placeholders within the message text. + + + + + + Return the default code for this resolvable. + + + The default code of this resolvable; this will be the last code in + the codes array, or if this instance has no + codes. + + + + + + Return the default message to be used to resolve this message. + + + The default message, or if there is no + default. + + + + + + Validates that object matches specified regular expression. + + +

    + The test expression must evaluate to a ; + otherwise, an exception is thrown. +

    +
    + Aleksandar Seovic + $Id: RegularExpressionValidator.cs,v 1.2 2006/04/09 07:19:04 markpollack Exp $ +
    + + + Creates a new instance of the class. + + + + + Creates a new instance of the class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + The regular expression to match against. + + + + Creates a new instance of the class. + + The expression to validate. + The expression that determines if this validator should be evaluated. + The regular expression to match against. + + + + Validates an object. + + Object to validate. + + if the supplied + object is valid. + + + If the supplied is not a + + + + + + The regular expression text to match against. + + The regular expression text. + + + + The for the regular expression evaluation. + + The regular expression evaluation options. + + + + + Allows developers to specify which validator should be used + to validate method argument. + + Damjan Tomic + Aleksandar Seovic + $Id: ValidatedAttribute.cs,v 1.1 2008/04/02 23:02:36 markpollack Exp $ + + + + Creates an attribute instance. + + + The name of the validator to use (must be defined within + Spring application context). + + + + + Gets the name of the validator to use. + + The name of the validator to use. + + + + An implementation of the Java Properties class. + + Simon White + $Id: Properties.cs,v 1.16 2007/07/31 18:16:26 bbaia Exp $ + + + + Creates an empty property list with no default values. + + + + + Creates a property list with the specified initial properties. + + The initial properties. + + + + Reads a property list (key and element pairs) from the input stream. + + The stream to load from. + + + + Reads a property list (key and element pairs) from a text reader. + + The text reader to load from. + + + + Reads a property list (key and element pairs) from the input stream. + + the dictionary to put it in + The stream to load from. + + + + Reads a property list (key and element pairs) from a text reader. + + the dictionary to put it in + The text reader to load from. + + + + Strips whitespace from the front of the specified string. + + The string. + The string with all leading whitespace removed. + + + + Splits the specified string into a key / value pair. + + The line to split. + An array containing the key / value pair. + + + + Searches for the property with the specified key in this property list. + + The key. + The property, or null if the key was not found. + + + + Searches for the property with the specified key in this property list. + + The key. + + The default value to be returned if the key is not found. + + The property, or the default value. + + + + Writes this property list out to the specified stream. + + The stream to write to. + + + + Sets the specified property key / value pair. + + The key. + The value. + + + + Writes the properties in this instance out to the supplied stream. + + The stream to write to. + Arbitrary header information. + + + + Removes the key / value pair identified by the supplied key. + + + The key identifying the key / value pair to be removed. + + + + + Adds the specified key / object pair to this collection. + + The key. + The value. + + + + Adds the specified key / object pair to this collection. + + + + + Miscellaneous collection utility methods. + + + Mainly for internal use within the framework. + + Mark Pollack (.NET) + $Id: CollectionUtils.cs,v 1.9 2006/04/09 07:19:00 markpollack Exp $ + + + + Determine whether a given collection only contains + a single unique object + + + + + + + Determines whether the contains the specified . + + The collection to check. + The object to locate in the collection. + if the element is in the collection, otherwise. + + + + Adds the specified to the specified . + + The collection to add the element to. + The object to add to the collection. + + + + Determines whether the collection contains all the elements in the specified collection. + + The collection to check. + Collection whose elements would be checked for containment. + true if the target collection contains all the elements of the specified collection. + + + + Removes all the elements from the target collection that are contained in the source collection. + + Collection where the elements will be removed. + Elements to remove from the target collection. + + + + Converts an instance to an instance. + + The instance to be converted. + An instance in which its elements are the elements of the instance. + if the is null. + + + + Holds information and value for an individual property. + + +

    + Using an object here, rather than just storing all properties in a + map keyed by property name, allows for more flexibility, and the + ability to handle indexed properties in a special way if necessary. +

    +

    + Note that the value doesn't need to be the final required + : an + implementation must + handle any necessary conversion, as this object doesn't know anything + about the objects it will be applied to. +

    +
    + Rod Johnson + Mark Pollack (.NET) + $Id: PropertyValue.cs,v 1.15 2007/07/31 00:08:42 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + The name of the property. + + The value of the property (possibly before type conversion). + + + If the supplied is or + contains only whitespace character(s). + + + + + Creates a new instance of the + class. + + The name of the property. + + The value of the property (possibly before type conversion). + + Pre-parsed property name. + + If the supplied or + is , or if the name contains only whitespace characters. + + + + + Print a string representation of the property. + + A string representation of the property. + + + + Determines whether the supplied + is equal to the current . + + The other instance. + + if they are equal in content. + + + + + Serves as a hash function for a particular type, suitable for use + in hashing algorithms and data structures like a hash table. + + + A hash code for the current . + + + + The name of the property. + The name of the property. + + + + Parsed property expression. + + + + + Return the value of the property. + + +

    + Note that type conversion will not have occurred here. + It is the responsibility of the + implementation to + perform type conversion. +

    +
    + The (possibly unresolved) value of the property. +
    + + + Exception thrown if an + is not fully + initialized, for example if it is involved in a circular reference. + + +

    + This is usually indicated by any of the variants of the + + method returning . +

    +

    + A circular reference with an + cannot be solved by eagerly caching singleton instances (as is the + case with normal objects. The reason is that every + needs to be fully + initialized before it can return the created object, while only specific + normal objects need to be initialized - that is, if a collaborating object + actually invokes them on initialization instead of just storing the reference. +

    +
    + Juergen Hoeller + Rick Evans (.NET) + $Id: FactoryObjectNotInitializedException.cs,v 1.4 2007/12/05 00:28:04 bbaia Exp $ +
    + + + Creates a new instance of the + FactoryObjectNotInitializedException class. + + + + + Creates a new instance of the FactoryObjectNotInitializedException class. + + + A message about the exception. + + + + + Creates a new instance of the FactoryObjectNotInitializedException class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + FactoryObjectCircularReferenceException class. + + + The name of the object that triggered the exception. + + + A message about the exception. + + + + + Creates a new instance of the FactoryObjectCircularReferenceException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + A convenience class to create a + given the resource base + name and assembly name. + + +

    + This is currently the preferred way of injecting resources into view + tier components (such as Windows Forms GUIs and ASP.NET ASPX pages). + A GUI component (typically a Windows Form) is injected with + an instance, and can + then proceed to use the various GetXxx() methods on the + to retrieve images, + strings, custom resources, etc. +

    +
    + Mark Pollack + $Id: ResourceManagerFactoryObject.cs,v 1.11 2007/03/16 04:01:39 aseovic Exp $ + + + +
    + + + Creates a . + + + If an exception occured during object creation. + + The object returned by this factory. + + + + + + Invoked by an + after it has set all object properties supplied + (and satisfied the + + and + interfaces). + + + In the event of misconfiguration (such as failure to set an essential + property) or if initialization fails. + + + + + + The root name of the resources. + + +

    + For example, the root name for the resource file named + "MyResource.en-US.resources" is "MyResource". +

    + + The namespace is also prefixed before the resource file name. + +
    +
    + + + The string representation of the assembly that contains the resource. + + + + + The . + + + + + implementation that + retrieves a static or non-static public field value. + + +

    + Typically used for retrieving public constants. +

    +
    + +

    + The following example retrieves the field value... +

    + + + + + + +

    + The previous example could also have been written using the convenience + + property, like so... +

    + + + + + +

    + This class also implements the + interface + (). + If the id (or name) of one's + + object definition is set to the + of the field to be retrieved, then the id (or + name) of one's object definition will be used for the name of the + field lookup. See below for an example of this + concise style of definition. +

    + + + + + + + +

    + The usage for retrieving instance fields is similar. No example is shown + because public instance fields are generally bad practice; but if + you have some legacy code that exposes public instance fields, or if you + just really like coding public instance fields, then you can use this + implementation to + retrieve such field values. +

    + + Juergen Hoeller + Rick Evans (.NET) + $Id: FieldRetrievingFactoryObject.cs,v 1.8 2007/07/31 18:16:49 bbaia Exp $ + + + + Invoked by an + after it has set all object properties supplied + (and satisfied + and ApplicationContextAware). + + +

    + This method allows the object instance to perform initialization only + possible when all object properties have been set and to throw an + exception in the event of misconfiguration. +

    +
    + + In the event of misconfiguration (such as failure to set an essential + property) or if initialization fails. + +
    + + + Return an instance (possibly shared or independent) of the object + managed by this factory. + + + An instance (possibly shared or independent) of the object managed by + this factory. + + + + + + The of the + field to be retrieved. + + + + + Set the name of the object in the object factory that created this object. + + + The name of the object in the factory. + + +

    + In the context of the + + class, the + + value will be interepreted as the value of the + + property if no value has been explicitly assigned to the + + property. This allows for concise object definitions with just an id or name; + see the class documentation for + + for an example of this style of usage. +

    +
    +
    + + + The name of the field the value of which is to be retrieved. + + +

    + If the + + has been set (and is not ), then the value of this property + refers to an instance field name; it otherwise refers to a + field name. +

    +
    +
    + + + The object instance on which the field is defined. + + + + + The on which the field is defined. + + + + + The of object that this + creates, or + if not known in advance. + + + + + Is the object managed by this factory a singleton or a prototype? + + + + + implementation that + creates delegates. + + +

    + Supports the creation of s for both + instance and methods. +

    +
    + Rick Evans + $Id: DelegateFactoryObject.cs,v 1.6 2007/03/16 04:01:35 aseovic Exp $ +
    + + + Callback method called once all factory properties have been set. + + + In the event of misconfiguration (such as failure to set an essential + property) or if initialization fails. + + + + + + Creates the delegate. + + + If an exception occured during object creation. + + The object returned by this factory. + + + + + The of + created by this factory. + + +

    + Returns the + if accessed prior to the method + being called. +

    +
    +
    + + + The of the + created by this factory. + + + + + The name of the method that is to be invoked by the created + delegate. + + + + + The target if the + refers to a method. + + + + + The target object if the + refers to an instance method. + + + + + Visitor class for traversing objects, in particular + the property values and constructor arguments contained in them resolving + object metadata values. + + + Used by and + to parse all string values contained in a ObjectDefinition, resolving any placeholders found. + + Mark Pollack + $Id: ObjectDefinitionVisitor.cs,v 1.9 2008/02/18 00:02:27 bbaia Exp $ + + + + Initializes a new instance of the class, + applying the specified IVariableSource to all object metadata values. + + The variable source. + + + + Initializes a new instance of the class + for subclassing + + Subclasses should override the ResolveStringValue method + + + + Traverse the given ObjectDefinition object and the MutablePropertyValues + and ConstructorArgumentValues contained in them. + + The object definition to traverse. + + + + Visits the ObjectDefinition property ObjectTypeName, replacing string values using + the specified IVariableSource. + + The object definition. + + + + Visits the property values of the ObjectDefinition, replacing string values + using the specified IVariableSource. + + The object definition. + + + + Visits the indexed constructor argument values, replacing string values using the + specified IVariableSource. + + The indexed argument values. + + + + Visits the named constructor argument values, replacing string values using the + specified IVariableSource. + + The named argument values. + + + + Visits the generic constructor argument values, replacing string values using + the specified IVariableSource. + + The genreic argument values. + + + + Configures the constructor argument ValueHolder. + + The vconstructor alue holder. + + + + Resolves the given value taken from an object definition according to its type + + the value to resolve + the resolved value + + + + Visits the ManagedList property ElementTypeName and + calls for list element. + + + + + Visits the ManagedSet property ElementTypeName and + calls for list element. + + + + + Visits the ManagedSet properties KeyTypeName and ValueTypeName and + calls for dictionary's value element. + + + + + Visits the elements of a NameValueCollection and calls + for value of each element. + + + + + Looks up the value of the given variable name in the configured . + + The name of the variable to be looked up + + The value of this variable, as returned from the passed + into the constructor + + If no has been configured. + + + + Loads a list of resources that should be applied from the .NET . + + +

    + This implementation will iterate over all resource managers + within the message source and return a list of all the resources whose name starts with '$this'. +

    +

    + All other resources will be ignored, but you can retrieve them by calling one of + GetMessage methods on the message source directly. +

    +
    + Aleksandar Seovic + $Id: ResourceSetLocalizer.cs,v 1.11 2007/07/24 17:26:25 oakinger Exp $ +
    + + + Loads resources from the storage and creates a list of instances that should be applied to the target. + + + This feature is not currently supported on version 1.0 of the .NET platform. + + Target to get a list of resources for. + instance to retrieve resources from. + Resource locale. + A list of resources to apply. + + + + Represents parsed variable node. + + Aleksandar Seovic + $Id: VariableNode.cs,v 1.8 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns value of the variable represented by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Sets value of the variable represented by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + New value for this node. + + + + Represents parsed integer literal node. + + Aleksandar Seovic + $Id: IntLiteralNode.cs,v 1.10 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the integer literal node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Represents parsed named argument node in the expression. + + Aleksandar Seovic + $Id: QualifiedIdentifier.cs,v 1.7 2007/09/07 03:01:26 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns the value of the named argument defined by this node. + + Context to evaluate expressions against. + Current expression evaluation context. + Node's value. + + + + Overrides getText to allow easy way to get fully + qualified identifier. + + + Fully qualified identifier as a string. + + + + + Represents parsed boolean literal node. + + Aleksandar Seovic + $Id: BooleanLiteralNode.cs,v 1.9 2007/09/07 03:01:21 markpollack Exp $ + + + + Create a new instance + + + + + Create a new instance from SerializationInfo + + + + + Returns a value for the boolean literal node. + + + This is the entrypoint into evaluating this expression. + + Node's value. + + + + Factory class to conceal any default implementation. + + Rod Johnson + Simon White (.NET) + $Id: ControlFlowFactory.cs,v 1.9 2007/07/31 01:35:07 markpollack Exp $ + + + + Creates a new instance of the + implementation provided by this factory. + + + A new instance of the + implementation provided by this factory. + + + + + Creates a new instance of the + class. + + + + + Detects whether the caller is under the supplied , + according to the current stacktrace. + + + + + + Detects whether the caller is under the supplied + and , according to the current stacktrace. + + +

    + Matches the whole method name. +

    +
    + +
    + + + Does the current stack trace contain the supplied ? + + +

    + This leaves it up to the caller to decide what matches, but is obviously less of + an abstraction because the caller must know the exact format of the underlying + stack trace. +

    +
    + +
    + + + Empty implementation that + simply delegates all method calls to it's parent + . + + +

    + If no parent is available, + no messages will be resolved (and a + will be thrown). +

    +

    + Used as placeholder by the + class, + if the context definition doesn't define its own + . Not intended for direct use + in applications. +

    +
    + Juergan Hoeller + Rick Evans (.NET) + $Id: DelegatingMessageSource.cs,v 1.5 2007/07/02 21:24:39 markpollack Exp $ + +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The parent message source used to try and resolve messages that + this object can't resolve. + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If the message could not be resolved. + + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If the message could not be resolved. + + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + + The that represents + the culture for which the resource is localized. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If the message could not be resolved. + + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + + The that represents + the culture for which the resource is localized. + + + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If the message could not be resolved. + + + + + + Resolve the message identified by the supplied + . + + The name of the message to resolve. + The default message. + + The that represents + the culture for which the resource is localized. + + + The array of arguments that will be filled in for parameters within + the message, or if there are no parameters + within the message. Parameters within a message should be + referenced using the same syntax as the format string for the + method. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If the message could not be resolved. + + + + + + Resolve the message using all of the attributes contained within + the supplied + argument. + + + The value object storing those attributes that are required to + properly resolve a message. + + + The that represents + the culture for which the resource is localized. + + + The resolved message if the lookup was successful (see above for + the return value in the case of an unsuccessful lookup). + + + If the message could not be resolved. + + + If the message could not be resolved. + + + + + + Gets a localized resource object identified by the supplied + . + + + The name of the resource object to resolve. + + + The resolved object, or if not found. + + + + + + Gets a localized resource object identified by the supplied + . + + + The name of the resource object to resolve. + + + The with which the + resource is associated. + + + The resolved object, or if not found. + + + + + + Applies resources to object properties. + + + An object that contains the property values to be applied. + + + The base name of the object to use for key lookup. + + + The with which the + resource is associated. + + + + + + The parent message source used to try and resolve messages that + this object can't resolve. + + + + + + A simple implementation backed by a dictionary that + never expires cache items. + + Aleksandar Seovic + $Id: NonExpiringCache.cs,v 1.4 2007/08/24 22:43:55 oakinger Exp $ + + + + Retrieves an item from the cache. + + + Item key. + + + Item for the specified , or null. + + + + + Removes an item from the cache. + + + Item key. + + + + + Removes collection of items from the cache. + + + Collection of keys to remove. + + + + + Removes all items from the cache. + + + + + Inserts an item into the cache. + + + Item key. + + + Item value. + + + Item's time-to-live (TTL) in milliseconds. + + + + + Gets the number of items in the cache. + + + + + Gets a collection of all cache item keys. + + + + + This attribute should be used to mark methods whose result + needs to be cached. + + +

    + This attribute allows application developers to mark that a result + of the method invocation should be cached, but it will not do any + caching by itself. +

    +

    + In order to actually cache the result, an application developer + must apply a Spring.Aspects.Cache.CacheResultAdvice to + all of the members that have this attribute defined. +

    +
    + Aleksandar Seovic + $Id: CacheResultAttribute.cs,v 1.1 2007/02/09 07:12:23 aseovic Exp $ +
    + + + Creates an attribute instance. + + + + + Creates an attribute instance. + + + The name of the cache to use. + + + An expression string that should be evaluated in order to determine + the cache key for the item. + + + + diff --git a/src/Spring/Spring.Core/Threading/CallContextStorage.cs b/src/Spring/Spring.Core/Threading/CallContextStorage.cs new file mode 100644 index 00000000..c56b046f --- /dev/null +++ b/src/Spring/Spring.Core/Threading/CallContextStorage.cs @@ -0,0 +1,41 @@ +using System.Runtime.Remoting.Messaging; + +namespace Spring.Threading +{ + /// + /// Implements by using . + /// + /// Erich Eichinger + /// $Id: CallContextStorage.cs,v 1.1 2007/02/02 21:30:34 oakinger Exp $ + public class CallContextStorage : IThreadStorage + { + /// + /// Retrieves an object with the specified name. + /// + /// The name of the item. + /// The object in the call context associated with the specified name or null if no object has been stored previously + public object GetData(string name) + { + return CallContext.GetData(name); + } + + /// + /// Stores a given object and associates it with the specified name. + /// + /// The name with which to associate the new item. + /// The object to store in the call context. + public void SetData(string name, object value) + { + CallContext.SetData(name, value); + } + + /// + /// Empties a data slot with the specified name. + /// + /// The name of the data slot to empty. + public void FreeNamedDataSlot(string name) + { + CallContext.FreeNamedDataSlot(name); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Threading/ISync.cs b/src/Spring/Spring.Core/Threading/ISync.cs new file mode 100644 index 00000000..e0deeee2 --- /dev/null +++ b/src/Spring/Spring.Core/Threading/ISync.cs @@ -0,0 +1,92 @@ +#region License +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion + +namespace Spring.Threading +{ + /// + /// Acquire/Release protocol, base of many concurrency utilities. + /// + /// + /// + ///

    objects isolate waiting and notification for particular logical + /// states, resource availability, events, and the like that are shared + /// across multiple threads.

    + /// + ///

    Use of s sometimes (but by no means always) adds + /// flexibility and efficiency compared to the use of plain + /// .Net monitor methods and locking, and are sometimes (but by no means + /// always) simpler to program with.

    + /// + ///

    Used for implementation of a

    + ///
    + /// + /// Doug Lea + /// Federico Spinazzi (.Net) + /// $Id: ISync.cs,v 1.6 2006/09/15 19:06:08 markpollack Exp $ + public interface ISync + { + /// Wait (possibly forever) until successful passage. + /// Fail only upon interuption. Interruptions always result in + /// `clean' failures. On failure, you can be sure that it has not + /// been acquired, and that no + /// corresponding release should be performed. Conversely, + /// a normal return guarantees that the acquire was successful. + /// + /// + void Acquire(); + + /// Potentially enable others to pass. + ///

    + /// Because release does not raise exceptions, + /// it can be used in `finally' clauses without requiring extra + /// embedded try/catch blocks. But keep in mind that + /// as with any java method, implementations may + /// still throw unchecked exceptions such as Error or NullPointerException + /// when faced with uncontinuable errors. However, these should normally + /// only be caught by higher-level error handlers. + ///

    + ///
    + void Release (); + + /// + /// Wait at most msecs to pass; report whether passed. + ///

    + /// The method has best-effort semantics: + /// The msecs bound cannot + /// be guaranteed to be a precise upper bound on wait time in Java. + /// Implementations generally can only attempt to return as soon as possible + /// after the specified bound. Also, timers in Java do not stop during garbage + /// collection, so timeouts can occur just because a GC intervened. + /// So, msecs arguments should be used in + /// a coarse-grained manner. Further, + /// implementations cannot always guarantee that this method + /// will return at all without blocking indefinitely when used in + /// unintended ways. For example, deadlocks may be encountered + /// when called in an unintended context. + ///

    + ///
    + /// the number of milleseconds to wait + /// An argument less than or equal to zero means not to wait at all. + /// However, this may still require + /// access to a synchronization lock, which can impose unbounded + /// delay if there is a lot of contention among threads. + /// + /// true if acquired + bool Attempt(long msecs); + } +} diff --git a/src/Spring/Spring.Core/Threading/IThreadStorage.cs b/src/Spring/Spring.Core/Threading/IThreadStorage.cs new file mode 100644 index 00000000..13199766 --- /dev/null +++ b/src/Spring/Spring.Core/Threading/IThreadStorage.cs @@ -0,0 +1,40 @@ +namespace Spring.Threading +{ + /// + /// Specifies the contract a strategy must be implement to store and + /// retrieve data that is specific to the executing thread. + /// + /// + /// All implementations of this interface must treat keys case-sensitive. + /// + /// Erich Eichinger + /// $Id: IThreadStorage.cs,v 1.1 2007/02/02 21:30:34 oakinger Exp $ + public interface IThreadStorage + { + /// + /// Retrieves an object with the specified . + /// + /// The name of the item. + /// + /// The object in the current thread's context associated with the + /// specified or null if no object has been stored previously + /// + object GetData(string name); + + /// + /// Stores a given object and associates it with the specified . + /// + /// The name with which to associate the new item. + /// The object to store in the current thread's context. + void SetData(string name, object value); + + /// + /// Empties a data slot with the specified name. + /// + /// + /// If the object with the specified is not found, the method does nothing. + /// + /// The name of the object to remove. + void FreeNamedDataSlot(string name); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Threading/Latch.cs b/src/Spring/Spring.Core/Threading/Latch.cs new file mode 100644 index 00000000..17ba51ff --- /dev/null +++ b/src/Spring/Spring.Core/Threading/Latch.cs @@ -0,0 +1,132 @@ +#region License +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion + +using System; +using System.Threading; + +namespace Spring.Threading +{ + /// A latch is a boolean condition that is set at most once, ever. + /// Once a single release is issued, all acquires will pass. + ///

    + /// Sample usage. Here are a set of classes that use + /// a latch as a start signal for a group of worker threads that + /// are created and started beforehand, and then later enabled. + ///

    + /// + /// class Worker implements IRunnable { + /// private readonly Latch startSignal; + /// Worker(Latch l) + /// { + /// startSignal = l; + /// } + /// + /// public void Run() { + /// startSignal.acquire(); + /// DoWork(); + /// } + /// + /// void DoWork() { ... } + /// } + /// + /// class Driver { // ... + /// void Main() { + /// Latch go = new Latch(); + /// for (int i = 0; i < N; ++i) // make threads + /// new Thread(new ThreadStart(new Worker(go)).Start(); + /// DoSomethingElse(); // don't let run yet + /// go.Release(); // let all threads proceed + /// } + /// } + /// + ///
    + /// Doug Lea + /// Federico Spinazzi (.Net) + /// $Id: Latch.cs,v 1.11 2006/09/15 19:06:08 markpollack Exp $ + public class Latch : ISync + { + /// + /// can acquire ? + /// + protected bool latched_ = false; + + /// + /// Method mainly used by clients who are trying to get the latch + /// + public void Acquire () + { + lock (this) + { + while (!latched_) + { + Monitor.Wait (this); + } + } + } + + /// Wait at most msecs millisconds for a permit + public bool Attempt (long msecs) + { + lock (this) + { + if (latched_) + { + return true; + } + else if (msecs <= 0) + { + return false; + } + else + { + long waitTime = msecs; + //double start = new TimeSpan(DateTime.UtcNow.Ticks).TotalMilliseconds; + double start = Utils.CurrentTimeMillis; + for (;;) + { + Monitor.Wait (this, TimeSpan.FromMilliseconds(waitTime)); + if (latched_) + { + return true; + } + else + { + waitTime = (long) (msecs - (Utils.CurrentTimeMillis - start)); + if (waitTime <= 0) + { + return false; + } + } + } + } + } + } + + /// + /// Enable all current and future acquires to pass + /// + public void Release () + { + lock (this) + { + latched_ = true; + Monitor.PulseAll(this); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Threading/LogicalThreadContext.cs b/src/Spring/Spring.Core/Threading/LogicalThreadContext.cs new file mode 100644 index 00000000..fc471af4 --- /dev/null +++ b/src/Spring/Spring.Core/Threading/LogicalThreadContext.cs @@ -0,0 +1,97 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Remoting.Messaging; +using Spring.Util; + +#endregion + +namespace Spring.Threading +{ + /// + /// An abstraction to safely store "ThreadStatic" data. + /// + /// + /// By default, is used to store thread-specific data. + /// You may switch the storage strategy by calling .

    + /// NOTE: Access to the underlying storage is not synchronized for performance reasons. + /// You should call only once at application startup! + /// + /// Erich Eichinger + /// $Id: LogicalThreadContext.cs,v 1.3 2007/07/03 22:32:04 markpollack Exp $ + public sealed class LogicalThreadContext + { + ///

    + /// Holds the current strategy. + /// + /// + /// Access to this variable is not synchronized on purpose for performance reasons. + /// Setting a different strategy should happen only once + /// at application startup. + /// + private static IThreadStorage threadStorage = new CallContextStorage(); + + /// + /// Set the new strategy. + /// + public static void SetStorage(IThreadStorage storage) + { + AssertUtils.ArgumentNotNull(storage, "storage"); + threadStorage = storage; + } + + private LogicalThreadContext() + { + throw new NotSupportedException("must not be instantiated"); + } + + /// + /// Retrieves an object with the specified name. + /// + /// The name of the item. + /// The object in the context associated with the specified name or null if no object has been stored previously + public static object GetData(string name) + { + return threadStorage.GetData(name); + } + + /// + /// Stores a given object and associates it with the specified name. + /// + /// The name with which to associate the new item. + /// The object to store in the current thread's context. + public static void SetData(string name, object value) + { + threadStorage.SetData(name, value); + } + + /// + /// Empties a data slot with the specified name. + /// + /// The name of the data slot to empty. + public static void FreeNamedDataSlot(string name) + { + threadStorage.FreeNamedDataSlot(name); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Threading/Semaphore.cs b/src/Spring/Spring.Core/Threading/Semaphore.cs new file mode 100644 index 00000000..9541ab64 --- /dev/null +++ b/src/Spring/Spring.Core/Threading/Semaphore.cs @@ -0,0 +1,187 @@ +#region License +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion + +using System; +using System.Threading; + +namespace Spring.Threading +{ + /// + ///

    Base class for counting semaphores based on Semaphore implementation + /// from Doug Lea.

    + ///
    + /// + /// + ///

    Conceptually, a semaphore + /// maintains a set of permits. Each acquire() blocks if + /// necessary until a permit is available, and then takes it.

    + /// + ///

    Each release adds a permit. However, no actual permit objects are used; + /// the Semaphore just keeps a count of the number available + /// and acts accordingly.

    + /// + ///

    A semaphore initialized to 1 can serve as a mutual exclusion lock.

    + /// + /// Used for implementation of a + ///
    + /// Doug Lea + /// Federico Spinazzi (.Net) + /// $Id: Semaphore.cs,v 1.10 2006/09/15 21:30:09 markpollack Exp $ + public class Semaphore : ISync + { + /// + /// current number of available permits + /// + protected long nPermits; + + /// + ///

    Create a Semaphore with the given initial number of permits.

    + ///

    Using a seed of 1 makes the semaphore act as a mutual + /// exclusion lock.

    + /// + ///

    Negative seeds are also allowed, + /// in which case no acquires will proceed until the number of + /// releases has pushed the number of permits past 0.

    + ///
    + public Semaphore(long initialPermits) + { + nPermits = initialPermits; + } + + /// + /// Release a permit + /// + public virtual void Release () + { + lock (this) + { + ++nPermits; + Monitor.Pulse(this); + } + } + + /// + /// Acquire a permit + /// + public virtual void Acquire () + { + lock (this) + { + try + { + while (nPermits <= 0) + Monitor.Wait(this); + --nPermits; + } + catch (ThreadInterruptedException ex) + { + Monitor.Pulse(this); + throw ex; + } + } + } + + /// + /// Wait at most msecs millisconds for a permit + /// + /// number of ms to wait + /// true if aquired + public virtual bool Attempt(long msecs) + { + lock (this) + { + if (nPermits > 0) + { + --nPermits; + return true; + } + else if (msecs <= 0) + return false; + else + { + try + { + double startTime = Utils.CurrentTimeMillis; + long waitTime = msecs; + + for (; ; ) + { + Monitor.Wait(this, TimeSpan.FromMilliseconds(waitTime)); + if (nPermits > 0) + { + --nPermits; + return true; + } + else + { + waitTime = (long) (msecs - (Utils.CurrentTimeMillis - startTime)); + if (waitTime <= 0) + return false; + } + } + } + catch (ThreadInterruptedException ex) + { + Monitor.Pulse(this); + throw ex; + } + } + } + } + + /// Release N permits. release(n) is + /// equivalent in effect to: + ///
    +        /// for (int i = 0; i < n; ++i) release();
    +        /// 
    + /// But may be more efficient in some semaphore implementations. + ///
    + /// if n is negative. + /// + /// + public virtual void Release(long n) + { + lock (this) + { + if (n < 0) + throw new ArgumentOutOfRangeException("n", n, "Negative argument"); + + nPermits += n; + for (long i = 0; i < n; ++i) + Monitor.Pulse(this); + } + } + + /// Return the current number of available permits. + /// Returns an accurate, but possibly unstable value, + /// that may change immediately after returning. + /// + public virtual long Permits + { + get + { + lock (this) + { + return nPermits; + } + } + } + + + } +} diff --git a/src/Spring/Spring.Core/Threading/SyncHolder.cs b/src/Spring/Spring.Core/Threading/SyncHolder.cs new file mode 100644 index 00000000..fe03a134 --- /dev/null +++ b/src/Spring/Spring.Core/Threading/SyncHolder.cs @@ -0,0 +1,70 @@ +#region License +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion + +using System; + +namespace Spring.Threading +{ + /// + /// Utility class to use an with the + /// C# using () {} idiom + /// + public class SyncHolder : IDisposable + { + ISync _sync; + + /// + /// Creates a new trying to the given + /// + /// + /// the to be held + public SyncHolder (ISync sync) + { + Init (sync); + } + + /// + /// Creates a new trying to the given + /// + /// + /// the to be held + /// millisecond to try to acquire the lock + public SyncHolder (ISync sync, long msecs) + { + Init(new TimeoutSync(sync, msecs)); + } + + /// + /// Releases the held + /// + public void Dispose () + { + _sync.Release(); + } + + /// + /// initializes and acquire access to the + /// + /// + private void Init (ISync sync) + { + this._sync = sync; + _sync.Acquire(); + } + } +} diff --git a/src/Spring/Spring.Core/Threading/ThreadStaticStorage.cs b/src/Spring/Spring.Core/Threading/ThreadStaticStorage.cs new file mode 100644 index 00000000..ab79444d --- /dev/null +++ b/src/Spring/Spring.Core/Threading/ThreadStaticStorage.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections; + +namespace Spring.Threading +{ + /// + /// Implements by using a hashtable. + /// + /// Erich Eichinger + /// $Id: ThreadStaticStorage.cs,v 1.1 2007/02/02 21:30:34 oakinger Exp $ + public class ThreadStaticStorage : IThreadStorage + { + [ThreadStatic] + private static Hashtable data; + + private static Hashtable Data + { + get + { + if (data == null) data = new Hashtable(); + return data; + } + } + + /// + /// Retrieves an object with the specified name. + /// + /// The name of the item. + /// The object in the call context associated with the specified name or null if no object has been stored previously + public object GetData(string name) + { + return Data[name]; + } + + /// + /// Stores a given object and associates it with the specified name. + /// + /// The name with which to associate the new item. + /// The object to store in the call context. + public void SetData(string name, object value) + { + Data[name] = value; + } + + /// + /// Empties a data slot with the specified name. + /// + /// The name of the data slot to empty. + public void FreeNamedDataSlot(string name) + { + Data.Remove(name); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Threading/TimeoutException.cs b/src/Spring/Spring.Core/Threading/TimeoutException.cs new file mode 100644 index 00000000..0b129e83 --- /dev/null +++ b/src/Spring/Spring.Core/Threading/TimeoutException.cs @@ -0,0 +1,128 @@ +#region License + +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +using System; +using System.Runtime.Serialization; +using System.Threading; + + +/* +Originally written by Doug Lea and released into the public domain. +This may be used for any purposes whatsoever without acknowledgment. +Thanks for the assistance and support of Sun Microsystems Labs, +and everyone contributing, testing, and using this code. +*/ +namespace Spring.Threading +{ + /// Thrown by synchronization classes that report + /// timeouts via exceptions. The exception is treated + /// as a form (subclass) of InterruptedException. This both + /// simplifies handling, and conceptually reflects the fact that + /// timed-out operations are artificially interrupted by timers. + /// + /// + [Serializable] + public class TimeoutException : ThreadInterruptedException + { + /// The approximate time that the operation lasted before + /// this timeout exception was thrown. + /// + /// + private readonly long _duration; + /// + /// Creates a new instance of the + /// class. + /// + public TimeoutException( ) : base(){} + /// + /// Creates a new instance of the + /// class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + public TimeoutException( string message ) : base( message ){} + /// + /// Creates a new instance of the + /// class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public TimeoutException( string message, Exception innerException ) : base( message, innerException ){} + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected TimeoutException( SerializationInfo info, StreamingContext context ) + : base( info, context ) + { + _duration = info.GetInt32( "duration" ); + } + /// + /// Override of GetObjectData to allow for private serialization + /// + /// serialization info + /// streaming context + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue( "duration", _duration ); + base.GetObjectData( info, context ); + } + /// Constructs a TimeoutException with given duration value. + /// + /// + public TimeoutException( long time ) + { + _duration = time; + } + /// Constructs a TimeoutException with the + /// specified duration value and detail message. + /// + public TimeoutException( long time, String message ) : base( message ) + { + _duration = time; + } + /// + /// Gets the approximate time that the operation lasted before + /// this timeout exception was thrown. + /// + public long Duration + { + get + { + return _duration; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Threading/TimeoutSync.cs b/src/Spring/Spring.Core/Threading/TimeoutSync.cs new file mode 100644 index 00000000..b28cc7fb --- /dev/null +++ b/src/Spring/Spring.Core/Threading/TimeoutSync.cs @@ -0,0 +1,79 @@ +#region License +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +/* +Originally written by Doug Lea and released into the public domain. +This may be used for any purposes whatsoever without acknowledgment. +Thanks for the assistance and support of Sun Microsystems Labs, +and everyone contributing, testing, and using this code. +*/ + +namespace Spring.Threading +{ + + /// A TimeoutSync is an adaptor class that transforms all + /// calls to acquire to instead invoke attempt with a predetermined + /// timeout value. + /// + /// + public class TimeoutSync : ISync + { + /// + /// the adapted sync + /// + protected readonly internal ISync sync_; + /// + /// timeout value + /// + protected readonly internal long timeout_; + + /// Create a TimeoutSync using the given Sync object, and + /// using the given timeout value for all calls to acquire. + /// + public TimeoutSync(ISync sync, long timeout) + { + sync_ = sync; + timeout_ = timeout; + } + + /// + /// Try to acquire the sync before the timeout + /// + /// In case a time out occurred + public virtual void Acquire() + { + if (!sync_.Attempt(timeout_)) + throw new TimeoutException(timeout_); + } + + /// + /// + /// + public virtual bool Attempt(long msecs) + { + return sync_.Attempt(msecs); + } + + /// + /// + /// + public virtual void Release() + { + sync_.Release(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Threading/Utils.cs b/src/Spring/Spring.Core/Threading/Utils.cs new file mode 100644 index 00000000..97a61aa0 --- /dev/null +++ b/src/Spring/Spring.Core/Threading/Utils.cs @@ -0,0 +1,136 @@ +#region License + +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +using System; +using System.Threading; + +namespace Spring.Threading +{ + /// + /// Support to account for differences between java nad .NET: + ///
      + ///
    + ///
    + public class Utils + { + private Utils() + { + } + + /// + /// .NET threads have not a method to check if they have been interrupted. + /// Moreover, differently from java threads, when entering locked + /// blocks, Monitor, Sleep, SpinWait and so on, a + /// will be raised by the runtime. + ///

    Spring.Threading classes usually call this method before entering a lock block, to mirror java code + ///

    Usually this is non issue because the same exception will be raised entering the monitor + /// associated with the lock () + ///

    + ///
    + /// if the thread has been interrupted + public static void FailFastIfInterrupted() + { + Thread.Sleep(0); + } + + /// + /// Placeholder for java.lang.System.currentTimeMillis + /// + /// The current machine time in milliseconds + public static long CurrentTimeMillis + { + get { return ToTimeMillis(DateTime.Now); } + } + + /// + /// Normalize the given so that + /// is is comparable with . + /// + /// Date. + /// + public static long ToTimeMillis(DateTime date) + { + // may be also new TimeSpan(DateTime.UtcNow.Ticks).TotalMilliseconds; + // see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_jlca/html/vberrjavalangsystemcurrenttimemillis.asp + return (date.Ticks - 621355968000000000)/10000; + } + + /// + /// + /// + /// + /// + /// the difference between millisecodns of the first and second date + public static long DeltaTimeMillis(DateTime one, DateTime another) + { + return (one.Ticks - another.Ticks)/10000; + } + + /// + /// Returns the number of nanoseconds for the current value of + /// + /// Current number of nanoseconds + public static long CurrentNanoSeconds() + { + return (DateTime.Now.Ticks - 621355968000000000)/10000*1000000; + } + + /// + /// Returns the number of nano seconds represented by the + /// + /// to use + /// Number of nano seconds for + public static long TimeSpanNanoSeconds(TimeSpan timeSpan) + { + return (timeSpan.Ticks - 621355968000000000)/10000*1000000; + } + /// + /// Returns a representing the number of nanoseconds passed in via . + /// + /// Number of nanoseconds. + /// representing the number of nanoseconds passed in. + public static TimeSpan NanoSecondsTimeSpan(long nanoSeconds ) + { + int milliseconds = Convert.ToInt32(nanoSeconds / 1000000); + return new TimeSpan(0,0,0,0, milliseconds); + } + + /// + /// Has been interrupted this thread + /// + public static bool ThreadInterrupted + { + get + { + bool interrupted = false; + try + { + Utils.FailFastIfInterrupted(); + } + catch (ThreadInterruptedException) + { + Thread.CurrentThread.Interrupt(); + interrupted = true; + } + return interrupted; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/ArrayUtils.cs b/src/Spring/Spring.Core/Util/ArrayUtils.cs new file mode 100644 index 00000000..2428ebc0 --- /dev/null +++ b/src/Spring/Spring.Core/Util/ArrayUtils.cs @@ -0,0 +1,163 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Text; + +#endregion + +namespace Spring.Util +{ + /// + /// Various utility methods relating to the manipulation of arrays. + /// + /// Aleksandar Seovic + /// $Id: ArrayUtils.cs,v 1.15 2007/02/17 08:28:12 bbaia Exp $ + public sealed class ArrayUtils + { + /// + /// Checks if the given array or collection is null or has no elements. + /// + /// + /// + public static bool HasLength(ICollection collection) + { + return !( (collection == null) || (collection.Count == 0) ); + } + + /// + /// Tests equality of two single-dimensional arrays by checking each element + /// for equality. + /// + /// The first array to be checked. + /// The second array to be checked. + /// True if arrays are the same, false otherwise. + public static bool AreEqual(Array a, Array b) + { + if (a == null && b == null) + { + return true; + } + + if (a != null && b != null) + { + if (a.Length == b.Length) + { + for (int i = 0; i < a.Length; i++) + { + object elemA = a.GetValue(i); + object elemB = b.GetValue(i); + + if (elemA is Array && elemB is Array) + { + if (!AreEqual(elemA as Array, elemB as Array)) + { + return false; + } + } + else if (!Equals(elemA, elemB)) + { + return false; + } + } + return true; + } + } + return false; + } + + /// + /// Returns hash code for an array that is generated based on the elements. + /// + /// + /// Hash code returned by this method is guaranteed to be the same for + /// arrays with equal elements. + /// + /// + /// Array to calculate hash code for. + /// + /// + /// A hash code for the specified array. + /// + public static int GetHashCode(Array array) + { + int hashCode = 0; + + if (array != null) + { + for (int i = 0; i < array.Length; i++) + { + object el = array.GetValue(i); + if (el != null) + { + if (el is Array) + { + hashCode += 17 * GetHashCode(el as Array); + } + else + { + hashCode += 13 * el.GetHashCode(); + } + } + } + } + + return hashCode; + } + + /// + /// Returns string representation of an array. + /// + /// + /// Array to return as a string. + /// + /// + /// String representation of the specified . + /// + public static string ToString(Array array) + { + if (array == null) + { + return "null"; + } + + StringBuilder sb = new StringBuilder(); + sb.Append('{'); + + for (int i = 0; i < array.Length; i++) + { + object val = array.GetValue(i); + sb.Append(val == null ? "null" : val.ToString()); + + if (i < array.Length - 1) + { + sb.Append(", "); + } + } + + sb.Append('}'); + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/AssertUtils.cs b/src/Spring/Spring.Core/Util/AssertUtils.cs new file mode 100644 index 00000000..6c2664e7 --- /dev/null +++ b/src/Spring/Spring.Core/Util/AssertUtils.cs @@ -0,0 +1,233 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; + +#endregion + +namespace Spring.Util +{ + /// + /// Assertion utility methods that simplify things such as argument checks. + /// + /// + ///

    + /// Not intended to be used directly by applications. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: AssertUtils.cs,v 1.13 2008/03/14 10:45:08 bbaia Exp $ + public sealed class AssertUtils + { + /// + /// Checks the value of the supplied and throws an + /// if it is . + /// + /// The object to check. + /// The argument name. + /// + /// If the supplied is . + /// + public static void ArgumentNotNull(object argument, string name) + { + if (argument == null) + { + throw new ArgumentNullException ( + name, + string.Format ( + CultureInfo.InvariantCulture, + "Argument '{0}' cannot be null.", name)); + } + } + + /// + /// Checks the value of the supplied and throws an + /// if it is . + /// + /// The object to check. + /// The argument name. + /// + /// An arbitrary message that will be passed to any thrown + /// . + /// + /// + /// If the supplied is . + /// + public static void ArgumentNotNull(object argument, string name, string message) + { + if (argument == null) + { + throw new ArgumentNullException(name, message); + } + } + + /// + /// Checks the value of the supplied string and throws an + /// if it is or + /// contains only whitespace character(s). + /// + /// The string to check. + /// The argument name. + /// + /// If the supplied is or + /// contains only whitespace character(s). + /// + public static void ArgumentHasText(string argument, string name) + { + if (StringUtils.IsNullOrEmpty(argument)) + { + throw new ArgumentNullException ( + name, + string.Format ( + CultureInfo.InvariantCulture, + "Argument '{0}' cannot be null or resolve to an empty string : '{1}'.", name, argument)); + } + } + + /// + /// Checks the value of the supplied string and throws an + /// if it is or + /// contains only whitespace character(s). + /// + /// The string to check. + /// The argument name. + /// + /// An arbitrary message that will be passed to any thrown + /// . + /// + /// + /// If the supplied is or + /// contains only whitespace character(s). + /// + public static void ArgumentHasText(string argument, string name, string message) + { + if (StringUtils.IsNullOrEmpty(argument)) + { + throw new ArgumentNullException(name, message); + } + } + + /// + /// Checks the value of the supplied and throws + /// an if it is or contains no elements. + /// + /// The array or collection to check. + /// The argument name. + /// + /// If the supplied is or + /// contains no elements. + /// + public static void ArgumentHasLength(ICollection argument, string name) + { + if (!ArrayUtils.HasLength(argument)) + { + throw new ArgumentNullException( + name, + string.Format( + CultureInfo.InvariantCulture, + "Argument '{0}' cannot be null or resolve to an empty array", name)); + } + } + + /// + /// Checks the value of the supplied and throws + /// an if it is or contains no elements. + /// + /// The array or collection to check. + /// The argument name. + /// An arbitrary message that will be passed to any thrown . + /// + /// If the supplied is or + /// contains no elements. + /// + public static void ArgumentHasLength(ICollection argument, string name, string message) + { + if(!ArrayUtils.HasLength(argument)) + { + throw new ArgumentNullException(name, message); + } + } + + /// + /// Checks whether the specified can be cast + /// into the . + /// + /// + /// The argument to check. + /// + /// + /// The name of the argument to check. + /// + /// + /// The required type for the argument. + /// + /// + /// An arbitrary message that will be passed to any thrown + /// . + /// + public static void AssertArgumentType(object argument, string argumentName, Type requiredType, string message) + { + if (argument != null && requiredType != null && !requiredType.IsAssignableFrom(argument.GetType())) + { + throw new ArgumentException(message, argumentName); + } + } + + /// + /// Assert a bool expression, throwing InvalidOperationException + /// if the expression is false. + /// + /// a boolean expression. + /// The exception message to use if the assertion fails + /// if expression is false + public static void State(bool expression, string message) + { + if (!expression) + { + throw new InvalidOperationException(message); + } + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private AssertUtils() + { + } + + // CLOVER:ON + + #endregion + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/CollectionUtils.cs b/src/Spring/Spring.Core/Util/CollectionUtils.cs new file mode 100644 index 00000000..266edabc --- /dev/null +++ b/src/Spring/Spring.Core/Util/CollectionUtils.cs @@ -0,0 +1,207 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; + +#endregion + +namespace Spring.Util +{ + /// + /// Miscellaneous collection utility methods. + /// + /// + /// Mainly for internal use within the framework. + /// + /// Mark Pollack (.NET) + /// $Id: CollectionUtils.cs,v 1.9 2006/04/09 07:19:00 markpollack Exp $ + public sealed class CollectionUtils + { + #region Methods + + /// + /// Determine whether a given collection only contains + /// a single unique object + /// + /// + /// + public static bool HasUniqueObject(ICollection coll) + { + if (coll.Count == 0) + { + return false; + } + object candidate = null; + foreach (object elem in coll) + { + if (candidate == null) + { + candidate = elem; + } + else if (candidate != elem) + { + return false; + } + } + return true; + } + + /// + /// Determines whether the contains the specified . + /// + /// The collection to check. + /// The object to locate in the collection. + /// if the element is in the collection, otherwise. + public static bool Contains(ICollection collection, Object element) + { + if (collection == null) + { + throw new ArgumentNullException("Collection cannot be null."); + } + MethodInfo method; + method = collection.GetType().GetMethod("contains", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + if (null == method) + { + throw new InvalidOperationException("Collection type " + collection.GetType() + " does not implement a Contains() method."); + } + return (bool) method.Invoke(collection, new Object[] {element}); + } + + /// + /// Adds the specified to the specified . + /// + /// The collection to add the element to. + /// The object to add to the collection. + public static void Add(ICollection collection, object element) + { + if (collection == null) + { + throw new ArgumentNullException("Collection cannot be null."); + } + MethodInfo method; + method = collection.GetType().GetMethod("add", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + if (null == method) + { + throw new InvalidOperationException("Collection type " + collection.GetType() + " does not implement a Add() method."); + } + method.Invoke(collection, new Object[] {element}); + } + + /// + /// Determines whether the collection contains all the elements in the specified collection. + /// + /// The collection to check. + /// Collection whose elements would be checked for containment. + /// true if the target collection contains all the elements of the specified collection. + public static bool ContainsAll(ICollection targetCollection, ICollection sourceCollection) + { + if (targetCollection == null || sourceCollection == null) + { + throw new ArgumentNullException("Collection cannot be null."); + } + if ( sourceCollection.Count == 0 && targetCollection.Count > 1 ) + return true; + + IEnumerator sourceCollectionEnumerator = sourceCollection.GetEnumerator(); + + bool contains = false; + + MethodInfo method; + method = targetCollection.GetType().GetMethod("containsAll", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + + if (method != null) + contains = (bool) method.Invoke(targetCollection, new Object[] {sourceCollection}); + else + { + method = targetCollection.GetType().GetMethod("Contains", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + if (method == null) + { + throw new InvalidOperationException("Target collection does not implment a Contains() or ContainsAll() method."); + } + while (sourceCollectionEnumerator.MoveNext() == true) + { + if ((contains = (bool) method.Invoke(targetCollection, new Object[] {sourceCollectionEnumerator.Current})) == false) + break; + } + } + return contains; + } + + /// + /// Removes all the elements from the target collection that are contained in the source collection. + /// + /// Collection where the elements will be removed. + /// Elements to remove from the target collection. + public static void RemoveAll(ICollection targetCollection, ICollection sourceCollection) + { + if (targetCollection == null || sourceCollection == null) + { + throw new ArgumentNullException("Collection cannot be null."); + } + ArrayList al = ToArrayList(sourceCollection); + + MethodInfo method; + method = targetCollection.GetType().GetMethod("removeAll", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + + if (method != null) + method.Invoke(targetCollection, new Object[] {al}); + else + { + method = targetCollection.GetType().GetMethod("Remove", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public, null, new Type[1] {typeof(object)}, null ); + MethodInfo methodContains = targetCollection.GetType().GetMethod("Contains", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + if ( method == null ) + { + throw new InvalidOperationException("Target Collection must implement either a RemoveAll() or Remove() method."); + } + if ( methodContains == null ) + { + throw new InvalidOperationException("TargetCollection must implement a Contains() method."); + } + IEnumerator e = al.GetEnumerator(); + while (e.MoveNext() == true) + { + while ((bool) methodContains.Invoke(targetCollection, new Object[] {e.Current}) == true) + method.Invoke(targetCollection, new Object[] {e.Current}); + } + } + } + + /// + /// Converts an instance to an instance. + /// + /// The instance to be converted. + /// An instance in which its elements are the elements of the instance. + /// if the is null. + public static ArrayList ToArrayList(ICollection inputCollection) + { + if ( inputCollection == null ) + { + throw new ArgumentNullException("Collection cannot be null."); + } + return new ArrayList(inputCollection); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/CompareUtils.cs b/src/Spring/Spring.Core/Util/CompareUtils.cs new file mode 100644 index 00000000..16c8e756 --- /dev/null +++ b/src/Spring/Spring.Core/Util/CompareUtils.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Util +{ + /// + /// Utility class containing helper methods for object comparison. + /// + /// Aleksandar Seovic + /// $Id: CompareUtils.cs,v 1.2 2007/02/17 08:28:12 bbaia Exp $ + public class CompareUtils + { + /// Compares two objects. + /// First object. + /// Second object. + /// + /// 0, if objects are equal; + /// less than zero, if the first object is smaller than the second one; + /// greater than zero, if the first object is greater than the second one. + public static int Compare(object first, object second) + { + // anything is greater than null, unless both operands are null + if (first == null) + { + return (second == null ? 0 : -1); + } + else if (second == null) + { + return 1; + } + + if (!first.GetType().Equals(second.GetType())) + { + if (!CoerceTypes(ref first, ref second)) + { + throw new ArgumentException("Cannot compare instances of [" + + first.GetType().FullName + + "] and [" + + second.GetType().FullName + + "] because they cannot be coerced to the same type."); + } + } + + if (first is IComparable) + { + return ((IComparable)first).CompareTo(second); + } + else + { + throw new ArgumentException("Cannot compare instances of the type [" + + first.GetType().FullName + + "] because it doesn't implement IComparable"); + } + } + + private static bool CoerceTypes(ref object left, ref object right) + { + if (NumberUtils.IsNumber(left) && NumberUtils.IsNumber(right)) + { + NumberUtils.CoerceTypes(ref right, ref left); + return true; + } + return false; + } + + } +} diff --git a/src/Spring/Spring.Core/Util/ConfigurationUtils.cs b/src/Spring/Spring.Core/Util/ConfigurationUtils.cs new file mode 100644 index 00000000..a544b76d --- /dev/null +++ b/src/Spring/Spring.Core/Util/ConfigurationUtils.cs @@ -0,0 +1,222 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Configuration; +using System.Xml; + +#endregion + +namespace Spring.Util +{ + /// + /// Utility class for .NET configuration files management. + /// + /// Aleksandar Seovic + /// $Id: ConfigurationUtils.cs,v 1.5 2008/01/10 16:37:43 bbaia Exp $ + public class ConfigurationUtils + { + /// + /// Parses the configuration section. + /// + /// + ///

    + /// Primary purpose of this method is to allow us to parse and + /// load configuration sections using the same API regardless + /// of the .NET framework version. + ///

    + ///

    + /// If Microsoft paid a bit more attention to preserving backwards + /// compatibility we would not even need it, but... :( + ///

    + ///
    + /// Name of the configuration section. + /// Object created by a corresponding . + public static object GetSection(string sectionName) + { +#if !NET_2_0 + return ConfigurationSettings.GetConfig(sectionName.TrimEnd('/')); +#else + return ConfigurationManager.GetSection(sectionName.TrimEnd('/')); +#endif + } + + /// + /// Refresh the configuration section. + /// + /// + ///

    + /// Primary purpose of this method is to allow us to parse and + /// load configuration sections using the same API regardless + /// of the .NET framework version. + ///

    + ///

    + /// If Microsoft paid a bit more attention to preserving backwards + /// compatibility we would not even need it, but... :( + ///

    + ///
    + /// Name of the configuration section. + public static void RefreshSection(string sectionName) + { +#if !NET_2_0 + // TODO : Add support for .NET 1.x +#else + ConfigurationManager.RefreshSection(sectionName); +#endif + } + + /// + /// Creates the configuration exception. + /// + /// The message to display to the client when the exception is thrown. + /// The inner exception. + /// Name of the configuration file. + /// The line where exception occured. + /// Configuration exception. + public static Exception CreateConfigurationException(string message, Exception inner, string fileName, int line) + { +#if !NET_2_0 + return new ConfigurationException(message, inner, fileName, line); +#else + return new ConfigurationErrorsException(message, inner, fileName, line); +#endif + } + + /// + /// Creates the configuration exception. + /// + /// The message to display to the client when the exception is thrown. + /// Name of the configuration file. + /// The line where exception occured. + /// Configuration exception. + public static Exception CreateConfigurationException(string message, string fileName, int line) + { + return CreateConfigurationException(message, null, fileName, line); + } + + /// + /// Creates the configuration exception. + /// + /// The message to display to the client when the exception is thrown. + /// The inner exception. + /// XML node where exception occured. + /// Configuration exception. + public static Exception CreateConfigurationException(string message, Exception inner, XmlNode node) + { +#if !NET_2_0 + return new ConfigurationException(message, inner, node); +#else + return new ConfigurationErrorsException(message, inner, node); +#endif + } + + /// + /// Creates the configuration exception. + /// + /// The message to display to the client when the exception is thrown. + /// XML node where exception occured. + /// Configuration exception. + public static Exception CreateConfigurationException(string message, XmlNode node) + { + return CreateConfigurationException(message, null, node); + } + + /// + /// Creates the configuration exception. + /// + /// The message to display to the client when the exception is thrown. + /// The inner exception. + /// Configuration exception. + public static Exception CreateConfigurationException(string message, Exception inner) + { +#if !NET_2_0 + return new ConfigurationException(message, inner); +#else + return new ConfigurationErrorsException(message, inner); +#endif + } + + /// + /// Creates the configuration exception. + /// + /// The message to display to the client when the exception is thrown. + /// Configuration exception. + public static Exception CreateConfigurationException(string message) + { + return CreateConfigurationException(message, (Exception) null); + } + + /// + /// Creates the configuration exception. + /// + /// Configuration exception. + public static Exception CreateConfigurationException() + { + return CreateConfigurationException(null, (Exception) null); + } + + /// + /// Determines whether the specified exception is configuration exception. + /// + /// The exception to check. + /// + /// true if the specified exception is configuration exception; otherwise, false. + /// + public static bool IsConfigurationException(Exception exception) + { +#if !NET_2_0 + return exception is ConfigurationException; +#else + return exception is ConfigurationErrorsException; +#endif + } + + /// + /// Returns the line number of the specified node. + /// + /// Node to get the line number for. + /// The line number of the specified node. + public static int GetLineNumber(XmlNode node) + { +#if !NET_2_0 + return ConfigurationException.GetXmlNodeLineNumber(node); +#else + return ConfigurationErrorsException.GetLineNumber(node); +#endif + } + + /// + /// Returns the name of the file specified node is defined in. + /// + /// Node to get the file name for. + /// The name of the file specified node is defined in. + public static string GetFileName(XmlNode node) + { +#if !NET_2_0 + return ConfigurationException.GetXmlNodeFilename(node); +#else + return ConfigurationErrorsException.GetFilename(node); +#endif + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/DelegateInfo.cs b/src/Spring/Spring.Core/Util/DelegateInfo.cs new file mode 100644 index 00000000..7ebdbe57 --- /dev/null +++ b/src/Spring/Spring.Core/Util/DelegateInfo.cs @@ -0,0 +1,255 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Util +{ + /// + /// Discovers the attributes of a + /// and provides access to the + /// s metadata. + /// + /// Rick Evans + /// $Id: DelegateInfo.cs,v 1.9 2006/04/09 07:19:00 markpollack Exp $ + public sealed class DelegateInfo + { + #region Constants + + /// + /// The method name associated with a delegate invocation. + /// + private const string InvocationMethod = "Invoke"; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The event used to extract the delegate + /// from. + /// + /// + /// if the supplied is + /// . + /// + public DelegateInfo(EventInfo eventMeta) : this(eventMeta.EventHandlerType) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The delegate . + /// + /// + /// If the supplied is not a subclass of the + /// class, or is . + /// + public DelegateInfo(Type type) + { + DelegateType = type; + } + + #endregion + + #region Properties + + /// + /// The of the delegate. + /// + private Type DelegateType + { + get { return _delegateType; } + set + { + if (!DelegateInfo.IsDelegate(value)) + { + throw new ArgumentException("Not a delegate Type"); + } + _delegateType = value; + } + } + + #endregion + + #region Methods + + /// + /// Checks to see if the method encapsulated by the supplied method + /// metadata is compatible with the method signature associated with + /// this delegate type. + /// + /// The method to be checked. + /// + /// if the method signature is compatible with + /// the signature of this delegate; if not, or + /// if the supplied parameter is + /// . + /// + public bool IsSignatureCompatible(MethodInfo method) + { + if (method != null && + method.ReturnType.Equals(GetReturnType())) + { + ParameterInfo[] methodParameters = + method.GetParameters(); + Type[] delegateParameters = GetParameterTypes(); + if (methodParameters.Length == delegateParameters.Length) + { + for (int i = 0; i < methodParameters.Length; ++i) + { + if (!methodParameters[i].ParameterType. + Equals(delegateParameters[i])) + { + return false; + } + } + return true; + } + } + return false; + } + + /// + /// Gets the s of the parameters of the + /// method signature associated with this delegate type. + /// + /// + ///

    + /// This method will never return ; the returned + /// array may be empty, but it most certainly + /// will not be . + ///

    + ///
    + /// + /// A array of the parameter + /// s; or the + /// array if the method signature has no parameters. + /// + public Type[] GetParameterTypes() + { + ParameterInfo[] parameters = GetMethod().GetParameters(); + if (parameters != null + && parameters.Length > 0) + { + Type[] types = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; ++i) + { + types[i] = parameters[i].ParameterType; + } + return types; + } + return Type.EmptyTypes; + } + + /// + /// Gets the return of the + /// method signature associated with this delegate type. + /// + /// The return . + public Type GetReturnType() + { + return GetMethod().ReturnType; + } + + /// + /// Gets the metadata about the method signature associated + /// with this delegate type. + /// + /// + /// The metadata about the method signature associated + /// with this delegate type. + /// + public MethodInfo GetMethod() + { + return DelegateType.GetMethod(DelegateInfo.InvocationMethod); + } + + /// + /// Determines whether the supplied + /// is a type. + /// + /// + /// The to be checked. + /// + /// + /// if the supplied + /// is a ; + /// if not or the supplied + /// is . + /// + public static bool IsDelegate(Type type) + { + return type == null ? + false : + type.IsSubclassOf(typeof (Delegate)); + } + + /// + /// Checks if the signature of the supplied + /// is compatible with the signature expected by the supplied + /// . + /// + /// The event to be checked against. + /// + /// The method signature to check for compatibility. + /// + /// + /// if the signature of the supplied + /// is compatible with the signature + /// expected by the supplied ; + /// if not or either of the supplied + /// parameters is . + /// + /// + public static bool IsSignatureCompatible( + EventInfo eventMeta, MethodInfo handlerMethod) + { + bool compatible = false; + if (eventMeta != null + && DelegateInfo.IsDelegate(eventMeta.EventHandlerType)) + { + compatible = new DelegateInfo(eventMeta.EventHandlerType) + .IsSignatureCompatible(handlerMethod); + } + return compatible; + } + + #endregion + + #region Fields + + private Type _delegateType; + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Util/DynamicCodeManager.cs b/src/Spring/Spring.Core/Util/DynamicCodeManager.cs new file mode 100644 index 00000000..cfceb9d6 --- /dev/null +++ b/src/Spring/Spring.Core/Util/DynamicCodeManager.cs @@ -0,0 +1,144 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; + +#endregion + +namespace Spring.Util +{ + /// + /// Use this class for obtaining instances for dynamic code generation. + /// + /// + ///

    + /// The purpose of this class is to provide a simple abstraction for creating and managing dynamic assemblies. + ///

    + /// + /// Using this factory you can't define several modules within a single dynamic assembly - only a simple one2one relation between assembly/module is used. + /// + ///
    + /// + ///

    The following excerpt from demonstrates usage:

    + /// + /// public class DynamicProxyManager + /// { + /// public const string PROXY_ASSEMBLY_NAME = "Spring.Proxy"; + /// + /// public static TypeBuilder CreateTypeBuilder(string name, Type baseType) + /// { + /// // Generates type name + /// string typeName = String.Format("{0}.{1}_{2}", PROXY_ASSEMBLY_NAME, name, Guid.NewGuid().ToString("N")); + /// ModuleBuilder module = DynamicCodeManager.GetModuleBuilder(PROXY_ASSEMBLY_NAME); + /// return module.DefineType(typeName, PROXY_TYPE_ATTRIBUTES); + /// } + /// } + /// + ///
    + /// Erich Eichinger + /// $Id: DynamicCodeManager.cs,v 1.6 2007/08/08 04:00:16 bbaia Exp $ + /// + /// + /// + public sealed class DynamicCodeManager + { + private static readonly Hashtable s_moduleCache = CollectionsUtil.CreateCaseInsensitiveHashtable(); + + /// + /// prevent instantiation + /// + private DynamicCodeManager() + { + throw new InvalidOperationException(); + } + + /// + /// Returns the for the dynamic module within the specified assembly. + /// + /// + /// If the assembly does not exist yet, it will be created.
    + /// This factory caches any dynamic assembly it creates - calling GetModule() twice with + /// the same name will *not* create 2 distinct modules! + ///
    + /// The assembly-name of the module to be returned + /// the that can be used to define new types within the specified assembly + public static ModuleBuilder GetModuleBuilder( string assemblyName ) + { + lock(s_moduleCache.SyncRoot) + { + ModuleBuilder module = (ModuleBuilder) s_moduleCache[assemblyName]; + if (module == null) + { + AssemblyName an = new AssemblyName(); + an.Name = assemblyName; + + AssemblyBuilder assembly; +#if DEBUG_DYNAMIC + assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave, null, null, null, null,null, true ); + module = assembly.DefineDynamicModule(an.Name, an.Name + ".dll", true); +#else + assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run, null, null, null, null,null, true ); +#if DEBUG + module = assembly.DefineDynamicModule(an.Name, true); +#else + module = assembly.DefineDynamicModule(an.Name, false); +#endif +#endif + s_moduleCache[assemblyName] = module; + } + return module; + } + } + + /// + /// Persists the specified dynamic assembly to the file-system + /// + /// the name of the dynamic assembly to persist + /// + /// Can only be called in DEBUG_DYNAMIC mode, per ConditionalAttribute rules. + /// + [Conditional("DEBUG_DYNAMIC")] + public static void SaveAssembly( string assemblyName ) + { + AssertUtils.ArgumentHasText(assemblyName, "assemblyName"); + + ModuleBuilder module = null; + lock(s_moduleCache.SyncRoot) + { + module = (ModuleBuilder) s_moduleCache[assemblyName]; + } + + if(module == null) + { + throw new ArgumentException(string.Format("'{0}' is not a valid dynamic assembly name", assemblyName), "assemblyName"); + } + + AssemblyBuilder assembly = (AssemblyBuilder) module.Assembly; + assembly.Save(assembly.GetName().Name + ".dll"); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/EventUtils.cs b/src/Spring/Spring.Core/Util/EventUtils.cs new file mode 100644 index 00000000..a2561a94 --- /dev/null +++ b/src/Spring/Spring.Core/Util/EventUtils.cs @@ -0,0 +1,108 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Util +{ + /// + /// A utility class for raising events in a generic and consistent fashion. + /// + /// Rick Evans + /// $Id: EventUtils.cs,v 1.5 2006/04/09 07:19:00 markpollack Exp $ + public class EventRaiser + { + /// + /// Raises the event encapsulated by the supplied + /// , passing the supplied + /// to the event. + /// + /// The event to be raised. + /// The arguments to the event. + public virtual void Raise (Delegate source, params object [] arguments) + { + if (source == null) + { + return; + } + Delegate [] delegates = source.GetInvocationList (); + foreach (Delegate sink in delegates) + { + Invoke (sink, arguments); + } + } + + /// + /// Invokes the supplied , passing the supplied + /// to the sink. + /// + /// The sink to be invoked. + /// The arguments to the sink. + protected virtual void Invoke (Delegate sink, object [] arguments) + { + try + { + sink.DynamicInvoke (arguments); + } + catch (TargetInvocationException ex) + { + // unwrap the exception that actually caused the TargetInvocationException and throw that... + throw ex.GetBaseException(); + } + } + } + + /// + /// Raises events defensively. + /// + /// + ///

    + /// Raising events defensively means that as the raised event is passed to each handler, + /// any thrown by a handler will be caught and silently + /// ignored. + ///

    + ///
    + /// Rick Evans + /// $Id: EventUtils.cs,v 1.5 2006/04/09 07:19:00 markpollack Exp $ + public class DefensiveEventRaiser : EventRaiser + { + /// + /// Defensively invokes the supplied , passing the + /// supplied to the sink. + /// + /// The sink to be invoked. + /// The arguments to the sink. + protected override void Invoke (Delegate sink, object [] arguments) + { + try + { + sink.DynamicInvoke (arguments); + } + catch + { + } + } + } +} diff --git a/src/Spring/Spring.Core/Util/FatalReflectionException.cs b/src/Spring/Spring.Core/Util/FatalReflectionException.cs new file mode 100644 index 00000000..747563a5 --- /dev/null +++ b/src/Spring/Spring.Core/Util/FatalReflectionException.cs @@ -0,0 +1,92 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using Spring.Util; + +#endregion + +namespace Spring.Util +{ + /// + /// Thrown on an unrecoverable problem encountered in the + /// objects namespace or sub-namespaces, e.g. bad class or field. + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: FatalReflectionException.cs,v 1.1 2007/07/31 03:47:23 markpollack Exp $ + /// + [Serializable] + public class FatalReflectionException : ReflectionException + { + /// + /// Creates a new instance of the FatalObjectException class. + /// + public FatalReflectionException() + { + } + + /// + /// Creates a new instance of the FatalObjectException class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + public FatalReflectionException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the FatalObjectException class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public FatalReflectionException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the FatalObjectException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected FatalReflectionException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/Generic/CollectionUtils.cs b/src/Spring/Spring.Core/Util/Generic/CollectionUtils.cs new file mode 100644 index 00000000..c83f977e --- /dev/null +++ b/src/Spring/Spring.Core/Util/Generic/CollectionUtils.cs @@ -0,0 +1,137 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; + +#endregion + +namespace Spring.Util.Generic +{ + /// + /// Miscellaneous generic collection utility methods. + /// + /// + /// Mainly for internal use within the framework. + /// + /// Mark Pollack (.NET) + /// $Id: CollectionUtils.cs,v 1.2 2006/12/02 07:51:28 markpollack Exp $ + public sealed class CollectionUtils + { + #region Methods + + /// + /// Determine whether a given collection only contains + /// a single unique object + /// + /// + /// + public static bool HasUniqueObject(ICollection coll) + { + if (coll.Count == 0) + { + return false; + } + object candidate = null; + foreach (object elem in coll) + { + if (candidate == null) + { + candidate = elem; + } + else if (candidate != elem) + { + return false; + } + } + return true; + } + + /// + /// Determines whether the contains the specified . + /// + /// The collection to check. + /// The object to locate in the collection. + /// if the element is in the collection, otherwise. + public static bool Contains(ICollection collection, Object element) + { + if (collection == null) + { + throw new ArgumentNullException("Collection cannot be null."); + } + MethodInfo method; + method = collection.GetType().GetMethod("contains", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + if (null == method) + { + throw new InvalidOperationException("Collection type " + collection.GetType() + " does not implement a Contains() method."); + } + return (bool) method.Invoke(collection, new Object[] {element}); + } + + /// + /// Determines whether the collection contains all the elements in the specified collection. + /// + /// The collection to check. + /// Collection whose elements would be checked for containment. + /// true if the target collection contains all the elements of the specified collection. + public static bool ContainsAll(ICollection targetCollection, ICollection sourceCollection) + { + if (targetCollection == null || sourceCollection == null) + { + throw new ArgumentNullException("Collection cannot be null."); + } + if ( sourceCollection.Count == 0 && targetCollection.Count > 1 ) + return true; + + IEnumerator sourceCollectionEnumerator = sourceCollection.GetEnumerator(); + + bool contains = false; + + MethodInfo method; + method = targetCollection.GetType().GetMethod("containsAll", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + + if (method != null) + contains = (bool) method.Invoke(targetCollection, new Object[] {sourceCollection}); + else + { + method = targetCollection.GetType().GetMethod("Contains", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + if (method == null) + { + throw new InvalidOperationException("Target collection does not implment a Contains() or ContainsAll() method."); + } + while (sourceCollectionEnumerator.MoveNext()) + { + if ((contains = (bool) method.Invoke(targetCollection, new Object[] {sourceCollectionEnumerator.Current})) == false) + break; + } + } + return contains; + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/NumberUtils.cs b/src/Spring/Spring.Core/Util/NumberUtils.cs new file mode 100644 index 00000000..79faf987 --- /dev/null +++ b/src/Spring/Spring.Core/Util/NumberUtils.cs @@ -0,0 +1,349 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using Spring.Core.TypeConversion; + +#endregion + +namespace Spring.Util +{ + /// + /// Various utility methods relating to numbers. + /// + /// + ///

    + /// Mainly for internal use within the framework. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: NumberUtils.cs,v 1.6 2008/03/20 23:58:16 oakinger Exp $ + public sealed class NumberUtils + { + /// + /// Determines whether the supplied is an integer. + /// + /// The object to check. + /// + /// if the supplied is an integer. + /// + public static bool IsInteger(object number) + { + return (number is Int32 || number is Int16 || number is Int64 || number is UInt32 + || number is UInt16 || number is UInt64 || number is Byte || number is SByte); + } + + /// + /// Determines whether the supplied is a decimal number. + /// + /// The object to check. + /// + /// if the supplied is a decimal number. + /// + public static bool IsDecimal(object number) + { + + return (number is Single || number is Double || number is Decimal); + } + + /// + /// Determines whether the supplied is of numeric type. + /// + /// The object to check. + /// + /// true if the specified object is of numeric type; otherwise, false. + /// + public static bool IsNumber(object number) + { + return (IsInteger(number) || IsDecimal(number)); + } + + /// + /// Determines whether the supplied can be converted to an integer. + /// + /// The object to check. + /// + /// if the supplied can be converted to an integer. + /// + public static bool CanConvertToInteger(object number) + { + TypeConverter converter = TypeDescriptor.GetConverter(number); + return (converter.CanConvertTo(typeof(Int32)) + || converter.CanConvertTo(typeof(Int16)) + || converter.CanConvertTo(typeof(Int64)) + || converter.CanConvertTo(typeof(UInt16)) + || converter.CanConvertTo(typeof(UInt64)) + || converter.CanConvertTo(typeof(Byte)) + || converter.CanConvertTo(typeof(SByte)) + ); + } + + /// + /// Determines whether the supplied can be converted to an integer. + /// + /// The object to check. + /// + /// if the supplied can be converted to an integer. + /// + public static bool CanConvertToDecimal(object number) + { + TypeConverter converter = TypeDescriptor.GetConverter(number); + return (converter.CanConvertTo(typeof(Single)) + || converter.CanConvertTo(typeof(Double)) + || converter.CanConvertTo(typeof(Decimal)) + ); + } + + /// + /// Determines whether the supplied can be converted to a number. + /// + /// The object to check. + /// + /// true if the specified object is decimal number; otherwise, false. + /// + public static bool CanConvertToNumber(object number) + { + return (CanConvertToInteger(number) || CanConvertToDecimal(number)); + } + + /// + /// Is the supplied equal to zero (0)? + /// + /// The number to check. + /// + /// id the supplied is equal to zero (0). + /// + public static bool IsZero(object number) + { + if (number is Int32) return ((Int32) number) == 0; + else if (number is Int16) return ((Int16) number) == 0; + else if (number is Int64) return ((Int64) number) == 0; + else if (number is UInt16) return ((Int32) number) == 0; + else if (number is UInt32) return ((Int64) number) == 0; + else if (number is UInt64) return (Convert.ToDecimal(number) == 0); + else if (number is Byte) return ((Int16) number) == 0; + else if (number is SByte) return ((Int16) number) == 0; + else if (number is Single) return ((Single) number) == 0f; + else if (number is Double) return ((Double) number) == 0d; + else if (number is Decimal) return ((Decimal) number) == 0m; + return false; + } + + /// + /// Negates the supplied . + /// + /// The number to negate. + /// The supplied negated. + /// + /// If the supplied is not a supported numeric type. + /// + public static object Negate(object number) + { + if (number is Int32) return -((Int32) number); + else if (number is Int16) return -((Int16) number); + else if (number is Int64) return -((Int64) number); + else if (number is UInt16) return -((Int32) number); + else if (number is UInt32) return -((Int64) number); + else if (number is UInt64) return -(Convert.ToDecimal(number)); + else if (number is Byte) return -((Int16) number); + else if (number is SByte) return -((Int16) number); + else if (number is Single) return -((Single) number); + else if (number is Double) return -((Double) number); + else if (number is Decimal) return -((Decimal) number); + else + { + throw new ArgumentException(string.Format("'{0}' is not one of the supported numeric types.", number)); + } + } + + /// + /// Adds the specified numbers. + /// + /// The first number. + /// The second number. + public static object Add(object m, object n) + { + CoerceTypes(ref m, ref n); + + if (n is Int32) return (Int32) m + (Int32) n; + else if (n is Int16) return (Int16) m + (Int16) n; + else if (n is Int64) return (Int64) m + (Int64) n; + else if (n is UInt16) return (UInt16) m + (UInt16) n; + else if (n is UInt32) return (UInt32) m + (UInt32) n; + else if (n is UInt64) return (UInt64) m + (UInt64) n; + else if (n is Byte) return (Byte) m + (Byte) n; + else if (n is SByte) return (SByte) m + (SByte) n; + else if (n is Single) return (Single) m + (Single) n; + else if (n is Double) return (Double) m + (Double) n; + else if (n is Decimal) return (Decimal) m + (Decimal) n; + + return null; + } + + /// + /// Subtracts the specified numbers. + /// + /// The first number. + /// The second number. + public static object Subtract(object m, object n) + { + CoerceTypes(ref m, ref n); + + if (n is Int32) return (Int32) m - (Int32) n; + else if (n is Int16) return (Int16) m - (Int16) n; + else if (n is Int64) return (Int64) m - (Int64) n; + else if (n is UInt16) return (UInt16) m - (UInt16) n; + else if (n is UInt32) return (UInt32) m - (UInt32) n; + else if (n is UInt64) return (UInt64) m - (UInt64) n; + else if (n is Byte) return (Byte) m - (Byte) n; + else if (n is SByte) return (SByte) m - (SByte) n; + else if (n is Single) return (Single) m - (Single) n; + else if (n is Double) return (Double) m - (Double) n; + else if (n is Decimal) return (Decimal) m - (Decimal) n; + + return null; + } + + /// + /// Multiplies the specified numbers. + /// + /// The first number. + /// The second number. + public static object Multiply(object m, object n) + { + CoerceTypes(ref m, ref n); + + if (n is Int32) return (Int32) m*(Int32) n; + else if (n is Int16) return (Int16) m*(Int16) n; + else if (n is Int64) return (Int64) m*(Int64) n; + else if (n is UInt16) return (UInt16) m*(UInt16) n; + else if (n is UInt32) return (UInt32) m*(UInt32) n; + else if (n is UInt64) return (UInt64) m*(UInt64) n; + else if (n is Byte) return (Byte) m*(Byte) n; + else if (n is SByte) return (SByte) m*(SByte) n; + else if (n is Single) return (Single) m*(Single) n; + else if (n is Double) return (Double) m*(Double) n; + else if (n is Decimal) return (Decimal) m*(Decimal) n; + + return null; + } + + /// + /// Divides the specified numbers. + /// + /// The first number. + /// The second number. + public static object Divide(object m, object n) + { + CoerceTypes(ref m, ref n); + + if (n is Int32) return (Int32) m/(Int32) n; + else if (n is Int16) return (Int16) m/(Int16) n; + else if (n is Int64) return (Int64) m/(Int64) n; + else if (n is UInt16) return (UInt16) m/(UInt16) n; + else if (n is UInt32) return (UInt32) m/(UInt32) n; + else if (n is UInt64) return (UInt64) m/(UInt64) n; + else if (n is Byte) return (Byte) m/(Byte) n; + else if (n is SByte) return (SByte) m/(SByte) n; + else if (n is Single) return (Single) m/(Single) n; + else if (n is Double) return (Double) m/(Double) n; + else if (n is Decimal) return (Decimal) m/(Decimal) n; + + return null; + } + + /// + /// Calculates remainder for the specified numbers. + /// + /// The first number (dividend). + /// The second number (divisor). + public static object Modulus(object m, object n) + { + CoerceTypes(ref m, ref n); + + if (n is Int32) return (Int32) m%(Int32) n; + else if (n is Int16) return (Int16) m%(Int16) n; + else if (n is Int64) return (Int64) m%(Int64) n; + else if (n is UInt16) return (UInt16) m%(UInt16) n; + else if (n is UInt32) return (UInt32) m%(UInt32) n; + else if (n is UInt64) return (UInt64) m%(UInt64) n; + else if (n is Byte) return (Byte) m%(Byte) n; + else if (n is SByte) return (SByte) m%(SByte) n; + else if (n is Single) return (Single) m%(Single) n; + else if (n is Double) return (Double) m%(Double) n; + else if (n is Decimal) return (Decimal) m%(Decimal) n; + + return null; + } + + /// + /// Raises first number to the power of the second one. + /// + /// The first number. + /// The second number. + public static object Power(object m, object n) + { + return Math.Pow(Convert.ToDouble(m), Convert.ToDouble(n)); + } + + /// + /// Coerces the types so they can be compared. + /// + /// The right. + /// The left. + public static void CoerceTypes(ref object m, ref object n) + { + TypeCode leftTypeCode = Convert.GetTypeCode(m); + TypeCode rightTypeCode = Convert.GetTypeCode(n); + + if (leftTypeCode > rightTypeCode) + { + n = Convert.ChangeType(n, leftTypeCode); + } + else + { + m = Convert.ChangeType(m, rightTypeCode); + } + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private NumberUtils() + { + } + + // CLOVER:ON + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/ObjectUtils.cs b/src/Spring/Spring.Core/Util/ObjectUtils.cs new file mode 100644 index 00000000..9596fe6b --- /dev/null +++ b/src/Spring/Spring.Core/Util/ObjectUtils.cs @@ -0,0 +1,492 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using System.Reflection; +using System.Runtime.Remoting; +using Spring.Objects; + +#endregion + +namespace Spring.Util +{ + /// + /// Helper methods with regard to objects, types, properties, etc. + /// + /// + ///

    + /// Not intended to be used directly by applications. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: ObjectUtils.cs,v 1.9 2007/12/29 00:31:00 markpollack Exp $ + public sealed class ObjectUtils + { + #region Constants + + /// + /// An empty object array. + /// + public static readonly object[] EmptyObjects = new object[] {}; + + #endregion + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private ObjectUtils() + { + } + + // CLOVER:ON + + #endregion + + #region Methods + + /// + /// Instantiates the type using the assembly specified to load the type. + /// + /// This is a convenience in the case of needing to instantiate a type but not + /// wanting to specify in the string the version, culture and public key token. + /// The assembly. + /// Name of the type. + /// + /// + /// If the or is + /// + /// + /// If cannot load the type from the assembly or the call to InstantiateType(Type) fails. + /// + public static object InstantiateType(Assembly assembly, string typeName) + { + AssertUtils.ArgumentNotNull(assembly, "assembly"); + AssertUtils.ArgumentNotNull(typeName, "typeName"); + Type resolvedType = assembly.GetType(typeName, false, false); + if (resolvedType == null) + { + throw new FatalReflectionException( + string.Format( + CultureInfo.InvariantCulture, "Cannot load type named [{0}] from assembly [{1}].", typeName, assembly)); + } + return InstantiateType(resolvedType); + } + /// + /// Convenience method to instantiate a using + /// its no-arg constructor. + /// + /// + ///

    + /// As this method doesn't try to instantiate s + /// by name, it should avoid loading issues. + ///

    + ///
    + /// + /// The to instantiate* + /// + /// A new instance of the . + /// + /// If the is + /// + /// + /// If the is an abstract class, an interface, + /// an open generic type or does not have a public no-argument constructor. + /// + public static object InstantiateType(Type type) + { + AssertUtils.ArgumentNotNull(type, "type"); + + ConstructorInfo constructor = GetZeroArgConstructorInfo(type); + return ObjectUtils.InstantiateType(constructor, ObjectUtils.EmptyObjects); + } + + /// + /// Gets the zero arg ConstructorInfo object, if the type offers such functionality. + /// + /// The type. + /// Zero argument ConstructorInfo + /// + /// If the type is an interface, abstract, open generic type, or does not have a zero-arg constructor. + /// + public static ConstructorInfo GetZeroArgConstructorInfo(Type type) + { + IsInstantiable(type); + ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes); + if (constructor == null) + { + throw new FatalReflectionException( + string.Format( + CultureInfo.InvariantCulture, "Cannot instantiate a class that does not have a public no-argument constructor [{0}].", type)); + } + return constructor; + } + + /// + /// Determines whether the specified type is instantiable, i.e. not an interface, abstract class or contains + /// open generic type parameters. + /// + /// The type. + public static void IsInstantiable(Type type) + { + if (type.IsInterface) + { + throw new FatalReflectionException( + string.Format( + CultureInfo.InvariantCulture, "Cannot instantiate an interface [{0}].", type)); + } + if (type.IsAbstract) + { + throw new FatalReflectionException( + string.Format( + CultureInfo.InvariantCulture, "Cannot instantiate an abstract class [{0}].", type)); + } +#if NET_2_0 + if (type.ContainsGenericParameters) + { + throw new FatalReflectionException( + string.Format( + CultureInfo.InvariantCulture, "Cannot instantiate an open generic type [{0}].", type)); + } +#endif + } + + /// + /// Convenience method to instantiate a using + /// the given constructor. + /// + /// + ///

    + /// As this method doesn't try to instantiate s + /// by name, it should avoid loading issues. + ///

    + ///
    + /// + /// The constructor to use for the instantiation. + /// + /// + /// The arguments to be passed to the constructor. + /// + /// A new instance. + /// + /// If the is + /// + /// + /// If the 's declaring type is an abstract class, + /// an interface, an open generic type or does not have a public no-argument constructor. + /// + public static object InstantiateType(ConstructorInfo constructor, object[] arguments) + { + AssertUtils.ArgumentNotNull(constructor, "constructor"); + + if (constructor.DeclaringType.IsInterface) + { + throw new FatalReflectionException( + string.Format( + CultureInfo.InvariantCulture, "Cannot instantiate an interface [{0}].", constructor.DeclaringType)); + } + if (constructor.DeclaringType.IsAbstract) + { + throw new FatalReflectionException( + string.Format( + CultureInfo.InvariantCulture, "Cannot instantiate an abstract class [{0}].", constructor.DeclaringType)); + } +#if NET_2_0 + if (constructor.DeclaringType.ContainsGenericParameters) + { + throw new FatalReflectionException( + string.Format( + CultureInfo.InvariantCulture, "Cannot instantiate an open generic type [{0}].", constructor.DeclaringType)); + } +#endif + try + { + return constructor.Invoke(arguments); + } + catch (Exception ex) + { + Type ctorType = constructor.DeclaringType; + throw new FatalReflectionException( + string.Format( + CultureInfo.InvariantCulture, + "Cannot instantiate Type [{0}] using ctor [{1}] : '{2}'", + constructor.DeclaringType, constructor, ex.Message), + ex); + } + } + + /// + /// Checks whether the supplied is not a transparent proxy and is + /// assignable to the supplied . + /// + /// + ///

    + /// Neccessary when dealing with server-activated remote objects, because the + /// object is of the type TransparentProxy and regular is testing for assignable + /// types does not work. + ///

    + ///

    + /// Transparent proxy instances always return when tested + /// with the 'is' operator (C#). This method only checks if the object + /// is assignable to the type if it is not a transparent proxy. + ///

    + ///
    + /// The target to be checked. + /// The value that should be assigned to the type. + /// + /// if the supplied is not a + /// transparent proxy and is assignable to the supplied . + /// + public static bool IsAssignableAndNotTransparentProxy(Type type, object instance) + { + if (!RemotingServices.IsTransparentProxy(instance)) + { + return IsAssignable(type, instance); + } + return false; + } + + /// + /// Determine if the given is assignable from the + /// given value, assuming setting by reflection. + /// + /// + ///

    + /// Considers primitive wrapper classes as assignable to the + /// corresponding primitive types. + ///

    + ///

    + /// For example used in an object factory's constructor resolution. + ///

    + ///
    + /// The target . + /// The value that should be assigned to the type. + /// True if the type is assignable from the value. + public static bool IsAssignable(Type type, object obj) + { + return (type.IsInstanceOfType(obj) || + (!type.IsPrimitive && obj == null) || + (type.Equals(typeof (bool)) && obj is Boolean) || + (type.Equals(typeof (byte)) && obj is Byte) || + (type.Equals(typeof (char)) && obj is Char) || + (type.Equals(typeof (sbyte)) && obj is SByte) || + (type.Equals(typeof (int)) && obj is Int32) || + (type.Equals(typeof (short)) && obj is Int16) || + (type.Equals(typeof (long)) && obj is Int64) || + (type.Equals(typeof (float)) && obj is Single) || + (type.Equals(typeof (double)) && obj is Double)); + } + + /// + /// Check if the given represents a + /// "simple" property, + /// i.e. a primitive, a , a + /// , or a corresponding array. + /// + /// + ///

    + /// Used to determine properties to check for a "simple" dependency-check. + ///

    + ///
    + /// + /// The to check. + /// + public static bool IsSimpleProperty(Type type) + { + return type.IsPrimitive + || type.Equals(typeof (string)) + || type.Equals(typeof (string[])) + || IsPrimitiveArray(type) + || type.Equals(typeof (Type)) + || type.Equals(typeof (Type[])); + } + + /// + /// Check if the given class represents a primitive array, + /// i.e. boolean, byte, char, short, int, long, float, or double. + /// + public static bool IsPrimitiveArray(Type type) + { + return typeof (bool[]).Equals(type) + || typeof (sbyte[]).Equals(type) + || typeof (char[]).Equals(type) + || typeof (short[]).Equals(type) + || typeof (int[]).Equals(type) + || typeof (long[]).Equals(type) + || typeof (float[]).Equals(type) + || typeof (double[]).Equals(type); + } + + /// + /// Determine if the given objects are equal, returning + /// if both are respectively + /// if only one is . + /// + /// The first object to compare. + /// The second object to compare. + /// + /// if the given objects are equal. + /// + public static bool NullSafeEquals(object o1, object o2) + { + return (o1 == o2 || (o1 != null && o1.Equals(o2))); + } + + /// + /// Returns the first element in the supplied . + /// + /// + /// The to use to enumerate + /// elements. + /// + /// + /// The first element in the supplied . + /// + /// + /// If the supplied did not have any elements. + /// + public static object EnumerateFirstElement(IEnumerator enumerator) + { + return ObjectUtils.EnumerateElementAtIndex(enumerator, 0); + } + + /// + /// Returns the first element in the supplied . + /// + /// + /// The to use to enumerate + /// elements. + /// + /// + /// The first element in the supplied . + /// + /// + /// If the supplied did not have any elements. + /// + /// + /// If the supplied is . + /// + public static object EnumerateFirstElement(IEnumerable enumerable) + { + AssertUtils.ArgumentNotNull(enumerable, "enumerable"); + return ObjectUtils.EnumerateElementAtIndex(enumerable.GetEnumerator(), 0); + } + + /// + /// Returns the element at the specified index using the supplied + /// . + /// + /// + /// The to use to enumerate + /// elements until the supplied is reached. + /// + /// + /// The index of the element in the enumeration to return. + /// + /// + /// The element at the specified index using the supplied + /// . + /// + /// + /// If the supplied was less than zero, or the + /// supplied did not contain enough elements + /// to be able to reach the supplied . + /// + public static object EnumerateElementAtIndex(IEnumerator enumerator, int index) + { + if (index < 0) + { + throw new ArgumentOutOfRangeException(); + } + object element = null; + int i = 0; + while (enumerator.MoveNext()) + { + element = enumerator.Current; + if (++i > index) + { + break; + } + } + if (i < index) + { + throw new ArgumentOutOfRangeException(); + } + return element; + } + + /// + /// Returns the element at the specified index using the supplied + /// . + /// + /// + /// The to use to enumerate + /// elements until the supplied is reached. + /// + /// + /// The index of the element in the enumeration to return. + /// + /// + /// The element at the specified index using the supplied + /// . + /// + /// + /// If the supplied was less than zero, or the + /// supplied did not contain enough elements + /// to be able to reach the supplied . + /// + /// + /// If the supplied is . + /// + public static object EnumerateElementAtIndex(IEnumerable enumerable, int index) + { + AssertUtils.ArgumentNotNull(enumerable, "enumerable"); + return ObjectUtils.EnumerateElementAtIndex(enumerable.GetEnumerator(), index); + } + + #endregion + + /// + /// Gets the qualified name of the given method, consisting of + /// fully qualified interface/class name + "." method name. + /// + /// The method. + /// qualified name of the method. + public static string GetQualifiedMethodName(MethodInfo method) + { + AssertUtils.ArgumentNotNull(method,"method", "MethodInfo must not be null"); + return method.DeclaringType.FullName + "." + method.Name; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/PathMatcher.cs b/src/Spring/Spring.Core/Util/PathMatcher.cs new file mode 100644 index 00000000..f604275e --- /dev/null +++ b/src/Spring/Spring.Core/Util/PathMatcher.cs @@ -0,0 +1,229 @@ +//// +#region License + +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +#region Imports + +using System; +using System.Text; +using System.Text.RegularExpressions; + +#endregion + +namespace Spring.Util +{ + /// + /// Support matching of file system paths in a manner similar to that of the + /// NAnt FileSet. + /// + /// + ///

    + /// Any (back)slashes are converted to forward slashes. + ///

    + ///
    + /// + /// + /// // true + /// PathMatcher.Match("c:/*.bat", @"c:\autoexec.bat"); + /// PathMatcher.Match("c:\fo*\*.bat", @"c:/foobar/autoexec.bat"); + /// PathMatcher.Match("c:\fo?\*.bat", @"c:/foo/autoexec.bat"); + /// // false + /// PathMatcher.Match("c:\fo?\*.bat", @"c:/fo/autoexec.bat"); + /// + /// + /// Federico Spinazzi + /// $Id: PathMatcher.cs,v 1.7 2007/02/23 18:46:41 markpollack Exp $ + public sealed class PathMatcher + { + private const string AllFilesInThisDirectory = "*.*"; + + /// + /// Determines if a given path matches a NAnt-like pattern. + /// + /// + /// A forward or back-slashed fileset-like pattern. + /// + /// A forward or back-slashed full path. + /// should the match consider the case + /// + /// if the path is matched by the pattern; + /// otherwise . + /// + //// + public static bool Match(string pattern, string path, bool ignoreCase) + //// + { + return Match(ToLower(pattern), ToLower(path)); + } + + /// + /// Determines if a given path matches a NAnt-like pattern. + /// + /// + /// A forward or back-slashed fileset-like pattern. + /// + /// A forward or back-slashed full path. + /// + /// if the path is matched by the pattern; + /// otherwise . + /// + //// + public static bool Match(string pattern, string path) + //// + { + pattern = ForwardifySlashes(pattern); + path = ForwardifySlashes(path); + + if (MatchAll(pattern)) + return true; + + String regex = BuildRegex(pattern); + return Regex.Match(path, regex).Success; + } + + /// + /// Replaces back(slashes) with forward slashes. + /// + /// + /// The path or the pattern to modify. + /// + /// A forward-slashed string. + public static string ForwardifySlashes(string path) + { + return path.Replace("\\", "/"); + } + + /// + /// Helper method to convert a NAnt-like pattern into the + /// appropriate pattern for a regular expression. + /// + /// The NAnt-like pattern. + /// A regex-compatible pattern. + public static String BuildRegex(string pattern) + { + if (AllFilesInThisDirectory.Equals(pattern)) + { + return "^[^/]*$"; + } + string[] parts = pattern.Split('/'); + int indexOfLastSplittedPart = parts.Length - 1; + int indexOfTheCurrentSplittedPart = 0; + StringBuilder regex = new StringBuilder(); + foreach (string currentSplittedPart in parts) + { + bool currentPartIsTheLast = indexOfTheCurrentSplittedPart == indexOfLastSplittedPart; + string partToAdd = currentSplittedPart; + switch (currentSplittedPart) + { + case "**": + partToAdd = TranslateDoubleAsterisk(currentPartIsTheLast); + break; + default: + partToAdd = TranslateDot(partToAdd); + partToAdd = TranslateAsterisk(partToAdd); + partToAdd = TranslateQuestionMark(partToAdd); + partToAdd = TranslateLiteral(partToAdd, currentPartIsTheLast); + break; + } + regex.Append(partToAdd); + indexOfTheCurrentSplittedPart++; + } + return regex.ToString(); + } + + private static string ToLower(string pattern) + { + return pattern == null ? pattern : pattern.ToLower(); + } + + private static string TranslateLiteral(string part, bool isLastPart) + { + if (isLastPart) + { + return part + "$"; + } + else + { + return part + "/?"; + } + } + + private static string TranslateDoubleAsterisk(bool isLastPart) + { + string slashTerminated = "(.*/?(?<=/))"; + if (isLastPart) + slashTerminated = String.Format("($|(?<=/){0}*)", slashTerminated); + return slashTerminated; + } + + private static string TranslateQuestionMark(string thisPart) + { + return thisPart.Replace("?", "[^./]"); + } + + private static string TranslateAsterisk(string thisPart) + { + return thisPart.Replace("*", "[^/]*"); + } + + private static string TranslateDot(string thisPart) + { + return thisPart.Replace(".", @"\."); + } + + private static bool MatchAll(string pattern) + { + if (AllFilesInThisDirectory.Equals(pattern)) + { + return false; + } + + foreach (char c in pattern) + { + if (c != '*' && c != '/' && c != '.') + { + return false; + } + } + return true; + } + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private PathMatcher() + { + } + + // CLOVER:ON + + #endregion + } +} +////
    \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/PatternMatchUtils.cs b/src/Spring/Spring.Core/Util/PatternMatchUtils.cs new file mode 100644 index 00000000..74a53a7d --- /dev/null +++ b/src/Spring/Spring.Core/Util/PatternMatchUtils.cs @@ -0,0 +1,99 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Text.RegularExpressions; + +#endregion + +namespace Spring.Util +{ + /// Utility methods for simple pattern matching, in particular for + /// Spring's typical "xxx*", "*xxx" and "*xxx*" pattern styles. + /// + /// Juergen Hoeller + /// Mark Pollack + /// $Id: PatternMatchUtils.cs,v 1.4 2007/07/31 18:16:25 bbaia Exp $ + public abstract class PatternMatchUtils + { + /// Match a String against the given pattern, supporting the following simple + /// pattern styles: "xxx*", "*xxx" and "*xxx*" matches, as well as direct equality. + /// + /// the pattern to match against + /// + /// the String to match + /// + /// whether the String matches the given pattern + /// + public static bool SimpleMatch(System.String pattern, System.String str) + { + if (ObjectUtils.NullSafeEquals(pattern, str) || "*".Equals(pattern)) + { + return true; + } + if (pattern == null || str == null) + { + return false; + } + if (pattern.StartsWith("*") && pattern.EndsWith("*") && + str.IndexOf(pattern.Substring(1, (pattern.Length - 1) - (1))) != -1) + { + return true; + } + if (pattern.StartsWith("*") && str.EndsWith(pattern.Substring(1, (pattern.Length) - (1)))) + { + return true; + } + if (pattern.EndsWith("*") && str.StartsWith(pattern.Substring(0, (pattern.Length - 1) - (0)))) + { + return true; + } + return false; + } + + /// Match a String against the given patterns, supporting the following simple + /// pattern styles: "xxx*", "*xxx" and "*xxx*" matches, as well as direct equality. + /// + /// the patterns to match against + /// + /// the String to match + /// + /// whether the String matches any of the given patterns + /// + public static bool SimpleMatch(System.String[] patterns, System.String str) + { + if (patterns != null) + { + for (int i = 0; i < patterns.Length; i++) + { + + if (SimpleMatch(patterns[i], str)) + { + return true; + } + } + } + return false; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/Properties.cs b/src/Spring/Spring.Core/Util/Properties.cs new file mode 100644 index 00000000..6a10b56f --- /dev/null +++ b/src/Spring/Spring.Core/Util/Properties.cs @@ -0,0 +1,325 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.IO; + +#endregion + +namespace Spring.Util +{ + /// + /// An implementation of the Java Properties class. + /// + /// Simon White + /// $Id: Properties.cs,v 1.16 2007/07/31 18:16:26 bbaia Exp $ + [Serializable] + public class Properties : Hashtable + { + #region Constants + + private const string Comments = "#!"; + private const string Separators = ":="; + private const string Whitespace = " \t\r\n"; + private const string WhitespaceWithSeparators = Whitespace + Separators; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates an empty property list with no default values. + /// + public Properties() + { + } + + /// + /// Creates a property list with the specified initial properties. + /// + /// The initial properties. + public Properties(Properties p) : base(p) + { + } + + #endregion + + #region Methods + + /// + /// Reads a property list (key and element pairs) from the input stream. + /// + /// The stream to load from. + public void Load(Stream stream) + { + Load(this, stream); + } + + /// + /// Reads a property list (key and element pairs) from a text reader. + /// + /// The text reader to load from. + public void Load(TextReader textReader) + { + Load(this, textReader); + } + + /// + /// Reads a property list (key and element pairs) from the input stream. + /// + /// the dictionary to put it in + /// The stream to load from. + public static void Load(IDictionary dictionary, Stream stream) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + Load(dictionary, streamReader); + } + } + + /// + /// Reads a property list (key and element pairs) from a text reader. + /// + /// the dictionary to put it in + /// The text reader to load from. + public static void Load(IDictionary dictionary, TextReader textReader) + { + bool isContinuation = false; + string key = null; + string value = null; + string line = null; + while ((line = textReader.ReadLine()) != null) + { + line = RemoveLeadingWhitespace(line); + if (line != null && Comments.IndexOf(line[0]) == -1) + { + if (!isContinuation) + { + string[] keyvalue = SplitLine(line); + if (keyvalue == null) + { + continue; + } + key = keyvalue[0]; + value = keyvalue[1]; + + if (value.EndsWith("\\")) + { + value = value.Substring(0, value.Length - 1); + isContinuation = true; + } + else + { + dictionary[key] = StringUtils.ConvertEscapedCharacters(value); + } + } + else + { + if (line.EndsWith("\\")) + { + value += line.Substring(0, line.Length - 1); + } + else + { + value += line; + isContinuation = false; + dictionary[key] = StringUtils.ConvertEscapedCharacters(value); + } + } + } + } + } + + /// + /// Strips whitespace from the front of the specified string. + /// + /// The string. + /// The string with all leading whitespace removed. + private static string RemoveLeadingWhitespace(string line) + { + string trimmed = null; + for (int i = 0; i < line.Length; i++) + { + if (Whitespace.IndexOf(line[i]) == -1) + { + trimmed = line.Substring(i); + break; + } + } + return trimmed; + } + + /// + /// Splits the specified string into a key / value pair. + /// + /// The line to split. + /// An array containing the key / value pair. + private static string[] SplitLine(string line) + { + string key = null; + string value = null; + + int index = 0; + int len = line.Length; + for (; index < len; index++) + { + if (WhitespaceWithSeparators.IndexOf(line[index]) != -1) + { + if (line[index - 1].Equals('\\')) + { + line = line.Remove(index - 1, 1); + len--; + index--; + } + else + { + key = line.Substring(0, index); + break; + } + } + } + + // got key, now find the start of the value + // first ignore leading whitespace and initial separator + // (if one's there) + index++; + if (index >= len) + { + return null; + } + value = line.Substring(index); + value = RemoveLeadingWhitespace(value); + + if (Separators.IndexOf(value[0]) != -1) + { + value = value.Substring(1); + value = RemoveLeadingWhitespace(value); + } + + return new string[] {key, value}; + } + + /// + /// Searches for the property with the specified key in this property list. + /// + /// The key. + /// The property, or null if the key was not found. + public string GetProperty(string key) + { + return this[key] as string; + } + + /// + /// Searches for the property with the specified key in this property list. + /// + /// The key. + /// + /// The default value to be returned if the key is not found. + /// + /// The property, or the default value. + public string GetProperty(string key, string def) + { + string val = this[key] as string; + return (val != null ? val : def); + } + + /// + /// Writes this property list out to the specified stream. + /// + /// The stream to write to. + public void List(Stream stream) + { + using (StreamWriter sw = new StreamWriter(stream)) + { + foreach (DictionaryEntry de in this) + { + sw.WriteLine(de.Key + "=" + de.Value); + } + } + } + + /// + /// Sets the specified property key / value pair. + /// + /// The key. + /// The value. + public void SetProperty(string key, string theValue) + { + this[key] = theValue; + } + + /// + /// Writes the properties in this instance out to the supplied stream. + /// + /// The stream to write to. + /// Arbitrary header information. + public void Store(Stream stream, string header) + { + using (StreamWriter sw = new StreamWriter(stream)) + { + sw.WriteLine(header); + + foreach (DictionaryEntry de in this) + { + sw.WriteLine(de.Key + "=" + de.Value); + } + } + } + + #endregion + + #region IDictionary Members + + /// + /// Adds the specified key / object pair to this collection. + /// + public override object this[object key] + { + get { return base[key as string]; } + set { base[key as string] = value as string; } + } + + /// + /// Removes the key / value pair identified by the supplied key. + /// + /// + /// The key identifying the key / value pair to be removed. + /// + public override void Remove(object key) + { + base.Remove(key as string); + } + + /// + /// Adds the specified key / object pair to this collection. + /// + /// The key. + /// The value. + public override void Add(object key, object value) + { + base.Add(key as string, value as string); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/ReflectionException.cs b/src/Spring/Spring.Core/Util/ReflectionException.cs new file mode 100644 index 00000000..5c069dc3 --- /dev/null +++ b/src/Spring/Spring.Core/Util/ReflectionException.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Util +{ + /// + /// Superclass for all exceptions thrown in the Objects namespace and sub-namespaces. + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: ReflectionException.cs,v 1.1 2007/07/31 03:47:23 markpollack Exp $ + /// + [Serializable] + public class ReflectionException : ApplicationException + { + #region Constructor (s) / Destructor + /// Creates a new instance of the ObjectsException class. + public ReflectionException() + { + } + + /// + /// Creates a new instance of the ObjectsException class. with the specified message. + /// + /// + /// A message about the exception. + /// + public ReflectionException (string message) : base(message) + { + } + + /// + /// Creates a new instance of the ObjectsException class with the specified message + /// and root cause. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ReflectionException (string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the ObjectsException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ReflectionException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + } + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/ReflectionUtils.cs b/src/Spring/Spring.Core/Util/ReflectionUtils.cs new file mode 100644 index 00000000..f0c07c7b --- /dev/null +++ b/src/Spring/Spring.Core/Util/ReflectionUtils.cs @@ -0,0 +1,1474 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +#if NET_2_0 +using System.Collections.Generic; +using System.Collections.ObjectModel; +#endif +using System.Globalization; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Runtime.CompilerServices; + +#endregion + +namespace Spring.Util +{ + /// + /// Various reflection related methods that are missing from the standard library. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Aleksandar Seovic (.NET) + /// Stan Dvoychenko (.NET) + /// Bruno Baia (.NET) + /// $Id: ReflectionUtils.cs,v 1.59 2008/05/13 14:22:46 oakinger Exp $ + public sealed class ReflectionUtils + { + /// + /// Convenience value that will + /// match all private and public, static and instance members on a class + /// in a case inSenSItivE fashion. + /// + public const BindingFlags AllMembersCaseInsensitiveFlags = BindingFlags.Public | + BindingFlags.NonPublic | BindingFlags.Instance + | BindingFlags.Static + | BindingFlags.IgnoreCase; + + /// + /// Returns signature for the specified , method name and argument + /// s. + /// + /// The the method is in. + /// The method name. + /// + /// The argument s. + /// + /// The method signature. + public static string GetSignature( + Type type, string method, Type[] argumentTypes) + { + StringBuilder sb = new StringBuilder(); + sb.Append(type.FullName).Append("::").Append(method).Append("("); + string separator = ""; + for (int i = 0; i < argumentTypes.Length; i++) + { + sb.Append(separator).Append(argumentTypes[i].FullName); + separator = ","; + } + sb.Append(")"); + return sb.ToString(); + } + + + /// + /// Returns method for the specified , method + /// name and argument + /// s. + /// + /// Searches with BindingFlags + /// + /// The target to find the method on. + /// + /// The method to find. + /// + /// The argument s. May be + /// if the method has no arguments. + /// + /// The target method. + public static MethodInfo GetMethod( + Type targetType, string method, Type[] argumentTypes) + { + AssertUtils.ArgumentNotNull(targetType, "Type must not be null"); + // try method exactly as specified first... + MethodInfo retMethod = targetType.GetMethod( + method, + ReflectionUtils.AllMembersCaseInsensitiveFlags, + null, + argumentTypes == null ? Type.EmptyTypes : argumentTypes, + null); + + if (retMethod == null) + { + // try explicit interface implementation... + int idx = method.LastIndexOf('.'); + if (idx > -1) + { + method = method.Substring(idx + 1); + retMethod = ReflectionUtils.GetMethod(targetType, method, argumentTypes); + } + } + return retMethod; + } + + /// + /// Returns an array of parameter s for the specified method + /// or constructor. + /// + /// The method (or constructor). + /// An array containing the parameter s. + /// + /// If is . + /// + public static Type[] GetParameterTypes(MethodBase method) + { + AssertUtils.ArgumentNotNull(method, "method"); + return GetParameterTypes(method.GetParameters()); + } + + /// + /// Returns an array of parameter s for the + /// specified parameter info array. + /// + /// The parameter info array. + /// An array containing parameter s. + /// + /// If is or any of the + /// elements is . + /// + public static Type[] GetParameterTypes(ParameterInfo[] args) + { + AssertUtils.ArgumentNotNull(args, "args"); + Type[] types = new Type[args.Length]; + for (int i = 0; i < args.Length; i++) + { + types[i] = args[i].ParameterType; + } + return types; + } + +#if NET_2_0 + /// + /// Returns an array of s that represent + /// the names of the generic type parameter. + /// + /// The method. + /// An array containing the parameter names. + /// + /// If is . + /// + public static string[] GetGenericParameterNames(MethodInfo method) + { + AssertUtils.ArgumentNotNull(method, "method"); + return GetGenericParameterNames(method.GetGenericArguments()); + } + + /// + /// Returns an array of s that represent + /// the names of the generic type parameter. + /// + /// The parameter info array. + /// An array containing parameter names. + /// + /// If is or any of the + /// elements is . + /// + public static string[] GetGenericParameterNames(Type[] args) + { + AssertUtils.ArgumentNotNull(args, "args"); + string[] names = new string[args.Length]; + for (int i = 0; i < args.Length; i++) + { + names[i] = args[i].Name; + } + return names; + } +#endif + + /// + /// From a given list of methods, selects the method having an exact match on the given ' types. + /// + /// the list of methods to choose from + /// the arguments to the method + /// the method matching exactly the passed ' types + /// + /// If more than 1 matching methods are found in the list. + /// + public static MethodInfo GetMethodByArgumentValues(MethodInfo[] methods, object[] argValues) + { + return (MethodInfo)GetMethodBaseByArgumentValues("method", methods, argValues); + } + + /// + /// From a given list of methods, selects the method having an exact match on the given ' types. + /// + /// the type of method (used for exception reporting only) + /// the list of methods to choose from + /// the arguments to the method + /// the method matching exactly the passed ' types + /// + /// If more than 1 matching methods are found in the list. + /// + private static MethodBase GetMethodBaseByArgumentValues(string methodTypeName, MethodBase[] methods, + object[] argValues) + { + MethodBase match = null; + int matchCount = 0; + + foreach (MethodBase m in methods) + { + ParameterInfo[] parameters = m.GetParameters(); + bool isMatch = true; + bool isExactMatch = true; + object[] paramValues = argValues; + + try + { + if (parameters.Length > 0) + { + ParameterInfo lastParameter = parameters[parameters.Length - 1]; + if (lastParameter.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0) + { + paramValues = + PackageParamArray(argValues, parameters.Length, + lastParameter.ParameterType.GetElementType()); + } + } + + for (int i = 0; i < parameters.Length; i++) + { + Type paramType = parameters[i].ParameterType; + object paramValue = paramValues[i]; + if ((paramValue == null && paramType.IsValueType) + || (paramValue != null && !paramType.IsAssignableFrom(paramValue.GetType()))) + { + isMatch = false; + break; + } + if (paramValue == null || paramType != paramValue.GetType()) + { + isExactMatch = false; + } + } + } + catch (InvalidCastException) + { + isMatch = false; + } + + if (isMatch) + { + if (isExactMatch) + { + return m; + } + + matchCount++; + if (matchCount == 1) + { + match = m; + } + else + { + throw new AmbiguousMatchException( + string.Format("Ambiguous match for {0} '{1}' for the specified number and types of arguments.", methodTypeName, + m.Name)); + } + } + } + + return match; + } + + /// + /// From a given list of constructors, selects the constructor having an exact match on the given ' types. + /// + /// the list of constructors to choose from + /// the arguments to the method + /// the constructor matching exactly the passed ' types + /// + /// If more than 1 matching methods are found in the list. + /// + public static ConstructorInfo GetConstructorByArgumentValues(ConstructorInfo[] methods, object[] argValues) + { + return (ConstructorInfo)GetMethodBaseByArgumentValues("constructor", methods, argValues); + } + + + /// + /// Packages arguments into argument list containing parameter array as a last argument. + /// + /// Argument vaklues to package. + /// Total number of oarameters. + /// Type of the param array element. + /// Packaged arguments. + public static object[] PackageParamArray(object[] argValues, int argCount, Type elementType) + { + object[] values = new object[argCount]; + int i = 0; + + // copy regular arguments + while (i < argCount - 1) + { + values[i] = argValues[i]; + i++; + } + + // package param array into last argument + Array paramArray = Array.CreateInstance(elementType, argValues.Length - i); + int j = 0; + while (i < argValues.Length) + { + paramArray.SetValue(argValues[i++], j++); + } + values[values.Length - 1] = paramArray; + + return values; + } + + /// + /// Convenience method to convert an interface + /// to a array that contains + /// all the interfaces inherited and the specified interface. + /// + /// The interface to convert. + /// An array of interface s. + /// + /// If the specified is not an interface. + /// + /// + /// If is . + /// + public static Type[] ToInterfaceArray(Type intf) + { + AssertUtils.ArgumentNotNull(intf, "intf"); + + if (!intf.IsInterface) + { + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, + "[{0}] is a class.", + intf.FullName)); + } + + ArrayList interfaces = new ArrayList(intf.GetInterfaces()); + interfaces.Add(intf); + + return (Type[])interfaces.ToArray(typeof(Type)); + } + + /// + /// Is the supplied the default indexer for the + /// supplied ? + /// + /// + /// The name of the property on the supplied to be checked. + /// + /// + /// The to be checked. + /// + /// + /// if the supplied is the + /// default indexer for the supplied . + /// + /// + /// If the supplied is . + /// + public static bool PropertyIsIndexer(string propertyName, Type type) + { + DefaultMemberAttribute[] attribs = + (DefaultMemberAttribute[])type.GetCustomAttributes(typeof(DefaultMemberAttribute), true); + if (attribs.Length != 0) + { + foreach (DefaultMemberAttribute attrib in attribs) + { + if (attrib.MemberName.Equals(propertyName)) + { + return true; + } + } + } + return false; + } + + /// + /// Is the supplied declared on one of these interfaces? + /// + /// The method to check. + /// The array of interfaces we want to check. + /// + /// if the method is declared on one of these interfaces. + /// + /// + /// If any of the s specified is not an interface. + /// + /// + /// If or any of the specified interfaces is + /// . + /// + public static bool MethodIsOnOneOfTheseInterfaces(MethodBase method, Type[] interfaces) + { + AssertUtils.ArgumentNotNull(method, "method"); + if (interfaces == null) + { + return false; + } + Type[] paramTypes = GetParameterTypes(method.GetParameters()); + for (int i = 0; i < interfaces.Length; i++) + { + Type interfaceType = interfaces[i]; + AssertUtils.ArgumentNotNull(interfaceType, StringUtils.Surround("interfaces[", i, "]")); + if (!interfaceType.IsInterface) + { + throw new ArgumentException(interfaces[i].FullName + " is not an interface"); + } + try + { + MethodInfo mi = interfaceType.GetMethod( + method.Name, + BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly, + null, paramTypes, null); + if (mi != null) + { + // found it... + return true; + } + } + catch + { + // didn't find it, so keep going... + } + } + return false; + } + + /// + /// Returns the default value for the specified + /// + /// + ///

    + /// Follows the standard .NET conventions for default values where + /// relevant; for example, all numeric types default to the value + /// 0. + ///

    + ///
    + /// + /// The to return default value for. + /// + /// + /// The default value for the specified . + /// + /// + /// If the supplied is an enumerated type that + /// has no values. + /// + public static object GetDefaultValue(Type type) + { + if (!type.IsValueType) + { + return null; + } + if (type == typeof(Boolean)) + { + return false; + } + if (type == typeof(DateTime)) + { + return DateTime.MinValue; + } + if (type == typeof(Char)) + { + return Char.MinValue; + } + if (type.IsEnum) + { + Array values = Enum.GetValues(type); + if (values == null || values.Length == 0) + { + throw new ArgumentException("Bad 'enum' Type : cannot get default value because 'enum' has no values."); + } + return values.GetValue(0); + } + return 0; + } + + /// + /// Returns an array consisting of the default values for the supplied + /// . + /// + /// + /// The array of s to return default values for. + /// + /// + /// An array consisting of the default values for the supplied + /// . + /// + /// + /// If any of the elements in the supplied + /// array is an enumerated type that has no values. + /// + /// + public static object[] GetDefaultValues(Type[] types) + { + object[] defaults = new object[types.Length]; + for (int i = 0; i < types.Length; ++i) + { + defaults[i] = GetDefaultValue(types[i]); + } + return defaults; + } + + /// + /// Checks that the parameter s of the + /// supplied match the parameter + /// s of the supplied + /// . + /// + /// The method to be checked. + /// + /// The array of parameter s to check against. + /// + /// + /// if the parameter s + /// match. + /// + public static bool ParameterTypesMatch( + MethodInfo candidate, Type[] parameterTypes) + { + #region Sanity Checks + + AssertUtils.ArgumentNotNull(candidate, "candidate"); + AssertUtils.ArgumentNotNull(parameterTypes, "parameterTypes"); + + #endregion + + Type[] candidatesParameterTypes + = ReflectionUtils.GetParameterTypes(candidate); + if (candidatesParameterTypes.Length != parameterTypes.Length) + { + return false; + } + for (int i = 0; i < candidatesParameterTypes.Length; ++i) + { + if (!candidatesParameterTypes[i].Equals(parameterTypes[i])) + { + return false; + } + } + return true; + } + + /// + /// Returns an array containing the s of the + /// objects in the supplied array. + /// + /// + /// The objects array for which the corresponding s + /// are needed. + /// + /// + /// An array containing the s of the objects + /// in the supplied array; this array will be empty (but not + /// if the supplied + /// is null or has no elements. + /// + /// + ///

    + /// [C#]
    + /// Given an array containing the following objects, + /// [83, "Foo", new object ()], the + /// array returned from this method call would consist of the following + /// elements... + /// [Int32, String, Object]. + ///

    + ///
    + public static Type[] GetTypes(object[] args) + { + if (args == null || args.Length == 0) + { + return Type.EmptyTypes; + } + Type[] paramsType = new Type[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + object arg = args[i]; + paramsType[i] = (arg != null) ? args[i].GetType() : typeof(object); + } + return paramsType; + } + + /// + /// Does the given and/or it's superclasses + /// have at least one or more methods with the given name (with any + /// argument types)? + /// + /// + ///

    + /// Includes non-public methods in the methods searched. + ///

    + ///
    + /// + /// The to be checked. + /// + /// + /// The name of the method to be searched for. Case inSenSItivE. + /// + /// + /// if the given or / and it's + /// superclasses have at least one or more methods (with any argument types); + /// if not, or either of the parameters is . + /// + public static bool HasAtLeastOneMethodWithName(Type type, string name) + { + if (type == null || StringUtils.IsNullOrEmpty(name)) + { + return false; + } + return MethodCountForName(type, name) > 0; + } + + /// + /// Within , counts the number of overloads for the method with the given (case-insensitive!) + /// + /// The type to be searched + /// the name of the method for which overloads shall be counted + /// The number of overloads for method within type + public static int MethodCountForName(Type type, string name) + { + AssertUtils.ArgumentNotNull(type, "type", "Type must not be null"); + AssertUtils.ArgumentNotNull(name, "name", "Method name must not be null"); + MemberInfo[] methods = type.FindMembers( + MemberTypes.Method, + ReflectionUtils.AllMembersCaseInsensitiveFlags, + new MemberFilter(ReflectionUtils.MethodNameFilter), + name); + return methods.Length; + } + + private static bool MethodNameFilter(MemberInfo member, object criteria) + { + MethodInfo method = member as MethodInfo; + string name = criteria as string; + return String.Compare(method.Name, name, true, CultureInfo.InvariantCulture) == 0; + } + + /// + /// Creates a . + /// + /// + ///

    + /// Note that if a non- + /// is supplied, any read write properties exposed by the + /// will be used to overwrite values that may have been passed in via the + /// . That is, the will be used + /// to initialize the custom attribute, and then any read-write properties on the + /// will be plugged in. + ///

    + ///
    + /// + /// The desired . + /// + /// + /// Any constructor arguments for the attribute (may be + /// in the case of no arguments). + /// + /// + /// Source attribute to copy properties from (may be ). + /// + /// A custom attribute builder. + /// + /// If the parameter is . + /// + /// + /// If the parameter is not a + /// that derives from the class. + /// + /// + public static CustomAttributeBuilder CreateCustomAttribute( + Type type, object[] ctorArgs, Attribute sourceAttribute) + { + #region Sanity Checks + + AssertUtils.ArgumentNotNull(type, "type"); + if (!typeof(Attribute).IsAssignableFrom(type)) + { + throw new ArgumentException( + string.Format("[{0}] does not derive from the [System.Attribute] class.", + type.FullName)); + } + + #endregion + + ConstructorInfo ci = type.GetConstructor(ReflectionUtils.GetTypes(ctorArgs)); + if (ci == null && ctorArgs.Length == 0) + { + ci = type.GetConstructors()[0]; + ctorArgs = GetDefaultValues(GetParameterTypes(ci.GetParameters())); + } + + if (sourceAttribute != null) + { + object defaultAttribute = null; + try + { + defaultAttribute = ci.Invoke(ctorArgs); + } + catch + { + } + + IList getSetProps = new ArrayList(); + IList getSetValues = new ArrayList(); + IList readOnlyProps = new ArrayList(); + IList readOnlyValues = new ArrayList(); + foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) + { + if (pi.DeclaringType == typeof(Attribute)) + continue; + + if (pi.CanRead) + { + if (pi.CanWrite) + { + object propValue = pi.GetValue(sourceAttribute, null); + if (defaultAttribute != null) + { + object defaultValue = pi.GetValue(defaultAttribute, null); + if ((propValue == null && defaultValue == null) || + (propValue != null && propValue.Equals(defaultValue))) + continue; + } + getSetProps.Add(pi); + getSetValues.Add(propValue); + } + else + { + readOnlyProps.Add(pi); + readOnlyValues.Add(pi.GetValue(sourceAttribute, null)); + } + } + } + + if (readOnlyProps.Count == 1) + { + PropertyInfo pi = readOnlyProps[0] as PropertyInfo; + ConstructorInfo ciTemp = type.GetConstructor(new Type[1] { pi.PropertyType }); + if (ciTemp != null) + { + ci = ciTemp; + ctorArgs = new object[1] { readOnlyValues[0] }; + } + else + { + ciTemp = type.GetConstructor(new Type[1] { readOnlyValues[0].GetType() }); + if (ciTemp != null) + { + ci = ciTemp; + ctorArgs = new object[1] { readOnlyValues[0] }; + } + } + } + + PropertyInfo[] propertyInfos = new PropertyInfo[getSetProps.Count]; + getSetProps.CopyTo(propertyInfos, 0); + + object[] propertyValues = new object[getSetValues.Count]; + getSetValues.CopyTo(propertyValues, 0); + + return new CustomAttributeBuilder(ci, ctorArgs, propertyInfos, propertyValues); + } + else + { + return new CustomAttributeBuilder(ci, ctorArgs); + } + } + + /// + /// Creates a . + /// + /// + /// The desired . + /// + /// + /// Source attribute to copy properties from (may be ). + /// + /// A custom attribute builder. + public static CustomAttributeBuilder CreateCustomAttribute( + Type type, Attribute sourceAttribute) + { + return CreateCustomAttribute(type, new object[] { }, sourceAttribute); + } + + /// + /// Creates a . + /// + /// + /// The source attribute to copy properties from. + /// + /// A custom attribute builder. + /// + /// If the supplied is + /// . + /// + public static CustomAttributeBuilder CreateCustomAttribute(Attribute sourceAttribute) + { + return CreateCustomAttribute(sourceAttribute.GetType(), sourceAttribute); + } + + /// + /// Creates a . + /// + /// + /// The desired . + /// + /// A custom attribute builder. + public static CustomAttributeBuilder CreateCustomAttribute(Type type) + { + return CreateCustomAttribute(type, new object[] { }, null); + } + + /// + /// Creates a . + /// + /// + /// The desired . + /// + /// + /// Any constructor arguments for the attribute (may be + /// in the case of no arguments). + /// + /// A custom attribute builder. + public static CustomAttributeBuilder CreateCustomAttribute( + Type type, params object[] ctorArgs) + { + return CreateCustomAttribute(type, ctorArgs, null); + } + +#if NET_2_0 + /// + /// Creates a . + /// + /// + /// The to create + /// the custom attribute builder from. + /// + /// A custom attribute builder. + public static CustomAttributeBuilder CreateCustomAttribute(CustomAttributeData attributeData) + { + object[] parameterValues = new object[attributeData.ConstructorArguments.Count]; + Type[] parameterTypes = new Type[attributeData.ConstructorArguments.Count]; + + IList namedParameterValues = new ArrayList(); + IList namedFieldValues = new ArrayList(); + + // Fill arrays of the constructor parameters + for (int i = 0; i < attributeData.ConstructorArguments.Count; i++) + { + parameterTypes[i] = attributeData.ConstructorArguments[i].ArgumentType; + parameterValues[i] = ConvertValueIfNecessary(attributeData.ConstructorArguments[i].Value); + } + + Type attributeType = attributeData.Constructor.DeclaringType; + PropertyInfo[] attributeProperties = attributeType.GetProperties( + BindingFlags.Instance | BindingFlags.Public); + FieldInfo[] attributeFields = attributeType.GetFields( + BindingFlags.Instance | BindingFlags.Public); + + // Not using generics bellow as probably Spring.NET tries to keep + // it on .NET1 compatibility level right now I believe (SD) + // In case of using List the above note makes + // no sense (SD:) + IList propertiesToSet = new ArrayList(); + int k = 0; + + IList fieldsToSet = new ArrayList(); + int n = 0; + + + // Fills arrays of the constructor named parameters + foreach (CustomAttributeNamedArgument namedArgument in attributeData.NamedArguments) + { + bool noMatchingProperty = false; + + // Now iterate through all of the PropertyInfo, find the + // one with the corresponding to the NamedProperty name + // and add it to the array of properties to set. + for (int j = 0; j < attributeProperties.Length; j++) + { + if (attributeProperties[j].Name == namedArgument.MemberInfo.Name) + { + propertiesToSet.Add(attributeProperties[j]); + namedParameterValues.Add(ConvertValueIfNecessary(namedArgument.TypedValue.Value)); + break; + } + else + { + if (j == attributeProperties.Length - 1) + { + // In case of no match, throw + noMatchingProperty = true; + /* + throw new InvalidOperationException( + String.Format(CultureInfo.InvariantCulture, + "The property with name {0} can't be found in the " + + "type {1}, but is present as a named property " + + "on the attributeData {2}", namedArgument.MemberInfo.Name, + attributeType.FullName, attributeData)); + */ + } + } + } + if (noMatchingProperty) + { + for (int j = 0; j < attributeFields.Length; j++) + { + if (attributeFields[j].Name == namedArgument.MemberInfo.Name) + { + fieldsToSet.Add(attributeFields[j]); + namedFieldValues.Add(ConvertValueIfNecessary(namedArgument.TypedValue.Value)); + break; + } + else + { + if (j == attributeFields.Length - 1) + { + throw new InvalidOperationException( + String.Format(CultureInfo.InvariantCulture, + "A property or public field with name {0} can't be found in the " + + "type {1}, but is present as a named property " + + "on the attributeData {2}", namedArgument.MemberInfo.Name, + attributeType.FullName, attributeData)); + } + } + } + } + } + // Get constructor corresponding to the parameters and their types + ConstructorInfo constructor = attributeType.GetConstructor(parameterTypes); + + PropertyInfo[] namedProperties = new PropertyInfo[propertiesToSet.Count]; + propertiesToSet.CopyTo(namedProperties, 0); + + object[] propertyValues = new object[namedParameterValues.Count]; + namedParameterValues.CopyTo(propertyValues, 0); + + if (fieldsToSet.Count == 0) + { + return new CustomAttributeBuilder( + constructor, parameterValues, namedProperties, propertyValues); + } + else + { + FieldInfo[] namedFields = new FieldInfo[fieldsToSet.Count]; + fieldsToSet.CopyTo(namedFields, 0); + + object[] fieldValues = new object[namedFieldValues.Count]; + namedFieldValues.CopyTo(fieldValues, 0); + + return new CustomAttributeBuilder( + constructor, parameterValues, namedProperties, propertyValues, namedFields, fieldValues); + } + + + + } + + private static object ConvertValueIfNecessary(object value) + { + if (value == null) return value; + + // We are only hunting for the case of the ReadOnlyCollection here. + ReadOnlyCollection sourceArray = + value as ReadOnlyCollection; + + if (sourceArray == null) return value; + + Type underlyingType = null; // type to be used for arguments + Array returnArray = null; + for (int i = 0; i < sourceArray.Count; i++) + { + if (underlyingType == null) + { + underlyingType = sourceArray[i].ArgumentType; + returnArray = Array.CreateInstance(underlyingType, sourceArray.Count); + } + if (!underlyingType.Equals(sourceArray[i].ArgumentType)) + { + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, + "Types for the same named parameter of array type are expected to be same")); + } + + returnArray.SetValue(sourceArray[i].Value, i); + } + + return returnArray; + + } +#endif + + /// + /// Tries to find matching methods in the specified + /// for each method in the supplied list. + /// + /// + /// The to look for matching methods in. + /// + /// The methods to match. + /// + /// A flag that specifies whether to throw an exception if a matching + /// method is not found. + /// + /// A list of the matched methods. + /// + /// If either of the or + /// parameters are . + /// + public static MethodInfo[] GetMatchingMethods(Type type, MethodInfo[] methods, bool strict) + { + AssertUtils.ArgumentNotNull(type, "type"); + AssertUtils.ArgumentNotNull(methods, "methods"); + + BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase; + + MethodInfo[] matched = new MethodInfo[methods.Length]; + for (int i = 0; i < methods.Length; i++) + { + MethodInfo method = methods[i]; + MethodInfo match = type.GetMethod(method.Name, flags, null, ReflectionUtils.GetParameterTypes(method), null); + if ((match == null || match.ReturnType != method.ReturnType) && strict) + { + throw new Exception( + string.Format("Method '{0}' could not be matched in the target class [{1}].", + method.Name, type.FullName)); + } + matched[i] = match; + } + return matched; + } + + /// + /// Returns the of the supplied + /// . + /// + /// + ///

    + /// If the is a + /// instance, the return value of this method call with be the + /// parameter cast to a + /// . If the is + /// anything other than a , the return value + /// will be the result of invoking the 's + /// method. + ///

    + ///
    + /// + /// A or instance. + /// + /// + /// The argument if it is a + /// or the result of invoking + /// on the argument if it + /// is an . + /// + /// + /// If the is . + /// + public static Type TypeOfOrType(object source) + { + return source is Type ? source as Type : source.GetType(); + } + + +#if NET_2_0 + private static readonly MethodInfo Exception_InternalPreserveStackTrace = + typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic); +#else + private static readonly FieldInfo Exception_RemoteStackTraceString = + typeof(Exception).GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic); +#endif + + /// + /// Unwraps the supplied + /// and returns the inner exception preserving the stack trace. + /// + /// + /// The to unwrap. + /// + /// The unwrapped exception. + public static Exception UnwrapTargetInvocationException(TargetInvocationException ex) + { +#if NET_2_0 + Exception_InternalPreserveStackTrace.Invoke(ex.InnerException, new Object[] { }); +#else + Exception_RemoteStackTraceString.SetValue(ex.InnerException, ex.InnerException.StackTrace + Environment.NewLine); +#endif + return ex.InnerException; + } + + /// + /// Is the supplied can be accessed outside the assembly ? + /// + /// The type to check. + /// + /// if the type can be accessed outside the assembly; + /// Otherwise . + /// + public static bool IsTypeVisible(Type type) + { + return IsTypeVisible(type, null); + } + + /// + /// Is the supplied can be accessed + /// from the supplied friendly assembly ? + /// + /// The type to check. + /// The friendly assembly name. + /// + /// if the type can be accessed + /// from the supplied friendly assembly; Otherwise . + /// + public static bool IsTypeVisible(Type type, string friendlyAssemblyName) + { +#if NET_2_0 + if (type.IsVisible) + { + return true; + } + else + { + if (friendlyAssemblyName != null + && friendlyAssemblyName.Length > 0 + && (!type.IsNested || type.IsNestedPublic || + (!type.IsNestedPrivate && (type.IsNestedAssembly || type.IsNestedFamORAssem)))) + { + object[] attrs = type.Assembly.GetCustomAttributes(typeof(InternalsVisibleToAttribute), false); + foreach (InternalsVisibleToAttribute ivta in attrs) + { + if (ivta.AssemblyName == friendlyAssemblyName) + { + return true; + } + } + } + } +#else + if (type.IsPublic || (type.IsNestedPublic && type.DeclaringType.IsPublic)) + { + return true; + } +#endif + return false; + } + + /// + /// Gets all of the interfaces implemented by + /// the specified . + /// + /// + /// The object to get the interfaces of. + /// + /// + /// All of the interfaces implemented by the + /// . + /// + public static Type[] GetInterfaces(Type type) + { + AssertUtils.ArgumentNotNull(type, "type"); + + if (type.IsInterface) + { + ArrayList interfaces = new ArrayList(); + interfaces.Add(type); + interfaces.AddRange(type.GetInterfaces()); + return (Type[])interfaces.ToArray(typeof(Type)); + } + else + { + return type.GetInterfaces(); + } + } + + /// + /// Returns the explicit that is the root cause of an exception. + /// + /// + /// If the InnerException property of the current exception is a null reference + /// or a , returns the current exception. + /// + /// The last exception thrown. + /// + /// The first explicit exception thrown in a chain of exceptions. + /// + public static Exception GetExplicitBaseException(Exception ex) + { + Exception innerEx = ex.InnerException; + while (innerEx != null && + !(innerEx is NullReferenceException)) + { + ex = innerEx; + innerEx = innerEx.InnerException; + } + return ex; + } + + /// + /// Copies all fields from one object to another. + /// + /// + /// The types of both objects must be related. This means, that either of the following is true: + /// + /// fromObject.GetType() == toObject.GetType() + /// fromObject.GetType() is derived from toObject.GetType() + /// toObject.GetType() is derived from fromObject.GetType() + /// + /// + /// The source object + /// The object, who's fields will be populated with values from the source object + /// If the object's types are not related + public static void MemberwiseCopy(object fromObject, object toObject) + { + Type fromType = fromObject.GetType(); + Type toType = toObject.GetType(); + + Type smallerType; + + if (fromType.IsAssignableFrom(toType)) + { + smallerType = fromType; + } + else if (toType.IsAssignableFrom(fromType)) + { + smallerType = toType; + } + else + { + throw new ArgumentException("object types are not related"); + } + + MemberwiseCopyInternal(fromObject, toObject, smallerType); + } + +#if NET_2_0 + private static void MemberwiseCopyInternal(object fromObject, object toObject, Type smallerType) + { + MemberwiseCopyHandler impl = GetImpl(smallerType); + impl(fromObject, toObject); + } + + private delegate void MemberwiseCopyHandler(object a, object b); + + private static readonly Hashtable s_handlerCache = new Hashtable(); + + private static MemberwiseCopyHandler GetImpl(Type type) + { + MemberwiseCopyHandler handler = s_handlerCache[type] as MemberwiseCopyHandler; + if (handler != null) return handler; + + lock (s_handlerCache) + { + handler = s_handlerCache[type] as MemberwiseCopyHandler; + if (handler != null) return handler; + + FieldInfo[] fields = GetFields(type); + DynamicMethod dm = new DynamicMethod(type.FullName + ".ShallowCopy", null, new Type[] { typeof(object), typeof(object) }, type.Module, true); + ILGenerator ilGen = dm.GetILGenerator(); + ilGen.DeclareLocal(type); + ilGen.DeclareLocal(type); + ilGen.Emit(OpCodes.Ldarg_0); + ilGen.Emit(OpCodes.Castclass, type); + ilGen.Emit(OpCodes.Stloc_0); + ilGen.Emit(OpCodes.Ldarg_1); + ilGen.Emit(OpCodes.Castclass, type); + ilGen.Emit(OpCodes.Stloc_1); + + foreach (FieldInfo field in fields) + { + ilGen.Emit(OpCodes.Ldloc_1); + ilGen.Emit(OpCodes.Ldloc_0); + ilGen.Emit(OpCodes.Ldfld, field); + ilGen.Emit(OpCodes.Stfld, field); + } + ilGen.Emit(OpCodes.Ret); + + handler = (MemberwiseCopyHandler)dm.CreateDelegate(typeof(MemberwiseCopyHandler)); + s_handlerCache[type] = handler; + } + return handler; + } +#else + private static void MemberwiseCopyInternal(object fromObject, object toObject, Type smallerType) + { + FieldInfo[] fields = GetFields(smallerType); + for (int i = 0; i < fields.Length; i++) + { + FieldInfo field = fields[i]; + field.SetValue(toObject, field.GetValue(fromObject)); + } + } +#endif + + #region Field Cache Management for "MemberwiseCopy" + + private const BindingFlags FIELDBINDINGS = + BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic; + + private static readonly Hashtable s_fieldCache = new Hashtable(); + + private static FieldInfo[] GetFields(Type type) + { + lock (s_fieldCache) + { + FieldInfo[] fields = (FieldInfo[])s_fieldCache[type]; + if (fields == null) + { + ArrayList fieldList = new ArrayList(); + CollectFieldsRecursive(type, fieldList); + fields = (FieldInfo[])fieldList.ToArray(typeof(FieldInfo)); + s_fieldCache[type] = fields; + } + return fields; + } + } + + private static void CollectFieldsRecursive(Type type, ArrayList fieldList) + { + if (type == typeof(object)) return; + + FieldInfo[] fields = type.GetFields(FIELDBINDINGS); + fieldList.AddRange(fields); + CollectFieldsRecursive(type.BaseType, fieldList); + } + + #endregion Field Cache Management for "MemberwiseCopy" + + + #region CustomAttributeBuilderBuilder inner class definition + + /// + /// Creates a . + /// + /// Bruno Baia + /// $Id: ReflectionUtils.cs,v 1.59 2008/05/13 14:22:46 oakinger Exp $ + public class CustomAttributeBuilderBuilder + { + #region Fields + + private Type type; + private ArrayList constructorArgs; + private ArrayList namedProperties; + private ArrayList propertyValues; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// The custom attribute type. + public CustomAttributeBuilderBuilder(Type attributeType) + : + this(attributeType, ObjectUtils.EmptyObjects) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// The custom attribute type. + /// The custom attribute constructor arguments. + public CustomAttributeBuilderBuilder(Type attributeType, params object[] constructorArgs) + { + AssertUtils.ArgumentNotNull(attributeType, "attributeType"); + if (!typeof(Attribute).IsAssignableFrom(attributeType)) + { + throw new ArgumentException( + string.Format("[{0}] does not derive from the [System.Attribute] class.", + attributeType.FullName)); + } + this.type = attributeType; + this.constructorArgs = new ArrayList(constructorArgs); + this.namedProperties = new ArrayList(); + this.propertyValues = new ArrayList(); + } + + #endregion + + #region Public Methods + + /// + /// Adds the specified values to the constructor argument list + /// used to create the custom attribute. + /// + /// An array of argument values. + public void AddContructorArgument(params object[] values) + { + this.constructorArgs.AddRange(values); + } + + /// + /// Adds a property value to the custom attribute. + /// + /// The property name. + /// The property value. + public void AddPropertyValue(string name, object value) + { + PropertyInfo propertyInfo = this.type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public); + if (propertyInfo == null) + { + throw new ArgumentException( + String.Format("The property '{0}' does no exist in the attribute '{1}'.", name, this.type)); + } + + this.namedProperties.Add(propertyInfo); + this.propertyValues.Add(value); + } + + /// + /// Creates the . + /// + /// The created . + public CustomAttributeBuilder Build() + { + object[] caArray = (object[])this.constructorArgs.ToArray(typeof(object)); + ConstructorInfo ci = this.type.GetConstructor(ReflectionUtils.GetTypes(caArray)); + if (ci == null && caArray.Length == 0) + { + ci = this.type.GetConstructors()[0]; + caArray = ReflectionUtils.GetDefaultValues(ReflectionUtils.GetParameterTypes(ci.GetParameters())); + } + + if (namedProperties.Count > 0) + { + PropertyInfo[] npArray = (PropertyInfo[])this.namedProperties.ToArray(typeof(PropertyInfo)); + object[] pvArray = (object[])this.propertyValues.ToArray(typeof(object)); + return new CustomAttributeBuilder(ci, caArray, npArray, pvArray); + } + else + { + return new CustomAttributeBuilder(ci, caArray); + } + + } + + #endregion + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/StringUtils.cs b/src/Spring/Spring.Core/Util/StringUtils.cs new file mode 100644 index 00000000..ef9ffcb3 --- /dev/null +++ b/src/Spring/Spring.Core/Util/StringUtils.cs @@ -0,0 +1,575 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using System.Text; + +#endregion + +namespace Spring.Util +{ + /// + /// Miscellaneous utility methods. + /// + /// + ///

    + /// Mainly for internal use within the framework. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Keith Donald + /// Aleksandar Seovic (.NET) + /// Mark Pollack (.NET) + /// Rick Evans (.NET) + /// $Id: StringUtils.cs,v 1.29 2006/04/09 07:19:00 markpollack Exp $ + public sealed class StringUtils + { + /// + /// An empty array of instances. + /// + public static readonly string[] EmptyStrings = new string[] {}; + + /// + /// The string that signals the start of an Ant-style expression. + /// + private const string AntExpressionPrefix = "${"; + + /// + /// The string that signals the end of an Ant-style expression. + /// + private const string AntExpressionSuffix = "}"; + + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private StringUtils() + { + } + + // CLOVER:ON + + #endregion + + /// + /// Tokenize the given into a + /// array. + /// + /// + ///

    + /// If is , returns an empty + /// array. + ///

    + ///

    + /// If is or the empty + /// , returns a array with one + /// element: itself. + ///

    + ///
    + /// The to tokenize. + /// + /// The delimiter characters, assembled as a . + /// + /// + /// Trim the tokens via . + /// + /// + /// Omit empty tokens from the result array. + /// An array of the tokens. + public static string[] Split( + string s, string delimiters, bool trimTokens, bool ignoreEmptyTokens) + { + if (s == null) + { + return new string[0]; + } + if (StringUtils.IsNullOrEmpty(delimiters)) + { + return new string[] {s}; + } + string[] tmp = s.Split(delimiters.ToCharArray()); + // short circuit if String.Split default behavior is ok + if (!trimTokens && !ignoreEmptyTokens) + { + return tmp; + } + else + { + ArrayList tokens = new ArrayList(tmp.Length); + for (int i = 0; i < tmp.Length; ++i) + { + string token = (trimTokens ? tmp[i].Trim() : tmp[i]); + if (!(ignoreEmptyTokens && token.Length == 0)) + { + tokens.Add(token); + } + } + return (string[]) tokens.ToArray(typeof (string)); + } + } + + /// + /// Convert a CSV list into an array of s. + /// + /// A CSV list. + /// + /// An array of s, or the empty array + /// if is . + /// + public static string[] CommaDelimitedListToStringArray(string s) + { + return DelimitedListToStringArray(s, ","); + } + + /// + /// Take a which is a delimited list + /// and convert it to a array. + /// + /// + ///

    + /// If the supplied is a + /// or zero-length string, then a single element + /// array composed of the supplied + /// will be + /// eturned. If the supplied + /// is , then an empty, + /// zero-length array will be returned. + ///

    + ///
    + /// + /// The to be parsed. + /// + /// + /// The delimeter (this will not be returned). Note that only the first + /// character of the supplied is used. + /// + /// + /// An array of the tokens in the list. + /// + public static string[] DelimitedListToStringArray(string input, string delimiter) + { + if (input == null) + { + return new string[0]; + } + if (!HasLength(delimiter)) + { + return new string[] {input}; + } + return input.Split(delimiter[0]); + } + + /// + /// Convenience method to return an + /// as a delimited + /// (e.g. CSV) . + /// + /// + /// The to parse. + /// + /// + /// The delimiter to use (probably a ','). + /// + /// The delimited string representation. + public static string CollectionToDelimitedString( + ICollection c, string delimiter) + { + if (c == null) + { + return "null"; + } + StringBuilder sb = new StringBuilder(); + int i = 0; + foreach (object obj in c) + { + if (i++ > 0) + { + sb.Append(delimiter); + } + sb.Append(obj); + } + return sb.ToString(); + } + + /// + /// Convenience method to return an + /// as a CSV + /// . + /// + /// + /// The to display. + /// + /// The delimited string representation. + public static string CollectionToCommaDelimitedString( + ICollection collection) + { + return CollectionToDelimitedString(collection, ","); + } + + /// + /// Convenience method to return an array as a CSV + /// . + /// + /// + /// The array to parse. Elements may be of any type ( + /// will be called on each + /// element). + /// + public static string ArrayToCommaDelimitedString(object[] source) + { + return ArrayToDelimitedString(source, ","); + } + + /// + /// Convenience method to return a + /// array as a delimited (e.g. CSV) . + /// + /// + /// The array to parse. Elements may be of any type ( + /// will be called on each + /// element). + /// + /// + /// The delimiter to use (probably a ','). + /// + public static string ArrayToDelimitedString( + object[] source, string delimiter) + { + if (source == null) + { + return "null"; + } + else + { + return StringUtils.CollectionToDelimitedString(source, delimiter); + } + } + + /// Checks if a string has length. + /// + /// The string to check, may be . + /// + /// + /// if the string has length and is not + /// . + /// + /// + /// + /// StringUtils.HasLength(null) = false + /// StringUtils.HasLength("") = false + /// StringUtils.HasLength(" ") = true + /// StringUtils.HasLength("Hello") = true + /// + /// + public static bool HasLength(string target) + { + return (target != null && target.Length > 0); + } + + /// + /// Checks if a has text. + /// + /// + ///

    + /// More specifically, returns if the string is + /// not , it's is > + /// zero (0), and it has at least one non-whitespace character. + ///

    + ///
    + /// + /// The string to check, may be . + /// + /// + /// if the is not + /// , + /// > zero (0), and does not consist + /// solely of whitespace. + /// + /// + /// + /// StringUtils.HasText(null) = false + /// StringUtils.HasText("") = false + /// StringUtils.HasText(" ") = false + /// StringUtils.HasText("12345") = true + /// StringUtils.HasText(" 12345 ") = true + /// + /// + public static bool HasText(string target) + { + if (target == null) + { + return false; + } + else + { + return HasLength(target.Trim()); + } + } + + /// + /// Checks if a is + /// or an empty string. + /// + /// + ///

    + /// More specifically, returns if the string is + /// , it's is equal + /// to zero (0), or it is composed entirely of whitespace + /// characters. + ///

    + ///
    + /// + /// The string to check, may (obviously) be . + /// + /// + /// if the is + /// , has a length equal to zero (0), or + /// is composed entirely of whitespace characters. + /// + /// + /// + /// StringUtils.IsNullOrEmpty(null) = true + /// StringUtils.IsNullOrEmpty("") = true + /// StringUtils.IsNullOrEmpty(" ") = true + /// StringUtils.IsNullOrEmpty("12345") = false + /// StringUtils.IsNullOrEmpty(" 12345 ") = false + /// + /// + public static bool IsNullOrEmpty(string target) + { + return !HasText(target); + } + + /// + /// Strips first and last character off the string. + /// + /// The string to strip. + /// The stripped string. + public static string StripFirstAndLastCharacter(string text) + { + if (text != null + && text.Length > 2) + { + return text.Substring(1, text.Length - 2); + } + else + { + return String.Empty; + } + } + + /// + /// Returns a list of Ant-style expressions from the specified text. + /// + /// The text to inspect. + /// + /// A list of expressions that exist in the specified text. + /// + /// + /// If any of the expressions in the supplied + /// is empty (${}). + /// + public static IList GetAntExpressions(string text) + { + IList expressions = new ArrayList(); + if (StringUtils.HasText(text)) + { + int start = text.IndexOf(AntExpressionPrefix); + while (start >= 0) + { + int end = text.IndexOf(AntExpressionSuffix, start + 2); + if (end == -1) + { + // terminator character not found, so let's quit... + start = -1; + } + else + { + string exp = text.Substring(start + 2, end - start - 2); + if(StringUtils.IsNullOrEmpty(exp)) + { + throw new FormatException( + string.Format("Empty {0}{1} value found in text : '{2}'.", + AntExpressionPrefix, + AntExpressionSuffix, + text)); + } + if (expressions.IndexOf(exp) < 0) + { + expressions.Add(exp); + } + start = text.IndexOf(AntExpressionPrefix, end); + } + } + } + return expressions; + } + + /// + /// Replaces Ant-style expression placeholder with expression value. + /// + /// + ///

    + /// + ///

    + ///
    + /// The string to set the value in. + /// The name of the expression to set. + /// The expression value. + /// + /// A new string with the expression value set; the + /// value if the supplied + /// is , has a length + /// equal to zero (0), or is composed entirely of whitespace + /// characters. + /// + public static string SetAntExpression(string text, string expression, object expValue) + { + if (StringUtils.IsNullOrEmpty(text)) + { + return String.Empty; + } + if (expValue == null) + { + expValue = String.Empty; + } + return text.Replace( + StringUtils.Surround(AntExpressionPrefix, expression, AntExpressionSuffix), expValue.ToString()); + } + + /// + /// Surrounds (prepends and appends) the string value of the supplied + /// to the supplied . + /// + /// + ///

    + /// The return value of this method call is always guaranteed to be non + /// . If every value passed as a parameter to this method is + /// , the string will be returned. + ///

    + ///
    + /// + /// The prefix and suffix that respectively will be prepended and + /// appended to the target . If this value + /// is not a value, it's attendant + /// value will be used. + /// + /// + /// The target that is to be surrounded. If this value is not a + /// value, it's attendant + /// value will be used. + /// + /// The surrounded string. + public static string Surround(object fix, object target) + { + return StringUtils.Surround(fix, target, fix); + } + + /// + /// Surrounds (prepends and appends) the string values of the supplied + /// and to the supplied + /// . + /// + /// + ///

    + /// The return value of this method call is always guaranteed to be non + /// . If every value passed as a parameter to this method is + /// , the string will be returned. + ///

    + ///
    + /// + /// The value that will be prepended to the . If this value + /// is not a value, it's attendant + /// value will be used. + /// + /// + /// The target that is to be surrounded. If this value is not a + /// value, it's attendant + /// value will be used. + /// + /// + /// The value that will be appended to the . If this value + /// is not a value, it's attendant + /// value will be used. + /// + /// The surrounded string. + public static string Surround(object prefix, object target, object suffix) + { + return string.Format( + CultureInfo.InvariantCulture, "{0}{1}{2}", prefix, target, suffix); + } + + /// + /// Converts escaped characters (for example "\t") within a string + /// to their real character. + /// + /// The string to convert. + /// The converted string. + public static string ConvertEscapedCharacters( string inputString ) + { + StringBuilder sb = new StringBuilder(inputString.Length); + for (int i = 0 ; i < inputString.Length ; i++) + { + if (inputString[i].Equals('\\')) + { + i++; + if (inputString[i].Equals('t')) + { + sb.Append('\t'); + } + else if (inputString[i].Equals('r')) + { + sb.Append('\r'); + } + else if (inputString[i].Equals('n')) + { + sb.Append('\n'); + } + else if (inputString[i].Equals('\\')) + { + sb.Append('\\'); + } + else + { + sb.Append("\\" + inputString[i]); + } + } + else + { + sb.Append(inputString[i]); + } + } + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/SystemUtils.cs b/src/Spring/Spring.Core/Util/SystemUtils.cs new file mode 100644 index 00000000..c927f2f5 --- /dev/null +++ b/src/Spring/Spring.Core/Util/SystemUtils.cs @@ -0,0 +1,117 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Configuration; +using System.Reflection; +using System.Threading; +using System.Xml; + +#endregion + +namespace Spring.Util +{ + /// + /// Utility class containing miscellaneous system-level functionality. + /// + /// Aleksandar Seovic + /// $Id: SystemUtils.cs,v 1.5 2007/09/07 02:46:49 markpollack Exp $ + public sealed class SystemUtils + { + private static bool assemblyResolverRegistered = false; + private static object assemblyResolverLock = new object(); + + private static readonly bool isMono = Type.GetType("Mono.Runtime") == null ? false : true; + + /// + /// Registers assembly resolver that iterates over the + /// assemblies loaded into the current + /// in order to find an assembly that cannot be resolved. + /// + /// + /// This method has to be called if you need to serialize dynamically + /// generated types in transient assemblies, such as Spring AOP proxies, + /// because standard .NET serialization engine always tries to load + /// assembly from the disk. + /// + public static void RegisterLoadedAssemblyResolver() + { + if (!assemblyResolverRegistered) + { + lock(assemblyResolverLock) + { + AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(LoadedAssemblyResolver); + assemblyResolverRegistered = true; + } + } + } + + private static Assembly LoadedAssemblyResolver(object sender, ResolveEventArgs args) + { + Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly assembly in loadedAssemblies) + { + if (assembly.FullName == args.Name) + { + return assembly; + } + } + return null; + } + + + /// + /// Returns true if running on Mono + /// + /// Tests for the presence of the type Mono.Runtime + public static bool MonoRuntime + { + get { return isMono; } + } + + /// + /// Gets the thread id for the current thread. Use thread name is available, + /// otherwise use CurrentThread.GetHashCode() for .NET 1.0/1.1 and + /// CurrentThread.ManagedThreadId otherwise. + /// + /// The thread id. + public static string ThreadId + { + get + { + string name = Thread.CurrentThread.Name; + if (StringUtils.HasText(name)) + { + return name; + } + else + { +#if NET_1_0 || NET_1_1 + return Thread.CurrentThread.GetHashCode().ToString(); +#else + return Thread.CurrentThread.ManagedThreadId.ToString(); +#endif + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/UniqueKey.cs b/src/Spring/Spring.Core/Util/UniqueKey.cs new file mode 100644 index 00000000..db6213c7 --- /dev/null +++ b/src/Spring/Spring.Core/Util/UniqueKey.cs @@ -0,0 +1,175 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.Text; +using Spring.Core.TypeConversion; + +#endregion + +namespace Spring.Util +{ + /// + /// UniqueKey allows for generating keys unique to a type or particular instance and a partial name, + /// that can e.g. be used as keys in . + /// + /// + /// // shows usage type-scoped keys + /// UniqueKey classAKey = UniqueKey.GetTypeScoped(typeof(ClassA), "myKey"); + /// UniqueKey classBKey = UniqueKey.GetTypeScoped(typeof(ClassB), "myKey"); + /// + /// HttpContext.Current.Items.Add( classAKey, "some value unqiue for class A having key 'myKey'"); + /// object value = HttpContext.Current.Items[ UniqueKey.GetTypeScoped(typeof(ClassA), "myKey") ]; + /// Assert.AreEqual( "some value unique for class A having key 'myKey'", value); + /// + /// HttpContext.Current.Items.Add( classBKey, "some value unqiue for class B having key 'myKey'"); + /// object value = HttpContext.Current.Items[ UniqueKey.GetTypeScoped(typeof(ClassB), "myKey") ]; + /// Assert.AreEqual( "some value unique for class B having key 'myKey'", value); + /// + [Serializable] + [TypeConverter(typeof(UniqueKeyConverter))] + public sealed class UniqueKey +#if NET_2_0 + : IEquatable +#endif + { + private readonly string _generatedKey; + + /// + /// Initialize a new instance of from its string representation. + /// See and See for details. + /// + /// The string representation of the new instance. + internal UniqueKey(string key) + { + AssertUtils.ArgumentNotNull(key, "key"); + _generatedKey = key; + } + + /// + /// Compares this instance to another. + /// + public bool Equals(UniqueKey uniqueKey) + { + if (uniqueKey == null) return false; + return Equals(_generatedKey, uniqueKey._generatedKey); + } + + /// + /// Compares this instance to another. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(this, obj)) return true; + return Equals(obj as UniqueKey); + } + + /// + /// Returns the hash code for this key. + /// + /// + public override int GetHashCode() + { + return _generatedKey.GetHashCode(); + } + + /// + /// Returns a string representation of this key. + /// + public override string ToString() + { + return _generatedKey; + } + + /// + /// Creates a new key instance unique to the given instance. + /// + /// The instance the key shall be unique to + /// The partial key to be made unique + /// + /// + /// If is of type + public static UniqueKey GetInstanceScoped(object instance, string partialKey) + { + if (instance is Type) + { + throw new ArgumentException( + "please use GetTypeScoped(Type,string) for creating type specific keys", "instance"); + } + return new UniqueKey(GetInstanceScopedString(instance, partialKey)); + } + + /// + /// Creates a new key instance unique to the given type. + /// + /// The type the key shall be unique to + /// The partial key to be made unique + public static UniqueKey GetTypeScoped(Type type, string partialKey) + { + return new UniqueKey(GetTypeScopedString(type, partialKey)); + } + + /// + /// Returns a key unique for the given instance. + /// + /// The instance the key shall be unique to + /// The partial key to be made unique + /// A key formatted as typename[instance-id].partialkey + public static string GetInstanceScopedString(object instance, string partialKey) + { + AssertUtils.ArgumentNotNull(instance, "instance"); + AssertUtils.ArgumentHasText(partialKey, "partialKey"); + + if (instance is Type) + { + throw new ArgumentException( + "please use GetUniqueKey(Type,string) for creating type specific keys", "instance"); + } + return GetUniqueKey(instance.GetType(), instance, partialKey); + } + + /// + /// Returns a key unique for the given type. + /// + /// The type the key shall be unique to + /// The partial key to be made unique + /// A key formatted as typename.partialkey + public static string GetTypeScopedString(Type type, string partialKey) + { + AssertUtils.ArgumentNotNull(type, "type"); + AssertUtils.ArgumentHasText(partialKey, "partialKey"); + + return GetUniqueKey(type, null, partialKey); + } + + private static string GetUniqueKey(Type type, object instance, string partialKey) + { + StringBuilder sb = new StringBuilder(); + sb.Append(type.FullName); + if (instance != null) sb.Append('[').Append(instance.GetHashCode()).Append(']'); + sb.Append('.').Append(partialKey); + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Util/XmlUtils.cs b/src/Spring/Spring.Core/Util/XmlUtils.cs new file mode 100644 index 00000000..9f2778e4 --- /dev/null +++ b/src/Spring/Spring.Core/Util/XmlUtils.cs @@ -0,0 +1,151 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Configuration; +using System.Xml; +using System.IO; +using System.Xml.Schema; + +#endregion + +namespace Spring.Util +{ + /// + /// XML utility methods. + /// + /// Aleksandar Seovic + /// $Id: XmlUtils.cs,v 1.5 2007/04/22 00:02:38 oakinger Exp $ + public class XmlUtils + { +#if !NET_2_0 + /// + /// Gets an appropriate implementation + /// for the supplied . + /// + /// The XML that is going to be read. + /// XML schemas that should be used for validation. + /// Validation event handler. + /// + /// A validating implementation. + /// + public static XmlReader CreateValidatingReader(Stream stream, XmlSchemaCollection schemas, ValidationEventHandler eventHandler) + { + return CreateValidatingReader(stream, new XmlUrlResolver(), schemas, eventHandler); + } + + /// + /// Gets an appropriate implementation + /// for the supplied . + /// + /// The XML that is going to be read. + /// to be used for resolving external references + /// XML schemas that should be used for validation. + /// Validation event handler. + /// + /// A validating implementation. + /// + public static XmlReader CreateValidatingReader(Stream stream, XmlResolver xmlResolver, XmlSchemaCollection schemas, ValidationEventHandler eventHandler) + { + XmlValidatingReader reader = new XmlValidatingReader(new XmlTextReader(stream)); + reader.XmlResolver = xmlResolver; + reader.Schemas.Add(schemas); + reader.ValidationType = ValidationType.Schema; + if (eventHandler != null) + { + reader.ValidationEventHandler += eventHandler; + } + return reader; + } +#else + /// + /// Gets an appropriate implementation + /// for the supplied . + /// + /// The XML that is going to be read. + /// XML schemas that should be used for validation. + /// Validation event handler. + /// + /// A validating implementation. + /// + public static XmlReader CreateValidatingReader(Stream stream, XmlSchemaSet schemas, ValidationEventHandler eventHandler) + { + return CreateValidatingReader(stream, new XmlUrlResolver(), schemas, eventHandler); + } + + /// + /// Gets an appropriate implementation + /// for the supplied . + /// + /// The XML that is going to be read. + /// to be used for resolving external references + /// XML schemas that should be used for validation. + /// Validation event handler. + /// + /// A validating implementation. + /// + public static XmlReader CreateValidatingReader(Stream stream, XmlResolver xmlResolver, XmlSchemaSet schemas, ValidationEventHandler eventHandler) + { + XmlReaderSettings settings = new XmlReaderSettings(); + settings.XmlResolver = xmlResolver; + settings.Schemas.Add(schemas); + settings.ValidationType = ValidationType.Schema; + if (eventHandler != null) + { + settings.ValidationEventHandler += eventHandler; + } + + return XmlReader.Create(stream, settings); + } +#endif + + +#if !NET_2_0 + /// + /// Gets an implementation + /// for the supplied . + /// + /// The XML that is going to be read. + /// + /// A non-validating implementation. + /// + public static XmlReader CreateReader(Stream stream) + { + return new XmlTextReader(stream); + } +#else + + /// + /// Gets an appropriate implementation + /// for the supplied . + /// + /// The XML that is going to be read. + /// + /// A non-validating implementation. + /// + public static XmlReader CreateReader(Stream stream) + { + return XmlReader.Create(stream); + } +#endif + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/Actions/ErrorMessageAction.cs b/src/Spring/Spring.Core/Validation/Actions/ErrorMessageAction.cs new file mode 100644 index 00000000..b58f2adc --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Actions/ErrorMessageAction.cs @@ -0,0 +1,121 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +using Spring.Expressions; +using Spring.Util; + +namespace Spring.Validation.Actions +{ + /// + /// Implementation of that adds error message + /// to the validation errors container. + /// + /// Aleksandar Seovic + /// $Id: ErrorMessageAction.cs,v 1.5 2008/02/05 20:40:25 aseovic Exp $ + public class ErrorMessageAction : BaseValidationAction + { + private string messageId; + private IExpression[] messageParams; + private string[] providers; + + /// + /// Initializes a new instance of the class. + /// + /// Error message resource identifier. + /// Names of the error providers this message should be added to. + public ErrorMessageAction(string messageId, params string[] providers) + { + AssertUtils.ArgumentHasText(messageId, "messageId"); + if (providers == null || providers.Length == 0) + { + throw new ArgumentException("At least one error provider has to be specified.", "providers"); + } + + this.messageId = messageId; + this.providers = providers; + } + + /// + /// Sets the expressions that should be resolved to error message parameters. + /// + /// The expressions that should be resolved to error message parameters. + public IExpression[] Parameters + { + set { messageParams = value; } + } + + /// + /// Called when associated validator is invalid. + /// + /// Validation context. + /// Additional context parameters. + /// Validation errors container. + protected override void OnInvalid(object validationContext, IDictionary contextParams, IValidationErrors errors) + { + ErrorMessage error = CreateErrorMessage(validationContext, contextParams); + foreach (string provider in this.providers) + { + errors.AddError(provider.Trim(), error); + } + } + + /// + /// Resolves the error message. + /// + /// Validation context to resolve message parameters against. + /// Additional context parameters. + /// Resolved error message + private ErrorMessage CreateErrorMessage(object validationContext, IDictionary contextParams) + { + if (messageParams != null && messageParams.Length > 0) + { + object[] parameters = ResolveMessageParameters(messageParams, validationContext, contextParams); + return new ErrorMessage(messageId, parameters); + } + else + { + return new ErrorMessage(messageId, null); + } + } + + /// + /// Resolves the message parameters. + /// + /// List of parameters to resolve. + /// Validation context to resolve parameters against. + /// Additional context parameters. + /// Resolved message parameters. + private object[] ResolveMessageParameters(IList messageParams, object validationContext, IDictionary contextParams) + { + object[] parameters = new object[messageParams.Count]; + for (int i = 0; i < messageParams.Count; i++) + { + parameters[i] = ((IExpression) messageParams[i]).GetValue(validationContext, contextParams); + } + + return parameters; + } + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/Actions/ExpressionAction.cs b/src/Spring/Spring.Core/Validation/Actions/ExpressionAction.cs new file mode 100644 index 00000000..fc49b5fb --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Actions/ExpressionAction.cs @@ -0,0 +1,113 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +using Spring.Expressions; + +namespace Spring.Validation.Actions +{ + /// + /// Implementation of that allows you + /// to define Spring.NET expressions that should be evaluated after + /// validation. + /// + /// Aleksandar Seovic + /// $Id: ExpressionAction.cs,v 1.6 2008/02/05 20:40:25 aseovic Exp $ + public class ExpressionAction : BaseValidationAction + { + private IExpression onValid; + private IExpression onInvalid; + + /// + /// Initializes a new instance of the class. + /// + public ExpressionAction() + {} + + /// + /// Initializes a new instance of the class. + /// + /// Expression to execute when validator is valid. + /// Expression to execute when validator is not valid. + public ExpressionAction(string onValid, string onInvalid) + : this((onValid != null ? Expression.Parse(onValid) : null), (onInvalid != null ? Expression.Parse(onInvalid) : null)) + {} + + /// + /// Initializes a new instance of the class. + /// + /// Expression to execute when validator is valid. + /// Expression to execute when validator is not valid. + public ExpressionAction(IExpression onValid, IExpression onInvalid) + { + this.onValid = onValid; + this.onInvalid = onInvalid; + } + + /// + /// Gets or sets the expression to execute when validator is valid. + /// + /// The expression to execute when validator is valid. + public IExpression Valid + { + get { return onValid; } + set { onValid = value; } + } + + /// + /// Gets or sets the expression to execute when validator is not valid. + /// + /// The expression to execute when validator is not valid. + public IExpression Invalid + { + get { return onInvalid; } + set { onInvalid = value; } + } + + /// + /// Called when associated validator is valid. + /// + /// Validation context. + /// Additional context parameters. + /// Validation errors container. + protected override void OnValid(object validationContext, IDictionary contextParams, IValidationErrors errors) + { + if (Valid != null) + { + Valid.GetValue(validationContext, contextParams); + } + } + + /// + /// Called when associated validator is invalid. + /// + /// Validation context. + /// Additional context parameters. + /// Validation errors container. + protected override void OnInvalid(object validationContext, IDictionary contextParams, IValidationErrors errors) + { + if (Invalid != null) + { + Invalid.GetValue(validationContext, contextParams); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/AnyValidatorGroup.cs b/src/Spring/Spring.Core/Validation/AnyValidatorGroup.cs new file mode 100644 index 00000000..0392f047 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/AnyValidatorGroup.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +using Spring.Expressions; + +namespace Spring.Validation +{ + /// + /// implementation that supports grouping of validators. + /// + /// + ///

    + /// This validator will be valid when one or more of the validators in the Validators + /// collection are valid. + ///

    + ///

    + /// ValidationErrors property will return a union of all validation error messages + /// for the contained validators, but only if this validator is not valid (meaning, when none + /// of the contained validators are valid). + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: AnyValidatorGroup.cs,v 1.9 2008/02/05 20:40:26 aseovic Exp $ + public class AnyValidatorGroup : ValidatorGroup + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public AnyValidatorGroup() + {} + + /// + /// Initializes a new instance of the class. + /// + /// The expression that determines if this validator should be evaluated. + public AnyValidatorGroup(string when) : base(when) + {} + + /// + /// Initializes a new instance of the class. + /// + /// The expression that determines if this validator should be evaluated. + public AnyValidatorGroup(IExpression when) : base(when) + {} + + #endregion + + /// + /// Validates the specified object. + /// + /// The object to validate. + /// Additional context parameters. + /// instance to add error messages to. + /// True if validation was successful, False otherwise. + public override bool Validate(object validationContext, IDictionary contextParams, IValidationErrors errors) + { + ValidationErrors tmpErrors = new ValidationErrors(); + bool valid = true; + + if (EvaluateWhen(validationContext, contextParams)) + { + valid = false; + foreach (IValidator validator in Validators) + { + valid = validator.Validate(validationContext, contextParams, tmpErrors) || valid; + if (valid) + { + break; + } + } + + if (!valid) + { + errors.MergeErrors(tmpErrors); + } + ProcessActions(valid, validationContext, contextParams, errors); + } + + return valid; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/BaseValidationAction.cs b/src/Spring/Spring.Core/Validation/BaseValidationAction.cs new file mode 100644 index 00000000..e33265f5 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/BaseValidationAction.cs @@ -0,0 +1,142 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +using Spring.Expressions; + +namespace Spring.Validation +{ + /// + /// Abstract base class that should be extended by all + /// validation actions. + /// + /// + ///

    + /// This class implements template Execute method + /// and defines OnValid and OnInvalid methods that + /// can be overriden + /// by specific validation actions. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: BaseValidationAction.cs,v 1.6 2008/02/05 20:40:26 aseovic Exp $ + public abstract class BaseValidationAction : IValidationAction + { + #region Fields + + private IExpression when; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public BaseValidationAction() + {} + + #endregion + + #region Properties + + /// + /// Gets or sets the expression that determines if this validator should be evaluated. + /// + /// The expression that determines if this validator should be evaluated. + public IExpression When + { + get { return when; } + set { when = value; } + } + + #endregion + + /// + /// Executes the action. + /// + /// Whether associated validator is valid or not. + /// Validation context. + /// Additional context parameters. + /// Validation errors container. + public virtual void Execute(bool isValid, object validationContext, IDictionary contextParams, IValidationErrors errors) + { + if (EvaluateWhen(validationContext, contextParams)) + { + if (isValid) + { + OnValid(validationContext, contextParams, errors); + } + else + { + OnInvalid(validationContext, contextParams, errors); + } + } + } + + #region Abstract methods + + // CLOVER:OFF + + /// + /// Called when associated validator is valid. + /// + /// Validation context. + /// Additional context parameters. + /// Validation errors container. + protected virtual void OnValid(object validationContext, IDictionary contextParams, IValidationErrors errors) + {} + + /// + /// Called when associated validator is not valid. + /// + /// Validation context. + /// Additional context parameters. + /// Validation errors container. + protected virtual void OnInvalid(object validationContext, IDictionary contextParams, IValidationErrors errors) + {} + + // CLOVER:ON + + #endregion + + #region Helper methods + + /// + /// Evaluates 'when' expression. + /// + /// Root context to use for expression evaluation. + /// Additional context parameters. + /// True if the condition is true, False otherwise. + protected bool EvaluateWhen(object rootContext, IDictionary contextParams) + { + if (When == null) + { + return true; + } + + return Convert.ToBoolean(When.GetValue(rootContext, contextParams)); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/BaseValidator.cs b/src/Spring/Spring.Core/Validation/BaseValidator.cs new file mode 100644 index 00000000..50afd40b --- /dev/null +++ b/src/Spring/Spring.Core/Validation/BaseValidator.cs @@ -0,0 +1,210 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using Spring.Expressions; + +#endregion + +namespace Spring.Validation +{ + /// + /// Base class that defines common properties for all validators. + /// + /// + ///

    + /// Custom validators should always extend this class instead of + /// simply implementing interface, in + /// order to inherit common validator functionality. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: BaseValidator.cs,v 1.12 2008/02/05 20:40:26 aseovic Exp $ + public abstract class BaseValidator : IValidator + { + #region Fields + + private IList actions = new ArrayList(); + + private IExpression test; + private IExpression when; + + #endregion + + #region Constructors + + /// + /// Creates a new instance of the class. + /// + public BaseValidator() + {} + + /// + /// Creates a new instance of the class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public BaseValidator(string test, string when) + : this((test != null ? Expression.Parse(test) : null), (when != null ? Expression.Parse(when) : null)) + {} + + /// + /// Creates a new instance of the class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public BaseValidator(IExpression test, IExpression when) + { + this.test = test; + this.when = when; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the test expression. + /// + /// The test expression. + public IExpression Test + { + get { return test; } + set { test = value; } + } + + /// + /// Gets or sets the expression that determines if this validator should be evaluated. + /// + /// The expression that determines if this validator should be evaluated. + public IExpression When + { + get { return when; } + set { when = value; } + } + + /// + /// Gets or sets the validation actions. + /// + /// The actions that should be executed after validation. + public IList Actions + { + get { return actions; } + set { actions = value; } + } + + #endregion + + /// + /// Validates the specified object. + /// + /// The object to validate. + /// instance to add error messages to. + /// True if validation was successful, False otherwise. + public bool Validate(object validationContext, IValidationErrors errors) + { + return Validate(validationContext, null, errors); + } + + /// + /// Validates the specified object. + /// + /// The object to validate. + /// Additional context parameters. + /// instance to add error messages to. + /// True if validation was successful, False otherwise. + public virtual bool Validate(object validationContext, IDictionary contextParams, IValidationErrors errors) + { + bool valid = true; + + if (EvaluateWhen(validationContext, contextParams)) + { + valid = Validate(EvaluateTest(validationContext, contextParams)); + ProcessActions(valid, validationContext, contextParams, errors); + } + + return valid; + } + + /// + /// Validates test object. + /// + /// Object to validate. + /// True if specified object is valid, False otherwise. + protected abstract bool Validate(object objectToValidate); + + #region Helper Methods + + /// + /// Evaluates test expression. + /// + /// Root context to use for expression evaluation. + /// Additional context parameters. + /// Result of the test expression evaluation, or validation context if test is null. + protected object EvaluateTest(object rootContext, IDictionary contextParams) + { + if (Test == null) + { + return rootContext; + } + return Test.GetValue(rootContext, contextParams); + } + + /// + /// Evaluates when expression. + /// + /// Root context to use for expression evaluation. + /// Additional context parameters. + /// True if the condition is true, False otherwise. + protected bool EvaluateWhen(object rootContext, IDictionary contextParams) + { + if (When == null) + { + return true; + } + + return Convert.ToBoolean(When.GetValue(rootContext, contextParams)); + } + + /// + /// Processes the error messages. + /// + /// Whether validator is valid or not. + /// Validation context. + /// Additional context parameters. + /// Validation errors container. + protected void ProcessActions(bool isValid, object validationContext, IDictionary contextParams, IValidationErrors errors) + { + if (actions != null && actions.Count > 0) + { + foreach (IValidationAction action in actions) + { + action.Execute(isValid, validationContext, contextParams, errors); + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/CollectionValidator.cs b/src/Spring/Spring.Core/Validation/CollectionValidator.cs new file mode 100644 index 00000000..1b5c1e5b --- /dev/null +++ b/src/Spring/Spring.Core/Validation/CollectionValidator.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections; +using Spring.Expressions; + +namespace Spring.Validation +{ + /// + /// implementation that supports validating collections. + /// + /// + ///

    + /// This validator will be valid only when all of the validators in the Validators + /// collection are valid for all of the objects in the specified collection. + ///

    + ///

    + /// You can specify if you want to validate all of the collection elements regardless of the errors, by + /// setting the ValidateAll property to true. + ///

    + ///

    + /// If you set the IncludeElementErrors property to true, + /// ValidationErrors collection will contain a union of all validation error messages + /// for the contained validators; + /// Otherwise it will contain only error messages that were set for this Validator. + ///

    + ///
    + /// Damjan Tomic + /// Aleksandar Seovic + /// $Id: CollectionValidator.cs,v 1.5 2008/02/05 20:40:26 aseovic Exp $ + public class CollectionValidator : ValidatorGroup + { + #region Fields + + private bool validateAll = false; + private bool includeElementErrors = false; + private IExpression context; + + #endregion + + #region Properties + + /// + /// Gets or sets the value that indicates whether to validate all elements of the collection + /// regardless of the errors. + /// + public bool ValidateAll + { + get { return validateAll; } + set { validateAll = value; } + } + + /// + /// Gets or sets the value that indicates whether to capture all the errors of the specific + /// elements of the collection + /// + public bool IncludeElementErrors + { + get { return includeElementErrors; } + set { includeElementErrors = value; } + } + + + /// + /// Gets or sets the expression that should be used to narrow validation context. + /// + /// The expression that should be used to narrow validation context. + public IExpression Context + { + get { return context; } + set { context = value; } + } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public CollectionValidator() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The bool that determines if all elements of the collection should be evaluated. + /// regardless of the Errors + /// + /// The bool that determines whether Validate method should collect + /// all error messages returned by the item validators + public CollectionValidator(bool validateAll, bool includeElementErrors) + { + this.validateAll = validateAll; + this.includeElementErrors = includeElementErrors; + } + + /// + /// Initializes a new instance of the class. + /// + /// The expression that determines if this validator should be evaluated. + /// The bool that determines if this all elements of the collection should be evaluated. + /// regardless of the Errors + /// + /// The bool that determines whether Validate method should collect + /// all error messages returned by the item validators + + public CollectionValidator(IExpression when, bool validateAll, bool includeElementErrors) + : base(when) + { + this.validateAll = validateAll; + this.includeElementErrors = includeElementErrors; + } + + /// + /// Initializes a new instance of the class. + /// + /// The expression that determines if this validator should be evaluated. + /// The bool that determines if this all elements of the collection should be evaluated. + /// regardless of the Errors + /// + /// The bool that determines whether Validate method should collect + /// all error messages returned by the item validators + public CollectionValidator(string when, bool validateAll, bool includeElementErrors) + : this((when != null ? Expression.Parse(when) : null), validateAll,includeElementErrors) + { + this.validateAll = validateAll; + } + + #endregion + + /// + /// Validates the specified collection of objects. + /// If the IncludeElementErrors property was set to true, + /// collection will contain a union of all validation error messages + /// for the contained validators; + /// Otherwise it will contain only error messages that were set for this Validator. + /// + /// The collection to validate. + /// Additional context parameters. + /// instance to add error messages to. + /// True if validation was successful, False otherwise. + public override bool Validate(object validationContext, IDictionary contextParams, IValidationErrors errors) + { + if (Context != null) + { + validationContext = Context.GetValue(validationContext, contextParams); + } + + if (!(validationContext is IEnumerable)) + { + throw new ArgumentException("The type of the object for validation must be subtype of IEnumerable."); + } + + bool valid = true; + + if (EvaluateWhen(validationContext, contextParams)) + { + IEnumerable collectionToValidate = (validationContext is IDictionary + ? ((IDictionary) validationContext).Values + : (IEnumerable) validationContext); + + // decide whether to pass new validation errors collection + //(and discard error messages returned by the item validators) + // OR to pass validation errors collection that was passed to this method + //(and collect all error messages returned by the item validators) + IValidationErrors err = (includeElementErrors)? errors : new ValidationErrors(); + + foreach (object objectToValidate in collectionToValidate) + { + foreach (IValidator validator in Validators) + { + valid = validator.Validate(objectToValidate, contextParams, err) && valid; + if (!valid && !validateAll) + { + break; + } + } + + if (!valid && !validateAll) + { + break; + } + } + + ProcessActions(valid, validationContext, contextParams, errors); + } + + return valid; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/Config/ValidationNamespaceParser.cs b/src/Spring/Spring.Core/Validation/Config/ValidationNamespaceParser.cs new file mode 100644 index 00000000..23480a8a --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Config/ValidationNamespaceParser.cs @@ -0,0 +1,383 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Xml; + +using Spring.Core.TypeResolution; +using Spring.Context.Support; +using Spring.Expressions; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Threading; +using Spring.Util; + +#endregion + +namespace Spring.Validation.Config +{ + /// + /// Implementation of the custom configuration parser for validator definitions. + /// + /// Aleksandar Seovic + /// $Id: ValidationNamespaceParser.cs,v 1.2 2007/08/08 00:34:17 bbaia Exp $ + [ + NamespaceParser( + Namespace = "http://www.springframework.net/validation", + SchemaLocationAssemblyHint = typeof(ValidationNamespaceParser), + SchemaLocation = "/Spring.Validation.Config/spring-validation-1.1.xsd") + ] + public sealed class ValidationNamespaceParser : ObjectsNamespaceParser + { + private const string ValidatorTypePrefix = "validator: "; + +// [ThreadStatic] +// private int definitionCount = 0; + private readonly string key_DefinitionCount; + private int definitionCount + { + get + { + object tmp = LogicalThreadContext.GetData(key_DefinitionCount); + if (tmp != null) return (int)tmp; + LogicalThreadContext.SetData(key_DefinitionCount, 0); + return 0; + } + set + { + LogicalThreadContext.SetData(key_DefinitionCount, value); + } + } + + static ValidationNamespaceParser() + { + TypeRegistry.RegisterType(ValidatorTypePrefix + "group", typeof(ValidatorGroup)); + TypeRegistry.RegisterType(ValidatorTypePrefix + "any", typeof(AnyValidatorGroup)); + TypeRegistry.RegisterType(ValidatorTypePrefix + "exclusive", typeof(ExclusiveValidatorGroup)); + + + TypeRegistry.RegisterType(ValidatorTypePrefix + "collection", typeof(CollectionValidator)); + TypeRegistry.RegisterType(ValidatorTypePrefix + "required", typeof(RequiredValidator)); + TypeRegistry.RegisterType(ValidatorTypePrefix + "condition", typeof(ConditionValidator)); + TypeRegistry.RegisterType(ValidatorTypePrefix + "regex", typeof(RegularExpressionValidator)); + } + + /// + /// Initializes a new instance of the class. + /// + public ValidationNamespaceParser() + { + // generate unique key for instance field to be stored in LogicalThreadContext + string FIELDPREFIX = typeof(ValidationNamespaceParser).FullName + base.GetHashCode(); + key_DefinitionCount = FIELDPREFIX + ".definitionCount"; + } + + + /// + /// Parse the specified element and register any resulting + /// IObjectDefinitions with the IObjectDefinitionRegistry that is + /// embedded in the supplied ParserContext. + /// + /// The element to be parsed into one or more IObjectDefinitions + /// The object encapsulating the current state of the parsing + /// process. + /// + /// The primary IObjectDefinition (can be null as explained above) + /// + /// + /// Implementations should return the primary IObjectDefinition + /// that results from the parse phase if they wish to used nested + /// inside (for example) a <property> tag. + /// Implementations may return null if they will not + /// be used in a nested scenario. + /// + /// + public override IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + if (!element.HasAttribute("id")) + { + throw new ObjectDefinitionStoreException(parserContext.ReaderContext.Resource, "validator", "Top-level validator element must have an 'id' attribute defined."); + } + this.definitionCount = 0; + + //TODO pass down parserContext... + ParseAndRegisterValidator(element, parserContext.ParserHelper); + + return null; + //return definitionCount; + } + + /// + /// Parses the validator definition. + /// + /// Validator's identifier. + /// The element to parse. + /// The parser helper. + /// Validator object definition. + private IObjectDefinition ParseValidator(string id, XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + string typeName = GetTypeName(element); + string parent = element.GetAttribute(ObjectDefinitionConstants.ParentAttribute); + string test = element.GetAttribute(ValidatorDefinitionConstants.TestAttribute); + string when = element.GetAttribute(ValidatorDefinitionConstants.WhenAttribute); + string validateAll = element.GetAttribute(ValidatorDefinitionConstants.CollectionValidateAllAttribute); + string context = element.GetAttribute(ValidatorDefinitionConstants.CollectionContextAttribute); + string includeElementsErrors = element.GetAttribute(ValidatorDefinitionConstants.CollectionIncludeElementsErrors); + + string name = "validator: " + (StringUtils.HasText(id) ? id : this.definitionCount.ToString()); + MutablePropertyValues properties = new MutablePropertyValues(); + if (StringUtils.HasText(test)) + { + properties.Add("Test", test); + } + if (StringUtils.HasText(when)) + { + properties.Add("When", when); + } + if (StringUtils.HasText(validateAll)) + { + properties.Add("ValidateAll", validateAll); + } + if (StringUtils.HasText(validateAll)) + { + properties.Add("Context", context); + } + if (StringUtils.HasText(includeElementsErrors)) + { + properties.Add("IncludeElementErrors", includeElementsErrors); + } + + + ManagedList nestedValidators = new ManagedList(); + ManagedList actions = new ManagedList(); + foreach (XmlNode node in element.ChildNodes) + { + XmlElement child = node as XmlElement; + if (child != null) + { + switch (child.LocalName) + { + case ValidatorDefinitionConstants.PropertyElement: + string propertyName = child.GetAttribute(ValidatorDefinitionConstants.PropertyNameAttribute); + properties.Add(propertyName, base.GetPropertyValue(child, name, parserHelper)); + break; + case ValidatorDefinitionConstants.MessageElement: + actions.Add(ParseErrorMessageAction(child, parserHelper)); + break; + case ValidatorDefinitionConstants.ActionElement: + actions.Add(ParseGenericAction(child, parserHelper)); + break; + case ValidatorDefinitionConstants.ReferenceElement: + nestedValidators.Add(ParseValidatorReference(child, parserHelper)); + break; + default: + nestedValidators.Add(ParseAndRegisterValidator(child, parserHelper)); + break; + } + } + } + if (nestedValidators.Count > 0) + { + properties.Add("Validators", nestedValidators); + } + if (actions.Count > 0) + { + properties.Add("Actions", actions); + } + + IConfigurableObjectDefinition od + = parserHelper.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( + typeName, parent, parserHelper.ReaderContext.Reader.Domain); + + od.PropertyValues = properties; + od.IsSingleton = true; + od.IsLazyInit = true; + + return od; + } + + /// + /// Parses and potentially registers a validator. + /// + /// + /// Only validators that have id attribute specified are registered + /// as separate object definitions within application context. + /// + /// Validator XML element. + /// The parser helper. + /// Validator object definition. + private IObjectDefinition ParseAndRegisterValidator(XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + string id = element.GetAttribute(ObjectDefinitionConstants.IdAttribute); + IObjectDefinition validator = ParseValidator(id, element, parserHelper); + if (StringUtils.HasText(id)) + { + parserHelper.ReaderContext.Registry.RegisterObjectDefinition(id, validator); + this.definitionCount++; + } + return validator; + } + + /// + /// Gets the name of the object type for the specified element. + /// + /// The element. + /// The name of the object type. + private string GetTypeName(XmlElement element) + { + string typeName = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute); + if (StringUtils.IsNullOrEmpty(typeName)) + { + return ValidatorTypePrefix + element.LocalName; + } + return typeName; + } + + /// + /// Creates an error message action based on the specified message element. + /// + /// The message element. + /// The parser helper. + /// The error message action definition. + private static IObjectDefinition ParseErrorMessageAction(XmlElement message, ObjectDefinitionParserHelper parserHelper) + { + string messageId = message.GetAttribute(MessageConstants.IdAttribute); + string[] providers = message.GetAttribute(MessageConstants.ProvidersAttribute).Split(','); + ArrayList parameters = new ArrayList(); + + foreach (XmlElement param in message.ChildNodes) + { + IExpression paramExpression = Expression.Parse(param.GetAttribute(MessageConstants.ParameterValueAttribute)); + parameters.Add(paramExpression); + } + + string typeName = "Spring.Validation.Actions.ErrorMessageAction, Spring.Core"; + ConstructorArgumentValues ctorArgs = new ConstructorArgumentValues(); + ctorArgs.AddGenericArgumentValue(messageId); + ctorArgs.AddGenericArgumentValue(providers); + + string when = message.GetAttribute(ValidatorDefinitionConstants.WhenAttribute); + MutablePropertyValues properties = new MutablePropertyValues(); + if (StringUtils.HasText(when)) + { + properties.Add("When", when); + } + if (parameters.Count > 0) + { + properties.Add("Parameters", parameters.ToArray(typeof(IExpression))); + } + + IConfigurableObjectDefinition action = + parserHelper.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition(typeName, null, parserHelper.ReaderContext.Reader.Domain); + action.ConstructorArgumentValues = ctorArgs; + action.PropertyValues = properties; + + return action; + } + + /// + /// Creates a generic action based on the specified element. + /// + /// The action definition element. + /// The parser helper. + /// Generic validation action definition. + private IObjectDefinition ParseGenericAction(XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + string typeName = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute); + string when = element.GetAttribute(ValidatorDefinitionConstants.WhenAttribute); + MutablePropertyValues properties = base.GetPropertyValueSubElements("validator:action", element, parserHelper); + if (StringUtils.HasText(when)) + { + properties.Add("When", when); + } + + IConfigurableObjectDefinition action = + parserHelper.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition(typeName, null, parserHelper.ReaderContext.Reader.Domain); + action.PropertyValues = properties; + + return action; + } + + /// + /// Creates object definition for the validator reference. + /// + /// The action definition element. + /// The parser helper. + /// Generic validation action definition. + private IObjectDefinition ParseValidatorReference(XmlElement element, ObjectDefinitionParserHelper parserHelper) + { + string typeName = "Spring.Validation.ValidatorReference, Spring.Core"; + string name = element.GetAttribute(ValidatorDefinitionConstants.ReferenceNameAttribute); + string context = element.GetAttribute(ValidatorDefinitionConstants.ReferenceContextAttribute); + + MutablePropertyValues properties = new MutablePropertyValues(); + properties.Add("Name", name); + if (StringUtils.HasText(context)) + { + properties.Add("Context", context); + } + + IConfigurableObjectDefinition reference = + parserHelper.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition(typeName, null, parserHelper.ReaderContext.Reader.Domain); + reference.PropertyValues = properties; + return reference; + } + + #region Element & Attribute Name Constants + + private class ValidatorDefinitionConstants + { + public const string PropertyElement = "property"; + public const string MessageElement = "message"; + public const string ActionElement = "action"; + public const string ReferenceElement = "ref"; + + public const string TypeAttribute = "type"; + public const string TestAttribute = "test"; + public const string NameAttribute = "name"; + public const string WhenAttribute = "when"; + + public const string PropertyNameAttribute = "name"; + + public const string ReferenceNameAttribute = "name"; + public const string ReferenceContextAttribute = "context"; + + public const string CollectionValidateAllAttribute = "validate-all"; + public const string CollectionContextAttribute = "context"; + public const string CollectionIncludeElementsErrors = "include-element-errors"; + } + + private class MessageConstants + { + public const string ParamElement = "param"; + + public const string IdAttribute = "id"; + public const string ProvidersAttribute = "providers"; + public const string ParameterValueAttribute = "value"; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/Config/spring-validation-1.1.xsd b/src/Spring/Spring.Core/Validation/Config/spring-validation-1.1.xsd new file mode 100644 index 00000000..0290b8b5 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Config/spring-validation-1.1.xsd @@ -0,0 +1,160 @@ + + + + + + + + Spring.NET Validation Framework Config Schema Definition + + Author: Aleksandar Seovic + + This file defines a configuration schema for the validation framework + object definitions. Using elements from this schema instead of the + standard object definitions can greatly simplify validator configuration. + + + + + + Defines a message type. + + + + + + + + + + + + Defines a message parameter type. + + + + + + + Defines an action type. + + + + + + + + + + + Defines a validator reference type. + + + + + + + + Defines base validator type. + + + + + + + + + + + + + + Defines a generic validator type. + + + + + + + + + + + + + + + + Defines a regex validator type. + + + + + + + + + + + + + + + Defines a validator group type. + + + + + + + + + + + + + + + + + + + + + + + + + + + Defines a collection validator group type. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Core/Validation/ErrorMessage.cs b/src/Spring/Spring.Core/Validation/ErrorMessage.cs new file mode 100644 index 00000000..61d2a869 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/ErrorMessage.cs @@ -0,0 +1,180 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Spring.Context; + +#endregion + +namespace Spring.Validation +{ + /// + /// Represents a single validation error message. + /// + /// Aleksandar Seovic + /// Goran Milosavljevic + /// $Id: ErrorMessage.cs,v 1.5 2007/02/19 17:56:42 aseovic Exp $ + [Serializable] + public class ErrorMessage : IXmlSerializable + { + #region Constructors + + /// + /// Default constructor. + /// + public ErrorMessage() + {} + + /// + /// Initializes a new instance of the class. + /// + /// Error message resource identifier. + /// Parameters that should be used for message resolution. + public ErrorMessage(string id, params object[] parameters) + { + this.id = id; + this.parameters = parameters; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the resource identifier for this message. + /// + /// The resource identifier for this message. + public string Id + { + get { return id; } + } + + /// + /// Gets or sets the message parameters. + /// + /// The message parameters. + public object[] Parameters + { + get { return parameters; } + } + + #endregion + + #region IXmlSerializable implementations + + /// + /// This property is reserved, apply the + /// + /// to the class instead. + /// + /// + /// An + /// that describes the XML representation of the object that + /// is produced by the + /// + /// method and consumed by the + /// + /// method. + /// + /// + public XmlSchema GetSchema() + { + return null; + } + + /// + /// Generates an object from its XML representation. + /// + /// + /// The stream + /// from which the object is deserialized. + /// + public void ReadXml(XmlReader reader) + { + id = reader.GetAttribute("Id"); + if (!reader.IsEmptyElement) + { + reader.Read(); + reader.Read(); + XmlSerializer xs = new XmlSerializer(typeof(object[])); + parameters = (object[])xs.Deserialize(reader); + reader.Read(); + } + reader.Read(); + } + + /// + /// Converts an object into its XML representation. + /// + /// + /// The stream + /// to which the object is serialized. + /// + public void WriteXml(XmlWriter writer) + { + writer.WriteAttributeString("Id", id.ToString()); + + if (parameters != null) + { + writer.WriteStartElement("Parameters"); + + XmlSerializer xs = new XmlSerializer(parameters.GetType()); + xs.Serialize(writer, parameters); + + writer.WriteEndElement(); + } + } + + #endregion + + #region ErrorMessage methods + + /// + /// Resolves the message against specified . + /// + /// Message source to resolve this error message against. + /// Resolved error message. + public string GetMessage(IMessageSource messageSource) + { + if (Parameters == null) + { + return messageSource.GetMessage(Id); + } + else + { + return messageSource.GetMessage(Id, Parameters); + } + } + + #endregion + + #region Data members + + private string id; + private object[] parameters; + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/ExclusiveValidatorGroup.cs b/src/Spring/Spring.Core/Validation/ExclusiveValidatorGroup.cs new file mode 100644 index 00000000..d8ef54c8 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/ExclusiveValidatorGroup.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +using Spring.Expressions; + +namespace Spring.Validation +{ + /// + /// implementation that supports grouping of validators. + /// + /// + ///

    + /// This validator will be valid when one and only one of the validators in the Validators collection are valid + ///

    + ///

    + /// ValidationErrors property will return a union of all validation error messages + /// for the contained validators, but only if this validator is not valid (meaning, when none + /// of the contained validators are valid). + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ExclusiveValidatorGroup.cs,v 1.8 2008/02/05 20:40:26 aseovic Exp $ + public class ExclusiveValidatorGroup : ValidatorGroup + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public ExclusiveValidatorGroup() + {} + + /// + /// Initializes a new instance of the class. + /// + /// The expression that determines if this validator should be evaluated. + public ExclusiveValidatorGroup(string when) : base(when) + {} + + /// + /// Initializes a new instance of the class. + /// + /// The expression that determines if this validator should be evaluated. + public ExclusiveValidatorGroup(IExpression when) : base(when) + {} + + #endregion + + /// + /// Validates the specified object. + /// + /// The object to validate. + /// Additional context parameters. + /// instance to add error messages to. + /// True if validation was successful, False otherwise. + public override bool Validate(object validationContext, IDictionary contextParams, IValidationErrors errors) + { + IValidationErrors tmpErrors = new ValidationErrors(); + bool valid = true; + + if (EvaluateWhen(validationContext, contextParams)) + { + valid = false; + foreach (IValidator validator in Validators) + { + bool tmpValid = validator.Validate(validationContext, contextParams, tmpErrors); + if (valid && tmpValid) + { + valid = false; + break; + } + else if (tmpValid) + { + valid = true; + } + } + + ProcessActions(valid, validationContext, contextParams, errors); + } + + return valid; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/IValidationAction.cs b/src/Spring/Spring.Core/Validation/IValidationAction.cs new file mode 100644 index 00000000..418086bf --- /dev/null +++ b/src/Spring/Spring.Core/Validation/IValidationAction.cs @@ -0,0 +1,57 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +using Spring.Validation.Actions; + +#endregion + +namespace Spring.Validation +{ + /// + /// An action that should be executed after validator is evaluated. + /// + /// + ///

    + /// This interface allows us to define the actions that should be executed + /// after validation in a generic fashion. + ///

    + ///

    + /// For example, addition of error messages to validation errors collection + /// is performed by one specific implementation of this interface, . + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: IValidationAction.cs,v 1.4 2008/02/05 20:40:26 aseovic Exp $ + public interface IValidationAction + { + /// + /// Executes the action. + /// + /// Whether associated validator is valid or not. + /// Validation context. + /// Additional context parameters. + /// Validation errors container. + void Execute(bool isValid, object validationContext, IDictionary contextParams, IValidationErrors errors); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/IValidationErrors.cs b/src/Spring/Spring.Core/Validation/IValidationErrors.cs new file mode 100644 index 00000000..5c6b2543 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/IValidationErrors.cs @@ -0,0 +1,90 @@ +using System.Collections; +using Spring.Context; + +namespace Spring.Validation +{ + /// + /// An interface that validation errors containers have to implement. + /// + /// Aleksandar Seovic + /// $Id: IValidationErrors.cs,v 1.1 2008/02/05 20:40:26 aseovic Exp $ + public interface IValidationErrors + { + /// + /// Does this instance contain any validation errors? + /// + /// + ///

    + /// If this returns , this means that it (obviously) + /// contains no validation errors. + ///

    + ///
    + /// if this instance is empty. + bool IsEmpty { get; } + + /// + /// Gets the list of all error providers. + /// + IList Providers { get; } + + /// + /// Adds the supplied to this + /// instance's collection of errors. + /// + /// + /// The provider that should be used for message grouping; can't be + /// . + /// + /// The error message to add. + /// + /// If the supplied or is . + /// + void AddError(string provider, ErrorMessage message); + + /// + /// Merges another instance of into this one. + /// + /// + ///

    + /// If the supplied is , + /// then no errors will be added to this instance, and this method will + /// (silently) return. + ///

    + ///
    + /// + /// The validation errors to merge; can be . + /// + void MergeErrors(ValidationErrors errorsToMerge); + + /// + /// Gets the list of errors for the supplied error . + /// + /// + ///

    + /// If there are no errors for the supplied , + /// an empty will be returned. + ///

    + ///
    + /// Error key that was used to group messages. + /// + /// A list of all s for the supplied lookup . + /// + IList GetErrors(string provider); + + /// + /// Gets the list of resolved error messages for the supplied lookup . + /// + /// + ///

    + /// If there are no errors for the supplied lookup , + /// an empty will be returned. + ///

    + ///
    + /// Error key that was used to group messages. + /// to resolve messages against. + /// + /// A list of resolved error messages for the supplied lookup . + /// + IList GetResolvedErrors(string provider, IMessageSource messageSource); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/IValidator.cs b/src/Spring/Spring.Core/Validation/IValidator.cs new file mode 100644 index 00000000..48e0f5f4 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/IValidator.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +#endregion + +namespace Spring.Validation +{ + /// + /// An object that can validate application-specific objects. + /// + /// + ///

    + /// The primary motivation for this interface is to enable validation to be + /// decoupled from the (user) interface and placed in business objects. + ///

    + ///

    + /// Application developers writing their own custom + /// implementations will + /// typically not implement this interface directly. In most cases, custom + /// validators woud be better served deriving from the + /// class, with the + /// custom validation ligic being implemented in an override of the + /// + /// + /// template method. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: IValidator.cs,v 1.8 2008/02/05 20:40:26 aseovic Exp $ + /// + public interface IValidator + { + /// + /// Validates the specified object. + /// + /// The object to validate. + /// + /// The instance to add any error + /// messages to in the case of validation failure. + /// + /// + /// if validation was successful. + /// + bool Validate(object validationContext, IValidationErrors errors); + + /// + /// Validates the specified object. + /// + /// The object to validate. + /// Additional context parameters. + /// + /// The instance to add any error + /// messages to in the case of validation failure. + /// + /// + /// if validation was successful. + /// + bool Validate(object validationContext, IDictionary contextParams, IValidationErrors errors); + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/ValidatedAttribute.cs b/src/Spring/Spring.Core/Validation/ValidatedAttribute.cs new file mode 100644 index 00000000..bdd782ad --- /dev/null +++ b/src/Spring/Spring.Core/Validation/ValidatedAttribute.cs @@ -0,0 +1,39 @@ +using System; + +namespace Spring.Validation +{ + /// + /// Allows developers to specify which validator should be used + /// to validate method argument. + /// + /// Damjan Tomic + /// Aleksandar Seovic + /// $Id: ValidatedAttribute.cs,v 1.1 2008/04/02 23:02:36 markpollack Exp $ + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] + [Serializable] + public class ValidatedAttribute : Attribute + { + private readonly string validatorName; + + /// + /// Creates an attribute instance. + /// + /// + /// The name of the validator to use (must be defined within + /// Spring application context). + /// + public ValidatedAttribute(string validatorName) + { + this.validatorName = validatorName; + } + + /// + /// Gets the name of the validator to use. + /// + /// The name of the validator to use. + public string ValidatorName + { + get { return validatorName; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/ValidationErrors.cs b/src/Spring/Spring.Core/Validation/ValidationErrors.cs new file mode 100644 index 00000000..384d7e30 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/ValidationErrors.cs @@ -0,0 +1,288 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using Spring.Context; +using Spring.Util; + +#endregion + +namespace Spring.Validation +{ + /// + /// A container for validation errors. + /// + /// + ///

    + /// This class groups validation errors by validator names and allows + /// access to both the complete errors collection and to the errors for a + /// certain validator. + ///

    + ///
    + /// Aleksandar Seovic + /// Goran Milosavljevic + /// $Id: ValidationErrors.cs,v 1.9 2008/02/05 20:40:26 aseovic Exp $ + [Serializable] + public class ValidationErrors : IValidationErrors, IXmlSerializable + { + #region Constructors + + /// + /// Default constructor. + /// + public ValidationErrors() + {} + + #endregion + + #region IXmlSerializable implementations + + /// + /// This property is reserved, apply the + /// + /// to the class instead. + /// + /// + /// An that describes the + /// XML representation of the object that is produced by + /// the + /// method and consumed by the + /// + /// method. + /// + public XmlSchema GetSchema() + { + return null; + } + + /// + /// Generates an object from its XML representation. + /// + /// + /// The stream + /// from which the object is deserialized. + /// + public void ReadXml(XmlReader reader) + { + if (!reader.IsEmptyElement) + { + reader.Read(); + while (reader.Name == "Provider") + { + object key = reader.GetAttribute("Id"); + reader.Read(); + while (reader.Name == "ErrorMessage") + { + XmlSerializer xs = new XmlSerializer(typeof(ErrorMessage)); + object value = xs.Deserialize(reader); + + IList mapValue = (IList)errorMap[key]; + + if (mapValue == null) + { + mapValue = new ArrayList(); + errorMap[key] = mapValue; + } + + mapValue.Add(value); + } + reader.Read(); + } + } + } + + /// + /// Converts an object into its XML representation. + /// + /// + /// The stream + /// to which the object is serialized. + /// + public void WriteXml(XmlWriter writer) + { + foreach (DictionaryEntry entry in errorMap) + { + writer.WriteStartElement("Provider"); + writer.WriteAttributeString("Id", entry.Key as String); + + if (entry.Value != null) + { + ArrayList errorsList = (ArrayList)entry.Value; + foreach (object o in errorsList) + { + ErrorMessage error = (ErrorMessage)o; + XmlSerializer xs = new XmlSerializer(typeof(ErrorMessage)); + xs.Serialize(writer, error); + } + } + + writer.WriteEndElement(); + } + } + + #endregion + + #region ValidationErrors methods + + /// + /// Does this instance contain any validation errors? + /// + /// + ///

    + /// If this returns , this means that it (obviously) + /// contains no validation errors. + ///

    + ///
    + /// if this instance is empty. + public bool IsEmpty + { + get { return this.errorMap.Count == 0; } + } + + /// + /// Gets the list of all providers. + /// + public IList Providers + { + get { return new ArrayList(this.errorMap.Keys); } + } + + /// + /// Adds the supplied to this + /// instance's collection of errors. + /// + /// + /// The provider that should be used for message grouping; can't be + /// . + /// + /// The error message to add. + /// + /// If the supplied or is . + /// + public void AddError(string provider, ErrorMessage message) + { + AssertUtils.ArgumentNotNull(provider, "provider"); + AssertUtils.ArgumentNotNull(message, "errorMessage"); + + IList errors = (IList) errorMap[provider]; + if (errors == null) + { + errors = new ArrayList(); + errorMap[provider] = errors; + } + errors.Add(message); + } + + /// + /// Merges another instance of into this one. + /// + /// + ///

    + /// If the supplied is , + /// then no errors will be added to this instance, and this method will + /// (silently) return. + ///

    + ///
    + /// + /// The validation errors to merge; can be . + /// + public void MergeErrors(ValidationErrors errorsToMerge) + { + if (errorsToMerge != null) + { + foreach (DictionaryEntry errorEntry in errorsToMerge.errorMap) + { + ArrayList errList = (ArrayList) this.errorMap[errorEntry.Key]; + if (errList == null) + { + this.errorMap[errorEntry.Key] = errorEntry.Value; + } + else + { + errList.AddRange((IList) errorEntry.Value); + } + } + } + } + + /// + /// Gets the list of errors for the supplied lookup . + /// + /// + ///

    + /// If there are no errors for the supplied lookup , + /// an empty will be returned. + ///

    + ///
    + /// Error key that was used to group messages. + /// + /// A list of all s for the supplied lookup . + /// + public IList GetErrors(string provider) + { + IList errors = (IList) errorMap[provider]; + return errors == null ? ObjectUtils.EmptyObjects : errors; + } + + /// + /// Gets the list of resolved error messages for the supplied lookup . + /// + /// + ///

    + /// If there are no errors for the supplied lookup , + /// an empty will be returned. + ///

    + ///
    + /// Error key that was used to group messages. + /// to resolve messages against. + /// + /// A list of resolved error messages for the supplied lookup . + /// + public IList GetResolvedErrors(string provider, IMessageSource messageSource) + { + IList messages = new ArrayList(); + IList errors = (IList) errorMap[provider]; + + if (errors != null) + { + foreach (ErrorMessage error in errors) + { + messages.Add(error.GetMessage(messageSource)); + } + } + + return messages; + } + + #endregion + + #region Data members + + private readonly IDictionary errorMap = new Hashtable(); + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/ValidationException.cs b/src/Spring/Spring.Core/Validation/ValidationException.cs new file mode 100644 index 00000000..68b1c789 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/ValidationException.cs @@ -0,0 +1,165 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Validation +{ + /// + /// Thrown by the validation advice if the method parameters validation fails. + /// + /// Aleksandar Seovic + /// $Id: ValidationException.cs,v 1.1 2008/02/05 20:40:26 aseovic Exp $ + [Serializable] + public class ValidationException : Exception + { + private readonly IValidationErrors errors; + + /// + /// Creates a new instance of the ValidationException class. + /// + public ValidationException() + { + } + + /// + /// Creates a new instance of the ValidationException class with + /// specified validation errors. + /// + /// + /// Validation errors. + /// + public ValidationException(IValidationErrors errors) + { + this.errors = errors; + } + + /// + /// Creates a new instance of the ValidationException class with the + /// specified message. + /// + /// + /// A message about the exception. + /// + public ValidationException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the ValidationException class with the + /// specified message and validation errors. + /// + /// + /// A message about the exception. + /// + /// + /// Validation errors. + /// + public ValidationException(string message, IValidationErrors errors) + : base(message) + { + this.errors = errors; + } + + /// + /// Creates a new instance of the ValidationException class with the + /// specified message and root cause. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public ValidationException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the ValidationException class with the + /// specified message, root cause and validation errors. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + /// + /// Validation errors. + /// + public ValidationException(string message, Exception rootCause, IValidationErrors errors) + : base(message, rootCause) + { + this.errors = errors; + } + + /// + /// Creates a new instance of the ValidationException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ValidationException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + this.errors = (IValidationErrors) info.GetValue("errors", typeof (IValidationErrors)); + } + + /// + /// Implements object serialization. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("errors", this.errors); + } + + /// + /// Gets validation errors. + /// + /// Validation errors. + public IValidationErrors ValidationErrors + { + get { return errors; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/ValidatorGroup.cs b/src/Spring/Spring.Core/Validation/ValidatorGroup.cs new file mode 100644 index 00000000..02154c48 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/ValidatorGroup.cs @@ -0,0 +1,122 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +using Spring.Expressions; + +namespace Spring.Validation +{ + /// + /// implementation that supports grouping of validators. + /// + /// + ///

    + /// This validator will be valid only when all of the validators in the Validators + /// collection are valid. + ///

    + ///

    + /// ValidationErrors property will return a union of all validation error messages + /// for the contained validators. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ValidatorGroup.cs,v 1.10 2008/02/05 20:40:26 aseovic Exp $ + public class ValidatorGroup : BaseValidator + { + #region Fields + + private IList validators = new ArrayList(); + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public ValidatorGroup() + {} + + /// + /// Initializes a new instance of the class. + /// + /// The expression that determines if this validator should be evaluated. + public ValidatorGroup(string when) : this((when != null ? Expression.Parse(when) : null)) + {} + + /// + /// Initializes a new instance of the class. + /// + /// The expression that determines if this validator should be evaluated. + public ValidatorGroup(IExpression when) : base(null, when) + {} + + #endregion + + #region Properties + + /// + /// Gets or sets the validators. + /// + /// The validators. + public IList Validators + { + get { return validators; } + set { validators = value; } + } + + #endregion + + /// + /// Validates the specified object. + /// + /// The object to validate. + /// Additional context parameters. + /// instance to add error messages to. + /// True if validation was successful, False otherwise. + public override bool Validate(object validationContext, IDictionary contextParams, IValidationErrors errors) + { + bool valid = true; + + if (EvaluateWhen(validationContext, contextParams)) + { + foreach (IValidator validator in validators) + { + valid = validator.Validate(validationContext, contextParams, errors) && valid; + } + ProcessActions(valid, validationContext, contextParams, errors); + } + + return valid; + } + + /// + /// Doesn't do anything for validator group as there is no single test. + /// + /// Object to validate. + /// True if specified object is valid, False otherwise. + protected override bool Validate(object objectToValidate) + { + throw new NotSupportedException("Validator group does not support this method."); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/ValidatorReference.cs b/src/Spring/Spring.Core/Validation/ValidatorReference.cs new file mode 100644 index 00000000..a710b11b --- /dev/null +++ b/src/Spring/Spring.Core/Validation/ValidatorReference.cs @@ -0,0 +1,146 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +using Spring.Expressions; +using Spring.Objects.Factory; + +namespace Spring.Validation +{ + /// + /// Represents a reference to an externally defined validator object + /// + /// + ///

    + /// This class allows validation groups to reference validators that + /// are defined outside of the group itself. + ///

    + ///

    + /// It also allows users to narrow the context for the referenced validator + /// by specifying value for the Context property. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ValidatorReference.cs,v 1.5 2008/02/05 20:40:26 aseovic Exp $ + public class ValidatorReference : IValidator, IObjectFactoryAware + { + #region Fields + + private IObjectFactory objectFactory; + + private string name; + private IExpression context; + private IValidator validator; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public ValidatorReference() + {} + + #endregion + + #region Properties + + /// + /// Gets or sets the name of the referenced validator. + /// + /// The name of the referenced validator. + public string Name + { + get { return name; } + set { name = value; } + } + + /// + /// Gets or sets the expression that should be used to narrow validation context. + /// + /// The expression that should be used to narrow validation context. + public IExpression Context + { + get { return context; } + set { context = value; } + } + + #endregion + + /// + /// Validates the specified object. + /// + /// The object to validate. + /// instance to add error messages to. + /// True if validation was successful, False otherwise. + public bool Validate(object validationContext, IValidationErrors errors) + { + return Validate(validationContext, null, errors); + } + + /// + /// Validates the specified object. + /// + /// The object to validate. + /// Additional context parameters. + /// instance to add error messages to. + /// True if validation was successful, False otherwise. + public bool Validate(object validationContext, IDictionary contextParams, IValidationErrors errors) + { + if (Context != null) + { + validationContext = Context.GetValue(validationContext, contextParams); + } + + if (validator == null) + { + validator = (IValidator) objectFactory.GetObject(Name); + } + + return validator.Validate(validationContext, contextParams, errors); + } + + /// + /// Callback that supplies the owning factory to an object instance. + /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + /// + ///

    + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

    + ///
    + /// + /// In case of initialization errors. + /// + public IObjectFactory ObjectFactory + { + set { objectFactory = value; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/Validators/ConditionValidator.cs b/src/Spring/Spring.Core/Validation/Validators/ConditionValidator.cs new file mode 100644 index 00000000..07a98cf6 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Validators/ConditionValidator.cs @@ -0,0 +1,88 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Validation +{ + /// + /// Evaluates validator test using condition evaluator. + /// + /// Aleksandar Seovic + /// $Id: ConditionValidator.cs,v 1.5 2006/04/09 07:19:04 markpollack Exp $ + public class ConditionValidator : BaseValidator + { + #region Constructors + + /// + /// Creates a new instance of the class. + /// + public ConditionValidator() + {} + + /// + /// Creates a new instance of the class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public ConditionValidator(string test, string when) : base(test, when) + { + AssertUtils.ArgumentHasText(test, "test"); + } + + /// + /// Creates a new instance of the class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public ConditionValidator(IExpression test, IExpression when) : base(test, when) + { + AssertUtils.ArgumentNotNull(test, "test"); + } + + #endregion + + /// + /// Evaluates the test using condition evaluator. + /// + /// + ///

    + /// Test can be any logical expression that is supported by the Spring.NET logical + /// expression evaluation engine, and can use any variables that can be resolved + /// by the variable resolver used by the validation engine. + ///

    + ///
    + /// The object to validate. + /// + /// if the supplied is valid. + /// + protected override bool Validate(object objectToValidate) + { + return Convert.ToBoolean(objectToValidate); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/Validators/CreditCardValidator.cs b/src/Spring/Spring.Core/Validation/Validators/CreditCardValidator.cs new file mode 100644 index 00000000..e3c3c5f1 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Validators/CreditCardValidator.cs @@ -0,0 +1,321 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Perform credit card validations. + /// + /// + /// By default, all supported card types are allowed. You can specify + /// which credit card type validator should be used by setting + /// the value of property to a concrete + /// instance. + /// + public class CreditCardValidator : BaseValidator + { + #region Properties + + /// + /// Credit card type validator to use. + /// + /// + /// Can be concrete implementations of + /// interface. The following are available implementations: + /// , , , + /// . + /// + public ICreditCardType CardType + { + get { return m_cardType; } + set { m_cardType = value; } + } + + #endregion + + #region Constructors + + /// + /// Creates a new instance of the UrlValidator class. + /// + public CreditCardValidator() + {} + + /// + /// Creates a new instance of the UrlValidator class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + /// Credit Card type validator to use. + public CreditCardValidator(string test, string when, ICreditCardType cardType) + : base(test, when) + { + AssertUtils.ArgumentHasText(test, "test"); + this.m_cardType = cardType; + } + + /// + /// Creates a new instance of the UrlValidator class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + /// Credit Card type validator to use. + public CreditCardValidator(IExpression test, IExpression when, ICreditCardType cardType) + : base(test, when) + { + AssertUtils.ArgumentNotNull(test, "test"); + this.m_cardType = cardType; + } + + #endregion + + #region BaseValidator methods + + /// + /// Validates the supplied . + /// + /// + /// In the case of the class, + /// the test should be a string variable that will be evaluated and the object + /// obtained as a result of this evaluation will be checked if it is + /// a valid credit card number. + /// + /// The object to validate. + /// + /// if the supplied is valid + /// credit card number. + /// + protected override bool Validate(object objectToValidate) + { + string text = objectToValidate as string; + if (StringUtils.IsNullOrEmpty(text)) + { + return true; + } + + return IsValid(text); + } + + #endregion + + #region CreditCardValidator methods + + /// + /// Checks if the is a valid credit card number. + /// + /// + /// The card number to validate. + /// + /// + /// true if the card number is valid. + /// + public bool IsValid(String card) + { + // check card number length + if ((card == null) || (card.Length < 13) || (card.Length > 19)) + { + return false; + } + + // check if the card is a valid credit card number + if (!LuhnCheck(card)) + { + return false; + } + + // validate card with credit card type validator + if (CardType != null) + { + return ValidateCard(card); + } + else + { + throw new ArgumentException("Property CardType cannot be null."); + } + } + + /// + /// Validates card number with the specified validator. + /// + /// + /// Credit card number to validate. + /// + /// + /// true if credit card number is a valid number of credit card type specified. + /// + private bool ValidateCard(string cardNumber) + { + String card = cardNumber == null ? null : cardNumber.Trim(); + + if (card == null) + { + return false; + } + + return CardType.Matches(card); + } + + /// + /// Checks for a valid credit card number. + /// + /// + /// Credit Card Number. + /// + /// + /// true if the card number passes the LuhnCheck. + /// + private bool LuhnCheck(string cardNumber) + { + // number must be validated as 0..9 numeric first!! + int digits = cardNumber.Length; + int oddOrEven = digits & 1; + long sum = 0; + for (int count = 0; count < digits; count++) + { + int digit; + try + { + digit = Int32.Parse(cardNumber[count] + ""); + } + catch (FormatException) + { + return false; + } + + if (((count & 1) ^ oddOrEven) == 0) + { // not + digit *= 2; + if (digit > 9) + { + digit -= 9; + } + } + sum += digit; + } + + return (sum == 0) ? false : (sum % 10 == 0); + } + + #endregion + + #region Data members + + private ICreditCardType m_cardType; + + #endregion + } + + #region CreditCardType classes + + /// + /// CreditCardType interface defines how validation is performed + /// for one type/brand of credit card. + /// + public interface ICreditCardType + { + /// + /// Returns true if the card number matches this type of + /// credit card. + /// + /// + /// The card number, never null. + /// + /// + /// true if the number matches. + /// + bool Matches(String card); + } + + /// + /// Visa credit card type validation support. + /// + public class Visa : ICreditCardType + { + private static readonly String PREFIX = "4"; + + /// + /// Indicates, wheter the given credit card number matches a visa number. + /// + public bool Matches(String card) + { + return (card.Substring(0, 1).Equals(PREFIX) && (card.Length == 13 || card.Length == 16)); + } + } + + /// + /// American Express credit card type validation support. + /// + public class Amex : ICreditCardType + { + private static readonly String PREFIX = "34,37,"; + + /// + /// Indicates, wheter the given credit card number matches an amex number. + /// + public bool Matches(String card) + { + String prefix2 = card.Substring(0, 2) + ","; + return ((PREFIX.IndexOf(prefix2) != -1) && (card.Length == 15)); + } + } + + /// + /// Discover credit card type validation support. + /// + public class Discover : ICreditCardType + { + private static readonly String PREFIX = "6011"; + + /// + /// Indicates, wheter the given credit card number matches a discover number. + /// + public bool Matches(String card) + { + return (card.Substring(0, 4).Equals(PREFIX) && (card.Length == 16)); + } + } + + /// + /// Mastercard credit card type validation support. + /// + public class Mastercard : ICreditCardType + { + private static readonly String PREFIX = "51,52,53,54,55,"; + + /// + /// Indicates, wheter the given credit card number matches a mastercard number. + /// + public bool Matches(String card) + { + String prefix2 = card.Substring(0, 2) + ","; + return ((PREFIX.IndexOf(prefix2) != -1) && (card.Length == 16)); + } + } + + #endregion +} diff --git a/src/Spring/Spring.Core/Validation/Validators/EmailValidator.cs b/src/Spring/Spring.Core/Validation/Validators/EmailValidator.cs new file mode 100644 index 00000000..691fbb32 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Validators/EmailValidator.cs @@ -0,0 +1,115 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text.RegularExpressions; +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Perform email validations. + /// + /// + ///

    + /// This implementation is not guaranteed to catch all possible errors in an + /// email address. For example, an address like nobody@noplace.nowhere will + /// pass validator, even though there is no TLD "nowhere". + /// + /// Goran Milosavljevic + public class EmailValidator : BaseValidator + { + #region Constructors + + ///

    + /// Creates a new instance of the EmailValidator class. + /// + public EmailValidator() + {} + + /// + /// Creates a new instance of the EmailValidator class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public EmailValidator(string test, string when) + : base(test, when) + { + AssertUtils.ArgumentHasText(test, "test"); + } + + /// + /// Creates a new instance of the EmailValidator class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public EmailValidator(IExpression test, IExpression when) + : base(test, when) + { + AssertUtils.ArgumentNotNull(test, "test"); + } + + #endregion + + #region BaseValidator methods + + /// + /// Validates the supplied . + /// + /// + /// In the case of the class, + /// the test should be a string variable that will be evaluated and the object + /// obtained as a result of this evaluation will be checked if it is + /// a valid e-mail address. + /// + /// The object to validate. + /// + /// if the supplied is valid + /// e-mail address. + /// + protected override bool Validate(object objectToValidate) + { + string text = objectToValidate as string; + if (StringUtils.IsNullOrEmpty(text)) + { + return true; + } + + Match match = Regex.Match(text, emailCheck); + return match.Success && match.Index == 0 && match.Length == text.Length; + } + + #endregion + + #region Data members + + /// + /// Regular expression used for validation of object passed to this . + /// + private static string emailCheck = @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"; + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Validation/Validators/ISBNValidator.cs b/src/Spring/Spring.Core/Validation/Validators/ISBNValidator.cs new file mode 100644 index 00000000..5c303125 --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Validators/ISBNValidator.cs @@ -0,0 +1,167 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text.RegularExpressions; +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Validates that the object is valid ISBN-10 or ISBN-13 value. + /// + /// Goran Milosavljevic + public class ISBNValidator : BaseValidator + { + #region Constructors + + /// + /// Creates a new instance of the ISBNValidator class. + /// + public ISBNValidator() + {} + + /// + /// Creates a new instance of the ISBNValidator class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public ISBNValidator(string test, string when) + : base(test, when) + { + AssertUtils.ArgumentHasText(test, "test"); + } + + /// + /// Creates a new instance of the ISBNValidator class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public ISBNValidator(IExpression test, IExpression when) + : base(test, when) + { + AssertUtils.ArgumentNotNull(test, "test"); + } + + #endregion + + #region BaseValidator methods + + /// + /// Validates the supplied . + /// + /// + /// In the case of the class, + /// the test should be a string variable that will be evaluated and the object + /// obtained as a result of this evaluation will be tested using the ISBN-10 or + /// ISBN-13 validation rules. + /// + /// The object to validate. + /// + /// if the supplied is valid ISBN. + /// + protected override bool Validate(object objectToValidate) + { + String isbn = objectToValidate as String; + if (StringUtils.IsNullOrEmpty(isbn)) + { + return true; + } + + return IsValid(isbn); + } + + #endregion + + #region ISBNValidator methods + + /// + /// Validates against ISBN-10 or ISBN-13 validation + /// rules. + /// + /// + /// ISBN string to validate. + /// + /// + /// true if is a valid ISBN-10 or ISBN-13 code. + /// + private bool IsValid(String isbn) + { + String code = (isbn == null ? null : isbn.Trim().Replace("-", "").Replace(" ", "")); + + // check the length + if ((code == null) || (code.Length < 10 || code.Length>13)) + { + return false; + } + + // validate/reformat using regular expression + Match match; + String pattern; + if (code.Length == 10) + { + pattern = ISBN10_PATTERN; + } + else + { + pattern = ISBN13_PATTERN; + } + + match = Regex.Match(code, pattern); + return match.Success && match.Index == 0 && match.Length == code.Length; + } + + #endregion + + #region Data members + + private static readonly String SEP = "(?:\\-|\\s)"; + private static readonly String GROUP = "(\\d{1,5})"; + private static readonly String PUBLISHER = "(\\d{1,7})"; + private static readonly String TITLE = "(\\d{1,6})"; + + /// + /// ISBN-10 consists of 4 groups of numbers separated by either + /// dashes (-) or spaces. + /// + /// + /// The first group is 1-5 characters, second 1-7, third 1-6, + /// and fourth is 1 digit or an X. + /// + static readonly String ISBN10_PATTERN = "^(?:(\\d{9}[0-9X])|(?:" + GROUP + SEP + PUBLISHER + SEP + TITLE + SEP + "([0-9X])))$"; + + /// + /// ISBN-13 consists of 5 groups of numbers separated by either + /// dashes (-) or spaces. + /// + /// + /// The first group is 978 or 979, the second group is + /// 1-5 characters, third 1-7, fourth 1-6, and fifth is 1 digit. + /// + static readonly String ISBN13_PATTERN = "^(978|979)(?:(\\d{10})|(?:" + SEP + GROUP + SEP + PUBLISHER + SEP + TITLE + SEP + "([0-9])))$"; + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Validation/Validators/RegularExpressionValidator.cs b/src/Spring/Spring.Core/Validation/Validators/RegularExpressionValidator.cs new file mode 100644 index 00000000..b20bc2fc --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Validators/RegularExpressionValidator.cs @@ -0,0 +1,140 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text.RegularExpressions; + +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Validation +{ + /// + /// Validates that object matches specified regular expression. + /// + /// + ///

    + /// The test expression must evaluate to a ; + /// otherwise, an exception is thrown. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: RegularExpressionValidator.cs,v 1.2 2006/04/09 07:19:04 markpollack Exp $ + public class RegularExpressionValidator : BaseValidator + { + #region Fields + + private string expression = string.Empty; + private RegexOptions options; + + #endregion + + #region Constructors + + /// + /// Creates a new instance of the class. + /// + public RegularExpressionValidator() + { + } + + /// + /// Creates a new instance of the class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + /// The regular expression to match against. + public RegularExpressionValidator(string test, string when, string expression) : base(test, when) + { + AssertUtils.ArgumentHasText(test, "test"); + this.expression = expression; + } + + /// + /// Creates a new instance of the class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + /// The regular expression to match against. + public RegularExpressionValidator(IExpression test, IExpression when, string expression) : base(test, when) + { + AssertUtils.ArgumentNotNull(test, "test"); + this.expression = expression; + } + + #endregion + + #region Properties + + /// + /// The regular expression text to match against. + /// + /// The regular expression text. + public string Expression + { + get { return expression; } + set { expression = value; } + } + + /// + /// The for the regular expression evaluation. + /// + /// The regular expression evaluation options. + /// + public RegexOptions Options + { + get { return options; } + set { options = value; } + } + + #endregion + + /// + /// Validates an object. + /// + /// Object to validate. + /// + /// if the supplied + /// object is valid. + /// + /// + /// If the supplied is not a + /// + /// + protected override bool Validate(object objectToValidate) + { + string text = objectToValidate as string; + if (text == null) + { + throw new ArgumentException("Test for RegularExpressionValidator must evaluate to a string."); + } + if (!StringUtils.HasLength(text)) + { + return true; + } + Match match = Regex.Match(text, this.Expression, this.Options); + return match.Success && match.Index == 0 && match.Length == text.Length; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/Validators/RequiredValidator.cs b/src/Spring/Spring.Core/Validation/Validators/RequiredValidator.cs new file mode 100644 index 00000000..173e47dd --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Validators/RequiredValidator.cs @@ -0,0 +1,143 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Validation +{ + /// + /// Validates that required value is not empty. + /// + /// + ///

    + /// This validator uses following rules to determine if target value is valid: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///
    Target Valid Value
    A .Not or an empty string.
    A .Not and not .
    One of the number types.Not zero.
    A .Not or whitespace.
    Any reference type other than .Not .
    + ///

    + ///

    + /// You cannot use this validator to validate any value types other than the ones + /// specified in the table above. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: RequiredValidator.cs,v 1.8 2008/03/21 14:12:02 markpollack Exp $ + public class RequiredValidator : BaseValidator + { + #region Constructors + + /// + /// Creates a new instance of the class. + /// + public RequiredValidator() + {} + + /// + /// Creates a new instance of the class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public RequiredValidator(string test, string when) : base(test, when) + { + AssertUtils.ArgumentHasText(test, "test"); + } + + /// + /// Creates a new instance of the class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public RequiredValidator(IExpression test, IExpression when) : base(test, when) + { + AssertUtils.ArgumentNotNull(test, "test"); + } + + #endregion + + /// + /// Validates the supplied . + /// + /// + /// In the case of the class, + /// the test should be a variable expression that will be evaluated and the object + /// obtained as a result of this evaluation will be tested using the rules described + /// in the class overview of the + /// class. + /// + /// The object to validate. + /// + /// if the supplied is valid. + /// + protected override bool Validate(object objectToValidate) + { + if (objectToValidate is String && StringUtils.IsNullOrEmpty((string) objectToValidate)) + { + return false; + } + else if (objectToValidate is DateTime && (((DateTime) objectToValidate) == DateTime.MinValue || ((DateTime) objectToValidate) == DateTime.MaxValue)) + { + return false; + } + else if (NumberUtils.IsInteger(objectToValidate) && NumberUtils.IsZero(objectToValidate)) + { + return false; + } + else if (objectToValidate is Char && (((char) objectToValidate) == Char.MinValue || Char.IsWhiteSpace((char) objectToValidate))) + { + return false; + } + else if (NumberUtils.IsDecimal(objectToValidate) && NumberUtils.IsZero(objectToValidate)) + { + return false; + } + return objectToValidate != null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Validation/Validators/UrlValidator.cs b/src/Spring/Spring.Core/Validation/Validators/UrlValidator.cs new file mode 100644 index 00000000..018bcd2f --- /dev/null +++ b/src/Spring/Spring.Core/Validation/Validators/UrlValidator.cs @@ -0,0 +1,107 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text.RegularExpressions; +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Validates that the value is valid URL. + /// + /// Goran Milosavljevic + public class UrlValidator : BaseValidator + { + #region Constructors + + /// + /// Creates a new instance of the UrlValidator class. + /// + public UrlValidator() + {} + + /// + /// Creates a new instance of the UrlValidator class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public UrlValidator(string test, string when) + : base(test, when) + { + AssertUtils.ArgumentHasText(test, "test"); + } + + /// + /// Creates a new instance of the UrlValidator class. + /// + /// The expression to validate. + /// The expression that determines if this validator should be evaluated. + public UrlValidator(IExpression test, IExpression when) + : base(test, when) + { + AssertUtils.ArgumentNotNull(test, "test"); + } + + #endregion + + #region BaseValidator methods + + /// + /// Validates the supplied . + /// + /// + /// In the case of the class, + /// the test should be a string variable that will be evaluated and the object + /// obtained as a result of this evaluation will be tested using the URL validation rules. + /// + /// The object to validate. + /// + /// if the supplied is valid. + /// + protected override bool Validate(object objectToValidate) + { + string text = objectToValidate as string; + if (StringUtils.IsNullOrEmpty(text)) + { + return true; + } + + Match match = Regex.Match(text, urlCheck); + return match.Success && match.Index == 0 && match.Length == text.Length; + } + + #endregion + + #region Data members + + /// + /// Regular expression used for validation of object passed to this . + /// + private static string urlCheck = "((http|https)://)?[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(([0-9]{1,5})?/.*)?"; + + #endregion + } +} diff --git a/src/Spring/Spring.Data.NHibernate/AssemblyInfo.cs b/src/Spring/Spring.Data.NHibernate/AssemblyInfo.cs new file mode 100644 index 00000000..5d721b48 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring.Net NHibernate 1.0 support")] +[assembly: AssemblyDescription("Interfaces and classes that provide NHibernate 1.0 support in Spring.Net")] \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateAccessor.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateAccessor.cs new file mode 100644 index 00000000..453eb164 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateAccessor.cs @@ -0,0 +1,730 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using AopAlliance.Intercept; +using Common.Logging; +using NHibernate; +using NHibernate.Impl; +using NHibernate.Type; +using Spring.Core.TypeResolution; +using Spring.Dao; +using Spring.Data.Support; +using Spring.Objects.Factory; +using IInterceptor=NHibernate.IInterceptor; +using ICriteria=NHibernate.ICriteria; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Base class for HibernateTemplate defining common + /// properties like SessionFactory and flushing behavior. + /// + /// + ///

    Not intended to be used directly. See HibernateTemplate. + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: HibernateAccessor.cs,v 1.6 2008/01/25 15:04:39 markpollack Exp $ + public abstract class HibernateAccessor : IInitializingObject, IObjectFactoryAware + { + + private Type criteriaType; + + #region Constants + + /// + /// The instance for this class. + /// + private readonly ILog log = LogManager.GetLogger(typeof (HibernateAccessor)); + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public HibernateAccessor() + { + + } + + #endregion + + + #region Properties + + /// + /// Gets or sets if a new Session should be created when no transactional Session + /// can be found for the current thread. + /// + /// + /// true if allowed to create non-transaction session; + /// otherwise, false. + /// + /// + ///

    HibernateTemplate is aware of a corresponding Session bound to the + /// current thread, for example when using HibernateTransactionManager. + /// If allowCreate is true, a new non-transactional Session will be created + /// if none found, which needs to be closed at the end of the operation. + /// If false, an InvalidOperationException will get thrown in this case. + ///

    + ///
    + public abstract bool AllowCreate + { + get; + set; + } + + /// + /// Gets or sets a value indicating whether to always + /// use a new Hibernate Session for this template. + /// + /// true if always use new session; otherwise, false. + /// + ///

    + /// Default is "false"; if activated, all operations on this template will + /// work on a new NHibernate ISession even in case of a pre-bound ISession + /// (for example, within a transaction). + ///

    + ///

    Within a transaction, a new NHibernate ISession used by this template + /// will participate in the transaction through using the same ADO.NET + /// Connection. In such a scenario, multiple Sessions will participate + /// in the same database transaction. + ///

    + ///

    Turn this on for operations that are supposed to always execute + /// independently, without side effects caused by a shared NHibernate ISession. + ///

    + ///
    + public abstract bool AlwaysUseNewSession + { + get; + set; + } + + /// + /// Set whether to expose the native Hibernate Session to IHibernateCallback + /// code. Default is "false": a Session proxy will be returned, + /// suppressing close calls and automatically applying + /// query cache settings and transaction timeouts. + /// + /// true if expose native session; otherwise, false. + public abstract bool ExposeNativeSession + { + get; + set; + } + + /// + /// Gets or sets the template flush mode. + /// + /// + /// Default is Auto. Will get applied to any new ISession + /// created by the template. + /// + /// The template flush mode. + public abstract TemplateFlushMode TemplateFlushMode + { + get; + set; + } + + /// + /// Gets or sets the entity interceptor that allows to inspect and change + /// property values before writing to and reading from the database. + /// + /// + /// Will get applied to any new ISession created by this object. + ///

    Such an interceptor can either be set at the ISessionFactory level, + /// i.e. on LocalSessionFactoryObject, or at the ISession level, i.e. on + /// HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + /// It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + /// to avoid repeated configuration and guarantee consistent behavior in transactions. + ///

    + ///
    + /// The interceptor. + public abstract IInterceptor EntityInterceptor + { + get; + set; + } + + /// + /// Set the object name of a Hibernate entity interceptor that allows to inspect + /// and change property values before writing to and reading from the database. + /// + /// + /// Will get applied to any new Session created by this transaction manager. + ///

    Requires the object factory to be known, to be able to resolve the object + /// name to an interceptor instance on session creation. Typically used for + /// prototype interceptors, i.e. a new interceptor instance per session. + ///

    + ///

    Can also be used for shared interceptor instances, but it is recommended + /// to set the interceptor reference directly in such a scenario. + ///

    + ///
    + public abstract string EntityInterceptorObjectName + { + set; + } + + /// + /// Gets or sets the session factory that should be used to create + /// NHibernate ISessions. + /// + /// The session factory. + public abstract ISessionFactory SessionFactory + { + get; + set; + } + + /// + /// Set the object factory instance. + /// + public abstract IObjectFactory ObjectFactory + { + set; + } + + /// + /// Gets or sets a value indicating whether to + /// cache all queries executed by this template. + /// + /// + /// If this is true, all IQuery and ICriteria objects created by + /// this template will be marked as cacheable (including all + /// queries through find methods). + ///

    To specify the query region to be used for queries cached + /// by this template, set the QueryCacheRegion property. + ///

    + ///
    + /// true if cache queries; otherwise, false. + public abstract bool CacheQueries + { + get; + set; + } + + /// + /// Gets or sets the name of the cache region for queries executed by this template. + /// + /// + /// If this is specified, it will be applied to all IQuery and ICriteria objects + /// created by this template (including all queries through find methods). + ///

    The cache region will not take effect unless queries created by this + /// template are configured to be cached via the CacheQueries property. + ///

    + ///
    + /// The query cache region. + public abstract string QueryCacheRegion + { + get; + set; + } + + /// + /// Gets or sets the fetch size for this HibernateTemplate. + /// + /// The size of the fetch. + /// This is important for processing + /// large result sets: Setting this higher than the default value will increase + /// processing speed at the cost of memory consumption; setting this lower can + /// avoid transferring row data that will never be read by the application. + ///

    Default is 0, indicating to use the driver's default.

    + ///
    + public abstract int FetchSize + { + get; + set; + } + + /// + /// Gets or sets the maximum number of rows for this HibernateTemplate. + /// + /// The max results. + /// + /// This is important + /// for processing subsets of large result sets, avoiding to read and hold + /// the entire result set in the database or in the ADO.NET driver if we're + /// never interested in the entire result in the first place (for example, + /// when performing searches that might return a large number of matches). + ///

    Default is 0, indicating to use the driver's default.

    + ///
    + public abstract int MaxResults + { + get; + set; + } + + /// + /// Set the ADO.NET exception translator for this instance. + /// Applied to System.Data.Common.DbException (or provider specific exception type + /// in .NET 1.1) thrown by callback code, be it direct + /// DbException or wrapped Hibernate ADOExceptions. + ///

    The default exception translator is either a ErrorCodeExceptionTranslator + /// if a DbProvider is available, or a FalbackExceptionTranslator otherwise + ///

    + ///
    + /// The ADO exception translator. + public abstract IAdoExceptionTranslator AdoExceptionTranslator + { + set; + get; + } + + /// + /// Gets a Session for use by this template. + /// + /// The session. + /// + /// - Returns a new Session in case of "alwaysUseNewSession" (using the same ADO.NET connection as a transaction Session, if applicable) + /// - a pre-bound Session in case of "AllowCreate" is set to false (not the default) + /// - or a pre-bound Session or new Session if no transactional or other pre-bound Session exists. + /// + protected ISession Session + { + get + { + if (AlwaysUseNewSession) + { + return SessionFactoryUtils.GetNewSession(SessionFactory, EntityInterceptor); + } + else if (!AllowCreate) + { + return SessionFactoryUtils.GetSession(SessionFactory, false); + } + else + { + return SessionFactoryUtils.GetSession( + SessionFactory, EntityInterceptor, AdoExceptionTranslator); + } + + } + + } + + #endregion + + #region Methods + + /// + /// Apply the flush mode that's been specified for this accessor + /// to the given Session. + /// + /// The current Hibernate Session. + /// if set to true + /// if executing within an existing transaction. + /// + /// the previous flush mode to restore after the operation, + /// or null if none + /// + protected FlushModeHolder ApplyFlushMode(ISession session, bool existingTransaction) + { + if (TemplateFlushMode == TemplateFlushMode.Never) + { + if (existingTransaction) + { + FlushMode previousFlushMode = session.FlushMode; + if (previousFlushMode != FlushMode.Never) + { + session.FlushMode = FlushMode.Never; + return new FlushModeHolder(previousFlushMode); + } + } + else + { + session.FlushMode = FlushMode.Never; + } + } + else if (TemplateFlushMode == TemplateFlushMode.Eager) + { + if (existingTransaction) + { + FlushMode previousFlushMode = session.FlushMode; + if (previousFlushMode != FlushMode.Auto) + { + session.FlushMode = FlushMode.Auto; + return new FlushModeHolder(previousFlushMode); + } + } + else + { + // rely on default FlushMode.AUTO + } + } + else if (TemplateFlushMode == TemplateFlushMode.Commit) + { + if (existingTransaction) + { + FlushMode previousFlushMode = session.FlushMode; + if (previousFlushMode == FlushMode.Auto) + { + session.FlushMode = FlushMode.Commit; + return new FlushModeHolder(previousFlushMode); + } + } + else + { + session.FlushMode = FlushMode.Commit; + } + } + return new FlushModeHolder(); + } + + + /// + /// Flush the given Hibernate Session if necessary. + /// + /// The current Hibernate Session. + /// if set to true + /// if executing within an existing transaction. + protected void FlushIfNecessary(ISession session, bool existingTransaction) + { + if (TemplateFlushMode == TemplateFlushMode.Eager || + (!existingTransaction && TemplateFlushMode != TemplateFlushMode.Never)) + { + log.Debug("Eagerly flushing Hibernate session"); + session.Flush(); + } + } + + /// + /// Convert the given HibernateException to an appropriate exception from the + /// org.springframework.dao hierarchy. Will automatically detect + /// wrapped ADO.NET Exceptions and convert them accordingly. + /// + /// HibernateException that occured. + /// + /// The corresponding DataAccessException instance + /// + /// + /// The default implementation delegates to SessionFactoryUtils + /// and convertAdoAccessException. Can be overridden in subclasses. + /// + public virtual DataAccessException ConvertHibernateAccessException(HibernateException ex) + { + if (ex is ADOException) + { + return ConvertAdoAccessException((ADOException) ex); + } + return SessionFactoryUtils.ConvertHibernateAccessException(ex); + } + + /// + /// Converts the ADO.NET access exception to an appropriate exception from the + /// org.springframework.dao hierarchy. Can be overridden in subclasses. + /// + /// ADOException that occured, wrapping underlying ADO.NET exception. + /// + /// the corresponding DataAccessException instance + /// + protected virtual DataAccessException ConvertAdoAccessException(ADOException ex) + { + return AdoExceptionTranslator.Translate( + "Hibernate operation: " + ex.Message, null, ex.InnerException); + } + + /// + /// Converts the ADO.NET access exception to an appropriate exception from the + /// org.springframework.dao hierarchy. Can be overridden in subclasses. + /// + /// + /// + /// Note that a direct SQLException can just occur when callback code + /// performs direct ADO.NET access via ISession.Connection(). + /// + /// + /// The ADO.NET exception. + /// The corresponding DataAccessException instance + protected virtual DataAccessException ConvertAdoAccessException(Exception ex) + { + return AdoExceptionTranslator.Translate("Hibernate operation", null, ex); + } + + + /// + /// Prepare the given IQuery object, applying cache settings and/or + /// a transaction timeout. + /// + /// The query object to prepare. + public virtual void PrepareQuery(IQuery queryObject) + { + if (CacheQueries) + { + queryObject.SetCacheable(true); + if (QueryCacheRegion != null) + { + queryObject.SetCacheRegion(QueryCacheRegion); + } + } + + if (FetchSize > 0) + { + AbstractQueryImpl queryImpl = queryObject as AbstractQueryImpl; + if (queryImpl != null) + { + queryImpl.SetFetchSize(FetchSize); + } + else + { + log.Warn("Could not set FetchSize for IQuery. Expected Implemention to be of type AbstractQueryImpl"); + } + } + + if (MaxResults > 0) + { + queryObject.SetMaxResults(MaxResults); + } + + SessionFactoryUtils.ApplyTransactionTimeout(queryObject, SessionFactory); + + } + + /// + /// Apply the given name parameter to the given Query object. + /// + /// The query object. + /// Name of the parameter + /// The value of the parameter + /// The NHibernate type of the parameter (or null if none specified) + public virtual void ApplyNamedParameterToQuery(IQuery queryObject, string paramName, object value, IType type) + { + + if (value is ICollection) + { + if (type != null) + { + queryObject.SetParameterList(paramName, (ICollection)value, type); + } + else + { + queryObject.SetParameterList(paramName, (ICollection)value); + } + } + else if (value is Object[]) + { + + //TODO investigate support for this conversion. + if (type != null) + { + queryObject.SetParameterList(paramName, (Object[])value, type); + } + else + { + queryObject.SetParameterList(paramName, (Object[])value); + } + } + else + { + if (type != null) + { + queryObject.SetParameter(paramName, value, type); + } + else + { + queryObject.SetParameter(paramName, value); + } + } + } + + /// + /// Prepare the given Criteria object, applying cache settings and/or + /// a transaction timeout. + /// + /// + /// Note that for NHibernate 1.2 this only works if the + /// implementation is of the type CriteriaImpl, which should generally + /// be the case. The SetFetchSize method is not available on the + /// ICriteria interface + /// + /// This is a no-op for NHibernate 1.0.x since + /// the SetFetchSize method is not on the ICriteria interface and + /// the implementation class is has internal access. + /// + /// To remove the method completely for Spring's NHibernate 1.0 + /// support while reusing code for NHibernate 1.2 would not be + /// possible. So now this ineffectual operation is left in tact for + /// NHibernate 1.0.2 support. + /// + /// The criteria object to prepare + public void PrepareCriteria(ICriteria criteria) + { + if (CacheQueries) + { + criteria.SetCacheable(true); + if (QueryCacheRegion != null) + { + criteria.SetCacheRegion(QueryCacheRegion); + } + } + + + + if (FetchSize > 0) + { + //TODO see if we can optimize performance. + //CriteriaImpl is internal in NH 1.0.x + object[] args = new object[] { FetchSize }; + try + { + Type t = TypeResolutionUtils.ResolveType("NHibernate.Impl.CriteriaImpl, NHibernate"); + if (t != null) + { + t.InvokeMember("SetFetchSize", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance + | BindingFlags.InvokeMethod, + null, criteria, args); + } + } + catch (TypeLoadException e) + { + log.Warn("Can't set FetchSize for ICriteria", e); + } + } + + if (MaxResults > 0) + { + criteria.SetMaxResults(MaxResults); + } + + SessionFactoryUtils.ApplyTransactionTimeout(criteria, SessionFactory); + } + + private void InitCriteriaType() + { + + try + { + criteriaType = TypeResolutionUtils.ResolveType("Hibernate.Impl.CriteriaImpl, NHibernate"); + } + catch (TypeLoadException e) + { + log.Warn("CriteriaImpl not available. FetchSize can not be set on ICriteria objects", e); + } + } + + #endregion + + /// + /// Ensure SessionFactory is not null + /// + public virtual void AfterPropertiesSet() + { + if (SessionFactory == null) + { + throw new ArgumentException("sessionFactory is required"); + } + } + + #region Helper Classes + /// + /// Helper class to determine if the FlushMode enumeration + /// was changed from its default value + /// + protected class FlushModeHolder + { + /// + /// Gets or sets a value indicating whether the FlushMode + /// property was set.. + /// + /// true if FlushMode was set; otherwise, false. + public bool ModeWasSet + { + get { return modeWasSet; } + set { modeWasSet = value; } + } + + /// + /// Gets or sets the FlushMode. + /// + /// The FlushMode. + public FlushMode Mode + { + get { return flushMode; } + set { flushMode = value; } + } + + private bool modeWasSet = false; + private FlushMode flushMode; + + /// + /// Initializes a new instance of the class. + /// + public FlushModeHolder() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The flush mode. + public FlushModeHolder(FlushMode mode) + { + Mode = mode; + ModeWasSet = true; + } + + } + + + #endregion + + + } + + internal class CloseSuppressingMethodInterceptor : IMethodInterceptor + { + private HibernateAccessor hibernateAccessor; + + public CloseSuppressingMethodInterceptor(HibernateAccessor accessor) + { + hibernateAccessor = accessor; + } + + public object Invoke(IMethodInvocation invocation) + { + //anything special for equals/hashcode? + if (invocation.Method.Name.Equals("Close")) + { + return null; + } + else + { + object retValue = invocation.Proceed(); + if (retValue is IQuery) + { + hibernateAccessor.PrepareQuery((IQuery)retValue); + } + if (retValue is ICriteria) + { + hibernateAccessor.PrepareCriteria((ICriteria)retValue); + } + return retValue; + } + } + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateAdoException.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateAdoException.cs new file mode 100644 index 00000000..6ec61040 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateAdoException.cs @@ -0,0 +1,91 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using NHibernate; +using Spring.Dao; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Hibernate-specific subclass of UncategorizedDataAccessException, + /// for ADO.NET exceptions that Hibernate rethrew and could not be + /// mapped into the DAO exception heirarchy. + /// + /// Mark Pollack (.NET) + /// $Id: HibernateAdoException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + [Serializable] + public class HibernateAdoException : UncategorizedDataAccessException + { + + #region Constructor (s) + + + /// + /// Initializes a new instance of the class. + /// + public HibernateAdoException() : base() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public HibernateAdoException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception from the underlying data access API - ADO.NET + /// + public HibernateAdoException( string message, ADOException rootCause ) : base( message, rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected HibernateAdoException( SerializationInfo info, StreamingContext context ) : base( info, context ) {} + + + #endregion + + + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateDelegate.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateDelegate.cs new file mode 100644 index 00000000..b05380d8 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateDelegate.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NHibernate; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public delegate object HibernateDelegate(ISession session); +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateObjectRetrievalFailureException.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateObjectRetrievalFailureException.cs new file mode 100644 index 00000000..a8953b84 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateObjectRetrievalFailureException.cs @@ -0,0 +1,109 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using NHibernate; +using Spring.Dao; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Hibernate-specific subclass of ObjectRetrievalFailureException. + /// + /// + /// Converts Hibernate's UnresolvableObjectException, ObjectNotFoundException, + /// ObjectDeletedException, and WrongClassException. + /// + /// Mark Pollack (.NET) + /// $Id: HibernateObjectRetrievalFailureException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + [Serializable] + public class HibernateObjectRetrievalFailureException : ObjectRetrievalFailureException + { + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public HibernateObjectRetrievalFailureException() + { + } + + + /// + /// Initializes a new instance of the class. + /// + /// The ex. + public HibernateObjectRetrievalFailureException(UnresolvableObjectException ex) : base(ex.PersistentClass, ex.Identifier, ex.Message, ex) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The ex. + public HibernateObjectRetrievalFailureException(ObjectNotFoundException ex) : base(ex.PersistentClass, ex.Identifier, ex.Message, ex) + + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The ex. + public HibernateObjectRetrievalFailureException(ObjectDeletedException ex) : base(ex.PersistentClass, ex.Identifier, ex.Message, ex) + { + + } + + //TODO investigate WrongClassException.Type as equivalent to ex.PersistentClass + /// + /// Initializes a new instance of the class. + /// + /// The ex. + public HibernateObjectRetrievalFailureException(WrongClassException ex) : base(ex.Type, ex.Identifier, ex.Message, ex) + { + + } + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected HibernateObjectRetrievalFailureException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + + + #endregion + + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateOptimisticLockingFailureException.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateOptimisticLockingFailureException.cs new file mode 100644 index 00000000..f2cac64e --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateOptimisticLockingFailureException.cs @@ -0,0 +1,91 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using NHibernate; +using Spring.Dao; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Hibernate-specific subclass of ObjectOptimisticLockingFailureException. + /// + /// + /// Converts Hibernate's StaleObjectStateException. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: HibernateOptimisticLockingFailureException.cs,v 1.2 2008/04/08 20:26:37 markpollack Exp $ + [Serializable] + public class HibernateOptimisticLockingFailureException : ObjectOptimisticLockingFailureException + { + #region Fields + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public HibernateOptimisticLockingFailureException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The StaleObjectStateException. + public HibernateOptimisticLockingFailureException(StaleObjectStateException ex) : base(ex.PersistentType, ex.Identifier, ex.Message, ex) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected HibernateOptimisticLockingFailureException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateQueryException.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateQueryException.cs new file mode 100644 index 00000000..9955ff77 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateQueryException.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using NHibernate; +using Spring.Dao; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Hibernate-specific subclass of InvalidDataAccessResourceUsageException, + /// thrown on invalid HQL query syntax. + /// + /// Mark Pollack (.NET) + /// $Id: HibernateQueryException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + [Serializable] + public class HibernateQueryException : InvalidDataAccessResourceUsageException + { + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public HibernateQueryException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The ex. + public HibernateQueryException(QueryException ex) : base(ex.Message, ex) + { + } + + /// + /// Gets the query string that was invalid. + /// + /// The query string that was invalid. + public string QueryString + { + get { return ((QueryException) this.InnerException).QueryString; } + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected HibernateQueryException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateSystemException.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateSystemException.cs new file mode 100644 index 00000000..ec1a79ef --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateSystemException.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using NHibernate; +using Spring.Dao; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Hibernate-specific subclass of UncategorizedDataAccessException, + /// for Hibernate system errors that do not match any concrete + /// Spring.Dao exceptions. + /// + /// Mark Pollack (.NET) + /// $Id: HibernateSystemException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + [Serializable] + public class HibernateSystemException : UncategorizedDataAccessException + { + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public HibernateSystemException() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The cause. + public HibernateSystemException(HibernateException cause) : base(cause != null ? cause.Message : null, cause) + { + + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected HibernateSystemException( SerializationInfo info, StreamingContext context ) : base( info, context ) {} + + + + #endregion + + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateTemplate.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateTemplate.cs new file mode 100644 index 00000000..35b962fa --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateTemplate.cs @@ -0,0 +1,2238 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Common.Logging; +using Spring.Aop.Framework; +using Spring.Data.Common; +using Spring.Data.Support; +using IInterceptor = NHibernate.IInterceptor; + +using NHibernate; +using NHibernate.Type; +using Spring.Dao; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Helper class that simplifies NHibernate data access code + /// + /// + ///

    Typically used to implement data access or business logic services that + /// use NHibernate within their implementation but are Hibernate-agnostic in their + /// interface. The latter or code calling the latter only have to deal with + /// domain objects.

    + /// + ///

    The central method is Execute supporting Hibernate access code + /// implementing the HibernateCallback interface. It provides NHibernate Session + /// handling such that neither the IHibernateCallback implementation nor the calling + /// code needs to explicitly care about retrieving/closing NHibernate Sessions, + /// or handling Session lifecycle exceptions. For typical single step actions, + /// there are various convenience methods (Find, Load, SaveOrUpdate, Delete). + ///

    + /// + ///

    Can be used within a service implementation via direct instantiation + /// with a ISessionFactory reference, or get prepared in an application context + /// and given to services as an object reference. Note: The ISessionFactory should + /// always be configured as an object in the application context, in the first case + /// given to the service directly, in the second case to the prepared template. + ///

    + /// + ///

    This class can be considered as direct alternative to working with the raw + /// Hibernate Session API (through SessionFactoryUtils.Session). + ///

    + /// + ///

    LocalSessionFactoryObject is the preferred way of obtaining a reference + /// to a specific NHibernate ISessionFactory. + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: HibernateTemplate.cs,v 1.3 2008/01/24 17:29:16 markpollack Exp $ + public class HibernateTemplate : HibernateAccessor, IHibernateOperations + { + #region Fields + + /// + /// The instance for this class. + /// + private readonly ILog log = LogManager.GetLogger(typeof(HibernateTemplate)); + + private bool checkWriteOperations = true; + + + private bool exposeNativeSession = false; + + private bool alwaysUseNewSession = false; + private int maxResults = 0; + private TemplateFlushMode templateFlushMode = TemplateFlushMode.Auto; + private bool allowCreate = true; + private ISessionFactory sessionFactory; + private object entityInterceptor; + private IObjectFactory objectFactory; + private bool cacheQueries = false; + private string queryCacheRegion; + private int fetchSize = 0; + + private IAdoExceptionTranslator adoExceptionTranslator; + + private readonly object syncRoot = new object(); + private ProxyFactory sessionProxyFactory; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public HibernateTemplate() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The default for creating a new non-transactional + /// session when no transactional Session can be found for the current thread + /// is set to true. + /// The session factory to create sessions. + public HibernateTemplate(ISessionFactory sessionFactory) + { + SessionFactory = sessionFactory; + AfterPropertiesSet(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The session factory to create sessions. + /// if set to true allow creation + /// of a new non-transactional when no transactional Session can be found + /// for the current thread. + public HibernateTemplate(ISessionFactory sessionFactory, bool allowCreate) + { + SessionFactory = sessionFactory; + AllowCreate = allowCreate; + AfterPropertiesSet(); + } + #endregion + + #region Properties + + /// + /// Gets or sets if a new Session should be created when no transactional Session + /// can be found for the current thread. + /// + /// + /// true if allowed to create non-transaction session; + /// otherwise, false. + /// + /// + ///

    HibernateTemplate is aware of a corresponding Session bound to the + /// current thread, for example when using HibernateTransactionManager. + /// If allowCreate is true, a new non-transactional Session will be created + /// if none found, which needs to be closed at the end of the operation. + /// If false, an InvalidOperationException will get thrown in this case. + ///

    + ///
    + public override bool AllowCreate + { + get + { + + return allowCreate; + } + set { allowCreate = value; } + } + + /// + /// Gets or sets a value indicating whether to always + /// use a new Hibernate Session for this template. + /// + /// true if always use new session; otherwise, false. + /// + ///

    + /// Default is "false"; if activated, all operations on this template will + /// work on a new NHibernate ISession even in case of a pre-bound ISession + /// (for example, within a transaction). + ///

    + ///

    Within a transaction, a new NHibernate ISession used by this template + /// will participate in the transaction through using the same ADO.NET + /// Connection. In such a scenario, multiple Sessions will participate + /// in the same database transaction. + ///

    + ///

    Turn this on for operations that are supposed to always execute + /// independently, without side effects caused by a shared NHibernate ISession. + ///

    + ///
    + public override bool AlwaysUseNewSession + { + get { return alwaysUseNewSession; } + set { alwaysUseNewSession = value; } + } + + + /// + /// Gets or sets the template flush mode. + /// + /// + /// Default is Auto. Will get applied to any new ISession + /// created by the template. + /// + /// The template flush mode. + public override TemplateFlushMode TemplateFlushMode + { + get { return templateFlushMode; } + set { templateFlushMode = value; } + } + + /// + /// Gets or sets the entity interceptor that allows to inspect and change + /// property values before writing to and reading from the database. + /// + /// + /// Will get applied to any new ISession created by this object. + ///

    Such an interceptor can either be set at the ISessionFactory level, + /// i.e. on LocalSessionFactoryObject, or at the ISession level, i.e. on + /// HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + /// It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + /// to avoid repeated configuration and guarantee consistent behavior in transactions. + ///

    + ///
    + /// The interceptor. + /// If object factory is not set and need to retrieve entity interceptor by name. + public override IInterceptor EntityInterceptor + { + get + { + if (this.entityInterceptor is string) + { + if (this.objectFactory == null) + { + throw new InvalidOperationException("Cannot get entity interceptor via object name if no object factory set"); + } + return (IInterceptor)this.objectFactory.GetObject((String)this.entityInterceptor, typeof(IInterceptor)); + } + + return (IInterceptor)entityInterceptor; + } + set + { + entityInterceptor = value; + } + } + /// + /// Gets or sets the name of the cache region for queries executed by this template. + /// + /// + /// If this is specified, it will be applied to all IQuery and ICriteria objects + /// created by this template (including all queries through find methods). + ///

    The cache region will not take effect unless queries created by this + /// template are configured to be cached via the CacheQueries property. + ///

    + ///
    + /// The query cache region. + public override string QueryCacheRegion + { + get { return queryCacheRegion; } + set { queryCacheRegion = value; } + } + + + /// + /// Gets or sets a value indicating whether to + /// cache all queries executed by this template. + /// + /// + /// If this is true, all IQuery and ICriteria objects created by + /// this template will be marked as cacheable (including all + /// queries through find methods). + ///

    To specify the query region to be used for queries cached + /// by this template, set the QueryCacheRegion property. + ///

    + ///
    + /// true if cache queries; otherwise, false. + public override bool CacheQueries + { + get { return cacheQueries; } + set { cacheQueries = value; } + } + + /// + /// Gets or sets the maximum number of rows for this HibernateTemplate. + /// + /// The max results. + /// + /// This is important + /// for processing subsets of large result sets, avoiding to read and hold + /// the entire result set in the database or in the ADO.NET driver if we're + /// never interested in the entire result in the first place (for example, + /// when performing searches that might return a large number of matches). + ///

    Default is 0, indicating to use the driver's default.

    + ///
    + public override int MaxResults + { + get { return maxResults; } + set { maxResults = value; } + } + + /// + /// Set whether to expose the native Hibernate Session to IHibernateCallback + /// code. Default is "false": a Session proxy will be returned, + /// suppressing close calls and automatically applying + /// query cache settings and transaction timeouts. + /// + /// true if expose native session; otherwise, false. + public override bool ExposeNativeSession + { + get { return exposeNativeSession; } + set { exposeNativeSession = value; } + } + /// + /// Gets or sets whether to check that the Hibernate Session is not in read-only mode + /// in case of write operations (save/update/delete). + /// + /// + /// true if check that the Hibernate Session is not in read-only mode + /// in case of write operations; otherwise, false. + /// + /// + /// Default is "true", for fail-fast behavior when attempting write operations + /// within a read-only transaction. Turn this off to allow save/update/delete + /// on a Session with flush mode NEVER. + /// + public virtual bool CheckWriteOperations + { + get { return checkWriteOperations; } + set { checkWriteOperations = value; } + } + + /// + /// Set the object name of a Hibernate entity interceptor that allows to inspect + /// and change property values before writing to and reading from the database. + /// + /// + /// Will get applied to any new Session created by this transaction manager. + ///

    Requires the object factory to be known, to be able to resolve the object + /// name to an interceptor instance on session creation. Typically used for + /// prototype interceptors, i.e. a new interceptor instance per session. + ///

    + ///

    Can also be used for shared interceptor instances, but it is recommended + /// to set the interceptor reference directly in such a scenario. + ///

    + ///
    + /// The name of the entity interceptor in the object factory/application context. + public override string EntityInterceptorObjectName + { + set + { + this.entityInterceptor = value; + } + } + + /// + /// Set the object factory instance. + /// + /// The object factory instance + public override IObjectFactory ObjectFactory + { + set + { + objectFactory = value; + } + } + + /// + /// Gets or sets the session factory that should be used to create + /// NHibernate ISessions. + /// + /// The session factory. + public override ISessionFactory SessionFactory + { + get { return sessionFactory; } + set + { + sessionFactory = value; + } + } + + /// + /// Gets or sets the fetch size for this HibernateTemplate. + /// + /// The size of the fetch. + /// This is important for processing + /// large result sets: Setting this higher than the default value will increase + /// processing speed at the cost of memory consumption; setting this lower can + /// avoid transferring row data that will never be read by the application. + ///

    Default is 0, indicating to use the driver's default.

    + ///
    + public override int FetchSize + { + get { return fetchSize; } + set { fetchSize = value; } + } + + + /// + /// Gets or sets the proxy factory. + /// + /// This may be useful to set if you create many instances of + /// HibernateTemplate and/or HibernateDaoSupport. This allows the same + /// ProxyFactory implementation to be used thereby limiting the + /// number of dynamic proxy types created in the temporary assembly, which + /// are never garbage collected due to .NET runtime semantics. + /// + /// The proxy factory. + public virtual ProxyFactory ProxyFactory + { + get { return sessionProxyFactory; } + set { sessionProxyFactory = value; } + } + + #endregion + + #region IHibernateOperations Members + + /// + /// Set the ADO.NET exception translator for this instance. + /// Applied to System.Data.Common.DbException (or provider specific exception type + /// in .NET 1.1) thrown by callback code, be it direct + /// DbException or wrapped Hibernate ADOExceptions. + ///

    The default exception translator is either a ErrorCodeExceptionTranslator + /// if a DbProvider is available, or a FalbackExceptionTranslator otherwise + ///

    + ///
    + /// The ADO exception translator. + public override IAdoExceptionTranslator AdoExceptionTranslator + { + set { adoExceptionTranslator = value; } + get + { + if (adoExceptionTranslator == null) + { + adoExceptionTranslator = SessionFactoryUtils.NewAdoExceptionTranslator(SessionFactory); + } + return adoExceptionTranslator; + } + } + + + /// + /// Delegate function that clears the session. + /// + /// The hibernate session. + /// null + protected object ClearAction(ISession session) + { + session.Clear(); + return null; + } + + /// + /// Flush all pending saves, updates and deletes to the database. + /// + /// + /// Only invoke this for selective eager flushing, for example when ADO.NET code + /// needs to see certain changes within the same transaction. Else, it's preferable + /// to rely on auto-flushing at transaction completion. + /// + /// In case of Hibernate errors + public void Flush() + { + Execute(new HibernateDelegate(FlushAction), true); + } + + private object FlushAction(ISession session) + { + session.Flush(); + return null; + } + + /// + /// Return the persistent instance of the given entity type + /// with the given identifier, or null if not found. + /// + /// The type. + /// An identifier of the persistent instance. + /// The persistent instance, or null if not found + /// In case of Hibernate errors + public object Get(Type entityType, object id) + { + return Get(entityType, id, null); + } + + /// + /// Return the persistent instance of the given entity type + /// with the given identifier, or null if not found. + /// Obtains the specified lock mode if the instance exists. + /// + /// The type. + /// The lock mode to obtain. + /// The lock mode. + /// the persistent instance, or null if not found + /// the persistent instance, or null if not found + /// In case of Hibernate errors + public object Get(Type type, object id, LockMode lockMode) + { + return Execute(new GetByTypeHibernateCallback(type, id, lockMode),true); + + } + + /// + /// Return the persistent instance of the given entity class + /// with the given identifier, throwing an exception if not found. + /// + /// Type of the entity. + /// An identifier of the persistent instance. + /// The persistent instance + /// If not found + /// In case of Hibernate errors + public object Load(Type entityType, object id) + { + return Load(entityType, id, null); + } + + /// + /// Return the persistent instance of the given entity class + /// with the given identifier, throwing an exception if not found. + /// Obtains the specified lock mode if the instance exists. + /// + /// Type of the entity. + /// An identifier of the persistent instance. + /// The lock mode. + /// The persistent instance + /// If not found + /// In case of Hibernate errors + public object Load(Type entityType, object id, LockMode lockMode) + { + return Execute(new LoadByTypeHibernateCallback(entityType, id, lockMode),true); + + } + + /// + /// Load the persistent instance with the given identifier + /// into the given object, throwing an exception if not found. + /// + /// Entity the object (of the target class) to load into. + /// An identifier of the persistent instance. + /// If object not found. + /// In case of Hibernate errors + public void Load(object entity, object id) + { + Execute(new LoadByEntityHibernateCallback(entity, id),true); + } + + /// + /// Return all persistent instances of the given entity class. + /// Note: Use queries or criteria for retrieving a specific subset. + /// + /// Type of the entity. + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList LoadAll(Type entityType) + { + return (IList)Execute(new LoadAllByTypeHibernateCallback(this, entityType),true); + } + + /// + /// Re-read the state of the given persistent instance. + /// + /// The persistent instance to re-read. + /// In case of Hibernate errors + public void Refresh(object entity) + { + Refresh(entity, null); + } + + /// + /// Re-read the state of the given persistent instance. + /// Obtains the specified lock mode for the instance. + /// + /// The persistent instance to re-read. + /// The lock mode to obtain. + /// In case of Hibernate errors + public void Refresh(object entity, LockMode lockMode) + { + Execute(new RefreshHibernateCallback(entity, lockMode),true); + } + + /// + /// Obtain the specified lock level upon the given object, implicitly + /// checking whether the corresponding database entry still exists + /// (throwing an OptimisticLockingFailureException if not found). + /// + /// The he persistent instance to lock. + /// The lock mode to obtain. + /// If not found + /// In case of Hibernate errors + public void Lock(object entity, LockMode lockMode) + { + Execute(new LockHibernateCallback(entity, lockMode),true); + } + + /// + /// Persist the given transient instance. + /// + /// The transient instance to persist. + /// The generated identifier. + /// In case of Hibernate errors + public object Save(object entity) + { + return Execute(new SaveObjectHibernateCallback(this, entity),true); + } + + /// + /// Persist the given transient instance with the given identifier. + /// + /// The transient instance to persist. + /// The identifier to assign. + /// In case of Hibernate errors + public void Save(object entity, object id) + { + Execute(new SaveObjectWithIdHibernateCallback(this, entity, id),true); + } + + /// + /// Update the given persistent instance. + /// + /// The persistent instance to update. + /// In case of Hibernate errors + public void Update(object entity) + { + Update(entity, null); + } + + /// + /// Update the given persistent instance. + /// Obtains the specified lock mode if the instance exists, implicitly + /// checking whether the corresponding database entry still exists + /// (throwing an OptimisticLockingFailureException if not found). + /// + /// The persistent instance to update. + /// The lock mode to obtain. + /// In case of Hibernate errors + public void Update(object entity, LockMode lockMode) + { + Execute(new UpdateObjectHibernateCallback(this, entity, lockMode),true); + } + + /// + /// Save or update the given persistent instance, + /// according to its id (matching the configured "unsaved-value"?). + /// + /// Tthe persistent instance to save or update + /// (to be associated with the Hibernate Session). + /// In case of Hibernate errors + public void SaveOrUpdate(object entity) + { + Execute(new SaveOrUpdateObjectHibernateCallback(this, entity),true); + } + + /// + /// Save or update all given persistent instances, + /// according to its id (matching the configured "unsaved-value"?). + /// + /// Tthe persistent instances to save or update + /// (to be associated with the Hibernate Session)he entities. + /// In case of Hibernate errors + public void SaveOrUpdateAll(ICollection entities) + { + Execute(new SaveOrUpdateAllHibernateCallback(this, entities), true); + } + + /// + /// Save or update the contents of given persistent object, + /// according to its id (matching the configured "unsaved-value"?). + /// Will copy the contained fields to an already loaded instance + /// with the same id, if appropriate. + /// + /// The persistent object to save or update. + /// (not necessarily to be associated with the Hibernate Session) + /// + /// The actually associated persistent object. + /// (either an already loaded instance with the same id, or the given object) + /// In case of Hibernate errors + public object SaveOrUpdateCopy(object entity) + { + return Execute(new SaveOrUpdateCopyHibernateCallback(this, entity),true); + } + + + /// + /// Remove all objects from the Session cache, and cancel all pending saves, + /// updates and deletes. + /// + public void Clear() + { + Execute(new HibernateDelegate(ClearAction), true); + } + + + + /// + /// Determines whether the given object is in the Session cache. + /// + /// the persistence instance to check. + /// + /// true if session cache contains the specified entity; otherwise, false. + /// + /// In case of Hibernate errors + public bool Contains(object entity) + { + return (bool)Execute(new ContainsHibernateCallback(entity)); + } + + /// + /// Remove the given object from the Session cache. + /// + /// The persistent instance to evict. + /// In case of Hibernate errors + public void Evict(object entity) + { + Execute(new EvictHibernateCallback(entity), true); + + } + + + + /// + /// Delete the given persistent instance. + /// + /// The persistent instance to delete. + /// In case of Hibernate errors + public void Delete(object entity) + { + Delete(entity, null); + } + + + /// + /// Delete the given persistent instance. + /// + /// Tthe persistent instance to delete. + /// The lock mode to obtain. + /// + /// Obtains the specified lock mode if the instance exists, implicitly + /// checking whether the corresponding database entry still exists + /// (throwing an OptimisticLockingFailureException if not found). + /// + /// In case of Hibernate errors + public void Delete(object entity, LockMode lockMode) + { + Execute(new DeleteLockModeHibernateCallback(this, entity, lockMode), true); + } + + /// + /// Delete all objects returned by the query. + /// + /// a query expressed in Hibernate's query language. + /// The number of entity instances deleted. + /// In case of Hibernate errors + public int Delete(string queryString) + { + return Delete(queryString, (Object[]) null, (IType[]) null); + } + + /// + /// Delete all objects returned by the query. + /// + /// a query expressed in Hibernate's query language. + /// The value of the parameter. + /// The Hibernate type of the parameter (or null). + /// The number of entity instances deleted. + /// In case of Hibernate errors + public int Delete(string queryString, object value, IType type) + { + return Delete(queryString, new Object[] {value}, new IType[] {type}); + } + + /// + /// Delete all objects returned by the query. + /// + /// a query expressed in Hibernate's query language. + /// The values of the parameters. + /// Hibernate types of the parameters (or null) + /// The number of entity instances deleted. + /// In case of Hibernate errors + /// If length for argument values and types are not equal. + public int Delete(String queryString, Object[] values, IType[] types) + { + if (values != null && types != null && values.Length != types.Length) + { + throw new ArgumentOutOfRangeException("values", "Length of values array must match length of types array"); + } + return (int)Execute(new DeletebyQueryHibernateCallback(this, queryString, values, types),true); + + } + + + /// + /// Delete all given persistent instances. + /// + /// The persistent instances to delete. + /// + /// This can be combined with any of the find methods to delete by query + /// in two lines of code, similar to Session's delete by query methods. + /// + /// In case of Hibernate errors + public void DeleteAll(ICollection entities) + { + Execute(new DeleteAllHibernateCallback(this, entities),true); + } + + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning a result object, i.e. a domain + /// object or a collection of domain objects. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + /// The delegate callback object that specifies the Hibernate action. + /// a result object returned by the action, or null + /// + /// In case of Hibernate errors + public object Execute(HibernateDelegate del) + { + return Execute(new ExecuteHibernateCallbackUsingDelegate(del)); + } + + /// + /// Execute the action specified by the delegate within a Session. + /// + /// The HibernateDelegate that specifies the action + /// to perform. + /// if set to true expose the native hibernate session to + /// callback code. + /// a result object returned by the action, or null + /// + public object Execute(HibernateDelegate del, bool exposeNativeSession) + { + return Execute(new ExecuteHibernateCallbackUsingDelegate(del), true); + } + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// The callback object that specifies the Hibernate action. + /// + /// a result object returned by the action, or null + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning a result object, i.e. a domain + /// object or a collection of domain objects. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + /// In case of Hibernate errors + public object Execute(IHibernateCallback action) + { + return Execute(action, ExposeNativeSession); + } + + /// + /// Execute the specified action assuming that the result object is a List. + /// + /// + /// This is a convenience method for executing Hibernate find calls or + /// queries within an action. + /// + /// The calback object that specifies the Hibernate action. + /// A IList returned by the action, or null + /// + /// In case of Hibernate errors + public IList ExecuteFind(IHibernateCallback action) + { + Object result = Execute(action, ExposeNativeSession); + if (result != null && !(result is IList)) { + throw new InvalidDataAccessApiUsageException( + "Result object returned from HibernateCallback isn't a List: [" + result + "]"); + } + return (IList) result; + } + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// callback object that specifies the Hibernate action. + /// if set to true expose the native hibernate session to + /// callback code. + /// + /// a result object returned by the action, or null + /// + public object Execute(IHibernateCallback action, bool exposeNativeSession) + { + ISession session = Session; + + bool existingTransaction = SessionFactoryUtils.IsSessionTransactional(session, SessionFactory); + if (existingTransaction) + { + if(log.IsDebugEnabled) log.Debug("Found thread-bound Session for HibernateTemplate"); + } + + FlushModeHolder previousFlushModeHolder = new FlushModeHolder(); + try + { + previousFlushModeHolder = ApplyFlushMode(session, existingTransaction); + ISession sessionToExpose = (exposeNativeSession ? session : CreateSessionProxy(session)); + Object result = action.DoInHibernate(sessionToExpose); + FlushIfNecessary(session, existingTransaction); + return result; + } + catch (ADOException ex) + { + IDbProvider dbProvider = SessionFactoryUtils.GetDbProvider(SessionFactory); + if (dbProvider != null && dbProvider.IsDataAccessException(ex.InnerException)) + { + throw ConvertAdoAccessException(ex); + } + else + { + throw new HibernateSystemException(ex); + } + } + catch (HibernateException ex) + { + throw ConvertHibernateAccessException(ex); + } + catch (Exception ex) + { + IDbProvider dbProvider = SessionFactoryUtils.GetDbProvider(SessionFactory); + if (dbProvider != null && dbProvider.IsDataAccessException(ex)) + { + throw ConvertAdoAccessException(ex); + } + else + { + // Callback code throw application exception or other non DB related exception. + throw; + } + } + finally + { + if (existingTransaction) + { + if (log.IsDebugEnabled) log.Debug("Not closing pre-bound Hibernate Session after HibernateTemplate"); + if (previousFlushModeHolder.ModeWasSet) + { + session.FlushMode = previousFlushModeHolder.Mode; + } + } + else + { + // Never use deferred close for an explicitly new Session. + if (AlwaysUseNewSession) + { + SessionFactoryUtils.CloseSession(session); + } + else + { + SessionFactoryUtils.CloseSessionOrRegisterDeferredClose(session, SessionFactory); + } + } + } + } + + /// + /// Execute a query for persistent instances. + /// + /// a query expressed in Hibernate's query language + /// + /// a List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + public IList Find(string queryString) + { + return Find(queryString, (object[])null, (IType[])null ); + } + + /// + /// Execute a query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// + /// a query expressed in Hibernate's query language + /// the value of the parameter + /// + /// a List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + public IList Find(string queryString, object value) + { + return Find(queryString, new object[] {value}, (IType[]) null); + } + + /// + /// Execute a query for persistent instances, binding one value + /// to a "?" parameter of the given type in the query string. + /// + /// a query expressed in Hibernate's query language + /// The value of the parameter. + /// Hibernate type of the parameter (or null) + /// + /// a List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + public IList Find(string queryString, object value, IType type) + { + return Find(queryString, new object[] {value}, new IType[] {type}); + } + + /// + /// Execute a query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// + /// a query expressed in Hibernate's query language + /// the values of the parameters + /// a List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList Find(string queryString, object[] values) + { + return Find(queryString, values, (IType[]) null); + } + + /// + /// Execute a query for persistent instances, binding a number of + /// values to "?" parameters of the given types in the query string. + /// + /// A query expressed in Hibernate's query language + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// + /// a List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + /// If values and types are not null and their lengths are not equal + public IList Find(string queryString, object[] values, IType[] types) + { + if (values != null && types != null && values.Length != types.Length) + { + throw new ArgumentException("Length of values array must match length of types array"); + } + return (IList)Execute(new FindHibernateCallback(this, queryString, values, types),true); + } + + /// + /// Execute a query for persistent instances, binding + /// one value to a named parameter in the query string. + /// + /// The name of a Hibernate query in a mapping file + /// The name of the parameter + /// The value of the parameter + /// a List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedParam(string queryName, string paramName, object value) + { + return FindByNamedParam(queryName, paramName, value, null); + } + + /// + /// Execute a query for persistent instances, binding + /// one value to a named parameter in the query string. + /// + /// The name of a Hibernate query in a mapping file + /// The name of the parameter + /// The value of the parameter + /// Hibernate type of the parameter (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedParam(string queryName, string paramName, object value, IType type) + { + return FindByNamedParam(queryName, new string[] {paramName}, new object[] {value}, new IType[] {type}); + } + + /// + /// Execute a query for persistent instances, binding a + /// number of values to named parameters in the query string. + /// + /// A query expressed in Hibernate's query language + /// The names of the parameters + /// The values of the parameters + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedParam(string queryString, string[] paramNames, object[] values) + { + return FindByNamedParam(queryString, paramNames, values, null); + } + + /// + /// Execute a query for persistent instances, binding a + /// number of values to named parameters in the query string. + /// + /// A query expressed in Hibernate's query language + /// The names of the parameters + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If paramNames length is not equal to values length or + /// if paramNames length is not equal to types length (when types is not null) + public IList FindByNamedParam(string queryString, string[] paramNames, object[] values, IType[] types) + { + if (paramNames.Length != values.Length) + { + throw new ArgumentOutOfRangeException("paramNames","Length of paramNames array must match length of values array"); + } + if (types != null && paramNames.Length != types.Length) + { + throw new ArgumentOutOfRangeException("paramNames","Length of paramNames array must match length of types array"); + } + + return (IList)Execute(new FindByNamedParamHibernateCallback(this, queryString, paramNames, values, types),true); + + } + + /// + /// Execute a named query for persistent instances. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQuery(string queryName) + { + return FindByNamedQuery(queryName, (object[]) null, (IType[]) null); + } + + /// + /// Execute a named query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The value of the parameter + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQuery(string queryName, object value) + { + return FindByNamedQuery(queryName, new object[] {value}, (IType[]) null); + } + + /// + /// Execute a named query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The value of the parameter + /// Hibernate type of the parameter (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQuery(string queryName, object value, IType type) + { + return FindByNamedQuery(queryName, new object[] { value }, new IType[] { type }); + } + + /// + /// Execute a named query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQuery(string queryName, object[] values) + { + return FindByNamedQuery(queryName, values, (IType[]) null); + } + + /// + /// Execute a named query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If values and types are not null and their lengths differ. + public IList FindByNamedQuery(string queryName, object[] values, IType[] types) + { + if (values != null && types != null && values.Length != types.Length) + { + throw new ArgumentOutOfRangeException("Length of values array must match length of types array"); + } + return (IList)Execute(new FindByNamedQueryHibernateCallback(this, queryName, values, types),true); + + + } + + /// + /// Execute a named query for persistent instances, binding + /// one value to a named parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// Name of the parameter + /// The value of the parameter + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQueryAndNamedParam(string queryName, string paramName, object value) + { + return FindByNamedQueryAndNamedParam(queryName, paramName, value, null); + + } + + /// + /// Execute a named query for persistent instances, binding + /// one value to a named parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// Name of the parameter + /// The value of the parameter + /// The Hibernate type of the parameter (or null) + /// A List containing 0 or more persistent instances + public IList FindByNamedQueryAndNamedParam(string queryName, string paramName, object value, IType type) + { + return FindByNamedQueryAndNamedParam( + queryName, new string[] {paramName}, new object[] {value}, new IType[] {type}); + + } + + /// + /// Execute a named query for persistent instances, binding + /// number of values to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The names of the parameters + /// The values of the parameters. + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQueryAndNamedParam(string queryName, string[] paramNames, object[] values) + { + return FindByNamedQueryAndNamedParam(queryName, paramNames, values, null); + + } + + /// + /// Execute a named query for persistent instances, binding + /// number of values to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The names of the parameters + /// The values of the parameters. + /// Hibernate types of the parameters (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If paramNames length is not equal to values length or + /// if paramNames length is not equal to types length (when types is not null) + public IList FindByNamedQueryAndNamedParam(string queryName, string[] paramNames, object[] values, IType[] types) + { + if (paramNames != null && values != null && paramNames.Length != values.Length) + { + throw new ArgumentOutOfRangeException("paramNames","Length of paramNames array must match length of values array"); + } + if (paramNames != null && types != null && paramNames.Length != types.Length) + { + throw new ArgumentOutOfRangeException("paramNams","Length of paramNames array must match length of types array"); + } + return (IList)Execute(new FindByNamedQueryAndNamedParamHibernateCallback(this, queryName, paramNames, values, types),true); + + } + + /// + /// Execute a named query for persistent instances, binding the properties + /// of the given object to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQueryAndValueObject(string queryName, object valueObject) + { + return (IList)Execute(new FindByNamedQueryAndValueObjectHibernateCallback(this, queryName, valueObject),true); + + } + + /// + /// Execute a query for persistent instances, binding the properties + /// of the given object to named parameters in the query string. + /// + /// A query expressed in Hibernate's query language + /// The values of the parameters + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByValueObject(string queryString, object valueObject) + { + return (IList)Execute(new FindByValueObjectHibernateCallback(this, queryString, valueObject), true); + } + + #endregion + + #region Methods + + + /// + /// Create a close-suppressing proxy for the given Hibernate Session. + /// The proxy also prepares returned Query and Criteria objects. + /// + /// The session. + /// The session proxy. + public virtual ISession CreateSessionProxy(ISession session) + { + //TODO can move to HibernateAccessor and make protected + // if issue reported with AOP+Multiple Threads resolve. + // have not been able to reproduce so added lock as a precaution. + // + lock (syncRoot) + { + if (sessionProxyFactory == null) + { + sessionProxyFactory = new ProxyFactory(); + sessionProxyFactory.AddAdvice(new CloseSuppressingMethodInterceptor(this)); + } + + sessionProxyFactory.Target = session; + + return (ISession)sessionProxyFactory.GetProxy(); + } + } + + /// + /// Check whether write operations are allowed on the given Session. + /// + /// + /// Default implementation throws an InvalidDataAccessApiUsageException + /// in case of FlushMode.Never. Can be overridden in subclasses. + /// + /// The current Hibernate session. + /// If write operation is attempted in read-only mode + /// + public virtual void CheckWriteOperationAllowed(ISession session) + { + if (CheckWriteOperations && TemplateFlushMode != TemplateFlushMode.Eager && + AreEqualFlushMode(TemplateFlushMode.Never, session.FlushMode)) + { + throw new InvalidDataAccessApiUsageException( + "Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session " + + "into FlushMode.AUTO or remove 'readOnly' marker from transaction definition"); + } + } + + + + /// + /// Compares if the flush mode enumerations, Spring's + /// TemplateFlushMode and NHibernates FlushMode have equal + /// settings. + /// + /// The template flush mode. + /// The NHibernate flush mode. + /// + /// Returns true if both are Never, Auto, or Commit, false + /// otherwise. + /// + protected bool AreEqualFlushMode(TemplateFlushMode tfm, FlushMode fm) + { + if ( (tfm ==TemplateFlushMode.Never && fm == FlushMode.Never) || + (tfm ==TemplateFlushMode.Auto && fm == FlushMode.Auto) || + (tfm ==TemplateFlushMode.Commit && fm == FlushMode.Commit) ) + { + return true; + } + else + { + return false; + } + //TODO other combinations. + } + + #endregion + } + + #region Internal Supporting Callback Classes + + //TODO see if can create common base class for some callbacks. + + internal class ContainsHibernateCallback : IHibernateCallback + { + private object entity; + public ContainsHibernateCallback(object entity) + { + this.entity = entity; + } + + public object DoInHibernate(ISession session) + { + return session.Contains(entity); + } + } + + + internal class DeleteLockModeHibernateCallback : IHibernateCallback + { + private HibernateTemplate outer; + private object entity; + private LockMode lockMode; + public DeleteLockModeHibernateCallback(HibernateTemplate template, object entity, LockMode lockMode) + { + this.outer = template; + this.entity = entity; + this.lockMode = lockMode; + } + + public object DoInHibernate(ISession session) + { + outer.CheckWriteOperationAllowed(session); + if (lockMode != null) + { + session.Lock(entity, lockMode); + } + session.Delete(entity); + return null; + } + } + + + internal class DeletebyQueryHibernateCallback : IHibernateCallback + { + private HibernateTemplate outer; + private string queryString; + private object[] values; + private IType[] types; + + public DeletebyQueryHibernateCallback(HibernateTemplate template, string queryString, object[] values, IType[] types) + { + this.outer = template; + this.queryString = queryString; + this.values = values; + this.types = types; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + outer.CheckWriteOperationAllowed(session); + if (values != null) + { + return session.Delete(queryString, values, types); + } + else + { + return session.Delete(queryString); + } + } + } + + + + internal class DeleteAllHibernateCallback : IHibernateCallback + { + HibernateTemplate outer; + private ICollection entities; + + public DeleteAllHibernateCallback(HibernateTemplate template, ICollection entities) + { + this.outer = template; + this.entities = entities; + + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + outer.CheckWriteOperationAllowed(session); + foreach (object entity in entities) + { + session.Delete(entity); + } + return null; + } + + + + } + + + internal class EvictHibernateCallback : IHibernateCallback + { + private object entity; + + public EvictHibernateCallback(object entity) + { + this.entity = entity; + + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + session.Evict(entity); + return null; + } + + + + } + + + internal class FindHibernateCallback : IHibernateCallback + { + private HibernateTemplate outer; + private string queryString; + private object[] values; + private IType[] types; + + public FindHibernateCallback(HibernateTemplate template, string queryString, object[] values, IType[] types) + { + this.outer = template; + this.queryString = queryString; + this.values = values; + this.types = types; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + IQuery queryObject = session.CreateQuery(queryString); + outer.PrepareQuery(queryObject); + if (values != null) + { + for (int i = 0; i < values.Length; i++) + { + if (types != null && types[i] != null) + { + queryObject.SetParameter(i, values[i], types[i]); + } + else + { + queryObject.SetParameter(i, values[i]); + } + } + } + + return queryObject.List(); + } + } + + + internal class FindByNamedParamHibernateCallback : IHibernateCallback + { + HibernateTemplate outer; + private string queryString; + private string[] paramNames; + private object[] values; + private IType[] types; + + public FindByNamedParamHibernateCallback(HibernateTemplate template, string queryString, string[] paramNames, object[] values, IType[] types) + { + this.outer = template; + this.queryString = queryString; + this.paramNames = paramNames; + this.values = values; + this.types = types; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + IQuery queryObject = session.CreateQuery(queryString); + outer.PrepareQuery(queryObject); + if (values != null) + { + for (int i = 0; i < values.Length; i++) + { + outer.ApplyNamedParameterToQuery(queryObject, paramNames[i], values[i], (types != null ? types[i] : null)); + } + } + return queryObject.List(); + + } + } + + + internal class FindByNamedQueryHibernateCallback : IHibernateCallback + { + HibernateTemplate outer; + private string queryName; + private object[] values; + private IType[] types; + + public FindByNamedQueryHibernateCallback(HibernateTemplate template, string queryName, object[] values, IType[] types) + { + this.outer = template; + this.queryName = queryName; + this.values = values; + this.types = types; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + IQuery queryObject = session.GetNamedQuery(queryName); + outer.PrepareQuery(queryObject); + if (values != null) + { + for (int i = 0; i < values.Length; i++) + { + if (types != null && types[i] != null) + { + queryObject.SetParameter(i, values[i], types[i]); + } + else + { + queryObject.SetParameter(i, values[i]); + } + } + } + return queryObject.List(); + + } + } + + + internal class FindByNamedQueryAndNamedParamHibernateCallback : IHibernateCallback + { + HibernateTemplate outer; + private string queryName; + private string[] paramNames; + private object[] values; + private IType[] types; + + public FindByNamedQueryAndNamedParamHibernateCallback(HibernateTemplate template, string queryName, string[] paramNames, object[] values, IType[] types) + { + this.outer = template; + this.queryName = queryName; + this.paramNames = paramNames; + this.values = values; + this.types = types; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + IQuery queryObject = session.GetNamedQuery(queryName); + outer.PrepareQuery(queryObject); + if (values != null) + { + for (int i = 0; i < values.Length; i++) + { + outer.ApplyNamedParameterToQuery(queryObject, paramNames[i], values[i], (types != null ? types[i] : null)); + } + } + return queryObject.List(); + + } + } + + + internal class FindByNamedQueryAndValueObjectHibernateCallback : IHibernateCallback + { + HibernateTemplate outer; + private string queryName; + private object valueObject; + + public FindByNamedQueryAndValueObjectHibernateCallback(HibernateTemplate template, string queryName, object valueObject) + { + this.outer = template; + this.queryName = queryName; + this.valueObject = valueObject; + + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + IQuery queryObject = session.GetNamedQuery(queryName); + outer.PrepareQuery(queryObject); + queryObject.SetProperties(valueObject); + return queryObject.List(); + + } + + + + } + + internal class FindByValueObjectHibernateCallback : IHibernateCallback + { + HibernateTemplate outer; + private string queryString; + private object valueObject; + + public FindByValueObjectHibernateCallback(HibernateTemplate template, string queryString, object valueObject) + { + this.outer = template; + this.queryString = queryString; + this.valueObject = valueObject; + + } + + public object DoInHibernate(ISession session) + { + IQuery queryObject = session.CreateQuery(queryString); + outer.PrepareQuery(queryObject); + queryObject.SetProperties(valueObject); + return queryObject.List(); + + } + + } + + internal class ExecuteHibernateCallbackUsingDelegate : IHibernateCallback + { + private HibernateDelegate del; + + public ExecuteHibernateCallbackUsingDelegate(HibernateDelegate d) + { + del = d; + } + + public object DoInHibernate(ISession session) + { + return del(session); + } + } + + + internal class GetByTypeHibernateCallback : IHibernateCallback + { + private Type entityType; + private object id; + private LockMode lockMode; + + public GetByTypeHibernateCallback(Type entityType, object id, LockMode lockMode) + { + this.entityType = entityType; + this.id = id; + this.lockMode = lockMode; + + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + if (lockMode != null) + { + return session.Get(entityType, id, lockMode); + } + else + { + return session.Get(entityType, id); + } + } + + + + } + + + internal class LoadByTypeHibernateCallback : IHibernateCallback + { + private Type entityType; + private object id; + private LockMode lockMode; + + public LoadByTypeHibernateCallback(Type entityType, object id, LockMode lockMode) + { + this.entityType = entityType; + this.id = id; + this.lockMode = lockMode; + + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + if (lockMode != null) + { + return session.Load(entityType, id, lockMode); + } + else + { + return session.Load(entityType, id); + } + } + + + + } + + + internal class LoadByEntityHibernateCallback : IHibernateCallback + { + private object entity; + private object id; + + public LoadByEntityHibernateCallback(object entity, object id) + { + this.entity = entity; + this.id = id; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + session.Load(entity, id); + return null; + } + + + + } + + + internal class LoadAllByTypeHibernateCallback : IHibernateCallback + { + private HibernateTemplate outer; + private Type entityType; + + public LoadAllByTypeHibernateCallback(HibernateTemplate template, Type entityType) + { + outer = template; + this.entityType = entityType; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + ICriteria criteria = session.CreateCriteria(entityType); + outer.PrepareCriteria(criteria); + return criteria.List(); + } + } + + + internal class LockHibernateCallback : IHibernateCallback + { + private object entity; + private LockMode lockMode; + + public LockHibernateCallback(object entity, LockMode lockMode) + { + this.entity = entity; + this.lockMode = lockMode; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + session.Lock(entity, lockMode); + return null; + } + } + + + internal class RefreshHibernateCallback : IHibernateCallback + { + private object entity; + private LockMode lockMode; + + public RefreshHibernateCallback(object entity, LockMode lockMode) + { + this.entity = entity; + this.lockMode = lockMode; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + if (lockMode != null) + { + session.Refresh(entity, lockMode); + } + else + { + session.Refresh(entity); + } + return null; + } + } + + + internal class SaveObjectHibernateCallback : IHibernateCallback + { + private HibernateTemplate outer; + private object entity; + + public SaveObjectHibernateCallback(HibernateTemplate template, object entity) + { + this.outer = template; + this.entity = entity; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + outer.CheckWriteOperationAllowed(session); + return session.Save(entity); + } + } + + + internal class SaveObjectWithIdHibernateCallback : IHibernateCallback + { + private HibernateTemplate outer; + private object entity; + private object id; + + public SaveObjectWithIdHibernateCallback(HibernateTemplate template, object entity, object id) + { + this.outer = template; + this.entity = entity; + this.id = id; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + outer.CheckWriteOperationAllowed(session); + session.Save(entity, id); + return null; + } + } + + + internal class UpdateObjectHibernateCallback : IHibernateCallback + { + private HibernateTemplate outer; + private object entity; + private LockMode lockMode; + + public UpdateObjectHibernateCallback(HibernateTemplate template, object entity, LockMode lockMode) + { + this.outer = template; + this.entity = entity; + this.lockMode = lockMode; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + outer.CheckWriteOperationAllowed(session); + session.Update(entity); + if (lockMode != null) + { + session.Lock(entity, lockMode); + } + return null; + } + } + + + internal class SaveOrUpdateObjectHibernateCallback : IHibernateCallback + { + private HibernateTemplate outer; + private object entity; + + public SaveOrUpdateObjectHibernateCallback(HibernateTemplate template, object entity) + { + this.outer = template; + this.entity = entity; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + outer.CheckWriteOperationAllowed(session); + session.SaveOrUpdate(entity); + return null; + } + } + + internal class SaveOrUpdateAllHibernateCallback : IHibernateCallback + { + private HibernateTemplate outer; + private ICollection entities; + + public SaveOrUpdateAllHibernateCallback(HibernateTemplate template, ICollection entities) + { + this.outer = template; + this.entities = entities; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + outer.CheckWriteOperationAllowed(session); + foreach (object entity in entities) + { + session.SaveOrUpdate(entity); + } + return null; + } + } + internal class SaveOrUpdateCopyHibernateCallback : IHibernateCallback + { + private HibernateTemplate outer; + private object entity; + + public SaveOrUpdateCopyHibernateCallback(HibernateTemplate template, object entity) + { + this.outer = template; + this.entity = entity; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public object DoInHibernate(ISession session) + { + outer.CheckWriteOperationAllowed(session); + return session.SaveOrUpdateCopy(entity); + } + } + + + #endregion + + +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateTransactionManager.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateTransactionManager.cs new file mode 100644 index 00000000..6dda6ef1 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/HibernateTransactionManager.cs @@ -0,0 +1,916 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using System.Reflection; + +using NHibernate; +using NHibernate.Transaction; + +using Spring.Core.TypeResolution; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Data.Support; +using Spring.Objects.Factory; +using Spring.Transaction; +using Spring.Transaction.Support; +using Spring.Util; +using HibernateTransactionException = NHibernate.TransactionException; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// PlatformTransactionManager implementation for a single Hibernate SessionFactory. + /// Binds a Hibernate Session from the specified factory to the thread, potentially + /// allowing for one thread Session per factory + /// + /// + /// SessionFactoryUtils and HibernateTemplate are aware of thread-bound Sessions and participate in such + /// transactions automatically. Using either of those is required for Hibernate + /// access code that needs to support this transaction handling mechanism. + /// + /// Supports custom isolation levels at the start of the transaction + /// , and timeouts that get applied as appropriate + /// Hibernate query timeouts. To support the latter, application code must either use + /// HibernateTemplate (which by default applies the timeouts) or call + /// SessionFactoryUtils.applyTransactionTimeout for each created + /// Hibernate Query object. + /// + /// Note that you can specify a Spring IDbProvider instance which if shared with + /// a corresponding instance of AdoTemplate will allow for mixing ADO.NET/NHibernate + /// operations within a single transaction. + /// + /// Mark Pollack (.NET) + /// $Id: HibernateTransactionManager.cs,v 1.7 2007/11/20 14:39:48 markpollack Exp $ + public class HibernateTransactionManager : AbstractPlatformTransactionManager, IObjectFactoryAware, IInitializingObject + { + #region Fields + + private ISessionFactory sessionFactory; + + private IDbProvider dbProvider; + + private bool autodetectDbProvider = true; + + private Object entityInterceptor; + + private IAdoExceptionTranslator adoExceptionTranslator; + + private IAdoExceptionTranslator defaultExceptionTranslator; + + /// + /// Just needed for entityInterceptorBeanName. + /// + private IObjectFactory objectFactory; + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public HibernateTransactionManager() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The session factory. + public HibernateTransactionManager(ISessionFactory sessionFactory) + { + this.sessionFactory = sessionFactory; + AfterPropertiesSet(); + } + + #endregion + + #region Properties + + /// + /// Gets or sets the db provider. + /// + /// The db provider. + public IDbProvider DbProvider + { + get { return dbProvider; } + set { dbProvider = value; } + } + + /// + /// Gets or sets a Hibernate entity interceptor that allows to inspect and change + /// property values before writing to and reading from the database. + /// When getting, return the current Hibernate entity interceptor, or null if none. + /// + /// The entity interceptor. + /// + /// Resolves an entity interceptor object name via the object factory, + /// if necessary. + /// Will get applied to any new Session created by this transaction manager. + /// Such an interceptor can either be set at the SessionFactory level, + /// i.e. on LocalSessionFactoryObject, or at the Session level, i.e. on + /// HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + /// It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + /// to avoid repeated configuration and guarantee consistent behavior in transactions. + /// + /// If object factory is null and need to get entity interceptor via object name. + public IInterceptor EntityInterceptor + { + get + { + if (this.entityInterceptor is IInterceptor) + { + return (IInterceptor) entityInterceptor; + } + else if (this.entityInterceptor is string) + { + if (this.objectFactory == null) + { + throw new InvalidOperationException ("Cannot get entity interceptor via object name if no object factory set"); + } + String objectName = (String) this.entityInterceptor; + return (IInterceptor) this.objectFactory.GetObject(objectName, typeof(IInterceptor)); + } + else + { + return null; + } + } + set + { + entityInterceptor = value; + } + } + + /// + /// Sets the object name of a Hibernate entity interceptor that + /// allows to inspect and change property values before writing to and reading from the database. + /// + /// The name of the entity interceptor object. + /// + /// Will get applied to any new Session created by this transaction manager. + ///

    Requires the object factory to be known, to be able to resolve the object + /// name to an interceptor instance on session creation. Typically used for + /// prototype interceptors, i.e. a new interceptor instance per session. + ///

    + ///

    Can also be used for shared interceptor instances, but it is recommended + /// to set the interceptor reference directly in such a scenario. + ///

    + ///
    + public string EntityInterceptorObjectName + { + set + { + entityInterceptor = value; + } + } + + /// + /// Gets or sets the ADO.NET exception translator for this transaction manager. + /// + /// + /// Applied to ADO.NET Exceptions (wrapped by Hibernate's ADOException) + /// + /// The ADO exception translator. + public IAdoExceptionTranslator AdoExceptionTranslator + { + get { return adoExceptionTranslator; } + set { adoExceptionTranslator = value; } + } + + /// + /// Gets the default IAdoException translator, lazily creating it if nece + /// + /// The default IAdoException translator. + public IAdoExceptionTranslator DefaultAdoExceptionTranslator + { + get + { lock(this) + { + if (defaultExceptionTranslator == null) + { + if (dbProvider != null) + { + defaultExceptionTranslator = new ErrorCodeExceptionTranslator(dbProvider); + } + else + { + defaultExceptionTranslator = SessionFactoryUtils.NewAdoExceptionTranslator(SessionFactory); + } + } + return defaultExceptionTranslator; + } + } + } + + /// + /// Gets or sets the SessionFactory that this instance should manage transactions for. + /// + /// The session factory. + public ISessionFactory SessionFactory + { + get { return sessionFactory; } + set { sessionFactory = value; } + } + + /// + /// Set whether to autodetect a ADO.NET connection used by the Hibernate SessionFactory, + /// if set via LocalSessionFactoryObject's DbProvider. Default is "true". + /// + /// + /// true if [autodetect data source]; otherwise, false. + /// + /// + ///

    Can be turned off to deliberately ignore an available IDbProvider, + /// to not expose Hibernate transactions as ADO.NET transactions for that IDbProvider. + ///

    + ///
    + public bool AutodetectDbProvider + { + set { autodetectDbProvider = value; } + } + + #endregion + + #region Methods + + #endregion + + + /// + /// The object factory just needs to be known for resolving entity interceptor + /// It does not need to be set for any other mode of operation. + /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + public IObjectFactory ObjectFactory + { + set + { + objectFactory = value; + } + } + + /// + /// Return the current transaction object. + /// + /// The current transaction object. + /// + /// If transaction support is not available. + /// + /// + /// In the case of lookup or system errors. + /// + protected override object DoGetTransaction() + { + HibernateTransactionObject txObject = new HibernateTransactionObject(); + txObject.SavepointAllowed = NestedTransactionsAllowed; + if (TransactionSynchronizationManager.HasResource(SessionFactory)) + { + SessionHolder sessionHolder = + (SessionHolder) TransactionSynchronizationManager.GetResource(SessionFactory); + if (log.IsDebugEnabled) + { + log.Debug("Found thread-bound Session [" + sessionHolder.Session + + "] for Hibernate transaction"); + } + txObject.SetSessionHolder(sessionHolder, false); + if (DbProvider != null) + { + ConnectionHolder conHolder = (ConnectionHolder) + TransactionSynchronizationManager.GetResource(DbProvider); + txObject.ConnectionHolder = conHolder; + } + } + return txObject; + } + + /// + /// Check if the given transaction object indicates an existing, + /// i.e. already begun, transaction. + /// + /// + /// Transaction object returned by + /// . + /// + /// True if there is an existing transaction. + /// + /// In the case of system errors. + /// + protected override bool IsExistingTransaction(object transaction) + { + return ((HibernateTransactionObject) transaction).HasTransaction(); + } + + /// + /// Begin a new transaction with the given transaction definition. + /// + /// + /// Transaction object returned by + /// . + /// + /// + /// instance, describing + /// propagation behavior, isolation level, timeout etc. + /// + /// + /// Does not have to care about applying the propagation behavior, + /// as this has already been handled by this abstract manager. + /// + /// + /// In the case of creation or system errors. + /// + protected override void DoBegin(object transaction, ITransactionDefinition definition) + { + HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; + + if (DbProvider != null && TransactionSynchronizationManager.HasResource(DbProvider) + && !txObject.ConnectionHolder.SynchronizedWithTransaction) + { + throw new IllegalTransactionStateException( + "Pre-bound ADO.NET Connection found - HibernateTransactionManager does not support " + + "running within AdoTransactionManager if told to manage the DbProvider itself. " + + "It is recommended to use a single HibernateTransactionManager for all transactions " + + "on a single DbProvider, no matter whether Hibernate or ADO.NET access."); + } + ISession session = null; + try + { + + if (txObject.SessionHolder == null || txObject.SessionHolder.SynchronizedWithTransaction) + { + IInterceptor interceptor = EntityInterceptor; + ISession newSession = (interceptor != null ? + SessionFactory.OpenSession(interceptor) : SessionFactory.OpenSession()); + + if (log.IsDebugEnabled) + { + log.Debug("Opened new Session [" + newSession + "] for Hibernate transaction"); + } + txObject.SetSessionHolder(new SessionHolder(newSession), true); + + } + txObject.SessionHolder.SynchronizedWithTransaction = true; + session = txObject.SessionHolder.Session; + + IDbConnection con = session.Connection; + //TODO isolation level mgmt + //IsolationLevel previousIsolationLevel = + + if (definition.ReadOnly && txObject.NewSessionHolder) + { + // Just set to NEVER in case of a new Session for this transaction. + session.FlushMode = FlushMode.Never; + } + + if (!definition.ReadOnly && !txObject.NewSessionHolder) + { + // We need AUTO or COMMIT for a non-read-only transaction. + FlushMode flushMode = session.FlushMode; + if (FlushMode.Never == flushMode) + { + session.FlushMode = FlushMode.Auto; + txObject.SessionHolder.PreviousFlushMode = flushMode; + } + } + + // Add the Hibernate transaction to the session holder. + // for now pass in tx options isolation level. + ITransaction hibernateTx = session.BeginTransaction(definition.TransactionIsolationLevel); + IDbTransaction adoTx = GetIDbTransaction(hibernateTx); + + // Add the Hibernate transaction to the session holder. + txObject.SessionHolder.Transaction = hibernateTx; + + // Register transaction timeout. + int timeout = DetermineTimeout(definition); + if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT) + { + txObject.SessionHolder.TimeoutInSeconds = timeout; + } + + // Register the Hibernate Session's ADO.NET Connection/TX pair for the DbProvider, if set. + if (DbProvider != null) + { + //investigate passing null for tx. + ConnectionHolder conHolder = new ConnectionHolder(con, adoTx); + if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT) + { + conHolder.TimeoutInMillis = definition.TransactionTimeout; + } + if (log.IsDebugEnabled) + { + log.Debug("Exposing Hibernate transaction as ADO transaction [" + con + "]"); + } + TransactionSynchronizationManager.BindResource(DbProvider, conHolder); + txObject.ConnectionHolder = conHolder; + } + + // Bind the session holder to the thread. + if (txObject.NewSessionHolder) + { + TransactionSynchronizationManager.BindResource(SessionFactory, txObject.SessionHolder); + } + + } catch (Exception ex) + { + SessionFactoryUtils.CloseSession(session); + throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex); + } + + + } + + + + + /// + /// Suspend the resources of the current transaction. + /// + /// + /// Transaction object returned by + /// . + /// + /// + /// An object that holds suspended resources (will be kept unexamined for passing it into + /// .) + /// + /// + /// Transaction synchronization will already have been suspended. + /// + /// + /// If suspending is not supported by the transaction manager implementation. + /// + /// + /// in case of system errors. + /// + protected override object DoSuspend(object transaction) + { + HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; + txObject.SetSessionHolder(null, false); + SessionHolder sessionHolder = + (SessionHolder) TransactionSynchronizationManager.UnbindResource(SessionFactory); + ConnectionHolder connectionHolder = null; + if (DbProvider != null) + { + connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.UnbindResource(DbProvider); + } + return new SuspendedResourcesHolder(sessionHolder, connectionHolder); + + } + + /// + /// Resume the resources of the current transaction. + /// + /// + /// Transaction object returned by + /// . + /// + /// + /// The object that holds suspended resources as returned by + /// . + /// + /// + /// Transaction synchronization will be resumed afterwards. + /// + /// + /// If suspending is not supported by the transaction manager implementation. + /// + /// + /// In the case of system errors. + /// + protected override void DoResume(object transaction, object suspendedResources) + { + SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources; + if (TransactionSynchronizationManager.HasResource(SessionFactory)) + { + // From non-transactional code running in active transaction synchronization + // -> can be safely removed, will be closed on transaction completion. + TransactionSynchronizationManager.UnbindResource(SessionFactory); + } + TransactionSynchronizationManager.BindResource(SessionFactory, resourcesHolder.SessionHolder); + if (DbProvider != null) + { + TransactionSynchronizationManager.BindResource(DbProvider, resourcesHolder.ConnectionHolder); + } + } + + /// + /// Perform an actual commit on the given transaction. + /// + /// The status representation of the transaction. + /// + ///

    + /// An implementation does not need to check the rollback-only flag. + ///

    + ///
    + /// + /// In the case of system errors. + /// + protected override void DoCommit(DefaultTransactionStatus status) + { + HibernateTransactionObject txObject = (HibernateTransactionObject) status.Transaction; + if (status.Debug) + { + log.Debug("Committing Hibernate transaction on Session [" + + txObject.SessionHolder.Session + "]"); + } + try + { + txObject.SessionHolder.Transaction.Commit(); + } + // Note, unfortunate collision of namespaces/classname for NHibernate.TransactionException + // and Spring.Data.NHibernate requires this wierd construct. + catch (Exception ex) + { + Type nhibTxExceptiontype = TypeResolutionUtils.ResolveType("NHibernate.TransactionException, NHibernate"); + if (ex.GetType().Equals(nhibTxExceptiontype)) + { + // assumably from commit call to the underlying ADO.NET connection + throw new TransactionSystemException("Could not commit Hibernate transaction", ex); + } + HibernateException hibEx = ex as HibernateException; + if (hibEx != null) + { + // assumably failed to flush changes to database + throw ConvertHibernateAccessException(hibEx); + } + throw; + } + } + + /// + /// Perform an actual rollback on the given transaction. + /// + /// The status representation of the transaction. + /// + /// An implementation does not need to check the new transaction flag. + /// + /// + /// In the case of system errors. + /// + protected override void DoRollback(DefaultTransactionStatus status) + { + HibernateTransactionObject txObject = (HibernateTransactionObject) status.Transaction; + if (status.Debug) + { + log.Debug("Rolling back Hibernate transaction on Session [" + + txObject.SessionHolder.Session + "]"); + } + try + { + txObject.SessionHolder.Transaction.Rollback(); + } + catch (HibernateTransactionException ex) + { + throw new TransactionSystemException("Could not roll back Hibernate transaction", ex); + } + catch (HibernateException ex) + { + // Shouldn't really happen, as a rollback doesn't cause a flush. + throw ConvertHibernateAccessException(ex); + } + finally + { + if (!txObject.NewSessionHolder) + { + // Clear all pending inserts/updates/deletes in the Session. + // Necessary for pre-bound Sessions, to avoid inconsistent state. + txObject.SessionHolder.Session.Clear(); + } + } + + + + } + + + /// + /// Set the given transaction rollback-only. Only called on rollback + /// if the current transaction takes part in an existing one. + /// + /// The status representation of the transaction. + /// + /// In the case of system errors. + /// + protected override void DoSetRollbackOnly(DefaultTransactionStatus status) + { + HibernateTransactionObject txObject = (HibernateTransactionObject) status.Transaction; + if (status.Debug) + { + log.Debug("Setting Hibernate transaction on Session [" + + txObject.SessionHolder.Session + "] rollback-only"); + } + txObject.SetRollbackOnly(); + } + + + /// + /// Gets the ADO.NET IDbTransaction object from the NHibernate ITransaction object. + /// + /// The hibernate transaction. + /// The ADO.NET transaction. Null if could not get the transaction. Warning + /// messages will be logged in that case. + protected IDbTransaction GetIDbTransaction(ITransaction hibernateTx) + { + AdoTransaction hibernateAdoTx = hibernateTx as AdoTransaction; + + IDbTransaction adoTransaction = null; + if (hibernateAdoTx != null) + { + try + { + FieldInfo fi = hibernateAdoTx.GetType().GetField("trans", BindingFlags.Instance | BindingFlags.NonPublic); + adoTransaction = fi.GetValue(hibernateAdoTx) as IDbTransaction; + } + catch (Exception e) + { + log.Warn("Could not extract IDbTransaction from Hibernate AdoTransaction using field name trans.", e); + } + } + else + { + log.Warn("Hibernate ITransaction not of expected type AdoTransaction. Could not extract IDbTransaction from Hibernate AdoTransaction."); + } + return adoTransaction; + } + + /// + /// Convert the given HibernateException to an appropriate exception from + /// the Spring.Dao hierarchy. Can be overridden in subclasses. + /// + /// The HibernateException that occured. + /// The corresponding DataAccessException instance + protected virtual DataAccessException ConvertHibernateAccessException(HibernateException ex) + { + if (AdoExceptionTranslator != null && ex is ADOException) + { + return ConvertAdoAccessException((ADOException) ex, AdoExceptionTranslator); + } + else if (ex is ADOException) + { + return ConvertAdoAccessException((ADOException)ex, DefaultAdoExceptionTranslator); + } + return SessionFactoryUtils.ConvertHibernateAccessException(ex); + } + + /// + /// Convert the given ADOException to an appropriate exception from the + /// the Spring.Dao hierarchy. Can be overridden in subclasses. + /// + /// The ADOException that occured, wrapping the underlying + /// ADO.NET thrown exception. + /// The translator to convert hibernate ADOExceptions. + /// + /// The corresponding DataAccessException instance + /// + protected virtual DataAccessException ConvertAdoAccessException(ADOException ex, IAdoExceptionTranslator translator) + { + return translator.Translate("Hibernate flusing: " + ex.Message, null, ex.InnerException); + } + + /// + /// Cleanup resources after transaction completion. + /// + /// Transaction object returned by + /// . + /// + /// + /// This implemenation unbinds the SessionFactory and + /// DbProvider from thread local storage and closes the + /// ISession. + /// + ///

    + /// Called after + /// and + /// + /// execution on any outcome. + ///

    + ///

    + /// Should not throw any exceptions but just issue warnings on errors. + ///

    + ///
    + protected override void DoCleanupAfterCompletion( object transaction ) + { + HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; + + // Remove the session holder from the thread. + if (txObject.NewSessionHolder) + { + TransactionSynchronizationManager.UnbindResource(SessionFactory); + } + // Remove the ADO.NET connection holder from the thread, if exposed. + if (DbProvider != null) + { + TransactionSynchronizationManager.UnbindResource(DbProvider); + } + /* + try + { + //TODO investigate isolation level settings... + //IDbConnection con = txObject.SessionHolder.Session.Connection; + //AdoUtils.ResetConnectionAfterTransaction(con, txObject.PreviousIsolationLevel); + } + catch (HibernateException ex) + { + log.Info("Could not access ADO.NET IDbConnection of Hibernate Session", ex); + } + */ + ISession session = txObject.SessionHolder.Session; + if (txObject.NewSessionHolder) + { + if (log.IsDebugEnabled) + { + log.Debug("Closing Hibernate Session [" + session + "] after transaction"); + } + SessionFactoryUtils.CloseSessionOrRegisterDeferredClose(session, SessionFactory); + } + else + { + if (log.IsDebugEnabled) + { + log.Debug("Not closing pre-bound Hibernate Session [" + session + "] after transaction"); + } + if (txObject.SessionHolder.AssignedPreviousFlushMode) + { + session.FlushMode = txObject.SessionHolder.PreviousFlushMode; + } + } + txObject.SessionHolder.Clear(); + + + } + + private class HibernateTransactionObject : AdoTransactionObjectSupport + { + + private SessionHolder sessionHolder; + + private bool newSessionHolder; + + + public void SetSessionHolder(SessionHolder sessionHolder, bool newSessionHolder) + { + this.sessionHolder = sessionHolder; + this.newSessionHolder = newSessionHolder; + } + + + public SessionHolder SessionHolder + { + get + { + return sessionHolder; + } + } + + public bool NewSessionHolder + { + get + { + return newSessionHolder; + } + } + + public bool HasTransaction() + { + return (this.sessionHolder != null && this.sessionHolder.Transaction != null); + } + + public void SetRollbackOnly() + { + SessionHolder.RollbackOnly = true; + if (ConnectionHolder != null) + { + ConnectionHolder.RollbackOnly = true; + } + } + + /// + /// Return whether the transaction is internally marked as rollback-only. + /// + /// + /// True of the transaction is marked as rollback-only. + public override bool RollbackOnly + { + get + { + return SessionHolder.RollbackOnly || + (ConnectionHolder != null && ConnectionHolder.RollbackOnly); + } + } + } + + private class SuspendedResourcesHolder + { + + private readonly SessionHolder sessionHolder; + + private readonly ConnectionHolder connectionHolder; + + public SuspendedResourcesHolder(SessionHolder sessionHolder, ConnectionHolder conHolder) + { + this.sessionHolder = sessionHolder; + this.connectionHolder = conHolder; + } + + public SessionHolder SessionHolder + { + get + { + return sessionHolder; + } + + } + + public ConnectionHolder ConnectionHolder + { + get + { + return connectionHolder; + } + + } + } + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public void AfterPropertiesSet() + { + if (SessionFactory == null) { + throw new ArgumentException("sessionFactory is required"); + } + if (this.entityInterceptor is string && this.objectFactory == null) { + throw new ArgumentException ("objectFactory is required for entityInterceptorBeanName"); + } + + // Try to derive a DbProvider given the SessionFactory. + if (this.autodetectDbProvider && DbProvider == null) { + IDbProvider sfDbProvider = SessionFactoryUtils.GetDbProvider(SessionFactory); + if (sfDbProvider != null) { + // Use the SessionFactory's DataSource for exposing transactions to ADO.NET code. + if (log.IsInfoEnabled) { + log.Info("Derived DbProvider [" + sfDbProvider.DbMetadata.ProductName + + "] of Hibernate SessionFactory for HibernateTransactionManager"); + } + DbProvider = sfDbProvider; + } + else + { + log.Info("Could not auto detect DbProvider from SessionFactory configuration"); + } + + } + } + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/ICommonHibernateOperations.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/ICommonHibernateOperations.cs new file mode 100644 index 00000000..0284b187 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/ICommonHibernateOperations.cs @@ -0,0 +1,245 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NHibernate; +using NHibernate.Type; +using Spring.Dao; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Interface that specifies a set of Hibernate operations that + /// are common across versions of Hibernate. + /// + /// + /// Base interface for generic and non generic IHibernateOperations interfaces + /// Not often used, but a useful option + /// to enhance testability, as it can easily be mocked or stubbed. + ///

    Provides HibernateTemplate's data access methods that mirror + /// various Session methods. See the NHibernate ISession documentation + /// for details on those methods. + ///

    + ///
    + /// Mark Pollack (.NET) + /// + /// $Id: ICommonHibernateOperations.cs,v 1.2 2007/09/19 22:58:22 markpollack Exp $ + public interface ICommonHibernateOperations + { + /// + /// Remove all objects from the Session cache, and cancel all pending saves, + /// updates and deletes. + /// + /// In case of Hibernate errors + void Clear(); + + + /// + /// Delete the given persistent instance. + /// + /// The persistent instance to delete. + /// In case of Hibernate errors + void Delete(object entity); + + /// + /// Delete the given persistent instance. + /// + /// + /// Obtains the specified lock mode if the instance exists, implicitly + /// checking whether the corresponding database entry still exists + /// (throwing an OptimisticLockingFailureException if not found). + /// + /// Tthe persistent instance to delete. + /// The lock mode to obtain. + /// In case of Hibernate errors + void Delete(object entity, LockMode lockMode); + + + /// + /// Delete all objects returned by the query. + /// + /// a query expressed in Hibernate's query language. + /// The number of entity instances deleted. + /// In case of Hibernate errors + int Delete(string queryString); + + /// + /// Delete all objects returned by the query. + /// + /// a query expressed in Hibernate's query language. + /// The value of the parameter. + /// The Hibernate type of the parameter (or null). + /// The number of entity instances deleted. + /// In case of Hibernate errors + int Delete(string queryString, object value, IType type); + + + /// + /// Delete all objects returned by the query. + /// + /// a query expressed in Hibernate's query language. + /// The values of the parameters. + /// Hibernate types of the parameters (or null) + /// The number of entity instances deleted. + /// In case of Hibernate errors + int Delete(String queryString, Object[] values, IType[] types); + + + /// + /// Flush all pending saves, updates and deletes to the database. + /// + /// + /// Only invoke this for selective eager flushing, for example when ADO.NET code + /// needs to see certain changes within the same transaction. Else, it's preferable + /// to rely on auto-flushing at transaction completion. + /// + /// In case of Hibernate errors + void Flush(); + + #region Convenience methods for loading individual objects + + /// + /// Load the persistent instance with the given identifier + /// into the given object, throwing an exception if not found. + /// + /// Entity the object (of the target class) to load into. + /// An identifier of the persistent instance. + /// If object not found. + /// In case of Hibernate errors + void Load(object entity, object id); + + + /// + /// Re-read the state of the given persistent instance. + /// + /// The persistent instance to re-read. + /// In case of Hibernate errors + void Refresh(object entity); + + /// + /// Re-read the state of the given persistent instance. + /// Obtains the specified lock mode for the instance. + /// + /// The persistent instance to re-read. + /// The lock mode to obtain. + /// In case of Hibernate errors + void Refresh(object entity, LockMode lockMode); + + /// + /// Determines whether the given object is in the Session cache. + /// + /// the persistence instance to check. + /// + /// true if session cache contains the specified entity; otherwise, false. + /// + /// In case of Hibernate errors + bool Contains(object entity); + + /// + /// Remove the given object from the Session cache. + /// + /// The persistent instance to evict. + /// In case of Hibernate errors + void Evict(object entity); + + #endregion + + #region Convenience methods for storing individual objects + + + /// + /// Obtain the specified lock level upon the given object, implicitly + /// checking whether the corresponding database entry still exists + /// (throwing an OptimisticLockingFailureException if not found). + /// + /// The he persistent instance to lock. + /// The lock mode to obtain. + /// If not found + /// In case of Hibernate errors + void Lock(object entity, LockMode lockMode); + + + /// + /// Persist the given transient instance. + /// + /// The transient instance to persist. + /// The generated identifier. + /// In case of Hibernate errors + object Save(object entity); + + /// + /// Persist the given transient instance with the given identifier. + /// + /// The transient instance to persist. + /// The identifier to assign. + /// In case of Hibernate errors + void Save(object entity, object id); + + /// + /// Update the given persistent instance. + /// + /// The persistent instance to update. + /// In case of Hibernate errors + void Update(object entity); + + /// + /// Update the given persistent instance. + /// Obtains the specified lock mode if the instance exists, implicitly + /// checking whether the corresponding database entry still exists + /// (throwing an OptimisticLockingFailureException if not found). + /// + /// The persistent instance to update. + /// The lock mode to obtain. + /// In case of Hibernate errors + void Update(object entity, LockMode lockMode); + + /// + /// Save or update the given persistent instance, + /// according to its id (matching the configured "unsaved-value"?). + /// + /// Tthe persistent instance to save or update + /// (to be associated with the Hibernate Session). + /// In case of Hibernate errors + void SaveOrUpdate(object entity); + + /// + /// Save or update the contents of given persistent object, + /// according to its id (matching the configured "unsaved-value"?). + /// Will copy the contained fields to an already loaded instance + /// with the same id, if appropriate. + /// + /// The persistent object to save or update. + /// (not necessarily to be associated with the Hibernate Session) + /// + /// The actually associated persistent object. + /// (either an already loaded instance with the same id, or the given object) + /// In case of Hibernate errors + object SaveOrUpdateCopy(object entity); + + + #endregion + + + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/IHibernateCallback.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/IHibernateCallback.cs new file mode 100644 index 00000000..fcc87154 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/IHibernateCallback.cs @@ -0,0 +1,57 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NHibernate; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Callback interface for NHibernate code. + /// + /// To be used with HibernateTemplate execute + /// method. The typical implementation will call + /// Session.load/find/save/update to perform + /// some operations on persistent objects. + /// + /// Mark Pollack (.NET) + /// $Id: IHibernateCallback.cs,v 1.2 2007/09/19 22:58:22 markpollack Exp $ + public interface IHibernateCallback + { + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + /// A result object, or null if none. + object DoInHibernate(ISession session); + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/IHibernateOperations.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/IHibernateOperations.cs new file mode 100644 index 00000000..231ec17d --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/IHibernateOperations.cs @@ -0,0 +1,438 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NHibernate; +using NHibernate.Type; +using Spring.Dao; +using Spring.Data.NHibernate; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Interface that specifies a basic set of Hibernate operations. + /// + /// + /// Implemented by HibernateTemplate. Not often used, but a useful option + /// to enhance testability, as it can easily be mocked or stubbed. + ///

    Provides HibernateTemplate's data access methods that mirror + /// various Session methods. See the NHibernate ISession documentation + /// for details on those methods. + ///

    + ///
    + /// + /// Mark Pollack (.NET) + /// $Id: IHibernateOperations.cs,v 1.2 2007/09/19 22:58:22 markpollack Exp $ + public interface IHibernateOperations : ICommonHibernateOperations + { + + /// + /// Delete all given persistent instances. + /// + /// The persistent instances to delete. + /// + /// This can be combined with any of the find methods to delete by query + /// in two lines of code, similar to Session's delete by query methods. + /// + /// In case of Hibernate errors + void DeleteAll(ICollection entities); + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning a result object, i.e. a domain + /// object or a collection of domain objects. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + /// The delegate callback object that specifies the Hibernate action. + /// a result object returned by the action, or null + /// + /// In case of Hibernate errors + object Execute(HibernateDelegate del); + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning a result object, i.e. a domain + /// object or a collection of domain objects. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + /// The callback object that specifies the Hibernate action. + /// a result object returned by the action, or null + /// + /// In case of Hibernate errors + object Execute(IHibernateCallback action); + + + /// + /// Execute the specified action assuming that the result object is a List. + /// + /// + /// This is a convenience method for executing Hibernate find calls or + /// queries within an action. + /// + /// The calback object that specifies the Hibernate action. + /// A IList returned by the action, or null + /// + /// In case of Hibernate errors + IList ExecuteFind(IHibernateCallback action); + + #region Finder Methods + /// + /// Execute a query for persistent instances. + /// + /// a query expressed in Hibernate's query language + /// a List containing 0 or more persistent instances + /// In case of Hibernate errors + IList Find(string queryString); + + + /// + /// Execute a query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// + /// a query expressed in Hibernate's query language + /// the value of the parameter + /// a List containing 0 or more persistent instances + /// In case of Hibernate errors + IList Find(string queryString, object value); + + /// + /// Execute a query for persistent instances, binding one value + /// to a "?" parameter of the given type in the query string. + /// + /// a query expressed in Hibernate's query language + /// The value of the parameter. + /// Hibernate type of the parameter (or null) + /// a List containing 0 or more persistent instances + /// In case of Hibernate errors + IList Find(string queryString, object value, IType type); + + + /// + /// Execute a query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// + /// a query expressed in Hibernate's query language + /// the values of the parameters + /// a List containing 0 or more persistent instances + /// In case of Hibernate errors + IList Find(string queryString, object[] values); + + + + /// + /// Execute a query for persistent instances, binding a number of + /// values to "?" parameters of the given types in the query string. + /// + /// A query expressed in Hibernate's query language + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// a List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If values and types are not null and their lengths are not equal + IList Find(string queryString, object[] values, IType[] types); + + + /// + /// Execute a query for persistent instances, binding + /// one value to a named parameter in the query string. + /// + /// The name of a Hibernate query in a mapping file + /// The name of the parameter + /// The value of the parameter + /// a List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedParam(string queryName, string paramName, object value); + + /// + /// Execute a query for persistent instances, binding + /// one value to a named parameter in the query string. + /// + /// The name of a Hibernate query in a mapping file + /// The name of the parameter + /// The value of the parameter + /// Hibernate type of the parameter (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedParam(string queryName, string paramName, object value, IType type); + + /// + /// Execute a query for persistent instances, binding a + /// number of values to named parameters in the query string. + /// + /// A query expressed in Hibernate's query language + /// The names of the parameters + /// The values of the parameters + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedParam(string queryString, string[] paramNames, object[] values); + + + /// + /// Execute a query for persistent instances, binding a + /// number of values to named parameters in the query string. + /// + /// A query expressed in Hibernate's query language + /// The names of the parameters + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If paramNames length is not equal to values length or + /// if paramNames length is not equal to types length (when types is not null) + IList FindByNamedParam(string queryString, string[] paramNames, object[] values, IType[] types); + + /// + /// Execute a named query for persistent instances. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQuery(string queryName); + + /// + /// Execute a named query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The value of the parameter + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQuery(string queryName, object value); + + /// + /// Execute a named query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The value of the parameter + /// Hibernate type of the parameter (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQuery(string queryName, object value, IType type); + + + /// + /// Execute a named query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQuery(string queryName, object[] values); + + + /// + /// Execute a named query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If values and types are not null and their lengths differ. + IList FindByNamedQuery(string queryName, object[] values, IType[] types); + + /// + /// Execute a named query for persistent instances, binding + /// one value to a named parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// Name of the parameter + /// The value of the parameter + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQueryAndNamedParam(string queryName, string paramName, object value); + + /// + /// Execute a named query for persistent instances, binding + /// one value to a named parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// Name of the parameter + /// The value of the parameter + /// The Hibernate type of the parameter (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQueryAndNamedParam(string queryName, string paramName, object value, IType type); + + /// + /// Execute a named query for persistent instances, binding + /// number of values to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The names of the parameters + /// The values of the parameters. + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQueryAndNamedParam(string queryName, string[] paramNames, object[] values); + + + /// + /// Execute a named query for persistent instances, binding + /// number of values to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The names of the parameters + /// The values of the parameters. + /// Hibernate types of the parameters (or null) + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If paramNames length is not equal to values length or + /// if paramNames length is not equal to types length (when types is not null) + IList FindByNamedQueryAndNamedParam(string queryName, string[] paramNames, object[] values, IType[] types); + + + /// + /// Execute a named query for persistent instances, binding the properties + /// of the given object to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQueryAndValueObject(string queryName, object valueObject); + + /// + /// Execute a query for persistent instances, binding the properties + /// of the given object to named parameters in the query string. + /// + /// A query expressed in Hibernate's query language + /// The values of the parameters + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByValueObject(string queryString, object valueObject); + + #endregion + + #region Convenience methods for loading individual objects + + /// + /// Return the persistent instance of the given entity type + /// with the given identifier, or null if not found. + /// + /// a persistent type. + /// An identifier of the persistent instance. + /// the persistent instance, or null if not found + /// In case of Hibernate errors + object Get(Type entityType, object id); + + /// + /// Return the persistent instance of the given entity type + /// with the given identifier, or null if not found. + /// Obtains the specified lock mode if the instance exists. + /// + /// A persistent class. + /// An identifier of the persistent instance. + /// The lock mode. + /// the persistent instance, or null if not found + /// the persistent instance, or null if not found + /// In case of Hibernate errors + object Get(Type entityType, object id, LockMode lockMode); + + /// + /// Return the persistent instance of the given entity class + /// with the given identifier, throwing an exception if not found. + /// + /// Type of the entity. + /// An identifier of the persistent instance. + /// The persistent instance + /// If not found + /// In case of Hibernate errors + object Load(Type entityType, object id); + + /// + /// Return the persistent instance of the given entity class + /// with the given identifier, throwing an exception if not found. + /// Obtains the specified lock mode if the instance exists. + /// + /// Type of the entity. + /// An identifier of the persistent instance. + /// The lock mode. + /// The persistent instance + /// If not found + /// In case of Hibernate errors + object Load(Type entityType, object id, LockMode lockMode); + + + + /// + /// Return all persistent instances of the given entity class. + /// Note: Use queries or criteria for retrieving a specific subset. + /// + /// Type of the entity. + /// A List containing 0 or more persistent instances + /// In case of Hibernate errors + IList LoadAll(Type entityType); + + + + + + #endregion + + #region Convenience methods for storing individual objects + + /// + /// Save or update all given persistent instances, + /// according to its id (matching the configured "unsaved-value"?). + /// + /// Tthe persistent instances to save or update + /// (to be associated with the Hibernate Session)he entities. + /// In case of Hibernate errors + void SaveOrUpdateAll(ICollection entities); + + + #endregion + + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/LocalSessionFactoryObject.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/LocalSessionFactoryObject.cs new file mode 100644 index 00000000..78c62118 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/LocalSessionFactoryObject.cs @@ -0,0 +1,391 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Data; +using Common.Logging; +using NHibernate; +using NHibernate.Cfg; +using NHibernate.Connection; +using Spring.Core.IO; +using Spring.Data.Common; +using Spring.Objects.Factory; +using Environment = NHibernate.Cfg.Environment; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// An IFactoryObject that creates a local Hibernate SessionFactory instance. + /// Behaves like a SessionFactory instance when used as bean reference, + /// e.g. for HibernateTemplate's "SessionFactory" property. + /// + /// + /// The typical usage will be to register this as singleton factory + /// in an application context and give objects references to application services + /// that need it. + /// + /// Hibernate configuration settings can be set using the IDictionary property 'HibernateProperties'. + /// + /// + /// + /// Mark Pollack (.NET) + /// $Id: LocalSessionFactoryObject.cs,v 1.7 2008/03/21 14:12:16 markpollack Exp $ + public class LocalSessionFactoryObject : IFactoryObject, IInitializingObject, System.IDisposable + { + #region Fields + + private Configuration configuration; + + private ISessionFactory sessionFactory; + + private string[] mappingAssemblies; + + private string[] mappingResources; + + private string[] configFilenames; + + /// + /// TODO: consider changing to NamevalueCollection for easier + /// cut-n-paste from existing App.config based configurations. + /// + private IDictionary hibernateProperties; + + private IDbProvider dbProvider; + + #endregion + + #region Constants + + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (LocalSessionFactoryObject)); + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public LocalSessionFactoryObject() + { + + } + + #endregion + + #region Properties + + /// + /// Sets the assemblies to load that contain mapping files. + /// + /// The mapping assemblies. + public string[] MappingAssemblies + { + set { mappingAssemblies = value; } + } + + /// + /// Sets the hibernate configuration files to load, i.e. hibernate.cfg.xml. + /// + public string[] ConfigFilenames + { + set { configFilenames = value; } + } + + /// + /// Sets the locations of Spring IResources that contain mapping + /// files. + /// + /// The location of mapping resources. + public string[] MappingResources + { + set { mappingResources = value;} + } + + /// + /// Return the Configuration object used to build the SessionFactory. + /// Allows access to configuration metadata stored there (rarely needed). + /// + /// The hibernate configuration. + public Configuration Configuration + { + get { return configuration; } + } + + /// + /// Set NHibernate configuration properties, like "hibernate.dialect". + /// + /// The hibernate properties. + /// + ///

    Can be used to override values in a NHibernate XML config file, + /// or to specify all necessary properties locally. + ///

    + ///

    Note: Do not specify a transaction provider here when using + /// Spring-driven transactions. It is also advisable to omit connection + /// provider settings and use a Spring-set IDbProvider instead. + ///

    + ///
    + public IDictionary HibernateProperties + { + get + { + if (hibernateProperties == null) + { + hibernateProperties = new Hashtable(); + } + return hibernateProperties; + } + set + { + hibernateProperties = value; + } + } + + /// + /// Get or set the DataSource to be used by the SessionFactory. + /// + /// The db provider. + /// + /// If set, this will override corresponding settings in Hibernate properties. + /// Note: If this is set, the Hibernate settings should not define + /// a connection string + /// (hibernate.connection.connection_string) to avoid meaningless double configuration. + /// + /// + public IDbProvider DbProvider + { + set { dbProvider = value; } + get { return dbProvider; } + } + + + #endregion + + #region Methods + + #endregion + + /// + /// Return the singleon session factory. + /// + public object GetObject() + { + return sessionFactory; + } + + /// + /// Return the type or subclass. + /// + /// The type created by this factory + public System.Type ObjectType + { + get + { + return (sessionFactory != null) ? sessionFactory.GetType() : typeof(ISessionFactory); + } + } + + /// + /// Returns true + /// + /// true + public bool IsSingleton + { + get + { + return true; + } + } + + /// + /// Initialize the SessionFactory for the given or the + /// default location. + /// + public void AfterPropertiesSet() + { + // Create Configuration instance. + Configuration config = NewConfiguration(); + + + if (this.dbProvider != null) + { + config.SetProperty(Environment.ConnectionString, dbProvider.ConnectionString); + config.SetProperty(Environment.ConnectionProvider, typeof(DbProviderWrapper).AssemblyQualifiedName); + } + + if (this.hibernateProperties != null) + { + if (config.GetProperty(Environment.ConnectionProvider) != null && + hibernateProperties.Contains(Environment.ConnectionProvider)) + { + #region Logging + if (log.IsWarnEnabled) + { + log.Warn("Overriding use of Spring's Hibernate Connection Provider with [" + + hibernateProperties[Environment.ConnectionProvider] + "]"); + } + #endregion + config.Properties.Remove(Environment.ConnectionProvider); + } + config.AddProperties(hibernateProperties); + } + if (this.mappingAssemblies != null) + { + foreach (string assemblyName in mappingAssemblies) + { + config.AddAssembly(assemblyName); + } + } + + IResourceLoader resourceLoader = new ConfigurableResourceLoader(); + + if (this.mappingResources != null) + { + foreach (string resourceName in mappingResources) + { + config.AddInputStream(resourceLoader.GetResource(resourceName).InputStream); + } + } + + if (configFilenames != null) + { + foreach (string configFilename in configFilenames) + { + config.Configure(configFilename); + } + } + + // Perform custom post-processing in subclasses. + PostProcessConfiguration(config); + + // Build SessionFactory instance. + log.Info("Building new Hibernate SessionFactory"); + this.configuration = config; + this.sessionFactory = NewSessionFactory(config); + + + } + + /// + /// Close the SessionFactory on application context shutdown. + /// + public void Dispose() + { + if (sessionFactory != null) + { + #region Instrumentation + if (log.IsInfoEnabled) + { + log.Info("Closing Hibernate SessionFactory"); + } + #endregion + sessionFactory.Close(); + } + } + + /// + /// Subclasses can override this method to perform custom initialization + /// of the Configuration instance used for ISessionFactory creation. + /// + /// + /// The properties of this LocalSessionFactoryObject will be applied to + /// the Configuration object that gets returned here. + ///

    The default implementation creates a new Configuration instance. + /// A custom implementation could prepare the instance in a specific way, + /// or use a custom Configuration subclass. + ///

    + ///
    + protected virtual Configuration NewConfiguration() + { + return new Configuration(); + } + + /// + /// To be implemented by subclasses that want to to perform custom + /// post-processing of the Configuration object after this FactoryObject + /// performed its default initialization. + /// + /// The current configuration object. + protected virtual void PostProcessConfiguration(Configuration config) + { + } + + /// + /// Subclasses can override this method to perform custom initialization + /// of the SessionFactory instance, creating it via the given Configuration + /// object that got prepared by this LocalSessionFactoryObject. + /// + /// + ///

    The default implementation invokes Configuration's BuildSessionFactory. + /// A custom implementation could prepare the instance in a specific way, + /// or use a custom ISessionFactory subclass. + ///

    + ///
    + protected virtual ISessionFactory NewSessionFactory(Configuration config) + { + ISessionFactory sf = config.BuildSessionFactory(); + DbProviderWrapper dbProviderWrapper = sf.ConnectionProvider as DbProviderWrapper; + if (dbProviderWrapper != null) + { + dbProviderWrapper.DbProvider = dbProvider; + } + return sf; + } + + #region DbProviderWrapper Helper class + + internal class DbProviderWrapper : ConnectionProvider + { + private IDbProvider _dbProvider; + + public DbProviderWrapper() + { + } + + public IDbProvider DbProvider + { + get { return _dbProvider; } + set { _dbProvider = value; } + } + + public override void CloseConnection(IDbConnection conn) + { + base.CloseConnection(conn); + conn.Dispose(); + } + + public override IDbConnection GetConnection() + { + IDbConnection dbCon = _dbProvider.CreateConnection(); + dbCon.Open(); + return dbCon; + } + } + + #endregion // DbProviderWrapper Helper class + + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/SessionFactoryUtils.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/SessionFactoryUtils.cs new file mode 100644 index 00000000..343347a4 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/SessionFactoryUtils.cs @@ -0,0 +1,713 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Common.Logging; +using NHibernate; +using NHibernate.Connection; +using NHibernate.Driver; +using NHibernate.Engine; +using Spring.Collections; +using Spring.Context; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Data.Support; +using Spring.Objects.Factory.Config; +using Spring.Threading; +using Spring.Transaction.Support; +using Spring.Util; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Helper class featuring methods for Hibernate Session handling, + /// allowing for reuse of Hibernate Session instances within transactions. + /// Also provides support for exception translation. + /// + /// Mark Pollack (.NET) + /// $Id: SessionFactoryUtils.cs,v 1.5 2008/04/08 20:26:37 markpollack Exp $ + public abstract class SessionFactoryUtils + { + #region Fields + + /// + /// The instance for this class. + /// + private static readonly ILog log = LogManager.GetLogger(typeof(SessionFactoryUtils)); + + #endregion + + #region Constants + + /// + /// The ordering value for synchronizaiton this session resources. + /// Set to be lower than ADO.NET synchronization. + /// + public static readonly int SESSION_SYNCHRONIZATION_ORDER = + AdoUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100; + + private static readonly string DeferredCloseHolderDataSlotName = "Spring.Data.NHibernate:deferredCloseHolder"; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public SessionFactoryUtils() + { + + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + /// + /// Get a new Hibernate Session from the given SessionFactory. + /// Will return a new Session even if there already is a pre-bound + /// Session for the given SessionFactory. + /// + /// + /// Within a transaction, this method will create a new Session + /// that shares the transaction's ADO.NET Connection. More specifically, + /// it will use the same ADO.NET Connection as the pre-bound Hibernate Session. + /// + /// The session factory to create the session with. + /// The Hibernate entity interceptor, or null if none. + /// The new session. + /// If could not open Hibernate session + public static ISession GetNewSession(ISessionFactory sessionFactory, IInterceptor interceptor) + { + try + { + SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory); + if (sessionHolder != null && !sessionHolder.IsEmpty) + { + if (interceptor != null) + { + return sessionFactory.OpenSession(sessionHolder.AnySession.Connection, interceptor); + } + else + { + return sessionFactory.OpenSession(sessionHolder.AnySession.Connection); + } + } + else + { + if (interceptor != null) + { + return sessionFactory.OpenSession(interceptor); + } + else + { + return sessionFactory.OpenSession(); + } + } + } + catch (HibernateException ex) + { + throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex); + } + } + + /// + /// Get a Hibernate Session for the given SessionFactory. Is aware of and will + /// return any existing corresponding Session bound to the current thread, for + /// example when using HibernateTransactionManager. Will always create a new + /// Session otherwise. + /// + /// + /// Supports setting a Session-level Hibernate entity interceptor that allows + /// to inspect and change property values before writing to and reading from the + /// database. Such an interceptor can also be set at the SessionFactory level + /// (i.e. on LocalSessionFactoryObject), on HibernateTransactionManager, or on + /// HibernateInterceptor/HibernateTemplate. + /// + /// The session factory to create the + /// session with. + /// Hibernate entity interceptor, or null if none. + /// AdoExceptionTranslator to use for flushing the + /// Session on transaction synchronization (can be null; only used when actually + /// registering a transaction synchronization). + /// The Hibernate Session + /// + /// If the session couldn't be created. + /// + /// + /// If no thread-bound Session found and allowCreate is false. + /// + public static ISession GetSession( + ISessionFactory sessionFactory, IInterceptor entityInterceptor, + IAdoExceptionTranslator adoExceptionTranslator) + { + try + { + return GetSession(sessionFactory, entityInterceptor, adoExceptionTranslator, true); + } + catch (HibernateException ex) + { + throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex); + } + } + + /// + /// Get a Hibernate Session for the given SessionFactory. Is aware of and will + /// return any existing corresponding Session bound to the current thread, for + /// example when using . Will create a new Session + /// otherwise, if allowCreate is true. + /// + /// The session factory to create the session with. + /// if set to true create a non-transactional Session when no + /// transactional Session can be found for the current thread. + /// The hibernate session + /// + /// If the session couldn't be created. + /// + /// + /// If no thread-bound Session found and allowCreate is false. + /// + public static ISession GetSession(ISessionFactory sessionFactory, bool allowCreate) + { + try + { + return GetSession(sessionFactory, null, null, allowCreate); + } + catch (HibernateException ex) + { + throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex); + } + } + + /// + /// Get a Hibernate Session for the given SessionFactory. + /// + /// Is aware of and will return any existing corresponding + /// Session bound to the current thread, for example whenusing + /// . Will create a new + /// Session otherwise, if "allowCreate" is true. + ///

    Throws the orginal HibernateException, in contrast to + /// . + ///

    + /// The session factory. + /// if set to true [allow create]. + /// The Hibernate Session + /// + /// if the Session couldn't be created + /// + /// + /// If no thread-bound Session found and allowCreate is false. + /// + public static ISession DoGetSession(ISessionFactory sessionFactory, bool allowCreate) + { + return DoGetSession(sessionFactory, null, null, allowCreate); + } + + private static ISession GetSession( + ISessionFactory sessionFactory, IInterceptor entityInterceptor, + IAdoExceptionTranslator adoExceptionTranslator, bool allowCreate) + { + try + { + return DoGetSession(sessionFactory, entityInterceptor, adoExceptionTranslator, allowCreate); + } + catch (HibernateException ex) + { + throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex); + } + } + + + private static ISession DoGetSession( + ISessionFactory sessionFactory, IInterceptor entityInterceptor, + IAdoExceptionTranslator adoExceptionTranslator, bool allowCreate) + { + AssertUtils.ArgumentNotNull(sessionFactory, "sessionFactory", "SessionFactory can not be null"); + + SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory); + if (sessionHolder != null && !sessionHolder.IsEmpty) + { + // pre-bound Hibernate Session + ISession session = null; + if (TransactionSynchronizationManager.SynchronizationActive && + sessionHolder.DoesNotHoldNonDefaultSession) + { + // Spring transaction management is active -> + // register pre-bound Session with it for transactional flushing. + session = sessionHolder.ValidatedSession; + if (!sessionHolder.SynchronizedWithTransaction) + { + log.Debug("Registering Spring transaction synchronization for existing Hibernate Session"); + TransactionSynchronizationManager.RegisterSynchronization( + new SpringSessionSynchronization(sessionHolder, sessionFactory, adoExceptionTranslator, false)); + sessionHolder.SynchronizedWithTransaction = true; + // Switch to FlushMode.AUTO if we're not within a read-only transaction. + FlushMode flushMode = session.FlushMode; + if (FlushMode.Never == flushMode && + !TransactionSynchronizationManager.CurrentTransactionReadOnly) + { + session.FlushMode = FlushMode.Auto; + sessionHolder.PreviousFlushMode = flushMode; + } + } + } + else + { + // No Spring transaction management active -> simply return default thread-bound Session, if any + // (possibly from OpenSessionInViewModule) + session = sessionHolder.ValidatedSession; + } + + if (session != null) + { + return session; + } + + } + + + ISession sess = OpenSession(sessionFactory, entityInterceptor); + // Set Session to FlushMode.Never if we're within a read-only transaction. + // Use same Session for further Hibernate actions within the transaction. + // Thread object will get removed by synchronization at transaction completion. + if (TransactionSynchronizationManager.SynchronizationActive) + { + log.Debug("Registering Spring transaction synchronization for new Hibernate Session"); + SessionHolder holderToUse = sessionHolder; + if (holderToUse == null) + { + holderToUse = new SessionHolder(sess); + } + else + { + holderToUse.AddSession(sess); + } + if (TransactionSynchronizationManager.CurrentTransactionReadOnly) + { + sess.FlushMode = FlushMode.Never; + } + TransactionSynchronizationManager.RegisterSynchronization( + new SpringSessionSynchronization(holderToUse, sessionFactory, adoExceptionTranslator, true)); + holderToUse.SynchronizedWithTransaction = true; + if (holderToUse != sessionHolder) + { + TransactionSynchronizationManager.BindResource(sessionFactory, holderToUse); + } + } + + + + // Check whether we are allowed to return the Session. + if (!allowCreate && !IsSessionTransactional(sess, sessionFactory)) + { + CloseSession(sess); + throw new InvalidOperationException ("No Hibernate Session bound to thread, " + + "and configuration does not allow creation of non-transactional one here"); + } + + return sess; + + + } + + /// + /// Open a new Session from the factory. + /// + /// The session factory to create the session with. + /// Hibernate entity interceptor, or null if none. + /// the newly opened session + internal static ISession OpenSession(ISessionFactory sessionFactory, IInterceptor entityInterceptor) + { + log.Debug("Opening Hibernate Session"); + ISession session = ( + (entityInterceptor != null) + ? sessionFactory.OpenSession(entityInterceptor) + : sessionFactory.OpenSession() + ); + + return session; + } + + /// + /// Perform the actual closing of the Hibernate Session + /// catching and logging any cleanup exceptions thrown. + /// + /// The hibernate session to close + public static void CloseSession(ISession session) + { + if (session != null) + { + log.Debug("Closing Hibernate Session"); + try + { + session.Close(); + } + catch (HibernateException ex) + { + log.Error("Could not close Hibernate Session", ex); + } + catch (Exception ex) + { + log.Error("Unexpected exception on closing Hibernate Session", ex); + } + } + } + + /// + /// Return whether the given Hibernate Session is transactional, that is, + /// bound to the current thread by Spring's transaction facilities. + /// + /// The hibernate session to check + /// The session factory that the session + /// was created with, can be null. + /// + /// true if the session transactional; otherwise, false. + /// + public static bool IsSessionTransactional(ISession session, ISessionFactory sessionFactory) + { + if (sessionFactory == null) + { + return false; + } + SessionHolder sessionHolder = + (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory); + return (sessionHolder != null && sessionHolder.ContainsSession(session)); + } + + #endregion + + /// + /// Convert the given HibernateException to an appropriate exception from the + /// Spring.Dao hierarchy. Note that it is advisable to + /// handle AdoException specifically by using a AdoExceptionTranslator for the + /// underlying ADO.NET exception. + /// + /// The Hibernate exception that occured. + /// DataAccessException instance + public static DataAccessException ConvertHibernateAccessException(HibernateException ex) + { + if (ex is ADOException) + { + // ADOException during Hibernate access: only passed in here from custom code, + // as HibernateTemplate etc will use AdoExceptionTranslator-based handling. + return new HibernateAdoException("Ado Exception", (ADOException) ex); + } + if (ex is UnresolvableObjectException) + { + return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex); + } + if (ex is ObjectDeletedException) + { + return new InvalidDataAccessApiUsageException(ex.Message, ex); + } + if (ex is WrongClassException) + { + return new HibernateObjectRetrievalFailureException((WrongClassException) ex); + } + if (ex is StaleObjectStateException) + { + return new HibernateOptimisticLockingFailureException((StaleObjectStateException) ex); + } + if (ex is QueryException) + { + return new HibernateQueryException((QueryException) ex); + } + if (ex is PersistentObjectException) + { + return new InvalidDataAccessApiUsageException(ex.Message, ex); + } + if (ex is TransientObjectException) + { + return new InvalidDataAccessApiUsageException(ex.Message, ex); + } + + if (ex is PropertyValueException) + { + return new DataIntegrityViolationException(ex.Message, ex); + } + if (ex is PersistentObjectException) + { + return new InvalidDataAccessApiUsageException(ex.Message, ex); + } + if (ex is NonUniqueResultException) + { + return new IncorrectResultSizeDataAccessException(ex.Message, 1); + } + // fallback + return new HibernateSystemException(ex); + } + + /// + /// Close the given Session, created via the given factory, + /// if it is not managed externally (i.e. not bound to the thread). + /// + /// The hibernate session to close + /// The hibernate SessionFactory that + /// the session was created with. + public static void ReleaseSession(ISession session, ISessionFactory sessionFactory) + { + if (session == null) + { + return; + } + // Only close non-transactional Sessions. + if (!IsSessionTransactional(session, sessionFactory)) + { + CloseSessionOrRegisterDeferredClose(session, sessionFactory); + } + } + + /// + /// Close the given Session or register it for deferred close. + /// + /// The session. + /// The session factory. + internal static void CloseSessionOrRegisterDeferredClose(ISession session, ISessionFactory sessionFactory) + { + IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary; + + if (holderDictionary != null && sessionFactory != null && holderDictionary.Contains(sessionFactory)) + { + log.Debug("Registering Hibernate Session for deferred close"); + // Switch Session to FlushMode.NEVER for remaining lifetime. + session.FlushMode = FlushMode.Never; + Set sessions = (Set) holderDictionary[sessionFactory]; + sessions.Add(session); + } + else + { + CloseSession(session); + } + } + + /// + ///Initialize deferred close for the current thread and the given SessionFactory. + /// Sessions will not be actually closed on close calls then, but rather at a + /// processDeferredClose call at a finishing point (like request completion). + /// + /// The session factory. + public static void InitDeferredClose(ISessionFactory sessionFactory) + { + AssertUtils.ArgumentNotNull(sessionFactory, "No SessionFactory specified"); + + log.Debug("Initializing deferred close of Hibernate Sessions"); + + IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary; + + if (holderDictionary == null) + { + holderDictionary = new Hashtable(); + LogicalThreadContext.SetData(DeferredCloseHolderDataSlotName, holderDictionary); + } + holderDictionary.Add(sessionFactory, new ListSet()); + } + + /// + /// Return if deferred close is active for the current thread + /// and the given SessionFactory. + /// The session factory. + /// + /// true if [is deferred close active] [the specified session factory]; otherwise, false. + /// + /// If SessionFactory argument is null. + public static bool IsDeferredCloseActive(ISessionFactory sessionFactory) + { + if (sessionFactory == null) + { + throw new ArgumentNullException("sessionFactory", "No SessionFactory specified"); + } + IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary; + return (holderDictionary != null && holderDictionary.Contains(sessionFactory)); + } + + /// + /// Process Sessions that have been registered for deferred close + /// for the given SessionFactory. + /// + /// The session factory. + /// If there is no session factory associated with the thread. + public static void ProcessDeferredClose(ISessionFactory sessionFactory) + { + AssertUtils.ArgumentNotNull(sessionFactory, "No SessionFactory specified"); + + IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary; + + if (holderDictionary == null || !holderDictionary.Contains(sessionFactory)) + { + throw new InvalidOperationException("Deferred close not active for SessionFactory [" + sessionFactory + "]"); + } + log.Debug("Processing deferred close of Hibernate Sessions"); + Set sessions = (Set) holderDictionary[sessionFactory]; + holderDictionary.Remove(sessionFactory); + foreach (ISession session in sessions) + { + CloseSession(session); + } + + if (holderDictionary.Count == 0) + { + LogicalThreadContext.FreeNamedDataSlot(DeferredCloseHolderDataSlotName); + } + + + } + + /// + /// Applies the current transaction timeout, if any, to the given + /// criteria object + /// + /// The Hibernate Criteria object. + /// Hibernate SessionFactory that the Criteria was created for + /// (can be null). + /// If criteria argument is null. + public static void ApplyTransactionTimeout(ICriteria criteria, ISessionFactory sessionFactory) + { + if (criteria == null) + { + throw new ArgumentNullException("criteria", "No Criteria object specified"); + } + + SessionHolder sessionHolder = + (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory); + if (sessionHolder != null && sessionHolder.HasTimeout) + { + criteria.SetTimeout(sessionHolder.TimeToLiveInSeconds); + } + } + + /// + /// Applies the current transaction timeout, if any, to the given + /// Hibenrate query object. + /// + /// The Hibernate Query object. + /// Hibernate SessionFactory that the Query was created for + /// (can be null). + /// If query argument is null. + public static void ApplyTransactionTimeout(IQuery query, ISessionFactory sessionFactory) + { + if (query == null) + { + throw new ArgumentNullException("queryObject", "No query object specified"); + } + if (sessionFactory != null) + { + SessionHolder sessionHolder = + (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory); + if (sessionHolder != null && sessionHolder.HasTimeout) + { + query.SetTimeout(sessionHolder.TimeToLiveInSeconds); + } + } + } + /// + /// Gets the Spring IDbProvider given the ISessionFactory. + /// + /// The matching is performed by comparing the assembly qualified + /// name string of the hibernate Driver.ConnectionType to those in + /// the DbProviderFactory definitions. No connections are created + /// in performing this comparison. + /// The session factory. + /// The corresponding IDbProvider, null if no mapping was found. + /// If DbProviderFactory's ApplicaitonContext is not + /// an instance of IConfigurableApplicaitonContext. + public static IDbProvider GetDbProvider(ISessionFactory sessionFactory) + { + ISessionFactoryImplementor sfi = sessionFactory as ISessionFactoryImplementor; + if (sfi != null) + { + + ConnectionProvider cp = sfi.ConnectionProvider as ConnectionProvider; + if (cp != null) + { + IConfigurableApplicationContext ctx = + Spring.Data.Common.DbProviderFactory.ApplicationContext as IConfigurableApplicationContext; + if (ctx == null) + { + throw new InvalidOperationException( + "Implementations of IApplicationContext must also implement IConfigurableApplicationContext"); + } + + + DriverBase db = cp.Driver as DriverBase; + if (db != null) + { + Type hibCommandType = db.CreateCommand().GetType(); + //Type hibConnectionType = cp.Driver.ConnectionType; + + string[] providerNames = ctx.GetObjectNamesForType(typeof(DbProvider), true, false); + string hibCommandAQN = hibCommandType.AssemblyQualifiedName; + foreach (string providerName in providerNames) + { + IObjectDefinition objectdef = ctx.ObjectFactory.GetObjectDefinition(providerName); + ConstructorArgumentValues ctorArgs = objectdef.ConstructorArgumentValues; + ConstructorArgumentValues.ValueHolder vh = ctorArgs.NamedArgumentValues["dbmetadata"] as ConstructorArgumentValues.ValueHolder; + IObjectDefinition od = vh.Value as IObjectDefinition; + ConstructorArgumentValues dbmdCtorArgs = od.ConstructorArgumentValues; + string commandType = dbmdCtorArgs.GetArgumentValue("commandType", typeof(string)).Value as string; + string assemblyName = dbmdCtorArgs.GetArgumentValue("assemblyName", typeof(string)).Value as string; + + + if (hibCommandAQN.Equals(commandType)) + { + IDbProvider prov = Spring.Data.Common.DbProviderFactory.GetDbProvider(providerName); + return prov; + } + } + } + else + { + log.Info("Could not derive IDbProvider from SessionFactory"); + } + } + + + } + return null; + } + + /// + /// Create a IAdoExceptionTranslator from the given SessionFactory. + /// + /// If a corresponding IDbProvider is found, a ErrorcodeExceptionTranslator + /// for the IDbProvider is created. Otherwise, a FallbackException is created. + /// The session factory to create the translator for + /// An IAdoExceptionTranslator + public static IAdoExceptionTranslator NewAdoExceptionTranslator(ISessionFactory sessionFactory) + { + IDbProvider dbProvider = GetDbProvider(sessionFactory); + if (dbProvider != null) + { + return new ErrorCodeExceptionTranslator(dbProvider); + } + log.Warn("Using FallbackException Translator. Could not translate from ISessionFactory to IDbProvider"); + return new FallbackExceptionTranslator(); + + } + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/SessionHolder.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/SessionHolder.cs new file mode 100644 index 00000000..b821b6d7 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/SessionHolder.cs @@ -0,0 +1,375 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using Common.Logging; +using NHibernate; +using Spring.Collections; +using Spring.Transaction.Support; +using Spring.Util; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Session holder, wrapping a NHibernate ISession and a NHibernate Transaction. + /// HibernateTransactionManager binds instances of this class + /// to the thread, for a given ISessionFactory. + /// + /// + /// Note: This is an SPI class, not intended to be used by applications. + /// + /// Mark Pollack (.NET) + /// $Id: SessionHolder.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + public class SessionHolder : ResourceHolderSupport + { + #region Fields + + private static readonly object DEFAULT_KEY = new object(); + + private readonly Hashtable sessionDictionary = new Hashtable(1); + + private IDbConnection connection; + + private ITransaction transaction; + + private FlushMode flushMode; + + //needed to see if we actually assigned the enum value... + private bool assignedPreviousFlushMode = false; + + #endregion + + #region Logging Definition + + private static readonly ILog log = LogManager.GetLogger(typeof (SessionHolder)); + + #endregion + + #region Constructor (s) + + /// + /// May be used by derived classes to create an empty SessionHolder. + /// + /// + /// When using this ctor in your derived class, you MUST override ! + /// + protected SessionHolder() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The session. + public SessionHolder(ISession session) + { + AddSession(session); + } + + /// + /// Initializes a new instance of the class. + /// + /// The key to store the session under. + /// The hibernate session. + public SessionHolder(object key, ISession session) + { + AddSession(key, session); + } + + /// + /// May be overridden in a derived class to e.g. lazily create a session + /// + protected virtual void EnsureInitialized() + { + // noop here - but may be overridden to lazily create a session + } + + #endregion + + #region Properties + + /// + /// Gets the session using the default key + /// + /// The hibernate session. + public ISession Session + { + get + { + lock (sessionDictionary.SyncRoot) + { + EnsureInitialized(); + return sessionDictionary[DEFAULT_KEY] as ISession; + } + } + } + + /// + /// Gets the first session based on iteration over + /// the IDictionary storage. + /// + /// Any hibernate session. + public ISession AnySession + { + get + { + lock(sessionDictionary.SyncRoot) + { + EnsureInitialized(); + if (sessionDictionary.Count > 0) + { + IEnumerator enumerator = sessionDictionary.Values.GetEnumerator(); + enumerator.MoveNext(); + return (ISession)enumerator.Current; + } + return null; + } + } + } + + /// + /// Gets a value indicating whether dictionary of + /// hibernate sessions is empty. + /// + /// + /// true if this session holder is empty; otherwise, false. + /// + public bool IsEmpty + { + get + { + lock(sessionDictionary.SyncRoot) + { + EnsureInitialized(); + return (sessionDictionary.Count > 0 ? false : true); + } + } + } + + /// + /// Gets a value indicating whether this SessionHolder + /// does not hold non default session. + /// + /// + /// true if does not hold non default session; otherwise, false. + /// + public bool DoesNotHoldNonDefaultSession + { + get + { + lock (sessionDictionary.SyncRoot) + { + EnsureInitialized(); + return ( + sessionDictionary.Count == 0 || + (sessionDictionary.Count == 1 && sessionDictionary.Contains(DEFAULT_KEY)) + ); + + } + } + } + + /// + /// Gets or sets the hibernate transaction. + /// + /// The transaction. + public ITransaction Transaction + { + get { return transaction; } + set { transaction = value; } + } + + /// + /// Gets or sets the ADO.NET Connection used to create the session. + /// + /// The ADO.NET connection. + public IDbConnection Connection + { + get { return connection; } + set { connection = value; } + } + + /// + /// Gets or sets the previous flush mode. + /// + /// The previous flush mode. + public FlushMode PreviousFlushMode + { + get { return flushMode; } + set + { + flushMode = value; + assignedPreviousFlushMode = true; + } + } + + /// + /// Gets a value indicating whether the PreviousFlushMode property + /// was set. + /// + /// + /// true if assigned PreviousFlushMode property; otherwise, false. + /// + public bool AssignedPreviousFlushMode + { + get + { + return assignedPreviousFlushMode; + } + } + + /// + /// Gets the validated session. + /// + /// The validated session. + public ISession ValidatedSession + { + get + { + return GetValidatedSession(DEFAULT_KEY); + } + } + + #endregion + + #region Methods + + /// + /// Gets the session given key identifier + /// + /// The key. + /// A hibernate session + public ISession GetSession(object key) + { + lock (sessionDictionary.SyncRoot) + { + EnsureInitialized(); + return sessionDictionary[key] as ISession; + } + } + + + /// + /// Gets the session given the key and removes the session from + /// the dictionary storage. + /// + /// The key. + /// A hibernate session + public ISession GetValidatedSession(object key) + { + lock (sessionDictionary.SyncRoot) + { + EnsureInitialized(); + ISession session = sessionDictionary[key] as ISession; + // Check for dangling Session that's around but already closed. + // Effectively an assertion: that should never happen in practice. + // We'll seamlessly remove the Session here, to not let it cause + // any side effects. + if (session != null && !session.IsOpen) + { + sessionDictionary.Remove(key); + session = null; + } + return session; + } + } + /// + /// Adds the session to the dictionary storage using the default key. + /// + /// The hibernate session. + public void AddSession(ISession session) + { + AddSession(DEFAULT_KEY, session); + } + + /// + /// Adds the session to the dictionary storage using the supplied key. + /// + /// The key. + /// The hibernate session. + public void AddSession(object key, ISession session) + { + AssertUtils.ArgumentNotNull(key, "key", "Key must not be null"); + AssertUtils.ArgumentNotNull(session, "session", "Session must not be null"); + + lock (sessionDictionary.SyncRoot) + { + if (sessionDictionary.ContainsKey(key)) + { + log.Debug("Overwriting Session in SessionHolder with key = "+ key); + } + + sessionDictionary[key] = session; + } + } + + /// + /// Removes the session from the dictionary storage for the given key. + /// + /// The key. + /// The session that was previously contained in the + /// dictionary storage. + public ISession RemoveSession(object key) + { + lock(sessionDictionary.SyncRoot) + { + ISession oldSession = sessionDictionary[key] as ISession; + sessionDictionary.Remove(key); + return oldSession; + } + } + + /// + /// Determines whether the holder the specified session. + /// + /// The session. + /// + /// true if the holder contains the specified session; otherwise, false. + /// + public bool ContainsSession(object session) + { + lock (sessionDictionary.SyncRoot) + { + EnsureInitialized(); + return sessionDictionary.ContainsValue(session); + } + } + + /// + /// Clear the transaction state of this resource holder. + /// + public override void Clear() + { + base.Clear(); + Transaction = null; + assignedPreviousFlushMode = false; + Connection = null; + } + + #endregion + + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/SpringSessionSynchronization.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/SpringSessionSynchronization.cs new file mode 100644 index 00000000..f95b4362 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/SpringSessionSynchronization.cs @@ -0,0 +1,275 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Common.Logging; +using NHibernate; +using NHibernate.Engine; +using Spring.Core; +using Spring.Data.Support; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// NHibnerations actions taken during the transaction lifecycle. + /// + /// Mark Pollack (.NET) + /// $Id: SpringSessionSynchronization.cs,v 1.2 2007/08/29 03:42:06 markpollack Exp $ + public class SpringSessionSynchronization : TransactionSynchronizationAdapter, IOrdered + { + #region Fields + + /// + /// The instance for this class. + /// + private readonly ILog log = LogManager.GetLogger(typeof(SpringSessionSynchronization)); + + private readonly SessionHolder sessionHolder; + + private readonly ISessionFactory sessionFactory; + + private readonly IAdoExceptionTranslator adoExceptionTranslator; + + private readonly bool newSession; + + private bool holderActive = true; + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public SpringSessionSynchronization(SessionHolder sessionHolder, ISessionFactory sessionFactory, + IAdoExceptionTranslator adoExceptionTranslator, bool newSession) + { + this.sessionHolder = sessionHolder; + this.sessionFactory = sessionFactory; + this.adoExceptionTranslator = adoExceptionTranslator; + this.newSession = newSession; + } + + #endregion + + #region Properties + + /// + /// Return the order value of this object, where a higher value means greater in + /// terms of sorting. + /// + /// + ///

    + /// Normally starting with 0 or 1, with indicating + /// greatest. Same order values will result in arbitrary positions for the affected + /// objects. + ///

    + ///

    + /// Higher value can be interpreted as lower priority, consequently the first object + /// has highest priority. + ///

    + ///
    + /// The order value. + public int Order + { + get + { + return SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER; + } + } + + #endregion + + #region Methods + + /// + /// Suspend this synchronization. + /// + /// + ///

    + /// Unbind Hibernate resources (SessionHolder) from + /// + /// if managing any. + ///

    + ///
    + public override void Suspend() + { + if (this.holderActive) + { + TransactionSynchronizationManager.UnbindResource(this.sessionFactory); + } + } + + /// + /// Resume this synchronization. + /// + /// + ///

    + /// Rebind Hibernate resources from + /// + /// if managing any. + ///

    + ///
    + public override void Resume() + { + if (this.holderActive) + { + TransactionSynchronizationManager.BindResource(this.sessionFactory, this.sessionHolder); + } + } + + /// + /// Invoked before transaction commit (before + /// ) + /// + /// + /// If the transaction is defined as a read-only transaction. + /// + /// + ///

    + /// Can flush transactional sessions to the database. + ///

    + ///

    + /// Note that exceptions will get propagated to the commit caller and + /// cause a rollback of the transaction. + ///

    + ///
    + public override void BeforeCommit(bool readOnly) + { + if (!readOnly) + { + // read-write transaction -> flush the Hibernate Session + log.Debug("Flushing Hibernate Session on transaction synchronization"); + ISession session = this.sessionHolder.Session; + //Further check: only flush when not FlushMode.NEVER + if (session.FlushMode != FlushMode.Never) + { + try + { + session.Flush(); + //TODO can throw System.ObjectDisposedException... + } + catch (ADOException ex) + { + if (this.adoExceptionTranslator != null) + { + //TODO investigate how ADOException wraps inner exception. + throw this.adoExceptionTranslator.Translate( + "Hibernate transaction synchronization: " + ex.Message, null, ex.InnerException); + } + else + { + throw new HibernateAdoException("ADO.NET Exception", ex); + } + } + catch (HibernateException ex) + { + throw SessionFactoryUtils.ConvertHibernateAccessException(ex); + } + } + + } + } + + /// + /// Invoked before transaction commit (before + /// ) + /// Can e.g. flush transactional O/R Mapping sessions to the database + /// + /// + /// + /// This callback does not mean that the transaction will actually be + /// commited. A rollback decision can still occur after this method + /// has been called. This callback is rather meant to perform work + /// that's only relevant if a commit still has a chance + /// to happen, such as flushing SQL statements to the database. + /// + /// + /// Note that exceptions will get propagated to the commit caller and cause a + /// rollback of the transaction. + /// + /// (note: do not throw TransactionException subclasses here!) + /// + /// + public override void BeforeCompletion() + { + if (this.newSession) + { + // Default behavior: unbind and close the thread-bound Hibernate Session. + TransactionSynchronizationManager.UnbindResource(this.sessionFactory); + this.holderActive = false; + } + else if (this.sessionHolder.AssignedPreviousFlushMode == true) + { + // In case of pre-bound Session, restore previous flush mode. + this.sessionHolder.Session.FlushMode = (this.sessionHolder.PreviousFlushMode); + } + } + + /// + /// Invoked after transaction commit/rollback. + /// + /// + /// Status according to + /// + /// + /// Can e.g. perform resource cleanup, in this case after transaction completion. + ///

    + /// Note that exceptions will get propagated to the commit or rollback + /// caller, although they will not influence the outcome of the transaction. + ///

    + ///
    + public override void AfterCompletion(TransactionSynchronizationStatus status) + { + + ISession session = sessionHolder.Session; + + // Provide correct transaction status for releasing the Session's cache locks, + // if possible. Else, closing will release all cache locks assuming a rollback. + ISessionImplementor sessionImplementor = session as ISessionImplementor; + if (sessionImplementor != null) + { + sessionImplementor.AfterTransactionCompletion(status == TransactionSynchronizationStatus.Committed); + } + + if (newSession) + { + SessionFactoryUtils.CloseSessionOrRegisterDeferredClose(session, sessionFactory); + } + + if (!newSession && status != TransactionSynchronizationStatus.Committed) + { + // Clear all pending inserts/updates/deletes in the Session. + // Necessary for pre-bound Sessions, to avoid inconsistent state. + sessionHolder.Session.Clear(); + } + + if (this.sessionHolder.DoesNotHoldNonDefaultSession) { + sessionHolder.SynchronizedWithTransaction = false; + } + + } + + #endregion + + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/ConfigSectionSessionScopeSettings.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/ConfigSectionSessionScopeSettings.cs new file mode 100644 index 00000000..698ab8c8 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/ConfigSectionSessionScopeSettings.cs @@ -0,0 +1,121 @@ +#region Licence + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NHibernate; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Data.NHibernate.Support +{ + /// + /// Holds the references and configuration settings for a instance. + /// References are resolved by looking up the given object names in the root obtained by . + /// + public class ConfigSectionSessionScopeSettings + : SessionScopeSettings + { + /// + /// The default session factory name to use when retrieving the Hibernate session factory from + /// the root context. + /// + public static readonly string DEFAULT_SESSION_FACTORY_OBJECT_NAME = "SessionFactory"; + + private readonly string sessionFactoryObjectName = DEFAULT_SESSION_FACTORY_OBJECT_NAME; + private readonly string entityInterceptorObjectName = null; + + /// + /// Initializes a new instance. + /// + /// The type, who's name will be used to prefix setting variables with + public ConfigSectionSessionScopeSettings(Type ownerType) + : this(ownerType, "appSettings") + { + // noop + } + + /// + /// Initializes a new instance. + /// + /// The type, who's name will be used to prefix setting variables with + /// The configuration section to read setting variables from. + public ConfigSectionSessionScopeSettings(Type ownerType, string sectionName) + : this(ownerType, new ConfigSectionVariableSource(sectionName)) + { + // noop + } + + /// + /// Initializes a new instance. + /// + /// The type, who's name will be used to prefix setting variables with + /// The variable source to obtain settings from. + public ConfigSectionSessionScopeSettings(Type ownerType, IVariableSource variableSource) + : base() + { + string sessionFactoryObjectNameSettingsKey = UniqueKey.GetTypeScopedString(ownerType, "SessionFactoryObjectName"); + string entityInterceptorObjectNameSettingsKey = UniqueKey.GetTypeScopedString(ownerType, "EntityInterceptorObjectName"); + string singleSessionSettingsKey = UniqueKey.GetTypeScopedString(ownerType, "SingleSession"); + string defaultFlushModeSettingsKey = UniqueKey.GetTypeScopedString(ownerType, "DefaultFlushMode"); + + VariableAccessor variables = new VariableAccessor(variableSource); + this.sessionFactoryObjectName = variables.GetString(sessionFactoryObjectNameSettingsKey, DEFAULT_SESSION_FACTORY_OBJECT_NAME); + this.entityInterceptorObjectName = variables.GetString(entityInterceptorObjectNameSettingsKey, null); + this.SingleSession = variables.GetBoolean(singleSessionSettingsKey, this.SingleSession); + this.DefaultFlushMode = (FlushMode)variables.GetEnum(defaultFlushModeSettingsKey, this.DefaultFlushMode); + + AssertUtils.ArgumentNotNull(sessionFactoryObjectName, "sessionFactoryObjectName"); // just to be sure + } + + /// + /// Resolve the entityInterceptor by looking up + /// in the root application context. + /// + /// The resolved instance or + protected override IInterceptor ResolveEntityInterceptor() + { + if (StringUtils.HasText(entityInterceptorObjectName)) + { + return (IInterceptor)ContextRegistry.GetContext().GetObject(entityInterceptorObjectName); + } + return null; + } + + /// + /// Resolve the by looking up + /// in the root application context. + /// + /// The resolved instance or + protected override ISessionFactory ResolveSessionFactory() + { + if (StringUtils.HasText(sessionFactoryObjectName)) + { + return (ISessionFactory)ContextRegistry.GetContext().GetObject(sessionFactoryObjectName); + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/HibernateDaoSupport.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/HibernateDaoSupport.cs new file mode 100644 index 00000000..16d3e748 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/HibernateDaoSupport.cs @@ -0,0 +1,237 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NHibernate; +using Spring.Dao; +using Spring.Dao.Support; + +#endregion + +namespace Spring.Data.NHibernate.Support +{ + /// + /// Convenient super class for Hibernate data access objects. + /// + /// + /// Requires a SessionFactory to be set, providing a HibernateTemplate + /// based on it to subclasses. Can alternatively be initialized directly with + /// a HibernateTemplate, to reuse the latter's settings such as the SessionFactory, + /// exception translator, flush mode, etc + /// + /// This base call is mainly intended for HibernateTemplate usage. + /// + /// This class will create its own HibernateTemplate if only a SessionFactory + /// is passed in. The "allowCreate" flag on that HibernateTemplate will be "true" + /// by default. A custom HibernateTemplate instance can be used through overriding + /// CreateHibernateTemplate. + /// + /// + /// Mark Pollack (.NET) + /// $Id: HibernateDaoSupport.cs,v 1.2 2007/08/28 15:26:37 markpollack Exp $ + public abstract class HibernateDaoSupport : DaoSupport + { + #region Fields + + private HibernateTemplate hibernateTemplate; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public HibernateDaoSupport() + { + + } + + #endregion + + #region Properties + + /// + /// Gets or sets the hibernate template. + /// + /// Set the HibernateTemplate for this DAO explicitly, + /// as an alternative to specifying a SessionFactory. + /// + /// The hibernate template. + public HibernateTemplate HibernateTemplate + { + get + { + return hibernateTemplate; + } + set + { + hibernateTemplate = value; + } + } + + /// + /// Gets or sets the session factory to be used by this DAO. + /// Will automatically create a HibernateTemplate for the given SessionFactory. + /// + /// The session factory. + public ISessionFactory SessionFactory + { + get + { + return (this.hibernateTemplate != null ? this.hibernateTemplate.SessionFactory : null); + } + set + { + hibernateTemplate = CreateHibernateTemplate(value); + } + } + + /// + /// Get a Hibernate Session, either from the current transaction or a new one. + /// The latter is only allowed if the "allowCreate" setting of this object's + /// HibernateTemplate is true. + /// + /// + ///

    Note that this is not meant to be invoked from HibernateTemplate code + /// but rather just in plain Hibernate code. Use it in combination with + /// ReleaseSession. + ///

    + ///

    In general, it is recommended to use HibernateTemplate, either with + /// the provided convenience operations or with a custom HibernateCallback + /// that provides you with a Session to work on. HibernateTemplate will care + /// for all resource management and for proper exception conversion. + ///

    + ///
    + /// The Hibernate session. + public ISession Session + { + get + { + return DoGetSession(HibernateTemplate.AllowCreate); + } + } + + + + #endregion + + #region Methods + + /// + /// Create a HibernateTemplate for the given ISessionFactory. + /// + /// + /// Only invoked if populating the DAO with a ISessionFactory reference! + ///

    Can be overridden in subclasses to provide a HibernateTemplate instance + /// with different configuration, or a custom HibernateTemplate subclass. + ///

    + ///
    + protected virtual HibernateTemplate CreateHibernateTemplate(ISessionFactory sessionFactory) + { + return new HibernateTemplate(sessionFactory); + } + + /// + /// Check if the hibernate template property has been set. + /// + protected override void CheckDaoConfig() + { + if (this.hibernateTemplate == null) + { + throw new ArgumentException("sessionFactory or hibernateTemplate is required"); + } + } + + /// + /// Get a Hibernate Session, either from the current transaction or + /// a new one. The latter is only allowed if "allowCreate" is true. + /// + /// Note that this is not meant to be invoked from HibernateTemplate code + /// but rather just in plain Hibernate code. Either rely on a thread-bound + /// Session (via HibernateInterceptor), or use it in combination with + /// ReleaseSession. + /// + /// In general, it is recommended to use HibernateTemplate, either with + /// the provided convenience operations or with a custom HibernateCallback + /// that provides you with a Session to work on. HibernateTemplate will care + /// for all resource management and for proper exception conversion. + /// + /// + /// if a non-transactional Session should be created when no + /// transactional Session can be found for the current thread + /// + /// Hibernate session. + /// + /// If the Session couldn't be created + /// + /// + /// if no thread-bound Session found and allowCreate false + /// + /// + protected ISession DoGetSession(bool allowCreate) + { + return (!allowCreate ? + SessionFactoryUtils.GetSession(SessionFactory, false) : + SessionFactoryUtils.GetSession( + SessionFactory, + this.hibernateTemplate.EntityInterceptor, + this.hibernateTemplate.AdoExceptionTranslator)); + } + + /// + /// Convert the given HibernateException to an appropriate exception from the + /// org.springframework.dao hierarchy. Will automatically detect + /// wrapped ADO.NET Exceptions and convert them accordingly. + /// + /// HibernateException that occured. + /// + /// The corresponding DataAccessException instance + /// + /// + /// The default implementation delegates to SessionFactoryUtils + /// and convertAdoAccessException. Can be overridden in subclasses. + /// + protected DataAccessException ConvertHibernateAccessException(HibernateException ex) + { + return hibernateTemplate.ConvertHibernateAccessException(ex); + } + + + /// + /// Close the given Hibernate Session, created via this DAO's SessionFactory, + /// if it isn't bound to the thread. + /// + /// + /// Typically used in plain Hibernate code, in combination with the + /// Session property and ConvertHibernateAccessException. + /// + /// The session to close. + protected void ReleaseSession(ISession session) { + SessionFactoryUtils.ReleaseSession(session, SessionFactory); + } + #endregion + + + + + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/OpenSessionInViewModule.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/OpenSessionInViewModule.cs new file mode 100644 index 00000000..be321e60 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/OpenSessionInViewModule.cs @@ -0,0 +1,83 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web; + +#endregion + +namespace Spring.Data.NHibernate.Support +{ + /// + /// Provide support for the open session in view pattern for lazily loaded hibernate objects + /// used in ASP.NET pages. + /// + /// jjx: http://forum.springframework.net/member.php?u=29 + /// Mark Pollack (.NET) + /// Erich Eichinger + /// Harald Radi + /// $Id: OpenSessionInViewModule.cs,v 1.7 2008/01/21 06:39:12 oakinger Exp $ + public class OpenSessionInViewModule : SessionScope, IHttpModule + { + /// + /// Initializes a new instance of the class. Creates a SessionScope, + /// but does not yet associate a session with a thread, that is lef to the lifecycle of the request. + /// + public OpenSessionInViewModule() : base("appSettings", typeof(OpenSessionInViewModule), false) + { + + } + + #region IHttpModule Members + + /// + /// Register context handler and look up SessionFactoryObjectName under the application configuration key, + /// Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName if not using the default value + /// (i.e. sessionFactory) and look up the SingleSession setting under the application configuration key, + /// Spring.Data.NHibernate.Support.OpenSessionInViewModule.SingleSession if not using the default value of true. + /// + /// The standard HTTP application context + public void Init( HttpApplication context ) + { + context.BeginRequest += new EventHandler(context_BeginRequest); + context.EndRequest += new EventHandler(context_EndRequest); + } + /// + /// A do nothing dispose method. + /// + public override void Dispose() + { + } + + #endregion + + private void context_BeginRequest(object sender, EventArgs e) + { + Open(); + } + + private void context_EndRequest(object sender, EventArgs e) + { + Close(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/SessionScope.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/SessionScope.cs new file mode 100644 index 00000000..14bf33f9 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/SessionScope.cs @@ -0,0 +1,508 @@ +#region Licence + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Text; +using Common.Logging; +using NHibernate; +using Spring.Threading; +using Spring.Transaction.Support; +using Spring.Util; + +#endregion + +namespace Spring.Data.NHibernate.Support +{ + /// + /// Implementation of SessionScope that associates a single session within the using scope. + /// + /// + /// It is recommended to be used in the following type of scenario: + /// + /// using (new SessionScope()) + /// { + /// ... do multiple operation, possibly in multiple transactions. + /// } + /// + /// At the end of "using", the session is automatically closed. All transactions within the scope use the same session, + /// if you are using Spring's HibernateTemplate or using Spring's implementation of NHibernate 1.2's + /// ICurrentSessionContext interface. + /// + /// + /// It is assumed that the session factory object name is called "SessionFactory". In case that you named the object + /// in different way you can specify your can specify it in the application settings using the key + /// Spring.Data.NHibernate.Support.SessionScope.SessionFactoryObjectName. Values for EntityInterceptorObjectName + /// and SingleSessionMode can be specified similarly. + /// + /// + /// Note: + /// The session is managed on a per thread basis on the thread that opens the scope instance. This means that you must + /// never pass a reference to a instance over to another thread! + /// + /// + /// Robert M. (.NET) + /// Harald Radi (.NET) + public class SessionScope : IDisposable + { + #region Fields + + /// + /// The logging instance. + /// + protected readonly ILog log = LogManager.GetLogger(MethodInfo.GetCurrentMethod().DeclaringType); + + private readonly SessionScopeSettings settings; + + // Keys into LogicalThreadContext for runtime values. + private readonly string PARTICIPATE_KEY; + private readonly string ISOPEN_KEY; + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class in single session mode, + /// associating a session with the thread. The session is opened lazily on demand. + /// + public SessionScope() + : this(true) + { + } + + + /// + /// Initializes a new instance of the class. + /// + /// + /// If set to true associate a session with the thread. If false, another + /// collaborating class will associate the session with the thread, potentially by calling + /// the Open method on this class. + /// + public SessionScope(bool open) + : this("appSettings", open) + { + // noop + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The name of the configuration section to read configuration settings from. + /// See for more info. + /// + /// + /// If set to true associate a session with the thread. If false, another + /// collaborating class will associate the session with the thread, potentially by calling + /// the Open method on this class. + /// + public SessionScope(string sectionName, bool open) + : this( sectionName, typeof(SessionScope), open) + { + // noop + } + + + /// + /// Initializes a new instance of the class. + /// + /// + /// The name of the configuration section to read configuration settings from. + /// See for more info. + /// + /// The type, who's full name is used for prefixing appSetting keys + /// + /// If set to true associate a session with the thread. If false, another + /// collaborating class will associate the session with the thread, potentially by calling + /// the Open method on this class. + /// + public SessionScope(string sectionName, Type namespaceType, bool open) + : this(new ConfigSectionSessionScopeSettings(namespaceType, sectionName), open) + { + // noop + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The instance to be used for obtaining instances. + /// + /// + /// If set to true associate a session with the thread. If false, another + /// collaborating class will associate the session with the thread, potentially by calling + /// the Open method on this class. + /// + public SessionScope(ISessionFactory sessionFactory, bool open) + : this(new SessionScopeSettings(sessionFactory), open) + { + // noop + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The instance to be used for obtaining instances. + /// + /// + /// Specify the to be set on each session provided by this instance. + /// + /// + /// Set whether to use a single session for each request. See property for details. + /// + /// + /// Specify the flushmode to be applied on each session provided by this instance. + /// + /// + /// If set to true associate a session with the thread. If false, another + /// collaborating class will associate the session with the thread, potentially by calling + /// the Open method on this class. + /// + public SessionScope(ISessionFactory sessionFactory, IInterceptor entityInterceptor, bool singleSession, FlushMode defaultFlushMode, bool open) + :this(new SessionScopeSettings(sessionFactory, entityInterceptor, singleSession, defaultFlushMode), open) + { + // noop + } + + /// + /// Initializes a new instance of the class. + /// + /// An instance holding the scope configuration + /// + /// If set to true associate a session with the thread. If false, another + /// collaborating class will associate the session with the thread, potentially by calling + /// the Open method on this class. + /// + public SessionScope(SessionScopeSettings settings, bool open) + { + log = LogManager.GetLogger(this.GetType()); + this.settings = settings; + + PARTICIPATE_KEY = UniqueKey.GetInstanceScopedString(this, "Participate"); + ISOPEN_KEY = UniqueKey.GetInstanceScopedString(this, "IsOpen"); + + if (open) + { + Open(); + } + } + + #endregion + + #region Properties + + /// + /// Set whether to use a single session for each request. Default is "true". + /// If set to false, each data access operation or transaction will use + /// its own session (like without Open Session in View). Each of those + /// sessions will be registered for deferred close, though, actually + /// processed at request completion. + /// + public bool SingleSession + { + get { return settings.SingleSession; } + } + + /// + /// Gets the flushmode to be applied on each newly created session. + /// + /// + /// This property defaults to to ensure that modifying objects outside the boundaries + /// of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation + /// within a transaction. + /// + public FlushMode DefaultFlushMode + { + get { return settings.DefaultFlushMode; } + } + + /// + /// Get or set the configured SessionFactory + /// + public ISessionFactory SessionFactory + { + get + { + return settings.SessionFactory; + } + } + + /// + /// Get or set the configured EntityInterceptor + /// + public IInterceptor EntityInterceptor + { + get + { + return settings.EntityInterceptor; + } + } + + /// + /// Gets a flag, whether this scope is in "open" state on the current logical thread. + /// + public bool IsOpen + { + get + { + return (null != LogicalThreadContext.GetData(ISOPEN_KEY)); + } + } + + /// + /// Gets a flag, whether this scope manages it's own session for the current logical thread or not. + /// + public bool IsParticipating + { + get + { + return (null != LogicalThreadContext.GetData(PARTICIPATE_KEY)); + } + } + + /// + /// Sets a flag, whether this scope is in "open" state on the current logical thread. + /// + private void SetOpen(bool isOpen) + { + if (isOpen) + { + LogicalThreadContext.SetData(ISOPEN_KEY, ISOPEN_KEY); + } + else + { + LogicalThreadContext.FreeNamedDataSlot(ISOPEN_KEY); + } + } + + /// + /// Gets/Sets a flag, whether this scope manages it's own session for the current logical thread or not. + /// + /// false if session is managed by this module. false otherwise + private void SetParticipating(bool participating) + { + if (participating) + { + LogicalThreadContext.SetData(PARTICIPATE_KEY, PARTICIPATE_KEY); + } + else + { + LogicalThreadContext.FreeNamedDataSlot(PARTICIPATE_KEY); + } + } + + #endregion + + #region IDisposable Members + + /// + /// Call Close(), + /// + public virtual void Dispose() + { + Close(); + } + + #endregion + + #region Methods + + /// + /// Opens a new session or participates in an existing session and + /// registers with spring's . + /// + public void Open() + { + if (IsParticipating || IsOpen) + { + throw new InvalidOperationException("This scope is already open"); + } + + bool isDebugEnabled = log.IsDebugEnabled; + + if (SingleSession) + { + // single session mode + if (TransactionSynchronizationManager.HasResource(SessionFactory)) + { + // Do not modify the Session: just set the participate flag. + if (isDebugEnabled) log.Debug("Participating in existing Hibernate SessionFactory"); + SetParticipating(true); + } + else + { + if (isDebugEnabled) log.Debug("Opening single Hibernate Session in SessionScope"); + TransactionSynchronizationManager.BindResource(SessionFactory, new LazySessionHolder(this)); + } + } + else + { + // deferred close mode + if (SessionFactoryUtils.IsDeferredCloseActive(SessionFactory)) + { + // Do not modify deferred close: just set the participate flag. + if (isDebugEnabled) log.Debug("Participating in active deferred close mode"); + SetParticipating(true); + } + else + { + if (isDebugEnabled) log.Debug("Initializing deferred close mode"); + SessionFactoryUtils.InitDeferredClose(SessionFactory); + } + } + + SetOpen(true); + } + + /// + /// Close the current view's session and unregisters + /// from spring's . + /// + public void Close() + { + bool isDebugEnabled = log.IsDebugEnabled; + if (isDebugEnabled) log.Debug("Trying to close SessionScope"); + + if (IsOpen) + { + try + { + DoClose(isDebugEnabled); + } + finally + { + SetOpen(false); + SetParticipating(false); + } + } + else + { + if (isDebugEnabled) log.Debug("SessionScope is already closed - doing nothing"); + } + } + + private void DoClose(bool isLogDebugEnabled) + { + if (!IsParticipating) + { + if (SingleSession) + { + // single session mode + if (isLogDebugEnabled) log.Debug("Closing single Hibernate Session in SessionScope"); + TransactionSynchronizationManager.UnbindResource(SessionFactory); + LazySessionHolder.Current.Close(); + } + else + { + // deferred close mode + if (isLogDebugEnabled) log.Debug("Closing all Hibernate Sessions"); + SessionFactoryUtils.ProcessDeferredClose(SessionFactory); + } + } + else + { + if (isLogDebugEnabled) log.Debug("Only participated Hibernate Session - doing nothing"); + } + } + + private ISession DoOpenSession() + { + ISession session = SessionFactoryUtils.OpenSession(SessionFactory, EntityInterceptor); + session.FlushMode = DefaultFlushMode; + return session; + } + + #endregion + + #region LazySessionHolder utility class + + /// + /// This sessionHolder creates a default session only if it is needed. + /// + /// + /// Although a NHibernateSession deferes creation of db-connections until they are really + /// needed, instantiation a session is imho still more expensive than this LazySessionHolder. (EE) + /// + private class LazySessionHolder : SessionHolder + { + private static readonly string ThreadDataSlotKey = UniqueKey.GetTypeScopedString(typeof(LazySessionHolder), "Current"); + + private readonly ILog log = LogManager.GetLogger(typeof(LazySessionHolder)); + private SessionScope owner; + private ISession session; + + /// + /// Initialize a new instance. + /// + public LazySessionHolder(SessionScope owner) + { + if (log.IsDebugEnabled) log.Debug("Created LazySessionHolder"); + this.owner = owner; + Current = this; + } + + /// + /// Gets or sets the LazySessionHolder instance for the current request + /// + public static LazySessionHolder Current + { + get { return (LazySessionHolder) LogicalThreadContext.GetData(ThreadDataSlotKey); } + set { LogicalThreadContext.SetData(ThreadDataSlotKey, value); } + } + + /// + /// Create a new session on demand + /// + protected override void EnsureInitialized() + { + if (session == null) + { + if (log.IsDebugEnabled) log.Debug("session instance requested - opening new session"); + session = owner.DoOpenSession(); + AddSession(session); + } + } + + /// + /// Ensure session is closed (if any) and remove circular references to avoid memory leaks! + /// + public void Close() + { + owner = null; + Current = null; + if (session != null) + { + ISession tmpSession = session; + session = null; + SessionFactoryUtils.CloseSession(tmpSession); + } + if (log.IsDebugEnabled) log.Debug("Closed LazySessionHolder"); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/SessionScopeSettings.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/SessionScopeSettings.cs new file mode 100644 index 00000000..76523890 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/SessionScopeSettings.cs @@ -0,0 +1,208 @@ +#region Licence + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NHibernate; +using Spring.Util; + +#endregion + +namespace Spring.Data.NHibernate.Support +{ + /// + /// Holds the references and configuration settings for a instance. + /// + public class SessionScopeSettings + { + /// + /// Default value for property. + /// + public static readonly bool SINGLESESSION_DEFAULT = true; + /// + /// Default value for property. + /// + public static readonly FlushMode FLUSHMODE_DEFAULT = FlushMode.Never; + + private ISessionFactory sessionFactory; + private bool sessionFactoryInitialized; + private IInterceptor entityInterceptor; + private bool entityInterceptorInitialized; + private bool singleSession; + private FlushMode defaultFlushMode; + + /// + /// Initialize a new instance of with default values. + /// + /// + /// Calling this constructor from your derived class leaves and + /// uninitialized. See and for more. + /// + protected SessionScopeSettings() + { + this.sessionFactory = null; + this.sessionFactoryInitialized = false; + this.entityInterceptor = null; + this.entityInterceptorInitialized = false; + this.singleSession = SINGLESESSION_DEFAULT; + this.defaultFlushMode = FLUSHMODE_DEFAULT; + } + + /// + /// Initialize a new instance of with the given sessionFactory + /// and default values for all other settings. + /// + /// + /// The instance to be used for obtaining instances. + /// + /// + /// Calling this constructor marks all properties initialized. + /// + public SessionScopeSettings(ISessionFactory sessionFactory) + :this(sessionFactory, null, SINGLESESSION_DEFAULT, FLUSHMODE_DEFAULT) + { + // noop + this.entityInterceptorInitialized = false; + } + + /// + /// Initialize a new instance of with the given values and references. + /// + /// + /// The instance to be used for obtaining instances. + /// + /// + /// Specify the to be set on each session provided by the instance. + /// + /// + /// Set whether to use a single session for each request. See property for details. + /// + /// + /// Specify the flushmode to be applied on each session provided by the instance. + /// + /// + /// Calling this constructor marks all properties initialized. + /// + public SessionScopeSettings(ISessionFactory sessionFactory, IInterceptor entityInterceptor, bool singleSession, FlushMode defaultFlushMode) + { + AssertUtils.ArgumentNotNull(sessionFactory, "sessionFactory"); + + this.sessionFactory = sessionFactory; + this.sessionFactoryInitialized = true; + this.entityInterceptor = entityInterceptor; + this.entityInterceptorInitialized = true; + this.singleSession = singleSession; + this.defaultFlushMode = defaultFlushMode; + } + + #region Properties + + /// + /// Gets the configured instance to be used. + /// + /// + /// If the entity interceptor is not set by the constructor, this property calls + /// to obtain an instance. This allows derived classes to + /// override the behaviour of how to obtain the concrete instance. + /// + public IInterceptor EntityInterceptor + { + get + { + if (!entityInterceptorInitialized) + { + return ResolveEntityInterceptor(); + } + return entityInterceptor; + } + } + + /// + /// Gets the configured instance to be used. + /// + /// + /// If this property is requested for the first time, is called. + /// This allows derived classes to override the behaviour of how to obtain the concrete instance. + /// + /// If the instance cannot be resolved. + public ISessionFactory SessionFactory + { + get + { + if (!sessionFactoryInitialized) + { + sessionFactoryInitialized = true; + sessionFactory = ResolveSessionFactory(); + if (sessionFactory == null) + { + throw new ArgumentException(string.Format("mandatory SessionFactory not found")); + } + } + return sessionFactory; + } + } + + /// + /// Set whether to use a single session for each request. Default is "true". + /// If set to false, each data access operation or transaction will use + /// its own session (like without Open Session in View). Each of those + /// sessions will be registered for deferred close, though, actually + /// processed at request completion. + /// + public bool SingleSession + { + get { return singleSession; } + set { singleSession = value; } + } + + /// + /// Gets or Sets the flushmode to be applied on each newly created session. + /// + /// + /// This property defaults to to ensure that modifying objects outside the boundaries + /// of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation + /// within a transaction. + /// + public FlushMode DefaultFlushMode + { + get { return defaultFlushMode; } + set { defaultFlushMode = value; } + } + + #endregion + + /// + /// Override this method to resolve an instance according to your chosen strategy. + /// + protected virtual IInterceptor ResolveEntityInterceptor() + { + return null; + } + + /// + /// Override this method to resolve an instance according to your chosen strategy. + /// + protected virtual ISessionFactory ResolveSessionFactory() + { + throw new NotImplementedException("you need to override this method to resolve an ISessionFactory instance"); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate/Data/NHibernate/TemplateFlushMode.cs b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/TemplateFlushMode.cs new file mode 100644 index 00000000..2f1ee184 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Data/NHibernate/TemplateFlushMode.cs @@ -0,0 +1,98 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +namespace Spring.Data.NHibernate +{ + /// + /// Enumeration for the various Hibernate flush modes. + /// + /// Mark Pollack (.NET) + /// $Id: TemplateFlushMode.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + public enum TemplateFlushMode + { + /// Never flush is a good strategy for read-only units of work. + /// + /// + /// Hibernate will not track and look for changes in this case, + /// avoiding any overhead of modification detection. + ///

    In case of an existing ISession, TemplateFlushMode.Never will turn + /// the hibenrate flush mode + /// to FlushMode.Never for the scope of the current operation, resetting the previous + /// flush mode afterwards. + ///

    + ///
    + Never, + + /// Automatic flushing is the default mode for a Hibernate Session. + /// + /// + /// A session will get flushed on transaction commit, and on certain find + /// operations that might involve already modified instances, but not + /// after each unit of work like with eager flushing. + ///

    In case of an existing Session, TemplateFlushMode.Auto + /// will participate in the existing flush mode, not modifying + /// it for the current operation. + /// This in particular means that this setting will not modify an existing + /// hibernate flush mode FlushMode.Never, in contrast to TemplateFlushMode.Eager. + ///

    + ///
    + Auto, + + /// + /// Eager flushing leads to immediate synchronization with the database, + /// even if in a transaction. + /// + /// + /// This causes inconsistencies to show up and throw + /// a respective exception immediately, and ADO access code that participates + /// in the same transaction will see the changes as the database is already + /// aware of them then. But the drawbacks are: + ///
      + ///
    • additional communication roundtrips with the database, instead of a + /// single batch at transaction commit;
    • + ///
    • the fact that an actual database rollback is needed if the Hibernate + /// transaction rolls back (due to already submitted SQL statements).
    • + ///
    + ///

    In case of an existing Session, TemplateFlushMode.Eager + /// will turn the NHibernate flush mode + /// to FlushMode.Auto for the scope of the current operation and issue a flush at the + /// end, resetting the previous flush mode afterwards. + ///

    + ///
    + Eager, + + /// + /// Flushing at commit only is intended for units of work where no + /// intermediate flushing is desired, not even for find operations + /// that might involve already modified instances. + /// + /// + ///

    In case of an existing Session, TemplateFlushMode.Commit + /// will turn the NHibernate flush mode + /// to FlushMode.Commit for the scope of the current operation, resetting the previous + /// flush mode afterwards. The only exception is an existing flush mode + /// FlushMode.Never, which will not be modified through this setting. + ///

    + ///
    + Commit + + } +} diff --git a/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.2003.csproj b/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.2003.csproj new file mode 100644 index 00000000..75adff5b --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.2003.csproj @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.2005.csproj b/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.2005.csproj new file mode 100644 index 00000000..b13ac41a --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.2005.csproj @@ -0,0 +1,108 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {130E1609-45A7-4F65-B112-105F2DD3E2CE} + Library + Properties + Spring + Spring.Data.NHibernate + + + true + full + false + ..\..\..\build\VS.Net.2005\Spring.Data.NHibernate\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + Spring.Data.NHibernate.xml + true + + + pdbonly + true + ..\..\..\build\VS.Net.2005\Spring.Data.NHibernate\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\lib\NHibernate10\net\2.0\Castle.DynamicProxy.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\NHibernate10\net\2.0\Iesi.Collections.dll + + + False + ..\..\..\lib\NHibernate10\net\2.0\NHibernate.dll + + + + + + + + + CommonAssemblyInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 + + + {BA4789EB-281A-48EA-8763-28B9F0596A18} + Spring.Web.2005 + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.build b/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.build new file mode 100644 index 00000000..a957c7a4 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.build @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.xml b/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.xml new file mode 100644 index 00000000..eab1ba6c --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate/Spring.Data.NHibernate.xml @@ -0,0 +1,3906 @@ + + + + Spring.Data.NHibernate + + + + + Holds the references and configuration settings for a instance. + References are resolved by looking up the given object names in the root obtained by . + + + + + Holds the references and configuration settings for a instance. + + + + + Default value for property. + + + + + Default value for property. + + + + + Initialize a new instance of with default values. + + + Calling this constructor from your derived class leaves and + uninitialized. See and for more. + + + + + Initialize a new instance of with the given sessionFactory + and default values for all other settings. + + + The instance to be used for obtaining instances. + + + Calling this constructor marks all properties initialized. + + + + + Initialize a new instance of with the given values and references. + + + The instance to be used for obtaining instances. + + + Specify the to be set on each session provided by the instance. + + + Set whether to use a single session for each request. See property for details. + + + Specify the flushmode to be applied on each session provided by the instance. + + + Calling this constructor marks all properties initialized. + + + + + Override this method to resolve an instance according to your chosen strategy. + + + + + Override this method to resolve an instance according to your chosen strategy. + + + + + Gets the configured instance to be used. + + + If the entity interceptor is not set by the constructor, this property calls + to obtain an instance. This allows derived classes to + override the behaviour of how to obtain the concrete instance. + + + + + Gets the configured instance to be used. + + + If this property is requested for the first time, is called. + This allows derived classes to override the behaviour of how to obtain the concrete instance. + + If the instance cannot be resolved. + + + + Set whether to use a single session for each request. Default is "true". + If set to false, each data access operation or transaction will use + its own session (like without Open Session in View). Each of those + sessions will be registered for deferred close, though, actually + processed at request completion. + + + + + Gets or Sets the flushmode to be applied on each newly created session. + + + This property defaults to to ensure that modifying objects outside the boundaries + of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation + within a transaction. + + + + + The default session factory name to use when retrieving the Hibernate session factory from + the root context. + + + + + Initializes a new instance. + + The type, who's name will be used to prefix setting variables with + + + + Initializes a new instance. + + The type, who's name will be used to prefix setting variables with + The configuration section to read setting variables from. + + + + Initializes a new instance. + + The type, who's name will be used to prefix setting variables with + The variable source to obtain settings from. + + + + Resolve the entityInterceptor by looking up + in the root application context. + + The resolved instance or + + + + Resolve the by looking up + in the root application context. + + The resolved instance or + + + + Helper class featuring methods for Hibernate Session handling, + allowing for reuse of Hibernate Session instances within transactions. + Also provides support for exception translation. + + Mark Pollack (.NET) + $Id: SessionFactoryUtils.cs,v 1.5 2008/04/08 20:26:37 markpollack Exp $ + + + + The instance for this class. + + + + + The ordering value for synchronizaiton this session resources. + Set to be lower than ADO.NET synchronization. + + + + + Initializes a new instance of the class. + + + + + Get a new Hibernate Session from the given SessionFactory. + Will return a new Session even if there already is a pre-bound + Session for the given SessionFactory. + + + Within a transaction, this method will create a new Session + that shares the transaction's ADO.NET Connection. More specifically, + it will use the same ADO.NET Connection as the pre-bound Hibernate Session. + + The session factory to create the session with. + The Hibernate entity interceptor, or null if none. + The new session. + If could not open Hibernate session + + + + Get a Hibernate Session for the given SessionFactory. Is aware of and will + return any existing corresponding Session bound to the current thread, for + example when using HibernateTransactionManager. Will always create a new + Session otherwise. + + + Supports setting a Session-level Hibernate entity interceptor that allows + to inspect and change property values before writing to and reading from the + database. Such an interceptor can also be set at the SessionFactory level + (i.e. on LocalSessionFactoryObject), on HibernateTransactionManager, or on + HibernateInterceptor/HibernateTemplate. + + The session factory to create the + session with. + Hibernate entity interceptor, or null if none. + AdoExceptionTranslator to use for flushing the + Session on transaction synchronization (can be null; only used when actually + registering a transaction synchronization). + The Hibernate Session + + If the session couldn't be created. + + + If no thread-bound Session found and allowCreate is false. + + + + + Get a Hibernate Session for the given SessionFactory. Is aware of and will + return any existing corresponding Session bound to the current thread, for + example when using . Will create a new Session + otherwise, if allowCreate is true. + + The session factory to create the session with. + if set to true create a non-transactional Session when no + transactional Session can be found for the current thread. + The hibernate session + + If the session couldn't be created. + + + If no thread-bound Session found and allowCreate is false. + + + + + Get a Hibernate Session for the given SessionFactory. + + Is aware of and will return any existing corresponding + Session bound to the current thread, for example whenusing + . Will create a new + Session otherwise, if "allowCreate" is true. +

    Throws the orginal HibernateException, in contrast to + . +

    + The session factory. + if set to true [allow create]. + The Hibernate Session + + if the Session couldn't be created + + + If no thread-bound Session found and allowCreate is false. + +
    + + + Open a new Session from the factory. + + The session factory to create the session with. + Hibernate entity interceptor, or null if none. + the newly opened session + + + + Perform the actual closing of the Hibernate Session + catching and logging any cleanup exceptions thrown. + + The hibernate session to close + + + + Return whether the given Hibernate Session is transactional, that is, + bound to the current thread by Spring's transaction facilities. + + The hibernate session to check + The session factory that the session + was created with, can be null. + + true if the session transactional; otherwise, false. + + + + + Convert the given HibernateException to an appropriate exception from the + Spring.Dao hierarchy. Note that it is advisable to + handle AdoException specifically by using a AdoExceptionTranslator for the + underlying ADO.NET exception. + + The Hibernate exception that occured. + DataAccessException instance + + + + Close the given Session, created via the given factory, + if it is not managed externally (i.e. not bound to the thread). + + The hibernate session to close + The hibernate SessionFactory that + the session was created with. + + + + Close the given Session or register it for deferred close. + + The session. + The session factory. + + + + Initialize deferred close for the current thread and the given SessionFactory. + Sessions will not be actually closed on close calls then, but rather at a + processDeferredClose call at a finishing point (like request completion). + + The session factory. + + + + Return if deferred close is active for the current thread + and the given SessionFactory. + The session factory. + + true if [is deferred close active] [the specified session factory]; otherwise, false. + + If SessionFactory argument is null. + + + + Process Sessions that have been registered for deferred close + for the given SessionFactory. + + The session factory. + If there is no session factory associated with the thread. + + + + Applies the current transaction timeout, if any, to the given + criteria object + + The Hibernate Criteria object. + Hibernate SessionFactory that the Criteria was created for + (can be null). + If criteria argument is null. + + + + Applies the current transaction timeout, if any, to the given + Hibenrate query object. + + The Hibernate Query object. + Hibernate SessionFactory that the Query was created for + (can be null). + If query argument is null. + + + + Gets the Spring IDbProvider given the ISessionFactory. + + The matching is performed by comparing the assembly qualified + name string of the hibernate Driver.ConnectionType to those in + the DbProviderFactory definitions. No connections are created + in performing this comparison. + The session factory. + The corresponding IDbProvider, null if no mapping was found. + If DbProviderFactory's ApplicaitonContext is not + an instance of IConfigurableApplicaitonContext. + + + + Create a IAdoExceptionTranslator from the given SessionFactory. + + If a corresponding IDbProvider is found, a ErrorcodeExceptionTranslator + for the IDbProvider is created. Otherwise, a FallbackException is created. + The session factory to create the translator for + An IAdoExceptionTranslator + + + + Callback interface for NHibernate code. + + To be used with HibernateTemplate execute + method. The typical implementation will call + Session.load/find/save/update to perform + some operations on persistent objects. + + Mark Pollack (.NET) + $Id: IHibernateCallback.cs,v 1.2 2007/09/19 22:58:22 markpollack Exp $ + + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    + A result object, or null if none. +
    + + + Base class for HibernateTemplate defining common + properties like SessionFactory and flushing behavior. + + +

    Not intended to be used directly. See HibernateTemplate. +

    +
    + Mark Pollack (.NET) + $Id: HibernateAccessor.cs,v 1.6 2008/01/25 15:04:39 markpollack Exp $ +
    + + + The instance for this class. + + + + + Initializes a new instance of the class. + + + + + Apply the flush mode that's been specified for this accessor + to the given Session. + + The current Hibernate Session. + if set to true + if executing within an existing transaction. + + the previous flush mode to restore after the operation, + or null if none + + + + + Flush the given Hibernate Session if necessary. + + The current Hibernate Session. + if set to true + if executing within an existing transaction. + + + + Convert the given HibernateException to an appropriate exception from the + org.springframework.dao hierarchy. Will automatically detect + wrapped ADO.NET Exceptions and convert them accordingly. + + HibernateException that occured. + + The corresponding DataAccessException instance + + + The default implementation delegates to SessionFactoryUtils + and convertAdoAccessException. Can be overridden in subclasses. + + + + + Converts the ADO.NET access exception to an appropriate exception from the + org.springframework.dao hierarchy. Can be overridden in subclasses. + + ADOException that occured, wrapping underlying ADO.NET exception. + + the corresponding DataAccessException instance + + + + + Converts the ADO.NET access exception to an appropriate exception from the + org.springframework.dao hierarchy. Can be overridden in subclasses. + + + + Note that a direct SQLException can just occur when callback code + performs direct ADO.NET access via ISession.Connection(). + + + The ADO.NET exception. + The corresponding DataAccessException instance + + + + Prepare the given IQuery object, applying cache settings and/or + a transaction timeout. + + The query object to prepare. + + + + Apply the given name parameter to the given Query object. + + The query object. + Name of the parameter + The value of the parameter + The NHibernate type of the parameter (or null if none specified) + + + + Prepare the given Criteria object, applying cache settings and/or + a transaction timeout. + + + Note that for NHibernate 1.2 this only works if the + implementation is of the type CriteriaImpl, which should generally + be the case. The SetFetchSize method is not available on the + ICriteria interface + + This is a no-op for NHibernate 1.0.x since + the SetFetchSize method is not on the ICriteria interface and + the implementation class is has internal access. + + To remove the method completely for Spring's NHibernate 1.0 + support while reusing code for NHibernate 1.2 would not be + possible. So now this ineffectual operation is left in tact for + NHibernate 1.0.2 support. + + The criteria object to prepare + + + + Ensure SessionFactory is not null + + + + + Gets or sets if a new Session should be created when no transactional Session + can be found for the current thread. + + + true if allowed to create non-transaction session; + otherwise, false. + + +

    HibernateTemplate is aware of a corresponding Session bound to the + current thread, for example when using HibernateTransactionManager. + If allowCreate is true, a new non-transactional Session will be created + if none found, which needs to be closed at the end of the operation. + If false, an InvalidOperationException will get thrown in this case. +

    +
    +
    + + + Gets or sets a value indicating whether to always + use a new Hibernate Session for this template. + + true if always use new session; otherwise, false. + +

    + Default is "false"; if activated, all operations on this template will + work on a new NHibernate ISession even in case of a pre-bound ISession + (for example, within a transaction). +

    +

    Within a transaction, a new NHibernate ISession used by this template + will participate in the transaction through using the same ADO.NET + Connection. In such a scenario, multiple Sessions will participate + in the same database transaction. +

    +

    Turn this on for operations that are supposed to always execute + independently, without side effects caused by a shared NHibernate ISession. +

    +
    +
    + + + Set whether to expose the native Hibernate Session to IHibernateCallback + code. Default is "false": a Session proxy will be returned, + suppressing close calls and automatically applying + query cache settings and transaction timeouts. + + true if expose native session; otherwise, false. + + + + Gets or sets the template flush mode. + + + Default is Auto. Will get applied to any new ISession + created by the template. + + The template flush mode. + + + + Gets or sets the entity interceptor that allows to inspect and change + property values before writing to and reading from the database. + + + Will get applied to any new ISession created by this object. +

    Such an interceptor can either be set at the ISessionFactory level, + i.e. on LocalSessionFactoryObject, or at the ISession level, i.e. on + HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + to avoid repeated configuration and guarantee consistent behavior in transactions. +

    +
    + The interceptor. +
    + + + Set the object name of a Hibernate entity interceptor that allows to inspect + and change property values before writing to and reading from the database. + + + Will get applied to any new Session created by this transaction manager. +

    Requires the object factory to be known, to be able to resolve the object + name to an interceptor instance on session creation. Typically used for + prototype interceptors, i.e. a new interceptor instance per session. +

    +

    Can also be used for shared interceptor instances, but it is recommended + to set the interceptor reference directly in such a scenario. +

    +
    +
    + + + Gets or sets the session factory that should be used to create + NHibernate ISessions. + + The session factory. + + + + Set the object factory instance. + + + + + Gets or sets a value indicating whether to + cache all queries executed by this template. + + + If this is true, all IQuery and ICriteria objects created by + this template will be marked as cacheable (including all + queries through find methods). +

    To specify the query region to be used for queries cached + by this template, set the QueryCacheRegion property. +

    +
    + true if cache queries; otherwise, false. +
    + + + Gets or sets the name of the cache region for queries executed by this template. + + + If this is specified, it will be applied to all IQuery and ICriteria objects + created by this template (including all queries through find methods). +

    The cache region will not take effect unless queries created by this + template are configured to be cached via the CacheQueries property. +

    +
    + The query cache region. +
    + + + Gets or sets the fetch size for this HibernateTemplate. + + The size of the fetch. + This is important for processing + large result sets: Setting this higher than the default value will increase + processing speed at the cost of memory consumption; setting this lower can + avoid transferring row data that will never be read by the application. +

    Default is 0, indicating to use the driver's default.

    +
    +
    + + + Gets or sets the maximum number of rows for this HibernateTemplate. + + The max results. + + This is important + for processing subsets of large result sets, avoiding to read and hold + the entire result set in the database or in the ADO.NET driver if we're + never interested in the entire result in the first place (for example, + when performing searches that might return a large number of matches). +

    Default is 0, indicating to use the driver's default.

    +
    +
    + + + Set the ADO.NET exception translator for this instance. + Applied to System.Data.Common.DbException (or provider specific exception type + in .NET 1.1) thrown by callback code, be it direct + DbException or wrapped Hibernate ADOExceptions. +

    The default exception translator is either a ErrorCodeExceptionTranslator + if a DbProvider is available, or a FalbackExceptionTranslator otherwise +

    +
    + The ADO exception translator. +
    + + + Gets a Session for use by this template. + + The session. + + - Returns a new Session in case of "alwaysUseNewSession" (using the same ADO.NET connection as a transaction Session, if applicable) + - a pre-bound Session in case of "AllowCreate" is set to false (not the default) + - or a pre-bound Session or new Session if no transactional or other pre-bound Session exists. + + + + + Helper class to determine if the FlushMode enumeration + was changed from its default value + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The flush mode. + + + + Gets or sets a value indicating whether the FlushMode + property was set.. + + true if FlushMode was set; otherwise, false. + + + + Gets or sets the FlushMode. + + The FlushMode. + + + + Provide support for the open session in view pattern for lazily loaded hibernate objects + used in ASP.NET pages. + + jjx: http://forum.springframework.net/member.php?u=29 + Mark Pollack (.NET) + Erich Eichinger + Harald Radi + $Id: OpenSessionInViewModule.cs,v 1.7 2008/01/21 06:39:12 oakinger Exp $ + + + + Implementation of SessionScope that associates a single session within the using scope. + + + It is recommended to be used in the following type of scenario: + + using (new SessionScope()) + { + ... do multiple operation, possibly in multiple transactions. + } + + At the end of "using", the session is automatically closed. All transactions within the scope use the same session, + if you are using Spring's HibernateTemplate or using Spring's implementation of NHibernate 1.2's + ICurrentSessionContext interface. + + + It is assumed that the session factory object name is called "SessionFactory". In case that you named the object + in different way you can specify your can specify it in the application settings using the key + Spring.Data.NHibernate.Support.SessionScope.SessionFactoryObjectName. Values for EntityInterceptorObjectName + and SingleSessionMode can be specified similarly. + + + Note: + The session is managed on a per thread basis on the thread that opens the scope instance. This means that you must + never pass a reference to a instance over to another thread! + + + Robert M. (.NET) + Harald Radi (.NET) + + + + The logging instance. + + + + + Initializes a new instance of the class in single session mode, + associating a session with the thread. The session is opened lazily on demand. + + + + + Initializes a new instance of the class. + + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Initializes a new instance of the class. + + + The name of the configuration section to read configuration settings from. + See for more info. + + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Initializes a new instance of the class. + + + The name of the configuration section to read configuration settings from. + See for more info. + + The type, who's full name is used for prefixing appSetting keys + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Initializes a new instance of the class. + + + The instance to be used for obtaining instances. + + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Initializes a new instance of the class. + + + The instance to be used for obtaining instances. + + + Specify the to be set on each session provided by this instance. + + + Set whether to use a single session for each request. See property for details. + + + Specify the flushmode to be applied on each session provided by this instance. + + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Initializes a new instance of the class. + + An instance holding the scope configuration + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Sets a flag, whether this scope is in "open" state on the current logical thread. + + + + + Gets/Sets a flag, whether this scope manages it's own session for the current logical thread or not. + + false if session is managed by this module. false otherwise + + + + Call Close(), + + + + + Opens a new session or participates in an existing session and + registers with spring's . + + + + + Close the current view's session and unregisters + from spring's . + + + + + Set whether to use a single session for each request. Default is "true". + If set to false, each data access operation or transaction will use + its own session (like without Open Session in View). Each of those + sessions will be registered for deferred close, though, actually + processed at request completion. + + + + + Gets the flushmode to be applied on each newly created session. + + + This property defaults to to ensure that modifying objects outside the boundaries + of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation + within a transaction. + + + + + Get or set the configured SessionFactory + + + + + Get or set the configured EntityInterceptor + + + + + Gets a flag, whether this scope is in "open" state on the current logical thread. + + + + + Gets a flag, whether this scope manages it's own session for the current logical thread or not. + + + + + This sessionHolder creates a default session only if it is needed. + + + Although a NHibernateSession deferes creation of db-connections until they are really + needed, instantiation a session is imho still more expensive than this LazySessionHolder. (EE) + + + + + Session holder, wrapping a NHibernate ISession and a NHibernate Transaction. + HibernateTransactionManager binds instances of this class + to the thread, for a given ISessionFactory. + + + Note: This is an SPI class, not intended to be used by applications. + + Mark Pollack (.NET) + $Id: SessionHolder.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + + May be used by derived classes to create an empty SessionHolder. + + + When using this ctor in your derived class, you MUST override ! + + + + + Initializes a new instance of the class. + + The session. + + + + Initializes a new instance of the class. + + The key to store the session under. + The hibernate session. + + + + May be overridden in a derived class to e.g. lazily create a session + + + + + Gets the session given key identifier + + The key. + A hibernate session + + + + Gets the session given the key and removes the session from + the dictionary storage. + + The key. + A hibernate session + + + + Adds the session to the dictionary storage using the default key. + + The hibernate session. + + + + Adds the session to the dictionary storage using the supplied key. + + The key. + The hibernate session. + + + + Removes the session from the dictionary storage for the given key. + + The key. + The session that was previously contained in the + dictionary storage. + + + + Determines whether the holder the specified session. + + The session. + + true if the holder contains the specified session; otherwise, false. + + + + + Clear the transaction state of this resource holder. + + + + + Gets the session using the default key + + The hibernate session. + + + + Gets the first session based on iteration over + the IDictionary storage. + + Any hibernate session. + + + + Gets a value indicating whether dictionary of + hibernate sessions is empty. + + + true if this session holder is empty; otherwise, false. + + + + + Gets a value indicating whether this SessionHolder + does not hold non default session. + + + true if does not hold non default session; otherwise, false. + + + + + Gets or sets the hibernate transaction. + + The transaction. + + + + Gets or sets the ADO.NET Connection used to create the session. + + The ADO.NET connection. + + + + Gets or sets the previous flush mode. + + The previous flush mode. + + + + Gets a value indicating whether the PreviousFlushMode property + was set. + + + true if assigned PreviousFlushMode property; otherwise, false. + + + + + Gets the validated session. + + The validated session. + + + + Initialize a new instance. + + + + + Create a new session on demand + + + + + Ensure session is closed (if any) and remove circular references to avoid memory leaks! + + + + + Gets or sets the LazySessionHolder instance for the current request + + + + + Initializes a new instance of the class. Creates a SessionScope, + but does not yet associate a session with a thread, that is lef to the lifecycle of the request. + + + + + Register context handler and look up SessionFactoryObjectName under the application configuration key, + Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName if not using the default value + (i.e. sessionFactory) and look up the SingleSession setting under the application configuration key, + Spring.Data.NHibernate.Support.OpenSessionInViewModule.SingleSession if not using the default value of true. + + The standard HTTP application context + + + + A do nothing dispose method. + + + + + PlatformTransactionManager implementation for a single Hibernate SessionFactory. + Binds a Hibernate Session from the specified factory to the thread, potentially + allowing for one thread Session per factory + + + SessionFactoryUtils and HibernateTemplate are aware of thread-bound Sessions and participate in such + transactions automatically. Using either of those is required for Hibernate + access code that needs to support this transaction handling mechanism. + + Supports custom isolation levels at the start of the transaction + , and timeouts that get applied as appropriate + Hibernate query timeouts. To support the latter, application code must either use + HibernateTemplate (which by default applies the timeouts) or call + SessionFactoryUtils.applyTransactionTimeout for each created + Hibernate Query object. + + Note that you can specify a Spring IDbProvider instance which if shared with + a corresponding instance of AdoTemplate will allow for mixing ADO.NET/NHibernate + operations within a single transaction. + + Mark Pollack (.NET) + $Id: HibernateTransactionManager.cs,v 1.7 2007/11/20 14:39:48 markpollack Exp $ + + + + Just needed for entityInterceptorBeanName. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The session factory. + + + + Return the current transaction object. + + The current transaction object. + + If transaction support is not available. + + + In the case of lookup or system errors. + + + + + Check if the given transaction object indicates an existing, + i.e. already begun, transaction. + + + Transaction object returned by + . + + True if there is an existing transaction. + + In the case of system errors. + + + + + Begin a new transaction with the given transaction definition. + + + Transaction object returned by + . + + + instance, describing + propagation behavior, isolation level, timeout etc. + + + Does not have to care about applying the propagation behavior, + as this has already been handled by this abstract manager. + + + In the case of creation or system errors. + + + + + Suspend the resources of the current transaction. + + + Transaction object returned by + . + + + An object that holds suspended resources (will be kept unexamined for passing it into + .) + + + Transaction synchronization will already have been suspended. + + + If suspending is not supported by the transaction manager implementation. + + + in case of system errors. + + + + + Resume the resources of the current transaction. + + + Transaction object returned by + . + + + The object that holds suspended resources as returned by + . + + + Transaction synchronization will be resumed afterwards. + + + If suspending is not supported by the transaction manager implementation. + + + In the case of system errors. + + + + + Perform an actual commit on the given transaction. + + The status representation of the transaction. + +

    + An implementation does not need to check the rollback-only flag. +

    +
    + + In the case of system errors. + +
    + + + Perform an actual rollback on the given transaction. + + The status representation of the transaction. + + An implementation does not need to check the new transaction flag. + + + In the case of system errors. + + + + + Set the given transaction rollback-only. Only called on rollback + if the current transaction takes part in an existing one. + + The status representation of the transaction. + + In the case of system errors. + + + + + Gets the ADO.NET IDbTransaction object from the NHibernate ITransaction object. + + The hibernate transaction. + The ADO.NET transaction. Null if could not get the transaction. Warning + messages will be logged in that case. + + + + Convert the given HibernateException to an appropriate exception from + the Spring.Dao hierarchy. Can be overridden in subclasses. + + The HibernateException that occured. + The corresponding DataAccessException instance + + + + Convert the given ADOException to an appropriate exception from the + the Spring.Dao hierarchy. Can be overridden in subclasses. + + The ADOException that occured, wrapping the underlying + ADO.NET thrown exception. + The translator to convert hibernate ADOExceptions. + + The corresponding DataAccessException instance + + + + + Cleanup resources after transaction completion. + + Transaction object returned by + . + + + This implemenation unbinds the SessionFactory and + DbProvider from thread local storage and closes the + ISession. + +

    + Called after + and + + execution on any outcome. +

    +

    + Should not throw any exceptions but just issue warnings on errors. +

    +
    +
    + + + Invoked by an + after it has injected all of an object's dependencies. + + +

    + This method allows the object instance to perform the kind of + initialization only possible when all of it's dependencies have + been injected (set), and to throw an appropriate exception in the + event of misconfiguration. +

    +

    + Please do consult the class level documentation for the + interface for a + description of exactly when this method is invoked. In + particular, it is worth noting that the + + and + callbacks will have been invoked prior to this method being + called. +

    +
    + + In the event of misconfiguration (such as the failure to set a + required property) or if initialization fails. + +
    + + + Gets or sets the db provider. + + The db provider. + + + + Gets or sets a Hibernate entity interceptor that allows to inspect and change + property values before writing to and reading from the database. + When getting, return the current Hibernate entity interceptor, or null if none. + + The entity interceptor. + + Resolves an entity interceptor object name via the object factory, + if necessary. + Will get applied to any new Session created by this transaction manager. + Such an interceptor can either be set at the SessionFactory level, + i.e. on LocalSessionFactoryObject, or at the Session level, i.e. on + HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + to avoid repeated configuration and guarantee consistent behavior in transactions. + + If object factory is null and need to get entity interceptor via object name. + + + + Sets the object name of a Hibernate entity interceptor that + allows to inspect and change property values before writing to and reading from the database. + + The name of the entity interceptor object. + + Will get applied to any new Session created by this transaction manager. +

    Requires the object factory to be known, to be able to resolve the object + name to an interceptor instance on session creation. Typically used for + prototype interceptors, i.e. a new interceptor instance per session. +

    +

    Can also be used for shared interceptor instances, but it is recommended + to set the interceptor reference directly in such a scenario. +

    +
    +
    + + + Gets or sets the ADO.NET exception translator for this transaction manager. + + + Applied to ADO.NET Exceptions (wrapped by Hibernate's ADOException) + + The ADO exception translator. + + + + Gets the default IAdoException translator, lazily creating it if nece + + The default IAdoException translator. + + + + Gets or sets the SessionFactory that this instance should manage transactions for. + + The session factory. + + + + Set whether to autodetect a ADO.NET connection used by the Hibernate SessionFactory, + if set via LocalSessionFactoryObject's DbProvider. Default is "true". + + + true if [autodetect data source]; otherwise, false. + + +

    Can be turned off to deliberately ignore an available IDbProvider, + to not expose Hibernate transactions as ADO.NET transactions for that IDbProvider. +

    +
    +
    + + + The object factory just needs to be known for resolving entity interceptor + It does not need to be set for any other mode of operation. + + + Owning + (may not be ). The object can immediately + call methods on the factory. + + + + + Return whether the transaction is internally marked as rollback-only. + + + True of the transaction is marked as rollback-only. + + + + Helper class that simplifies NHibernate data access code + + +

    Typically used to implement data access or business logic services that + use NHibernate within their implementation but are Hibernate-agnostic in their + interface. The latter or code calling the latter only have to deal with + domain objects.

    + +

    The central method is Execute supporting Hibernate access code + implementing the HibernateCallback interface. It provides NHibernate Session + handling such that neither the IHibernateCallback implementation nor the calling + code needs to explicitly care about retrieving/closing NHibernate Sessions, + or handling Session lifecycle exceptions. For typical single step actions, + there are various convenience methods (Find, Load, SaveOrUpdate, Delete). +

    + +

    Can be used within a service implementation via direct instantiation + with a ISessionFactory reference, or get prepared in an application context + and given to services as an object reference. Note: The ISessionFactory should + always be configured as an object in the application context, in the first case + given to the service directly, in the second case to the prepared template. +

    + +

    This class can be considered as direct alternative to working with the raw + Hibernate Session API (through SessionFactoryUtils.Session). +

    + +

    LocalSessionFactoryObject is the preferred way of obtaining a reference + to a specific NHibernate ISessionFactory. +

    +
    + Mark Pollack (.NET) + $Id: HibernateTemplate.cs,v 1.3 2008/01/24 17:29:16 markpollack Exp $ +
    + + + Interface that specifies a basic set of Hibernate operations. + + + Implemented by HibernateTemplate. Not often used, but a useful option + to enhance testability, as it can easily be mocked or stubbed. +

    Provides HibernateTemplate's data access methods that mirror + various Session methods. See the NHibernate ISession documentation + for details on those methods. +

    +
    + + Mark Pollack (.NET) + $Id: IHibernateOperations.cs,v 1.2 2007/09/19 22:58:22 markpollack Exp $ +
    + + + Interface that specifies a set of Hibernate operations that + are common across versions of Hibernate. + + + Base interface for generic and non generic IHibernateOperations interfaces + Not often used, but a useful option + to enhance testability, as it can easily be mocked or stubbed. +

    Provides HibernateTemplate's data access methods that mirror + various Session methods. See the NHibernate ISession documentation + for details on those methods. +

    +
    + Mark Pollack (.NET) + + $Id: ICommonHibernateOperations.cs,v 1.2 2007/09/19 22:58:22 markpollack Exp $ +
    + + + Remove all objects from the Session cache, and cancel all pending saves, + updates and deletes. + + In case of Hibernate errors + + + + Delete the given persistent instance. + + The persistent instance to delete. + In case of Hibernate errors + + + + Delete the given persistent instance. + + + Obtains the specified lock mode if the instance exists, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + Tthe persistent instance to delete. + The lock mode to obtain. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The number of entity instances deleted. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The value of the parameter. + The Hibernate type of the parameter (or null). + The number of entity instances deleted. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The values of the parameters. + Hibernate types of the parameters (or null) + The number of entity instances deleted. + In case of Hibernate errors + + + + Flush all pending saves, updates and deletes to the database. + + + Only invoke this for selective eager flushing, for example when ADO.NET code + needs to see certain changes within the same transaction. Else, it's preferable + to rely on auto-flushing at transaction completion. + + In case of Hibernate errors + + + + Load the persistent instance with the given identifier + into the given object, throwing an exception if not found. + + Entity the object (of the target class) to load into. + An identifier of the persistent instance. + If object not found. + In case of Hibernate errors + + + + Re-read the state of the given persistent instance. + + The persistent instance to re-read. + In case of Hibernate errors + + + + Re-read the state of the given persistent instance. + Obtains the specified lock mode for the instance. + + The persistent instance to re-read. + The lock mode to obtain. + In case of Hibernate errors + + + + Determines whether the given object is in the Session cache. + + the persistence instance to check. + + true if session cache contains the specified entity; otherwise, false. + + In case of Hibernate errors + + + + Remove the given object from the Session cache. + + The persistent instance to evict. + In case of Hibernate errors + + + + Obtain the specified lock level upon the given object, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + The he persistent instance to lock. + The lock mode to obtain. + If not found + In case of Hibernate errors + + + + Persist the given transient instance. + + The transient instance to persist. + The generated identifier. + In case of Hibernate errors + + + + Persist the given transient instance with the given identifier. + + The transient instance to persist. + The identifier to assign. + In case of Hibernate errors + + + + Update the given persistent instance. + + The persistent instance to update. + In case of Hibernate errors + + + + Update the given persistent instance. + Obtains the specified lock mode if the instance exists, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + The persistent instance to update. + The lock mode to obtain. + In case of Hibernate errors + + + + Save or update the given persistent instance, + according to its id (matching the configured "unsaved-value"?). + + Tthe persistent instance to save or update + (to be associated with the Hibernate Session). + In case of Hibernate errors + + + + Save or update the contents of given persistent object, + according to its id (matching the configured "unsaved-value"?). + Will copy the contained fields to an already loaded instance + with the same id, if appropriate. + + The persistent object to save or update. + (not necessarily to be associated with the Hibernate Session) + + The actually associated persistent object. + (either an already loaded instance with the same id, or the given object) + In case of Hibernate errors + + + + Delete all given persistent instances. + + The persistent instances to delete. + + This can be combined with any of the find methods to delete by query + in two lines of code, similar to Session's delete by query methods. + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning a result object, i.e. a domain + object or a collection of domain objects. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + The delegate callback object that specifies the Hibernate action. + a result object returned by the action, or null + + In case of Hibernate errors +
    + + + Execute the action specified by the given action object within a Session. + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning a result object, i.e. a domain + object or a collection of domain objects. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + The callback object that specifies the Hibernate action. + a result object returned by the action, or null + + In case of Hibernate errors +
    + + + Execute the specified action assuming that the result object is a List. + + + This is a convenience method for executing Hibernate find calls or + queries within an action. + + The calback object that specifies the Hibernate action. + A IList returned by the action, or null + + In case of Hibernate errors + + + + Execute a query for persistent instances. + + a query expressed in Hibernate's query language + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a "?" parameter in the query string. + + a query expressed in Hibernate's query language + the value of the parameter + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding one value + to a "?" parameter of the given type in the query string. + + a query expressed in Hibernate's query language + The value of the parameter. + Hibernate type of the parameter (or null) + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to "?" parameters in the query string. + + a query expressed in Hibernate's query language + the values of the parameters + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a number of + values to "?" parameters of the given types in the query string. + + A query expressed in Hibernate's query language + The values of the parameters + Hibernate types of the parameters (or null) + a List containing 0 or more persistent instances + In case of Hibernate errors + If values and types are not null and their lengths are not equal + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The value of the parameter + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The value of the parameter + Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If values and types are not null and their lengths differ. + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + The Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances, binding the properties + of the given object to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding the properties + of the given object to named parameters in the query string. + + A query expressed in Hibernate's query language + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + + a persistent type. + An identifier of the persistent instance. + the persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + Obtains the specified lock mode if the instance exists. + + A persistent class. + An identifier of the persistent instance. + The lock mode. + the persistent instance, or null if not found + the persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + + Type of the entity. + An identifier of the persistent instance. + The persistent instance + If not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + Obtains the specified lock mode if the instance exists. + + Type of the entity. + An identifier of the persistent instance. + The lock mode. + The persistent instance + If not found + In case of Hibernate errors + + + + Return all persistent instances of the given entity class. + Note: Use queries or criteria for retrieving a specific subset. + + Type of the entity. + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Save or update all given persistent instances, + according to its id (matching the configured "unsaved-value"?). + + Tthe persistent instances to save or update + (to be associated with the Hibernate Session)he entities. + In case of Hibernate errors + + + + The instance for this class. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The default for creating a new non-transactional + session when no transactional Session can be found for the current thread + is set to true. + The session factory to create sessions. + + + + Initializes a new instance of the class. + + The session factory to create sessions. + if set to true allow creation + of a new non-transactional when no transactional Session can be found + for the current thread. + + + + Delegate function that clears the session. + + The hibernate session. + null + + + + Flush all pending saves, updates and deletes to the database. + + + Only invoke this for selective eager flushing, for example when ADO.NET code + needs to see certain changes within the same transaction. Else, it's preferable + to rely on auto-flushing at transaction completion. + + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + + The type. + An identifier of the persistent instance. + The persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + Obtains the specified lock mode if the instance exists. + + The type. + The lock mode to obtain. + The lock mode. + the persistent instance, or null if not found + the persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + + Type of the entity. + An identifier of the persistent instance. + The persistent instance + If not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + Obtains the specified lock mode if the instance exists. + + Type of the entity. + An identifier of the persistent instance. + The lock mode. + The persistent instance + If not found + In case of Hibernate errors + + + + Load the persistent instance with the given identifier + into the given object, throwing an exception if not found. + + Entity the object (of the target class) to load into. + An identifier of the persistent instance. + If object not found. + In case of Hibernate errors + + + + Return all persistent instances of the given entity class. + Note: Use queries or criteria for retrieving a specific subset. + + Type of the entity. + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Re-read the state of the given persistent instance. + + The persistent instance to re-read. + In case of Hibernate errors + + + + Re-read the state of the given persistent instance. + Obtains the specified lock mode for the instance. + + The persistent instance to re-read. + The lock mode to obtain. + In case of Hibernate errors + + + + Obtain the specified lock level upon the given object, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + The he persistent instance to lock. + The lock mode to obtain. + If not found + In case of Hibernate errors + + + + Persist the given transient instance. + + The transient instance to persist. + The generated identifier. + In case of Hibernate errors + + + + Persist the given transient instance with the given identifier. + + The transient instance to persist. + The identifier to assign. + In case of Hibernate errors + + + + Update the given persistent instance. + + The persistent instance to update. + In case of Hibernate errors + + + + Update the given persistent instance. + Obtains the specified lock mode if the instance exists, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + The persistent instance to update. + The lock mode to obtain. + In case of Hibernate errors + + + + Save or update the given persistent instance, + according to its id (matching the configured "unsaved-value"?). + + Tthe persistent instance to save or update + (to be associated with the Hibernate Session). + In case of Hibernate errors + + + + Save or update all given persistent instances, + according to its id (matching the configured "unsaved-value"?). + + Tthe persistent instances to save or update + (to be associated with the Hibernate Session)he entities. + In case of Hibernate errors + + + + Save or update the contents of given persistent object, + according to its id (matching the configured "unsaved-value"?). + Will copy the contained fields to an already loaded instance + with the same id, if appropriate. + + The persistent object to save or update. + (not necessarily to be associated with the Hibernate Session) + + The actually associated persistent object. + (either an already loaded instance with the same id, or the given object) + In case of Hibernate errors + + + + Remove all objects from the Session cache, and cancel all pending saves, + updates and deletes. + + + + + Determines whether the given object is in the Session cache. + + the persistence instance to check. + + true if session cache contains the specified entity; otherwise, false. + + In case of Hibernate errors + + + + Remove the given object from the Session cache. + + The persistent instance to evict. + In case of Hibernate errors + + + + Delete the given persistent instance. + + The persistent instance to delete. + In case of Hibernate errors + + + + Delete the given persistent instance. + + Tthe persistent instance to delete. + The lock mode to obtain. + + Obtains the specified lock mode if the instance exists, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The number of entity instances deleted. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The value of the parameter. + The Hibernate type of the parameter (or null). + The number of entity instances deleted. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The values of the parameters. + Hibernate types of the parameters (or null) + The number of entity instances deleted. + In case of Hibernate errors + If length for argument values and types are not equal. + + + + Delete all given persistent instances. + + The persistent instances to delete. + + This can be combined with any of the find methods to delete by query + in two lines of code, similar to Session's delete by query methods. + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning a result object, i.e. a domain + object or a collection of domain objects. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + The delegate callback object that specifies the Hibernate action. + a result object returned by the action, or null + + In case of Hibernate errors +
    + + + Execute the action specified by the delegate within a Session. + + The HibernateDelegate that specifies the action + to perform. + if set to true expose the native hibernate session to + callback code. + a result object returned by the action, or null + + + + + Execute the action specified by the given action object within a Session. + + The callback object that specifies the Hibernate action. + + a result object returned by the action, or null + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning a result object, i.e. a domain + object or a collection of domain objects. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + In case of Hibernate errors +
    + + + Execute the specified action assuming that the result object is a List. + + + This is a convenience method for executing Hibernate find calls or + queries within an action. + + The calback object that specifies the Hibernate action. + A IList returned by the action, or null + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + callback object that specifies the Hibernate action. + if set to true expose the native hibernate session to + callback code. + + a result object returned by the action, or null + + + + + Execute a query for persistent instances. + + a query expressed in Hibernate's query language + + a List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a "?" parameter in the query string. + + a query expressed in Hibernate's query language + the value of the parameter + + a List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding one value + to a "?" parameter of the given type in the query string. + + a query expressed in Hibernate's query language + The value of the parameter. + Hibernate type of the parameter (or null) + + a List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to "?" parameters in the query string. + + a query expressed in Hibernate's query language + the values of the parameters + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a number of + values to "?" parameters of the given types in the query string. + + A query expressed in Hibernate's query language + The values of the parameters + Hibernate types of the parameters (or null) + + a List containing 0 or more persistent instances + + In case of Hibernate errors + If values and types are not null and their lengths are not equal + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The value of the parameter + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The value of the parameter + Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If values and types are not null and their lengths differ. + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + The Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances, binding the properties + of the given object to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding the properties + of the given object to named parameters in the query string. + + A query expressed in Hibernate's query language + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Create a close-suppressing proxy for the given Hibernate Session. + The proxy also prepares returned Query and Criteria objects. + + The session. + The session proxy. + + + + Check whether write operations are allowed on the given Session. + + + Default implementation throws an InvalidDataAccessApiUsageException + in case of FlushMode.Never. Can be overridden in subclasses. + + The current Hibernate session. + If write operation is attempted in read-only mode + + + + + Compares if the flush mode enumerations, Spring's + TemplateFlushMode and NHibernates FlushMode have equal + settings. + + The template flush mode. + The NHibernate flush mode. + + Returns true if both are Never, Auto, or Commit, false + otherwise. + + + + + Gets or sets if a new Session should be created when no transactional Session + can be found for the current thread. + + + true if allowed to create non-transaction session; + otherwise, false. + + +

    HibernateTemplate is aware of a corresponding Session bound to the + current thread, for example when using HibernateTransactionManager. + If allowCreate is true, a new non-transactional Session will be created + if none found, which needs to be closed at the end of the operation. + If false, an InvalidOperationException will get thrown in this case. +

    +
    +
    + + + Gets or sets a value indicating whether to always + use a new Hibernate Session for this template. + + true if always use new session; otherwise, false. + +

    + Default is "false"; if activated, all operations on this template will + work on a new NHibernate ISession even in case of a pre-bound ISession + (for example, within a transaction). +

    +

    Within a transaction, a new NHibernate ISession used by this template + will participate in the transaction through using the same ADO.NET + Connection. In such a scenario, multiple Sessions will participate + in the same database transaction. +

    +

    Turn this on for operations that are supposed to always execute + independently, without side effects caused by a shared NHibernate ISession. +

    +
    +
    + + + Gets or sets the template flush mode. + + + Default is Auto. Will get applied to any new ISession + created by the template. + + The template flush mode. + + + + Gets or sets the entity interceptor that allows to inspect and change + property values before writing to and reading from the database. + + + Will get applied to any new ISession created by this object. +

    Such an interceptor can either be set at the ISessionFactory level, + i.e. on LocalSessionFactoryObject, or at the ISession level, i.e. on + HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + to avoid repeated configuration and guarantee consistent behavior in transactions. +

    +
    + The interceptor. + If object factory is not set and need to retrieve entity interceptor by name. +
    + + + Gets or sets the name of the cache region for queries executed by this template. + + + If this is specified, it will be applied to all IQuery and ICriteria objects + created by this template (including all queries through find methods). +

    The cache region will not take effect unless queries created by this + template are configured to be cached via the CacheQueries property. +

    +
    + The query cache region. +
    + + + Gets or sets a value indicating whether to + cache all queries executed by this template. + + + If this is true, all IQuery and ICriteria objects created by + this template will be marked as cacheable (including all + queries through find methods). +

    To specify the query region to be used for queries cached + by this template, set the QueryCacheRegion property. +

    +
    + true if cache queries; otherwise, false. +
    + + + Gets or sets the maximum number of rows for this HibernateTemplate. + + The max results. + + This is important + for processing subsets of large result sets, avoiding to read and hold + the entire result set in the database or in the ADO.NET driver if we're + never interested in the entire result in the first place (for example, + when performing searches that might return a large number of matches). +

    Default is 0, indicating to use the driver's default.

    +
    +
    + + + Set whether to expose the native Hibernate Session to IHibernateCallback + code. Default is "false": a Session proxy will be returned, + suppressing close calls and automatically applying + query cache settings and transaction timeouts. + + true if expose native session; otherwise, false. + + + + Gets or sets whether to check that the Hibernate Session is not in read-only mode + in case of write operations (save/update/delete). + + + true if check that the Hibernate Session is not in read-only mode + in case of write operations; otherwise, false. + + + Default is "true", for fail-fast behavior when attempting write operations + within a read-only transaction. Turn this off to allow save/update/delete + on a Session with flush mode NEVER. + + + + + Set the object name of a Hibernate entity interceptor that allows to inspect + and change property values before writing to and reading from the database. + + + Will get applied to any new Session created by this transaction manager. +

    Requires the object factory to be known, to be able to resolve the object + name to an interceptor instance on session creation. Typically used for + prototype interceptors, i.e. a new interceptor instance per session. +

    +

    Can also be used for shared interceptor instances, but it is recommended + to set the interceptor reference directly in such a scenario. +

    +
    + The name of the entity interceptor in the object factory/application context. +
    + + + Set the object factory instance. + + The object factory instance + + + + Gets or sets the session factory that should be used to create + NHibernate ISessions. + + The session factory. + + + + Gets or sets the fetch size for this HibernateTemplate. + + The size of the fetch. + This is important for processing + large result sets: Setting this higher than the default value will increase + processing speed at the cost of memory consumption; setting this lower can + avoid transferring row data that will never be read by the application. +

    Default is 0, indicating to use the driver's default.

    +
    +
    + + + Gets or sets the proxy factory. + + This may be useful to set if you create many instances of + HibernateTemplate and/or HibernateDaoSupport. This allows the same + ProxyFactory implementation to be used thereby limiting the + number of dynamic proxy types created in the temporary assembly, which + are never garbage collected due to .NET runtime semantics. + + The proxy factory. + + + + Set the ADO.NET exception translator for this instance. + Applied to System.Data.Common.DbException (or provider specific exception type + in .NET 1.1) thrown by callback code, be it direct + DbException or wrapped Hibernate ADOExceptions. +

    The default exception translator is either a ErrorCodeExceptionTranslator + if a DbProvider is available, or a FalbackExceptionTranslator otherwise +

    +
    + The ADO exception translator. +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Hibernate-specific subclass of InvalidDataAccessResourceUsageException, + thrown on invalid HQL query syntax. + + Mark Pollack (.NET) + $Id: HibernateQueryException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The ex. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Gets the query string that was invalid. + + The query string that was invalid. + + + + NHibnerations actions taken during the transaction lifecycle. + + Mark Pollack (.NET) + $Id: SpringSessionSynchronization.cs,v 1.2 2007/08/29 03:42:06 markpollack Exp $ + + + + The instance for this class. + + + + + Initializes a new instance of the class. + + + + + Suspend this synchronization. + + +

    + Unbind Hibernate resources (SessionHolder) from + + if managing any. +

    +
    +
    + + + Resume this synchronization. + + +

    + Rebind Hibernate resources from + + if managing any. +

    +
    +
    + + + Invoked before transaction commit (before + ) + + + If the transaction is defined as a read-only transaction. + + +

    + Can flush transactional sessions to the database. +

    +

    + Note that exceptions will get propagated to the commit caller and + cause a rollback of the transaction. +

    +
    +
    + + + Invoked before transaction commit (before + ) + Can e.g. flush transactional O/R Mapping sessions to the database + + + + This callback does not mean that the transaction will actually be + commited. A rollback decision can still occur after this method + has been called. This callback is rather meant to perform work + that's only relevant if a commit still has a chance + to happen, such as flushing SQL statements to the database. + + + Note that exceptions will get propagated to the commit caller and cause a + rollback of the transaction. + + (note: do not throw TransactionException subclasses here!) + + + + + + Invoked after transaction commit/rollback. + + + Status according to + + + Can e.g. perform resource cleanup, in this case after transaction completion. +

    + Note that exceptions will get propagated to the commit or rollback + caller, although they will not influence the outcome of the transaction. +

    +
    +
    + + + Return the order value of this object, where a higher value means greater in + terms of sorting. + + +

    + Normally starting with 0 or 1, with indicating + greatest. Same order values will result in arbitrary positions for the affected + objects. +

    +

    + Higher value can be interpreted as lower priority, consequently the first object + has highest priority. +

    +
    + The order value. +
    + + + Enumeration for the various Hibernate flush modes. + + Mark Pollack (.NET) + $Id: TemplateFlushMode.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + Never flush is a good strategy for read-only units of work. + + + Hibernate will not track and look for changes in this case, + avoiding any overhead of modification detection. +

    In case of an existing ISession, TemplateFlushMode.Never will turn + the hibenrate flush mode + to FlushMode.Never for the scope of the current operation, resetting the previous + flush mode afterwards. +

    +
    +
    + + Automatic flushing is the default mode for a Hibernate Session. + + + A session will get flushed on transaction commit, and on certain find + operations that might involve already modified instances, but not + after each unit of work like with eager flushing. +

    In case of an existing Session, TemplateFlushMode.Auto + will participate in the existing flush mode, not modifying + it for the current operation. + This in particular means that this setting will not modify an existing + hibernate flush mode FlushMode.Never, in contrast to TemplateFlushMode.Eager. +

    +
    +
    + + + Eager flushing leads to immediate synchronization with the database, + even if in a transaction. + + + This causes inconsistencies to show up and throw + a respective exception immediately, and ADO access code that participates + in the same transaction will see the changes as the database is already + aware of them then. But the drawbacks are: +
      +
    • additional communication roundtrips with the database, instead of a + single batch at transaction commit;
    • +
    • the fact that an actual database rollback is needed if the Hibernate + transaction rolls back (due to already submitted SQL statements).
    • +
    +

    In case of an existing Session, TemplateFlushMode.Eager + will turn the NHibernate flush mode + to FlushMode.Auto for the scope of the current operation and issue a flush at the + end, resetting the previous flush mode afterwards. +

    +
    +
    + + + Flushing at commit only is intended for units of work where no + intermediate flushing is desired, not even for find operations + that might involve already modified instances. + + +

    In case of an existing Session, TemplateFlushMode.Commit + will turn the NHibernate flush mode + to FlushMode.Commit for the scope of the current operation, resetting the previous + flush mode afterwards. The only exception is an existing flush mode + FlushMode.Never, which will not be modified through this setting. +

    +
    +
    + + + An IFactoryObject that creates a local Hibernate SessionFactory instance. + Behaves like a SessionFactory instance when used as bean reference, + e.g. for HibernateTemplate's "SessionFactory" property. + + + The typical usage will be to register this as singleton factory + in an application context and give objects references to application services + that need it. + + Hibernate configuration settings can be set using the IDictionary property 'HibernateProperties'. + + + + Mark Pollack (.NET) + $Id: LocalSessionFactoryObject.cs,v 1.7 2008/03/21 14:12:16 markpollack Exp $ + + + + TODO: consider changing to NamevalueCollection for easier + cut-n-paste from existing App.config based configurations. + + + + + The shared instance for this class (and derived classes). + + + + + Initializes a new instance of the class. + + + + + Return the singleon session factory. + + + + + Initialize the SessionFactory for the given or the + default location. + + + + + Close the SessionFactory on application context shutdown. + + + + + Subclasses can override this method to perform custom initialization + of the Configuration instance used for ISessionFactory creation. + + + The properties of this LocalSessionFactoryObject will be applied to + the Configuration object that gets returned here. +

    The default implementation creates a new Configuration instance. + A custom implementation could prepare the instance in a specific way, + or use a custom Configuration subclass. +

    +
    +
    + + + To be implemented by subclasses that want to to perform custom + post-processing of the Configuration object after this FactoryObject + performed its default initialization. + + The current configuration object. + + + + Subclasses can override this method to perform custom initialization + of the SessionFactory instance, creating it via the given Configuration + object that got prepared by this LocalSessionFactoryObject. + + +

    The default implementation invokes Configuration's BuildSessionFactory. + A custom implementation could prepare the instance in a specific way, + or use a custom ISessionFactory subclass. +

    +
    +
    + + + Sets the assemblies to load that contain mapping files. + + The mapping assemblies. + + + + Sets the hibernate configuration files to load, i.e. hibernate.cfg.xml. + + + + + Sets the locations of Spring IResources that contain mapping + files. + + The location of mapping resources. + + + + Return the Configuration object used to build the SessionFactory. + Allows access to configuration metadata stored there (rarely needed). + + The hibernate configuration. + + + + Set NHibernate configuration properties, like "hibernate.dialect". + + The hibernate properties. + +

    Can be used to override values in a NHibernate XML config file, + or to specify all necessary properties locally. +

    +

    Note: Do not specify a transaction provider here when using + Spring-driven transactions. It is also advisable to omit connection + provider settings and use a Spring-set IDbProvider instead. +

    +
    +
    + + + Get or set the DataSource to be used by the SessionFactory. + + The db provider. + + If set, this will override corresponding settings in Hibernate properties. + Note: If this is set, the Hibernate settings should not define + a connection string + (hibernate.connection.connection_string) to avoid meaningless double configuration. + + + + + + Return the type or subclass. + + The type created by this factory + + + + Returns true + + true + + + + Hibernate-specific subclass of UncategorizedDataAccessException, + for Hibernate system errors that do not match any concrete + Spring.Dao exceptions. + + Mark Pollack (.NET) + $Id: HibernateSystemException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The cause. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Hibernate-specific subclass of ObjectOptimisticLockingFailureException. + + + Converts Hibernate's StaleObjectStateException. + + Juergen Hoeller + Mark Pollack (.NET) + $Id: HibernateOptimisticLockingFailureException.cs,v 1.2 2008/04/08 20:26:37 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The StaleObjectStateException. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Hibernate-specific subclass of ObjectRetrievalFailureException. + + + Converts Hibernate's UnresolvableObjectException, ObjectNotFoundException, + ObjectDeletedException, and WrongClassException. + + Mark Pollack (.NET) + $Id: HibernateObjectRetrievalFailureException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The ex. + + + + Initializes a new instance of the class. + + The ex. + + + + Initializes a new instance of the class. + + The ex. + + + + Initializes a new instance of the class. + + The ex. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Hibernate-specific subclass of UncategorizedDataAccessException, + for ADO.NET exceptions that Hibernate rethrew and could not be + mapped into the DAO exception heirarchy. + + Mark Pollack (.NET) + $Id: HibernateAdoException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception from the underlying data access API - ADO.NET + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Convenient super class for Hibernate data access objects. + + + Requires a SessionFactory to be set, providing a HibernateTemplate + based on it to subclasses. Can alternatively be initialized directly with + a HibernateTemplate, to reuse the latter's settings such as the SessionFactory, + exception translator, flush mode, etc + + This base call is mainly intended for HibernateTemplate usage. + + This class will create its own HibernateTemplate if only a SessionFactory + is passed in. The "allowCreate" flag on that HibernateTemplate will be "true" + by default. A custom HibernateTemplate instance can be used through overriding + CreateHibernateTemplate. + + + Mark Pollack (.NET) + $Id: HibernateDaoSupport.cs,v 1.2 2007/08/28 15:26:37 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Create a HibernateTemplate for the given ISessionFactory. + + + Only invoked if populating the DAO with a ISessionFactory reference! +

    Can be overridden in subclasses to provide a HibernateTemplate instance + with different configuration, or a custom HibernateTemplate subclass. +

    +
    +
    + + + Check if the hibernate template property has been set. + + + + + Get a Hibernate Session, either from the current transaction or + a new one. The latter is only allowed if "allowCreate" is true. + + Note that this is not meant to be invoked from HibernateTemplate code + but rather just in plain Hibernate code. Either rely on a thread-bound + Session (via HibernateInterceptor), or use it in combination with + ReleaseSession. + + In general, it is recommended to use HibernateTemplate, either with + the provided convenience operations or with a custom HibernateCallback + that provides you with a Session to work on. HibernateTemplate will care + for all resource management and for proper exception conversion. + + + if a non-transactional Session should be created when no + transactional Session can be found for the current thread + + Hibernate session. + + If the Session couldn't be created + + + if no thread-bound Session found and allowCreate false + + + + + + Convert the given HibernateException to an appropriate exception from the + org.springframework.dao hierarchy. Will automatically detect + wrapped ADO.NET Exceptions and convert them accordingly. + + HibernateException that occured. + + The corresponding DataAccessException instance + + + The default implementation delegates to SessionFactoryUtils + and convertAdoAccessException. Can be overridden in subclasses. + + + + + Close the given Hibernate Session, created via this DAO's SessionFactory, + if it isn't bound to the thread. + + + Typically used in plain Hibernate code, in combination with the + Session property and ConvertHibernateAccessException. + + The session to close. + + + + Gets or sets the hibernate template. + + Set the HibernateTemplate for this DAO explicitly, + as an alternative to specifying a SessionFactory. + + The hibernate template. + + + + Gets or sets the session factory to be used by this DAO. + Will automatically create a HibernateTemplate for the given SessionFactory. + + The session factory. + + + + Get a Hibernate Session, either from the current transaction or a new one. + The latter is only allowed if the "allowCreate" setting of this object's + HibernateTemplate is true. + + +

    Note that this is not meant to be invoked from HibernateTemplate code + but rather just in plain Hibernate code. Use it in combination with + ReleaseSession. +

    +

    In general, it is recommended to use HibernateTemplate, either with + the provided convenience operations or with a custom HibernateCallback + that provides you with a Session to work on. HibernateTemplate will care + for all resource management and for proper exception conversion. +

    +
    + The Hibernate session. +
    +
    +
    diff --git a/src/Spring/Spring.Data.NHibernate12/AssemblyInfo.cs b/src/Spring/Spring.Data.NHibernate12/AssemblyInfo.cs new file mode 100644 index 00000000..18ecf7b4 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring.Net NHibernate 1.2 support")] +[assembly: AssemblyDescription("Interfaces and classes that provide NHibernate 1.2 support in Spring.Net")] \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/FindHibernateDelegate.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/FindHibernateDelegate.cs new file mode 100644 index 00000000..bc229e6a --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/FindHibernateDelegate.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NHibernate; +using System.Collections.Generic; + +#endregion + +namespace Spring.Data.NHibernate.Generic +{ + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning an IList of result objects created within the callback. + /// Note that there's special support for single step actions: + /// see HibernateTemplate.find etc. + ///

    + ///
    + /// The type of result object + /// Sree Nivask (.NET) + /// $Id: FindHibernateDelegate.cs,v 1.2 2007/09/19 22:58:10 markpollack Exp $ + public delegate IList FindHibernateDelegate(ISession session); +} diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/HibernateDaoSupport.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/HibernateDaoSupport.cs new file mode 100644 index 00000000..3d210f3b --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/HibernateDaoSupport.cs @@ -0,0 +1,235 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NHibernate; +using Spring.Dao; +using Spring.Dao.Support; + +#endregion + +namespace Spring.Data.NHibernate.Generic.Support +{ + /// + /// Convenient super class for Hibernate data access objects. + /// + /// + /// Requires a SessionFactory to be set, providing a HibernateTemplate + /// based on it to subclasses. Can alternatively be initialized directly with + /// a HibernateTemplate, to reuse the latter's settings such as the SessionFactory, + /// exception translator, flush mode, etc + /// + /// This base call is mainly intended for HibernateTemplate usage. + /// + /// This class will create its own HibernateTemplate if only a SessionFactory + /// is passed in. The "allowCreate" flag on that HibernateTemplate will be "true" + /// by default. A custom HibernateTemplate instance can be used through overriding + /// CreateHibernateTemplate. + /// + /// + /// Sree Nivask (.NET) + /// Mark Pollack (.NET) + /// $Id: HibernateDaoSupport.cs,v 1.3 2007/09/19 22:58:10 markpollack Exp $ + public abstract class HibernateDaoSupport : DaoSupport + { + #region Fields + + private HibernateTemplate hibernateTemplate; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public HibernateDaoSupport() + { + + } + + #endregion + + #region Properties + + /// + /// Gets or sets the hibernate template. + /// + /// Set the HibernateTemplate for this DAO explicitly, + /// as an alternative to specifying a SessionFactory. + /// + /// The hibernate template. + public HibernateTemplate HibernateTemplate + { + get + { + return hibernateTemplate; + } + set + { + hibernateTemplate = value; + } + } + + /// + /// Gets or sets the session factory to be used by this DAO. + /// Will automatically create a HibernateTemplate for the given SessionFactory. + /// + /// The session factory. + public ISessionFactory SessionFactory + { + get + { + return (this.hibernateTemplate != null ? this.hibernateTemplate.SessionFactory : null); + } + set + { + hibernateTemplate = CreateHibernateTemplate(value); + } + } + + /// + /// Get a Hibernate Session, either from the current transaction or a new one. + /// The latter is only allowed if the "allowCreate" setting of this object's + /// HibernateTemplate is true. + /// + /// + ///

    Note that this is not meant to be invoked from HibernateTemplate code + /// but rather just in plain Hibernate code. Use it in combination with + /// ReleaseSession. + ///

    + ///

    In general, it is recommended to use HibernateTemplate, either with + /// the provided convenience operations or with a custom HibernateCallback + /// that provides you with a Session to work on. HibernateTemplate will care + /// for all resource management and for proper exception conversion. + ///

    + ///
    + /// The Hibernate session. + public ISession Session + { + get + { + return DoGetSession(HibernateTemplate.AllowCreate); + } + } + + #endregion + + #region Methods + + /// + /// Create a HibernateTemplate for the given ISessionFactory. + /// + /// + /// Only invoked if populating the DAO with a ISessionFactory reference! + ///

    Can be overridden in subclasses to provide a HibernateTemplate instance + /// with different configuration, or a custom HibernateTemplate subclass. + ///

    + ///
    + /// The new HibernateTemplate instance + protected virtual HibernateTemplate CreateHibernateTemplate(ISessionFactory sessionFactory) + { + return new HibernateTemplate(sessionFactory); + } + + /// + /// Check if the hibernate template property has been set. + /// + /// If HibernateTemplate property is null. + protected override void CheckDaoConfig() + { + if (this.hibernateTemplate == null) + { + throw new ArgumentException("sessionFactory or hibernateTemplate is required"); + } + } + /// + /// Get a Hibernate Session, either from the current transaction or + /// a new one. The latter is only allowed if "allowCreate" is true. + /// + /// Note that this is not meant to be invoked from HibernateTemplate code + /// but rather just in plain Hibernate code. Either rely on a thread-bound + /// Session (via HibernateInterceptor), or use it in combination with + /// ReleaseSession. + /// + /// In general, it is recommended to use HibernateTemplate, either with + /// the provided convenience operations or with a custom HibernateCallback + /// that provides you with a Session to work on. HibernateTemplate will care + /// for all resource management and for proper exception conversion. + /// + /// + /// if a non-transactional Session should be created when no + /// transactional Session can be found for the current thread + /// + /// Hibernate session. + /// + /// If the Session couldn't be created + /// + /// + /// if no thread-bound Session found and allowCreate false + /// + /// + protected ISession DoGetSession(bool allowCreate) + { + return (!allowCreate ? + SessionFactoryUtils.GetSession(SessionFactory, false) : + SessionFactoryUtils.GetSession( + SessionFactory, + this.hibernateTemplate.EntityInterceptor, + this.hibernateTemplate.AdoExceptionTranslator)); + } + + /// + /// Convert the given HibernateException to an appropriate exception from the + /// org.springframework.dao hierarchy. Will automatically detect + /// wrapped ADO.NET Exceptions and convert them accordingly. + /// + /// HibernateException that occured. + /// + /// The corresponding DataAccessException instance + /// + /// + /// The default implementation delegates to SessionFactoryUtils + /// and convertAdoAccessException. Can be overridden in subclasses. + /// + protected DataAccessException ConvertHibernateAccessException(HibernateException ex) + { + return hibernateTemplate.ConvertHibernateAccessException(ex); + } + + /// + /// Close the given Hibernate Session, created via this DAO's SessionFactory, + /// if it isn't bound to the thread. + /// + /// + /// Typically used in plain Hibernate code, in combination with the + /// Session property and ConvertHibernateAccessException. + /// + /// The session to close. + protected void ReleaseSession(ISession session) + { + SessionFactoryUtils.ReleaseSession(session, SessionFactory); + } + #endregion + + + } +} diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/HibernateDelegate.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/HibernateDelegate.cs new file mode 100644 index 00000000..fb8b49c0 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/HibernateDelegate.cs @@ -0,0 +1,45 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NHibernate; + +#endregion + +namespace Spring.Data.NHibernate.Generic +{ + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning the result object created within the callback. + /// Note that there's special support for single step actions: + /// see HibernateTemplate.find etc. + ///

    + ///
    + /// The type of result object + /// Sree Nivask (.NET) + /// $Id: HibernateDelegate.cs,v 1.2 2007/09/19 22:58:10 markpollack Exp $ + public delegate T HibernateDelegate(ISession session); +} diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/HibernateTemplate.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/HibernateTemplate.cs new file mode 100644 index 00000000..5b994ab8 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/HibernateTemplate.cs @@ -0,0 +1,1706 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Generic; +using NHibernate; +using NHibernate.Type; +using Spring.Aop.Framework; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Data.Support; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Data.NHibernate.Generic +{ + /// + /// Generic version of the Helper class that simplifies NHibernate data access code + /// + /// + ///

    Typically used to implement data access or business logic services that + /// use NHibernate within their implementation but are Hibernate-agnostic in their + /// interface. The latter or code calling the latter only have to deal with + /// domain objects.

    + /// + ///

    The central method is Execute() supporting Hibernate access code which + /// implements the HibernateCallback interface. It provides NHibernate Session + /// handling such that neither the IHibernateCallback implementation nor the calling + /// code needs to explicitly care about retrieving/closing NHibernate Sessions, + /// or handling Session lifecycle exceptions. For typical single step actions, + /// there are various convenience methods (Find, Load, SaveOrUpdate, Delete). + ///

    + /// + ///

    Can be used within a service implementation via direct instantiation + /// with a ISessionFactory reference, or get prepared in an application context + /// and given to services as an object reference. Note: The ISessionFactory should + /// always be configured as an object in the application context, in the first case + /// given to the service directly, in the second case to the prepared template. + ///

    + /// + ///

    This class can be considered as a direct alternative to working with the raw + /// Hibernate Session API (through SessionFactoryUtils.Session). + ///

    + /// + ///

    LocalSessionFactoryObject is the preferred way of obtaining a reference + /// to a specific NHibernate ISessionFactory. + ///

    + ///
    + /// Sree Nivask (.NET) + /// Mark Pollack (.NET) + /// $Id: HibernateTemplate.cs,v 1.3 2008/01/24 17:29:09 markpollack Exp $ + public class HibernateTemplate : HibernateAccessor, IHibernateOperations + { + #region Fields + + NHibernate.HibernateTemplate classicHibernateTemplate; + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public HibernateTemplate() + { + classicHibernateTemplate = new NHibernate.HibernateTemplate(); + } + + /// + /// Initializes a new instance of the class. + /// + /// Allows creation of a new non-transactional session when no + /// transactional Session can be found for the current thread + /// The session factory to create sessions. + public HibernateTemplate(ISessionFactory sessionFactory) + { + classicHibernateTemplate = new Spring.Data.NHibernate.HibernateTemplate(sessionFactory); + AfterPropertiesSet(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The session factory to create sessions. + /// if set to true allow creation + /// of a new non-transactional session when no transactional Session can be found + /// for the current thread. + public HibernateTemplate(ISessionFactory sessionFactory, bool allowCreate) + { + classicHibernateTemplate = new Spring.Data.NHibernate.HibernateTemplate(sessionFactory, allowCreate); + AfterPropertiesSet(); + } + + /// + /// Gets or sets if a new Session should be created when no transactional Session + /// can be found for the current thread. + /// + /// + /// true if allowed to create non-transaction session; + /// otherwise, false. + /// + /// + ///

    HibernateTemplate is aware of a corresponding Session bound to the + /// current thread, for example when using HibernateTransactionManager. + /// If allowCreate is true, a new non-transactional Session will be created + /// if none found, which needs to be closed at the end of the operation. + /// If false, an InvalidOperationException will get thrown in this case. + ///

    + ///
    + public override bool AllowCreate + { + get { return classicHibernateTemplate.AllowCreate; } + set { classicHibernateTemplate.AllowCreate = value; } + } + /// + /// Gets or sets a value indicating whether to always + /// use a new Hibernate Session for this template. + /// + /// true if always use new session; otherwise, false. + /// + ///

    + /// Default is "false"; if activated, all operations on this template will + /// work on a new NHibernate ISession even in case of a pre-bound ISession + /// (for example, within a transaction). + ///

    + ///

    Within a transaction, a new NHibernate ISession used by this template + /// will participate in the transaction through using the same ADO.NET + /// Connection. In such a scenario, multiple Sessions will participate + /// in the same database transaction. + ///

    + ///

    Turn this on for operations that are supposed to always execute + /// independently, without side effects caused by a shared NHibernate ISession. + ///

    + ///
    + public override bool AlwaysUseNewSession + { + get { return classicHibernateTemplate.AlwaysUseNewSession; } + set { classicHibernateTemplate.AlwaysUseNewSession = value; } + } + + + /// + /// Set whether to expose the native Hibernate Session to IHibernateCallback + /// code. Default is "false": a Session proxy will be returned, + /// suppressing close calls and automatically applying + /// query cache settings and transaction timeouts. + /// + /// true if expose native session; otherwise, false. + public override bool ExposeNativeSession + { + get { return classicHibernateTemplate.ExposeNativeSession; } + set { classicHibernateTemplate.ExposeNativeSession = value; } + } + + /// + /// Gets or sets the template flush mode. + /// + /// + /// Default is Auto. Will get applied to any new ISession + /// created by the template. + /// + /// The template flush mode. + public override TemplateFlushMode TemplateFlushMode + { + get { return classicHibernateTemplate.TemplateFlushMode; } + set { classicHibernateTemplate.TemplateFlushMode = value; } + } + + /// + /// Gets or sets the entity interceptor that allows to inspect and change + /// property values before writing to and reading from the database. + /// + /// + /// Will get applied to any new ISession created by this object. + ///

    Such an interceptor can either be set at the ISessionFactory level, + /// i.e. on LocalSessionFactoryObject, or at the ISession level, i.e. on + /// HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + /// It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + /// to avoid repeated configuration and guarantee consistent behavior in transactions. + ///

    + ///
    + /// The interceptor. + public override IInterceptor EntityInterceptor + { + get { return classicHibernateTemplate.EntityInterceptor; } + set { classicHibernateTemplate.EntityInterceptor = value; } + } + + /// + /// Set the object name of a Hibernate entity interceptor that allows to inspect + /// and change property values before writing to and reading from the database. + /// + /// + /// Will get applied to any new Session created by this transaction manager. + ///

    Requires the object factory to be known, to be able to resolve the object + /// name to an interceptor instance on session creation. Typically used for + /// prototype interceptors, i.e. a new interceptor instance per session. + ///

    + ///

    Can also be used for shared interceptor instances, but it is recommended + /// to set the interceptor reference directly in such a scenario. + ///

    + ///
    + /// The name of the entity interceptor in the object factory/application context. + public override string EntityInterceptorObjectName + { + set { classicHibernateTemplate.EntityInterceptorObjectName = value;} + } + + /// + /// Gets or sets the session factory that should be used to create + /// NHibernate ISessions. + /// + /// The session factory. + public override ISessionFactory SessionFactory + { + get { return classicHibernateTemplate.SessionFactory; } + set { classicHibernateTemplate.SessionFactory = value; } + } + + /// + /// Set the object factory instance. + /// + /// The object factory instance + public override IObjectFactory ObjectFactory + { + set { classicHibernateTemplate.ObjectFactory = value; } + } + + /// + /// Gets or sets a value indicating whether to + /// cache all queries executed by this template. + /// + /// + /// If this is true, all IQuery and ICriteria objects created by + /// this template will be marked as cacheable (including all + /// queries through find methods). + ///

    To specify the query region to be used for queries cached + /// by this template, set the QueryCacheRegion property. + ///

    + ///
    + /// true if cache queries; otherwise, false. + public override bool CacheQueries + { + get { return classicHibernateTemplate.CacheQueries; } + set { classicHibernateTemplate.CacheQueries = value; } + } + + /// + /// Gets or sets the name of the cache region for queries executed by this template. + /// + /// + /// If this is specified, it will be applied to all IQuery and ICriteria objects + /// created by this template (including all queries through find methods). + ///

    The cache region will not take effect unless queries created by this + /// template are configured to be cached via the CacheQueries property. + ///

    + ///
    + /// The query cache region. + public override string QueryCacheRegion + { + get { return classicHibernateTemplate.QueryCacheRegion; } + set { classicHibernateTemplate.QueryCacheRegion = value; } + } + + /// + /// Gets or sets the fetch size for this HibernateTemplate. + /// + /// The size of the fetch. + /// This is important for processing + /// large result sets: Setting this higher than the default value will increase + /// processing speed at the cost of memory consumption; setting this lower can + /// avoid transferring row data that will never be read by the application. + ///

    Default is 0, indicating to use the driver's default.

    + ///
    + public override int FetchSize + { + get { return classicHibernateTemplate.FetchSize; } + set { classicHibernateTemplate.FetchSize = value; } + } + + /// + /// Gets or sets the maximum number of rows for this HibernateTemplate. + /// + /// The max results. + /// + /// This is important + /// for processing subsets of large result sets, avoiding to read and hold + /// the entire result set in the database or in the ADO.NET driver if we're + /// never interested in the entire result in the first place (for example, + /// when performing searches that might return a large number of matches). + ///

    Default is 0, indicating to use the driver's default.

    + ///
    + public override int MaxResults + { + get { return classicHibernateTemplate.MaxResults; } + set { classicHibernateTemplate.MaxResults = value; } + } + + /// + /// Set the ADO.NET exception translator for this instance. + /// Applied to System.Data.Common.DbException (or provider specific exception type + /// in .NET 1.1) thrown by callback code, be it direct + /// DbException or wrapped Hibernate ADOExceptions. + ///

    The default exception translator is either a ErrorCodeExceptionTranslator + /// if a DbProvider is available, or a FalbackExceptionTranslator otherwise + ///

    + ///
    + /// The ADO exception translator. + public override IAdoExceptionTranslator AdoExceptionTranslator + { + get { return classicHibernateTemplate.AdoExceptionTranslator; } + set { classicHibernateTemplate.AdoExceptionTranslator = value; } + } + + /// + /// Gets the classic hibernate template for access to non-generic methods. + /// + /// The classic hibernate template. + public NHibernate.HibernateTemplate ClassicHibernateTemplate + { + get { return classicHibernateTemplate; } + } + + /// + /// Gets or sets the proxy factory. + /// + /// This may be useful to set if you create many instances of + /// HibernateTemplate and/or HibernateDaoSupport. This allows the same + /// ProxyFactory implementation to be used thereby limiting the + /// number of dynamic proxy types created in the temporary assembly, which + /// are never garbage collected due to .NET runtime semantics. + /// + /// The proxy factory. + public virtual ProxyFactory ProxyFactory + { + get { return classicHibernateTemplate.ProxyFactory; } + set { classicHibernateTemplate.ProxyFactory = value; } + } + + #endregion + + #region Properties + + + + + #endregion + + #region Methods + + /// + /// Remove all objects from the Session cache, and cancel all pending saves, + /// updates and deletes. + /// + public void Clear() + { + classicHibernateTemplate.Clear(); + } + + + /// + /// Delete the given persistent instance. + /// + /// The persistent instance to delete. + /// In case of Hibernate errors + public void Delete(object entity) + { + classicHibernateTemplate.Delete(entity); + } + + + /// + /// Delete the given persistent instance. + /// + /// Tthe persistent instance to delete. + /// The lock mode to obtain. + /// + /// Obtains the specified lock mode if the instance exists, implicitly + /// checking whether the corresponding database entry still exists + /// (throwing an OptimisticLockingFailureException if not found). + /// + /// In case of Hibernate errors + public void Delete(object entity, LockMode lockMode) + { + classicHibernateTemplate.Delete(entity, lockMode); + } + + /// + /// Delete all objects returned by the query. + /// + /// a query expressed in Hibernate's query language. + /// The number of entity instances deleted. + /// In case of Hibernate errors + public int Delete(string queryString) + { + return classicHibernateTemplate.Delete(queryString); + } + + /// + /// Delete all objects returned by the query. + /// + /// a query expressed in Hibernate's query language. + /// The value of the parameter. + /// The Hibernate type of the parameter (or null). + /// The number of entity instances deleted. + /// In case of Hibernate errors + public int Delete(string queryString, object value, IType type) + { + return classicHibernateTemplate.Delete(queryString, value, type); + } + + /// + /// Delete all objects returned by the query. + /// + /// a query expressed in Hibernate's query language. + /// The values of the parameters. + /// Hibernate types of the parameters (or null) + /// The number of entity instances deleted. + /// In case of Hibernate errors + public int Delete(string queryString, object[] values, IType[] types) + { + return classicHibernateTemplate.Delete(queryString, values, types); + } + + /// + /// Flush all pending saves, updates and deletes to the database. + /// + /// + /// Only invoke this for selective eager flushing, for example when ADO.NET code + /// needs to see certain changes within the same transaction. Else, it's preferable + /// to rely on auto-flushing at transaction completion. + /// + /// In case of Hibernate errors + public void Flush() + { + classicHibernateTemplate.Flush(); + } + + /// + /// Load the persistent instance with the given identifier + /// into the given object, throwing an exception if not found. + /// + /// Entity the object (of the target class) to load into. + /// An identifier of the persistent instance. + /// If object not found. + /// In case of Hibernate errors + public void Load(object entity, object id) + { + classicHibernateTemplate.Load(entity, id); + } + + /// + /// Re-read the state of the given persistent instance. + /// + /// The persistent instance to re-read. + /// In case of Hibernate errors + public void Refresh(object entity) + { + classicHibernateTemplate.Refresh(entity); + } + + /// + /// Re-read the state of the given persistent instance. + /// Obtains the specified lock mode for the instance. + /// + /// The persistent instance to re-read. + /// The lock mode to obtain. + /// In case of Hibernate errors + public void Refresh(object entity, LockMode lockMode) + { + classicHibernateTemplate.Refresh(entity, lockMode); + } + + /// + /// Determines whether the given object is in the Session cache. + /// + /// the persistence instance to check. + /// + /// true if session cache contains the specified entity; otherwise, false. + /// + /// In case of Hibernate errors + public bool Contains(object entity) + { + return classicHibernateTemplate.Contains(entity); + } + + /// + /// Remove the given object from the Session cache. + /// + /// The persistent instance to evict. + /// In case of Hibernate errors + public void Evict(object entity) + { + classicHibernateTemplate.Evict(entity); + } + + /// + /// Obtain the specified lock level upon the given object, implicitly + /// checking whether the corresponding database entry still exists + /// (throwing an OptimisticLockingFailureException if not found). + /// + /// The he persistent instance to lock. + /// The lock mode to obtain. + /// If not found + /// In case of Hibernate errors + public void Lock(object entity, LockMode lockMode) + { + classicHibernateTemplate.Lock(entity, lockMode); + } + + /// + /// Persist the given transient instance. + /// + /// The transient instance to persist. + /// The generated identifier. + /// In case of Hibernate errors + public object Save(object entity) + { + return classicHibernateTemplate.Save(entity); + } + + /// + /// Persist the given transient instance with the given identifier. + /// + /// The transient instance to persist. + /// The identifier to assign. + /// In case of Hibernate errors + public void Save(object entity, object id) + { + classicHibernateTemplate.Save(entity, id); + } + + /// + /// Update the given persistent instance. + /// + /// The persistent instance to update. + /// In case of Hibernate errors + public void Update(object entity) + { + classicHibernateTemplate.Update(entity); + } + + /// + /// Update the given persistent instance. + /// Obtains the specified lock mode if the instance exists, implicitly + /// checking whether the corresponding database entry still exists + /// (throwing an OptimisticLockingFailureException if not found). + /// + /// The persistent instance to update. + /// The lock mode to obtain. + /// In case of Hibernate errors + public void Update(object entity, LockMode lockMode) + { + classicHibernateTemplate.Update(entity, lockMode); + } + + /// + /// Save or update the given persistent instance, + /// according to its id (matching the configured "unsaved-value"?). + /// + /// Tthe persistent instance to save or update + /// (to be associated with the Hibernate Session). + /// In case of Hibernate errors + public void SaveOrUpdate(object entity) + { + classicHibernateTemplate.SaveOrUpdate(entity); + } + + /// + /// Save or update the contents of given persistent object, + /// according to its id (matching the configured "unsaved-value"?). + /// Will copy the contained fields to an already loaded instance + /// with the same id, if appropriate. + /// + /// The persistent object to save or update. + /// (not necessarily to be associated with the Hibernate Session) + /// + /// The actually associated persistent object. + /// (either an already loaded instance with the same id, or the given object) + /// In case of Hibernate errors + public object SaveOrUpdateCopy(object entity) + { + return classicHibernateTemplate.SaveOrUpdateCopy(entity); + } + + #endregion + + #region IHibernateOperations Gets + + /// + /// Return the persistent instance of the given entity type + /// with the given identifier, or null if not found. + /// Obtains the specified lock mode if the instance exists. + /// + /// The object type to get. + /// The id of the object to get. + /// the persistent instance, or null if not found + /// In case of Hibernate errors + public T Get(object id) + { + return Get(id, null); + } + + /// + /// Return the persistent instance of the given entity type + /// with the given identifier, or null if not found. + /// Obtains the specified lock mode if the instance exists. + /// + /// The object type to get. + /// The lock mode to obtain. + /// The lock mode. + /// the persistent instance, or null if not found + /// In case of Hibernate errors + public T Get(object id, LockMode lockMode) + { + return Execute(new GetByTypeHibernateCallback(id, lockMode), true); + } + + #endregion + + #region IHibernateOperations Loads + + /// + /// Return the persistent instance of the given entity class + /// with the given identifier, throwing an exception if not found. + /// + /// The object type to load. + /// An identifier of the persistent instance. + /// The persistent instance + /// If not found + /// In case of Hibernate errors + public T Load(object id) + { + return Load(id, null); + } + + /// + /// Return the persistent instance of the given entity class + /// with the given identifier, throwing an exception if not found. + /// Obtains the specified lock mode if the instance exists. + /// + /// The object type to load. + /// An identifier of the persistent instance. + /// The lock mode. + /// The persistent instance + /// If not found + /// In case of Hibernate errors + public T Load(object id, LockMode lockMode) + { + return Execute(new LoadByTypeHibernateCallback(id, lockMode), true); + } + + /// + /// Return all persistent instances of the given entity class. + /// Note: Use queries or criteria for retrieving a specific subset. + /// + /// The object type to load. + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList LoadAll() + { + return ExecuteFind(new LoadAllByTypeHibernateCallback(this), true); + } + + #endregion + + #region IHibernateOperations Finds + + /// + /// Execute a query for persistent instances. + /// + /// The object type to find. + /// a query expressed in Hibernate's query language + /// + /// a generic List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + public IList Find(string queryString) + { + return Find(queryString, (object[]) null, (IType[]) null); + } + + /// + /// Execute a query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// + /// The object type to find. + /// a query expressed in Hibernate's query language + /// the value of the parameter + /// + /// a generic List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + public IList Find(string queryString, object value) + { + return Find(queryString, new object[] {value}, (IType[]) null); + } + + /// + /// Execute a query for persistent instances, binding one value + /// to a "?" parameter of the given type in the query string. + /// + /// The object type to find. + /// a query expressed in Hibernate's query language + /// The value of the parameter. + /// Hibernate type of the parameter (or null) + /// + /// a generic List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + public IList Find(string queryString, object value, IType type) + { + return Find(queryString, new object[] {value}, new IType[] {type}); + } + + /// + /// Execute a query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// + /// The object type to find. + /// a query expressed in Hibernate's query language + /// the values of the parameters + /// a generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList Find(string queryString, object[] values) + { + return Find(queryString, values, (IType[]) null); + } + + /// + /// Execute a query for persistent instances, binding a number of + /// values to "?" parameters of the given types in the query string. + /// + /// The object type to find. + /// A query expressed in Hibernate's query language + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// + /// a generic List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + /// If values and types are not null and their lengths are not equal + public IList Find(string queryString, object[] values, IType[] types) + { + if (values != null && types != null && values.Length != types.Length) + { + throw new ArgumentException("Length of values array must match length of types array"); + } + return ExecuteFind(new FindHibernateCallback(this, queryString, values, types), true); + } + + /// + /// Execute a query for persistent instances, binding + /// one value to a named parameter in the query string. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The name of the parameter + /// The value of the parameter + /// a generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedParam(string queryName, string paramName, object value) + { + return FindByNamedParam(queryName, paramName, value, null); + } + + /// + /// Execute a query for persistent instances, binding + /// one value to a named parameter in the query string. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The name of the parameter + /// The value of the parameter + /// Hibernate type of the parameter (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedParam(string queryName, string paramName, object value, IType type) + { + return FindByNamedParam(queryName, new string[] {paramName}, new object[] {value}, new IType[] {type}); + } + + /// + /// Execute a query for persistent instances, binding a + /// number of values to named parameters in the query string. + /// + /// The object type to find. + /// A query expressed in Hibernate's query language + /// The names of the parameters + /// The values of the parameters + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedParam(string queryString, string[] paramNames, object[] values) + { + return FindByNamedParam(queryString, paramNames, values, null); + } + + /// + /// Execute a query for persistent instances, binding a + /// number of values to named parameters in the query string. + /// + /// The object type to find. + /// A query expressed in Hibernate's query language + /// The names of the parameters + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If paramNames length is not equal to values length or + /// if paramNames length is not equal to types length (when types is not null) + public IList FindByNamedParam(string queryString, string[] paramNames, object[] values, IType[] types) + { + if (paramNames.Length != values.Length) + { + throw new ArgumentOutOfRangeException("paramNames", + "Length of paramNames array must match length of values array"); + } + if (types != null && paramNames.Length != types.Length) + { + throw new ArgumentOutOfRangeException("paramNames", + "Length of paramNames array must match length of types array"); + } + + return + ExecuteFind(new FindByNamedParamHibernateCallback(this, queryString, paramNames, values, types), true); + } + + /// + /// Execute a named query for persistent instances. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQuery(string queryName) + { + return FindByNamedQuery(queryName, (object[]) null, (IType[]) null); + } + + /// + /// Execute a named query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The value of the parameter + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQuery(string queryName, object value) + { + return FindByNamedQuery(queryName, new object[] {value}, (IType[]) null); + } + + /// + /// Execute a named query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The value of the parameter + /// Hibernate type of the parameter (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQuery(string queryName, object value, IType type) + { + return FindByNamedQuery(queryName, new object[] {value}, new IType[] {type}); + } + + /// + /// Execute a named query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQuery(string queryName, object[] values) + { + return FindByNamedQuery(queryName, values, (IType[]) null); + } + + /// + /// Execute a named query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If values and types are not null and their lengths differ. + public IList FindByNamedQuery(string queryName, object[] values, IType[] types) + { + if (values != null && types != null && values.Length != types.Length) + { + throw new ArgumentOutOfRangeException("Length of values array must match length of types array"); + } + return ExecuteFind(new FindByNamedQueryHibernateCallback(this, queryName, values, types), true); + } + + /// + /// Execute a named query for persistent instances, binding + /// one value to a named parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// Name of the parameter + /// The value of the parameter + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQueryAndNamedParam(string queryName, string paramName, object value) + { + return FindByNamedQueryAndNamedParam(queryName, paramName, value, null); + } + + /// + /// Execute a named query for persistent instances, binding + /// one value to a named parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// Name of the parameter + /// The value of the parameter + /// The Hibernate type of the parameter (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQueryAndNamedParam(string queryName, string paramName, object value, IType type) + { + return FindByNamedQueryAndNamedParam( + queryName, new string[] {paramName}, new object[] {value}, new IType[] {type}); + } + + /// + /// Execute a named query for persistent instances, binding + /// number of values to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The names of the parameters + /// The values of the parameters. + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQueryAndNamedParam(string queryName, string[] paramNames, object[] values) + { + return FindByNamedQueryAndNamedParam(queryName, paramNames, values, null); + } + + /// + /// Execute a named query for persistent instances, binding + /// number of values to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The names of the parameters + /// The values of the parameters. + /// Hibernate types of the parameters (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If paramNames length is not equal to values length or + /// if paramNames length is not equal to types length (when types is not null) + public IList FindByNamedQueryAndNamedParam(string queryName, string[] paramNames, object[] values, + IType[] types) + { + if (paramNames != null && values != null && paramNames.Length != values.Length) + { + throw new ArgumentOutOfRangeException("paramNames", + "Length of paramNames array must match length of values array"); + } + if (paramNames != null && types != null && paramNames.Length != types.Length) + { + throw new ArgumentOutOfRangeException("paramNams", + "Length of paramNames array must match length of types array"); + } + return + ExecuteFind( + new FindByNamedQueryAndNamedParamHibernateCallback(this, queryName, paramNames, values, types), + true); + } + + /// + /// Execute a named query for persistent instances, binding the properties + /// of the given object to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByNamedQueryAndValueObject(string queryName, object valueObject) + { + return + ExecuteFind(new FindByNamedQueryAndValueObjectHibernateCallback(this, queryName, valueObject), true); + } + + /// + /// Execute a query for persistent instances, binding the properties + /// of the given object to named parameters in the query string. + /// + /// The object type to find. + /// A query expressed in Hibernate's query language + /// The values of the parameters + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + public IList FindByValueObject(string queryString, object valueObject) + { + return ExecuteFind(new FindByValueObjectHibernateCallback(this, queryString, valueObject), true); + } + + #endregion + + #region Execute methods + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning the result object. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + /// The object type retrieved. + /// The delegate callback object that specifies the Hibernate action. + /// a result object returned by the action, or null + /// + /// In case of Hibernate errors + public T Execute(HibernateDelegate del) + { + return Execute(new ExecuteHibernateCallbackUsingDelegate(del)); + } + + /// + /// Execute the action specified by the delegate within a Session. + /// + /// The object type retrieved. + /// The HibernateDelegate that specifies the action + /// to perform. + /// if set to true expose the native hibernate session to + /// callback code. + /// a result object returned by the action, or null + /// + /// In case of Hibernate errors + public T Execute(HibernateDelegate del, bool exposeNativeSession) + { + return Execute(new ExecuteHibernateCallbackUsingDelegate(del), true); + } + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// The object type retrieved. + /// The callback object that specifies the Hibernate action. + /// + /// a result object returned by the action, or null + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning the result object. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + /// In case of Hibernate errors + public T Execute(IHibernateCallback action) + { + return Execute(action, ExposeNativeSession); + } + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// The object type retrieved. + /// callback object that specifies the Hibernate action. + /// if set to true expose the native hibernate session to + /// callback code. + /// + /// a result object returned by the action, or null + /// + /// In case of Hibernate errors + public T Execute(IHibernateCallback action, bool exposeNativeSession) + { + ISession session = Session; + + bool existingTransaction = SessionFactoryUtils.IsSessionTransactional(session, SessionFactory); + if (existingTransaction) + { + //log.Debug("Found thread-bound Session for HibernateTemplate"); + } + + FlushModeHolder previousFlushModeHolder = new FlushModeHolder(); + try + { + previousFlushModeHolder = ApplyFlushMode(session, existingTransaction); + ISession sessionToExpose = (exposeNativeSession ? session : classicHibernateTemplate.CreateSessionProxy(session)); + T result = action.DoInHibernate(sessionToExpose); + FlushIfNecessary(session, existingTransaction); + return result; + } + catch (ADOException ex) + { + IDbProvider dbProvider = SessionFactoryUtils.GetDbProvider(SessionFactory); + if (dbProvider != null && dbProvider.IsDataAccessException(ex.InnerException)) + { + throw ConvertAdoAccessException(ex); + } + else + { + throw new HibernateSystemException(ex); + } + } + catch (HibernateException ex) + { + throw ConvertHibernateAccessException(ex); + } + catch (Exception ex) + { + IDbProvider dbProvider = SessionFactoryUtils.GetDbProvider(SessionFactory); + if (dbProvider != null && dbProvider.IsDataAccessException(ex)) + { + throw ConvertAdoAccessException(ex); + } + else + { + // Callback code throw application exception or other non DB related exception. + throw; + } + } + finally + { + if (existingTransaction) + { + //log.Debug("Not closing pre-bound Hibernate Session after HibernateTemplate"); + if (previousFlushModeHolder.ModeWasSet) + { + session.FlushMode = previousFlushModeHolder.Mode; + } + } + else + { + SessionFactoryUtils.ReleaseSession(session, SessionFactory); + } + } + } + + /// + /// Execute the action specified by the given action object within a Session assuming that an IList is returned. + /// + /// The object type to find. + /// callback object that specifies the Hibernate action. + /// if set to true expose the native hibernate session to + /// callback code. + /// + /// an IList returned by the action, or null + /// + /// In case of Hibernate errors + public IList ExecuteFind(IFindHibernateCallback action, bool exposeNativeSession) + { + ISession session = Session; + + bool existingTransaction = SessionFactoryUtils.IsSessionTransactional(session, SessionFactory); + + FlushModeHolder previousFlushModeHolder = new FlushModeHolder(); + try + { + previousFlushModeHolder = ApplyFlushMode(session, existingTransaction); + ISession sessionToExpose = (exposeNativeSession ? session : classicHibernateTemplate.CreateSessionProxy(session)); + IList result = action.DoInHibernate(sessionToExpose); + FlushIfNecessary(session, existingTransaction); + return result; + } + catch (ADOException ex) + { + IDbProvider dbProvider = SessionFactoryUtils.GetDbProvider(SessionFactory); + if (dbProvider != null && dbProvider.IsDataAccessException(ex.InnerException)) + { + throw ConvertAdoAccessException(ex); + } + else + { + // Callback code throw application exception or other non DB related exception. + throw; + } + } + catch (HibernateException ex) + { + throw ConvertHibernateAccessException(ex); + } + catch (Exception ex) + { + IDbProvider dbProvider = SessionFactoryUtils.GetDbProvider(SessionFactory); + if (dbProvider != null && dbProvider.IsDataAccessException(ex)) + { + throw ConvertAdoAccessException(ex); + } + else + { + // Callback code throw application exception or other non DB related exception. + throw; + } + } + finally + { + if (existingTransaction) + { + if (previousFlushModeHolder.ModeWasSet) + { + session.FlushMode = previousFlushModeHolder.Mode; + } + } + else + { + SessionFactoryUtils.ReleaseSession(session, SessionFactory); + } + } + } + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning the result object. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + /// The object type to find. + /// The delegate callback object that specifies the Hibernate action. + /// A generic IList returned by the action, or null + /// + /// In case of Hibernate errors + public IList ExecuteFind(FindHibernateDelegate del) + { + return ExecuteFind(new ExecuteFindHibernateCallbackUsingDelegate(del)); + } + + /// + /// Execute the action specified by the delegate within a Session. + /// + /// The object type to find. + /// The FindHibernateDelegate that specifies the action + /// to perform. + /// if set to true expose the native hibernate session to + /// callback code. + /// A generic IList returned by the action, or null + /// + /// In case of Hibernate errors + public IList ExecuteFind(FindHibernateDelegate del, bool exposeNativeSession) + { + return ExecuteFind(new ExecuteFindHibernateCallbackUsingDelegate(del), true); + } + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// The object type to find. + /// The callback object that specifies the Hibernate action. + /// + /// A generic IList returned by the action, or null + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning the result object. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + public IList ExecuteFind(IFindHibernateCallback action) + { + return ExecuteFind(action, ExposeNativeSession); + } + + #endregion + + } + + #region Internal Supporting Callback Classes + + internal class ExecuteHibernateCallbackUsingDelegate : IHibernateCallback + { + private HibernateDelegate del; + + public ExecuteHibernateCallbackUsingDelegate(HibernateDelegate d) + { + del = d; + } + + public T DoInHibernate(ISession session) + { + return del(session); + } + } + + internal class GetByTypeHibernateCallback : IHibernateCallback + { + private object id; + private LockMode lockMode; + + public GetByTypeHibernateCallback(object id, LockMode lockMode) + { + this.id = id; + this.lockMode = lockMode; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public T DoInHibernate(ISession session) + { + if (lockMode != null) + { + return session.Get(id, lockMode); + } + else + { + return session.Get(id); + } + } + } + + internal class LoadByTypeHibernateCallback : IHibernateCallback + { + private object id; + private LockMode lockMode; + + public LoadByTypeHibernateCallback(object id, LockMode lockMode) + { + this.id = id; + this.lockMode = lockMode; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public T DoInHibernate(ISession session) + { + if (lockMode != null) + { + return session.Load(id, lockMode); + } + else + { + return session.Load(id); + } + } + } + + internal class LoadAllByTypeHibernateCallback : IFindHibernateCallback + { + private HibernateTemplate outer; + + public LoadAllByTypeHibernateCallback(HibernateTemplate template) + { + outer = template; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public IList DoInHibernate(ISession session) + { + ICriteria criteria = session.CreateCriteria(typeof (T)); + outer.PrepareCriteria(criteria); + return criteria.List(); + } + } + + internal class FindHibernateCallback : IFindHibernateCallback + { + private HibernateTemplate outer; + private string queryString; + private object[] values; + private IType[] types; + + public FindHibernateCallback(HibernateTemplate template, string queryString, object[] values, IType[] types) + { + outer = template; + this.queryString = queryString; + this.values = values; + this.types = types; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public IList DoInHibernate(ISession session) + { + IQuery queryObject = session.CreateQuery(queryString); + outer.PrepareQuery(queryObject); + + if (values != null) + { + for (int i = 0; i < values.Length; i++) + { + if (types != null && types[i] != null) + { + queryObject.SetParameter(i, values[i], types[i]); + } + else + { + queryObject.SetParameter(i, values[i]); + } + } + } + + return queryObject.List(); + } + } + + internal class FindByNamedParamHibernateCallback : IFindHibernateCallback + { + HibernateTemplate outer; + private string queryString; + private string[] paramNames; + private object[] values; + private IType[] types; + + public FindByNamedParamHibernateCallback(HibernateTemplate template, string queryString, string[] paramNames, + object[] values, IType[] types) + { + outer = template; + this.queryString = queryString; + this.paramNames = paramNames; + this.values = values; + this.types = types; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public IList DoInHibernate(ISession session) + { + IQuery queryObject = session.CreateQuery(queryString); + outer.PrepareQuery(queryObject); + if (values != null) + { + for (int i = 0; i < values.Length; i++) + { + outer.ApplyNamedParameterToQuery(queryObject, paramNames[i], values[i], + (types != null ? types[i] : null)); + } + } + return queryObject.List(); + } + } + + internal class FindByNamedQueryHibernateCallback : IFindHibernateCallback + { + HibernateTemplate outer; + private string queryName; + private object[] values; + private IType[] types; + + public FindByNamedQueryHibernateCallback(HibernateTemplate template, string queryName, object[] values, + IType[] types) + { + outer = template; + this.queryName = queryName; + this.values = values; + this.types = types; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public IList DoInHibernate(ISession session) + { + IQuery queryObject = session.GetNamedQuery(queryName); + outer.PrepareQuery(queryObject); + if (values != null) + { + for (int i = 0; i < values.Length; i++) + { + if (types != null && types[i] != null) + { + queryObject.SetParameter(i, values[i], types[i]); + } + else + { + queryObject.SetParameter(i, values[i]); + } + } + } + return queryObject.List(); + } + } + + internal class FindByNamedQueryAndNamedParamHibernateCallback : IFindHibernateCallback + { + HibernateTemplate outer; + private string queryName; + private string[] paramNames; + private object[] values; + private IType[] types; + + public FindByNamedQueryAndNamedParamHibernateCallback(HibernateTemplate template, string queryName, + string[] paramNames, object[] values, IType[] types) + { + outer = template; + this.queryName = queryName; + this.paramNames = paramNames; + this.values = values; + this.types = types; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public IList DoInHibernate(ISession session) + { + IQuery queryObject = session.GetNamedQuery(queryName); + outer.PrepareQuery(queryObject); + if (values != null) + { + for (int i = 0; i < values.Length; i++) + { + outer.ApplyNamedParameterToQuery(queryObject, paramNames[i], values[i], + (types != null ? types[i] : null)); + } + } + return queryObject.List(); + } + } + + internal class FindByNamedQueryAndValueObjectHibernateCallback : IFindHibernateCallback + { + HibernateTemplate outer; + private string queryName; + private object valueObject; + + public FindByNamedQueryAndValueObjectHibernateCallback(HibernateTemplate template, string queryName, + object valueObject) + { + outer = template; + this.queryName = queryName; + this.valueObject = valueObject; + } + + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + public IList DoInHibernate(ISession session) + { + IQuery queryObject = session.GetNamedQuery(queryName); + outer.PrepareQuery(queryObject); + queryObject.SetProperties(valueObject); + return queryObject.List(); + } + } + + internal class FindByValueObjectHibernateCallback : IFindHibernateCallback + { + HibernateTemplate outer; + private string queryString; + private object valueObject; + + public FindByValueObjectHibernateCallback(HibernateTemplate template, string queryString, object valueObject) + { + outer = template; + this.queryString = queryString; + this.valueObject = valueObject; + } + + public IList DoInHibernate(ISession session) + { + IQuery queryObject = session.CreateQuery(queryString); + outer.PrepareQuery(queryObject); + queryObject.SetProperties(valueObject); + return queryObject.List(); + } + } + + internal class ExecuteFindHibernateCallbackUsingDelegate : IFindHibernateCallback + { + private FindHibernateDelegate del; + + public ExecuteFindHibernateCallbackUsingDelegate(FindHibernateDelegate d) + { + del = d; + } + + public IList DoInHibernate(ISession session) + { + return del(session); + } + } + #endregion +} \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/IFindHibernateCallback.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/IFindHibernateCallback.cs new file mode 100644 index 00000000..b84c65f4 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/IFindHibernateCallback.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections.Generic; +using NHibernate; + +namespace Spring.Data.NHibernate.Generic +{ + /// + /// Callback interface (Generic version) for NHibernate code that + /// returns a List of objects. + /// + /// The type of result object + /// To be used with HibernateTemplate execute + /// method. The typical implementation will call + /// Session.load/find/save/update to perform + /// some operations on persistent objects. + /// + /// Sree Nivask (.NET) + /// Mark Pollack (.NET) + /// $Id: IFindHibernateCallback.cs,v 1.2 2007/09/19 22:58:10 markpollack Exp $ + public interface IFindHibernateCallback + { + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + /// Collection result object. + IList DoInHibernate(ISession session); + } +} diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/IHibernateCallback.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/IHibernateCallback.cs new file mode 100644 index 00000000..6fd97f3f --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/IHibernateCallback.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NHibernate; + +#endregion + +namespace Spring.Data.NHibernate.Generic +{ + /// + /// Callback interface (Generic version) for NHibernate code. + /// + /// The type of result object + /// To be used with HibernateTemplate execute + /// method. The typical implementation will call + /// Session.load/find/save/update to perform + /// some operations on persistent objects. + /// + /// + /// Sree Nivask (.NET) + /// $Id: IHibernateCallback.cs,v 1.2 2007/09/19 22:58:10 markpollack Exp $ + public interface IHibernateCallback + { + /// + /// Gets called by HibernateTemplate with an active + /// Hibernate Session. Does not need to care about activating or closing + /// the Session, or handling transactions. + /// + /// + ///

    + /// Allows for returning a result object created within the callback, i.e. + /// a domain object or a collection of domain objects. Note that there's + /// special support for single step actions: see HibernateTemplate.find etc. + ///

    + ///
    + /// A result object. + /// The active Hibernate session + T DoInHibernate(ISession session); + } +} diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/IHibernateOperations.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/IHibernateOperations.cs new file mode 100644 index 00000000..899c88bc --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Generic/IHibernateOperations.cs @@ -0,0 +1,492 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections.Generic; +using NHibernate; +using NHibernate.Type; +using Spring.Dao; + +namespace Spring.Data.NHibernate.Generic +{ + /// + /// Interface that specifies a basic set of Hibernate operations. + /// + /// + /// Implemented by HibernateTemplate. Not often used, but a useful option + /// to enhance testability, as it can easily be mocked or stubbed. + ///

    Provides HibernateTemplate's data access methods that mirror + /// various Session methods. See the NHibernate ISession documentation + /// for details on those methods. + ///

    + ///
    + /// + /// Sree Nivask (.NET) + /// Mark Pollack (.NET) + /// $Id: IHibernateOperations.cs,v 1.2 2007/09/19 22:58:10 markpollack Exp $ + public interface IHibernateOperations : ICommonHibernateOperations + { + + /// + /// Return the persistent instance of the given entity type + /// with the given identifier, or if not found. + /// Obtains the specified lock mode if the instance exists. + /// + /// The object type to get. + /// The id of the object to get. + /// the persistent instance, or if not found + /// In case of Hibernate errors + T Get(object id); + + /// + /// Return the persistent instance of the given entity type + /// with the given identifier, or null if not found. + /// Obtains the specified lock mode if the instance exists. + /// + /// The object type to get. + /// The lock mode to obtain. + /// The lock mode. + /// the persistent instance, or null if not found + /// In case of Hibernate errors + T Get(object id, LockMode lockMode); + + /// + /// Return the persistent instance of the given entity class + /// with the given identifier, throwing an exception if not found. + /// + /// The object type to load. + /// An identifier of the persistent instance. + /// The persistent instance + /// If not found + /// In case of Hibernate errors + T Load(object id); + + /// + /// Return the persistent instance of the given entity class + /// with the given identifier, throwing an exception if not found. + /// Obtains the specified lock mode if the instance exists. + /// + /// The object type to load. + /// An identifier of the persistent instance. + /// The lock mode. + /// The persistent instance + /// If not found + /// In case of Hibernate errors + T Load(object id, LockMode lockMode); + + /// + /// Return all persistent instances of the given entity class. + /// Note: Use queries or criteria for retrieving a specific subset. + /// + /// The object type to load. + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList LoadAll(); + + /// + /// Execute a query for persistent instances. + /// + /// The object type to find. + /// a query expressed in Hibernate's query language + /// + /// a generic List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + IList Find(string queryString); + + /// + /// Execute a query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// + /// The object type to find. + /// a query expressed in Hibernate's query language + /// the value of the parameter + /// + /// a generic List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + IList Find(string queryString, object value); + + /// + /// Execute a query for persistent instances, binding one value + /// to a "?" parameter of the given type in the query string. + /// + /// The object type to find. + /// a query expressed in Hibernate's query language + /// The value of the parameter. + /// Hibernate type of the parameter (or null) + /// + /// a generic List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + IList Find(string queryString, object value, IType type); + + /// + /// Execute a query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// + /// The object type to find. + /// a query expressed in Hibernate's query language + /// the values of the parameters + /// a generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList Find(string queryString, object[] values); + + /// + /// Execute a query for persistent instances, binding a number of + /// values to "?" parameters of the given types in the query string. + /// + /// The object type to find. + /// A query expressed in Hibernate's query language + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// + /// a generic List containing 0 or more persistent instances + /// + /// In case of Hibernate errors + /// If values and types are not null and their lenths are not equal + IList Find(string queryString, object[] values, IType[] types); + + /// + /// Execute a query for persistent instances, binding + /// one value to a named parameter in the query string. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The name of the parameter + /// The value of the parameter + /// a generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedParam(string queryName, string paramName, object value); + + /// + /// Execute a query for persistent instances, binding + /// one value to a named parameter in the query string. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The name of the parameter + /// The value of the parameter + /// Hibernate type of the parameter (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedParam(string queryName, string paramName, object value, IType type); + + /// + /// Execute a query for persistent instances, binding a + /// number of values to named parameters in the query string. + /// + /// The object type to find. + /// A query expressed in Hibernate's query language + /// The names of the parameters + /// The values of the parameters + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedParam(string queryString, string[] paramNames, object[] values); + + /// + /// Execute a query for persistent instances, binding a + /// number of values to named parameters in the query string. + /// + /// The object type to find. + /// A query expressed in Hibernate's query language + /// The names of the parameters + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If paramNames length is not equal to values length or + /// if paramNames length is not equal to types length (when types is not null) + IList FindByNamedParam(string queryString, string[] paramNames, object[] values, IType[] types); + + /// + /// Execute a named query for persistent instances. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQuery(string queryName); + + /// + /// Execute a named query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The value of the parameter + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQuery(string queryName, object value); + + /// + /// Execute a named query for persistent instances, binding + /// one value to a "?" parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The value of the parameter + /// Hibernate type of the parameter (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQuery(string queryName, object value, IType type); + + /// + /// Execute a named query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQuery(string queryName, object[] values); + + /// + /// Execute a named query for persistent instances, binding a + /// number of values to "?" parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// Hibernate types of the parameters (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If values and types are not null and their lengths differ. + IList FindByNamedQuery(string queryName, object[] values, IType[] types); + + /// + /// Execute a named query for persistent instances, binding + /// one value to a named parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// Name of the parameter + /// The value of the parameter + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQueryAndNamedParam(string queryName, string paramName, object value); + + /// + /// Execute a named query for persistent instances, binding + /// one value to a named parameter in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// Name of the parameter + /// The value of the parameter + /// The Hibernate type of the parameter (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQueryAndNamedParam(string queryName, string paramName, object value, IType type); + + /// + /// Execute a named query for persistent instances, binding + /// number of values to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The names of the parameters + /// The values of the parameters. + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQueryAndNamedParam(string queryName, string[] paramNames, object[] values); + + /// + /// Execute a named query for persistent instances, binding + /// number of values to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The names of the parameters + /// The values of the parameters. + /// Hibernate types of the parameters (or null) + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + /// If paramNames length is not equal to values length or + /// if paramNames length is not equal to types length (when types is not null) + IList FindByNamedQueryAndNamedParam(string queryName, string[] paramNames, object[] values, IType[] types); + + /// + /// Execute a named query for persistent instances, binding the properties + /// of the given object to named parameters in the query string. + /// A named query is defined in a Hibernate mapping file. + /// + /// The object type to find. + /// The name of a Hibernate query in a mapping file + /// The values of the parameters + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByNamedQueryAndValueObject(string queryName, object valueObject); + + /// + /// Execute a query for persistent instances, binding the properties + /// of the given object to named parameters in the query string. + /// + /// The object type to find. + /// A query expressed in Hibernate's query language + /// The values of the parameters + /// A generic List containing 0 or more persistent instances + /// In case of Hibernate errors + IList FindByValueObject(string queryString, object valueObject); + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning the result object. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + /// The object type retrieved. + /// The delegate callback object that specifies the Hibernate action. + /// a result object returned by the action, or null + /// + /// In case of Hibernate errors + T Execute(HibernateDelegate del); + + /// + /// Execute the action specified by the delegate within a Session. + /// + /// The object type retrieved. + /// The HibernateDelegate that specifies the action + /// to perform. + /// if set to true expose the native hibernate session to + /// callback code. + /// a result object returned by the action, or null + /// + /// In case of Hibernate errors + T Execute(HibernateDelegate del, bool exposeNativeSession); + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// The object type retrieved. + /// The callback object that specifies the Hibernate action. + /// + /// a result object returned by the action, or null + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning the result object. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + /// In case of Hibernate errors + T Execute(IHibernateCallback action); + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// The object type retrieved. + /// callback object that specifies the Hibernate action. + /// if set to true expose the native hibernate session to + /// callback code. + /// + /// a result object returned by the action, or null + /// + /// In case of Hibernate errors + T Execute(IHibernateCallback action, bool exposeNativeSession); + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning the result object. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + /// The object type to find. + /// The delegate callback object that specifies the Hibernate action. + /// A generic IList returned by the action, or null + /// + /// In case of Hibernate errors + IList ExecuteFind(FindHibernateDelegate del); + + /// + /// Execute the action specified by the delegate within a Session. + /// + /// The object type to find. + /// The FindHibernateDelegate that specifies the action + /// to perform. + /// if set to true expose the native hibernate session to + /// callback code. + /// A generic IList returned by the action, or null + /// + /// In case of Hibernate errors + IList ExecuteFind(FindHibernateDelegate del, bool exposeNativeSession); + + /// + /// Execute the action specified by the given action object within a Session. + /// + /// The object type to find. + /// The callback object that specifies the Hibernate action. + /// + /// A generic IList returned by the action, or null + /// + /// + /// Application exceptions thrown by the action object get propagated to the + /// caller (can only be unchecked). Hibernate exceptions are transformed into + /// appropriate DAO ones. Allows for returning the result object. + ///

    Note: Callback code is not supposed to handle transactions itself! + /// Use an appropriate transaction manager like HibernateTransactionManager. + /// Generally, callback code must not touch any Session lifecycle methods, + /// like close, disconnect, or reconnect, to let the template do its work. + ///

    + ///
    + IList ExecuteFind(IFindHibernateCallback action); + + /// + /// Execute the action specified by the given action object within a Session assuming that an IList is returned. + /// + /// The object type to find. + /// callback object that specifies the Hibernate action. + /// if set to true expose the native hibernate session to + /// callback code. + /// + /// an IList returned by the action, or null + /// + /// In case of Hibernate errors + IList ExecuteFind(IFindHibernateCallback action, bool exposeNativeSession); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/HibernateAccessor.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/HibernateAccessor.cs new file mode 100644 index 00000000..eb2f24c0 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/HibernateAccessor.cs @@ -0,0 +1,738 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using AopAlliance.Intercept; +using Common.Logging; +using NHibernate; +using NHibernate.Impl; +using NHibernate.Type; +using Spring.Core.TypeResolution; +using Spring.Dao; +using Spring.Data.Support; +using Spring.Objects.Factory; +using IInterceptor=NHibernate.IInterceptor; +using ICriteria=NHibernate.ICriteria; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Base class for HibernateTemplate defining common + /// properties like SessionFactory and flushing behavior. + /// + /// + ///

    Not intended to be used directly. See HibernateTemplate. + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: HibernateAccessor.cs,v 1.7 2008/01/25 15:04:28 markpollack Exp $ + public abstract class HibernateAccessor : IInitializingObject, IObjectFactoryAware + { + + private Type criteriaType; + + #region Constants + + /// + /// The instance for this class. + /// + private readonly ILog log = LogManager.GetLogger(typeof (HibernateAccessor)); + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public HibernateAccessor() + { + + } + + #endregion + + + #region Properties + + /// + /// Gets or sets if a new Session should be created when no transactional Session + /// can be found for the current thread. + /// + /// + /// true if allowed to create non-transaction session; + /// otherwise, false. + /// + /// + ///

    HibernateTemplate is aware of a corresponding Session bound to the + /// current thread, for example when using HibernateTransactionManager. + /// If allowCreate is true, a new non-transactional Session will be created + /// if none found, which needs to be closed at the end of the operation. + /// If false, an InvalidOperationException will get thrown in this case. + ///

    + ///
    + public abstract bool AllowCreate + { + get; + set; + } + + /// + /// Gets or sets a value indicating whether to always + /// use a new Hibernate Session for this template. + /// + /// true if always use new session; otherwise, false. + /// + ///

    + /// Default is "false"; if activated, all operations on this template will + /// work on a new NHibernate ISession even in case of a pre-bound ISession + /// (for example, within a transaction). + ///

    + ///

    Within a transaction, a new NHibernate ISession used by this template + /// will participate in the transaction through using the same ADO.NET + /// Connection. In such a scenario, multiple Sessions will participate + /// in the same database transaction. + ///

    + ///

    Turn this on for operations that are supposed to always execute + /// independently, without side effects caused by a shared NHibernate ISession. + ///

    + ///
    + public abstract bool AlwaysUseNewSession + { + get; + set; + } + + /// + /// Set whether to expose the native Hibernate Session to IHibernateCallback + /// code. Default is "false": a Session proxy will be returned, + /// suppressing close calls and automatically applying + /// query cache settings and transaction timeouts. + /// + /// true if expose native session; otherwise, false. + public abstract bool ExposeNativeSession + { + get; + set; + } + + /// + /// Gets or sets the template flush mode. + /// + /// + /// Default is Auto. Will get applied to any new ISession + /// created by the template. + /// + /// The template flush mode. + public abstract TemplateFlushMode TemplateFlushMode + { + get; + set; + } + + /// + /// Gets or sets the entity interceptor that allows to inspect and change + /// property values before writing to and reading from the database. + /// + /// + /// Will get applied to any new ISession created by this object. + ///

    Such an interceptor can either be set at the ISessionFactory level, + /// i.e. on LocalSessionFactoryObject, or at the ISession level, i.e. on + /// HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + /// It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + /// to avoid repeated configuration and guarantee consistent behavior in transactions. + ///

    + ///
    + /// The interceptor. + public abstract IInterceptor EntityInterceptor + { + get; + set; + } + + /// + /// Set the object name of a Hibernate entity interceptor that allows to inspect + /// and change property values before writing to and reading from the database. + /// + /// + /// Will get applied to any new Session created by this transaction manager. + ///

    Requires the object factory to be known, to be able to resolve the object + /// name to an interceptor instance on session creation. Typically used for + /// prototype interceptors, i.e. a new interceptor instance per session. + ///

    + ///

    Can also be used for shared interceptor instances, but it is recommended + /// to set the interceptor reference directly in such a scenario. + ///

    + ///
    + /// The name of the entity interceptor in the object factory/application context. + public abstract string EntityInterceptorObjectName + { + set; + } + + /// + /// Gets or sets the session factory that should be used to create + /// NHibernate ISessions. + /// + /// The session factory. + public abstract ISessionFactory SessionFactory + { + get; + set; + } + + /// + /// Set the object factory instance. + /// + /// The object factory instance. + public abstract IObjectFactory ObjectFactory + { + set; + } + + /// + /// Gets or sets a value indicating whether to + /// cache all queries executed by this template. + /// + /// + /// If this is true, all IQuery and ICriteria objects created by + /// this template will be marked as cacheable (including all + /// queries through find methods). + ///

    To specify the query region to be used for queries cached + /// by this template, set the QueryCacheRegion property. + ///

    + ///
    + /// true if cache queries; otherwise, false. + public abstract bool CacheQueries + { + get; + set; + } + + /// + /// Gets or sets the name of the cache region for queries executed by this template. + /// + /// + /// If this is specified, it will be applied to all IQuery and ICriteria objects + /// created by this template (including all queries through find methods). + ///

    The cache region will not take effect unless queries created by this + /// template are configured to be cached via the CacheQueries property. + ///

    + ///
    + /// The query cache region. + public abstract string QueryCacheRegion + { + get; + set; + } + + /// + /// Gets or sets the fetch size for this HibernateTemplate. + /// + /// The size of the fetch. + /// This is important for processing + /// large result sets: Setting this higher than the default value will increase + /// processing speed at the cost of memory consumption; setting this lower can + /// avoid transferring row data that will never be read by the application. + ///

    Default is 0, indicating to use the driver's default.

    + ///
    + public abstract int FetchSize + { + get; + set; + } + + /// + /// Gets or sets the maximum number of rows for this HibernateTemplate. + /// + /// The max results. + /// + /// This is important + /// for processing subsets of large result sets, avoiding to read and hold + /// the entire result set in the database or in the ADO.NET driver if we're + /// never interested in the entire result in the first place (for example, + /// when performing searches that might return a large number of matches). + ///

    Default is 0, indicating to use the driver's default.

    + ///
    + public abstract int MaxResults + { + get; + set; + } + + /// + /// Set the ADO.NET exception translator for this instance. + /// Applied to System.Data.Common.DbException (or provider specific exception type + /// in .NET 1.1) thrown by callback code, be it direct + /// DbException or wrapped Hibernate ADOExceptions. + ///

    The default exception translator is either a ErrorCodeExceptionTranslator + /// if a DbProvider is available, or a FalbackExceptionTranslator otherwise + ///

    + ///
    + /// The ADO exception translator. + public abstract IAdoExceptionTranslator AdoExceptionTranslator + { + set; + get; + } + + /// + /// Gets a Session for use by this template. + /// + /// The session. + /// + /// - Returns a new Session in case of "alwaysUseNewSession" (using the same ADO.NET connection as a transaction Session, if applicable) + /// - a pre-bound Session in case of "AllowCreate" is set to false (not the default) + /// - or a pre-bound Session or new Session if no transactional or other pre-bound Session exists. + /// + protected ISession Session + { + get + { + if (AlwaysUseNewSession) + { + return SessionFactoryUtils.GetNewSession(SessionFactory, EntityInterceptor); + } + else if (!AllowCreate) + { + return SessionFactoryUtils.GetSession(SessionFactory, false); + } + else + { + return SessionFactoryUtils.GetSession( + SessionFactory, EntityInterceptor, AdoExceptionTranslator); + } + + } + + } + + #endregion + + #region Methods + + /// + /// Apply the flush mode that's been specified for this accessor + /// to the given Session. + /// + /// The current Hibernate Session. + /// if set to true + /// if executing within an existing transaction. + /// + /// the previous flush mode to restore after the operation, + /// or null if none + /// + protected FlushModeHolder ApplyFlushMode(ISession session, bool existingTransaction) + { + if (TemplateFlushMode == TemplateFlushMode.Never) + { + if (existingTransaction) + { + FlushMode previousFlushMode = session.FlushMode; + if (previousFlushMode != FlushMode.Never) + { + session.FlushMode = FlushMode.Never; + return new FlushModeHolder(previousFlushMode); + } + } + else + { + session.FlushMode = FlushMode.Never; + } + } + else if (TemplateFlushMode == TemplateFlushMode.Eager) + { + if (existingTransaction) + { + FlushMode previousFlushMode = session.FlushMode; + if (previousFlushMode != FlushMode.Auto) + { + session.FlushMode = FlushMode.Auto; + return new FlushModeHolder(previousFlushMode); + } + } + else + { + // rely on default FlushMode.AUTO + } + } + else if (TemplateFlushMode == TemplateFlushMode.Commit) + { + if (existingTransaction) + { + FlushMode previousFlushMode = session.FlushMode; + if (previousFlushMode == FlushMode.Auto) + { + session.FlushMode = FlushMode.Commit; + return new FlushModeHolder(previousFlushMode); + } + } + else + { + session.FlushMode = FlushMode.Commit; + } + } + return new FlushModeHolder(); + } + + + /// + /// Flush the given Hibernate Session if necessary. + /// + /// The current Hibernate Session. + /// if set to true + /// if executing within an existing transaction. + protected void FlushIfNecessary(ISession session, bool existingTransaction) + { + if (TemplateFlushMode == TemplateFlushMode.Eager || + (!existingTransaction && TemplateFlushMode != TemplateFlushMode.Never)) + { + log.Debug("Eagerly flushing Hibernate session"); + session.Flush(); + } + } + + /// + /// Convert the given HibernateException to an appropriate exception from the + /// org.springframework.dao hierarchy. Will automatically detect + /// wrapped ADO.NET Exceptions and convert them accordingly. + /// + /// HibernateException that occured. + /// + /// The corresponding DataAccessException instance + /// + /// + /// The default implementation delegates to SessionFactoryUtils + /// and convertAdoAccessException. Can be overridden in subclasses. + /// + public virtual DataAccessException ConvertHibernateAccessException(HibernateException ex) + { + if (ex is ADOException) + { + return ConvertAdoAccessException((ADOException) ex); + } + return SessionFactoryUtils.ConvertHibernateAccessException(ex); + } + + /// + /// Converts the ADO.NET access exception to an appropriate exception from the + /// org.springframework.dao hierarchy. Can be overridden in subclasses. + /// + /// ADOException that occured, wrapping underlying ADO.NET exception. + /// + /// the corresponding DataAccessException instance + /// + protected virtual DataAccessException ConvertAdoAccessException(ADOException ex) + { + + string sqlString = (ex.SqlString != null) + ? ex.SqlString.ToString() + : string.Empty; + return AdoExceptionTranslator.Translate( + "Hibernate operation: " + ex.Message, sqlString, ex.InnerException); + + } + + /// + /// Converts the ADO.NET access exception to an appropriate exception from the + /// org.springframework.dao hierarchy. Can be overridden in subclasses. + /// + /// + /// + /// Note that a direct SQLException can just occur when callback code + /// performs direct ADO.NET access via ISession.Connection(). + /// + /// + /// The ADO.NET exception. + /// The corresponding DataAccessException instance + protected virtual DataAccessException ConvertAdoAccessException(Exception ex) + { + return AdoExceptionTranslator.Translate("Hibernate operation", null, ex); + } + + + /// + /// Prepare the given IQuery object, applying cache settings and/or + /// a transaction timeout. + /// + /// The query object to prepare. + public virtual void PrepareQuery(IQuery queryObject) + { + if (CacheQueries) + { + queryObject.SetCacheable(true); + if (QueryCacheRegion != null) + { + queryObject.SetCacheRegion(QueryCacheRegion); + } + } + + if (FetchSize > 0) + { + AbstractQueryImpl queryImpl = queryObject as AbstractQueryImpl; + if (queryImpl != null) + { + queryImpl.SetFetchSize(FetchSize); + } + else + { + log.Warn("Could not set FetchSize for IQuery. Expected Implemention to be of type AbstractQueryImpl"); + } + } + + if (MaxResults > 0) + { + queryObject.SetMaxResults(MaxResults); + } + + SessionFactoryUtils.ApplyTransactionTimeout(queryObject, SessionFactory); + + } + + /// + /// Apply the given name parameter to the given Query object. + /// + /// The query object. + /// Name of the parameter + /// The value of the parameter + /// The NHibernate type of the parameter (or null if none specified) + public virtual void ApplyNamedParameterToQuery(IQuery queryObject, string paramName, object value, IType type) + { + + if (value is ICollection) + { + if (type != null) + { + queryObject.SetParameterList(paramName, (ICollection)value, type); + } + else + { + queryObject.SetParameterList(paramName, (ICollection)value); + } + } + else if (value is Object[]) + { + + //TODO investigate support for this conversion. + if (type != null) + { + queryObject.SetParameterList(paramName, (Object[])value, type); + } + else + { + queryObject.SetParameterList(paramName, (Object[])value); + } + } + else + { + if (type != null) + { + queryObject.SetParameter(paramName, value, type); + } + else + { + queryObject.SetParameter(paramName, value); + } + } + } + + /// + /// Prepare the given Criteria object, applying cache settings and/or + /// a transaction timeout. + /// + /// + /// Note that for NHibernate 1.2 this only works if the + /// implementation is of the type CriteriaImpl, which should generally + /// be the case. The SetFetchSize method is not available on the + /// ICriteria interface + /// + /// This is a no-op for NHibernate 1.0.x since + /// the SetFetchSize method is not on the ICriteria interface and + /// the implementation class is has internal access. + /// + /// To remove the method completely for Spring's NHibernate 1.0 + /// support while reusing code for NHibernate 1.2 would not be + /// possible. So now this ineffectual operation is left in tact for + /// NHibernate 1.0.2 support. + /// + /// The criteria object to prepare + public void PrepareCriteria(ICriteria criteria) + { + if (CacheQueries) + { + criteria.SetCacheable(true); + if (QueryCacheRegion != null) + { + criteria.SetCacheRegion(QueryCacheRegion); + } + } + + + + if (FetchSize > 0) + { + //TODO see if we can optimize performance. + //CriteriaImpl is internal in NH 1.0.x + object[] args = new object[] { FetchSize }; + try + { + Type t = TypeResolutionUtils.ResolveType("NHibernate.Impl.CriteriaImpl, NHibernate"); + if (t != null) + { + t.InvokeMember("SetFetchSize", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance + | BindingFlags.InvokeMethod, + null, criteria, args); + } + } + catch (TypeLoadException e) + { + log.Warn("Can't set FetchSize for ICriteria", e); + } + } + + if (MaxResults > 0) + { + criteria.SetMaxResults(MaxResults); + } + + SessionFactoryUtils.ApplyTransactionTimeout(criteria, SessionFactory); + } + + private void InitCriteriaType() + { + + try + { + criteriaType = TypeResolutionUtils.ResolveType("Hibernate.Impl.CriteriaImpl, NHibernate"); + } + catch (TypeLoadException e) + { + log.Warn("CriteriaImpl not available. FetchSize can not be set on ICriteria objects", e); + } + } + + #endregion + + /// + /// Ensure SessionFactory is not null + /// + /// If SessionFactory property is null. + public virtual void AfterPropertiesSet() + { + if (SessionFactory == null) + { + throw new ArgumentException("sessionFactory is required"); + } + } + + #region Helper Classes + /// + /// Helper class to determine if the FlushMode enumeration + /// was changed from its default value + /// + protected class FlushModeHolder + { + /// + /// Gets or sets a value indicating whether the FlushMode + /// property was set.. + /// + /// true if FlushMode was set; otherwise, false. + public bool ModeWasSet + { + get { return modeWasSet; } + set { modeWasSet = value; } + } + + /// + /// Gets or sets the FlushMode. + /// + /// The FlushMode. + public FlushMode Mode + { + get { return flushMode; } + set { flushMode = value; } + } + + private bool modeWasSet = false; + private FlushMode flushMode; + + /// + /// Initializes a new instance of the class. + /// + public FlushModeHolder() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The flush mode. + public FlushModeHolder(FlushMode mode) + { + Mode = mode; + ModeWasSet = true; + } + + } + + + #endregion + + + } + + internal class CloseSuppressingMethodInterceptor : IMethodInterceptor + { + private HibernateAccessor hibernateAccessor; + + public CloseSuppressingMethodInterceptor(HibernateAccessor accessor) + { + hibernateAccessor = accessor; + } + + public object Invoke(IMethodInvocation invocation) + { + //anything special for equals/hashcode? + if (invocation.Method.Name.Equals("Close")) + { + return null; + } + else + { + object retValue = invocation.Proceed(); + if (retValue is IQuery) + { + hibernateAccessor.PrepareQuery((IQuery)retValue); + } + if (retValue is ICriteria) + { + hibernateAccessor.PrepareCriteria((ICriteria)retValue); + } + return retValue; + } + } + } +} diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/HibernateOptimisticLockingFailureException.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/HibernateOptimisticLockingFailureException.cs new file mode 100644 index 00000000..a196d416 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/HibernateOptimisticLockingFailureException.cs @@ -0,0 +1,99 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using NHibernate; +using Spring.Dao; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Hibernate-specific subclass of ObjectOptimisticLockingFailureException. + /// + /// + /// Converts Hibernate's StaleObjectStateException. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: HibernateOptimisticLockingFailureException.cs,v 1.1 2008/04/08 20:26:30 markpollack Exp $ + [Serializable] + public class HibernateOptimisticLockingFailureException : ObjectOptimisticLockingFailureException + { + #region Fields + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public HibernateOptimisticLockingFailureException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The StaleObjectStateException. + public HibernateOptimisticLockingFailureException(StaleObjectStateException ex) : base(ex.PersistentType, ex.Identifier, ex.Message, ex) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The StaleStateException. + public HibernateOptimisticLockingFailureException(StaleStateException ex) : base(ex.Message, ex) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected HibernateOptimisticLockingFailureException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/LocalSessionFactoryObject.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/LocalSessionFactoryObject.cs new file mode 100644 index 00000000..e3232651 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/LocalSessionFactoryObject.cs @@ -0,0 +1,420 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Data; +using Common.Logging; +using NHibernate; +using NHibernate.Cfg; +using NHibernate.Connection; +using Spring.Core.IO; +using Spring.Data.Common; +using Spring.Objects.Factory; +using Environment = NHibernate.Cfg.Environment; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// An IFactoryObject that creates a local Hibernate SessionFactory instance. + /// Behaves like a SessionFactory instance when used as bean reference, + /// e.g. for HibernateTemplate's "SessionFactory" property. + /// + /// + /// The typical usage will be to register this as singleton factory + /// in an application context and give objects references to application services + /// that need it. + /// + /// Hibernate configuration settings can be set using the IDictionary property 'HibernateProperties'. + /// + /// + /// + /// Mark Pollack (.NET) + /// $Id: LocalSessionFactoryObject.cs,v 1.11 2008/03/21 14:12:08 markpollack Exp $ + public class LocalSessionFactoryObject : IFactoryObject, IInitializingObject, System.IDisposable + { + #region Fields + + private Configuration configuration; + + private ISessionFactory sessionFactory; + + private string[] mappingAssemblies; + + private string[] mappingResources; + + private string[] configFilenames; + + /// + /// TODO: consider changing to NamevalueCollection for easier + /// cut-n-paste from existing App.config based configurations. + /// + private IDictionary hibernateProperties; + + private IDbProvider dbProvider; + + private bool exposeTransactionAwareSessionFactory = false; + + #endregion + + #region Constants + + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (LocalSessionFactoryObject)); + + + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public LocalSessionFactoryObject() + { + + } + + #endregion + + #region Properties + + /// + /// Sets the assemblies to load that contain mapping files. + /// + /// The mapping assemblies. + public string[] MappingAssemblies + { + set { mappingAssemblies = value; } + } + + /// + /// Sets the hibernate configuration files to load, i.e. hibernate.cfg.xml. + /// + public string[] ConfigFilenames + { + set { configFilenames = value; } + } + + /// + /// Sets the locations of Spring IResources that contain mapping + /// files. + /// + /// The location of mapping resources. + public string[] MappingResources + { + set { mappingResources = value;} + } + + /// + /// Return the Configuration object used to build the SessionFactory. + /// Allows access to configuration metadata stored there (rarely needed). + /// + /// The hibernate configuration. + public Configuration Configuration + { + get { return configuration; } + } + + /// + /// Set NHibernate configuration properties, like "hibernate.dialect". + /// + /// The hibernate properties. + /// + ///

    Can be used to override values in a NHibernate XML config file, + /// or to specify all necessary properties locally. + ///

    + ///

    Note: Do not specify a transaction provider here when using + /// Spring-driven transactions. It is also advisable to omit connection + /// provider settings and use a Spring-set IDbProvider instead. + ///

    + ///
    + public IDictionary HibernateProperties + { + get + { + if (hibernateProperties == null) + { + hibernateProperties = new Hashtable(); + } + return hibernateProperties; + } + set + { + hibernateProperties = value; + } + } + + /// + /// Get or set the DataSource to be used by the SessionFactory. + /// + /// The db provider. + /// + /// If set, this will override corresponding settings in Hibernate properties. + /// Note: If this is set, the Hibernate settings should not define + /// a connection string + /// (hibernate.connection.connection_string) to avoid meaningless double configuration. + /// + /// + public IDbProvider DbProvider + { + set { dbProvider = value; } + get { return dbProvider; } + } + + /// + /// Gets or sets a value indicating whether to expose a transaction aware session factory. + /// + /// + /// true if want to expose transaction aware session factory; otherwise, false. + /// + public bool ExposeTransactionAwareSessionFactory + { + set { exposeTransactionAwareSessionFactory = value; } + get { return exposeTransactionAwareSessionFactory; } + } + + #endregion + + #region Methods + + #endregion + + /// + /// Return the singleon session factory. + /// + /// The singleon session factory. + public object GetObject() + { + return sessionFactory; + } + + /// + /// Return the type or subclass. + /// + /// The type created by this factory + public System.Type ObjectType + { + get + { + return (sessionFactory != null) ? sessionFactory.GetType() : typeof(ISessionFactory); + } + } + + /// + /// Returns true + /// + /// true + public bool IsSingleton + { + get + { + return true; + } + } + + /// + /// Initialize the SessionFactory for the given or the + /// default location. + /// + public virtual void AfterPropertiesSet() + { + // Create Configuration instance. + Configuration config = NewConfiguration(); + + + if (this.dbProvider != null) + { + config.SetProperty(Environment.ConnectionString, + dbProvider.ConnectionString); + config.SetProperty(Environment.ConnectionProvider, typeof(DbProviderWrapper).AssemblyQualifiedName); + + } + + if (ExposeTransactionAwareSessionFactory) + { + // Set ICurrentSessionContext implementation, + // providng the Spring-managed ISession s current Session. + // Can be overridden by a custom value for the corresponding Hibernate property + config.SetProperty(Environment.CurrentSessionContextClass, + "Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate12"); + } + + if (this.hibernateProperties != null) + { + if (config.GetProperty(Environment.ConnectionProvider) !=null && + hibernateProperties.Contains(Environment.ConnectionProvider)) + { + #region Logging + if (log.IsWarnEnabled) + { + log.Warn("Overriding use of Spring's Hibernate Connection Provider with [" + + hibernateProperties[Environment.ConnectionProvider] + "]"); + } + #endregion + config.Properties.Remove(Environment.ConnectionProvider); + } + config.AddProperties(hibernateProperties); + } + if (this.mappingAssemblies != null) + { + foreach (string assemblyName in mappingAssemblies) + { + config.AddAssembly(assemblyName); + } + } + + IResourceLoader resourceLoader = new ConfigurableResourceLoader(); + + if (this.mappingResources != null) + { + foreach (string resourceName in mappingResources) + { + config.AddInputStream(resourceLoader.GetResource(resourceName).InputStream); + } + } + + if (configFilenames != null) + { + foreach (string configFilename in configFilenames) + { + config.Configure(configFilename); + } + } + + // Perform custom post-processing in subclasses. + PostProcessConfiguration(config); + + // Build SessionFactory instance. + log.Info("Building new Hibernate SessionFactory"); + this.configuration = config; + this.sessionFactory = NewSessionFactory(config); + + + } + + /// + /// Close the SessionFactory on application context shutdown. + /// + public void Dispose() + { + if (sessionFactory != null) + { + #region Instrumentation + if (log.IsInfoEnabled) + { + log.Info("Closing Hibernate SessionFactory"); + } + #endregion + sessionFactory.Close(); + } + + } + + /// + /// Subclasses can override this method to perform custom initialization + /// of the Configuration instance used for ISessionFactory creation. + /// + /// + /// The properties of this LocalSessionFactoryObject will be applied to + /// the Configuration object that gets returned here. + ///

    The default implementation creates a new Configuration instance. + /// A custom implementation could prepare the instance in a specific way, + /// or use a custom Configuration subclass. + ///

    + ///
    + /// The configuration instance. + protected virtual Configuration NewConfiguration() + { + return new Configuration(); + } + + /// + /// To be implemented by subclasses that want to to perform custom + /// post-processing of the Configuration object after this FactoryObject + /// performed its default initialization. + /// + /// The current configuration object. + protected virtual void PostProcessConfiguration(Configuration config) + { + } + + /// + /// Subclasses can override this method to perform custom initialization + /// of the SessionFactory instance, creating it via the given Configuration + /// object that got prepared by this LocalSessionFactoryObject. + /// + /// + ///

    The default implementation invokes Configuration's BuildSessionFactory. + /// A custom implementation could prepare the instance in a specific way, + /// or use a custom ISessionFactory subclass. + ///

    + ///
    + /// The ISessionFactory instance. + protected virtual ISessionFactory NewSessionFactory(Configuration config) + { + ISessionFactory sf = config.BuildSessionFactory(); + DbProviderWrapper dbProviderWrapper = sf.ConnectionProvider as DbProviderWrapper; + if (dbProviderWrapper != null) + { + dbProviderWrapper.DbProvider = dbProvider; + } + return sf; + } + + #region DbProviderWrapper Helper class + + internal class DbProviderWrapper : ConnectionProvider + { + private IDbProvider _dbProvider; + + public DbProviderWrapper() + { + } + + public IDbProvider DbProvider + { + get { return _dbProvider; } + set { _dbProvider = value; } + } + + public override void CloseConnection(IDbConnection conn) + { + base.CloseConnection(conn); + conn.Dispose(); + } + + public override IDbConnection GetConnection() + { + IDbConnection dbCon = _dbProvider.CreateConnection(); + dbCon.Open(); + return dbCon; + } + } + + #endregion // DbProviderWrapper Helper class + } +} diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SessionFactoryUtils.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SessionFactoryUtils.cs new file mode 100644 index 00000000..9361bbb4 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SessionFactoryUtils.cs @@ -0,0 +1,718 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Common.Logging; +using NHibernate; +using NHibernate.Connection; +using NHibernate.Driver; +using NHibernate.Engine; +using Spring.Collections; +using Spring.Context; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Data.Support; +using Spring.Objects.Factory.Config; +using Spring.Threading; +using Spring.Transaction.Support; +using Spring.Util; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Helper class featuring methods for Hibernate Session handling, + /// allowing for reuse of Hibernate Session instances within transactions. + /// Also provides support for exception translation. + /// + /// Mark Pollack (.NET) + /// $Id: SessionFactoryUtils.cs,v 1.1 2008/04/08 20:26:30 markpollack Exp $ + public abstract class SessionFactoryUtils + { + #region Fields + + /// + /// The instance for this class. + /// + private static readonly ILog log = LogManager.GetLogger(typeof(SessionFactoryUtils)); + + #endregion + + #region Constants + + /// + /// The ordering value for synchronizaiton this session resources. + /// Set to be lower than ADO.NET synchronization. + /// + public static readonly int SESSION_SYNCHRONIZATION_ORDER = + AdoUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100; + + private static readonly string DeferredCloseHolderDataSlotName = "Spring.Data.NHibernate:deferredCloseHolder"; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public SessionFactoryUtils() + { + + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + /// + /// Get a new Hibernate Session from the given SessionFactory. + /// Will return a new Session even if there already is a pre-bound + /// Session for the given SessionFactory. + /// + /// + /// Within a transaction, this method will create a new Session + /// that shares the transaction's ADO.NET Connection. More specifically, + /// it will use the same ADO.NET Connection as the pre-bound Hibernate Session. + /// + /// The session factory to create the session with. + /// The Hibernate entity interceptor, or null if none. + /// The new session. + /// If could not open Hibernate session + public static ISession GetNewSession(ISessionFactory sessionFactory, IInterceptor interceptor) + { + try + { + SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory); + if (sessionHolder != null && !sessionHolder.IsEmpty) + { + if (interceptor != null) + { + return sessionFactory.OpenSession(sessionHolder.AnySession.Connection, interceptor); + } + else + { + return sessionFactory.OpenSession(sessionHolder.AnySession.Connection); + } + } + else + { + if (interceptor != null) + { + return sessionFactory.OpenSession(interceptor); + } + else + { + return sessionFactory.OpenSession(); + } + } + } + catch (HibernateException ex) + { + throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex); + } + } + + /// + /// Get a Hibernate Session for the given SessionFactory. Is aware of and will + /// return any existing corresponding Session bound to the current thread, for + /// example when using HibernateTransactionManager. Will always create a new + /// Session otherwise. + /// + /// + /// Supports setting a Session-level Hibernate entity interceptor that allows + /// to inspect and change property values before writing to and reading from the + /// database. Such an interceptor can also be set at the SessionFactory level + /// (i.e. on LocalSessionFactoryObject), on HibernateTransactionManager, or on + /// HibernateInterceptor/HibernateTemplate. + /// + /// The session factory to create the + /// session with. + /// Hibernate entity interceptor, or null if none. + /// AdoExceptionTranslator to use for flushing the + /// Session on transaction synchronization (can be null; only used when actually + /// registering a transaction synchronization). + /// The Hibernate Session + /// + /// If the session couldn't be created. + /// + /// + /// If no thread-bound Session found and allowCreate is false. + /// + public static ISession GetSession( + ISessionFactory sessionFactory, IInterceptor entityInterceptor, + IAdoExceptionTranslator adoExceptionTranslator) + { + try + { + return GetSession(sessionFactory, entityInterceptor, adoExceptionTranslator, true); + } + catch (HibernateException ex) + { + throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex); + } + } + + /// + /// Get a Hibernate Session for the given SessionFactory. Is aware of and will + /// return any existing corresponding Session bound to the current thread, for + /// example when using . Will create a new Session + /// otherwise, if allowCreate is true. + /// + /// The session factory to create the session with. + /// if set to true create a non-transactional Session when no + /// transactional Session can be found for the current thread. + /// The hibernate session + /// + /// If the session couldn't be created. + /// + /// + /// If no thread-bound Session found and allowCreate is false. + /// + public static ISession GetSession(ISessionFactory sessionFactory, bool allowCreate) + { + try + { + return GetSession(sessionFactory, null, null, allowCreate); + } + catch (HibernateException ex) + { + throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex); + } + } + + /// + /// Get a Hibernate Session for the given SessionFactory. + /// + /// Is aware of and will return any existing corresponding + /// Session bound to the current thread, for example whenusing + /// . Will create a new + /// Session otherwise, if "allowCreate" is true. + ///

    Throws the orginal HibernateException, in contrast to + /// . + ///

    + /// The session factory. + /// if set to true [allow create]. + /// The Hibernate Session + /// + /// if the Session couldn't be created + /// + /// + /// If no thread-bound Session found and allowCreate is false. + /// + public static ISession DoGetSession(ISessionFactory sessionFactory, bool allowCreate) + { + return DoGetSession(sessionFactory, null, null, allowCreate); + } + + private static ISession GetSession( + ISessionFactory sessionFactory, IInterceptor entityInterceptor, + IAdoExceptionTranslator adoExceptionTranslator, bool allowCreate) + { + try + { + return DoGetSession(sessionFactory, entityInterceptor, adoExceptionTranslator, allowCreate); + } + catch (HibernateException ex) + { + throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex); + } + } + + + private static ISession DoGetSession( + ISessionFactory sessionFactory, IInterceptor entityInterceptor, + IAdoExceptionTranslator adoExceptionTranslator, bool allowCreate) + { + AssertUtils.ArgumentNotNull(sessionFactory, "sessionFactory", "SessionFactory can not be null"); + + SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory); + if (sessionHolder != null && !sessionHolder.IsEmpty) + { + // pre-bound Hibernate Session + ISession session = null; + if (TransactionSynchronizationManager.SynchronizationActive && + sessionHolder.DoesNotHoldNonDefaultSession) + { + // Spring transaction management is active -> + // register pre-bound Session with it for transactional flushing. + session = sessionHolder.ValidatedSession; + if (!sessionHolder.SynchronizedWithTransaction) + { + log.Debug("Registering Spring transaction synchronization for existing Hibernate Session"); + TransactionSynchronizationManager.RegisterSynchronization( + new SpringSessionSynchronization(sessionHolder, sessionFactory, adoExceptionTranslator, false)); + sessionHolder.SynchronizedWithTransaction = true; + // Switch to FlushMode.AUTO if we're not within a read-only transaction. + FlushMode flushMode = session.FlushMode; + if (FlushMode.Never == flushMode && + !TransactionSynchronizationManager.CurrentTransactionReadOnly) + { + session.FlushMode = FlushMode.Auto; + sessionHolder.PreviousFlushMode = flushMode; + } + } + } + else + { + // No Spring transaction management active -> simply return default thread-bound Session, if any + // (possibly from OpenSessionInViewModule) + session = sessionHolder.ValidatedSession; + } + + if (session != null) + { + return session; + } + + } + + + ISession sess = OpenSession(sessionFactory, entityInterceptor); + // Set Session to FlushMode.Never if we're within a read-only transaction. + // Use same Session for further Hibernate actions within the transaction. + // Thread object will get removed by synchronization at transaction completion. + if (TransactionSynchronizationManager.SynchronizationActive) + { + log.Debug("Registering Spring transaction synchronization for new Hibernate Session"); + SessionHolder holderToUse = sessionHolder; + if (holderToUse == null) + { + holderToUse = new SessionHolder(sess); + } + else + { + holderToUse.AddSession(sess); + } + if (TransactionSynchronizationManager.CurrentTransactionReadOnly) + { + sess.FlushMode = FlushMode.Never; + } + TransactionSynchronizationManager.RegisterSynchronization( + new SpringSessionSynchronization(holderToUse, sessionFactory, adoExceptionTranslator, true)); + holderToUse.SynchronizedWithTransaction = true; + if (holderToUse != sessionHolder) + { + TransactionSynchronizationManager.BindResource(sessionFactory, holderToUse); + } + } + + + + // Check whether we are allowed to return the Session. + if (!allowCreate && !IsSessionTransactional(sess, sessionFactory)) + { + CloseSession(sess); + throw new InvalidOperationException ("No Hibernate Session bound to thread, " + + "and configuration does not allow creation of non-transactional one here"); + } + + return sess; + + + } + + /// + /// Open a new Session from the factory. + /// + /// The session factory to create the session with. + /// Hibernate entity interceptor, or null if none. + /// the newly opened session + internal static ISession OpenSession(ISessionFactory sessionFactory, IInterceptor entityInterceptor) + { + log.Debug("Opening Hibernate Session"); + ISession session = ( + (entityInterceptor != null) + ? sessionFactory.OpenSession(entityInterceptor) + : sessionFactory.OpenSession() + ); + + return session; + } + + /// + /// Perform the actual closing of the Hibernate Session + /// catching and logging any cleanup exceptions thrown. + /// + /// The hibernate session to close + public static void CloseSession(ISession session) + { + if (session != null) + { + log.Debug("Closing Hibernate Session"); + try + { + session.Close(); + } + catch (HibernateException ex) + { + log.Error("Could not close Hibernate Session", ex); + } + catch (Exception ex) + { + log.Error("Unexpected exception on closing Hibernate Session", ex); + } + } + } + + /// + /// Return whether the given Hibernate Session is transactional, that is, + /// bound to the current thread by Spring's transaction facilities. + /// + /// The hibernate session to check + /// The session factory that the session + /// was created with, can be null. + /// + /// true if the session transactional; otherwise, false. + /// + public static bool IsSessionTransactional(ISession session, ISessionFactory sessionFactory) + { + if (sessionFactory == null) + { + return false; + } + SessionHolder sessionHolder = + (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory); + return (sessionHolder != null && sessionHolder.ContainsSession(session)); + } + + #endregion + + /// + /// Convert the given HibernateException to an appropriate exception from the + /// Spring.Dao hierarchy. Note that it is advisable to + /// handle AdoException specifically by using a AdoExceptionTranslator for the + /// underlying ADO.NET exception. + /// + /// The Hibernate exception that occured. + /// DataAccessException instance + public static DataAccessException ConvertHibernateAccessException(HibernateException ex) + { + if (ex is ADOException) + { + // ADOException during Hibernate access: only passed in here from custom code, + // as HibernateTemplate etc will use AdoExceptionTranslator-based handling. + return new HibernateAdoException("Ado Exception", (ADOException) ex); + } + if (ex is UnresolvableObjectException) + { + return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex); + } + if (ex is ObjectDeletedException) + { + return new InvalidDataAccessApiUsageException(ex.Message, ex); + } + if (ex is WrongClassException) + { + return new HibernateObjectRetrievalFailureException((WrongClassException) ex); + } + if (ex is StaleObjectStateException) + { + return new HibernateOptimisticLockingFailureException((StaleObjectStateException) ex); + } + if (ex is StaleStateException) + { + return new HibernateOptimisticLockingFailureException((StaleStateException)ex); + } + if (ex is QueryException) + { + return new HibernateQueryException((QueryException) ex); + } + + if (ex is PersistentObjectException) + { + return new InvalidDataAccessApiUsageException(ex.Message, ex); + } + if (ex is TransientObjectException) + { + return new InvalidDataAccessApiUsageException(ex.Message, ex); + } + + if (ex is PropertyValueException) + { + return new DataIntegrityViolationException(ex.Message, ex); + } + if (ex is PersistentObjectException) + { + return new InvalidDataAccessApiUsageException(ex.Message, ex); + } + if (ex is NonUniqueResultException) + { + return new IncorrectResultSizeDataAccessException(ex.Message, 1); + } + // fallback + return new HibernateSystemException(ex); + } + + /// + /// Close the given Session, created via the given factory, + /// if it is not managed externally (i.e. not bound to the thread). + /// + /// The hibernate session to close + /// The hibernate SessionFactory that + /// the session was created with. + public static void ReleaseSession(ISession session, ISessionFactory sessionFactory) + { + if (session == null) + { + return; + } + // Only close non-transactional Sessions. + if (!IsSessionTransactional(session, sessionFactory)) + { + CloseSessionOrRegisterDeferredClose(session, sessionFactory); + } + } + + /// + /// Close the given Session or register it for deferred close. + /// + /// The session. + /// The session factory. + internal static void CloseSessionOrRegisterDeferredClose(ISession session, ISessionFactory sessionFactory) + { + IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary; + + if (holderDictionary != null && sessionFactory != null && holderDictionary.Contains(sessionFactory)) + { + log.Debug("Registering Hibernate Session for deferred close"); + // Switch Session to FlushMode.NEVER for remaining lifetime. + session.FlushMode = FlushMode.Never; + Set sessions = (Set) holderDictionary[sessionFactory]; + sessions.Add(session); + } + else + { + CloseSession(session); + } + } + + /// + ///Initialize deferred close for the current thread and the given SessionFactory. + /// Sessions will not be actually closed on close calls then, but rather at a + /// processDeferredClose call at a finishing point (like request completion). + /// + /// The session factory. + public static void InitDeferredClose(ISessionFactory sessionFactory) + { + AssertUtils.ArgumentNotNull(sessionFactory, "No SessionFactory specified"); + + log.Debug("Initializing deferred close of Hibernate Sessions"); + + IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary; + + if (holderDictionary == null) + { + holderDictionary = new Hashtable(); + LogicalThreadContext.SetData(DeferredCloseHolderDataSlotName, holderDictionary); + } + holderDictionary.Add(sessionFactory, new ListSet()); + } + + /// + /// Return if deferred close is active for the current thread + /// and the given SessionFactory. + /// The session factory. + /// + /// true if [is deferred close active] [the specified session factory]; otherwise, false. + /// + /// If SessionFactory argument is null. + public static bool IsDeferredCloseActive(ISessionFactory sessionFactory) + { + if (sessionFactory == null) + { + throw new ArgumentNullException("sessionFactory", "No SessionFactory specified"); + } + IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary; + return (holderDictionary != null && holderDictionary.Contains(sessionFactory)); + } + + /// + /// Process Sessions that have been registered for deferred close + /// for the given SessionFactory. + /// + /// The session factory. + /// If there is no session factory associated with the thread. + public static void ProcessDeferredClose(ISessionFactory sessionFactory) + { + AssertUtils.ArgumentNotNull(sessionFactory, "No SessionFactory specified"); + + IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary; + + if (holderDictionary == null || !holderDictionary.Contains(sessionFactory)) + { + throw new InvalidOperationException("Deferred close not active for SessionFactory [" + sessionFactory + "]"); + } + log.Debug("Processing deferred close of Hibernate Sessions"); + Set sessions = (Set) holderDictionary[sessionFactory]; + holderDictionary.Remove(sessionFactory); + foreach (ISession session in sessions) + { + CloseSession(session); + } + + if (holderDictionary.Count == 0) + { + LogicalThreadContext.FreeNamedDataSlot(DeferredCloseHolderDataSlotName); + } + + + } + + /// + /// Applies the current transaction timeout, if any, to the given + /// criteria object + /// + /// The Hibernate Criteria object. + /// Hibernate SessionFactory that the Criteria was created for + /// (can be null). + /// If criteria argument is null. + public static void ApplyTransactionTimeout(ICriteria criteria, ISessionFactory sessionFactory) + { + if (criteria == null) + { + throw new ArgumentNullException("criteria", "No Criteria object specified"); + } + + SessionHolder sessionHolder = + (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory); + if (sessionHolder != null && sessionHolder.HasTimeout) + { + criteria.SetTimeout(sessionHolder.TimeToLiveInSeconds); + } + } + + /// + /// Applies the current transaction timeout, if any, to the given + /// Hibenrate query object. + /// + /// The Hibernate Query object. + /// Hibernate SessionFactory that the Query was created for + /// (can be null). + /// If query argument is null. + public static void ApplyTransactionTimeout(IQuery query, ISessionFactory sessionFactory) + { + if (query == null) + { + throw new ArgumentNullException("queryObject", "No query object specified"); + } + if (sessionFactory != null) + { + SessionHolder sessionHolder = + (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory); + if (sessionHolder != null && sessionHolder.HasTimeout) + { + query.SetTimeout(sessionHolder.TimeToLiveInSeconds); + } + } + } + /// + /// Gets the Spring IDbProvider given the ISessionFactory. + /// + /// The matching is performed by comparing the assembly qualified + /// name string of the hibernate Driver.ConnectionType to those in + /// the DbProviderFactory definitions. No connections are created + /// in performing this comparison. + /// The session factory. + /// The corresponding IDbProvider, null if no mapping was found. + /// If DbProviderFactory's ApplicaitonContext is not + /// an instance of IConfigurableApplicaitonContext. + public static IDbProvider GetDbProvider(ISessionFactory sessionFactory) + { + ISessionFactoryImplementor sfi = sessionFactory as ISessionFactoryImplementor; + if (sfi != null) + { + + ConnectionProvider cp = sfi.ConnectionProvider as ConnectionProvider; + if (cp != null) + { + IConfigurableApplicationContext ctx = + Spring.Data.Common.DbProviderFactory.ApplicationContext as IConfigurableApplicationContext; + if (ctx == null) + { + throw new InvalidOperationException( + "Implementations of IApplicationContext must also implement IConfigurableApplicationContext"); + } + + + DriverBase db = cp.Driver as DriverBase; + if (db != null) + { + Type hibCommandType = db.CreateCommand().GetType(); + //Type hibConnectionType = cp.Driver.ConnectionType; + + string[] providerNames = ctx.GetObjectNamesForType(typeof(DbProvider), true, false); + string hibCommandAQN = hibCommandType.AssemblyQualifiedName; + foreach (string providerName in providerNames) + { + IObjectDefinition objectdef = ctx.ObjectFactory.GetObjectDefinition(providerName); + ConstructorArgumentValues ctorArgs = objectdef.ConstructorArgumentValues; + ConstructorArgumentValues.ValueHolder vh = ctorArgs.NamedArgumentValues["dbmetadata"] as ConstructorArgumentValues.ValueHolder; + IObjectDefinition od = vh.Value as IObjectDefinition; + ConstructorArgumentValues dbmdCtorArgs = od.ConstructorArgumentValues; + string commandType = dbmdCtorArgs.GetArgumentValue("commandType", typeof(string)).Value as string; + string assemblyName = dbmdCtorArgs.GetArgumentValue("assemblyName", typeof(string)).Value as string; + + + if (hibCommandAQN.Equals(commandType)) + { + IDbProvider prov = Spring.Data.Common.DbProviderFactory.GetDbProvider(providerName); + return prov; + } + } + } + else + { + log.Info("Could not derive IDbProvider from SessionFactory"); + } + } + + + } + return null; + } + + /// + /// Create a IAdoExceptionTranslator from the given SessionFactory. + /// + /// If a corresponding IDbProvider is found, a ErrorcodeExceptionTranslator + /// for the IDbProvider is created. Otherwise, a FallbackException is created. + /// The session factory to create the translator for + /// An IAdoExceptionTranslator + public static IAdoExceptionTranslator NewAdoExceptionTranslator(ISessionFactory sessionFactory) + { + IDbProvider dbProvider = GetDbProvider(sessionFactory); + if (dbProvider != null) + { + return new ErrorCodeExceptionTranslator(dbProvider); + } + log.Warn("Using FallbackException Translator. Could not translate from ISessionFactory to IDbProvider"); + return new FallbackExceptionTranslator(); + + } + } +} diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SpringSessionContext.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SpringSessionContext.cs new file mode 100644 index 00000000..c0ab893e --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SpringSessionContext.cs @@ -0,0 +1,77 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using NHibernate; +using NHibernate.Context; +using NHibernate.Engine; + +namespace Spring.Data.NHibernate +{ + /// + /// Implementation of NHibernates 1.2's ICurrentSessionContext interface + /// that delegates to Spring's SessionFactoryUtils for providing a + /// Spirng-managed current Session. + /// + /// Used by Spring's LocalSessionFactoryBean if told to expose + /// a transaction-aware SessionFactory. + ///

    This ICurrentSessionContext implementation can also be specified in + /// custom ISessionFactory setup through the + /// "hibernate.current_session_context_class" property, with the fully + /// qualified name of this class as value.

    + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: SpringSessionContext.cs,v 1.2 2007/09/19 22:58:11 markpollack Exp $ + /// + public class SpringSessionContext : ICurrentSessionContext + { + private readonly ISessionFactoryImplementor sessionFactory; + + /// + /// Initializes a new instance of the class + /// + /// The NHibernate session factory. + public SpringSessionContext(ISessionFactoryImplementor sessionFactory) + { + this.sessionFactory = sessionFactory; + } + + #region ICurrentSessionContext Members + + /// + /// Retrieve the Spring-managed Session for the current thread. + /// + /// Current session associated with the thread + /// On errors retrieving thread bound session. + public ISession CurrentSession() + { + try + { + return SessionFactoryUtils.DoGetSession(sessionFactory, false); + } + catch (InvalidOperationException ex) + { + throw new HibernateException(ex.Message); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SpringSessionSynchronization.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SpringSessionSynchronization.cs new file mode 100644 index 00000000..78ca458b --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SpringSessionSynchronization.cs @@ -0,0 +1,276 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Common.Logging; +using NHibernate; +using NHibernate.Engine; +using Spring.Core; +using Spring.Data.Support; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// NHibnerations actions taken during the transaction lifecycle. + /// + /// Mark Pollack (.NET) + /// $Id: SpringSessionSynchronization.cs,v 1.1 2007/06/01 02:34:12 markpollack Exp $ + public class SpringSessionSynchronization : TransactionSynchronizationAdapter, IOrdered + { + #region Fields + + /// + /// The instance for this class. + /// + private readonly ILog log = LogManager.GetLogger(typeof(SpringSessionSynchronization)); + + private readonly SessionHolder sessionHolder; + + private readonly ISessionFactory sessionFactory; + + private readonly IAdoExceptionTranslator adoExceptionTranslator; + + private readonly bool newSession; + + private bool holderActive = true; + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public SpringSessionSynchronization(SessionHolder sessionHolder, ISessionFactory sessionFactory, + IAdoExceptionTranslator adoExceptionTranslator, bool newSession) + { + this.sessionHolder = sessionHolder; + this.sessionFactory = sessionFactory; + this.adoExceptionTranslator = adoExceptionTranslator; + this.newSession = newSession; + } + + #endregion + + #region Properties + + /// + /// Return the order value of this object, where a higher value means greater in + /// terms of sorting. + /// + /// + ///

    + /// Normally starting with 0 or 1, with indicating + /// greatest. Same order values will result in arbitrary positions for the affected + /// objects. + ///

    + ///

    + /// Higher value can be interpreted as lower priority, consequently the first object + /// has highest priority. + ///

    + ///
    + /// The order value. + public int Order + { + get + { + return SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER; + } + } + + #endregion + + #region Methods + + /// + /// Suspend this synchronization. + /// + /// + ///

    + /// Unbind Hibernate resources (SessionHolder) from + /// + /// if managing any. + ///

    + ///
    + public override void Suspend() + { + if (this.holderActive) + { + TransactionSynchronizationManager.UnbindResource(this.sessionFactory); + } + } + + /// + /// Resume this synchronization. + /// + /// + ///

    + /// Rebind Hibernate resources from + /// + /// if managing any. + ///

    + ///
    + public override void Resume() + { + if (this.holderActive) + { + TransactionSynchronizationManager.BindResource(this.sessionFactory, this.sessionHolder); + } + } + + /// + /// Invoked before transaction commit (before + /// ) + /// + /// + /// If the transaction is defined as a read-only transaction. + /// + /// + ///

    + /// Can flush transactional sessions to the database. + ///

    + ///

    + /// Note that exceptions will get propagated to the commit caller and + /// cause a rollback of the transaction. + ///

    + ///
    + public override void BeforeCommit(bool readOnly) + { + if (!readOnly) + { + // read-write transaction -> flush the Hibernate Session + log.Debug("Flushing Hibernate Session on transaction synchronization"); + ISession session = this.sessionHolder.Session; + //Further check: only flush when not FlushMode.NEVER + if (session.FlushMode != FlushMode.Never) + { + try + { + session.Flush(); + //TODO can throw System.ObjectDisposedException... + } + catch (ADOException ex) + { + if (this.adoExceptionTranslator != null) + { + //TODO investigate how ADOException wraps inner exception. + throw this.adoExceptionTranslator.Translate( + "Hibernate transaction synchronization: " + ex.Message, null, ex.InnerException); + } + else + { + throw new HibernateAdoException("ADO.NET Exception", ex); + } + } + catch (HibernateException ex) + { + throw SessionFactoryUtils.ConvertHibernateAccessException(ex); + } + } + + } + } + + /// + /// Invoked before transaction commit (before + /// ) + /// Can e.g. flush transactional O/R Mapping sessions to the database + /// + /// + /// + /// This callback does not mean that the transaction will actually be + /// commited. A rollback decision can still occur after this method + /// has been called. This callback is rather meant to perform work + /// that's only relevant if a commit still has a chance + /// to happen, such as flushing SQL statements to the database. + /// + /// + /// Note that exceptions will get propagated to the commit caller and cause a + /// rollback of the transaction. + /// + /// (note: do not throw TransactionException subclasses here!) + /// + /// + public override void BeforeCompletion() + { + if (this.newSession) + { + // Default behavior: unbind and close the thread-bound Hibernate Session. + TransactionSynchronizationManager.UnbindResource(this.sessionFactory); + this.holderActive = false; + } + else if (this.sessionHolder.AssignedPreviousFlushMode == true) + { + // In case of pre-bound Session, restore previous flush mode. + this.sessionHolder.Session.FlushMode = (this.sessionHolder.PreviousFlushMode); + } + } + + /// + /// Invoked after transaction commit/rollback. + /// + /// + /// Status according to + /// + /// + /// Can e.g. perform resource cleanup, in this case after transaction completion. + ///

    + /// Note that exceptions will get propagated to the commit or rollback + /// caller, although they will not influence the outcome of the transaction. + ///

    + ///
    + public override void AfterCompletion(TransactionSynchronizationStatus status) + { + if (!newSession) + { + ISession session = sessionHolder.Session; + + // Provide correct transaction status for releasing the Session's cache locks, + // if possible. Else, closing will release all cache locks assuming a rollback. + ISessionImplementor sessionImplementor = session as ISessionImplementor; + if (sessionImplementor != null) + { + sessionImplementor.AfterTransactionCompletion(status == TransactionSynchronizationStatus.Committed, sessionHolder.Transaction); + } + + if (newSession) + { + SessionFactoryUtils.CloseSessionOrRegisterDeferredClose(session, sessionFactory); + } + } + if (!newSession && status != TransactionSynchronizationStatus.Committed) + { + // Clear all pending inserts/updates/deletes in the Session. + // Necessary for pre-bound Sessions, to avoid inconsistent state. + sessionHolder.Session.Clear(); + } + + if (this.sessionHolder.DoesNotHoldNonDefaultSession) { + sessionHolder.SynchronizedWithTransaction = false; + } + + } + + #endregion + + } +} diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Support/dontdelete.txt b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Support/dontdelete.txt new file mode 100644 index 00000000..b4472e3f --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/Support/dontdelete.txt @@ -0,0 +1 @@ +sources are linked here from Spring.Data.NHibernate \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.2003.csproj b/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.2003.csproj new file mode 100644 index 00000000..e65acf0d --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.2003.csproj @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.2005.csproj b/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.2005.csproj new file mode 100644 index 00000000..b97f4698 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.2005.csproj @@ -0,0 +1,141 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {90F2D070-6F98-4926-A626-BD7A6071D6D9} + Library + Properties + Spring + Spring.Data.NHibernate12 + + + true + full + false + ..\..\..\build\VS.Net.2005\Spring.Data.NHibernate12\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + Spring.Data.NHibernate12.xml + + + pdbonly + true + ..\..\..\build\VS.Net.2005\Spring.Data.NHibernate12\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\NHibernate12\net\2.0\log4net.dll + + + False + ..\..\..\lib\NHibernate12\net\2.0\NHibernate.dll + + + + + + + + + CommonAssemblyInfo.cs + + + Data\NHibernate\HibernateAdoException.cs + + + Data\NHibernate\HibernateDelegate.cs + + + Data\NHibernate\HibernateObjectRetrievalFailureException.cs + + + Data\NHibernate\HibernateQueryException.cs + + + Data\NHibernate\HibernateSystemException.cs + + + Data\NHibernate\HibernateTemplate.cs + + + Data\NHibernate\HibernateTransactionManager.cs + + + Data\NHibernate\ICommonHibernateOperations.cs + + + Data\NHibernate\IHibernateCallback.cs + + + Data\NHibernate\IHibernateOperations.cs + + + Data\NHibernate\SessionHolder.cs + + + Data\NHibernate\Support\ConfigSectionSessionScopeSettings.cs + + + Data\NHibernate\Support\HibernateDaoSupport.cs + + + Data\NHibernate\Support\OpenSessionInViewModule.cs + + + Data\NHibernate\Support\SessionScope.cs + + + Data\NHibernate\Support\SessionScopeSettings.cs + + + Data\NHibernate\TemplateFlushMode.cs + + + + + + + + + + + + + + + + + + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.build b/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.build new file mode 100644 index 00000000..408cb040 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.build @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.xml b/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.xml new file mode 100644 index 00000000..a5db6e34 --- /dev/null +++ b/src/Spring/Spring.Data.NHibernate12/Spring.Data.NHibernate12.xml @@ -0,0 +1,5627 @@ + + + + Spring.Data.NHibernate12 + + + + + Holds the references and configuration settings for a instance. + References are resolved by looking up the given object names in the root obtained by . + + + + + Holds the references and configuration settings for a instance. + + + + + Default value for property. + + + + + Default value for property. + + + + + Initialize a new instance of with default values. + + + Calling this constructor from your derived class leaves and + uninitialized. See and for more. + + + + + Initialize a new instance of with the given sessionFactory + and default values for all other settings. + + + The instance to be used for obtaining instances. + + + Calling this constructor marks all properties initialized. + + + + + Initialize a new instance of with the given values and references. + + + The instance to be used for obtaining instances. + + + Specify the to be set on each session provided by the instance. + + + Set whether to use a single session for each request. See property for details. + + + Specify the flushmode to be applied on each session provided by the instance. + + + Calling this constructor marks all properties initialized. + + + + + Override this method to resolve an instance according to your chosen strategy. + + + + + Override this method to resolve an instance according to your chosen strategy. + + + + + Gets the configured instance to be used. + + + If the entity interceptor is not set by the constructor, this property calls + to obtain an instance. This allows derived classes to + override the behaviour of how to obtain the concrete instance. + + + + + Gets the configured instance to be used. + + + If this property is requested for the first time, is called. + This allows derived classes to override the behaviour of how to obtain the concrete instance. + + If the instance cannot be resolved. + + + + Set whether to use a single session for each request. Default is "true". + If set to false, each data access operation or transaction will use + its own session (like without Open Session in View). Each of those + sessions will be registered for deferred close, though, actually + processed at request completion. + + + + + Gets or Sets the flushmode to be applied on each newly created session. + + + This property defaults to to ensure that modifying objects outside the boundaries + of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation + within a transaction. + + + + + The default session factory name to use when retrieving the Hibernate session factory from + the root context. + + + + + Initializes a new instance. + + The type, who's name will be used to prefix setting variables with + + + + Initializes a new instance. + + The type, who's name will be used to prefix setting variables with + The configuration section to read setting variables from. + + + + Initializes a new instance. + + The type, who's name will be used to prefix setting variables with + The variable source to obtain settings from. + + + + Resolve the entityInterceptor by looking up + in the root application context. + + The resolved instance or + + + + Resolve the by looking up + in the root application context. + + The resolved instance or + + + + Callback interface for NHibernate code. + + To be used with HibernateTemplate execute + method. The typical implementation will call + Session.load/find/save/update to perform + some operations on persistent objects. + + Mark Pollack (.NET) + $Id: IHibernateCallback.cs,v 1.2 2007/09/19 22:58:22 markpollack Exp $ + + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    + A result object, or null if none. +
    + + + An IFactoryObject that creates a local Hibernate SessionFactory instance. + Behaves like a SessionFactory instance when used as bean reference, + e.g. for HibernateTemplate's "SessionFactory" property. + + + The typical usage will be to register this as singleton factory + in an application context and give objects references to application services + that need it. + + Hibernate configuration settings can be set using the IDictionary property 'HibernateProperties'. + + + + Mark Pollack (.NET) + $Id: LocalSessionFactoryObject.cs,v 1.11 2008/03/21 14:12:08 markpollack Exp $ + + + + TODO: consider changing to NamevalueCollection for easier + cut-n-paste from existing App.config based configurations. + + + + + The shared instance for this class (and derived classes). + + + + + Initializes a new instance of the class. + + + + + Return the singleon session factory. + + The singleon session factory. + + + + Initialize the SessionFactory for the given or the + default location. + + + + + Close the SessionFactory on application context shutdown. + + + + + Subclasses can override this method to perform custom initialization + of the Configuration instance used for ISessionFactory creation. + + + The properties of this LocalSessionFactoryObject will be applied to + the Configuration object that gets returned here. +

    The default implementation creates a new Configuration instance. + A custom implementation could prepare the instance in a specific way, + or use a custom Configuration subclass. +

    +
    + The configuration instance. +
    + + + To be implemented by subclasses that want to to perform custom + post-processing of the Configuration object after this FactoryObject + performed its default initialization. + + The current configuration object. + + + + Subclasses can override this method to perform custom initialization + of the SessionFactory instance, creating it via the given Configuration + object that got prepared by this LocalSessionFactoryObject. + + +

    The default implementation invokes Configuration's BuildSessionFactory. + A custom implementation could prepare the instance in a specific way, + or use a custom ISessionFactory subclass. +

    +
    + The ISessionFactory instance. +
    + + + Sets the assemblies to load that contain mapping files. + + The mapping assemblies. + + + + Sets the hibernate configuration files to load, i.e. hibernate.cfg.xml. + + + + + Sets the locations of Spring IResources that contain mapping + files. + + The location of mapping resources. + + + + Return the Configuration object used to build the SessionFactory. + Allows access to configuration metadata stored there (rarely needed). + + The hibernate configuration. + + + + Set NHibernate configuration properties, like "hibernate.dialect". + + The hibernate properties. + +

    Can be used to override values in a NHibernate XML config file, + or to specify all necessary properties locally. +

    +

    Note: Do not specify a transaction provider here when using + Spring-driven transactions. It is also advisable to omit connection + provider settings and use a Spring-set IDbProvider instead. +

    +
    +
    + + + Get or set the DataSource to be used by the SessionFactory. + + The db provider. + + If set, this will override corresponding settings in Hibernate properties. + Note: If this is set, the Hibernate settings should not define + a connection string + (hibernate.connection.connection_string) to avoid meaningless double configuration. + + + + + + Gets or sets a value indicating whether to expose a transaction aware session factory. + + + true if want to expose transaction aware session factory; otherwise, false. + + + + + Return the type or subclass. + + The type created by this factory + + + + Returns true + + true + + + + Callback interface (Generic version) for NHibernate code. + + The type of result object + To be used with HibernateTemplate execute + method. The typical implementation will call + Session.load/find/save/update to perform + some operations on persistent objects. + + + Sree Nivask (.NET) + $Id: IHibernateCallback.cs,v 1.2 2007/09/19 22:58:10 markpollack Exp $ + + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    + A result object. + The active Hibernate session +
    + + + NHibnerations actions taken during the transaction lifecycle. + + Mark Pollack (.NET) + $Id: SpringSessionSynchronization.cs,v 1.1 2007/06/01 02:34:12 markpollack Exp $ + + + + The instance for this class. + + + + + Initializes a new instance of the class. + + + + + Suspend this synchronization. + + +

    + Unbind Hibernate resources (SessionHolder) from + + if managing any. +

    +
    +
    + + + Resume this synchronization. + + +

    + Rebind Hibernate resources from + + if managing any. +

    +
    +
    + + + Invoked before transaction commit (before + ) + + + If the transaction is defined as a read-only transaction. + + +

    + Can flush transactional sessions to the database. +

    +

    + Note that exceptions will get propagated to the commit caller and + cause a rollback of the transaction. +

    +
    +
    + + + Invoked before transaction commit (before + ) + Can e.g. flush transactional O/R Mapping sessions to the database + + + + This callback does not mean that the transaction will actually be + commited. A rollback decision can still occur after this method + has been called. This callback is rather meant to perform work + that's only relevant if a commit still has a chance + to happen, such as flushing SQL statements to the database. + + + Note that exceptions will get propagated to the commit caller and cause a + rollback of the transaction. + + (note: do not throw TransactionException subclasses here!) + + + + + + Invoked after transaction commit/rollback. + + + Status according to + + + Can e.g. perform resource cleanup, in this case after transaction completion. +

    + Note that exceptions will get propagated to the commit or rollback + caller, although they will not influence the outcome of the transaction. +

    +
    +
    + + + Return the order value of this object, where a higher value means greater in + terms of sorting. + + +

    + Normally starting with 0 or 1, with indicating + greatest. Same order values will result in arbitrary positions for the affected + objects. +

    +

    + Higher value can be interpreted as lower priority, consequently the first object + has highest priority. +

    +
    + The order value. +
    + + + Base class for HibernateTemplate defining common + properties like SessionFactory and flushing behavior. + + +

    Not intended to be used directly. See HibernateTemplate. +

    +
    + Mark Pollack (.NET) + $Id: HibernateAccessor.cs,v 1.7 2008/01/25 15:04:28 markpollack Exp $ +
    + + + The instance for this class. + + + + + Initializes a new instance of the class. + + + + + Apply the flush mode that's been specified for this accessor + to the given Session. + + The current Hibernate Session. + if set to true + if executing within an existing transaction. + + the previous flush mode to restore after the operation, + or null if none + + + + + Flush the given Hibernate Session if necessary. + + The current Hibernate Session. + if set to true + if executing within an existing transaction. + + + + Convert the given HibernateException to an appropriate exception from the + org.springframework.dao hierarchy. Will automatically detect + wrapped ADO.NET Exceptions and convert them accordingly. + + HibernateException that occured. + + The corresponding DataAccessException instance + + + The default implementation delegates to SessionFactoryUtils + and convertAdoAccessException. Can be overridden in subclasses. + + + + + Converts the ADO.NET access exception to an appropriate exception from the + org.springframework.dao hierarchy. Can be overridden in subclasses. + + ADOException that occured, wrapping underlying ADO.NET exception. + + the corresponding DataAccessException instance + + + + + Converts the ADO.NET access exception to an appropriate exception from the + org.springframework.dao hierarchy. Can be overridden in subclasses. + + + + Note that a direct SQLException can just occur when callback code + performs direct ADO.NET access via ISession.Connection(). + + + The ADO.NET exception. + The corresponding DataAccessException instance + + + + Prepare the given IQuery object, applying cache settings and/or + a transaction timeout. + + The query object to prepare. + + + + Apply the given name parameter to the given Query object. + + The query object. + Name of the parameter + The value of the parameter + The NHibernate type of the parameter (or null if none specified) + + + + Prepare the given Criteria object, applying cache settings and/or + a transaction timeout. + + + Note that for NHibernate 1.2 this only works if the + implementation is of the type CriteriaImpl, which should generally + be the case. The SetFetchSize method is not available on the + ICriteria interface + + This is a no-op for NHibernate 1.0.x since + the SetFetchSize method is not on the ICriteria interface and + the implementation class is has internal access. + + To remove the method completely for Spring's NHibernate 1.0 + support while reusing code for NHibernate 1.2 would not be + possible. So now this ineffectual operation is left in tact for + NHibernate 1.0.2 support. + + The criteria object to prepare + + + + Ensure SessionFactory is not null + + If SessionFactory property is null. + + + + Gets or sets if a new Session should be created when no transactional Session + can be found for the current thread. + + + true if allowed to create non-transaction session; + otherwise, false. + + +

    HibernateTemplate is aware of a corresponding Session bound to the + current thread, for example when using HibernateTransactionManager. + If allowCreate is true, a new non-transactional Session will be created + if none found, which needs to be closed at the end of the operation. + If false, an InvalidOperationException will get thrown in this case. +

    +
    +
    + + + Gets or sets a value indicating whether to always + use a new Hibernate Session for this template. + + true if always use new session; otherwise, false. + +

    + Default is "false"; if activated, all operations on this template will + work on a new NHibernate ISession even in case of a pre-bound ISession + (for example, within a transaction). +

    +

    Within a transaction, a new NHibernate ISession used by this template + will participate in the transaction through using the same ADO.NET + Connection. In such a scenario, multiple Sessions will participate + in the same database transaction. +

    +

    Turn this on for operations that are supposed to always execute + independently, without side effects caused by a shared NHibernate ISession. +

    +
    +
    + + + Set whether to expose the native Hibernate Session to IHibernateCallback + code. Default is "false": a Session proxy will be returned, + suppressing close calls and automatically applying + query cache settings and transaction timeouts. + + true if expose native session; otherwise, false. + + + + Gets or sets the template flush mode. + + + Default is Auto. Will get applied to any new ISession + created by the template. + + The template flush mode. + + + + Gets or sets the entity interceptor that allows to inspect and change + property values before writing to and reading from the database. + + + Will get applied to any new ISession created by this object. +

    Such an interceptor can either be set at the ISessionFactory level, + i.e. on LocalSessionFactoryObject, or at the ISession level, i.e. on + HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + to avoid repeated configuration and guarantee consistent behavior in transactions. +

    +
    + The interceptor. +
    + + + Set the object name of a Hibernate entity interceptor that allows to inspect + and change property values before writing to and reading from the database. + + + Will get applied to any new Session created by this transaction manager. +

    Requires the object factory to be known, to be able to resolve the object + name to an interceptor instance on session creation. Typically used for + prototype interceptors, i.e. a new interceptor instance per session. +

    +

    Can also be used for shared interceptor instances, but it is recommended + to set the interceptor reference directly in such a scenario. +

    +
    + The name of the entity interceptor in the object factory/application context. +
    + + + Gets or sets the session factory that should be used to create + NHibernate ISessions. + + The session factory. + + + + Set the object factory instance. + + The object factory instance. + + + + Gets or sets a value indicating whether to + cache all queries executed by this template. + + + If this is true, all IQuery and ICriteria objects created by + this template will be marked as cacheable (including all + queries through find methods). +

    To specify the query region to be used for queries cached + by this template, set the QueryCacheRegion property. +

    +
    + true if cache queries; otherwise, false. +
    + + + Gets or sets the name of the cache region for queries executed by this template. + + + If this is specified, it will be applied to all IQuery and ICriteria objects + created by this template (including all queries through find methods). +

    The cache region will not take effect unless queries created by this + template are configured to be cached via the CacheQueries property. +

    +
    + The query cache region. +
    + + + Gets or sets the fetch size for this HibernateTemplate. + + The size of the fetch. + This is important for processing + large result sets: Setting this higher than the default value will increase + processing speed at the cost of memory consumption; setting this lower can + avoid transferring row data that will never be read by the application. +

    Default is 0, indicating to use the driver's default.

    +
    +
    + + + Gets or sets the maximum number of rows for this HibernateTemplate. + + The max results. + + This is important + for processing subsets of large result sets, avoiding to read and hold + the entire result set in the database or in the ADO.NET driver if we're + never interested in the entire result in the first place (for example, + when performing searches that might return a large number of matches). +

    Default is 0, indicating to use the driver's default.

    +
    +
    + + + Set the ADO.NET exception translator for this instance. + Applied to System.Data.Common.DbException (or provider specific exception type + in .NET 1.1) thrown by callback code, be it direct + DbException or wrapped Hibernate ADOExceptions. +

    The default exception translator is either a ErrorCodeExceptionTranslator + if a DbProvider is available, or a FalbackExceptionTranslator otherwise +

    +
    + The ADO exception translator. +
    + + + Gets a Session for use by this template. + + The session. + + - Returns a new Session in case of "alwaysUseNewSession" (using the same ADO.NET connection as a transaction Session, if applicable) + - a pre-bound Session in case of "AllowCreate" is set to false (not the default) + - or a pre-bound Session or new Session if no transactional or other pre-bound Session exists. + + + + + Helper class to determine if the FlushMode enumeration + was changed from its default value + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The flush mode. + + + + Gets or sets a value indicating whether the FlushMode + property was set.. + + true if FlushMode was set; otherwise, false. + + + + Gets or sets the FlushMode. + + The FlushMode. + + + + Generic version of the Helper class that simplifies NHibernate data access code + + +

    Typically used to implement data access or business logic services that + use NHibernate within their implementation but are Hibernate-agnostic in their + interface. The latter or code calling the latter only have to deal with + domain objects.

    + +

    The central method is Execute() supporting Hibernate access code which + implements the HibernateCallback interface. It provides NHibernate Session + handling such that neither the IHibernateCallback implementation nor the calling + code needs to explicitly care about retrieving/closing NHibernate Sessions, + or handling Session lifecycle exceptions. For typical single step actions, + there are various convenience methods (Find, Load, SaveOrUpdate, Delete). +

    + +

    Can be used within a service implementation via direct instantiation + with a ISessionFactory reference, or get prepared in an application context + and given to services as an object reference. Note: The ISessionFactory should + always be configured as an object in the application context, in the first case + given to the service directly, in the second case to the prepared template. +

    + +

    This class can be considered as a direct alternative to working with the raw + Hibernate Session API (through SessionFactoryUtils.Session). +

    + +

    LocalSessionFactoryObject is the preferred way of obtaining a reference + to a specific NHibernate ISessionFactory. +

    +
    + Sree Nivask (.NET) + Mark Pollack (.NET) + $Id: HibernateTemplate.cs,v 1.3 2008/01/24 17:29:09 markpollack Exp $ +
    + + + Interface that specifies a basic set of Hibernate operations. + + + Implemented by HibernateTemplate. Not often used, but a useful option + to enhance testability, as it can easily be mocked or stubbed. +

    Provides HibernateTemplate's data access methods that mirror + various Session methods. See the NHibernate ISession documentation + for details on those methods. +

    +
    + + Sree Nivask (.NET) + Mark Pollack (.NET) + $Id: IHibernateOperations.cs,v 1.2 2007/09/19 22:58:10 markpollack Exp $ +
    + + + Interface that specifies a set of Hibernate operations that + are common across versions of Hibernate. + + + Base interface for generic and non generic IHibernateOperations interfaces + Not often used, but a useful option + to enhance testability, as it can easily be mocked or stubbed. +

    Provides HibernateTemplate's data access methods that mirror + various Session methods. See the NHibernate ISession documentation + for details on those methods. +

    +
    + Mark Pollack (.NET) + + $Id: ICommonHibernateOperations.cs,v 1.2 2007/09/19 22:58:22 markpollack Exp $ +
    + + + Remove all objects from the Session cache, and cancel all pending saves, + updates and deletes. + + In case of Hibernate errors + + + + Delete the given persistent instance. + + The persistent instance to delete. + In case of Hibernate errors + + + + Delete the given persistent instance. + + + Obtains the specified lock mode if the instance exists, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + Tthe persistent instance to delete. + The lock mode to obtain. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The number of entity instances deleted. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The value of the parameter. + The Hibernate type of the parameter (or null). + The number of entity instances deleted. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The values of the parameters. + Hibernate types of the parameters (or null) + The number of entity instances deleted. + In case of Hibernate errors + + + + Flush all pending saves, updates and deletes to the database. + + + Only invoke this for selective eager flushing, for example when ADO.NET code + needs to see certain changes within the same transaction. Else, it's preferable + to rely on auto-flushing at transaction completion. + + In case of Hibernate errors + + + + Load the persistent instance with the given identifier + into the given object, throwing an exception if not found. + + Entity the object (of the target class) to load into. + An identifier of the persistent instance. + If object not found. + In case of Hibernate errors + + + + Re-read the state of the given persistent instance. + + The persistent instance to re-read. + In case of Hibernate errors + + + + Re-read the state of the given persistent instance. + Obtains the specified lock mode for the instance. + + The persistent instance to re-read. + The lock mode to obtain. + In case of Hibernate errors + + + + Determines whether the given object is in the Session cache. + + the persistence instance to check. + + true if session cache contains the specified entity; otherwise, false. + + In case of Hibernate errors + + + + Remove the given object from the Session cache. + + The persistent instance to evict. + In case of Hibernate errors + + + + Obtain the specified lock level upon the given object, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + The he persistent instance to lock. + The lock mode to obtain. + If not found + In case of Hibernate errors + + + + Persist the given transient instance. + + The transient instance to persist. + The generated identifier. + In case of Hibernate errors + + + + Persist the given transient instance with the given identifier. + + The transient instance to persist. + The identifier to assign. + In case of Hibernate errors + + + + Update the given persistent instance. + + The persistent instance to update. + In case of Hibernate errors + + + + Update the given persistent instance. + Obtains the specified lock mode if the instance exists, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + The persistent instance to update. + The lock mode to obtain. + In case of Hibernate errors + + + + Save or update the given persistent instance, + according to its id (matching the configured "unsaved-value"?). + + Tthe persistent instance to save or update + (to be associated with the Hibernate Session). + In case of Hibernate errors + + + + Save or update the contents of given persistent object, + according to its id (matching the configured "unsaved-value"?). + Will copy the contained fields to an already loaded instance + with the same id, if appropriate. + + The persistent object to save or update. + (not necessarily to be associated with the Hibernate Session) + + The actually associated persistent object. + (either an already loaded instance with the same id, or the given object) + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or if not found. + Obtains the specified lock mode if the instance exists. + + The object type to get. + The id of the object to get. + the persistent instance, or if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + Obtains the specified lock mode if the instance exists. + + The object type to get. + The lock mode to obtain. + The lock mode. + the persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + + The object type to load. + An identifier of the persistent instance. + The persistent instance + If not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + Obtains the specified lock mode if the instance exists. + + The object type to load. + An identifier of the persistent instance. + The lock mode. + The persistent instance + If not found + In case of Hibernate errors + + + + Return all persistent instances of the given entity class. + Note: Use queries or criteria for retrieving a specific subset. + + The object type to load. + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances. + + The object type to find. + a query expressed in Hibernate's query language + + a generic List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a "?" parameter in the query string. + + The object type to find. + a query expressed in Hibernate's query language + the value of the parameter + + a generic List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding one value + to a "?" parameter of the given type in the query string. + + The object type to find. + a query expressed in Hibernate's query language + The value of the parameter. + Hibernate type of the parameter (or null) + + a generic List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to "?" parameters in the query string. + + The object type to find. + a query expressed in Hibernate's query language + the values of the parameters + a generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a number of + values to "?" parameters of the given types in the query string. + + The object type to find. + A query expressed in Hibernate's query language + The values of the parameters + Hibernate types of the parameters (or null) + + a generic List containing 0 or more persistent instances + + In case of Hibernate errors + If values and types are not null and their lenths are not equal + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The object type to find. + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + a generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The object type to find. + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + Hibernate type of the parameter (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + The object type to find. + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + The object type to find. + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + Hibernate types of the parameters (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The value of the parameter + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The value of the parameter + Hibernate type of the parameter (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The values of the parameters + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The values of the parameters + Hibernate types of the parameters (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + If values and types are not null and their lengths differ. + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + The Hibernate type of the parameter (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + Hibernate types of the parameters (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances, binding the properties + of the given object to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The values of the parameters + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding the properties + of the given object to named parameters in the query string. + + The object type to find. + A query expressed in Hibernate's query language + The values of the parameters + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning the result object. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + The object type retrieved. + The delegate callback object that specifies the Hibernate action. + a result object returned by the action, or null + + In case of Hibernate errors +
    + + + Execute the action specified by the delegate within a Session. + + The object type retrieved. + The HibernateDelegate that specifies the action + to perform. + if set to true expose the native hibernate session to + callback code. + a result object returned by the action, or null + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + The object type retrieved. + The callback object that specifies the Hibernate action. + + a result object returned by the action, or null + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning the result object. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + In case of Hibernate errors +
    + + + Execute the action specified by the given action object within a Session. + + The object type retrieved. + callback object that specifies the Hibernate action. + if set to true expose the native hibernate session to + callback code. + + a result object returned by the action, or null + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning the result object. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + The object type to find. + The delegate callback object that specifies the Hibernate action. + A generic IList returned by the action, or null + + In case of Hibernate errors +
    + + + Execute the action specified by the delegate within a Session. + + The object type to find. + The FindHibernateDelegate that specifies the action + to perform. + if set to true expose the native hibernate session to + callback code. + A generic IList returned by the action, or null + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + The object type to find. + The callback object that specifies the Hibernate action. + + A generic IList returned by the action, or null + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning the result object. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    +
    + + + Execute the action specified by the given action object within a Session assuming that an IList is returned. + + The object type to find. + callback object that specifies the Hibernate action. + if set to true expose the native hibernate session to + callback code. + + an IList returned by the action, or null + + In case of Hibernate errors + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + Allows creation of a new non-transactional session when no + transactional Session can be found for the current thread + The session factory to create sessions. + + + + Initializes a new instance of the class. + + The session factory to create sessions. + if set to true allow creation + of a new non-transactional session when no transactional Session can be found + for the current thread. + + + + Remove all objects from the Session cache, and cancel all pending saves, + updates and deletes. + + + + + Delete the given persistent instance. + + The persistent instance to delete. + In case of Hibernate errors + + + + Delete the given persistent instance. + + Tthe persistent instance to delete. + The lock mode to obtain. + + Obtains the specified lock mode if the instance exists, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The number of entity instances deleted. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The value of the parameter. + The Hibernate type of the parameter (or null). + The number of entity instances deleted. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The values of the parameters. + Hibernate types of the parameters (or null) + The number of entity instances deleted. + In case of Hibernate errors + + + + Flush all pending saves, updates and deletes to the database. + + + Only invoke this for selective eager flushing, for example when ADO.NET code + needs to see certain changes within the same transaction. Else, it's preferable + to rely on auto-flushing at transaction completion. + + In case of Hibernate errors + + + + Load the persistent instance with the given identifier + into the given object, throwing an exception if not found. + + Entity the object (of the target class) to load into. + An identifier of the persistent instance. + If object not found. + In case of Hibernate errors + + + + Re-read the state of the given persistent instance. + + The persistent instance to re-read. + In case of Hibernate errors + + + + Re-read the state of the given persistent instance. + Obtains the specified lock mode for the instance. + + The persistent instance to re-read. + The lock mode to obtain. + In case of Hibernate errors + + + + Determines whether the given object is in the Session cache. + + the persistence instance to check. + + true if session cache contains the specified entity; otherwise, false. + + In case of Hibernate errors + + + + Remove the given object from the Session cache. + + The persistent instance to evict. + In case of Hibernate errors + + + + Obtain the specified lock level upon the given object, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + The he persistent instance to lock. + The lock mode to obtain. + If not found + In case of Hibernate errors + + + + Persist the given transient instance. + + The transient instance to persist. + The generated identifier. + In case of Hibernate errors + + + + Persist the given transient instance with the given identifier. + + The transient instance to persist. + The identifier to assign. + In case of Hibernate errors + + + + Update the given persistent instance. + + The persistent instance to update. + In case of Hibernate errors + + + + Update the given persistent instance. + Obtains the specified lock mode if the instance exists, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + The persistent instance to update. + The lock mode to obtain. + In case of Hibernate errors + + + + Save or update the given persistent instance, + according to its id (matching the configured "unsaved-value"?). + + Tthe persistent instance to save or update + (to be associated with the Hibernate Session). + In case of Hibernate errors + + + + Save or update the contents of given persistent object, + according to its id (matching the configured "unsaved-value"?). + Will copy the contained fields to an already loaded instance + with the same id, if appropriate. + + The persistent object to save or update. + (not necessarily to be associated with the Hibernate Session) + + The actually associated persistent object. + (either an already loaded instance with the same id, or the given object) + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + Obtains the specified lock mode if the instance exists. + + The object type to get. + The id of the object to get. + the persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + Obtains the specified lock mode if the instance exists. + + The object type to get. + The lock mode to obtain. + The lock mode. + the persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + + The object type to load. + An identifier of the persistent instance. + The persistent instance + If not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + Obtains the specified lock mode if the instance exists. + + The object type to load. + An identifier of the persistent instance. + The lock mode. + The persistent instance + If not found + In case of Hibernate errors + + + + Return all persistent instances of the given entity class. + Note: Use queries or criteria for retrieving a specific subset. + + The object type to load. + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances. + + The object type to find. + a query expressed in Hibernate's query language + + a generic List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a "?" parameter in the query string. + + The object type to find. + a query expressed in Hibernate's query language + the value of the parameter + + a generic List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding one value + to a "?" parameter of the given type in the query string. + + The object type to find. + a query expressed in Hibernate's query language + The value of the parameter. + Hibernate type of the parameter (or null) + + a generic List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to "?" parameters in the query string. + + The object type to find. + a query expressed in Hibernate's query language + the values of the parameters + a generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a number of + values to "?" parameters of the given types in the query string. + + The object type to find. + A query expressed in Hibernate's query language + The values of the parameters + Hibernate types of the parameters (or null) + + a generic List containing 0 or more persistent instances + + In case of Hibernate errors + If values and types are not null and their lengths are not equal + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The object type to find. + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + a generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The object type to find. + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + Hibernate type of the parameter (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + The object type to find. + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + The object type to find. + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + Hibernate types of the parameters (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The value of the parameter + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The value of the parameter + Hibernate type of the parameter (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The values of the parameters + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The values of the parameters + Hibernate types of the parameters (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + If values and types are not null and their lengths differ. + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + The Hibernate type of the parameter (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + Hibernate types of the parameters (or null) + A generic List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances, binding the properties + of the given object to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The object type to find. + The name of a Hibernate query in a mapping file + The values of the parameters + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding the properties + of the given object to named parameters in the query string. + + The object type to find. + A query expressed in Hibernate's query language + The values of the parameters + A generic List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning the result object. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + The object type retrieved. + The delegate callback object that specifies the Hibernate action. + a result object returned by the action, or null + + In case of Hibernate errors +
    + + + Execute the action specified by the delegate within a Session. + + The object type retrieved. + The HibernateDelegate that specifies the action + to perform. + if set to true expose the native hibernate session to + callback code. + a result object returned by the action, or null + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + The object type retrieved. + The callback object that specifies the Hibernate action. + + a result object returned by the action, or null + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning the result object. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + In case of Hibernate errors +
    + + + Execute the action specified by the given action object within a Session. + + The object type retrieved. + callback object that specifies the Hibernate action. + if set to true expose the native hibernate session to + callback code. + + a result object returned by the action, or null + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session assuming that an IList is returned. + + The object type to find. + callback object that specifies the Hibernate action. + if set to true expose the native hibernate session to + callback code. + + an IList returned by the action, or null + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning the result object. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + The object type to find. + The delegate callback object that specifies the Hibernate action. + A generic IList returned by the action, or null + + In case of Hibernate errors +
    + + + Execute the action specified by the delegate within a Session. + + The object type to find. + The FindHibernateDelegate that specifies the action + to perform. + if set to true expose the native hibernate session to + callback code. + A generic IList returned by the action, or null + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + The object type to find. + The callback object that specifies the Hibernate action. + + A generic IList returned by the action, or null + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning the result object. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    +
    + + + Gets or sets if a new Session should be created when no transactional Session + can be found for the current thread. + + + true if allowed to create non-transaction session; + otherwise, false. + + +

    HibernateTemplate is aware of a corresponding Session bound to the + current thread, for example when using HibernateTransactionManager. + If allowCreate is true, a new non-transactional Session will be created + if none found, which needs to be closed at the end of the operation. + If false, an InvalidOperationException will get thrown in this case. +

    +
    +
    + + + Gets or sets a value indicating whether to always + use a new Hibernate Session for this template. + + true if always use new session; otherwise, false. + +

    + Default is "false"; if activated, all operations on this template will + work on a new NHibernate ISession even in case of a pre-bound ISession + (for example, within a transaction). +

    +

    Within a transaction, a new NHibernate ISession used by this template + will participate in the transaction through using the same ADO.NET + Connection. In such a scenario, multiple Sessions will participate + in the same database transaction. +

    +

    Turn this on for operations that are supposed to always execute + independently, without side effects caused by a shared NHibernate ISession. +

    +
    +
    + + + Set whether to expose the native Hibernate Session to IHibernateCallback + code. Default is "false": a Session proxy will be returned, + suppressing close calls and automatically applying + query cache settings and transaction timeouts. + + true if expose native session; otherwise, false. + + + + Gets or sets the template flush mode. + + + Default is Auto. Will get applied to any new ISession + created by the template. + + The template flush mode. + + + + Gets or sets the entity interceptor that allows to inspect and change + property values before writing to and reading from the database. + + + Will get applied to any new ISession created by this object. +

    Such an interceptor can either be set at the ISessionFactory level, + i.e. on LocalSessionFactoryObject, or at the ISession level, i.e. on + HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + to avoid repeated configuration and guarantee consistent behavior in transactions. +

    +
    + The interceptor. +
    + + + Set the object name of a Hibernate entity interceptor that allows to inspect + and change property values before writing to and reading from the database. + + + Will get applied to any new Session created by this transaction manager. +

    Requires the object factory to be known, to be able to resolve the object + name to an interceptor instance on session creation. Typically used for + prototype interceptors, i.e. a new interceptor instance per session. +

    +

    Can also be used for shared interceptor instances, but it is recommended + to set the interceptor reference directly in such a scenario. +

    +
    + The name of the entity interceptor in the object factory/application context. +
    + + + Gets or sets the session factory that should be used to create + NHibernate ISessions. + + The session factory. + + + + Set the object factory instance. + + The object factory instance + + + + Gets or sets a value indicating whether to + cache all queries executed by this template. + + + If this is true, all IQuery and ICriteria objects created by + this template will be marked as cacheable (including all + queries through find methods). +

    To specify the query region to be used for queries cached + by this template, set the QueryCacheRegion property. +

    +
    + true if cache queries; otherwise, false. +
    + + + Gets or sets the name of the cache region for queries executed by this template. + + + If this is specified, it will be applied to all IQuery and ICriteria objects + created by this template (including all queries through find methods). +

    The cache region will not take effect unless queries created by this + template are configured to be cached via the CacheQueries property. +

    +
    + The query cache region. +
    + + + Gets or sets the fetch size for this HibernateTemplate. + + The size of the fetch. + This is important for processing + large result sets: Setting this higher than the default value will increase + processing speed at the cost of memory consumption; setting this lower can + avoid transferring row data that will never be read by the application. +

    Default is 0, indicating to use the driver's default.

    +
    +
    + + + Gets or sets the maximum number of rows for this HibernateTemplate. + + The max results. + + This is important + for processing subsets of large result sets, avoiding to read and hold + the entire result set in the database or in the ADO.NET driver if we're + never interested in the entire result in the first place (for example, + when performing searches that might return a large number of matches). +

    Default is 0, indicating to use the driver's default.

    +
    +
    + + + Set the ADO.NET exception translator for this instance. + Applied to System.Data.Common.DbException (or provider specific exception type + in .NET 1.1) thrown by callback code, be it direct + DbException or wrapped Hibernate ADOExceptions. +

    The default exception translator is either a ErrorCodeExceptionTranslator + if a DbProvider is available, or a FalbackExceptionTranslator otherwise +

    +
    + The ADO exception translator. +
    + + + Gets the classic hibernate template for access to non-generic methods. + + The classic hibernate template. + + + + Gets or sets the proxy factory. + + This may be useful to set if you create many instances of + HibernateTemplate and/or HibernateDaoSupport. This allows the same + ProxyFactory implementation to be used thereby limiting the + number of dynamic proxy types created in the temporary assembly, which + are never garbage collected due to .NET runtime semantics. + + The proxy factory. + + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Callback interface (Generic version) for NHibernate code that + returns a List of objects. + + The type of result object + To be used with HibernateTemplate execute + method. The typical implementation will call + Session.load/find/save/update to perform + some operations on persistent objects. + + Sree Nivask (.NET) + Mark Pollack (.NET) + $Id: IFindHibernateCallback.cs,v 1.2 2007/09/19 22:58:10 markpollack Exp $ + + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    + Collection result object. +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Convenient super class for Hibernate data access objects. + + + Requires a SessionFactory to be set, providing a HibernateTemplate + based on it to subclasses. Can alternatively be initialized directly with + a HibernateTemplate, to reuse the latter's settings such as the SessionFactory, + exception translator, flush mode, etc + + This base call is mainly intended for HibernateTemplate usage. + + This class will create its own HibernateTemplate if only a SessionFactory + is passed in. The "allowCreate" flag on that HibernateTemplate will be "true" + by default. A custom HibernateTemplate instance can be used through overriding + CreateHibernateTemplate. + + + Sree Nivask (.NET) + Mark Pollack (.NET) + $Id: HibernateDaoSupport.cs,v 1.3 2007/09/19 22:58:10 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Create a HibernateTemplate for the given ISessionFactory. + + + Only invoked if populating the DAO with a ISessionFactory reference! +

    Can be overridden in subclasses to provide a HibernateTemplate instance + with different configuration, or a custom HibernateTemplate subclass. +

    +
    + The new HibernateTemplate instance +
    + + + Check if the hibernate template property has been set. + + If HibernateTemplate property is null. + + + + Get a Hibernate Session, either from the current transaction or + a new one. The latter is only allowed if "allowCreate" is true. + + Note that this is not meant to be invoked from HibernateTemplate code + but rather just in plain Hibernate code. Either rely on a thread-bound + Session (via HibernateInterceptor), or use it in combination with + ReleaseSession. + + In general, it is recommended to use HibernateTemplate, either with + the provided convenience operations or with a custom HibernateCallback + that provides you with a Session to work on. HibernateTemplate will care + for all resource management and for proper exception conversion. + + + if a non-transactional Session should be created when no + transactional Session can be found for the current thread + + Hibernate session. + + If the Session couldn't be created + + + if no thread-bound Session found and allowCreate false + + + + + + Convert the given HibernateException to an appropriate exception from the + org.springframework.dao hierarchy. Will automatically detect + wrapped ADO.NET Exceptions and convert them accordingly. + + HibernateException that occured. + + The corresponding DataAccessException instance + + + The default implementation delegates to SessionFactoryUtils + and convertAdoAccessException. Can be overridden in subclasses. + + + + + Close the given Hibernate Session, created via this DAO's SessionFactory, + if it isn't bound to the thread. + + + Typically used in plain Hibernate code, in combination with the + Session property and ConvertHibernateAccessException. + + The session to close. + + + + Gets or sets the hibernate template. + + Set the HibernateTemplate for this DAO explicitly, + as an alternative to specifying a SessionFactory. + + The hibernate template. + + + + Gets or sets the session factory to be used by this DAO. + Will automatically create a HibernateTemplate for the given SessionFactory. + + The session factory. + + + + Get a Hibernate Session, either from the current transaction or a new one. + The latter is only allowed if the "allowCreate" setting of this object's + HibernateTemplate is true. + + +

    Note that this is not meant to be invoked from HibernateTemplate code + but rather just in plain Hibernate code. Use it in combination with + ReleaseSession. +

    +

    In general, it is recommended to use HibernateTemplate, either with + the provided convenience operations or with a custom HibernateCallback + that provides you with a Session to work on. HibernateTemplate will care + for all resource management and for proper exception conversion. +

    +
    + The Hibernate session. +
    + + + Provide support for the open session in view pattern for lazily loaded hibernate objects + used in ASP.NET pages. + + jjx: http://forum.springframework.net/member.php?u=29 + Mark Pollack (.NET) + Erich Eichinger + Harald Radi + $Id: OpenSessionInViewModule.cs,v 1.7 2008/01/21 06:39:12 oakinger Exp $ + + + + Implementation of SessionScope that associates a single session within the using scope. + + + It is recommended to be used in the following type of scenario: + + using (new SessionScope()) + { + ... do multiple operation, possibly in multiple transactions. + } + + At the end of "using", the session is automatically closed. All transactions within the scope use the same session, + if you are using Spring's HibernateTemplate or using Spring's implementation of NHibernate 1.2's + ICurrentSessionContext interface. + + + It is assumed that the session factory object name is called "SessionFactory". In case that you named the object + in different way you can specify your can specify it in the application settings using the key + Spring.Data.NHibernate.Support.SessionScope.SessionFactoryObjectName. Values for EntityInterceptorObjectName + and SingleSessionMode can be specified similarly. + + + Note: + The session is managed on a per thread basis on the thread that opens the scope instance. This means that you must + never pass a reference to a instance over to another thread! + + + Robert M. (.NET) + Harald Radi (.NET) + + + + The logging instance. + + + + + Initializes a new instance of the class in single session mode, + associating a session with the thread. The session is opened lazily on demand. + + + + + Initializes a new instance of the class. + + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Initializes a new instance of the class. + + + The name of the configuration section to read configuration settings from. + See for more info. + + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Initializes a new instance of the class. + + + The name of the configuration section to read configuration settings from. + See for more info. + + The type, who's full name is used for prefixing appSetting keys + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Initializes a new instance of the class. + + + The instance to be used for obtaining instances. + + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Initializes a new instance of the class. + + + The instance to be used for obtaining instances. + + + Specify the to be set on each session provided by this instance. + + + Set whether to use a single session for each request. See property for details. + + + Specify the flushmode to be applied on each session provided by this instance. + + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Initializes a new instance of the class. + + An instance holding the scope configuration + + If set to true associate a session with the thread. If false, another + collaborating class will associate the session with the thread, potentially by calling + the Open method on this class. + + + + + Sets a flag, whether this scope is in "open" state on the current logical thread. + + + + + Gets/Sets a flag, whether this scope manages it's own session for the current logical thread or not. + + false if session is managed by this module. false otherwise + + + + Call Close(), + + + + + Opens a new session or participates in an existing session and + registers with spring's . + + + + + Close the current view's session and unregisters + from spring's . + + + + + Set whether to use a single session for each request. Default is "true". + If set to false, each data access operation or transaction will use + its own session (like without Open Session in View). Each of those + sessions will be registered for deferred close, though, actually + processed at request completion. + + + + + Gets the flushmode to be applied on each newly created session. + + + This property defaults to to ensure that modifying objects outside the boundaries + of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation + within a transaction. + + + + + Get or set the configured SessionFactory + + + + + Get or set the configured EntityInterceptor + + + + + Gets a flag, whether this scope is in "open" state on the current logical thread. + + + + + Gets a flag, whether this scope manages it's own session for the current logical thread or not. + + + + + This sessionHolder creates a default session only if it is needed. + + + Although a NHibernateSession deferes creation of db-connections until they are really + needed, instantiation a session is imho still more expensive than this LazySessionHolder. (EE) + + + + + Session holder, wrapping a NHibernate ISession and a NHibernate Transaction. + HibernateTransactionManager binds instances of this class + to the thread, for a given ISessionFactory. + + + Note: This is an SPI class, not intended to be used by applications. + + Mark Pollack (.NET) + $Id: SessionHolder.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + + May be used by derived classes to create an empty SessionHolder. + + + When using this ctor in your derived class, you MUST override ! + + + + + Initializes a new instance of the class. + + The session. + + + + Initializes a new instance of the class. + + The key to store the session under. + The hibernate session. + + + + May be overridden in a derived class to e.g. lazily create a session + + + + + Gets the session given key identifier + + The key. + A hibernate session + + + + Gets the session given the key and removes the session from + the dictionary storage. + + The key. + A hibernate session + + + + Adds the session to the dictionary storage using the default key. + + The hibernate session. + + + + Adds the session to the dictionary storage using the supplied key. + + The key. + The hibernate session. + + + + Removes the session from the dictionary storage for the given key. + + The key. + The session that was previously contained in the + dictionary storage. + + + + Determines whether the holder the specified session. + + The session. + + true if the holder contains the specified session; otherwise, false. + + + + + Clear the transaction state of this resource holder. + + + + + Gets the session using the default key + + The hibernate session. + + + + Gets the first session based on iteration over + the IDictionary storage. + + Any hibernate session. + + + + Gets a value indicating whether dictionary of + hibernate sessions is empty. + + + true if this session holder is empty; otherwise, false. + + + + + Gets a value indicating whether this SessionHolder + does not hold non default session. + + + true if does not hold non default session; otherwise, false. + + + + + Gets or sets the hibernate transaction. + + The transaction. + + + + Gets or sets the ADO.NET Connection used to create the session. + + The ADO.NET connection. + + + + Gets or sets the previous flush mode. + + The previous flush mode. + + + + Gets a value indicating whether the PreviousFlushMode property + was set. + + + true if assigned PreviousFlushMode property; otherwise, false. + + + + + Gets the validated session. + + The validated session. + + + + Initialize a new instance. + + + + + Create a new session on demand + + + + + Ensure session is closed (if any) and remove circular references to avoid memory leaks! + + + + + Gets or sets the LazySessionHolder instance for the current request + + + + + Initializes a new instance of the class. Creates a SessionScope, + but does not yet associate a session with a thread, that is lef to the lifecycle of the request. + + + + + Register context handler and look up SessionFactoryObjectName under the application configuration key, + Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName if not using the default value + (i.e. sessionFactory) and look up the SingleSession setting under the application configuration key, + Spring.Data.NHibernate.Support.OpenSessionInViewModule.SingleSession if not using the default value of true. + + The standard HTTP application context + + + + A do nothing dispose method. + + + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning an IList of result objects created within the callback. + Note that there's special support for single step actions: + see HibernateTemplate.find etc. +

    +
    + The type of result object + Sree Nivask (.NET) + $Id: FindHibernateDelegate.cs,v 1.2 2007/09/19 22:58:10 markpollack Exp $ +
    + + + PlatformTransactionManager implementation for a single Hibernate SessionFactory. + Binds a Hibernate Session from the specified factory to the thread, potentially + allowing for one thread Session per factory + + + SessionFactoryUtils and HibernateTemplate are aware of thread-bound Sessions and participate in such + transactions automatically. Using either of those is required for Hibernate + access code that needs to support this transaction handling mechanism. + + Supports custom isolation levels at the start of the transaction + , and timeouts that get applied as appropriate + Hibernate query timeouts. To support the latter, application code must either use + HibernateTemplate (which by default applies the timeouts) or call + SessionFactoryUtils.applyTransactionTimeout for each created + Hibernate Query object. + + Note that you can specify a Spring IDbProvider instance which if shared with + a corresponding instance of AdoTemplate will allow for mixing ADO.NET/NHibernate + operations within a single transaction. + + Mark Pollack (.NET) + $Id: HibernateTransactionManager.cs,v 1.7 2007/11/20 14:39:48 markpollack Exp $ + + + + Just needed for entityInterceptorBeanName. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The session factory. + + + + Return the current transaction object. + + The current transaction object. + + If transaction support is not available. + + + In the case of lookup or system errors. + + + + + Check if the given transaction object indicates an existing, + i.e. already begun, transaction. + + + Transaction object returned by + . + + True if there is an existing transaction. + + In the case of system errors. + + + + + Begin a new transaction with the given transaction definition. + + + Transaction object returned by + . + + + instance, describing + propagation behavior, isolation level, timeout etc. + + + Does not have to care about applying the propagation behavior, + as this has already been handled by this abstract manager. + + + In the case of creation or system errors. + + + + + Suspend the resources of the current transaction. + + + Transaction object returned by + . + + + An object that holds suspended resources (will be kept unexamined for passing it into + .) + + + Transaction synchronization will already have been suspended. + + + If suspending is not supported by the transaction manager implementation. + + + in case of system errors. + + + + + Resume the resources of the current transaction. + + + Transaction object returned by + . + + + The object that holds suspended resources as returned by + . + + + Transaction synchronization will be resumed afterwards. + + + If suspending is not supported by the transaction manager implementation. + + + In the case of system errors. + + + + + Perform an actual commit on the given transaction. + + The status representation of the transaction. + +

    + An implementation does not need to check the rollback-only flag. +

    +
    + + In the case of system errors. + +
    + + + Perform an actual rollback on the given transaction. + + The status representation of the transaction. + + An implementation does not need to check the new transaction flag. + + + In the case of system errors. + + + + + Set the given transaction rollback-only. Only called on rollback + if the current transaction takes part in an existing one. + + The status representation of the transaction. + + In the case of system errors. + + + + + Gets the ADO.NET IDbTransaction object from the NHibernate ITransaction object. + + The hibernate transaction. + The ADO.NET transaction. Null if could not get the transaction. Warning + messages will be logged in that case. + + + + Convert the given HibernateException to an appropriate exception from + the Spring.Dao hierarchy. Can be overridden in subclasses. + + The HibernateException that occured. + The corresponding DataAccessException instance + + + + Convert the given ADOException to an appropriate exception from the + the Spring.Dao hierarchy. Can be overridden in subclasses. + + The ADOException that occured, wrapping the underlying + ADO.NET thrown exception. + The translator to convert hibernate ADOExceptions. + + The corresponding DataAccessException instance + + + + + Cleanup resources after transaction completion. + + Transaction object returned by + . + + + This implemenation unbinds the SessionFactory and + DbProvider from thread local storage and closes the + ISession. + +

    + Called after + and + + execution on any outcome. +

    +

    + Should not throw any exceptions but just issue warnings on errors. +

    +
    +
    + + + Invoked by an + after it has injected all of an object's dependencies. + + +

    + This method allows the object instance to perform the kind of + initialization only possible when all of it's dependencies have + been injected (set), and to throw an appropriate exception in the + event of misconfiguration. +

    +

    + Please do consult the class level documentation for the + interface for a + description of exactly when this method is invoked. In + particular, it is worth noting that the + + and + callbacks will have been invoked prior to this method being + called. +

    +
    + + In the event of misconfiguration (such as the failure to set a + required property) or if initialization fails. + +
    + + + Gets or sets the db provider. + + The db provider. + + + + Gets or sets a Hibernate entity interceptor that allows to inspect and change + property values before writing to and reading from the database. + When getting, return the current Hibernate entity interceptor, or null if none. + + The entity interceptor. + + Resolves an entity interceptor object name via the object factory, + if necessary. + Will get applied to any new Session created by this transaction manager. + Such an interceptor can either be set at the SessionFactory level, + i.e. on LocalSessionFactoryObject, or at the Session level, i.e. on + HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + to avoid repeated configuration and guarantee consistent behavior in transactions. + + If object factory is null and need to get entity interceptor via object name. + + + + Sets the object name of a Hibernate entity interceptor that + allows to inspect and change property values before writing to and reading from the database. + + The name of the entity interceptor object. + + Will get applied to any new Session created by this transaction manager. +

    Requires the object factory to be known, to be able to resolve the object + name to an interceptor instance on session creation. Typically used for + prototype interceptors, i.e. a new interceptor instance per session. +

    +

    Can also be used for shared interceptor instances, but it is recommended + to set the interceptor reference directly in such a scenario. +

    +
    +
    + + + Gets or sets the ADO.NET exception translator for this transaction manager. + + + Applied to ADO.NET Exceptions (wrapped by Hibernate's ADOException) + + The ADO exception translator. + + + + Gets the default IAdoException translator, lazily creating it if nece + + The default IAdoException translator. + + + + Gets or sets the SessionFactory that this instance should manage transactions for. + + The session factory. + + + + Set whether to autodetect a ADO.NET connection used by the Hibernate SessionFactory, + if set via LocalSessionFactoryObject's DbProvider. Default is "true". + + + true if [autodetect data source]; otherwise, false. + + +

    Can be turned off to deliberately ignore an available IDbProvider, + to not expose Hibernate transactions as ADO.NET transactions for that IDbProvider. +

    +
    +
    + + + The object factory just needs to be known for resolving entity interceptor + It does not need to be set for any other mode of operation. + + + Owning + (may not be ). The object can immediately + call methods on the factory. + + + + + Return whether the transaction is internally marked as rollback-only. + + + True of the transaction is marked as rollback-only. + + + + Helper class that simplifies NHibernate data access code + + +

    Typically used to implement data access or business logic services that + use NHibernate within their implementation but are Hibernate-agnostic in their + interface. The latter or code calling the latter only have to deal with + domain objects.

    + +

    The central method is Execute supporting Hibernate access code + implementing the HibernateCallback interface. It provides NHibernate Session + handling such that neither the IHibernateCallback implementation nor the calling + code needs to explicitly care about retrieving/closing NHibernate Sessions, + or handling Session lifecycle exceptions. For typical single step actions, + there are various convenience methods (Find, Load, SaveOrUpdate, Delete). +

    + +

    Can be used within a service implementation via direct instantiation + with a ISessionFactory reference, or get prepared in an application context + and given to services as an object reference. Note: The ISessionFactory should + always be configured as an object in the application context, in the first case + given to the service directly, in the second case to the prepared template. +

    + +

    This class can be considered as direct alternative to working with the raw + Hibernate Session API (through SessionFactoryUtils.Session). +

    + +

    LocalSessionFactoryObject is the preferred way of obtaining a reference + to a specific NHibernate ISessionFactory. +

    +
    + Mark Pollack (.NET) + $Id: HibernateTemplate.cs,v 1.3 2008/01/24 17:29:16 markpollack Exp $ +
    + + + Interface that specifies a basic set of Hibernate operations. + + + Implemented by HibernateTemplate. Not often used, but a useful option + to enhance testability, as it can easily be mocked or stubbed. +

    Provides HibernateTemplate's data access methods that mirror + various Session methods. See the NHibernate ISession documentation + for details on those methods. +

    +
    + + Mark Pollack (.NET) + $Id: IHibernateOperations.cs,v 1.2 2007/09/19 22:58:22 markpollack Exp $ +
    + + + Delete all given persistent instances. + + The persistent instances to delete. + + This can be combined with any of the find methods to delete by query + in two lines of code, similar to Session's delete by query methods. + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning a result object, i.e. a domain + object or a collection of domain objects. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + The delegate callback object that specifies the Hibernate action. + a result object returned by the action, or null + + In case of Hibernate errors +
    + + + Execute the action specified by the given action object within a Session. + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning a result object, i.e. a domain + object or a collection of domain objects. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + The callback object that specifies the Hibernate action. + a result object returned by the action, or null + + In case of Hibernate errors +
    + + + Execute the specified action assuming that the result object is a List. + + + This is a convenience method for executing Hibernate find calls or + queries within an action. + + The calback object that specifies the Hibernate action. + A IList returned by the action, or null + + In case of Hibernate errors + + + + Execute a query for persistent instances. + + a query expressed in Hibernate's query language + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a "?" parameter in the query string. + + a query expressed in Hibernate's query language + the value of the parameter + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding one value + to a "?" parameter of the given type in the query string. + + a query expressed in Hibernate's query language + The value of the parameter. + Hibernate type of the parameter (or null) + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to "?" parameters in the query string. + + a query expressed in Hibernate's query language + the values of the parameters + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a number of + values to "?" parameters of the given types in the query string. + + A query expressed in Hibernate's query language + The values of the parameters + Hibernate types of the parameters (or null) + a List containing 0 or more persistent instances + In case of Hibernate errors + If values and types are not null and their lengths are not equal + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The value of the parameter + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The value of the parameter + Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If values and types are not null and their lengths differ. + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + The Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances, binding the properties + of the given object to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding the properties + of the given object to named parameters in the query string. + + A query expressed in Hibernate's query language + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + + a persistent type. + An identifier of the persistent instance. + the persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + Obtains the specified lock mode if the instance exists. + + A persistent class. + An identifier of the persistent instance. + The lock mode. + the persistent instance, or null if not found + the persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + + Type of the entity. + An identifier of the persistent instance. + The persistent instance + If not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + Obtains the specified lock mode if the instance exists. + + Type of the entity. + An identifier of the persistent instance. + The lock mode. + The persistent instance + If not found + In case of Hibernate errors + + + + Return all persistent instances of the given entity class. + Note: Use queries or criteria for retrieving a specific subset. + + Type of the entity. + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Save or update all given persistent instances, + according to its id (matching the configured "unsaved-value"?). + + Tthe persistent instances to save or update + (to be associated with the Hibernate Session)he entities. + In case of Hibernate errors + + + + The instance for this class. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The default for creating a new non-transactional + session when no transactional Session can be found for the current thread + is set to true. + The session factory to create sessions. + + + + Initializes a new instance of the class. + + The session factory to create sessions. + if set to true allow creation + of a new non-transactional when no transactional Session can be found + for the current thread. + + + + Delegate function that clears the session. + + The hibernate session. + null + + + + Flush all pending saves, updates and deletes to the database. + + + Only invoke this for selective eager flushing, for example when ADO.NET code + needs to see certain changes within the same transaction. Else, it's preferable + to rely on auto-flushing at transaction completion. + + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + + The type. + An identifier of the persistent instance. + The persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity type + with the given identifier, or null if not found. + Obtains the specified lock mode if the instance exists. + + The type. + The lock mode to obtain. + The lock mode. + the persistent instance, or null if not found + the persistent instance, or null if not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + + Type of the entity. + An identifier of the persistent instance. + The persistent instance + If not found + In case of Hibernate errors + + + + Return the persistent instance of the given entity class + with the given identifier, throwing an exception if not found. + Obtains the specified lock mode if the instance exists. + + Type of the entity. + An identifier of the persistent instance. + The lock mode. + The persistent instance + If not found + In case of Hibernate errors + + + + Load the persistent instance with the given identifier + into the given object, throwing an exception if not found. + + Entity the object (of the target class) to load into. + An identifier of the persistent instance. + If object not found. + In case of Hibernate errors + + + + Return all persistent instances of the given entity class. + Note: Use queries or criteria for retrieving a specific subset. + + Type of the entity. + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Re-read the state of the given persistent instance. + + The persistent instance to re-read. + In case of Hibernate errors + + + + Re-read the state of the given persistent instance. + Obtains the specified lock mode for the instance. + + The persistent instance to re-read. + The lock mode to obtain. + In case of Hibernate errors + + + + Obtain the specified lock level upon the given object, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + The he persistent instance to lock. + The lock mode to obtain. + If not found + In case of Hibernate errors + + + + Persist the given transient instance. + + The transient instance to persist. + The generated identifier. + In case of Hibernate errors + + + + Persist the given transient instance with the given identifier. + + The transient instance to persist. + The identifier to assign. + In case of Hibernate errors + + + + Update the given persistent instance. + + The persistent instance to update. + In case of Hibernate errors + + + + Update the given persistent instance. + Obtains the specified lock mode if the instance exists, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + The persistent instance to update. + The lock mode to obtain. + In case of Hibernate errors + + + + Save or update the given persistent instance, + according to its id (matching the configured "unsaved-value"?). + + Tthe persistent instance to save or update + (to be associated with the Hibernate Session). + In case of Hibernate errors + + + + Save or update all given persistent instances, + according to its id (matching the configured "unsaved-value"?). + + Tthe persistent instances to save or update + (to be associated with the Hibernate Session)he entities. + In case of Hibernate errors + + + + Save or update the contents of given persistent object, + according to its id (matching the configured "unsaved-value"?). + Will copy the contained fields to an already loaded instance + with the same id, if appropriate. + + The persistent object to save or update. + (not necessarily to be associated with the Hibernate Session) + + The actually associated persistent object. + (either an already loaded instance with the same id, or the given object) + In case of Hibernate errors + + + + Remove all objects from the Session cache, and cancel all pending saves, + updates and deletes. + + + + + Determines whether the given object is in the Session cache. + + the persistence instance to check. + + true if session cache contains the specified entity; otherwise, false. + + In case of Hibernate errors + + + + Remove the given object from the Session cache. + + The persistent instance to evict. + In case of Hibernate errors + + + + Delete the given persistent instance. + + The persistent instance to delete. + In case of Hibernate errors + + + + Delete the given persistent instance. + + Tthe persistent instance to delete. + The lock mode to obtain. + + Obtains the specified lock mode if the instance exists, implicitly + checking whether the corresponding database entry still exists + (throwing an OptimisticLockingFailureException if not found). + + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The number of entity instances deleted. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The value of the parameter. + The Hibernate type of the parameter (or null). + The number of entity instances deleted. + In case of Hibernate errors + + + + Delete all objects returned by the query. + + a query expressed in Hibernate's query language. + The values of the parameters. + Hibernate types of the parameters (or null) + The number of entity instances deleted. + In case of Hibernate errors + If length for argument values and types are not equal. + + + + Delete all given persistent instances. + + The persistent instances to delete. + + This can be combined with any of the find methods to delete by query + in two lines of code, similar to Session's delete by query methods. + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning a result object, i.e. a domain + object or a collection of domain objects. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + The delegate callback object that specifies the Hibernate action. + a result object returned by the action, or null + + In case of Hibernate errors +
    + + + Execute the action specified by the delegate within a Session. + + The HibernateDelegate that specifies the action + to perform. + if set to true expose the native hibernate session to + callback code. + a result object returned by the action, or null + + + + + Execute the action specified by the given action object within a Session. + + The callback object that specifies the Hibernate action. + + a result object returned by the action, or null + + + Application exceptions thrown by the action object get propagated to the + caller (can only be unchecked). Hibernate exceptions are transformed into + appropriate DAO ones. Allows for returning a result object, i.e. a domain + object or a collection of domain objects. +

    Note: Callback code is not supposed to handle transactions itself! + Use an appropriate transaction manager like HibernateTransactionManager. + Generally, callback code must not touch any Session lifecycle methods, + like close, disconnect, or reconnect, to let the template do its work. +

    +
    + In case of Hibernate errors +
    + + + Execute the specified action assuming that the result object is a List. + + + This is a convenience method for executing Hibernate find calls or + queries within an action. + + The calback object that specifies the Hibernate action. + A IList returned by the action, or null + + In case of Hibernate errors + + + + Execute the action specified by the given action object within a Session. + + callback object that specifies the Hibernate action. + if set to true expose the native hibernate session to + callback code. + + a result object returned by the action, or null + + + + + Execute a query for persistent instances. + + a query expressed in Hibernate's query language + + a List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a "?" parameter in the query string. + + a query expressed in Hibernate's query language + the value of the parameter + + a List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding one value + to a "?" parameter of the given type in the query string. + + a query expressed in Hibernate's query language + The value of the parameter. + Hibernate type of the parameter (or null) + + a List containing 0 or more persistent instances + + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to "?" parameters in the query string. + + a query expressed in Hibernate's query language + the values of the parameters + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a number of + values to "?" parameters of the given types in the query string. + + A query expressed in Hibernate's query language + The values of the parameters + Hibernate types of the parameters (or null) + + a List containing 0 or more persistent instances + + In case of Hibernate errors + If values and types are not null and their lengths are not equal + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + a List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding + one value to a named parameter in the query string. + + The name of a Hibernate query in a mapping file + The name of the parameter + The value of the parameter + Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding a + number of values to named parameters in the query string. + + A query expressed in Hibernate's query language + The names of the parameters + The values of the parameters + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The value of the parameter + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a "?" parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The value of the parameter + Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding a + number of values to "?" parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If values and types are not null and their lengths differ. + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + one value to a named parameter in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + Name of the parameter + The value of the parameter + The Hibernate type of the parameter (or null) + A List containing 0 or more persistent instances + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a named query for persistent instances, binding + number of values to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The names of the parameters + The values of the parameters. + Hibernate types of the parameters (or null) + A List containing 0 or more persistent instances + In case of Hibernate errors + If paramNames length is not equal to values length or + if paramNames length is not equal to types length (when types is not null) + + + + Execute a named query for persistent instances, binding the properties + of the given object to named parameters in the query string. + A named query is defined in a Hibernate mapping file. + + The name of a Hibernate query in a mapping file + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Execute a query for persistent instances, binding the properties + of the given object to named parameters in the query string. + + A query expressed in Hibernate's query language + The values of the parameters + A List containing 0 or more persistent instances + In case of Hibernate errors + + + + Create a close-suppressing proxy for the given Hibernate Session. + The proxy also prepares returned Query and Criteria objects. + + The session. + The session proxy. + + + + Check whether write operations are allowed on the given Session. + + + Default implementation throws an InvalidDataAccessApiUsageException + in case of FlushMode.Never. Can be overridden in subclasses. + + The current Hibernate session. + If write operation is attempted in read-only mode + + + + + Compares if the flush mode enumerations, Spring's + TemplateFlushMode and NHibernates FlushMode have equal + settings. + + The template flush mode. + The NHibernate flush mode. + + Returns true if both are Never, Auto, or Commit, false + otherwise. + + + + + Gets or sets if a new Session should be created when no transactional Session + can be found for the current thread. + + + true if allowed to create non-transaction session; + otherwise, false. + + +

    HibernateTemplate is aware of a corresponding Session bound to the + current thread, for example when using HibernateTransactionManager. + If allowCreate is true, a new non-transactional Session will be created + if none found, which needs to be closed at the end of the operation. + If false, an InvalidOperationException will get thrown in this case. +

    +
    +
    + + + Gets or sets a value indicating whether to always + use a new Hibernate Session for this template. + + true if always use new session; otherwise, false. + +

    + Default is "false"; if activated, all operations on this template will + work on a new NHibernate ISession even in case of a pre-bound ISession + (for example, within a transaction). +

    +

    Within a transaction, a new NHibernate ISession used by this template + will participate in the transaction through using the same ADO.NET + Connection. In such a scenario, multiple Sessions will participate + in the same database transaction. +

    +

    Turn this on for operations that are supposed to always execute + independently, without side effects caused by a shared NHibernate ISession. +

    +
    +
    + + + Gets or sets the template flush mode. + + + Default is Auto. Will get applied to any new ISession + created by the template. + + The template flush mode. + + + + Gets or sets the entity interceptor that allows to inspect and change + property values before writing to and reading from the database. + + + Will get applied to any new ISession created by this object. +

    Such an interceptor can either be set at the ISessionFactory level, + i.e. on LocalSessionFactoryObject, or at the ISession level, i.e. on + HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager. + It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager + to avoid repeated configuration and guarantee consistent behavior in transactions. +

    +
    + The interceptor. + If object factory is not set and need to retrieve entity interceptor by name. +
    + + + Gets or sets the name of the cache region for queries executed by this template. + + + If this is specified, it will be applied to all IQuery and ICriteria objects + created by this template (including all queries through find methods). +

    The cache region will not take effect unless queries created by this + template are configured to be cached via the CacheQueries property. +

    +
    + The query cache region. +
    + + + Gets or sets a value indicating whether to + cache all queries executed by this template. + + + If this is true, all IQuery and ICriteria objects created by + this template will be marked as cacheable (including all + queries through find methods). +

    To specify the query region to be used for queries cached + by this template, set the QueryCacheRegion property. +

    +
    + true if cache queries; otherwise, false. +
    + + + Gets or sets the maximum number of rows for this HibernateTemplate. + + The max results. + + This is important + for processing subsets of large result sets, avoiding to read and hold + the entire result set in the database or in the ADO.NET driver if we're + never interested in the entire result in the first place (for example, + when performing searches that might return a large number of matches). +

    Default is 0, indicating to use the driver's default.

    +
    +
    + + + Set whether to expose the native Hibernate Session to IHibernateCallback + code. Default is "false": a Session proxy will be returned, + suppressing close calls and automatically applying + query cache settings and transaction timeouts. + + true if expose native session; otherwise, false. + + + + Gets or sets whether to check that the Hibernate Session is not in read-only mode + in case of write operations (save/update/delete). + + + true if check that the Hibernate Session is not in read-only mode + in case of write operations; otherwise, false. + + + Default is "true", for fail-fast behavior when attempting write operations + within a read-only transaction. Turn this off to allow save/update/delete + on a Session with flush mode NEVER. + + + + + Set the object name of a Hibernate entity interceptor that allows to inspect + and change property values before writing to and reading from the database. + + + Will get applied to any new Session created by this transaction manager. +

    Requires the object factory to be known, to be able to resolve the object + name to an interceptor instance on session creation. Typically used for + prototype interceptors, i.e. a new interceptor instance per session. +

    +

    Can also be used for shared interceptor instances, but it is recommended + to set the interceptor reference directly in such a scenario. +

    +
    + The name of the entity interceptor in the object factory/application context. +
    + + + Set the object factory instance. + + The object factory instance + + + + Gets or sets the session factory that should be used to create + NHibernate ISessions. + + The session factory. + + + + Gets or sets the fetch size for this HibernateTemplate. + + The size of the fetch. + This is important for processing + large result sets: Setting this higher than the default value will increase + processing speed at the cost of memory consumption; setting this lower can + avoid transferring row data that will never be read by the application. +

    Default is 0, indicating to use the driver's default.

    +
    +
    + + + Gets or sets the proxy factory. + + This may be useful to set if you create many instances of + HibernateTemplate and/or HibernateDaoSupport. This allows the same + ProxyFactory implementation to be used thereby limiting the + number of dynamic proxy types created in the temporary assembly, which + are never garbage collected due to .NET runtime semantics. + + The proxy factory. + + + + Set the ADO.NET exception translator for this instance. + Applied to System.Data.Common.DbException (or provider specific exception type + in .NET 1.1) thrown by callback code, be it direct + DbException or wrapped Hibernate ADOExceptions. +

    The default exception translator is either a ErrorCodeExceptionTranslator + if a DbProvider is available, or a FalbackExceptionTranslator otherwise +

    +
    + The ADO exception translator. +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Helper class featuring methods for Hibernate Session handling, + allowing for reuse of Hibernate Session instances within transactions. + Also provides support for exception translation. + + Mark Pollack (.NET) + $Id: SessionFactoryUtils.cs,v 1.1 2008/04/08 20:26:30 markpollack Exp $ + + + + The instance for this class. + + + + + The ordering value for synchronizaiton this session resources. + Set to be lower than ADO.NET synchronization. + + + + + Initializes a new instance of the class. + + + + + Get a new Hibernate Session from the given SessionFactory. + Will return a new Session even if there already is a pre-bound + Session for the given SessionFactory. + + + Within a transaction, this method will create a new Session + that shares the transaction's ADO.NET Connection. More specifically, + it will use the same ADO.NET Connection as the pre-bound Hibernate Session. + + The session factory to create the session with. + The Hibernate entity interceptor, or null if none. + The new session. + If could not open Hibernate session + + + + Get a Hibernate Session for the given SessionFactory. Is aware of and will + return any existing corresponding Session bound to the current thread, for + example when using HibernateTransactionManager. Will always create a new + Session otherwise. + + + Supports setting a Session-level Hibernate entity interceptor that allows + to inspect and change property values before writing to and reading from the + database. Such an interceptor can also be set at the SessionFactory level + (i.e. on LocalSessionFactoryObject), on HibernateTransactionManager, or on + HibernateInterceptor/HibernateTemplate. + + The session factory to create the + session with. + Hibernate entity interceptor, or null if none. + AdoExceptionTranslator to use for flushing the + Session on transaction synchronization (can be null; only used when actually + registering a transaction synchronization). + The Hibernate Session + + If the session couldn't be created. + + + If no thread-bound Session found and allowCreate is false. + + + + + Get a Hibernate Session for the given SessionFactory. Is aware of and will + return any existing corresponding Session bound to the current thread, for + example when using . Will create a new Session + otherwise, if allowCreate is true. + + The session factory to create the session with. + if set to true create a non-transactional Session when no + transactional Session can be found for the current thread. + The hibernate session + + If the session couldn't be created. + + + If no thread-bound Session found and allowCreate is false. + + + + + Get a Hibernate Session for the given SessionFactory. + + Is aware of and will return any existing corresponding + Session bound to the current thread, for example whenusing + . Will create a new + Session otherwise, if "allowCreate" is true. +

    Throws the orginal HibernateException, in contrast to + . +

    + The session factory. + if set to true [allow create]. + The Hibernate Session + + if the Session couldn't be created + + + If no thread-bound Session found and allowCreate is false. + +
    + + + Open a new Session from the factory. + + The session factory to create the session with. + Hibernate entity interceptor, or null if none. + the newly opened session + + + + Perform the actual closing of the Hibernate Session + catching and logging any cleanup exceptions thrown. + + The hibernate session to close + + + + Return whether the given Hibernate Session is transactional, that is, + bound to the current thread by Spring's transaction facilities. + + The hibernate session to check + The session factory that the session + was created with, can be null. + + true if the session transactional; otherwise, false. + + + + + Convert the given HibernateException to an appropriate exception from the + Spring.Dao hierarchy. Note that it is advisable to + handle AdoException specifically by using a AdoExceptionTranslator for the + underlying ADO.NET exception. + + The Hibernate exception that occured. + DataAccessException instance + + + + Close the given Session, created via the given factory, + if it is not managed externally (i.e. not bound to the thread). + + The hibernate session to close + The hibernate SessionFactory that + the session was created with. + + + + Close the given Session or register it for deferred close. + + The session. + The session factory. + + + + Initialize deferred close for the current thread and the given SessionFactory. + Sessions will not be actually closed on close calls then, but rather at a + processDeferredClose call at a finishing point (like request completion). + + The session factory. + + + + Return if deferred close is active for the current thread + and the given SessionFactory. + The session factory. + + true if [is deferred close active] [the specified session factory]; otherwise, false. + + If SessionFactory argument is null. + + + + Process Sessions that have been registered for deferred close + for the given SessionFactory. + + The session factory. + If there is no session factory associated with the thread. + + + + Applies the current transaction timeout, if any, to the given + criteria object + + The Hibernate Criteria object. + Hibernate SessionFactory that the Criteria was created for + (can be null). + If criteria argument is null. + + + + Applies the current transaction timeout, if any, to the given + Hibenrate query object. + + The Hibernate Query object. + Hibernate SessionFactory that the Query was created for + (can be null). + If query argument is null. + + + + Gets the Spring IDbProvider given the ISessionFactory. + + The matching is performed by comparing the assembly qualified + name string of the hibernate Driver.ConnectionType to those in + the DbProviderFactory definitions. No connections are created + in performing this comparison. + The session factory. + The corresponding IDbProvider, null if no mapping was found. + If DbProviderFactory's ApplicaitonContext is not + an instance of IConfigurableApplicaitonContext. + + + + Create a IAdoExceptionTranslator from the given SessionFactory. + + If a corresponding IDbProvider is found, a ErrorcodeExceptionTranslator + for the IDbProvider is created. Otherwise, a FallbackException is created. + The session factory to create the translator for + An IAdoExceptionTranslator + + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning the result object created within the callback. + Note that there's special support for single step actions: + see HibernateTemplate.find etc. +

    +
    + The type of result object + Sree Nivask (.NET) + $Id: HibernateDelegate.cs,v 1.2 2007/09/19 22:58:10 markpollack Exp $ +
    + + + Hibernate-specific subclass of InvalidDataAccessResourceUsageException, + thrown on invalid HQL query syntax. + + Mark Pollack (.NET) + $Id: HibernateQueryException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The ex. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Gets the query string that was invalid. + + The query string that was invalid. + + + + Implementation of NHibernates 1.2's ICurrentSessionContext interface + that delegates to Spring's SessionFactoryUtils for providing a + Spirng-managed current Session. + + Used by Spring's LocalSessionFactoryBean if told to expose + a transaction-aware SessionFactory. +

    This ICurrentSessionContext implementation can also be specified in + custom ISessionFactory setup through the + "hibernate.current_session_context_class" property, with the fully + qualified name of this class as value.

    + Juergen Hoeller + Mark Pollack (.NET) + $Id: SpringSessionContext.cs,v 1.2 2007/09/19 22:58:11 markpollack Exp $ + +
    + + + Initializes a new instance of the class + + The NHibernate session factory. + + + + Retrieve the Spring-managed Session for the current thread. + + Current session associated with the thread + On errors retrieving thread bound session. + + + + Hibernate-specific subclass of ObjectOptimisticLockingFailureException. + + + Converts Hibernate's StaleObjectStateException. + + Juergen Hoeller + Mark Pollack (.NET) + $Id: HibernateOptimisticLockingFailureException.cs,v 1.1 2008/04/08 20:26:30 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The StaleObjectStateException. + + + + Initializes a new instance of the class. + + The StaleStateException. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Enumeration for the various Hibernate flush modes. + + Mark Pollack (.NET) + $Id: TemplateFlushMode.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + Never flush is a good strategy for read-only units of work. + + + Hibernate will not track and look for changes in this case, + avoiding any overhead of modification detection. +

    In case of an existing ISession, TemplateFlushMode.Never will turn + the hibenrate flush mode + to FlushMode.Never for the scope of the current operation, resetting the previous + flush mode afterwards. +

    +
    +
    + + Automatic flushing is the default mode for a Hibernate Session. + + + A session will get flushed on transaction commit, and on certain find + operations that might involve already modified instances, but not + after each unit of work like with eager flushing. +

    In case of an existing Session, TemplateFlushMode.Auto + will participate in the existing flush mode, not modifying + it for the current operation. + This in particular means that this setting will not modify an existing + hibernate flush mode FlushMode.Never, in contrast to TemplateFlushMode.Eager. +

    +
    +
    + + + Eager flushing leads to immediate synchronization with the database, + even if in a transaction. + + + This causes inconsistencies to show up and throw + a respective exception immediately, and ADO access code that participates + in the same transaction will see the changes as the database is already + aware of them then. But the drawbacks are: +
      +
    • additional communication roundtrips with the database, instead of a + single batch at transaction commit;
    • +
    • the fact that an actual database rollback is needed if the Hibernate + transaction rolls back (due to already submitted SQL statements).
    • +
    +

    In case of an existing Session, TemplateFlushMode.Eager + will turn the NHibernate flush mode + to FlushMode.Auto for the scope of the current operation and issue a flush at the + end, resetting the previous flush mode afterwards. +

    +
    +
    + + + Flushing at commit only is intended for units of work where no + intermediate flushing is desired, not even for find operations + that might involve already modified instances. + + +

    In case of an existing Session, TemplateFlushMode.Commit + will turn the NHibernate flush mode + to FlushMode.Commit for the scope of the current operation, resetting the previous + flush mode afterwards. The only exception is an existing flush mode + FlushMode.Never, which will not be modified through this setting. +

    +
    +
    + + + Hibernate-specific subclass of UncategorizedDataAccessException, + for Hibernate system errors that do not match any concrete + Spring.Dao exceptions. + + Mark Pollack (.NET) + $Id: HibernateSystemException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The cause. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Gets called by HibernateTemplate with an active + Hibernate Session. Does not need to care about activating or closing + the Session, or handling transactions. + + +

    + Allows for returning a result object created within the callback, i.e. + a domain object or a collection of domain objects. Note that there's + special support for single step actions: see HibernateTemplate.find etc. +

    +
    +
    + + + Hibernate-specific subclass of ObjectRetrievalFailureException. + + + Converts Hibernate's UnresolvableObjectException, ObjectNotFoundException, + ObjectDeletedException, and WrongClassException. + + Mark Pollack (.NET) + $Id: HibernateObjectRetrievalFailureException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The ex. + + + + Initializes a new instance of the class. + + The ex. + + + + Initializes a new instance of the class. + + The ex. + + + + Initializes a new instance of the class. + + The ex. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Hibernate-specific subclass of UncategorizedDataAccessException, + for ADO.NET exceptions that Hibernate rethrew and could not be + mapped into the DAO exception heirarchy. + + Mark Pollack (.NET) + $Id: HibernateAdoException.cs,v 1.1 2007/05/31 20:25:13 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception from the underlying data access API - ADO.NET + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Convenient super class for Hibernate data access objects. + + + Requires a SessionFactory to be set, providing a HibernateTemplate + based on it to subclasses. Can alternatively be initialized directly with + a HibernateTemplate, to reuse the latter's settings such as the SessionFactory, + exception translator, flush mode, etc + + This base call is mainly intended for HibernateTemplate usage. + + This class will create its own HibernateTemplate if only a SessionFactory + is passed in. The "allowCreate" flag on that HibernateTemplate will be "true" + by default. A custom HibernateTemplate instance can be used through overriding + CreateHibernateTemplate. + + + Mark Pollack (.NET) + $Id: HibernateDaoSupport.cs,v 1.2 2007/08/28 15:26:37 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Create a HibernateTemplate for the given ISessionFactory. + + + Only invoked if populating the DAO with a ISessionFactory reference! +

    Can be overridden in subclasses to provide a HibernateTemplate instance + with different configuration, or a custom HibernateTemplate subclass. +

    +
    +
    + + + Check if the hibernate template property has been set. + + + + + Get a Hibernate Session, either from the current transaction or + a new one. The latter is only allowed if "allowCreate" is true. + + Note that this is not meant to be invoked from HibernateTemplate code + but rather just in plain Hibernate code. Either rely on a thread-bound + Session (via HibernateInterceptor), or use it in combination with + ReleaseSession. + + In general, it is recommended to use HibernateTemplate, either with + the provided convenience operations or with a custom HibernateCallback + that provides you with a Session to work on. HibernateTemplate will care + for all resource management and for proper exception conversion. + + + if a non-transactional Session should be created when no + transactional Session can be found for the current thread + + Hibernate session. + + If the Session couldn't be created + + + if no thread-bound Session found and allowCreate false + + + + + + Convert the given HibernateException to an appropriate exception from the + org.springframework.dao hierarchy. Will automatically detect + wrapped ADO.NET Exceptions and convert them accordingly. + + HibernateException that occured. + + The corresponding DataAccessException instance + + + The default implementation delegates to SessionFactoryUtils + and convertAdoAccessException. Can be overridden in subclasses. + + + + + Close the given Hibernate Session, created via this DAO's SessionFactory, + if it isn't bound to the thread. + + + Typically used in plain Hibernate code, in combination with the + Session property and ConvertHibernateAccessException. + + The session to close. + + + + Gets or sets the hibernate template. + + Set the HibernateTemplate for this DAO explicitly, + as an alternative to specifying a SessionFactory. + + The hibernate template. + + + + Gets or sets the session factory to be used by this DAO. + Will automatically create a HibernateTemplate for the given SessionFactory. + + The session factory. + + + + Get a Hibernate Session, either from the current transaction or a new one. + The latter is only allowed if the "allowCreate" setting of this object's + HibernateTemplate is true. + + +

    Note that this is not meant to be invoked from HibernateTemplate code + but rather just in plain Hibernate code. Use it in combination with + ReleaseSession. +

    +

    In general, it is recommended to use HibernateTemplate, either with + the provided convenience operations or with a custom HibernateCallback + that provides you with a Session to work on. HibernateTemplate will care + for all resource management and for proper exception conversion. +

    +
    + The Hibernate session. +
    +
    +
    diff --git a/src/Spring/Spring.Data/AssemblyInfo.cs b/src/Spring/Spring.Data/AssemblyInfo.cs new file mode 100644 index 00000000..c68f64b9 --- /dev/null +++ b/src/Spring/Spring.Data/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring.Net Data Access support")] +[assembly: AssemblyDescription("Interfaces and classes that provide Data access support in Spring.Net")] \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/CannotAcquireLockException.cs b/src/Spring/Spring.Data/Dao/CannotAcquireLockException.cs new file mode 100644 index 00000000..a73c4ef4 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/CannotAcquireLockException.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown on failure to aquire a lock during an update i.e a select for + /// update statement. + /// + /// + ///

    + /// This exception will be thrown either by O/R mapping tools or by custom DAO + /// implementations. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: CannotAcquireLockException.cs,v 1.6 2008/04/08 20:26:43 markpollack Exp $ + [Serializable] + public class CannotAcquireLockException : PessimisticLockingFailureException + { + /// + /// Creates a new instance of the + /// class. + /// + public CannotAcquireLockException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public CannotAcquireLockException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public CannotAcquireLockException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected CannotAcquireLockException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/CannotSerializeTransactionException.cs b/src/Spring/Spring.Data/Dao/CannotSerializeTransactionException.cs new file mode 100644 index 00000000..bdfcf15e --- /dev/null +++ b/src/Spring/Spring.Data/Dao/CannotSerializeTransactionException.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown on failure to complete a transaction in serialized mode due to + /// update conflicts. + /// + /// + ///

    + /// This exception will be thrown either by O/R mapping tools or by custom DAO + /// implementations. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: CannotSerializeTransactionException.cs,v 1.6 2008/04/08 20:26:43 markpollack Exp $ + [Serializable] + public class CannotSerializeTransactionException : PessimisticLockingFailureException + { + /// + /// Creates a new instance of the + /// class. + /// + public CannotSerializeTransactionException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public CannotSerializeTransactionException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public CannotSerializeTransactionException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected CannotSerializeTransactionException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/CleanupFailureDataAccessException.cs b/src/Spring/Spring.Data/Dao/CleanupFailureDataAccessException.cs new file mode 100644 index 00000000..5c344dab --- /dev/null +++ b/src/Spring/Spring.Data/Dao/CleanupFailureDataAccessException.cs @@ -0,0 +1,94 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown when we couldn't cleanup after a data access operation, + /// but the actual operation went OK. + /// + /// + ///

    + /// For example, this exception or a subclass might be thrown if an ADO.NET + /// connection couldn't be closed after it had been used successfully. + ///

    + ///

    + /// Note that data access code might perform resource cleanup in a + /// finally block and therefore log cleanup failure rather than rethrow it, + /// to keep the original data access exception, if any. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: CleanupFailureDataAccessException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public class CleanupFailureDataAccessException : DataAccessException + { + /// + /// Creates a new instance of the + /// class. + /// + public CleanupFailureDataAccessException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public CleanupFailureDataAccessException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public CleanupFailureDataAccessException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected CleanupFailureDataAccessException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/ConcurrencyFailureException.cs b/src/Spring/Spring.Data/Dao/ConcurrencyFailureException.cs new file mode 100644 index 00000000..2c6ffc71 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/ConcurrencyFailureException.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown on concurrency failure. This exception should be + /// sublassed to indicate the type of failure - optimistic locking, + /// failure to acquire lock, etc. + /// + /// + ///

    + /// This exception will be thrown either by O/R mapping tools or by custom DAO + /// implementations. + ///

    + ///
    + /// Thomas Risberg + /// Griffin Caprio (.NET) + /// $Id: ConcurrencyFailureException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public class ConcurrencyFailureException : DataAccessException + { + /// + /// Creates a new instance of the + /// class. + /// + public ConcurrencyFailureException() : base("No Exception Message") {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public ConcurrencyFailureException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public ConcurrencyFailureException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ConcurrencyFailureException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/DataAccessException.cs b/src/Spring/Spring.Data/Dao/DataAccessException.cs new file mode 100644 index 00000000..66b84c64 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/DataAccessException.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Root of the hierarchy of data access exceptions + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + [Serializable] + public abstract class DataAccessException : Exception + { + /// + /// Creates a new instance of the + /// class. + /// + protected DataAccessException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + protected DataAccessException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + protected DataAccessException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected DataAccessException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} diff --git a/src/Spring/Spring.Data/Dao/DataAccessResourceFailureException.cs b/src/Spring/Spring.Data/Dao/DataAccessResourceFailureException.cs new file mode 100644 index 00000000..e762cbdd --- /dev/null +++ b/src/Spring/Spring.Data/Dao/DataAccessResourceFailureException.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Data access exception thrown when a resource fails completely: + /// for example, if we can't connect to a database using ADO.NET. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: DataAccessResourceFailureException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public class DataAccessResourceFailureException : DataAccessException + { + /// + /// Creates a new instance of the + /// class. + /// + public DataAccessResourceFailureException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public DataAccessResourceFailureException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public DataAccessResourceFailureException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected DataAccessResourceFailureException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/DataIntegrityViolationException.cs b/src/Spring/Spring.Data/Dao/DataIntegrityViolationException.cs new file mode 100644 index 00000000..86c77d3c --- /dev/null +++ b/src/Spring/Spring.Data/Dao/DataIntegrityViolationException.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown when an attempt to insert or update data + /// results in violation of an integrity constraint. + /// + /// + ///

    + /// Note that this is not purely a relational concept; unique primary keys are + /// required by most database types. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: DataIntegrityViolationException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public class DataIntegrityViolationException : DataAccessException + { + /// + /// Creates a new instance of the + /// class. + /// + public DataIntegrityViolationException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public DataIntegrityViolationException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public DataIntegrityViolationException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected DataIntegrityViolationException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/DataRetrievalFailureException.cs b/src/Spring/Spring.Data/Dao/DataRetrievalFailureException.cs new file mode 100644 index 00000000..0c5c8238 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/DataRetrievalFailureException.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown if certain expected data could not be retrieved, e.g. + /// when looking up specific data via a known identifier. + /// + /// + ///

    + /// This exception will be thrown either by O/R mapping tools or by custom DAO + /// implementations. + ///

    + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: DataRetrievalFailureException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public class DataRetrievalFailureException : DataAccessException + { + /// + /// Creates a new instance of the + /// class. + /// + public DataRetrievalFailureException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public DataRetrievalFailureException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public DataRetrievalFailureException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected DataRetrievalFailureException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/DeadlockLoserDataAccessException.cs b/src/Spring/Spring.Data/Dao/DeadlockLoserDataAccessException.cs new file mode 100644 index 00000000..ae62ad45 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/DeadlockLoserDataAccessException.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Generic exception thrown when the current process was + /// a deadlock loser, and its transaction rolled back. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: DeadlockLoserDataAccessException.cs,v 1.6 2008/04/08 20:26:43 markpollack Exp $ + [Serializable] + public class DeadlockLoserDataAccessException : PessimisticLockingFailureException + { + /// + /// Creates a new instance of the + /// class. + /// + public DeadlockLoserDataAccessException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public DeadlockLoserDataAccessException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public DeadlockLoserDataAccessException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected DeadlockLoserDataAccessException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/EmptyResultDataAccessException.cs b/src/Spring/Spring.Data/Dao/EmptyResultDataAccessException.cs new file mode 100644 index 00000000..fb83d4f0 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/EmptyResultDataAccessException.cs @@ -0,0 +1,94 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Data access exception thrown when a result was not of the expected size, + /// for example when expecting a single row but getting 0 or more than 1 rows. + /// + /// Mark Pollack (.NET) + /// Juergen Hoeller + /// $Id: EmptyResultDataAccessException.cs,v 1.2 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public class EmptyResultDataAccessException : IncorrectResultSizeDataAccessException + { + + /// + /// Initializes a new instance of the class. + /// + public EmptyResultDataAccessException() : base () + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public EmptyResultDataAccessException(string message) : base (message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public EmptyResultDataAccessException(string message, Exception innerException) : base (message, innerException) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// The expected size. + public EmptyResultDataAccessException(int expectedSize) : base (expectedSize, 0) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// A message about the exception. + /// The expected size. + public EmptyResultDataAccessException( string message, int expectedSize ) : base( message, expectedSize, 0 ) {} + + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is . + /// The class name is or is zero (0). + protected EmptyResultDataAccessException( SerializationInfo info, StreamingContext context ) : base( info, context ) + { + } + } +} diff --git a/src/Spring/Spring.Data/Dao/IncorrectResultSizeDataAccessException.cs b/src/Spring/Spring.Data/Dao/IncorrectResultSizeDataAccessException.cs new file mode 100644 index 00000000..c0064c19 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/IncorrectResultSizeDataAccessException.cs @@ -0,0 +1,173 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Data access exception thrown when a result was not of the expected size, + /// for example when expecting a single row but getting 0 or more than 1 rows. + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: IncorrectResultSizeDataAccessException.cs,v 1.6 2007/11/20 04:03:48 markpollack Exp $ + [Serializable] + public class IncorrectResultSizeDataAccessException : InvalidDataAccessApiUsageException, ISerializable + { + private int _expectedSize; + private int _actualSize; + + /// Return the expected result size. + public virtual int ExpectedSize + { + get + { + return _expectedSize; + } + } + + /// Return the actual result size (or -1 if unknown). + public virtual int ActualSize + { + get + { + return _actualSize; + } + } + + /// + /// Creates a new instance of the + /// class. + /// + public IncorrectResultSizeDataAccessException() : this( -1, -1 ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public IncorrectResultSizeDataAccessException( string message ) : this( message, -1, -1 ) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public IncorrectResultSizeDataAccessException( string message, Exception rootCause ) + : base( message, rootCause ) + { + _expectedSize = -1; + _actualSize = -1; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// The expected result size. + /// The actual result size (or -1 if unknown). + public IncorrectResultSizeDataAccessException( int expectedSize, int actualSize ) + : this ( "Incorrect result size: expected " + expectedSize + ", actual " + actualSize, expectedSize, actualSize) + { + } + + /// + /// Creates a new instance of the + /// class. + /// > + /// + /// A message about the exception. + /// + /// The expected result size. + /// The actual result size (or -1 if unknown). + public IncorrectResultSizeDataAccessException( string message, int expectedSize, int actualSize ) + : base( message ) + { + this._expectedSize = expectedSize; + this._actualSize = actualSize; + } + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception + /// The expected result size. + public IncorrectResultSizeDataAccessException(string message, int expectedSize) + : base(message) + { + this._expectedSize = expectedSize; + this._actualSize = -1; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected IncorrectResultSizeDataAccessException( SerializationInfo info, StreamingContext context ) : base( info, context ) + { + _expectedSize = info.GetInt32( "expectedSize" ); + _actualSize = info.GetInt32( "actualSize" ); + } + + #region ISerializable Members + /// + /// Override of + /// to allow for private serialization. + /// + /// + /// The + /// that holds the serialized object data about the exception. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue( "expectedSize", _expectedSize ); + info.AddValue( "actualSize", _actualSize ); + base.GetObjectData( info, context ); + } + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/IncorrectUpdateSemanticsDataAccessException.cs b/src/Spring/Spring.Data/Dao/IncorrectUpdateSemanticsDataAccessException.cs new file mode 100644 index 00000000..e50c3834 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/IncorrectUpdateSemanticsDataAccessException.cs @@ -0,0 +1,97 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Data access exception thrown when something unintended appears to have + /// happened with an update, but the transaction hasn't already been rolled back. + /// + /// + ///

    + /// Thrown, for example, when we wanted to update 1 row in an RDBMS but actually + /// updated 3. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: IncorrectUpdateSemanticsDataAccessException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public abstract class IncorrectUpdateSemanticsDataAccessException + : InvalidDataAccessResourceUsageException + { + /// Return whether or not data was updated. + /// + /// True if data was updated (as opposed to being incorrectly + /// updated). If this property returns false, there's nothing to roll back. + /// + public abstract bool DataWasUpdated { get; } + + /// + /// Creates a new instance of the + /// class. + /// + protected IncorrectUpdateSemanticsDataAccessException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + protected IncorrectUpdateSemanticsDataAccessException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + protected IncorrectUpdateSemanticsDataAccessException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected IncorrectUpdateSemanticsDataAccessException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/InvalidDataAccessApiUsageException.cs b/src/Spring/Spring.Data/Dao/InvalidDataAccessApiUsageException.cs new file mode 100644 index 00000000..f7f27c01 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/InvalidDataAccessApiUsageException.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown on incorrect usage of the API, such as failing to "compile" a query + /// object that needed compilation before execution. + /// + /// + ///

    + /// This represents a problem in our data access framework, not the underlying data access + /// infrastructure. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: InvalidDataAccessApiUsageException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public class InvalidDataAccessApiUsageException : DataAccessException + { + /// + /// Creates a new instance of the + /// class. + /// + public InvalidDataAccessApiUsageException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public InvalidDataAccessApiUsageException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public InvalidDataAccessApiUsageException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected InvalidDataAccessApiUsageException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/InvalidDataAccessResourceUsageException.cs b/src/Spring/Spring.Data/Dao/InvalidDataAccessResourceUsageException.cs new file mode 100644 index 00000000..65cec5ef --- /dev/null +++ b/src/Spring/Spring.Data/Dao/InvalidDataAccessResourceUsageException.cs @@ -0,0 +1,88 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Root for exceptions thrown when we use a data access resource incorrectly. + /// + /// + ///

    + /// Thrown for example on specifying bad SQL when using a RDBMS. + /// Resource-specific subclasses will probably be supplied by data access packages. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: InvalidDataAccessResourceUsageException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public class InvalidDataAccessResourceUsageException : DataAccessException + { + /// + /// Creates a new instance of the + /// class. + /// + public InvalidDataAccessResourceUsageException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public InvalidDataAccessResourceUsageException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public InvalidDataAccessResourceUsageException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected InvalidDataAccessResourceUsageException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/ObjectOptimisticLockingFailureException.cs b/src/Spring/Spring.Data/Dao/ObjectOptimisticLockingFailureException.cs new file mode 100644 index 00000000..39fd8b0c --- /dev/null +++ b/src/Spring/Spring.Data/Dao/ObjectOptimisticLockingFailureException.cs @@ -0,0 +1,176 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown on an optimistic locking violation for a mapped object. + /// Provides information about the persistent class and the identifier. + /// + /// Mark Pollack (.NET) + /// $Id: ObjectOptimisticLockingFailureException.cs,v 1.1 2006/12/13 18:40:11 markpollack Exp $ + [Serializable] + public class ObjectOptimisticLockingFailureException : OptimisticLockingFailureException, ISerializable + { + #region Fields + + private object persistentClass; + + private object identifier; + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public ObjectOptimisticLockingFailureException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception.. + public ObjectOptimisticLockingFailureException(string message) : base(message) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception from the underlying data access API + /// + public ObjectOptimisticLockingFailureException(string message, Exception rootCause) : base(message, rootCause) + { + } + + public ObjectOptimisticLockingFailureException(Type persistentClass, Object identifier) : this(persistentClass, identifier, + "Object of class [" + persistentClass.Name + "] with identifier [" + identifier + + "]: optimistic locking failed", null) + { + + } + + public ObjectOptimisticLockingFailureException( + Type persistentClass, Object identifier, String msg, Exception ex) : base(msg, ex) + { + this.persistentClass = persistentClass; + this.identifier = identifier; + } + + public ObjectOptimisticLockingFailureException(String persistentClassName, Object identifier) : this(persistentClassName, identifier, + "Object of class [" + persistentClassName + "] with identifier [" + identifier + + "]: optimistic locking failed", null) + { + + } + + public ObjectOptimisticLockingFailureException( + String persistentClassName, Object identifier, String msg, Exception ex) : base(msg, ex) + { + this.persistentClass = persistentClassName; + this.identifier = identifier; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectOptimisticLockingFailureException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + #endregion + + public Type PersistentClass + { + get { return persistentClass as Type;} + } + + public object Identifier + { + get { return identifier; } + } + + public string PersistentClassName + { + get + { + if (this.persistentClass is Type) + { + return ((Type) this.persistentClass).Name; + } + return (this.persistentClass != null ? this.persistentClass.ToString() : null); + + } + } + + + + + #region Properties + + #endregion + + #region Methods + + #endregion + + #region ISerializable Members + + /// + /// When overridden in a derived class, sets the + /// with information about the exception. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is a null reference ( in Visual Basic). + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue( "persistentClass", persistentClass ); + info.AddValue( "identifier", identifier ); + base.GetObjectData( info, context ); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/ObjectRetrievalFailureException.cs b/src/Spring/Spring.Data/Dao/ObjectRetrievalFailureException.cs new file mode 100644 index 00000000..42c37439 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/ObjectRetrievalFailureException.cs @@ -0,0 +1,157 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown if a mapped object could not be retrieved via its identifier. + /// Provides information about the persistent class and the identifier. + /// + /// Mark Pollack (.NET) + /// $Id: ObjectRetrievalFailureException.cs,v 1.1 2006/12/13 18:40:11 markpollack Exp $ + [Serializable] + public class ObjectRetrievalFailureException : DataRetrievalFailureException, ISerializable + { + private object persistentClass; + + private object identifier; + + #region Constructor (s) + + /// + /// Creates a new instance of the + /// class. + /// + public ObjectRetrievalFailureException() {} + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception. + public ObjectRetrievalFailureException(string message) : base(message) + { + + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception from the underlying data access API + /// + public ObjectRetrievalFailureException(string message, Exception rootCause) : base(message, rootCause) + { + } + + + public ObjectRetrievalFailureException(Type persistentClass, object identifier) : this(persistentClass, identifier, "Object of class [" + persistentClass.Name + "] with identifier [" + identifier + "]: not found", null) + { + } + + + public ObjectRetrievalFailureException( + Type persistentClass, Object identifier, String msg, Exception ex) : base(msg, ex) + { + this.persistentClass = persistentClass; + this.identifier = identifier; + } + + public ObjectRetrievalFailureException(String persistentClassName, Object identifier) : this(persistentClassName, identifier, + "Object of class [" + persistentClassName + "] with identifier [" + identifier + "]: not found", + null) + { + + } + + public ObjectRetrievalFailureException( + String persistentClassName, Object identifier, String msg, Exception ex) : base(msg, ex) + { + this.persistentClass = persistentClassName; + this.identifier = identifier; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected ObjectRetrievalFailureException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + + + #endregion + + public object PersistentClass + { + get { return persistentClass; } + } + + public object Identifier + { + get { return identifier; } + } + + public string PersistentClassName + { + get + { + if (this.persistentClass is Type) + { + return ((Type) this.persistentClass).Name; + } + return (this.persistentClass != null ? this.persistentClass.ToString() : null); + + } + } + + /// + /// When overridden in a derived class, sets the + /// with information about the exception. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is a null reference ( in Visual Basic). + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue( "persistentClass", persistentClass ); + info.AddValue( "identifier", identifier ); + base.GetObjectData( info, context ); + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/OptimisticLockingFailureException.cs b/src/Spring/Spring.Data/Dao/OptimisticLockingFailureException.cs new file mode 100644 index 00000000..a77e3044 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/OptimisticLockingFailureException.cs @@ -0,0 +1,88 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown on an optimistic locking violation. + /// + /// + ///

    + /// This exception will be thrown either by O/R mapping tools or by custom DAO + /// implementations. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: OptimisticLockingFailureException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public class OptimisticLockingFailureException : ConcurrencyFailureException + { + /// + /// Creates a new instance of the + /// class. + /// + public OptimisticLockingFailureException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public OptimisticLockingFailureException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public OptimisticLockingFailureException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected OptimisticLockingFailureException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/PermissionDeniedDataAccessException.cs b/src/Spring/Spring.Data/Dao/PermissionDeniedDataAccessException.cs new file mode 100644 index 00000000..9d91eb92 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/PermissionDeniedDataAccessException.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown when the underlyingresource denied a permission to + /// access a specific element, such as a specific database table. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: PermissionDeniedDataAccessException.cs,v 1.1 2006/06/12 10:39:19 markpollack Exp $ + [Serializable] + public class PermissionDeniedDataAccessException : InvalidDataAccessResourceUsageException + { + /// + /// Creates a new instance of the + /// class. + /// + public PermissionDeniedDataAccessException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public PermissionDeniedDataAccessException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public PermissionDeniedDataAccessException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected PermissionDeniedDataAccessException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/PessimisticLockingFailureException.cs b/src/Spring/Spring.Data/Dao/PessimisticLockingFailureException.cs new file mode 100644 index 00000000..901ab619 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/PessimisticLockingFailureException.cs @@ -0,0 +1,91 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown on a pessimistic locking violation. + /// + /// + /// Serves as a superclass for more specific exceptions, like + /// CannotAcquireLockException and DeadlockLoserDataAccessException + /// + /// + /// This exception will be thrown either by O/R mapping tools or by custom DAO + /// implementations. + /// + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: PessimisticLockingFailureException.cs,v 1.1 2008/04/08 20:26:43 markpollack Exp $ + [Serializable] + public class PessimisticLockingFailureException : ConcurrencyFailureException + { + /// + /// Creates a new instance of the + /// class. + /// + public PessimisticLockingFailureException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public PessimisticLockingFailureException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public PessimisticLockingFailureException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected PessimisticLockingFailureException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/Support/DaoSupport.cs b/src/Spring/Spring.Data/Dao/Support/DaoSupport.cs new file mode 100644 index 00000000..e547f7ac --- /dev/null +++ b/src/Spring/Spring.Data/Dao/Support/DaoSupport.cs @@ -0,0 +1,115 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Dao.Support +{ + /// + /// Generic base class for DAOs, defining template methods for DAO initialization. + /// + /// + /// Extended by Spring's specific DAO support classes, such as: + /// AdoDaoSupport, HibernateDaoSupport, etc. + /// + /// Mark Pollack (.NET) + /// $Id: DaoSupport.cs,v 1.3 2006/11/13 07:04:45 markpollack Exp $ + public abstract class DaoSupport : IInitializingObject + { + #region Fields + + #endregion + + #region Constants + + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (DaoSupport)); + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public DaoSupport() + { + + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + /// + /// Abstract subclasses must override this to check their configuration. + /// + /// + ///

    Implementors should be marked as sealed, to make it clear that + /// concrete subclasses are not supposed to override this template method themselves. + ///

    + ///
    + protected abstract void CheckDaoConfig(); + + /// + /// Concrete subclasses can override this for custom initialization behavior. + /// + /// + /// Gets called after population of this instance's object properties. + /// Exception thrown if InitDao fails will be rethrown as + /// a ObjectInitializationException. + /// + protected virtual void InitDao() + { + + } + + + #endregion + + public void AfterPropertiesSet() + { + // Let abstract subclasses check their configuration. + CheckDaoConfig(); + + // Let concrete implementations initialize themselves. + try + { + InitDao(); + } + catch (Exception ex) + { + throw new ObjectInitializationException("Initialization of DAO failed: " + ex.Message, ex); + } + } + } +} diff --git a/src/Spring/Spring.Data/Dao/Support/DataAccessUtils.cs b/src/Spring/Spring.Data/Dao/Support/DataAccessUtils.cs new file mode 100644 index 00000000..c93e984d --- /dev/null +++ b/src/Spring/Spring.Data/Dao/Support/DataAccessUtils.cs @@ -0,0 +1,72 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Util; + +#endregion + +namespace Spring.Dao.Support +{ + /// + /// Miscellaneous utility methods for DAO implementations. + /// Useful with any data access technology. + /// + /// Mark Pollack (.NET) + /// $Id: DataAccessUtils.cs,v 1.3 2006/12/02 07:35:23 markpollack Exp $ + public class DataAccessUtils + { + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public DataAccessUtils() + { + + } + + #endregion + + #region Methods + + public static object RequiredUniqueResultSet(IList results) + { + int size = (results != null ? results.Count : 0); + if (size == 0) + { + throw new EmptyResultDataAccessException(1); + } + if ( ! CollectionUtils.HasUniqueObject(results)) + { + throw new IncorrectResultSizeDataAccessException(1, size); + } + IEnumerator enumerator = results.GetEnumerator(); + enumerator.MoveNext(); + return enumerator.Current; + } + + #endregion + } + +} diff --git a/src/Spring/Spring.Data/Dao/Support/Generic/DataAccessUtils.cs b/src/Spring/Spring.Data/Dao/Support/Generic/DataAccessUtils.cs new file mode 100644 index 00000000..99b53f48 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/Support/Generic/DataAccessUtils.cs @@ -0,0 +1,76 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Collections; +using System.Collections.Generic; +using Spring.Util.Generic; + +#endregion + +namespace Spring.Dao.Support.Generic +{ + /// + /// Miscellaneous utility methods for DAO implementations. + /// Useful with any data access technology. + /// + /// Mark Pollack (.NET) + /// $Id: DataAccessUtils.cs,v 1.1 2006/12/02 20:50:21 markpollack Exp $ + public class DataAccessUtils + { + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public DataAccessUtils() + { + + } + + #endregion + + #region Methods + + public static T RequiredUniqueResultSet(IList results) + { + int size = (results != null ? results.Count : 0); + if (size == 0) + { + throw new EmptyResultDataAccessException(1); + } + if ( ! CollectionUtils.HasUniqueObject(results)) + { + throw new IncorrectResultSizeDataAccessException(1, size); + } + IEnumerator enumerator = results.GetEnumerator(); + enumerator.MoveNext(); + return enumerator.Current; + } + + #endregion + } + +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/TypeMismatchDataAccessException.cs b/src/Spring/Spring.Data/Dao/TypeMismatchDataAccessException.cs new file mode 100644 index 00000000..b74c8fa7 --- /dev/null +++ b/src/Spring/Spring.Data/Dao/TypeMismatchDataAccessException.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Exception thrown on mismatch between CLS type and database type: + /// for example on an attempt to set an object of the wrong type + /// in an RDBMS column. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: TypeMismatchDataAccessException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public class TypeMismatchDataAccessException : InvalidDataAccessResourceUsageException + { + /// + /// Creates a new instance of the + /// class. + /// + public TypeMismatchDataAccessException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public TypeMismatchDataAccessException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public TypeMismatchDataAccessException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected TypeMismatchDataAccessException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Dao/UncategorizedDataAccessException.cs b/src/Spring/Spring.Data/Dao/UncategorizedDataAccessException.cs new file mode 100644 index 00000000..1ea9397e --- /dev/null +++ b/src/Spring/Spring.Data/Dao/UncategorizedDataAccessException.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Dao +{ + /// + /// Normal superclass when we can't distinguish anything more specific + /// than "something went wrong with the underlying resource": for example, + /// a SQLException from Sql Server that we can't pinpoint more precisely. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: UncategorizedDataAccessException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + [Serializable] + public abstract class UncategorizedDataAccessException : DataAccessException + { + /// + /// Creates a new instance of the + /// class. + /// + public UncategorizedDataAccessException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public UncategorizedDataAccessException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception (from the underlying data access API, such as ADO.NET). + /// + public UncategorizedDataAccessException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected UncategorizedDataAccessException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/BadSqlGrammarException.cs b/src/Spring/Spring.Data/Data/BadSqlGrammarException.cs new file mode 100644 index 00000000..80f0d98a --- /dev/null +++ b/src/Spring/Spring.Data/Data/BadSqlGrammarException.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using Spring.Dao; + +#endregion + +namespace Spring.Data +{ + /// + /// Exception thrown when SQL specified is invalid. + /// + /// Mark Pollack (.NET) + /// $Id: BadSqlGrammarException.cs,v 1.3 2006/11/29 07:18:45 markpollack Exp $ + [Serializable] + public class BadSqlGrammarException : InvalidDataAccessResourceUsageException, ISerializable + { + #region Fields + + /// + /// SQL that led to the problem + /// + private string sql; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public BadSqlGrammarException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception. + public BadSqlGrammarException(string message): base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception. + /// The inner exception. + public BadSqlGrammarException(string message, Exception inner): base(message, inner) + { + } + + + /// + /// Initializes a new instance of the class. + /// + /// name of the current task. + /// The offending SQL statment + /// The root cause. + public BadSqlGrammarException(string task, String sql, Exception ex) : base(task + "; bad SQL grammar [" + sql + "]; " + ex.Message, ex) + { + this.sql = sql; + } + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected BadSqlGrammarException( SerializationInfo info, StreamingContext context ) : base( info, context ) {} + + + + #endregion + + #region Properties + + public string Sql + { + get + { + return sql; + } + } + #endregion + + + #region ISerializable Members + + /// + /// When overridden in a derived class, sets the + /// with information about the exception. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is a null reference ( in Visual Basic). + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue( "sql", sql ); + base.GetObjectData( info, context ); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Data/Data/CannotGetAdoConnectionException.cs b/src/Spring/Spring.Data/Data/CannotGetAdoConnectionException.cs new file mode 100644 index 00000000..649cda4e --- /dev/null +++ b/src/Spring/Spring.Data/Data/CannotGetAdoConnectionException.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using Spring.Dao; + +#endregion + +namespace Spring.Data +{ + /// + /// Fatal exception thrown when we can't connect to an RDBMS using ADO.NET + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: CannotGetAdoConnectionException.cs,v 1.1 2006/11/24 05:57:46 markpollack Exp $ + [Serializable] + public class CannotGetAdoConnectionException : InvalidDataAccessResourceUsageException, ISerializable + { + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public CannotGetAdoConnectionException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception. + public CannotGetAdoConnectionException(string message): base(message) + { + } + + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception. + /// The inner exception. + public CannotGetAdoConnectionException(string message, Exception inner) + : base(message, inner) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected CannotGetAdoConnectionException(SerializationInfo info, StreamingContext context) : base(info, context) { } + + + + #endregion + + + + } +} diff --git a/src/Spring/Spring.Data/Data/CommandDelegate.cs b/src/Spring/Spring.Data/Data/CommandDelegate.cs new file mode 100644 index 00000000..5031e919 --- /dev/null +++ b/src/Spring/Spring.Data/Data/CommandDelegate.cs @@ -0,0 +1,42 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; + +namespace Spring.Data +{ + /// + /// Callback delegate for code that operates on a IDbCommand. + /// + /// the ADO.NET command object. + /// + ///

    Allows you to execute any number of operations + /// on a single IDbCommand, for example a single ExecuteScalar + /// call or repeated execute calls with varying parameters. + ///

    + ///

    Used internally by AdoTemplate, but also useful for + /// application code. Note that the passed in IDbCommand + /// has been created by the framework and will have its + /// Connection property set and the Transaction property + /// set based on the transaction context.

    + ///
    + /// Mark Pollack + public delegate object CommandDelegate(IDbCommand command); +} diff --git a/src/Spring/Spring.Data/Data/CommandSetterDelegate.cs b/src/Spring/Spring.Data/Data/CommandSetterDelegate.cs new file mode 100644 index 00000000..6427eec5 --- /dev/null +++ b/src/Spring/Spring.Data/Data/CommandSetterDelegate.cs @@ -0,0 +1,34 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; + +namespace Spring.Data +{ + /// + /// Callback delegate to set any necessary parameters or other + /// properties on the command object. The CommandType and + /// CommandText properties will have already been supplied + /// + /// The database command object. + /// Mark Pollack + public delegate void CommandSetterDelegate(IDbCommand dbCommand); + +} diff --git a/src/Spring/Spring.Data/Data/Common/DbMetadata.cs b/src/Spring/Spring.Data/Data/Common/DbMetadata.cs new file mode 100644 index 00000000..69cf20c7 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/DbMetadata.cs @@ -0,0 +1,420 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Util; + +#endregion + +namespace Spring.Data.Common +{ + /// + /// Provides database metdata information. + /// + /// Mark Pollack (.NET) + /// $Id: DbMetadata.cs,v 1.13 2007/11/21 18:01:45 markpollack Exp $ + public class DbMetadata : IDbMetadata + { + #region Fields + + private bool supportsDeriveParametersMethod = true; + + private string productName; + private string assemblyName; + private Type connectionType; + private Type commandType; + private Type parameterType; + private Type dataAdapterType; + + + private Type parameterDbType; + private PropertyInfo parameterDbTypeProperty; + private PropertyInfo parameterIsNullableProperty; + private string parameterNamePrefix; + + private Type exceptionType; + private bool useParameterNamePrefixInParameterCollection; + private bool useParameterPrefixInSql; + private bool bindByName; + + + + + private Type commandBuilderType; + private MethodInfo commandBuilderDeriveParametersMethod; + + private string errorCodeExceptionExpression = string.Empty; + + private ErrorCodes errorCodes; + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + /// Name of the product. + /// Name of the assembly. + /// Type of the connection. + /// Type of the command. + /// Type of the parameter. + /// Type of the data adapter. + /// Type of the command builder. + /// The command builder derive parameters method. + /// Type of the parameter db. + /// The parameter db type property. + /// The parameter is nullable property. + /// The parameter name prefix. + /// Type of the exception. + /// if set to true [use parameter name prefix in parameter collection]. + /// if set to true [use parameter prefix in SQL]. + /// if set to true [bind by name]. + /// The error code exception expression. + public DbMetadata(string productName, + string assemblyName, + Type connectionType, + Type commandType, + Type parameterType, + Type dataAdapterType, + Type commandBuilderType, + string commandBuilderDeriveParametersMethod, + Type parameterDbType, + string parameterDbTypeProperty, + string parameterIsNullableProperty, + string parameterNamePrefix, + Type exceptionType, + bool useParameterNamePrefixInParameterCollection, + bool useParameterPrefixInSql, + bool bindByName, + string errorCodeExceptionExpression + + ) + { + + AssertUtils.ArgumentHasText(productName, "ProductName"); + this.productName = productName; + AssertUtils.ArgumentHasText(assemblyName, "assemblyName", GetErrorMessage() ); + AssertUtils.ArgumentNotNull(connectionType, "connectionType", GetErrorMessage() ); + AssertUtils.ArgumentNotNull(commandType, "commandType", GetErrorMessage()); + AssertUtils.ArgumentNotNull(parameterType, "parameterType", GetErrorMessage()); + AssertUtils.ArgumentNotNull(dataAdapterType, "dataAdapterType", GetErrorMessage()); + AssertUtils.ArgumentNotNull(commandBuilderType, "commandBuilderType", GetErrorMessage()); + AssertUtils.ArgumentHasText(commandBuilderDeriveParametersMethod, "commandBuilderDeriveParametersMethod", GetErrorMessage()); + AssertUtils.ArgumentNotNull(parameterDbType, "parameterDbType", GetErrorMessage()); + AssertUtils.ArgumentHasText(parameterDbTypeProperty, "parameterDbTypeProperty", GetErrorMessage()); + AssertUtils.ArgumentHasText(parameterIsNullableProperty, "parameterIsNullableProperty", GetErrorMessage()); + AssertUtils.ArgumentHasText(parameterNamePrefix, "parameterNamePrefix", GetErrorMessage()); + AssertUtils.ArgumentNotNull(exceptionType, "exceptionType", GetErrorMessage()); + + this.assemblyName = assemblyName; + + this.connectionType = connectionType; + this.commandType = commandType; + this.parameterType = parameterType; + this.dataAdapterType = dataAdapterType; + this.commandBuilderType = commandBuilderType; + + //TODO consider support for derive parameters via a stored procedure. + if (commandBuilderDeriveParametersMethod.ToLower().Trim().Equals("not supported")) + { + supportsDeriveParametersMethod = false; + } + + if (supportsDeriveParametersMethod) + { + this.commandBuilderDeriveParametersMethod = + ReflectionUtils.GetMethod(this.commandBuilderType, commandBuilderDeriveParametersMethod, + new Type[] {this.commandType}); + + AssertUtils.ArgumentNotNull(this.commandBuilderDeriveParametersMethod, + "commandBuilderDeriveParametersMethod", GetErrorMessage() + + ", could not resolve commandBuilderDeriveParametersMethod " + + commandBuilderDeriveParametersMethod + + " to MethodInfo. Please check dbproviders.xml entry for correct metadata listing."); + } + + this.commandType = commandType; + + this.parameterDbType = parameterDbType; + + this.parameterDbTypeProperty = this.parameterType.GetProperty(parameterDbTypeProperty, + BindingFlags.Instance | BindingFlags.Public); + AssertUtils.ArgumentNotNull(this.parameterDbTypeProperty, "parameterDbTypeProperty", GetErrorMessage() + + ", could not resolve parameterDbTypeProperty " + + parameterDbTypeProperty + + " to PropertyInfo. Please check dbproviders.xml entry for correct metadata listing."); + + this.parameterIsNullableProperty = this.parameterType.GetProperty(parameterIsNullableProperty, + BindingFlags.Instance | BindingFlags.Public); + + AssertUtils.ArgumentNotNull(this.parameterIsNullableProperty, "parameterIsNullableProperty", GetErrorMessage() + + ", could not resolve parameterIsNullableProperty " + + parameterIsNullableProperty + + " to PropertyInfo. Please check dbproviders.xml entry for correct metadata listing."); + + this.parameterNamePrefix = parameterNamePrefix; + this.exceptionType = exceptionType; + + this.useParameterNamePrefixInParameterCollection = useParameterNamePrefixInParameterCollection; + this.useParameterPrefixInSql = useParameterPrefixInSql; + this.bindByName = bindByName; + + this.errorCodeExceptionExpression = errorCodeExceptionExpression; + + + errorCodes = new ErrorCodes(); + + + } + + private string GetErrorMessage() + { + return "DbProvider product name = " + productName; + } + + + #endregion + + #region IDbMetadata Members + + /// + /// Gets a value indicating whether to use param name prefix in parameter collection. + /// + /// + /// true if should use param name prefix in parameter collection; otherwise, false. + /// + public bool UseParamNamePrefixInParameterCollection + { + get { return useParameterNamePrefixInParameterCollection; } + } + + /// + /// Gets the name of the assembly. + /// + /// Example: System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The name of the assembly. + public string AssemblyName + { + get { return assemblyName; } + } + + /// + /// Gets a descriptive name of the product. + /// + /// Example: Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0 + /// The name of the product. + public string ProductName + { + get { return productName; } + } + + /// + /// Gets the type of the connection. The fully qualified type name is given since some providers, + /// notably for SqlServerCe, do not use the same namespace for all data access types. + /// + /// Example: System.Data.SqlClient.SqlCommand, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the connection. + public Type ConnectionType + { + get { return connectionType; } + } + + /// + /// Gets the type of the command. + /// + /// Example: System.Data.SqlClient.SqlCommand, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the command. + public Type CommandType + { + get { return commandType; } + } + + /// + /// Gets the type of the parameter. + /// + /// Example: System.Data.SqlClient.SqlParameter, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the parameter. + public Type ParameterType + { + get { return parameterType; } + } + + /// + /// Gets the type of the data adapter. + /// + /// Example: System.Data.SqlClient.SqlDataAdapter, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the data adapter. + public Type DataAdapterType + { + get { return dataAdapterType; } + } + + /// + /// Gets the type of the command builder. + /// + /// Example: System.Data.SqlClient.SqlCommandBuilder, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the command builder. + public Type CommandBuilderType + { + get { return commandBuilderType; } + } + + /// + /// Gets the command builder derive parameters method. + /// + /// If the value 'not supported' is specified then this method will throw an ArgumentException + /// Example: DeriveParameters + /// The command builder derive parameters method. + public MethodInfo CommandBuilderDeriveParametersMethod + { + get + { + if (supportsDeriveParametersMethod) + { + return commandBuilderDeriveParametersMethod; + } else + { + throw new ArgumentException("This provider does not support the DeriveParameters functionality"); + } + } + } + + /// + /// Provide the prefix used to indentify named parameters in SQL text. + /// + /// @ for Sql Server + public string ParameterNamePrefix + { + get { return parameterNamePrefix; } + } + + /// + /// Gets the type of the exception. + /// + /// Example: System.Data.SqlClient.SqlException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the exception. + public Type ExceptionType + { + get { return exceptionType; } + } + + /// + /// Does the driver require the use of the parameter prefix when + /// specifying the name of the parameter in the Command's + /// Parameter collection. + /// + /// + /// If true, then commamd.Parameters["@parameterName"] is + /// used, otherwise, command.Parameters["parameterName"]. + /// + public bool UseParameterNamePrefixInParameterCollection + { + get { return useParameterNamePrefixInParameterCollection; } + } + + + /// + /// Gets a value indicating whether the Provider requires + /// the use of a named prefix in the SQL string. + /// + /// + /// true if use parameter prefix in SQL; otherwise, false. + /// + /// + /// The OLE DB/ODBC .NET Provider does not support named parameters for + /// passing parameters to an SQL Statement or a stored procedure called + /// by an IDbCommand when CommandType is set to Text. + /// + public bool UseParameterPrefixInSql + { + get { return useParameterPrefixInSql; } + } + + /// + /// For providers that allow you to choose between binding parameters + /// to a command by name (true) or by position (false). + /// + /// + public bool BindByName + { + get { return bindByName; } + set { bindByName = value; } + } + + /// + /// Gets the type of the parameter db. + /// + /// Example: System.Data.SqlDbType, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the parameter db. + public Type ParameterDbType + { + get { return parameterDbType; } + } + + /// + /// Gets the parameter db type property. + /// + /// Example: SqlDbType for SqlServer + /// The parameter db type property. + public PropertyInfo ParameterDbTypeProperty + { + get { return parameterDbTypeProperty; } + } + + /// + /// Gets the parameter is nullable property. + /// + /// Example: IsNullable for Sql Server + /// The parameter is nullable property. + public PropertyInfo ParameterIsNullableProperty + { + get { return parameterIsNullableProperty; } + } + + /// + /// Gets or sets the error codes. + /// + /// The collection of error codes to map error code integer values to Spring's DAO exception hierarchy + /// The error codes. + public ErrorCodes ErrorCodes + { + get { return errorCodes; } + set { errorCodes = value;} + } + + /// + /// Gets the error code exception expression. + /// + /// Example Errors[0].Number.ToString() for Sql Server + /// The error code exception expression. + public string ErrorCodeExceptionExpression + { + get { return errorCodeExceptionExpression; } + } + + #endregion + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Common/DbParameter.cs b/src/Spring/Spring.Data/Data/Common/DbParameter.cs new file mode 100644 index 00000000..819e9049 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/DbParameter.cs @@ -0,0 +1,194 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; + +#endregion + +namespace Spring.Data.Common +{ + /// + /// A parameter class used by + /// + /// Mark Pollack (.NET) + /// $Id: DbParameter.cs,v 1.6 2008/05/30 21:09:22 markpollack Exp $ + public class DbParameter : Spring.Data.Common.IDbParameter + { + #region Fields + private Enum dbType; + private ParameterDirection direction; + private bool isNullable; + private string name; + private string sourceColumn; + private DataRowVersion sourceVersion; + private object val; + private byte precision; + private byte scale; + private int size; + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + /// + /// Sets the SourceVersion to be Default and the ParameterDirection to be Input. + /// + public DbParameter() + { + sourceVersion = DataRowVersion.Default; + direction = ParameterDirection.Input; + } + + #endregion + + #region IDbParameter Members + + public Enum DbType + { + set + { + dbType = value; + } + } + + #endregion + + public IDbParameter Type(Enum dbType) + { + this.dbType = dbType; + return this; + } + + public IDbParameter Type(Enum dbType, int size) + { + this.dbType = dbType; + this.size = size; + return this; + } + + public Enum GetDbType() + { + return dbType; + } + + public IDbParameter Direction(ParameterDirection direction) + { + this.direction = direction; + return this; + } + + public ParameterDirection GetDirection() + { + return direction; + } + + public IDbParameter IsNullable(bool isNullable) + { + this.isNullable = isNullable; + return this; + } + + public bool GetIsNullable() + { + return isNullable; + } + + public IDbParameter Name(string name) + { + this.name = name; + return this; + } + + public string GetName() + { + return name; + } + + public IDbParameter SourceColumn(string sourceColumn) + { + this.sourceColumn = sourceColumn; + return this; + } + + public string GetSourceColumn() + { + return sourceColumn; + } + + public IDbParameter SourceVersion(DataRowVersion sourceVersion) + { + this.sourceVersion = sourceVersion; + return this; + } + + public DataRowVersion GetSourceVersion() + { + return sourceVersion; + } + + public IDbParameter Value(object val) + { + this.val = val; + return this; + } + + public object GetValue() + { + return val; + } + + public IDbParameter Precision(byte precision) + { + this.precision = precision; + return this; + } + + public byte GetPrecision() + { + return precision; + } + + public IDbParameter Scale(byte scale) + { + this.scale = scale; + return this; + } + + public byte GetScale() + { + return scale; + } + + public IDbParameter Size(int size) + { + this.size = size; + return this; + } + + public int GetSize() + { + return size; + } + } +} diff --git a/src/Spring/Spring.Data/Data/Common/DbParameters.cs b/src/Spring/Spring.Data/Data/Common/DbParameters.cs new file mode 100644 index 00000000..7a1a6bda --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/DbParameters.cs @@ -0,0 +1,302 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using System.Reflection; +using Spring.Dao; + +#endregion + +namespace Spring.Data.Common +{ + /// + /// A more portable means to create a collection of ADO.NET + /// parameters. + /// + /// Mark Pollack (.NET) + /// $Id: DbParameters.cs,v 1.14 2007/12/06 22:34:56 markpollack Exp $ + public class DbParameters : IDbParameters + { + + #region Fields + + private IDbProvider dbProvider; + + //Just used as a container for the underlying parameter collection. + private IDbCommand dbCommand; + private IDataParameterCollection dataParameterCollection; + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public DbParameters(IDbProvider dbProvider) + { + this.dbProvider = dbProvider; + dbCommand = dbProvider.CreateCommand(); + dataParameterCollection = dbCommand.Parameters; + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + #endregion + + public IDataParameter this[string parameterName] + { + get { return (IDbDataParameter) dataParameterCollection[parameterName]; } + set { dataParameterCollection[parameterName] = value; } + } + + public IDataParameter this[int index] + { + get { return (IDbDataParameter) dataParameterCollection[index]; } + set { dataParameterCollection[index] = value; } + } + + public IDataParameterCollection DataParameterCollection + { + get { return dataParameterCollection; } + } + + public int Count + { + get + { + return dataParameterCollection.Count; + } + } + public bool Contains(string parameterName) + { + return dataParameterCollection.Contains(parameterName); + } + + public void AddParameter(IDataParameter dbParameter) + { + dataParameterCollection.Add(dbParameter); + } + + + public IDbDataParameter AddParameter(string name, + Enum parameterType, + int size, + ParameterDirection direction, + bool isNullable, + byte precision, + byte scale, + string sourceColumn, + DataRowVersion sourceVersion, + object parameterValue) + { + IDbDataParameter parameter = dbCommand.CreateParameter(); + + parameter.ParameterName = dbProvider.CreateParameterNameForCollection(name); + + AssignParameterType(parameter, parameterType); + + if (size > 0) + { + parameter.Size = size; + } + parameter.Direction = direction; + + if (isNullable) + { + AssignIsNullable(isNullable, parameter); + } + + parameter.Precision = precision; + + if (scale > 0) + { + parameter.Scale = scale; + } + + if (sourceColumn != null && sourceColumn != string.Empty) + { + parameter.SourceColumn = sourceColumn; + } + + parameter.SourceVersion = sourceVersion; + + parameter.Value = (parameterValue == null) ? DBNull.Value : parameterValue; + + dataParameterCollection.Add(parameter); + + return parameter; + } + + public IDbDataParameter AddParameter(string name, + Enum parameterType, + ParameterDirection direction, + bool isNullable, + byte precision, + byte scale, + string sourceColumn, + DataRowVersion sourceVersion, + object parameterValue) + { + return AddParameter(name, parameterType, 0, direction, isNullable, precision, scale, sourceColumn, sourceVersion, + parameterValue); + } + + + public int Add(object parameterValue) + { + IDbDataParameter parameter = dbCommand.CreateParameter(); + //TODO investigate default behavior of values. + parameter.Value = (parameterValue == null) ? DBNull.Value : parameterValue; + dataParameterCollection.Add(parameter); + return dataParameterCollection.Count - 1; + } + + public void AddRange(Array values) + { + foreach (Array value in values) + { + Add(value); + } + } + + public IDbDataParameter AddWithValue(string name, object parameterValue) + { + return AddParameter(name, null, -1, ParameterDirection.Input, false, + 0, 0, null, DataRowVersion.Default, parameterValue); + } + + public IDbDataParameter Add(string name, Enum parameterType) + { + return AddParameter(name, parameterType, -1, ParameterDirection.Input, + false, 0, 0, null, DataRowVersion.Default, null); + } + + public IDbDataParameter Add(string name, Enum parameterType, int size) + { + return AddParameter(name, parameterType, size, ParameterDirection.Input, + false, 0, 0, null, DataRowVersion.Default, null); + } + + public IDbDataParameter Add(string name, Enum parameterType, int size, string sourceColumn) + { + return AddParameter(name, parameterType, size, ParameterDirection.Input, + false, 0, 0, sourceColumn, DataRowVersion.Default, null); + } + + public IDbDataParameter AddOut(string name, Enum parameterType) + { + return AddParameter(name, parameterType, -1, ParameterDirection.Output, + false, 0, 0, null, DataRowVersion.Default, null); + } + + public IDbDataParameter AddOut(string name, Enum parameterType, int size) + { + return AddParameter(name, parameterType, size, ParameterDirection.Output, + false, 0, 0, null, DataRowVersion.Default, null); + } + + public IDbDataParameter AddInOut(string name, Enum parameterType) + { + return AddParameter(name, parameterType, -1, ParameterDirection.InputOutput, + false, 0, 0, null, DataRowVersion.Default, null); + } + + public IDbDataParameter AddInOut(string name, Enum parameterType, int size) + { + return AddParameter(name, parameterType, size, ParameterDirection.InputOutput, + false, 0, 0, null, DataRowVersion.Default, null); + } + + public IDbDataParameter AddReturn(string name, Enum parameterType) + { + return AddParameter(name, parameterType, -1, ParameterDirection.ReturnValue, + false, 0, 0, null, DataRowVersion.Default, null); + } + + public IDbDataParameter AddReturn(string name, Enum parameterType, int size) + { + return AddParameter(name, parameterType, size, ParameterDirection.ReturnValue, + false, 0, 0, null, DataRowVersion.Default, null); + } + + + public object GetValue(string name) + { + return dataParameterCollection[dbProvider.CreateParameterNameForCollection(name)]; + } + + public void SetValue(string name, object parameterValue) + { + IDbDataParameter parameter = dataParameterCollection[dbProvider.CreateParameterNameForCollection(name)] as IDbDataParameter; + if (parameter != null) + { + parameter.Value = (parameterValue == null) ? DBNull.Value : parameterValue; + } + } + + + protected void AssignParameterType(IDbDataParameter parameter, Enum parameterType) + { + if (parameterType != null) + { + if (parameterType is DbType) + { + parameter.DbType = (DbType) parameterType; + } + else + { + if (parameterType.GetType() != dbProvider.DbMetadata.ParameterDbType) + { + throw new TypeMismatchDataAccessException("Invalid parameter type specified for parameter name [" + + parameter.ParameterName + "]. [" + + parameterType.GetType().AssemblyQualifiedName + "] is not of expected type [" + + dbProvider.DbMetadata.ParameterDbType.AssemblyQualifiedName + "]"); + } + dbProvider.DbMetadata.ParameterDbTypeProperty.SetValue(parameter, parameterType, null); + } + } + } + + protected void AssignIsNullable(bool isNullable, IDbDataParameter parameter) + { + PropertyInfo propertyInfo = dbProvider.DbMetadata.ParameterIsNullableProperty; + if (propertyInfo != null) + { + propertyInfo.SetValue(parameter, isNullable, null); + } + //TODO investigate default behavior. + if (parameter.Value == null) + { + parameter.Value = DBNull.Value; + } + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Common/DbParametersBuilder.cs b/src/Spring/Spring.Data/Data/Common/DbParametersBuilder.cs new file mode 100644 index 00000000..a5b6f769 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/DbParametersBuilder.cs @@ -0,0 +1,111 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using Common.Logging; + +#endregion + +namespace Spring.Data.Common +{ + /// + /// Assists in creating a collection of parameters by keeping track of all + /// IDbParameters its creates. After creating a number of parameters ask for the collection + /// with the GetParameters methods. + /// + /// This builder is stateful, you must create a new one for each collection + /// of parameters you would like to create. + /// Mark Pollack (.NET) + /// $Id: DbParametersBuilder.cs,v 1.2 2007/05/01 00:37:10 markpollack Exp $ + public class DbParametersBuilder : IDbParametersBuilder + { + #region Fields + + private IDbProvider dbProvider; + private IList parameterList; + + #endregion + + #region Constants + + /// + /// The shared log instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (DbParametersBuilder)); + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public DbParametersBuilder(IDbProvider dbProvider) + { + this.dbProvider = dbProvider; + parameterList = new ArrayList(); + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + public IDbParameter Create() + { + IDbParameter p = new DbParameter(); + parameterList.Add(p); + return p; + } + + public IDbParameters GetParameters() + { + IDbParameters dbParameters = CreateDbParameters(); + foreach (IDbParameter parameter in parameterList) + { + dbParameters.AddParameter(parameter.GetName(), + parameter.GetDbType(), + parameter.GetSize(), + parameter.GetDirection(), + parameter.GetIsNullable(), + parameter.GetPrecision(), + parameter.GetScale(), + parameter.GetSourceColumn(), + parameter.GetSourceVersion(), + parameter.GetValue()); + } + return dbParameters; + } + + + protected IDbParameters CreateDbParameters() + { + return new DbParameters(dbProvider); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Common/DbProvider.cs b/src/Spring/Spring.Data/Data/Common/DbProvider.cs new file mode 100644 index 00000000..c4d07e54 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/DbProvider.cs @@ -0,0 +1,250 @@ +#region Licence + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Data; +using Spring.Expressions; +using Spring.Reflection.Dynamic; +using Spring.Util; + +namespace Spring.Data.Common +{ + /// + /// Implemenation of of DbProvider that uses metadata to create provider specific ADO.NET objects. + /// + /// $Id: + public class DbProvider : IDbProvider + { + private string connectionString; + private IDbMetadata dbMetadata; + private IDynamicConstructor newCommand; + private IDynamicConstructor newConnection; + private IDynamicConstructor newCommandBuilder; + private IDynamicConstructor newDataAdapter; + private IDynamicConstructor newParameter; + + /// + /// Initializes a new instance of the class. + /// + /// The db metadata. + public DbProvider(IDbMetadata dbMetadata) + { + this.dbMetadata = dbMetadata; + newCommand = DynamicConstructor.Create(dbMetadata.CommandType.GetConstructor(Type.EmptyTypes)); + newConnection = DynamicConstructor.Create(dbMetadata.ConnectionType.GetConstructor(Type.EmptyTypes)); + newCommandBuilder = DynamicConstructor.Create(dbMetadata.CommandBuilderType.GetConstructor(Type.EmptyTypes)); + newDataAdapter = DynamicConstructor.Create(dbMetadata.DataAdapterType.GetConstructor(Type.EmptyTypes)); + newParameter = DynamicConstructor.Create(dbMetadata.ParameterType.GetConstructor(Type.EmptyTypes)); + } + + /// + /// Returns a new command object for executing SQL statments/Stored Procedures + /// against the database. + /// + /// An new + public IDbCommand CreateCommand() + { + return newCommand.Invoke(ObjectUtils.EmptyObjects) as IDbCommand; + } + + /// + /// Returns a new instance of the providers CommandBuilder class. + /// + /// A new Command Builder + /// In .NET 1.1 there was no common base class or interface + /// for command builders, hence the return signature is object to + /// be portable (but more loosely typed) across .NET 1.1/2.0 + public object CreateCommandBuilder() + { + return newCommandBuilder.Invoke(ObjectUtils.EmptyObjects); + } + + /// + /// Returns a new connection object to communicate with the database. + /// + /// A new + public IDbConnection CreateConnection() + { + IDbConnection conn = newConnection.Invoke(ObjectUtils.EmptyObjects) as IDbConnection; + if (conn != null) + { + conn.ConnectionString = ConnectionString; + } + return conn; + } + + /// + /// Returns a new adapter objects for use with offline DataSets. + /// + /// A new + public IDbDataAdapter CreateDataAdapter() + { + return newDataAdapter.Invoke(ObjectUtils.EmptyObjects) as IDbDataAdapter; + } + + /// + /// Returns a new parameter object for binding values to parameter + /// placeholders in SQL statements or Stored Procedure variables. + /// + /// A new + public IDbDataParameter CreateParameter() + { + return newParameter.Invoke(ObjectUtils.EmptyObjects) as IDbDataParameter; + } + + /// + /// Creates the name of the parameter in the format appropriate to use inside IDbCommand.CommandText. + /// + /// The unformatted name of the parameter. + /// + /// The parameter name formatted foran IDbCommand.CommandText. + /// + /// In most cases this adds the parameter prefix to the name passed into this method. + public string CreateParameterName(string name) + { + if (dbMetadata.BindByName) + { + if (DbMetadata.UseParameterPrefixInSql) + { + return DbMetadata.ParameterNamePrefix + name; + } + else + { + return name; + } + } + else + { + return DbMetadata.ParameterNamePrefix; + } + } + + /// + /// Creates the name ofthe parameter in the format appropriate for an IDataParameter, i.e. to be + /// part of a IDataParameterCollection. + /// + /// The unformatted name of the parameter. + /// + /// The parameter name formatted for an IDataParameter + /// + public string CreateParameterNameForCollection(string name) + { + if (dbMetadata.BindByName) + { + if (DbMetadata.UseParameterNamePrefixInParameterCollection) + { + return DbMetadata.ParameterNamePrefix + name; + } + else + { + return name; + } + } + else + { + return DbMetadata.ParameterNamePrefix; + } + } + + /// + /// Return metadata information about the database provider + /// + /// + public IDbMetadata DbMetadata + { + get { return dbMetadata; } + } + + /// + /// Connection string used to create connections. + /// + /// + public string ConnectionString + { + get { return connectionString; } + set { connectionString = value; } + } + + /// + /// Extracts the provider specific error code as a string. + /// + /// The data access exception. + /// The provider specific error code + public string ExtractError(Exception e) + { + + if (!StringUtils.IsNullOrEmpty(dbMetadata.ErrorCodeExceptionExpression)) + { + return ExpressionEvaluator.GetValue(e, dbMetadata.ErrorCodeExceptionExpression).ToString(); + } + else + { + return "Could not extract error code exception type." + e.GetType(); + } + + } + + + + /// + /// Determines whether the provided exception is in fact related + /// to database access. This can be provider dependent in .NET 1.1 since + /// there isn't a common base class for ADO.NET exceptions. + /// + /// The exception thrown when performing data access + /// operations. + /// + /// true if is a valid data access exception for the specified + /// exception; otherwise, false. + /// + public bool IsDataAccessException(Exception e) + { + +#if NET_2_0 + if (e is System.Data.Common.DbException) + { + return true; + } + else + { + return false; + } +#else + return IsDataAccessExceptionBCL11(e); +#endif + } + + /// + /// Determines whether is data access exception in .NET 1.1 for the specified exception. + /// + /// The candidate exception. + /// + /// true if is data access exception in .NET 1.1 for the specified exception; otherwise, false. + /// + private bool IsDataAccessExceptionBCL11(Exception e) + { + if (e == null) + { + return false; + } + return e.GetType().IsAssignableFrom(DbMetadata.ExceptionType); + } + } +} diff --git a/src/Spring/Spring.Data/Data/Common/DbProviderFactory.cs b/src/Spring/Spring.Data/Data/Common/DbProviderFactory.cs new file mode 100644 index 00000000..90089610 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/DbProviderFactory.cs @@ -0,0 +1,165 @@ +#region Licence + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Common.Logging; +using Spring.Context; +using Spring.Context.Support; +using Spring.Core.IO; +using Spring.Util; + +namespace Spring.Data.Common +{ + /// + /// Create DbProviders based on configuration information in assembly resource + /// dbproviders.xml + /// + /// + /// + /// + /// TODO: Provide over-ride resource location. + /// TODO: Add error codes to provider. + /// + /// Mark Pollack (.NET) + /// $Id: DbProviderFactory.cs,v 1.12 2007/08/16 20:38:43 markpollack Exp $ + public class DbProviderFactory + { + #region Constants + + /// + /// The shared log instance for this class (and derived classes). + /// + protected static ILog log = LogManager.GetLogger(typeof(DbProviderFactory)); + + private static readonly string DBPROVIDER_DEFAULT_RESOURCE_NAME = + "assembly://" + typeof(DbProviderFactory).Assembly.FullName + "/Spring.Data.Common/dbproviders.xml"; + + public static string DBPROVIDER_ADDITIONAL_RESOURCE_NAME = + "file://dbProviders.xml"; + + private static readonly string DBPROVIDER_CONTEXTNAME = "DBPROVIDERFACTORY_CONTEXT"; + + #endregion + + #region Fields + + private volatile static XmlApplicationContext ctx; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + static DbProviderFactory() + { + } + + #endregion + + + /// + /// Gets the DbProvider given an identifying name. + /// + /// + /// Familiar names for the .NET 2.0 provider model are supported, i.e. + /// System.Data.SqlClient. Refer to the documentation for a complete + /// listing of supported DbProviders and their names. You may + /// also use the method GetDbProviderClasses or obtain the + /// underlying IApplicationContext to progammatically obtain information + /// about supported providers. + /// + /// Name of the provider invariant. + /// + public static IDbProvider GetDbProvider(string providerInvariantName) + { + InitializeDbProviderFactoryIfNeeded(); + + return ctx.GetObject(providerInvariantName, typeof(IDbProvider)) as IDbProvider; + + } + + /// + /// Gets the application context that contains the definitions of the + /// various providers. + /// + /// This method should rarely, if ever, be used in + /// application code. It is used by the framework itself + /// to map other data access products abstractions for a 'DbProvider/DataSource' + /// onto Spring's model. + /// The application context. + public static IApplicationContext ApplicationContext + { + get + { + InitializeDbProviderFactoryIfNeeded(); + return ctx; + } + } + + //TODO GetDbProviderClasses + private static void InitializeDbProviderFactoryIfNeeded() + { + if (ctx == null) + { + lock(typeof(DbProviderFactory)) + { + if (ctx == null) + { + try + { + + //TODO get from well know location in app.config.... + ConfigurableResourceLoader loader = new ConfigurableResourceLoader(DBPROVIDER_ADDITIONAL_RESOURCE_NAME); + + + if (loader.GetResource(DBPROVIDER_ADDITIONAL_RESOURCE_NAME).Exists) + { + #region Instrumentation + if (log.IsDebugEnabled) + { + log.Debug("Loading additional DbProviders from " + DBPROVIDER_ADDITIONAL_RESOURCE_NAME); + } + #endregion + ctx = new XmlApplicationContext(DBPROVIDER_CONTEXTNAME, true, new string[] { DBPROVIDER_DEFAULT_RESOURCE_NAME, + DBPROVIDER_ADDITIONAL_RESOURCE_NAME}); + } + else + { + ctx = new XmlApplicationContext(DBPROVIDER_CONTEXTNAME, true, new string[] { DBPROVIDER_DEFAULT_RESOURCE_NAME }); + } + + string[] dbProviderNames = ctx.GetObjectNamesForType(typeof(IDbProvider)); + if (log.IsInfoEnabled) + { + log.Info(String.Format("{0} DbProviders Available. [{1}]", dbProviderNames.Length, StringUtils.ArrayToCommaDelimitedString(dbProviderNames))); + } + } + catch (Exception e) + { + log.Error("Error processing " + DBPROVIDER_DEFAULT_RESOURCE_NAME, e); + throw; + } + } + } + } + } + } +} diff --git a/src/Spring/Spring.Data/Data/Common/DbProviderFactoryObject.cs b/src/Spring/Spring.Data/Data/Common/DbProviderFactoryObject.cs new file mode 100644 index 00000000..848d0563 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/DbProviderFactoryObject.cs @@ -0,0 +1,153 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Objects.Factory; +using Spring.Util; + +namespace Spring.Data.Common +{ + /// + /// A implementation that + /// creates instances of the class. + /// + /// Typically used as a convenience for retrieving shared + /// in a Spring XML configuration file as compared to + /// using explict factory method support. + /// + public class DbProviderFactoryObject : IFactoryObject, IInitializingObject + { + private string provider; + private string connectionString; + private IDbProvider dbProvider; + + /// + /// Creates a new instance of the class. + /// + public DbProviderFactoryObject() + { + } + + /// + /// Return an instance of and IDbProvider as configured by this factory + /// managed by this factory. + /// + /// + /// The same (singleton) instance of the IDbProvider managed by + /// this factory. + /// + /// + /// + /// If this method is being called in the context of an enclosing IoC container and + /// returns , the IoC container will consider this factory + /// object as not being fully initialized and throw a corresponding (and most + /// probably fatal) exception. + /// + /// + public object GetObject() + { + lock (this) + { + if (dbProvider == null) + { + ValidateProperties(); + dbProvider = DbProviderFactory.GetDbProvider(Provider); + if (connectionString != null) + { + dbProvider.ConnectionString = this.connectionString; + } + } + + return dbProvider; + } + } + + /// + /// Return the type of + /// + public Type ObjectType + { + get { return typeof (IDbProvider); } + } + + /// + /// Returns true, as the the object managed by this factory is a singleton. + /// + /// + public bool IsSingleton + { + get { return true; } + } + + /// + /// Validates that the provider name is specified. + /// + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + /// In the event of not setting the ProviderName. + /// + public void AfterPropertiesSet() + { + ValidateProperties(); + } + + /// + /// Gets or sets the name of the database provider. + /// + /// The name of the database provider. + public string Provider + { + get { return provider; } + set + { + AssertUtils.ArgumentHasText(value, "The 'ProviderName' property must have a value."); + provider = value.Trim(); + } + } + + /// + /// Gets or sets the connection string. + /// + /// The connection string. + public string ConnectionString + { + get { return connectionString; } + set + { + AssertUtils.ArgumentHasText(value, "The 'ConnectionString' property must have a value."); + connectionString = value.Trim(); + } + } + + /// + /// Validates the properties. + /// + private void ValidateProperties() + { + if (StringUtils.IsNullOrEmpty(Provider)) + { + throw new ArgumentException("The 'DbProviderName' property has not been set."); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Common/DelegatingDbProvider.cs b/src/Spring/Spring.Data/Data/Common/DelegatingDbProvider.cs new file mode 100644 index 00000000..133b1c1f --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/DelegatingDbProvider.cs @@ -0,0 +1,228 @@ +#region Licence + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Data; +using Spring.Objects.Factory; +using Spring.Util; + +namespace Spring.Data.Common +{ + /// + /// IDbProvider implementation that delegates all calls to a given target + /// IDbProvider + /// + /// Mark Pollack + /// $Id: DelegatingDbProvider.cs,v 1.1 2008/01/29 22:19:20 markpollack Exp $ + public class DelegatingDbProvider : IDbProvider, IInitializingObject + { + private IDbProvider targetDbProvider; + + + /// + /// Initializes a new instance of the class. + /// + public DelegatingDbProvider() + { + } + + + /// + /// Initializes a new instance of the class. + /// + /// The target db provider. + public DelegatingDbProvider(IDbProvider targetDbProvider) + { + this.targetDbProvider = targetDbProvider; + } + + + /// + /// Gets or sets the target IDbProvider that this IDbProvider should delegate to + /// + /// The target db provider. + public IDbProvider TargetDbProvider + { + get { return targetDbProvider; } + set + { + AssertUtils.ArgumentNotNull(value,"TargetDbProvider"); + targetDbProvider = value; + } + } + + #region IDbProvider Members + + /// + /// Returns a new command object for executing SQL statments/Stored Procedures + /// against the database. + /// + /// An new + public virtual IDbCommand CreateCommand() + { + return TargetDbProvider.CreateCommand(); + } + + /// + /// Returns a new instance of the providers CommandBuilder class. + /// + /// In .NET 1.1 there was no common base class or interface + /// for command builders, hence the return signature is object to + /// be portable (but more loosely typed) across .NET 1.1/2.0 + /// A new Command Builder + public virtual object CreateCommandBuilder() + { + return TargetDbProvider.CreateCommandBuilder(); + } + + /// + /// Returns a new connection object to communicate with the database. + /// + /// A new + public virtual IDbConnection CreateConnection() + { + return TargetDbProvider.CreateConnection(); + } + + /// + /// Returns a new adapter objects for use with offline DataSets. + /// + /// A new + public virtual IDbDataAdapter CreateDataAdapter() + { + return TargetDbProvider.CreateDataAdapter(); + } + + /// + /// Returns a new parameter object for binding values to parameter + /// placeholders in SQL statements or Stored Procedure variables. + /// + /// A new + public virtual IDbDataParameter CreateParameter() + { + return TargetDbProvider.CreateParameter(); + } + + /// + /// Creates the name of the parameter in the format appropriate to use inside IDbCommand.CommandText. + /// + /// In most cases this adds the parameter prefix to the name passed into this method. + /// The unformatted name of the parameter. + /// The parameter name formatted foran IDbCommand.CommandText. + public virtual string CreateParameterName(string name) + { + return TargetDbProvider.CreateParameterName(name); + } + + /// + /// Creates the name ofthe parameter in the format appropriate for an IDataParameter, i.e. to be + /// part of a IDataParameterCollection. + /// + /// The unformatted name of the parameter. + /// The parameter name formatted for an IDataParameter + public virtual string CreateParameterNameForCollection(string name) + { + return TargetDbProvider.CreateParameterNameForCollection(name); + } + + /// + /// Return metadata information about the database provider + /// + public virtual IDbMetadata DbMetadata + { + get { return TargetDbProvider.DbMetadata; } + } + + /// + /// Connection string used to create connections. + /// + public virtual string ConnectionString + { + get { return TargetDbProvider.ConnectionString; } + set { TargetDbProvider.ConnectionString = value; } + } + + /// + /// Extracts the provider specific error code as a string. + /// + /// The data access exception. + /// The provider specific error code + public virtual string ExtractError(Exception e) + { + return TargetDbProvider.ExtractError(e); + } + + /// + /// Determines whether the provided exception is in fact related + /// to database access. This can be provider dependent in .NET 1.1 since + /// there isn't a common base class for ADO.NET exceptions. + /// + /// The exception thrown when performing data access + /// operations. + /// + /// true if is a valid data access exception for the specified + /// exception; otherwise, false. + /// + public virtual bool IsDataAccessException(Exception e) + { + return TargetDbProvider.IsDataAccessException(e); + } + + #endregion + + #region IInitializingObject Members + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public virtual void AfterPropertiesSet() + { + if (TargetDbProvider == null) + { + throw new ArgumentNullException("Property 'TargetDbProvider' is required."); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Common/ErrorCodes.cs b/src/Spring/Spring.Data/Data/Common/ErrorCodes.cs new file mode 100644 index 00000000..9552801a --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/ErrorCodes.cs @@ -0,0 +1,157 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; + +#endregion + +namespace Spring.Data.Common +{ + /// + /// Holds ADO.NET error codes for a particular provider. + /// + /// + /// Used by ErrorCodeExceptionTranslator. The embedded resource + /// "dbProviders.xml" in Spring.Data.Common contains default + /// ErrorCodes instances for various providers. + /// + /// Mark Pollack (.NET) + /// $Id: ErrorCodes.cs,v 1.1 2007/08/01 18:53:06 markpollack Exp $ + public class ErrorCodes + { + #region Fields + private String[] databaseProductNames; + + private String[] badSqlGrammarCodes = new String[0]; + + private String[] invalidResultSetAccessCodes = new String[0]; + + private String[] dataAccessResourceFailureCodes = new String[0]; + + private String[] permissionDeniedCodes = new String[0]; + + private String[] dataIntegrityViolationCodes = new String[0]; + + private String[] cannotAcquireLockCodes = new String[0]; + + private String[] deadlockLoserCodes = new String[0]; + + private String[] cannotSerializeTransactionCodes = new String[0]; + + // CustomErrorCodesTranslation[] customTranslations; + #endregion + + #region Constants + + /// + /// The shared log instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (ErrorCodes)); + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public ErrorCodes() + { + + } + + #endregion + + #region Properties + public string DatabaseProductName + { + get + { + return (databaseProductNames != null && databaseProductNames.Length > 0 ? + databaseProductNames[0] : null); + } + } + + public string[] DatabaseProductNames + { + get { return databaseProductNames; } + set { databaseProductNames = value; } + } + + public string[] BadSqlGrammarCodes + { + get { return badSqlGrammarCodes; } + set { badSqlGrammarCodes = value; } + } + + public string[] InvalidResultSetAccessCodes + { + get { return invalidResultSetAccessCodes; } + set { invalidResultSetAccessCodes = value; } + } + + public string[] DataAccessResourceFailureCodes + { + get { return dataAccessResourceFailureCodes; } + set { dataAccessResourceFailureCodes = value; } + } + + public string[] PermissionDeniedCodes + { + get { return permissionDeniedCodes; } + set { permissionDeniedCodes = value; } + } + + public string[] DataIntegrityViolationCodes + { + get { return dataIntegrityViolationCodes; } + set { dataIntegrityViolationCodes = value; } + } + + public string[] CannotAcquireLockCodes + { + get { return cannotAcquireLockCodes; } + set { cannotAcquireLockCodes = value; } + } + + public string[] DeadlockLoserCodes + { + get { return deadlockLoserCodes; } + set { deadlockLoserCodes = value; } + } + + public string[] CannotSerializeTransactionCodes + { + get { return cannotSerializeTransactionCodes; } + set { cannotSerializeTransactionCodes = value; } + } + + #endregion + + #region Methods + + #endregion + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Common/IDbMetadata.cs b/src/Spring/Spring.Data/Data/Common/IDbMetadata.cs new file mode 100644 index 00000000..958a6446 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/IDbMetadata.cs @@ -0,0 +1,172 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Data.Common +{ + /// + /// Provides minimal database metadata information to support the + /// functionality in Spring.NET ADO.NET Framework. + /// + /// + /// Mark Pollack (.NET) + /// $Id: IDbMetadata.cs,v 1.8 2007/11/21 18:01:45 markpollack Exp $ + public interface IDbMetadata + { + /// + /// Gets a descriptive name of the product. + /// + /// Example: Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0 + /// The name of the product. + string ProductName { get; } + + /// + /// Gets the type of the connection. The fully qualified type name is given since some providers, + /// notably for SqlServerCe, do not use the same namespace for all data access types. + /// + /// Example: System.Data.SqlClient.SqlCommand, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the connection. + Type ConnectionType { get; } + + /// + /// Gets the type of the command. + /// + /// Example: System.Data.SqlClient.SqlCommand, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the command. + Type CommandType { get; } + + /// + /// Gets the type of the parameter. + /// + /// Example: System.Data.SqlClient.SqlParameter, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the parameter. + Type ParameterType { get; } + + /// + /// Gets the type of the data adapter. + /// + /// Example: System.Data.SqlClient.SqlDataAdapter, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the data adapter. + Type DataAdapterType { get; } + + /// + /// Gets the type of the command builder. + /// + /// Example: System.Data.SqlClient.SqlCommandBuilder, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the command builder. + Type CommandBuilderType { get; } + + /// + /// Gets the type of the exception. + /// + /// Example: System.Data.SqlClient.SqlException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the exception. + Type ExceptionType { get; } + + /// + /// Gets the error code exception expression. + /// + /// Example Errors[0].Number.ToString() for Sql Server + /// The error code exception expression. + string ErrorCodeExceptionExpression { get; } + + /// + /// Gets the command builder derive parameters method. + /// + /// If the value 'not supported' is specified then this method will throw an ArgumentException + /// Example: DeriveParameters + /// The command builder derive parameters method. + MethodInfo CommandBuilderDeriveParametersMethod { get; } + + /// + /// Provide the prefix used to indentify named parameters in SQL text. + /// + /// @ for Sql Server + string ParameterNamePrefix { get; } + + /// + /// Does the driver require the use of the parameter prefix when + /// specifying the name of the parameter in the Command's + /// Parameter collection. + /// + /// If true, then commamd.Parameters["@parameterName"] is + /// used, otherwise, command.Parameters["parameterName"]. + /// + bool UseParameterNamePrefixInParameterCollection { get; } + + + /// + /// Gets a value indicating whether the Provider requires + /// the use of a named prefix in the SQL string. + /// + /// + /// The OLE DB/ODBC .NET Provider does not support named parameters for + /// passing parameters to an SQL Statement or a stored procedure called + /// by an IDbCommand when CommandType is set to Text. + /// + /// + /// true if use parameter prefix in SQL; otherwise, false. + /// + bool UseParameterPrefixInSql { get; } + + /// + /// For providers that allow you to choose between binding parameters + /// to a command by name (true) or by position (false). + /// + bool BindByName { get; + //TODO will go away when make all of this fully configuration driven. + set; + } + + /// + /// Gets the type of the parameter db. + /// + /// Example: System.Data.SqlDbType, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + /// The type of the parameter db. + Type ParameterDbType { get; } + + /// + /// Gets the parameter db type property. + /// + /// Example: SqlDbType for SqlServer + /// The parameter db type property. + PropertyInfo ParameterDbTypeProperty { get; } + + /// + /// Gets the parameter is nullable property. + /// + /// Example: IsNullable for Sql Server + /// The parameter is nullable property. + PropertyInfo ParameterIsNullableProperty { get; } + + /// + /// Gets or sets the error codes. + /// + /// The collection of error codes to map error code integer values to Spring's DAO exception hierarchy + /// The error codes. + ErrorCodes ErrorCodes { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Common/IDbParameter.cs b/src/Spring/Spring.Data/Data/Common/IDbParameter.cs new file mode 100644 index 00000000..2ead7d42 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/IDbParameter.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; + +#endregion + +namespace Spring.Data.Common +{ + /// + /// A parameter interface used by + /// + /// Mark Pollack (.NET) + /// $Id: IDbParameter.cs,v 1.6 2006/12/02 07:35:23 markpollack Exp $ + public interface IDbParameter + { + + IDbParameter Type(Enum dbType); + + IDbParameter Type(Enum dbType, int size); + + Enum GetDbType(); + + IDbParameter Direction(ParameterDirection direction); + + ParameterDirection GetDirection(); + + IDbParameter IsNullable(bool isNullable); + + bool GetIsNullable(); + + IDbParameter Name(string name); + + string GetName(); + + IDbParameter SourceColumn(string sourceColumn); + + string GetSourceColumn(); + + IDbParameter SourceVersion(DataRowVersion sourceVersion); + + DataRowVersion GetSourceVersion(); + + + IDbParameter Value(object val); + + object GetValue(); + + IDbParameter Precision(byte precision); + + byte GetPrecision(); + + + IDbParameter Scale(byte scale); + + byte GetScale(); + + + IDbParameter Size(int size); + + int GetSize(); + + } +} diff --git a/src/Spring/Spring.Data/Data/Common/IDbParameters.cs b/src/Spring/Spring.Data/Data/Common/IDbParameters.cs new file mode 100644 index 00000000..c9b7b636 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/IDbParameters.cs @@ -0,0 +1,130 @@ +using System; +using System.Data; + +namespace Spring.Data.Common +{ + public interface IDbParameters + { + //TODO implement IDataParameterCollection....? + + //TODO should add methods return IDbDataParameter? + +#if !MONO // EE: mono/win32 doesn't accept comments on indexer?!? + /// + /// Gets the underlying standard ADO.NET for the specified parameter name. + /// +#endif + IDataParameter this[string parameterName] { get; set; } + +#if !MONO // EE: mono/win32 doesn't accept comments on indexer?!? + /// + /// Gets the underlying standard ADO.NET for the specified index. + /// +#endif + IDataParameter this[int index] { get; set; } + + int Count + { + get; + } + + /// + /// Determines whether this collection contains the specified parameter name. + /// + /// Name of the parameter. + /// + /// true if contains the specified parameter name; otherwise, false. + /// + bool Contains(string parameterName); + + /// + /// Returns the underlying standard ADO.NET parameters collection. + /// + IDataParameterCollection DataParameterCollection { get; } + + /// + /// Add an instance of . + /// + /// + void AddParameter(IDataParameter dbParameter); + + /// + /// Add a parameter specifying the all the individual properties of a + /// IDbDataParameter. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// The newly created parameter + IDbDataParameter AddParameter(string name, Enum parameterType, int size, ParameterDirection direction, bool isNullable, + byte precision, byte scale, string sourceColumn, DataRowVersion sourceVersion, object parameterValue); + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// The newly created parameter + IDbDataParameter AddParameter(string name, Enum parameterType, ParameterDirection direction, bool isNullable, byte precision, + byte scale, string sourceColumn, DataRowVersion sourceVersion, object parameterValue); + + + /// + /// Adds a value to the parameter collection + /// + /// This method can not be used with stored procedures for + /// providers that require named parameters. However, it is of + /// great convenience for other cases. + /// The value of the parameter. + /// Index of added parameter. + int Add(object parameterValue); + + /// + /// Adds a range of vlaues to the parameter collection. + /// + /// This method can not be used with stored procedures for + /// providers that require named parameters. However, it is of + /// great convenience for other cases. + /// + void AddRange(Array values); + + IDbDataParameter AddWithValue(string name, object parameterValue); + + IDbDataParameter Add(string name, Enum parameterType); + + IDbDataParameter Add(string name, Enum parameterType, int size); + + IDbDataParameter Add(string name, Enum parameterType, int size, string sourceColumn); + + IDbDataParameter AddOut(string name, Enum parameterType); + + IDbDataParameter AddOut(string name, Enum parameterType, int size); + + IDbDataParameter AddInOut(string name, Enum parameterType); + + IDbDataParameter AddInOut(string name, Enum parameterType, int size); + + IDbDataParameter AddReturn(string name, Enum parameterType); + + IDbDataParameter AddReturn(string name, Enum parameterType, int size); + + object GetValue(string name); + + void SetValue(string name, object parameterValue); + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Common/IDbParametersBuilder.cs b/src/Spring/Spring.Data/Data/Common/IDbParametersBuilder.cs new file mode 100644 index 00000000..45a19f3f --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/IDbParametersBuilder.cs @@ -0,0 +1,34 @@ + +#region Imports + + + +#endregion + +namespace Spring.Data.Common +{ + /// + /// A builder to create a collection of ADO.NET parameters. + /// + /// The chaining of IDbParameter methods does the + /// building of the parameter details while the builder class + /// itself keeps track of the collection. + /// + /// Mark Pollack (.NET) + /// $Id: IDbParametersBuilder.cs,v 1.1 2006/12/02 07:38:15 markpollack Exp $ + public interface IDbParametersBuilder + { + /// + /// Creates a IDbParameter object and adds it to an internal collection for + /// later retrieval via the GetParameters method. + /// + /// + IDbParameter Create(); + + /// + /// Gets all the parameters created and configured via the Create method. + /// + /// An instance of IDbParameters + IDbParameters GetParameters(); + } +} diff --git a/src/Spring/Spring.Data/Data/Common/IDbProvider.cs b/src/Spring/Spring.Data/Data/Common/IDbProvider.cs new file mode 100644 index 00000000..b5ad14ed --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/IDbProvider.cs @@ -0,0 +1,126 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Data; + +namespace Spring.Data.Common +{ + /// + /// Factory interface to create provider specific ADO.NET objects. + /// + public interface IDbProvider + { + + /// + /// Returns a new command object for executing SQL statments/Stored Procedures + /// against the database. + /// + /// An new + IDbCommand CreateCommand(); + + /// + /// Returns a new instance of the providers CommandBuilder class. + /// + /// In .NET 1.1 there was no common base class or interface + /// for command builders, hence the return signature is object to + /// be portable (but more loosely typed) across .NET 1.1/2.0 + /// A new Command Builder + object CreateCommandBuilder(); + + /// + /// Returns a new connection object to communicate with the database. + /// + /// A new + IDbConnection CreateConnection(); + + + /// + /// Returns a new adapter objects for use with offline DataSets. + /// + /// A new + IDbDataAdapter CreateDataAdapter(); + + /// + /// Returns a new parameter object for binding values to parameter + /// placeholders in SQL statements or Stored Procedure variables. + /// + /// A new + IDbDataParameter CreateParameter(); + + + /// + /// Creates the name of the parameter in the format appropriate to use inside IDbCommand.CommandText. + /// + /// In most cases this adds the parameter prefix to the name passed into this method. + /// The unformatted name of the parameter. + /// The parameter name formatted foran IDbCommand.CommandText. + string CreateParameterName(string name); + + /// + /// Creates the name ofthe parameter in the format appropriate for an IDataParameter, i.e. to be + /// part of a IDataParameterCollection. + /// + /// The unformatted name of the parameter. + /// The parameter name formatted for an IDataParameter + string CreateParameterNameForCollection(string name); + + /// + /// Return metadata information about the database provider + /// + IDbMetadata DbMetadata + { + get; + } + + + + /// + /// Connection string used to create connections. + /// + string ConnectionString + { + set; + get; + } + + + /// + /// Extracts the provider specific error code as a string. + /// + /// The data access exception. + /// The provider specific error code + string ExtractError(Exception e); + + /// + /// Determines whether the provided exception is in fact related + /// to database access. This can be provider dependent in .NET 1.1 since + /// there isn't a common base class for ADO.NET exceptions. + /// + /// The exception thrown when performing data access + /// operations. + /// + /// true if is a valid data access exception for the specified + /// exception; otherwise, false. + /// + bool IsDataAccessException(Exception e); + + } +} diff --git a/src/Spring/Spring.Data/Data/Common/MultiDelegatingDbProvider.cs b/src/Spring/Spring.Data/Data/Common/MultiDelegatingDbProvider.cs new file mode 100644 index 00000000..2d2d0a08 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/MultiDelegatingDbProvider.cs @@ -0,0 +1,267 @@ +#region Licence + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Data; +using Spring.Collections; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Objects.Factory; +using Spring.Threading; + + +namespace Spring.Data +{ + /// + /// A wrapper implementation for IDbProvider such that multiple DbProvider instances can be + /// selected at runtime, say based on web request criteria. + /// + /// + /// The name of which DbProvider to use, as provided to the IDictionary property TargetDbProviders + /// is "dbProviderName". Once the target dbprovider name is known, set the name via a call to + /// LogicalThreadContext.SetData("dbProviderName", "database1ProviderName"). The value + /// "database1ProviderName" must match a key in the provided TargetDbProviders dictionary. + /// + /// Mark Pollack + /// $Id: MultiDelegatingDbProvider.cs,v 1.5 2008/05/29 17:26:35 markpollack Exp $ + public class MultiDelegatingDbProvider : IDbProvider, IInitializingObject + { + private IDictionary targetDbProviders = new SynchronizedHashtable(); + + #region Constructors + /// + /// Initializes a new instance of the class. + /// + public MultiDelegatingDbProvider() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The target db providers. + public MultiDelegatingDbProvider(IDictionary targetDbProviders) + { + this.targetDbProviders = targetDbProviders; + } + + #endregion + + /// + /// Sets the target db providers. + /// + /// The target db providers. + public IDictionary TargetDbProviders + { + set { targetDbProviders = value; } + } + + /// + /// Ensures that the there are values in the TargetDbProviders dictionary and that the + /// key is of the type string and value is of the type IDbProvider. + /// + /// If the above conditions are not met. + public void AfterPropertiesSet() + { + if (targetDbProviders.Count == 0) + { + throw new ArgumentException("Target DbProvider collection required."); + } + foreach (DictionaryEntry entry in targetDbProviders) + { + if (! entry.Key.GetType().Equals(typeof(string))) + { + throw new ArgumentException("Key identifying target IDbProvider in TargetDbProviders dictionary property is required to be of type string. Key = " + entry.Key); + } + IDbProvider targetProvider = entry.Value as IDbProvider; + if (targetProvider == null) + { + throw new ArgumentException("Value in TargetDbProviders dictionary is not of type IDbProvider."); + } + } + } + + #region IDbProvider methods + + + /// + /// Returns a new command object for executing SQL statments/Stored Procedures + /// against the database. + /// + /// An new + public IDbCommand CreateCommand() + { + return GetTargetProvider().CreateCommand(); + } + + /// + /// Returns a new connection object to communicate with the database. + /// + /// A new + public IDbConnection CreateConnection() + { + return GetTargetProvider().CreateConnection(); + } + + /// + /// Returns a new parameter object for binding values to parameter + /// placeholders in SQL statements or Stored Procedure variables. + /// + /// A new + public IDbDataParameter CreateParameter() + { + return GetTargetProvider().CreateParameter(); + } + + /// + /// Returns a new adapter objects for use with offline DataSets. + /// + /// A new + public IDbDataAdapter CreateDataAdapter() + { + return GetTargetProvider().CreateDataAdapter(); + } + + /// + /// Returns a new instance of the providers CommandBuilder class. + /// + /// A new Command Builder + /// In .NET 1.1 there was no common base class or interface + /// for command builders, hence the return signature is object to + /// be portable (but more loosely typed) across .NET 1.1/2.0 + public object CreateCommandBuilder() + { + return GetTargetProvider().CreateDataAdapter(); + } + + /// + /// Creates the name of the parameter in the format appropriate to use inside IDbCommand.CommandText. + /// + /// The unformatted name of the parameter. + /// + /// The parameter name formatted foran IDbCommand.CommandText. + /// + /// In most cases this adds the parameter prefix to the name passed into this method. + public string CreateParameterName(string name) + { + return GetTargetProvider().CreateParameterName(name); + } + + + /// + /// Creates the name ofthe parameter in the format appropriate for an IDataParameter, i.e. to be + /// part of a IDataParameterCollection. + /// + /// The unformatted name of the parameter. + /// + /// The parameter name formatted for an IDataParameter + /// + public string CreateParameterNameForCollection(string name) + { + return GetTargetProvider().CreateParameterNameForCollection(name); + } + + /// + /// Return metadata information about the database provider + /// + /// + public IDbMetadata DbMetadata + { + get { return GetTargetProvider().DbMetadata; } + } + + /// + /// Connection string used to create connections. + /// + /// + public string ConnectionString + { + get { return GetTargetProvider().ConnectionString; } + set { GetTargetProvider().ConnectionString = value; } + } + + /// + /// Extracts the provider specific error code as a string. + /// + /// The data access exception. + /// The provider specific error code + public string ExtractError(Exception e) + { + return GetTargetProvider().ExtractError(e); + } + + /// + /// Determines whether the provided exception is in fact related + /// to database access. This can be provider dependent in .NET 1.1 since + /// there isn't a common base class for ADO.NET exceptions. + /// + /// The exception thrown when performing data access + /// operations. + /// + /// true if is a valid data access exception for the specified + /// exception; otherwise, false. + /// + public bool IsDataAccessException(Exception e) + { +#if NET_2_0 + if (e is System.Data.Common.DbException) + { + return true; + } + else + { + return false; + } +#else + return IsDataAccessExceptionBCL11(e); +#endif + } + + /// + /// Determines whether is data access exception in .NET 1.1 for the specified exception. + /// + /// The candidate exception. + /// + /// true if is data access exception in .NET 1.1 for the specified exception; otherwise, false. + /// + public bool IsDataAccessExceptionBCL11(Exception e) + { + return false; + } + #endregion + + /// + /// Gets the target provider based on the thread local name "dbProviderName" + /// + /// The corresonding IDbProvider. + protected virtual IDbProvider GetTargetProvider() + { + string dbProviderName = (string)LogicalThreadContext.GetData("dbProviderName"); + if (targetDbProviders.Contains(dbProviderName)) + { + return (IDbProvider)targetDbProviders[dbProviderName]; + } + throw new InvalidDataAccessApiUsageException("'" + dbProviderName + "'" + + "was not under the thread local key 'dbProviderName'"); + } + } +} diff --git a/src/Spring/Spring.Data/Data/Common/UserCredentialsDbProvider.cs b/src/Spring/Spring.Data/Data/Common/UserCredentialsDbProvider.cs new file mode 100644 index 00000000..8ec7247e --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/UserCredentialsDbProvider.cs @@ -0,0 +1,137 @@ +#region Licence + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; +using Spring.Threading; +using Spring.Util; + +namespace Spring.Data.Common +{ + /// + /// An adapter for a target IDbProvider, applying the specified user credentials + /// to the connection string for every GetConnection call. + /// + public class UserCredentialsDbProvider : DelegatingDbProvider + { + private string username; + + private string password; + + private string separator = ";"; + + private const string USERNAME = "UserCredentialsDbProvider.UserName"; + + private const string PASSWORD = "UserCredentialsDbProvider.Password"; + + /// + /// Sets the username string that will be appended to the connection string. + /// + /// The username. + public string Username + { + set { username = value; } + } + + /// + /// Sets the password string that will be appended to the connection string. + /// + /// The password. + public string Password + { + set { password = value; } + } + + + /// + /// Sets the separator used to separate elements of the connection string. Default is ';'. + /// + /// + /// The separator used to separate elements in the connection string. + public string Separator + { + set { separator = value; } + } + + /// + /// Sets the user credentials for current thread. The given username and password + /// strings will be added to the connection string for all subsequent GetConnection + /// requests. This will override any statically specified user credentials, that is, + /// set by the properties Username nad Password. + /// + /// The username part of the connection string. + /// The password part of the connection string. + public void SetCredentialsForCurrentThread(string user, string pass) + { + LogicalThreadContext.SetData(USERNAME, user); + LogicalThreadContext.SetData(PASSWORD, pass); + } + + /// + /// Removes the user credentials from current thread. Use statically specified + /// credentials afterwards. + /// + public void RemoveCredentialsFromCurrentThread() + { + LogicalThreadContext.FreeNamedDataSlot(USERNAME); + LogicalThreadContext.FreeNamedDataSlot(PASSWORD); + } + + + /// + /// Returns a new connection object to communicate with the database. + /// Determine if there are currently thread-bound credentials, using them if + /// available, falling back to the statically specified username and password + /// (i.e. values of the properties 'Username' and 'Password') otherwise. + /// The username and password will be concatenated on the connection string + /// using string in the Separator property + /// + /// A new + public override IDbConnection CreateConnection() + { + string user = LogicalThreadContext.GetData(USERNAME) as string; + string pass = LogicalThreadContext.GetData(PASSWORD) as string; + if (user != null && pass != null) + { + return DoCreateConnection(user, pass); + } + else + { + return DoCreateConnection(username, password); + } + } + + protected virtual IDbConnection DoCreateConnection(string user, string pass) + { + AssertUtils.ArgumentNotNull(TargetDbProvider,"TargetDbProvider"); + if (StringUtils.HasLength(user)) + { + IDbConnection conn = TargetDbProvider.CreateConnection(); + string s= ConnectionString + separator + user + separator + pass; + conn.ConnectionString = s; + return conn; + } + else + { + return TargetDbProvider.CreateConnection(); + } + } + } + +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Common/dbproviders.xml b/src/Spring/Spring.Data/Data/Common/dbproviders.xml new file mode 100644 index 00000000..274adadc --- /dev/null +++ b/src/Spring/Spring.Data/Data/Common/dbproviders.xml @@ -0,0 +1,1166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 156,170,207,208 + + + 229 + + + 544,2627,8114,8115 + + + 1205 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 156,170,207,208 + + + 229 + + + 544,2627,8114,8115 + + + 1205 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 156,170,207,208 + + + 229 + + + 2627,8114,8115 + + + 1205 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 156,170,207,208 + + + 229 + + + 2627,8114,8115 + + + 1205 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 900,903,904,917,936,942,17006 + + + 17003 + + + 17002,17447 + + + 1,1400,1722,2291,2292 + + + 54 + + + 8177 + + + 60 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 900,903,904,917,936,942,17006 + + + 17003 + + + 17002,17447 + + + 1,1400,1722,2291,2292 + + + 54 + + + 8177 + + + 60 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1054,1064,1146 + + + 1 + + + 630,839,840,893,1062,1169,1215,1216,1217,1451,1452,1557 + + + 1205 + + + 1213 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1054,1064,1146 + + + 1 + + + 630,839,840,893,1062,1169,1215,1216,1217,1451,1452,1557 + + + 1205 + + + 1213 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1054,1064,1146 + + + 1 + + + 630,839,840,893,1062,1169,1215,1216,1217,1451,1452,1557 + + + 1205 + + + 1213 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1054,1064,1146 + + + 1 + + + 630,839,840,893,1062,1169,1215,1216,1217,1451,1452,1557 + + + 1205 + + + 1213 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1054,1064,1146 + + + 1 + + + 630,839,840,893,1062,1169,1215,1216,1217,1451,1452,1557 + + + 1205 + + + 1213 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1054,1064,1146 + + + 1 + + + 630,839,840,893,1062,1169,1215,1216,1217,1451,1452,1557 + + + 1205 + + + 1213 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 03000,42000,42601,42602,42622,42804,42P01 + + + 53000,53100,53200,53300 + + + 23000,23502,23503,23505,23514 + + + 55P03 + + + 40001 + + + 40P01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 03000,42000,42601,42602,42622,42804,42P01 + + + 53000,53100,53200,53300 + + + 23000,23502,23503,23505,23514 + + + 55P03 + + + 40001 + + + 40P01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -007,-029,-097,-104,-109,-115,-128,-199,-204,-206,-301,-408,-441,-491 + + + -904,-971 + + + -407,-530,-531,-532,-543,-544,-545,-603,-667,-803 + + + -911,-913 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -007,-029,-097,-104,-109,-115,-128,-199,-204,-206,-301,-408,-441,-491 + + + -904,-971 + + + -407,-530,-531,-532,-543,-544,-545,-603,-667,-803 + + + -911,-913 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -007,-029,-097,-104,-109,-115,-128,-199,-204,-206,-301,-408,-441,-491 + + + -904,-971 + + + -407,-530,-531,-532,-543,-544,-545,-603,-667,-803 + + + -911,-913 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -007,-029,-097,-104,-109,-115,-128,-199,-204,-206,-301,-408,-441,-491 + + + -904,-971 + + + -407,-530,-531,-532,-543,-544,-545,-603,-667,-803 + + + -911,-913 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -007,-029,-097,-104,-109,-115,-128,-199,-204,-206,-301,-408,-441,-491 + + + -904,-971 + + + -407,-530,-531,-532,-543,-544,-545,-603,-667,-803 + + + -911,-913 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + 15 + + + 5,6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + 15 + + + 5,6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + 15 + + + 5,6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 101,102,103,104,105,106,107,108,109,110,111,112,113,116,120,121,123,207,208,213,257,512 + + + 423,511,515,530,547,2601,2615,2714 + + + 1205 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 101,102,103,104,105,106,107,108,109,110,111,112,113,116,120,121,123,207,208,213,257,512 + + + 423,511,515,530,547,2601,2615,2714 + + + 1205 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Data/Data/Config/DatabaseNamespaceParser.cs b/src/Spring/Spring.Data/Data/Config/DatabaseNamespaceParser.cs new file mode 100644 index 00000000..2ee04c63 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Config/DatabaseNamespaceParser.cs @@ -0,0 +1,170 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Globalization; +using System.Xml; + +using Spring.Data.Common; +using Spring.Core.TypeResolution; +using Spring.Objects; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +#endregion + +namespace Spring.Data.Config +{ + /// + /// Implementation of the custom configuration parser for database definitions. + /// + /// Mark Pollack + /// $Id: DatabaseNamespaceParser.cs,v 1.3 2007/08/08 00:34:33 bbaia Exp $ + [ + NamespaceParser( + Namespace = "http://www.springframework.net/database", + SchemaLocationAssemblyHint = typeof(DatabaseNamespaceParser), + SchemaLocation = "/Spring.Data.Config/spring-database-1.1.xsd") + ] + public class DatabaseNamespaceParser : ObjectsNamespaceParser + { + + private const string DatabaseTypePrefix = "database: "; + + static DatabaseNamespaceParser() + { + TypeRegistry.RegisterType( + DatabaseTypePrefix + DbProviderFactoryObjectConstants.DbProviderFactoryObjectElement, + typeof (DbProviderFactoryObject)); + } + + /// + /// Initializes a new instance of the class. + /// + public DatabaseNamespaceParser() + { + } + + + + + /// + /// Parse the specified element and register any resulting + /// IObjectDefinitions with the IObjectDefinitionRegistry that is + /// embedded in the supplied ParserContext. + /// + /// The element to be parsed into one or more IObjectDefinitions + /// The object encapsulating the current state of the parsing + /// process. + /// + /// The primary IObjectDefinition (can be null as explained above) + /// + /// + /// Implementations should return the primary IObjectDefinition + /// that results from the parse phase if they wish to used nested + /// inside (for example) a <property> tag. + /// Implementations may return null if they will not + /// be used in a nested scenario. + /// + /// + public override IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + string id = element.GetAttribute(ObjectDefinitionConstants.IdAttribute); + IConfigurableObjectDefinition databaseConfiguration = ParseDatabaseDefinition(element, id, parserContext); + if (!StringUtils.HasText(id)) + { + id = ObjectDefinitionReaderUtils.GenerateObjectName(databaseConfiguration, parserContext.Registry); + } + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + CultureInfo.InvariantCulture, + "Registering object definition with id '{0}'.", id)); + } + + #endregion + parserContext.Registry.RegisterObjectDefinition(id, databaseConfiguration); + + return null; + } + + private IConfigurableObjectDefinition ParseDatabaseDefinition(XmlElement element, string name, ParserContext parserContext) + { + switch (element.LocalName) + { + case DbProviderFactoryObjectConstants.DbProviderFactoryObjectElement: + return ParseDatabaseConfigurer(element, name, parserContext); + } + return null; + } + + private IConfigurableObjectDefinition ParseDatabaseConfigurer(XmlElement element, string name, ParserContext parserContext) + { + string typeName = GetTypeName(element); + string providerNameAttribute = element.GetAttribute(DbProviderFactoryObjectConstants.ProviderNameAttribute); + string connectionString = element.GetAttribute(DbProviderFactoryObjectConstants.ConnectionStringAttribute); + + MutablePropertyValues properties = new MutablePropertyValues(); + if (StringUtils.HasText(providerNameAttribute)) + { + properties.Add("Provider", providerNameAttribute); + } + if (StringUtils.HasText(connectionString)) + { + properties.Add("ConnectionString", connectionString); + } + + IConfigurableObjectDefinition cod = parserContext.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( + typeName, null, parserContext.ReaderContext.Reader.Domain); + cod.PropertyValues = properties; + + return cod; + } + + /// + /// Gets the name of the object type for the specified element. This has already been aliased + /// in the static constructor. + /// + /// The element. + /// The name of the object type. + private string GetTypeName(XmlElement element) + { + string typeName = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute); + if (StringUtils.IsNullOrEmpty(typeName)) + { + return DatabaseTypePrefix + element.LocalName; + } + return typeName; + } + + private class DbProviderFactoryObjectConstants + { + public const string DbProviderFactoryObjectElement = "provider"; + public const string ProviderNameAttribute = "provider"; + public const string ConnectionStringAttribute = "connectionString"; + } + } +} diff --git a/src/Spring/Spring.Data/Data/Config/spring-database-1.1.xsd b/src/Spring/Spring.Data/Data/Config/spring-database-1.1.xsd new file mode 100644 index 00000000..e68c0b27 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Config/spring-database-1.1.xsd @@ -0,0 +1,224 @@ + + + + + + + + Spring.NET Database Framework Config Schema Definition + + Author: Mark Pollack + + This file defines a configuration schema for the database framework + object definitions. Using elements from this schema instead of the + standard object definitions can greatly simplify remoting configuration. + + + + + + Defines a DbProvider instance + + + + + The id of the DbProvider instance to be referenced. + + + + + The name of the database provider. + + + + + The database connection string. + + + + + + + + + + + + + + + Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0 + + + + + Microsoft SQL Server Compact Edition, provider V9.0.242.0 + + + + + OleDb, provider V2.0.0.0 in framework .NET V2.0 + + + + + Oracle, Oracle provider V2.102.2.20 + + + + + Oracle, Oracle provider V2.102.2.20 + + + + + MySQL provider 5.1.4.0 + + + + + SQLite 1.0.47 provider + + + + + + Microsoft SQL Server, provider V1.0.5000.0 in framework .NET V1.1 + + + + + Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0 + + + + + Microsoft SQL Server Compact Edition, provider V9.0.242.0 + + + + + OleDb, provider V1.0.5000.0 in framework .NET V1.1 + + + + + OleDb, provider V2.0.0.0 in framework .NET V2.0 + + + + + Oracle, Microsoft provider V2.0.0.0 + + + + + Oracle, Oracle provider V2.102.2.20 + + + + + MySQL provider 1.0.7.3007 + + + + + MySQL provider 1.0.9.0 + + + + + MySQL provider 5.0.7.0 + + + + + MySQL provider 5.0.8.1 + + + + + MySQL provider 5.1.2.2 + + + + + MySQL provider 5.1.4.0 + + + + + Npgsql provider 1.0.0.0 + + + + + Npgsql provider beta-1 1.98.1.0 + + + + + IBM DB2 Data Provider 9.0.0 for .NET Framework 1.1 + + + + + IBM DB2 Data Provider 9.0.0 for .NET Framework 2.0 + + + + + IBM DB2 Data Provider 9.1.0 for .NET Framework 1.1 + + + + + IBM DB2 Data Provider 9.1.0 for .NET Framework 2.0 + + + + + IBM iSeries DB2 Data Provider 10.0.0.0 + + + + + SQLite 1.0.43 provider + + + + + SQLite 1.0.44 provider + + + + + Sybase ASE 12.5, 1.1.411 provider + + + + + Sybase ASE 15, 1.15.152 provider + + + + + Microsoft ODBC, provider V1.0.5000.0 in framework .NET V1.1 + + + + + Microsoft ODBC, provider V2.0.0.0 in framework .NET V2 + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Config/spring-database-1.1.xsx b/src/Spring/Spring.Data/Data/Config/spring-database-1.1.xsx new file mode 100644 index 00000000..ff71343b --- /dev/null +++ b/src/Spring/Spring.Data/Data/Config/spring-database-1.1.xsx @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Core/AdoAccessor.cs b/src/Spring/Spring.Data/Data/Core/AdoAccessor.cs new file mode 100644 index 00000000..87240431 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Core/AdoAccessor.cs @@ -0,0 +1,216 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Data; +using Spring.Data.Common; +using Spring.Data.Support; +using Spring.Objects.Factory; + +namespace Spring.Data.Core +{ + /// + /// Base class for AdoTemplate and other ADO.NET DAO helper classes defining + /// common properties like DbProvider. + /// + /// Mark Pollack (.NET) + /// Juergen Hoeller + public abstract class AdoAccessor : IInitializingObject + { + protected int commandTimeout; + + #region Properties + + /// + /// An instance of a DbProvider implementation. + /// + public abstract IDbProvider DbProvider + { + get; + set; + } + + + + /// + /// Gets or sets a value indicating whether to lazily initialize the + /// IAdoExceptionTranslator for this accessor, on first encounter of a + /// exception from the data provider. Default is "true"; can be switched to + /// "false" for initialization on startup. + /// + /// true if to lazy initialize the IAdoExceptionTranslator; + /// otherwise, false. + public abstract bool LazyInit + { + get; + set; + } + + + /// + /// Gets or sets the exception translator. If no custom translator is provided, a default + /// is used. + /// + /// The exception translator. + public abstract IAdoExceptionTranslator ExceptionTranslator + { + get; + set; + } + + + /// + /// Gets or set the System.Type to use to create an instance of IDataReaderWrapper + /// for the purpose of having defaults values to use in case of DBNull values read + /// from IDataReader. + /// + /// The type of the data reader wrapper. + public abstract Type DataReaderWrapperType + { + get; + set; + } + + /// + /// Gets or sets the command timeout for IDbCommands that this AdoTemplate executes. + /// + /// Default is 0, indicating to use the database provider's default. + /// Any timeout specified here will be overridden by the remaining + /// transaction timeout when executing within a transaction that has a + /// timeout specified at the transaction level. + /// + /// The command timeout. + public virtual int CommandTimeout + { + get { return commandTimeout; } + set { commandTimeout = value; } + } + + #endregion + + /// + /// Prepare the command setting the transaction timeout. + /// + /// + protected void ApplyCommandSettings(IDbCommand command) + { + ConnectionUtils.ApplyTransactionTimeout(command, DbProvider, CommandTimeout ); + } + + protected virtual string GetCommandText(object cmdTextProvider) + { + ICommandTextProvider commandTextProvider = cmdTextProvider as ICommandTextProvider; + if (commandTextProvider != null) + { + return commandTextProvider.CommandText; + } + else + { + return null; + } + } + + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public abstract void AfterPropertiesSet(); + + /// + /// Creates the data reader wrapper for use in AdoTemplate callback methods. + /// + /// The reader to wrap. + /// The data reader used in AdoTemplate callbacks + public abstract IDataReader CreateDataReaderWrapper(IDataReader readerToWrap); + + /// + /// Creates the a db parameters collection, adding to the collection a parameter created from + /// the method parameters. + /// + /// The name of the parameter + /// The type of the parameter. + /// The size of the parameter, for use in defining lengths of string values. Use + /// 0 if not applicable. + /// The parameter value. + /// A collection of db parameters with a single parameter in the collection based + /// on the method parameters + protected IDbParameters CreateDbParameters(string name, Enum dbType, int size, object parameterValue) + { + IDbParameters parameters = new DbParameters(DbProvider); + parameters.Add(name, dbType, size).Value = parameterValue; + return parameters; + } + + + #region Parameter Creation Helper Methods + + /// + /// Creates a new instance of + /// + /// a new instance of + public virtual IDbParameters CreateDbParameters() + { + return new DbParameters(DbProvider); + } + + /// + /// Derives the parameters of a stored procedure, not including the return parameter. + /// + /// Name of the procedure. + /// The stored procedure parameters. + public virtual IDataParameter[] DeriveParameters(string procedureName) + { + return DeriveParameters(procedureName, false); + } + + /// + /// Derives the parameters of a stored procedure including the return parameter + /// + /// Name of the procedure. + /// if set to true to include return parameter. + /// The stored procedure parameters + public abstract IDataParameter[] DeriveParameters(string procedureName, bool includeReturnParameter); + + #endregion + } +} diff --git a/src/Spring/Spring.Data/Data/Core/AdoDaoSupport.cs b/src/Spring/Spring.Data/Data/Core/AdoDaoSupport.cs new file mode 100644 index 00000000..2b8aae91 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Core/AdoDaoSupport.cs @@ -0,0 +1,142 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Data; +using Spring.Dao.Support; +using Spring.Data.Common; +using Spring.Data.Support; + +namespace Spring.Data.Core +{ + /// + /// Convenient super class for ADO.NET data access objects. + /// + /// + /// Requires a IDBProvider to be set, providing a + /// AdoTemplate based on it to subclasses. + /// This base class is mainly intended for AdoTemplate usage. + /// + public class AdoDaoSupport : DaoSupport + { + private AdoTemplate adoTemplate; + + /// + /// The DbProvider instance used by this DAO + /// + public IDbProvider DbProvider + { + set + { + adoTemplate = CreateAdoTemplate(value); + } + get + { + if (adoTemplate != null) + { + return adoTemplate.DbProvider; + } + else + { + return null; + } + } + + } + + /// + /// Set the AdoTemplate for this DAO explicity, as + /// an alternative to specifying a IDbProvider + /// + public AdoTemplate AdoTemplate + { + set + { + adoTemplate = value; + } + get + { + return adoTemplate; + } + + } + + protected override void CheckDaoConfig() + { + if (adoTemplate == null) + { + throw new ArgumentException("DbProvider or AdoTemplate is required"); + } + } + + protected IDbConnection Connection + { + get + { + return ConnectionUtils.GetConnection(DbProvider); + } + } + + protected IAdoExceptionTranslator ExceptionTranslator + { + get + { + return null; //Investigate AdoExceptionTranslator on AdoAccessor + } + } + + protected void DisposeConnection(IDbConnection conn, IDbProvider dbProvider) + { + ConnectionUtils.DisposeConnection(conn, dbProvider); + } + + + /// + /// Create a AdoTemplate for a given DbProvider + /// Only invoked if populating the DAO with a DbProvider reference. + /// + /// + /// Can be overriden in subclasses to provide AdoTemplate instances + /// with a different configuration, or a cusotm AdoTemplate subclass. + /// + /// The DbProvider to create a AdoTemplate for + protected virtual AdoTemplate CreateAdoTemplate(IDbProvider dbProvider) + { + return new AdoTemplate(dbProvider); + } + + /// + /// Convenience method to create a parameters builder. + /// + /// Virtual for sublcasses to override with custom + /// implementation. + /// A new DbParameterBuilder + protected virtual IDbParametersBuilder CreateDbParametersBuilder() + { + return new DbParametersBuilder(DbProvider); + } + + protected virtual IDbParameters CreateDbParameters() + { + return AdoTemplate.CreateDbParameters(); + } + + } +} diff --git a/src/Spring/Spring.Data/Data/Core/AdoPlatformTransactionManager.cs b/src/Spring/Spring.Data/Data/Core/AdoPlatformTransactionManager.cs new file mode 100644 index 00000000..612aafa9 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Core/AdoPlatformTransactionManager.cs @@ -0,0 +1,431 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using Common.Logging; +using Spring.Data.Common; +using Spring.Data.Support; +using Spring.Objects.Factory; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion +namespace Spring.Data.Core +{ + /// + /// ADO.NET based implementation of the + /// interface. + /// + /// Mark Pollack (.NET) + /// $Id: AdoPlatformTransactionManager.cs,v 1.7 2007/11/30 18:42:11 markpollack Exp $ + public class AdoPlatformTransactionManager : AbstractPlatformTransactionManager, IInitializingObject + { + + private IDbProvider dbProvider; + + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof (AdoPlatformTransactionManager)); + + #endregion + + public AdoPlatformTransactionManager() + { + NestedTransactionsAllowed = true; + } + + public AdoPlatformTransactionManager(IDbProvider dbProvider) : this() + { + DbProvider = dbProvider; + + } + + #region Propeties + + public IDbProvider DbProvider + { + get { return dbProvider; } + set { dbProvider = value; } + } + + #endregion + + /// + /// Return the current transaction object. + /// + /// The current transaction object. + /// + /// If transaction support is not available. + /// + /// + /// In the case of lookup or system errors. + /// + protected override object DoGetTransaction() + { + DbProviderTransactionObject txMgrStateObject = + new DbProviderTransactionObject(); + txMgrStateObject.SavepointAllowed = NestedTransactionsAllowed; + ConnectionHolder conHolder = + (ConnectionHolder) TransactionSynchronizationManager.GetResource(DbProvider); + txMgrStateObject.SetConnectionHolder(conHolder, false); + return txMgrStateObject; + } + + /// + /// Check if the given transaction object indicates an existing, + /// i.e. already begun, transaction. + /// + /// + /// Transaction object returned by + /// . + /// + /// True if there is an existing transaction. + /// + /// In the case of system errors. + /// + protected override bool IsExistingTransaction(object transaction) + { + DbProviderTransactionObject txMgrStateObject = + (DbProviderTransactionObject)transaction; + return (txMgrStateObject.ConnectionHolder != null + && + txMgrStateObject.ConnectionHolder.TransactionActive); + + } + + /// + /// Begin a new transaction with the given transaction definition. + /// + /// + /// Transaction object returned by + /// . + /// + /// + /// instance, describing + /// propagation behavior, isolation level, timeout etc. + /// + /// + /// Does not have to care about applying the propagation behavior, + /// as this has already been handled by this abstract manager. + /// + /// + /// In the case of creation or system errors. + /// + protected override void DoBegin(object transaction, ITransactionDefinition definition) + { + DbProviderTransactionObject txMgrStateObject = + (DbProviderTransactionObject)transaction; + IDbConnection con = null; + + if (dbProvider == null) + { + throw new ArgumentException("DbProvider is required to be set on AdoPlatformTransactionManager"); + } + + try + { + if (txMgrStateObject.ConnectionHolder == null || txMgrStateObject.ConnectionHolder.SynchronizedWithTransaction) + { + IDbConnection newCon = DbProvider.CreateConnection(); + if (log.IsDebugEnabled) + { + log.Debug("Acquired Connection [" + newCon + ", " + newCon.ConnectionString + "] for ADO.NET transaction"); + } + newCon.Open(); + + //TODO isolation level mgmt - will need to abstract out SQL used to specify this in DbMetaData + //MSDN docs... + //With one exception, you can switch from one isolation level to another at any time during a transaction. The exception occurs when changing from any isolation level to SNAPSHOT isolation + + + //IsolationLevel previousIsolationLevel = + + IDbTransaction newTrans = newCon.BeginTransaction(definition.TransactionIsolationLevel); + + txMgrStateObject.SetConnectionHolder(new ConnectionHolder(newCon, newTrans), true); + + } + txMgrStateObject.ConnectionHolder.SynchronizedWithTransaction = true; + con = txMgrStateObject.ConnectionHolder.Connection; + + + txMgrStateObject.ConnectionHolder.TransactionActive = true; + + int timeout = DetermineTimeout(definition); + if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT) + { + txMgrStateObject.ConnectionHolder.TimeoutInSeconds = timeout; + } + + + //Bind transactional resources to thread + if (txMgrStateObject.NewConnectionHolder) + { + TransactionSynchronizationManager.BindResource(DbProvider, + txMgrStateObject.ConnectionHolder); + } + + } + //TODO catch specific exception + catch (Exception e) + { + ConnectionUtils.DisposeConnection(con, DbProvider); + throw new CannotCreateTransactionException("Could not create ADO.NET connection for transaction", e); + } + + } + + + /// + /// Suspend the resources of the current transaction. + /// + /// Transaction object returned by + /// . + /// + /// An object that holds suspended resources (will be kept unexamined for passing it into + /// .) + /// + /// + /// Transaction synchronization will already have been suspended. + /// + /// + /// If suspending is not supported by the transaction manager implementation. + /// + /// + /// in case of system errors. + /// + protected override object DoSuspend(object transaction) + { + DbProviderTransactionObject txMgrStateObject = (DbProviderTransactionObject)transaction; + txMgrStateObject.ConnectionHolder = null; + ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.UnbindResource(DbProvider); + return conHolder; + } + + + /// + /// Resume the resources of the current transaction. + /// + /// Transaction object returned by + /// . + /// The object that holds suspended resources as returned by + /// . + /// + /// Transaction synchronization will be resumed afterwards. + /// + /// + /// If suspending is not supported by the transaction manager implementation. + /// + /// + /// In the case of system errors. + /// + protected override void DoResume(object transaction, object suspendedResources) + { + ConnectionHolder conHolder = (ConnectionHolder)suspendedResources; + TransactionSynchronizationManager.BindResource(DbProvider, conHolder); + } + + + /// + /// Perform an actual commit on the given transaction. + /// + /// The status representation of the transaction. + /// + ///

    + /// An implementation does not need to check the rollback-only flag. + ///

    + ///
    + /// + /// In the case of system errors. + /// + protected override void DoCommit(DefaultTransactionStatus status) + { + DbProviderTransactionObject txMgrStateObject = + (DbProviderTransactionObject)status.Transaction; + IDbTransaction trans = txMgrStateObject.ConnectionHolder.Transaction; + if (status.Debug) + { + IDbConnection conn = txMgrStateObject.ConnectionHolder.Connection; + log.Debug("Committing ADO.NET transaction on Connection [" + conn + ", " + conn.ConnectionString + "]"); + } + try + { + trans.Commit(); + } + catch (Exception e) + { + throw new TransactionSystemException("Could not commit ADO.NET transaction", e); + } + + } + + /// + /// Perform an actual rollback on the given transaction. + /// + /// The status representation of the transaction. + /// + /// An implementation does not need to check the new transaction flag. + /// + /// + /// In the case of system errors. + /// + protected override void DoRollback(DefaultTransactionStatus status) + { + DbProviderTransactionObject txMgrStateObject = + (DbProviderTransactionObject)status.Transaction; + IDbConnection conn = txMgrStateObject.ConnectionHolder.Connection; + IDbTransaction trans = txMgrStateObject.ConnectionHolder.Transaction; + if (status.Debug) + { + log.Debug("Rolling back ADO.NET transaction on Connection [" + conn + ", " + conn.ConnectionString + "]" ); + } + try + { + trans.Rollback(); + } + catch (Exception e) + { + throw new TransactionSystemException("Could not rollback ADO.NET transaction", e); + } + } + + /// + /// Set the given transaction rollback-only. Only called on rollback + /// if the current transaction takes part in an existing one. + /// + /// The status representation of the transaction. + /// + /// In the case of system errors. + /// + protected override void DoSetRollbackOnly(DefaultTransactionStatus status) + { + DbProviderTransactionObject txMgrStateObject = + (DbProviderTransactionObject)status.Transaction; + if (status.Debug) + { + IDbConnection conn = txMgrStateObject.ConnectionHolder.Connection; + log.Debug("Setting ADO.NET transaction [" + conn + ", " + conn.ConnectionString + "] rollback-only."); + } + txMgrStateObject.SetRollbackOnly(); + + } + + protected override void DoCleanupAfterCompletion(object transaction) + { + DbProviderTransactionObject txMgrStateObject = + (DbProviderTransactionObject)transaction; + if (txMgrStateObject.NewConnectionHolder) + { + TransactionSynchronizationManager.UnbindResource(DbProvider); + } + IDbConnection con = txMgrStateObject.ConnectionHolder.Connection; + + if (log.IsDebugEnabled) + { + log.Debug("Releasing ADO.NET Connection [" + con + ", " + con.ConnectionString + "] after transaction"); + } + + ConnectionUtils.DisposeConnection(con, DbProvider); + //TODO clear out IDbTransaction object? + + txMgrStateObject.ConnectionHolder.Clear(); + + + } + + + + /// + /// DbProvider transaction (state) object, representing a ConnectionHolder. + /// Used as a transaction object by AdoPlatformTransactionManager + /// + /// Derives from AdoTransactionObjectSupport to inherit the capability + /// to manage Savepoints. + /// + /// + private class DbProviderTransactionObject : AdoTransactionObjectSupport + { + private bool newConnectionHolder; + + public void SetConnectionHolder(ConnectionHolder connectionHolder, + bool newConnection) + { + ConnectionHolder = connectionHolder; + newConnectionHolder = newConnection; + } + + public bool NewConnectionHolder + { + get + { + return newConnectionHolder; + } + } + + public bool HasTransaction + { + get + { + return (ConnectionHolder != null && ConnectionHolder.TransactionActive); + } + } + + /// + /// Sets the rollback only. + /// + public void SetRollbackOnly() + { + ConnectionHolder.RollbackOnly = true; + } + + /// + /// Return whether the transaction is internally marked as rollback-only. + /// + /// + /// True of the transaction is marked as rollback-only. + public override bool RollbackOnly + { + get + { + return ConnectionHolder.RollbackOnly; + } + } + + } + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + /// If DbProvider is null. + /// + public void AfterPropertiesSet() + { + if (dbProvider == null) + { + throw new ArgumentException("DbProvider is required"); + } + } + } +} diff --git a/src/Spring/Spring.Data/Data/Core/AdoTemplate.cs b/src/Spring/Spring.Data/Data/Core/AdoTemplate.cs new file mode 100644 index 00000000..8325b999 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Core/AdoTemplate.cs @@ -0,0 +1,3051 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using System.Data.Common; +using System.Globalization; +using System.Reflection; +using Common.Logging; +using Spring.Dao.Support; +using Spring.Data.Common; +using Spring.Data.Support; +using Spring.Reflection.Dynamic; +using Spring.Util; + +#endregion + +namespace Spring.Data.Core +{ + /// + /// This is the central class in the Spring.Data namespace. + /// It simplifies the use of ADO.NET and helps to avoid commons errors. + /// + /// Mark Pollack (.NET) + /// $Id: AdoTemplate.cs,v 1.9 2007/12/28 19:37:13 markpollack Exp $ + public class AdoTemplate : AdoAccessor, IAdoOperations + { + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof(AdoTemplate)); + + #endregion + + #region Fields + + private IDbProvider dbProvider; + + private IAdoExceptionTranslator exceptionTranslator; + + private bool lazyInit = true; + protected Type dataReaderWrapperType; + protected IDynamicConstructor newDataReaderWrapper; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public AdoTemplate() + : base() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The database provider. + public AdoTemplate(IDbProvider provider) + { + DbProvider = provider; + AfterPropertiesSet(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The database provider. + /// if set to false + /// lazily initialize the ErrorCodeExceptionTranslator. + public AdoTemplate(IDbProvider provider, bool lazyInit) + { + DbProvider = provider; + LazyInit = lazyInit; + AfterPropertiesSet(); + } + + #endregion + + #region Properties + + /// + /// An instance of a DbProvider implementation. + /// + public override IDbProvider DbProvider + { + get { return dbProvider; } + set + { + dbProvider = value; + } + } + + /// + /// Gets or sets a value indicating whether to lazily initialize the + /// IAdoExceptionTranslator for this accessor, on first encounter of a + /// exception from the data provider. Default is "true"; can be switched to + /// "false" for initialization on startup. + /// + /// true if to lazy initialize the IAdoExceptionTranslator; + /// otherwise, false. + public override bool LazyInit + { + get { return lazyInit; } + set { lazyInit = value; } + } + + /// + /// Gets or sets the exception translator. If no custom translator is provided, a default + /// is used. + /// + /// The exception translator. + public override IAdoExceptionTranslator ExceptionTranslator + { + get + { + InitExceptionTranslator(); + return exceptionTranslator; + } + set + { + exceptionTranslator = value; + } + } + + /// + /// Gets or set the System.Type to use to create an instance of IDataReaderWrapper + /// for the purpose of having defaults values to use in case of DBNull values read + /// from IDataReader. + /// + /// The type of the data reader wrapper. + public override Type DataReaderWrapperType + { + get { return dataReaderWrapperType; } + set + { + if (dataReaderWrapperType == null) + { + if (typeof(IDataReaderWrapper).IsAssignableFrom(value)) + { + dataReaderWrapperType = value; + ConstructorInfo constructor = ObjectUtils.GetZeroArgConstructorInfo(dataReaderWrapperType); + newDataReaderWrapper = DynamicConstructor.Create(constructor); + + } + else + { + throw new ArgumentException("DataReaderWrapper type must implement IDataReaderWrapper. Implemented interfaces on " + + value.GetType().Name + "are [" + + StringUtils.ArrayToCommaDelimitedString( + ReflectionUtils.ToInterfaceArray(value)) + "]"); + + } + } + else + { + LOG.Warn("Ignoring assignment of DataReaderWrapperType since it has already been assigned."); + } + + } + + } + + #endregion + + + public override void AfterPropertiesSet() + { + if (DbProvider == null) + { + throw new ArgumentException("DbProvider is required"); + } + if (!LazyInit) + { + InitExceptionTranslator(); + } + } + + + #region General Execute Callback Methods + /// + /// Execute a ADO.NET operation on a command object using a delegate callback. + /// + /// The delegate called with a command object. + /// + /// A result object returned by the action or null + /// + /// This allows for implementing arbitrary data access operations + /// on a single command within Spring's managed ADO.NET environment. + public Object Execute(CommandDelegate del) + { + return Execute(new ExecuteCommandCallbackUsingDelegate(del)); + } + + /// + /// Callback to execute a IDbCommand. + /// + /// the callback to execute + /// object returned from callback + public object Execute(ICommandCallback action) + { + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider); + + IDbCommand command = null; + try + { + command = DbProvider.CreateCommand(); + command.Connection = connectionTxPairToUse.Connection; + command.Transaction = connectionTxPairToUse.Transaction; + //TODO collect warnings... + //RegisterEventHandlers(command.Connection); + ApplyCommandSettings(command); + Object result = action.DoInCommand(command); + //SqlWarnings sqlWarnings = GetSqlWarnings() + //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings); + return result; + } + catch (Exception e) + { + AdoUtils.DisposeCommand(command); + command = null; + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + connectionTxPairToUse.Connection = null; + if (DbProvider.IsDataAccessException(e)) + { + throw ExceptionTranslator.Translate("CommandCallback", GetCommandText(action), e); + } + else + { + throw; + } + + + } + finally + { + AdoUtils.DisposeCommand(command); + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + } + + } + + /// + /// Executes ADO.NET operations on a command object, created by the provided IDbCommandCreator, + /// using the interface based callback IDbCommandCallback. + /// + /// The command creator. + /// The callback to execute based on IDbCommand + /// A result object returned by the action or null + public object Execute(IDbCommandCreator commandCreator, ICommandCallback action) + { + AssertUtils.ArgumentNotNull(commandCreator, "commandCreator", "IDbCommandCreator must not be null"); + AssertUtils.ArgumentNotNull(action, "action", "Callback object must not be null"); + + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider); + + + IDbCommand command = null; + try + { + command = commandCreator.CreateDbCommand(DbProvider); + command.Connection = connectionTxPairToUse.Connection; + command.Transaction = connectionTxPairToUse.Transaction; + ApplyCommandSettings(command); + //TODO collect warnings... + //RegisterEventHandlers(command.Connection); + Object result = action.DoInCommand(command); + + //SqlWarnings sqlWarnings = GetSqlWarnings() + //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings); + + return result; + } + catch (Exception e) + { + commandCreator = null; + AdoUtils.DisposeCommand(command); + command = null; + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + connectionTxPairToUse.Connection = null; + if (DbProvider.IsDataAccessException(e)) + { + throw ExceptionTranslator.Translate("CommandCallback", GetCommandText(action), e); + } + else + { + throw; + } + } + finally + { + AdoUtils.DisposeCommand(command); + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + } + + } + + /// + /// Execute ADO.NET operations on a IDbDataAdapter object using an interface based callback. + /// + /// This allows for implementing abritrary data access operations + /// on a single DataAdapter within Spring's managed ADO.NET environment. + /// + /// The data adapter callback. + /// A result object returned by the callback or null + public object Execute(IDataAdapterCallback dataAdapterCallback) + { + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider); + IDbDataAdapter dataAdapter = null; + try + { + dataAdapter = DbProvider.CreateDataAdapter(); + //TODO row updated event handling... + dataAdapter.SelectCommand = DbProvider.CreateCommand(); + dataAdapter.SelectCommand.Connection = connectionTxPairToUse.Connection; + //TODO register for warnings on connection. + dataAdapter.SelectCommand.Transaction = connectionTxPairToUse.Transaction; + ApplyCommandSettings(dataAdapter.SelectCommand); + object result = dataAdapterCallback.DoInDataAdapter(dataAdapter); + return result; + + } + catch (Exception) + { + AdoUtils.DisposeDataAdapterCommands(dataAdapter); + //TODO set dataAdapter command's = null; ? + //TODO exception translation? different hierarchy for data set operations. + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + connectionTxPairToUse.Connection = null; + throw; + } + finally + { + AdoUtils.DisposeDataAdapterCommands(dataAdapter); + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + } + + + } + + #endregion + + #region ExecuteNonQuery + + + /// + /// Executes a non query returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The number of rows affected. + public int ExecuteNonQuery(CommandType cmdType, string cmdText) + { + #region Instrumentation + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing NonQuery " + cmdType + "[" + cmdText + "]"); + } + #endregion + + return (int)Execute(new ExecuteNonQueryCallbackWithParameters(cmdType, cmdText, null)); + } + + /// + /// Executes a non query returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + /// The number of rows affected. + public int ExecuteNonQuery(CommandType cmdType, string cmdText, + string parameterName, Enum dbType, int size, object parameterValue) + { + return (int)Execute(new ExecuteNonQueryCallbackWithParameters(cmdType, cmdText, + CreateDbParameters(parameterName, dbType, size, parameterValue))); + + + } + + /// + /// Executes a non query returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The parameter collection to map. + /// The number of rows affected. + public int ExecuteNonQuery(CommandType cmdType, string cmdText, + IDbParameters parameters) + { + #region Instrumentation + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing NonQuery. " + cmdType + "[" + cmdText + "]"); + } + #endregion + + return (int)Execute(new ExecuteNonQueryCallbackWithParameters(cmdType, cmdText, parameters)); + + } + + /// + /// Executes a non query with parameters set via the + /// command setter, returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The command setter. + /// The number of rows affected. + public int ExecuteNonQuery(CommandType cmdType, string cmdText, + ICommandSetter commandSetter) + { + #region Instrumentation + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing NonQuery. " + cmdType + "[" + cmdText + "]"); + } + #endregion + + return (int)Execute(new ExecuteNonQueryCallbackWithCommandSetter(cmdType, cmdText, commandSetter)); + } + + + /// + /// Executes a non query with a command created via IDbCommandCreator and + /// parameters. + /// + /// Output parameters can be retrieved via the returned + /// dictionary. + /// + /// More commonly used as a lower level support method within the framework, + /// for example StoredProcedure/AdoScalar. + /// + /// + /// The callback to create a IDbCommand. + /// The number of rows affected. + public IDictionary ExecuteNonQuery(IDbCommandCreator commandCreator) + { + + return (IDictionary)Execute(commandCreator, new AdoNonQueryWithOutputParamsCommandCallback()); + + } + + #endregion + + #region ExecuteScalar + + /// + /// Execute the query with the specified command text. + /// + /// No parameters are used. As with + /// IDbCommand.ExecuteScalar, it returns the first column of the first row in the resultset + /// returned by the query. Extra columns or row are ignored. + /// The command type + /// The command text to execute. + /// The first column of the first row in the result set + public object ExecuteScalar(CommandType cmdType, string cmdText) + { + return Execute(new ExecuteScalarCallbackWithParameters(cmdType, cmdText, null)); + } + + /// + /// Execute the query with the specified command text and parameter returning a scalar result + /// + /// The command type + /// The command text to execute. + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + /// The first column of the first row in the result set + public object ExecuteScalar(CommandType cmdType, string cmdText, + string parameterName, Enum dbType, int size, object parameterValue) + { + return Execute(new ExecuteScalarCallbackWithParameters(cmdType, cmdText, + CreateDbParameters(parameterName, dbType, size, parameterValue))); + + } + + + /// + /// Execute the query with the specified command text and parameters returning a scalar result + /// + /// The command type + /// The command text to execute. + /// The parameter collection to map. + /// The first column of the first row in the result set + public object ExecuteScalar(CommandType cmdType, string cmdText, + IDbParameters parameters) + { + return Execute(new ExecuteScalarCallbackWithParameters(cmdType, cmdText, parameters)); + + } + + /// + /// Execute the query with the specified command text and parameters set via the + /// command setter, returning a scalar result + /// + /// The command type + /// The command text to execute. + /// The command setter. + /// The first column of the first row in the result set + public object ExecuteScalar(CommandType cmdType, string cmdText, + ICommandSetter commandSetter) + { + #region Instrumentation + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing ExecuteScalar. " + cmdType + "[" + cmdText + "]"); + } + #endregion + + return (int)Execute(new ExecuteScalarCallbackWithCommandSetter(cmdType, cmdText, commandSetter)); + + } + + /// + /// Execute the query with a command created via IDbCommandCreator and + /// parameters + /// + /// Output parameters can be retrieved via the returned + /// dictionary. + /// + /// More commonly used as a lower level support method within the framework, + /// for example for StoredProcedure/AdoScalar. + /// + /// The callback to create a IDbCommand. + /// A dictionary containing output parameters, if any + public IDictionary ExecuteScalar(IDbCommandCreator commandCreator) + { + return (IDictionary)Execute(commandCreator, new AdoStoredProcedureScalarCommandCallback()); + } + + #endregion + + #region Query with RowCallback + + /// + /// Execute a query given IDbCommand's type and text, reading a + /// single result set on a per-row basis with a . + /// + /// The type of command + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// + public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback) + { + QueryWithResultSetExtractor(cmdType, cmdText, new RowCallbackResultSetExtractor(rowCallback)); + } + + /// + /// Execute a query given IDbCommand's type and text by + /// passing the created IDbCommand to a ICommandSetter implementation + /// that knows how to bind values to the IDbCommand, reading a + /// single result set on a per-row basis with a . + /// + /// The type of command + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// + /// The command setter. + public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback, + ICommandSetter commandSetter) + { + QueryWithResultSetExtractor(cmdType, cmdText, new RowCallbackResultSetExtractor(rowCallback), commandSetter); + + } + + /// + /// Execute a query given IDbCommand's type and text and provided parameter + /// information, reading a + /// single result set on a per-row basis with a . + /// + /// The type of command + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback, + string parameterName, Enum dbType, int size, object parameterValue) + { + QueryWithResultSetExtractor(cmdType, cmdText, new RowCallbackResultSetExtractor(rowCallback), parameterName, dbType, size, parameterValue); + } + + + public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback, IDbParameters parameters) + { + QueryWithResultSetExtractor(cmdType, cmdText, new RowCallbackResultSetExtractor(rowCallback), parameters); + } + + #endregion + + // RowCallback with Delegate Questionable for 1.1 since no anonymous delegates and there isn't a collecting parameter + // in the delegate method signature. + + #region Query with RowCallback Delegate + + public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate) + { + QueryWithResultSetExtractor(cmdType, sql, new RowCallbackResultSetExtractor(rowCallbackDelegate)); + + } + public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, ICommandSetter commandSetter) + { + QueryWithResultSetExtractor(cmdType, sql, new RowCallbackResultSetExtractor(rowCallbackDelegate), commandSetter); + + } + + public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, + string name, Enum dbType, int size, object parameterValue) + { + QueryWithResultSetExtractor(cmdType, sql, new RowCallbackResultSetExtractor(rowCallbackDelegate), name, dbType, size, parameterValue); + + } + + public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, IDbParameters parameters) + { + QueryWithResultSetExtractor(cmdType, sql, new RowCallbackResultSetExtractor(rowCallbackDelegate), parameters); + + } + + + + #endregion + + #region Query with RowMapper + + public IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper) + { + return (IList)QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapper)); + } + + public IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, ICommandSetter commandSetter) + { + + return (IList)QueryWithResultSetExtractor(cmdType, cmdText, + new RowMapperResultSetExtractor(rowMapper), commandSetter); + + } + + + public IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, + string name, Enum dbType, int size, object parameterValue) + { + return (IList)QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapper), name, dbType, size, parameterValue); + } + + + public IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, IDbParameters parameters) + { + return (IList)QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapper), parameters); + } + + + #endregion + + #region Query With RowMapper Delegate + + public IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate) + { + return (IList)QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapperDelegate)); + } + + public IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, ICommandSetter commandSetter) + { + return (IList)QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapperDelegate), commandSetter); + } + + + public IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, + string parameterName, Enum dbType, int size, object parameterValue) + { + return (IList)QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapperDelegate), parameterName, dbType, size, parameterValue); + } + + + public IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, + IDbParameters parameters) + { + return (IList)QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapperDelegate), parameters); + } + + #endregion + + #region Query with ResultSetExtractor + + /// + /// Execute a query given static SQL/Stored Procedure name + /// and process a single result set with an instance of IResultSetExtractor + /// + /// The type of command. + /// The SQL/Stored Procedure to execute + /// Object that will extract all rows of a result set + /// An arbitrary result object, as returned by the IResultSetExtractor + public object QueryWithResultSetExtractor(CommandType cmdType, string sql, IResultSetExtractor rse) + { + AssertUtils.ArgumentNotNull(sql, "sql", "SQL must not be null"); + + //TODO check for parameter placeholders... + + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing SQL [" + sql + "]"); + } + + return Execute(new QueryCallback(this, cmdType, sql, rse, null)); + + } + + public object QueryWithResultSetExtractor(CommandType cmdType, string cmdText, IResultSetExtractor resultSetExtractor, ICommandSetter commandSetter) + { + AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null"); + return Execute(new QueryCallbackWithCommandSetter(this, cmdType, cmdText, + resultSetExtractor, commandSetter)); + } + + + public object QueryWithResultSetExtractor(CommandType cmdType, string cmdText, IResultSetExtractor resultSetExtractor, + string name, Enum dbType, int size, object parameterValue) + { + AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null"); + return Execute(new QueryCallback(this, cmdType, cmdText, resultSetExtractor, CreateDbParameters(name, dbType, size, parameterValue))); + } + + public object QueryWithResultSetExtractor(CommandType cmdType, string cmdText, IResultSetExtractor resultSetExtractor, + IDbParameters parameters) + { + AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null"); + return Execute(new QueryCallback(this, cmdType, cmdText, resultSetExtractor, parameters)); + } + + + #endregion + + + #region Query with ResultSetExtractorDelegate + + /// + /// Execute a query given static SQL/Stored Procedure name + /// and process a single result set with an instance of IResultSetExtractor + /// + /// The type of command. + /// The SQL/Stored Procedure to execute + /// Delegate that will extract all rows of a result set + /// An arbitrary result object, as returned by the IResultSetExtractor + public object QueryWithResultSetExtractorDelegate(CommandType cmdType, string sql, ResultSetExtractorDelegate resultSetExtractorDelegate) + { + AssertUtils.ArgumentNotNull(sql, "sql", "SQL must not be null"); + + //TODO check for parameter placeholders... + + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing SQL [" + sql + "]"); + } + + return Execute(new QueryCallback(this, cmdType, sql, resultSetExtractorDelegate, null)); + + } + + public object QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractorDelegate, + ICommandSetter commandSetter) + { + AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null"); + return Execute(new QueryCallbackWithCommandSetter(this, cmdType, cmdText, + resultSetExtractorDelegate, commandSetter)); + } + + + public object QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractorDelegate, + string name, Enum dbType, int size, object parameterValue) + { + AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null"); + return Execute(new QueryCallback(this, cmdType, cmdText, resultSetExtractorDelegate, CreateDbParameters(name, dbType, size, parameterValue))); + } + + public object QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractorDelegate, + IDbParameters parameters) + { + AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null"); + return Execute(new QueryCallback(this, cmdType, cmdText, resultSetExtractorDelegate, parameters)); + } + + + #endregion + + #region Query for Object + + /// + /// Execute a query with the specified command text, mapping a single result + /// row to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// object that will map one object per row + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + public object QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper) + { + IList results = QueryWithRowMapper(cmdType, cmdText, rowMapper); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + /// + /// Execute a query with the specified command text and parameters set via the + /// command setter, mapping a single result row to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// object that will map one object per row + /// The command setter. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + public object QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper, ICommandSetter commandSetter) + { + IList results = QueryWithRowMapper(cmdType, cmdText, rowMapper, commandSetter); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + /// + /// Execute a query with the specified command text and parameters, mapping a single result row + /// to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// object that will map one object per row + /// The parameter collection to use in the query. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + public object QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper, IDbParameters parameters) + { + IList results = QueryWithRowMapper(cmdType, cmdText, rowMapper, parameters); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + /// + /// Execute a query with the specified command text and parameter, mapping a single result row + /// to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// object that will map one object per row + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + public object QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper, string parameterName, Enum dbType, int size, + object parameterValue) + { + IList results = QueryWithRowMapper(cmdType, cmdText, rowMapper, parameterName, dbType, size, parameterValue); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + #endregion + + #region Query for ObjectDelegate + + /// + /// Execute a query with the specified command text, mapping a single result + /// row to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// delegate that will map one object per row + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + public object QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate) + { + IList results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + + /// + /// Execute a query with the specified command text and parameters set via the + /// command setter, mapping a single result row to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// delegate that will map one object per row + /// The command setter. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + public object QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, ICommandSetter commandSetter) + { + IList results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate, commandSetter); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + /// + /// Execute a query with the specified command text and parameters, mapping a single result row + /// to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// delegate that will map one object per row + /// The parameter collection to use in the query. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + public object QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, IDbParameters parameters) + { + IList results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate, parameters); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + /// + /// Execute a query with the specified command text and parameter, mapping a single result row + /// to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// delegate that will map one object per row + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + public object QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, string parameterName, Enum dbType, int size, + object parameterValue) + { + IList results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate, parameterName, dbType, size, parameterValue); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + #endregion + + #region Query with CommandCreator + + public object QueryWithCommandCreator(IDbCommandCreator cc, IResultSetExtractor rse) + { + return QueryWithCommandCreator(cc, rse, null); + } + + public void QueryWithCommandCreator(IDbCommandCreator cc, IRowCallback rowCallback) + { + QueryWithCommandCreator(cc, rowCallback, null); + } + + public IList QueryWithCommandCreator(IDbCommandCreator cc, IRowMapper rowMapper) + { + return QueryWithCommandCreator(cc, rowMapper, null); + } + + public object QueryWithCommandCreator(IDbCommandCreator cc, IResultSetExtractor rse, IDictionary returnedParameters) + { + if (rse == null) + { + throw new ArgumentNullException("Result Set Extractor must not be null"); + } + + return Execute(cc, new AdoResultSetExtractorWithOutputParamsCommandCallback(this, rse, returnedParameters)); + } + + public void QueryWithCommandCreator(IDbCommandCreator cc, IRowCallback rowCallback, IDictionary returnedParameters) + { + if (rowCallback == null) + { + throw new ArgumentNullException("RowCallback must not be null"); + } + Execute(cc, new AdoRowCallbackCommandCallback(this, rowCallback, returnedParameters)); + } + + public IList QueryWithCommandCreator(IDbCommandCreator cc, IRowMapper rowMapper, IDictionary returnedParameters) + { + if (rowMapper == null) + { + throw new ArgumentNullException("rowMapper must not be null"); + } + + return (IList)Execute(cc, new AdoRowMapperQueryCommandCallback(this, rowMapper, returnedParameters)); + } + + + + + public IDictionary QueryWithCommandCreator(IDbCommandCreator cc, IList namedResultSetProcessors) + { + return (IDictionary)Execute(cc, new AdoResultProcessorsQueryCommandCallback(this, namedResultSetProcessors)); + } + + #endregion + + // DataSet/DataTable related methods. + + #region DataTable Create operations without parameters + + public DataTable DataTableCreate(CommandType commandType, string sql) + { + DataTable dataTable = CreateDataTable(); + DataTableFill(dataTable, commandType, sql); + return dataTable; + } + + public DataTable DataTableCreate(CommandType commandType, string sql, + string tableMappingName) + { + DataTable dataTable = CreateDataTable(); + DataTableFill(dataTable, commandType, sql, tableMappingName); + return dataTable; + } + + public DataTable DataTableCreate(CommandType commandType, string sql, + ITableMapping tableMapping) + { + DataTable dataTable = CreateDataTable(); + DataTableFill(dataTable, commandType, sql, tableMapping); + return dataTable; + } + + public DataTable DataTableCreate(CommandType commandType, string sql, + ITableMapping tableMapping, + IDataAdapterSetter setter) + { + DataTable dataTable = CreateDataTable(); + DataTableFill(dataTable, commandType, sql, tableMapping, setter); + return dataTable; + } + + #endregion + + #region DataTable Create operations with parameters + + public DataTable DataTableCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters) + { + DataTable dataTable = CreateDataTable(); + DataTableFillWithParams(dataTable, commandType, sql, parameters); + return dataTable; + } + + public DataTable DataTableCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + string tableMappingName) + { + DataTable dataTable = CreateDataTable(); + DataTableFillWithParams(dataTable, commandType, sql, parameters, tableMappingName); + return dataTable; + } + + public DataTable DataTableCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + ITableMapping tableMapping) + { + DataTable dataTable = CreateDataTable(); + DataTableFillWithParams(dataTable, commandType, sql, parameters, tableMapping); + return dataTable; + } + + public DataTable DataTableCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + ITableMapping tableMapping, + IDataAdapterSetter dataAdapterSetter) + { + DataTable dataTable = CreateDataTable(); + DataTableFillWithParams(dataTable, commandType, sql, parameters, tableMapping, dataAdapterSetter); + return dataTable; + } + + + #endregion + + #region DataTable Fill operations without parameters + /// + /// Fill a based on a select command that requires no parameters. + /// + /// The to populate + /// The type of command + /// SQL query to execute + /// The number of rows successfully added to or refreshed in the + public int DataTableFill(DataTable dataTable, CommandType commandType, string sql) + { + ValidateFillArguments(dataTable, sql); + + #region Instrumentation + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing DataTableFill " + commandType + "[" + sql + "]"); + } + #endregion + + ITableMappingCollection mappingCollection = DoCreateMappingCollection(null); + return (int)Execute(new DataAdapterFillCallback(dataTable, + commandType, sql, + mappingCollection, null, null, null)); + } + + public int DataTableFill(DataTable dataTable, CommandType commandType, string sql, + string tableMappingName) + { + ValidateFillArguments(dataTable, sql); + + #region Instrumentation + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing DataTableFill " + commandType + "[" + sql + "] with table mapping name " + tableMappingName); + } + #endregion + + if (tableMappingName == null) + { + tableMappingName = "Table"; + } + ITableMappingCollection mappingCollection = DoCreateMappingCollection(new string[] { tableMappingName }); + return (int)Execute(new DataAdapterFillCallback(dataTable, + commandType, sql, + mappingCollection, null, null, null)); + + } + + public int DataTableFill(DataTable dataTable, CommandType commandType, string sql, + ITableMapping tableMapping) + { + ValidateFillArguments(dataTable, sql, tableMapping); + ITableMappingCollection mappingCollection = new DataTableMappingCollection(); + mappingCollection.Add((object)tableMapping); + + return (int)Execute(new DataAdapterFillCallback(dataTable, + commandType, sql, + mappingCollection, null, null, null)); + } + + public int DataTableFill(DataTable dataTable, CommandType commandType, string sql, + ITableMapping tableMapping, + IDataAdapterSetter setter) + { + ValidateFillArguments(dataTable, sql, tableMapping); + ITableMappingCollection mappingCollection = new DataTableMappingCollection(); + mappingCollection.Add((object)tableMapping); + return (int)Execute(new DataAdapterFillCallback(dataTable, + commandType, sql, + mappingCollection, setter, null, null)); + } + + + #endregion + + #region DataTable Fill operations with parameters + + public int DataTableFillWithParams(DataTable dataTable, CommandType commandType, string sql, + IDbParameters parameters) + { + ValidateFillWithParameterArguments(dataTable, sql, parameters); + ITableMappingCollection mappingCollection = DoCreateMappingCollection(null); + return (int)Execute(new DataAdapterFillCallback(dataTable, + commandType, sql, + mappingCollection, null, null, + parameters)); + } + + public int DataTableFillWithParams(DataTable dataTable, CommandType commandType, string sql, + IDbParameters parameters, + string tableMappingName) + { + ValidateFillWithParameterArguments(dataTable, sql, parameters); + if (tableMappingName == null) + { + tableMappingName = "Table"; + } + ITableMappingCollection mappingCollection = DoCreateMappingCollection(new string[] { tableMappingName }); + return (int)Execute(new DataAdapterFillCallback(dataTable, + commandType, sql, + mappingCollection, null, null, parameters)); + } + + public int DataTableFillWithParams(DataTable dataTable, CommandType commandType, string sql, + IDbParameters parameters, + ITableMapping tableMapping) + { + ValidateFillWithParameterArguments(dataTable, sql, parameters, tableMapping); + ITableMappingCollection mappingCollection = new DataTableMappingCollection(); + mappingCollection.Add((object)tableMapping); + return (int)Execute(new DataAdapterFillCallback(dataTable, + commandType, sql, + mappingCollection, null, null, parameters)); + } + + public int DataTableFillWithParams(DataTable dataTable, CommandType commandType, string sql, + IDbParameters parameters, + ITableMapping tableMapping, + IDataAdapterSetter dataAdapterSetter) + { + ValidateFillWithParameterArguments(dataTable, sql, parameters, tableMapping); + ITableMappingCollection mappingCollection = new DataTableMappingCollection(); + mappingCollection.Add((object)tableMapping); + return (int)Execute(new DataAdapterFillCallback(dataTable, + commandType, sql, + mappingCollection, dataAdapterSetter, null, parameters)); + } + + + #endregion + + #region DataTable Update operations + //TODO conflict options... + + public int DataTableUpdateWithCommandBuilder(DataTable dataTable, + CommandType commandType, + string selectSql, + IDbParameters parameters, + string tableName) + { + ValidateUpdateWithCommandBuilderArguments(dataTable, tableName, selectSql); + ITableMappingCollection mappingCollection = DoCreateMappingCollection(new string[] { tableName }); + return (int)Execute(new DataAdapterUpdateWithCommandBuilderCallback(dataTable, + DbProvider.CreateCommandBuilder(), + mappingCollection, + commandType, + selectSql, + parameters, + null)); + } + + public int DataTableUpdateWithCommandBuilder(DataTable dataTable, + CommandType commandType, + string selectSql, + IDbParameters parameters, + string tableName, + IDataAdapterSetter dataAdapterSetter) + { + ValidateUpdateWithCommandBuilderArguments(dataTable, tableName, selectSql); + ITableMappingCollection mappingCollection = DoCreateMappingCollection(new string[] { tableName }); + return (int)Execute(new DataAdapterUpdateWithCommandBuilderCallback(dataTable, + DbProvider.CreateCommandBuilder(), + mappingCollection, + commandType, + selectSql, + parameters, + dataAdapterSetter)); + } + + + public int DataTableUpdateWithCommandBuilder(DataTable dataTable, + CommandType commandType, + string selectSql, + IDbParameters parameters, + ITableMapping tableMapping, + IDataAdapterSetter dataAdapterSetter) + { + ValidateUpdateWithCommandBuilderArguments(dataTable, tableMapping, selectSql); + ITableMappingCollection mappingCollection = new DataTableMappingCollection(); + mappingCollection.Add(tableMapping); + return (int)Execute(new DataAdapterUpdateWithCommandBuilderCallback(dataTable, + DbProvider.CreateCommandBuilder(), + mappingCollection, + commandType, + selectSql, + parameters, + dataAdapterSetter)); + } + + + + public int DataTableUpdate(DataTable dataTable, + string tableName, + CommandType insertCommandtype, string insertSql, IDbParameters insertParameters, + CommandType updateCommandtype, string updateSql, IDbParameters updateParameters, + CommandType deleteCommandtype, string deleteSql, IDbParameters deleteParameters) + { + ValidateUpdateArguments(dataTable, tableName); + ITableMappingCollection mappingCollection = DoCreateMappingCollection(new string[] { tableName }); + return DataTableUpdate(dataTable, mappingCollection, + insertCommandtype, insertSql, insertParameters, + updateCommandtype, updateSql, updateParameters, + deleteCommandtype, deleteSql, deleteParameters, + null); + + } + + public int DataTableUpdate(DataTable dataTable, + string tableName, + CommandType insertCommandtype, string insertSql, IDbParameters insertParameters, + CommandType updateCommandtype, string updateSql, IDbParameters updateParameters, + CommandType deleteCommandtype, string deleteSql, IDbParameters deleteParameters, + IDataAdapterSetter dataAdapterSetter) + { + ValidateUpdateArguments(dataTable, tableName); + ITableMappingCollection mappingCollection = DoCreateMappingCollection(new string[] { tableName }); + return DataTableUpdate(dataTable, mappingCollection, + insertCommandtype, insertSql, insertParameters, + updateCommandtype, updateSql, updateParameters, + deleteCommandtype, deleteSql, deleteParameters, + dataAdapterSetter); + } + + public int DataTableUpdate(DataTable dataTable, + ITableMapping tableMapping, + CommandType insertCommandtype, string insertSql, IDbParameters insertParameters, + CommandType updateCommandtype, string updateSql, IDbParameters updateParameters, + CommandType deleteCommandtype, string deleteSql, IDbParameters deleteParameters, + IDataAdapterSetter dataAdapterSetter) + { + ValidateUpdateArguments(dataTable, tableMapping); + ITableMappingCollection mappingCollection = new DataTableMappingCollection(); + + mappingCollection.Add((object)tableMapping); + + return DataTableUpdate(dataTable, mappingCollection, + insertCommandtype, insertSql, insertParameters, + updateCommandtype, updateSql, updateParameters, + deleteCommandtype, deleteSql, deleteParameters, + dataAdapterSetter); + + + } + + + + #endregion + + #region DataSet Create operations without parameters + + public DataSet DataSetCreate(CommandType commandType, string sql) + { + DataSet dataSet = CreateDataSet(); + DataSetFill(dataSet, commandType, sql); + return dataSet; + } + + + public DataSet DataSetCreate(CommandType commandType, string sql, + string[] tableNames) + { + DataSet dataSet = CreateDataSet(); + DataSetFill(dataSet, commandType, sql, tableNames); + return dataSet; + } + + public DataSet DataSetCreate(CommandType commandType, string sql, + ITableMappingCollection tableMapping) + { + DataSet dataSet = CreateDataSet(); + DataSetFill(dataSet, commandType, sql, tableMapping); + return dataSet; + } + + public DataSet DataSetCreate(CommandType commandType, string sql, + ITableMappingCollection tableMapping, + IDataAdapterSetter setter) + { + DataSet dataSet = CreateDataSet(); + DataSetFill(dataSet, commandType, sql, tableMapping, setter); + return dataSet; + } + + public DataSet DataSetCreate(CommandType commandType, string sql, + ITableMappingCollection tableMapping, + IDataAdapterSetter setter, + IDataSetFillLifecycleProcessor fillLifecycleProcessor) + { + DataSet dataSet = CreateDataSet(); + DataSetFill(dataSet, commandType, sql, tableMapping, setter, fillLifecycleProcessor); + return dataSet; + } + #endregion + + #region DataSet Create operations with parameters + + public DataSet DataSetCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters) + { + DataSet dataSet = CreateDataSet(); + DataSetFillWithParameters(dataSet, commandType, sql, parameters); + return dataSet; + } + + public DataSet DataSetCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + string[] tableNames) + { + DataSet dataSet = CreateDataSet(); + DataSetFillWithParameters(dataSet, commandType, sql, parameters, tableNames); + return dataSet; + } + + public DataSet DataSetCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping) + { + DataSet dataSet = CreateDataSet(); + DataSetFillWithParameters(dataSet, commandType, sql, parameters, tableMapping); + return dataSet; + } + + public DataSet DataSetCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping, + IDataAdapterSetter dataAdapterSetter) + { + DataSet dataSet = CreateDataSet(); + DataSetFillWithParameters(dataSet, commandType, sql, parameters, tableMapping, dataAdapterSetter); + return dataSet; + } + + public DataSet DataSetCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping, + IDataAdapterSetter dataAdapterSetter, + IDataSetFillLifecycleProcessor fillLifecycleProcessor) + { + DataSet dataSet = CreateDataSet(); + DataSetFillWithParameters(dataSet, commandType, sql, parameters, tableMapping, dataAdapterSetter, fillLifecycleProcessor); + return dataSet; + } + + #endregion + + #region DataSet Fill operations without parameters + + public int DataSetFill(DataSet dataSet, CommandType commandType, string sql) + { + ValidateFillArguments(dataSet, sql); + + #region Instrumentation + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing DataSetFill " + commandType + "[" + sql + "]"); + } + #endregion + + + ITableMappingCollection mappingCollection = DoCreateMappingCollection(null); + return (int)Execute(new DataAdapterFillCallback(dataSet, + commandType, sql, + mappingCollection, null, null, null)); + } + + + + public int DataSetFill(DataSet dataSet, CommandType commandType, string sql, string[] tableNames) + { + ValidateFillArguments(dataSet, sql); + + #region Instrumentation + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing DataSetFill " + commandType + "[" + sql + "] with table names " + tableNames); + } + #endregion + + if (tableNames == null) + { + tableNames = new string[] { "Table" }; + } + ITableMappingCollection mappingCollection = DoCreateMappingCollection(tableNames); + return (int)Execute(new DataAdapterFillCallback(dataSet, + commandType, sql, + mappingCollection, null, null, null)); + } + + + public int DataSetFill(DataSet dataSet, CommandType commandType, string sql, + ITableMappingCollection tableMapping) + { + ValidateFillArguments(dataSet, sql, tableMapping); + return (int)Execute(new DataAdapterFillCallback(dataSet, + commandType, sql, + tableMapping, null, null, null)); + + } + + public int DataSetFill(DataSet dataSet, CommandType commandType, string sql, + ITableMappingCollection tableMapping, + IDataAdapterSetter setter) + { + ValidateFillArguments(dataSet, sql, tableMapping); + return (int)Execute(new DataAdapterFillCallback(dataSet, + commandType, sql, + tableMapping, setter, null, null)); + + } + + public int DataSetFill(DataSet dataSet, CommandType commandType, string sql, + ITableMappingCollection tableMapping, + IDataAdapterSetter setter, + IDataSetFillLifecycleProcessor fillLifecycleProcessor) + { + ValidateFillArguments(dataSet, sql, tableMapping); + return (int)Execute(new DataAdapterFillCallback(dataSet, + commandType, sql, + tableMapping, setter, fillLifecycleProcessor, null)); + + } + + #endregion + + #region DataSet Fill operations with parameters + + public int DataSetFillWithParameters(DataSet dataSet, CommandType commandType, string sql, + IDbParameters parameters) + { + ValidateFillWithParameterArguments(dataSet, sql, parameters); + ITableMappingCollection mappingCollection = DoCreateMappingCollection(null); + return (int)Execute(new DataAdapterFillCallback(dataSet, + commandType, sql, + mappingCollection, null, null, + parameters)); + } + + public int DataSetFillWithParameters(DataSet dataSet, CommandType commandType, string sql, + IDbParameters parameters, + string[] tableNames) + { + ValidateFillWithParameterArguments(dataSet, sql, parameters); + if (tableNames == null) + { + tableNames = new string[] { "Table" }; + } + ITableMappingCollection tableMapping = DoCreateMappingCollection(tableNames); + return (int)Execute(new DataAdapterFillCallback(dataSet, + commandType, sql, + tableMapping, null, null, parameters)); + + } + + public int DataSetFillWithParameters(DataSet dataSet, CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping) + { + ValidateFillWithParameterArguments(dataSet, sql, parameters, tableMapping); + return (int)Execute(new DataAdapterFillCallback(dataSet, + commandType, sql, + tableMapping, null, null, parameters)); + } + + public int DataSetFillWithParameters(DataSet dataSet, CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping, + IDataAdapterSetter dataAdapterSetter) + { + ValidateFillWithParameterArguments(dataSet, sql, parameters, tableMapping); + return (int)Execute(new DataAdapterFillCallback(dataSet, + commandType, sql, + tableMapping, dataAdapterSetter, null, parameters)); + } + + public int DataSetFillWithParameters(DataSet dataSet, CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping, + IDataAdapterSetter dataAdapterSetter, + IDataSetFillLifecycleProcessor fillLifecycleProcessor) + { + ValidateFillWithParameterArguments(dataSet, sql, parameters, tableMapping); + return (int)Execute(new DataAdapterFillCallback(dataSet, + commandType, sql, + tableMapping, dataAdapterSetter, fillLifecycleProcessor, parameters)); + } + + #endregion + + #region DataSet Update operations + public int DataSetUpdateWithCommandBuilder(DataSet dataSet, + CommandType commandType, + string selectSql, + IDbParameters selectParameters, + string tableName) + { + ValidateUpdateWithCommandBuilderArguments(dataSet, tableName, selectSql); + ITableMappingCollection mappingCollection = DoCreateMappingCollection(new string[] { tableName }); + return (int)Execute(new DataAdapterUpdateWithCommandBuilderCallback(dataSet, + DbProvider.CreateCommandBuilder(), + mappingCollection, + commandType, + selectSql, + selectParameters, + null)); + } + + public int DataSetUpdateWithCommandBuilder(DataSet dataSet, + CommandType commandType, + string selectSql, + IDbParameters selectParameters, + string tableName, + IDataAdapterSetter dataAdapterSetter) + { + ValidateUpdateWithCommandBuilderArguments(dataSet, tableName, selectSql); + ITableMappingCollection mappingCollection = DoCreateMappingCollection(new string[] { tableName }); + return (int)Execute(new DataAdapterUpdateWithCommandBuilderCallback(dataSet, + DbProvider.CreateCommandBuilder(), + mappingCollection, + commandType, + selectSql, + selectParameters, + dataAdapterSetter)); + } + + public int DataSetUpdateWithCommandBuilder(DataSet dataSet, + CommandType commandType, + string selectSql, + IDbParameters selectParameters, + ITableMappingCollection mappingCollection, + IDataAdapterSetter dataAdapterSetter) + { + ValidateUpdateWithCommandBuilderArguments(dataSet, mappingCollection, selectSql); + return (int)Execute(new DataAdapterUpdateWithCommandBuilderCallback(dataSet, + DbProvider.CreateCommandBuilder(), + mappingCollection, + commandType, + selectSql, + selectParameters, + dataAdapterSetter)); + } + + public int DataSetUpdate(DataSet dataSet, + string tableName, + IDbCommand insertCommand, + IDbCommand updateCommand, + IDbCommand deleteCommand) + { + ValidateUpdateArguments(dataSet, tableName); + + ITableMappingCollection mappingCollection = DoCreateMappingCollection(new string[] { tableName }); + + return (int)Execute(new DataAdapterUpdateCallback(dataSet, + mappingCollection, + insertCommand, + updateCommand, + deleteCommand, + null)); + } + + public int DataSetUpdate(DataSet dataSet, + string tableName, + CommandType insertCommandtype, string insertSql, IDbParameters insertParameters, + CommandType updateCommandtype, string updateSql, IDbParameters updateParameters, + CommandType deleteCommandtype, string deleteSql, IDbParameters deleteParameters) + { + ValidateUpdateArguments(dataSet, tableName); + IDbCommand insertCommand = null; + if (insertSql != null) + { + insertCommand = DbProvider.CreateCommand(); + insertCommand.CommandType = insertCommandtype; + insertCommand.CommandText = insertSql; + ParameterUtils.CopyParameters(insertCommand, insertParameters); + } + IDbCommand updateCommand = null; + if (updateSql != null) + { + updateCommand = DbProvider.CreateCommand(); + updateCommand.CommandType = updateCommandtype; + updateCommand.CommandText = updateSql; + ParameterUtils.CopyParameters(updateCommand, updateParameters); + } + IDbCommand deleteCommand = null; + if (deleteSql != null) + { + deleteCommand = DbProvider.CreateCommand(); + deleteCommand.CommandType = deleteCommandtype; + deleteCommand.CommandText = deleteSql; + ParameterUtils.CopyParameters(deleteCommand, deleteParameters); + } + ITableMappingCollection mappingCollection = DoCreateMappingCollection(new string[] { tableName }); + + int returnVal = (int)Execute(new DataAdapterUpdateCallback(dataSet, mappingCollection, + insertCommand, updateCommand, deleteCommand, null)); + + if (insertSql != null) + { + ParameterUtils.CopyParameters(insertParameters, insertCommand); + } + if (updateSql != null) + { + ParameterUtils.CopyParameters(updateParameters, updateCommand); + } + if (deleteSql != null) + { + ParameterUtils.CopyParameters(deleteParameters, deleteCommand); + } + return returnVal; + } + + + + public int DataSetUpdate(DataSet dataSet, + string tableName, + IDbCommand insertCommand, + IDbCommand updateCommand, + IDbCommand deleteCommand, + IDataAdapterSetter dataAdapterSetter) + { + ValidateUpdateArguments(dataSet, tableName); + ITableMappingCollection tableMapping = DoCreateMappingCollection(new string[] { tableName }); + return (int)Execute(new DataAdapterUpdateCallback(dataSet, + tableMapping, + insertCommand, + updateCommand, + deleteCommand, + dataAdapterSetter)); + } + + + public int DataSetUpdate(DataSet dataSet, + ITableMappingCollection tableMapping, + IDbCommand insertCommand, + IDbCommand updateCommand, + IDbCommand deleteCommand) + { + ValidateUpdateArguments(dataSet, tableMapping); + return (int)Execute(new DataAdapterUpdateCallback(dataSet, + tableMapping, + insertCommand, + updateCommand, + deleteCommand, + null)); + } + + public int DataSetUpdate(DataSet dataSet, + ITableMappingCollection tableMapping, + IDbCommand insertCommand, + IDbCommand updateCommand, + IDbCommand deleteCommand, + IDataAdapterSetter dataAdapterSetter) + { + ValidateUpdateArguments(dataSet, tableMapping); + return (int)Execute(new DataAdapterUpdateCallback(dataSet, + tableMapping, + insertCommand, + updateCommand, + deleteCommand, + dataAdapterSetter)); + } + + #endregion + + #region Parameter Creation Helper Methods + + public override IDataParameter[] DeriveParameters(string procedureName, bool includeReturnParameter) + { + return (IDataParameter[])Execute(new DeriveParametersCommandCallback(DbProvider, procedureName, includeReturnParameter)); + + } + + #endregion + + #region Private Helper Methods + + private IDbParameters CreateDbParameters(IDbDataParameter parameter) + { + IDbParameters parameters = new DbParameters(DbProvider); + parameters.AddParameter(parameter); + return parameters; + } + + private IDbParameters CreateDbParameters(IList parameterList) + { + IDbParameters parameters = null; + if (parameterList != null) + { + parameters = new DbParameters(DbProvider); + foreach (IDbDataParameter parameter in parameterList) + { + parameters.AddParameter(parameter); + } + } + return parameters; + } + + private IDbParameters CreateDbParameters(object[] parameterValues) + { + IDbParameters parameters = null; + if (parameterValues != null) + { + parameters = new DbParameters(DbProvider); + foreach (object parameterValue in parameterValues) + { + parameters.Add(parameterValue); + } + } + return parameters; + } + + public int DataTableUpdate(DataTable dataTable, + ITableMappingCollection mappingCollection, + CommandType insertCommandtype, string insertSql, IDbParameters insertParameters, + CommandType updateCommandtype, string updateSql, IDbParameters updateParameters, + CommandType deleteCommandtype, string deleteSql, IDbParameters deleteParameters, + IDataAdapterSetter dataAdapterSetter) + { + //TODO - refactor to remove cut-n-pasted code. + IDbCommand insertCommand = null; + if (insertSql != null) + { + insertCommand = DbProvider.CreateCommand(); + insertCommand.CommandType = insertCommandtype; + insertCommand.CommandText = insertSql; + ParameterUtils.CopyParameters(insertCommand, insertParameters); + } + IDbCommand updateCommand = null; + if (updateSql != null) + { + updateCommand = DbProvider.CreateCommand(); + updateCommand.CommandType = updateCommandtype; + updateCommand.CommandText = updateSql; + ParameterUtils.CopyParameters(updateCommand, updateParameters); + } + IDbCommand deleteCommand = null; + if (deleteSql != null) + { + deleteCommand = DbProvider.CreateCommand(); + deleteCommand.CommandType = deleteCommandtype; + deleteCommand.CommandText = deleteSql; + ParameterUtils.CopyParameters(deleteCommand, deleteParameters); + } + + int returnVal = (int)Execute(new DataAdapterUpdateCallback(dataTable, mappingCollection, + insertCommand, updateCommand, deleteCommand, null)); + + if (insertSql != null) + { + ParameterUtils.CopyParameters(insertParameters, insertCommand); + } + if (updateSql != null) + { + ParameterUtils.CopyParameters(updateParameters, updateCommand); + } + if (deleteSql != null) + { + ParameterUtils.CopyParameters(deleteParameters, deleteCommand); + } + return returnVal; + } + #endregion + + #region Protected Helper Methods + + protected void InitExceptionTranslator() + { + if (exceptionTranslator == null) + { + IDbProvider provider = DbProvider; + if (provider != null) + { + exceptionTranslator = new ErrorCodeExceptionTranslator(provider); + } + else + { + exceptionTranslator = new FallbackExceptionTranslator(); + } + } + } + + #endregion + + #region Protected DataAdapter Helper methods + + protected virtual DataTable CreateDataTable() + { + DataTable dataTable = new DataTable(); + dataTable.Locale = CultureInfo.InvariantCulture; + return dataTable; + } + + protected virtual DataSet CreateDataSet() + { + DataSet dataSet = new DataSet(); + dataSet.Locale = CultureInfo.InvariantCulture; + return dataSet; + } + + protected virtual void ValidateFillArguments(DataTable dataTable, string sql) + { + if (dataTable == null) + { + throw new ArgumentNullException("dataTable", "DataTable argument can not be null"); + } + if (sql == null) + { + throw new ArgumentNullException("sql", "SQL for DataSet Fill operation can not be null"); + } + } + protected virtual void ValidateFillArguments(DataSet dataSet, string sql) + { + if (dataSet == null) + { + throw new ArgumentNullException("dataSet", "DataSet argument can not be null"); + } + if (sql == null) + { + throw new ArgumentNullException("sql", "SQL for DataSet Fill operation can not be null"); + } + } + protected virtual void ValidateFillArguments(DataTable dataTable, string sql, + ITableMapping tableMapping) + { + ValidateFillArguments(dataTable, sql); + if (tableMapping == null) + { + throw new ArgumentNullException("tableMapping", "ITableMapping for DataTable Fill operations can not be null"); + } + } + + protected virtual void ValidateFillArguments(DataSet dataSet, string sql, + ITableMappingCollection tableMappingCollection) + { + ValidateFillArguments(dataSet, sql); + if (tableMappingCollection == null) + { + throw new ArgumentNullException("tableMappingCollection", "ITableMappingCollection for DataSet Fill operations can not be null"); + } + } + protected virtual void ValidateFillWithParameterArguments(DataTable dataTable, string sql, IDbParameters parameters) + { + ValidateFillArguments(dataTable, sql); + if (parameters == null) + { + throw new ArgumentNullException("parameters", "IDbParameters for DataTable Fill operations can not be null"); + } + } + protected virtual void ValidateFillWithParameterArguments(DataSet dataSet, string sql, IDbParameters parameters) + { + ValidateFillArguments(dataSet, sql); + if (parameters == null) + { + throw new ArgumentNullException("parameters", "IDbParameters for DataSet Fill operations can not be null"); + } + } + protected virtual void ValidateFillWithParameterArguments(DataTable dataTable, string sql, IDbParameters parameters, ITableMapping tableMapping) + { + ValidateFillWithParameterArguments(dataTable, sql, parameters); + if (tableMapping == null) + { + throw new ArgumentNullException("tableMapping", "ITableMappingCollection for DataTable Fill operations can not be null"); + } + } + protected virtual void ValidateFillWithParameterArguments(DataSet dataSet, string sql, IDbParameters parameters, ITableMappingCollection tableMapping) + { + ValidateFillWithParameterArguments(dataSet, sql, parameters); + if (tableMapping == null) + { + throw new ArgumentNullException("tableMapping", "ITableMappingCollection for DataSet Fill operations can not be null"); + } + } + + protected virtual void ValidateUpdateArguments(DataSet dataSet, ITableMappingCollection tableMapping) + { + if (dataSet == null) + { + throw new ArgumentNullException("dataSet", "DataSet argument can not be null for update operation"); + } + if (tableMapping == null) + { + throw new ArgumentNullException("tableMapping", "TableMappings for DataSet Update operation can not be null"); + } + } + + protected virtual void ValidateUpdateArguments(DataSet dataSet, string tableName) + { + if (dataSet == null) + { + throw new ArgumentNullException("dataSet", "DataSet argument can not be null for update operation"); + } + if (tableName == null) + { + throw new ArgumentNullException("tableName", "TableName for DataSet Update operation can not be null"); + } + } + + protected virtual void ValidateUpdateArguments(DataTable dataTable, ITableMapping tableMapping) + { + if (dataTable == null) + { + throw new ArgumentNullException("dataTable", "DataTable argument can not be null for DataTable update operation"); + } + if (tableMapping == null) + { + throw new ArgumentNullException("tableMapping", "TableMapping for DataTable Update operation can not be null"); + } + } + protected virtual void ValidateUpdateArguments(DataTable dataTable, string tableName) + { + if (dataTable == null) + { + throw new ArgumentNullException("dataTable", "DataTable argument can not be null for DataTable update operation"); + } + if (tableName == null) + { + throw new ArgumentNullException("tableName", "TableName for DataTable Update operation can not be null"); + } + } + protected virtual void ValidateUpdateWithCommandBuilderArguments(DataSet dataSet, ITableMappingCollection tableMapping, string selectSql) + { + if (dataSet == null) + { + throw new ArgumentNullException("dataSet", "DataSet can not be null for update operation"); + } + if (tableMapping == null) + { + throw new ArgumentNullException("tableMapping", "TableMapping for DataSet Update operation can not be null"); + } + if (selectSql == null) + { + throw new ArgumentNullException("selectSql", "SelectSql for DataSet Update operations can not be null"); + } + } + + protected virtual void ValidateUpdateWithCommandBuilderArguments(DataTable dataTable, string tableName, string selectSql) + { + if (dataTable == null) + { + throw new ArgumentNullException("dataTable", "DataTable can not be null for update operation"); + } + if (tableName == null) + { + throw new ArgumentNullException("tableName", "TableName for DataSet Update operation can not be null"); + } + if (selectSql == null) + { + throw new ArgumentNullException("selectSql", "SelectSql for DataSet Update operations can not be null"); + } + } + + protected virtual void ValidateUpdateWithCommandBuilderArguments(DataTable dataTable, ITableMapping tableMapping, string selectSql) + { + if (dataTable == null) + { + throw new ArgumentNullException("dataTable", "DataTable can not be null for update operation"); + } + if (tableMapping == null) + { + throw new ArgumentNullException("tableMapping", "TableMapping for DataSet Update operation can not be null"); + } + if (selectSql == null) + { + throw new ArgumentNullException("selectSql", "SelectSql for DataSet Update operations can not be null"); + } + } + protected virtual void ValidateUpdateWithCommandBuilderArguments(DataSet dataSet, string tableName, string selectSql) + { + if (dataSet == null) + { + throw new ArgumentNullException("dataSet", "DataSet can not be null for update operation"); + } + if (tableName == null) + { + throw new ArgumentNullException("tableName", "TableName for DataSet Update operation can not be null"); + } + if (selectSql == null) + { + throw new ArgumentNullException("selectSql", "SelectSql for DataSet Update operations can not be null"); + } + } + + + protected virtual ITableMappingCollection DoCreateMappingCollection(string[] dataSetTableNames) + { + DataTableMappingCollection mappingCollection; + + if (dataSetTableNames == null) + { + dataSetTableNames = new string[] { "Table" }; + } + foreach (string tableName in dataSetTableNames) + { + if (StringUtils.IsNullOrEmpty(tableName)) + { + throw new ArgumentException("TableName for DataTable mapping can not be null or empty"); + } + } + mappingCollection = new DataTableMappingCollection(); + int counter = 0; + foreach (string dataSetTableName in dataSetTableNames) + { + string sourceTableName; + if (counter == 0) + { + sourceTableName = "Table"; + } + else + { + sourceTableName = "Table" + ++counter; + } + mappingCollection.Add(sourceTableName, dataSetTableName); + } + return mappingCollection; + } + + #endregion + + #region Private DataAdapter Helper callbacks + + + + private class DataAdapterFillCallback : IDataAdapterCallback + { + private bool containsDataSet; + private DataSet dataSet; + private DataTable dataTable; + private CommandType commandType; + private string sql; + private ITableMappingCollection mappingCollection; + private IDataAdapterSetter dataAdapterSetter; + private IDataSetFillLifecycleProcessor fillLifecycleProcessor; + private IDbParameters parameters; + + public DataAdapterFillCallback(DataSet dataSet, + CommandType commandType, + string sql, + ITableMappingCollection mappingCollection, + IDataAdapterSetter dataAdapterSetter, + IDataSetFillLifecycleProcessor fillLifecycleProcessor, + IDbParameters parameters) + { + containsDataSet = true; + this.dataSet = dataSet; + this.commandType = commandType; + this.sql = sql; + this.mappingCollection = mappingCollection; + this.dataAdapterSetter = dataAdapterSetter; + this.fillLifecycleProcessor = fillLifecycleProcessor; + this.parameters = parameters; + } + + public DataAdapterFillCallback(DataTable dataTable, + CommandType commandType, + string sql, + ITableMappingCollection mappingCollection, + IDataAdapterSetter dataAdapterSetter, + IDataSetFillLifecycleProcessor fillLifecycleProcessor, + IDbParameters parameters) + { + containsDataSet = false; + this.dataTable = dataTable; + this.commandType = commandType; + this.sql = sql; + this.mappingCollection = mappingCollection; + this.dataAdapterSetter = dataAdapterSetter; + this.fillLifecycleProcessor = fillLifecycleProcessor; + this.parameters = parameters; + } + + + + public object DoInDataAdapter(IDbDataAdapter dataAdapter) + { + dataAdapter.SelectCommand.CommandType = commandType; + dataAdapter.SelectCommand.CommandText = sql; + //TODO investigate performance of cloning....would need to change signature to + // DataTableMapping[] otherwise... + foreach (DataTableMapping dataTableMapping in mappingCollection) + { + dataAdapter.TableMappings.Add(((ICloneable)dataTableMapping).Clone()); + } + + ParameterUtils.CopyParameters(dataAdapter.SelectCommand, parameters); + + //TODO Review these lifecycle hooks... + if (dataAdapterSetter != null) + { + dataAdapterSetter.SetValues(dataAdapter); + } + if (fillLifecycleProcessor != null) + { + fillLifecycleProcessor.BeforeFill(dataSet, dataAdapter.TableMappings); + } + + int returnVal; + if (containsDataSet) + { + returnVal = dataAdapter.Fill(dataSet); + } + else + { + //TODO should query metadata to see if supports filling dataTable directly. + if (dataAdapter is DbDataAdapter) + { + returnVal = ((DbDataAdapter)dataAdapter).Fill(dataTable); + } + else + { + //TODO could create DataSet and extract DataTable... for now just throw + throw new DataException("Provider does not support filling DataTable directly"); + } + } + + ParameterUtils.CopyParameters(parameters, dataAdapter.SelectCommand); + + if (fillLifecycleProcessor != null) + { + fillLifecycleProcessor.AfterFill(dataSet, dataAdapter.TableMappings); + } + return returnVal; + } + } + + + private class DataAdapterUpdateCallback : IDataAdapterCallback + { + private bool containsDataSet; + private DataSet dataSet; + private DataTable dataTable; + private ITableMappingCollection mappingCollection; + private IDbCommand insertCommand; + private IDbCommand updateCommand; + private IDbCommand deleteCommand; + private IDataAdapterSetter dataAdapterSetter; + + public DataAdapterUpdateCallback(DataSet dataSet, + ITableMappingCollection mappingCollection, + IDbCommand insertCommand, + IDbCommand updateCommand, + IDbCommand deleteCommand, + IDataAdapterSetter dataAdapterSetter) + { + containsDataSet = true; + this.dataSet = dataSet; + this.mappingCollection = mappingCollection; + this.insertCommand = insertCommand; + this.updateCommand = updateCommand; + this.deleteCommand = deleteCommand; + this.dataAdapterSetter = dataAdapterSetter; + } + + public DataAdapterUpdateCallback(DataTable dataTable, + ITableMappingCollection mappingCollection, + IDbCommand insertCommand, + IDbCommand updateCommand, + IDbCommand deleteCommand, + IDataAdapterSetter dataAdapterSetter) + { + containsDataSet = false; + this.dataTable = dataTable; + this.mappingCollection = mappingCollection; + this.insertCommand = insertCommand; + this.updateCommand = updateCommand; + this.deleteCommand = deleteCommand; + this.dataAdapterSetter = dataAdapterSetter; + } + #region IDataAdapterCallback Members + + public object DoInDataAdapter(IDbDataAdapter dataAdapter) + { + //TODO - did not make copies of parameters... + if (insertCommand == null && updateCommand == null && deleteCommand == null) + { + throw new ArgumentException("All commands for DataSet Update operation are null"); + } + + if (insertCommand != null) + { + dataAdapter.InsertCommand = insertCommand; + ApplyConnectionAndTx(dataAdapter.InsertCommand, dataAdapter.SelectCommand); + } + if (updateCommand != null) + { + dataAdapter.UpdateCommand = updateCommand; + ApplyConnectionAndTx(dataAdapter.UpdateCommand, dataAdapter.SelectCommand); + } + if (deleteCommand != null) + { + dataAdapter.DeleteCommand = deleteCommand; + ApplyConnectionAndTx(dataAdapter.DeleteCommand, dataAdapter.SelectCommand); + } + foreach (DataTableMapping dataTableMapping in mappingCollection) + { + dataAdapter.TableMappings.Add(((ICloneable)dataTableMapping).Clone()); + } + + if (dataAdapterSetter != null) + { + dataAdapterSetter.SetValues(dataAdapter); + } + + if (containsDataSet) + { + return dataAdapter.Update(dataSet); + } + else + { + //TODO should query metadata to see if supports filling dataTable directly. + if (dataAdapter is DbDataAdapter) + { + return ((DbDataAdapter)dataAdapter).Update(dataTable); + } + else + { + //TODO could create DataSet and extract DataTable... for now just throw + throw new DataException("Provider does not support filling DataTable directly"); + } + } + } + + private static void ApplyConnectionAndTx(IDbCommand dbCommand, IDbCommand sourceCommand) + { + dbCommand.Connection = sourceCommand.Connection; + dbCommand.Transaction = sourceCommand.Transaction; + } + + #endregion + + } + + private class DataAdapterUpdateWithCommandBuilderCallback : IDataAdapterCallback + { + private bool containsDataSet; + private DataSet dataSet; + private DataTable dataTable; + private object commandBuilder; + private ITableMappingCollection mappingCollection; + private CommandType selectCommandType; + private string selectSql; + private IDbParameters selectParameters; + private IDataAdapterSetter dataAdapterSetter; + + public DataAdapterUpdateWithCommandBuilderCallback(DataSet dataSet, + object commandBuilder, + ITableMappingCollection mappingCollection, + CommandType selectCommandType, + string selectSql, + IDbParameters selectParameters, + IDataAdapterSetter dataAdapterSetter) + { + containsDataSet = true; + this.dataSet = dataSet; + this.commandBuilder = commandBuilder; + this.mappingCollection = mappingCollection; + this.selectCommandType = selectCommandType; + this.selectSql = selectSql; + this.selectParameters = selectParameters; + this.dataAdapterSetter = dataAdapterSetter; + } + + public DataAdapterUpdateWithCommandBuilderCallback(DataTable dataTable, + object commandBuilder, + ITableMappingCollection mappingCollection, + CommandType selectCommandType, + string selectSql, + IDbParameters selectParameters, + IDataAdapterSetter dataAdapterSetter) + { + containsDataSet = false; + this.dataTable = dataTable; + this.commandBuilder = commandBuilder; + this.mappingCollection = mappingCollection; + this.selectCommandType = selectCommandType; + this.selectSql = selectSql; + this.selectParameters = selectParameters; + this.dataAdapterSetter = dataAdapterSetter; + } + #region IDataAdapterCallback Members + + public object DoInDataAdapter(IDbDataAdapter dataAdapter) + { + dataAdapter.SelectCommand.CommandType = selectCommandType; + dataAdapter.SelectCommand.CommandText = selectSql; + ParameterUtils.CopyParameters(dataAdapter.SelectCommand, selectParameters); + + + foreach (DataTableMapping dataTableMapping in mappingCollection) + { + dataAdapter.TableMappings.Add(((ICloneable)dataTableMapping).Clone()); + } + + if (dataAdapterSetter != null) + { + dataAdapterSetter.SetValues(dataAdapter); + } + + //TODO consider refactoring to put this inside IDbMetadata + PropertyInfo selectCommandProperty = commandBuilder.GetType().GetProperty("DataAdapter", + BindingFlags.DeclaredOnly | + BindingFlags.GetProperty | + BindingFlags.Public | + BindingFlags.Instance + ); + + selectCommandProperty.SetValue(commandBuilder, dataAdapter, null); + + ParameterUtils.CopyParameters(selectParameters, dataAdapter.SelectCommand); + if (containsDataSet) + { + return dataAdapter.Update(dataSet); + } + else + { + //TODO should query metadata to see if supports filling dataTable directly. + if (dataAdapter is DbDataAdapter) + { + return ((DbDataAdapter)dataAdapter).Update(dataTable); + } + else + { + //TODO could create DataSet and extract DataTable... for now just throw + throw new DataException("Provider does not support filling DataTable directly"); + } + } + } + + private static void ApplyConnectionAndTx(IDbCommand dbCommand, IDbCommand sourceCommand) + { + dbCommand.Connection = sourceCommand.Connection; + dbCommand.Transaction = sourceCommand.Transaction; + } + + #endregion + + } + #endregion + + #region Private Helper Classes + + private class AdoStoredProcedureScalarCommandCallback : ICommandCallback + { + + public object DoInCommand(IDbCommand command) + { + IDictionary returnedResults = new Hashtable(); + object scalar = command.ExecuteScalar(); + ParameterUtils.ExtractOutputParameters(returnedResults, command); + returnedResults.Add("scalar", scalar); + return returnedResults; + + } + } + + private class AdoNonQueryWithOutputParamsCommandCallback : ICommandCallback + { + + public object DoInCommand(IDbCommand command) + { + IDictionary returnedResults = new Hashtable(); + int rowsAffected = command.ExecuteNonQuery(); + ParameterUtils.ExtractOutputParameters(returnedResults, command); + returnedResults.Add("rowsAffected", rowsAffected); + return returnedResults; + + } + } + + + private class AdoResultProcessorsQueryCommandCallback : ICommandCallback + { + private AdoTemplate adoTemplate; + private IList namedResultSetProcessors; + //private IDbParameters declaredParameters; + + public AdoResultProcessorsQueryCommandCallback(AdoTemplate adoTemplate, IList namedResultSetProcessors) + { + //AssertUtils.ArgumentHasLength(namedResultSetProcessors, "namedResultSetProcessors"); + + this.adoTemplate = adoTemplate; + this.namedResultSetProcessors = namedResultSetProcessors; + //this.declaredParameters = declaredParameters; + } + + public object DoInCommand(IDbCommand command) + { + IDictionary returnedResults = new Hashtable(); + int resultSetIndex = 0; + IDataReader reader = null; + try + { + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + + //TODO On >= .NET 2.0 platforms make use of DbDataReader.HasRows property to + // see if there is a result set. now currently assuming matching + // NamedResultSetProcessor/ResultSet pairs. + + do + { + if (namedResultSetProcessors.Count == 0) + { + //We could just have output parameters and/or return value, that is, no result sets + //If we didn't register a result set processor, it is likely that a result set wasn't expected. + break; + } + NamedResultSetProcessor namedResultSetProcessor = null; + try + { + namedResultSetProcessor + = namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor; + //Will only have possibility of run-time type error if using QueryWithCommandCreator + if (namedResultSetProcessor == null) + { + LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex + + ", is not of expected type NamedResultSetProcessor. Type = " + + namedResultSetProcessors[resultSetIndex].GetType() + + "; Skipping processing for this result set."); + continue; + } + } + catch (IndexOutOfRangeException e) + { + LOG.Error("No NamedResultSetProcessor associated with result set index " + resultSetIndex, e); + continue; + } + catch (ArgumentOutOfRangeException e) + { + LOG.Error("No NamedResultSetProcessor associated with result set index " + resultSetIndex, e); + continue; + } + + + string parameterName = namedResultSetProcessor.Name; + if (namedResultSetProcessor.ResultSetProcessor is IResultSetExtractor) + { + IResultSetExtractor rse = (IResultSetExtractor)namedResultSetProcessor.ResultSetProcessor; + object result = rse.ExtractData(reader); + returnedResults.Add(parameterName, result); + } + else if (namedResultSetProcessor.ResultSetProcessor is IRowMapper) + { + IRowMapper rowMapper = (IRowMapper)namedResultSetProcessor.ResultSetProcessor; + object result = (new RowMapperResultSetExtractor(rowMapper)).ExtractData(reader); + returnedResults.Add(parameterName, result); + } + else if (namedResultSetProcessor.ResultSetProcessor is IRowCallback) + { + IRowCallback rowCallback = (IRowCallback)namedResultSetProcessor.ResultSetProcessor; + (new RowCallbackResultSetExtractor(rowCallback)).ExtractData(reader); + returnedResults.Add(parameterName, "ResultSet returned was processed by an IRowCallback"); + } + resultSetIndex++; + + } while (reader.NextResult()); + } + finally + { + AdoUtils.CloseReader(reader); + } + ParameterUtils.ExtractOutputParameters(returnedResults, command); + return returnedResults; + } + } + + + private class DeriveParametersCommandCallback : ICommandCallback, ICommandTextProvider + { + private IDbProvider provider; + private string procedureName; + private bool includeReturnParameter; + + public DeriveParametersCommandCallback(IDbProvider provider, string procedureName, bool includeReturnParameter) + { + this.provider = provider; + this.procedureName = procedureName; + this.includeReturnParameter = includeReturnParameter; + } + + public string CommandText + { + get { return procedureName; } + } + + public object DoInCommand(IDbCommand command) + { + command.CommandType = CommandType.StoredProcedure; + command.CommandText = procedureName; + + //The DeriveParameter is static in all providers...it seems.... + Type commandBuilderType = provider.DbMetadata.CommandBuilderType; + commandBuilderType.InvokeMember(provider.DbMetadata.CommandBuilderDeriveParametersMethod.Name, + BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, + new object[] { command }); + + if (command.Parameters.Count > 0) + { + IDataParameter param = (IDataParameter)command.Parameters[0]; + if (param.Direction == ParameterDirection.ReturnValue) + { + if (!includeReturnParameter) + { + command.Parameters.RemoveAt(0); + } + } + } + return ParameterUtils.CloneParameters(command); + } + } + + private class AdoRowCallbackCommandCallback : ICommandCallback + { + private AdoTemplate adoTemplate; + private IRowCallback rowCallback; + private IDictionary returnedParameters; + + public AdoRowCallbackCommandCallback(AdoTemplate adoTemplate, IRowCallback rowCallback, IDictionary returnedParameters) + { + this.adoTemplate = adoTemplate; + this.rowCallback = rowCallback; + this.returnedParameters = returnedParameters; + } + + public object DoInCommand(IDbCommand command) + { + //Extract the single returned result set + IDataReader reader = null; + try + { + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + rowCallback.ProcessRow(reader); + } + finally + { + AdoUtils.CloseReader(reader); + } + ParameterUtils.ExtractOutputParameters(returnedParameters, command); + return null; + } + } + private class AdoResultSetExtractorWithOutputParamsCommandCallback : ICommandCallback + { + private AdoTemplate adoTemplate; + private IResultSetExtractor rse; + private IDictionary returnedParameters; + + public AdoResultSetExtractorWithOutputParamsCommandCallback(AdoTemplate adoTemplate, IResultSetExtractor rse, IDictionary returnedParameters) + { + this.adoTemplate = adoTemplate; + this.rse = rse; + this.returnedParameters = returnedParameters; + } + + public object DoInCommand(IDbCommand command) + { + object returnVal = null; + //Extract the single returned result set + IDataReader reader = null; + try + { + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + returnVal = rse.ExtractData(reader); + } + finally + { + AdoUtils.CloseReader(reader); + } + ParameterUtils.ExtractOutputParameters(returnedParameters, command); + return returnVal; + } + } + + + private class AdoRowMapperQueryCommandCallback : ICommandCallback + { + private AdoTemplate adoTemplate; + private IRowMapper rowMapper; + //private IDbParameters declaredParameters; + private IDictionary returnedParameters; + + public AdoRowMapperQueryCommandCallback(AdoTemplate adoTemplate, IRowMapper rowMapper, IDictionary returnedParameters) + { + this.adoTemplate = adoTemplate; + this.rowMapper = rowMapper; + //this.declaredParameters = declaredParameters; + this.returnedParameters = returnedParameters; + } + + public object DoInCommand(IDbCommand command) + { + IList objectList = null; + //Extract the single returned result set + IDataReader reader = null; + try + { + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + RowMapperResultSetExtractor rse = new RowMapperResultSetExtractor(rowMapper, 1); + objectList = (IList)rse.ExtractData(reader); + } + finally + { + AdoUtils.CloseReader(reader); + } + ParameterUtils.ExtractOutputParameters(returnedParameters, command); + return objectList; + } + } + + + private class ExecuteNonQueryCallbackWithParameters : ICommandCallback, ICommandTextProvider + { + private CommandType commandType; + private string commandText; + private IDbParameters parameters; + + public ExecuteNonQueryCallbackWithParameters(CommandType commandType, string commandText, IDbParameters dbParameters) + { + this.commandType = commandType; + this.commandText = commandText; + parameters = dbParameters; + } + + public string CommandText + { + get { return commandText; } + } + + public Object DoInCommand(IDbCommand command) + { + command.CommandType = commandType; + command.CommandText = commandText; + ParameterUtils.CopyParameters(command, parameters); + Object returnValue = command.ExecuteNonQuery(); + ParameterUtils.CopyParameters(parameters, command); + return returnValue; + } + + } + + + private class ExecuteNonQueryCallbackWithCommandSetter : ICommandCallback, ICommandTextProvider + { + private CommandType commandType; + private string commandText; + private ICommandSetter commandSetter; + + public ExecuteNonQueryCallbackWithCommandSetter(CommandType commandType, string commandText, ICommandSetter commandSetter) + { + this.commandType = commandType; + this.commandText = commandText; + this.commandSetter = commandSetter; + } + + public string CommandText + { + get { return commandText; } + } + + public Object DoInCommand(IDbCommand command) + { + command.CommandType = commandType; + command.CommandText = commandText; + if (commandSetter != null) + { + commandSetter.SetValues(command); + } + Object rowsAffected = command.ExecuteNonQuery(); + if (LOG.IsDebugEnabled) + { + LOG.Debug("ExecuteNonQuery affected " + rowsAffected + " rows"); + } + return rowsAffected; + } + + } + + + private class ExecuteScalarCallbackWithParameters : ICommandCallback, ICommandTextProvider + { + private CommandType commandType = CommandType.Text; + private string commandText; + private IDbParameters parameters; + + public ExecuteScalarCallbackWithParameters(CommandType cmdType, string cmdText, IDbParameters dbParameters) + { + commandType = cmdType; + commandText = cmdText; + parameters = dbParameters; + } + + public string CommandText + { + get { return commandText; } + } + + public Object DoInCommand(IDbCommand command) + { + command.CommandType = commandType; + command.CommandText = commandText; + ParameterUtils.CopyParameters(command, parameters); + Object returnValue = command.ExecuteScalar(); + ParameterUtils.CopyParameters(parameters, command); + return returnValue; + } + } + + private class ExecuteScalarCallbackWithCommandSetter : ICommandCallback, ICommandTextProvider + { + private CommandType commandType; + private string commandText; + private ICommandSetter commandSetter; + + public ExecuteScalarCallbackWithCommandSetter(CommandType commandType, string commandText, ICommandSetter commandSetter) + { + this.commandType = commandType; + this.commandText = commandText; + this.commandSetter = commandSetter; + } + + public string CommandText + { + get { return commandText; } + } + + public Object DoInCommand(IDbCommand command) + { + command.CommandType = commandType; + command.CommandText = commandText; + if (commandSetter != null) + { + commandSetter.SetValues(command); + } + Object returnValue = command.ExecuteScalar(); + if (LOG.IsDebugEnabled) + { + LOG.Debug("ExecuteScalar return value = " + returnValue); + } + return returnValue; + } + + } + + + private class ExecuteCommandCallbackUsingDelegate : ICommandCallback, ICommandTextProvider + { + private CommandDelegate del; + private string commandText; + + public ExecuteCommandCallbackUsingDelegate(CommandDelegate d) + { + del = d; + } + + public string CommandText + { + get { return commandText; } + } + + public Object DoInCommand(IDbCommand command) + { + try + { + return del(command); + } + catch (Exception e) + { + e.GetType(); + commandText = command.CommandText; + throw; + } + } + } + + + + private class QueryCallbackWithCommandSetter : ICommandCallback, ICommandTextProvider + { + private AdoTemplate adoTemplate; + private IResultSetExtractor rse; + private ResultSetExtractorDelegate resultSetExtractorDelegate; + private ICommandSetter commandSetter; + private CommandType commandType; + private string commandText; + + public QueryCallbackWithCommandSetter(AdoTemplate adoTemplate, CommandType cmdType, string cmdText, IResultSetExtractor rse, ICommandSetter commandSetter) + { + this.adoTemplate = adoTemplate; + commandType = cmdType; + commandText = cmdText; + this.rse = rse; + this.commandSetter = commandSetter; + } + + public QueryCallbackWithCommandSetter(AdoTemplate adoTemplate, CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractorDelegate, ICommandSetter commandSetter) + { + this.adoTemplate = adoTemplate; + commandType = cmdType; + commandText = cmdText; + this.resultSetExtractorDelegate = resultSetExtractorDelegate; + this.commandSetter = commandSetter; + } + + public string CommandText + { + get { return commandText; } + } + + public object DoInCommand(IDbCommand command) + { + IDataReader reader = null; + try + { + command.CommandType = commandType; + command.CommandText = commandText; + if (commandSetter != null) + { + commandSetter.SetValues(command); + } + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + if (rse != null) + { + return rse.ExtractData(reader); + } + else + { + return resultSetExtractorDelegate(reader); + } + } + finally + { + AdoUtils.CloseReader(reader); + } + } + + } + + private class QueryCallback : ICommandCallback, ICommandTextProvider + { + private AdoTemplate adoTemplate; + private IResultSetExtractor rse; + private ResultSetExtractorDelegate resultSetExtractorDelegate; + private CommandType commandType; + private string commandText; + private IDbParameters parameters; + + public QueryCallback(AdoTemplate adoTemplate, CommandType cmdType, string cmdText, IResultSetExtractor rse, IDbParameters dbParameters) + { + this.adoTemplate = adoTemplate; + commandType = cmdType; + commandText = cmdText; + this.rse = rse; + parameters = dbParameters; + } + + public QueryCallback(AdoTemplate adoTemplate, CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractorDelegate, IDbParameters dbParameters) + { + this.adoTemplate = adoTemplate; + commandType = cmdType; + commandText = cmdText; + this.resultSetExtractorDelegate = resultSetExtractorDelegate; + parameters = dbParameters; + } + + + public string CommandText + { + get { return commandText; } + } + + public object DoInCommand(IDbCommand command) + { + IDataReader reader = null; + try + { + command.CommandType = commandType; + command.CommandText = commandText; + ParameterUtils.CopyParameters(command, parameters); + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + object returnValue; + if (rse != null) + { + returnValue = rse.ExtractData(reader); + } + else + { + returnValue = resultSetExtractorDelegate(reader); + } + return returnValue; + } + finally + { + AdoUtils.CloseReader(reader); + ParameterUtils.CopyParameters(parameters, command); + } + } + } + + #endregion + + /// + /// Creates the data reader wrapper for use in AdoTemplate callback methods. + /// + /// The reader to wrap. + /// The data reader used in AdoTemplate callbacks + public override IDataReader CreateDataReaderWrapper(IDataReader readerToWrap) + { + if (dataReaderWrapperType != null && newDataReaderWrapper != null) + { + IDataReaderWrapper wrapper = (IDataReaderWrapper) newDataReaderWrapper.Invoke(ObjectUtils.EmptyObjects); + wrapper.WrappedReader = readerToWrap; + return wrapper; + } + else + { + return readerToWrap; + } + } + } + +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Core/RowMapperResultSetExtractor.cs b/src/Spring/Spring.Data/Data/Core/RowMapperResultSetExtractor.cs new file mode 100644 index 00000000..67195813 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Core/RowMapperResultSetExtractor.cs @@ -0,0 +1,137 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Collections; +using Spring.Data.Support; + +#endregion + +namespace Spring.Data.Core +{ + /// + /// Adapter implementation of the ResultSetExtractor interface that delegates + /// to a RowMapper which is supposed to create an object for each row. + /// Each object is added to the results List of this ResultSetExtractor. + /// + /// + /// Useful for the typical case of one object per row in the database table. + /// The number of entries in the results list will match the number of rows. + ///

    + /// Note that a RowMapper object is typically stateless and thus reusable; + /// just the RowMapperResultSetExtractor adapter is stateful. + ///

    + ///

    + /// As an alternative consider subclassing MappingAdoQuery from the + /// Spring.Data.Objects namespace: Instead of working with separate + /// AdoTemplate and IRowMapper objects you can have executable + /// query objects (containing row-mapping logic) there. + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: RowMapperResultSetExtractor.cs,v 1.1 2007/08/03 19:51:11 markpollack Exp $ + public class RowMapperResultSetExtractor : IResultSetExtractor + { + #region Fields + + private IRowMapper rowMapper; + + private RowMapperDelegate rowMapperDelegate; + + private int rowsExpected; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public RowMapperResultSetExtractor(IRowMapper rowMapper) : this(rowMapper,0, null) + { + } + + public RowMapperResultSetExtractor(IRowMapper rowMapper, int rowsExpected) : this(rowMapper, rowsExpected, null) + { + } + public RowMapperResultSetExtractor(IRowMapper rowMapper, int rowsExpected, IDataReaderWrapper dataReaderWrapper) + { + //TODO use datareaderwrapper + if (rowMapper == null) + { + throw new ArgumentNullException("rowMapper"); + } + this.rowMapper = rowMapper; + this.rowsExpected = rowsExpected; + } + + public RowMapperResultSetExtractor(RowMapperDelegate rowMapperDelegate) + : this(rowMapperDelegate, 0, null) + { + } + + public RowMapperResultSetExtractor(RowMapperDelegate rowMapperDelegate, int rowsExpected) + : this(rowMapperDelegate, rowsExpected, null) + { + } + public RowMapperResultSetExtractor(RowMapperDelegate rowMapperDelegate, int rowsExpected, IDataReaderWrapper dataReaderWrapper) + { + //TODO use datareaderwrapper + if (rowMapperDelegate == null) + { + throw new ArgumentNullException("rowMapperDelegate"); + } + this.rowMapperDelegate = rowMapperDelegate; + this.rowsExpected = rowsExpected; + } + + #endregion + + #region IResultSetExtractor Members + + public object ExtractData(System.Data.IDataReader reader) + { + // Use the more efficient collection if we know how many rows to expect: + // ArrayList in case of a known row count, LinkedList if unknown + IList results = (rowsExpected > 0) ? (IList) new ArrayList(rowsExpected) : new LinkedList(); + int rowNum = 0; + if (rowMapper != null) + { + while (reader.Read()) + { + results.Add(rowMapper.MapRow(reader, rowNum++)); + } + } + else + { + while (reader.Read()) + { + results.Add(rowMapperDelegate(reader, rowNum++)); + } + } + + return results; + } + + #endregion + } +} diff --git a/src/Spring/Spring.Data/Data/Core/ServiceDomainPlatformTransactionManager.cs b/src/Spring/Spring.Data/Data/Core/ServiceDomainPlatformTransactionManager.cs new file mode 100644 index 00000000..532afacb --- /dev/null +++ b/src/Spring/Spring.Data/Data/Core/ServiceDomainPlatformTransactionManager.cs @@ -0,0 +1,402 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if (!NET_1_0) + +#region Imports + +using System; +using System.EnterpriseServices; +using System.Threading; +using Spring.Data.Support; +using Spring.Objects.Factory; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.Core +{ + /// + /// Transaction Manager that uses EnterpriseServices to access the + /// MS-DTC. It requires the support of 'Services without Components' + /// functionality which is available on Win 2003 and Win XP SP2. + /// + /// Mark Pollack (.NET) + /// $Id: ServiceDomainPlatformTransactionManager.cs,v 1.6 2007/12/04 19:43:16 markpollack Exp $ + public class ServiceDomainPlatformTransactionManager : AbstractPlatformTransactionManager, IInitializingObject + { + private IServiceDomainAdapter txAdapter; + + private bool trackingEnabled = true; + private string trackingAppName = "Spring.NET"; + private string trackingComponentName = "ServiceDomainPlatformTransactionManager"; + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public ServiceDomainPlatformTransactionManager() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// This is indented only for unit testing purposes and should not be + /// called by production application code. + /// The tx adapter. + public ServiceDomainPlatformTransactionManager(IServiceDomainAdapter txAdapter) + { + this.txAdapter = txAdapter; + } + + #endregion + + + /// + /// Gets or sets a value indicating whether tracking is enabled. + /// + /// true if tracking is enabled; otherwise, false. + public bool TrackingEnabled + { + get { return trackingEnabled; } + set { trackingEnabled = value; } + } + + /// + /// Gets or sets a text string that corresponds to the application ID under which tracker information is reported. + /// The default value is 'Spring.NET' + /// + /// The name of the tracking app. + public string TrackingAppName + { + get { return trackingAppName; } + set { trackingAppName = value; } + } + + /// + /// Gets or sets a text string that corresponds to the context name under which tracker information is reported. + /// + /// The name of the tracking component. + public string TrackingComponentName + { + get { return trackingComponentName; } + set { trackingComponentName = value; } + } + + public void AfterPropertiesSet() + { + //TODO decide what should be validated for advanced configurations. + } + + protected override object DoGetTransaction() + { + ServiceDomainTransactionObject txObject = new ServiceDomainTransactionObject(); + if (txAdapter != null) + { + txObject.ServiceDomainAdapter = txAdapter; + } + return txObject; + } + + + + protected override bool IsExistingTransaction(object transaction) + { + ServiceDomainTransactionObject txObject = (ServiceDomainTransactionObject)transaction; + if (txObject.ServiceDomainAdapter.IsInTransaction) + { + return true; + } + else + { + return false; + } + } + + protected override void DoBegin(object transaction, ITransactionDefinition definition) + { + try + { + ServiceDomainTransactionObject txObject = (ServiceDomainTransactionObject)transaction; + + DoServiceDomainBegin(txObject, definition); + } + catch (PlatformNotSupportedException ex) + { + throw new TransactionSystemException("ServiceDomain failure on begin of transaction. Platform does not support EnterpriseServices 'Services without Components'", ex); + } + catch (Exception e) + { + throw new CannotCreateTransactionException("ServiceDomain failure on begin of transaction", e); + } + } + + private void DoServiceDomainBegin(ServiceDomainTransactionObject serviceDomainTxObject, ITransactionDefinition definition) + { + SimpleServiceConfig serviceConfig = CreateServiceConfig(definition); + //The context is created when we call Enter. + serviceDomainTxObject.ServiceDomainAdapter.Enter(serviceConfig); + if (log.IsDebugEnabled) + { + log.Debug("Context created. TransactionId = " + ContextUtil.TransactionId + + ", ActivityId = " + ContextUtil.ActivityId); + } + } + + protected override object DoSuspend(object transaction) + { + // Passing the current transaction object, literally an 'object' as the 'suspended resource', + // even though it is not used just to avoid passing null + // ServiceDomainPlatformTransactionManager is not binding any resources to the local thread, instead delegating to + // System.EnterpriseServices to handle thread local resources. + return transaction; + } + + protected override void DoResume(object transaction, object suspendedResources) + { + } + + private SimpleServiceConfig CreateServiceConfig(ITransactionDefinition definition) + { + SimpleServiceConfig serviceConfig = new SimpleServiceConfig(); + + //TODO investigate BringYourOwnTransaction and TipUrl properties of ServiceConfig + serviceConfig.TransactionDescription = definition.Name; + + serviceConfig.TrackingEnabled = TrackingEnabled; + serviceConfig.TrackingAppName = TrackingAppName; + serviceConfig.TrackingComponentName = TrackingComponentName; + + ApplyPropagationBehavior(serviceConfig, definition); + + ApplyIsolationLevel(serviceConfig, definition); + + // infinite==-1 would cause transactions to be aborted immediately! + if(definition.TransactionTimeout != Timeout.Infinite) + { + serviceConfig.TransactionTimeout = definition.TransactionTimeout; + } + return serviceConfig; + } + + protected void ApplyIsolationLevel(SimpleServiceConfig serviceConfig, ITransactionDefinition definition) + { + switch (definition.TransactionIsolationLevel) + { + + case System.Data.IsolationLevel.Chaos: + if (log.IsInfoEnabled) + { + log.Info("IsolationLevel Chaos does not have a direct counterpart in EnterpriseServices, using Any"); + } + serviceConfig.IsolationLevel = TransactionIsolationLevel.Any; + break; + case System.Data.IsolationLevel.ReadCommitted: + serviceConfig.IsolationLevel = TransactionIsolationLevel.ReadCommitted; + break; + case System.Data.IsolationLevel.ReadUncommitted: + serviceConfig.IsolationLevel = TransactionIsolationLevel.ReadUncommitted; + break; + case System.Data.IsolationLevel.RepeatableRead: + serviceConfig.IsolationLevel = TransactionIsolationLevel.RepeatableRead; + break; + case System.Data.IsolationLevel.Serializable: + serviceConfig.IsolationLevel = TransactionIsolationLevel.Serializable; + break; +#if NET_2_0 + case System.Data.IsolationLevel.Snapshot: + if (log.IsInfoEnabled) + { + log.Info("IsolationLevel Snapshot does not have a direct counterpart in EnterpriseServices, using ReadCommitted. Introduced in SqlServer 2005. Consider using System.Transactions for transaction management instead."); + } + serviceConfig.IsolationLevel = TransactionIsolationLevel.ReadCommitted; //err on the side of consistency + break; +#endif + case System.Data.IsolationLevel.Unspecified: + serviceConfig.IsolationLevel = TransactionIsolationLevel.Any; + break; + } + + } + + protected void ApplyPropagationBehavior(SimpleServiceConfig serviceConfig, ITransactionDefinition definition) + { + if (definition.PropagationBehavior == TransactionPropagation.Required) + { + serviceConfig.TransactionOption = TransactionOption.Required; + } + else if (definition.PropagationBehavior == TransactionPropagation.RequiresNew) + { + serviceConfig.TransactionOption = TransactionOption.RequiresNew; + } + else if (definition.PropagationBehavior == TransactionPropagation.Supports) + { + serviceConfig.TransactionOption = TransactionOption.Supported; + } + else if (definition.PropagationBehavior == TransactionPropagation.NotSupported) + { + serviceConfig.TransactionOption = TransactionOption.NotSupported; + } + else if (definition.PropagationBehavior == TransactionPropagation.Never) + { + //TODO check the validity of this mapping + serviceConfig.TransactionOption = TransactionOption.Disabled; + } else + { + //TODO Should we throw an exception instead? + log.Warn("The requested transaction propagation option " + + definition.PropagationBehavior + " is not supported. " + + "Defaulting to Never(Disabled) "); + } + } + + + + + protected override void DoCommit(DefaultTransactionStatus status) + { + ServiceDomainTransactionObject txObject = (ServiceDomainTransactionObject) status.Transaction; + bool globalRollbackOnly = status.GlobalRollbackOnly; + try + { + if (txObject.ServiceDomainAdapter.IsInTransaction) + { + + if (txObject.ServiceDomainAdapter.MyTransactionVote == TransactionVote.Commit) + { + txObject.ServiceDomainAdapter.SetComplete(); + } + else + { + txObject.ServiceDomainAdapter.SetAbort(); + } + } + TransactionStatus serviceDomainTxstatus = txObject.ServiceDomainAdapter.Leave(); + if (log.IsDebugEnabled) + { + log.Debug("ServiceDomain Transaction Status upon leaving ServiceDomain = " + serviceDomainTxstatus); + } + txObject.TransactionStatus = serviceDomainTxstatus; + if (!globalRollbackOnly && serviceDomainTxstatus == TransactionStatus.Aborted) + { + throw new UnexpectedRollbackException("Transaction unexpectedly rolled-back (maybe due to a timeout)"); + } + } + catch (PlatformNotSupportedException ex) + { + throw new TransactionSystemException("Failure on Commit. Platform does not support EnterpriseServices 'Services without Components'", ex); + } + catch (Exception e) + { + throw new TransactionSystemException("Failure upon Leaving ServiceDomain (for Commit)", e); + } + + } + + protected override void DoRollback(DefaultTransactionStatus status) + { + ServiceDomainTransactionObject txObject = (ServiceDomainTransactionObject)status.Transaction; + if (txObject.ServiceDomainAdapter.IsInTransaction) + { + try + { + txObject.ServiceDomainAdapter.SetAbort(); + txObject.ServiceDomainAdapter.Leave(); + } + catch (PlatformNotSupportedException ex) + { + throw new TransactionSystemException("Failure on Rollback. Platform does not support EnterpriseServices 'Services without Components'", ex); + } + catch (Exception e) + { + throw new Spring.Transaction.TransactionSystemException("Failure upon Leaving ServiceDomain (for Rollback)", e); + } + } + } + + protected override void DoSetRollbackOnly(DefaultTransactionStatus status) + { + ServiceDomainTransactionObject txObject = (ServiceDomainTransactionObject)status.Transaction; + if (status.Debug) + { + log.Debug("Setting transaction rollback-only"); + } + try + { + txObject.ServiceDomainAdapter.MyTransactionVote = TransactionVote.Abort; + } + catch (Exception ex) + { + throw new TransactionSystemException("Failure on System.Transactions.Transaction.Current.Rollback", ex); + } + + } + + protected override bool ShouldCommitOnGlobalRollbackOnly + { + get { return true; } + } + + + public class ServiceDomainTransactionObject : ISmartTransactionObject + { + private TransactionStatus transactionStatus; + private IServiceDomainAdapter serviceDomainAdapter; + + + public ServiceDomainTransactionObject() + { + serviceDomainAdapter = new DefaultServiceDomainAdapter(); + } + + public IServiceDomainAdapter ServiceDomainAdapter + { + get { return serviceDomainAdapter; } + set { serviceDomainAdapter = value; } + } + + public TransactionStatus TransactionStatus + { + get { return transactionStatus; } + set { transactionStatus = value; } + } + + public bool RollbackOnly + { + get + { + if ( serviceDomainAdapter.MyTransactionVote == TransactionVote.Abort) + { + return true; + } + else + { + return false; + } + } + } + } + } +} + +#endif // (!NET_1_0) diff --git a/src/Spring/Spring.Data/Data/Core/TxScopeTransactionManager.cs b/src/Spring/Spring.Data/Data/Core/TxScopeTransactionManager.cs new file mode 100644 index 00000000..342cb01f --- /dev/null +++ b/src/Spring/Spring.Data/Data/Core/TxScopeTransactionManager.cs @@ -0,0 +1,284 @@ +#if NET_2_0 + +#region License + +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Transactions; +using Spring.Data.Support; +using Spring.Objects.Factory; +using Spring.Transaction; +using Spring.Transaction.Support; + +namespace Spring.Data.Core +{ + /// + /// TransactionManager that uses TransactionScope provided by System.Transactions. + /// + /// Mark Pollack (.NET) + /// $Id: TxScopeTransactionManager.cs,v 1.5 2007/12/04 19:43:16 markpollack Exp $ + public class TxScopeTransactionManager : AbstractPlatformTransactionManager, IInitializingObject + { + private ITransactionScopeAdapter txAdapter; + + /// + /// Initializes a new instance of the class. + /// + public TxScopeTransactionManager() + { + + } + /// + /// Initializes a new instance of the class. + /// + /// This is indented only for unit testing purposes and should not be + /// called by production application code. + /// The tx adapter. + public TxScopeTransactionManager(ITransactionScopeAdapter txAdapter) + { + this.txAdapter = txAdapter; + } + + #region IInitializingObject Members + + /// + /// No-op initialization + /// + public void AfterPropertiesSet() + { + // placeholder for more advanced configurations. + } + + #endregion + + protected override object DoGetTransaction() + { + PromotableTxScopeTransactionObject txObject = new PromotableTxScopeTransactionObject(); + + if (txAdapter != null) + { + txObject.TxScopeAdapter = txAdapter; + } + return txObject; + } + + protected override bool IsExistingTransaction(object transaction) + { + PromotableTxScopeTransactionObject txObject = + (PromotableTxScopeTransactionObject)transaction; + return txObject.TxScopeAdapter.IsExistingTransaction; + } + + protected override void DoBegin(object transaction, Spring.Transaction.ITransactionDefinition definition) + { + PromotableTxScopeTransactionObject txObject = + (PromotableTxScopeTransactionObject)transaction; + try + { + DoTxScopeBegin(txObject, definition); + } + catch (Exception e) + { + throw new CannotCreateTransactionException("Transaction Scope failure on begin", e); + } + } + + protected override object DoSuspend(object transaction) + { + // Passing the current TxScopeAdapter as the 'suspended resource', even though it is not used just to avoid passing null + // TxScopeTransactionManager is not binding any resources to the local thread, instead delegating to + // System.Transactions to handle thread local resources. + PromotableTxScopeTransactionObject txMgrStateObject = (PromotableTxScopeTransactionObject) transaction; + return txMgrStateObject.TxScopeAdapter; + } + + protected override void DoResume(object transaction, object suspendedResources) + { + } + + protected override void DoCommit(DefaultTransactionStatus status) + { + PromotableTxScopeTransactionObject txObject = + (PromotableTxScopeTransactionObject)status.Transaction; + try + { + txObject.TxScopeAdapter.Complete(); + txObject.TxScopeAdapter.Dispose(); + } + catch (TransactionAbortedException ex) + { + throw new UnexpectedRollbackException("Transaction unexpectedly rolled back (maybe due to a timeout)", ex); + } + catch (TransactionInDoubtException ex) + { + throw new HeuristicCompletionException(TransactionOutcomeState.Unknown, ex); + } + catch (Exception ex) + { + throw new TransactionSystemException("Failure on Transaction Scope Commit", ex); + } + } + + protected override void DoRollback(DefaultTransactionStatus status) + { + PromotableTxScopeTransactionObject txObject = + (PromotableTxScopeTransactionObject)status.Transaction; + + try + { + + txObject.TxScopeAdapter.Dispose(); + } + catch (Exception e) + { + throw new Spring.Transaction.TransactionSystemException("Failure on Transaction Scope rollback.", e); + } + } + + + protected override void DoSetRollbackOnly(DefaultTransactionStatus status) + { + if (status.Debug) + { + log.Debug("Setting transaction rollback-only"); + } + try + { + System.Transactions.Transaction.Current.Rollback(); + } catch (Exception ex) + { + throw new TransactionSystemException("Failure on System.Transactions.Transaction.Current.Rollback", ex); + } + } + + protected override bool ShouldCommitOnGlobalRollbackOnly + { + get { return true; } + } + + private void DoTxScopeBegin(PromotableTxScopeTransactionObject txObject, + Spring.Transaction.ITransactionDefinition definition) + { + + TransactionScopeOption txScopeOption = CreateTransactionScopeOptions(definition); + TransactionOptions txOptions = CreateTransactionOptions(definition); + txObject.TxScopeAdapter.CreateTransactionScope(txScopeOption, txOptions, definition.EnterpriseServicesInteropOption); + + } + + private static TransactionOptions CreateTransactionOptions(ITransactionDefinition definition) + { + TransactionOptions txOptions = new TransactionOptions(); + switch (definition.TransactionIsolationLevel ) + { + case System.Data.IsolationLevel.Chaos: + txOptions.IsolationLevel = IsolationLevel.Chaos; + break; + case System.Data.IsolationLevel.ReadCommitted: + txOptions.IsolationLevel = IsolationLevel.ReadCommitted; + break; + case System.Data.IsolationLevel.ReadUncommitted: + txOptions.IsolationLevel = IsolationLevel.ReadUncommitted; + break; + case System.Data.IsolationLevel.RepeatableRead: + txOptions.IsolationLevel = IsolationLevel.RepeatableRead; + break; + case System.Data.IsolationLevel.Serializable: + txOptions.IsolationLevel = IsolationLevel.Serializable; + break; + case System.Data.IsolationLevel.Snapshot: + txOptions.IsolationLevel = IsolationLevel.Snapshot; + break; + case System.Data.IsolationLevel.Unspecified: + txOptions.IsolationLevel = IsolationLevel.Unspecified; + break; + } + + if (definition.TransactionTimeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT) + { + txOptions.Timeout = new TimeSpan(0, 0, definition.TransactionTimeout); + } + return txOptions; + } + + private static TransactionScopeOption CreateTransactionScopeOptions(ITransactionDefinition definition) + { + TransactionScopeOption txScopeOption; + if (definition.PropagationBehavior == TransactionPropagation.Required) + { + txScopeOption = TransactionScopeOption.Required; + } + else if (definition.PropagationBehavior == TransactionPropagation.RequiresNew) + { + txScopeOption = TransactionScopeOption.RequiresNew; + } + else + { + throw new Spring.Transaction.TransactionSystemException("Transaction Propagation Behavior" + + definition.PropagationBehavior + + " not supported by TransactionScope. Use Required or RequiredNew"); + } + return txScopeOption; + } + + /// + /// The transaction resource object that encapsulates the state and functionality + /// contained in TransactionScope and Transaction.Current via the ITransactionScopeAdapter + /// property. + /// + public class PromotableTxScopeTransactionObject : ISmartTransactionObject + { + private ITransactionScopeAdapter txScopeAdapter; + + /// + /// Initializes a new instance of the class. + /// Will create an instance of . + /// + public PromotableTxScopeTransactionObject() + { + txScopeAdapter = new DefaultTransactionScopeAdapter(); + } + + /// + /// Gets or sets the transaction scope adapter. + /// + /// The transaction scope adapter. + public ITransactionScopeAdapter TxScopeAdapter + { + get { return txScopeAdapter; } + set { txScopeAdapter = value; } + } + + + /// + /// Return whether the transaction is internally marked as rollback-only. + /// + /// + /// True of the transaction is marked as rollback-only. + public bool RollbackOnly + { + get { + return txScopeAdapter.RollbackOnly; + } + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/AdoDaoSupport.cs b/src/Spring/Spring.Data/Data/Generic/AdoDaoSupport.cs new file mode 100644 index 00000000..99264e21 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/AdoDaoSupport.cs @@ -0,0 +1,153 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Data; +using Spring.Dao.Support; +using Spring.Data.Common; +using Spring.Data.Support; + +#endregion + +namespace Spring.Data.Generic +{ + /// + /// Convenient super class for ADO.NET data access objects using generics. + /// + /// + /// Requires a IDBProvider to be set, providing a + /// AdoTemplate based on it to subclasses. + /// This base class is mainly intended for AdoTemplate usage. + /// + public class AdoDaoSupport : DaoSupport + { + private AdoTemplate adoTemplate; + + /// + /// The DbProvider instance used by this DAO + /// + public IDbProvider DbProvider + { + set + { + adoTemplate = CreateAdoTemplate(value); + } + get + { + if (adoTemplate != null) + { + return adoTemplate.DbProvider; + } + else + { + return null; + } + } + + } + + /// + /// Set the AdoTemplate for this DAO explicity, as + /// an alternative to specifying a IDbProvider + /// + public AdoTemplate AdoTemplate + { + set + { + adoTemplate = value; + } + get + { + return adoTemplate; + } + + } + + protected override void CheckDaoConfig() + { + if (adoTemplate == null) + { + throw new ArgumentException("DbProvider or AdoTemplate is required"); + } + } + + protected IDbConnection Connection + { + get + { + return ConnectionUtils.GetConnection(DbProvider); + } + } + + protected IAdoExceptionTranslator ExceptionTranslator + { + get + { + return null; //Investigate AdoExceptionTranslator on AdoAccessor + } + } + + protected void DisposeConnection(IDbConnection conn, IDbProvider dbProvider) + { + ConnectionUtils.DisposeConnection(conn, dbProvider); + } + + + /// + /// Create a AdoTemplate for a given DbProvider + /// Only invoked if populating the DAO with a DbProvider reference. + /// + /// + /// Can be overriden in subclasses to provide AdoTemplate instances + /// with a different configuration, or a cusotm AdoTemplate subclass. + /// + /// The DbProvider to create a AdoTemplate for + protected virtual AdoTemplate CreateAdoTemplate(IDbProvider dbProvider) + { + return new AdoTemplate(dbProvider); + } + + /// + /// Convenience method to create a parameters builder. + /// + /// Virtual for sublcasses to override with custom + /// implementation. + /// A new DbParameterBuilder + protected virtual IDbParametersBuilder CreateDbParametersBuilder() + { + return new DbParametersBuilder(DbProvider); + } + + protected virtual IDbParameters CreateDbParameters() + { + return AdoTemplate.CreateDbParameters(); + } + + + + + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/AdoTemplate.cs b/src/Spring/Spring.Data/Data/Generic/AdoTemplate.cs new file mode 100644 index 00000000..65eeb28c --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/AdoTemplate.cs @@ -0,0 +1,1707 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using Common.Logging; +using Spring.Dao; +using Spring.Dao.Support.Generic; +using Spring.Data.Common; +using Spring.Data.Core; +using Spring.Data.Support; +using Spring.Util; + +namespace Spring.Data.Generic +{ + /// + /// This is the central class in the Spring.Data.Generic namespace. + /// It simplifies the use of ADO.NET and helps to avoid commons errors. + /// + /// Mark Pollack (.NET) + /// $Id: AdoTemplate.cs,v 1.19 2007/12/07 22:56:22 markpollack Exp $ + public class AdoTemplate : AdoAccessor, IAdoOperations + { + + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof(AdoTemplate)); + + #endregion + + #region Fields + + private Core.AdoTemplate classicAdoTemplate; + protected Type dataReaderWrapperType; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public AdoTemplate() + : base() + { + classicAdoTemplate = new Core.AdoTemplate(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The provider. + public AdoTemplate(IDbProvider provider) + { + classicAdoTemplate = new Core.AdoTemplate(provider); + AfterPropertiesSet(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The database provider. + /// if set to false + /// lazily initialize the ErrorCodeExceptionTranslator. + public AdoTemplate(IDbProvider provider, bool lazyInit) + { + classicAdoTemplate = new Core.AdoTemplate(provider, lazyInit); + AfterPropertiesSet(); + } + + /// + /// Initializes a new instance of the class. + /// + /// Useful if you have a subclass of Spring.Data.Core.AdoTemplate. + /// The classic ADO template. + public AdoTemplate(Core.AdoTemplate classicAdoTemplate) + { + this.classicAdoTemplate = classicAdoTemplate; + AfterPropertiesSet(); + } + #endregion + + #region Properties + + /// + /// An instance of a DbProvider implementation. + /// + public override IDbProvider DbProvider + { + get { return classicAdoTemplate.DbProvider; } + set { classicAdoTemplate.DbProvider = value; } + } + + /// + /// Gets or sets a value indicating whether to lazily initialize the + /// IAdoExceptionTranslator for this accessor, on first encounter of a + /// exception from the data provider. Default is "true"; can be switched to + /// "false" for initialization on startup. + /// + /// true if to lazy initialize the IAdoExceptionTranslator; + /// otherwise, false. + public override bool LazyInit + { + get { return classicAdoTemplate.LazyInit; } + set { classicAdoTemplate.LazyInit = value; } + } + + /// + /// Gets or sets the exception translator. If no custom translator is provided, a default + /// is used. + /// + /// The exception translator. + public override IAdoExceptionTranslator ExceptionTranslator + { + get { return classicAdoTemplate.ExceptionTranslator; } + set { classicAdoTemplate.ExceptionTranslator = value; } + } + + + /// + /// Gets or set the System.Type to use to create an instance of IDataReaderWrapper + /// for the purpose of having defaults values to use in case of DBNull values read + /// from IDataReader. + /// + /// The type of the data reader wrapper. + public override Type DataReaderWrapperType + { + get { return classicAdoTemplate.DataReaderWrapperType; } + set { classicAdoTemplate.DataReaderWrapperType = value; } + } + + /// + /// Gets the 'Classic' AdoTemplate in Spring.Data + /// + /// + /// Useful to access methods that return non-generic collections. + /// + /// The 'Classic; AdoTemplate in Spring.Data. + public Core.AdoTemplate ClassicAdoTemplate + { + get { return classicAdoTemplate; } + } + + /// + /// Gets or sets the command timeout for IDbCommands that this AdoTemplate executes. It also + /// sets the value of the contained 'ClassicAdoTemplate'. + /// + /// The command timeout. + /// Default is 0, indicating to use the database provider's default. + /// Any timeout specified here will be overridden by the remaining + /// transaction timeout when executing within a transaction that has a + /// timeout specified at the transaction level. + /// + public override int CommandTimeout + { + get { return commandTimeout; } + set + { + commandTimeout = value; + classicAdoTemplate.CommandTimeout = value; + } + } + + #endregion + + #region General Execute Callback methods + + /// + /// Execute a ADO.NET operation on a command object using a interface based callback. + /// + /// the callback to execute + /// object returned from callback + public T Execute(ICommandCallback action) + { + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider); + + IDbCommand command = null; + try + { + command = DbProvider.CreateCommand(); + command.Connection = connectionTxPairToUse.Connection; + command.Transaction = connectionTxPairToUse.Transaction; + //TODO collect warnings... + //RegisterEventHandlers(command.Connection); + ApplyCommandSettings(command); + //TODO explicit check to cast to base class. + DbCommand commandToUse = command as DbCommand; + if (commandToUse != null) + { + T result = action.DoInCommand((DbCommand)command); + //SqlWarnings sqlWarnings = GetSqlWarnings() + //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings); + return result; + } + else + { + throw new InvalidDataAccessApiUsageException( + "Providers implementation of IDbCommand does not inherit from System.Data.Common.DbCommand. Use the alternative overloaded Execute method that specifies IDbCommandCallback as a parameter."); + } + + } + catch (Exception e) + { + AdoUtils.DisposeCommand(command); + command = null; + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + connectionTxPairToUse.Connection = null; + if (DbProvider.IsDataAccessException(e)) + { + throw ExceptionTranslator.Translate("CommandCallback", GetCommandText(action), e); + } + else + { + throw; + } + + + } + finally + { + AdoUtils.DisposeCommand(command); + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + } + } + + /// + /// Execute a ADO.NET operation on a command object using a delegate callback. + /// + /// This allows for implementing arbitrary data access operations + /// on a single command within Spring's managed ADO.NET environment. + /// The delegate called with a command object. + /// A result object returned by the action or null + public T Execute(CommandDelegate del) + { + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider); + + IDbCommand command = null; + try + { + command = DbProvider.CreateCommand(); + command.Connection = connectionTxPairToUse.Connection; + command.Transaction = connectionTxPairToUse.Transaction; + //TODO collect warnings... + //RegisterEventHandlers(command.Connection); + ApplyCommandSettings(command); + DbCommand commandToUse = command as DbCommand; + if (commandToUse != null) + { + T result = del((DbCommand) command); + //SqlWarnings sqlWarnings = GetSqlWarnings() + //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings); + return result; + } + else + { + throw new InvalidDataAccessApiUsageException( + "Providers implementation of IDbCommand does not inherit from System.Data.Common.DbCommand. Use the alternative overloaded Execute method that specifies IDbCommandDelegate as a parameter."); + } + } + catch (Exception e) + { + string commandText = command.CommandText; + AdoUtils.DisposeCommand(command); + command = null; + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + connectionTxPairToUse.Connection = null; + if (DbProvider.IsDataAccessException(e)) + { + throw ExceptionTranslator.Translate("CommandCallback", commandText, e); + } + else + { + throw; + } + + + } + finally + { + AdoUtils.DisposeCommand(command); + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + } + } + + + /// + /// Execute a ADO.NET operation on a command object using a interface based callback. + /// + /// the callback to execute + /// object returned from callback + public T Execute(IDbCommandCallback action) + { + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider); + + IDbCommand command = null; + try + { + command = DbProvider.CreateCommand(); + command.Connection = connectionTxPairToUse.Connection; + command.Transaction = connectionTxPairToUse.Transaction; + //TODO collect warnings... + //RegisterEventHandlers(command.Connection); + ApplyCommandSettings(command); + T result = action.DoInCommand(command); + //SqlWarnings sqlWarnings = GetSqlWarnings() + //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings); + return result; + } + catch (Exception e) + { + AdoUtils.DisposeCommand(command); + command = null; + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + connectionTxPairToUse.Connection = null; + if (DbProvider.IsDataAccessException(e)) + { + throw ExceptionTranslator.Translate("CommandCallback", GetCommandText(action), e); + } + else + { + throw; + } + + + } + finally + { + AdoUtils.DisposeCommand(command); + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + } + } + + /// + /// Execute a ADO.NET operation on a command object using a delegate callback. + /// + /// This allows for implementing arbitrary data access operations + /// on a single command within Spring's managed ADO.NET environment. + /// The delegate called with a command object. + /// A result object returned by the action or null + public T Execute(IDbCommandDelegate del) + { + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider); + + IDbCommand command = null; + try + { + command = DbProvider.CreateCommand(); + command.Connection = connectionTxPairToUse.Connection; + command.Transaction = connectionTxPairToUse.Transaction; + //TODO collect warnings... + //RegisterEventHandlers(command.Connection); + ApplyCommandSettings(command); + T result = del(command); + //SqlWarnings sqlWarnings = GetSqlWarnings() + //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings); + return result; + } + catch (Exception e) + { + string commandText = command.CommandText; + AdoUtils.DisposeCommand(command); + command = null; + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + connectionTxPairToUse.Connection = null; + if (DbProvider.IsDataAccessException(e)) + { + throw ExceptionTranslator.Translate("CommandCallback", commandText, e); + } + else + { + throw; + } + + + } + finally + { + AdoUtils.DisposeCommand(command); + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + } + } + + + /// + /// Executes ADO.NET operations on a command object, created by the provided IDbCommandCreator, + /// using the interface based callback IDbCommandCallback. + /// + /// The type of object returned from the callback. + /// The command creator. + /// The callback to execute based on IDbCommand + /// + /// A result object returned by the action or null + /// + public T Execute(IDbCommandCreator commandCreator, IDbCommandCallback action) + { + AssertUtils.ArgumentNotNull(commandCreator, "commandCreator", "IDbCommandCreator must not be null"); + AssertUtils.ArgumentNotNull(action, "action", "Callback object must not be null"); + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider); + + IDbCommand command = null; + try + { + command = commandCreator.CreateDbCommand(DbProvider); + command.Connection = connectionTxPairToUse.Connection; + command.Transaction = connectionTxPairToUse.Transaction; + //TODO collect warnings... + //RegisterEventHandlers(command.Connection); + ApplyCommandSettings(command); + T result = action.DoInCommand(command); + //SqlWarnings sqlWarnings = GetSqlWarnings() + //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings); + return result; + } + catch (Exception e) + { + AdoUtils.DisposeCommand(command); + command = null; + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + connectionTxPairToUse.Connection = null; + if (DbProvider.IsDataAccessException(e)) + { + throw ExceptionTranslator.Translate("CommandCallback", GetCommandText(action), e); + } + else + { + throw; + } + + + } + finally + { + AdoUtils.DisposeCommand(command); + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + } + + } + + /// + /// Execute ADO.NET operations on a IDbDataAdapter object using an interface based callback. + /// + /// The type of object returned from the callback. + /// The data adapter callback. + /// + /// A result object returned by the callback or null + /// + /// This allows for implementing abritrary data access operations + /// on a single DataAdapter within Spring's managed ADO.NET environment. + /// + public T Execute(IDataAdapterCallback dataAdapterCallback) + { + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider); + IDbDataAdapter dataAdapter = null; + try + { + dataAdapter = DbProvider.CreateDataAdapter(); + //TODO row updated event handling... + dataAdapter.SelectCommand = DbProvider.CreateCommand(); + dataAdapter.SelectCommand.Connection = connectionTxPairToUse.Connection; + //TODO register for warnings on connection. + dataAdapter.SelectCommand.Transaction = connectionTxPairToUse.Transaction; + ApplyCommandSettings(dataAdapter.SelectCommand); + T result = dataAdapterCallback.DoInDataAdapter(dataAdapter); + return result; + } + catch (Exception) + { + AdoUtils.DisposeDataAdapterCommands(dataAdapter); + //TODO set dataAdapter command's = null; ? + //TODO exception translation? different hierarchy for data set operations. + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + connectionTxPairToUse.Connection = null; + throw; + } + finally + { + AdoUtils.DisposeDataAdapterCommands(dataAdapter); + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + } + + + } + + /// + /// Execute ADO.NET operations on a IDbDataAdapter object using an delgate based callback. + /// + /// This allows for implementing abritrary data access operations + /// on a single DataAdapter within Spring's managed ADO.NET environment. + /// + /// The type of object returned from the callback. + /// The delegate called with a IDbDataAdapter object. + /// A result object returned by the callback or null + public T Execute(DataAdapterDelegate dataAdapterCallback) + { + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider); + IDbDataAdapter dataAdapter = null; + try + { + dataAdapter = DbProvider.CreateDataAdapter(); + //TODO row updated event handling... + dataAdapter.SelectCommand = DbProvider.CreateCommand(); + dataAdapter.SelectCommand.Connection = connectionTxPairToUse.Connection; + //TODO register for warnings on connection. + dataAdapter.SelectCommand.Transaction = connectionTxPairToUse.Transaction; + ApplyCommandSettings(dataAdapter.SelectCommand); + T result = dataAdapterCallback(dataAdapter); + return result; + + } + catch (Exception) + { + AdoUtils.DisposeDataAdapterCommands(dataAdapter); + //TODO set dataAdapter command's = null; ? + //TODO exception translation? different hierarchy for data set operations. + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + connectionTxPairToUse.Connection = null; + throw; + } + finally + { + AdoUtils.DisposeDataAdapterCommands(dataAdapter); + ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider); + } + } + + #endregion + + #region ExecuteNonQuery + + /// + /// Executes a non query returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The number of rows affected. + public int ExecuteNonQuery(CommandType cmdType, string cmdText) + { + return classicAdoTemplate.ExecuteNonQuery(cmdType, cmdText); + } + + + /// + /// Executes a non query returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + /// The number of rows affected. + public int ExecuteNonQuery(CommandType cmdType, string cmdText, + string parameterName, Enum dbType, int size, object parameterValue) + { + return classicAdoTemplate.ExecuteNonQuery(cmdType, cmdText, parameterName, dbType, size, parameterValue); + + + } + + /// + /// Executes a non query returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The parameter collection to map. + /// The number of rows affected. + public int ExecuteNonQuery(CommandType cmdType, string cmdText, + IDbParameters parameters) + { + return classicAdoTemplate.ExecuteNonQuery(cmdType, cmdText, parameters); + + } + + /// + /// Executes a non query with parameters set via the + /// command setter, returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The command setter. + /// The number of rows affected. + public int ExecuteNonQuery(CommandType cmdType, string cmdText, + ICommandSetter commandSetter) + { + return classicAdoTemplate.ExecuteNonQuery(cmdType, cmdText, commandSetter); + } + + /// + /// Executes a non query with a command created via IDbCommandCreator and + /// parameters. + /// + /// Output parameters can be retrieved via the returned + /// dictionary. + /// + /// More commonly used as a lower level support method within the framework, + /// for example StoredProcedure/AdoScalar. + /// + /// + /// The callback to create a IDbCommand. + /// The number of rows affected. + public IDictionary ExecuteNonQuery(IDbCommandCreator commandCreator) + { + return classicAdoTemplate.ExecuteNonQuery(commandCreator); + } + + #endregion + + #region ExecuteScalar + + /// + /// Execute the query with the specified command text. + /// + /// No parameters are used. As with + /// IDbCommand.ExecuteScalar, it returns the first column of the first row in the result set + /// returned by the query. Extra columns or row are ignored. + /// The command type + /// The command text to execute. + /// The first column of the first row in the result set + public object ExecuteScalar(CommandType cmdType, string cmdText) + { + return classicAdoTemplate.ExecuteScalar(cmdType, cmdText); + } + + /// + /// Execute the query with the specified command text and parameter returning a scalar result + /// + /// The command type + /// The command text to execute. + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + /// The first column of the first row in the result set + public object ExecuteScalar(CommandType cmdType, string cmdText, + string parameterName, Enum dbType, int size, object parameterValue) + { + return classicAdoTemplate.ExecuteScalar(cmdType, cmdText, parameterName, dbType, size, parameterValue); + + } + + + /// + /// Execute the query with the specified command text and parameters returning a scalar result + /// + /// The command type + /// The command text to execute. + /// The parameter collection to map. + /// The first column of the first row in the result set + public object ExecuteScalar(CommandType cmdType, string cmdText, + IDbParameters parameters) + { + return classicAdoTemplate.ExecuteScalar(cmdType, cmdText, parameters); + } + + /// + /// Execute the query with the specified command text and parameters set via the + /// command setter, returning a scalar result + /// + /// The command type + /// The command text to execute. + /// The command setter. + /// The first column of the first row in the result set + public object ExecuteScalar(CommandType cmdType, string cmdText, + ICommandSetter commandSetter) + { + return classicAdoTemplate.ExecuteScalar(cmdType, cmdText, commandSetter); + } + + /// + /// Execute the query with a command created via IDbCommandCreator and + /// parameters + /// + /// Output parameters can be retrieved via the returned + /// dictionary. + /// + /// More commonly used as a lower level support method within the framework, + /// for example for StoredProcedure/AdoScalar. + /// + /// The callback to create a IDbCommand. + /// A dictionary containing output parameters, if any + public IDictionary ExecuteScalar(IDbCommandCreator commandCreator) + { + return classicAdoTemplate.ExecuteScalar(commandCreator); + } + + #endregion + + #region Queries with RowCallback + + /// + /// Execute a query given IDbCommand's type and text by + /// passing the created IDbCommand to a ICommandSetter implementation + /// that knows how to bind values to the IDbCommand, reading a + /// single result set on a per-row basis with a . + /// + /// The type of command + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// + /// The command setter. + public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback, + ICommandSetter commandSetter) + { + classicAdoTemplate.QueryWithRowCallback(cmdType, cmdText, rowCallback, commandSetter); + } + /// + /// Execute a query given IDbCommand's type and text, reading a + /// single result set on a per-row basis with a . + /// + /// The type of command + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// + public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback) + { + classicAdoTemplate.QueryWithRowCallback(cmdType, cmdText, rowCallback); + } + + /// + /// Execute a query given IDbCommand's type and text and provided parameter + /// information, reading a + /// single result set on a per-row basis with a . + /// + /// The type of command + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback, + string parameterName, Enum dbType, int size, object parameterValue) + { + classicAdoTemplate.QueryWithRowCallback(cmdType, cmdText, rowCallback, + parameterName, dbType, size, parameterValue); + } + + /// + /// Execute a query given IDbCommand's type and text and provided IDbParameters, + /// reading a single result set on a per-row basis with a . + /// + /// Type of the command. + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// The parameter collection to map. + public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback, + IDbParameters parameter) + { + classicAdoTemplate.QueryWithRowCallback(cmdType, cmdText, rowCallback, + parameter); + } + + #endregion + + #region Queries with RowCallback Delegate + + public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate) + { + classicAdoTemplate.QueryWithRowCallbackDelegate(cmdType, sql, rowCallbackDelegate); + + } + public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, ICommandSetter commandSetter) + { + throw new NotImplementedException(); + } + + public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, + string parameterName, Enum dbType, int size, object parameterValue) + { + classicAdoTemplate.QueryWithRowCallbackDelegate(cmdType, sql, rowCallbackDelegate, + parameterName, dbType, size, parameterValue); + + } + + public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, IDbParameters parameters) + { + classicAdoTemplate.QueryWithRowCallbackDelegate(cmdType, sql, rowCallbackDelegate, parameters); + + } + + #endregion + + #region Queries with RowMapper + + public IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper) + { + return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapper)); + + } + + + public IList QueryWithRowMapper(CommandType cmdType, String cmdText, IRowMapper rowMapper, + ICommandSetter commandSetter) + { + return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapper), commandSetter); + + } + + public IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, + string parameterName, Enum dbType, int size, object parameterValue) + { + return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapper), parameterName, dbType, size, parameterValue); + + } + + public IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, IDbParameters parameters) + { + return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapper), parameters); + + } + + + + #endregion + + #region Queries with RowMapperDelegate + + public IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate) + { + return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapperDelegate)); + + } + + public IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, + ICommandSetter commandSetter) + { + return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapperDelegate), + commandSetter); + } + + public IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, + string parameterName, Enum dbType, int size, object parameterValue) + { + return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapperDelegate), + parameterName, dbType, size, parameterValue); + } + + public IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, + IDbParameters parameters) + { + return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor(rowMapperDelegate), + parameters); + } + #endregion + + #region Queries with ResultSetExtractor + + public T QueryWithResultSetExtractor(CommandType cmdType, + string cmdText, + IResultSetExtractor resultSetExtractor) + { + AssertUtils.ArgumentNotNull(cmdText, "cmdText", "CommandText must not be null"); + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing CommandText [" + cmdText + "]"); + } + return Execute(new QueryCallback(this, cmdType, cmdText, resultSetExtractor, null)); + } + + public T QueryWithResultSetExtractor(CommandType commandType, string cmdText, IResultSetExtractor resultSetExtractor, + string name, Enum dbType, int size, object parameterValue) + { + AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null"); + return Execute(new QueryCallback(this, commandType, cmdText, resultSetExtractor, + CreateDbParameters(name, dbType, size, parameterValue))); + } + + public T QueryWithResultSetExtractor(CommandType commandType, string cmdText, + IResultSetExtractor resultSetExtractor, + IDbParameters parameters) + { + AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null"); + return Execute(new QueryCallback(this, commandType, cmdText, resultSetExtractor, parameters)); + } + + public T QueryWithResultSetExtractor(CommandType cmdType, string cmdText, + IResultSetExtractor resultSetExtractor, + ICommandSetter commandSetter) + { + AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null"); + return Execute(new QueryCallbackWithCommandSetter(this, cmdType, cmdText, + resultSetExtractor, + commandSetter)); + } + + public T QueryWithResultSetExtractor(CommandType cmdType, string cmdText, + IResultSetExtractor resultSetExtractor, + CommandSetterDelegate commandSetterDelegate) + { + AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null"); + return Execute(new QueryCallbackWithCommandSetterDelegate(this, cmdType, cmdText, + resultSetExtractor, + commandSetterDelegate)); + } + + #endregion + + + #region Queries with ResultSetExtractorDelegate + + public T QueryWithResultSetExtractorDelegate(CommandType cmdType, + string cmdText, + ResultSetExtractorDelegate resultSetExtractorDelegate) + { + AssertUtils.ArgumentNotNull(cmdText, "cmdText", "CommandText must not be null"); + AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null"); + if (LOG.IsDebugEnabled) + { + LOG.Debug("Executing CommandText [" + cmdText + "]"); + } + + return Execute(new QueryCallback(this, cmdType, cmdText, resultSetExtractorDelegate, null)); + } + + public T QueryWithResultSetExtractorDelegate(CommandType commandType, string cmdText, ResultSetExtractorDelegate resultSetExtractorDelegate, + string paramenterName, Enum dbType, int size, object parameterValue) + { + AssertUtils.ArgumentNotNull(cmdText, "cmdText", "CommandText must not be null"); + AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null"); + return Execute(new QueryCallback(this, commandType, cmdText, resultSetExtractorDelegate, + CreateDbParameters(paramenterName, dbType, size, parameterValue))); + } + + public T QueryWithResultSetExtractorDelegate(CommandType commandType, string cmdText, ResultSetExtractorDelegate resultSetExtractorDelegate, + IDbParameters parameters) + { + AssertUtils.ArgumentNotNull(cmdText, "cmdText", "CommandText must not be null"); + AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null"); + return Execute(new QueryCallback(this, commandType, cmdText, resultSetExtractorDelegate, parameters)); + } + + public T QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractorDelegate, + ICommandSetter commandSetter) + { + AssertUtils.ArgumentNotNull(cmdText, "cmdText", "CommandText must not be null"); + AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null"); + return Execute(new QueryCallbackWithCommandSetter(this, cmdType, cmdText, + resultSetExtractorDelegate, + commandSetter)); + } + + public T QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractorDelegate, + CommandSetterDelegate commandSetterDelegate) + { + AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null"); + return Execute(new QueryCallbackWithCommandSetterDelegate(this, cmdType, cmdText, + resultSetExtractorDelegate, + commandSetterDelegate)); + } + + #endregion + + #region Query for Object + + public T QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper) + { + IList results = QueryWithRowMapper(cmdType, cmdText, rowMapper); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + public T QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper, ICommandSetter commandSetter) + { + IList results = QueryWithRowMapper(cmdType, cmdText, rowMapper, commandSetter); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + public T QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper, IDbParameters parameters) + { + IList results = QueryWithRowMapper(cmdType, cmdText, rowMapper, parameters); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + + public T QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper, + string parameterName, Enum dbType, int size, object parameterValue) + { + IList results = QueryWithRowMapper(cmdType, cmdText, rowMapper, parameterName, dbType, size, parameterValue); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + #endregion + + #region Query for ObjectDelegate + + public T QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate) + { + IList results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + public T QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, ICommandSetter commandSetter) + { + IList results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate, commandSetter); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + public T QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, IDbParameters parameters) + { + IList results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate, parameters); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + + public T QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, + string parameterName, Enum dbType, int size, object parameterValue) + { + IList results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate, parameterName, dbType, size, parameterValue); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + #endregion + + + #region Query with CommandCreator + + public T QueryWithCommandCreator(IDbCommandCreator cc, IResultSetExtractor rse) + { + return QueryWithCommandCreator(cc, rse, null); + } + + public void QueryWithCommandCreator(IDbCommandCreator cc, IRowCallback rowCallback) + { + classicAdoTemplate.QueryWithCommandCreator(cc, rowCallback, null); + } + + public IList QueryWithCommandCreator(IDbCommandCreator cc, IRowMapper rowMapper) + { + return QueryWithCommandCreator(cc, rowMapper, null); + } + + public T QueryWithCommandCreator(IDbCommandCreator cc, IResultSetExtractor rse, IDictionary returnedParameters) + { + if (rse == null) + { + throw new ArgumentNullException("Result Set Extractor must not be null"); + } + + return Execute(cc, new AdoResultSetExtractorWithOutputParamsCommandCallback(this, rse, returnedParameters)); + } + + + public void QueryWithCommandCreator(IDbCommandCreator cc, IRowCallback rowCallback, IDictionary returnedParameters) + { + classicAdoTemplate.QueryWithCommandCreator(cc, rowCallback, returnedParameters); + } + + + public IList QueryWithCommandCreator(IDbCommandCreator cc, IRowMapper rowMapper, IDictionary returnedParameters) + { + if (rowMapper == null) + { + throw new ArgumentNullException("rowMapper must not be null"); + } + + return Execute(cc, new AdoRowMapperQueryCommandCallback(this, rowMapper, returnedParameters)); + } + + + public IDictionary QueryWithCommandCreator(IDbCommandCreator cc, IList namedResultSetProcessors) + { + return classicAdoTemplate.Execute(cc, new AdoResultProcessorsQueryCommandCallback(this, namedResultSetProcessors)) as IDictionary; + } + + + public IDictionary QueryWithCommandCreator(IDbCommandCreator cc, IList namedResultSetProcessors) + { + return classicAdoTemplate.Execute(cc, new AdoResultProcessorsQueryCommandCallback(this, namedResultSetProcessors)) as IDictionary; + } + + + #endregion + + #region General Helper Methods + + /// + /// Checks if DbProvider is not null and creates ExceptionTranslator if not LazyInit. + /// + public override void AfterPropertiesSet() + { + classicAdoTemplate.AfterPropertiesSet(); + classicAdoTemplate.CommandTimeout = CommandTimeout; + } + + /// + /// Creates the data reader wrapper for use in AdoTemplate callback methods. + /// + /// The reader to wrap. + /// The data reader used in AdoTemplate callbacks + public override IDataReader CreateDataReaderWrapper(IDataReader readerToWrap) + { + return classicAdoTemplate.CreateDataReaderWrapper(readerToWrap); + } + + #endregion + + #region Parameter Creation Helper Methods + + /// + /// Derives the parameters of a stored procedure including the return parameter + /// + /// Name of the procedure. + /// if set to true to include return parameter. + /// The stored procedure parameters + public override IDataParameter[] DeriveParameters(string procedureName, bool includeReturnParameter) + { + return classicAdoTemplate.DeriveParameters(procedureName, includeReturnParameter); + } + + #endregion + + + + + + #region Internal Helper classes + + internal class QueryCallback : IDbCommandCallback, ICommandTextProvider + { + private AdoTemplate adoTemplate; + private IResultSetExtractor rse; + private ResultSetExtractorDelegate resultSetExtractorDelegate; + private CommandType commandType; + private string commandText; + private IDbParameters parameters; + + public QueryCallback(AdoTemplate adoTemplate, + CommandType cmdType, + string cmdText, + IResultSetExtractor rse, + IDbParameters dbParameters) + { + this.adoTemplate = adoTemplate; + commandType = cmdType; + commandText = cmdText; + this.rse = rse; + parameters = dbParameters; + } + + public QueryCallback(AdoTemplate adoTemplate, + CommandType cmdType, + string cmdText, + ResultSetExtractorDelegate resultSetExtractorDelegate, + IDbParameters dbParameters) + { + this.adoTemplate = adoTemplate; + commandType = cmdType; + commandText = cmdText; + this.resultSetExtractorDelegate = resultSetExtractorDelegate; + parameters = dbParameters; + } + + public string CommandText + { + get { return commandText; } + } + + public T DoInCommand(IDbCommand command) + { + IDataReader reader = null; + try + { + command.CommandType = commandType; + command.CommandText = commandText; + ParameterUtils.CopyParameters(command, parameters); + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + T returnValue = default(T); + if (rse != null) + { + returnValue = rse.ExtractData(reader); + } + else + { + returnValue = resultSetExtractorDelegate(reader); + } + return returnValue; + } + finally + { + AdoUtils.CloseReader(reader); + ParameterUtils.CopyParameters(parameters, command); + } + } + + + } + + internal class QueryCallbackWithCommandSetter : IDbCommandCallback, ICommandTextProvider + { + private AdoTemplate adoTemplate; + private IResultSetExtractor rse; + private ResultSetExtractorDelegate resultSetExtractorDelegate; + private CommandType commandType; + private string commandText; + private ICommandSetter commandSetter; + + public QueryCallbackWithCommandSetter(AdoTemplate adoTemplate, + CommandType cmdType, + string cmdText, + IResultSetExtractor rse, + ICommandSetter commandSetter) + { + this.adoTemplate = adoTemplate; + commandType = cmdType; + commandText = cmdText; + this.rse = rse; + this.commandSetter = commandSetter; + } + + public QueryCallbackWithCommandSetter(AdoTemplate adoTemplate, + CommandType cmdType, + string cmdText, + ResultSetExtractorDelegate resultSetExtractorDelegate, + ICommandSetter commandSetter) + { + this.adoTemplate = adoTemplate; + commandType = cmdType; + commandText = cmdText; + this.resultSetExtractorDelegate = resultSetExtractorDelegate; + this.commandSetter = commandSetter; + } + + public string CommandText + { + get { return commandText; } + } + + public T DoInCommand(IDbCommand command) + { + IDataReader reader = null; + try + { + command.CommandType = commandType; + command.CommandText = commandText; + if (commandSetter != null) + { + commandSetter.SetValues(command); + } + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + if (rse != null) + { + return rse.ExtractData(reader); + } + else + { + return resultSetExtractorDelegate(reader); + } + + } + finally + { + AdoUtils.CloseReader(reader); + } + } + + + } + + internal class QueryCallbackWithCommandSetterDelegate : IDbCommandCallback, ICommandTextProvider + { + private AdoTemplate adoTemplate; + private IResultSetExtractor rse; + private ResultSetExtractorDelegate resultSetExtractorDelegate; + private CommandType commandType; + private string commandText; + private CommandSetterDelegate commandSetterDelegate; + + public QueryCallbackWithCommandSetterDelegate(AdoTemplate adoTemplate, + CommandType cmdType, + string cmdText, + IResultSetExtractor rse, + CommandSetterDelegate commandSetterDelegate) + { + this.adoTemplate = adoTemplate; + commandType = cmdType; + commandText = cmdText; + this.rse = rse; + this.commandSetterDelegate = commandSetterDelegate; + } + + public QueryCallbackWithCommandSetterDelegate(AdoTemplate adoTemplate, + CommandType cmdType, + string cmdText, + ResultSetExtractorDelegate resultSetExtractorDelegate, + CommandSetterDelegate commandSetterDelegate) + { + this.adoTemplate = adoTemplate; + commandType = cmdType; + commandText = cmdText; + this.resultSetExtractorDelegate = resultSetExtractorDelegate; + this.commandSetterDelegate = commandSetterDelegate; + } + + public string CommandText + { + get { return commandText; } + } + + public T DoInCommand(IDbCommand command) + { + IDataReader reader = null; + try + { + command.CommandType = commandType; + command.CommandText = commandText; + if (commandSetterDelegate != null) + { + commandSetterDelegate(command); + } + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + if (rse != null) + { + return rse.ExtractData(reader); + } + else + { + return resultSetExtractorDelegate(reader); + } + } + finally + { + AdoUtils.CloseReader(reader); + } + } + + + } + + internal class AdoRowMapperQueryCommandCallback : IDbCommandCallback> + { + private AdoTemplate adoTemplate; + private IRowMapper rowMapper; + private IDictionary returnedParameters; + + public AdoRowMapperQueryCommandCallback(AdoTemplate adoTemplate, IRowMapper rowMapper, IDictionary returnedParameters) + { + this.adoTemplate = adoTemplate; + this.rowMapper = rowMapper; + this.returnedParameters = returnedParameters; + } + + public IList DoInCommand(IDbCommand command) + { + IList objectList; + //Extract the single returned result set + IDataReader reader = null; + try + { + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + RowMapperResultSetExtractor rse = new RowMapperResultSetExtractor(rowMapper, 1); + objectList = rse.ExtractData(reader); + } + finally + { + AdoUtils.CloseReader(reader); + } + ParameterUtils.ExtractOutputParameters(returnedParameters, command); + return objectList; + } + } + + internal class AdoResultSetExtractorWithOutputParamsCommandCallback : IDbCommandCallback + { + private AdoTemplate adoTemplate; + private IResultSetExtractor rse; + private IDictionary returnedParameters; + + public AdoResultSetExtractorWithOutputParamsCommandCallback(AdoTemplate adoTemplate, IResultSetExtractor rse, IDictionary returnedParameters) + { + this.adoTemplate = adoTemplate; + this.rse = rse; + this.returnedParameters = returnedParameters; + } + + public T DoInCommand(IDbCommand command) + { + T returnVal; + //Extract the single returned result set + IDataReader reader = null; + try + { + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + returnVal = rse.ExtractData(reader); + } + finally + { + AdoUtils.CloseReader(reader); + } + ParameterUtils.ExtractOutputParameters(returnedParameters, command); + return returnVal; + } + } + + internal class BaseAdoResultProcessorsQueryCommandCallback + { + public static void ProcessNonGenericResultSetProcessor(NamedResultSetProcessor otherResultSetProcessor, IDataReader reader, IDictionary returnedResults) + { + string parameterName = otherResultSetProcessor.Name; + if (otherResultSetProcessor.ResultSetProcessor is IResultSetExtractor) + { + IResultSetExtractor rse = (IResultSetExtractor)otherResultSetProcessor.ResultSetProcessor; + object result = rse.ExtractData(reader); + returnedResults.Add(parameterName, result); + } + else if (otherResultSetProcessor.ResultSetProcessor is IRowMapper) + { + IRowMapper rowMapper = (IRowMapper)otherResultSetProcessor.ResultSetProcessor; + object result = (new RowMapperResultSetExtractor(rowMapper)).ExtractData(reader); + returnedResults.Add(parameterName, result); + } + else if (otherResultSetProcessor.ResultSetProcessor is IRowCallback) + { + IRowCallback rowCallback = (IRowCallback)otherResultSetProcessor.ResultSetProcessor; + (new RowCallbackResultSetExtractor(rowCallback)).ExtractData(reader); + returnedResults.Add(parameterName, "ResultSet returned was processed by an IRowCallback"); + } + } + + public static void ProcessFirstGenericResultSetProcessor(NamedResultSetProcessor firstResultSetProcessor, IDataReader reader, IDictionary returnedResults) + { + string parameterName = firstResultSetProcessor.Name; + if (firstResultSetProcessor.ResultSetExtractor != null) + { + IResultSetExtractor rse = firstResultSetProcessor.ResultSetExtractor; + T result = rse.ExtractData(reader); + returnedResults.Add(parameterName, result); + } + else if (firstResultSetProcessor.RowMapper != null) + { + IRowMapper rowMapper = firstResultSetProcessor.RowMapper; + object result = (new RowMapperResultSetExtractor(rowMapper)).ExtractData(reader); + returnedResults.Add(parameterName, result); + } + else if (firstResultSetProcessor.RowCallback != null) + { + IRowCallback rowCallback = firstResultSetProcessor.RowCallback; + (new RowCallbackResultSetExtractor(rowCallback)).ExtractData(reader); + returnedResults.Add(parameterName, "ResultSet returned was processed by an IRowCallback"); + } + } + } + + internal class AdoResultProcessorsQueryCommandCallback : BaseAdoResultProcessorsQueryCommandCallback, ICommandCallback + { + private AdoTemplate adoTemplate; + private IList namedResultSetProcessors; + + public AdoResultProcessorsQueryCommandCallback(AdoTemplate adoTemplate, IList namedResultSetProcessors) + { + this.adoTemplate = adoTemplate; + this.namedResultSetProcessors = namedResultSetProcessors; + } + + public object DoInCommand(IDbCommand command) + { + IDictionary returnedResults = new Hashtable(); + int resultSetIndex = 0; + IDataReader reader = null; + try + { + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + + //TODO On >= .NET 2.0 platforms make use of DbDataReader.HasRows property to + // see if there is a result set. now currently assuming matching + // NamedResultSetProcessor/ResultSet pairs. + + do + { + if (namedResultSetProcessors.Count == 0) + { + //We could just have output parameters and/or return value, that is, no result sets + //If we didn't register a result set processor, it is likely that a result set wasn't expected. + break; + } + NamedResultSetProcessor firstResultSetProcessor = null; + NamedResultSetProcessor otherResultSetProcessor = null; + try + { + if (resultSetIndex == 0) + { + firstResultSetProcessor + = namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor; + //Will have possibility of run-time type error if using QueryWithCommandCreator + if (firstResultSetProcessor == null) + { + LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex + + ", is not of expected type NamedResultSetProcessor. Type = " + + namedResultSetProcessors[resultSetIndex].GetType() + + "; Skipping processing for this result set."); + continue; + } + } + else + { + otherResultSetProcessor = + namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor; + if (otherResultSetProcessor == null) + { + + LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex + + ", is not of expected type NamedResultSetProcessor Type = " + + namedResultSetProcessors[resultSetIndex].GetType() + + "; Skipping processing for this result set."); + continue; + } + } + } + catch (IndexOutOfRangeException e) + { + LOG.Error("No NamedResultSetProcessor associated with result set index " + resultSetIndex, e); + continue; + } + catch (ArgumentOutOfRangeException e) + { + LOG.Error("No NamedResultSetProcessor associated with result set index " + resultSetIndex, e); + continue; + } + + + if (resultSetIndex == 0) + { + ProcessFirstGenericResultSetProcessor(firstResultSetProcessor, reader, returnedResults); + } + else + { + ProcessNonGenericResultSetProcessor(otherResultSetProcessor, reader, returnedResults); + } + resultSetIndex++; + + } while (reader.NextResult()); + } + finally + { + AdoUtils.CloseReader(reader); + } + ParameterUtils.ExtractOutputParameters(returnedResults, command); + return returnedResults; + } + } + + + internal class AdoResultProcessorsQueryCommandCallback : BaseAdoResultProcessorsQueryCommandCallback, ICommandCallback + { + private AdoTemplate adoTemplate; + private IList namedResultSetProcessors; + + public AdoResultProcessorsQueryCommandCallback(AdoTemplate adoTemplate, IList namedResultSetProcessors) + { + this.adoTemplate = adoTemplate; + this.namedResultSetProcessors = namedResultSetProcessors; + } + + public object DoInCommand(IDbCommand command) + { + IDictionary returnedResults = new Hashtable(); + int resultSetIndex = 0; + IDataReader reader = null; + try + { + reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader()); + + //TODO On >= .NET 2.0 platforms make use of DbDataReader.HasRows property to + // see if there is a result set. now currently assuming matching + // NamedResultSetProcessor/ResultSet pairs. + + do + { + if (namedResultSetProcessors.Count == 0) + { + //We could just have output parameters and/or return value, that is, no result sets + //If we didn't register a result set processor, it is likely that a result set wasn't expected. + break; + } + NamedResultSetProcessor firstResultSetProcessor = null; + NamedResultSetProcessor secondResultSetProcessor = null; + NamedResultSetProcessor otherResultSetProcessor = null; + try + { + if (resultSetIndex == 0) + { + firstResultSetProcessor = namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor; + //Will only have possibility of run-time type error if using QueryWithCommandCreator + if (firstResultSetProcessor == null) + { + + LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex + + ", is not of expected type NamedResultSetProcessor Type = " + + namedResultSetProcessors[resultSetIndex].GetType() + + "; Skipping processing for this result set."); + continue; + } + } else if (resultSetIndex == 1) + { + secondResultSetProcessor + = namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor; + if (secondResultSetProcessor == null) + { + + LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex + + ", is not of expected type NamedResultSetProcessor Type = " + + namedResultSetProcessors[resultSetIndex].GetType() + + "; Skipping processing for this result set."); + continue; + } + } else + { + otherResultSetProcessor = + namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor; + if (otherResultSetProcessor == null) + { + + LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex + + ", is not of expected type NamedResultSetProcessor Type = " + + namedResultSetProcessors[resultSetIndex].GetType() + + "; Skipping processing for this result set."); + continue; + } + } + + } + catch (IndexOutOfRangeException e) + { + LOG.Error("No NamedResultSetProcessor associated with result set index " + resultSetIndex, e); + continue; + } + catch (ArgumentOutOfRangeException e) + { + LOG.Error("No NamedResultSetProcessor associated with result set index " + resultSetIndex, e); + continue; + } + + if (resultSetIndex == 0) + { + ProcessFirstGenericResultSetProcessor(firstResultSetProcessor, reader, returnedResults); + } + else if (resultSetIndex == 1) + { + string parameterName = secondResultSetProcessor.Name; + if (secondResultSetProcessor.ResultSetExtractor != null) + { + IResultSetExtractor rse = secondResultSetProcessor.ResultSetExtractor; + U result = rse.ExtractData(reader); + returnedResults.Add(parameterName, result); + } + else if (secondResultSetProcessor.RowMapper != null) + { + IRowMapper rowMapper = secondResultSetProcessor.RowMapper; + object result = (new RowMapperResultSetExtractor(rowMapper)).ExtractData(reader); + returnedResults.Add(parameterName, result); + } + else if (secondResultSetProcessor.RowCallback != null) + { + //In this case a dummy type parameter would need to be specified a better alternative would + //be to to use the single type parameterization of this callback. + IRowCallback rowCallback = secondResultSetProcessor.RowCallback; + (new RowCallbackResultSetExtractor(rowCallback)).ExtractData(reader); + returnedResults.Add(parameterName, "ResultSet returned was processed by an IRowCallback"); + } + } + else + { + ProcessNonGenericResultSetProcessor(otherResultSetProcessor, reader, returnedResults); + } + resultSetIndex++; + + } while (reader.NextResult()); + } + finally + { + AdoUtils.CloseReader(reader); + } + ParameterUtils.ExtractOutputParameters(returnedResults, command); + return returnedResults; + } + + + } + #endregion + + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/CommandDelegate.cs b/src/Spring/Spring.Data/Data/Generic/CommandDelegate.cs new file mode 100644 index 00000000..f924380f --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/CommandDelegate.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System.Data.Common; + +namespace Spring.Data.Generic +{ + /// + /// Generic callback delegate for code that operates on a DbCommand. + /// + /// + ///

    Allows you to execute any number of operations + /// on a single DbCommand, for example a single ExecuteScalar + /// call or repeated execute calls with varying parameters. + ///

    + ///

    Used internally by AdoTemplate, but also useful for + /// application code. Note that the passed in DbCommand + /// has been created by the framework and will have its + /// Connection property set and the Transaction property + /// set based on the transaction context.

    + ///
    + /// The return type from executing the + /// callback + /// The ADO.NET DbCommand object + /// The object returned from processing with the + /// provided DbCommand + /// Mark Pollack + public delegate T CommandDelegate(DbCommand command); +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/DataAdapterDelegate.cs b/src/Spring/Spring.Data/Data/Generic/DataAdapterDelegate.cs new file mode 100644 index 00000000..2ac0ee66 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/DataAdapterDelegate.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System.Data; + +namespace Spring.Data.Generic +{ + /// + /// Called by AdoTemplate.Execute with an preconfigured + /// ADO.NET IDbDataAdapter instance with its SelectCommand + /// property populated with CommandType and Text values + /// along with Connection/Transaction properties based on the + /// calling transaction context. + /// + /// An active IDbDataAdapter instance + /// The result object + public delegate T DataAdapterDelegate(IDbDataAdapter dataAdapter); + +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/IAdoOperations.cs b/src/Spring/Spring.Data/Data/Generic/IAdoOperations.cs new file mode 100644 index 00000000..9c37910b --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/IAdoOperations.cs @@ -0,0 +1,237 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using Spring.Data.Common; + +namespace Spring.Data.Generic +{ + /// + /// Interface that defines ADO.NET related database operations using generics + /// + /// Mark Pollack (.NET) + /// $Id: IAdoOperations.cs,v 1.9 2007/12/04 06:48:21 markpollack Exp $ + public interface IAdoOperations : ICommonAdoOperations + { + + #region General Execute Callback Methods + + + /// + /// Execute a ADO.NET operation on a command object using a generic interface based callback. + /// + /// The type of object returned from the callback. + /// the callback to execute based on DbCommand + /// An object returned from callback + T Execute(ICommandCallback action); + + /// + /// Execute a ADO.NET operation on a command object using a generic interface based callback. + /// + /// The type of object returned from the callback. + /// The callback to execute based on IDbCommand + /// An object returned from callback + T Execute(IDbCommandCallback action); + + /// + /// Execute a ADO.NET operation on a command object using a generic delegate callback. + /// + /// The type of object returned from the callback. + /// This allows for implementing arbitrary data access operations + /// on a single command within Spring's managed ADO.NET environment. + /// The delegate called with a DbCommand object. + /// A result object returned by the action or null + T Execute(CommandDelegate del); + + /// + /// Execute a ADO.NET operation on a command object using a generic delegate callback. + /// + /// The type of object returned from the callback. + /// This allows for implementing arbitrary data access operations + /// on a single command within Spring's managed ADO.NET environment. + /// The delegate called with a IDbCommand object. + /// A result object returned by the action or null + T Execute(IDbCommandDelegate del); + + /// + /// Executes ADO.NET operations on a command object, created by the provided IDbCommandCreator, + /// using the interface based callback IDbCommandCallback. + /// + /// The type of object returned from the callback. + /// The command creator. + /// The callback to execute based on IDbCommand + /// A result object returned by the action or null + T Execute(IDbCommandCreator commandCreator, IDbCommandCallback action); + + /// + /// Execute ADO.NET operations on a IDbDataAdapter object using an interface based callback. + /// + /// This allows for implementing abritrary data access operations + /// on a single DataAdapter within Spring's managed ADO.NET environment. + /// + /// The type of object returned from the callback. + /// The data adapter callback. + /// A result object returned by the callback or null + T Execute(IDataAdapterCallback dataAdapterCallback); + + /// + /// Execute ADO.NET operations on a IDbDataAdapter object using an delgate based callback. + /// + /// This allows for implementing abritrary data access operations + /// on a single DataAdapter within Spring's managed ADO.NET environment. + /// + /// The type of object returned from the callback. + /// The delegate called with a IDbDataAdapter object. + /// A result object returned by the callback or null + T Execute(DataAdapterDelegate del); + + #endregion + + #region Queries with RowMapper + + IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper); + + IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, + ICommandSetter commandSetter); + + IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, + string parameterName, Enum dbType, int size, object parameterValue); + + IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, + IDbParameters parameters); + + + #endregion + + #region Queries with RowMapperDelegate + + IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate); + + IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, + ICommandSetter commandSetter); + + + IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, + string parameterName, Enum dbType, int size, object parameterValue); + + IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, + IDbParameters parameters); + + + #endregion + + #region Queries with ResultSetExtractor + + T QueryWithResultSetExtractor(CommandType cmdType, string cmdText, IResultSetExtractor resultSetExtractor); + + T QueryWithResultSetExtractor(CommandType cmdType, string cmdText, IResultSetExtractor resultSetExtractor, + string name, Enum dbType, int size, object parameterValue); + + T QueryWithResultSetExtractor(CommandType cmdType, string cmdText, IResultSetExtractor resultSetExtractor, + IDbParameters parameters); + + T QueryWithResultSetExtractor(CommandType cmdType, string cmdText, IResultSetExtractor resultSetExtractor, + ICommandSetter commandSetter); + + T QueryWithResultSetExtractor(CommandType cmdType, string cmdText, IResultSetExtractor resultSetExtractor, + CommandSetterDelegate commandSetterDelegate); + #endregion + + + #region Queries with ResultSetExtractorDelegate + + T QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractor); + + T QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractor, + string paramenterName, Enum dbType, int size, object parameterValue); + + T QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractor, + IDbParameters parameters); + + T QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractor, + ICommandSetter commandSetter); + + T QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractor, + CommandSetterDelegate commandSetterDelegate); + #endregion + + #region Queries for Object + + T QueryForObject(CommandType cmdType, string sql, IRowMapper rowMapper); + + + T QueryForObject(CommandType cmdType, string sql, IRowMapper rowMapper, ICommandSetter commandSetter); + + + T QueryForObject(CommandType cmdType, string sql, IRowMapper rowMapper, IDbParameters parameters); + + + T QueryForObject(CommandType cmdType, string sql, IRowMapper rowMapper, + string name, Enum dbType, int size, object parameterValue); + + + + #endregion + + #region Queries for ObjectDelegate + + T QueryForObjectDelegate(CommandType cmdType, string sql, RowMapperDelegate rowMapper); + + + T QueryForObjectDelegate(CommandType cmdType, string sql, RowMapperDelegate rowMapper, ICommandSetter commandSetter); + + + T QueryForObjectDelegate(CommandType cmdType, string sql, RowMapperDelegate rowMapper, IDbParameters parameters); + + + T QueryForObjectDelegate(CommandType cmdType, string sql, RowMapperDelegate rowMapper, + string name, Enum dbType, int size, object parameterValue); + + + + #endregion + + #region Query With CommandCreator + + T QueryWithCommandCreator(IDbCommandCreator cc, IResultSetExtractor rse); + + IList QueryWithCommandCreator(IDbCommandCreator cc, IRowMapper rowMapper); + + T QueryWithCommandCreator(IDbCommandCreator cc, IResultSetExtractor rse, IDictionary returnedParameters); + + IList QueryWithCommandCreator(IDbCommandCreator cc, IRowMapper rowMapper, + IDictionary returnedParameters); + + + IDictionary QueryWithCommandCreator(IDbCommandCreator cc, IList namedResultSetProcessors); + + IDictionary QueryWithCommandCreator(IDbCommandCreator cc, IList namedResultSetProcessors); + + #endregion + + + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/ICommandCallback.cs b/src/Spring/Spring.Data/Data/Generic/ICommandCallback.cs new file mode 100644 index 00000000..ccbf2ec2 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/ICommandCallback.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System.Data.Common; + +namespace Spring.Data.Generic +{ + + /// + /// Generic callback interface for code that operates on a + /// DbCommand. + /// + /// The return type from executing the + /// callback + /// + ///

    Allows you to execute any number of operations + /// on a single DbCommand, for example a single ExecuteScalar + /// call or repeated execute calls with varying parameters. + ///

    + ///

    Used internally by AdoTemplate, but also useful for + /// application code. Note that the passed in DbCommand + /// has been created by the framework and will have its + /// Connection property set and the Transaction property + /// set based on the transaction context.

    + ///
    + /// Mark Pollack + public interface ICommandCallback + { + /// + /// Called by AdoTemplate.Execute with an active ADO.NET IDbCommand. + /// The calling code does not need to care about closing the + /// command or the connection, or + /// about handling transactions: this will all be handled by + /// Spring's AdoTemplate + /// + /// An active IDbCommand instance + /// The result object + T DoInCommand(DbCommand command); + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/IDataAdapterCallback.cs b/src/Spring/Spring.Data/Data/Generic/IDataAdapterCallback.cs new file mode 100644 index 00000000..5663c770 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/IDataAdapterCallback.cs @@ -0,0 +1,62 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System.Data; + +namespace Spring.Data.Generic +{ + /// + /// Generic callback interface for code that operates on a + /// IDbDataAdapter. + /// + /// + ///

    Allows you to execute any number of operations + /// on a IDbDataAdapter, for example to Fill a DataSet + /// or other more advanced operations such as the transfering + /// data between two different DataSets. + ///

    + ///

    Note that the passed in IDbDataAdapter + /// has been created by the framework and its SelectCommand + /// will be populated with values for CommandType and Text properties + /// along with Connection/Transaction properties based on the + /// calling transaction context. + ///

    + /// Execute(IDataAdapterCallback dataAdapterCallback) + /// method. + ///
    + /// Mark Pollack (.NET) + /// $Id: IDataAdapterCallback.cs,v 1.1 2007/12/04 06:48:21 markpollack Exp $ + public interface IDataAdapterCallback + { + /// + /// Called by AdoTemplate.Execute with an preconfigured + /// ADO.NET IDbDataAdapter instance with its SelectCommand + /// property populated with CommandType and Text values + /// along with Connection/Transaction properties based on the + /// calling transaction context. + /// + /// An active IDbDataAdapter instance + /// The result object + T DoInDataAdapter(IDbDataAdapter dataAdapter); + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/IDbCommandCallback.cs b/src/Spring/Spring.Data/Data/Generic/IDbCommandCallback.cs new file mode 100644 index 00000000..d23859b9 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/IDbCommandCallback.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System.Data; + +namespace Spring.Data.Generic +{ + /// + /// Generic callback interface for code that operates on a + /// IDbCommand. + /// + /// The return type from executing the + /// callback + /// + ///

    Allows you to execute any number of operations + /// on a single IDbCommand, for example a single ExecuteScalar + /// call or repeated execute calls with varying parameters. + ///

    + ///

    Used internally by AdoTemplate, but also useful for + /// application code. Note that the passed in IDbCommand + /// has been created by the framework and will have its + /// Connection property set and the Transaction property + /// set based on the transaction context.

    + ///

    As an alternative to using this interface you can use + /// the delegate version which is particularly nice when using + /// anonymous delegates for writing more terse code and having + /// easy access to the variables in the calling class.

    + ///

    See for a version that has the + /// base class DbCommand as the callback argument.

    + ///
    + /// Mark Pollack + public interface IDbCommandCallback + { + /// + /// Called by AdoTemplate.Execute with an active ADO.NET IDbCommand. + /// The calling code does not need to care about closing the + /// command or the connection, or + /// about handling transactions: this will all be handled by + /// Spring's AdoTemplate + /// + /// An active IDbCommand instance + /// The result object + T DoInCommand(IDbCommand command); + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/IDbCommandDelegate.cs b/src/Spring/Spring.Data/Data/Generic/IDbCommandDelegate.cs new file mode 100644 index 00000000..f8751d84 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/IDbCommandDelegate.cs @@ -0,0 +1,50 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System.Data; + +namespace Spring.Data.Generic +{ + /// + /// Generic callback delegate for code that operates on a IDbCommand. + /// + /// + ///

    Allows you to execute any number of operations + /// on a single IDbCommand, for example a single ExecuteScalar + /// call or repeated execute calls with varying parameters. + ///

    + ///

    Used internally by AdoTemplate, but also useful for + /// application code. Note that the passed in DbCommand + /// has been created by the framework and will have its + /// Connection property set and the Transaction property + /// set based on the transaction context.

    + ///
    + /// The return type from executing the + /// callback + /// The ADO.NET DbCommand object + /// The object returned from processing with the + /// provided DbCommand + /// Mark Pollack + /// $Id: IDbCommandDelegate.cs,v 1.1 2007/06/27 21:41:57 markpollack Exp $ + public delegate T IDbCommandDelegate(IDbCommand command); +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/IResultSetExtractor.cs b/src/Spring/Spring.Data/Data/Generic/IResultSetExtractor.cs new file mode 100644 index 00000000..810b0114 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/IResultSetExtractor.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System.Data; + +namespace Spring.Data.Generic +{ + /// + /// Callback interface to process all results sets and rows in + /// an AdoTemplate query method. + /// + /// Implementations of this interface perform the work + /// of extracting results but don't need worry about managing + /// ADO.NET resources, such as closing the reader. + ///

    + /// This interface is mainly used within the ADO.NET + /// framework. An IResultSetExtractor is usually a simpler choice + /// for result set (DataReader) processing, in particular a + /// RowMapperResultSetExtractor in combination with a IRowMapper. + ///

    + /// Note: in contracts to a IRowCallbackHandler, a ResultSetExtractor + /// is usually stateless and thus reusable, as long as it doesn't access + /// stateful resources or keep result state within the object. + /// + ///
    + /// + public interface IResultSetExtractor + { + /// + /// Implementations must implement this method to process all + /// result set and rows in the IDataReader. + /// + /// The IDataReader to extract data from. + /// Implementations should not close this: it will be closed + /// by the AdoTemplate. + /// An arbitrary result object or null if none. The + /// extractor will typically be stateful in the latter case. + T ExtractData(IDataReader reader); + + + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Generic/IRowMapper.cs b/src/Spring/Spring.Data/Data/Generic/IRowMapper.cs new file mode 100644 index 00000000..e4e635f2 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/IRowMapper.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System.Data; + +namespace Spring.Data.Generic +{ + /// + /// Generic callback to process each row of data in a result set to an object. + /// + /// Implementations of this interface perform the actual work + /// of mapping rows, but don't need worry about managing + /// ADO.NET resources, such as closing the reader. + ///

    + /// Typically used for AdoTemplate's query methods (with RowMapperResultSetExtractor + /// adapters). RowMapper objects are typically stateless and thus + /// reusable; they are ideal choices for implementing row-mapping logic in a single + /// place. + ///

    + ///

    Alternatively, consider subclassing MappingSqlQuery from the Spring.Data.Object + /// namespace: Instead of working with separate AdoTemplate and RowMapper objects, + /// you can have executable query objects (containing row-mapping logic) there. + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: IRowMapper.cs,v 1.4 2007/06/28 18:48:18 markpollack Exp $ + public interface IRowMapper + { + /// + /// Implementations must implement this method to map each row of data in the + /// result set (DataReader). This method should not call Next() on the + /// DataReader; it should only extract the values of the current row. + /// + /// The IDataReader to map (pre-initialized to the current row) + /// The number of the current row.. + /// The specific typed result object for the current row. + T MapRow(IDataReader reader, int rowNum); + } +} + +#endif diff --git a/src/Spring/Spring.Data/Data/Generic/NamedResultSetProcessor.cs b/src/Spring/Spring.Data/Data/Generic/NamedResultSetProcessor.cs new file mode 100644 index 00000000..31dc1d57 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/NamedResultSetProcessor.cs @@ -0,0 +1,123 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + + + +#endregion + +using Spring.Data.Support; + +namespace Spring.Data.Generic +{ + /// + /// Provides a name to a ResultSetProcessor for use with AdoOperation subclasses + /// such as StoredProcedure. + /// + /// Mark Pollack (.NET) + /// $Id: NamedResultSetProcessor.cs,v 1.2 2007/07/25 13:32:31 oakinger Exp $ + public class NamedResultSetProcessor + { + #region Fields + + private IRowCallback rowCallback; + private IRowMapper rowMapper; + private IResultSetExtractor resultSetExtractor; + private string name; + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class with a + /// IRowCallback instance + /// + /// The name of the associated row callback for use with output + /// result set mappers. + /// A row callback instance. + public NamedResultSetProcessor(string name, IRowCallback rowcallback) + { + this.name = name; + this.rowCallback = rowcallback; + } + + /// + /// Initializes a new instance of the class with a + /// IRowMapper instance + /// + /// The name of the associated row mapper. + /// A IRowMapper instance. + public NamedResultSetProcessor(string name, IRowMapper rowMapper) + { + this.name = name; + this.rowMapper = rowMapper; + } + + /// + /// Initializes a new instance of the class with a + /// IResultSetExtractor instance + /// + /// The name of the associated result set extractor. + /// A IResultSetExtractor instance. + public NamedResultSetProcessor(string name, IResultSetExtractor resultSetExtractor) + { + this.name = name; + this.resultSetExtractor = resultSetExtractor; + } + + + #endregion + + #region Properties + public string Name + { + get + { + return name; + } + } + + + public IRowCallback RowCallback + { + get { return rowCallback; } + } + + public IRowMapper RowMapper + { + get { return rowMapper; } + } + + public IResultSetExtractor ResultSetExtractor + { + get { return resultSetExtractor; } + } + + #endregion + + + } +} + +#endif diff --git a/src/Spring/Spring.Data/Data/Generic/ResultSetExtractorDelegate.cs b/src/Spring/Spring.Data/Data/Generic/ResultSetExtractorDelegate.cs new file mode 100644 index 00000000..baa348e0 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/ResultSetExtractorDelegate.cs @@ -0,0 +1,40 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System.Data; + +namespace Spring.Data.Generic +{ + + /// + /// Callback delegate to process all result sets and row in an + /// AdoTemplate query method. + /// + /// The type returned from the result set mapping. + /// The IDataReader to extract data from. + /// Implementations should not close this: it will be closed + /// by the AdoTemplate. + /// An arbitrary result object or null if none. + public delegate T ResultSetExtractorDelegate(IDataReader reader); +} + +#endif diff --git a/src/Spring/Spring.Data/Data/Generic/RowMapperDelegate.cs b/src/Spring/Spring.Data/Data/Generic/RowMapperDelegate.cs new file mode 100644 index 00000000..6a0fea16 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/RowMapperDelegate.cs @@ -0,0 +1,38 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +using System.Data; + +namespace Spring.Data.Generic +{ + /// + /// Callback delegate to process each row of data in a result set to an object. + /// + /// The type of the object returned from the mapping operation. + /// The IDataReader to map + /// the number of the current row. + /// An abrirary object, typically derived from data + /// in the result set. + public delegate T RowMapperDelegate(IDataReader dataReader, int rowNum); +} + +#endif diff --git a/src/Spring/Spring.Data/Data/Generic/RowMapperResultSetExtractor.cs b/src/Spring/Spring.Data/Data/Generic/RowMapperResultSetExtractor.cs new file mode 100644 index 00000000..b0ae3e42 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Generic/RowMapperResultSetExtractor.cs @@ -0,0 +1,151 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections.Generic; +using System.Data; +using Spring.Data.Support; +using Spring.Util; + +#endregion + +namespace Spring.Data.Generic +{ + /// + /// Adapter implementation of the ResultSetExtractor interface that delegates + /// to a RowMapper which is supposed to create an object for each row. + /// Each object is added to the results List of this ResultSetExtractor. + /// + /// + /// Useful for the typical case of one object per row in the database table. + /// The number of entries in the results list will match the number of rows. + ///

    + /// Note that a RowMapper object is typically stateless and thus reusable; + /// just the RowMapperResultSetExtractor adapter is stateful. + ///

    + ///

    + /// As an alternative consider subclassing MappingAdoQuery from the + /// Spring.Data.Objects namespace: Instead of working with separate + /// AdoTemplate and IRowMapper objects you can have executable + /// query objects (containing row-mapping logic) there. + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: RowMapperResultSetExtractor.cs,v 1.4 2007/08/03 19:51:10 markpollack Exp $ + public class RowMapperResultSetExtractor : IResultSetExtractor> + { + #region Fields + + private IRowMapper rowMapper; + + private RowMapperDelegate rowMapperDelegate; + + private int rowsExpected; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public RowMapperResultSetExtractor(IRowMapper rowMapper) : this(rowMapper,0, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The row mapper. + /// The rows expected. + public RowMapperResultSetExtractor(IRowMapper rowMapper, int rowsExpected) : this(rowMapper, rowsExpected, null) + { + } + public RowMapperResultSetExtractor(IRowMapper rowMapper, int rowsExpected, IDataReaderWrapper dataReaderWrapper) + { + //TODO use datareaderwrapper + AssertUtils.ArgumentNotNull(rowMapper, "rowMapper"); + + this.rowMapper = rowMapper; + this.rowsExpected = rowsExpected; + } + + public RowMapperResultSetExtractor(RowMapperDelegate rowMapperDelegate) + : this(rowMapperDelegate, 0, null) + { + } + + public RowMapperResultSetExtractor(RowMapperDelegate rowMapperDelegate, int rowsExpected) + : this(rowMapperDelegate, rowsExpected, null) + { + } + public RowMapperResultSetExtractor(RowMapperDelegate rowMapperDelegate, int rowsExpected, IDataReaderWrapper dataReaderWrapper) + { + //TODO use datareaderwrapper + + AssertUtils.ArgumentNotNull(rowMapperDelegate, "rowMapperDelegate"); + + this.rowMapperDelegate = rowMapperDelegate; + this.rowsExpected = rowsExpected; + } + + #endregion + + #region IResultSetExtractor Members + + public IList ExtractData(IDataReader reader) + { + // Use the more efficient collection if we know how many rows to expect: + // ArrayList in case of a known row count, LinkedList if unknown + //IList results = (rowsExpected > 0) ? new List(rowsExpected) : new LinkedList(); + + //how come LinkedList doesn't implement IList ?!?!?! + //some web entries claim slow indexer... need to write our own again? return ICollection instead? + //http://blogs.msdn.com/kcwalina/archive/2005/09/23/Collections.aspx + //We did not implement IList on LinkedList because the indexer would be + //very slow. If you really need the interface, you probably can inherit from + //LinkedList and implement the interface on the subtype. + IList results = new List(); + int rowNum = 0; + if (rowMapper != null) + { + while (reader.Read()) + { + results.Add(rowMapper.MapRow(reader, rowNum++)); + } + } + else + { + while (reader.Read()) + { + results.Add(rowMapperDelegate(reader, rowNum++)); + } + } + + return results; + } + + #endregion + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/IAdoOperations.cs b/src/Spring/Spring.Data/Data/IAdoOperations.cs new file mode 100644 index 00000000..c22b9cff --- /dev/null +++ b/src/Spring/Spring.Data/Data/IAdoOperations.cs @@ -0,0 +1,747 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data +{ + /// + /// Interface that defines ADO.NET related database operations. + /// + /// Mark Pollack (.NET) + /// $Id: IAdoOperations.cs,v 1.24 2007/12/04 06:52:19 markpollack Exp $ + public interface IAdoOperations : ICommonAdoOperations + { + + #region General Execute Methods with Callbacks + /// + /// Execute a ADO.NET operation on a command object using a delegate callback. + /// + /// This allows for implementing arbitrary data access operations + /// on a single command within Spring's managed ADO.NET environment. + /// The delegate called with a command object. + /// A result object returned by the action or null + object Execute(CommandDelegate del); + + /// + /// Execute a ADO.NET operation on a command object using an interface based callback. + /// + /// the callback to execute + /// object returned from callback + object Execute(ICommandCallback action); + + /// + /// Executes ADO.NET operations on a command object, created by the provided IDbCommandCreator, + /// using the interface based callback IDbCommandCallback. + /// + /// The command creator. + /// The callback to execute based on IDbCommand + /// A result object returned by the action or null + object Execute(IDbCommandCreator commandCreator, ICommandCallback action); + + /// + /// Execute ADO.NET operations on a IDbDataAdapter object using an interface based callback. + /// + /// This allows for implementing abritrary data access operations + /// on a single DataAdapter within Spring's managed ADO.NET environment. + /// + /// The data adapter callback. + /// A result object returned by the callback or null + object Execute(IDataAdapterCallback dataAdapterCallback); + + #endregion + + + + + + #region Queries With ResultSetExtractor + + // Static Queries + + /// + /// Execute a query given IDbCommand's type and text, processing a + /// single result set with an instance of IResultSetExtractor + /// + /// The type of command + /// The text of the query. + /// Object that will extract all rows of a result set + /// An arbitrary result object, as returned by the IResultSetExtractor + /// + /// If there is any problem executing the query. + /// + object QueryWithResultSetExtractor(CommandType cmdType, string cmdText, IResultSetExtractor resultSetExtractor); + + + // Parameterized Queries + + + //only input parameters can be used... + + + /// + /// Execute a query given IDbCommand's type and text with parameters set + /// via the command setter, processing a + /// single result set with an instance of IResultSetExtractor + /// + /// The command type. + /// The command text to execute. + /// The result set extractor. + /// The command setter. + /// An arbitrary result object, as returned by the IResultSetExtractor + /// + /// If there is any problem executing the query. + /// + object QueryWithResultSetExtractor(CommandType cmdType, string cmdText, IResultSetExtractor resultSetExtractor, + ICommandSetter commandSetter); + + //only input parameters can be used. + object QueryWithResultSetExtractor(CommandType commandType, string cmdText, IResultSetExtractor resultSetExtractor, + string parameterName, Enum dbType, int size, object parameterValue); + + //iterate over one or more result sets and then assign output parameters. + object QueryWithResultSetExtractor(CommandType commandType, string cmdText, IResultSetExtractor resultSetExtractor, + IDbParameters parameters); + + + #endregion + + #region Queries With ResultSetExtractorDelegate + + // Static Queries + + /// + /// Execute a query given static SQL/Stored Procedure name + /// and process a single result set with an instance of ResultSetExtractorDelegate + /// + /// The type of command. + /// The command text. + /// Delegate that will extract all rows of a result set + /// An arbitrary result object, as returned by the IResultSetExtractor + /// + /// If there is any problem executing the query. + /// + object QueryWithResultSetExtractorDelegate(CommandType cmdType, string cmdText, ResultSetExtractorDelegate resultSetExtractorDelegate); + + + // Parameterized Queries + + + //only input parameters can be used... + object QueryWithResultSetExtractorDelegate(CommandType commandType, string sql, ResultSetExtractorDelegate resultSetExtractorDelegate, + ICommandSetter commandSetter); + + //only input parameters can be used. + object QueryWithResultSetExtractorDelegate(CommandType commandType, string sql, ResultSetExtractorDelegate resultSetExtractorDelegate, + string parameterName, Enum dbType, int size, object parameterValue); + + //iterate over one or more result sets and then assign output parameters. + object QueryWithResultSetExtractorDelegate(CommandType commandType, string sql, ResultSetExtractorDelegate resultSetExtractorDelegate, + IDbParameters parameter); + + + #endregion + + #region Queries with RowMapperDelegate + + IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelgate); + + IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelgate, ICommandSetter commandSetter); + + IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelgate, + string parameterName, Enum dbType, int size, object parameterValue); + + IList QueryWithRowMapperDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelgate, IDbParameters parameter); + + + #endregion + + #region Queries with RowMapper + + // Static Queries + + + /// + /// Execute a query given static SQL, mapping each row to a .NET object + /// iva a RowMapper + /// + /// The type of command + /// SQL query to execute + /// object that will map one object per row + /// the result list containing mapped objects + IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper); + + // Parameterized Queries + + //Using IRowMapper to process 1 result set + IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, ICommandSetter commandSetter); + + IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, + string name, Enum dbType, int size, object parameterValue); + + IList QueryWithRowMapper(CommandType cmdType, string cmdText, IRowMapper rowMapper, IDbParameters parameter); + + + + #endregion + + #region Query for Object + + + + + + /// + /// Execute a query with the specified command text, mapping a single result + /// row to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// object that will map one object per row + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + object QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper); + + + /// + /// Execute a query with the specified command text and parameters set via the + /// command setter, mapping a single result row to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// object that will map one object per row + /// The command setter. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + object QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper, ICommandSetter commandSetter); + + /// + /// Execute a query with the specified command text and parameters, mapping a single result row + /// to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// object that will map one object per row + /// The parameter collection to use in the query. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + object QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper, IDbParameters parameters); + + /// + /// Execute a query with the specified command text and parameter, mapping a single result row + /// to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// object that will map one object per row + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + object QueryForObject(CommandType cmdType, string cmdText, IRowMapper rowMapper, + string parameterName, Enum dbType, int size, object parameterValue); + + + /* + /// + /// Execute a query for a result list, given static SQL. + /// + /// + /// The results will be mapped to a List (one entry for each row) + /// of IDictionaries (one entry for each column using he column + /// name as the key). Each element in the list will be of the form + /// returned by ths interfaces QueryForMap() methods. + /// + /// The type of the command. + /// The SQL query to execute + /// + IList QueryForList(CommandType cmdType, string sql); + */ + //IDictionary QueryForDictionary(CommandType cmdType, string sql); + //TODO - + //Object QueryForObject(CommandType cmdType, String sql, Type requiredType) throws DataAccessException; + //IDictionary QueryForDictionary + //IList QueryForList + + #endregion + + #region Query for ObjectDelegate + + /// + /// Execute a query with the specified command text, mapping a single result + /// row to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// delegate that will map one object per row + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + object QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelgate); + + + + /// + /// Execute a query with the specified command text and parameters set via the + /// command setter, mapping a single result row to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// delegate that will map one object per row + /// The command setter. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + object QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, ICommandSetter commandSetter); + + + /// + /// Execute a query with the specified command text and parameters, mapping a single result row + /// to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// delegate that will map one object per row + /// The parameter collection to use in the query. + /// The single mapped object. + /// + /// If the query does not return exactly one row. + /// + /// + /// If there is any problem executing the query. + /// + object QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, IDbParameters parameters); + + + /// + /// Execute a query with the specified command text and parameter, mapping a single result row + /// to an object via a RowMapper. + /// + /// The command type. + /// The command text to execute. + /// delegate that will map one object per row + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + /// + object QueryForObjectDelegate(CommandType cmdType, string cmdText, RowMapperDelegate rowMapperDelegate, + string parameterName, Enum dbType, int size, object parameterValue); + + + #endregion + + #region Query With CommandCreator + + // Using IResultSetExtractor to process one result set. + object QueryWithCommandCreator(IDbCommandCreator commandCreator, IResultSetExtractor resultSetExtractor); + + IList QueryWithCommandCreator(IDbCommandCreator cc, IRowMapper rowMapper); + + // Using IResultSetExtractor to return one result set, multiple output parameters. + object QueryWithCommandCreator(IDbCommandCreator commandCreator, IResultSetExtractor resultSetExtractor, IDictionary returnedParameters); + + // Using IRowMapper to return one result set, multiple output parameters. + IList QueryWithCommandCreator(IDbCommandCreator commandCreator, IRowMapper rowMapper, IDictionary returnedParameters); + + + // Multiple return result sets, (A list of lists) + // each with either a IRowMapper, IResultSetExtractor, IRowCallback + // and multiple output parameters. + IDictionary QueryWithCommandCreator(IDbCommandCreator commandCreator, IList resultProcessors); + + //TODO + //Object QueryForObject(... Type requiredType) + //IDictionary QueryForDictionary + //IList QueryForList + + #endregion + + // *************************************************************** + // NB: Complex DataSet/DataTable operations are best handled with + // DataSetOperations class in Spring.Data.Objects + // *************************************************************** + + #region DataTable Create operations without parameters + + DataTable DataTableCreate(CommandType commandType, string sql); + + DataTable DataTableCreate(CommandType commandType, string sql, + string tableMappingName); + + DataTable DataTableCreate(CommandType commandType, string sql, + ITableMapping tableMapping); + + DataTable DataTableCreate(CommandType commandType, string sql, + ITableMapping tableMapping, + IDataAdapterSetter setter); + + #endregion + + #region DataTable Create operations with parameters + + DataTable DataTableCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters); + + DataTable DataTableCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + string tableMappingName); + + DataTable DataTableCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + ITableMapping tableMapping); + + DataTable DataTableCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + ITableMapping tableMapping, + IDataAdapterSetter dataAdapterSetter); + + + //TODO + /* + DataTable DataTableCreateWithParams(IDbDataAdapterCreator dataAdapterCreator); + */ + + #endregion + + #region DataTable Fill operations without parameters + /// + /// Fill a based on a select command that requires no parameters. + /// + /// The to populate + /// The type of command + /// SQL query to execute + /// The number of rows successfully added to or refreshed in the + int DataTableFill(DataTable dataTable, CommandType commandType, string sql); + + int DataTableFill(DataTable dataTable, CommandType commandType, string sql, + string tableMappingName); + + int DataTableFill(DataTable dataTable, CommandType commandType, string sql, + ITableMapping tableMapping); + + int DataTableFill(DataTable dataTable, CommandType commandType, string sql, + ITableMapping tableMapping, + IDataAdapterSetter setter); + + + #endregion + + #region DataTable Fill operations with parameters + + int DataTableFillWithParams(DataTable dataTable, CommandType commandType, string sql, + IDbParameters parameters); + + int DataTableFillWithParams(DataTable dataTable, CommandType commandType, string sql, + IDbParameters parameters, + string tableName); + + int DataTableFillWithParams(DataTable dataTable, CommandType commandType, string sql, + IDbParameters parameters, + ITableMapping tableMapping); + + int DataTableFillWithParams(DataTable dataTable, CommandType commandType, string sql, + IDbParameters parameters, + ITableMapping tableMapping, + IDataAdapterSetter dataAdapterSetter); + + //TODO + /* + int DataTableFillWithParams(DataTable dataTable, + IDbDataAdapterCreator dataAdapterCreator); + + */ + #endregion + + #region DataTable Update operations + //TODO conflict options... + + int DataTableUpdateWithCommandBuilder(DataTable dataTable, + CommandType commandType, + string selectSql, + IDbParameters parameters, + string tableName); + + int DataTableUpdateWithCommandBuilder(DataTable dataTable, + CommandType commandType, + string selectSql, + IDbParameters parameters, + string tableName, + IDataAdapterSetter dataAdapterSetter); + + int DataTableUpdateWithCommandBuilder(DataTable dataTable, + CommandType commandType, + string selectSql, + IDbParameters parameters, + ITableMapping tableMapping, + IDataAdapterSetter dataAdapterSetter); + + int DataTableUpdate(DataTable dataTable, + string tableName, + CommandType insertCommandtype, string insertSql, IDbParameters insertParameters, + CommandType updateCommandtype, string updateSql, IDbParameters updateParameters, + CommandType deleteCommandtype, string deleteSql, IDbParameters deleteParameters); + + int DataTableUpdate(DataTable dataTable, + string tableName, + CommandType insertCommandtype, string insertSql, IDbParameters insertParameters, + CommandType updateCommandtype, string updateSql, IDbParameters updateParameters, + CommandType deleteCommandtype, string deleteSql, IDbParameters deleteParameters, + IDataAdapterSetter dataAdapterSetter); + + int DataTableUpdate(DataTable dataTable, + ITableMapping tableMapping, + CommandType insertCommandtype, string insertSql, IDbParameters insertParameters, + CommandType updateCommandtype, string updateSql, IDbParameters updateParameters, + CommandType deleteCommandtype, string deleteSql, IDbParameters deleteParameters, + IDataAdapterSetter dataAdapterSetter); + + + + #endregion + + #region DataSet Create operations without parameters + + DataSet DataSetCreate(CommandType commandType, string sql); + + DataSet DataSetCreate(CommandType commandType, string sql, + string[] tableNames); + + DataSet DataSetCreate(CommandType commandType, string sql, + ITableMappingCollection tableMapping); + + DataSet DataSetCreate(CommandType commandType, string sql, + ITableMappingCollection tableMapping, + IDataAdapterSetter setter); + + DataSet DataSetCreate(CommandType commandType, string sql, + ITableMappingCollection tableMapping, + IDataAdapterSetter setter, + IDataSetFillLifecycleProcessor fillLifecycleProcessor); + #endregion + + #region DataSet Create operations with parameters + + DataSet DataSetCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters); + + DataSet DataSetCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + string[] tableNames); + + DataSet DataSetCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping); + + DataSet DataSetCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping, + IDataAdapterSetter dataAdapterSetter); + + DataSet DataSetCreateWithParams(CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping, + IDataAdapterSetter dataAdapterSetter, + IDataSetFillLifecycleProcessor fillLifecycleProcessor); + + #endregion + + #region DataSet Fill operations without parameters + + /// + /// Fill a based on a select command that requires no parameters. + /// + /// The to populate + /// The type of command + /// SQL query to execute + /// The number of rows successfully added to or refreshed in the + int DataSetFill(DataSet dataSet, CommandType commandType, string sql); + + /// + /// Fill a based on a select command that requires no parameters + /// that returns one or more result sets that are added as + /// s to the + /// + /// The to populate + /// The type of command + /// SQL query to execute + /// The mapping of table names for each + /// created + /// + int DataSetFill(DataSet dataSet, CommandType commandType, string sql, + string[] tableNames); + + int DataSetFill(DataSet dataSet, CommandType commandType, string sql, + ITableMappingCollection tableMapping); + + int DataSetFill(DataSet dataSet, CommandType commandType, string sql, + ITableMappingCollection tableMapping, + IDataAdapterSetter setter); + + int DataSetFill(DataSet dataSet, CommandType commandType, string sql, + ITableMappingCollection tableMapping, + IDataAdapterSetter setter, + IDataSetFillLifecycleProcessor fillLifecycleProcessor); + + #endregion + + #region DataSet Fill operations with parameters + + int DataSetFillWithParameters(DataSet dataSet, CommandType commandType, string sql, + IDbParameters parameters); + + int DataSetFillWithParameters(DataSet dataSet, CommandType commandType, string sql, + IDbParameters parameters, + string[] tableNames); + + int DataSetFillWithParameters(DataSet dataSet, CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping); + + int DataSetFillWithParameters(DataSet dataSet, CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping, + IDataAdapterSetter dataAdapterSetter); + + int DataSetFillWithParameters(DataSet dataSet, CommandType commandType, string sql, + IDbParameters parameters, + ITableMappingCollection tableMapping, + IDataAdapterSetter dataAdapterSetter, + IDataSetFillLifecycleProcessor fillLifecycleProcessor); + + #endregion + + #region DataSet Update operations + //TODO expose RowUpdate event in a generic manner + //TODO any way to integrate Accept/RejectChanges based on tx outcome? + + int DataSetUpdateWithCommandBuilder(DataSet dataSet, + CommandType commandType, + string selectSql, + IDbParameters selectParameters, + string tableName); + + int DataSetUpdateWithCommandBuilder(DataSet dataSet, + CommandType commandType, + string selectSql, + IDbParameters selectParameters, + string tableName, + IDataAdapterSetter dataAdapterSetter); + + int DataSetUpdateWithCommandBuilder(DataSet dataSet, + CommandType commandType, + string selectSql, + IDbParameters selectParameters, + ITableMappingCollection tableMapping, + IDataAdapterSetter dataAdapterSetter); + + + int DataSetUpdate(DataSet dataSet, + string tableName, + IDbCommand insertCommand, + IDbCommand updateCommand, + IDbCommand deleteCommand); + + + int DataSetUpdate(DataSet dataSet, + string tableName, + CommandType insertCommandtype, string insertSql, IDbParameters insertParameters, + CommandType updateCommandtype, string updateSql, IDbParameters updateParameters, + CommandType deleteCommandtype, string deleteSql, IDbParameters deleteParameters); + + + int DataSetUpdate(DataSet dataSet, + string tableName, + IDbCommand insertCommand, + IDbCommand updateCommand, + IDbCommand deleteCommand, + IDataAdapterSetter dataAdapterSetter); + + int DataSetUpdate(DataSet dataSet, + ITableMappingCollection tableMapping, + IDbCommand insertCommand, + IDbCommand updateCommand, + IDbCommand deleteCommand); + + int DataSetUpdate(DataSet dataSet, + ITableMappingCollection tableMapping, + IDbCommand insertCommand, + IDbCommand updateCommand, + IDbCommand deleteCommand, + IDataAdapterSetter dataAdapterSetter); + + #endregion + + #region Parameter Creation Helper Methods + + IDbParameters CreateDbParameters(); + + /// + /// Note that output parameters are marked input/output after derivation.... + /// (TODO - double check....) + /// + /// + /// + IDataParameter[] DeriveParameters(string procedureName); + + IDataParameter[] DeriveParameters(string procedureName, bool includeReturnParameter); + + #endregion + } + + +} diff --git a/src/Spring/Spring.Data/Data/ICommandCallback.cs b/src/Spring/Spring.Data/Data/ICommandCallback.cs new file mode 100644 index 00000000..3ba7f388 --- /dev/null +++ b/src/Spring/Spring.Data/Data/ICommandCallback.cs @@ -0,0 +1,53 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Data; + +namespace Spring.Data +{ + /// + /// Generic callback interface for code that operates on a + /// IDbCommand. + /// + /// + ///

    Allows you to execute any number of operations + /// on a single IDbCommand, for example a single ExecuteScalar + /// call or repeated execute calls with varying parameters. + ///

    + ///

    Used internally by AdoTemplate, but also useful for + /// application code. Note that the passed in IDbCommand + /// has been created by the framework.

    + ///
    + /// Mark Pollack + public interface ICommandCallback + { + /// + /// Called by AdoTemplate.Execute with an active ADO.NET IDbCommand. + /// The calling code does not need to care about closing the + /// command or the connection, or + /// about handling transactions: this will all be handled by + /// Spring's AdoTemplate + /// + /// An active IDbCommand instance + /// The result object + object DoInCommand(IDbCommand command); + } +} diff --git a/src/Spring/Spring.Data/Data/ICommandSetter.cs b/src/Spring/Spring.Data/Data/ICommandSetter.cs new file mode 100644 index 00000000..71d3ce58 --- /dev/null +++ b/src/Spring/Spring.Data/Data/ICommandSetter.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Data; + +#endregion + +namespace Spring.Data +{ + + /// + /// Called by the AdoTemplate class to allow implementations + /// to set any necessary parameters on the command object. + /// The CommandType and CommandText will have already been supplied. + /// + /// Mark Pollack (.NET) + /// $Id: ICommandSetter.cs,v 1.5 2006/12/02 07:35:23 markpollack Exp $ + public interface ICommandSetter + { + + + void SetValues(IDbCommand dbCommand); + } +} diff --git a/src/Spring/Spring.Data/Data/ICommandTextProvider.cs b/src/Spring/Spring.Data/Data/ICommandTextProvider.cs new file mode 100644 index 00000000..9d19b423 --- /dev/null +++ b/src/Spring/Spring.Data/Data/ICommandTextProvider.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Data +{ + /// + /// Interface to be implemented by objects that can provide SQL strings + /// + /// Typically implemented by IDbCommandCreator and + /// ICommandCallbacks that want to expose the CommandText they + /// use to create their ADO.NET commands, to allow for better + /// contextual information in case of exceptions + /// Mark Pollack(.NET) + /// Juergen Hoeller + public interface ICommandTextProvider + { + string CommandText + { + get; + } + } +} diff --git a/src/Spring/Spring.Data/Data/ICommonAdoOperations.cs b/src/Spring/Spring.Data/Data/ICommonAdoOperations.cs new file mode 100644 index 00000000..18658692 --- /dev/null +++ b/src/Spring/Spring.Data/Data/ICommonAdoOperations.cs @@ -0,0 +1,248 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data +{ + public interface ICommonAdoOperations + { + #region ExecuteScalar + + /// + /// Execute the query with the specified command text returning a scalar result + /// + /// No parameters are used. As with + /// IDbCommand.ExecuteScalar, it returns the first column of the first row in the resultset + /// returned by the query. Extra columns or row are ignored. + /// The command type + /// The command text to execute. + /// The first column of the first row in the result set. + object ExecuteScalar(CommandType cmdType, string cmdText); + + + /// + /// Execute the query with the specified command text and parameter returning a scalar result + /// + /// The command type + /// The command text to execute. + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + /// The first column of the first row in the result set + object ExecuteScalar(CommandType cmdType, string cmdText, + string parameterName, Enum dbType, int size, object parameterValue); + + /// + /// Execute the query with the specified command text and parameters returning a scalar result + /// + /// The command type + /// The command text to execute. + /// The parameter collection to map. + /// The first column of the first row in the result set + object ExecuteScalar(CommandType cmdType, string cmdText, + IDbParameters parameters); + + /// + /// Execute the query with the specified command text and parameters set via the + /// command setter, returning a scalar result + /// + /// The command type + /// The command text to execute. + /// The command setter. + /// The first column of the first row in the result set + object ExecuteScalar(CommandType cmdType, + string cmdText, + ICommandSetter commandSetter); + + /// + /// Execute the query with a command created via IDbCommandCreator and + /// parameters + /// + /// Output parameters can be retrieved via the returned + /// dictionary. + /// + /// More commonly used as a lower level support method within the framework, + /// for example StoredProcedure/AdoScalar. + /// + /// + /// The callback to create a IDbCommand. + /// A dictionary containing output parameters, if any + IDictionary ExecuteScalar(IDbCommandCreator commandCreator); + + #endregion + + #region ExecuteNonQuery + + /// + /// Executes a non query returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The number of rows affected. + int ExecuteNonQuery(CommandType cmdType, string cmdText); + + /// + /// Executes a non query returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + /// The number of rows affected. + int ExecuteNonQuery(CommandType cmdType, string cmdText, + string parameterName, Enum dbType, int size, object parameterValue); + + /// + /// Executes a non query returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The parameter collection to map. + /// The number of rows affected. + int ExecuteNonQuery(CommandType cmdType, string cmdText, + IDbParameters parameters); + + /// + /// Executes a non query with parameters set via the + /// command setter, returning the number of rows affected. + /// + /// The command type. + /// The command text to execute. + /// The command setter. + /// The number of rows affected. + int ExecuteNonQuery(CommandType cmdType, string cmdText, + ICommandSetter commandSetter); + + + /// + /// Executes a non query with a command created via IDbCommandCreator and + /// parameters. + /// + /// Output parameters can be retrieved via the returned + /// dictionary. + /// + /// More commonly used as a lower level support method within the framework, + /// for example StoredProcedure/AdoScalar. + /// + /// + /// The callback to create a IDbCommand. + /// The number of rows affected. + IDictionary ExecuteNonQuery(IDbCommandCreator commandCreator); + + + #endregion + + #region Query with RowCallback + + /// + /// Execute a query given IDbCommand's type and text, reading a + /// single result set on a per-row basis with a . + /// + /// The type of command. + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// + void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback); + + + /// + /// Execute a query given IDbCommand's type and text and provided parameter + /// information, reading a + /// single result set on a per-row basis with a . + /// + /// The type of command + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// + /// The name of the parameter to map. + /// One of the database parameter type enumerations. + /// The length of the parameter. 0 if not applicable to parameter type. + /// The parameter value. + void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback, + string parameterName, Enum dbType, int size, object parameterValue); + + /// + /// Execute a query given IDbCommand's type and text and provided IDbParameters, + /// reading a single result set on a per-row basis with a . + /// + /// Type of the command. + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// The parameter collection to map. + void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback, + IDbParameters parameters); + + /// + /// Execute a query given IDbCommand's type and text by + /// passing the created IDbCommand to a ICommandSetter implementation + /// that knows how to bind values to the IDbCommand, reading a + /// single result set on a per-row basis with a . + /// + /// The type of command + /// The text of the query. + /// callback that will extract results + /// one row at a time. + /// + /// The command setter. + void QueryWithRowCallback(CommandType cmdType, String cmdText, IRowCallback rowCallback, + ICommandSetter commandSetter); + + + #endregion + + #region Query with RowCallback Delegate + + void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate); + + void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, ICommandSetter commandSetter); + + + void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, + string name, Enum dbType, int size, object parameterValue); + + + void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, IDbParameters parameters); + + #endregion + + #region Query with CommandCreator + + void QueryWithCommandCreator(IDbCommandCreator cc, IRowCallback rowCallback); + + void QueryWithCommandCreator(IDbCommandCreator cc, IRowCallback rowCallback, IDictionary returnedParameters); + + #endregion + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/IDataAdapterCallback.cs b/src/Spring/Spring.Data/Data/IDataAdapterCallback.cs new file mode 100644 index 00000000..987df433 --- /dev/null +++ b/src/Spring/Spring.Data/Data/IDataAdapterCallback.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Data; + +#endregion + +namespace Spring.Data +{ + /// + /// Generic callback interface for code that operates on a + /// IDbDataAdapter. + /// + /// + ///

    Allows you to execute any number of operations + /// on a IDbDataAdapter, for example to Fill a DataSet + /// or other more advanced operations such as the transfering + /// data between two different DataSets. + ///

    + ///

    Note that the passed in IDbDataAdapter + /// has been created by the framework and its SelectCommand + /// will be populated with values for CommandType and Text properties + /// along with Connection/Transaction properties based on the + /// calling transaction context. + ///

    + /// Execute(IDataAdapterCallback dataAdapterCallback) + /// method. + ///
    + /// Mark Pollack (.NET) + /// $Id: IDataAdapterCallback.cs,v 1.3 2007/12/04 06:49:17 markpollack Exp $ + public interface IDataAdapterCallback + { + /// + /// Called by AdoTemplate.Execute with an preconfigured + /// ADO.NET IDbDataAdapter instance with its SelectCommand + /// property populated with CommandType and Text values + /// along with Connection/Transaction properties based on the + /// calling transaction context. + /// + /// An active IDbDataAdapter instance + /// The result object + object DoInDataAdapter(IDbDataAdapter dataAdapter); + } +} diff --git a/src/Spring/Spring.Data/Data/IDataAdapterSetter.cs b/src/Spring/Spring.Data/Data/IDataAdapterSetter.cs new file mode 100644 index 00000000..30403a90 --- /dev/null +++ b/src/Spring/Spring.Data/Data/IDataAdapterSetter.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Data; + +#endregion + +namespace Spring.Data +{ + /// + /// Called by the AdoTemplate class to allow implementations + /// to set any necessary properties on the DbDataAdapter object. + /// + /// + ///

    For example, this callback can be used to set the + /// AcceptChangesDuringFill property, register an event handler + /// for the FillErrors event, set update batch sizes if your provider + /// supports such functionality, set the ContinueUpdateOnError property, + /// or in .NET 2.0 to set the property AcceptChangesDuringUpdate. + ///

    + ///

    + /// Downcast to the appropriate subtype to access vendor specific + /// functionality.

    + ///

    + /// The DataAdapter SelectCommand will be already be + /// populated with values for CommandType and Text properties + /// along with Connection/Transaction properties based on the + /// calling transaction context. + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: IDataAdapterSetter.cs,v 1.3 2006/11/29 07:18:45 markpollack Exp $ + public interface IDataAdapterSetter + { + void SetValues(IDbDataAdapter dataAdapter); + } +} diff --git a/src/Spring/Spring.Data/Data/IDataReaderWrapper.cs b/src/Spring/Spring.Data/Data/IDataReaderWrapper.cs new file mode 100644 index 00000000..b38c0c04 --- /dev/null +++ b/src/Spring/Spring.Data/Data/IDataReaderWrapper.cs @@ -0,0 +1,54 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Data; + +#endregion + +namespace Spring.Data +{ + /// + /// Custom data reader implementations often delegate to an underlying + /// instance. This interface captures that relationship for reuse in + /// the framework. + /// + /// Implementations will typically add behavior to standard IDataReader methods, + /// for example, by providing default values for DbNull values. + /// See as an example. + /// + /// Mark Pollack (.NET) + /// $Id: IDataReaderWrapper.cs,v 1.2 2007/10/30 15:24:44 markpollack Exp $ + public interface IDataReaderWrapper : IDataReader + { + /// + /// The underlying reader implementation to delegate to for accessing data + /// from a returned result sets. + /// + /// The wrapped reader. + IDataReader WrappedReader + { + get; + set; + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/IDataSetFillLifecycleProcessor.cs b/src/Spring/Spring.Data/Data/IDataSetFillLifecycleProcessor.cs new file mode 100644 index 00000000..ddbd63bb --- /dev/null +++ b/src/Spring/Spring.Data/Data/IDataSetFillLifecycleProcessor.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Data; + +#endregion + +namespace Spring.Data +{ + /// + /// Lifecycle callback methods that can be registered when + /// performing Fill operations with AdoTemplate. + /// + /// + ///

    + /// The methods let you set various properties and invoke + /// methods on the DataSet before and after it gets filled by a DataAdapter. + /// For example, EnforceConstraints, BeginLoadData, and EndLoadData + /// can be called to optimize the loading of large DataSets with + /// many related tables. + ///

    + ///

    Vendors may expose other propriety methods on their DataSet + /// implementation, downcast to access this specific functionality. + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: IDataSetFillLifecycleProcessor.cs,v 1.3 2006/11/29 07:18:45 markpollack Exp $ + public interface IDataSetFillLifecycleProcessor + { + /// + /// Called before a DataAdapter is used to fill a DataSet + /// the the provided tablename. + /// + /// The DataSet to be filled with a DataTable + /// The table collection to be filled + void BeforeFill(DataSet ds, ITableMappingCollection tableMappingCollection); + + /// + /// Called after a DataAdapter is used to fill a DataSet + /// the the provided tablename. + /// + /// The DataSet to be filled with a DataTable + /// The table collection to be filled + void AfterFill(DataSet ds, ITableMappingCollection tableMappingCollection); + + } +} diff --git a/src/Spring/Spring.Data/Data/IDbCommandCreator.cs b/src/Spring/Spring.Data/Data/IDbCommandCreator.cs new file mode 100644 index 00000000..8242e1d5 --- /dev/null +++ b/src/Spring/Spring.Data/Data/IDbCommandCreator.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Data; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data +{ + /// + /// One of the central callback interfaces used by the AdoTemplate class + /// This interface creates a IDbCommand. Implementations are responsible + /// for configuring the created command with command type, command text and + /// any necessary parameters. + /// + /// Mark Pollack (.NET) + /// $Id: IDbCommandCreator.cs,v 1.6 2007/08/06 20:13:39 markpollack Exp $ + public interface IDbCommandCreator + { + /// + /// Creates a IDbCommand. + /// + /// The IDbProvider reference that can be used to create the command in a + /// provider independent manner. The provider supplied is the same as used in configuration of AdoTemplate. + /// + IDbCommand CreateDbCommand(IDbProvider dbProvider); + } +} diff --git a/src/Spring/Spring.Data/Data/IDbCommandCreatorFactory.cs b/src/Spring/Spring.Data/Data/IDbCommandCreatorFactory.cs new file mode 100644 index 00000000..3fa344d4 --- /dev/null +++ b/src/Spring/Spring.Data/Data/IDbCommandCreatorFactory.cs @@ -0,0 +1,232 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using Spring.Dao; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data +{ + /// + /// Helper class that can efficiently create multiple IDbCommand + /// objects with different parameters based on a SQL statement and a single + /// set of parameter declarations. + /// + /// Mark Pollack (.NET) + /// $Id: IDbCommandCreatorFactory.cs,v 1.11 2007/08/06 20:13:39 markpollack Exp $ + public class IDbCommandCreatorFactory + { + #region Fields + + private String sql; + + private IDbParameters declaredParameters; + + private IDbProvider dbProvider; + + private CommandType commandType; + + #endregion + + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public IDbCommandCreatorFactory(IDbProvider dbProvider, CommandType commandType, string sql, IDbParameters declaredParameters) + { + this.dbProvider = dbProvider; + this.sql = sql; + this.commandType = commandType; + this.declaredParameters = declaredParameters; + } + + #endregion + + #region Properties + + public IDbParameters DeclaredParameters + { + get + { + return declaredParameters; + } + } + + public string Sql + { + get { return sql; } + } + + public IDbProvider DbProvider + { + get { return dbProvider; } + } + + public CommandType CommandType + { + get { return commandType; } + } + #endregion + + #region Methods + + public IDbCommandCreator NewDbCommandCreatorWithParamValues(params object[] inParamValues) + { + + return new CommandCreatorWithParamValues(this, inParamValues != null ? ArrayList.Adapter(inParamValues) : new ArrayList()); + } + + + public IDbCommandCreator NewDbCommandCreator(IDictionary inParameters) + { + + return new CommandCreatorImpl(this, inParameters != null ? inParameters : new Hashtable()); + } + + #endregion + + private class CommandCreatorWithParamValues : IDbCommandCreator + { + private IDbCommandCreatorFactory factory; + private IList inParamValues; + + public CommandCreatorWithParamValues(IDbCommandCreatorFactory factory, IList inParamValues) + { + this.factory = factory; + this.inParamValues = inParamValues; + } + + public IDbCommand CreateDbCommand(IDbProvider provider) + { + IDbCommand command = factory.dbProvider.CreateCommand(); + command.CommandText = factory.sql; + command.CommandType = factory.commandType; + if (factory.declaredParameters != null) + { + int parameterIndex = 0; + foreach ( IDbDataParameter declaredParameter in factory.declaredParameters.DataParameterCollection) + { + IDbDataParameter clonedParameter = (IDbDataParameter)((ICloneable)declaredParameter).Clone(); + if (!(clonedParameter.Direction == ParameterDirection.Output) && + !(clonedParameter.Direction == ParameterDirection.ReturnValue)) + { + if (clonedParameter.Direction == ParameterDirection.InputOutput) + { + //heuristic for case when have output parameter from stored procedure discovery classified + //as in-out. Assume out parameters come last in the stored proc definition and ignore + //setting values if there are not enough input values as passed in by the user. + if (parameterIndex < inParamValues.Count) + { + //The value may still be null + Object inValue = inParamValues[parameterIndex]; + clonedParameter.Value = (inValue == null) ? DBNull.Value : inValue; + } else + { + //Since it thinks it is an input-output value and we have no value from the user - pass in null + //as a workaround. + clonedParameter.Value = DBNull.Value; + } + + } + else + { + //The value may still be null + Object inValue = inParamValues[parameterIndex]; + clonedParameter.Value = (inValue == null) ? DBNull.Value : inValue; + } + // only increment index if we could assign a value from the user specified inputs. + parameterIndex++; + } + + command.Parameters.Add(clonedParameter); + + } + } + return command; + } + + } + private class CommandCreatorImpl : IDbCommandCreator{ + + private IDbCommandCreatorFactory factory; + private IDictionary inParams; + + + public CommandCreatorImpl(IDbCommandCreatorFactory factory, IDictionary inParameters) + { + this.factory = factory; + this.inParams = inParameters; + } + + public IDbCommand CreateDbCommand(IDbProvider provider) + { + IDbCommand command = factory.dbProvider.CreateCommand(); + command.CommandText = factory.sql; + command.CommandType = factory.commandType; + if (factory.declaredParameters != null) + { + foreach ( IDbDataParameter declaredParameter in factory.declaredParameters.DataParameterCollection) + { + IDbDataParameter clonedParameter = (IDbDataParameter)((ICloneable)declaredParameter).Clone(); + + if (IsInputParamter(clonedParameter) && !inParams.Contains(clonedParameter.ParameterName)) + { + throw new InvalidDataAccessApiUsageException("Required input parameter '" + + clonedParameter.ParameterName + "' is missing"); + } + //The value may still be null +// Object inValue = +// inParams[factory.dbProvider.CreateParameterName(clonedParameter.ParameterName)]; + + // TODO: The above code doesn't work as it duplicates parameter prefix, thus failing + // to obtain parameter values. The whole parameter handling mechanism should be refactored + // not to require parameter prefix to be specified when populating named parameters collection + // within AdoOperations. Code below is just a temporary workaround until this issue is resolved. + + Object inValue = inParams[clonedParameter.ParameterName]; + if (!(clonedParameter.Direction == ParameterDirection.Output) && + !(clonedParameter.Direction == ParameterDirection.ReturnValue)) + { + clonedParameter.Value = (inValue == null) ? DBNull.Value : inValue; + } + + command.Parameters.Add(clonedParameter); + } + } + return command; + } + + private bool IsInputParamter(IDataParameter parameter) + { + //Due to quirk in DeriveParameters can only verify Input parameter as output parameters + //are incorrectly listed as InputOutput + return (parameter.Direction == ParameterDirection.Input); + } + + } + } +} diff --git a/src/Spring/Spring.Data/Data/IDbDataAdapterCreator.cs b/src/Spring/Spring.Data/Data/IDbDataAdapterCreator.cs new file mode 100644 index 00000000..7f82b897 --- /dev/null +++ b/src/Spring/Spring.Data/Data/IDbDataAdapterCreator.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Data; + +#endregion + +namespace Spring.Data +{ + /// + /// This interface creates a IDbDataAdapterCommand. + /// Implementations are responsible + /// for configuring the created command with appropriate + /// select and actions commands along with their parameters. + /// + /// + /// Generally used to to support the DataSet functionality in + /// the Spring.Data.Objects namespace. + /// + /// Mark Pollack (.NET) + /// $Id: IDbDataAdapterCreator.cs,v 1.3 2007/12/07 08:09:52 markpollack Exp $ + public interface IDbDataAdapterCreator + { + /// + /// Creates the data adapter. + /// + /// A new IDbDataAdapter instance + IDbDataAdapter CreateDataAdapter(); + } +} diff --git a/src/Spring/Spring.Data/Data/IResultSetExtractor.cs b/src/Spring/Spring.Data/Data/IResultSetExtractor.cs new file mode 100644 index 00000000..fd8c3c5a --- /dev/null +++ b/src/Spring/Spring.Data/Data/IResultSetExtractor.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +using System.Data; + +namespace Spring.Data +{ + /// + /// Callback interface to process all result sets and rows in + /// an AdoTemplate query method. + /// + /// Implementations of this interface perform the work + /// of extracting results but don't need worry about managing + /// ADO.NET resources, such as closing the reader or transaction management. + ///

    + /// This interface is mainly used within the ADO.NET + /// framework. An IResultSetExtractor is usually a simpler choice + /// for result set (DataReader) processing, in particular a + /// RowMapperResultSetExtractor in combination with a IRowMapper. + ///

    + /// Note: in contracts to a IRowCallbackHandler, a ResultSetExtractor + /// is usually stateless and thus reusable, as long as it doesn't access + /// stateful resources or keep result state within the object. + /// + ///
    + /// + public interface IResultSetExtractor + { + /// + /// Implementations must implement this method to process all + /// result set and rows in the IDataReader. + /// + /// The IDataReader to extract data from. + /// Implementations should not close this: it will be closed + /// by the AdoTemplate. + /// An arbitrary result object or null if none. The + /// extractor will typically be stateful in the latter case. + object ExtractData(IDataReader reader); + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/IRowCallback.cs b/src/Spring/Spring.Data/Data/IRowCallback.cs new file mode 100644 index 00000000..08f08a0b --- /dev/null +++ b/src/Spring/Spring.Data/Data/IRowCallback.cs @@ -0,0 +1,35 @@ + + +using System.Data; + +namespace Spring.Data +{ + /// + /// Callback to process each row of data in an AdoOperation's Query method. + /// + /// + /// Implementations of this interface perform the actual work of extracting + /// results. In contrast to a IResultSetExtractor, a IRowCallback object is typically + /// stateful. It keeps the result state within the object, to be available + /// for later inspection. + /// + /// + public interface IRowCallback + { + /// + /// Implementations must implement this method to process each row of data + /// in the data reader. + /// + /// + /// This method should not advance the cursor by calling Read() + /// on IDataReader but only extract the current values. The + /// caller does not need to care about closing the reader, command, connection, or + /// about handling transactions: this will all be handled by + /// Spring's AdoTemplate + /// + /// An active IDataReader instance + /// The result object + void ProcessRow(IDataReader reader); + + } +} diff --git a/src/Spring/Spring.Data/Data/IRowMapper.cs b/src/Spring/Spring.Data/Data/IRowMapper.cs new file mode 100644 index 00000000..4f2aa3e0 --- /dev/null +++ b/src/Spring/Spring.Data/Data/IRowMapper.cs @@ -0,0 +1,60 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Data; + +#endregion + +namespace Spring.Data +{ + /// + /// Callback to process each row of data in a result set to an object. + /// + /// Implementations of this interface perform the actual work + /// of mapping rows, but don't need worry about managing + /// ADO.NET resources, such as closing the reader. + ///

    + /// Typically used for AdoTemplate's query methods (with RowMapperResultSetExtractor + /// adapters). RowMapper objects are typically stateless and thus + /// reusable; they are ideal choices for implementing row-mapping logic in a single + /// place. + ///

    + ///

    Alternatively, consider subclassing MappingSqlQuery from the Spring.Data.Object + /// namespace: Instead of working with separate AdoTemplate and RowMapper objects, + /// you can have executable query objects (containing row-mapping logic) there. + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: IRowMapper.cs,v 1.6 2007/06/28 18:48:19 markpollack Exp $ + public interface IRowMapper + { + /// + /// Implementations must implement this method to map each row of data in the + /// result set (DataReader). This method should not call Next() on the + /// DataReader; it should only extract the values of the current row. + /// + /// The IDataReader to map (pre-initialized for the current row) + /// The number of the current row. + /// The result object for the current row. + object MapRow(IDataReader reader, int rowNum); + } +} diff --git a/src/Spring/Spring.Data/Data/InvalidResultSetAccessException.cs b/src/Spring/Spring.Data/Data/InvalidResultSetAccessException.cs new file mode 100644 index 00000000..a2442bf4 --- /dev/null +++ b/src/Spring/Spring.Data/Data/InvalidResultSetAccessException.cs @@ -0,0 +1,138 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using Spring.Dao; + +#endregion + +namespace Spring.Data +{ + /// + /// Exception thrown when a result set has been accessed in an invalid fashion. + /// + /// Mark Pollack (.NET) + /// $Id: InvalidResultSetAccessException.cs,v 1.2 2007/12/07 08:09:52 markpollack Exp $ + [Serializable] + public class InvalidResultSetAccessException : InvalidDataAccessResourceUsageException, ISerializable + { + #region Fields + + /// + /// SQL that led to the problem + /// + private string sql; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public InvalidResultSetAccessException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception. + public InvalidResultSetAccessException(string message): base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception. + /// The inner exception. + public InvalidResultSetAccessException(string message, Exception inner): base(message, inner) + { + } + + + /// + /// Initializes a new instance of the class. + /// + /// name of the current task. + /// The offending SQL statment + /// The root cause. + public InvalidResultSetAccessException(string task, String sql, Exception ex) : base(task + "; invalid ResultSet access for SQL [" + sql + "]; " + ex.Message, ex) + { + this.sql = sql; + } + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected InvalidResultSetAccessException( SerializationInfo info, StreamingContext context ) : base( info, context ) {} + + + + #endregion + + #region Properties + + /// + /// Gets the SQL that caused the exception + /// + /// The SQL that caused the exception. + public string Sql + { + get + { + return sql; + } + } + #endregion + + #region Methods + + #endregion + + #region ISerializable Members + + /// + /// When overridden in a derived class, sets the + /// with information about the exception. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is a null reference ( in Visual Basic). + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue( "sql", sql ); + base.GetObjectData( info, context ); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Data/Data/Objects/AbstractAdoOperation.cs b/src/Spring/Spring.Data/Data/Objects/AbstractAdoOperation.cs new file mode 100644 index 00000000..d8eb4830 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/AbstractAdoOperation.cs @@ -0,0 +1,330 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Data; +using Common.Logging; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Objects.Factory; + +namespace Spring.Data.Objects +{ + /// + /// Abstract base class providing common functionality for generic and non + /// generic implementations of "AdoOperation" subclasses. + /// + /// Mark Pollack (.NET) + /// $Id: AbstractAdoOperation.cs,v 1.2 2008/05/30 21:09:22 markpollack Exp $ + public abstract class AbstractAdoOperation : IInitializingObject + { + #region Logging Definition + + protected readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + #endregion + + #region Fields + + private string sql; + + private CommandType commandType = CommandType.Text; + + private IDbParameters declaredParameters; + + + /// + /// Has this operation been compiled? Compilation means at + /// least checking that a IDbProvider and sql have been provided, + /// but subclasses may also implement their own custom validation. + /// + protected bool compiled; + + /// + /// Object enabling us to create IDbCommands + /// efficiently, based on this class's declared parameters. + /// + private IDbCommandCreatorFactory commandFactory; + + #endregion + + + #region Properties + + + /// + /// Gets or sets the type of the command. + /// + /// The type of the command. + public CommandType CommandType + { + get { return commandType; } + set { commandType = value; } + } + + /// + /// Gets or sets the db provider. + /// + /// The db provider. + public abstract IDbProvider DbProvider + { + get; set; + } + + /// + /// Gets or sets the SQL to execute + /// + /// The SQL. + public string Sql + { + get + { + return sql; + } + set + { + sql = value; + } + } + + /// + /// Gets or sets the declared parameters. + /// + /// The declared parameters. + public IDbParameters DeclaredParameters + { + get + { + return declaredParameters; + } + set + { + if (Compiled) + { + throw new InvalidDataAccessApiUsageException("Cannot add parameters once operation is compiled"); + } + declaredParameters = value; + } + } + + /// + /// Gets a value indicating whether this is compiled. + /// + /// Compilation means that the operation is fully configured, + /// and ready to use. The exact meaning of compilation will vary between subclasses. + /// true if compiled; otherwise, false. + public bool Compiled + { + get { return compiled; } + } + + /// + /// Sets the command timeout for IDbCommands that this AdoTemplate executes. + /// + /// Default is 0, indicating to use the database provider's default. + /// Any timeout specified here will be overridden by the remaining + /// transaction timeout when executing within a transaction that has a + /// timeout specified at the transaction level. + /// + /// The command timeout. + public abstract int CommandTimeout + { + set; + } + + + #endregion + + #region Methods + + /// + /// Ensures compilation if used in an IApplicationContext + /// + public void AfterPropertiesSet() + { + Compile(); + } + + /// + /// Compiles this operation. Ignores subsequent attempts to compile. + /// + public abstract void Compile(); + + + + + /// + /// Check whether this operation has been compiled already; + /// lazily compile it if not already compiled. + /// + /// Automatically called by ValidateParameters and ValidateNamedParameters + protected void CheckCompiled() + { + if (!Compiled) + { + log.Debug("ADO operation not compiled before execution - invoking compile"); + Compile(); + } + } + + + protected virtual void ValidateParameters(params object[] inParamValues) + { + CheckCompiled(); + int declaredInParameters = 0; + if (DeclaredParameters != null) + { + for (int i = 0; i < declaredParameters.Count; i++) + { + IDataParameter declaredParameter = DeclaredParameters[i]; + if (IsInputParameter(declaredParameter)) + { + declaredInParameters++; + } + } + } + + if (inParamValues != null) + { + if (DeclaredParameters == null) + { + throw new InvalidDataAccessApiUsageException( + "Didn't expect any parameters: none were declared"); + } + if (inParamValues.Length < declaredInParameters) + { + throw new InvalidDataAccessApiUsageException( + inParamValues.Length + " parameters were supplied, but " + + declaredInParameters + " in parameters were declared in class [" + + GetType().AssemblyQualifiedName + "]"); + } + if (inParamValues.Length > declaredInParameters) + { + throw new InvalidDataAccessApiUsageException( + inParamValues.Length + " parameters were supplied, but " + + DeclaredParameters.Count + " parameters were declared " + + "in class [" + GetType().AssemblyQualifiedName + "]"); + } + } + else + { + // No parameters were supplied + if (DeclaredParameters != null && !(DeclaredParameters.Count == 0)) + { + throw new InvalidDataAccessApiUsageException( + DeclaredParameters.Count + " parameters must be supplied"); + } + } + } + + protected virtual bool IsInputParameter(IDataParameter parameter) + { + return (parameter.Direction == ParameterDirection.Input + || parameter.Direction == ParameterDirection.InputOutput); + } + + /// + /// Validates the named parameters passed to an AdoTemplate ExecuteXXX method based on + /// declared parameters. + /// + /// + /// Subclasses should invoke this method very every ExecuteXXX method. + /// + /// The parameter dictionary supplied. May by null. + protected virtual void ValidateNamedParameters(IDictionary parameters) + { + CheckCompiled(); + IDictionary paramsToUse = (parameters != null ? parameters : new Hashtable()); + + if (declaredParameters != null) + { + for (int i = 0; i < declaredParameters.Count; i++) + { + IDataParameter declaredParameter = DeclaredParameters[i]; + if (declaredParameter.ParameterName == null) + { + throw new InvalidDataAccessApiUsageException( + "All parameters must have name specified when using the methods " + + "dedicated to named parameter support"); + } + if (IsInputParameter(declaredParameter) && !paramsToUse.Contains(declaredParameter.ParameterName)) + { + throw new InvalidDataAccessApiUsageException( + "The parameter named '" + declaredParameter + + "' was not among the parameters supplied: " + paramsToUse.Keys); + } + } + } + + + if (DeclaredParameters != null && DeclaredParameters.Count > 0) + { + if (DeclaredParameters == null) + { + throw new InvalidDataAccessApiUsageException( + "Didn't expect any parameters: none were declared"); + } + } + else + { + // No parameters were supplied + if (DeclaredParameters != null && !(DeclaredParameters.Count == 0)) + { + throw new InvalidDataAccessApiUsageException( + "Parameters must be supplied"); + } + } + + } + + /// + /// Subclasses must implement to perform their own compilation. + /// Invoked after this class's compilation is complete. + /// Subclasses can assume that SQL has been supplied and that + /// a IDbProvider has been supplied. + /// + protected virtual void CompileInternal() + { + commandFactory = new IDbCommandCreatorFactory(DbProvider, CommandType, Sql, DeclaredParameters); + OnCompileInternal(); + + + } + /// + /// Hook method that subclasses may override to react to compilation. + /// This implementation does nothing. + /// + protected virtual void OnCompileInternal() + { + } + + protected virtual IDbCommandCreator NewCommandCreator(IDictionary inParams) + { + return commandFactory.NewDbCommandCreator(inParams); + } + + protected virtual IDbCommandCreator NewCommandCreatorWithParamValues(params object[] inParams) + { + return commandFactory.NewDbCommandCreatorWithParamValues(inParams); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Objects/AdoNonQuery.cs b/src/Spring/Spring.Data/Data/Objects/AdoNonQuery.cs new file mode 100644 index 00000000..ab5baee2 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/AdoNonQuery.cs @@ -0,0 +1,84 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Data; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.Objects +{ + /// + /// Encapsulate Command.ExecuteNonQuery operations within a reusable class. + /// + /// Mark Pollack (.NET) + /// The default CommandType is CommandType.Text + /// $Id: AdoNonQuery.cs,v 1.3 2007/07/25 08:25:20 markpollack Exp $ + public class AdoNonQuery : AdoOperation + { + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public AdoNonQuery() + {} + + /// + /// Initializes a new instance of the class. + /// + public AdoNonQuery(IDbProvider provider) + : base(provider) + {} + + /// + /// Initializes a new instance of the class. + /// + public AdoNonQuery(IDbProvider dbProvider, string sql) + : base(dbProvider, sql) + {} + + #endregion + + #region Properties + + #endregion + + #region Methods + + public IDictionary ExecuteNonQuery(params object[] inParameterValues) + { + ValidateParameters(inParameterValues); + return AdoTemplate.ExecuteNonQuery(NewCommandCreatorWithParamValues(inParameterValues)); + } + + + public IDictionary ExecuteNonQueryByNamedParam(IDictionary inParams) + { + ValidateNamedParameters(inParams); + return AdoTemplate.ExecuteNonQuery(NewCommandCreator(inParams)); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Data/Data/Objects/AdoOperation.cs b/src/Spring/Spring.Data/Data/Objects/AdoOperation.cs new file mode 100644 index 00000000..e9416d48 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/AdoOperation.cs @@ -0,0 +1,199 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Data.Core; + +#endregion + +namespace Spring.Data.Objects +{ + /// + /// A "AdoOperation" is a thread-safe, reusable object representing + /// an ADO Query, "NonQuery" (Create, Update, Delete), stored procedure + /// or DataSet manipualtions. + /// + /// + /// Subclasses should set Command Text and add parameters before invoking + /// Compile(). The order in which parameters are added is generally + /// significant when using providers or functionality that do not use + /// named parameters. + /// + /// + /// Mark Pollack (.NET) + /// $Id: AdoOperation.cs,v 1.15 2008/05/30 21:09:22 markpollack Exp $ + public abstract class AdoOperation : AbstractAdoOperation + { + + #region Fields + + private AdoTemplate adoTemplate = new AdoTemplate(); + + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + /// A DbProvider, SQL and any parameters must be supplied + /// before invoking the compile method and using this object. + /// + public AdoOperation() + {} + + /// + /// Initializes a new instance of the class. + /// + /// A DbProvider, SQL and any parameters must be supplied + /// before invoking the compile method and using this object. + /// + /// Database provider to use. + public AdoOperation(IDbProvider provider) + : this(provider, null) + {} + + /// + /// Initializes a new instance of the class. + /// + /// A DbProvider, SQL and any parameters must be supplied + /// before invoking the compile method and using this object. + /// + /// Database provider to use. + /// SQL query or stored procedure name. + public AdoOperation(IDbProvider provider, String sql) + { + //intialized AdoTemplate with the DbProvider + DbProvider = provider; + Sql = sql; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the ADO template. + /// + /// The ADO template. + public AdoTemplate AdoTemplate + { + set + { + if (value == null) + { + throw new ArgumentNullException("AdoTemplate"); + } + adoTemplate = value; + } + get + { + return adoTemplate; + } + } + + + /// + /// Gets or sets the db provider. + /// + /// The db provider. + public override IDbProvider DbProvider + { + set + { + adoTemplate.DbProvider = value; + if (DeclaredParameters == null) + { + DeclaredParameters = new DbParameters(value); + } + } + get + { + return adoTemplate.DbProvider; + } + } + + + + /// + /// Sets the command timeout for IDbCommands that this AdoTemplate executes. + /// + /// Default is 0, indicating to use the database provider's default. + /// Any timeout specified here will be overridden by the remaining + /// transaction timeout when executing within a transaction that has a + /// timeout specified at the transaction level. + /// + /// The command timeout. + public override int CommandTimeout + { + set { adoTemplate.CommandTimeout = value; } + } + + #endregion + + #region Methods + + + /// + /// Compiles this operation. Ignores subsequent attempts to compile. + /// + public override void Compile() + { + if (!Compiled) + { + if (Sql == null) + { + throw new InvalidDataAccessApiUsageException("Setting of Sql is required"); + } + if (CommandType == 0) + { + throw new InvalidDataAccessApiUsageException("Setting of CommandType is required"); + } + + try + { + adoTemplate.AfterPropertiesSet(); + } + catch (ArgumentException ex) + { + throw new InvalidDataAccessApiUsageException(ex.Message); + } + + + CompileInternal(); + compiled = true; + + + } + } + + + + + + + #endregion + } +} diff --git a/src/Spring/Spring.Data/Data/Objects/AdoQuery.cs b/src/Spring/Spring.Data/Data/Objects/AdoQuery.cs new file mode 100644 index 00000000..de09700c --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/AdoQuery.cs @@ -0,0 +1,112 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using Spring.Dao.Support; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.Objects +{ + /// + /// Place together the mapping logic to a plain .NET object + /// for one result set within the same class. + /// + /// Mark Pollack (.NET) + /// $Id: AdoQuery.cs,v 1.8 2008/05/30 21:09:22 markpollack Exp $ + public abstract class AdoQuery : AdoOperation + { + #region Fields + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public AdoQuery() + { + + } + + public AdoQuery(IDbProvider provider, string sql) + { + DbProvider = provider; + Sql = sql; + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + public IList Query() + { + return QueryByNamedParam(null); + } + + public IList QueryByNamedParam(IDictionary inParams) + { + return QueryByNamedParam(inParams, null); + } + + public IList QueryByNamedParam(IDictionary inParams, IDictionary returnedParameters) + { + return QueryByNamedParam(inParams, returnedParameters, null); + } + + public IList QueryByNamedParam(IDictionary inParams, IDictionary returnedParameters, IDictionary callingContext) + { + ValidateNamedParameters(inParams); + IRowMapper rowMapper = NewRowMapper(inParams, callingContext); + return AdoTemplate.QueryWithCommandCreator(NewCommandCreator(inParams), rowMapper, returnedParameters); + } + + public object QueryForObject(IDictionary inParams) + { + IList results = QueryByNamedParam(inParams); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + public object QueryForObject(IDictionary inParams, IDictionary returnedParameters) + { + IList results = QueryByNamedParam(inParams, returnedParameters); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + public object QueryForObject(IDictionary inParams, IDictionary returnedParameters, IDictionary callingContext) + { + IList results = QueryByNamedParam(inParams, returnedParameters, callingContext); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + + protected abstract IRowMapper NewRowMapper(IDictionary inParams, IDictionary callingContext); + + #endregion + + } +} diff --git a/src/Spring/Spring.Data/Data/Objects/AdoScalar.cs b/src/Spring/Spring.Data/Data/Objects/AdoScalar.cs new file mode 100644 index 00000000..721d1e19 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/AdoScalar.cs @@ -0,0 +1,79 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Data; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.Objects +{ + /// + /// Encapsulate Command ExecuteScalar operations within a reusable class. + /// + /// The default CommandType is CommandType.Text + /// Mark Pollack (.NET) + /// $Id: AdoScalar.cs,v 1.3 2007/07/25 08:25:20 markpollack Exp $ + public class AdoScalar : AdoOperation + { + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public AdoScalar() + {} + + /// + /// Initializes a new instance of the class. + /// + public AdoScalar(IDbProvider provider) + : base(provider) + {} + + /// + /// Initializes a new instance of the class. + /// + public AdoScalar(IDbProvider dbProvider, string sql) + : base(dbProvider, sql) + {} + + #endregion + + #region Methods + + public IDictionary ExecuteScalar(params object[] inParameterValues) + { + ValidateParameters(inParameterValues); + return AdoTemplate.ExecuteScalar(NewCommandCreatorWithParamValues(inParameterValues)); + } + + public IDictionary ExecuteScalarByNamedParam(IDictionary inParams) + { + ValidateNamedParameters(inParams); + return AdoTemplate.ExecuteScalar(NewCommandCreator(inParams)); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Objects/Generic/AdoOperation.cs b/src/Spring/Spring.Data/Data/Objects/Generic/AdoOperation.cs new file mode 100644 index 00000000..4f9a354a --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/Generic/AdoOperation.cs @@ -0,0 +1,202 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using Spring.Dao; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.Objects.Generic +{ + /// + /// A "AdoOperation" is a thread-safe, reusable object representing + /// an ADO Query, "NonQuery" (Create, Update, Delete), stored procedure + /// or DataSet manipualtions. + /// + /// + /// Subclasses should set Command Text and add parameters before invoking + /// Compile(). The order in which parameters are added is generally + /// significant when using providers or functionality that do not use + /// named parameters. + /// + /// + /// Mark Pollack (.NET) + /// $Id: AdoOperation.cs,v 1.3 2008/05/30 21:09:32 markpollack Exp $ + public abstract class AdoOperation : AbstractAdoOperation + { + + #region Fields + + private Data.Generic.AdoTemplate adoTemplate = new Data.Generic.AdoTemplate(); + + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + /// A DbProvider, SQL and any parameters must be supplied + /// before invoking the compile method and using this object. + /// + public AdoOperation() + {} + + /// + /// Initializes a new instance of the class. + /// + /// A DbProvider, SQL and any parameters must be supplied + /// before invoking the compile method and using this object. + /// + /// Database provider to use. + public AdoOperation(IDbProvider provider) + : this(provider, null) + {} + + /// + /// Initializes a new instance of the class. + /// + /// A DbProvider, SQL and any parameters must be supplied + /// before invoking the compile method and using this object. + /// + /// Database provider to use. + /// SQL query or stored procedure name. + public AdoOperation(IDbProvider provider, String sql) + { + //intialized AdoTemplate with the DbProvider + DbProvider = provider; + Sql = sql; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the ADO template. + /// + /// The ADO template. + public Data.Generic.AdoTemplate AdoTemplate + { + set + { + if (value == null) + { + throw new ArgumentNullException("AdoTemplate"); + } + adoTemplate = value; + } + get + { + return adoTemplate; + } + } + + + /// + /// Gets or sets the db provider. + /// + /// The db provider. + public override IDbProvider DbProvider + { + set + { + adoTemplate.DbProvider = value; + if (DeclaredParameters == null) + { + DeclaredParameters = new DbParameters(value); + } + } + get + { + return adoTemplate.DbProvider; + } + } + + + + /// + /// Sets the command timeout for IDbCommands that this AdoTemplate executes. + /// + /// Default is 0, indicating to use the database provider's default. + /// Any timeout specified here will be overridden by the remaining + /// transaction timeout when executing within a transaction that has a + /// timeout specified at the transaction level. + /// + /// The command timeout. + public override int CommandTimeout + { + set { adoTemplate.CommandTimeout = value; } + } + + #endregion + + #region Methods + + + /// + /// Compiles this operation. Ignores subsequent attempts to compile. + /// + public override void Compile() + { + if (!Compiled) + { + if (Sql == null) + { + throw new InvalidDataAccessApiUsageException("Setting of Sql is required"); + } + if (CommandType == 0) + { + throw new InvalidDataAccessApiUsageException("Setting of CommandType is required"); + } + + try + { + adoTemplate.AfterPropertiesSet(); + } + catch (ArgumentException ex) + { + throw new InvalidDataAccessApiUsageException(ex.Message); + } + + + CompileInternal(); + compiled = true; + + + } + } + + + + + + + #endregion + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Objects/Generic/AdoQuery.cs b/src/Spring/Spring.Data/Data/Objects/Generic/AdoQuery.cs new file mode 100644 index 00000000..afa1054a --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/Generic/AdoQuery.cs @@ -0,0 +1,152 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections.Generic; +using Spring.Dao.Support.Generic; +using Spring.Data.Common; +using Spring.Data.Generic; + +#endregion + +namespace Spring.Data.Objects.Generic +{ + /// + /// Place together the mapping logic to a plain .NET object + /// for one result set within the same class. + /// + /// Mark Pollack (.NET) + /// $Id: AdoQuery.cs,v 1.3 2007/07/25 13:32:39 oakinger Exp $ + public abstract class AdoQuery : AdoOperation + { + #region Fields + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public AdoQuery() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The database provider. + /// The SQL to execute + public AdoQuery(IDbProvider provider, string sql) + { + DbProvider = provider; + Sql = sql; + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + + /// + /// Convenient method to execute query without parameters or calling context. + /// + /// The type parameter for the returned collection + /// List of mapped objects + public IList Query() + { + return QueryByNamedParam(null); + } + + /// + /// Convenient method to execute query with parameters but without calling context. + /// + /// The type parameter for the returned collection + /// The parameters for the query. + /// + public IList QueryByNamedParam(System.Collections.IDictionary inParams) + { + return QueryByNamedParam(inParams, null); + } + + /// + /// Convenient method to execute query with parameters and access to output parameter values. + /// + /// The type parameter for the returned collection + /// The parameters for the query. + /// The returned output parameters. + /// + public IList QueryByNamedParam(System.Collections.IDictionary inParams, + System.Collections.IDictionary outParams) + { + return QueryByNamedParam(inParams, outParams, null); + } + + /// + /// Central execution method. All named parameter execution goes through this method. + /// + /// the type parameter for the returned collection + /// The in params. + /// The out params. + /// The calling context. + /// + public IList QueryByNamedParam(System.Collections.IDictionary inParams, + System.Collections.IDictionary outParams, + System.Collections.IDictionary callingContext) + { + ValidateNamedParameters(inParams); + IRowMapper rowMapper = NewRowMapper(inParams, callingContext); + return AdoTemplate.QueryWithCommandCreator(NewCommandCreator(inParams), rowMapper, outParams); + } + + public T QueryForObject(System.Collections.IDictionary inParams) + { + IList results = QueryByNamedParam(inParams); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + public T QueryForObject(System.Collections.IDictionary inParams, System.Collections.IDictionary returnedParameters) + { + IList results = QueryByNamedParam(inParams, returnedParameters); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + public T QueryForObject(System.Collections.IDictionary inParams, System.Collections.IDictionary returnedParameters, System.Collections.IDictionary callingContext) + { + IList results = QueryByNamedParam(inParams, returnedParameters, callingContext); + return DataAccessUtils.RequiredUniqueResultSet(results); + } + + + protected abstract IRowMapper NewRowMapper(System.Collections.IDictionary inParams, System.Collections.IDictionary callingContext); + + #endregion + + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Objects/Generic/MappingAdoQuery.cs b/src/Spring/Spring.Data/Data/Objects/Generic/MappingAdoQuery.cs new file mode 100644 index 00000000..956f79cf --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/Generic/MappingAdoQuery.cs @@ -0,0 +1,76 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections; +using System.Data; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.Objects.Generic +{ + /// + /// Reusable query in which concrete subclasses must implement the + /// MapRow method to map each row of a single result set into an + /// object. + /// + /// + /// Simplifies MappingSqlQueryWithContext API by dropping parameters and + /// context. Most subclasses won't care about parameters. If you don't use + /// contextual information, subclass this instead of MappingSqlQueryWithContext. + /// + /// Mark Pollack (.NET) + /// $Id: MappingAdoQuery.cs,v 1.2 2007/07/25 13:32:39 oakinger Exp $ + public abstract class MappingAdoQuery : MappingAdoQueryWithContext + { + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public MappingAdoQuery() + { + } + + public MappingAdoQuery(IDbProvider dbProvider, string sql) : base(dbProvider, sql) + { + } + + #endregion + + #region Methods + + protected override T MapRow(IDataReader reader, int rowNum, IDictionary inParams, + IDictionary callingContext) + { + return MapRow(reader, rowNum); + } + + protected abstract T MapRow(IDataReader reader, int num); + + #endregion + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Objects/Generic/MappingAdoQueryWithContext.cs b/src/Spring/Spring.Data/Data/Objects/Generic/MappingAdoQueryWithContext.cs new file mode 100644 index 00000000..b773cc15 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/Generic/MappingAdoQueryWithContext.cs @@ -0,0 +1,102 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections; +using System.Data; +using Spring.Data.Common; +using Spring.Data.Generic; + +#endregion + +namespace Spring.Data.Objects.Generic +{ + /// + /// Reusable query in which concrete subclasses must implement the + /// abstract MapRow method to map each row of a single result set into an + /// object. + /// + /// The MapRow method is also passed the input parameters and + /// a dictionary to provide the method with calling context information + /// (if necessary). If you do not need these additional parameters, use + /// the subclass MappingAdoQuery. + /// + /// Mark Pollack (.NET) + /// $Id: MappingAdoQueryWithContext.cs,v 1.2 2007/07/25 13:32:39 oakinger Exp $ + public abstract class MappingAdoQueryWithContext : AdoQuery + { + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public MappingAdoQueryWithContext() + { + + } + + public MappingAdoQueryWithContext(IDbProvider dbProvider, string sql) : base(dbProvider, sql) + { + + } + #endregion + + #region Properties + + #endregion + + #region Methods + + protected override IRowMapper NewRowMapper(System.Collections.IDictionary inParams, + System.Collections.IDictionary callingContext) + { + return new RowMapperImpl(this, inParams, callingContext); + } + + protected abstract T MapRow(IDataReader reader, int rowNum, IDictionary inParams, IDictionary callingContext); + + + #endregion + + private class RowMapperImpl : IRowMapper + { + private MappingAdoQueryWithContext outer; + private IDictionary inParams; + private IDictionary callingContext; + + public RowMapperImpl(MappingAdoQueryWithContext mappingAdoQueryWithContext, IDictionary inParams, IDictionary callingContext) + { + outer = mappingAdoQueryWithContext; + this.inParams = inParams; + this.callingContext = callingContext; + } + + public T MapRow(IDataReader dataReader, int rowNum) + { + return outer.MapRow(dataReader, rowNum, inParams, callingContext); + } + } + + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Objects/Generic/StoredProcedure.cs b/src/Spring/Spring.Data/Data/Objects/Generic/StoredProcedure.cs new file mode 100644 index 00000000..1b5603c5 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/Generic/StoredProcedure.cs @@ -0,0 +1,255 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections; +using System.Data; +using Spring.Collections; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Data.Generic; +using Spring.Data.Support; + +#endregion + +namespace Spring.Data.Objects.Generic +{ + /// + /// A superclass for object based abstractions of RDBMS stored procedures. + /// + /// Mark Pollack (.NET) + /// $Id: StoredProcedure.cs,v 1.3 2007/07/25 13:32:39 oakinger Exp $ + public abstract class StoredProcedure : AdoOperation + { + #region Fields + + //A collection of NamedResultSetProcessor + + private IList resultProcessors = new LinkedList(); + private bool usingDerivedParameters = false; + + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public StoredProcedure() + { + CommandType = CommandType.StoredProcedure; + } + + public StoredProcedure(IDbProvider dbProvider, string procedureName) : base(dbProvider, procedureName) + { + CommandType = CommandType.StoredProcedure; + } + + + + #endregion + + #region Properties + + #endregion + + #region Methods + + public void DeriveParameters() + { + DeriveParameters(false); + } + + + public void DeriveParameters(bool includeReturnParameter) + { + //TODO does this account for offsets? + IDataParameter[] derivedParameters = AdoTemplate.DeriveParameters(Sql, includeReturnParameter); + for (int i = 0; i < derivedParameters.Length; i++) + { + IDataParameter parameter = derivedParameters[i]; + DeclaredParameters.AddParameter(parameter); + } + usingDerivedParameters = true; + } + + + #region Non-generic result set processors + public void AddResultSetExtractor(string name, IResultSetExtractor resultSetExtractor) + { + if (Compiled) + { + throw new InvalidDataAccessApiUsageException("Cannot add ResultSetExtractors once operation is compiled"); + } + resultProcessors.Add(new NamedResultSetProcessor(name, resultSetExtractor)); + } + + public void AddRowCallback(string name, IRowCallback rowCallback) + { + if (Compiled) + { + throw new InvalidDataAccessApiUsageException("Cannot add RowCallbacks once operation is compiled"); + } + resultProcessors.Add(new NamedResultSetProcessor(name, rowCallback)); + } + + public void AddRowMapper(string name, IRowMapper rowMapper) + { + if (Compiled) + { + throw new InvalidDataAccessApiUsageException("Cannot add RowMappers once operation is compiled"); + } + resultProcessors.Add(new NamedResultSetProcessor(name, rowMapper)); + } + + #endregion + + #region Generic result set processors + + public void AddResultSetExtractor(string name, IResultSetExtractor resultSetExtractor) + { + if (Compiled) + { + throw new InvalidDataAccessApiUsageException("Cannot add ResultSetExtractors once operation is compiled"); + } + resultProcessors.Add(new NamedResultSetProcessor(name, resultSetExtractor)); + } + + + public void AddRowMapper(string name, IRowMapper rowMapper) + { + if (Compiled) + { + throw new InvalidDataAccessApiUsageException("Cannot add RowMappers once operation is compiled"); + } + resultProcessors.Add(new NamedResultSetProcessor(name,rowMapper)); + } + #endregion + + #region Operations that use derived parameters + protected virtual IDictionary ExecuteScalar(params object[] inParameterValues) + { + ValidateParameters(inParameterValues); + return AdoTemplate.ExecuteScalar(NewCommandCreatorWithParamValues(inParameterValues)); + } + + protected virtual IDictionary ExecuteNonQuery(params object[] inParameterValues) + { + ValidateParameters(inParameterValues); + return AdoTemplate.ExecuteNonQuery(NewCommandCreatorWithParamValues(inParameterValues)); + } + + + public System.Collections.Generic.IList QueryWithRowMapper(params object[] inParameterValues) + { + ValidateParameters(inParameterValues); + if (resultProcessors.Count == 0) + { + throw new InvalidDataAccessApiUsageException("No row mapper is specified."); + } + + NamedResultSetProcessor resultSetProcessor = resultProcessors[0] as NamedResultSetProcessor; + if (resultSetProcessor == null) + { + throw new InvalidDataAccessApiUsageException("No row mapper is specified."); + } + + if (resultSetProcessor.RowMapper == null) + { + throw new InvalidDataAccessApiUsageException("No row mapper is specified as first result set processor."); + } + IDictionary outParams = Query(inParameterValues); + return outParams[resultSetProcessor.Name] as System.Collections.Generic.IList; + + } + + protected virtual IDictionary Query(params object[] inParameterValues) + { + ValidateParameters(inParameterValues); + return AdoTemplate.QueryWithCommandCreator(NewCommandCreatorWithParamValues(inParameterValues), resultProcessors); + + } + + protected virtual IDictionary Query(params object[] inParameterValues) + { + ValidateParameters(inParameterValues); + return AdoTemplate.QueryWithCommandCreator(NewCommandCreatorWithParamValues(inParameterValues), resultProcessors); + + } + + #endregion + + + #region Operations that used provided named parameters + /// + /// Execute the stored procedure using 'ExecuteScalar' + /// + /// Value of input parameters. + /// Dictionary with any named output parameters and the value of the + /// scalar under the key "scalar". + protected virtual IDictionary ExecuteScalarByNamedParam(IDictionary inParams) + { + ValidateNamedParameters(inParams); + return AdoTemplate.ExecuteScalar(NewCommandCreator(inParams)); + } + + protected virtual IDictionary ExecuteNonQueryByNamedParam(IDictionary inParams) + { + ValidateNamedParameters(inParams); + return AdoTemplate.ExecuteNonQuery(NewCommandCreator(inParams)); + } + + + protected virtual IDictionary QueryByNamedParam(IDictionary inParams) + { + ValidateNamedParameters(inParams); + return AdoTemplate.QueryWithCommandCreator(NewCommandCreator(inParams), resultProcessors); + } + + protected virtual IDictionary QueryByNamedParam(IDictionary inParams) + { + ValidateNamedParameters(inParams); + return AdoTemplate.QueryWithCommandCreator(NewCommandCreator(inParams), resultProcessors); + } + + #endregion + + protected override bool IsInputParameter(IDataParameter parameter) + { + if (usingDerivedParameters) + { + //Can only count Input, derived output parameters are incorrectly classified as input-output + return (parameter.Direction == ParameterDirection.Input); + } + else + { + return base.IsInputParameter(parameter); + } + } + + #endregion + + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Objects/MappingAdoQuery.cs b/src/Spring/Spring.Data/Data/Objects/MappingAdoQuery.cs new file mode 100644 index 00000000..e56743dc --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/MappingAdoQuery.cs @@ -0,0 +1,75 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Data; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.Objects +{ + /// + /// Reusable query in which concrete subclasses must implement the + /// MapRow method to map each row of a single result set into an + /// object. + /// + /// + /// Simplifies MappingAdoQueryWithContext API by dropping parameters and + /// context. Most subclasses won't care about parameters. If you don't use + /// contextual information, subclass this instead of MappingSqlQueryWithContext. + /// + /// Mark Pollack (.NET) + /// $Id: MappingAdoQuery.cs,v 1.5 2008/05/29 17:26:35 markpollack Exp $ + public abstract class MappingAdoQuery : MappingAdoQueryWithContext + { + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public MappingAdoQuery() + { + + } + + public MappingAdoQuery(IDbProvider dbProvider, string sql) : base(dbProvider, sql) + { + + } + + #endregion + + #region Methods + + protected override object MapRow(IDataReader reader, int rowNum, IDictionary inParams, + IDictionary callingContext) + { + return MapRow(reader, rowNum); + } + + protected abstract object MapRow(IDataReader reader, int num); + + #endregion + + + } +} diff --git a/src/Spring/Spring.Data/Data/Objects/MappingAdoQueryWithContext.cs b/src/Spring/Spring.Data/Data/Objects/MappingAdoQueryWithContext.cs new file mode 100644 index 00000000..bde0ded0 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/MappingAdoQueryWithContext.cs @@ -0,0 +1,97 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.Objects +{ + /// + /// Reusable query in which concrete subclasses must implement the + /// MapRow method to map each row of a single result set into an + /// object. + /// + /// The MapRow method is also passed the input parameters and + /// a dictionary to provide the method with calling context information + /// (if necessary). If you do not need these additional parameters, use + /// the subclass MappingAdoQuery. + /// + /// Mark Pollack (.NET) + /// $Id: MappingAdoQueryWithContext.cs,v 1.4 2008/05/29 17:26:35 markpollack Exp $ + public abstract class MappingAdoQueryWithContext : AdoQuery + { + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public MappingAdoQueryWithContext() + { + + } + + public MappingAdoQueryWithContext(IDbProvider dbProvider, string sql) : base(dbProvider, sql) + { + + } + #endregion + + #region Properties + + #endregion + + #region Methods + + protected override IRowMapper NewRowMapper(IDictionary inParams, IDictionary callingContext) + { + return new RowMapperImpl(this, inParams, callingContext); + } + + protected abstract Object MapRow(IDataReader reader, int rowNum, IDictionary inParams, IDictionary callingContext); + + + #endregion + + private class RowMapperImpl : IRowMapper + { + private MappingAdoQueryWithContext outer; + private IDictionary inParams; + private IDictionary callingContext; + + public RowMapperImpl(MappingAdoQueryWithContext mappingAdoQueryWithContext, IDictionary inParams, IDictionary callingContext) + { + outer = mappingAdoQueryWithContext; + this.inParams = inParams; + this.callingContext = callingContext; + } + + public object MapRow(IDataReader dataReader, int rowNum) + { + return outer.MapRow(dataReader, rowNum, inParams, callingContext); + } + } + + } +} diff --git a/src/Spring/Spring.Data/Data/Objects/StoredProcedure.cs b/src/Spring/Spring.Data/Data/Objects/StoredProcedure.cs new file mode 100644 index 00000000..876abb7e --- /dev/null +++ b/src/Spring/Spring.Data/Data/Objects/StoredProcedure.cs @@ -0,0 +1,180 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Data; +using Spring.Collections; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Data.Support; + +#endregion + +namespace Spring.Data.Objects +{ + /// + /// A superclass for object based abstractions of RDBMS stored procedures. + /// + /// Mark Pollack (.NET) + /// $Id: StoredProcedure.cs,v 1.9 2007/07/25 08:25:20 markpollack Exp $ + public abstract class StoredProcedure : AdoOperation + { + #region Fields + + //A collection of NamedResultSetProcessor + private IList resultProcessors = new LinkedList(); + private bool usingDerivedParameters = false; + + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public StoredProcedure() + { + CommandType = CommandType.StoredProcedure; + } + + public StoredProcedure(IDbProvider dbProvider, string procedureName) : base(dbProvider, procedureName) + { + CommandType = CommandType.StoredProcedure; + } + + + + #endregion + + #region Properties + + #endregion + + #region Methods + + public void DeriveParameters() + { + DeriveParameters(false); + } + + + public void DeriveParameters(bool includeReturnParameter) + { + //TODO does this account for offsets? + IDataParameter[] derivedParameters = AdoTemplate.DeriveParameters(Sql, includeReturnParameter); + for (int i = 0; i < derivedParameters.Length; i++) + { + IDataParameter parameter = derivedParameters[i]; + DeclaredParameters.AddParameter(parameter); + } + usingDerivedParameters = true; + } + + + public void AddResultSetExtractor(string name, IResultSetExtractor resultSetExtractor) + { + if (Compiled) + { + throw new InvalidDataAccessApiUsageException("Cannot add ResultSetExtractors once operation is compiled"); + } + resultProcessors.Add(new NamedResultSetProcessor(name, resultSetExtractor)); + } + + public void AddRowCallback(string name, IRowCallback rowCallback) + { + if (Compiled) + { + throw new InvalidDataAccessApiUsageException("Cannot add RowCallbacks once operation is compiled"); + } + resultProcessors.Add(new NamedResultSetProcessor(name,rowCallback)); + } + + public void AddRowMapper(string name, IRowMapper rowMapper) + { + if (Compiled) + { + throw new InvalidDataAccessApiUsageException("Cannot add RowMappers once operation is compiled"); + } + resultProcessors.Add(new NamedResultSetProcessor(name,rowMapper)); + } + + + protected virtual IDictionary ExecuteScalar(params object[] inParameterValues) + { + ValidateParameters(inParameterValues); + return AdoTemplate.ExecuteScalar(NewCommandCreatorWithParamValues(inParameterValues)); + } + + protected virtual IDictionary ExecuteNonQuery(params object[] inParameterValues) + { + ValidateParameters(inParameterValues); + return AdoTemplate.ExecuteNonQuery(NewCommandCreatorWithParamValues(inParameterValues)); + } + + protected virtual IDictionary Query(params object[] inParameterValues) + { + ValidateParameters(inParameterValues); + return AdoTemplate.QueryWithCommandCreator(NewCommandCreatorWithParamValues(inParameterValues), resultProcessors); + } + + /// + /// Execute the stored procedure using 'ExecuteScalar' + /// + /// Value of input parameters. + /// Dictionary with any named output parameters and the value of the + /// scalar under the key "scalar". + protected virtual IDictionary ExecuteScalarByNamedParam(IDictionary inParams) + { + ValidateNamedParameters(inParams); + return AdoTemplate.ExecuteScalar(NewCommandCreator(inParams)); + } + + protected virtual IDictionary ExecuteNonQueryByNamedParam(IDictionary inParams) + { + ValidateNamedParameters(inParams); + return AdoTemplate.ExecuteNonQuery(NewCommandCreator(inParams)); + } + + + protected virtual IDictionary QueryByNamedParam(IDictionary inParams) + { + ValidateNamedParameters(inParams); + return AdoTemplate.QueryWithCommandCreator(NewCommandCreator(inParams), resultProcessors); + } + + protected override bool IsInputParameter(IDataParameter parameter) + { + if (usingDerivedParameters) + { + //Can only count Input, derived output parameters are incorrectly classified as input-output + return (parameter.Direction == ParameterDirection.Input); + } + else + { + return base.IsInputParameter(parameter); + } + } + + #endregion + + } +} diff --git a/src/Spring/Spring.Data/Data/ResultSetExtractorDelegate.cs b/src/Spring/Spring.Data/Data/ResultSetExtractorDelegate.cs new file mode 100644 index 00000000..ac9eaf28 --- /dev/null +++ b/src/Spring/Spring.Data/Data/ResultSetExtractorDelegate.cs @@ -0,0 +1,40 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; + + +namespace Spring.Data +{ + /// + /// Callback delegate to process all result sets and row in an + /// AdoTemplate query method. + /// + /// + /// Implementations of this delegate perform the work + /// of extracting results but don't need worry about managing + /// ADO.NET resources, such as closing the reader, or transaction management. + /// + /// The IDataReader to extract data from. + /// Implementations should not close this: it will be closed + /// by the AdoTemplate. + /// An arbitrary result object or null if none. + public delegate object ResultSetExtractorDelegate(IDataReader reader); +} diff --git a/src/Spring/Spring.Data/Data/RowCallbackDelegate.cs b/src/Spring/Spring.Data/Data/RowCallbackDelegate.cs new file mode 100644 index 00000000..775e1349 --- /dev/null +++ b/src/Spring/Spring.Data/Data/RowCallbackDelegate.cs @@ -0,0 +1,8 @@ + + +using System.Data; + +namespace Spring.Data +{ + public delegate void RowCallbackDelegate(IDataReader dataReader); +} diff --git a/src/Spring/Spring.Data/Data/RowMapperDelegate.cs b/src/Spring/Spring.Data/Data/RowMapperDelegate.cs new file mode 100644 index 00000000..1f4c5dbf --- /dev/null +++ b/src/Spring/Spring.Data/Data/RowMapperDelegate.cs @@ -0,0 +1,35 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +using System.Data; + +namespace Spring.Data +{ + /// + /// Callback delegate to process each row of data in a result set to an object. + /// + /// The IDataReader to map + /// the number of the current row. + /// An abrirary object, typically derived from data + /// in the result set. + public delegate object RowMapperDelegate(IDataReader dataReader, int rowNum); + +} diff --git a/src/Spring/Spring.Data/Data/Support/AdoTransactionObjectSupport.cs b/src/Spring/Spring.Data/Data/Support/AdoTransactionObjectSupport.cs new file mode 100644 index 00000000..7e5862d0 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/AdoTransactionObjectSupport.cs @@ -0,0 +1,170 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using Common.Logging; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.Support +{ + /// + /// Convenient base class for ADO.NET transaction aware objects. + /// + /// + /// Can contain a ConnectionHolder object. + /// + /// Mark Pollack (.NET) + /// $Id: AdoTransactionObjectSupport.cs,v 1.7 2007/11/28 05:54:47 markpollack Exp $ + public abstract class AdoTransactionObjectSupport : ISavepointManager, ISmartTransactionObject + { + #region Fields + + private ConnectionHolder connectionHolder; + + private IsolationLevel previousIsolationLevel; + + private bool savepointAllowed; + + #endregion + + #region Constants + + /// + /// The shared log instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (AdoTransactionObjectSupport)); + + #endregion + + #region Properties + + public ConnectionHolder ConnectionHolder + { + get { return connectionHolder; } + set { connectionHolder = value; } + } + + public bool HasConnectionHolder + { + get { return (connectionHolder != null); } + } + + public IsolationLevel PreviousIsolationLevel + { + get { return previousIsolationLevel; } + set { previousIsolationLevel = value; } + } + + public bool SavepointAllowed + { + get { return savepointAllowed; } + set { savepointAllowed = value; } + } + + /// + /// Return whether the transaction is internally marked as rollback-only. + /// + /// + /// True of the transaction is marked as rollback-only. + public abstract bool RollbackOnly { get; } + + #endregion + + /// + /// Create a new savepoint. + /// + /// + /// The name of the savepoint to create. + /// + /// + /// You can roll back to a specific savepoint + /// via , + /// and explicitly release a savepoint that you don't need anymore via + /// . + ///

    + /// Note that most transaction managers will automatically release + /// savepoints at transaction completion. + ///

    + ///
    + /// + /// If the savepoint could not be created, + /// either because the backend does not support it or because the + /// transaction is not in an appropriate state. + /// + /// + /// A savepoint object, to be passed into + /// + /// or . + /// + public virtual void CreateSavepoint(string savepointName) + { + throw new NestedTransactionNotSupportedException( + "Cannot create a nested transaction because savepoints have not been implemented in the metadata for the DbProvider."); + } + + /// + /// Roll back to the given savepoint. + /// + /// + /// The savepoint will be automatically released afterwards. + /// + /// The savepoint to roll back to. + /// + /// If the rollback failed. + /// + public virtual void RollbackToSavepoint(string savepoint) + { + throw new NestedTransactionNotSupportedException( + "Cannot rollback to a savepoint in a nested transaction because savepoints have not been implemented in the metadata for the DbProvider."); + + } + + /// + /// Explicitly release the given savepoint. + /// + /// + ///

    + /// Note that most transaction managers will automatically release + /// savepoints at transaction completion. + ///

    + ///

    + /// Implementations should fail as silently as possible if + /// proper resource cleanup will still happen at transaction completion. + ///

    + ///
    + /// The savepoint to release. + /// + /// If the release failed. + /// + public virtual void ReleaseSavepoint(string savepoint) + { + throw new NestedTransactionNotSupportedException( + "Cannot release a savepoint in a nested transaction because savepoints have not been implemented in the metadata for the DbProvider."); + + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Support/AdoUtils.cs b/src/Spring/Spring.Data/Data/Support/AdoUtils.cs new file mode 100644 index 00000000..c49775ab --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/AdoUtils.cs @@ -0,0 +1,89 @@ +using System; +using System.Data; +using Common.Logging; + +namespace Spring.Data.Support +{ + /// + /// Summary description for AdoUtils. + /// + public abstract class AdoUtils + { + #region Logging + + private static readonly ILog LOG = LogManager.GetLogger(typeof (AdoUtils)); + + #endregion + + #region Constants + + /// + /// Order value for TransactionSynchronization objects that clean up + /// ADO.NET Connections. + /// + public static readonly int CONNECTION_SYNCHRONIZATION_ORDER = 1000; + + #endregion + /// + /// Property dispose of the command. Useful in finally or catch blocks. + /// + /// command to dispose + public static void DisposeCommand(IDbCommand command) + { + if (command != null) + { + DoDisposeCommand(command); + } + } + + + public static void DisposeDataAdapterCommands(IDbDataAdapter adapter) + { + if (adapter.SelectCommand != null) + { + DoDisposeCommand(adapter.SelectCommand); + } + if (adapter.InsertCommand != null) + { + DoDisposeCommand(adapter.InsertCommand); + } + if (adapter.UpdateCommand != null) + { + DoDisposeCommand(adapter.UpdateCommand); + } + if (adapter.DeleteCommand != null) + { + DoDisposeCommand(adapter.DeleteCommand); + } + + } + public static void CloseReader(IDataReader reader) + { + if (reader != null) + { + try + { + reader.Close(); + } + catch (Exception e) + { + LOG.Warn("Could not close IDataRader", e); + } + } + } + + private static void DoDisposeCommand(IDbCommand command) + { + try + { + command.Dispose(); + } + catch (Exception e) + { + LOG.Warn("Could not dispose of command", e); + } + } + + } + +} diff --git a/src/Spring/Spring.Data/Data/Support/ConnectionHolder.cs b/src/Spring/Spring.Data/Data/Support/ConnectionHolder.cs new file mode 100644 index 00000000..7e65fd72 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/ConnectionHolder.cs @@ -0,0 +1,72 @@ +using System.Data; +using Spring.Transaction.Support; + +namespace Spring.Data.Support +{ + /// + /// Connection holder, wrapping a ADO.NET connection and transaction. + /// + /// AdoTransactionManager binds instances of this class to the + /// thread for a given DbProvider. + /// Jurgen Hoeller + /// Mark Pollack (.NET) + public class ConnectionHolder : ResourceHolderSupport + { + private IDbConnection currentConnection; + + private IDbTransaction currentTransaction; + + private bool transactionActive = false; + + /// + /// Create a new ConnectionHolder + /// + /// The connection to hold + /// The transaction to hold + public ConnectionHolder(IDbConnection conn, IDbTransaction transaction) + { + //TODO assert conn is not null. + currentConnection = conn; + currentTransaction = transaction; + } + + public IDbConnection Connection + { + get + { + return currentConnection; + } + set + { + currentConnection = value; + } + + } + + public IDbTransaction Transaction + { + get { return currentTransaction; } + set { currentTransaction = value; } + } + + public bool HasConnection + { + get + { + return (currentConnection != null); + } + } + + public bool TransactionActive + { + get { return transactionActive; } + set { transactionActive = value; } + } + + public override void Clear() + { + base.Clear(); + transactionActive = false; + } + } +} diff --git a/src/Spring/Spring.Data/Data/Support/ConnectionSynchronization.cs b/src/Spring/Spring.Data/Data/Support/ConnectionSynchronization.cs new file mode 100644 index 00000000..d8a7e3d3 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/ConnectionSynchronization.cs @@ -0,0 +1,129 @@ +using Spring.Data.Common; +using Spring.Transaction.Support; + +namespace Spring.Data.Support +{ + /// + /// Callback for resource cleanup at end of transaction. + /// + public class ConnectionSynchronization : TransactionSynchronizationAdapter + { + + private ConnectionHolder connectionHolder; + + private IDbProvider dbProvider; + + private bool holderActive = true; + + private int order; + /// + /// Create a new instance. + /// + /// + /// + public ConnectionSynchronization(ConnectionHolder connHolder, IDbProvider provider) + { + connectionHolder = connHolder; + dbProvider = provider; + order = ConnectionUtils.CONNECTION_SYNCHRONIZATION_ORDER; + } + + + /// + /// Compares the current instance with another object of the same type. + /// + /// An object to compare with this instance. + /// + /// The value of the order property. + /// + public override int CompareTo(object obj) + { + return order; + } + /// + /// Suspend this synchronization. + /// + /// + ///

    + /// Supposed to unbind resources from + /// + /// if managing any. + ///

    + ///
    + public override void Suspend() + { + if (holderActive) + { + TransactionSynchronizationManager.UnbindResource(dbProvider); + + //TODO do we need to reset the connection inside the conHolder to null? + } + } + + /// + /// Resume this synchronization. + /// + /// + ///

    + /// Supposed to unbind resources from + /// + /// if managing any. + ///

    + ///
    + public override void Resume() + { + if (holderActive) + { + TransactionSynchronizationManager.BindResource(dbProvider, connectionHolder); + } + } + + /// + /// Invoked before transaction commit/rollback (after + /// , + /// even if + /// + /// threw an exception). + /// + /// + ///

    + /// Can e.g. perform resource cleanup. + ///

    + ///

    + /// Note that exceptions will get propagated to the commit caller + /// and cause a rollback of the transaction. + ///

    + ///
    + public override void BeforeCompletion() + { + if (connectionHolder.IsOpen ) + { + TransactionSynchronizationManager.UnbindResource(dbProvider); + holderActive= false; + ConnectionUtils.DisposeConnection(connectionHolder.Connection, dbProvider); + } + } + + /// + /// Invoked after transaction commit/rollback. + /// + /// Status according to + /// + /// Can e.g. perform resource cleanup, in this case after transaction completion. + ///

    + /// Note that exceptions will get propagated to the commit or rollback + /// caller, although they will not influence the outcome of the transaction. + ///

    + ///
    + public override void AfterCompletion(TransactionSynchronizationStatus status) + { + if (TransactionSynchronizationManager.HasResource(dbProvider)) + { + TransactionSynchronizationManager.UnbindResource(dbProvider); + holderActive = false; + ConnectionUtils.DisposeConnection(connectionHolder.Connection, dbProvider); + } + } + + } +} diff --git a/src/Spring/Spring.Data/Data/Support/ConnectionTxPair.cs b/src/Spring/Spring.Data/Data/Support/ConnectionTxPair.cs new file mode 100644 index 00000000..0ae2808e --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/ConnectionTxPair.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; + +namespace Spring.Data.Support +{ + /// + /// A simple holder for the current Connection/Transaction objects + /// to use within a given AdoTemplate Execute operation. Used internally. + /// + public class ConnectionTxPair + { + private IDbConnection connection; + private IDbTransaction transaction; + + /// + /// Initializes a new instance of the class. + /// + /// The connection. + /// The transaction. + public ConnectionTxPair(IDbConnection connection, IDbTransaction transaction) + { + this.connection = connection; + this.transaction = transaction; + } + + /// + /// Gets the connection. + /// + /// The connection. + public IDbConnection Connection + { + get { return connection; } + set { connection = value; } + } + + /// + /// Gets the transaction. + /// + /// The transaction. + public IDbTransaction Transaction + { + get { return transaction; } + } + } +} diff --git a/src/Spring/Spring.Data/Data/Support/ConnectionUtils.cs b/src/Spring/Spring.Data/Data/Support/ConnectionUtils.cs new file mode 100644 index 00000000..40e82181 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/ConnectionUtils.cs @@ -0,0 +1,246 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Data; +using Common.Logging; +using Spring.Data.Common; +using Spring.Transaction.Support; +using Spring.Util; + +namespace Spring.Data.Support +{ + /// + /// Summary description for DbProviderUtils. + /// + public abstract class ConnectionUtils + { + #region Logging + + private static readonly ILog LOG = LogManager.GetLogger(typeof (ConnectionUtils)); + + #endregion + + public static readonly int CONNECTION_SYNCHRONIZATION_ORDER = 1000; + /// + /// Dispose of the given Connection, created via the given IDbProvider, + /// if it is not managed externally (that is, not bound to the thread). + /// + /// The connection to close if necessary. If + /// this is null the call will be ignored. + /// The IDbProvider the connection came from + public static void DisposeConnection(IDbConnection conn, IDbProvider dbProvider) + { + try + { + DoDisposeConnection(conn, dbProvider); + } catch (Exception e) + { + LOG.Warn("Could not close connection", e); + } + + } + private static void DoDisposeConnection(IDbConnection conn, IDbProvider dbProvider) + { + if (conn == null) + { + return; + } + + if (dbProvider != null) + { + ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.GetResource(dbProvider); + if (conHolder != null && connectionEquals(conHolder.Connection, conn)) + { + // It's the transactional connection bound to the thread so don't close it. + conHolder.Released(); + return; + } + } + if (LOG.IsDebugEnabled) + { + LOG.Debug("Disposing of IDbConnection with connection string = [" + dbProvider.ConnectionString + "]"); + } + conn.Dispose(); + } + + + /// + /// Get a ADO.NET Connection/Transaction Pair for the given IDbProvider. + /// Changes any exception into the Spring hierarchy of generic data access + /// exceptions, simplifying calling code and making any exception that is + /// thrown more meaningful. + /// + /// + /// Is aware of a corresponding Connection/Transaction bound to the current thread, for example + /// when using AdoPlatformTransactionManager. Will bind a IDbConnection to the thread + /// if transaction synchronization is active + /// + /// The provider. + /// A Connection/Transaction pair. + public static ConnectionTxPair GetConnectionTxPair(IDbProvider provider) + { + try + { + return DoGetConnection(provider); + } catch (Exception e) + { + throw new CannotGetAdoConnectionException("Could not get ADO.NET connection.", e); + } + + } + + + /// + /// Get a ADO.NET Connection/Transaction Pair for the given IDbProvider. + /// Same as but throwing original provider + /// exception. + /// + /// + /// Is aware of a corresponding Connection/Transaction bound to the current thread, for example + /// when using AdoPlatformTransactionManager. Will bind a IDbConnection to the thread + /// if transaction synchronization is active + /// + /// The provider. + /// + public static ConnectionTxPair DoGetConnection(IDbProvider provider) + { + AssertUtils.ArgumentNotNull(provider, "provider"); + ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.GetResource(provider); + if (conHolder != null && (conHolder.HasConnection || conHolder.SynchronizedWithTransaction)) + { + conHolder.Requested(); + if (!conHolder.HasConnection) + { + if (LOG.IsDebugEnabled) + { + LOG.Debug("Fetching resumed ADO.NET connection from DbProvider"); + } + conHolder.Connection = provider.CreateConnection(); + } + return new ConnectionTxPair(conHolder.Connection, conHolder.Transaction); + } + + // Else we either got no holder or an empty thread-bound holder here. + if (LOG.IsDebugEnabled) + { + LOG.Debug("Fetching Connection from DbProvider"); + } + IDbConnection conn = provider.CreateConnection(); + conn.Open(); + + if (TransactionSynchronizationManager.SynchronizationActive) + { + LOG.Debug("Registering transaction synchronization for IDbConnection"); + //Use same connection for further ADO.NET actions with the transaction. + //Thread-bound object will get removed by manager at transaction completion. + + //TODO investigate creating tx object... + ConnectionHolder holderToUse = conHolder; + if (holderToUse == null) + { + holderToUse = new ConnectionHolder(conn, null); + } + else + { + holderToUse.Connection = conn; + } + holderToUse.Requested(); + TransactionSynchronizationManager.RegisterSynchronization( + new ConnectionSynchronization(holderToUse, provider)); + holderToUse.SynchronizedWithTransaction = true; + if (holderToUse != conHolder) + { + TransactionSynchronizationManager.BindResource(provider, holderToUse); + } + + } + return new ConnectionTxPair(conn, null); + } + + //TODO exception translation? + /// + /// Do the connection mgmt. + /// + /// + /// + public static IDbConnection GetConnection(IDbProvider provider) + { + AssertUtils.ArgumentNotNull(provider, "provider"); + + return GetConnectionTxPair(provider).Connection; + + } + + private static bool connectionEquals(IDbConnection heldCon, IDbConnection passedInCon) + { + return (heldCon == passedInCon || heldCon.Equals(passedInCon) || + getTargetConnection(heldCon).Equals(passedInCon)); + } + + private static IDbConnection getTargetConnection(IDbConnection con) + { + IDbConnection conToUse = con; + /* + while (conToUse is ConnectionProxy) + { + conToUse = (ConnectionProxy)conToUse.getTargetConnection(); + } + */ + return conToUse; + } + + /// + /// Applies the current transaction timeout, if any, to the given ADO.NET IDbCommand object. + /// + /// The command. + /// The db provider. + public static void ApplyTransactionTimeout(IDbCommand command, IDbProvider dbProvider) + { + ApplyTransactionTimeout(command, dbProvider, 0); + } + + /// + /// Applies the specified timeout - overridden by the current transaction timeout, if any, to to the + /// given ADO.NET IDb command object. + /// + /// The command. + /// The db provider the command was obtained from. + /// The timeout to apply (or 0 for no timeout outside of a transaction. + public static void ApplyTransactionTimeout(IDbCommand command, IDbProvider dbProvider, int timeout) + { + AssertUtils.ArgumentNotNull(command, "command", "No IDbCommand specified."); + AssertUtils.ArgumentNotNull(dbProvider, "dbProvider", "No IDbProvider specified."); + + ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.GetResource(dbProvider); + if (conHolder != null && conHolder.HasTimeout) + { + // Remaining transaction timeout overrides specified value. + command.CommandTimeout = conHolder.TimeToLiveInSeconds; + } + else if (timeout > 0) + { + // No current transaction timeout -> apply specified value. + command.CommandTimeout = timeout; + } + + } + } +} diff --git a/src/Spring/Spring.Data/Data/Support/DefaultServiceDomainAdapter.cs b/src/Spring/Spring.Data/Data/Support/DefaultServiceDomainAdapter.cs new file mode 100644 index 00000000..3a28ae0f --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/DefaultServiceDomainAdapter.cs @@ -0,0 +1,95 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if (!NET_1_0) + +using System.EnterpriseServices; + +namespace Spring.Data.Support +{ + /// + /// Implementation that delegates to ServiceDomain and ContextUtil for all functionality. + /// + /// Introduced for purposes of unit testing. + /// Mark Pollack (.NET) + /// $Id $ + public class DefaultServiceDomainAdapter : IServiceDomainAdapter + { + + /// + /// Creates the context specified by the ServiceConfig object and pushes it onto the context stack to + /// become the current context. + /// + /// A ServiceConfig that contains the configuration information for the services to be used within the enclosed code. + public void Enter(SimpleServiceConfig config) + { + ServiceConfig serviceConfig = config.CreateServiceConfig(); + ServiceDomain.Enter(serviceConfig); + } + + /// + /// Leaves this ServiceDomain. The current context is then popped from the context stack, and the + /// context that was running when Enter was called becomes the current context. + /// + /// One of the TransactionStatus values + public TransactionStatus Leave() + { + return ServiceDomain.Leave(); + } + + /// + /// Sets the consistent bit and the done bit to true in the COM+ context + /// + public void SetComplete() + { + ContextUtil.SetComplete(); + } + + /// + /// Sets the consistent bit to false and the done bit to true in the COM+ context. + /// + public void SetAbort() + { + ContextUtil.SetAbort(); + } + + /// + /// Gets or sets the consistent bit in the COM+ context. + /// + /// My transaction vote. + public TransactionVote MyTransactionVote + { + get { return ContextUtil.MyTransactionVote; } + set { ContextUtil.MyTransactionVote = value; } + } + + /// + /// Gets a value indicating whether the current context is transactional. + /// + /// + /// true if this instance is existing transaction; otherwise, false. + /// + public bool IsInTransaction + { + get { return ContextUtil.IsInTransaction; } + } + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Support/DefaultTransactionScopeAdapter.cs b/src/Spring/Spring.Data/Data/Support/DefaultTransactionScopeAdapter.cs new file mode 100644 index 00000000..9bb8c6cb --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/DefaultTransactionScopeAdapter.cs @@ -0,0 +1,109 @@ +#if NET_2_0 + +#region License + +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Transactions; + +namespace Spring.Data.Support +{ + /// + /// Uses and System.Transactions.Transaction.Current to provide + /// necessary state and operations to TxScopeTransactionManager. + /// + /// Mark Pollack (.NET) + /// $Id: DefaultTransactionScopeAdapter.cs,v 1.1 2007/11/30 18:38:47 markpollack Exp $ + public class DefaultTransactionScopeAdapter : ITransactionScopeAdapter + { + private TransactionScope txScope; + + /// + /// Call Complete() on the TransactionScope object created by this instance. + /// + public void Complete() + { + txScope.Complete(); + } + + /// + /// Call Disponse() on the TransactionScope object created by this instance. + /// + public void Dispose() + { + txScope.Dispose(); + } + + + /// + /// Gets a value indicating whether there is a new transaction or an existing transaction. + /// + /// + /// true if this instance is existing transaction; otherwise, false. + /// + public bool IsExistingTransaction + { + get { + if (System.Transactions.Transaction.Current != null) + { + return true; + } + else + { + return false; + } + } + } + + /// + /// Gets a value indicating whether rollback only has been called (i.e. Rollback() on the + /// Transaction object) and therefore voting that the transaction will be aborted. + /// + /// true if rollback only; otherwise, false. + public bool RollbackOnly + { + get + { + if (System.Transactions.Transaction.Current != null && + System.Transactions.Transaction.Current.TransactionInformation != null && + System.Transactions.Transaction.Current.TransactionInformation.Status == TransactionStatus.Aborted) + { + return true; + } + else + { + return false; + } + } + } + + /// + /// Creates the transaction scope. + /// + /// The tx scope option. + /// The tx options. + /// The interop option. + public void CreateTransactionScope(TransactionScopeOption txScopeOption, TransactionOptions txOptions, + EnterpriseServicesInteropOption interopOption) + { + txScope = new TransactionScope(txScopeOption, txOptions, interopOption); + } + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Support/ErrorCodeExceptionTranslator.cs b/src/Spring/Spring.Data/Data/Support/ErrorCodeExceptionTranslator.cs new file mode 100644 index 00000000..5724bbf1 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/ErrorCodeExceptionTranslator.cs @@ -0,0 +1,301 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using Spring.Dao; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.Support +{ + /// + /// Implementation of IAdoExceptionTranslator that analyzes provider specific + /// error codes and translates into the DAO exception hierarchy. + /// + /// This class loads the obtains error codes from + /// the IDbProvider metadata property ErrorCodes + /// which defines error code mappings for various providers. + /// + /// Mark Pollack (.NET) + /// $Id: ErrorCodeExceptionTranslator.cs,v 1.11 2007/12/06 20:14:30 markpollack Exp $ + public class ErrorCodeExceptionTranslator : IAdoExceptionTranslator + { + #region Fields + + private ErrorCodes errorCodes; + + private IAdoExceptionTranslator fallbackTranslator; + + private IDbProvider dbProvider; + + #endregion + + #region Constants + + /// + /// The shared log instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (ErrorCodeExceptionTranslator)); + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + /// The data provider. + public ErrorCodeExceptionTranslator(IDbProvider provider) + { + DbProvider = provider; + } + + + + /// + /// Initializes a new instance of the class. + /// + /// The data provider. + /// The error code collection. + public ErrorCodeExceptionTranslator(IDbProvider provider, ErrorCodes ec) + { + DbProvider = provider; + errorCodes = ec; + } + + + + #endregion + + #region Properties + + /// + /// Sets the db provider. + /// + /// The db provider. + public IDbProvider DbProvider + { + set + { + dbProvider = value; + fallbackTranslator = new FallbackExceptionTranslator(); + errorCodes = dbProvider.DbMetadata.ErrorCodes; + + } + } + + /// + /// Gets the error codes for the provider + /// + /// The error codes. + public ErrorCodes ErrorCodes + { + get + { + return errorCodes; + } + } + + #endregion + + /// + /// Gets or sets the fallback translator to use in case error code based translation + /// fails. + /// + /// The fallback translator. + public IAdoExceptionTranslator FallbackTranslator + { + get + { + return fallbackTranslator; + } + set + { + fallbackTranslator = value; + } + } + + + #region Methods + + #endregion + + /// + /// Translate the given into a generic data access exception. + /// + /// A readable string describing the task being attempted. + /// The SQL query or update that caused the problem. May be null. + /// + /// The encountered by the ADO.NET implementation. + /// + /// + /// A appropriate for the supplied + /// . + /// + public virtual DataAccessException Translate(string task, string sql, Exception exception) + { + if (task == null) + { + task = ""; + } + if (sql == null) + { + sql = ""; + } + string errorCode = ExtractErrorCode(exception); + + // First, try custom translation from overridden method. + DataAccessException dex = TranslateException(task, sql, errorCode, exception); + if (dex != null) + { + return dex; + } + + + if (errorCodes != null) + { + + if (errorCode != null) + { + if (Array.IndexOf(errorCodes.BadSqlGrammarCodes, errorCode) >= 0) + { + LogTranslation(task, sql, errorCode, exception, false); + return new BadSqlGrammarException(task, sql, exception); + } + else if (Array.IndexOf(errorCodes.InvalidResultSetAccessCodes, errorCode) >= 0) + { + LogTranslation(task, sql, errorCode, exception, false); + return new InvalidResultSetAccessException(task, sql, exception); + } + else if (Array.IndexOf(errorCodes.DataAccessResourceFailureCodes, errorCode) >= 0) + { + LogTranslation(task, sql, errorCode, exception, false); + return new DataAccessResourceFailureException(BuildMessage(task, sql, exception), exception); + } + else if (Array.IndexOf(errorCodes.PermissionDeniedCodes, errorCode) >= 0) + { + LogTranslation(task, sql, errorCode, exception, false); + return new PermissionDeniedDataAccessException(BuildMessage(task, sql, exception), exception); + } + else if (Array.IndexOf(errorCodes.DataIntegrityViolationCodes, errorCode) >= 0) + { + LogTranslation(task, sql, errorCode, exception, false); + return new DataIntegrityViolationException(BuildMessage(task, sql, exception), exception); + } + else if (Array.IndexOf(errorCodes.CannotAcquireLockCodes, errorCode) >= 0) + { + LogTranslation(task, sql, errorCode, exception, false); + return new CannotAcquireLockException(BuildMessage(task, sql, exception), exception); + } + else if (Array.IndexOf(errorCodes.DeadlockLoserCodes, errorCode) >= 0) + { + LogTranslation(task, sql, errorCode, exception, false); + return new DeadlockLoserDataAccessException(BuildMessage(task, sql, exception), exception); + } + else if (Array.IndexOf(errorCodes.CannotSerializeTransactionCodes, errorCode) >= 0) + { + LogTranslation(task, sql, errorCode, exception, false); + return new CannotSerializeTransactionException(BuildMessage(task, sql, exception), exception); + } + } + } + if (log.IsDebugEnabled) + { + log.Debug("Unable to translate exception eith errorCode '" + errorCode + "', will use the fallback translator"); + } + return fallbackTranslator.Translate(task, sql, exception); + } + + /// + /// Subclasses can override this method to attempt a custom mapping from a data access Exception + /// to DataAccessException. + /// + /// Readable text describing the task being attempted. + /// SQL query or update that caused the problem. May be null. + /// The error code extracted from the generic data access exception for + /// a particular provider. + /// The exception thrown from a data access operation. + /// + /// null if no custom translation was possible, otherwise a DataAccessException + /// resulting from custom translation. This exception should include the exception parameter + /// as a nested root cause. This implementation always returns null, meaning that + /// the translator always falls back to the default error codes. + /// + protected virtual DataAccessException TranslateException(string task, string sql, string errorCode, Exception exception) + { + return null; + } + + + /// + /// Builds the message. + /// + /// The task. + /// The SQL. + /// The exception. + /// + protected virtual string BuildMessage(string task, string sql, Exception exception) + { + return task + "; SQL [" + sql + "]; " + exception.Message; + } + + /// + /// Determines whether the specified exception is valid data access exception. + /// + /// The exception. + /// + /// true if is valid data access exception; otherwise, false. + /// + public bool IsValidDataAccessException(Exception e) + { + return dbProvider.IsDataAccessException(e); + } + + /// + /// Logs the translation. + /// + /// The task. + /// The SQL. + /// The error code. + /// The exception. + /// if set to true [b]. + private void LogTranslation(string task, string sql, string errorCode, Exception exception, bool b) + { + if (log.IsDebugEnabled) + { + String intro = "Translating"; + log.Debug(intro + " ADO exception with error code '" + errorCode + + "', message [" + exception.Message + + "]; SQL was [" + sql + "] for task [" + task + "]"); + } + } + + private string ExtractErrorCode(Exception exception) + { + // Use provider specific reflection information to extract error code. + return dbProvider.ExtractError(exception); + } + } +} diff --git a/src/Spring/Spring.Data/Data/Support/FallbackExceptionTranslator.cs b/src/Spring/Spring.Data/Data/Support/FallbackExceptionTranslator.cs new file mode 100644 index 00000000..73e3e9f4 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/FallbackExceptionTranslator.cs @@ -0,0 +1,71 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Dao; + +#endregion + +namespace Spring.Data.Support +{ + /// + /// Translates all exceptions to an UncategorizedAdoException. + /// + /// + /// This exception translator used when an exception is thrown using the + /// "primary" implementation of IAdoExceptionTranslator, i.e. AdoExceptionTranslator. + /// + /// Mark Pollack (.NET) + /// $Id: FallbackExceptionTranslator.cs,v 1.6 2007/01/01 22:22:50 markpollack Exp $ + public class FallbackExceptionTranslator : IAdoExceptionTranslator + { + + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public FallbackExceptionTranslator() + { + } + + #endregion + + + /// + /// Translate the given into a generic data access exception. + /// + /// A readable string describing the task being attempted. + /// The SQL query or update that caused the problem. May be null. + /// + /// The encountered by the ADO.NET implementation. + /// + /// + /// A appropriate for the supplied + /// . + /// + public DataAccessException Translate(string task, string sql, Exception exception) + { + throw new UncategorizedAdoException(task, sql, "", exception); + } + } +} diff --git a/src/Spring/Spring.Data/Data/Support/IAdoExceptionTranslator.cs b/src/Spring/Spring.Data/Data/Support/IAdoExceptionTranslator.cs new file mode 100644 index 00000000..e73270d4 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/IAdoExceptionTranslator.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Dao; + +namespace Spring.Data.Support +{ + /// + /// Interface to be implemented by classes that can translate between properietary SQL exceptions + /// and Spring.NET's data access strategy-agnostic . + /// + /// + ///

    + /// Implementations can be generic (for example, ADO.NET Exceptions) or proprietary (for example, + /// using SQL Server or Oracle error codes) for greater precision. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// Mark Pollack + /// $Id: IAdoExceptionTranslator.cs,v 1.2 2006/11/29 07:18:46 markpollack Exp $ + public interface IAdoExceptionTranslator + { + /// + /// Translate the given into a generic data access exception. + /// + /// A readable string describing the task being attempted. + /// The SQL query or update that caused the problem. May be null. + /// + /// The encountered by the ADO.NET implementation. + /// + /// + /// A appropriate for the supplied + /// . + /// + DataAccessException Translate( string task, string sql, Exception exception ); + } +} diff --git a/src/Spring/Spring.Data/Data/Support/IServiceDomainAdapter.cs b/src/Spring/Spring.Data/Data/Support/IServiceDomainAdapter.cs new file mode 100644 index 00000000..68ed2566 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/IServiceDomainAdapter.cs @@ -0,0 +1,77 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if (!NET_1_0) + +using System.EnterpriseServices; + +namespace Spring.Data.Support +{ + /// + /// Provides the necessary transactional state and operations for ServiceDomainTransactionManager to + /// work with ServiceDomain and ContextUtil. + /// + /// Introduced for purposes of unit testing. + /// Mark Pollack (.NET) + /// $Id $ + public interface IServiceDomainAdapter + { + /// + /// Creates the context specified by the ServiceConfig object and pushes it onto the context stack to + /// become the current context. + /// + /// A ServiceConfig that contains the configuration information for the services to be used within the enclosed code. + void Enter(SimpleServiceConfig config); + + /// + /// Leaves this ServiceDomain. The current context is then popped from the context stack, and the + /// context that was running when Enter was called becomes the current context. + /// + /// One of the TransactionStatus values + TransactionStatus Leave(); + + /// + /// Sets the consistent bit and the done bit to true in the COM+ context + /// + void SetComplete(); + + /// + /// Sets the consistent bit to false and the done bit to true in the COM+ context. + /// + void SetAbort(); + + /// + /// Gets or sets the consistent bit in the COM+ context. + /// + /// My transaction vote. + TransactionVote MyTransactionVote { get; set;} + + /// + /// Gets a value indicating whether the current context is transactional. + /// + /// + /// true if this instance is existing transaction; otherwise, false. + /// + bool IsInTransaction { get; } + + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Support/ITransactionScopeAdapter.cs b/src/Spring/Spring.Data/Data/Support/ITransactionScopeAdapter.cs new file mode 100644 index 00000000..71e23650 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/ITransactionScopeAdapter.cs @@ -0,0 +1,74 @@ +#if NET_2_0 + +#region License + +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Transactions; + +namespace Spring.Data.Support +{ + /// + /// Provides the necessary transactional state and operations for TxScopeTransactionManager to + /// work with TransactionScope and Transaction.Current. + /// + /// Introduced for purposes of unit testing. + /// Mark Pollack (.NET) + /// $Id: ITransactionScopeAdapter.cs,v 1.1 2007/11/30 18:38:47 markpollack Exp $ + public interface ITransactionScopeAdapter + { + /// + /// Creates the transaction scope. + /// + /// The tx scope option. + /// The tx options. + /// The interop option. + void CreateTransactionScope(TransactionScopeOption txScopeOption, TransactionOptions txOptions, EnterpriseServicesInteropOption interopOption); + + /// + /// Call Complete() on the TransactionScope object created by this instance. + /// + void Complete(); + + + /// + /// Call Disponse() on the TransactionScope object created by this instance. + /// + void Dispose(); + + + /// + /// Gets a value indicating whether there is a new transaction or an existing transaction. + /// + /// + /// true if this instance is existing transaction; otherwise, false. + /// + bool IsExistingTransaction { get; } + + + /// + /// Gets a value indicating whether rollback only has been called (i.e. Rollback() on the + /// Transaction object) and therefore voting that the transaction will be aborted. + /// + /// true if rollback only; otherwise, false. + bool RollbackOnly { get; } + } + +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Support/NamedResultSetProcessor.cs b/src/Spring/Spring.Data/Data/Support/NamedResultSetProcessor.cs new file mode 100644 index 00000000..52a74603 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/NamedResultSetProcessor.cs @@ -0,0 +1,106 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + + +#endregion + +namespace Spring.Data.Support +{ + /// + /// Provides a name to a ResultSetProcessor for use with AdoOperation subclasses + /// such as StoredProcedure. + /// + /// Mark Pollack (.NET) + /// $Id: NamedResultSetProcessor.cs,v 1.3 2006/11/29 07:18:46 markpollack Exp $ + public class NamedResultSetProcessor + { + #region Fields + + private object resultSetProcessor; + private string name; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class with a + /// IRowCallback instance + /// + /// The name of the associated row callback for use with output + /// result set mappers. + /// A row callback instance. + public NamedResultSetProcessor(string name, IRowCallback rowcallback) + { + this.name = name; + resultSetProcessor = rowcallback; + } + + /// + /// Initializes a new instance of the class with a + /// IRowMapper instance + /// + /// The name of the associated row mapper. + /// A IRowMapper instance. + public NamedResultSetProcessor(string name, IRowMapper rowMapper) + { + this.name = name; + resultSetProcessor = rowMapper; + } + + /// + /// Initializes a new instance of the class with a + /// IResultSetExtractor instance + /// + /// The name of the associated result set extractor. + /// A IResultSetExtractor instance. + public NamedResultSetProcessor(string name, IResultSetExtractor resultSetExtractor) + { + this.name = name; + resultSetProcessor = resultSetExtractor; + } + + + #endregion + + #region Properties + public string Name + { + get + { + return name; + } + } + + public object ResultSetProcessor + { + get + { + return resultSetProcessor; + } + } + + #endregion + + + } +} diff --git a/src/Spring/Spring.Data/Data/Support/NullMappingDataReader.cs b/src/Spring/Spring.Data/Data/Support/NullMappingDataReader.cs new file mode 100644 index 00000000..00a4ecbe --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/NullMappingDataReader.cs @@ -0,0 +1,348 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; + +#endregion + +namespace Spring.Data.Support +{ + /// + /// A data reader implementation that mapps DBNull values to sensible defaults. + /// + /// IDataRecord methods are virtual. + /// Mark Pollack (.NET) + /// $Id: NullMappingDataReader.cs,v 1.2 2006/10/08 04:56:06 markpollack Exp $ + public class NullMappingDataReader : IDataReaderWrapper + { + #region Fields + + protected IDataReader dataReader; + private bool alreadyDisposed = false; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public NullMappingDataReader() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The data reader to delegate operations to. + public NullMappingDataReader(IDataReader dataReader) + { + this.dataReader = dataReader; + } + + #endregion + + ~NullMappingDataReader() + { + Dispose(false); + } + #region Methods + protected virtual void Dispose(bool isDisposing) + { + // Don't dispose more than once + if (alreadyDisposed) + { + return; + } + if (isDisposing) + { + //free managed resourced + dataReader.Dispose(); + } + alreadyDisposed = true; + } + #endregion + + #region IDataReaderWrapper Members + + public IDataReader WrappedReader + { + get + { + return dataReader; + } + set + { + dataReader = value; + } + } + + #endregion + + #region IDataReader Members + + public int RecordsAffected + { + get + { + return this.dataReader.RecordsAffected; + } + } + + public bool IsClosed + { + get + { + return this.dataReader.IsClosed; + } + } + + public bool NextResult() + { + return dataReader.NextResult(); + } + + public void Close() + { + dataReader.Close(); + } + + public bool Read() + { + return dataReader.Read(); + } + + public int Depth + { + get + { + return dataReader.Depth; + } + } + + public DataTable GetSchemaTable() + { + return dataReader.GetSchemaTable(); + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(true); + } + + #endregion + + #region IDataRecord Members + + /// + /// Gets the 32-bit signed integer value of the specified field. + /// + /// The index of the field to find. + /// + /// The 32-bit signed integer value of the specified field or 0 if null + /// + /// The index passed was outside the range of 0 through . + public virtual int GetInt32(int i) + { + if (dataReader.IsDBNull(i)) + { + return 0; + } + else + { + return dataReader.GetInt32(i); + } + } + + + /// + /// Gets the with the specified name. + /// Returns null if value equals DBNull.Value + /// + /// + public virtual object this[string name] + { + get + { + object objectValue = dataReader[name]; + return DBNull.Value.Equals(objectValue) ? null : objectValue; + } + } + + + /// + /// Gets the with the specified i. + /// Returns null if value equals DBNull.Value + /// + /// Return the object or null if the value equals DBNull.Value + public virtual object this[int i] + { + get + { + + return dataReader.IsDBNull(i) ? null : dataReader[i]; + } + } + + /// + /// Return the value of the specified field, null if value equals DBNull.Value + /// + /// The index of the field to find. + /// + /// The which will contain the field value upon return. + /// Returns null if value equals DBNull.Value + /// + /// + /// The index passed was outside the range of 0 through . + /// + public virtual object GetValue(int i) + { + return dataReader.IsDBNull(i) ? null : dataReader.GetValue(i); + } + + /// + /// Return whether the specified field is set to null. + /// + /// The index of the field to find. + /// + /// if the specified field is set to null, otherwise + /// . + /// + /// The index passed was outside the range of 0 through . + public virtual bool IsDBNull(int i) + { + return dataReader.IsDBNull(i); + } + + public virtual long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) + { + return dataReader.IsDBNull(i) ? 0 : dataReader.GetBytes(i, fieldOffset, buffer, bufferoffset, length); + } + + public virtual byte GetByte(int i) + { + return dataReader.IsDBNull(i) ? (byte)0 : dataReader.GetByte(i); + } + + public virtual Type GetFieldType(int i) + { + return dataReader.GetFieldType(i); + } + + public virtual decimal GetDecimal(int i) + { + return dataReader.IsDBNull(i) ? 0 : dataReader.GetDecimal(i); + } + + public virtual int GetValues(object[] values) + { + return dataReader.GetValues(values); + } + + public virtual string GetName(int i) + { + return dataReader.GetName(i); + } + + public virtual int FieldCount + { + get + { + return dataReader.FieldCount; + } + } + + public virtual long GetInt64(int i) + { + return dataReader.IsDBNull(i) ? 0 : dataReader.GetInt64(i); + } + + public virtual double GetDouble(int i) + { + return dataReader.IsDBNull(i) ? 0 : dataReader.GetDouble(i); + } + + public virtual bool GetBoolean(int i) + { + return dataReader.IsDBNull(i) ? false : dataReader.GetBoolean(i); + } + + public virtual Guid GetGuid(int i) + { + return dataReader.IsDBNull(i) ? Guid.Empty : dataReader.GetGuid(i); + } + + public virtual DateTime GetDateTime(int i) + { + return dataReader.IsDBNull(i) ? DateTime.MinValue : dataReader.GetDateTime(i); + } + + public virtual int GetOrdinal(string name) + { + return dataReader.GetOrdinal(name); + } + + public virtual string GetDataTypeName(int i) + { + return dataReader.GetDataTypeName(i); + } + + public virtual float GetFloat(int i) + { + return dataReader.IsDBNull(i) ? 0 : dataReader.GetFloat(i); + } + + public virtual IDataReader GetData(int i) + { + return dataReader.GetData(i); + } + + public virtual long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) + { + return dataReader.IsDBNull(i) ? 0 : dataReader.GetChars(i, fieldoffset, buffer, bufferoffset, length); + } + + public virtual string GetString(int i) + { + return dataReader.IsDBNull(i) ? string.Empty : dataReader.GetString(i); + } + + public virtual char GetChar(int i) + { + return dataReader.IsDBNull(i) ? char.MinValue : dataReader.GetChar(i); + } + + public virtual short GetInt16(int i) + { + return dataReader.IsDBNull(i) ? (short)0 : dataReader.GetInt16(i); + } + + #endregion + + } +} diff --git a/src/Spring/Spring.Data/Data/Support/ParameterUtils.cs b/src/Spring/Spring.Data/Data/Support/ParameterUtils.cs new file mode 100644 index 00000000..754bf3ea --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/ParameterUtils.cs @@ -0,0 +1,174 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using Common.Logging; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.Support +{ + /// + /// Miscellaneous utility methods for manipulating parameter objects. + /// + /// Mark Pollack (.NET) + /// $Id: ParameterUtils.cs,v 1.12 2007/10/11 04:41:00 markpollack Exp $ + public class ParameterUtils + { + #region Fields + + #endregion + + #region Constants + + /// + /// The shared log instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (ParameterUtils)); + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public ParameterUtils() + { + + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + + /// + /// Copies the parameters from IDbParameters to the parameter collection in IDbCommand + /// + /// The command. + /// The spring param collection. + public static void CopyParameters(IDbCommand command, IDbParameters springParamCollection) + { + if (springParamCollection != null) + { + IDataParameterCollection collection = springParamCollection.DataParameterCollection; + + foreach (IDbDataParameter parameter in collection) + { + IDbDataParameter pClone = (IDbDataParameter)((ICloneable)parameter).Clone(); + command.Parameters.Add(pClone); + } + } + } + + public static IDataParameter[] CloneParameters(IDbCommand command) + { + IDataParameter[] returnParameters = new IDataParameter[command.Parameters.Count]; + for (int i = 0; i < command.Parameters.Count; i++) + { + IDbDataParameter pClone = (IDbDataParameter)((ICloneable)command.Parameters[i]).Clone(); + returnParameters[i] = pClone; + } + return returnParameters; + } + + + /// + /// Copies the parameters in IDbCommand to IDbParameters + /// + /// The spring param collection. + /// The command. + public static void CopyParameters(IDbParameters springParamCollection, + IDbCommand command) + { + if (springParamCollection != null) + { + IDataParameterCollection cmdParameterCollection = command.Parameters; + //TODO investigate copying only the values over, not a full clone. + int count = 0; + foreach (IDbDataParameter dbDataParameter in cmdParameterCollection) + { + springParamCollection[count] = (IDbDataParameter)((ICloneable)dbDataParameter).Clone();; + count++; + } + } + } + + #endregion + + public static void ExtractOutputParameters(IDictionary returnedParameters, IDbCommand command) + { + if (returnedParameters == null) + { + return; + } + IDataParameterCollection paramCollection = command.Parameters; + int count = 0; + foreach (IDbDataParameter dbDataParameter in paramCollection) + { + if (dbDataParameter.Direction == ParameterDirection.Output + || dbDataParameter.Direction == ParameterDirection.InputOutput) + { + if (dbDataParameter.ParameterName == null) + { + returnedParameters.Add("P" + count, dbDataParameter.Value); + } + else + { + returnedParameters.Add(dbDataParameter.ParameterName, dbDataParameter.Value); + } + } + if (dbDataParameter.Direction == ParameterDirection.ReturnValue) + { + returnedParameters.Add("RETURN_VALUE", dbDataParameter.Value); + } + count++; + } + /* + for (int i = 0; i < declaredParameters.Count; i++) + { + IDataParameter declaredParameter = declaredParameters[i]; + if (declaredParameter.Direction == ParameterDirection.Output + || declaredParameter.Direction == ParameterDirection.InputOutput) + { + IDataParameter outParameter = (IDataParameter)command.Parameters[i]; + if (declaredParameter.ParameterName == null) + { + returnedParameters.Add("P" + i, outParameter.Value); + } + else + { + returnedParameters.Add(outParameter.ParameterName, outParameter.Value); + } + } + }*/ + } + } +} diff --git a/src/Spring/Spring.Data/Data/Support/RowCallbackResultSetExtractor.cs b/src/Spring/Spring.Data/Data/Support/RowCallbackResultSetExtractor.cs new file mode 100644 index 00000000..38bf549c --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/RowCallbackResultSetExtractor.cs @@ -0,0 +1,88 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; +using Spring.Util; + +namespace Spring.Data.Support +{ + /// + /// Adapter to enable use of a IRowCallback inside a ResultSetExtractor. + /// + /// We don't use it for navigating since this could lead to unpredictable consequences. + /// Mark Pollack + /// $Id: RowCallbackResultSetExtractor.cs,v 1.1 2007/07/25 08:27:08 markpollack Exp $ + public class RowCallbackResultSetExtractor : IResultSetExtractor + { + private IRowCallback rowCallback; + + private RowCallbackDelegate rowCallbackDelegate; + + /// + /// Initializes a new instance of the class. + /// + /// The row callback. + public RowCallbackResultSetExtractor(IRowCallback rowCallback) + { + AssertUtils.ArgumentNotNull(rowCallback, "rowCallback"); + this.rowCallback = rowCallback; + } + + /// + /// Initializes a new instance of the class. + /// + /// The row callback delegate. + public RowCallbackResultSetExtractor(RowCallbackDelegate rowCallbackDelegate) + { + AssertUtils.ArgumentNotNull(rowCallbackDelegate, "rowCallbackDelegate"); + this.rowCallbackDelegate = rowCallbackDelegate; + } + + /// + /// All rows of the data reader are passed to the IRowCallback + /// associated with this class. + /// + /// The IDataReader to extract data from. + /// Implementations should not close this: it will be closed + /// by the AdoTemplate. + /// + /// Null is returned since IRowCallback manages its own state. + /// + public object ExtractData(IDataReader reader) + { + if (rowCallback != null) + { + while (reader.Read()) + { + rowCallback.ProcessRow(reader); + } + } + else + { + while (reader.Read()) + { + rowCallbackDelegate(reader); + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Support/SimpleServiceConfig.cs b/src/Spring/Spring.Data/Data/Support/SimpleServiceConfig.cs new file mode 100644 index 00000000..3cc11052 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/SimpleServiceConfig.cs @@ -0,0 +1,127 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if (!NET_1_0) + +using System.EnterpriseServices; + +namespace Spring.Data.Support +{ + /// + /// A class that contains the properties of ServiceConfig used by ServiceDomainPlatformTransactionManager. + /// + /// This is done to enhance testability of the transaction manager since ServiceConfig does not override Equals or Hashcode + /// Mark Pollack (.NET) + /// $Id $ + public class SimpleServiceConfig + { + private string transactionDescription; + private bool trackingEnabled = true; + private string trackingAppName; + private string trackingComponentName; + private TransactionOption transactionOption; + private TransactionIsolationLevel isolationLevel; + private int transactionTimeout; + + + public string TransactionDescription + { + get { return transactionDescription; } + set { transactionDescription = value; } + } + + public bool TrackingEnabled + { + get { return trackingEnabled; } + set { trackingEnabled = value; } + } + + public string TrackingAppName + { + get { return trackingAppName; } + set { trackingAppName = value; } + } + + public string TrackingComponentName + { + get { return trackingComponentName; } + set { trackingComponentName = value; } + } + + public TransactionOption TransactionOption + { + get { return transactionOption; } + set { transactionOption = value; } + } + + public TransactionIsolationLevel IsolationLevel + { + get { return isolationLevel; } + set { isolationLevel = value; } + } + + public int TransactionTimeout + { + get { return transactionTimeout; } + set { transactionTimeout = value; } + } + + public ServiceConfig CreateServiceConfig() + { + ServiceConfig serviceConfig = new ServiceConfig(); + serviceConfig.TransactionDescription = this.transactionDescription; + serviceConfig.TrackingEnabled = this.trackingEnabled; + serviceConfig.TrackingAppName = this.trackingAppName; + serviceConfig.TrackingComponentName = this.trackingComponentName; + serviceConfig.Transaction = this.transactionOption; + serviceConfig.IsolationLevel = this.isolationLevel; + serviceConfig.TransactionTimeout = this.transactionTimeout; + return serviceConfig; + } + + public override bool Equals(object obj) + { + if (this == obj) return true; + SimpleServiceConfig simpleServiceConfig = obj as SimpleServiceConfig; + if (simpleServiceConfig == null) return false; + if (!Equals(transactionDescription, simpleServiceConfig.transactionDescription)) return false; + if (!Equals(trackingEnabled, simpleServiceConfig.trackingEnabled)) return false; + if (!Equals(trackingAppName, simpleServiceConfig.trackingAppName)) return false; + if (!Equals(trackingComponentName, simpleServiceConfig.trackingComponentName)) return false; + if (!Equals(transactionOption, simpleServiceConfig.transactionOption)) return false; + if (!Equals(isolationLevel, simpleServiceConfig.isolationLevel)) return false; + if (transactionTimeout != simpleServiceConfig.transactionTimeout) return false; + return true; + } + + public override int GetHashCode() + { + int result = transactionDescription != null ? transactionDescription.GetHashCode() : 0; + result = 29*result + trackingEnabled.GetHashCode(); + result = 29*result + (trackingAppName != null ? trackingAppName.GetHashCode() : 0); + result = 29*result + (trackingComponentName != null ? trackingComponentName.GetHashCode() : 0); + result = 29*result + transactionOption.GetHashCode(); + result = 29*result + isolationLevel.GetHashCode(); + result = 29*result + transactionTimeout; + return result; + } + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/Support/Sql/sqlconnectionstring.xsd b/src/Spring/Spring.Data/Data/Support/Sql/sqlconnectionstring.xsd new file mode 100644 index 00000000..b36cd970 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/Sql/sqlconnectionstring.xsd @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Data/Data/Support/TypedDataSetUtils.cs b/src/Spring/Spring.Data/Data/Support/TypedDataSetUtils.cs new file mode 100644 index 00000000..72224ed4 --- /dev/null +++ b/src/Spring/Spring.Data/Data/Support/TypedDataSetUtils.cs @@ -0,0 +1,170 @@ +#if NET_2_0 + +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; +using System.Reflection; +using Common.Logging; +using Spring.Data.Common; + +namespace Spring.Data.Support +{ + /// + /// Using reflection on VS.NET 2005 a generated typed dataset, apply the + /// connection/transaction pair associated with the current Spring based + /// transaction scope. + /// + /// This avoids the limitations of using System.Transaction + /// based transaction scope for multiple operation on typed datasets withone one + /// transaction. Reflection was used rather than partial classes to provide + /// a general solution that can be written and applied once. + /// Usage within a DAO method, FindAll() is shown below. Note: Convenience methods + /// to simplify calling syntax maybe provided in the future, it is a trade off on type + /// safety calling the typed adapter defined outside the anonymous method as + /// compared to casting inside a "DoInDbAdapter(object dbAdapter) method. + /// + /// public PrintGroupMappingDataSet FindAll() + /// { + /// + /// PrintGroupMappingTableAdapter adapter = new PrintGroupMappingTableAdapter(); + /// PrintGroupMappingDataSet printGroupMappingDataSet = new PrintGroupMappingDataSet(); + /// + /// + /// printGroupMappingDataSet = AdoTemplate.Execute(delegate(IDbCommand command) + /// { + /// TypedDataSetUtils.ApplyConnectionAndTx(adapter, command); + /// adapter.Fill(printGroupMappingDataSet.PrintGroupMapping); + /// return printGroupMappingDataSet; + /// }) + /// as PrintGroupMappingDataSet; + /// + /// return printGroupMappingDataSet; + /// } + /// + /// See http://www.code-magazine.com/articleprint.aspx?quickid=0605031 for a + /// more complete discussion. + /// + /// + /// $Id: + public abstract class TypedDataSetUtils + { + private static readonly ILog LOG = LogManager.GetLogger(typeof (TypedDataSetUtils)); + + + /// + /// Applies the connection and tx to the provided typed dataset. The connection and transaction + /// used are taken from the Spring's current transactional context. See ConnectionUtils.GetConnectionTxPair + /// for more information + /// + /// The typed data set adapter. + /// The db provider. + public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider dbProvider) + { + ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(dbProvider); + ApplyConnectionAndTxToDataAdapter(typedDataSetAdapter, connectionTxPairToUse.Connection, connectionTxPairToUse.Transaction); + } + + /// + /// Applies the connection and tx to the provided typed dataset. + /// + /// Generated dataset to not inherit from a common base + /// class so that we must pass in the generic object type. + /// The typed data set adapter. + /// The IDbCommand managed by Spring and set with + /// the connection/transaction of the current Spring transaction scope. + public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbCommand sourceCommand) + { + if (sourceCommand != null) + { + ApplyConnectionAndTxToDataAdapter(typedDataSetAdapter, sourceCommand.Connection, sourceCommand.Transaction); + } + } + + private static IDbCommand[] GetCommandCollection(object typedDataSetAdapter) + { + PropertyInfo pi = + typedDataSetAdapter.GetType().GetProperty("CommandCollection", + BindingFlags.NonPublic | BindingFlags.Instance); + MethodInfo mi = pi.GetGetMethod(true); + return mi.Invoke(typedDataSetAdapter, null) as IDbCommand[]; + } + + private static IDbDataAdapter GetDataAdapter(object typedDataSetAdapter) + { + PropertyInfo api = + typedDataSetAdapter.GetType().GetProperty("Adapter", + BindingFlags.NonPublic | BindingFlags.Instance); + MethodInfo ami = api.GetGetMethod(true); + return ami.Invoke(typedDataSetAdapter, null) as IDbDataAdapter; + } + + private static void ApplyConnectionAndTxToDataAdapter(object typedDataSetAdapter, IDbConnection connection, IDbTransaction transaction) + { + IDbDataAdapter dataAdapter = GetDataAdapter(typedDataSetAdapter); + if (dataAdapter != null) + { + ApplyConnectionAndTxToCommand(dataAdapter.SelectCommand, connection, transaction); + + + ApplyConnectionAndTxToCommand(dataAdapter.InsertCommand, connection, transaction); + + + ApplyConnectionAndTxToCommand(dataAdapter.UpdateCommand, connection, transaction); + + + ApplyConnectionAndTxToCommand(dataAdapter.DeleteCommand, connection, transaction); + } + else + { + LOG.Warn("Could not extract IDbDataAdapter from TypedDataset."); + } + + + IDbCommand[] commandCollection = GetCommandCollection(typedDataSetAdapter); + if (commandCollection != null) + { + //don't know which one to set, so set them all. + foreach (IDbCommand dbCommand in commandCollection) + { + dbCommand.Connection = connection; + dbCommand.Transaction = transaction; + } + } + else + { + LOG.Warn("Could not extract IDbCommand collection from TypedDataset."); + } + } + + + private static void ApplyConnectionAndTxToCommand(IDbCommand targetDbCommand, IDbConnection connection, IDbTransaction transaction) + { + if (targetDbCommand != null) + { + targetDbCommand.Connection = connection; + targetDbCommand.Transaction = transaction; + } + } + + + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Data/Data/UncategorizedAdoException.cs b/src/Spring/Spring.Data/Data/UncategorizedAdoException.cs new file mode 100644 index 00000000..62a3711f --- /dev/null +++ b/src/Spring/Spring.Data/Data/UncategorizedAdoException.cs @@ -0,0 +1,136 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using Spring.Dao; + +#endregion + +namespace Spring.Data +{ + /// + /// Exception thrown when we can't classify a SQLException into + /// one of our generic data access exceptions. + /// + /// Mark Pollack (.NET) + /// $Id: UncategorizedAdoException.cs,v 1.4 2006/11/29 07:18:46 markpollack Exp $ + [Serializable] + public class UncategorizedAdoException : UncategorizedDataAccessException, ISerializable + { + #region Fields + + /// + /// SQL that led to the problem + /// + private readonly string sql; + + /// + /// The error code, if available, that was unable to be categorized. + /// + private readonly string errorCode; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public UncategorizedAdoException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception. + public UncategorizedAdoException(string message): base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A message about the exception. + /// The inner exception. + public UncategorizedAdoException(string message, Exception inner): base(message, inner) + { + } + + + /// + /// Initializes a new instance of the class. + /// + /// name of the current task. + /// the underlying error code that could not be translated + /// The offending SQL statment + /// The root cause. + public UncategorizedAdoException(string task, string sql, string errorCode, Exception ex) : base(task + "; uncategorized DataException for SQL [" + sql + "]; " + "ErrorCode [" + errorCode + "]; " + ex.Message, ex) + { + this.sql = sql; + this.errorCode = errorCode; + } + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected UncategorizedAdoException( SerializationInfo info, StreamingContext context ) : base( info, context ) {} + + + + #endregion + + #region Properties + + #endregion + + #region Methods + + #endregion + + #region ISerializable Members + + /// + /// When overridden in a derived class, sets the + /// with information about the exception. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is a null reference ( in Visual Basic). + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue( "sql", sql ); + info.AddValue( "errorCode", errorCode); + base.GetObjectData( info, context ); + } + + #endregion + } +} diff --git a/src/Spring/Spring.Data/Spring.Data.2003.csproj b/src/Spring/Spring.Data/Spring.Data.2003.csproj new file mode 100644 index 00000000..368c5db1 --- /dev/null +++ b/src/Spring/Spring.Data/Spring.Data.2003.csproj @@ -0,0 +1,1016 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Data/Spring.Data.2005.csproj b/src/Spring/Spring.Data/Spring.Data.2005.csproj new file mode 100644 index 00000000..06d2bfdc --- /dev/null +++ b/src/Spring/Spring.Data/Spring.Data.2005.csproj @@ -0,0 +1,309 @@ + + + Local + 8.0.50727 + 2.0 + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Debug + AnyCPU + + + + + Spring.Data + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + ..\..\..\build\VS.Net.2005\Spring.Data\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + Spring.Data.xml + true + 4096 + false + 1587, 1591, 219, 162, 618 + false + false + false + false + 4 + full + prompt + + + ..\..\..\build\VS.Net.2005\Spring.Data\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + System + + + + System.Data + + + System.Drawing + + + + + System.Web + + + System.XML + + + + + CommonAssemblyInfo.cs + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + Designer + + + + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Data/Spring.Data.build b/src/Spring/Spring.Data/Spring.Data.build new file mode 100644 index 00000000..65612c1c --- /dev/null +++ b/src/Spring/Spring.Data/Spring.Data.build @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Data/Spring.Data.xml b/src/Spring/Spring.Data/Spring.Data.xml new file mode 100644 index 00000000..3640e377 --- /dev/null +++ b/src/Spring/Spring.Data/Spring.Data.xml @@ -0,0 +1,10944 @@ + + + + Spring.Data + + + + + This is the central interface in Spring.NET's transaction support. + + +

    + Applications can use this directly, but it is not primarily meant as an API. + Typically, applications will work with either + or the AOP transaction + interceptor. +

    +

    + For implementers, + is a good starting point. +

    +
    + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + $Id: IPlatformTransactionManager.cs,v 1.10 2007/08/01 18:53:05 markpollack Exp $ +
    + + + Return a currently active transaction or create a new one. + + +

    + Note that parameters like isolation level or timeout will only be applied + to new transactions, and thus be ignored when participating in active ones. + Furthermore, they aren't supported by every transaction manager: + a proper implementation should throw an exception when custom values + that it doesn't support are specified. +

    +
    + + instance (can be null for + defaults), describing propagation behavior, isolation level, timeout etc. + + + In case of lookup, creation, or system errors. + + + A representing the new or current transaction. + +
    + + + Commit the given transaction, with regard to its status. + + +

    + If the transaction has been marked rollback-only programmatically, + perform a rollback. +

    +

    + If the transaction wasn't a new one, omit the commit to take part + in the surrounding transaction properly. +

    +
    + + The instance returned by the + () method. + + + In case of commit or system errors + +
    + + + Roll back the given transaction, with regard to its status. + + +

    + If the transaction wasn't a new one, just set it rollback-only + to take part in the surrounding transaction properly. +

    +
    + + The instance returned by the + () method. + + + In case of system errors. + +
    + + + An AOP Alliance providing + declarative transaction management using the common Spring.NET transaction infrastructure. + + +

    + That class contains the necessary calls into Spring.NET's underlying + transaction API: subclasses such as this are responsible for calling + superclass methods such as + + in the correct order, in the event of normal invocation return or an exception. +

    +

    + s are thread-safe. +

    +
    + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: TransactionInterceptor.cs,v 1.12 2007/09/07 04:46:53 markpollack Exp $ +
    + + + Superclass for transaction aspects, such as the AOP Alliance-compatible + . + + +

    + This enables the underlying Spring transaction infrastructure to be used + to easily implement an aspect for any aspect system. +

    +

    + Subclasses are responsible for calling methods in this class in the correct order. +

    +

    + Uses the Strategy design pattern. A + implementation will perform the actual transaction management +

    +

    + A transaction aspect is serializable if its + and + are serializable. +

    +
    + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: TransactionAspectSupport.cs,v 1.15 2008/05/27 19:06:57 markpollack Exp $ +
    + + + The name in thread local storage where the TransactionInfo object is located + + + + + The currently referenced by + this aspect + + + + + The currently + referenced by this aspect. + + + + + Creates a new instance of the + class. + + + + + Checks that the required properties are set. + + + + + Create a transaction if necessary + + Method about to execute + Type that the method is on + + A object, + whether or not a transaction was created. +

    + The + + property on the + + class can be used to tell if there was a transaction created. +

    +
    +
    + + + Creates the transaction if necessary. + + The source transaction attribute. + The joinpoint identification. + Transaction Info for declarative transaction management. + + + + Identifies the method by providing the qualfied method name. + + The method info. + qualified mehtod name. + + + + Execute after the successful completion of call, but not after an exception was handled. + + +

    + Do nothing if we didn't create a transaction. +

    +
    + + The + + about the current transaction. + +
    + + + Handle a exception, closing out the transaction. + + +

    + We may commit or roll back, depending on our configuration. +

    +
    + + The + + about the current transaction. + + The encountered. +
    + + + Resets the + + for this thread. + + + + Call this in all cases: exceptions or normal return. + + + + The + + about the current transaction. May be null. + + + + + Gets and sets the for + this aspect. + + + + + Gets and sets the + for + this aspect. + + + + + Returns the + of the current method invocation. + + + Mainly intended for code that wants to set the current transaction + rollback-only but not throw an application exception. + + + If the transaction info cannot be found, because the method was invoked + outside of an AOP invocation context. + + + + + Subclasses can use this to return the current + . + + + Only subclasses that cannot handle all operations in one method + need to use this mechanism to get at the current + . + An around advice such as an AOP Alliance + can hold a reference to the + + throughout the aspect method. + A + will be returned even if no transaction was created. The + + property can be used to query this. + + + If no transaction has been created by an aspect. + + + + + Set properties with method names as keys and transaction attribute + descriptors (parsed via ) as values: + e.g. key = "MyMethod", value = "PROPAGATION_REQUIRED,readOnly". + + + + Method names are always applied to the target class, no matter if defined in an + interface or the class itself. + +

    + Internally, a + + will be created from the given properties. +

    +
    +
    + + + Opaque object used to hold transaction information. + + +

    + Subclasses must pass it back to method on this class, but not see its internals. +

    +
    +
    + + + Creates a new instance of the + + class for the supplied . + + The transaction attributes to associate with any transaction. + The info for diagnostic display of joinpoint + + + + Binds this + + instance to the thread local storage variable for the current thread and + backs up the existing + + object for the current thread. + + + + + Restores the previous + + object to the current thread. + + + + + Does this instance currently have a transaction? + + True if this instance has a transaction. + + + + Gets and sets the for this object. + + + + + Gets the current + for this + + object. + + + + + Gets the joinpoint identification. + + The joinpoint identification. + + + + AOP Alliance invoke call that handles all transaction plumbing. + + + The method that is to execute in the context of a transaction. + + The return value from the method invocation. + + + + Tag class. Its class means it has the opposite behaviour to the + superclass. + + Rod Johnson + Griffin Caprio (.NET) + $Id: NoRollbackRuleAttribute.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Rule determining whether or not a given exception (and any subclasses) should + cause a rollback. + + +

    + Multiple such rules can be applied to determine whether a transaction should commit + or rollback after an exception has been thrown. +

    +
    + Griffin Caprio (.NET) + $Id: RollbackRuleAttribute.cs,v 1.7 2008/03/31 20:09:59 markpollack Exp $ +
    + + + Canonical instance representing default behavior for rolling back on + all s. + + + + + Creates a new instance of the + class + for the named . + + The exception name. + +

    + As always, the should be the full + assembly qualified version. +

    +
    +
    + + + Creates a new instance of the + class + for the suplied . + + +

    + The exception class must be or a subclass. +

    +

    + This is the preferred way to construct a + , + matching the exception class and subclasses. +

    +
    + + The class that will trigger a rollback. + +
    + + + Return the depth to the matching superclass execption . + + + A return value of 0 means that the matches. + + + The of exception to find. + + + Return -1 if there's no match. Otherwise, return depth. Lowest depth wins. + + + + + Return the depth to the matching superclass execption . + + + A return value of 0 means that the s + matches. + + + The exception object to find. + + + Return -1 if there's no match. Otherwise, return depth. Lowest depth wins. + + + + + Returns a representation of this instance. + + + A containing the exception covered by this instance. + + + + + Override of . + + The hashcode of the exception name covered by this instance. + + + + Override of . + + The object to compare. + True if the input object is equal to this instance. + + + + Strongly typed Equals() implementation. + + + The to compare. + + + True if the input object is equal to the supplied . + + + + + Returns the name of the exception. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + The class that will trigger a rollback. + + + + + Callback delegate to process each row of data in a result set to an object. + + The IDataReader to map + the number of the current row. + An abrirary object, typically derived from data + in the result set. + + + + This is the central class in the Spring.Data.Generic namespace. + It simplifies the use of ADO.NET and helps to avoid commons errors. + + Mark Pollack (.NET) + $Id: AdoTemplate.cs,v 1.19 2007/12/07 22:56:22 markpollack Exp $ + + + + Base class for AdoTemplate and other ADO.NET DAO helper classes defining + common properties like DbProvider. + + Mark Pollack (.NET) + Juergen Hoeller + + + + Prepare the command setting the transaction timeout. + + + + + + Invoked by an + after it has injected all of an object's dependencies. + + +

    + This method allows the object instance to perform the kind of + initialization only possible when all of it's dependencies have + been injected (set), and to throw an appropriate exception in the + event of misconfiguration. +

    +

    + Please do consult the class level documentation for the + interface for a + description of exactly when this method is invoked. In + particular, it is worth noting that the + + and + callbacks will have been invoked prior to this method being + called. +

    +
    + + In the event of misconfiguration (such as the failure to set a + required property) or if initialization fails. + +
    + + + Creates the data reader wrapper for use in AdoTemplate callback methods. + + The reader to wrap. + The data reader used in AdoTemplate callbacks + + + + Creates the a db parameters collection, adding to the collection a parameter created from + the method parameters. + + The name of the parameter + The type of the parameter. + The size of the parameter, for use in defining lengths of string values. Use + 0 if not applicable. + The parameter value. + A collection of db parameters with a single parameter in the collection based + on the method parameters + + + + Creates a new instance of + + a new instance of + + + + Derives the parameters of a stored procedure, not including the return parameter. + + Name of the procedure. + The stored procedure parameters. + + + + Derives the parameters of a stored procedure including the return parameter + + Name of the procedure. + if set to true to include return parameter. + The stored procedure parameters + + + + An instance of a DbProvider implementation. + + + + + Gets or sets a value indicating whether to lazily initialize the + IAdoExceptionTranslator for this accessor, on first encounter of a + exception from the data provider. Default is "true"; can be switched to + "false" for initialization on startup. + + true if to lazy initialize the IAdoExceptionTranslator; + otherwise, false. + + + + Gets or sets the exception translator. If no custom translator is provided, a default + is used. + + The exception translator. + + + + Gets or set the System.Type to use to create an instance of IDataReaderWrapper + for the purpose of having defaults values to use in case of DBNull values read + from IDataReader. + + The type of the data reader wrapper. + + + + Gets or sets the command timeout for IDbCommands that this AdoTemplate executes. + + Default is 0, indicating to use the database provider's default. + Any timeout specified here will be overridden by the remaining + transaction timeout when executing within a transaction that has a + timeout specified at the transaction level. + + The command timeout. + + + + Interface that defines ADO.NET related database operations using generics + + Mark Pollack (.NET) + $Id: IAdoOperations.cs,v 1.9 2007/12/04 06:48:21 markpollack Exp $ + + + + Execute the query with the specified command text returning a scalar result + + No parameters are used. As with + IDbCommand.ExecuteScalar, it returns the first column of the first row in the resultset + returned by the query. Extra columns or row are ignored. + The command type + The command text to execute. + The first column of the first row in the result set. + + + + Execute the query with the specified command text and parameter returning a scalar result + + The command type + The command text to execute. + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + The first column of the first row in the result set + + + + Execute the query with the specified command text and parameters returning a scalar result + + The command type + The command text to execute. + The parameter collection to map. + The first column of the first row in the result set + + + + Execute the query with the specified command text and parameters set via the + command setter, returning a scalar result + + The command type + The command text to execute. + The command setter. + The first column of the first row in the result set + + + + Execute the query with a command created via IDbCommandCreator and + parameters + + Output parameters can be retrieved via the returned + dictionary. + + More commonly used as a lower level support method within the framework, + for example StoredProcedure/AdoScalar. + + + The callback to create a IDbCommand. + A dictionary containing output parameters, if any + + + + Executes a non query returning the number of rows affected. + + The command type. + The command text to execute. + The number of rows affected. + + + + Executes a non query returning the number of rows affected. + + The command type. + The command text to execute. + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + The number of rows affected. + + + + Executes a non query returning the number of rows affected. + + The command type. + The command text to execute. + The parameter collection to map. + The number of rows affected. + + + + Executes a non query with parameters set via the + command setter, returning the number of rows affected. + + The command type. + The command text to execute. + The command setter. + The number of rows affected. + + + + Executes a non query with a command created via IDbCommandCreator and + parameters. + + Output parameters can be retrieved via the returned + dictionary. + + More commonly used as a lower level support method within the framework, + for example StoredProcedure/AdoScalar. + + + The callback to create a IDbCommand. + The number of rows affected. + + + + Execute a query given IDbCommand's type and text, reading a + single result set on a per-row basis with a . + + The type of command. + The text of the query. + callback that will extract results + one row at a time. + + + + + Execute a query given IDbCommand's type and text and provided parameter + information, reading a + single result set on a per-row basis with a . + + The type of command + The text of the query. + callback that will extract results + one row at a time. + + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + + + + Execute a query given IDbCommand's type and text and provided IDbParameters, + reading a single result set on a per-row basis with a . + + Type of the command. + The text of the query. + callback that will extract results + one row at a time. + The parameter collection to map. + + + + Execute a query given IDbCommand's type and text by + passing the created IDbCommand to a ICommandSetter implementation + that knows how to bind values to the IDbCommand, reading a + single result set on a per-row basis with a . + + The type of command + The text of the query. + callback that will extract results + one row at a time. + + The command setter. + + + + Execute a ADO.NET operation on a command object using a generic interface based callback. + + The type of object returned from the callback. + the callback to execute based on DbCommand + An object returned from callback + + + + Execute a ADO.NET operation on a command object using a generic interface based callback. + + The type of object returned from the callback. + The callback to execute based on IDbCommand + An object returned from callback + + + + Execute a ADO.NET operation on a command object using a generic delegate callback. + + The type of object returned from the callback. + This allows for implementing arbitrary data access operations + on a single command within Spring's managed ADO.NET environment. + The delegate called with a DbCommand object. + A result object returned by the action or null + + + + Execute a ADO.NET operation on a command object using a generic delegate callback. + + The type of object returned from the callback. + This allows for implementing arbitrary data access operations + on a single command within Spring's managed ADO.NET environment. + The delegate called with a IDbCommand object. + A result object returned by the action or null + + + + Executes ADO.NET operations on a command object, created by the provided IDbCommandCreator, + using the interface based callback IDbCommandCallback. + + The type of object returned from the callback. + The command creator. + The callback to execute based on IDbCommand + A result object returned by the action or null + + + + Execute ADO.NET operations on a IDbDataAdapter object using an interface based callback. + + This allows for implementing abritrary data access operations + on a single DataAdapter within Spring's managed ADO.NET environment. + + The type of object returned from the callback. + The data adapter callback. + A result object returned by the callback or null + + + + Execute ADO.NET operations on a IDbDataAdapter object using an delgate based callback. + + This allows for implementing abritrary data access operations + on a single DataAdapter within Spring's managed ADO.NET environment. + + The type of object returned from the callback. + The delegate called with a IDbDataAdapter object. + A result object returned by the callback or null + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The provider. + + + + Initializes a new instance of the class. + + The database provider. + if set to false + lazily initialize the ErrorCodeExceptionTranslator. + + + + Initializes a new instance of the class. + + Useful if you have a subclass of Spring.Data.Core.AdoTemplate. + The classic ADO template. + + + + Execute a ADO.NET operation on a command object using a interface based callback. + + the callback to execute + object returned from callback + + + + Execute a ADO.NET operation on a command object using a delegate callback. + + This allows for implementing arbitrary data access operations + on a single command within Spring's managed ADO.NET environment. + The delegate called with a command object. + A result object returned by the action or null + + + + Execute a ADO.NET operation on a command object using a interface based callback. + + the callback to execute + object returned from callback + + + + Execute a ADO.NET operation on a command object using a delegate callback. + + This allows for implementing arbitrary data access operations + on a single command within Spring's managed ADO.NET environment. + The delegate called with a command object. + A result object returned by the action or null + + + + Executes ADO.NET operations on a command object, created by the provided IDbCommandCreator, + using the interface based callback IDbCommandCallback. + + The type of object returned from the callback. + The command creator. + The callback to execute based on IDbCommand + + A result object returned by the action or null + + + + + Execute ADO.NET operations on a IDbDataAdapter object using an interface based callback. + + The type of object returned from the callback. + The data adapter callback. + + A result object returned by the callback or null + + This allows for implementing abritrary data access operations + on a single DataAdapter within Spring's managed ADO.NET environment. + + + + + Execute ADO.NET operations on a IDbDataAdapter object using an delgate based callback. + + This allows for implementing abritrary data access operations + on a single DataAdapter within Spring's managed ADO.NET environment. + + The type of object returned from the callback. + The delegate called with a IDbDataAdapter object. + A result object returned by the callback or null + + + + Executes a non query returning the number of rows affected. + + The command type. + The command text to execute. + The number of rows affected. + + + + Executes a non query returning the number of rows affected. + + The command type. + The command text to execute. + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + The number of rows affected. + + + + Executes a non query returning the number of rows affected. + + The command type. + The command text to execute. + The parameter collection to map. + The number of rows affected. + + + + Executes a non query with parameters set via the + command setter, returning the number of rows affected. + + The command type. + The command text to execute. + The command setter. + The number of rows affected. + + + + Executes a non query with a command created via IDbCommandCreator and + parameters. + + Output parameters can be retrieved via the returned + dictionary. + + More commonly used as a lower level support method within the framework, + for example StoredProcedure/AdoScalar. + + + The callback to create a IDbCommand. + The number of rows affected. + + + + Execute the query with the specified command text. + + No parameters are used. As with + IDbCommand.ExecuteScalar, it returns the first column of the first row in the result set + returned by the query. Extra columns or row are ignored. + The command type + The command text to execute. + The first column of the first row in the result set + + + + Execute the query with the specified command text and parameter returning a scalar result + + The command type + The command text to execute. + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + The first column of the first row in the result set + + + + Execute the query with the specified command text and parameters returning a scalar result + + The command type + The command text to execute. + The parameter collection to map. + The first column of the first row in the result set + + + + Execute the query with the specified command text and parameters set via the + command setter, returning a scalar result + + The command type + The command text to execute. + The command setter. + The first column of the first row in the result set + + + + Execute the query with a command created via IDbCommandCreator and + parameters + + Output parameters can be retrieved via the returned + dictionary. + + More commonly used as a lower level support method within the framework, + for example for StoredProcedure/AdoScalar. + + The callback to create a IDbCommand. + A dictionary containing output parameters, if any + + + + Execute a query given IDbCommand's type and text by + passing the created IDbCommand to a ICommandSetter implementation + that knows how to bind values to the IDbCommand, reading a + single result set on a per-row basis with a . + + The type of command + The text of the query. + callback that will extract results + one row at a time. + + The command setter. + + + + Execute a query given IDbCommand's type and text, reading a + single result set on a per-row basis with a . + + The type of command + The text of the query. + callback that will extract results + one row at a time. + + + + + Execute a query given IDbCommand's type and text and provided parameter + information, reading a + single result set on a per-row basis with a . + + The type of command + The text of the query. + callback that will extract results + one row at a time. + + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + + + + Execute a query given IDbCommand's type and text and provided IDbParameters, + reading a single result set on a per-row basis with a . + + Type of the command. + The text of the query. + callback that will extract results + one row at a time. + The parameter collection to map. + + + + Checks if DbProvider is not null and creates ExceptionTranslator if not LazyInit. + + + + + Creates the data reader wrapper for use in AdoTemplate callback methods. + + The reader to wrap. + The data reader used in AdoTemplate callbacks + + + + Derives the parameters of a stored procedure including the return parameter + + Name of the procedure. + if set to true to include return parameter. + The stored procedure parameters + + + + An instance of a DbProvider implementation. + + + + + Gets or sets a value indicating whether to lazily initialize the + IAdoExceptionTranslator for this accessor, on first encounter of a + exception from the data provider. Default is "true"; can be switched to + "false" for initialization on startup. + + true if to lazy initialize the IAdoExceptionTranslator; + otherwise, false. + + + + Gets or sets the exception translator. If no custom translator is provided, a default + is used. + + The exception translator. + + + + Gets or set the System.Type to use to create an instance of IDataReaderWrapper + for the purpose of having defaults values to use in case of DBNull values read + from IDataReader. + + The type of the data reader wrapper. + + + + Gets the 'Classic' AdoTemplate in Spring.Data + + + Useful to access methods that return non-generic collections. + + The 'Classic; AdoTemplate in Spring.Data. + + + + Gets or sets the command timeout for IDbCommands that this AdoTemplate executes. It also + sets the value of the contained 'ClassicAdoTemplate'. + + The command timeout. + Default is 0, indicating to use the database provider's default. + Any timeout specified here will be overridden by the remaining + transaction timeout when executing within a transaction that has a + timeout specified at the transaction level. + + + + + Generic callback interface for code that operates on a + IDbCommand. + + The return type from executing the + callback + +

    Allows you to execute any number of operations + on a single IDbCommand, for example a single ExecuteScalar + call or repeated execute calls with varying parameters. +

    +

    Used internally by AdoTemplate, but also useful for + application code. Note that the passed in IDbCommand + has been created by the framework and will have its + Connection property set and the Transaction property + set based on the transaction context.

    +

    As an alternative to using this interface you can use + the delegate version which is particularly nice when using + anonymous delegates for writing more terse code and having + easy access to the variables in the calling class.

    +

    See for a version that has the + base class DbCommand as the callback argument.

    +
    + Mark Pollack +
    + + + Called by AdoTemplate.Execute with an active ADO.NET IDbCommand. + The calling code does not need to care about closing the + command or the connection, or + about handling transactions: this will all be handled by + Spring's AdoTemplate + + An active IDbCommand instance + The result object + + + + Interface to be implemented by objects that can provide SQL strings + + Typically implemented by IDbCommandCreator and + ICommandCallbacks that want to expose the CommandText they + use to create their ADO.NET commands, to allow for better + contextual information in case of exceptions + Mark Pollack(.NET) + Juergen Hoeller + + + + Generic callback interface for code that operates on a + IDbCommand. + + +

    Allows you to execute any number of operations + on a single IDbCommand, for example a single ExecuteScalar + call or repeated execute calls with varying parameters. +

    +

    Used internally by AdoTemplate, but also useful for + application code. Note that the passed in IDbCommand + has been created by the framework.

    +
    + Mark Pollack +
    + + + Called by AdoTemplate.Execute with an active ADO.NET IDbCommand. + The calling code does not need to care about closing the + command or the connection, or + about handling transactions: this will all be handled by + Spring's AdoTemplate + + An active IDbCommand instance + The result object + + + + Callback delegate to process all result sets and row in an + AdoTemplate query method. + + The type returned from the result set mapping. + The IDataReader to extract data from. + Implementations should not close this: it will be closed + by the AdoTemplate. + An arbitrary result object or null if none. + + + + Provides database metdata information. + + Mark Pollack (.NET) + $Id: DbMetadata.cs,v 1.13 2007/11/21 18:01:45 markpollack Exp $ + + + + Provides minimal database metadata information to support the + functionality in Spring.NET ADO.NET Framework. + + + Mark Pollack (.NET) + $Id: IDbMetadata.cs,v 1.8 2007/11/21 18:01:45 markpollack Exp $ + + + + Gets a descriptive name of the product. + + Example: Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0 + The name of the product. + + + + Gets the type of the connection. The fully qualified type name is given since some providers, + notably for SqlServerCe, do not use the same namespace for all data access types. + + Example: System.Data.SqlClient.SqlCommand, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the connection. + + + + Gets the type of the command. + + Example: System.Data.SqlClient.SqlCommand, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the command. + + + + Gets the type of the parameter. + + Example: System.Data.SqlClient.SqlParameter, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the parameter. + + + + Gets the type of the data adapter. + + Example: System.Data.SqlClient.SqlDataAdapter, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the data adapter. + + + + Gets the type of the command builder. + + Example: System.Data.SqlClient.SqlCommandBuilder, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the command builder. + + + + Gets the type of the exception. + + Example: System.Data.SqlClient.SqlException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the exception. + + + + Gets the error code exception expression. + + Example Errors[0].Number.ToString() for Sql Server + The error code exception expression. + + + + Gets the command builder derive parameters method. + + If the value 'not supported' is specified then this method will throw an ArgumentException + Example: DeriveParameters + The command builder derive parameters method. + + + + Provide the prefix used to indentify named parameters in SQL text. + + @ for Sql Server + + + + Does the driver require the use of the parameter prefix when + specifying the name of the parameter in the Command's + Parameter collection. + + If true, then commamd.Parameters["@parameterName"] is + used, otherwise, command.Parameters["parameterName"]. + + + + + Gets a value indicating whether the Provider requires + the use of a named prefix in the SQL string. + + + The OLE DB/ODBC .NET Provider does not support named parameters for + passing parameters to an SQL Statement or a stored procedure called + by an IDbCommand when CommandType is set to Text. + + + true if use parameter prefix in SQL; otherwise, false. + + + + + For providers that allow you to choose between binding parameters + to a command by name (true) or by position (false). + + + + + Gets the type of the parameter db. + + Example: System.Data.SqlDbType, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the parameter db. + + + + Gets the parameter db type property. + + Example: SqlDbType for SqlServer + The parameter db type property. + + + + Gets the parameter is nullable property. + + Example: IsNullable for Sql Server + The parameter is nullable property. + + + + Gets or sets the error codes. + + The collection of error codes to map error code integer values to Spring's DAO exception hierarchy + The error codes. + + + + Initializes a new instance of the class. + + Name of the product. + Name of the assembly. + Type of the connection. + Type of the command. + Type of the parameter. + Type of the data adapter. + Type of the command builder. + The command builder derive parameters method. + Type of the parameter db. + The parameter db type property. + The parameter is nullable property. + The parameter name prefix. + Type of the exception. + if set to true [use parameter name prefix in parameter collection]. + if set to true [use parameter prefix in SQL]. + if set to true [bind by name]. + The error code exception expression. + + + + Gets a value indicating whether to use param name prefix in parameter collection. + + + true if should use param name prefix in parameter collection; otherwise, false. + + + + + Gets the name of the assembly. + + Example: System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The name of the assembly. + + + + Gets a descriptive name of the product. + + Example: Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0 + The name of the product. + + + + Gets the type of the connection. The fully qualified type name is given since some providers, + notably for SqlServerCe, do not use the same namespace for all data access types. + + Example: System.Data.SqlClient.SqlCommand, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the connection. + + + + Gets the type of the command. + + Example: System.Data.SqlClient.SqlCommand, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the command. + + + + Gets the type of the parameter. + + Example: System.Data.SqlClient.SqlParameter, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the parameter. + + + + Gets the type of the data adapter. + + Example: System.Data.SqlClient.SqlDataAdapter, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the data adapter. + + + + Gets the type of the command builder. + + Example: System.Data.SqlClient.SqlCommandBuilder, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the command builder. + + + + Gets the command builder derive parameters method. + + If the value 'not supported' is specified then this method will throw an ArgumentException + Example: DeriveParameters + The command builder derive parameters method. + + + + Provide the prefix used to indentify named parameters in SQL text. + + @ for Sql Server + + + + Gets the type of the exception. + + Example: System.Data.SqlClient.SqlException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the exception. + + + + Does the driver require the use of the parameter prefix when + specifying the name of the parameter in the Command's + Parameter collection. + + + If true, then commamd.Parameters["@parameterName"] is + used, otherwise, command.Parameters["parameterName"]. + + + + + Gets a value indicating whether the Provider requires + the use of a named prefix in the SQL string. + + + true if use parameter prefix in SQL; otherwise, false. + + + The OLE DB/ODBC .NET Provider does not support named parameters for + passing parameters to an SQL Statement or a stored procedure called + by an IDbCommand when CommandType is set to Text. + + + + + For providers that allow you to choose between binding parameters + to a command by name (true) or by position (false). + + + + + + Gets the type of the parameter db. + + Example: System.Data.SqlDbType, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The type of the parameter db. + + + + Gets the parameter db type property. + + Example: SqlDbType for SqlServer + The parameter db type property. + + + + Gets the parameter is nullable property. + + Example: IsNullable for Sql Server + The parameter is nullable property. + + + + Gets or sets the error codes. + + The collection of error codes to map error code integer values to Spring's DAO exception hierarchy + The error codes. + + + + Gets the error code exception expression. + + Example Errors[0].Number.ToString() for Sql Server + The error code exception expression. + + + + Exception thrown on a pessimistic locking violation. + + + Serves as a superclass for more specific exceptions, like + CannotAcquireLockException and DeadlockLoserDataAccessException + + + This exception will be thrown either by O/R mapping tools or by custom DAO + implementations. + + + Rod Johnson + Mark Pollack (.NET) + $Id: PessimisticLockingFailureException.cs,v 1.1 2008/04/08 20:26:43 markpollack Exp $ + + + + Exception thrown on concurrency failure. This exception should be + sublassed to indicate the type of failure - optimistic locking, + failure to acquire lock, etc. + + +

    + This exception will be thrown either by O/R mapping tools or by custom DAO + implementations. +

    +
    + Thomas Risberg + Griffin Caprio (.NET) + $Id: ConcurrencyFailureException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ +
    + + + Root of the hierarchy of data access exceptions + + Rod Johnson + Griffin Caprio (.NET) + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Helper class that simplifies programmatic transaction demarcation and + transaction exception handling. + + +

    + The central methods are + + and + supporting transactional code wrapped in the delegate instance. It handles the + transaction lifecycle and possible exceptions such that neither the delegate + implementation nor the calling code needs to explicitly handle transactions. +

    +

    + Can be used within a service implementation via direct instantiation with + a transaction manager reference, or get prepared in an application context + and given to services as object reference. +

    + + The transaction manager should always be configured as an object in the application + context, in the first case given to the service directly, in the second case to the + prepared template. + +

    + Supports setting the propagation behavior and the isolation level by name, + for convenient configuration in context definitions. +

    +
    + Juergen Hoeller + Mark Pollack (.NET) + Griffin Caprio (.NET) + $Id: TransactionTemplate.cs,v 1.13 2007/08/01 18:53:14 markpollack Exp $ +
    + + + Default implementation of the + interface, offering object-style configuration and sensible default values. + + +

    + Base class for both and + . +

    +
    + Juergen Hoeller + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: DefaultTransactionDefinition.cs,v 1.15 2007/12/07 08:10:03 markpollack Exp $ +
    + + + Interface for classes that define transaction properties. Base interface for + . + + +

    + Note that isolation level, timeout and read-only settings will only + get applied when starting a new transaction. As only + and + can actually cause that, it doesn't make sense + to specify any of those settings else. Furthermore, not all transaction + managers will support those features and thus throw respective exceptions + when given non-default values. +

    +
    + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: ITransactionDefinition.cs,v 1.9 2007/12/07 08:10:03 markpollack Exp $ +
    + + + Return the propagation behavior of type + . + + + + + Return the isolation level of type . + + +

    + Only makes sense in combination with + and + . +

    +

    + Note that a transaction manager that does not support custom isolation levels + will throw an exception when given any other level than + . +

    +
    +
    + + + Return the transaction timeout. + + +

    + Must return a number of seconds, or -1. + Only makes sense in combination with + and + . + Note that a transaction manager that does not support timeouts will + throw an exception when given any other timeout than -1. +

    +
    +
    + + + Get whether to optimize as read-only transaction. + + +

    + This just serves as hint for the actual transaction subsystem, + it will not necessarily cause failure of write accesses. +

    +

    + Only makes sense in combination with + and + . +

    +

    + A transaction manager that cannot interpret the read-only hint + will not throw an exception when given ReadOnly=true. +

    +
    +
    + + + Return the name of this transaction. Can be null. + + + This will be used as a transaction name to be shown in a + transaction monitor, if applicable. In the case of Spring + declarative transactions, the exposed name will be the fully + qualified type name + "." method name + assembly (by default). + + + + + Gets the enterprise services interop option. + + The enterprise services interop option. + + + + The default transaction timeout. + + + + + Prefix for Propagation settings. + + + + + Prefix for IsolationLevel settings. + + + + + Prefix for transaction timeout values in description strings. + + + + + Marker for read-only transactions in description strings. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class + with the supplied + behaviour. + + + The desired behavior. + + + + + An override of the default method. + + The to compare to. + True if the objects are equal. + + + + An override of the default method that returns the + hashcode of the + + property. + + + The hashcode of the + + property. + + + + + An override of the default method that returns a string + representation of the + + property. + + + A string representation of the + + property. + + + + + Gets / Sets the propagation + behavior. + + + + + Return the isolation level of type . + + +

    + Only makes sense in combination with + and + . +

    +

    + Note that a transaction manager that does not support custom isolation levels + will throw an exception when given any other level than + . +

    +
    +
    + + + Return the transaction timeout. + + +

    + Must return a number of seconds, or -1. + Only makes sense in combination with + and + . + Note that a transaction manager that does not support timeouts will + throw an exception when given any other timeout than -1. +

    +
    +
    + + + Get whether to optimize as read-only transaction. + + +

    + This just serves as hint for the actual transaction subsystem, + it will not necessarily cause failure of write accesses. +

    +

    + Only makes sense in combination with + and + . +

    +

    + A transaction manager that cannot interpret the read-only hint + will not throw an exception when given ReadOnly=true. +

    +
    +
    + + + Return the name of this transaction. Can be null. + + + + This will be used as a transaction name to be shown in a + transaction monitor, if applicable. In the case of Spring + declarative transactions, the exposed name will be the fully + qualified type name + "." method name + assembly (by default). + + + + + Gets the enterprise services interop option. + + The enterprise services interop option. + + + + Returns a representation of the + + property. + + + + + Interface specifying basic transaction exectuion operations. + + + Implemented by . Not often used directly, + but a useful option to enhance testability, as it can easily be mocked or stubbed. + + Juergen Hoeller + Mark Pollac (.NET) + $Id: ITransactionOperations.cs,v 1.1 2007/08/01 18:53:14 markpollack Exp $ + + + + Executes the the action specified by the given delegate callback within a transaction. + + Allows for returning a result object created within the transaction, that is, + a domain object or a collection of domain objects. An exception thrown by the callback + is treated as a fatal exception that enforces a rollback. Such an exception gets + propagated to the caller of the template. + + The delegate that specifies the transactional action. + A result object returned by the callback, or null if one + + In case of initialization or system errors. + + + + + Executes the action specified by the given callback object within a transaction. + + Allows for returning a result object created within the transaction, that is, + a domain object or a collection of domain objects. An exception thrown by the callback + is treated as a fatal exception that enforces a rollback. Such an exception gets + propagated to the caller of the template. + + The callback object that specifies the transactional action. + A result object returned by the callback, or null if one + + In case of initialization or system errors. + + + + + Creates a new instance of the + class. + + +

    + Mainly targeted at configuration by an object factory. +

    + + The + + property must be set before any calls to the + + or + method. + +
    + +
    + + + Creates a new instance of the + class. + + +

    + Mainly targeted at configuration by an object factory. +

    +
    + + The transaction management strategy to be used. + +
    + + + Ensures that the + + has been set. + + + + + Executes the the action specified by the given delegate callback within a transaction. + + The delegate that specifies the transactional action. + + A result object returned by the callback, or null if one + + Allows for returning a result object created within the transaction, that is, + a domain object or a collection of domain objects. An exception thrown by the callback + is treated as a fatal exception that enforces a rollback. Such an exception gets + propagated to the caller of the template. + + + In case of initialization or system errors. + + + + + Executes the action specified by the given callback object within a transaction. + + The callback object that specifies the transactional action. + + A result object returned by the callback, or null if one + + Allows for returning a result object created within the transaction, that is, + a domain object or a collection of domain objects. An exception thrown by the callback + is treated as a fatal exception that enforces a rollback. Such an exception gets + propagated to the caller of the template. + + + In case of initialization or system errors. + + + + + Perform a rollback, handling rollback exceptions properly. + + The object representing the transaction. + The thrown application exception or error. + + + + Gets and sets the to + be used. + + + + + Interface that specifies means to programmatically manage + transaction savepoints in a generic fashion. + + +

    + Note that savepoints can only work within an active transaction. + Just use this programmatic savepoint handling for advanced needs; + else, a subtransaction with a value + of is preferable. +

    +
    + Juergen Hoeller + Griffin Caprio (.NET) + $Id: ISavepointManager.cs,v 1.5 2006/05/17 17:48:25 bbaia Exp $ +
    + + + Create a new savepoint. + + + The name of the savepoint to create. + + + You can roll back to a specific savepoint + via , + and explicitly release a savepoint that you don't need anymore via + . +

    + Note that most transaction managers will automatically release + savepoints at transaction completion. +

    +
    + + If the savepoint could not be created, + either because the backend does not support it or because the + transaction is not in an appropriate state. + + + A savepoint object, to be passed into + + or . + +
    + + + Roll back to the given savepoint. + + + The savepoint will be automatically released afterwards. + + The savepoint to roll back to. + + If the rollback failed. + + + + + Explicitly release the given savepoint. + + +

    + Note that most transaction managers will automatically release + savepoints at transaction completion. +

    +

    + Implementations should fail as silently as possible if + proper resource cleanup will still happen at transaction completion. +

    +
    + The savepoint to release. + + If the release failed. + +
    + + + This interface adds a + + specification to . + + Griffin Caprio (.NET) + $Id: ITransactionAttribute.cs,v 1.6 2007/12/29 00:28:53 markpollack Exp $ + + + + Decides if rollback is required for the supplied . + + The to evaluate. + True if the exception causes a rollback, false otherwise. + + + + Transaction attribute approach to rolling back on all exceptions, no other + exceptions by default. + + Rod Johnson + Griffin Caprio (.NET) + $Id: DefaultTransactionAttribute.cs,v 1.9 2007/12/29 00:28:53 markpollack Exp $ + + + + Prefix for rollback-on-exception rules in description strings. + + + + + Prefix for commit-on-exception rules in description strings. + + + + + Creates a new instance of the + + class. + + + + + Creates a new instance of the + + class, setting the propagation behavior to the supplied value. + + + The desired transaction propagation behaviour. + + + + + Decides if rollback is required for the supplied . + + +

    + The default behavior is to rollback on any exception. + Consistent with 's behavior. +

    +
    + The to evaluate. + True if the exception causes a rollback, false otherwise. +
    + + + Return a description of this transaction attribute. + + +

    + The format matches the one used by the + , + to be able to feed any result into a + instance's properties. +

    +
    +
    + + + Miscellaneous utility methods for manipulating parameter objects. + + Mark Pollack (.NET) + $Id: ParameterUtils.cs,v 1.12 2007/10/11 04:41:00 markpollack Exp $ + + + + The shared log instance for this class (and derived classes). + + + + + Initializes a new instance of the class. + + + + + Copies the parameters from IDbParameters to the parameter collection in IDbCommand + + The command. + The spring param collection. + + + + Copies the parameters in IDbCommand to IDbParameters + + The spring param collection. + The command. + + + + Interface to be implemented by classes that can translate between properietary SQL exceptions + and Spring.NET's data access strategy-agnostic . + + +

    + Implementations can be generic (for example, ADO.NET Exceptions) or proprietary (for example, + using SQL Server or Oracle error codes) for greater precision. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + Mark Pollack + $Id: IAdoExceptionTranslator.cs,v 1.2 2006/11/29 07:18:46 markpollack Exp $ +
    + + + Translate the given into a generic data access exception. + + A readable string describing the task being attempted. + The SQL query or update that caused the problem. May be null. + + The encountered by the ADO.NET implementation. + + + A appropriate for the supplied + . + + + + + Transaction Manager that uses EnterpriseServices to access the + MS-DTC. It requires the support of 'Services without Components' + functionality which is available on Win 2003 and Win XP SP2. + + Mark Pollack (.NET) + $Id: ServiceDomainPlatformTransactionManager.cs,v 1.6 2007/12/04 19:43:16 markpollack Exp $ + + + + Abstract base class that allows for easy implementation of concrete platform transaction managers. + + +

    Provides the following workflow handling: +

      +
    • Determines if there is an existing transaction
    • +
    • Applies the appropriate propagation behavior
    • +
    • Suspends and resumes transactions if necessary
    • +
    • Checks the rollback-only flag on commit
    • +
    • Applies the appropriate modification on rollback (actual rollback or setting rollback-only)
    • +
    • Triggers registered synchronization callbacks (if transaction synchronization is active)
    • +
    +

    +

    + Transaction synchronization is a generic mechanism for registering + callbacks that get invoked at transaction completion time. The same mechanism + can also be used for custom synchronization efforts. +

    +

    + The state of this class is serializable. It's up to subclasses if + they wish to make their state to be serializable. + They should implement if they need + to restore any transient state. +

    +
    + Juergen Hoeller + Mark Pollack (.NET) + Griffin Caprio (.NET) +
    + + + Return the current transaction object. + + The current transaction object. + + If transaction support is not available. + + + In the case of lookup or system errors. + + + + + Check if the given transaction object indicates an existing transaction + (that is, a transaction which has already started). + + + The result will be evaluated according to the specified propagation + behavior for the new transaction. An existing transaction might get + suspended (in case of PROPAGATION_REQUIRES_NEW), or the new transaction + might participate in the existing one (in case of PROPAGATION_REQUIRED). + Default implementation returns false, assuming that detection of or + participating in existing transactions is generally not supported. + Subclasses are of course encouraged to provide such support. + + Transaction object returned by + . + + True if there is an existing transaction. + + In the case of system errors. + + + + + Begin a new transaction with the given transaction definition. + + + Does not have to care about applying the propagation behavior, + as this has already been handled by this abstract manager. + + + Transaction object returned by + . + + + instance, describing + propagation behavior, isolation level, timeout etc. + + + In the case of creation or system errors. + + + + + Suspend the resources of the current transaction. + + + Transaction synchronization will already have been suspended. + + Default implementation throws a TransactionSuspensionNotSupportedException, + assuming that transaction suspension is generally not supported. + + + + Transaction object returned by + . + + + An object that holds suspended resources (will be kept unexamined for passing it into + .) + + + If suspending is not supported by the transaction manager implementation. + + + in case of system errors. + + + + + Resume the resources of the current transaction. + + Transaction synchronization will be resumed afterwards. + + Default implementation throws a TransactionSuspensionNotSupportedException, + assuming that transaction suspension is generally not supported. + + + + Transaction object returned by + . + + + The object that holds suspended resources as returned by + . + + + If suspending is not supported by the transaction manager implementation. + + + In the case of system errors. + + + + + Perform an actual commit on the given transaction. + + The status representation of the transaction. + +

    + An implementation does not need to check the rollback-only flag. +

    +
    + + In the case of system errors. + +
    + + + Perform an actual rollback on the given transaction. + + The status representation of the transaction. + + An implementation does not need to check the new transaction flag. + + + In the case of system errors. + + + + + Set the given transaction rollback-only. Only called on rollback + if the current transaction takes part in an existing one. + + Default implementation throws an IllegalTransactionStateException, + assuming that participating in existing transactions is generally not + supported. Subclasses are of course encouraged to provide such support. + + The status representation of the transaction. + + In the case of system errors. + + + + + Return whether to use a savepoint for a nested transaction. Default is true, + which causes delegation to + for holding a savepoint. + + + +

    + Subclasses can override this to return false, causing a further + invocation of + + despite an already existing transaction. +

    +
    +
    + + + Cleanup resources after transaction completion. + + + Transaction object returned by + . + + +

    + Called after + and + + execution on any outcome. +

    +

    + Should not throw any exceptions but just issue warnings on errors. +

    +

    + Default implementation does nothing. +

    +
    +
    + + + Return a currently active transaction or create a new one. + + +

    + This implementation handles propagation behavior. +

    +

    + Delegates to + , + , + and + . +

    +

    + Note that parameters like isolation level or timeout will only be applied + to new transactions, and thus be ignored when participating in active ones. + Furthermore, they aren't supported by every transaction manager: + a proper implementation should throw an exception when custom values + that it doesn't support are specified. +

    +
    + + instance (can be null for + defaults), describing propagation behavior, isolation level, timeout etc. + + + In case of lookup, creation, or system errors. + + + representing the new or current transaction. + +
    + + + This implementation of commit handles participating in existing transactions + and programmatic rollback requests. + + + + + ITransactionStatus object returned by the + () method. + + + In case of commit or system errors. + + + + + Roll back the given transaction, with regard to its status. + + +

    + This implementation handles participating in existing transactions. +

    +

    + Delegates to + , + and + . +

    +

    + If the transaction wasn't a new one, just set it rollback-only + to take part in the surrounding transaction properly. +

    +
    + + ITransactionStatusObject returned by the + () method. + + + In case of system errors. + +
    + + + Determines the timeout to use for the given definition. Will fall back to this manager's default + timeout if the transaction definition doesn't specify a non-default value. + + The transaction definition. + the actual timeout to use. + + + + Suspend the given transaction. Suspends transaction synchronization first, + then delegates to the doSuspend template method. + + the current transaction object + an object that holds suspended resources + + + + Resume the given transaction. Delegates to the doResume template method + first, then resuming transaction synchronization. + + the current transaction object + the object that holds suspended resources, as returned by suspend + + + + Invoke doRollback, handling rollback exceptions properly. + + object representing the transaction + the thrown application exception or error + + in case of a rollback error + + + + + Trigger beforeCommit callback. + + object representing the transaction + + + + Trigger beforeCompletion callback. + + object representing the transaction + + + + Trigger afterCompletion callback, handling exceptions properly. + + object representing the transaction + + Completion status according to + + + + + Clean up after completion, clearing synchronization if necessary, + and invoking doCleanupAfterCompletion. + + object representing the transaction + + + + Sets and gets when this transaction manager should activate the thread-bound + transaction synchronization support. Default is "always". + + +

    + Note that transaction synchronization isn't supported for + multiple concurrent transactions by different transaction managers. + Only one transaction manager is allowed to activate it at any time. +

    + +
    +
    + + + Sets and gets whether nested transactions are allowed. Default is false. + + +

    + Typically initialized with an appropriate default by the + concrete transaction manager subclass. +

    +
    +
    + + + Sets and gets a flag that determines whether or not the + + method must be invoked if a call to the + + method fails. Default is false. + + + Typically not necessary and thus to be avoided as it can override the + commit exception with a subsequent rollback exception. + + + + + Gets or sets a value indicating whether to fail early in case of the transaction being + globally marked as rollback-only. + + + Default is "false", only causing an UnexpectedRollbackException at the + outermost transaction boundary. Switch this flag on to cause an + UnexpectedRollbackException as early as the global rollback-only marker + has been first detected, even from within an inner transaction boundary. + + + true if fail early on global rollback; otherwise, false. + + + + + Gets or sets the default timeout that this transaction manager should apply if there + is no timeout specified at the transaction level, in seconds. + + Returns DefaultTransactionDefinition.TIMEOUT_DEFAULT to indicate the + underlying transaction infrastructure's default timeout. + The default timeout. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + This is indented only for unit testing purposes and should not be + called by production application code. + The tx adapter. + + + + Gets or sets a value indicating whether tracking is enabled. + + true if tracking is enabled; otherwise, false. + + + + Gets or sets a text string that corresponds to the application ID under which tracker information is reported. + The default value is 'Spring.NET' + + The name of the tracking app. + + + + Gets or sets a text string that corresponds to the context name under which tracker information is reported. + + The name of the tracking component. + + + + Interface to be implemented by transaction objects that are able to + return an internal rollback-only marker, typically from a another + transaction that has participated and marked it as rollback-only. + + +

    + Autodetected by , + to always return a current rollbackOnly flag even if not resulting from the current + . +

    +
    + Juergen Hoeller + Griffin Caprio (.NET) + $Id: ISmartTransactionObject.cs,v 1.6 2007/08/29 03:42:20 markpollack Exp $ +
    + + + Return whether the transaction is internally marked as rollback-only. + + True of the transaction is marked as rollback-only. + + + + Exception thrown on mismatch between CLS type and database type: + for example on an attempt to set an object of the wrong type + in an RDBMS column. + + Rod Johnson + Griffin Caprio (.NET) + $Id: TypeMismatchDataAccessException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + + + + Root for exceptions thrown when we use a data access resource incorrectly. + + +

    + Thrown for example on specifying bad SQL when using a RDBMS. + Resource-specific subclasses will probably be supplied by data access packages. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: InvalidDataAccessResourceUsageException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Data access exception thrown when a resource fails completely: + for example, if we can't connect to a database using ADO.NET. + + Rod Johnson + Griffin Caprio (.NET) + $Id: DataAccessResourceFailureException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Exception thrown when we couldn't cleanup after a data access operation, + but the actual operation went OK. + + +

    + For example, this exception or a subclass might be thrown if an ADO.NET + connection couldn't be closed after it had been used successfully. +

    +

    + Note that data access code might perform resource cleanup in a + finally block and therefore log cleanup failure rather than rethrow it, + to keep the original data access exception, if any. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: CleanupFailureDataAccessException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Exception thrown on failure to complete a transaction in serialized mode due to + update conflicts. + + +

    + This exception will be thrown either by O/R mapping tools or by custom DAO + implementations. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: CannotSerializeTransactionException.cs,v 1.6 2008/04/08 20:26:43 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Internal class that manages resources and transaction synchronizations per thread. + + + Supports one resource per key without overwriting, i.e. a resource needs to + be removed before a new one can be set for the same key. + Supports a list of transaction synchronizations if synchronization is active. +

    + Resource management code should check for thread-bound resources via GetResource(). + It is normally not supposed + to bind resources to threads, as this is the responsiblity of transaction managers. + A further option is to lazily bind on first use if transaction synchronization + is active, for performing transactions that span an arbitrary number of resources. +

    +

    + Transaction synchronization must be activated and deactivated by a transaction + manager via + InitSynchronization + and + ClearSynchronization. + This is automatically supported by + . +

    +

    + Resource management code should only register synchronizations when this + manager is active, and perform resource cleanup immediately else. + If transaction synchronization isn't active, there is either no current + transaction, or the transaction manager doesn't support synchronizations. +

    + Note that this class uses following naming convention for the + named 'data slots' for storage of thread local data, 'Spring.Transaction:Name' + where Name is either +
    + Juergen Hoeller + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: TransactionSynchronizationManager.cs,v 1.19 2008/02/17 13:17:13 markpollack Exp $ +
    + + + Check if there is a resource for the given key bound to the current thread. + + key to check + if there is a value bound to the current thread + + + + Retrieve a resource for the given key that is bound to the current thread. + + key to check + a value bound to the current thread, or null if none. + + + + Bind the given resource for teh given key to the current thread + + key to bind the value to + value to bind + + + + Unbind a resource for the given key from the current thread + + key to check + the previously bound value + if there is no value bound to the thread + + + + Activate transaction synchronization for the current thread. + + + Called by transaction manager at the beginning of a transaction. + + + If synchronization is already active. + + + + + Deactivate transaction synchronization for the current thread. + + + Called by transaction manager on transaction cleanup. + + + If synchronization is not active. + + + + + Clears the entire transaction synchronization state for the current thread, registered + synchronizations as well as the various transaction characteristics. + + + + + Register a new transaction synchronization for the current thread. + + + Typically called by resource management code. + + + If synchronization is not active. + + + + + Return all resources that are bound to the current thread. + + Main for debugging purposes. Resource manager should always + invoke HasResource for a specific resource key that they are interested in. + + IDictionary with resource keys and resource objects or empty + dictionary if none is bound. + + + + Return an unmodifiable list of all registered synchronizations + for the current thread. + + + A list of + instances. + + + If synchronization is not active. + + + + + Return if transaction synchronization is active for the current thread. + + + Can be called before + InitSynchronization + to avoid unnecessary instance creation. + + + + + Gets or sets a value indicating whether the + current transaction is read only. + + + Called by transaction manager on transaction begin and on cleanup. + Return whether the current transaction is marked as read-only. + To be called by resource management code when preparing a newly + created resource (for example, a Hibernate Session). +

    Note that transaction synchronizations receive the read-only flag + as argument for the beforeCommit callback, to be able + to suppress change detection on commit. The present method is meant + to be used for earlier read-only checks, for example to set the + flush mode of a Hibernate Session to FlushMode.Never upfront. +

    +
    + + true if current transaction read only; otherwise, false. + +
    + + + Gets or sets the name of the current transaction, if any. + + Called by the transaction manager on transaction begin and on cleanup. + To be called by resource management code for optimizations per use case, for + example to optimize fetch strategies for specific named transactions. + The name of the current transactio or null if none set. + + + + Gets or sets a value indicating whether there currently is an actual transaction + active. + + This indicates wheter the current thread is associated with an actual + transaction rather than just with active transaction synchronization. + Called by the transaction manager on transaction begin and on cleanup. + To be called by resource management code that wants to discriminate between + active transaction synchronization (with or without backing resource transaction; + also on PROPAGATION_SUPPORTS) and an actual transaction being active; on + PROPAGATION_REQUIRES, PROPAGATION_REQUIRES_NEW, etC) + + true if [actual transaction active]; otherwise, false. + + + + + Gets or sets the current transaction isolation level, if any. + + Called by the transaction manager on transaction begin and on cleanup. + The current transaction isolation level. If no current transaction is + active, retrun IsolationLevel.Unspecified + + + + Type converter for + objects. + + + Takes s of the form +

    PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2

    +

    where only propagation code is required. For example:

    +

    PROPAGATION_MANDATORY,ISOLATION_DEFAULT

    +

    + The tokens can be in any order. Propagation and isolation codes + must use the names of the values in the + enumeration. Timeout values are in seconds. If no timeout is specified, the transaction + manager will apply a default timeout specific to the particular transaction manager. +

    +

    + A "+" before an exception name substring indicates that transactions should commit even + if this exception is thrown; a "-" that they should roll back. +

    +
    + Mark Pollack + $Id: TransactionAttributeConverter.cs,v 1.1 2007/12/29 00:28:53 markpollack Exp $ +
    + + + Returns whether this converter can convert an object of the given type to an ITransactionAttribute, using the specified context. + + An that provides a format context. + A that represents the type you want to convert from. + + true if this converter can perform the conversion; otherwise, false. + + + + + Converts from string to ITransactionAttribute + + The context. + The culture. + The string value to convert + An ITransactionAttribute instance + + + + Uses and System.Transactions.Transaction.Current to provide + necessary state and operations to TxScopeTransactionManager. + + Mark Pollack (.NET) + $Id: DefaultTransactionScopeAdapter.cs,v 1.1 2007/11/30 18:38:47 markpollack Exp $ + + + + Provides the necessary transactional state and operations for TxScopeTransactionManager to + work with TransactionScope and Transaction.Current. + + Introduced for purposes of unit testing. + Mark Pollack (.NET) + $Id: ITransactionScopeAdapter.cs,v 1.1 2007/11/30 18:38:47 markpollack Exp $ + + + + Creates the transaction scope. + + The tx scope option. + The tx options. + The interop option. + + + + Call Complete() on the TransactionScope object created by this instance. + + + + + Call Disponse() on the TransactionScope object created by this instance. + + + + + Gets a value indicating whether there is a new transaction or an existing transaction. + + + true if this instance is existing transaction; otherwise, false. + + + + + Gets a value indicating whether rollback only has been called (i.e. Rollback() on the + Transaction object) and therefore voting that the transaction will be aborted. + + true if rollback only; otherwise, false. + + + + Call Complete() on the TransactionScope object created by this instance. + + + + + Call Disponse() on the TransactionScope object created by this instance. + + + + + Creates the transaction scope. + + The tx scope option. + The tx options. + The interop option. + + + + Gets a value indicating whether there is a new transaction or an existing transaction. + + + true if this instance is existing transaction; otherwise, false. + + + + + Gets a value indicating whether rollback only has been called (i.e. Rollback() on the + Transaction object) and therefore voting that the transaction will be aborted. + + true if rollback only; otherwise, false. + + + + Callback interface to process all result sets and rows in + an AdoTemplate query method. + + Implementations of this interface perform the work + of extracting results but don't need worry about managing + ADO.NET resources, such as closing the reader or transaction management. +

    + This interface is mainly used within the ADO.NET + framework. An IResultSetExtractor is usually a simpler choice + for result set (DataReader) processing, in particular a + RowMapperResultSetExtractor in combination with a IRowMapper. +

    + Note: in contracts to a IRowCallbackHandler, a ResultSetExtractor + is usually stateless and thus reusable, as long as it doesn't access + stateful resources or keep result state within the object. + +
    + +
    + + + Implementations must implement this method to process all + result set and rows in the IDataReader. + + The IDataReader to extract data from. + Implementations should not close this: it will be closed + by the AdoTemplate. + An arbitrary result object or null if none. The + extractor will typically be stateful in the latter case. + + + + Custom data reader implementations often delegate to an underlying + instance. This interface captures that relationship for reuse in + the framework. + + Implementations will typically add behavior to standard IDataReader methods, + for example, by providing default values for DbNull values. + See as an example. + + Mark Pollack (.NET) + $Id: IDataReaderWrapper.cs,v 1.2 2007/10/30 15:24:44 markpollack Exp $ + + + + The underlying reader implementation to delegate to for accessing data + from a returned result sets. + + The wrapped reader. + + + + Convenient super class for ADO.NET data access objects using generics. + + + Requires a IDBProvider to be set, providing a + AdoTemplate based on it to subclasses. + This base class is mainly intended for AdoTemplate usage. + + + + + Generic base class for DAOs, defining template methods for DAO initialization. + + + Extended by Spring's specific DAO support classes, such as: + AdoDaoSupport, HibernateDaoSupport, etc. + + Mark Pollack (.NET) + $Id: DaoSupport.cs,v 1.3 2006/11/13 07:04:45 markpollack Exp $ + + + + The shared instance for this class (and derived classes). + + + + + Initializes a new instance of the class. + + + + + Abstract subclasses must override this to check their configuration. + + +

    Implementors should be marked as sealed, to make it clear that + concrete subclasses are not supposed to override this template method themselves. +

    +
    +
    + + + Concrete subclasses can override this for custom initialization behavior. + + + Gets called after population of this instance's object properties. + Exception thrown if InitDao fails will be rethrown as + a ObjectInitializationException. + + + + + Create a AdoTemplate for a given DbProvider + Only invoked if populating the DAO with a DbProvider reference. + + + Can be overriden in subclasses to provide AdoTemplate instances + with a different configuration, or a cusotm AdoTemplate subclass. + + The DbProvider to create a AdoTemplate for + + + + Convenience method to create a parameters builder. + + Virtual for sublcasses to override with custom + implementation. + A new DbParameterBuilder + + + + The DbProvider instance used by this DAO + + + + + Set the AdoTemplate for this DAO explicity, as + an alternative to specifying a IDbProvider + + + + + IDbProvider implementation that delegates all calls to a given target + IDbProvider + + Mark Pollack + $Id: DelegatingDbProvider.cs,v 1.1 2008/01/29 22:19:20 markpollack Exp $ + + + + Factory interface to create provider specific ADO.NET objects. + + + + + Returns a new command object for executing SQL statments/Stored Procedures + against the database. + + An new + + + + Returns a new instance of the providers CommandBuilder class. + + In .NET 1.1 there was no common base class or interface + for command builders, hence the return signature is object to + be portable (but more loosely typed) across .NET 1.1/2.0 + A new Command Builder + + + + Returns a new connection object to communicate with the database. + + A new + + + + Returns a new adapter objects for use with offline DataSets. + + A new + + + + Returns a new parameter object for binding values to parameter + placeholders in SQL statements or Stored Procedure variables. + + A new + + + + Creates the name of the parameter in the format appropriate to use inside IDbCommand.CommandText. + + In most cases this adds the parameter prefix to the name passed into this method. + The unformatted name of the parameter. + The parameter name formatted foran IDbCommand.CommandText. + + + + Creates the name ofthe parameter in the format appropriate for an IDataParameter, i.e. to be + part of a IDataParameterCollection. + + The unformatted name of the parameter. + The parameter name formatted for an IDataParameter + + + + Extracts the provider specific error code as a string. + + The data access exception. + The provider specific error code + + + + Determines whether the provided exception is in fact related + to database access. This can be provider dependent in .NET 1.1 since + there isn't a common base class for ADO.NET exceptions. + + The exception thrown when performing data access + operations. + + true if is a valid data access exception for the specified + exception; otherwise, false. + + + + + Return metadata information about the database provider + + + + + Connection string used to create connections. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The target db provider. + + + + Returns a new command object for executing SQL statments/Stored Procedures + against the database. + + An new + + + + Returns a new instance of the providers CommandBuilder class. + + In .NET 1.1 there was no common base class or interface + for command builders, hence the return signature is object to + be portable (but more loosely typed) across .NET 1.1/2.0 + A new Command Builder + + + + Returns a new connection object to communicate with the database. + + A new + + + + Returns a new adapter objects for use with offline DataSets. + + A new + + + + Returns a new parameter object for binding values to parameter + placeholders in SQL statements or Stored Procedure variables. + + A new + + + + Creates the name of the parameter in the format appropriate to use inside IDbCommand.CommandText. + + In most cases this adds the parameter prefix to the name passed into this method. + The unformatted name of the parameter. + The parameter name formatted foran IDbCommand.CommandText. + + + + Creates the name ofthe parameter in the format appropriate for an IDataParameter, i.e. to be + part of a IDataParameterCollection. + + The unformatted name of the parameter. + The parameter name formatted for an IDataParameter + + + + Extracts the provider specific error code as a string. + + The data access exception. + The provider specific error code + + + + Determines whether the provided exception is in fact related + to database access. This can be provider dependent in .NET 1.1 since + there isn't a common base class for ADO.NET exceptions. + + The exception thrown when performing data access + operations. + + true if is a valid data access exception for the specified + exception; otherwise, false. + + + + + Invoked by an + after it has injected all of an object's dependencies. + + +

    + This method allows the object instance to perform the kind of + initialization only possible when all of it's dependencies have + been injected (set), and to throw an appropriate exception in the + event of misconfiguration. +

    +

    + Please do consult the class level documentation for the + interface for a + description of exactly when this method is invoked. In + particular, it is worth noting that the + + and + callbacks will have been invoked prior to this method being + called. +

    +
    + + In the event of misconfiguration (such as the failure to set a + required property) or if initialization fails. + +
    + + + Gets or sets the target IDbProvider that this IDbProvider should delegate to + + The target db provider. + + + + Return metadata information about the database provider + + + + + Connection string used to create connections. + + + + + Exception thrown when SQL specified is invalid. + + Mark Pollack (.NET) + $Id: BadSqlGrammarException.cs,v 1.3 2006/11/29 07:18:45 markpollack Exp $ + + + + SQL that led to the problem + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + A message about the exception. + + + + Initializes a new instance of the class. + + A message about the exception. + The inner exception. + + + + Initializes a new instance of the class. + + name of the current task. + The offending SQL statment + The root cause. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + When overridden in a derived class, sets the + with information about the exception. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is a null reference ( in Visual Basic). + + + + Exception thrown on an optimistic locking violation. + + +

    + This exception will be thrown either by O/R mapping tools or by custom DAO + implementations. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: OptimisticLockingFailureException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Enumeration of status values when synchronizing transactions. + + Griffin Caprio + $Id: TransactionSynchronizationStatus.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Completion status in case of proper commit. + + + + + Completion status in case of proper rollback. + + + + + Completion status in case of heuristic mixed completion or system error. + + + + + Adapter for the + interface. + + + Contains empty implementations of all interface methods, for easy overriding of + single methods. + + Juergen Hoeller + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: TransactionSynchronizationAdapter.cs,v 1.7 2006/12/06 00:02:25 markpollack Exp $ + + + + Interface for transaction synchronization callbacks. + + + Supported by . + + Juergen Hoeller + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: ITransactionSynchronization.cs,v 1.7 2006/11/24 05:57:46 markpollack Exp $ + + + + Suspend this synchronization. + + +

    + Supposed to unbind resources from + + if managing any. +

    +
    +
    + + + Resume this synchronization. + + +

    + Supposed to rebind resources from + + if managing any. +

    +
    +
    + + + Invoked before transaction commit (before + ) + Can e.g. flush transactional O/R Mapping sessions to the database + + + + This callback does not mean that the transaction will actually be + commited. A rollback decision can still occur after this method + has been called. This callback is rather meant to perform work + that's only relevant if a commit still has a chance + to happen, such as flushing SQL statements to the database. + + + Note that exceptions will get propagated to the commit caller and cause a + rollback of the transaction. + + (note: do not throw TransactionException subclasses here!) + + + + If the transaction is defined as a read-only transaction. + + + + + Invoked after transaction commit. + + Can e.g. commit further operations that are supposed to follow on + a successful commit of the main transaction. + Throws exception in case of errors; will be propagated to the caller. + Note: To not throw TransactionExeption sbuclasses here! + + + + + + Invoked before transaction commit/rollback (after + , + even if + + threw an exception). + + +

    + Can e.g. perform resource cleanup. +

    +

    + Note that exceptions will get propagated to the commit caller + and cause a rollback of the transaction. +

    +
    +
    + + + Invoked after transaction commit/rollback. + + + Status according to + + + Can e.g. perform resource cleanup, in this case after transaction completion. +

    + Note that exceptions will get propagated to the commit or rollback + caller, although they will not influence the outcome of the transaction. +

    +
    +
    + + + Suspend this synchronization. + + +

    + Supposed to unbind resources from + + if managing any. +

    +
    +
    + + + Resume this synchronization. + + +

    + Supposed to unbind resources from + + if managing any. +

    +
    +
    + + + Invoked before transaction commit (before + ) + + + If the transaction is defined as a read-only transaction. + + +

    + Can flush transactional sessions to the database. +

    +

    + Note that exceptions will get propagated to the commit caller and + cause a rollback of the transaction. +

    +
    +
    + + + Invoked after transaction commit. + + Can e.g. commit further operations that are supposed to follow on + a successful commit of the main transaction. + Throws exception in case of errors; will be propagated to the caller. + Note: To not throw TransactionExeption sbuclasses here! + + + + + Invoked before transaction commit/rollback (after + , + even if + + threw an exception). + + +

    + Can e.g. perform resource cleanup. +

    +

    + Note that exceptions will get propagated to the commit caller + and cause a rollback of the transaction. +

    +
    +
    + + + Invoked after transaction commit/rollback. + + + Status according to + + + Can e.g. perform resource cleanup, in this case after transaction completion. +

    + Note that exceptions will get propagated to the commit or rollback + caller, although they will not influence the outcome of the transaction. +

    +
    +
    + + + Compares the current instance with another object of the same type. + + + + A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance is less than obj. Zero This instance is equal to obj. Greater than zero This instance is greater than obj. + + + An object to compare with this instance. + obj is not the same type as this instance. 2 + + + + Exception that gets thrown when an invalid isolation level is specified, + i.e. an isolation level that the transaction manager implementation + doesn't support. + + Juergen Hoeller + Griffin Caprio (.NET) + $Id: InvalidIsolationLevelException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Superclass for exceptions caused by inappropriate usage of + a Spring.NET transaction API. + + Juergen Hoeller + Griffin Caprio (.NET) + $Id: TransactionUsageException.cs,v 1.6 2006/05/18 21:37:51 markpollack Exp $ + + + + Base class for all transaction exceptions. + + Rod Johnson + Griffin Caprio (.NET) + $Id: TransactionException.cs,v 1.7 2006/05/18 21:37:51 markpollack Exp $ + + + + Creates a new instance of the TransactionException class. + + + + + Creates a new instance of the TransactionException class, with the specified message. + + + A message about the exception. + + + + + Creates a new instance of the TransactionException class with the specified message + and root cause. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the TransactionException class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Type converter for + objects. + + + Takes s of the form +

    PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2

    +

    where only propagation code is required. For example:

    +

    PROPAGATION_MANDATORY,ISOLATION_DEFAULT

    +

    + The tokens can be in any order. Propagation and isolation codes + must use the names of the values in the + enumeration. Timeout values are in seconds. If no timeout is specified, the transaction + manager will apply a default timeout specific to the particular transaction manager. +

    +

    + A "+" before an exception name substring indicates that transactions should commit even + if this exception is thrown; a "-" that they should roll back. +

    +
    + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + $Id: TransactionAttributeEditor.cs,v 1.7 2007/12/07 07:30:48 markpollack Exp $ +
    + + + Parses the input properties string into a valid + instance + + + The string defining the transactional properties. + + + + + Gets the + from this editor. + + + + + Summary description for DbProviderUtils. + + + + + Dispose of the given Connection, created via the given IDbProvider, + if it is not managed externally (that is, not bound to the thread). + + The connection to close if necessary. If + this is null the call will be ignored. + The IDbProvider the connection came from + + + + Get a ADO.NET Connection/Transaction Pair for the given IDbProvider. + Changes any exception into the Spring hierarchy of generic data access + exceptions, simplifying calling code and making any exception that is + thrown more meaningful. + + + Is aware of a corresponding Connection/Transaction bound to the current thread, for example + when using AdoPlatformTransactionManager. Will bind a IDbConnection to the thread + if transaction synchronization is active + + The provider. + A Connection/Transaction pair. + + + + Get a ADO.NET Connection/Transaction Pair for the given IDbProvider. + Same as but throwing original provider + exception. + + + Is aware of a corresponding Connection/Transaction bound to the current thread, for example + when using AdoPlatformTransactionManager. Will bind a IDbConnection to the thread + if transaction synchronization is active + + The provider. + + + + + Do the connection mgmt. + + + + + + + Applies the current transaction timeout, if any, to the given ADO.NET IDbCommand object. + + The command. + The db provider. + + + + Applies the specified timeout - overridden by the current transaction timeout, if any, to to the + given ADO.NET IDb command object. + + The command. + The db provider the command was obtained from. + The timeout to apply (or 0 for no timeout outside of a transaction. + + + + Place together the mapping logic to a plain .NET object + for one result set within the same class. + + Mark Pollack (.NET) + $Id: AdoQuery.cs,v 1.7 2007/07/25 08:25:20 markpollack Exp $ + + + + A "AdoOperation" is a thread-safe, reusable object representing + an ADO Query, "NonQuery" (Create, Update, Delete), stored procedure + or DataSet manipualtions. + + + Subclasses should set Command Text and add parameters before invoking + Compile(). The order in which parameters are added is generally + significant when using providers or functionality that do not use + named parameters. + + + Mark Pollack (.NET) + $Id: AdoOperation.cs,v 1.14 2007/12/07 08:09:52 markpollack Exp $ + + + + Abstract base class providing common functionality for generic and non + generic implementations of "AdoOperation" subclasses. + + Mark Pollack (.NET) + $Id: AbstractAdoOperation.cs,v 1.1 2007/07/24 21:01:11 markpollack Exp $ + + + + Has this operation been compiled? Compilation means at + least checking that a IDbProvider and sql have been provided, + but subclasses may also implement their own custom validation. + + + + + Object enabling us to create IDbCommands + efficiently, based on this class's declared parameters. + + + + + Ensures compilation if used in an IApplicationContext + + + + + Compiles this operation. Ignores subsequent attempts to compile. + + + + + Check whether this operation has been compiled already; + lazily compile it if not already compiled. + + Automatically called by ValidateParameters and ValidateNamedParameters + + + + Validates the named parameters passed to an AdoTemplate ExecuteXXX method based on + declared parameters. + + + Subclasses should invoke this method very every ExecuteXXX method. + + The parameter dictionary supplied. May by null. + + + + Subclasses must implement to perform their own compilation. + Invoked after this class's compilation is complete. + Subclasses can assume that SQL has been supplied and that + a IDbProvider has been supplied. + + + + + Hook method that subclasses may override to react to compilation. + This implementation does nothing. + + + + + Gets or sets the type of the command. + + The type of the command. + + + + Gets or sets the db provider. + + The db provider. + + + + Gets or sets the SQL to execute + + The SQL. + + + + Gets or sets the declared parameters. + + The declared parameters. + + + + Gets a value indicating whether this is compiled. + + Compilation means that the operation is fully configured, + and ready to use. The exact meaning of compilation will vary between subclasses. + true if compiled; otherwise, false. + + + + Sets the command timeout for IDbCommands that this AdoTemplate executes. + + Default is 0, indicating to use the database provider's default. + Any timeout specified here will be overridden by the remaining + transaction timeout when executing within a transaction that has a + timeout specified at the transaction level. + + The command timeout. + + + + Initializes a new instance of the class. + + A DbProvider, SQL and any parameters must be supplied + before invoking the compile method and using this object. + + + + + Initializes a new instance of the class. + + A DbProvider, SQL and any parameters must be supplied + before invoking the compile method and using this object. + + Database provider to use. + + + + Initializes a new instance of the class. + + A DbProvider, SQL and any parameters must be supplied + before invoking the compile method and using this object. + + Database provider to use. + SQL query or stored procedure name. + + + + Compiles this operation. Ignores subsequent attempts to compile. + + + + + Gets or sets the ADO template. + + The ADO template. + + + + Gets or sets the db provider. + + The db provider. + + + + Sets the command timeout for IDbCommands that this AdoTemplate executes. + + Default is 0, indicating to use the database provider's default. + Any timeout specified here will be overridden by the remaining + transaction timeout when executing within a transaction that has a + timeout specified at the transaction level. + + The command timeout. + + + + Initializes a new instance of the class. + + + + + Generic callback delegate for code that operates on a DbCommand. + + +

    Allows you to execute any number of operations + on a single DbCommand, for example a single ExecuteScalar + call or repeated execute calls with varying parameters. +

    +

    Used internally by AdoTemplate, but also useful for + application code. Note that the passed in DbCommand + has been created by the framework and will have its + Connection property set and the Transaction property + set based on the transaction context.

    +
    + The return type from executing the + callback + The ADO.NET DbCommand object + The object returned from processing with the + provided DbCommand + Mark Pollack +
    + + + Generic callback interface for code that operates on a + IDbDataAdapter. + + +

    Allows you to execute any number of operations + on a IDbDataAdapter, for example to Fill a DataSet + or other more advanced operations such as the transfering + data between two different DataSets. +

    +

    Note that the passed in IDbDataAdapter + has been created by the framework and its SelectCommand + will be populated with values for CommandType and Text properties + along with Connection/Transaction properties based on the + calling transaction context. +

    + Execute(IDataAdapterCallback dataAdapterCallback) + method. +
    + Mark Pollack (.NET) + $Id: IDataAdapterCallback.cs,v 1.1 2007/12/04 06:48:21 markpollack Exp $ +
    + + + Called by AdoTemplate.Execute with an preconfigured + ADO.NET IDbDataAdapter instance with its SelectCommand + property populated with CommandType and Text values + along with Connection/Transaction properties based on the + calling transaction context. + + An active IDbDataAdapter instance + The result object + + + + ADO.NET based implementation of the + interface. + + Mark Pollack (.NET) + $Id: AdoPlatformTransactionManager.cs,v 1.7 2007/11/30 18:42:11 markpollack Exp $ + + + + Return the current transaction object. + + The current transaction object. + + If transaction support is not available. + + + In the case of lookup or system errors. + + + + + Check if the given transaction object indicates an existing, + i.e. already begun, transaction. + + + Transaction object returned by + . + + True if there is an existing transaction. + + In the case of system errors. + + + + + Begin a new transaction with the given transaction definition. + + + Transaction object returned by + . + + + instance, describing + propagation behavior, isolation level, timeout etc. + + + Does not have to care about applying the propagation behavior, + as this has already been handled by this abstract manager. + + + In the case of creation or system errors. + + + + + Suspend the resources of the current transaction. + + Transaction object returned by + . + + An object that holds suspended resources (will be kept unexamined for passing it into + .) + + + Transaction synchronization will already have been suspended. + + + If suspending is not supported by the transaction manager implementation. + + + in case of system errors. + + + + + Resume the resources of the current transaction. + + Transaction object returned by + . + The object that holds suspended resources as returned by + . + + Transaction synchronization will be resumed afterwards. + + + If suspending is not supported by the transaction manager implementation. + + + In the case of system errors. + + + + + Perform an actual commit on the given transaction. + + The status representation of the transaction. + +

    + An implementation does not need to check the rollback-only flag. +

    +
    + + In the case of system errors. + +
    + + + Perform an actual rollback on the given transaction. + + The status representation of the transaction. + + An implementation does not need to check the new transaction flag. + + + In the case of system errors. + + + + + Set the given transaction rollback-only. Only called on rollback + if the current transaction takes part in an existing one. + + The status representation of the transaction. + + In the case of system errors. + + + + + Invoked by an + after it has injected all of an object's dependencies. + + + If DbProvider is null. + + + + + DbProvider transaction (state) object, representing a ConnectionHolder. + Used as a transaction object by AdoPlatformTransactionManager + + Derives from AdoTransactionObjectSupport to inherit the capability + to manage Savepoints. + + + + + + Convenient base class for ADO.NET transaction aware objects. + + + Can contain a ConnectionHolder object. + + Mark Pollack (.NET) + $Id: AdoTransactionObjectSupport.cs,v 1.7 2007/11/28 05:54:47 markpollack Exp $ + + + + The shared log instance for this class (and derived classes). + + + + + Create a new savepoint. + + + The name of the savepoint to create. + + + You can roll back to a specific savepoint + via , + and explicitly release a savepoint that you don't need anymore via + . +

    + Note that most transaction managers will automatically release + savepoints at transaction completion. +

    +
    + + If the savepoint could not be created, + either because the backend does not support it or because the + transaction is not in an appropriate state. + + + A savepoint object, to be passed into + + or . + +
    + + + Roll back to the given savepoint. + + + The savepoint will be automatically released afterwards. + + The savepoint to roll back to. + + If the rollback failed. + + + + + Explicitly release the given savepoint. + + +

    + Note that most transaction managers will automatically release + savepoints at transaction completion. +

    +

    + Implementations should fail as silently as possible if + proper resource cleanup will still happen at transaction completion. +

    +
    + The savepoint to release. + + If the release failed. + +
    + + + Return whether the transaction is internally marked as rollback-only. + + + True of the transaction is marked as rollback-only. + + + + Sets the rollback only. + + + + + Return whether the transaction is internally marked as rollback-only. + + + True of the transaction is marked as rollback-only. + + + + Miscellaneous utility methods for DAO implementations. + Useful with any data access technology. + + Mark Pollack (.NET) + $Id: DataAccessUtils.cs,v 1.1 2006/12/02 20:50:21 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Exception that gets thrown when an invalid timeout is specified, + for example when the transaction manager implementation doesn't support timeouts. + + Juergen Hoeller + Griffin Caprio (.NET) + $Id: InvalidTimeoutException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Invalid timeout value. + + + + + Creates a new instance of the + class + with the specified message and timeout value. + + + A message about the exception. + + The (possibly invalid) timeout value. + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Override of + to allow for private serialization. + + + The + that holds the serialized object data about the exception. + + + The + that contains contextual information about the source or destination. + + + + + Returns the invalid timeout for this exception. + + + + + ITransactionAttribute that delegates all calls to a give target attribute except for the + name, which is specified in the constructor. + + + + + Initializes a new instance of the class. + + The target attribute. + The identification. + + + + Decides if rollback is required for the supplied . + + The to evaluate. + + True if the exception causes a rollback, false otherwise. + + + + + Return a description of this transaction attribute. + + +

    + The format matches the one used by the + , + to be able to feed any result into a + instance's properties. +

    +
    +
    + + + Return the propagation behavior of type + . + + + + + + Return the isolation level of type . + + + +

    + Only makes sense in combination with + and + . +

    +

    + Note that a transaction manager that does not support custom isolation levels + will throw an exception when given any other level than + . +

    +
    +
    + + + Return the transaction timeout. + + + +

    + Must return a number of seconds, or -1. + Only makes sense in combination with + and + . + Note that a transaction manager that does not support timeouts will + throw an exception when given any other timeout than -1. +

    +
    +
    + + + Get whether to optimize as read-only transaction. + + + +

    + This just serves as hint for the actual transaction subsystem, + it will not necessarily cause failure of write accesses. +

    +

    + Only makes sense in combination with + and + . +

    +

    + A transaction manager that cannot interpret the read-only hint + will not throw an exception when given ReadOnly=true. +

    +
    +
    + + + Return the name of this transaction. + + + + The exposed name will be the fully + qualified type name + "." method name + assembly (by default). + + + + + Gets the enterprise services interop option. + + The enterprise services interop option. + + + + IObjectDefinitionParser implementation that allows users to easily configure all the + infrastructure objects required to enable attribute-driven transction demarcation. + + Rob Harrop + Juergen Hoeller + Mark Pollack (.NET) + + + + Object property name for injection the TransactionInterceptor + + + + + The 'proxy-target-type' attribute + + + + + The 'order' property/attribute + + + + + Central template method to actually parse the supplied XmlElement + into one or more IObjectDefinitions. + + The element that is to be parsed into one or more s + The the object encapsulating the current state of the parsing process; + provides access to a + + The primary IObjectDefinition resulting from the parsing of the supplied XmlElement + + + + + Configures the auto proxy creator. + + The parser context. + The element. + + + + Gets a value indicating whether an ID should be generated instead of read + from the passed in XmlElement. + + true if should generate id; otherwise, false. + Note that this flag is about always generating an ID; the parser + won't even check for an "id" attribute in this case. + + + + + A class that contains the properties of ServiceConfig used by ServiceDomainPlatformTransactionManager. + + This is done to enhance testability of the transaction manager since ServiceConfig does not override Equals or Hashcode + Mark Pollack (.NET) + $Id $ + + + + Implementation of the custom configuration parser for database definitions. + + Mark Pollack + $Id: DatabaseNamespaceParser.cs,v 1.3 2007/08/08 00:34:33 bbaia Exp $ + + + + Initializes a new instance of the class. + + + + + Parse the specified element and register any resulting + IObjectDefinitions with the IObjectDefinitionRegistry that is + embedded in the supplied ParserContext. + + The element to be parsed into one or more IObjectDefinitions + The object encapsulating the current state of the parsing + process. + + The primary IObjectDefinition (can be null as explained above) + + + Implementations should return the primary IObjectDefinition + that results from the parse phase if they wish to used nested + inside (for example) a <property> tag. + Implementations may return null if they will not + be used in a nested scenario. + + + + + + Gets the name of the object type for the specified element. This has already been aliased + in the static constructor. + + The element. + The name of the object type. + + + + Miscellaneous utility methods for DAO implementations. + Useful with any data access technology. + + Mark Pollack (.NET) + $Id: DataAccessUtils.cs,v 1.3 2006/12/02 07:35:23 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Generic exception thrown when the current process was + a deadlock loser, and its transaction rolled back. + + Rod Johnson + Griffin Caprio (.NET) + $Id: DeadlockLoserDataAccessException.cs,v 1.6 2008/04/08 20:26:43 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Exception thrown if certain expected data could not be retrieved, e.g. + when looking up specific data via a known identifier. + + +

    + This exception will be thrown either by O/R mapping tools or by custom DAO + implementations. +

    +
    + Juergen Hoeller + Griffin Caprio (.NET) + $Id: DataRetrievalFailureException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Callback delegate for performing actions within a transactional context. + + +

    To be used with 's Execute + methods. +

    +

    + Typically used to gather various calls to transaction-unaware low-level + services into a higher-level method implementation with transaction + demarcation. +

    +
    + The status of the transaction, can be used to + trigger a rollback the current transaction by settings its + RollbackOnly property to true. + A result object or null. +
    + + + Interface used by + to source transaction attributes. + + +

    + Implementations know how to source transaction attributes, whether from configuration, + metadata attributes at source level, or anywhere else. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: ITransactionAttributeSource.cs,v 1.7 2006/05/18 21:37:51 markpollack Exp $ +
    + + + Return the for this + method. + + The method to check. + + The target . May be null, in which case the declaring + class of the supplied must be used. + + + A or + null if the method is non-transactional. + + + + + Exception that represents a transaction failure caused by heuristics. + + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + $Id: HeuristicCompletionException.cs,v 1.6 2006/05/18 21:37:51 markpollack Exp $ + + + + The outcome state of the transaction: have some or all resources been committed? + + + + + Returns a representation of the supplied + . + + + The value that + is to be stringified. + + A representation. + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class + with the specified + and inner exception. + + + The + for the transaction. + + + The inner exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Override of + to allow for private serialization. + + + The + that holds the serialized object data about the exception. + + + The + that contains contextual information about the source or destination. + + + + + Returns the transaction's outcome state. + + + + + Using reflection on VS.NET 2005 a generated typed dataset, apply the + connection/transaction pair associated with the current Spring based + transaction scope. + + This avoids the limitations of using System.Transaction + based transaction scope for multiple operation on typed datasets withone one + transaction. Reflection was used rather than partial classes to provide + a general solution that can be written and applied once. + Usage within a DAO method, FindAll() is shown below. Note: Convenience methods + to simplify calling syntax maybe provided in the future, it is a trade off on type + safety calling the typed adapter defined outside the anonymous method as + compared to casting inside a "DoInDbAdapter(object dbAdapter) method. + + public PrintGroupMappingDataSet FindAll() + { + + PrintGroupMappingTableAdapter adapter = new PrintGroupMappingTableAdapter(); + PrintGroupMappingDataSet printGroupMappingDataSet = new PrintGroupMappingDataSet(); + + + printGroupMappingDataSet = AdoTemplate.Execute(delegate(IDbCommand command) + { + TypedDataSetUtils.ApplyConnectionAndTx(adapter, command); + adapter.Fill(printGroupMappingDataSet.PrintGroupMapping); + return printGroupMappingDataSet; + }) + as PrintGroupMappingDataSet; + + return printGroupMappingDataSet; + } + + See http://www.code-magazine.com/articleprint.aspx?quickid=0605031 for a + more complete discussion. + + + $Id: + + + + Applies the connection and tx to the provided typed dataset. The connection and transaction + used are taken from the Spring's current transactional context. See ConnectionUtils.GetConnectionTxPair + for more information + + The typed data set adapter. + The db provider. + + + + Applies the connection and tx to the provided typed dataset. + + Generated dataset to not inherit from a common base + class so that we must pass in the generic object type. + The typed data set adapter. + The IDbCommand managed by Spring and set with + the connection/transaction of the current Spring transaction scope. + + + + Connection holder, wrapping a ADO.NET connection and transaction. + + AdoTransactionManager binds instances of this class to the + thread for a given DbProvider. + Jurgen Hoeller + Mark Pollack (.NET) + + + + Convenient base class for resource holders. + + +

    + Features rollback-only support transactions. Can expire after a certain number of + seconds or milliseconds, to determine transactional timeouts. +

    +
    + Juergen Hoeller + Griffin Caprio (.NET) + $Id: ResourceHolderSupport.cs,v 1.9 2007/01/30 20:12:29 markpollack Exp $ +
    + + + Clear the transaction state of this resource holder. + + + + + Increase the reference count by one because the holder has been requested. + + + + + Decrease the reference count by one because the holder has been released. + + + + + Mark the resource as synchronized with a transaction. + + + + + Get or set whether the resource is synchronized with a transaction. + + true if synchronized; otherwise, false. + + + + Return the expiration deadline of this object. + + + + + Return whether this object has an associated timeout. + + + + + Return the time to live for this object in seconds. + + +

    + Rounds up eagerly, e.g. '9.00001' to '10'. +

    +
    + + If no deadline has been set. + +
    + + + Return the time to live for this object in milliseconds. + + + If no deadline has been set. + + + + + Sets the timeout for this object in milliseconds. + + Number of milliseconds until expiration. + + + + Sets the timeout for this object in seconds. + + Number of seconds until expiration. + + + + Return wheterh there are still open references to this holder + + + + + Create a new ConnectionHolder + + The connection to hold + The transaction to hold + + + + Summary description for AdoUtils. + + + + + Order value for TransactionSynchronization objects that clean up + ADO.NET Connections. + + + + + Property dispose of the command. Useful in finally or catch blocks. + + command to dispose + + + + Lifecycle callback methods that can be registered when + performing Fill operations with AdoTemplate. + + +

    + The methods let you set various properties and invoke + methods on the DataSet before and after it gets filled by a DataAdapter. + For example, EnforceConstraints, BeginLoadData, and EndLoadData + can be called to optimize the loading of large DataSets with + many related tables. +

    +

    Vendors may expose other propriety methods on their DataSet + implementation, downcast to access this specific functionality. +

    +
    + Mark Pollack (.NET) + $Id: IDataSetFillLifecycleProcessor.cs,v 1.3 2006/11/29 07:18:45 markpollack Exp $ +
    + + + Called before a DataAdapter is used to fill a DataSet + the the provided tablename. + + The DataSet to be filled with a DataTable + The table collection to be filled + + + + Called after a DataAdapter is used to fill a DataSet + the the provided tablename. + + The DataSet to be filled with a DataTable + The table collection to be filled + + + + TransactionManager that uses TransactionScope provided by System.Transactions. + + Mark Pollack (.NET) + $Id: TxScopeTransactionManager.cs,v 1.5 2007/12/04 19:43:16 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + This is indented only for unit testing purposes and should not be + called by production application code. + The tx adapter. + + + + No-op initialization + + + + + The transaction resource object that encapsulates the state and functionality + contained in TransactionScope and Transaction.Current via the ITransactionScopeAdapter + property. + + + + + Initializes a new instance of the class. + Will create an instance of . + + + + + Gets or sets the transaction scope adapter. + + The transaction scope adapter. + + + + Return whether the transaction is internally marked as rollback-only. + + + True of the transaction is marked as rollback-only. + + + + Create DbProviders based on configuration information in assembly resource + dbproviders.xml + + + + + TODO: Provide over-ride resource location. + TODO: Add error codes to provider. + + Mark Pollack (.NET) + $Id: DbProviderFactory.cs,v 1.12 2007/08/16 20:38:43 markpollack Exp $ + + + + The shared log instance for this class (and derived classes). + + + + + Initializes a new instance of the class. + + + + + Gets the DbProvider given an identifying name. + + + Familiar names for the .NET 2.0 provider model are supported, i.e. + System.Data.SqlClient. Refer to the documentation for a complete + listing of supported DbProviders and their names. You may + also use the method GetDbProviderClasses or obtain the + underlying IApplicationContext to progammatically obtain information + about supported providers. + + Name of the provider invariant. + + + + + Gets the application context that contains the definitions of the + various providers. + + This method should rarely, if ever, be used in + application code. It is used by the framework itself + to map other data access products abstractions for a 'DbProvider/DataSource' + onto Spring's model. + The application context. + + + + Data access exception thrown when something unintended appears to have + happened with an update, but the transaction hasn't already been rolled back. + + +

    + Thrown, for example, when we wanted to update 1 row in an RDBMS but actually + updated 3. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: IncorrectUpdateSemanticsDataAccessException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + Return whether or not data was updated. + + True if data was updated (as opposed to being incorrectly + updated). If this property returns false, there's nothing to roll back. + + + + + Data access exception thrown when a result was not of the expected size, + for example when expecting a single row but getting 0 or more than 1 rows. + + Juergen Hoeller + Griffin Caprio (.NET) + $Id: IncorrectResultSizeDataAccessException.cs,v 1.6 2007/11/20 04:03:48 markpollack Exp $ + + + + Exception thrown on incorrect usage of the API, such as failing to "compile" a query + object that needed compilation before execution. + + +

    + This represents a problem in our data access framework, not the underlying data access + infrastructure. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: InvalidDataAccessApiUsageException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + The expected result size. + The actual result size (or -1 if unknown). + + + + Creates a new instance of the + class. + > + + A message about the exception. + + The expected result size. + The actual result size (or -1 if unknown). + + + + Initializes a new instance of the class. + + A message about the exception + The expected result size. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Override of + to allow for private serialization. + + + The + that holds the serialized object data about the exception. + + + The + that contains contextual information about the source or destination. + + + + Return the expected result size. + + + Return the actual result size (or -1 if unknown). + + + + Enumeration describing Spring.NET's + transaction propagation settings. + + Griffin Caprio (.NET) + $Id: TransactionPropagation.cs,v 1.7 2007/05/29 20:00:33 markpollack Exp $ + + + + Support a current transaction, create a new one if none exists. + + +

    + Analogous to System.EnterpriseServices.TransactionOption value of the same name. + This is typically the default setting of a transaction definition. +

    +
    +
    + + + Support a current transaction, execute non-transactionally if none exists. + + +

    + Analogous to System.EnterpriseServices.TransactionOption.Supported. +

    +
    +
    + + + Support a current transaction, throw an exception if none exists. + + +

    + No corresponding System.EnterpriseServices.TransactionOption value. +

    +
    +
    + + + Create a new transaction, suspending the current transaction if one exists. + + +

    + Analogous to System.EnterpriseServices.TransactionOption value of the same name. +

    +
    +
    + + + Execute non-transactionally, suspending the current transaction if one exists. + + +

    + Analogous to System.EnterpriseServices.TransactionOption value of the same name. +

    +
    +
    + + + Execute non-transactionally, throw an exception if a transaction exists. + + +

    + Analogous to System.EnterpriseServices.TransactionOption.Disabled. +

    +
    +
    + + + Execute within a nested transaction if a current transaction exists, else + behave like . + + +

    + There is no analogous feature in TransactionOption. +

    +
    +
    + + + Callback interface for transactional code. + + +

    To be used with 's Execute + methods. +

    +

    + Typically used to gather various calls to transaction-unaware low-level + services into a higher-level method implementation with transaction + demarcation. +

    +
    + Juergen Hoeller + Mark Pollack (.NET) + $Id: ITransactionCallback.cs,v 1.4 2007/08/01 23:32:39 markpollack Exp $ +
    + + + Gets called by TransactionTemplate.Execute within a + transaction context. + + The associated transaction status. + A result object or null. + + + + Exception thrown when we can't classify a SQLException into + one of our generic data access exceptions. + + Mark Pollack (.NET) + $Id: UncategorizedAdoException.cs,v 1.4 2006/11/29 07:18:46 markpollack Exp $ + + + + Normal superclass when we can't distinguish anything more specific + than "something went wrong with the underlying resource": for example, + a SQLException from Sql Server that we can't pinpoint more precisely. + + Rod Johnson + Griffin Caprio (.NET) + $Id: UncategorizedDataAccessException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + SQL that led to the problem + + + + + The error code, if available, that was unable to be categorized. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + A message about the exception. + + + + Initializes a new instance of the class. + + A message about the exception. + The inner exception. + + + + Initializes a new instance of the class. + + name of the current task. + the underlying error code that could not be translated + The offending SQL statment + The root cause. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + When overridden in a derived class, sets the + with information about the exception. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is a null reference ( in Visual Basic). + + + + A data reader implementation that mapps DBNull values to sensible defaults. + + IDataRecord methods are virtual. + Mark Pollack (.NET) + $Id: NullMappingDataReader.cs,v 1.2 2006/10/08 04:56:06 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The data reader to delegate operations to. + + + + Gets the 32-bit signed integer value of the specified field. + + The index of the field to find. + + The 32-bit signed integer value of the specified field or 0 if null + + The index passed was outside the range of 0 through . + + + + Return the value of the specified field, null if value equals DBNull.Value + + The index of the field to find. + + The which will contain the field value upon return. + Returns null if value equals DBNull.Value + + + The index passed was outside the range of 0 through . + + + + + Return whether the specified field is set to null. + + The index of the field to find. + + if the specified field is set to null, otherwise + . + + The index passed was outside the range of 0 through . + + + + Gets the with the specified name. + Returns null if value equals DBNull.Value + + + + + + Gets the with the specified i. + Returns null if value equals DBNull.Value + + Return the object or null if the value equals DBNull.Value + + + + Encapsulate Command.ExecuteNonQuery operations within a reusable class. + + Mark Pollack (.NET) + The default CommandType is CommandType.Text + $Id: AdoNonQuery.cs,v 1.3 2007/07/25 08:25:20 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + + + Generic callback interface for code that operates on a + DbCommand. + + The return type from executing the + callback + +

    Allows you to execute any number of operations + on a single DbCommand, for example a single ExecuteScalar + call or repeated execute calls with varying parameters. +

    +

    Used internally by AdoTemplate, but also useful for + application code. Note that the passed in DbCommand + has been created by the framework and will have its + Connection property set and the Transaction property + set based on the transaction context.

    +
    + Mark Pollack +
    + + + Called by AdoTemplate.Execute with an active ADO.NET IDbCommand. + The calling code does not need to care about closing the + command or the connection, or + about handling transactions: this will all be handled by + Spring's AdoTemplate + + An active IDbCommand instance + The result object + + + + A "AdoOperation" is a thread-safe, reusable object representing + an ADO Query, "NonQuery" (Create, Update, Delete), stored procedure + or DataSet manipualtions. + + + Subclasses should set Command Text and add parameters before invoking + Compile(). The order in which parameters are added is generally + significant when using providers or functionality that do not use + named parameters. + + + Mark Pollack (.NET) + $Id: AdoOperation.cs,v 1.2 2007/07/25 13:32:39 oakinger Exp $ + + + + Initializes a new instance of the class. + + A DbProvider, SQL and any parameters must be supplied + before invoking the compile method and using this object. + + + + + Initializes a new instance of the class. + + A DbProvider, SQL and any parameters must be supplied + before invoking the compile method and using this object. + + Database provider to use. + + + + Initializes a new instance of the class. + + A DbProvider, SQL and any parameters must be supplied + before invoking the compile method and using this object. + + Database provider to use. + SQL query or stored procedure name. + + + + Compiles this operation. Ignores subsequent attempts to compile. + + + + + Gets or sets the ADO template. + + The ADO template. + + + + Gets or sets the db provider. + + The db provider. + + + + Sets the command timeout for IDbCommands that this AdoTemplate executes. + + Default is 0, indicating to use the database provider's default. + Any timeout specified here will be overridden by the remaining + transaction timeout when executing within a transaction that has a + timeout specified at the transaction level. + + The command timeout. + + + + Fatal exception thrown when we can't connect to an RDBMS using ADO.NET + + Rod Johnson + Mark Pollack (.NET) + $Id: CannotGetAdoConnectionException.cs,v 1.1 2006/11/24 05:57:46 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + A message about the exception. + + + + Initializes a new instance of the class. + + A message about the exception. + The inner exception. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Assists in creating a collection of parameters by keeping track of all + IDbParameters its creates. After creating a number of parameters ask for the collection + with the GetParameters methods. + + This builder is stateful, you must create a new one for each collection + of parameters you would like to create. + Mark Pollack (.NET) + $Id: DbParametersBuilder.cs,v 1.2 2007/05/01 00:37:10 markpollack Exp $ + + + + A builder to create a collection of ADO.NET parameters. + + The chaining of IDbParameter methods does the + building of the parameter details while the builder class + itself keeps track of the collection. + + Mark Pollack (.NET) + $Id: IDbParametersBuilder.cs,v 1.1 2006/12/02 07:38:15 markpollack Exp $ + + + + Creates a IDbParameter object and adds it to an internal collection for + later retrieval via the GetParameters method. + + + + + + Gets all the parameters created and configured via the Create method. + + An instance of IDbParameters + + + + The shared log instance for this class (and derived classes). + + + + + Initializes a new instance of the class. + + + + + Advisor driven by a , used to exclude + a from methods that + are non-transactional. + + +

    + Because the AOP framework caches advice calculations, this is normally + faster than just letting the + run and find out itself that it has no work to do. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: TransactionAttributeSourceAdvisor.cs,v 1.11 2007/12/07 08:10:03 markpollack Exp $ +
    + + + Initializes a new instance of the class. + + +

    + This is an abstract class, and as such has no publicly + visible constructors. +

    +
    +
    + + + Creates a new instance of the + class. + + + The pre-configured transaction interceptor. + + + + + Tests the input method to see if it's covered by the advisor. + + The method to match. + The to match against. + + True if the supplied is covered by the advisor. + + + + + Sets the tx attribute source. + + The transaction interceptor. + + + + Sets the transaction interceptor. + + The transaction interceptor. + + + + Callback to process each row of data in a result set to an object. + + Implementations of this interface perform the actual work + of mapping rows, but don't need worry about managing + ADO.NET resources, such as closing the reader. +

    + Typically used for AdoTemplate's query methods (with RowMapperResultSetExtractor + adapters). RowMapper objects are typically stateless and thus + reusable; they are ideal choices for implementing row-mapping logic in a single + place. +

    +

    Alternatively, consider subclassing MappingSqlQuery from the Spring.Data.Object + namespace: Instead of working with separate AdoTemplate and RowMapper objects, + you can have executable query objects (containing row-mapping logic) there. +

    +
    + Mark Pollack (.NET) + $Id: IRowMapper.cs,v 1.6 2007/06/28 18:48:19 markpollack Exp $ +
    + + + Implementations must implement this method to map each row of data in the + result set (DataReader). This method should not call Next() on the + DataReader; it should only extract the values of the current row. + + The IDataReader to map (pre-initialized for the current row) + The number of the current row. + The result object for the current row. + + + + Called by the AdoTemplate class to allow implementations + to set any necessary parameters on the command object. + The CommandType and CommandText will have already been supplied. + + Mark Pollack (.NET) + $Id: ICommandSetter.cs,v 1.5 2006/12/02 07:35:23 markpollack Exp $ + + + + Provides a name to a ResultSetProcessor for use with AdoOperation subclasses + such as StoredProcedure. + + Mark Pollack (.NET) + $Id: NamedResultSetProcessor.cs,v 1.2 2007/07/25 13:32:31 oakinger Exp $ + + + + Initializes a new instance of the class with a + IRowCallback instance + + The name of the associated row callback for use with output + result set mappers. + A row callback instance. + + + + Initializes a new instance of the class with a + IRowMapper instance + + The name of the associated row mapper. + A IRowMapper instance. + + + + Initializes a new instance of the class with a + IResultSetExtractor instance + + The name of the associated result set extractor. + A IResultSetExtractor instance. + + + + Exception to be thrown when a transaction has timed out. + + + + + Create a new instance + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + NamespaceParser allowing for the configuration of + declarative transaction management using either XML or using attributes. + + This namespace handler is the central piece of functionality in the + Spring transaction management facilities and offers two appraoches + to declaratively manage transactions. + + One approach uses transaction semantics defined in XML using the + <tx:advice> elements, the other uses attributes + in combination with the <tx:annotation-driven> element. + Both approached are detailed in the Spring reference manual. + + + + + Register the for the 'advice' and + 'attribute-driven' tags. + + + + + The for the <tx:advice> tag. + + Rob Harrop + Juergen Hoeller + Adrian Colyer + Mark Pollack (.NET) + $Id: TxAdviceObjectDefinitionParser.cs,v 1.2 2007/06/18 10:31:19 bbaia Exp $ + + + + A superclass for object based abstractions of RDBMS stored procedures. + + Mark Pollack (.NET) + $Id: StoredProcedure.cs,v 1.9 2007/07/25 08:25:20 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Execute the stored procedure using 'ExecuteScalar' + + Value of input parameters. + Dictionary with any named output parameters and the value of the + scalar under the key "scalar". + + + + Reusable query in which concrete subclasses must implement the + MapRow method to map each row of a single result set into an + object. + + + Simplifies MappingAdoQueryWithContext API by dropping parameters and + context. Most subclasses won't care about parameters. If you don't use + contextual information, subclass this instead of MappingSqlQueryWithContext. + + Mark Pollack (.NET) + $Id: MappingAdoQuery.cs,v 1.4 2007/07/17 04:33:44 markpollack Exp $ + + + + Reusable query in which concrete subclasses must implement the + MapRow method to map each row of a single result set into an + object. + + The MapRow method is also passed the input parameters and + a dictionary to provide the method with calling context information + (if necessary). If you do not need these additional parameters, use + the subclass MappingAdoQuery. + + Mark Pollack (.NET) + $Id: MappingAdoQueryWithContext.cs,v 1.3 2007/07/17 04:33:44 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + + + Callback to process each row of data in an AdoOperation's Query method. + + + Implementations of this interface perform the actual work of extracting + results. In contrast to a IResultSetExtractor, a IRowCallback object is typically + stateful. It keeps the result state within the object, to be available + for later inspection. + + + + + + Implementations must implement this method to process each row of data + in the data reader. + + + This method should not advance the cursor by calling Read() + on IDataReader but only extract the current values. The + caller does not need to care about closing the reader, command, connection, or + about handling transactions: this will all be handled by + Spring's AdoTemplate + + An active IDataReader instance + The result object + + + + Convenient super class for ADO.NET data access objects. + + + Requires a IDBProvider to be set, providing a + AdoTemplate based on it to subclasses. + This base class is mainly intended for AdoTemplate usage. + + + + + Create a AdoTemplate for a given DbProvider + Only invoked if populating the DAO with a DbProvider reference. + + + Can be overriden in subclasses to provide AdoTemplate instances + with a different configuration, or a cusotm AdoTemplate subclass. + + The DbProvider to create a AdoTemplate for + + + + Convenience method to create a parameters builder. + + Virtual for sublcasses to override with custom + implementation. + A new DbParameterBuilder + + + + The DbProvider instance used by this DAO + + + + + Set the AdoTemplate for this DAO explicity, as + an alternative to specifying a IDbProvider + + + + + Implemenation of of DbProvider that uses metadata to create provider specific ADO.NET objects. + + $Id: + + + + Initializes a new instance of the class. + + The db metadata. + + + + Returns a new command object for executing SQL statments/Stored Procedures + against the database. + + An new + + + + Returns a new instance of the providers CommandBuilder class. + + A new Command Builder + In .NET 1.1 there was no common base class or interface + for command builders, hence the return signature is object to + be portable (but more loosely typed) across .NET 1.1/2.0 + + + + Returns a new connection object to communicate with the database. + + A new + + + + Returns a new adapter objects for use with offline DataSets. + + A new + + + + Returns a new parameter object for binding values to parameter + placeholders in SQL statements or Stored Procedure variables. + + A new + + + + Creates the name of the parameter in the format appropriate to use inside IDbCommand.CommandText. + + The unformatted name of the parameter. + + The parameter name formatted foran IDbCommand.CommandText. + + In most cases this adds the parameter prefix to the name passed into this method. + + + + Creates the name ofthe parameter in the format appropriate for an IDataParameter, i.e. to be + part of a IDataParameterCollection. + + The unformatted name of the parameter. + + The parameter name formatted for an IDataParameter + + + + + Extracts the provider specific error code as a string. + + The data access exception. + The provider specific error code + + + + Determines whether the provided exception is in fact related + to database access. This can be provider dependent in .NET 1.1 since + there isn't a common base class for ADO.NET exceptions. + + The exception thrown when performing data access + operations. + + true if is a valid data access exception for the specified + exception; otherwise, false. + + + + + Determines whether is data access exception in .NET 1.1 for the specified exception. + + The candidate exception. + + true if is data access exception in .NET 1.1 for the specified exception; otherwise, false. + + + + + Return metadata information about the database provider + + + + + + Connection string used to create connections. + + + + + + Exception thrown when an attempt to insert or update data + results in violation of an integrity constraint. + + +

    + Note that this is not purely a relational concept; unique primary keys are + required by most database types. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: DataIntegrityViolationException.cs,v 1.5 2006/05/18 21:37:50 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Represents a transaction's current state. + + Griffin Caprio (.NET) + $Id: TransactionOutcomeState.cs,v 1.4 2006/05/18 21:37:51 markpollack Exp $ + + + + The transaction state is unknown. + + + + + The transaction has been committed. + + + + + The transaction has been rolled back. + + + + + The transaction is in an unknown, mixed state. + + + + + Enumeration containing the state of transaction synchronization. + + Griffin Caprio (.NET) + $Id: TransactionSynchronizationState.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Always activate transaction synchronization, even for "empty" transactions + that result from . + + with no existing backend transaction. + + + + Activate transaction synchronization only for actual transactions, + i.e. not for empty ones that result from . + + with no + existing backend transaction. + + + + Never active transaction synchronization. + + + + + Simple convenience class for TransactionCallback implementation. + Allows for implementing a DoInTransaction version without result, + i.e. without the need for a return statement. + + Mark Pollack (.NET) + $Id: TransactionCallbackWithoutResult.cs,v 1.3 2006/06/21 14:07:30 markpollack Exp $ + + + + Gets called by TransactionTemplate.execute within a transactional context + when no return value is required. + + The status. + + Does not need to care about transactions itself, although it can retrieve + and influence the status of the current transaction via the given status + object, e.g. setting rollback-only. + A RuntimeException thrown by the callback is treated as application + exception that enforces a rollback. An exception gets propagated to the + caller of the template. + + + + + Gets called by TransactionTemplate.Execute within a + transaction context. + + The associated transaction status. + a result object or null + + + + Exception thrown when a transaction can't be created using an + underlying transaction API such as COM+. + + Rod Johnson + Griffin Caprio (.NET) + $Id: CannotCreateTransactionException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Provides the necessary transactional state and operations for ServiceDomainTransactionManager to + work with ServiceDomain and ContextUtil. + + Introduced for purposes of unit testing. + Mark Pollack (.NET) + $Id $ + + + + Creates the context specified by the ServiceConfig object and pushes it onto the context stack to + become the current context. + + A ServiceConfig that contains the configuration information for the services to be used within the enclosed code. + + + + Leaves this ServiceDomain. The current context is then popped from the context stack, and the + context that was running when Enter was called becomes the current context. + + One of the TransactionStatus values + + + + Sets the consistent bit and the done bit to true in the COM+ context + + + + + Sets the consistent bit to false and the done bit to true in the COM+ context. + + + + + Gets or sets the consistent bit in the COM+ context. + + My transaction vote. + + + + Gets a value indicating whether the current context is transactional. + + + true if this instance is existing transaction; otherwise, false. + + + + + This interface creates a IDbDataAdapterCommand. + Implementations are responsible + for configuring the created command with appropriate + select and actions commands along with their parameters. + + + Generally used to to support the DataSet functionality in + the Spring.Data.Objects namespace. + + Mark Pollack (.NET) + $Id: IDbDataAdapterCreator.cs,v 1.3 2007/12/07 08:09:52 markpollack Exp $ + + + + Creates the data adapter. + + A new IDbDataAdapter instance + + + + Helper class that can efficiently create multiple IDbCommand + objects with different parameters based on a SQL statement and a single + set of parameter declarations. + + Mark Pollack (.NET) + $Id: IDbCommandCreatorFactory.cs,v 1.11 2007/08/06 20:13:39 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + One of the central callback interfaces used by the AdoTemplate class + This interface creates a IDbCommand. Implementations are responsible + for configuring the created command with command type, command text and + any necessary parameters. + + Mark Pollack (.NET) + $Id: IDbCommandCreator.cs,v 1.6 2007/08/06 20:13:39 markpollack Exp $ + + + + Creates a IDbCommand. + + The IDbProvider reference that can be used to create the command in a + provider independent manner. The provider supplied is the same as used in configuration of AdoTemplate. + + + + + Generic callback to process each row of data in a result set to an object. + + Implementations of this interface perform the actual work + of mapping rows, but don't need worry about managing + ADO.NET resources, such as closing the reader. +

    + Typically used for AdoTemplate's query methods (with RowMapperResultSetExtractor + adapters). RowMapper objects are typically stateless and thus + reusable; they are ideal choices for implementing row-mapping logic in a single + place. +

    +

    Alternatively, consider subclassing MappingSqlQuery from the Spring.Data.Object + namespace: Instead of working with separate AdoTemplate and RowMapper objects, + you can have executable query objects (containing row-mapping logic) there. +

    +
    + Mark Pollack (.NET) + $Id: IRowMapper.cs,v 1.4 2007/06/28 18:48:18 markpollack Exp $ +
    + + + Implementations must implement this method to map each row of data in the + result set (DataReader). This method should not call Next() on the + DataReader; it should only extract the values of the current row. + + The IDataReader to map (pre-initialized to the current row) + The number of the current row.. + The specific typed result object for the current row. + + + + Callback interface to process all results sets and rows in + an AdoTemplate query method. + + Implementations of this interface perform the work + of extracting results but don't need worry about managing + ADO.NET resources, such as closing the reader. +

    + This interface is mainly used within the ADO.NET + framework. An IResultSetExtractor is usually a simpler choice + for result set (DataReader) processing, in particular a + RowMapperResultSetExtractor in combination with a IRowMapper. +

    + Note: in contracts to a IRowCallbackHandler, a ResultSetExtractor + is usually stateless and thus reusable, as long as it doesn't access + stateful resources or keep result state within the object. + +
    + +
    + + + Implementations must implement this method to process all + result set and rows in the IDataReader. + + The IDataReader to extract data from. + Implementations should not close this: it will be closed + by the AdoTemplate. + An arbitrary result object or null if none. The + extractor will typically be stateful in the latter case. + + + + Generic callback delegate for code that operates on a IDbCommand. + + +

    Allows you to execute any number of operations + on a single IDbCommand, for example a single ExecuteScalar + call or repeated execute calls with varying parameters. +

    +

    Used internally by AdoTemplate, but also useful for + application code. Note that the passed in DbCommand + has been created by the framework and will have its + Connection property set and the Transaction property + set based on the transaction context.

    +
    + The return type from executing the + callback + The ADO.NET DbCommand object + The object returned from processing with the + provided DbCommand + Mark Pollack + $Id: IDbCommandDelegate.cs,v 1.1 2007/06/27 21:41:57 markpollack Exp $ +
    + + + Exception thrown when a general transaction system error is encountered, + for instance on commit or rollback. + + Juergen Hoeller + Griffin Caprio (.NET) + $Id: TransactionSystemException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Simple implementation of the + interface that allows attributes to be stored per method in a map. + + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + $Id: MethodMapTransactionAttributeSource.cs,v 1.14 2008/05/27 19:06:57 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Add an attribute for a transactional method. + + + Method names can end or start with "*" for matching multiple methods. + + The class and method name, separated by a dot. + The attribute to be associated with the method. + + + + Add an attribute for a transactional method. + + + Method names can end or start with "*" for matching multiple methods. + + The target interface or class. + The mapped method name. + + The attribute to be associated with the method. + + + + + Add an attribute for a transactional method. + + The transactional method. + + The attribute to be associated with the method. + + + + + Does the supplied match the supplied ? + + + The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, + as well as direct equality. This behaviour can (of course) be overridden in + derived classes. + + The method name of the class. + The name in the descriptor. + True if the names match. + + + + Return the for this + method. + + The method to check. + + The target . May be null, in which case the declaring + class of the supplied must be used. + + + A or + null if the method is non-transactional. + + + + + Set a name/attribute map, consisting of "FQCN.method, AssemblyName" method names + (e.g. "MyNameSpace.MyClass.MyMethod, MyAssembly") and ITransactionAttribute + instances (or Strings to be converted to instances). + + +

    + The key values of the supplied + must adhere to the following form:
    + FQCN.methodName, Assembly. +

    + + MyNameSpace.MyClass.MyMethod, MyAssembly + +
    +
    + + + Encapsulate Command ExecuteScalar operations within a reusable class. + + The default CommandType is CommandType.Text + Mark Pollack (.NET) + $Id: AdoScalar.cs,v 1.3 2007/07/25 08:25:20 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + + + Exception thrown when a result set has been accessed in an invalid fashion. + + Mark Pollack (.NET) + $Id: InvalidResultSetAccessException.cs,v 1.2 2007/12/07 08:09:52 markpollack Exp $ + + + + SQL that led to the problem + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + A message about the exception. + + + + Initializes a new instance of the class. + + A message about the exception. + The inner exception. + + + + Initializes a new instance of the class. + + name of the current task. + The offending SQL statment + The root cause. + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + When overridden in a derived class, sets the + with information about the exception. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is a null reference ( in Visual Basic). + + + + Gets the SQL that caused the exception + + The SQL that caused the exception. + + + + An adapter for a target IDbProvider, applying the specified user credentials + to the connection string for every GetConnection call. + + + + + Sets the user credentials for current thread. The given username and password + strings will be added to the connection string for all subsequent GetConnection + requests. This will override any statically specified user credentials, that is, + set by the properties Username nad Password. + + The username part of the connection string. + The password part of the connection string. + + + + Removes the user credentials from current thread. Use statically specified + credentials afterwards. + + + + + Returns a new connection object to communicate with the database. + Determine if there are currently thread-bound credentials, using them if + available, falling back to the statically specified username and password + (i.e. values of the properties 'Username' and 'Password') otherwise. + The username and password will be concatenated on the connection string + using string in the Separator property + + A new + + + + Sets the username string that will be appended to the connection string. + + The username. + + + + Sets the password string that will be appended to the connection string. + + The password. + + + + Sets the separator used to separate elements of the connection string. Default is ';'. + + + The separator used to separate elements in the connection string. + + + + A parameter interface used by + + Mark Pollack (.NET) + $Id: IDbParameter.cs,v 1.6 2006/12/02 07:35:23 markpollack Exp $ + + + + Holds ADO.NET error codes for a particular provider. + + + Used by ErrorCodeExceptionTranslator. The embedded resource + "dbProviders.xml" in Spring.Data.Common contains default + ErrorCodes instances for various providers. + + Mark Pollack (.NET) + $Id: ErrorCodes.cs,v 1.1 2007/08/01 18:53:06 markpollack Exp $ + + + + The shared log instance for this class (and derived classes). + + + + + Initializes a new instance of the class. + + + + + .NET Attribute for describing transactional behavior of methods in a class. + + This attribute type is generally directly comparable + to Spring's class and + in fact will + directly convert the data to a + so that Spring's transaction support code does not have to know about + attributes. If no rules are relevant to the exception it will be treaded + like DefaultTransactionAttribute, (rolling back on all exceptions). + + The default property values are TransactionPropagation.Required, IsolationLevel.ReadCommitted, + DefaultTransactionDefinition.TIMEOUT_DEFAULT (can be changed, by default is the default + value of the underlying transaction subsystem) + ReadOnly = false, and Type.EmtptyTypes specified for Rollbackfor and NoRollbackFor exception + types. + + + Mark Pollack (.NET) + $Id: TransactionAttribute.cs,v 1.8 2007/12/07 07:30:48 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The transaction propagation. + + + + Initializes a new instance of the class. + + The transaction propagation. + The isolation level. + + + + Initializes a new instance of the class. + + The isolation level. + + + + Gets the transaction propagation. + + Defaults to TransactionPropagation.Required + The transaction propagation. + + + + Gets the isolation level. + + Defaults to IsolationLevel.Unspecified + The isolation level. + + + + Gets or sets the timeout. + + Defaults to the default timeout of the underlying transaction system. + The timeout. + + + + Gets or sets a value indicating whether the transaction is readonly. + + Defaults to false + true if read-only; otherwise, false. + + + + Gets or sets the zero or more exception types which + indicating which exception types must cause a transaction + rollback. + + This is the preferred way to construct a rollback rule, + matching the exception class and subclasses. + The rollback types. + + + + Gets or sets zero or more exceptions types which + indicationg which exception type must not + cause a transaction rollback. + + This is the preferred way to construct a rollback rule, + matching the exception type. + The no rollback for. + + + + Gets or sets the enterprise services interop option. + + The enterprise services interop option. + + + + Exception thrown when the existence or non-existence of a transaction + amounts to an illegal state according to the transaction propagation + behavior that applies. + + Juergen Hoeller + Griffin Caprio (.NET) + $Id: IllegalTransactionStateException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + A simple holder for the current Connection/Transaction objects + to use within a given AdoTemplate Execute operation. Used internally. + + + + + Initializes a new instance of the class. + + The connection. + The transaction. + + + + Gets the connection. + + The connection. + + + + Gets the transaction. + + The transaction. + + + + Reusable query in which concrete subclasses must implement the + abstract MapRow method to map each row of a single result set into an + object. + + The MapRow method is also passed the input parameters and + a dictionary to provide the method with calling context information + (if necessary). If you do not need these additional parameters, use + the subclass MappingAdoQuery. + + Mark Pollack (.NET) + $Id: MappingAdoQueryWithContext.cs,v 1.2 2007/07/25 13:32:39 oakinger Exp $ + + + + Place together the mapping logic to a plain .NET object + for one result set within the same class. + + Mark Pollack (.NET) + $Id: AdoQuery.cs,v 1.3 2007/07/25 13:32:39 oakinger Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The database provider. + The SQL to execute + + + + Convenient method to execute query without parameters or calling context. + + The type parameter for the returned collection + List of mapped objects + + + + Convenient method to execute query with parameters but without calling context. + + The type parameter for the returned collection + The parameters for the query. + + + + + Convenient method to execute query with parameters and access to output parameter values. + + The type parameter for the returned collection + The parameters for the query. + The returned output parameters. + + + + + Central execution method. All named parameter execution goes through this method. + + the type parameter for the returned collection + The in params. + The out params. + The calling context. + + + + + Initializes a new instance of the class. + + + + + A implementation that + creates instances of the class. + + Typically used as a convenience for retrieving shared + in a Spring XML configuration file as compared to + using explict factory method support. + + + + + Creates a new instance of the class. + + + + + Return an instance of and IDbProvider as configured by this factory + managed by this factory. + + + The same (singleton) instance of the IDbProvider managed by + this factory. + + + + If this method is being called in the context of an enclosing IoC container and + returns , the IoC container will consider this factory + object as not being fully initialized and throw a corresponding (and most + probably fatal) exception. + + + + + + Validates that the provider name is specified. + + + Invoked by an + after it has injected all of an object's dependencies. + + + In the event of not setting the ProviderName. + + + + + Validates the properties. + + + + + Return the type of + + + + + Returns true, as the the object managed by this factory is a singleton. + + + + + + Gets or sets the name of the database provider. + + The name of the database provider. + + + + Gets or sets the connection string. + + The connection string. + + + + Exception thrown if a mapped object could not be retrieved via its identifier. + Provides information about the persistent class and the identifier. + + Mark Pollack (.NET) + $Id: ObjectRetrievalFailureException.cs,v 1.1 2006/12/13 18:40:11 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Initializes a new instance of the class. + + A message about the exception. + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception from the underlying data access API + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + When overridden in a derived class, sets the + with information about the exception. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is a null reference ( in Visual Basic). + + + + Proxy factory object for simplified declarative transaction handling. + + +

    + Alternative to the standard AOP + with a . +

    +

    + This class is intended to cover the typical case of declarative + transaction demarcation: namely, wrapping a (singleton) target object with a + transactional proxy, proxying all the interfaces that the target implements. +

    +

    + Internally, a + instance is used, but the user of this class does not have to care. Optionally, an + can be specified to cause conditional invocation of + the underlying . +

    +

    + The + + and + + properties can be set to add additional interceptors to the mix. +

    +
    + Juergen Hoeller + Dmitriy Kopylenko + Rod Johnson + Griffin Caprio (.NET) + $Id: TransactionProxyFactoryObject.cs,v 1.7 2007/08/01 17:56:16 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Returns the object wrapped by this proxy factory. + + The target object proxy. + + + + Method run after all the properties have been set for this object. + Responsible for actual proxy creation. + + + + + Set the target or . + + + The target. If this is an implementation of the + interface, it is used as our ; otherwise it is + wrapped in a . + + An for this object. + + + + Set the transaction manager for this factory. + + + It is this instance that will perform actual transaction management: this class is + just a way of invoking it. + + + + + Set the target object, i.e. the object to be wrapped with a transactional proxy. + + +

    + The target may be any object, in which case a + will + be created. If it is a , no wrapper + is created: this enables the use of a pooling + or prototype . +

    +
    +
    + + + Specify the set of interfaces being proxied. + + +

    + If left null (the default), the AOP infrastructure works + out which interfaces need proxying by analyzing the target, + proxying all of the interfaces that the target object implements. +

    +
    +
    + + + Set properties with method names as keys and transaction attribute + descriptors as values. + + +

    + The various transaction attribute descriptors are parsed via + an instance of the + class. +

    + + Method names are always applied to the target class, no matter if defined in an + interface or the class itself. + +

    + Internally, a + + will be created from the given properties. +

    +
    + +

    + An example string (method name and transaction attributes) might be: +

    +

    + key = "myMethod", value = "PROPAGATION_REQUIRED,readOnly". +

    +
    +
    + + + Set the transaction attribute source which is used to find transaction + attributes. + + +

    + If specifying a property value, an + appropriate + implementation will create a + + from the value. +

    +
    +
    + + + Set a pointcut, i.e an object that can cause conditional invocation + of the + depending on method and attributes passed. + + + + Additional interceptors are always invoked. + + + + + + Set additional interceptors (or advisors) to be applied before the + implicit transaction interceptor. + + + + + Set additional interceptors (or advisors) to be applied after the + implicit transaction interceptor. + + +

    + Note that this is just necessary if you rely on those interceptors in general. +

    +
    +
    + + + Specify the to use. + + The default instance is the global AdvisorAdapterRegistry. + + + + Returns the object for this proxy factory. + + + + + Is this object a singleton? Always returns true in this implementation. + + + + + Implementation of that uses + s. + + Rod Johnson + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: AttributesTransactionAttributeSource.cs,v 1.6 2007/08/08 20:59:50 markpollack Exp $ + + + + Abstract implementation of + + that caches attributes for methods, and implements a default fallback policy. + + +

    + The default fallback policy applied by this class is: + + + most specific method + + + target class attribute + + + declaring method + + + declaring class + + +

    +

    + Defaults to using class's transaction attribute if none is associated + with the target method. Any transaction attribute associated with the + target method completely overrides a class transaction attribute. +

    +

    + This implementation caches attributes by method after they are first used. + If it's ever desirable to allow dynamic changing of transaction attributes + (unlikely) caching could be made configurable. Caching is desirable because + of the cost of evaluating rollback rules. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: AbstractFallbackTransactionAttributeSource.cs,v 1.8 2007/09/20 12:31:01 bbaia Exp $ +
    + + + Canonical value held in cache to indicate no transaction attibute was found + for this method, and we don't need to look again. + + + + + Cache of s, keyed by method and target class. + + + + + Creates a new instance of the + + class. + + +

    + This is an class, and as such exposes no public constructors. +

    +
    +
    + + + Subclasses should implement this to return all attributes for this method. + May return null. + + + We need all because of the need to analyze rollback rules. + + The method to retrieve attributes for. + The transactional attributes associated with the method. + + + + Subclasses should implement this to return all attributes for this class. + May return null. + + + The to retrieve attributes for. + + + All attributes associated with the supplied . + + + + + Return the transaction attribute for this method invocation. + + + Defaults to the class's transaction attribute if no method + attribute is found + + method for the current invocation. Can't be null + target class for this invocation. May be null. + for this method, or null if the method is non-transactional + + + + Return the transaction attribute, given this set of attributes + attached to a method or class. Return null if it's not transactional. + + + Protected rather than private as subclasses may want to customize + how this is done: for example, returning a + + affected by the values of other attributes. + This implementation takes into account + s, if + the TransactionAttribute is a RuleBasedTransactionAttribute. + + + Attributes attached to a method or class. May be null, in which case a null + will be returned. + + + The configured transaction attribute, or null + if none was found. + + + + + Initializes a new instance of the class. + + + + + + implementation. + + + We need all because of the need to analyze rollback rules. + + The method to retrieve attributes for. + The transactional attributes associated with the method. + + + + + implementation. + + + The to retrieve attributes for. + + + All attributes associated with the supplied . + + + + + Implementation of IAdoExceptionTranslator that analyzes provider specific + error codes and translates into the DAO exception hierarchy. + + This class loads the obtains error codes from + the IDbProvider metadata property ErrorCodes + which defines error code mappings for various providers. + + Mark Pollack (.NET) + $Id: ErrorCodeExceptionTranslator.cs,v 1.11 2007/12/06 20:14:30 markpollack Exp $ + + + + The shared log instance for this class (and derived classes). + + + + + Initializes a new instance of the class. + + The data provider. + + + + Initializes a new instance of the class. + + The data provider. + The error code collection. + + + + Translate the given into a generic data access exception. + + A readable string describing the task being attempted. + The SQL query or update that caused the problem. May be null. + + The encountered by the ADO.NET implementation. + + + A appropriate for the supplied + . + + + + + Subclasses can override this method to attempt a custom mapping from a data access Exception + to DataAccessException. + + Readable text describing the task being attempted. + SQL query or update that caused the problem. May be null. + The error code extracted from the generic data access exception for + a particular provider. + The exception thrown from a data access operation. + + null if no custom translation was possible, otherwise a DataAccessException + resulting from custom translation. This exception should include the exception parameter + as a nested root cause. This implementation always returns null, meaning that + the translator always falls back to the default error codes. + + + + + Builds the message. + + The task. + The SQL. + The exception. + + + + + Determines whether the specified exception is valid data access exception. + + The exception. + + true if is valid data access exception; otherwise, false. + + + + + Logs the translation. + + The task. + The SQL. + The error code. + The exception. + if set to true [b]. + + + + Sets the db provider. + + The db provider. + + + + Gets the error codes for the provider + + The error codes. + + + + Gets or sets the fallback translator to use in case error code based translation + fails. + + The fallback translator. + + + + Generic callback interface for code that operates on a + IDbDataAdapter. + + +

    Allows you to execute any number of operations + on a IDbDataAdapter, for example to Fill a DataSet + or other more advanced operations such as the transfering + data between two different DataSets. +

    +

    Note that the passed in IDbDataAdapter + has been created by the framework and its SelectCommand + will be populated with values for CommandType and Text properties + along with Connection/Transaction properties based on the + calling transaction context. +

    + Execute(IDataAdapterCallback dataAdapterCallback) + method. +
    + Mark Pollack (.NET) + $Id: IDataAdapterCallback.cs,v 1.3 2007/12/04 06:49:17 markpollack Exp $ +
    + + + Called by AdoTemplate.Execute with an preconfigured + ADO.NET IDbDataAdapter instance with its SelectCommand + property populated with CommandType and Text values + along with Connection/Transaction properties based on the + calling transaction context. + + An active IDbDataAdapter instance + The result object + + + + Callback delegate to process each row of data in a result set to an object. + + The type of the object returned from the mapping operation. + The IDataReader to map + the number of the current row. + An abrirary object, typically derived from data + in the result set. + + + + Adapter implementation of the ResultSetExtractor interface that delegates + to a RowMapper which is supposed to create an object for each row. + Each object is added to the results List of this ResultSetExtractor. + + + Useful for the typical case of one object per row in the database table. + The number of entries in the results list will match the number of rows. +

    + Note that a RowMapper object is typically stateless and thus reusable; + just the RowMapperResultSetExtractor adapter is stateful. +

    +

    + As an alternative consider subclassing MappingAdoQuery from the + Spring.Data.Objects namespace: Instead of working with separate + AdoTemplate and IRowMapper objects you can have executable + query objects (containing row-mapping logic) there. +

    +
    + Mark Pollack (.NET) + $Id: RowMapperResultSetExtractor.cs,v 1.1 2007/08/03 19:51:11 markpollack Exp $ +
    + + + Initializes a new instance of the class. + + + + + Determines whether this collection contains the specified parameter name. + + Name of the parameter. + + true if contains the specified parameter name; otherwise, false. + + + + + Add an instance of . + + + + + + Add a parameter specifying the all the individual properties of a + IDbDataParameter. + + + + + + + + + + + + The newly created parameter + + + + + + + + + + + + + + + The newly created parameter + + + + Adds a value to the parameter collection + + This method can not be used with stored procedures for + providers that require named parameters. However, it is of + great convenience for other cases. + The value of the parameter. + Index of added parameter. + + + + Adds a range of vlaues to the parameter collection. + + This method can not be used with stored procedures for + providers that require named parameters. However, it is of + great convenience for other cases. + + + + + Gets the underlying standard ADO.NET for the specified parameter name. + + + + + Gets the underlying standard ADO.NET for the specified index. + + + + + Returns the underlying standard ADO.NET parameters collection. + + + + + Callback delegate for code that operates on a IDbCommand. + + the ADO.NET command object. + +

    Allows you to execute any number of operations + on a single IDbCommand, for example a single ExecuteScalar + call or repeated execute calls with varying parameters. +

    +

    Used internally by AdoTemplate, but also useful for + application code. Note that the passed in IDbCommand + has been created by the framework and will have its + Connection property set and the Transaction property + set based on the transaction context.

    +
    + Mark Pollack +
    + + + Exception thrown when attempting to suspend an existing transaction + but transaction suspension is not supported by the underlying backend. + + Juergen Hoeller + Griffin Caprio (.NET) + $Id: TransactionSuspensionNotSupportedException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Default implementation of the interface, + used by . + + +

    + Holds all status information that + + needs internally, including a generic transaction object determined by + the concrete transaction manager implementation. +

    +

    + Supports delegating savepoint-related methods to a transaction object + that implements the interface. +

    +
    + Juergen Hoeller + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: DefaultTransactionStatus.cs,v 1.14 2007/12/06 06:23:33 markpollack Exp $ +
    + + + Representation of the status of a transaction, + consisting of a transaction object and some status flags. + + +

    + Transactional code can use this to retrieve status information, + and to programmatically request a rollback (instead of throwing + an exception that causes an implicit rollback). +

    +

    + Derives from the interface to provide access + to savepoint management facilities. Note that savepoint management + is just available if the actual transaction manager supports it. +

    +
    + Juergen Hoeller + Griffin Caprio (.NET) + Mark Pollack (.NET) + $Id: ITransactionStatus.cs,v 1.11 2007/08/01 18:53:05 markpollack Exp $ +
    + + + Returns true if the transaction is new, else false if participating + in an existing transaction. + + + + + Gets / sets if the transaction is rollback-only. + + +

    + This instructs the transaction manager that the only possible outcome of + the transaction may be a rollback, proceeding with the normal application + workflow though (i.e. no exception). +

    +

    + For transactions managed by a + or + . + An alternative way to trigger a rollback is throwing an transaction exception. +

    +
    +
    + + + Gets the current transaction object. + + + Returns the current transaction object for a given connection. +

    + Used to associate with the property. +

    +
    +
    + + + Creates a new instance of the + class. + + The underlying transaction object that can hold state for the internal + transaction implementation. + True if the transaction is new, else false if participating in an existing transaction. + True if a new transaction synchronization has been opened for the given + . + True if the transaction is read only. + if set to true, enable debug log in tx managers. + The suspended resources for the given . + + + + Determines whether there is an actual transaction active. + + + true if there is an actual transaction active; otherwise, false. + + + + + This implementation delegates to the underlying transaction object + (if it implements the interface) + to create a savepoint. + + + If the underlying transaction does not support savepoints. + + + + + This implementation delegates to the underlying transaction object + (if it implements the interface) + to rollback to the supplied . + + The savepoint to rollback to. + + + + This implementation delegates to the underlying transaction object + (if it implements the interface) + to release the supplied . + + The savepoint to release. + + + + Create a savepoint and hold it for the transaction. + + + If the underlying transaction does not support savepoints. + + + + + Roll back to the savepoint that is held for the transaction. + + + If no save point has been created. + + + + + Release the savepoint that is held for the transaction. + + + If no save point has been created. + + + + + Gets a value indicating whether the progress of this transaction is debugged. + This is used by AbstractPlatformTransactionManager as an optimization, to prevent repeated + calls to log.IsDebug. Not really intended for client code. + true if debug; otherwise, false. + + + + Returns the underlying transaction object. + + + + + Gets or sets a value indicating whether the Transaction is completed, that is commited or rolled back. + + true if completed; otherwise, false. + + + + Returns true if the underlying transaction is read only. + + + + + Flag indicating if a new transaction synchronization has been opened + for this transaction. + + + + + Returns suspended resources for this transaction. + + + + + Gets and sets the savepoint for the current transaction, if any. + + + + + Returns a flag indicating if the transaction has a savepoint. + + + + + Return the underlying transaction as a + , if possible. + + + If the underlying transaction does not support savepoints. + + + + + Return true if the underlying transaction implements the + interface. + + + + + Returns true if the transaction is new, else false if participating + in an existing transaction. + + + + + Determine the rollbackOnly flag via checking both this + + and the transaction object, provided that the latter implements the + interface. + + The property can only be set to true. + + + + Determine the rollback-only flag via checking this TransactionStatus. Will only + return true if the application set the property RollbackOnly to true on this + TransactionStatus object. + + true if [local rollback only]; otherwise, false. + + + + Editor that can convert values into + instances. + + + The transaction attribute string must be parseable by the + in this package. +

    + Strings must be specified in the following syntax:
    + FQCN.methodName=<transaction attribute string> (sans <>). +

    + + ExampleNamespace.ExampleClass.MyMethod=PROPAGATION_MANDATORY,ISOLATION_DEFAULT + + + The specified class must be the one where the methods are defined; in the case of + implementing an interface, the interface class name must be specified. + +

    + This will register all overloaded methods for a given name. Does not support explicit + registration of certain overloaded methods. Supports wildcard style mappings (in + the form "xxx*"), e.g. "Notify*" for "Notify" and "NotifyAll". +

    +
    + Rod Johnson + Juergen Hoeller + Griffin Caprio (.NET) + $Id: TransactionAttributeSourceEditor.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Parses the input properties into a valid + + instance + + The properties string to be parsed. + + + + Gets the + from this instance. + + + + + Internal class to parse property values. + + + + + Creates a new instance of the + class. + + The property values to be parsed. + + + + Indexer to return values based on index value. + + + + + Returns the collection of keys for properties. + + + + + implementation + that works out whether a given exception should cause transaction rollback by applying + a number of rollback rules, both positive and negative. + + + If no rules are relevant to the exception, it behaves like the + class + (rolling back on runtime exceptions).. +

    + The + creates objects of this class. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: RuleBasedTransactionAttribute.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ +
    + + + Creates a new instance of the + + class. + + + The desired transaction propagation behaviour. + + + The rollback rules list for this transaction attribute. + + + + + Creates a new instance of the + + class. + + + + + Will a transaction be rolled back if the supplied + is thrown during the lifecycle of a transaction to which this attribute is applied? + + The offending . + True if the exception should cause a rollback, false otherwise. + + + + Returns a representation of this instance. + + + A representation of this instance. + + + + + Adds a to this + attributes list of rollback rules. + + + The to add. + + + + + Clears the rollback rules for this attribute. + + + + + Sets the rollback rules list for this transaction attribute. + + + + + Simple implementation of the + that allows attributes to be matched by registered name. + + Juergen Hoeller + Griffin Caprio (.NET) + $Id: NameMatchTransactionAttributeSource.cs,v 1.8 2008/05/27 19:06:57 markpollack Exp $ + + + + Logger available to subclasses, static for optimal serialization + + + + + Keys are method names; values are ITransactionAttributes + + + + + Creates a new instance of the + class. + + + + + Does the supplied match the supplied ? + + + The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, + as well as direct equality. Can be overridden in subclasses. + + The method name of the class. + The name in the descriptor. + True if the names match. + + + + Add an attribute for a transactional method. + + +

    + Method names can end with "*" for matching multiple methods. +

    +
    + The transactional method name. + + The attribute to be associated with the method. + +
    + + + Parses the given properties into a name/attribute map. + + + Expects method names as keys and string attributes definitions as values, + parsable into + instances via + . + + The properties of the transaction. + + + + Return the for this + method. + + The method to check. + + The target . May be null, in which case the declaring + class of the supplied must be used. + + + A or + null if the method is non-transactional. + + + + + Set a name/attribute map, consisting of method names (e.g. "MyMethod") and + instances + (or Strings to be converted to ITransactionAttribute instances). + + + + + Parses the given properties into a name/attribute map. + + + Expects method names as keys and string attributes definitions as values, + parsable into + instances via + . + + The properties of the transaction. + + + + Very simple implementation of + which will always return the same + for all methods fed to it. + + +

    + The + may be specified, but will otherwise default to PROPAGATION_REQUIRED. This may be + used in the cases where you want to use the same transaction attribute with all + methods being handled by a transaction interceptor. +

    +
    + Colin Sampaleanu + Griffin Caprio (.NET) + $Id: MatchAlwaysTransactionAttributeSource.cs,v 1.7 2007/12/29 00:28:53 markpollack Exp $ +
    + + + Creates a new instance of the + + class. + + + + + Return the for this + method. + + The method to check. + + The target . May be null, in which case the declaring + class of the supplied must be used. + + + A or + null if the method is non-transactional. + + + + + Determines whether the specified is equal to the current . + + The to compare with the current . + + true if the specified is equal to the current ; otherwise, false. + + + + + Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + + + A hash code for the current . + + + + + Returns a that represents the current . + + + + A that represents the current . + + 2 + + + + Allows a transaction attribute to be specified, using the + form, for example, "PROPAGATION_REQUIRED". + + + + + Callback delegate to process all result sets and row in an + AdoTemplate query method. + + + Implementations of this delegate perform the work + of extracting results but don't need worry about managing + ADO.NET resources, such as closing the reader, or transaction management. + + The IDataReader to extract data from. + Implementations should not close this: it will be closed + by the AdoTemplate. + An arbitrary result object or null if none. + + + + This is the central class in the Spring.Data namespace. + It simplifies the use of ADO.NET and helps to avoid commons errors. + + Mark Pollack (.NET) + $Id: AdoTemplate.cs,v 1.9 2007/12/28 19:37:13 markpollack Exp $ + + + + Interface that defines ADO.NET related database operations. + + Mark Pollack (.NET) + $Id: IAdoOperations.cs,v 1.24 2007/12/04 06:52:19 markpollack Exp $ + + + + Execute a ADO.NET operation on a command object using a delegate callback. + + This allows for implementing arbitrary data access operations + on a single command within Spring's managed ADO.NET environment. + The delegate called with a command object. + A result object returned by the action or null + + + + Execute a ADO.NET operation on a command object using an interface based callback. + + the callback to execute + object returned from callback + + + + Executes ADO.NET operations on a command object, created by the provided IDbCommandCreator, + using the interface based callback IDbCommandCallback. + + The command creator. + The callback to execute based on IDbCommand + A result object returned by the action or null + + + + Execute ADO.NET operations on a IDbDataAdapter object using an interface based callback. + + This allows for implementing abritrary data access operations + on a single DataAdapter within Spring's managed ADO.NET environment. + + The data adapter callback. + A result object returned by the callback or null + + + + Execute a query given IDbCommand's type and text, processing a + single result set with an instance of IResultSetExtractor + + The type of command + The text of the query. + Object that will extract all rows of a result set + An arbitrary result object, as returned by the IResultSetExtractor + + If there is any problem executing the query. + + + + + Execute a query given IDbCommand's type and text with parameters set + via the command setter, processing a + single result set with an instance of IResultSetExtractor + + The command type. + The command text to execute. + The result set extractor. + The command setter. + An arbitrary result object, as returned by the IResultSetExtractor + + If there is any problem executing the query. + + + + + Execute a query given static SQL/Stored Procedure name + and process a single result set with an instance of ResultSetExtractorDelegate + + The type of command. + The command text. + Delegate that will extract all rows of a result set + An arbitrary result object, as returned by the IResultSetExtractor + + If there is any problem executing the query. + + + + + Execute a query given static SQL, mapping each row to a .NET object + iva a RowMapper + + The type of command + SQL query to execute + object that will map one object per row + the result list containing mapped objects + + + + Execute a query with the specified command text, mapping a single result + row to an object via a RowMapper. + + The command type. + The command text to execute. + object that will map one object per row + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameters set via the + command setter, mapping a single result row to an object via a RowMapper. + + The command type. + The command text to execute. + object that will map one object per row + The command setter. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameters, mapping a single result row + to an object via a RowMapper. + + The command type. + The command text to execute. + object that will map one object per row + The parameter collection to use in the query. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameter, mapping a single result row + to an object via a RowMapper. + + The command type. + The command text to execute. + object that will map one object per row + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text, mapping a single result + row to an object via a RowMapper. + + The command type. + The command text to execute. + delegate that will map one object per row + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameters set via the + command setter, mapping a single result row to an object via a RowMapper. + + The command type. + The command text to execute. + delegate that will map one object per row + The command setter. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameters, mapping a single result row + to an object via a RowMapper. + + The command type. + The command text to execute. + delegate that will map one object per row + The parameter collection to use in the query. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameter, mapping a single result row + to an object via a RowMapper. + + The command type. + The command text to execute. + delegate that will map one object per row + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + + + + + Fill a based on a select command that requires no parameters. + + The to populate + The type of command + SQL query to execute + The number of rows successfully added to or refreshed in the + + + + Fill a based on a select command that requires no parameters. + + The to populate + The type of command + SQL query to execute + The number of rows successfully added to or refreshed in the + + + + Fill a based on a select command that requires no parameters + that returns one or more result sets that are added as + s to the + + The to populate + The type of command + SQL query to execute + The mapping of table names for each + created + + + + + Note that output parameters are marked input/output after derivation.... + (TODO - double check....) + + + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The database provider. + + + + Initializes a new instance of the class. + + The database provider. + if set to false + lazily initialize the ErrorCodeExceptionTranslator. + + + + Execute a ADO.NET operation on a command object using a delegate callback. + + The delegate called with a command object. + + A result object returned by the action or null + + This allows for implementing arbitrary data access operations + on a single command within Spring's managed ADO.NET environment. + + + + Callback to execute a IDbCommand. + + the callback to execute + object returned from callback + + + + Executes ADO.NET operations on a command object, created by the provided IDbCommandCreator, + using the interface based callback IDbCommandCallback. + + The command creator. + The callback to execute based on IDbCommand + A result object returned by the action or null + + + + Execute ADO.NET operations on a IDbDataAdapter object using an interface based callback. + + This allows for implementing abritrary data access operations + on a single DataAdapter within Spring's managed ADO.NET environment. + + The data adapter callback. + A result object returned by the callback or null + + + + Executes a non query returning the number of rows affected. + + The command type. + The command text to execute. + The number of rows affected. + + + + Executes a non query returning the number of rows affected. + + The command type. + The command text to execute. + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + The number of rows affected. + + + + Executes a non query returning the number of rows affected. + + The command type. + The command text to execute. + The parameter collection to map. + The number of rows affected. + + + + Executes a non query with parameters set via the + command setter, returning the number of rows affected. + + The command type. + The command text to execute. + The command setter. + The number of rows affected. + + + + Executes a non query with a command created via IDbCommandCreator and + parameters. + + Output parameters can be retrieved via the returned + dictionary. + + More commonly used as a lower level support method within the framework, + for example StoredProcedure/AdoScalar. + + + The callback to create a IDbCommand. + The number of rows affected. + + + + Execute the query with the specified command text. + + No parameters are used. As with + IDbCommand.ExecuteScalar, it returns the first column of the first row in the resultset + returned by the query. Extra columns or row are ignored. + The command type + The command text to execute. + The first column of the first row in the result set + + + + Execute the query with the specified command text and parameter returning a scalar result + + The command type + The command text to execute. + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + The first column of the first row in the result set + + + + Execute the query with the specified command text and parameters returning a scalar result + + The command type + The command text to execute. + The parameter collection to map. + The first column of the first row in the result set + + + + Execute the query with the specified command text and parameters set via the + command setter, returning a scalar result + + The command type + The command text to execute. + The command setter. + The first column of the first row in the result set + + + + Execute the query with a command created via IDbCommandCreator and + parameters + + Output parameters can be retrieved via the returned + dictionary. + + More commonly used as a lower level support method within the framework, + for example for StoredProcedure/AdoScalar. + + The callback to create a IDbCommand. + A dictionary containing output parameters, if any + + + + Execute a query given IDbCommand's type and text, reading a + single result set on a per-row basis with a . + + The type of command + The text of the query. + callback that will extract results + one row at a time. + + + + + Execute a query given IDbCommand's type and text by + passing the created IDbCommand to a ICommandSetter implementation + that knows how to bind values to the IDbCommand, reading a + single result set on a per-row basis with a . + + The type of command + The text of the query. + callback that will extract results + one row at a time. + + The command setter. + + + + Execute a query given IDbCommand's type and text and provided parameter + information, reading a + single result set on a per-row basis with a . + + The type of command + The text of the query. + callback that will extract results + one row at a time. + + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + + + + Execute a query given static SQL/Stored Procedure name + and process a single result set with an instance of IResultSetExtractor + + The type of command. + The SQL/Stored Procedure to execute + Object that will extract all rows of a result set + An arbitrary result object, as returned by the IResultSetExtractor + + + + Execute a query given static SQL/Stored Procedure name + and process a single result set with an instance of IResultSetExtractor + + The type of command. + The SQL/Stored Procedure to execute + Delegate that will extract all rows of a result set + An arbitrary result object, as returned by the IResultSetExtractor + + + + Execute a query with the specified command text, mapping a single result + row to an object via a RowMapper. + + The command type. + The command text to execute. + object that will map one object per row + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameters set via the + command setter, mapping a single result row to an object via a RowMapper. + + The command type. + The command text to execute. + object that will map one object per row + The command setter. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameters, mapping a single result row + to an object via a RowMapper. + + The command type. + The command text to execute. + object that will map one object per row + The parameter collection to use in the query. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameter, mapping a single result row + to an object via a RowMapper. + + The command type. + The command text to execute. + object that will map one object per row + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text, mapping a single result + row to an object via a RowMapper. + + The command type. + The command text to execute. + delegate that will map one object per row + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameters set via the + command setter, mapping a single result row to an object via a RowMapper. + + The command type. + The command text to execute. + delegate that will map one object per row + The command setter. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameters, mapping a single result row + to an object via a RowMapper. + + The command type. + The command text to execute. + delegate that will map one object per row + The parameter collection to use in the query. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Execute a query with the specified command text and parameter, mapping a single result row + to an object via a RowMapper. + + The command type. + The command text to execute. + delegate that will map one object per row + The name of the parameter to map. + One of the database parameter type enumerations. + The length of the parameter. 0 if not applicable to parameter type. + The parameter value. + The single mapped object. + + If the query does not return exactly one row. + + + If there is any problem executing the query. + + + + + Fill a based on a select command that requires no parameters. + + The to populate + The type of command + SQL query to execute + The number of rows successfully added to or refreshed in the + + + + Creates the data reader wrapper for use in AdoTemplate callback methods. + + The reader to wrap. + The data reader used in AdoTemplate callbacks + + + + An instance of a DbProvider implementation. + + + + + Gets or sets a value indicating whether to lazily initialize the + IAdoExceptionTranslator for this accessor, on first encounter of a + exception from the data provider. Default is "true"; can be switched to + "false" for initialization on startup. + + true if to lazy initialize the IAdoExceptionTranslator; + otherwise, false. + + + + Gets or sets the exception translator. If no custom translator is provided, a default + is used. + + The exception translator. + + + + Gets or set the System.Type to use to create an instance of IDataReaderWrapper + for the purpose of having defaults values to use in case of DBNull values read + from IDataReader. + + The type of the data reader wrapper. + + + + A wrapper implementation for IDbProvider such that multiple DbProvider instances can be + selected at runtime, say based on web request criteria. + + + The name of which DbProvider to use, as provided to the IDictionary property TargetDbProviders + is "dbProviderName". Once the target dbprovider name is known, set the name via a call to + LogicalThreadContext.SetData("dbProviderName", "database1ProviderName"). The value + "database1ProviderName" must match a key in the provided TargetDbProviders dictionary. + + Mark Pollack + $Id: MultiDelegatingDbProvider.cs,v 1.4 2008/01/29 18:15:38 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The target db providers. + + + + Ensures that the there are values in the TargetDbProviders dictionary and that the + key is of the type string and value is of the type IDbProvider. + + If the above conditions are not met. + + + + Returns a new command object for executing SQL statments/Stored Procedures + against the database. + + An new + + + + Returns a new connection object to communicate with the database. + + A new + + + + Returns a new parameter object for binding values to parameter + placeholders in SQL statements or Stored Procedure variables. + + A new + + + + Returns a new adapter objects for use with offline DataSets. + + A new + + + + Returns a new instance of the providers CommandBuilder class. + + A new Command Builder + In .NET 1.1 there was no common base class or interface + for command builders, hence the return signature is object to + be portable (but more loosely typed) across .NET 1.1/2.0 + + + + Creates the name of the parameter in the format appropriate to use inside IDbCommand.CommandText. + + The unformatted name of the parameter. + + The parameter name formatted foran IDbCommand.CommandText. + + In most cases this adds the parameter prefix to the name passed into this method. + + + + Creates the name ofthe parameter in the format appropriate for an IDataParameter, i.e. to be + part of a IDataParameterCollection. + + The unformatted name of the parameter. + + The parameter name formatted for an IDataParameter + + + + + Extracts the provider specific error code as a string. + + The data access exception. + The provider specific error code + + + + Determines whether the provided exception is in fact related + to database access. This can be provider dependent in .NET 1.1 since + there isn't a common base class for ADO.NET exceptions. + + The exception thrown when performing data access + operations. + + true if is a valid data access exception for the specified + exception; otherwise, false. + + + + + Determines whether is data access exception in .NET 1.1 for the specified exception. + + The candidate exception. + + true if is data access exception in .NET 1.1 for the specified exception; otherwise, false. + + + + + Gets the target provider based on the thread local name "dbProviderName" + + The corresonding IDbProvider. + + + + Sets the target db providers. + + The target db providers. + + + + Return metadata information about the database provider + + + + + + Connection string used to create connections. + + + + + + Callback delegate to set any necessary parameters or other + properties on the command object. The CommandType and + CommandText properties will have already been supplied + + The database command object. + Mark Pollack + + + + Data access exception thrown when a result was not of the expected size, + for example when expecting a single row but getting 0 or more than 1 rows. + + Mark Pollack (.NET) + Juergen Hoeller + $Id: EmptyResultDataAccessException.cs,v 1.2 2006/05/18 21:37:50 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The message. + + + + Initializes a new instance of the class. + + The message. + The inner exception. + + + + Creates a new instance of the + class. + + The expected size. + + + + Creates a new instance of the + class. + + A message about the exception. + The expected size. + + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is . + The class name is or is zero (0). + + + + Exception thrown on failure to aquire a lock during an update i.e a select for + update statement. + + +

    + This exception will be thrown either by O/R mapping tools or by custom DAO + implementations. +

    +
    + Rod Johnson + Griffin Caprio (.NET) + $Id: CannotAcquireLockException.cs,v 1.6 2008/04/08 20:26:43 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Exception thrown when an operation is attempted that relies on an existing + transaction (such as setting rollback status) and there is no existing transaction. + This represents an illegal usage of the transaction API. + + Rod Johnson + Griffin Caprio (.NET) + $Id: NoTransactionException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Exception thrown when attempting to work with a nested transaction + but nested transactions are not supported by the underlying backend. + + Juergen Hoeller + Griffin Caprio (.NET) + $Id: NestedTransactionNotSupportedException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + This is a utility class to help in parsing the transaction namespace + + Mark Pollack + $Id: TxNamespaceUtils.cs,v 1.2 2007/12/07 08:10:03 markpollack Exp $ + + + + The transaction manager attribute + + + + + The source of transaction metadata + + + + + The property asociated with the transaction manager xml element + + + + + Implementation that delegates to ServiceDomain and ContextUtil for all functionality. + + Introduced for purposes of unit testing. + Mark Pollack (.NET) + $Id $ + + + + Creates the context specified by the ServiceConfig object and pushes it onto the context stack to + become the current context. + + A ServiceConfig that contains the configuration information for the services to be used within the enclosed code. + + + + Leaves this ServiceDomain. The current context is then popped from the context stack, and the + context that was running when Enter was called becomes the current context. + + One of the TransactionStatus values + + + + Sets the consistent bit and the done bit to true in the COM+ context + + + + + Sets the consistent bit to false and the done bit to true in the COM+ context. + + + + + Gets or sets the consistent bit in the COM+ context. + + My transaction vote. + + + + Gets a value indicating whether the current context is transactional. + + + true if this instance is existing transaction; otherwise, false. + + + + + Called by the AdoTemplate class to allow implementations + to set any necessary properties on the DbDataAdapter object. + + +

    For example, this callback can be used to set the + AcceptChangesDuringFill property, register an event handler + for the FillErrors event, set update batch sizes if your provider + supports such functionality, set the ContinueUpdateOnError property, + or in .NET 2.0 to set the property AcceptChangesDuringUpdate. +

    +

    + Downcast to the appropriate subtype to access vendor specific + functionality.

    +

    + The DataAdapter SelectCommand will be already be + populated with values for CommandType and Text properties + along with Connection/Transaction properties based on the + calling transaction context. +

    +
    + Mark Pollack (.NET) + $Id: IDataAdapterSetter.cs,v 1.3 2006/11/29 07:18:45 markpollack Exp $ +
    + + + A superclass for object based abstractions of RDBMS stored procedures. + + Mark Pollack (.NET) + $Id: StoredProcedure.cs,v 1.3 2007/07/25 13:32:39 oakinger Exp $ + + + + Initializes a new instance of the class. + + + + + Execute the stored procedure using 'ExecuteScalar' + + Value of input parameters. + Dictionary with any named output parameters and the value of the + scalar under the key "scalar". + + + + Reusable query in which concrete subclasses must implement the + MapRow method to map each row of a single result set into an + object. + + + Simplifies MappingSqlQueryWithContext API by dropping parameters and + context. Most subclasses won't care about parameters. If you don't use + contextual information, subclass this instead of MappingSqlQueryWithContext. + + Mark Pollack (.NET) + $Id: MappingAdoQuery.cs,v 1.2 2007/07/25 13:32:39 oakinger Exp $ + + + + Initializes a new instance of the class. + + + + + Exception thrown when the underlyingresource denied a permission to + access a specific element, such as a specific database table. + + Juergen Hoeller + Mark Pollack (.NET) + $Id: PermissionDeniedDataAccessException.cs,v 1.1 2006/06/12 10:39:19 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception (from the underlying data access API, such as ADO.NET). + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Exception thrown on an optimistic locking violation for a mapped object. + Provides information about the persistent class and the identifier. + + Mark Pollack (.NET) + $Id: ObjectOptimisticLockingFailureException.cs,v 1.1 2006/12/13 18:40:11 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + A message about the exception.. + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception from the underlying data access API + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + When overridden in a derived class, sets the + with information about the exception. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is a null reference ( in Visual Basic). + + + + Thrown when an attempt to commit a transaction resulted in an unexpected rollback. + + Rod Johnson + Griffin Caprio (.NET) + $Id: UnexpectedRollbackException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + + + + Creates a new instance of the + class. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + + + Creates a new instance of the + class. + + + A message about the exception. + + + The root exception that is being wrapped. + + + + + Creates a new instance of the + class. + + + The + that holds the serialized object data about the exception being thrown. + + + The + that contains contextual information about the source or destination. + + + + + Advisor driven by a , + used to exclude a general advice from methods that + are non-transactional. + + + + To put it another way, use this advisor when you would like to associate other AOP + advice based on the pointcut specified by declarative transaction demarcation, + attibute based or otherwise. + +

    + Because the AOP framework caches advice calculations, this is normally + faster than just letting the + run and find out itself that it has no work to do. +

    +
    + Mark Pollack (.NET) + $Id: DefaultTransactionAttributeSourceAdvisor.cs,v 1.1 2007/01/04 05:11:17 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Tests the input method to see if it's covered by the advisor. + + The method to match. + The to match against. + + True if the supplied is covered by the advisor. + + + + + Translates all exceptions to an UncategorizedAdoException. + + + This exception translator used when an exception is thrown using the + "primary" implementation of IAdoExceptionTranslator, i.e. AdoExceptionTranslator. + + Mark Pollack (.NET) + $Id: FallbackExceptionTranslator.cs,v 1.6 2007/01/01 22:22:50 markpollack Exp $ + + + + Initializes a new instance of the class. + + + + + Translate the given into a generic data access exception. + + A readable string describing the task being attempted. + The SQL query or update that caused the problem. May be null. + + The encountered by the ADO.NET implementation. + + + A appropriate for the supplied + . + + + + + Called by AdoTemplate.Execute with an preconfigured + ADO.NET IDbDataAdapter instance with its SelectCommand + property populated with CommandType and Text values + along with Connection/Transaction properties based on the + calling transaction context. + + An active IDbDataAdapter instance + The result object + + + + TODO: + + Mark Pollack (.NET) + $Id: DbParameter.cs,v 1.5 2006/12/03 08:25:46 markpollack Exp $ + + + + Initializes a new instance of the class. + + + Sets the SourceVersion to be Default and the ParameterDirection to be Input. + + + + + Adapter to enable use of a IRowCallback inside a ResultSetExtractor. + + We don't use it for navigating since this could lead to unpredictable consequences. + Mark Pollack + $Id: RowCallbackResultSetExtractor.cs,v 1.1 2007/07/25 08:27:08 markpollack Exp $ + + + + Initializes a new instance of the class. + + The row callback. + + + + Initializes a new instance of the class. + + The row callback delegate. + + + + All rows of the data reader are passed to the IRowCallback + associated with this class. + + The IDataReader to extract data from. + Implementations should not close this: it will be closed + by the AdoTemplate. + + Null is returned since IRowCallback manages its own state. + + + + + Provides a name to a ResultSetProcessor for use with AdoOperation subclasses + such as StoredProcedure. + + Mark Pollack (.NET) + $Id: NamedResultSetProcessor.cs,v 1.3 2006/11/29 07:18:46 markpollack Exp $ + + + + Initializes a new instance of the class with a + IRowCallback instance + + The name of the associated row callback for use with output + result set mappers. + A row callback instance. + + + + Initializes a new instance of the class with a + IRowMapper instance + + The name of the associated row mapper. + A IRowMapper instance. + + + + Initializes a new instance of the class with a + IResultSetExtractor instance + + The name of the associated result set extractor. + A IResultSetExtractor instance. + + + + Callback for resource cleanup at end of transaction. + + + + + Create a new instance. + + + + + + + Compares the current instance with another object of the same type. + + An object to compare with this instance. + + The value of the order property. + + + + + Suspend this synchronization. + + +

    + Supposed to unbind resources from + + if managing any. +

    +
    +
    + + + Resume this synchronization. + + +

    + Supposed to unbind resources from + + if managing any. +

    +
    +
    + + + Invoked before transaction commit/rollback (after + , + even if + + threw an exception). + + +

    + Can e.g. perform resource cleanup. +

    +

    + Note that exceptions will get propagated to the commit caller + and cause a rollback of the transaction. +

    +
    +
    + + + Invoked after transaction commit/rollback. + + Status according to + + Can e.g. perform resource cleanup, in this case after transaction completion. +

    + Note that exceptions will get propagated to the commit or rollback + caller, although they will not influence the outcome of the transaction. +

    +
    +
    + + + Adapter implementation of the ResultSetExtractor interface that delegates + to a RowMapper which is supposed to create an object for each row. + Each object is added to the results List of this ResultSetExtractor. + + + Useful for the typical case of one object per row in the database table. + The number of entries in the results list will match the number of rows. +

    + Note that a RowMapper object is typically stateless and thus reusable; + just the RowMapperResultSetExtractor adapter is stateful. +

    +

    + As an alternative consider subclassing MappingAdoQuery from the + Spring.Data.Objects namespace: Instead of working with separate + AdoTemplate and IRowMapper objects you can have executable + query objects (containing row-mapping logic) there. +

    +
    + Mark Pollack (.NET) + $Id: RowMapperResultSetExtractor.cs,v 1.4 2007/08/03 19:51:10 markpollack Exp $ +
    + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The row mapper. + The rows expected. + + + + A more portable means to create a collection of ADO.NET + parameters. + + Mark Pollack (.NET) + $Id: DbParameters.cs,v 1.14 2007/12/06 22:34:56 markpollack Exp $ + + + + Initializes a new instance of the class. + + +
    +
    diff --git a/src/Spring/Spring.Data/Transaction/CannotCreateTransactionException.cs b/src/Spring/Spring.Data/Transaction/CannotCreateTransactionException.cs new file mode 100644 index 00000000..87102da0 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/CannotCreateTransactionException.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Exception thrown when a transaction can't be created using an + /// underlying transaction API such as COM+. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: CannotCreateTransactionException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class CannotCreateTransactionException : TransactionException + { + /// + /// Creates a new instance of the + /// class. + /// + public CannotCreateTransactionException() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public CannotCreateTransactionException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public CannotCreateTransactionException( string message, Exception rootCause) + : base( message , rootCause ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected CannotCreateTransactionException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Config/AttributeDrivenObjectDefinitionParser.cs b/src/Spring/Spring.Data/Transaction/Config/AttributeDrivenObjectDefinitionParser.cs new file mode 100644 index 00000000..b008dcf5 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Config/AttributeDrivenObjectDefinitionParser.cs @@ -0,0 +1,120 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Xml; +using Spring.Aop.Config; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Transaction.Interceptor; + +namespace Spring.Transaction.Config +{ + /// + /// IObjectDefinitionParser implementation that allows users to easily configure all the + /// infrastructure objects required to enable attribute-driven transction demarcation. + /// + /// Rob Harrop + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class AttributeDrivenObjectDefinitionParser : AbstractObjectDefinitionParser + { + /// + /// Object property name for injection the TransactionInterceptor + /// + private static readonly string TRANSACTION_INTERCEPTOR = "transactionInterceptor"; + + /// + /// The 'proxy-target-type' attribute + /// + private static readonly string PROXY_TARGET_TYPE = "proxy-target-type"; + + /// + /// The 'order' property/attribute + /// + private static readonly string ORDER = "order"; + + /// + /// Central template method to actually parse the supplied XmlElement + /// into one or more IObjectDefinitions. + /// + /// The element that is to be parsed into one or more s + /// The the object encapsulating the current state of the parsing process; + /// provides access to a + /// + /// The primary IObjectDefinition resulting from the parsing of the supplied XmlElement + /// + protected override AbstractObjectDefinition ParseInternal(XmlElement element, ParserContext parserContext) + { + ConfigureAutoProxyCreator(parserContext, element); + + string transactionManagerName = element.GetAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE); + Type sourceType = typeof (AttributesTransactionAttributeSource); + + //Create the TransactionInterceptor definition. + RootObjectDefinition interceptorDefinition = new RootObjectDefinition(typeof (TransactionInterceptor)); + interceptorDefinition.PropertyValues.Add(TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY, + new RuntimeObjectReference(transactionManagerName)); + interceptorDefinition.PropertyValues.Add(TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE, + new RootObjectDefinition(sourceType)); + + //Create the TransactionAttributeSourceAdvisor definition. + RootObjectDefinition advisorDefinition = new RootObjectDefinition(typeof (TransactionAttributeSourceAdvisor)); + advisorDefinition.PropertyValues.Add(TRANSACTION_INTERCEPTOR, interceptorDefinition); + if (element.HasAttribute(ORDER)) + { + advisorDefinition.PropertyValues.Add(ORDER, element.GetAttribute(ORDER)); + } + + return advisorDefinition; + } + + /// + /// Configures the auto proxy creator. + /// + /// The parser context. + /// The element. + private static void ConfigureAutoProxyCreator(ParserContext parserContext, XmlElement element) + { + AopNamespaceUtils.RegisterAutoProxyCreatorIfNecessary(parserContext, element); + + bool proxyTargetClass = + parserContext.ParserHelper.IsTrueStringValue(element.GetAttribute(PROXY_TARGET_TYPE)); + if (proxyTargetClass) + { + AopNamespaceUtils.ForceAutoProxyCreatorToUseDecoratorProxy(parserContext.Registry); + } + } + + /// + /// Gets a value indicating whether an ID should be generated instead of read + /// from the passed in XmlElement. + /// + /// true if should generate id; otherwise, false. + /// Note that this flag is about always generating an ID; the parser + /// won't even check for an "id" attribute in this case. + /// + protected override bool ShouldGenerateId + { + get { return true; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Config/TxAdviceObjectDefinitionParser.cs b/src/Spring/Spring.Data/Transaction/Config/TxAdviceObjectDefinitionParser.cs new file mode 100644 index 00000000..9ea11c8c --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Config/TxAdviceObjectDefinitionParser.cs @@ -0,0 +1,177 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Data; +using System.Xml; +using Spring.Collections; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Transaction.Interceptor; +using Spring.Util; + +namespace Spring.Transaction.Config +{ + /// + /// The for the <tx:advice> tag. + /// + /// Rob Harrop + /// Juergen Hoeller + /// Adrian Colyer + /// Mark Pollack (.NET) + /// $Id: TxAdviceObjectDefinitionParser.cs,v 1.2 2007/06/18 10:31:19 bbaia Exp $ + public class TxAdviceObjectDefinitionParser : AbstractSingleObjectDefinitionParser + { + private static string TIMEOUT = "timeout"; + + private static string READ_ONLY = "read-only"; + + private static string NAME_MAP = "nameMap"; + + private static string PROPAGATION = "propagation"; + + private static string ISOLATION = "isolation"; + + private static string ROLLBACK_FOR = "rollback-for"; + + private static string NO_ROLLBACK_FOR = "no-rollback-for"; + + protected override Type GetObjectType(XmlElement element) + { + return typeof (TransactionInterceptor); + } + + protected override void DoParse(XmlElement element, ParserContext parserContext, ObjectDefinitionBuilder builder) + { + + builder.AddPropertyReference(TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY, + element.GetAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE)); + XmlNodeList txAttributes = element.SelectNodes("*[local-name()='attributes' and namespace-uri()='" + element.NamespaceURI + "']"); + if (txAttributes.Count > 1 ) + { + parserContext.ReaderContext.ReportException(element, "tx advice", "Element is allowed at most once inside element "); + } + else if (txAttributes.Count == 1) + { + //using xml defined source + XmlElement attributeSourceElement = txAttributes[0] as XmlElement; + AbstractObjectDefinition attributeSourceDefinition = + ParseAttributeSource(attributeSourceElement, parserContext); + builder.AddPropertyValue(TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE, attributeSourceDefinition); + } + else + { + //Assume attibutes source + ObjectDefinitionBuilder txAttributeSourceBuilder = + parserContext.ParserHelper.CreateRootObjectDefinitionBuilder(typeof (AttributesTransactionAttributeSource)); + + builder.AddPropertyValue(TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE, + txAttributeSourceBuilder.ObjectDefinition); + + } + } + + private AbstractObjectDefinition ParseAttributeSource(XmlElement element, ParserContext parserContext) + { + XmlNodeList methods = element.SelectNodes("*[local-name()='method' and namespace-uri()='" + element.NamespaceURI + "']"); + ManagedDictionary transactionAttributeMap = new ManagedDictionary(); + foreach (XmlElement methodElement in methods) + { + string name = methodElement.GetAttribute("name"); + TypedStringValue nameHolder = new TypedStringValue(name); + + RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute(); + string propagation = methodElement.GetAttribute(PROPAGATION); + string isolation = methodElement.GetAttribute(ISOLATION); + string timeout = methodElement.GetAttribute(TIMEOUT); + string readOnly = methodElement.GetAttribute(READ_ONLY); + if (StringUtils.HasText(propagation)) + { + attribute.PropagationBehavior = (TransactionPropagation) Enum.Parse(typeof (TransactionPropagation), propagation, true); + } + if (StringUtils.HasText(isolation)) + { + attribute.TransactionIsolationLevel = + (IsolationLevel) Enum.Parse(typeof (IsolationLevel), isolation, true); + } + if (StringUtils.HasText(timeout)) + { + try + { + attribute.TransactionTimeout = Int32.Parse(timeout); + } + catch (FormatException ex) + { + parserContext.ReaderContext.ReportException(methodElement,"tx advice","timeout must be an integer value: [" + timeout + "]", ex); + } + } + if (StringUtils.HasText(readOnly)) + { + attribute.ReadOnly = Boolean.Parse(methodElement.GetAttribute(READ_ONLY)); + } + IList rollbackRules = new LinkedList(); + if (methodElement.HasAttribute(ROLLBACK_FOR)) + { + string rollbackForValue = methodElement.GetAttribute(ROLLBACK_FOR); + AddRollbackRuleAttributesTo(rollbackRules, rollbackForValue); + } + if (methodElement.HasAttribute(NO_ROLLBACK_FOR)) + { + string noRollbackForValue = methodElement.GetAttribute(NO_ROLLBACK_FOR); + AddNoRollbackRuleAttributesTo(rollbackRules, noRollbackForValue); + } + attribute.RollbackRules = rollbackRules; + + transactionAttributeMap[nameHolder] = attribute; + + + + } + + ObjectDefinitionBuilder builder = + parserContext.ParserHelper.CreateRootObjectDefinitionBuilder(typeof (NameMatchTransactionAttributeSource)); + builder.AddPropertyValue(NAME_MAP, transactionAttributeMap); + return builder.ObjectDefinition; + + } + + + + private void AddRollbackRuleAttributesTo(IList rollbackRules, string rollbackForValue) + { + string[] exceptionTypeNames = StringUtils.CommaDelimitedListToStringArray(rollbackForValue); + foreach (string exceptionTypeName in exceptionTypeNames) + { + rollbackRules.Add(new RollbackRuleAttribute(exceptionTypeName.Trim())); + } + } + + private void AddNoRollbackRuleAttributesTo(IList rollbackRules, string noRollbackForValue) + { + string[] exceptionTypeNames = StringUtils.CommaDelimitedListToStringArray(noRollbackForValue); + foreach (string exceptionTypeName in exceptionTypeNames) + { + rollbackRules.Add(new NoRollbackRuleAttribute(exceptionTypeName.Trim())); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Config/TxNamespaceParser.cs b/src/Spring/Spring.Data/Transaction/Config/TxNamespaceParser.cs new file mode 100644 index 00000000..12061aae --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Config/TxNamespaceParser.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +using Spring.Objects.Factory.Xml; + +namespace Spring.Transaction.Config +{ + /// + /// NamespaceParser allowing for the configuration of + /// declarative transaction management using either XML or using attributes. + /// + /// This namespace handler is the central piece of functionality in the + /// Spring transaction management facilities and offers two appraoches + /// to declaratively manage transactions. + /// + /// One approach uses transaction semantics defined in XML using the + /// <tx:advice> elements, the other uses attributes + /// in combination with the <tx:annotation-driven> element. + /// Both approached are detailed in the Spring reference manual. + /// + [ + NamespaceParser( + Namespace = "http://www.springframework.net/tx", + SchemaLocationAssemblyHint = typeof(TxNamespaceParser), + SchemaLocation = "/Spring.Transaction.Config/spring-tx-1.1.xsd" + ) + ] + public class TxNamespaceParser : NamespaceParserSupport + { + /// + /// Register the for the 'advice' and + /// 'attribute-driven' tags. + /// + public override void Init() + { + RegisterObjectDefinitionParser("advice", new TxAdviceObjectDefinitionParser()); + RegisterObjectDefinitionParser("attribute-driven", new AttributeDrivenObjectDefinitionParser()); + + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Config/TxNamespaceUtils.cs b/src/Spring/Spring.Data/Transaction/Config/TxNamespaceUtils.cs new file mode 100644 index 00000000..f1bfa4e1 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Config/TxNamespaceUtils.cs @@ -0,0 +1,52 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Core; +using Spring.Transaction.Interceptor; + +namespace Spring.Transaction.Config +{ + /// + /// This is a utility class to help in parsing the transaction namespace + /// + /// Mark Pollack + /// $Id: TxNamespaceUtils.cs,v 1.2 2007/12/07 08:10:03 markpollack Exp $ + public sealed class TxNamespaceUtils + { + /// + /// The transaction manager attribute + /// + public const string TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager"; + + /// + /// The source of transaction metadata + /// + public const string TRANSACTION_ATTRIBUTE_SOURCE = "transactionAttributeSource"; + + /// + /// The property asociated with the transaction manager xml element + /// + public static readonly string TRANSACTION_MANAGER_PROPERTY = + Conventions.AttributeNameToPropertyName(TRANSACTION_MANAGER_ATTRIBUTE); + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Config/spring-tx-1.1.xsd b/src/Spring/Spring.Data/Transaction/Config/spring-tx-1.1.xsd new file mode 100644 index 00000000..3759bad6 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Config/spring-tx-1.1.xsd @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Data/Transaction/HeuristicCompletionException.cs b/src/Spring/Spring.Data/Transaction/HeuristicCompletionException.cs new file mode 100644 index 00000000..ca775a16 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/HeuristicCompletionException.cs @@ -0,0 +1,147 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Exception that represents a transaction failure caused by heuristics. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: HeuristicCompletionException.cs,v 1.6 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class HeuristicCompletionException : TransactionException, ISerializable + { + /// + /// The outcome state of the transaction: have some or all resources been committed? + /// + private TransactionOutcomeState _outcomeState = TransactionOutcomeState.Unknown; + + /// + /// Returns the transaction's outcome state. + /// + public TransactionOutcomeState OutcomeState + { + get { return _outcomeState; } + } + + /// + /// Returns a representation of the supplied + /// . + /// + /// + /// The value that + /// is to be stringified. + /// + /// A representation. + public static string GetStateString( TransactionOutcomeState outcomeState ) + { + return Enum.GetName (typeof (TransactionOutcomeState), outcomeState); + } + + /// + /// Creates a new instance of the + /// class. + /// + public HeuristicCompletionException( ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public HeuristicCompletionException( string message ) : base( message ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public HeuristicCompletionException( string message, Exception rootCause ) + : base( message, rootCause ) {} + + /// + /// Creates a new instance of the + /// class + /// with the specified + /// and inner exception. + /// + /// + /// The + /// for the transaction. + /// + /// + /// The inner exception that is being wrapped. + /// + public HeuristicCompletionException( + TransactionOutcomeState outcomeState, Exception innerException) + : base( "Heuristic completion: outcome state is " + GetStateString(outcomeState), innerException ) + { + _outcomeState = outcomeState; + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected HeuristicCompletionException( SerializationInfo info, StreamingContext context ) + : base( info, context ) + { + _outcomeState = ( TransactionOutcomeState ) info.GetInt32( "outcomeState" ); + } + + /// + /// Override of + /// to allow for private serialization. + /// + /// + /// The + /// that holds the serialized object data about the exception. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue( "outcomeState", _outcomeState ); + base.GetObjectData (info, context); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/IPlatformTransactionManager.cs b/src/Spring/Spring.Data/Transaction/IPlatformTransactionManager.cs new file mode 100644 index 00000000..bf2cf496 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/IPlatformTransactionManager.cs @@ -0,0 +1,110 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Transaction +{ + /// + /// This is the central interface in Spring.NET's transaction support. + /// + /// + ///

    + /// Applications can use this directly, but it is not primarily meant as an API. + /// Typically, applications will work with either + /// or the AOP transaction + /// interceptor. + ///

    + ///

    + /// For implementers, + /// is a good starting point. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: IPlatformTransactionManager.cs,v 1.10 2007/08/01 18:53:05 markpollack Exp $ + public interface IPlatformTransactionManager + { + /// + /// Return a currently active transaction or create a new one. + /// + /// + ///

    + /// Note that parameters like isolation level or timeout will only be applied + /// to new transactions, and thus be ignored when participating in active ones. + /// Furthermore, they aren't supported by every transaction manager: + /// a proper implementation should throw an exception when custom values + /// that it doesn't support are specified. + ///

    + ///
    + /// + /// instance (can be null for + /// defaults), describing propagation behavior, isolation level, timeout etc. + /// + /// + /// In case of lookup, creation, or system errors. + /// + /// + /// A representing the new or current transaction. + /// + ITransactionStatus GetTransaction( ITransactionDefinition definition ); + + /// + /// Commit the given transaction, with regard to its status. + /// + /// + ///

    + /// If the transaction has been marked rollback-only programmatically, + /// perform a rollback. + ///

    + ///

    + /// If the transaction wasn't a new one, omit the commit to take part + /// in the surrounding transaction properly. + ///

    + ///
    + /// + /// The instance returned by the + /// () method. + /// + /// + /// In case of commit or system errors + /// + void Commit( ITransactionStatus transactionStatus ); + + /// + /// Roll back the given transaction, with regard to its status. + /// + /// + ///

    + /// If the transaction wasn't a new one, just set it rollback-only + /// to take part in the surrounding transaction properly. + ///

    + ///
    + /// + /// The instance returned by the + /// () method. + /// + /// + /// In case of system errors. + /// + void Rollback( ITransactionStatus transactionStatus ); + } +} diff --git a/src/Spring/Spring.Data/Transaction/ISavepointManager.cs b/src/Spring/Spring.Data/Transaction/ISavepointManager.cs new file mode 100644 index 00000000..55495ab9 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/ISavepointManager.cs @@ -0,0 +1,99 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +using System; + +namespace Spring.Transaction +{ + /// + /// Interface that specifies means to programmatically manage + /// transaction savepoints in a generic fashion. + /// + /// + ///

    + /// Note that savepoints can only work within an active transaction. + /// Just use this programmatic savepoint handling for advanced needs; + /// else, a subtransaction with a value + /// of is preferable. + ///

    + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: ISavepointManager.cs,v 1.5 2006/05/17 17:48:25 bbaia Exp $ + public interface ISavepointManager + { + /// + /// Create a new savepoint. + /// + /// + /// The name of the savepoint to create. + /// + /// + /// You can roll back to a specific savepoint + /// via , + /// and explicitly release a savepoint that you don't need anymore via + /// . + ///

    + /// Note that most transaction managers will automatically release + /// savepoints at transaction completion. + ///

    + ///
    + /// + /// If the savepoint could not be created, + /// either because the backend does not support it or because the + /// transaction is not in an appropriate state. + /// + /// + /// A savepoint object, to be passed into + /// + /// or . + /// + void CreateSavepoint( string savepointName ); + + /// + /// Roll back to the given savepoint. + /// + /// + /// The savepoint will be automatically released afterwards. + /// + /// The savepoint to roll back to. + /// + /// If the rollback failed. + /// + void RollbackToSavepoint( string savepoint ); + + /// + /// Explicitly release the given savepoint. + /// + /// + ///

    + /// Note that most transaction managers will automatically release + /// savepoints at transaction completion. + ///

    + ///

    + /// Implementations should fail as silently as possible if + /// proper resource cleanup will still happen at transaction completion. + ///

    + ///
    + /// The savepoint to release. + /// + /// If the release failed. + /// + void ReleaseSavepoint( string savepoint ); + } +} diff --git a/src/Spring/Spring.Data/Transaction/ITransactionDefinition.cs b/src/Spring/Spring.Data/Transaction/ITransactionDefinition.cs new file mode 100644 index 00000000..3294ad1d --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/ITransactionDefinition.cs @@ -0,0 +1,122 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; + +namespace Spring.Transaction +{ + /// + /// Interface for classes that define transaction properties. Base interface for + /// . + /// + /// + ///

    + /// Note that isolation level, timeout and read-only settings will only + /// get applied when starting a new transaction. As only + /// and + /// can actually cause that, it doesn't make sense + /// to specify any of those settings else. Furthermore, not all transaction + /// managers will support those features and thus throw respective exceptions + /// when given non-default values. + ///

    + ///
    + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: ITransactionDefinition.cs,v 1.9 2007/12/07 08:10:03 markpollack Exp $ + public interface ITransactionDefinition + { + /// + /// Return the propagation behavior of type + /// . + /// + TransactionPropagation PropagationBehavior { get; } + + /// + /// Return the isolation level of type . + /// + /// + ///

    + /// Only makes sense in combination with + /// and + /// . + ///

    + ///

    + /// Note that a transaction manager that does not support custom isolation levels + /// will throw an exception when given any other level than + /// . + ///

    + ///
    + IsolationLevel TransactionIsolationLevel { get; } + + /// + /// Return the transaction timeout. + /// + /// + ///

    + /// Must return a number of seconds, or -1. + /// Only makes sense in combination with + /// and + /// . + /// Note that a transaction manager that does not support timeouts will + /// throw an exception when given any other timeout than -1. + ///

    + ///
    + int TransactionTimeout { get; } + + /// + /// Get whether to optimize as read-only transaction. + /// + /// + ///

    + /// This just serves as hint for the actual transaction subsystem, + /// it will not necessarily cause failure of write accesses. + ///

    + ///

    + /// Only makes sense in combination with + /// and + /// . + ///

    + ///

    + /// A transaction manager that cannot interpret the read-only hint + /// will not throw an exception when given ReadOnly=true. + ///

    + ///
    + bool ReadOnly { get; } + + /// + /// Return the name of this transaction. Can be null. + /// + /// + /// This will be used as a transaction name to be shown in a + /// transaction monitor, if applicable. In the case of Spring + /// declarative transactions, the exposed name will be the fully + /// qualified type name + "." method name + assembly (by default). + /// + string Name { get; } + +#if NET_2_0 + /// + /// Gets the enterprise services interop option. + /// + /// The enterprise services interop option. + System.Transactions.EnterpriseServicesInteropOption EnterpriseServicesInteropOption { get;} +#endif + } +} diff --git a/src/Spring/Spring.Data/Transaction/ITransactionStatus.cs b/src/Spring/Spring.Data/Transaction/ITransactionStatus.cs new file mode 100644 index 00000000..58c98a20 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/ITransactionStatus.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Data; + +namespace Spring.Transaction +{ + /// + /// Representation of the status of a transaction, + /// consisting of a transaction object and some status flags. + /// + /// + ///

    + /// Transactional code can use this to retrieve status information, + /// and to programmatically request a rollback (instead of throwing + /// an exception that causes an implicit rollback). + ///

    + ///

    + /// Derives from the interface to provide access + /// to savepoint management facilities. Note that savepoint management + /// is just available if the actual transaction manager supports it. + ///

    + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: ITransactionStatus.cs,v 1.11 2007/08/01 18:53:05 markpollack Exp $ + public interface ITransactionStatus : ISavepointManager + { + /// + /// Returns true if the transaction is new, else false if participating + /// in an existing transaction. + /// + bool IsNewTransaction { get; } + + /// + /// Gets / sets if the transaction is rollback-only. + /// + /// + ///

    + /// This instructs the transaction manager that the only possible outcome of + /// the transaction may be a rollback, proceeding with the normal application + /// workflow though (i.e. no exception). + ///

    + ///

    + /// For transactions managed by a + /// or + /// . + /// An alternative way to trigger a rollback is throwing an transaction exception. + ///

    + ///
    + bool RollbackOnly { get; set; } + + /// + /// Gets the current transaction object. + /// + /// + /// Returns the current transaction object for a given connection. + ///

    + /// Used to associate with the property. + ///

    + ///
    + object Transaction { get; } + + bool Completed { get; } + } +} diff --git a/src/Spring/Spring.Data/Transaction/IllegalTransactionStateException.cs b/src/Spring/Spring.Data/Transaction/IllegalTransactionStateException.cs new file mode 100644 index 00000000..67581711 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/IllegalTransactionStateException.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Exception thrown when the existence or non-existence of a transaction + /// amounts to an illegal state according to the transaction propagation + /// behavior that applies. + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: IllegalTransactionStateException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class IllegalTransactionStateException : CannotCreateTransactionException + { + /// + /// Creates a new instance of the + /// class. + /// + public IllegalTransactionStateException( ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public IllegalTransactionStateException( String message ) : base(message) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public IllegalTransactionStateException(string message, Exception rootCause) + : base(message, rootCause) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected IllegalTransactionStateException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs b/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs new file mode 100644 index 00000000..7902fba0 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs @@ -0,0 +1,299 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Reflection; + +using Spring.Collections; +using Spring.Util; +using Spring.Core; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Abstract implementation of + /// + /// that caches attributes for methods, and implements a default fallback policy. + /// + /// + ///

    + /// The default fallback policy applied by this class is: + /// + /// + /// most specific method + /// + /// + /// target class attribute + /// + /// + /// declaring method + /// + /// + /// declaring class + /// + /// + ///

    + ///

    + /// Defaults to using class's transaction attribute if none is associated + /// with the target method. Any transaction attribute associated with the + /// target method completely overrides a class transaction attribute. + ///

    + ///

    + /// This implementation caches attributes by method after they are first used. + /// If it's ever desirable to allow dynamic changing of transaction attributes + /// (unlikely) caching could be made configurable. Caching is desirable because + /// of the cost of evaluating rollback rules. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: AbstractFallbackTransactionAttributeSource.cs,v 1.8 2007/09/20 12:31:01 bbaia Exp $ + public abstract class AbstractFallbackTransactionAttributeSource : ITransactionAttributeSource + { + /// + /// Canonical value held in cache to indicate no transaction attibute was found + /// for this method, and we don't need to look again. + /// + private static readonly object NULL_TX_ATTIBUTE = new object(); + + /// + /// Cache of s, keyed by method and target class. + /// + private IDictionary _transactionAttibuteCache = new Hashtable(); + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + ///

    + /// This is an class, and as such exposes no public constructors. + ///

    + ///
    + protected AbstractFallbackTransactionAttributeSource() + { + } + + #region Protected Abstract Methods + + /// + /// Subclasses should implement this to return all attributes for this method. + /// May return null. + /// + /// + /// We need all because of the need to analyze rollback rules. + /// + /// The method to retrieve attributes for. + /// The transactional attributes associated with the method. + protected abstract Attribute[] FindAllAttributes(MethodInfo method); + + /// + /// Subclasses should implement this to return all attributes for this class. + /// May return null. + /// + /// + /// The to retrieve attributes for. + /// + /// + /// All attributes associated with the supplied . + /// + protected abstract Attribute[] FindAllAttributes(Type targetType); + + #endregion + + #region ITransactionAttributeSource Members + + /// + /// Return the transaction attribute for this method invocation. + /// + /// + /// Defaults to the class's transaction attribute if no method + /// attribute is found + /// + /// method for the current invocation. Can't be null + /// target class for this invocation. May be null. + /// for this method, or null if the method is non-transactional + public ITransactionAttribute ReturnTransactionAttribute(MethodInfo method, Type targetType) + { + object cacheKey = getCacheKey(method, targetType); + object cached = null; + + lock (_transactionAttibuteCache) + { + cached = _transactionAttibuteCache[cacheKey]; + + if (cached != null) + { + if (NULL_TX_ATTIBUTE == cached) + { + return null; + } + { + return (ITransactionAttribute)cached; + } + } + else + { + ITransactionAttribute transactionAttribute = computeTransactionAttribute(method, targetType); + if (null == transactionAttribute) + { + _transactionAttibuteCache.Add(cacheKey, NULL_TX_ATTIBUTE); + } + else + { + _transactionAttibuteCache.Add(cacheKey, transactionAttribute); + } + return transactionAttribute; + } + } + } + + #endregion + + /// + /// Return the transaction attribute, given this set of attributes + /// attached to a method or class. Return null if it's not transactional. + /// + /// + /// Protected rather than private as subclasses may want to customize + /// how this is done: for example, returning a + /// + /// affected by the values of other attributes. + /// This implementation takes into account + /// s, if + /// the TransactionAttribute is a RuleBasedTransactionAttribute. + /// + /// + /// Attributes attached to a method or class. May be null, in which case a null + /// will be returned. + /// + /// + /// The configured transaction attribute, or null + /// if none was found. + /// + protected virtual ITransactionAttribute FindTransactionAttribute(Attribute[] attributes) + { + if (null == attributes) + { + return null; + } + ITransactionAttribute transactionAttribute = null; + foreach (Attribute currentAttribute in attributes) + { + transactionAttribute = currentAttribute as ITransactionAttribute; + if (null != transactionAttribute) + { + break; + } + } + + RuleBasedTransactionAttribute ruleBasedTransactionAttribute = transactionAttribute as RuleBasedTransactionAttribute; + if (null != ruleBasedTransactionAttribute) + { + IList rollbackRules = new LinkedList(); + foreach (Attribute currentAttribute in attributes) + { + RollbackRuleAttribute rollbackRuleAttribute = currentAttribute as RollbackRuleAttribute; + if (null != rollbackRuleAttribute) + { + rollbackRules.Add(rollbackRuleAttribute); + } + } + ruleBasedTransactionAttribute.RollbackRules = rollbackRules; + return ruleBasedTransactionAttribute; + } + return transactionAttribute; + } + + #region Private Methods + + private object getCacheKey(MethodBase method, Type targetType) + { + return targetType + String.Empty + method.GetHashCode(); + } + + private ITransactionAttribute computeTransactionAttribute(MethodInfo method, Type targetType) + { + MethodInfo specificMethod; + if (targetType == null) + { + specificMethod = method; + } + else + { + ComposedCriteria searchCriteria = new ComposedCriteria(); + searchCriteria.Add(new MethodNameMatchCriteria(method.Name)); + searchCriteria.Add(new MethodParametersCountCriteria(method.GetParameters().Length)); +#if NET_2_0 + searchCriteria.Add(new MethodGenericArgumentsCountCriteria( + method.GetGenericArguments().Length)); +#endif + searchCriteria.Add(new MethodParametersCriteria(ReflectionUtils.GetParameterTypes(method))); + + MemberInfo[] matchingMethods = targetType.FindMembers( + MemberTypes.Method, + BindingFlags.Instance | BindingFlags.Public, + new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria), + searchCriteria); + + if (matchingMethods != null && matchingMethods.Length == 1) + { + specificMethod = matchingMethods[0] as MethodInfo; + } + else + { + specificMethod = method; + } + } + + ITransactionAttribute transactionAttribute = getTransactionAttribute(specificMethod); + if (null != transactionAttribute) + { + return transactionAttribute; + } + else if (specificMethod != method) + { + transactionAttribute = getTransactionAttribute(method); + } + return null; + } + + private ITransactionAttribute getTransactionAttribute(MethodInfo methodInfo) + { + ITransactionAttribute transactionAttribute = FindTransactionAttribute(FindAllAttributes(methodInfo)); + + if (null != transactionAttribute) + { + return transactionAttribute; + } + transactionAttribute = FindTransactionAttribute(FindAllAttributes(methodInfo.DeclaringType)); + if (null != transactionAttribute) + { + return transactionAttribute; + } + return null; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/AttributesTransactionAttributeSource.cs b/src/Spring/Spring.Data/Transaction/Interceptor/AttributesTransactionAttributeSource.cs new file mode 100644 index 00000000..513bc923 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/AttributesTransactionAttributeSource.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Reflection; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Implementation of that uses + /// s. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: AttributesTransactionAttributeSource.cs,v 1.6 2007/08/08 20:59:50 markpollack Exp $ + public class AttributesTransactionAttributeSource : AbstractFallbackTransactionAttributeSource + { + + /// + /// Initializes a new instance of the class. + /// + public AttributesTransactionAttributeSource( ) + { + } + + /// + /// + /// implementation. + /// + /// + /// We need all because of the need to analyze rollback rules. + /// + /// The method to retrieve attributes for. + /// The transactional attributes associated with the method. + protected override Attribute[] FindAllAttributes( MethodInfo method ) + { + return Attribute.GetCustomAttributes( method ); + } + + /// + /// + /// implementation. + /// + /// + /// The to retrieve attributes for. + /// + /// + /// All attributes associated with the supplied . + /// + protected override Attribute[] FindAllAttributes( Type targetType ) + { + return Attribute.GetCustomAttributes( targetType ); + } + + protected override ITransactionAttribute FindTransactionAttribute(Attribute[] attributes) + { + if (attributes == null) + { + return null; + } + foreach (Attribute attr in attributes) + { + if (attr is TransactionAttribute) + { + TransactionAttribute ta = (TransactionAttribute)attr; + RuleBasedTransactionAttribute rbta = + new RuleBasedTransactionAttribute(); + + //TODO another reminder to sync property names + rbta.PropagationBehavior = ta.TransactionPropagation; + rbta.TransactionIsolationLevel = ta.IsolationLevel; + rbta.ReadOnly = ta.ReadOnly; + +#if NET_2_0 + rbta.EnterpriseServicesInteropOption = ta.EnterpriseServicesInteropOption; +#endif + Type[] rbf = ta.RollbackFor; + + IList rollBackRules = new ArrayList(); + + if (rbf != null) + { + foreach (Type t in rbf) + { + RollbackRuleAttribute rule = new RollbackRuleAttribute(t); + rollBackRules.Add(rule); + } + } + + + Type[] nrbfc = ta.NoRollbackFor; + + if (nrbfc != null) + { + foreach (Type t in nrbfc) + { + NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(t); + rollBackRules.Add(rule); + } + } + + rbta.RollbackRules = rollBackRules; + + return rbta; + + } + } + return null; + } + + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/DefaultTransactionAttribute.cs b/src/Spring/Spring.Data/Transaction/Interceptor/DefaultTransactionAttribute.cs new file mode 100644 index 00000000..a70271f8 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/DefaultTransactionAttribute.cs @@ -0,0 +1,99 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Transaction.Support; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Transaction attribute approach to rolling back on all exceptions, no other + /// exceptions by default. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: DefaultTransactionAttribute.cs,v 1.9 2007/12/29 00:28:53 markpollack Exp $ + public class DefaultTransactionAttribute : DefaultTransactionDefinition, ITransactionAttribute + { + /// + /// Prefix for rollback-on-exception rules in description strings. + /// + public static readonly string ROLLBACK_RULE_PREFIX = "-"; + + /// + /// Prefix for commit-on-exception rules in description strings. + /// + public static readonly string COMMIT_RULE_PREFIX = "+"; + + /// + /// Creates a new instance of the + /// + /// class. + /// + public DefaultTransactionAttribute() {} + + /// + /// Creates a new instance of the + /// + /// class, setting the propagation behavior to the supplied value. + /// + /// + /// The desired transaction propagation behaviour. + /// + public DefaultTransactionAttribute( TransactionPropagation propagationBehavior ) + : base (propagationBehavior) {} + + #region ITransactionAttribute Members + /// + /// Decides if rollback is required for the supplied . + /// + /// + ///

    + /// The default behavior is to rollback on any exception. + /// Consistent with 's behavior. + ///

    + ///
    + /// The to evaluate. + /// True if the exception causes a rollback, false otherwise. + public virtual bool RollbackOn(Exception exception) + { + return ( true ); + } + #endregion + + /// + /// Return a description of this transaction attribute. + /// + /// + ///

    + /// The format matches the one used by the + /// , + /// to be able to feed any result into a + /// instance's properties. + ///

    + ///
    + public override string ToString() + { + string result = DefinitionDescription; + result += "," + ROLLBACK_RULE_PREFIX + "System.Exception"; + return result; + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/DefaultTransactionAttributeSourceAdvisor.cs b/src/Spring/Spring.Data/Transaction/Interceptor/DefaultTransactionAttributeSourceAdvisor.cs new file mode 100644 index 00000000..a1f73652 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/DefaultTransactionAttributeSourceAdvisor.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using AopAlliance.Aop; +using Spring.Aop.Framework; +using Spring.Aop.Support; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Advisor driven by a , + /// used to exclude a general advice from methods that + /// are non-transactional. + /// + /// + /// + /// To put it another way, use this advisor when you would like to associate other AOP + /// advice based on the pointcut specified by declarative transaction demarcation, + /// attibute based or otherwise. + /// + ///

    + /// Because the AOP framework caches advice calculations, this is normally + /// faster than just letting the + /// run and find out itself that it has no work to do. + ///

    + ///
    + /// Mark Pollack (.NET) + /// $Id: DefaultTransactionAttributeSourceAdvisor.cs,v 1.1 2007/01/04 05:11:17 markpollack Exp $ + [Serializable] + public class DefaultTransactionAttributeSourceAdvisor : StaticMethodMatcherPointcutAdvisor + { + private ITransactionAttributeSource _transactionAttributeSource; + + /// + /// Creates a new instance of the + /// class. + /// + public DefaultTransactionAttributeSourceAdvisor(ITransactionAttributeSource transactionAttributeSource, IAdvice advice) + : base( advice ) + { + if (transactionAttributeSource == null) + { + throw new AopConfigException("Cannot construct a DefaultTransactionAttributeSourceAdvisor if " + + "TransactionAttributeSource is null."); + } + _transactionAttributeSource = transactionAttributeSource; + } + + /// + /// Tests the input method to see if it's covered by the advisor. + /// + /// The method to match. + /// The to match against. + /// + /// True if the supplied is covered by the advisor. + /// + public override bool Matches(MethodInfo method, Type targetClass) + { + return ( _transactionAttributeSource.ReturnTransactionAttribute( (MethodInfo)method, targetClass ) != null); + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/DelegatingTransactionAttributeWithName.cs b/src/Spring/Spring.Data/Transaction/Interceptor/DelegatingTransactionAttributeWithName.cs new file mode 100644 index 00000000..6e5890e7 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/DelegatingTransactionAttributeWithName.cs @@ -0,0 +1,176 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +#if NET_2_0 +using System.Transactions; +#endif +using IsolationLevel=System.Data.IsolationLevel; + +namespace Spring.Transaction.Interceptor +{ + /// + /// ITransactionAttribute that delegates all calls to a give target attribute except for the + /// name, which is specified in the constructor. + /// + public class DelegatingTransactionAttributeWithName : ITransactionAttribute + { + private ITransactionAttribute targetAttribute; + private string joinpointIdentification; + + /// + /// Initializes a new instance of the class. + /// + /// The target attribute. + /// The identification. + public DelegatingTransactionAttributeWithName(ITransactionAttribute targetAttribute, string identification) + { + this.targetAttribute = targetAttribute; + joinpointIdentification = identification; + } + + /// + /// Decides if rollback is required for the supplied . + /// + /// The to evaluate. + /// + /// True if the exception causes a rollback, false otherwise. + /// + public bool RollbackOn(Exception exception) + { + return targetAttribute.RollbackOn(exception); + } + + /// + /// Return the propagation behavior of type + /// . + /// + /// + public TransactionPropagation PropagationBehavior + { + get { return targetAttribute.PropagationBehavior; } + } + + /// + /// Return the isolation level of type . + /// + /// + /// + ///

    + /// Only makes sense in combination with + /// and + /// . + ///

    + ///

    + /// Note that a transaction manager that does not support custom isolation levels + /// will throw an exception when given any other level than + /// . + ///

    + ///
    + public IsolationLevel TransactionIsolationLevel + { + get { return targetAttribute.TransactionIsolationLevel; } + } + + /// + /// Return the transaction timeout. + /// + /// + /// + ///

    + /// Must return a number of seconds, or -1. + /// Only makes sense in combination with + /// and + /// . + /// Note that a transaction manager that does not support timeouts will + /// throw an exception when given any other timeout than -1. + ///

    + ///
    + public int TransactionTimeout + { + get { return targetAttribute.TransactionTimeout; } + } + + /// + /// Get whether to optimize as read-only transaction. + /// + /// + /// + ///

    + /// This just serves as hint for the actual transaction subsystem, + /// it will not necessarily cause failure of write accesses. + ///

    + ///

    + /// Only makes sense in combination with + /// and + /// . + ///

    + ///

    + /// A transaction manager that cannot interpret the read-only hint + /// will not throw an exception when given ReadOnly=true. + ///

    + ///
    + public bool ReadOnly + { + get { return targetAttribute.ReadOnly; } + } + + /// + /// Return the name of this transaction. + /// + /// + /// + /// The exposed name will be the fully + /// qualified type name + "." method name + assembly (by default). + /// + public string Name + { + get { return joinpointIdentification; } + } + +#if NET_2_0 + + /// + /// Gets the enterprise services interop option. + /// + /// The enterprise services interop option. + public EnterpriseServicesInteropOption EnterpriseServicesInteropOption + { + get { return targetAttribute.EnterpriseServicesInteropOption; } + } +#endif + + /// + /// Return a description of this transaction attribute. + /// + /// + ///

    + /// The format matches the one used by the + /// , + /// to be able to feed any result into a + /// instance's properties. + ///

    + ///
    + public override string ToString() + { + return targetAttribute.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/ITransactionAttribute.cs b/src/Spring/Spring.Data/Transaction/Interceptor/ITransactionAttribute.cs new file mode 100644 index 00000000..b47686f1 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/ITransactionAttribute.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.ComponentModel; + +namespace Spring.Transaction.Interceptor +{ + /// + /// This interface adds a + /// + /// specification to . + /// + /// Griffin Caprio (.NET) + /// $Id: ITransactionAttribute.cs,v 1.6 2007/12/29 00:28:53 markpollack Exp $ + [TypeConverter(typeof(TransactionAttributeConverter))] + public interface ITransactionAttribute : ITransactionDefinition + { + /// + /// Decides if rollback is required for the supplied . + /// + /// The to evaluate. + /// True if the exception causes a rollback, false otherwise. + bool RollbackOn( Exception exception ); + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/ITransactionAttributeSource.cs b/src/Spring/Spring.Data/Transaction/Interceptor/ITransactionAttributeSource.cs new file mode 100644 index 00000000..92f19889 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/ITransactionAttributeSource.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Interface used by + /// to source transaction attributes. + /// + /// + ///

    + /// Implementations know how to source transaction attributes, whether from configuration, + /// metadata attributes at source level, or anywhere else. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: ITransactionAttributeSource.cs,v 1.7 2006/05/18 21:37:51 markpollack Exp $ + public interface ITransactionAttributeSource + { + /// + /// Return the for this + /// method. + /// + /// The method to check. + /// + /// The target . May be null, in which case the declaring + /// class of the supplied must be used. + /// + /// + /// A or + /// null if the method is non-transactional. + /// + ITransactionAttribute ReturnTransactionAttribute( MethodInfo method, Type targetType ); + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/MatchAlwaysTransactionAttributeSource.cs b/src/Spring/Spring.Data/Transaction/Interceptor/MatchAlwaysTransactionAttributeSource.cs new file mode 100644 index 00000000..29908e41 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/MatchAlwaysTransactionAttributeSource.cs @@ -0,0 +1,126 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Very simple implementation of + /// which will always return the same + /// for all methods fed to it. + /// + /// + ///

    + /// The + /// may be specified, but will otherwise default to PROPAGATION_REQUIRED. This may be + /// used in the cases where you want to use the same transaction attribute with all + /// methods being handled by a transaction interceptor. + ///

    + ///
    + /// Colin Sampaleanu + /// Griffin Caprio (.NET) + /// $Id: MatchAlwaysTransactionAttributeSource.cs,v 1.7 2007/12/29 00:28:53 markpollack Exp $ + [Serializable] + public class MatchAlwaysTransactionAttributeSource : ITransactionAttributeSource + { + private ITransactionAttribute _transactionAttribute; + + /// + /// Creates a new instance of the + /// + /// class. + /// + public MatchAlwaysTransactionAttributeSource() + { + _transactionAttribute = new DefaultTransactionAttribute(); + } + + /// + /// Allows a transaction attribute to be specified, using the + /// form, for example, "PROPAGATION_REQUIRED". + /// + public ITransactionAttribute TransactionAttribute + { + set { _transactionAttribute = value; } + } + + #region ITransactionAttributeSource Members + /// + /// Return the for this + /// method. + /// + /// The method to check. + /// + /// The target . May be null, in which case the declaring + /// class of the supplied must be used. + /// + /// + /// A or + /// null if the method is non-transactional. + /// + public ITransactionAttribute ReturnTransactionAttribute(MethodInfo method, Type targetType) + { + return _transactionAttribute; + } + #endregion + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + public override bool Equals(object obj) + { + if (this == obj) return true; + MatchAlwaysTransactionAttributeSource matchAlwaysTransactionAttributeSource = obj as MatchAlwaysTransactionAttributeSource; + if (matchAlwaysTransactionAttributeSource == null) return false; + return Equals(_transactionAttribute, matchAlwaysTransactionAttributeSource._transactionAttribute); + } + + /// + /// Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return _transactionAttribute != null ? _transactionAttribute.GetHashCode() : 0; + } + + + /// + ///Returns a that represents the current . + /// + /// + /// + ///A that represents the current . + /// + ///2 + public override string ToString() + { + return GetType().Name + ": " + _transactionAttribute; + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/MethodMapTransactionAttributeSource.cs b/src/Spring/Spring.Data/Transaction/Interceptor/MethodMapTransactionAttributeSource.cs new file mode 100644 index 00000000..89870d92 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/MethodMapTransactionAttributeSource.cs @@ -0,0 +1,294 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; + +using Common.Logging; +using Spring.Util; +using Spring.Core.TypeResolution; +using Spring.Core; + +#endregion + +namespace Spring.Transaction.Interceptor +{ + /// + /// Simple implementation of the + /// interface that allows attributes to be stored per method in a map. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: MethodMapTransactionAttributeSource.cs,v 1.15 2008/05/29 17:26:41 markpollack Exp $ + public class MethodMapTransactionAttributeSource : ITransactionAttributeSource + { + private IDictionary _methodMap; + private IDictionary _nameMap; + + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof (MethodMapTransactionAttributeSource)); + + #endregion + + /// + /// Creates a new instance of the + /// class. + /// + public MethodMapTransactionAttributeSource() + { + _methodMap = new Hashtable(); + _nameMap = new Hashtable(); + } + + /// + /// Set a name/attribute map, consisting of "FQCN.method, AssemblyName" method names + /// (e.g. "MyNameSpace.MyClass.MyMethod, MyAssembly") and ITransactionAttribute + /// instances (or Strings to be converted to instances). + /// + /// + ///

    + /// The key values of the supplied + /// must adhere to the following form:
    + /// FQCN.methodName, Assembly. + ///

    + /// + /// MyNameSpace.MyClass.MyMethod, MyAssembly + /// + ///
    + public IDictionary MethodMap + { + set + { + foreach (DictionaryEntry entry in value) + { + + string name = entry.Key as string; + ITransactionAttribute attribute = null; + //Check if we need to convert from a string to ITransactionAttribute + if (entry.Value is ITransactionAttribute) + { + attribute = entry.Value as ITransactionAttribute; + } + else + { + TransactionAttributeEditor editor = new TransactionAttributeEditor(); + editor.SetAsText(entry.Value.ToString()); + attribute = editor.Value; + } + AddTransactionalMethod( name, attribute ); + + } + + } + } + /// + /// Add an attribute for a transactional method. + /// + /// + /// Method names can end or start with "*" for matching multiple methods. + /// + /// The class and method name, separated by a dot. + /// The attribute to be associated with the method. + public void AddTransactionalMethod( string name, ITransactionAttribute attribute ) + { + AssertUtils.ArgumentNotNull(name, "name"); + int lastCommaIndex = name.LastIndexOf( "," ); + if (lastCommaIndex == -1) + { + throw new ArgumentException("'" + name + "'" + + " is not a valid method name, missing AssemblyName. Format is FQN.MethodName, AssemblyName."); + } + string fqnWithMethod = name.Substring(0, lastCommaIndex); + + int lastDotIndex = fqnWithMethod.LastIndexOf( "." ); + if ( lastDotIndex == -1 ) + { + throw new TransactionUsageException("'" + fqnWithMethod + "' is not a valid method name: format is FQN.methodName"); + } + string className = fqnWithMethod.Substring(0, lastDotIndex ); + string assemblyName = name.Substring(lastCommaIndex + 1); + string methodName = fqnWithMethod.Substring(lastDotIndex + 1 ); + string fqnClassName = className + ", " + assemblyName; + try + { + Type type = TypeResolutionUtils.ResolveType(fqnClassName); + AddTransactionalMethod( type, methodName, attribute ); + } catch ( TypeLoadException exception ) + { + throw new TransactionUsageException( "Type '" + fqnClassName + "' not found.", exception ); + } + } + + /// + /// Add an attribute for a transactional method. + /// + /// + /// Method names can end or start with "*" for matching multiple methods. + /// + /// The target interface or class. + /// The mapped method name. + /// + /// The attribute to be associated with the method. + /// + public void AddTransactionalMethod( + Type type, string mappedName, ITransactionAttribute transactionAttribute ) + { + // TODO address method overloading? At present this will + // simply match all methods that have the given name. + string name = type.Name + "." + mappedName; + MethodInfo[] methods = type.GetMethods(); + IList matchingMethods = new ArrayList(); + for ( int i = 0; i < methods.Length; i++ ) + { + if ( methods[i].Name.Equals( mappedName ) || IsMatch(methods[i].Name, mappedName )) + { + matchingMethods.Add( methods[i] ); + } + } + if ( matchingMethods.Count == 0 ) + { + throw new TransactionUsageException("Couldn't find method '" + mappedName + "' on Type [" + type.Name + "]"); + } + // register all matching methods + foreach ( MethodInfo currentMethod in matchingMethods ) + { + string regularMethodName = (string)_nameMap[currentMethod]; + if ( regularMethodName == null || ( !regularMethodName.Equals(name) && regularMethodName.Length <= name.Length)) + { + // No already registered method name, or more specific + // method name specification now -> (re-)register method. + if (LOG.IsDebugEnabled && regularMethodName != null) + { + LOG.Debug("Replacing attribute for transactional method [" + currentMethod + "]: current name '" + + name + "' is more specific than '" + regularMethodName + "'"); + } + _nameMap.Add( currentMethod, name ); + AddTransactionalMethod( currentMethod, transactionAttribute ); + } + else + { + if (LOG.IsDebugEnabled && regularMethodName != null) + { + LOG.Debug("Keeping attribute for transactional method [" + currentMethod + "]: current name '" + + name + "' is not more specific than '" + regularMethodName + "'"); + } + } + } + } + + /// + /// Add an attribute for a transactional method. + /// + /// The transactional method. + /// + /// The attribute to be associated with the method. + /// + public void AddTransactionalMethod( MethodInfo method, ITransactionAttribute transactionAttribute ) + { + _methodMap.Add( method, transactionAttribute ); + } + + /// + /// Does the supplied match the supplied ? + /// + /// + /// The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, + /// as well as direct equality. This behaviour can (of course) be overridden in + /// derived classes. + /// + /// The method name of the class. + /// The name in the descriptor. + /// True if the names match. + protected virtual bool IsMatch( string methodName, string mappedName ) + { + return PatternMatchUtils.SimpleMatch(mappedName, methodName); + } + + #region ITransactionAttributeSource Members + /// + /// Return the for this + /// method. + /// + /// The method to check. + /// + /// The target . May be null, in which case the declaring + /// class of the supplied must be used. + /// + /// + /// A or + /// null if the method is non-transactional. + /// + public ITransactionAttribute ReturnTransactionAttribute(MethodInfo method, Type targetType) + { + //Might have registered MethodInfo objects whose declaring type is the interface, so 'downcast' + //to the most specific method which is typically what is passed in as the first method argument. + foreach (DictionaryEntry dictionaryEntry in _methodMap) + { + MethodInfo currentMethod = (MethodInfo)dictionaryEntry.Key; + + MethodInfo specificMethod; + if (targetType == null) + { + specificMethod = currentMethod; + } + else + { + ComposedCriteria searchCriteria = new ComposedCriteria(); + searchCriteria.Add(new MethodNameMatchCriteria(currentMethod.Name)); + searchCriteria.Add(new MethodParametersCountCriteria(currentMethod.GetParameters().Length)); +#if NET_2_0 + searchCriteria.Add(new MethodGenericArgumentsCountCriteria( + currentMethod.GetGenericArguments().Length)); +#endif + searchCriteria.Add(new MethodParametersCriteria(ReflectionUtils.GetParameterTypes(currentMethod))); + + MemberInfo[] matchingMethods = targetType.FindMembers( + MemberTypes.Method, + BindingFlags.Instance | BindingFlags.Public, + new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria), + searchCriteria); + + if (matchingMethods != null && matchingMethods.Length == 1) + { + specificMethod = matchingMethods[0] as MethodInfo; + } + else + { + specificMethod = currentMethod; + } + } + + if (method == specificMethod) + { + return (ITransactionAttribute)dictionaryEntry.Value; + } + } + return (ITransactionAttribute)_methodMap[method]; + } + #endregion + + + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/NameMatchTransactionAttributeSource.cs b/src/Spring/Spring.Data/Transaction/Interceptor/NameMatchTransactionAttributeSource.cs new file mode 100644 index 00000000..4c1d57cf --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/NameMatchTransactionAttributeSource.cs @@ -0,0 +1,203 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Reflection; +using Common.Logging; +using Spring.Util; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Simple implementation of the + /// that allows attributes to be matched by registered name. + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: NameMatchTransactionAttributeSource.cs,v 1.9 2008/05/29 17:26:41 markpollack Exp $ + [Serializable] + public class NameMatchTransactionAttributeSource : ITransactionAttributeSource + { + /// + /// Logger available to subclasses, static for optimal serialization + /// + [NonSerialized()] + protected static readonly ILog log = LogManager.GetLogger(typeof(NameMatchTransactionAttributeSource)); + + /// + /// Keys are method names; values are ITransactionAttributes + /// + private IDictionary nameMap; + + /// + /// Creates a new instance of the + /// class. + /// + public NameMatchTransactionAttributeSource() + { + nameMap = new Hashtable(); + } + + /// + /// Set a name/attribute map, consisting of method names (e.g. "MyMethod") and + /// instances + /// (or Strings to be converted to ITransactionAttribute instances). + /// + public IDictionary NameMap + { + set + { + foreach (DictionaryEntry entry in value) + { + string name = entry.Key as string; + ITransactionAttribute attribute = null; + if (entry.Value is ITransactionAttribute) + { + attribute = entry.Value as ITransactionAttribute; + } + else + { + TransactionAttributeEditor editor = new TransactionAttributeEditor(); + editor.SetAsText(entry.Value.ToString()); + attribute = editor.Value; + } + AddTransactionMethod(name, attribute); + } + } + } + + /// + /// Parses the given properties into a name/attribute map. + /// + /// + /// Expects method names as keys and string attributes definitions as values, + /// parsable into + /// instances via + /// . + /// + /// The properties of the transaction. + public NameValueCollection NameProperties + { + set + { + SetProperties(value); + } + } + + /// + /// Does the supplied match the supplied ? + /// + /// + /// The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, + /// as well as direct equality. Can be overridden in subclasses. + /// + /// The method name of the class. + /// The name in the descriptor. + /// True if the names match. + protected virtual bool IsMatch( string methodName, string mappedName ) + { + return PatternMatchUtils.SimpleMatch(mappedName, methodName); + } + + /// + /// Add an attribute for a transactional method. + /// + /// + ///

    + /// Method names can end with "*" for matching multiple methods. + ///

    + ///
    + /// The transactional method name. + /// + /// The attribute to be associated with the method. + /// + public void AddTransactionMethod( string methodName, ITransactionAttribute attribute ) + { + #region Instrumentation + if (log.IsDebugEnabled) + { + log.Debug("Adding transactional method [" + methodName + "] with attribute [" + attribute + "]"); + } + #endregion + nameMap.Add( methodName, attribute ); + } + + /// + /// Parses the given properties into a name/attribute map. + /// + /// + /// Expects method names as keys and string attributes definitions as values, + /// parsable into + /// instances via + /// . + /// + /// The properties of the transaction. + public void SetProperties( NameValueCollection transactionAttributes ) + { + TransactionAttributeEditor editor = new TransactionAttributeEditor(); + foreach ( string methodName in transactionAttributes.Keys ) + { + string value = transactionAttributes[methodName]; + editor.SetAsText(value); + AddTransactionMethod(methodName, editor.Value); + } + } + + #region ITransactionAttributeSource Members + /// + /// Return the for this + /// method. + /// + /// The method to check. + /// + /// The target . May be null, in which case the declaring + /// class of the supplied must be used. + /// + /// + /// A or + /// null if the method is non-transactional. + /// + public ITransactionAttribute ReturnTransactionAttribute(MethodInfo method, Type targetType) + { + string methodName = method.Name; + ITransactionAttribute attribute = (ITransactionAttribute) nameMap[methodName]; + if ( attribute != null ) + { + return attribute; + } + else + { + string bestNameMatch = null; + foreach ( string mappedName in nameMap.Keys ) + { + if ( ( IsMatch( methodName, mappedName ) ) && ( bestNameMatch == null || bestNameMatch.Length <= mappedName.Length ) ) + { + attribute = (ITransactionAttribute) nameMap[mappedName]; + bestNameMatch = mappedName; + } + } + } + return attribute; + } + #endregion + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/NoRollbackRuleAttribute.cs b/src/Spring/Spring.Data/Transaction/Interceptor/NoRollbackRuleAttribute.cs new file mode 100644 index 00000000..6a34075b --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/NoRollbackRuleAttribute.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Tag class. Its class means it has the opposite behaviour to the + /// superclass. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: NoRollbackRuleAttribute.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + public class NoRollbackRuleAttribute : RollbackRuleAttribute + { + /// + /// Creates a new instance of the + /// class. + /// + public NoRollbackRuleAttribute( string exceptionType ) : base( exceptionType ){} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The class that will trigger a rollback. + /// + public NoRollbackRuleAttribute( Type exceptionType ) : base( exceptionType ){} + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/RollbackRuleAttribute.cs b/src/Spring/Spring.Data/Transaction/Interceptor/RollbackRuleAttribute.cs new file mode 100644 index 00000000..7e86714b --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/RollbackRuleAttribute.cs @@ -0,0 +1,202 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Aop.Framework; +using Spring.Util; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Rule determining whether or not a given exception (and any subclasses) should + /// cause a rollback. + /// + /// + ///

    + /// Multiple such rules can be applied to determine whether a transaction should commit + /// or rollback after an exception has been thrown. + ///

    + ///
    + /// Griffin Caprio (.NET) + /// $Id: RollbackRuleAttribute.cs,v 1.7 2008/03/31 20:09:59 markpollack Exp $ + [Serializable] + public class RollbackRuleAttribute : Attribute + { + private string _exceptionName; + + /// + /// Canonical instance representing default behavior for rolling back on + /// all s. + /// + public static RollbackRuleAttribute RollbackOnSystemExceptions + = new RollbackRuleAttribute(typeof (Exception).Name); + + /// + /// Creates a new instance of the + /// class + /// for the named . + /// + /// The exception name. + /// + ///

    + /// As always, the should be the full + /// assembly qualified version. + ///

    + ///
    + public RollbackRuleAttribute( string exceptionName ) + { + AssertUtils.ArgumentHasText(exceptionName, "exceptionName"); + _exceptionName = exceptionName; + } + + /// + /// Creates a new instance of the + /// class + /// for the suplied . + /// + /// + ///

    + /// The exception class must be or a subclass. + ///

    + ///

    + /// This is the preferred way to construct a + /// , + /// matching the exception class and subclasses. + ///

    + ///
    + /// + /// The class that will trigger a rollback. + /// + public RollbackRuleAttribute( Type exceptionType ) + { + AssertUtils.ArgumentNotNull(exceptionType, "exceptionType"); + if ( ! typeof(Exception).IsAssignableFrom( exceptionType ) ) + { + throw new AopConfigException("Cannot construct rollback rule from " + exceptionType + "; " + "It's not an Exception"); + } + _exceptionName = exceptionType.Name; + } + + /// + /// Returns the name of the exception. + /// + public string ExceptionName + { + get { return _exceptionName; } + } + + /// + /// Return the depth to the matching superclass execption . + /// + /// + /// A return value of 0 means that the matches. + /// + /// + /// The of exception to find. + /// + /// + /// Return -1 if there's no match. Otherwise, return depth. Lowest depth wins. + /// + public int GetDepth( Type exceptionType ) + { + return getDepth( exceptionType, 0 ); + } + + /// + /// Return the depth to the matching superclass execption . + /// + /// + /// A return value of 0 means that the s + /// matches. + /// + /// + /// The exception object to find. + /// + /// + /// Return -1 if there's no match. Otherwise, return depth. Lowest depth wins. + /// + public int GetDepth( object exception ) + { + return GetDepth( exception.GetType() ); + } + + /// + /// Returns a representation of this instance. + /// + /// + /// A containing the exception covered by this instance. + /// + public override string ToString() + { + return "RollbackRule with pattern '" + ExceptionName + "'"; + } + + /// + /// Override of . + /// + /// The hashcode of the exception name covered by this instance. + public override int GetHashCode() + { + return ExceptionName.GetHashCode(); + } + + /// + /// Override of . + /// + /// The object to compare. + /// True if the input object is equal to this instance. + public override bool Equals(object obj) + { + RollbackRuleAttribute rollbackRuleAttribute = obj as RollbackRuleAttribute; + if ( rollbackRuleAttribute == null ) + { + return false; + } + return Equals( rollbackRuleAttribute ); + } + + /// + /// Strongly typed Equals() implementation. + /// + /// + /// The to compare. + /// + /// + /// True if the input object is equal to the supplied . + /// + public bool Equals( RollbackRuleAttribute rollbackRuleAttribute ) + { + return ExceptionName.Equals(rollbackRuleAttribute.ExceptionName); + } + + private int getDepth( Type exceptionType, int depth ) + { + if ( ( exceptionType.Name.IndexOf( ExceptionName ) != -1 ) || ( exceptionType.FullName.IndexOf( ExceptionName ) != -1 ) ) + { + return depth; + } + if ( exceptionType == typeof(Exception)) + { + return -1; + } + return getDepth( exceptionType.BaseType, depth + 1 ); + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/RuleBasedTransactionAttribute.cs b/src/Spring/Spring.Data/Transaction/Interceptor/RuleBasedTransactionAttribute.cs new file mode 100644 index 00000000..9d362869 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/RuleBasedTransactionAttribute.cs @@ -0,0 +1,159 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Text; + +using Spring.Collections; + +namespace Spring.Transaction.Interceptor +{ + /// + /// implementation + /// that works out whether a given exception should cause transaction rollback by applying + /// a number of rollback rules, both positive and negative. + /// + /// + /// If no rules are relevant to the exception, it behaves like the + /// class + /// (rolling back on runtime exceptions).. + ///

    + /// The + /// creates objects of this class. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: RuleBasedTransactionAttribute.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + public class RuleBasedTransactionAttribute : DefaultTransactionAttribute + { + private IList _rollbackRules; + + /// + /// Creates a new instance of the + /// + /// class. + /// + /// + /// The desired transaction propagation behaviour. + /// + /// + /// The rollback rules list for this transaction attribute. + /// + public RuleBasedTransactionAttribute( + TransactionPropagation transactionPropagation, IList ruleList ) + : base(transactionPropagation) + { + _rollbackRules = ruleList; + } + + /// + /// Creates a new instance of the + /// + /// class. + /// + public RuleBasedTransactionAttribute( ) + { + _rollbackRules = new ArrayList(); + } + + /// + /// Sets the rollback rules list for this transaction attribute. + /// + public IList RollbackRules + { + set { _rollbackRules = value; } + } + + /// + /// Will a transaction be rolled back if the supplied + /// is thrown during the lifecycle of a transaction to which this attribute is applied? + /// + /// The offending . + /// True if the exception should cause a rollback, false otherwise. + public override bool RollbackOn(Exception exception) + { + RollbackRuleAttribute finalAttribute = null; + int deepest = Int32.MaxValue; + + if ( _rollbackRules != null ) + { + foreach ( RollbackRuleAttribute rule in _rollbackRules ) + { + int depth = rule.GetDepth( exception ); + if ( ( depth >= 0 ) && ( depth < deepest ) ) + { + deepest = depth; + finalAttribute = rule; + } + } + } + if ( null == finalAttribute ) + { + return base.RollbackOn( exception ); + } + return !( finalAttribute is NoRollbackRuleAttribute ); + } + + /// + /// Returns a representation of this instance. + /// + /// + /// A representation of this instance. + /// + public override string ToString() + { + StringBuilder result = new StringBuilder(); + result.Append(DefinitionDescription); + SortedSet rules = new SortedSet(); + foreach ( RollbackRuleAttribute rule in _rollbackRules ) + { + string sign = ( rule is NoRollbackRuleAttribute ) ? COMMIT_RULE_PREFIX : ROLLBACK_RULE_PREFIX; + rules.Add( sign + rule.ExceptionName ); + } + foreach ( string rule in rules ) + { + result.Append(','); + result.Append(rule); + } + return result.ToString( ); + } + /// + /// Adds a to this + /// attributes list of rollback rules. + /// + /// + /// The to add. + /// + public void AddRollbackRule( RollbackRuleAttribute rule ) + { + _rollbackRules.Add( rule ); + } + + /// + /// Clears the rollback rules for this attribute. + /// + public void ClearRollbackRules() + { + _rollbackRules.Clear(); + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAspectSupport.cs b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAspectSupport.cs new file mode 100644 index 00000000..661c6759 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAspectSupport.cs @@ -0,0 +1,496 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections.Specialized; +using System.Reflection; +using Common.Logging; +using Spring.Objects.Factory; +using Spring.Threading; +using Spring.Util; +using IsolationLevel=System.Data.IsolationLevel; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Superclass for transaction aspects, such as the AOP Alliance-compatible + /// . + /// + /// + ///

    + /// This enables the underlying Spring transaction infrastructure to be used + /// to easily implement an aspect for any aspect system. + ///

    + ///

    + /// Subclasses are responsible for calling methods in this class in the correct order. + ///

    + ///

    + /// Uses the Strategy design pattern. A + /// implementation will perform the actual transaction management + ///

    + ///

    + /// A transaction aspect is serializable if its + /// and + /// are serializable. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: TransactionAspectSupport.cs,v 1.15 2008/05/27 19:06:57 markpollack Exp $ + [Serializable] + public class TransactionAspectSupport : IInitializingObject + { + //TODO work on serialization support. + + #region TransactionInfo Class + /// + /// Opaque object used to hold transaction information. + /// + /// + ///

    + /// Subclasses must pass it back to method on this class, but not see its internals. + ///

    + ///
    + internal protected class TransactionInfo + { + + private ITransactionAttribute _transactionAttribute; + private string _joinpointIdentification; + private ITransactionStatus _transactionStatus; + private TransactionInfo _oldTransactionInfo; + + /// + /// Creates a new instance of the + /// + /// class for the supplied . + /// + /// The transaction attributes to associate with any transaction. + /// The info for diagnostic display of joinpoint + public TransactionInfo( ITransactionAttribute transactionAttribute, string joinpointIdentification) + { + _transactionAttribute = transactionAttribute; + _joinpointIdentification = joinpointIdentification; + } + + /// + /// Does this instance currently have a transaction? + /// + /// True if this instance has a transaction. + public bool HasTransaction + { + get { return _transactionStatus != null; } + } + + /// + /// Gets and sets the for this object. + /// + public ITransactionStatus TransactionStatus + { + set { _transactionStatus = value; } + get { return _transactionStatus; } + } + + /// + /// Binds this + /// + /// instance to the thread local storage variable for the current thread and + /// backs up the existing + /// + /// object for the current thread. + /// + public void BindToThread() + { + // Expose current TransactionStatus, preserving any existing TransactionStatus + // for restoration after this transaction is complete. + TransactionInfo currentTransactionInfo = LogicalThreadContext.GetData(CURRENT_TRANSACTIONINFO_SLOTNAME) as TransactionInfo; + _oldTransactionInfo = currentTransactionInfo; + LogicalThreadContext.SetData(CURRENT_TRANSACTIONINFO_SLOTNAME, this); + } + + /// + /// Restores the previous + /// + /// object to the current thread. + /// + public void RestoreThreadLocalStatus() + { + // Use stack to restore old transaction TransactionInfo. + // Will be null if none was set. + LogicalThreadContext.SetData(CURRENT_TRANSACTIONINFO_SLOTNAME, _oldTransactionInfo); + } + + /// + /// Gets the current + /// for this + /// + /// object. + /// + public ITransactionAttribute TransactionAttribute + { + get { return _transactionAttribute; } + } + + /// + /// Gets the joinpoint identification. + /// + /// The joinpoint identification. + public string JoinpointIdentification + { + get { return _joinpointIdentification; } + } + } + #endregion + + #region Logging Definition + + [NonSerialized] + protected ILog log = LogManager.GetLogger(typeof (TransactionAspectSupport)); + + #endregion + + + /// + /// The name in thread local storage where the TransactionInfo object is located + /// + public const string CURRENT_TRANSACTIONINFO_SLOTNAME = "TransactionAspectSupport.CurrentTransactionInfoSlotName"; + + /// + /// The currently referenced by + /// this aspect + /// + private IPlatformTransactionManager _transactionManager; + + /// + /// The currently + /// referenced by this aspect. + /// + private ITransactionAttributeSource _transactionAttributeSource; + + /// + /// Creates a new instance of the + /// class. + /// + public TransactionAspectSupport() {} + + /// + /// Gets and sets the for + /// this aspect. + /// + public IPlatformTransactionManager TransactionManager + { + get { return _transactionManager; } + set { _transactionManager = value; } + } + + /// + /// Gets and sets the + /// for + /// this aspect. + /// + public ITransactionAttributeSource TransactionAttributeSource + { + get { return _transactionAttributeSource; } + set { _transactionAttributeSource = value; } + } + + /// + /// Returns the + /// of the current method invocation. + /// + /// + /// Mainly intended for code that wants to set the current transaction + /// rollback-only but not throw an application exception. + /// + /// + /// If the transaction info cannot be found, because the method was invoked + /// outside of an AOP invocation context. + /// + public static ITransactionStatus CurrentTransactionStatus + { + get { return CurrentTransactionInfo.TransactionStatus; } + } + + /// + /// Subclasses can use this to return the current + /// . + /// + /// + /// Only subclasses that cannot handle all operations in one method + /// need to use this mechanism to get at the current + /// . + /// An around advice such as an AOP Alliance + /// can hold a reference to the + /// + /// throughout the aspect method. + /// A + /// will be returned even if no transaction was created. The + /// + /// property can be used to query this. + /// + /// + /// If no transaction has been created by an aspect. + /// + protected static TransactionInfo CurrentTransactionInfo + { + get + { + TransactionInfo currentTransactionInfo = + LogicalThreadContext.GetData(CURRENT_TRANSACTIONINFO_SLOTNAME) as TransactionInfo; + if (currentTransactionInfo == null) + { + throw new NoTransactionException("No transaction aspect-managed ITransactionStatus in scope"); + } + return currentTransactionInfo; + } + } + /// + /// Set properties with method names as keys and transaction attribute + /// descriptors (parsed via ) as values: + /// e.g. key = "MyMethod", value = "PROPAGATION_REQUIRED,readOnly". + /// + /// + /// + /// Method names are always applied to the target class, no matter if defined in an + /// interface or the class itself. + /// + ///

    + /// Internally, a + /// + /// will be created from the given properties. + ///

    + ///
    + public NameValueCollection TransactionAttributes + { + set + { + NameMatchTransactionAttributeSource attributeSource = new NameMatchTransactionAttributeSource(); + attributeSource.NameProperties = value; + _transactionAttributeSource = attributeSource; + } + } + + /// + /// Checks that the required properties are set. + /// + public void AfterPropertiesSet() + { + if ( null == _transactionManager) + { + throw new ArgumentException("IPlatformTransactionManager is required"); + } + if ( null == _transactionAttributeSource ) + { + throw new ArgumentException("Either 'TransactionAttributeSource' or 'TransactionAttribute' property is required: " + + "If there are no transactional methods, don't use a TransactionInterceptor " + + "or TransactionProxyFactoryObject." ); + } + } + + /// + /// Create a transaction if necessary + /// + /// Method about to execute + /// Type that the method is on + /// + /// A object, + /// whether or not a transaction was created. + ///

    + /// The + /// + /// property on the + /// + /// class can be used to tell if there was a transaction created. + ///

    + ///
    + protected TransactionInfo CreateTransactionIfNecessary( MethodInfo method, Type targetType ) + { + // If the transaction attribute is null, the method is non-transactional. + ITransactionAttribute sourceAttr = _transactionAttributeSource.ReturnTransactionAttribute( method, targetType ); + + return CreateTransactionIfNecessary(sourceAttr, MethodIdentification(method)); + } + + /// + /// Creates the transaction if necessary. + /// + /// The source transaction attribute. + /// The joinpoint identification. + /// Transaction Info for declarative transaction management. + protected TransactionInfo CreateTransactionIfNecessary(ITransactionAttribute sourceAttr, string joinpointIdentification) + { + ITransactionAttribute txAttr = sourceAttr; + + if (txAttr != null && txAttr.Name == null) + { + txAttr = new DelegatingTransactionAttributeWithName(txAttr, joinpointIdentification); + } + + TransactionInfo transactionInfo = new TransactionInfo(txAttr, joinpointIdentification); + if ( txAttr != null ) + { + // We need a transaction for this method + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Getting transaction for " + transactionInfo.JoinpointIdentification); + } + + #endregion + // The transaction manager will flag an error if an incompatible tx already exists + transactionInfo.TransactionStatus = _transactionManager.GetTransaction(txAttr); + } + else + { + // The TransactionInfo.HasTransaction property will return + // false. We created it only to preserve the integrity of + // the ThreadLocal stack maintained in this class. + if (log.IsDebugEnabled) + { + log.Debug("Don't need to create transaction for [" + joinpointIdentification + + "]: this method isn't transactional"); + } + } + + // We always bind the TransactionInfo to the thread, even if we didn't create + // a new transaction here. This guarantees that the TransactionInfo stack + // will be managed correctly even if no transaction was created by this aspect. + transactionInfo.BindToThread( ); + return transactionInfo; + } + + /// + /// Identifies the method by providing the qualfied method name. + /// + /// The method info. + /// qualified mehtod name. + protected string MethodIdentification(MethodInfo methodInfo) + { + return ObjectUtils.GetQualifiedMethodName(methodInfo); + } + + /// + /// Execute after the successful completion of call, but not after an exception was handled. + /// + /// + ///

    + /// Do nothing if we didn't create a transaction. + ///

    + ///
    + /// + /// The + /// + /// about the current transaction. + /// + protected void CommitTransactionAfterReturning( TransactionInfo transactionInfo ) + { + if ( transactionInfo != null && transactionInfo.HasTransaction ) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug("Invoking commit for transaction on " + transactionInfo.JoinpointIdentification); + } + + #endregion + + _transactionManager.Commit( transactionInfo.TransactionStatus ); + } + } + + /// + /// Handle a exception, closing out the transaction. + /// + /// + ///

    + /// We may commit or roll back, depending on our configuration. + ///

    + ///
    + /// + /// The + /// + /// about the current transaction. + /// + /// The encountered. + protected void CompleteTransactionAfterThrowing( + TransactionInfo transactionInfo, Exception exception ) + { + if ( transactionInfo != null && transactionInfo.HasTransaction ) + { + if (log.IsDebugEnabled) + { + log.Debug("Completing transaction for [" + transactionInfo.JoinpointIdentification + "] after exception: " + exception); + } + + if ( transactionInfo.TransactionAttribute.RollbackOn( exception )) + { + try + { + _transactionManager.Rollback( transactionInfo.TransactionStatus ); + } + catch (Exception e) + { + log.Error("Application exception overridden by rollback exception", e); + throw; + } + } + else + { + // We don't roll back on this exception. + // Will still roll back if TransactionStatus.RollbackOnly is true. + try + { + _transactionManager.Commit(transactionInfo.TransactionStatus); + } catch (Exception e) + { + log.Error("Application exception overriden by commit exception", e); + throw; + } + + } + } + } + /// + /// Resets the + /// + /// for this thread. + /// + /// + /// + /// Call this in all cases: exceptions or normal return. + /// + /// + /// + /// The + /// + /// about the current transaction. May be null. + /// + protected void CleanupTransactionInfo( TransactionInfo transactionInfo ) + { + if ( transactionInfo != null ) + { + transactionInfo.RestoreThreadLocalStatus( ); + } + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttribute.cs b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttribute.cs new file mode 100644 index 00000000..0a461ce5 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttribute.cs @@ -0,0 +1,203 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using Spring.Transaction.Support; + + +#endregion + +namespace Spring.Transaction.Interceptor +{ + /// + /// .NET Attribute for describing transactional behavior of methods in a class. + /// + /// This attribute type is generally directly comparable + /// to Spring's class and + /// in fact will + /// directly convert the data to a + /// so that Spring's transaction support code does not have to know about + /// attributes. If no rules are relevant to the exception it will be treaded + /// like DefaultTransactionAttribute, (rolling back on all exceptions). + /// + /// The default property values are TransactionPropagation.Required, IsolationLevel.ReadCommitted, + /// DefaultTransactionDefinition.TIMEOUT_DEFAULT (can be changed, by default is the default + /// value of the underlying transaction subsystem) + /// ReadOnly = false, and Type.EmtptyTypes specified for Rollbackfor and NoRollbackFor exception + /// types. + /// + /// + /// Mark Pollack (.NET) + /// $Id: TransactionAttribute.cs,v 1.8 2007/12/07 07:30:48 markpollack Exp $ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, + Inherited = true)] + [Serializable] + public class TransactionAttribute : Attribute + { + #region Fields + private TransactionPropagation _transactionPropagation = TransactionPropagation.Required; + private IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; + private int _timeout = DefaultTransactionDefinition.TIMEOUT_DEFAULT; + private bool _readOnly = false; + private Type[] _rollbackTypes = Type.EmptyTypes; + private Type[] _noRollbackTypes = Type.EmptyTypes; +#if NET_2_0 + private System.Transactions.EnterpriseServicesInteropOption _esInteropOption = + System.Transactions.EnterpriseServicesInteropOption.Automatic; +#endif + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public TransactionAttribute() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The transaction propagation. + public TransactionAttribute(TransactionPropagation transactionPropagation) : this() + { + _transactionPropagation = transactionPropagation; + } + + /// + /// Initializes a new instance of the class. + /// + /// The transaction propagation. + /// The isolation level. + public TransactionAttribute(TransactionPropagation transactionPropagation, + IsolationLevel isolationLevel) : this(transactionPropagation) + { + _isolationLevel = isolationLevel; + } + + /// + /// Initializes a new instance of the class. + /// + /// The isolation level. + public TransactionAttribute(IsolationLevel isolationLevel) + { + _isolationLevel = isolationLevel; + } + + #endregion + + #region Properties + + /// + /// Gets the transaction propagation. + /// + /// Defaults to TransactionPropagation.Required + /// The transaction propagation. + public TransactionPropagation TransactionPropagation + { + get { return _transactionPropagation; } + } + + /// + /// Gets the isolation level. + /// + /// Defaults to IsolationLevel.Unspecified + /// The isolation level. + public IsolationLevel IsolationLevel + { + get { return _isolationLevel; } + } + + /// + /// Gets or sets the timeout. + /// + /// Defaults to the default timeout of the underlying transaction system. + /// The timeout. + public int Timeout + { + get { return _timeout; } + set { _timeout = value; } + } + + /// + /// Gets or sets a value indicating whether the transaction is readonly. + /// + /// Defaults to false + /// true if read-only; otherwise, false. + public bool ReadOnly + { + get { return _readOnly; } + set { _readOnly = value; } + } + + /// + /// Gets or sets the zero or more exception types which + /// indicating which exception types must cause a transaction + /// rollback. + /// + /// This is the preferred way to construct a rollback rule, + /// matching the exception class and subclasses. + /// The rollback types. + public Type[] RollbackFor + { + get { return _rollbackTypes; } + set { _rollbackTypes = value; } + } + + /// + /// Gets or sets zero or more exceptions types which + /// indicationg which exception type must not + /// cause a transaction rollback. + /// + /// This is the preferred way to construct a rollback rule, + /// matching the exception type. + /// The no rollback for. + public Type[] NoRollbackFor + { + get { return _noRollbackTypes; } + set { _noRollbackTypes = value; } + } + + +#if NET_2_0 + /// + /// Gets or sets the enterprise services interop option. + /// + /// The enterprise services interop option. + public System.Transactions.EnterpriseServicesInteropOption EnterpriseServicesInteropOption + { + get { return _esInteropOption;} + set { _esInteropOption = value; } + } +#endif + + #endregion + + #region Methods + + #endregion + + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeConverter.cs b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeConverter.cs new file mode 100644 index 00000000..8a91e2ed --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeConverter.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +using System; +using System.ComponentModel; +using System.Globalization; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Type converter for + /// objects. + /// + /// + /// Takes s of the form + ///

    PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2

    + ///

    where only propagation code is required. For example:

    + ///

    PROPAGATION_MANDATORY,ISOLATION_DEFAULT

    + ///

    + /// The tokens can be in any order. Propagation and isolation codes + /// must use the names of the values in the + /// enumeration. Timeout values are in seconds. If no timeout is specified, the transaction + /// manager will apply a default timeout specific to the particular transaction manager. + ///

    + ///

    + /// A "+" before an exception name substring indicates that transactions should commit even + /// if this exception is thrown; a "-" that they should roll back. + ///

    + ///
    + /// Mark Pollack + /// $Id: TransactionAttributeConverter.cs,v 1.1 2007/12/29 00:28:53 markpollack Exp $ + public class TransactionAttributeConverter : TypeConverter + { + /// + /// Returns whether this converter can convert an object of the given type to an ITransactionAttribute, using the specified context. + /// + /// An that provides a format context. + /// A that represents the type you want to convert from. + /// + /// true if this converter can perform the conversion; otherwise, false. + /// + public override bool CanConvertFrom( + ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// Converts from string to ITransactionAttribute + /// + /// The context. + /// The culture. + /// The string value to convert + /// An ITransactionAttribute instance + public override object ConvertFrom( + ITypeDescriptorContext context, CultureInfo culture, object val) + { + if (val is string) + { + string value = val as string; + TransactionAttributeEditor editor = new TransactionAttributeEditor(); + editor.SetAsText(value); + return editor.Value; + } + return base.ConvertFrom(context, culture, val); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeEditor.cs b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeEditor.cs new file mode 100644 index 00000000..c64647cb --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeEditor.cs @@ -0,0 +1,154 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Data; +using Spring.Util; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Type converter for + /// objects. + /// + /// + /// Takes s of the form + ///

    PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2

    + ///

    where only propagation code is required. For example:

    + ///

    PROPAGATION_MANDATORY,ISOLATION_DEFAULT

    + ///

    + /// The tokens can be in any order. Propagation and isolation codes + /// must use the names of the values in the + /// enumeration. Timeout values are in seconds. If no timeout is specified, the transaction + /// manager will apply a default timeout specific to the particular transaction manager. + ///

    + ///

    + /// A "+" before an exception name substring indicates that transactions should commit even + /// if this exception is thrown; a "-" that they should roll back. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: TransactionAttributeEditor.cs,v 1.7 2007/12/07 07:30:48 markpollack Exp $ + public class TransactionAttributeEditor + { + private RuleBasedTransactionAttribute _attribute; + + /// + /// Parses the input properties string into a valid + /// instance + /// + /// + /// The string defining the transactional properties. + /// + public void SetAsText( string transactionProperties ) + { + if (!StringUtils.HasText(transactionProperties)) + { + _attribute = null; + } + else + { + string[] tokens = StringUtils.CommaDelimitedListToStringArray( transactionProperties ); + RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute(); + for ( int i = 0; i < tokens.Length; i++ ) + { + string token = tokens[i].Trim(); + if ( token.StartsWith(DefaultTransactionAttribute.PROPAGATION_CONSTANT_PREFIX)) + { + attribute.PropagationBehavior = convertPropagationValue( token ); + } else if ( token.StartsWith(DefaultTransactionAttribute.ISOLATION_CONSTANT_PREFIX ) ) + { + attribute.TransactionIsolationLevel = convertIsolationValue( token ); + } else if ( token.StartsWith(DefaultTransactionAttribute.TIMEOUT_PREFIX ) ) + { + string value = token.Substring(DefaultTransactionAttribute.TIMEOUT_PREFIX.Length); + attribute.TransactionTimeout = Convert.ToInt32( value ); + } else if ( token.StartsWith( DefaultTransactionAttribute.READ_ONLY_MARKER ) ) + { + attribute.ReadOnly = true; + } else if ( token.StartsWith( DefaultTransactionAttribute.COMMIT_RULE_PREFIX ) ) + { + attribute.AddRollbackRule( new NoRollbackRuleAttribute(token.Substring( 1 ) ) ); + } else if ( token.StartsWith( DefaultTransactionAttribute.ROLLBACK_RULE_PREFIX ) ) + { + attribute.AddRollbackRule( new RollbackRuleAttribute( token.Substring( 1 ) ) ); + } else + { + throw new ArgumentException("Illegal transaction attribute token: [" + token + "]"); + } + } + _attribute = attribute; + } + } + + /// + /// Gets the + /// from this editor. + /// + public ITransactionAttribute Value + { + get { return _attribute; } + } + + private TransactionPropagation convertPropagationValue( string transactionPropagation ) + { + switch ( transactionPropagation.ToUpper() ) + { + case "PROPAGATION_REQUIRED": + return TransactionPropagation.Required; + case "PROPAGATION_SUPPORTS": + return TransactionPropagation.Supports; + case "PROPAGATION_MANDATORY": + return TransactionPropagation.Mandatory; + case "PROPAGATION_REQUIRES_NEW": + return TransactionPropagation.RequiresNew; + case "PROPAGATION_NOT_SUPPORTED": + return TransactionPropagation.NotSupported; + case "PROPAGATION_NEVER": + return TransactionPropagation.Never; + case "PROPAGATION_NESTED": + return TransactionPropagation.Nested; + default: + throw new ArgumentException("Illegal transaction propagation token: [" + transactionPropagation + "]"); + } + } + + private IsolationLevel convertIsolationValue( string isolationLevel ) + { + switch ( isolationLevel.ToUpper() ) + { + case "ISOLATION_DEFAULT": + return IsolationLevel.ReadCommitted; + case "ISOLATION_READUNCOMMITTED": + return IsolationLevel.ReadUncommitted; + case "ISOLATION_READCOMMITTED": + return IsolationLevel.ReadCommitted; + case "ISOLATION_REPEATABLEREAD": + return IsolationLevel.RepeatableRead; + case "ISOLATION_SERIALIZABLE": + return IsolationLevel.Serializable; + default: + return IsolationLevel.ReadCommitted; + } + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeSourceAdvisor.cs b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeSourceAdvisor.cs new file mode 100644 index 00000000..b1403cf8 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeSourceAdvisor.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using Spring.Aop.Framework; +using Spring.Aop.Support; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Advisor driven by a , used to exclude + /// a from methods that + /// are non-transactional. + /// + /// + ///

    + /// Because the AOP framework caches advice calculations, this is normally + /// faster than just letting the + /// run and find out itself that it has no work to do. + ///

    + ///
    + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: TransactionAttributeSourceAdvisor.cs,v 1.11 2007/12/07 08:10:03 markpollack Exp $ + [Serializable] + public class TransactionAttributeSourceAdvisor : StaticMethodMatcherPointcutAdvisor + { + #region Fields + + private ITransactionAttributeSource _transactionAttributeSource; + + #endregion + + #region Constructor(s) + + /// + /// Initializes a new instance of the class. + /// + /// + ///

    + /// This is an abstract class, and as such has no publicly + /// visible constructors. + ///

    + ///
    + public TransactionAttributeSourceAdvisor() + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The pre-configured transaction interceptor. + /// + public TransactionAttributeSourceAdvisor( TransactionInterceptor transactionInterceptor ) + : base( transactionInterceptor ) + { + SetTxAttributeSource(transactionInterceptor); + } + + #endregion + + #region Properties + + /// + /// Sets the transaction interceptor. + /// + /// The transaction interceptor. + public TransactionInterceptor TransactionInterceptor + { + set + { + //TODO refactor + Advice = value; + SetTxAttributeSource(value); + } + } + + #endregion + + #region Methods + + /// + /// Tests the input method to see if it's covered by the advisor. + /// + /// The method to match. + /// The to match against. + /// + /// True if the supplied is covered by the advisor. + /// + public override bool Matches(MethodInfo method, Type targetClass) + { + return (_transactionAttributeSource.ReturnTransactionAttribute(method, targetClass) != null); + } + + /// + /// Sets the tx attribute source. + /// + /// The transaction interceptor. + protected void SetTxAttributeSource(TransactionInterceptor transactionInterceptor) + { + if (transactionInterceptor.TransactionAttributeSource == null) + { + throw new AopConfigException("Cannot construct a TransactionAttributeSourceAdvisor using a " + + "TransactionInterceptor that has no TransactionAttributeSource configured."); + } + _transactionAttributeSource = transactionInterceptor.TransactionAttributeSource; + } + + #endregion + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeSourceEditor.cs b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeSourceEditor.cs new file mode 100644 index 00000000..18aef49b --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionAttributeSourceEditor.cs @@ -0,0 +1,149 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using Spring.Util; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Editor that can convert values into + /// instances. + /// + /// + /// The transaction attribute string must be parseable by the + /// in this package. + ///

    + /// Strings must be specified in the following syntax:
    + /// FQCN.methodName=<transaction attribute string> (sans <>). + ///

    + /// + /// ExampleNamespace.ExampleClass.MyMethod=PROPAGATION_MANDATORY,ISOLATION_DEFAULT + /// + /// + /// The specified class must be the one where the methods are defined; in the case of + /// implementing an interface, the interface class name must be specified. + /// + ///

    + /// This will register all overloaded methods for a given name. Does not support explicit + /// registration of certain overloaded methods. Supports wildcard style mappings (in + /// the form "xxx*"), e.g. "Notify*" for "Notify" and "NotifyAll". + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: TransactionAttributeSourceEditor.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + public class TransactionAttributeSourceEditor + { + #region PropertiesEditor Class + /// + /// Internal class to parse property values. + /// + protected internal class PropertiesEditor + { + private IDictionary _properties; + + /// + /// Creates a new instance of the + /// class. + /// + /// The property values to be parsed. + public PropertiesEditor( string properties ) + { + _properties = new Hashtable(); + parseProperties( properties ); + } + + /// + /// Indexer to return values based on index value. + /// + public string this[ string index ] + { + get { return (string) _properties[index]; } + } + + /// + /// Returns the collection of keys for properties. + /// + public ICollection Keys + { + get { return _properties.Keys; } + } + + private void parseProperties( string properties ) + { + string[] tokens = StringUtils.DelimitedListToStringArray( properties, "\n"); + foreach ( string token in tokens ) + { + string[] property = token.Split('='); + _properties.Add( property[0].Trim(), property[1].Trim()); + } + } + } + #endregion + + private ITransactionAttributeSource _attributeSource; + + /// + /// Creates a new instance of the + /// class. + /// + public TransactionAttributeSourceEditor() {} + + /// + /// Parses the input properties into a valid + /// + /// instance + /// + /// The properties string to be parsed. + public void SetAsText( string attributeSource ) + { + MethodMapTransactionAttributeSource source = new MethodMapTransactionAttributeSource(); + if ( attributeSource == null || attributeSource.Length == 0 ) + { + _attributeSource = null; + } else + { + PropertiesEditor editor = new PropertiesEditor(attributeSource); + TransactionAttributeEditor tae = new TransactionAttributeEditor(); + + foreach ( string name in editor.Keys ) + { + string value = editor[name]; + tae.SetAsText( value ); + ITransactionAttribute transactionAttribute = tae.Value; + source.AddTransactionalMethod( name, transactionAttribute ); + } + } + _attributeSource = source; + } + + /// + /// Gets the + /// from this instance. + /// + public ITransactionAttributeSource Value + { + get { return _attributeSource; } + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/TransactionInterceptor.cs b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionInterceptor.cs new file mode 100644 index 00000000..9e8731ab --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionInterceptor.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using AopAlliance.Intercept; + +namespace Spring.Transaction.Interceptor +{ + /// + /// An AOP Alliance providing + /// declarative transaction management using the common Spring.NET transaction infrastructure. + /// + /// + ///

    + /// That class contains the necessary calls into Spring.NET's underlying + /// transaction API: subclasses such as this are responsible for calling + /// superclass methods such as + /// + /// in the correct order, in the event of normal invocation return or an exception. + ///

    + ///

    + /// s are thread-safe. + ///

    + ///
    + /// Rod Johnson + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: TransactionInterceptor.cs,v 1.12 2007/09/07 04:46:53 markpollack Exp $ + [Serializable] + public class TransactionInterceptor : TransactionAspectSupport, IMethodInterceptor + { + /// + /// AOP Alliance invoke call that handles all transaction plumbing. + /// + /// + /// The method that is to execute in the context of a transaction. + /// + /// The return value from the method invocation. + public object Invoke(IMethodInvocation invocation) + { + // Work out the target class: may be null. + // The TransactionAttributeSource should be passed the target class + // as well as the method, which may be from an interface. + Type targetType = ( invocation.This != null ) ? invocation.This.GetType() : null; + + // If the transaction attribute is null, the method is non-transactional. + TransactionInfo txnInfo = CreateTransactionIfNecessary( invocation.Method, targetType ); + object returnValue = null; + + try + { + // This is an around advice. + // Invoke the next interceptor in the chain. + // This will normally result in a target object being invoked. + returnValue = invocation.Proceed(); + } + catch ( Exception ex ) + { + // target invocation exception + CompleteTransactionAfterThrowing( txnInfo, ex ); + throw; + } + finally + { + CleanupTransactionInfo( txnInfo ); + } + CommitTransactionAfterReturning( txnInfo ); + return returnValue; + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/TransactionProxyFactoryObject.cs b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionProxyFactoryObject.cs new file mode 100644 index 00000000..0b05b2aa --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Interceptor/TransactionProxyFactoryObject.cs @@ -0,0 +1,353 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; + +using Spring.Aop; +using Spring.Aop.Framework; +using Spring.Aop.Framework.Adapter; +using Spring.Aop.Framework.DynamicProxy; +using Spring.Aop.Support; +using Spring.Aop.Target; +using Spring.Core.TypeResolution; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Transaction.Interceptor +{ + /// + /// Proxy factory object for simplified declarative transaction handling. + /// + /// + ///

    + /// Alternative to the standard AOP + /// with a . + ///

    + ///

    + /// This class is intended to cover the typical case of declarative + /// transaction demarcation: namely, wrapping a (singleton) target object with a + /// transactional proxy, proxying all the interfaces that the target implements. + ///

    + ///

    + /// Internally, a + /// instance is used, but the user of this class does not have to care. Optionally, an + /// can be specified to cause conditional invocation of + /// the underlying . + ///

    + ///

    + /// The + /// + /// and + /// + /// properties can be set to add additional interceptors to the mix. + ///

    + ///
    + /// Juergen Hoeller + /// Dmitriy Kopylenko + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: TransactionProxyFactoryObject.cs,v 1.7 2007/08/01 17:56:16 markpollack Exp $ + public class TransactionProxyFactoryObject : ProxyConfig, IFactoryObject, IInitializingObject + { + private TransactionInterceptor _transactionInterceptor; + private object _target; + private Type[] _proxyInterfaces; + private TruePointcut _pointcut; + private object[] _preInterceptors; + private object[] _postInterceptors; + private IAdvisorAdapterRegistry _advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.Instance; + private object _proxy; + + /// + /// Creates a new instance of the + /// class. + /// + public TransactionProxyFactoryObject() + { + _transactionInterceptor = new TransactionInterceptor(); + } + + /// + /// Set the transaction manager for this factory. + /// + /// + /// It is this instance that will perform actual transaction management: this class is + /// just a way of invoking it. + /// + public IPlatformTransactionManager PlatformTransactionManager + { + set { _transactionInterceptor.TransactionManager = value; } + } + + /// + /// Set the target object, i.e. the object to be wrapped with a transactional proxy. + /// + /// + ///

    + /// The target may be any object, in which case a + /// will + /// be created. If it is a , no wrapper + /// is created: this enables the use of a pooling + /// or prototype . + ///

    + ///
    + public object Target + { + set { _target = value; } + } + + /// + /// Specify the set of interfaces being proxied. + /// + /// + ///

    + /// If left null (the default), the AOP infrastructure works + /// out which interfaces need proxying by analyzing the target, + /// proxying all of the interfaces that the target object implements. + ///

    + ///
    + public string[] ProxyInterfaces + { + set { _proxyInterfaces = TypeResolutionUtils.ResolveInterfaceArray( value ); } + } + + /// + /// Set properties with method names as keys and transaction attribute + /// descriptors as values. + /// + /// + ///

    + /// The various transaction attribute descriptors are parsed via + /// an instance of the + /// class. + ///

    + /// + /// Method names are always applied to the target class, no matter if defined in an + /// interface or the class itself. + /// + ///

    + /// Internally, a + /// + /// will be created from the given properties. + ///

    + ///
    + /// + ///

    + /// An example string (method name and transaction attributes) might be: + ///

    + ///

    + /// key = "myMethod", value = "PROPAGATION_REQUIRED,readOnly". + ///

    + ///
    + public NameValueCollection TransactionAttributes + { + set { _transactionInterceptor.TransactionAttributes = value; } + } + + /// + /// Set the transaction attribute source which is used to find transaction + /// attributes. + /// + /// + ///

    + /// If specifying a property value, an + /// appropriate + /// implementation will create a + /// + /// from the value. + ///

    + ///
    + public ITransactionAttributeSource TransactionAttributeSource + { + set { _transactionInterceptor.TransactionAttributeSource = value; } + } + + /// + /// Set a pointcut, i.e an object that can cause conditional invocation + /// of the + /// depending on method and attributes passed. + /// + /// + /// + /// Additional interceptors are always invoked. + /// + /// + public TruePointcut TruePointcut + { + set { _pointcut = value; } + } + + /// + /// Set additional interceptors (or advisors) to be applied before the + /// implicit transaction interceptor. + /// + public object[] PreInterceptors + { + set { _preInterceptors = value; } + } + + /// + /// Set additional interceptors (or advisors) to be applied after the + /// implicit transaction interceptor. + /// + /// + ///

    + /// Note that this is just necessary if you rely on those interceptors in general. + ///

    + ///
    + public object[] PostInterceptors + { + set { _postInterceptors = value; } + } + + /// + /// Specify the to use. + /// + /// The default instance is the global AdvisorAdapterRegistry. + public IAdvisorAdapterRegistry AdvisorAdapterRegistry + { + set { _advisorAdapterRegistry = value; } + } + + #region IFactoryObject Members + /// + /// Returns the object for this proxy factory. + /// + public Type ObjectType + { + get + { + if ( _proxy != null ) + { + return _proxy.GetType(); + } + else if ( _target != null && _target is ITargetSource ) + { + return _target.GetType(); + } + else + { + return null; + } + } + } + /// + /// Returns the object wrapped by this proxy factory. + /// + /// The target object proxy. + public object GetObject() + { + return _proxy; + } + + /// + /// Is this object a singleton? Always returns true in this implementation. + /// + public bool IsSingleton + { + get + { + return true; + } + } + #endregion + + #region IInitializingObject Members + /// + /// Method run after all the properties have been set for this object. + /// Responsible for actual proxy creation. + /// + public void AfterPropertiesSet() + { + _transactionInterceptor.AfterPropertiesSet(); + + if ( _target == null ) + { + throw new ArgumentException("'target' is required."); + } + ProxyFactory proxyFactory = new ProxyFactory(); + + if ( _preInterceptors != null ) + { + for ( int i = 0; i < _preInterceptors.Length; i++ ) + { + proxyFactory.AddAdvisor(_advisorAdapterRegistry.Wrap(_preInterceptors[i])); + } + } + if ( _pointcut != null ) + { + IAdvisor advice = new DefaultPointcutAdvisor(_pointcut, _transactionInterceptor); + proxyFactory.AddAdvisor(advice); + } + else + { + proxyFactory.AddAdvisor( new TransactionAttributeSourceAdvisor( _transactionInterceptor ) ); + } + if ( _postInterceptors != null ) + { + for ( int i = 0; i < _postInterceptors.Length; i++ ) + { + proxyFactory.AddAdvisor(_advisorAdapterRegistry.Wrap(_postInterceptors[i])); + } + } + proxyFactory.CopyFrom(this); + proxyFactory.TargetSource = createTargetSource(_target); + if ( _proxyInterfaces != null ) + { + proxyFactory.Interfaces = _proxyInterfaces; + } + else if ( !ProxyTargetType ) + { + if ( _target is ITargetSource ) + { + throw new AopConfigException("Either 'ProxyInterfaces' or 'ProxyTargetType' is required " + + "when using an ITargetSource as 'target'"); + } + proxyFactory.Interfaces = AopUtils.GetAllInterfaces(_target); + } + _proxy = proxyFactory.GetProxy(); + } + #endregion + + /// + /// Set the target or . + /// + /// + /// The target. If this is an implementation of the + /// interface, it is used as our ; otherwise it is + /// wrapped in a . + /// + /// An for this object. + protected ITargetSource createTargetSource( object target ) + { + if ( target is ITargetSource ) + { + return (ITargetSource) target; + } else + { + return new SingletonTargetSource( target ); + } + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/InvalidIsolationLevelException.cs b/src/Spring/Spring.Data/Transaction/InvalidIsolationLevelException.cs new file mode 100644 index 00000000..5b2547dd --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/InvalidIsolationLevelException.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + + /// + /// Exception that gets thrown when an invalid isolation level is specified, + /// i.e. an isolation level that the transaction manager implementation + /// doesn't support. + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: InvalidIsolationLevelException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class InvalidIsolationLevelException : TransactionUsageException + { + /// + /// Creates a new instance of the + /// class. + /// + public InvalidIsolationLevelException( ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public InvalidIsolationLevelException( String message ) : base(message) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public InvalidIsolationLevelException(string message, Exception rootCause) + : base(message, rootCause) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected InvalidIsolationLevelException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/InvalidTimeoutException.cs b/src/Spring/Spring.Data/Transaction/InvalidTimeoutException.cs new file mode 100644 index 00000000..f4f2c31e --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/InvalidTimeoutException.cs @@ -0,0 +1,130 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Exception that gets thrown when an invalid timeout is specified, + /// for example when the transaction manager implementation doesn't support timeouts. + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: InvalidTimeoutException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class InvalidTimeoutException : TransactionUsageException, ISerializable + { + /// + /// Invalid timeout value. + /// + private int _timeout = -1; + + /// + /// Returns the invalid timeout for this exception. + /// + public int Timeout + { + get + { + return _timeout; + } + } + + /// + /// Creates a new instance of the + /// class + /// with the specified message and timeout value. + /// + /// + /// A message about the exception. + /// + /// The (possibly invalid) timeout value. + public InvalidTimeoutException(string message, int timeout):base(message) + { + _timeout = timeout; + } + + /// + /// Creates a new instance of the + /// class. + /// + public InvalidTimeoutException( ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public InvalidTimeoutException( String message ) : base(message) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public InvalidTimeoutException(string message, Exception rootCause) + : base(message, rootCause) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected InvalidTimeoutException( + SerializationInfo info, StreamingContext context ) : base( info, context ) + { + _timeout = info.GetInt32( "timeout" ); + } + + /// + /// Override of + /// to allow for private serialization. + /// + /// + /// The + /// that holds the serialized object data about the exception. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue( "timeout", _timeout ); + base.GetObjectData( info, context ); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/NestedTransactionNotSupportedException.cs b/src/Spring/Spring.Data/Transaction/NestedTransactionNotSupportedException.cs new file mode 100644 index 00000000..fadda7d6 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/NestedTransactionNotSupportedException.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Exception thrown when attempting to work with a nested transaction + /// but nested transactions are not supported by the underlying backend. + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: NestedTransactionNotSupportedException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class NestedTransactionNotSupportedException : CannotCreateTransactionException + { + /// + /// Creates a new instance of the + /// class. + /// + public NestedTransactionNotSupportedException( ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public NestedTransactionNotSupportedException( String message ) : base(message) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public NestedTransactionNotSupportedException(string message, Exception rootCause) + : base(message, rootCause) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected NestedTransactionNotSupportedException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/NoTransactionException.cs b/src/Spring/Spring.Data/Transaction/NoTransactionException.cs new file mode 100644 index 00000000..863010d6 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/NoTransactionException.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Exception thrown when an operation is attempted that relies on an existing + /// transaction (such as setting rollback status) and there is no existing transaction. + /// This represents an illegal usage of the transaction API. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: NoTransactionException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class NoTransactionException : TransactionUsageException + { + /// + /// Creates a new instance of the + /// class. + /// + public NoTransactionException( ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public NoTransactionException( String message ) : base(message) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public NoTransactionException(string message, Exception rootCause) + : base(message, rootCause) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected NoTransactionException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Support/AbstractPlatformTransactionManager.cs b/src/Spring/Spring.Data/Transaction/Support/AbstractPlatformTransactionManager.cs new file mode 100644 index 00000000..d286696a --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/AbstractPlatformTransactionManager.cs @@ -0,0 +1,1179 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Data; +using Common.Logging; + +namespace Spring.Transaction.Support +{ + /// + /// Abstract base class that allows for easy implementation of concrete platform transaction managers. + /// + /// + ///

    Provides the following workflow handling: + ///

      + ///
    • Determines if there is an existing transaction
    • + ///
    • Applies the appropriate propagation behavior
    • + ///
    • Suspends and resumes transactions if necessary
    • + ///
    • Checks the rollback-only flag on commit
    • + ///
    • Applies the appropriate modification on rollback (actual rollback or setting rollback-only)
    • + ///
    • Triggers registered synchronization callbacks (if transaction synchronization is active)
    • + ///
    + ///

    + ///

    + /// Transaction synchronization is a generic mechanism for registering + /// callbacks that get invoked at transaction completion time. The same mechanism + /// can also be used for custom synchronization efforts. + ///

    + ///

    + /// The state of this class is serializable. It's up to subclasses if + /// they wish to make their state to be serializable. + /// They should implement if they need + /// to restore any transient state. + ///

    + ///
    + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// Griffin Caprio (.NET) + [Serializable] + public abstract class AbstractPlatformTransactionManager : IPlatformTransactionManager + { + #region Private SuspendedResourcesHolder Helper class + + private class SuspendedResourcesHolder + { + private IList _suspendedSynchronizations; + private object _suspendedResources; + private string _name; + private bool _readOnly; + private IsolationLevel _isolationLevel; + private bool _wasActive; + + + public SuspendedResourcesHolder(object suspendedResources) + { + _suspendedResources = suspendedResources; + } + + public SuspendedResourcesHolder(IList suspendedSynchronizations, object suspendedResources, + string name, bool readOnly, IsolationLevel isolationLevel, bool wasActive) + { + _suspendedSynchronizations = suspendedSynchronizations; + _suspendedResources = suspendedResources; + _name = name; + _readOnly = readOnly; + _isolationLevel = isolationLevel; + _wasActive = wasActive; + } + + public IList SuspendedSynchronizations + { + get { return _suspendedSynchronizations; } + } + + public object SuspendedResources + { + get { return _suspendedResources; } + } + + + public string Name + { + get { return _name; } + } + + public bool ReadOnly + { + get { return _readOnly; } + } + + public IsolationLevel IsolationLevel + { + get { return _isolationLevel; } + } + + public bool WasActive + { + get { return _wasActive; } + } + } + + #endregion + + #region Private Variables + + private TransactionSynchronizationState _transactionSyncState = TransactionSynchronizationState.Always; + private bool _nestedTransactionsAllowed; + private bool _rollbackOnCommitFailure; + private bool _failEarlyOnGlobalRollbackOnly; + private int _defaultTimeout = DefaultTransactionDefinition.TIMEOUT_DEFAULT; + + #region Logging Definition + + [NonSerialized()] protected ILog log = LogManager.GetLogger(typeof (AbstractPlatformTransactionManager)); + + #endregion + + #endregion + + #region Public Properties + + /// + /// Sets and gets when this transaction manager should activate the thread-bound + /// transaction synchronization support. Default is "always". + /// + /// + ///

    + /// Note that transaction synchronization isn't supported for + /// multiple concurrent transactions by different transaction managers. + /// Only one transaction manager is allowed to activate it at any time. + ///

    + /// + ///
    + public TransactionSynchronizationState TransactionSynchronization + { + set { _transactionSyncState = value; } + get { return _transactionSyncState; } + } + + /// + /// Sets and gets whether nested transactions are allowed. Default is false. + /// + /// + ///

    + /// Typically initialized with an appropriate default by the + /// concrete transaction manager subclass. + ///

    + ///
    + public bool NestedTransactionsAllowed + { + get { return _nestedTransactionsAllowed; } + set { _nestedTransactionsAllowed = value; } + } + + /// + /// Sets and gets a flag that determines whether or not the + /// + /// method must be invoked if a call to the + /// + /// method fails. Default is false. + /// + /// + /// Typically not necessary and thus to be avoided as it can override the + /// commit exception with a subsequent rollback exception. + /// + public bool RollbackOnCommitFailure + { + get { return _rollbackOnCommitFailure; } + set { _rollbackOnCommitFailure = value; } + } + + + /// + /// Gets or sets a value indicating whether to fail early in case of the transaction being + /// globally marked as rollback-only. + /// + /// + /// Default is "false", only causing an UnexpectedRollbackException at the + /// outermost transaction boundary. Switch this flag on to cause an + /// UnexpectedRollbackException as early as the global rollback-only marker + /// has been first detected, even from within an inner transaction boundary. + /// + /// + /// true if fail early on global rollback; otherwise, false. + /// + public bool FailEarlyOnGlobalRollbackOnly + { + get { return _failEarlyOnGlobalRollbackOnly; } + set { _failEarlyOnGlobalRollbackOnly = value; } + } + + /// + /// Gets or sets the default timeout that this transaction manager should apply if there + /// is no timeout specified at the transaction level, in seconds. + /// + /// Returns DefaultTransactionDefinition.TIMEOUT_DEFAULT to indicate the + /// underlying transaction infrastructure's default timeout. + /// The default timeout. + public int DefaultTimeout + { + get + { + return _defaultTimeout; + } + set + { + if (_defaultTimeout < DefaultTransactionDefinition.TIMEOUT_DEFAULT) + { + throw new InvalidTimeoutException("Invalid default timeout", _defaultTimeout); + } + _defaultTimeout = value; + } + } + + #endregion + + #region Abstract Methods + + /// + /// Return the current transaction object. + /// + /// The current transaction object. + /// + /// If transaction support is not available. + /// + /// + /// In the case of lookup or system errors. + /// + protected abstract object DoGetTransaction(); + + /// + /// Check if the given transaction object indicates an existing transaction + /// (that is, a transaction which has already started). + /// + /// + /// The result will be evaluated according to the specified propagation + /// behavior for the new transaction. An existing transaction might get + /// suspended (in case of PROPAGATION_REQUIRES_NEW), or the new transaction + /// might participate in the existing one (in case of PROPAGATION_REQUIRED). + /// Default implementation returns false, assuming that detection of or + /// participating in existing transactions is generally not supported. + /// Subclasses are of course encouraged to provide such support. + /// + /// Transaction object returned by + /// . + /// + /// True if there is an existing transaction. + /// + /// In the case of system errors. + /// + protected virtual bool IsExistingTransaction(object transaction) + { + return false; + } + + /// + /// Begin a new transaction with the given transaction definition. + /// + /// + /// Does not have to care about applying the propagation behavior, + /// as this has already been handled by this abstract manager. + /// + /// + /// Transaction object returned by + /// . + /// + /// + /// instance, describing + /// propagation behavior, isolation level, timeout etc. + /// + /// + /// In the case of creation or system errors. + /// + protected abstract void DoBegin(object transaction, ITransactionDefinition definition); + + /// + /// Suspend the resources of the current transaction. + /// + /// + /// Transaction synchronization will already have been suspended. + /// + /// Default implementation throws a TransactionSuspensionNotSupportedException, + /// assuming that transaction suspension is generally not supported. + /// + /// + /// + /// Transaction object returned by + /// . + /// + /// + /// An object that holds suspended resources (will be kept unexamined for passing it into + /// .) + /// + /// + /// If suspending is not supported by the transaction manager implementation. + /// + /// + /// in case of system errors. + /// + protected virtual object DoSuspend(object transaction) + { + throw new TransactionSuspensionNotSupportedException( + "Transaction manager [" + GetType().Name + "] does not support transaction suspension"); + } + + /// + /// Resume the resources of the current transaction. + /// + /// Transaction synchronization will be resumed afterwards. + /// + /// Default implementation throws a TransactionSuspensionNotSupportedException, + /// assuming that transaction suspension is generally not supported. + /// + /// + /// + /// Transaction object returned by + /// . + /// + /// + /// The object that holds suspended resources as returned by + /// . + /// + /// + /// If suspending is not supported by the transaction manager implementation. + /// + /// + /// In the case of system errors. + /// + protected virtual void DoResume(object transaction, object suspendedResources) + { + throw new TransactionSuspensionNotSupportedException( + "Transaction manager [" + GetType().Name + "] does not support transaction suspension"); + } + + /// + /// Perform an actual commit on the given transaction. + /// + /// The status representation of the transaction. + /// + ///

    + /// An implementation does not need to check the rollback-only flag. + ///

    + ///
    + /// + /// In the case of system errors. + /// + protected abstract void DoCommit(DefaultTransactionStatus status); + + /// + /// Perform an actual rollback on the given transaction. + /// + /// The status representation of the transaction. + /// + /// An implementation does not need to check the new transaction flag. + /// + /// + /// In the case of system errors. + /// + protected abstract void DoRollback(DefaultTransactionStatus status); + + /// + /// Set the given transaction rollback-only. Only called on rollback + /// if the current transaction takes part in an existing one. + /// + /// Default implementation throws an IllegalTransactionStateException, + /// assuming that participating in existing transactions is generally not + /// supported. Subclasses are of course encouraged to provide such support. + /// + /// The status representation of the transaction. + /// + /// In the case of system errors. + /// + protected virtual void DoSetRollbackOnly(DefaultTransactionStatus status) + { + throw new IllegalTransactionStateException( + "Participating in existing transactions is not supported - when 'IsExistingTransaction' " + + "returns true, appropriate 'DoSetRollbackOnly' behavior must be provided"); + } + + /// + /// Return whether to use a savepoint for a nested transaction. Default is true, + /// which causes delegation to + /// for holding a savepoint. + /// + /// + /// + ///

    + /// Subclasses can override this to return false, causing a further + /// invocation of + /// + /// despite an already existing transaction. + ///

    + ///
    + protected virtual bool UseSavepointForNestedTransaction() + { + return true; + } + + /// + /// Cleanup resources after transaction completion. + /// + /// + /// Transaction object returned by + /// . + /// + /// + ///

    + /// Called after + /// and + /// + /// execution on any outcome. + ///

    + ///

    + /// Should not throw any exceptions but just issue warnings on errors. + ///

    + ///

    + /// Default implementation does nothing. + ///

    + ///
    + protected virtual void DoCleanupAfterCompletion(object transaction) + { + } + + #endregion + + #region IPlatformTransactionManager Members + + /// + /// Return a currently active transaction or create a new one. + /// + /// + ///

    + /// This implementation handles propagation behavior. + ///

    + ///

    + /// Delegates to + /// , + /// , + /// and + /// . + ///

    + ///

    + /// Note that parameters like isolation level or timeout will only be applied + /// to new transactions, and thus be ignored when participating in active ones. + /// Furthermore, they aren't supported by every transaction manager: + /// a proper implementation should throw an exception when custom values + /// that it doesn't support are specified. + ///

    + ///
    + /// + /// instance (can be null for + /// defaults), describing propagation behavior, isolation level, timeout etc. + /// + /// + /// In case of lookup, creation, or system errors. + /// + /// + /// representing the new or current transaction. + /// + public ITransactionStatus GetTransaction(ITransactionDefinition definition) + { + object transaction = DoGetTransaction(); + bool debugEnabled = log.IsDebugEnabled; + bool newSynchronization; + + if (debugEnabled) + { + log.Debug("Using transaction object [" + transaction + "]"); + } + + if (definition == null) + { + definition = new DefaultTransactionDefinition(); + } + if (IsExistingTransaction(transaction)) + { + // Existing transaction found -> check propagation behavior to find out how to behave. + return HandleExistingTransaction(definition, transaction, debugEnabled); + } + + // Check definition settings for new transaction. + if (definition.TransactionTimeout < DefaultTransactionDefinition.TIMEOUT_DEFAULT) + { + throw new InvalidTimeoutException("Invalid transaction timeout", definition.TransactionTimeout); + } + + // No existing transaction found -> check propagation behavior to find out how to proceed. + if (definition.PropagationBehavior == TransactionPropagation.Mandatory) + { + throw new IllegalTransactionStateException( + "Transaction propagation 'mandatory' but no existing transaction found"); + } + else if (definition.PropagationBehavior == TransactionPropagation.Required || + definition.PropagationBehavior == TransactionPropagation.RequiresNew || + definition.PropagationBehavior == TransactionPropagation.Nested) + { + object suspendedResources = Suspend(null); + if (debugEnabled) + { + log.Debug("Creating new transaction with name [" + definition.Name + "]:" + definition); + } + try + { + DoBegin(transaction, definition); + } catch (TransactionException) + { + Resume(null, suspendedResources); + throw; + } + newSynchronization = (_transactionSyncState != TransactionSynchronizationState.Never); + return NewTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, + suspendedResources); + } + else + { + // Create "empty" transaction: no actual transaction, but potentially synchronization. + newSynchronization = (_transactionSyncState == TransactionSynchronizationState.Always); + return NewTransactionStatus(definition, null, false, newSynchronization, debugEnabled, null); + + } + + } + + private ITransactionStatus HandleExistingTransaction(ITransactionDefinition definition, object transaction, bool debugEnabled) + { + //bool newSynchronization; + if (definition.PropagationBehavior == TransactionPropagation.Never) + { + throw new IllegalTransactionStateException( + "Transaction propagation 'never' but existing transaction found."); + } + if (definition.PropagationBehavior == TransactionPropagation.NotSupported) + { + if (debugEnabled) + { + log.Debug("Suspending current transaction"); + } + object suspendedResources = Suspend(transaction); + bool newSynchronization = (_transactionSyncState == TransactionSynchronizationState.Always); + return + NewTransactionStatus(definition, null, false, newSynchronization, debugEnabled, + suspendedResources); + } + + if (definition.PropagationBehavior == TransactionPropagation.RequiresNew) + { + if (debugEnabled) + { + log.Debug("Suspending current transaction, creating new transaction with name [" + + definition.Name + "]:" + definition); + } + object suspendedResources = Suspend(transaction); + try + { + DoBegin(transaction, definition); + } + catch (TransactionException beginEx) + { + try + { + Resume(transaction, suspendedResources); + } + catch (TransactionException resumeEx) + { + log.Error( + "Inner transaction begin exception overridden by outer transaction resume exception"); + log.Error("Begin Transaction Exception", beginEx); + log.Error("Resume Transaction Exception", resumeEx); + throw; + } + throw; + } + bool newSynchronization = (_transactionSyncState != TransactionSynchronizationState.Never); + return + NewTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); + } + if (definition.PropagationBehavior == TransactionPropagation.Nested) + { + if (!NestedTransactionsAllowed) + { + throw new NestedTransactionNotSupportedException( + "Transaction manager does not allow nested transactions by default - " + + "specify 'NestedTransactionsAllowed' property with value 'true'"); + } + if (debugEnabled) + { + log.Debug("Creating nested transaction with name [" + definition.Name + "]:" + definition); + } + + if (UseSavepointForNestedTransaction()) + { + DefaultTransactionStatus status = + NewTransactionStatus(definition, transaction, false, false, debugEnabled, null); + status.CreateAndHoldSavepoint(DateTime.Now.ToLongTimeString()); + return status; + } + else + { + DoBegin(transaction, definition); + bool newSynchronization = (_transactionSyncState != TransactionSynchronizationState.Never); + return NewTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null); + + } + } + // Assumably PROPAGATION_SUPPORTS. + if (debugEnabled) { + log.Debug("Participating in existing transaction"); + } + bool newSynch = (_transactionSyncState != TransactionSynchronizationState.Never); + return NewTransactionStatus(definition, transaction, false, newSynch, debugEnabled, null); + + } + + /// + /// This implementation of commit handles participating in existing transactions + /// and programmatic rollback requests. + /// + /// + /// + /// + /// ITransactionStatus object returned by the + /// () method. + /// + /// + /// In case of commit or system errors. + /// + public void Commit(ITransactionStatus transactionStatus) + { + if (transactionStatus.Completed) + { + throw new IllegalTransactionStateException( + "Transaction is already completed - do not call commit or rollback more than once per transaction"); + } + + DefaultTransactionStatus defaultStatus = (DefaultTransactionStatus) transactionStatus; + if (defaultStatus.LocalRollbackOnly) + { + if (defaultStatus.Debug) + { + log.Debug("Transaction code has requested rollback"); + } + ProcessRollback(defaultStatus); + return; + } + if ( !ShouldCommitOnGlobalRollbackOnly && defaultStatus.GlobalRollbackOnly) + { + if (defaultStatus.Debug) + { + log.Debug("Global transaction is marked as rollback-only but transactional code requested commit"); + } + ProcessRollback(defaultStatus); + // Throw UnexpectedRollbackException only at outermost transaction boundary + // or if explicitly asked to. + if (defaultStatus.IsNewTransaction || FailEarlyOnGlobalRollbackOnly) + { + throw new UnexpectedRollbackException( + "Transaction rolled back because it has been marked as rollback-only"); + } + return; + } + ProcessCommit(defaultStatus); + + } + + protected virtual bool ShouldCommitOnGlobalRollbackOnly + { + get { return false; } + } + + private void ProcessCommit(DefaultTransactionStatus status) + { + try + { + bool beforeCompletionInvoked = false; + try + { + TriggerBeforeCommit(status); + TriggerBeforeCompletion(status); + beforeCompletionInvoked = true; + bool globalRollbackOnly = false; + if (status.IsNewTransaction || FailEarlyOnGlobalRollbackOnly) + { + globalRollbackOnly = status.GlobalRollbackOnly; + } + if (status.HasSavepoint) + { + status.ReleaseHeldSavepoint(); + } + else if (status.IsNewTransaction) + { + DoCommit(status); + } + // Throw UnexpectedRollbackException if we have a global rollback-only + // marker but still didn't get a corresponding exception from commit. + if (globalRollbackOnly) + { + throw new UnexpectedRollbackException( + "Transaction silently rolled back because it has been marked as rollback-only"); + } + } + catch (UnexpectedRollbackException) + { + TriggerAfterCompletion(status, TransactionSynchronizationStatus.Rolledback); + throw; + } + catch (TransactionException ex) + { + if (RollbackOnCommitFailure) + { + DoRollbackOnCommitException(status, ex); + } + else + { + TriggerAfterCompletion(status, TransactionSynchronizationStatus.Unknown); + } + throw; + } + catch (Exception ex) + { + if (!beforeCompletionInvoked) + { + TriggerBeforeCompletion(status); + } + DoRollbackOnCommitException(status, ex); + throw; + } + // Trigger AfterCommit callbacks, with an exception thrown there + // propagated to callers but the transaction still considered as commited. + try + { + TriggerAfterCommit(status); + } + finally + { + TriggerAfterCompletion(status, TransactionSynchronizationStatus.Committed); + } + } + finally + { + CleanupAfterCompletion(status); + } + } + + private void TriggerAfterCommit(DefaultTransactionStatus status) + { + if (status.NewSynchronization) + { + if (status.Debug) + { + log.Debug("Trigger AfterCommit Synchronization"); + } + IList synchronizations = TransactionSynchronizationManager.Synchronizations; + foreach (ITransactionSynchronization currentTxnSynchronization in synchronizations) + { + try + { + currentTxnSynchronization.AfterCommit(); + } + catch (Exception e) + { + log.Error("TransactionSynchronization.AfterCommit thew exception", e); + } + } + } + } + + /// + /// Roll back the given transaction, with regard to its status. + /// + /// + ///

    + /// This implementation handles participating in existing transactions. + ///

    + ///

    + /// Delegates to + /// , + /// and + /// . + ///

    + ///

    + /// If the transaction wasn't a new one, just set it rollback-only + /// to take part in the surrounding transaction properly. + ///

    + ///
    + /// + /// ITransactionStatusObject returned by the + /// () method. + /// + /// + /// In case of system errors. + /// + public void Rollback(ITransactionStatus transactionStatus) + { + if (transactionStatus.Completed) + { + throw new IllegalTransactionStateException( + "Transaction is already completed - do not call commit or rollback more than once per transaction"); + } + DefaultTransactionStatus defaultStatus = (DefaultTransactionStatus) transactionStatus; + ProcessRollback(defaultStatus); + } + + private void ProcessRollback(DefaultTransactionStatus status) + { + try + { + try + { + TriggerBeforeCompletion(status); + if (status.HasSavepoint) + { + if (status.Debug) + { + log.Debug("Rolling back transaction to savepoint."); + } + status.RollbackToHeldSavepoint(); + } + else if (status.IsNewTransaction) + { + if (status.Debug) + { + log.Debug("Initiating transaction rollback"); + } + DoRollback(status); + } + else if (status.HasTransaction()) + { + if (status.LocalRollbackOnly) + { + if(status.Debug) + { + log.Debug("Participating transaction failed - marking existing transaction as rollback-only"); + } + } + DoSetRollbackOnly(status); + } + else + { + log.Debug("Should roll back transaction but cannot - no transaction available."); + } + } + catch (Exception) + { + TriggerAfterCompletion(status, TransactionSynchronizationStatus.Unknown); + throw; + } + TriggerAfterCompletion(status, TransactionSynchronizationStatus.Rolledback); + } + finally + { + CleanupAfterCompletion(status); + } + } + + #endregion + + #region Protected Method + + private DefaultTransactionStatus NewTransactionStatus(ITransactionDefinition definition, + object transaction, bool newTransaction, + bool newSynchronization, bool debug, + object suspendedResources) + { + bool actualNewSynchronization = newSynchronization && + !TransactionSynchronizationManager.SynchronizationActive; + if (actualNewSynchronization) + { + TransactionSynchronizationManager.ActualTransactionActive = (transaction != null); + TransactionSynchronizationManager.CurrentTransactionIsolationLevel = + definition.TransactionIsolationLevel; + TransactionSynchronizationManager.CurrentTransactionReadOnly = definition.ReadOnly; + TransactionSynchronizationManager.CurrentTransactionName = definition.Name; + TransactionSynchronizationManager.InitSynchronization(); + } + return + new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization, definition.ReadOnly, debug, + suspendedResources); + } + + /// + /// Determines the timeout to use for the given definition. Will fall back to this manager's default + /// timeout if the transaction definition doesn't specify a non-default value. + /// + /// The transaction definition. + /// the actual timeout to use. + protected int DetermineTimeout(ITransactionDefinition definition) + { + if (definition.TransactionTimeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT) + { + return definition.TransactionTimeout; + } + return _defaultTimeout; + } + + #endregion + + #region Private Methods + + + + + + /// + /// Suspend the given transaction. Suspends transaction synchronization first, + /// then delegates to the doSuspend template method. + /// + /// the current transaction object + /// an object that holds suspended resources + private object Suspend(object transaction) + { + if (TransactionSynchronizationManager.SynchronizationActive) + { + IList suspendedSynchronizations = DoSuspendSynchronization(); + + try + { + object suspendedResources = null; + if (transaction != null) + { + suspendedResources = DoSuspend(transaction); + } + + string name = TransactionSynchronizationManager.CurrentTransactionName; + TransactionSynchronizationManager.CurrentTransactionName = null; + bool readOnly = TransactionSynchronizationManager.CurrentTransactionReadOnly; + TransactionSynchronizationManager.CurrentTransactionReadOnly = false; + IsolationLevel isolationLevel = TransactionSynchronizationManager.CurrentTransactionIsolationLevel; + TransactionSynchronizationManager.CurrentTransactionIsolationLevel = IsolationLevel.Unspecified; + bool wasActive = TransactionSynchronizationManager.ActualTransactionActive; + TransactionSynchronizationManager.ActualTransactionActive = false; + + + return new SuspendedResourcesHolder(suspendedSynchronizations, suspendedResources, + name, readOnly, isolationLevel, wasActive); + + } catch (TransactionException) + { + // DoSuspend failed - original transaction is still active + DoResumeSynchronization(suspendedSynchronizations); + throw; + } + } + else if (transaction != null) + { + // Transaction active but no synchronization active. + object suspendedResources = DoSuspend(transaction); + return new SuspendedResourcesHolder(suspendedResources); + } + else + { + // Neither transaction nor synchronization active. + return null; + } + + } + + private IList DoSuspendSynchronization() + { + IList suspendedSynchronizations = TransactionSynchronizationManager.Synchronizations; + foreach (ITransactionSynchronization currentTxnSynchronization in suspendedSynchronizations) + { + currentTxnSynchronization.Suspend(); + } + TransactionSynchronizationManager.ClearSynchronization(); + return suspendedSynchronizations; + } + + private void DoResumeSynchronization(IList suspendedSynchronizations) + { + TransactionSynchronizationManager.InitSynchronization(); + foreach (ITransactionSynchronization currentTxnSynchronization in suspendedSynchronizations) + { + currentTxnSynchronization.Resume(); + TransactionSynchronizationManager.RegisterSynchronization(currentTxnSynchronization); + } + } + + /// + /// Resume the given transaction. Delegates to the doResume template method + /// first, then resuming transaction synchronization. + /// + /// the current transaction object + /// the object that holds suspended resources, as returned by suspend + private void Resume(object transaction, object suspendedResources) + { + SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources; + if (resourcesHolder != null) + { + object suspendedResourcesObject = resourcesHolder.SuspendedResources; + if (suspendedResourcesObject != null) + { + DoResume(transaction, suspendedResourcesObject); + } + IList suspendedSynchronizations = resourcesHolder.SuspendedSynchronizations; + if (suspendedSynchronizations != null) + { + TransactionSynchronizationManager.ActualTransactionActive = resourcesHolder.WasActive; + TransactionSynchronizationManager.CurrentTransactionIsolationLevel = resourcesHolder.IsolationLevel; + TransactionSynchronizationManager.CurrentTransactionReadOnly = resourcesHolder.ReadOnly; + TransactionSynchronizationManager.CurrentTransactionName = resourcesHolder.Name; + DoResumeSynchronization(suspendedSynchronizations); + } + + } + } + + + + /// + /// Invoke doRollback, handling rollback exceptions properly. + /// + /// object representing the transaction + /// the thrown application exception or error + /// + /// in case of a rollback error + /// + private void DoRollbackOnCommitException(DefaultTransactionStatus status, Exception exception) + { + try + { + if (status.IsNewTransaction) + { + if (status.Debug) + { + log.Debug("Initiating transaction rollback on commit exception."); + } + DoRollback(status); + } + else if (status.HasTransaction()) + { + if (status.Debug) + { + log.Debug("Marking existing transaction as rollback-only after commit exception", exception); + } + DoSetRollbackOnly(status); + } + } + catch (Exception) + { + //TODO investigate rollback behavior... + log.Error("Commit exception overridden by rollback exception", exception); + TriggerAfterCompletion(status, TransactionSynchronizationStatus.Unknown); + throw; + } + TriggerAfterCompletion(status, TransactionSynchronizationStatus.Rolledback); + } + + /// + /// Trigger beforeCommit callback. + /// + /// object representing the transaction + private void TriggerBeforeCommit(DefaultTransactionStatus status) + { + if (status.NewSynchronization) + { + IList synchronizations = TransactionSynchronizationManager.Synchronizations; + foreach (ITransactionSynchronization currentTxnSynchronization in synchronizations) + { + currentTxnSynchronization.BeforeCommit(status.ReadOnly); + } + } + } + + /// + /// Trigger beforeCompletion callback. + /// + /// object representing the transaction + private void TriggerBeforeCompletion(DefaultTransactionStatus status) + { + if (status.NewSynchronization) + { + if (status.Debug) + { + log.Debug("Trigger BeforeCompletion Synchronization"); + } + IList synchronizations = TransactionSynchronizationManager.Synchronizations; + foreach (ITransactionSynchronization synchronization in synchronizations) + { + try + { + synchronization.BeforeCompletion(); + } + catch (Exception e) + { + log.Error("TransactionSynchronization.BeforeCompletion threw exception", e); + } + } + } + } + + /// + /// Trigger afterCompletion callback, handling exceptions properly. + /// + /// object representing the transaction + /// + /// Completion status according to + /// + private void TriggerAfterCompletion(DefaultTransactionStatus status, TransactionSynchronizationStatus completionStatus) + { + if (status.NewSynchronization) + { + IList synchronizations = TransactionSynchronizationManager.Synchronizations; + if (!status.HasTransaction() || status.IsNewTransaction) + { + if (status.Debug) + { + log.Debug("Triggering afterCompletion synchronization"); + } + InvokeAfterCompletion(synchronizations, completionStatus); + } + else + { + //TODO investigate parallel of JTA/System.Txs + log.Info("Transaction controlled outside of spring tx manager."); + } + } + } + + private void InvokeAfterCompletion(IList synchronizations, TransactionSynchronizationStatus status) + { + foreach (ITransactionSynchronization synchronization in synchronizations) + { + try + { + synchronization.AfterCompletion(status); + } catch (Exception e) + { + log.Error("TransactionSynchronization.AfterCompletion threw exception", e); + } + } + } + + /// + /// Clean up after completion, clearing synchronization if necessary, + /// and invoking doCleanupAfterCompletion. + /// + /// object representing the transaction + private void CleanupAfterCompletion(DefaultTransactionStatus status) + { + status.Completed = true; + if (status.NewSynchronization) + { + TransactionSynchronizationManager.Clear(); + } + if (status.IsNewTransaction) + { + DoCleanupAfterCompletion(status.Transaction); + } + if (status.SuspendedResources != null) + { + if (status.Debug) + { + log.Debug("Resuming suspended transaction"); + } + Resume(status.Transaction, status.SuspendedResources); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Support/DefaultTransactionDefinition.cs b/src/Spring/Spring.Data/Transaction/Support/DefaultTransactionDefinition.cs new file mode 100644 index 00000000..52dd0725 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/DefaultTransactionDefinition.cs @@ -0,0 +1,289 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Data; +using System.Text; + +namespace Spring.Transaction.Support +{ + /// + /// Default implementation of the + /// interface, offering object-style configuration and sensible default values. + /// + /// + ///

    + /// Base class for both and + /// . + ///

    + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: DefaultTransactionDefinition.cs,v 1.15 2007/12/07 08:10:03 markpollack Exp $ + [Serializable] + public class DefaultTransactionDefinition : ITransactionDefinition + { + /// + /// Prefix for Propagation settings. + /// + public static readonly string PROPAGATION_CONSTANT_PREFIX = "PROPAGATION"; + + /// + /// Prefix for IsolationLevel settings. + /// + public static readonly string ISOLATION_CONSTANT_PREFIX = "ISOLATION"; + + /// + /// Prefix for transaction timeout values in description strings. + /// + public static readonly string TIMEOUT_PREFIX = "timeout_"; + + /// + /// Marker for read-only transactions in description strings. + /// + public static readonly string READ_ONLY_MARKER = "readOnly"; + + /// + /// The default transaction timeout. + /// + public const int TIMEOUT_DEFAULT = -1; + + //TODO Refactoring to sync with Spring 2.0 for nt/enums for various default values. + + private TransactionPropagation _transactionPropagation = TransactionPropagation.Required; + private IsolationLevel _transactionIsolationLevel = IsolationLevel.ReadCommitted; + private int _timeout = DefaultTransactionDefinition.TIMEOUT_DEFAULT; + private bool _readOnly = false; + private string _name = null; +#if NET_2_0 + private System.Transactions.EnterpriseServicesInteropOption _esInteropOption; + +#endif + + /// + /// Creates a new instance of the + /// class. + /// + public DefaultTransactionDefinition() {} + + /// + /// Creates a new instance of the + /// class + /// with the supplied + /// behaviour. + /// + /// + /// The desired behavior. + /// + public DefaultTransactionDefinition( TransactionPropagation transactionPropagation ) + { + _transactionPropagation = transactionPropagation; + } + + #region ITransactionDefinition Members + + // TODO change method name to same as type returned (TransactionPropagation) + + + + /// + /// Gets / Sets the propagation + /// behavior. + /// + public TransactionPropagation PropagationBehavior + { + get { return _transactionPropagation; } + set { _transactionPropagation = value; } + } + + + // TODO change method name to same as type returned (TransactionPropagation) + + + /// + /// Return the isolation level of type . + /// + /// + ///

    + /// Only makes sense in combination with + /// and + /// . + ///

    + ///

    + /// Note that a transaction manager that does not support custom isolation levels + /// will throw an exception when given any other level than + /// . + ///

    + ///
    + public IsolationLevel TransactionIsolationLevel + { + get { return _transactionIsolationLevel; } + set { _transactionIsolationLevel = value; } + } + + /// + /// Return the transaction timeout. + /// + /// + ///

    + /// Must return a number of seconds, or -1. + /// Only makes sense in combination with + /// and + /// . + /// Note that a transaction manager that does not support timeouts will + /// throw an exception when given any other timeout than -1. + ///

    + ///
    + public int TransactionTimeout + { + get { return _timeout; } + set + { + if ( value < DefaultTransactionDefinition.TIMEOUT_DEFAULT ) + { + throw new ArgumentException( "Timeout must be a positive integer or DefaultTransactionDefinition.TIMEOUT_DEFAULT" ); + } + _timeout = value; + } + } + + /// + /// Get whether to optimize as read-only transaction. + /// + /// + ///

    + /// This just serves as hint for the actual transaction subsystem, + /// it will not necessarily cause failure of write accesses. + ///

    + ///

    + /// Only makes sense in combination with + /// and + /// . + ///

    + ///

    + /// A transaction manager that cannot interpret the read-only hint + /// will not throw an exception when given ReadOnly=true. + ///

    + ///
    + public bool ReadOnly + { + get { return _readOnly; } + set { _readOnly = value; } + } + + /// + /// Return the name of this transaction. Can be null. + /// + /// + /// + /// This will be used as a transaction name to be shown in a + /// transaction monitor, if applicable. In the case of Spring + /// declarative transactions, the exposed name will be the fully + /// qualified type name + "." method name + assembly (by default). + /// + public string Name + { + get { return _name; } + set { _name = value;} + } + +#if NET_2_0 + /// + /// Gets the enterprise services interop option. + /// + /// The enterprise services interop option. + public System.Transactions.EnterpriseServicesInteropOption EnterpriseServicesInteropOption + { + get { return _esInteropOption; } + set { _esInteropOption = value; } + } +#endif + + #endregion + + /// + /// An override of the default method. + /// + /// The to compare to. + /// True if the objects are equal. + public override bool Equals(object obj) + { + return ( obj is ITransactionDefinition ) && ToString().Equals( obj.ToString() ); + } + + /// + /// An override of the default method that returns the + /// hashcode of the + /// + /// property. + /// + /// + /// The hashcode of the + /// + /// property. + /// + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + + /// + /// An override of the default method that returns a string + /// representation of the + /// + /// property. + /// + /// + /// A string representation of the + /// + /// property. + /// + public override string ToString() + { + return DefinitionDescription; + } + + /// + /// Returns a representation of the + /// + /// property. + /// + protected string DefinitionDescription + { + get + { + StringBuilder builder = new StringBuilder(); + builder.Append( PROPAGATION_CONSTANT_PREFIX +"_" + PropagationBehavior ); + builder.Append( "," ); + builder.Append( ISOLATION_CONSTANT_PREFIX + "_" +TransactionIsolationLevel ); + if ( TransactionTimeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT ) + { + builder.Append( ",timeout_" + _timeout ); + } + if ( ReadOnly ) + { + builder.Append( ",readOnly" ); + } + return builder.ToString(); + } + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/DefaultTransactionStatus.cs b/src/Spring/Spring.Data/Transaction/Support/DefaultTransactionStatus.cs new file mode 100644 index 00000000..3f176273 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/DefaultTransactionStatus.cs @@ -0,0 +1,328 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +namespace Spring.Transaction.Support +{ + /// + /// Default implementation of the interface, + /// used by . + /// + /// + ///

    + /// Holds all status information that + /// + /// needs internally, including a generic transaction object determined by + /// the concrete transaction manager implementation. + ///

    + ///

    + /// Supports delegating savepoint-related methods to a transaction object + /// that implements the interface. + ///

    + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: DefaultTransactionStatus.cs,v 1.14 2007/12/06 06:23:33 markpollack Exp $ + public class DefaultTransactionStatus : ITransactionStatus + { + private object _transaction; + private bool _newTransaction; + private bool _newSynchronization; + private bool _readOnly; + private readonly bool _debug; + private object _suspendedResources; + + private string _savepoint; + private bool _rollbackOnly; + private bool _completed = false; + + /// + /// Creates a new instance of the + /// class. + /// + /// The underlying transaction object that can hold state for the internal + /// transaction implementation. + /// True if the transaction is new, else false if participating in an existing transaction. + /// True if a new transaction synchronization has been opened for the given + /// . + /// True if the transaction is read only. + /// if set to true, enable debug log in tx managers. + /// The suspended resources for the given . + public DefaultTransactionStatus( object transaction, + bool newTransaction, + bool newSynchronization, + bool readOnly, + bool debug, + object suspendedResources) + { + _transaction = transaction; + _newTransaction = newTransaction; + _newSynchronization = newSynchronization; + _readOnly = readOnly; + _debug = debug; + _suspendedResources = suspendedResources; + } + + #region Properties + + /// + /// Gets a value indicating whether the progress of this transaction is debugged. + /// This is used by AbstractPlatformTransactionManager as an optimization, to prevent repeated + /// calls to log.IsDebug. Not really intended for client code. + /// true if debug; otherwise, false. + public bool Debug + { + get { return _debug; } + } + + /// + /// Returns the underlying transaction object. + /// + public object Transaction + { + get { return _transaction; } + } + + /// + /// Gets or sets a value indicating whether the Transaction is completed, that is commited or rolled back. + /// + /// true if completed; otherwise, false. + public bool Completed + { + get { return _completed; } + set { _completed = value; } + } + + /// + /// Returns true if the underlying transaction is read only. + /// + public bool ReadOnly + { + get { return _readOnly; } + } + + /// + /// Flag indicating if a new transaction synchronization has been opened + /// for this transaction. + /// + public bool NewSynchronization + { + get { return _newSynchronization; } + } + + /// + /// Returns suspended resources for this transaction. + /// + public object SuspendedResources + { + get { return _suspendedResources; } + } + + /// + /// Gets and sets the savepoint for the current transaction, if any. + /// + public string Savepoint + { + get { return _savepoint; } + set { _savepoint = value; } + } + + /// + /// Returns a flag indicating if the transaction has a savepoint. + /// + public bool HasSavepoint + { + get { return ( _savepoint != null ); } + } + + /// + /// Determines whether there is an actual transaction active. + /// + /// + /// true if there is an actual transaction active; otherwise, false. + /// + public bool HasTransaction() + { + return Transaction != null; + } + + /// + /// Return the underlying transaction as a + /// , if possible. + /// + /// + /// If the underlying transaction does not support savepoints. + /// + protected ISavepointManager SavepointManager + { + get + { + if ( ! IsTransactionSavepointManager ) + { + throw new NestedTransactionNotSupportedException( + "Transaction object [" + Transaction + "] does not support savepoints."); + } + return ( ISavepointManager) Transaction; + } + } + /// + /// Return true if the underlying transaction implements the + /// interface. + /// + public bool IsTransactionSavepointManager + { + get { return ( Transaction is ISavepointManager ); } + } + #endregion + + #region ITransactionStatus Members + /// + /// Returns true if the transaction is new, else false if participating + /// in an existing transaction. + /// + public bool IsNewTransaction + { + get + { + return ( HasTransaction() && _newTransaction ); + } + } + + /// + /// Determine the rollbackOnly flag via checking both this + /// + /// and the transaction object, provided that the latter implements the + /// interface. + /// + /// The property can only be set to true. + public bool RollbackOnly + { + get { + return ( LocalRollbackOnly || GlobalRollbackOnly); + } + + set { if (value) {_rollbackOnly = value;} } + } + #endregion + + + /// + /// Determine the rollback-only flag via checking this TransactionStatus. Will only + /// return true if the application set the property RollbackOnly to true on this + /// TransactionStatus object. + /// + /// true if [local rollback only]; otherwise, false. + public bool LocalRollbackOnly + { + get + { + return _rollbackOnly; + } + } + + public bool GlobalRollbackOnly + { + get + { + return ((_transaction is ISmartTransactionObject) && + ((ISmartTransactionObject) _transaction).RollbackOnly); + } + } + #region ISavepointManager Members + /// + /// This implementation delegates to the underlying transaction object + /// (if it implements the interface) + /// to create a savepoint. + /// + /// + /// If the underlying transaction does not support savepoints. + /// + public void CreateSavepoint( string savepoint ) + { + SavepointManager.CreateSavepoint( savepoint ); + } + + /// + /// This implementation delegates to the underlying transaction object + /// (if it implements the interface) + /// to rollback to the supplied . + /// + /// The savepoint to rollback to. + public void RollbackToSavepoint( string savepoint ) + { + SavepointManager.RollbackToSavepoint( savepoint ); + } + + /// + /// This implementation delegates to the underlying transaction object + /// (if it implements the interface) + /// to release the supplied . + /// + /// The savepoint to release. + public void ReleaseSavepoint( string savepoint) + { + SavepointManager.ReleaseSavepoint( savepoint ); + } + + #endregion + /// + /// Create a savepoint and hold it for the transaction. + /// + /// + /// If the underlying transaction does not support savepoints. + /// + public void CreateAndHoldSavepoint( string savepoint ) + { + SavepointManager.CreateSavepoint( savepoint ); + Savepoint = savepoint; + } + + /// + /// Roll back to the savepoint that is held for the transaction. + /// + /// + /// If no save point has been created. + /// + public void RollbackToHeldSavepoint() + { + if ( ! HasSavepoint ) + { + throw new TransactionUsageException( "No savepoint associated with current transaction" ); + } + SavepointManager.RollbackToSavepoint( Savepoint ); + } + + /// + /// Release the savepoint that is held for the transaction. + /// + /// + /// If no save point has been created. + /// + public void ReleaseHeldSavepoint() + { + if ( ! HasSavepoint ) + { + throw new TransactionUsageException( "No savepoint associated with current transaction" ); + } + SavepointManager.ReleaseSavepoint( Savepoint ); + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/ISmartTransactionObject.cs b/src/Spring/Spring.Data/Transaction/Support/ISmartTransactionObject.cs new file mode 100644 index 00000000..34b1b659 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/ISmartTransactionObject.cs @@ -0,0 +1,51 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Transaction.Support +{ + /// + /// Interface to be implemented by transaction objects that are able to + /// return an internal rollback-only marker, typically from a another + /// transaction that has participated and marked it as rollback-only. + /// + /// + ///

    + /// Autodetected by , + /// to always return a current rollbackOnly flag even if not resulting from the current + /// . + ///

    + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: ISmartTransactionObject.cs,v 1.6 2007/08/29 03:42:20 markpollack Exp $ + public interface ISmartTransactionObject + { + /// + /// Return whether the transaction is internally marked as rollback-only. + /// + /// True of the transaction is marked as rollback-only. + bool RollbackOnly + { + get; + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/ITransactionCallback.cs b/src/Spring/Spring.Data/Transaction/Support/ITransactionCallback.cs new file mode 100644 index 00000000..1e889632 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/ITransactionCallback.cs @@ -0,0 +1,50 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +namespace Spring.Transaction.Support +{ + /// + /// Callback interface for transactional code. + /// + /// + ///

    To be used with 's Execute + /// methods. + ///

    + ///

    + /// Typically used to gather various calls to transaction-unaware low-level + /// services into a higher-level method implementation with transaction + /// demarcation. + ///

    + ///
    + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: ITransactionCallback.cs,v 1.4 2007/08/01 23:32:39 markpollack Exp $ + public interface ITransactionCallback + { + /// + /// Gets called by TransactionTemplate.Execute within a + /// transaction context. + /// + /// The associated transaction status. + /// A result object or null. + object DoInTransaction(ITransactionStatus status); + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/ITransactionOperations.cs b/src/Spring/Spring.Data/Transaction/Support/ITransactionOperations.cs new file mode 100644 index 00000000..a606c762 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/ITransactionOperations.cs @@ -0,0 +1,65 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Transaction.Support +{ + /// + /// Interface specifying basic transaction exectuion operations. + /// + /// + /// Implemented by . Not often used directly, + /// but a useful option to enhance testability, as it can easily be mocked or stubbed. + /// + /// Juergen Hoeller + /// Mark Pollac (.NET) + /// $Id: ITransactionOperations.cs,v 1.1 2007/08/01 18:53:14 markpollack Exp $ + public interface ITransactionOperations + { + /// + /// Executes the the action specified by the given delegate callback within a transaction. + /// + /// Allows for returning a result object created within the transaction, that is, + /// a domain object or a collection of domain objects. An exception thrown by the callback + /// is treated as a fatal exception that enforces a rollback. Such an exception gets + /// propagated to the caller of the template. + /// + /// The delegate that specifies the transactional action. + /// A result object returned by the callback, or null if one + /// + /// In case of initialization or system errors. + /// + object Execute(TransactionDelegate transactionMethod); + + /// + /// Executes the action specified by the given callback object within a transaction. + /// + /// Allows for returning a result object created within the transaction, that is, + /// a domain object or a collection of domain objects. An exception thrown by the callback + /// is treated as a fatal exception that enforces a rollback. Such an exception gets + /// propagated to the caller of the template. + /// + /// The callback object that specifies the transactional action. + /// A result object returned by the callback, or null if one + /// + /// In case of initialization or system errors. + /// + object Execute(ITransactionCallback action); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Support/ITransactionSynchronization.cs b/src/Spring/Spring.Data/Transaction/Support/ITransactionSynchronization.cs new file mode 100644 index 00000000..94f11b8b --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/ITransactionSynchronization.cs @@ -0,0 +1,130 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Transaction.Support +{ + /// + /// Interface for transaction synchronization callbacks. + /// + /// + /// Supported by . + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: ITransactionSynchronization.cs,v 1.7 2006/11/24 05:57:46 markpollack Exp $ + public interface ITransactionSynchronization + { + /// + /// Suspend this synchronization. + /// + /// + ///

    + /// Supposed to unbind resources from + /// + /// if managing any. + ///

    + ///
    + void Suspend(); + + /// + /// Resume this synchronization. + /// + /// + ///

    + /// Supposed to rebind resources from + /// + /// if managing any. + ///

    + ///
    + void Resume(); + + /// + /// Invoked before transaction commit (before + /// ) + /// Can e.g. flush transactional O/R Mapping sessions to the database + /// + /// + /// + /// This callback does not mean that the transaction will actually be + /// commited. A rollback decision can still occur after this method + /// has been called. This callback is rather meant to perform work + /// that's only relevant if a commit still has a chance + /// to happen, such as flushing SQL statements to the database. + /// + /// + /// Note that exceptions will get propagated to the commit caller and cause a + /// rollback of the transaction. + /// + /// (note: do not throw TransactionException subclasses here!) + /// + /// + /// + /// If the transaction is defined as a read-only transaction. + /// + void BeforeCommit( bool readOnly ); + + /// + /// Invoked after transaction commit. + /// + /// Can e.g. commit further operations that are supposed to follow on + /// a successful commit of the main transaction. + /// Throws exception in case of errors; will be propagated to the caller. + /// Note: To not throw TransactionExeption sbuclasses here! + /// + /// + void AfterCommit(); + + /// + /// Invoked before transaction commit/rollback (after + /// , + /// even if + /// + /// threw an exception). + /// + /// + ///

    + /// Can e.g. perform resource cleanup. + ///

    + ///

    + /// Note that exceptions will get propagated to the commit caller + /// and cause a rollback of the transaction. + ///

    + ///
    + void BeforeCompletion(); + + /// + /// Invoked after transaction commit/rollback. + /// + /// + /// Status according to + /// + /// + /// Can e.g. perform resource cleanup, in this case after transaction completion. + ///

    + /// Note that exceptions will get propagated to the commit or rollback + /// caller, although they will not influence the outcome of the transaction. + ///

    + ///
    + void AfterCompletion( TransactionSynchronizationStatus status ); + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/ResourceHolderSupport.cs b/src/Spring/Spring.Data/Transaction/Support/ResourceHolderSupport.cs new file mode 100644 index 00000000..0a4bf827 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/ResourceHolderSupport.cs @@ -0,0 +1,199 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Transaction.Support +{ + /// + /// Convenient base class for resource holders. + /// + /// + ///

    + /// Features rollback-only support transactions. Can expire after a certain number of + /// seconds or milliseconds, to determine transactional timeouts. + ///

    + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: ResourceHolderSupport.cs,v 1.9 2007/01/30 20:12:29 markpollack Exp $ + public abstract class ResourceHolderSupport + { + private bool synchronizedWithTransaction = false; + private bool rollbackOnly = false; + private DateTime deadline; + private int referenceCount = 0; + + /// + /// Mark the resource as synchronized with a transaction. + /// + public bool SynchronizedWithTransaction + { + get { return synchronizedWithTransaction; } + set { synchronizedWithTransaction = value; } + } + + /// + /// Get or set whether the resource is synchronized with a transaction. + /// + /// true if synchronized; otherwise, false. + public bool RollbackOnly + { + get { return rollbackOnly; } + set { rollbackOnly = value;} + } + + /// + /// Return the expiration deadline of this object. + /// + public DateTime Deadline + { + get { return deadline; } + } + + /// + /// Return whether this object has an associated timeout. + /// + public bool HasTimeout + { + get { return ( deadline != DateTime.MinValue ); } + } + + /// + /// Return the time to live for this object in seconds. + /// + /// + ///

    + /// Rounds up eagerly, e.g. '9.00001' to '10'. + ///

    + ///
    + /// + /// If no deadline has been set. + /// + public int TimeToLiveInSeconds + { + get + { + int secs = (int)Math.Ceiling( TimeToLiveInMilliseconds / 1000 ); + checkTransactionTimeout(secs <= 0); + return secs; + } + } + + /// + /// Return the time to live for this object in milliseconds. + /// + /// + /// If no deadline has been set. + /// + public double TimeToLiveInMilliseconds + { + get + { + if ( deadline == DateTime.MinValue ) + { + throw new ArgumentException( "No deadline specified for this resource holder."); + } + TimeSpan duration = deadline - DateTime.Now; + checkTransactionTimeout(duration.TotalMilliseconds <= 0); + if (duration.TotalMilliseconds > 0) + { + return duration.TotalMilliseconds; + } + else + { + return 0; + } + } + } + + /// + /// Sets the timeout for this object in milliseconds. + /// + /// Number of milliseconds until expiration. + public long TimeoutInMillis + { + set + { + deadline = DateTime.Now.AddMilliseconds(value); + } + } + + /// + /// Sets the timeout for this object in seconds. + /// + /// Number of seconds until expiration. + public int TimeoutInSeconds + { + set + { + TimeoutInMillis = value * 1000; + } + } + + private void checkTransactionTimeout(bool deadlineReached) + { + if (deadlineReached) + { + RollbackOnly = true; + throw new TransactionTimedOutException("Transaction timed out: deadline was " + Deadline); + } + } + + + /// + /// Clear the transaction state of this resource holder. + /// + public virtual void Clear() + { + synchronizedWithTransaction = false; + rollbackOnly = false; + deadline = DateTime.MinValue; + } + + /// + /// Increase the reference count by one because the holder has been requested. + /// + public void Requested() + { + referenceCount++; + } + + /// + /// Decrease the reference count by one because the holder has been released. + /// + public void Released() + { + referenceCount--; + } + + /// + /// Return wheterh there are still open references to this holder + /// + public bool IsOpen + { + get + { + return (referenceCount > 0); + } + } + + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/TransactionCallbackWithoutResult.cs b/src/Spring/Spring.Data/Transaction/Support/TransactionCallbackWithoutResult.cs new file mode 100644 index 00000000..3c3304cc --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/TransactionCallbackWithoutResult.cs @@ -0,0 +1,75 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Transaction.Support +{ + /// + /// Simple convenience class for TransactionCallback implementation. + /// Allows for implementing a DoInTransaction version without result, + /// i.e. without the need for a return statement. + /// + /// Mark Pollack (.NET) + /// $Id: TransactionCallbackWithoutResult.cs,v 1.3 2006/06/21 14:07:30 markpollack Exp $ + public abstract class TransactionCallbackWithoutResult : ITransactionCallback + { + #region Methods + + /// + /// Gets called by TransactionTemplate.execute within a transactional context + /// when no return value is required. + /// + /// The status. + /// + /// Does not need to care about transactions itself, although it can retrieve + /// and influence the status of the current transaction via the given status + /// object, e.g. setting rollback-only. + /// A RuntimeException thrown by the callback is treated as application + /// exception that enforces a rollback. An exception gets propagated to the + /// caller of the template. + /// + public abstract void DoInTransactionWithoutResult(ITransactionStatus status); + + #endregion + + #region ITransactionCallback Members + + /// + /// Gets called by TransactionTemplate.Execute within a + /// transaction context. + /// + /// The associated transaction status. + /// a result object or null + public object DoInTransaction(ITransactionStatus status) + { + DoInTransactionWithoutResult(status); + return null; + } + + + + #endregion + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/TransactionDelegate.cs b/src/Spring/Spring.Data/Transaction/Support/TransactionDelegate.cs new file mode 100644 index 00000000..964ee795 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/TransactionDelegate.cs @@ -0,0 +1,42 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Transaction.Support +{ + + /// + /// Callback delegate for performing actions within a transactional context. + /// + /// + ///

    To be used with 's Execute + /// methods. + ///

    + ///

    + /// Typically used to gather various calls to transaction-unaware low-level + /// services into a higher-level method implementation with transaction + /// demarcation. + ///

    + ///
    + /// The status of the transaction, can be used to + /// trigger a rollback the current transaction by settings its + /// RollbackOnly property to true. + /// A result object or null. + public delegate object TransactionDelegate(ITransactionStatus status); +} diff --git a/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationAdapter.cs b/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationAdapter.cs new file mode 100644 index 00000000..590994d2 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationAdapter.cs @@ -0,0 +1,146 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Transaction.Support +{ + /// + /// Adapter for the + /// interface. + /// + /// + /// Contains empty implementations of all interface methods, for easy overriding of + /// single methods. + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: TransactionSynchronizationAdapter.cs,v 1.7 2006/12/06 00:02:25 markpollack Exp $ + public abstract class TransactionSynchronizationAdapter : ITransactionSynchronization, IComparable + { + + + #region ITransactionSynchronization Members + /// + /// Suspend this synchronization. + /// + /// + ///

    + /// Supposed to unbind resources from + /// + /// if managing any. + ///

    + ///
    + public virtual void Suspend() {} + + /// + /// Resume this synchronization. + /// + /// + ///

    + /// Supposed to unbind resources from + /// + /// if managing any. + ///

    + ///
    + public virtual void Resume() {} + + /// + /// Invoked before transaction commit (before + /// ) + /// + /// + /// If the transaction is defined as a read-only transaction. + /// + /// + ///

    + /// Can flush transactional sessions to the database. + ///

    + ///

    + /// Note that exceptions will get propagated to the commit caller and + /// cause a rollback of the transaction. + ///

    + ///
    + public virtual void BeforeCommit( bool readOnly ) {} + + + /// + /// Invoked after transaction commit. + /// + /// Can e.g. commit further operations that are supposed to follow on + /// a successful commit of the main transaction. + /// Throws exception in case of errors; will be propagated to the caller. + /// Note: To not throw TransactionExeption sbuclasses here! + /// + public virtual void AfterCommit() + { + } + + /// + /// Invoked before transaction commit/rollback (after + /// , + /// even if + /// + /// threw an exception). + /// + /// + ///

    + /// Can e.g. perform resource cleanup. + ///

    + ///

    + /// Note that exceptions will get propagated to the commit caller + /// and cause a rollback of the transaction. + ///

    + ///
    + public virtual void BeforeCompletion() {} + + /// + /// Invoked after transaction commit/rollback. + /// + /// + /// Status according to + /// + /// + /// Can e.g. perform resource cleanup, in this case after transaction completion. + ///

    + /// Note that exceptions will get propagated to the commit or rollback + /// caller, although they will not influence the outcome of the transaction. + ///

    + ///
    + public virtual void AfterCompletion( TransactionSynchronizationStatus status ) {} + #endregion + + /// + ///Compares the current instance with another object of the same type. + /// + /// + /// + ///A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance is less than obj. Zero This instance is equal to obj. Greater than zero This instance is greater than obj. + /// + /// + ///An object to compare with this instance. + ///obj is not the same type as this instance. 2 + public virtual int CompareTo(object obj) + { + return Int32.MinValue; + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationManager.cs b/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationManager.cs new file mode 100644 index 00000000..65ad218b --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationManager.cs @@ -0,0 +1,491 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Data; +using System.Globalization; +using System.Threading; +using Spring.Core; +using Spring.Threading; +using Spring.Util; + +namespace Spring.Transaction.Support +{ + /// + /// Internal class that manages resources and transaction synchronizations per thread. + /// + /// + /// Supports one resource per key without overwriting, i.e. a resource needs to + /// be removed before a new one can be set for the same key. + /// Supports a list of transaction synchronizations if synchronization is active. + ///

    + /// Resource management code should check for thread-bound resources via GetResource(). + /// It is normally not supposed + /// to bind resources to threads, as this is the responsiblity of transaction managers. + /// A further option is to lazily bind on first use if transaction synchronization + /// is active, for performing transactions that span an arbitrary number of resources. + ///

    + ///

    + /// Transaction synchronization must be activated and deactivated by a transaction + /// manager via + /// InitSynchronization + /// and + /// ClearSynchronization. + /// This is automatically supported by + /// . + ///

    + ///

    + /// Resource management code should only register synchronizations when this + /// manager is active, and perform resource cleanup immediately else. + /// If transaction synchronization isn't active, there is either no current + /// transaction, or the transaction manager doesn't support synchronizations. + ///

    + /// Note that this class uses following naming convention for the + /// named 'data slots' for storage of thread local data, 'Spring.Transaction:Name' + /// where Name is either + ///
    + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// Mark Pollack (.NET) + /// $Id: TransactionSynchronizationManager.cs,v 1.19 2008/02/17 13:17:13 markpollack Exp $ + public sealed class TransactionSynchronizationManager + { + #region Logging + + private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof (TransactionSynchronizationManager)); + + #endregion + + #region Fields + private static readonly string syncsDataSlotName = "Spring.Transactions:syncList"; + + private static readonly string resourcesDataSlotName = "Spring.Transactions:resources"; + + private static readonly string currentTxReadOnlyDataSlotName = "Spring.Transactions:currentTxReadOnly"; + + private static readonly string currentTxNameDataSlotName = "Spring.Transactions:currentTxName"; + + private static readonly string currentTxIsolationLevelDataSlotName = "Spring.Transactions:currentTxIsolationLevel"; + + private static readonly string actualTxActiveDataSlotName = "Spring.Transactions:actualTxActive"; + + private static IComparer syncComparer = new OrderComparator(); + + #endregion + + #region Management of transaction-associated resource handles + /// + /// Return all resources that are bound to the current thread. + /// + /// Main for debugging purposes. Resource manager should always + /// invoke HasResource for a specific resource key that they are interested in. + /// + /// IDictionary with resource keys and resource objects or empty + /// dictionary if none is bound. + public static IDictionary ResourceDictionary + { + get + { + IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary; + if (resources != null) + { + //TODO add readonly wrapper in Spring.Collections. + return resources; + } + else + { + return new Hashtable(); + } + } + } + + /// + /// Check if there is a resource for the given key bound to the current thread. + /// + /// key to check + /// if there is a value bound to the current thread + public static bool HasResource(Object key) + { + AssertUtils.ArgumentNotNull(key, "Key must not be null"); + return ResourceDictionary.Contains(key); + } + + /// + /// Retrieve a resource for the given key that is bound to the current thread. + /// + /// key to check + /// a value bound to the current thread, or null if none. + public static object GetResource(Object key) + { + AssertUtils.ArgumentNotNull(key, "Key must not be null"); + IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary; + if (resources == null) + { + return null; + } + //Check for contains since indexer returning null behavior changes in 2.0 + if (!resources.Contains(key)) + { + return null; + } + object val = resources[key]; + + if (val != null && LOG.IsDebugEnabled) + { + LOG.Debug("Retrieved value [" + Describe(val) + "] for key [" + Describe(key) + "] bound to thread [" + + SystemUtils.ThreadId + "]"); + } + return val; + } + + /// + /// Bind the given resource for teh given key to the current thread + /// + /// key to bind the value to + /// value to bind + public static void BindResource(Object key, Object value) + { + AssertUtils.ArgumentNotNull(key, "Key value for thread local storage of transactional resources must not be null"); + AssertUtils.ArgumentNotNull(value, "Transactional resource to bind to thread local storage must not be null" ); + + IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary; + //Set thread local resource storage if not found + if (resources == null) + { + resources = new Hashtable(); + LogicalThreadContext.SetData(resourcesDataSlotName, resources); + } + if (resources.Contains(key)) + { + throw new InvalidOperationException("Already value [" + resources[key] + "] for key [" + key + + "] bound to thread [" + SystemUtils.ThreadId + "]"); + } + resources.Add(key, value); + if (LOG.IsDebugEnabled) + { + LOG.Debug("Bound value [" + Describe(value) + "] for key [" + Describe(key) + "] to thread [" + + SystemUtils.ThreadId + "]"); + } + } + + + /// + /// Unbind a resource for the given key from the current thread + /// + /// key to check + /// the previously bound value + /// if there is no value bound to the thread + public static object UnbindResource(Object key) + { + AssertUtils.ArgumentNotNull(key, "Key must not be null"); + + IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary; + if (resources == null || !resources.Contains(key)) + { + throw new InvalidOperationException("No value for key [" + key + "] bound to thread [" + + SystemUtils.ThreadId + "]"); + } + Object val = resources[key]; + resources.Remove(key); + if (resources.Count == 0) + { + LogicalThreadContext.FreeNamedDataSlot(resourcesDataSlotName); + } + if (LOG.IsDebugEnabled) + { + LOG.Debug("Removed value [" + Describe(val) + "] for key [" + Describe(key) + "] from thread [" + + SystemUtils.ThreadId + "]"); + } + return val; + } + + #endregion + + /// + /// Activate transaction synchronization for the current thread. + /// + /// + /// Called by transaction manager at the beginning of a transaction. + /// + /// + /// If synchronization is already active. + /// + public static void InitSynchronization() + { + if ( SynchronizationActive ) + { + throw new InvalidOperationException( "Cannot activate transaction synchronization - already active" ); + } + if (LOG.IsDebugEnabled) + { + LOG.Debug("Initializing transaction synchronization"); + } + ArrayList syncs = new ArrayList(); + LogicalThreadContext.SetData(syncsDataSlotName, syncs); + } + + /// + /// Deactivate transaction synchronization for the current thread. + /// + /// + /// Called by transaction manager on transaction cleanup. + /// + /// + /// If synchronization is not active. + /// + public static void ClearSynchronization() + { + if ( !SynchronizationActive ) + { + throw new InvalidOperationException( "Cannot deactivate transaction synchronization - not active" ); + } + if (LOG.IsDebugEnabled) + { + LOG.Debug("Clearing transaction synchronization"); + } + LogicalThreadContext.FreeNamedDataSlot(syncsDataSlotName); + } + + /// + /// Clears the entire transaction synchronization state for the current thread, registered + /// synchronizations as well as the various transaction characteristics. + /// + public static void Clear() + { + ClearSynchronization(); + CurrentTransactionName = null; + CurrentTransactionReadOnly = false; + CurrentTransactionIsolationLevel = IsolationLevel.Unspecified; + ActualTransactionActive = false; + } + + /// + /// Register a new transaction synchronization for the current thread. + /// + /// + /// Typically called by resource management code. + /// + /// + /// If synchronization is not active. + /// + public static void RegisterSynchronization( ITransactionSynchronization synchronization ) + { + AssertUtils.ArgumentNotNull(synchronization, "TransactionSynchronization must not be null"); + if ( !SynchronizationActive ) + { + throw new InvalidOperationException( "Transaction synchronization is not active" ); + } + ArrayList syncs = LogicalThreadContext.GetData(syncsDataSlotName) as ArrayList; + if (syncs != null) + { + object root = syncs.SyncRoot; + lock (root) + { + syncs.Add(synchronization); + } + } + } + + private static string Describe(object obj) + { + return obj == null ? "" : obj + "@" + obj.GetHashCode().ToString("X"); + } + + #region Properties + + /// + /// Return an unmodifiable list of all registered synchronizations + /// for the current thread. + /// + /// + /// A list of + /// instances. + /// + /// + /// If synchronization is not active. + /// + public static IList Synchronizations + { + get + { + if ( ! SynchronizationActive ) + { + throw new InvalidOperationException( "Transaction synchronization is not active" ); + } + ArrayList syncs = LogicalThreadContext.GetData(syncsDataSlotName) as ArrayList; + if (syncs != null) + { + // Sort lazily here, not in registerSynchronization. + object root = syncs.SyncRoot; + lock (root) + { + syncs.Sort(syncComparer); + } + // Return unmodifiable snapshot, to avoid exceptions + // while iterating and invoking synchronization callbacks that in turn + // might register further synchronizations. + return ArrayList.ReadOnly(syncs); + } + else + { + return ArrayList.ReadOnly(new ArrayList()); + } + } + } + + /// + /// Return if transaction synchronization is active for the current thread. + /// + /// + /// Can be called before + /// InitSynchronization + /// to avoid unnecessary instance creation. + /// + public static bool SynchronizationActive + { + get + { + IList syncs = LogicalThreadContext.GetData(syncsDataSlotName) as IList; + return syncs != null; + } + } + + /// + /// Gets or sets a value indicating whether the + /// current transaction is read only. + /// + /// + /// Called by transaction manager on transaction begin and on cleanup. + /// Return whether the current transaction is marked as read-only. + /// To be called by resource management code when preparing a newly + /// created resource (for example, a Hibernate Session). + ///

    Note that transaction synchronizations receive the read-only flag + /// as argument for the beforeCommit callback, to be able + /// to suppress change detection on commit. The present method is meant + /// to be used for earlier read-only checks, for example to set the + /// flush mode of a Hibernate Session to FlushMode.Never upfront. + ///

    + ///
    + /// + /// true if current transaction read only; otherwise, false. + /// + public static bool CurrentTransactionReadOnly + { + get + { + return LogicalThreadContext.GetData(currentTxReadOnlyDataSlotName) != null; + } + set + { + if (value) + { + LogicalThreadContext.SetData(currentTxReadOnlyDataSlotName, true); + } + else + { + LogicalThreadContext.FreeNamedDataSlot(currentTxReadOnlyDataSlotName); + } + + } + } + + /// + /// Gets or sets the name of the current transaction, if any. + /// + /// Called by the transaction manager on transaction begin and on cleanup. + /// To be called by resource management code for optimizations per use case, for + /// example to optimize fetch strategies for specific named transactions. + /// The name of the current transactio or null if none set. + public static string CurrentTransactionName + { + get + { + return LogicalThreadContext.GetData(currentTxNameDataSlotName) as string; + } + set + { + LogicalThreadContext.SetData(currentTxNameDataSlotName, value); + } + } + + /// + /// Gets or sets a value indicating whether there currently is an actual transaction + /// active. + /// + /// This indicates wheter the current thread is associated with an actual + /// transaction rather than just with active transaction synchronization. + /// Called by the transaction manager on transaction begin and on cleanup. + /// To be called by resource management code that wants to discriminate between + /// active transaction synchronization (with or without backing resource transaction; + /// also on PROPAGATION_SUPPORTS) and an actual transaction being active; on + /// PROPAGATION_REQUIRES, PROPAGATION_REQUIRES_NEW, etC) + /// + /// true if [actual transaction active]; otherwise, false. + /// + public static bool ActualTransactionActive + { + get + { + return LogicalThreadContext.GetData(actualTxActiveDataSlotName) != null; + } + set + { + if (value) + { + LogicalThreadContext.SetData(actualTxActiveDataSlotName, value); + } + else + { + LogicalThreadContext.FreeNamedDataSlot(actualTxActiveDataSlotName); + } + } + } + + + /// + /// Gets or sets the current transaction isolation level, if any. + /// + /// Called by the transaction manager on transaction begin and on cleanup. + /// The current transaction isolation level. If no current transaction is + /// active, retrun IsolationLevel.Unspecified + public static IsolationLevel CurrentTransactionIsolationLevel + { + get + { + object data = + LogicalThreadContext.GetData(currentTxIsolationLevelDataSlotName); + if (data != null) + { + return (IsolationLevel) data; + } + else + { + return IsolationLevel.Unspecified; + } + } + set + { + LogicalThreadContext.SetData(currentTxIsolationLevelDataSlotName, value); + } + } + #endregion + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationState.cs b/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationState.cs new file mode 100644 index 00000000..29cc83ae --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationState.cs @@ -0,0 +1,50 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Transaction.Support +{ + /// + /// Enumeration containing the state of transaction synchronization. + /// + /// Griffin Caprio (.NET) + /// $Id: TransactionSynchronizationState.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + public enum TransactionSynchronizationState + { + /// + /// Always activate transaction synchronization, even for "empty" transactions + /// that result from . + /// + /// with no existing backend transaction. + Always, + /// + /// Activate transaction synchronization only for actual transactions, + /// i.e. not for empty ones that result from . + /// + /// with no + /// existing backend transaction. + OnActualTransaction, + /// + /// Never active transaction synchronization. + /// + Never + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationStatus.cs b/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationStatus.cs new file mode 100644 index 00000000..7a0e3570 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationStatus.cs @@ -0,0 +1,45 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Transaction.Support +{ + /// + /// Enumeration of status values when synchronizing transactions. + /// + /// Griffin Caprio + /// $Id: TransactionSynchronizationStatus.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + public enum TransactionSynchronizationStatus + { + /// + /// Completion status in case of proper commit. + /// + Committed, + /// + /// Completion status in case of proper rollback. + /// + Rolledback, + /// + /// Completion status in case of heuristic mixed completion or system error. + /// + Unknown, + } +} diff --git a/src/Spring/Spring.Data/Transaction/Support/TransactionTemplate.cs b/src/Spring/Spring.Data/Transaction/Support/TransactionTemplate.cs new file mode 100644 index 00000000..b5971b87 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/Support/TransactionTemplate.cs @@ -0,0 +1,218 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Common.Logging; +using Spring.Objects.Factory; + +namespace Spring.Transaction.Support +{ + /// + /// Helper class that simplifies programmatic transaction demarcation and + /// transaction exception handling. + /// + /// + ///

    + /// The central methods are + /// + /// and + /// supporting transactional code wrapped in the delegate instance. It handles the + /// transaction lifecycle and possible exceptions such that neither the delegate + /// implementation nor the calling code needs to explicitly handle transactions. + ///

    + ///

    + /// Can be used within a service implementation via direct instantiation with + /// a transaction manager reference, or get prepared in an application context + /// and given to services as object reference. + ///

    + /// + /// The transaction manager should always be configured as an object in the application + /// context, in the first case given to the service directly, in the second case to the + /// prepared template. + /// + ///

    + /// Supports setting the propagation behavior and the isolation level by name, + /// for convenient configuration in context definitions. + ///

    + ///
    + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// Griffin Caprio (.NET) + /// $Id: TransactionTemplate.cs,v 1.13 2007/08/01 18:53:14 markpollack Exp $ + public class TransactionTemplate : DefaultTransactionDefinition, ITransactionOperations, IInitializingObject + { + private IPlatformTransactionManager _platformTransactionManager; + + #region Logging Definition + + protected readonly ILog log = LogManager.GetLogger(typeof(TransactionTemplate)); + + #endregion + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// Mainly targeted at configuration by an object factory. + ///

    + /// + /// The + /// + /// property must be set before any calls to the + /// + /// or + /// method. + /// + ///
    + /// + public TransactionTemplate() {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// Mainly targeted at configuration by an object factory. + ///

    + ///
    + /// + /// The transaction management strategy to be used. + /// + public TransactionTemplate( IPlatformTransactionManager platformTransactionManager ) + { + _platformTransactionManager = platformTransactionManager; + } + + /// + /// Gets and sets the to + /// be used. + /// + public IPlatformTransactionManager PlatformTransactionManager + { + get { return _platformTransactionManager; } + set { _platformTransactionManager = value; } + } + + #region IInitializingObject Members + /// + /// Ensures that the + /// + /// has been set. + /// + public void AfterPropertiesSet() + { + if ( _platformTransactionManager == null ) + { + throw new ArgumentException( "IPlatformTransactionManager instance is required." ); + } + } + #endregion + + + /// + /// Executes the the action specified by the given delegate callback within a transaction. + /// + /// The delegate that specifies the transactional action. + /// + /// A result object returned by the callback, or null if one + /// + /// Allows for returning a result object created within the transaction, that is, + /// a domain object or a collection of domain objects. An exception thrown by the callback + /// is treated as a fatal exception that enforces a rollback. Such an exception gets + /// propagated to the caller of the template. + /// + /// + /// In case of initialization or system errors. + /// + public object Execute( TransactionDelegate transactionMethod ) + { + ITransactionStatus status = _platformTransactionManager.GetTransaction( this ); + object result; + try + { + result = transactionMethod( status ); + } + catch ( Exception ex ) + { + rollbackOnException( status, ex ); + throw; + } + _platformTransactionManager.Commit( status ); + return result; + } + + + /// + /// Executes the action specified by the given callback object within a transaction. + /// + /// The callback object that specifies the transactional action. + /// + /// A result object returned by the callback, or null if one + /// + /// Allows for returning a result object created within the transaction, that is, + /// a domain object or a collection of domain objects. An exception thrown by the callback + /// is treated as a fatal exception that enforces a rollback. Such an exception gets + /// propagated to the caller of the template. + /// + /// + /// In case of initialization or system errors. + /// + public object Execute(ITransactionCallback action) + { + ITransactionStatus status = _platformTransactionManager.GetTransaction( this ); + object result; + try + { + result = action.DoInTransaction(status); + } + catch ( Exception ex ) + { + rollbackOnException( status, ex ); + throw; + } + _platformTransactionManager.Commit( status ); + return result; + } + + /// + /// Perform a rollback, handling rollback exceptions properly. + /// + /// The object representing the transaction. + /// The thrown application exception or error. + private void rollbackOnException( ITransactionStatus status, Exception exception ) + { + if (log.IsDebugEnabled) + { + log.Debug("Initiating transaction rollback on application exception", exception); + } + try + { + _platformTransactionManager.Rollback( status ); + } + catch ( Exception ex ) + { + log.Error("Application exception overridden by rollback exception", ex); + throw; + } + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/TransactionException.cs b/src/Spring/Spring.Data/Transaction/TransactionException.cs new file mode 100644 index 00000000..9db40a8f --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/TransactionException.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Base class for all transaction exceptions. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: TransactionException.cs,v 1.7 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public abstract class TransactionException : ApplicationException + { + /// + /// Creates a new instance of the TransactionException class. + /// + protected TransactionException() + { + } + + /// + /// Creates a new instance of the TransactionException class, with the specified message. + /// + /// + /// A message about the exception. + /// + protected TransactionException (string message) : base(message) + { + } + + /// + /// Creates a new instance of the TransactionException class with the specified message + /// and root cause. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + protected TransactionException (string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the TransactionException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected TransactionException ( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + } + } +} diff --git a/src/Spring/Spring.Data/Transaction/TransactionOutcomeState.cs b/src/Spring/Spring.Data/Transaction/TransactionOutcomeState.cs new file mode 100644 index 00000000..d25bcafc --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/TransactionOutcomeState.cs @@ -0,0 +1,37 @@ +#region License + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Transaction +{ + /// + /// Represents a transaction's current state. + /// + /// Griffin Caprio (.NET) + /// $Id: TransactionOutcomeState.cs,v 1.4 2006/05/18 21:37:51 markpollack Exp $ + public enum TransactionOutcomeState + { + /// + /// The transaction state is unknown. + /// + Unknown, + /// + /// The transaction has been committed. + /// + Committed, + /// + /// The transaction has been rolled back. + /// + Rolledback, + /// + /// The transaction is in an unknown, mixed state. + /// + Mixed + } +} diff --git a/src/Spring/Spring.Data/Transaction/TransactionPropagation.cs b/src/Spring/Spring.Data/Transaction/TransactionPropagation.cs new file mode 100644 index 00000000..bcb3fe6b --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/TransactionPropagation.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Transaction +{ + /// + /// Enumeration describing Spring.NET's + /// transaction propagation settings. + /// + /// Griffin Caprio (.NET) + /// $Id: TransactionPropagation.cs,v 1.7 2007/05/29 20:00:33 markpollack Exp $ + public enum TransactionPropagation + { + /// + /// Support a current transaction, create a new one if none exists. + /// + /// + ///

    + /// Analogous to System.EnterpriseServices.TransactionOption value of the same name. + /// This is typically the default setting of a transaction definition. + ///

    + ///
    + Required, + + /// + /// Support a current transaction, execute non-transactionally if none exists. + /// + /// + ///

    + /// Analogous to System.EnterpriseServices.TransactionOption.Supported. + ///

    + ///
    + Supports, + + /// + /// Support a current transaction, throw an exception if none exists. + /// + /// + ///

    + /// No corresponding System.EnterpriseServices.TransactionOption value. + ///

    + ///
    + Mandatory, + + /// + /// Create a new transaction, suspending the current transaction if one exists. + /// + /// + ///

    + /// Analogous to System.EnterpriseServices.TransactionOption value of the same name. + ///

    + ///
    + RequiresNew, + + /// + /// Execute non-transactionally, suspending the current transaction if one exists. + /// + /// + ///

    + /// Analogous to System.EnterpriseServices.TransactionOption value of the same name. + ///

    + ///
    + NotSupported, + + /// + /// Execute non-transactionally, throw an exception if a transaction exists. + /// + /// + ///

    + /// Analogous to System.EnterpriseServices.TransactionOption.Disabled. + ///

    + ///
    + Never, + + /// + /// Execute within a nested transaction if a current transaction exists, else + /// behave like . + /// + /// + ///

    + /// There is no analogous feature in TransactionOption. + ///

    + ///
    + Nested + } +} diff --git a/src/Spring/Spring.Data/Transaction/TransactionSuspensionNotSupportedException.cs b/src/Spring/Spring.Data/Transaction/TransactionSuspensionNotSupportedException.cs new file mode 100644 index 00000000..33a6ca98 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/TransactionSuspensionNotSupportedException.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Exception thrown when attempting to suspend an existing transaction + /// but transaction suspension is not supported by the underlying backend. + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: TransactionSuspensionNotSupportedException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class TransactionSuspensionNotSupportedException : CannotCreateTransactionException + { + /// + /// Creates a new instance of the + /// class. + /// + public TransactionSuspensionNotSupportedException( ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public TransactionSuspensionNotSupportedException( String message ) : base(message) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public TransactionSuspensionNotSupportedException(string message, Exception rootCause) + : base(message, rootCause) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected TransactionSuspensionNotSupportedException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/TransactionSystemException.cs b/src/Spring/Spring.Data/Transaction/TransactionSystemException.cs new file mode 100644 index 00000000..31642283 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/TransactionSystemException.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Exception thrown when a general transaction system error is encountered, + /// for instance on commit or rollback. + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: TransactionSystemException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class TransactionSystemException : TransactionException + { + /// + /// Creates a new instance of the + /// class. + /// + public TransactionSystemException( ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public TransactionSystemException( String message ) : base(message) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public TransactionSystemException(string message, Exception rootCause) + : base(message, rootCause) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected TransactionSystemException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/TransactionTimedOutException.cs b/src/Spring/Spring.Data/Transaction/TransactionTimedOutException.cs new file mode 100644 index 00000000..65d3603f --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/TransactionTimedOutException.cs @@ -0,0 +1,56 @@ +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Exception to be thrown when a transaction has timed out. + /// + [Serializable] + public class TransactionTimedOutException : TransactionException + { + /// + /// Create a new instance + /// + public TransactionTimedOutException() : base() + { + + } + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public TransactionTimedOutException( String message ) : base(message) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public TransactionTimedOutException(string message, Exception rootCause) + : base(message, rootCause) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected TransactionTimedOutException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} diff --git a/src/Spring/Spring.Data/Transaction/TransactionUsageException.cs b/src/Spring/Spring.Data/Transaction/TransactionUsageException.cs new file mode 100644 index 00000000..faf62557 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/TransactionUsageException.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Superclass for exceptions caused by inappropriate usage of + /// a Spring.NET transaction API. + /// + /// Juergen Hoeller + /// Griffin Caprio (.NET) + /// $Id: TransactionUsageException.cs,v 1.6 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class TransactionUsageException : TransactionException + { + /// + /// Creates a new instance of the + /// class. + /// + public TransactionUsageException( ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public TransactionUsageException( String message ) : base(message) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public TransactionUsageException(string message, Exception rootCause) + : base(message, rootCause) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected TransactionUsageException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/UnexpectedRollbackException.cs b/src/Spring/Spring.Data/Transaction/UnexpectedRollbackException.cs new file mode 100644 index 00000000..7de89163 --- /dev/null +++ b/src/Spring/Spring.Data/Transaction/UnexpectedRollbackException.cs @@ -0,0 +1,78 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Transaction +{ + /// + /// Thrown when an attempt to commit a transaction resulted in an unexpected rollback. + /// + /// Rod Johnson + /// Griffin Caprio (.NET) + /// $Id: UnexpectedRollbackException.cs,v 1.5 2006/05/18 21:37:51 markpollack Exp $ + [Serializable] + public class UnexpectedRollbackException : TransactionException + { + /// + /// Creates a new instance of the + /// class. + /// + public UnexpectedRollbackException( ) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + public UnexpectedRollbackException( String message ) : base(message) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public UnexpectedRollbackException(string message, Exception rootCause) + : base(message, rootCause) {} + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected UnexpectedRollbackException( + SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Context/ILifecycle.cs b/src/Spring/Spring.Messaging.Nms/Context/ILifecycle.cs new file mode 100644 index 00000000..a939cee0 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Context/ILifecycle.cs @@ -0,0 +1,15 @@ + +namespace Spring.Context +{ + interface ILifecycle + { + void Start(); + + void Stop(); + + bool IsRunning + { + get; + } + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs new file mode 100644 index 00000000..95d923e5 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs @@ -0,0 +1,315 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using Spring.Transaction.Support; +using Spring.Util; +using NMS; + +namespace Spring.Messaging.Nms.IConnections +{ + /// Helper class for obtaining transactional NMS resources + /// for a given IConnectionFactory. + /// + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + public abstract class ConnectionFactoryUtils + { + + + /// Obtain a NMS ISession that is synchronized with the current transaction, if any. + /// the IConnectionFactory to obtain a ISession for + /// + /// the existing NMS IConnection to obtain a ISession for + /// (may be null) + /// + /// whether to allow for a local NMS transaction + /// that is synchronized with a Spring-managed transaction (where the main transaction + /// might be a JDBC-based one for a specific DataSource, for example), with the NMS + /// transaction committing right after the main transaction. If not allowed, the given + /// IConnectionFactory needs to handle transaction enlistment underneath the covers. + /// + /// the transactional ISession, or null if none found + /// + /// NMSException in case of NMS failure + public static ISession GetTransactionalSession(IConnectionFactory cf, IConnection existingCon, bool synchedLocalTransactionAllowed) + { + + return DoGetTransactionalSession(cf, new AnonymousClassResourceFactory(existingCon, cf, synchedLocalTransactionAllowed)); + } + + /// Obtain a NMS ISession that is synchronized with the current transaction, if any. + /// the TransactionSynchronizationManager key to bind to + /// (usually the IConnectionFactory) + /// + /// the ResourceFactory to use for extracting or creating + /// NMS resources + /// + /// the transactional ISession, or null if none found + /// + /// NMSException in case of NMS failure + public static ISession DoGetTransactionalSession(System.Object resourceKey, ConnectionFactoryUtils.ResourceFactory resourceFactory) + { + + AssertUtils.ArgumentNotNull(resourceKey, "Resource key must not be null"); + AssertUtils.ArgumentNotNull(resourceKey, "ResourceFactory must not be null"); + + NmsResourceHolder resourceHolder = (NmsResourceHolder)TransactionSynchronizationManager.GetResource(resourceKey); + if (resourceHolder != null) + { + ISession rssession = resourceFactory.GetSession(resourceHolder); + if (rssession != null || resourceHolder.Frozen) + { + return rssession; + } + } + if (!TransactionSynchronizationManager.SynchronizationActive) + { + return null; + } + NmsResourceHolder conHolderToUse = resourceHolder; + if (conHolderToUse == null) + { + conHolderToUse = new NmsResourceHolder(); + } + NMS.IConnection con = resourceFactory.GetConnection(conHolderToUse); + ISession session = null; + try + { + bool isExistingCon = (con != null); + if (!isExistingCon) + { + con = resourceFactory.CreateConnection(); + conHolderToUse.AddConnection(con); + } + session = resourceFactory.CreateSession(con); + conHolderToUse.AddSession(session, con); + if (!isExistingCon) + { + con.Start(); + } + } + catch (NMSException) + { + if (session != null) + { + try + { + session.Close(); + } + catch (System.Exception) + { + // ignore + } + } + if (con != null) + { + try + { + con.Close(); + } + catch (System.Exception) + { + // ignore + } + } + throw; + } + if (conHolderToUse != resourceHolder) + { + TransactionSynchronizationManager.RegisterSynchronization(new NmsResourceSynchronization(resourceKey, conHolderToUse, resourceFactory.AcknowledgementMode)); + conHolderToUse.SynchronizedWithTransaction = true; + TransactionSynchronizationManager.BindResource(resourceKey, conHolderToUse); + } + return session; + } + + + + + #region ResourceFactory helper classes + + private class AnonymousClassResourceFactory : ConnectionFactoryUtils.ResourceFactory + { + private IConnection existingCon; + private IConnectionFactory cf; + private bool synchedLocalTransactionAllowed; + + public AnonymousClassResourceFactory(NMS.IConnection existingCon, IConnectionFactory cf, bool synchedLocalTransactionAllowed) + { + InitBlock(existingCon, cf, synchedLocalTransactionAllowed); + } + + private void InitBlock(NMS.IConnection existingCon, IConnectionFactory cf, bool synchedLocalTransactionAllowed) + { + this.existingCon = existingCon; + this.cf = cf; + this.synchedLocalTransactionAllowed = synchedLocalTransactionAllowed; + } + + public virtual ISession GetSession(NmsResourceHolder holder) + { + return holder.GetSession(typeof(ISession), existingCon); + } + + public virtual NMS.IConnection GetConnection(NmsResourceHolder holder) + { + return (existingCon != null ? existingCon : holder.GetConnection()); + } + + public virtual NMS.IConnection CreateConnection() + { + return cf.CreateConnection(); + } + + public virtual ISession CreateSession(NMS.IConnection con) + { + return con.CreateSession(synchedLocalTransactionAllowed); + } + } + #endregion + + #region Helper classes/interfaces + + /// Callback interface for resource creation. + /// Serving as argument for the DoGetTransactionalSession method. + /// + public interface ResourceFactory + { + + /// Fetch an appropriate ISession from the given NmsResourceHolder. + /// the NmsResourceHolder + /// + /// an appropriate ISession fetched from the holder, + /// or null if none found + /// + ISession GetSession(NmsResourceHolder holder); + + /// Fetch an appropriate IConnection from the given NmsResourceHolder. + /// the NmsResourceHolder + /// + /// an appropriate IConnection fetched from the holder, + /// or null if none found + /// + IConnection GetConnection(NmsResourceHolder holder); + + /// Create a new NMS IConnection for registration with a NmsResourceHolder. + /// the new NMS IConnection + /// + /// NMSException if thrown by NMS API methods + IConnection CreateConnection(); + + /// Create a new NMS ISession for registration with a NmsResourceHolder. + /// the NMS IConnection to create a ISession for + /// + /// the new NMS ISession + /// + /// NMSException if thrown by NMS API methods + ISession CreateSession(NMS.IConnection con); + + + /// + /// Return whether to allow for a local NMS transaction that is synchronized with + /// a Spring-managed transaction (where the main transaction might be a ADO.NET-based + /// one for a specific IDbProvider, for example), with the NMS transaction + /// committing right after the main transaction. + /// Returns whether to allow for synchronizing a local NMS transaction + /// + /// + bool SynchedLocalTransactionAllowed + { + get; + } + } + + /// Callback for resource cleanup at the end of a non-native NMS transaction + /// + private class NmsResourceSynchronization : TransactionSynchronizationAdapter + { + + private object resourceKey; + + private NmsResourceHolder resourceHolder; + + private AcknowledgementMode acknowledgementMode; + + private bool holderActive = true; + + public NmsResourceSynchronization(object resourceKey, NmsResourceHolder resourceHolder, AcknowledgementMode acknowledgementMode) + { + this.resourceKey = resourceKey; + this.resourceHolder = resourceHolder; + this.acknowledgementMode = acknowledgementMode; + } + + public override void Suspend() + { + if (this.holderActive) + { + TransactionSynchronizationManager.UnbindResource(resourceKey); + } + } + + public override void Resume() + { + if (this.holderActive) + { + TransactionSynchronizationManager.BindResource(resourceKey, resourceHolder); + } + } + + public override void BeforeCompletion() + { + TransactionSynchronizationManager.UnbindResource(this.resourceKey); + this.holderActive = false; + if (this.acknowledgementMode != AcknowledgementMode.Transactional) + { + this.resourceHolder.CloseAll(); + } + } + + //TODO bring in new Spring.Data library to Integration project which has this method in interface. + public override void AfterCommit() + { + if (this.acknowledgementMode == AcknowledgementMode.Transactional) + { + try + { + this.resourceHolder.CommitAll(); + } + catch (NMSException ex) + { + throw new SynchedLocalTransactionFailedException("Local NMS transaction failed to commit", ex); + } + } + } + + //TODO bring in new Spring.Data library to Integration project which has this method in interface + public override void AfterCompletion(TransactionSynchronizationStatus status) + { + if (this.acknowledgementMode == AcknowledgementMode.Transactional) + { + this.resourceHolder.CloseAll(); + } + } + } + #endregion + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs new file mode 100644 index 00000000..085ccf77 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs @@ -0,0 +1,223 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using Common.Logging; +using Spring.Collections; +using Spring.Transaction.Support; +using Spring.Util; +using NMS; + +namespace Spring.Messaging.Nms.IConnections +{ + /// IConnection holder, wrapping a NMS IConnection and a NMS ISession. + /// NmsTransactionManager binds instances of this class to the thread, + /// for a given NMS IConnectionFactory. + /// + ///

    Note: This is an SPI class, not intended to be used by applications.

    + /// + ///
    + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class NmsResourceHolder : ResourceHolderSupport + { + #region Logging + + private static readonly ILog logger = LogManager.GetLogger(typeof(NmsResourceHolder)); + + #endregion + + #region Fields + + private bool frozen; + + private IList connections = new LinkedList(); + + private IList sessions = new LinkedList(); + + private IDictionary sessionsPerIConnection = new Hashtable(); + + #endregion + + + #region Constructor (s) + + /// Create a new NmsResourceHolder that is open for resources to be added. + public NmsResourceHolder() + { + this.frozen = false; + } + + /// Create a new NmsResourceHolder for the given NMS resources. + /// the NMS IConnection + /// + /// the NMS ISession + /// + public NmsResourceHolder(NMS.IConnection connection, ISession session) + { + AddConnection(connection); + AddSession(session, connection); + this.frozen = true; + } + #endregion + + #region Properties + + virtual public bool Frozen + { + get + { + return frozen; + } + + } + #endregion + + #region Methods + + public void AddConnection(NMS.IConnection connection) + { + //TODO - update Assert utility class... + //Assert.isTrue(!this.frozen, "Cannot add IConnection because NmsResourceHolder is frozen"); + AssertUtils.ArgumentNotNull(connection, "IConnection must not be null"); + if (!connections.Contains(connection)) + { + connections.Add(connection); + } + } + + public void AddSession(ISession session) + { + AddSession(session, null); + } + + public void AddSession(ISession session, NMS.IConnection connection) + { + //TOOD update AssertUtils class + //Assert.isTrue(!this.frozen, "Cannot add ISession because NmsResourceHolder is frozen"); + AssertUtils.ArgumentNotNull(session, "ISession must not be null"); + if (!sessions.Contains(session)) + { + sessions.Add(session); + if (connection != null) + { + IList sessionsList = (IList)sessionsPerIConnection[connection]; + if (sessionsList == null) + { + sessionsList = new LinkedList(); + sessionsPerIConnection[connection] = sessionsList; + } + sessionsList.Add(session); + } + } + } + + public virtual NMS.IConnection GetConnection() + { + return (!(this.connections.Count == 0) ? (NMS.IConnection)this.connections[0] : null); + } + + public virtual NMS.IConnection GetConnection(System.Type connectionType) + { + throw new NotImplementedException(); + //TODO Updae CollectionUtils... + //return (NMS.IConnection)CollectionUtils.FindValueOfType(this.connections, connectionType); + } + + public virtual ISession GetSession() + { + return (!(this.sessions.Count == 0) ? (ISession)this.sessions[0] : null); + } + + public virtual ISession GetSession(Type sessionType) + { + return GetSession(sessionType, null); + } + + public virtual ISession GetSession(System.Type sessionType, NMS.IConnection connection) + { + IList sessions = this.sessions; + if (connection != null) + { + sessions = (IList)sessionsPerIConnection[connection]; + } + throw new NotImplementedException(); + //TODO update collection utils + //return (ISession)CollectionUtils.FindValueOfType(sessions, sessionType); + } + + public virtual void CommitAll() + { + foreach (ISession session in sessions) + { + session.Commit(); + + // TODO are these exceptions valid? +// try +// { +// session.Commit(); +// } +// catch (TransactionInProgressException ex) +// { +// // Ignore -> can only happen in case of a distributed transaction. +// } +// catch (IllegalStateException ex) +// { +// // Ignore -> can only happen in case of a distributed transaction. +// } + } + } + + public virtual void CloseAll() + { + foreach (ISession session in sessions) + { + try + { + session.Close(); + } + catch (Exception ex) + { + logger.Debug("Could not close NMS ISession after transaction", ex); + } + } + foreach (NMS.IConnection connection in connections) + { + try + { + try + { + connection.Stop(); + } + finally + { + connection.Close(); + } + } + catch (Exception ex) + { + logger.Debug("Could not close NMS IConnection after transaction", ex); + } + } + } + #endregion + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SynchedLocalTransactionFailedException.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SynchedLocalTransactionFailedException.cs new file mode 100644 index 00000000..fe74651c --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SynchedLocalTransactionFailedException.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Messaging.Nms.IConnections +{ + //TODO should we have a generic spring exception for NMS? + + /// Exception thrown when a synchronized local transaction failed to complete + /// (after the main transaction has already completed). + /// + /// Jergen Hoeller + /// Mark Pollack (.NET) + [Serializable] + public class SynchedLocalTransactionFailedException : ApplicationException + { + #region Constructor (s) / Destructor + /// Creates a new instance of the SynchedLocalTransactionFailedException class. + public SynchedLocalTransactionFailedException() + { + } + + /// + /// Creates a new instance of the SynchedLocalTransactionFailedException class. with the specified message. + /// + /// + /// A message about the exception. + /// + public SynchedLocalTransactionFailedException (string message) : base(message) + { + } + + /// + /// Creates a new instance of the SynchedLocalTransactionFailedException class with the specified message + /// and root cause. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public SynchedLocalTransactionFailedException (string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the SynchedLocalTransactionFailedException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected SynchedLocalTransactionFailedException( + SerializationInfo info, StreamingContext context) + : base (info, context) + { + } + #endregion + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageCreator.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageCreator.cs new file mode 100644 index 00000000..fe840aee --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageCreator.cs @@ -0,0 +1,42 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using NMS; + +namespace Spring.Messaging.Nms +{ + /// Creates a NMS message given a ISession + /// + ///

    The ISession typically is provided by an instance + /// of the NmsTemplate class.

    + ///
    + /// Mark Pollack + public interface IMessageCreator + { + /// Create a IMessage to be sent. + /// the NMS ISession to be used to create the + /// IMessage (never null) + /// + /// the IMessage to be sent + /// + /// NMSException if thrown by NMS API methods + IMessage CreateMessage(ISession session); + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageListener.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageListener.cs new file mode 100644 index 00000000..75ec0acc --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageListener.cs @@ -0,0 +1,29 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using NMS; + +namespace Spring.Messaging.Nms +{ + public interface IMessageListener + { + void OnMessage(IMessage message); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessagePostProcessor.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessagePostProcessor.cs new file mode 100644 index 00000000..8567a061 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessagePostProcessor.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using NMS; + +namespace Spring.Messaging.Nms +{ + /// To be used with NmsTemplate's send method that + /// convert an object to a message. + /// + /// + /// It allows for further modification of the message after it has been processed + /// by the converter. This is useful for setting of NMS Header and Properties. + /// + /// Mark Pollack + public interface IMessagePostProcessor + { + /// Apply a IMessagePostProcessor to the message. The returned message is + /// typically a modified version of the original. + /// + /// the NMS message from the IMessageConverter + /// + /// the modified version of the IMessage + /// + /// NMSException if thrown by NMS API methods + IMessage PostProcessMessage(IMessage message); + + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/INmsOperations.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/INmsOperations.cs new file mode 100644 index 00000000..47a75ceb --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/INmsOperations.cs @@ -0,0 +1,383 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using NMS; + +namespace Spring.Messaging.Nms +{ + /// Specifies a basic set of NMS operations. + /// + /// + ///

    Implemented by NmsTemplate. Not often used but a useful option + /// to enhance testability, as it can easily be mocked or stubbed.

    + /// + ///

    Provides NmsTemplate's send(..) and + /// receive(..) methods that mirror various NMS API methods. + /// See the NMS specification and NMS API docs for details on those methods. + ///

    + ///
    + /// Mark Pollack + /// Juergen Hoeller + /// Mark Pollack (.NET) + public interface INmsOperations + { + /// Execute the action specified by the given action object within + /// a NMS ISession. + ///

    Note: The value of isPubSubDomain affects the behavior of this method. + /// If isPubSubDomain equals true, then a ISession is passed to the callback. + /// If false, then a ISession is passed to the callback.

    + ///
    + /// callback object that exposes the session + /// + /// the result object from working with the session + /// + /// NMSException if there is any problem + object Execute(ISessionCallback action); + + /// Send a message to a NMS destination. The callback gives access to + /// the NMS session and IMessageProducer in order to do more complex + /// send operations. + /// + /// callback object that exposes the session/producer pair + /// + /// the result object from working with the session + /// + /// NMSException if there is any problem + object Execute(IProducerCallback action); + + + //------------------------------------------------------------------------- + // Convenience methods for sending messages + //------------------------------------------------------------------------- + + /// Send a message to the default destination. + ///

    This will only work with a default destination specified!

    + ///
    + /// callback to create a message + /// + /// NMSException if there is any problem + void Send(IMessageCreator messageCreator); + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a ISession. + /// + /// the destination to send this message to + /// + /// callback to create a message + /// + /// NMSException if there is any problem + void Send(IDestination destination, IMessageCreator messageCreator); + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a ISession. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// callback to create a message + /// + /// NMSException if there is any problem + void Send(string destinationName, IMessageCreator messageCreator); + + //------------------------------------------------------------------------- + // Convenience methods for sending messages + //------------------------------------------------------------------------- + + /// Send a message to the default destination. + ///

    This will only work with a default destination specified!

    + ///
    + /// delegate callback to create a message + /// + /// NMSException if there is any problem + void SendWithDelegate(IMessageCreatorDelegate messageCreatorDelegate); + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a ISession. + /// + /// the destination to send this message to + /// + /// delegate callback to create a message + /// + /// NMSException if there is any problem + void SendWithDelegate(IDestination destination, IMessageCreatorDelegate messageCreatorDelegate); + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a ISession. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// delegate callback to create a message + /// + /// NMSException if there is any problem + void SendWithDelegate(string destinationName, IMessageCreatorDelegate messageCreatorDelegate); + + //------------------------------------------------------------------------- + // Convenience methods for sending auto-converted messages + //------------------------------------------------------------------------- + + /// Send the given object to the default destination, converting the object + /// to a NMS message with a configured IMessageConverter. + ///

    This will only work with a default destination specified!

    + ///
    + /// the object to convert to a message + /// + /// NMSException if there is any problem + void ConvertAndSend(object message); + + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. + /// + /// the destination to send this message to + /// + /// the object to convert to a message + /// + /// NMSException if there is any problem + void ConvertAndSend(IDestination destination, object message); + + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the object to convert to a message + /// + /// NMSException if there is any problem + void ConvertAndSend(string destinationName, object message); + + /// Send the given object to the default destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + ///

    This will only work with a default destination specified!

    + ///
    + /// the object to convert to a message + /// + /// the callback to modify the message + /// + /// NMSException if there is any problem + void ConvertAndSend(object message, IMessagePostProcessor postProcessor); + + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the destination to send this message to + /// + /// the object to convert to a message + /// + /// the callback to modify the message + /// + /// NMSException if there is any problem + void ConvertAndSend(IDestination destination, object message, IMessagePostProcessor postProcessor); + + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the object to convert to a message. + /// + /// the callback to modify the message + /// + /// NMSException if there is any problem + void ConvertAndSend(string destinationName, object message, IMessagePostProcessor postProcessor); + + + //------------------------------------------------------------------------- + // Convenience methods for receiving messages + //------------------------------------------------------------------------- + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///

    This will only work with a default destination specified!

    + ///
    + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + IMessage Receive(); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the destination to receive a message from + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + IMessage Receive(IDestination destination); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + IMessage Receive(string destinationName); + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///

    This will only work with a default destination specified!

    + ///
    + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + IMessage ReceiveSelected(string messageSelector); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the destination to receive a message from + /// + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + IMessage ReceiveSelected(IDestination destination, string messageSelector); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + IMessage ReceiveSelected(string destinationName, string messageSelector); + + + //------------------------------------------------------------------------- + // Convenience methods for receiving auto-converted messages + //------------------------------------------------------------------------- + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///

    This will only work with a default destination specified!

    + ///
    + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + object ReceiveAndConvert(); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the destination to receive a message from + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + object ReceiveAndConvert(IDestination destination); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + object ReceiveAndConvert(string destinationName); + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///

    This will only work with a default destination specified!

    + ///
    + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + object ReceiveSelectedAndConvert(string messageSelector); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the destination to receive a message from + /// + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + object ReceiveSelectedAndConvert(IDestination destination, string messageSelector); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + object ReceiveSelectedAndConvert(string destinationName, string messageSelector); + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IProducerCallback.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IProducerCallback.cs new file mode 100644 index 00000000..fdc85e02 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IProducerCallback.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using NMS; + +namespace Spring.Messaging.Nms +{ + /// Callback for sending a message to a NMS destination. + /// + ///

    To be used with the NmsTemplate.Execute(IProducerCallback) + /// method, often implemented as an anonymous inner class.

    + /// + ///

    The typical implementation will perform multiple operations on the + /// supplied NMS ISession and IMessageProducer.

    + ///
    + /// Mark Pollack + public interface IProducerCallback + { + /// Perform operations on the given ISession and IMessageProducer. + /// The message producer is not associated with any destination. + /// + /// the NMS ISession object to use + /// + /// the NMS IMessageProducer object to use + /// + /// a result object from working with the ISession, if any (can be null) + /// + object DoInNms(ISession session, IMessageProducer producer); + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/ISessionCallback.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/ISessionCallback.cs new file mode 100644 index 00000000..022b6aa3 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/ISessionCallback.cs @@ -0,0 +1,47 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using NMS; + +namespace Spring.Messaging.Nms +{ + /// Callback for executing any number of operations on a provided + /// ISession + /// + /// + ///

    To be used with the NmsTemplate.Execute(ISessionCallback)} + /// method, often implemented as an anonymous inner class.

    + ///
    + /// Mark Pollack + /// + /// + public interface ISessionCallback + { + /// Execute any number of operations against the supplied NMS + /// ISession, possibly returning a result. + /// + /// the NMS ISession + /// + /// a result object from working with the ISession, if any (so can be null) + /// + /// NMSException if there is any problem + object DoInNms(ISession session); + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs new file mode 100644 index 00000000..b975b2c4 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs @@ -0,0 +1,588 @@ +using System; +using System.Collections; +using Spring.Context; +using Spring.Messaging.Nms.Support; +using Spring.Messaging.Nms.Support.IDestinations; +using Spring.Util; +using NMS; + +namespace Spring.Messaging.Nms.Listener +{ + public abstract class AbstractMessageListenerContainer : NmsDestinationAccessor, ILifecycle, IDisposable + { + private String clientId; + + private object destination; + + private String messageSelector; + + private object messageListener; + + private bool subscriptionDurable = false; + + private string durableSubscriptionName; + + private ExceptionListener exceptionListener; + + private bool exposeListenerISession = true; + + private bool autoStartup = true; + + private IConnection sharedConnection; + + + private volatile bool active = false; + + private bool running = false; + + private IList pausedTasks = new Spring.Collections.LinkedList(); + + private object lifecycleMonitor = new object(); + + + private object sharedConnectionMonitor = new object(); + + #region Properties + + /// Set whether to automatically start the listener after initialization. + ///

    Default is "true"; set this to "false" to allow for manual startup.

    + ///
    + virtual public bool AutoStartup + { + set { this.autoStartup = value; } + } + + + public string ClientId + { + set { clientId = value; } + get { return clientId; } + } + + + public IDestination Destination + { + get + { + return (this.destination is IDestination ? (IDestination) this.destination : null); + } + set + { + AssertUtils.ArgumentNotNull(value, "destination"); + destination = value; + if (destination is ITopic && !(destination is IQueue)) + { + PubSubDomain = true; + } + + } + } + + virtual public bool Active + { + get + { + lock (this.lifecycleMonitor) + { + return this.active; + } + } + + } + + public string DestinationName + { + get + { + return (this.destination is string ? (string) this.destination : null); + + } + set + { + AssertUtils.ArgumentNotNull(value, "destinationName must not be null"); + this.destination = value; + } + } + + + public string MessageSelector + { + get { return messageSelector; } + set { messageSelector = value; } + } + + + public object MessageListener + { + set + { + CheckMessageListener(value); + if (durableSubscriptionName == null) + { + // Use message listener class name as default name for a durable subscription. + durableSubscriptionName = value.GetType().FullName; + } + messageListener = value; + } + get + { + return messageListener; + } + } + + + public bool SubscriptionDurable + { + get { return subscriptionDurable; } + set { subscriptionDurable = value; } + } + + + public string DurableSubscriptionName + { + get + { + return durableSubscriptionName; + } + set + { + AssertUtils.ArgumentNotNull(value, "durableSubscriptionName must not be null"); + durableSubscriptionName = value; + } + } + + + public ExceptionListener ExceptionListener + { + get { return exceptionListener; } + set { exceptionListener = value; } + } + + + public bool ExposeListenerSession + { + get { return exposeListenerISession; } + set { exposeListenerISession = value; } + } + + + public object LifecycleMonitor + { + get { return lifecycleMonitor; } + } + + + public IConnection SharedConnection + { + get + { + if (!SharedConnectionEnabled) + { + throw new System.SystemException("This message listener container does not maintain a shared IConnection"); + } + lock (this.sharedConnectionMonitor) + { + if (this.sharedConnection == null) + { + //TODO SharedConnectionNotInitializedException + throw new ApplicationException("This message listener container's shared IConnection has not been initialized yet"); + } + return this.sharedConnection; + } + } + } + + #endregion + + public void Start() + { + DoStart(); + } + + private void DoStart() + { + lock (this.lifecycleMonitor) + { + running = true; + System.Threading.Monitor.PulseAll(this.lifecycleMonitor); + + //TODO - PausedTasks + } + + if (SharedConnectionEnabled) + { + StartSharedConnection(); + } + } + + protected virtual void StartSharedConnection() + { + lock (sharedConnectionMonitor) + { + if (sharedConnection != null) + { + try + { + sharedConnection.Start(); + } + catch (Exception ex) + { + logger.Debug("Ignoring IConnection start exception - assuming already started", ex); + } + } + } + } + + public void Stop() + { + DoStop(); + } + + public bool IsRunning + { + get + { + lock (lifecycleMonitor) + { + return running; + } + } + } + + public void Dispose() + { + Shutdown(); + } + + public virtual void Shutdown() + { + logger.Debug("Shutting down message listener container"); + bool wasRunning = false; + lock (this.lifecycleMonitor) + { + wasRunning = this.running; + this.running = false; + this.active = false; + System.Threading.Monitor.PulseAll(this.lifecycleMonitor); + } + try + { + DestroyListener(); + } + finally + { + lock (this.sharedConnectionMonitor) + { + NmsUtils.CloseConnection(this.sharedConnection, wasRunning); + } + } + } + + protected virtual void DoStop() + { + lock (this.lifecycleMonitor) + { + this.running = false; + System.Threading.Monitor.PulseAll(this.lifecycleMonitor); + } + + if (SharedConnectionEnabled) + { + StopSharedConnection(); + } + } + + protected virtual void StopSharedConnection() + { + lock (this.sharedConnectionMonitor) + { + if (this.sharedConnection != null) + { + try + { + this.sharedConnection.Stop(); + } + catch (System.InvalidOperationException ex) + { + logger.Debug("Ignoring IConnection stop exception - assuming already stopped", ex); + } + } + } + } + + + #region Template methods for listeners + public virtual void ExecuteListener(ISession session, IMessage message) + { + try + { + DoExecuteListener(session, message); + } + //UPGRADE_NOTE: Exception 'java.lang.Throwable' was converted to 'System.Exception' which has different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1100'" + catch (System.Exception ex) + { + HandleListenerException(ex); + } + } + + + protected virtual void DoExecuteListener(ISession session, IMessage message) + { + try + { + InvokeListener(session, message); + } + catch (Exception ex) + { + RollbackOnExceptionIfNecessary(session, ex); + throw; + } + CommitIfNecessary(session, message); + } + + private void CommitIfNecessary(ISession session, IMessage message) + { + //TODO + //logger.Info("CommitIfNecessary not implemented"); + } + + private void RollbackOnExceptionIfNecessary(ISession session, Exception ex) + { + //TODO + //logger.Info("RollbackOnExceptionIfNecessary not implemented"); + } + + + protected internal virtual void InvokeListener(ISession session, IMessage message) + { + if (MessageListener is ISessionAwareMessageListener) + { + DoInvokeListener((ISessionAwareMessageListener) MessageListener, session, message); + } + else + { + if (MessageListener is IMessageListener) + { + DoInvokeListener((IMessageListener) MessageListener, message); + } + else + { + throw new System.ArgumentException("Only IMessageListener and ISessionAwareMessageListener supported"); + } + } + } + + protected virtual void DoInvokeListener(IMessageListener listener, IMessage message) + { + listener.OnMessage(message); + } + + protected virtual void DoInvokeListener(ISessionAwareMessageListener listener, ISession session, IMessage message) + { + IConnection conToClose = null; + ISession sessionToClose = null; + try + { + ISession sessionToUse = session; + if (!ExposeListenerSession) + { + //We need to expose a separate Session. + conToClose = CreateConnection(); + sessionToClose = CreateSession(conToClose); + sessionToUse = sessionToClose; + } + // Actually invoke the message listener + if (logger.IsDebugEnabled) + { + logger.Debug("Invoking listener with message of type [" + message.GetType() + + "] and session [" + sessionToUse + "]"); + } + listener.OnMessage(message, sessionToUse); + if (sessionToUse != session) + { + if (sessionToUse.Transacted) + { + NmsUtils.CommitIfNecessary(sessionToUse); + } + } + } finally + { + NmsUtils.CloseSession(sessionToClose); + NmsUtils.CloseConnection(conToClose); + } + } + + protected virtual void HandleListenerException(System.Exception ex) + { + if (ex is NMSException) + { + InvokeExceptionListener((NMSException)ex); + } + if (Active) + { + // Regular case: failed while active. + // Log at error level. + logger.Error("Execution of NMS message listener failed", ex); + } + else + { + // Rare case: listener thread failed after container shutdown. + // Log at debug level, to avoid spamming the shutdown log. + logger.Debug("Listener exception after container shutdown", ex); + } + } + + protected virtual void InvokeExceptionListener(NMSException ex) + { + ExceptionListener exceptionListener = ExceptionListener; + if (exceptionListener != null) + { + exceptionListener(ex); + } + } + + #endregion + + public override void AfterPropertiesSet() + { + base.AfterPropertiesSet(); + + if (this.destination == null) + { + throw new System.ArgumentException("destination or destinationName is required"); + } + if (this.messageListener == null) + { + throw new System.ArgumentException("messageListener is required"); + } + if (SubscriptionDurable && !PubSubDomain) + { + throw new System.ArgumentException("A durable subscription requires a topic (pub-sub domain)"); + } + + Initialize(); + } + + public virtual void Initialize() + { + try + { + lock (this.lifecycleMonitor) + { + this.active = true; + System.Threading.Monitor.PulseAll(this.lifecycleMonitor); + } + + if (SharedConnectionEnabled) + { + EstablishSharedConnection(); + } + + if (this.autoStartup) + { + DoStart(); + } + + RegisterListener(); + } + catch (Exception) + { + lock (this.sharedConnectionMonitor) + { + NmsUtils.CloseConnection(this.sharedConnection); + } + throw; + } + } + + protected virtual void EstablishSharedConnection() + { + RefreshSharedConnection(); + } + + protected void RefreshSharedConnection() + { + bool running = IsRunning; + lock (this.sharedConnectionMonitor) + { + NmsUtils.CloseConnection(this.sharedConnection, running); + + IConnection con = CreateConnection(); + try + { + PrepareSharedConnection(con); + } + catch (Exception) + { + NmsUtils.CloseConnection(con); + throw; + } + this.sharedConnection = con; + } + } + + protected virtual void PrepareSharedConnection(IConnection connection) + { + if (ClientId != null) + { + connection.ClientId = ClientId; + } + } + #region Template methods to be implemented by subclasses + + /// Return whether a shared NMS IConnection should be maintained + /// by this listener container base class. + /// + /// + /// + protected abstract bool SharedConnectionEnabled + { + get; + } + + /// Register the specified listener on the underlying NMS IConnection. + ///

    Subclasses need to implement this method for their specific + /// listener management process.

    + ///
    + /// NMSException if registration failed + /// + /// + /// + /// + protected abstract void RegisterListener(); + + /// Destroy the registered listener. + /// The NMS IConnection will automatically be closed afterwards + ///

    Subclasses need to implement this method for their specific + /// listener management process.

    + ///
    + /// NMSException if destruction failed + protected abstract void DestroyListener(); + + #endregion + + protected virtual IConnection CreateConnection() + { + return ConnectionFactory.CreateConnection(); + } + + protected virtual ISession CreateSession(IConnection con) + { + return con.CreateSession(SessionAcknowledgeMode); + } + + protected virtual bool IsClientAcknowledge(ISession session) + { + return (session.AcknowledgementMode == AcknowledgementMode.ClientAcknowledge); + } + + protected virtual void CheckMessageListener(System.Object messageListener) + { + AssertUtils.ArgumentNotNull(messageListener, "IMessage Listener can not be null"); + if (!(messageListener is IMessageListener || messageListener is ISessionAwareMessageListener)) + { + throw new System.ArgumentException("messageListener needs to be of type [" + typeof(IMessageListener).FullName + "] or [" + typeof(ISessionAwareMessageListener).FullName + "]"); + } + } + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/Adapter/MessageListenerAdapter.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/Adapter/MessageListenerAdapter.cs new file mode 100644 index 00000000..5be23271 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/Adapter/MessageListenerAdapter.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections; +using Common.Logging; +using Spring.Expressions; +using Spring.Messaging.Nms.Support; +using Spring.Messaging.Nms.Support.Converter; +using Spring.Messaging.Nms.Support.IDestinations; +using Spring.Util; +using NMS; + +namespace Spring.Messaging.Nms.Listener.Adapter +{ + public class MessageListenerAdapter : IMessageListener + { + #region Logging + + private readonly ILog logger = LogManager.GetLogger(typeof (MessageListenerAdapter)); + + #endregion + + private object delegateObject; + + private string defaultListenerMethod = "HandleMessage"; + + private IExpression processingExpression; + + private object defaultResponseDestination; + + private IDestinationResolver destinationResolver = new DynamicDestinationResolver(); + + private IMessageConverter messageConverter; + + public MessageListenerAdapter() + { + InitDefaultStrategies(); + delegateObject = this; + processingExpression = Spring.Expressions.Expression.Parse(defaultListenerMethod + "(#convertedObject)"); + } + + public MessageListenerAdapter(object delegateObject) + { + InitDefaultStrategies(); + this.delegateObject = delegateObject; + } + + // TODO name change? + + public object DelegateObject + { + get { return delegateObject; } + set { delegateObject = value; } + } + + public string DefaultListenerMethod + { + get { return defaultListenerMethod; } + set + { + defaultListenerMethod = value; + } + } + + + public object DefaultResponseDestination + { + set { defaultResponseDestination = value; } + } + + public string DefaultResponseDestinationQueueName + { + set { defaultResponseDestination = new DestinationNameHolder(value, false); } + } + + public string DefaultResponseDestinationTopicName + { + set { defaultResponseDestination = new DestinationNameHolder(value, true); } + } + + + public IDestinationResolver DestinationResolver + { + get { return destinationResolver; } + set + { + AssertUtils.ArgumentNotNull(value, "DestinationResolver must not be null"); + destinationResolver = value; + } + } + + public IMessageConverter MessageConverter + { + get { return messageConverter; } + set { messageConverter = value; } + } + + private void InitDefaultStrategies() + { + MessageConverter = new SimpleMessageConverter(); + } + + protected virtual void HandleListenerException(Exception e) + { + logger.Error("Listener execution failed", e); + } + + protected virtual string GetListenerMethodName(IMessage originalIMessage, object extractedMessage) + { + return DefaultListenerMethod; + } + + + public void OnMessage(IMessage message) + { + try + { + OnMessage(message, null); + } + catch (Exception e) + { + HandleListenerException(e); + } + } + + public void OnMessage(IMessage message, ISession session) + { + object convertedMessage = ExtractIMessage(message); + + + IDictionary vars = new Hashtable(); + vars["convertedObject"] = convertedMessage; + + //Need to parse each time since have overloaded methods and + //expression processor caches target of first invocation. + //TODO - use regular reflection. + processingExpression = Expression.Parse(defaultListenerMethod + "(#convertedObject)"); + + //Invoke message handler method and get result. + object result = processingExpression.GetValue(delegateObject, vars); + if (result != null) + { + HandleResult(result, message, session); + } + else + { + logger.Debug("No result object given - no result to handle"); + } + } + + private void HandleResult(object result, IMessage request, ISession session) + { + if (session != null) + { + if (logger.IsDebugEnabled) + { + logger.Debug("Listener method returned result [" + result + + "] - generating response message for it"); + } + IMessage response = BuildMessage(session, result); + PostProcessResponse(request, response); + IDestination destination = GetResponseDestination(request, response, session); + SendResponse(session, destination, response); + } + else + { + if (logger.IsDebugEnabled) + { + logger.Debug("Listener method returned result [" + result + + "]: not generating response message for it because of no NMS ISession given"); + } + } + } + + protected virtual IMessage BuildMessage(ISession session, Object result) + { + IMessageConverter converter = MessageConverter; + if (converter != null) + { + return converter.ToMessage(result, session); + } + else + { + //TODO ' use as ' + IMessage msg = result as IMessage; + if (msg == null) + { + throw new IMessageConversionException( + "No IMessageConverter specified - cannot handle message [" + result + "]"); + } + return msg; + } + } + + protected virtual void PostProcessResponse(IMessage request, IMessage response) + { + response.NMSCorrelationID = request.NMSCorrelationID; + } + + protected virtual IDestination GetResponseDestination(IMessage request, IMessage response, ISession session) + { + IDestination replyTo = request.NMSReplyTo; + if (replyTo == null) + { + replyTo = ResolveDefaultResponseDestination(session); + if (replyTo == null) + { + throw new InvalidDestinationException("Cannot determine response destination: " + + "Request message does not contain reply-to destination, and no default response destination set."); + } + } + return replyTo; + } + + protected virtual IDestination ResolveDefaultResponseDestination(ISession session) + { + IDestination dest = defaultResponseDestination as IDestination; + if (dest != null) + { + return dest; + } + + DestinationNameHolder destNameHolder = defaultResponseDestination as DestinationNameHolder; + if (destNameHolder != null) + { + return DestinationResolver.ResolveDestinationName(session, destNameHolder.Name, destNameHolder.IsTopic); + } + + return null; + } + + protected virtual void SendResponse(ISession session, IDestination destination, IMessage response) + { + IMessageProducer producer = session.CreateProducer(destination); + try + { + PostProcessProducer(producer, response); + producer.Send(response); + } + finally + { + NmsUtils.CloseMessageProducer(producer); + } + } + + protected virtual void PostProcessProducer(IMessageProducer producer, IMessage response) + { + + } + + + private object ExtractIMessage(IMessage message) + { + IMessageConverter converter = MessageConverter; + if (converter != null) + { + return converter.FromMessage(message); + } + return message; + } + } + + internal class DestinationNameHolder + { + private string name; + + private bool isTopic; + + public DestinationNameHolder(string name, bool isTopic) + { + this.name = name; + this.isTopic = isTopic; + } + + + public string Name + { + get { return name; } + } + + public bool IsTopic + { + get { return isTopic; } + } + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/DefaultMessageListenerContainer.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/DefaultMessageListenerContainer.cs new file mode 100644 index 00000000..f952d7f4 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/DefaultMessageListenerContainer.cs @@ -0,0 +1,6 @@ +namespace Spring.Messaging.Nms.Listener +{ + public class DefaultMessageListenerContainer + { + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/ISessionAwareMessageListener.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/ISessionAwareMessageListener.cs new file mode 100644 index 00000000..90b105f4 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/ISessionAwareMessageListener.cs @@ -0,0 +1,19 @@ +using NMS; + +namespace Spring.Messaging.Nms.Listener +{ + public interface ISessionAwareMessageListener + { + + /// Callback for processing a received NMS message. + /// Implementors are supposed to process the given IMessage, + /// typically sending reply messages through the given ISession. + /// + /// the received NMS message + /// + /// the underlying NMS ISession + /// + /// NMSException if thrown by NMS methods + void OnMessage(IMessage message, ISession session); + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/SimpleMessageListenerContainer.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/SimpleMessageListenerContainer.cs new file mode 100644 index 00000000..aeed04ff --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/SimpleMessageListenerContainer.cs @@ -0,0 +1,133 @@ +using Spring.Collections; +using Spring.Messaging.Nms.Support; +using NMS; + +namespace Spring.Messaging.Nms.Listener +{ + public class SimpleMessageListenerContainer : AbstractMessageListenerContainer + { + private bool pubSubNoLocal = false; + + private int concurrentConsumers = 1; + + //private TaskExecutor taskExecutor; + + private ISet sessions; + + private ISet consumers; + + + public int ConcurrentConsumers + { + get { return concurrentConsumers; } + set { concurrentConsumers = value; } + } + + public bool PubSubNoLocal + { + get { return pubSubNoLocal; } + set { pubSubNoLocal = value; } + } + + protected override bool SharedConnectionEnabled + { + get { return true; } + } + + protected override void RegisterListener() + { + this.sessions = new HashedSet(); + this.consumers = new HashedSet(); + for (int i = 0; i < this.concurrentConsumers; i++) + { + ISession session = CreateSession(SharedConnection); + IMessageConsumer consumer = CreateListenerConsumer(session); + this.sessions.Add(session); + this.consumers.Add(consumer); + } + } + + private IMessageConsumer CreateListenerConsumer(ISession session) + { + IDestination destination = Destination; + if (destination == null) + { + destination = ResolveDestinationName(session, DestinationName); + } + IMessageConsumer consumer = CreateConsumer(session, destination); + //TODO TaskExectuor abstraction would go here... + + SimpleMessageListener listener = new SimpleMessageListener(this, session); + consumer.Listener += new NMS.MessageListener(listener.OnMessage); + return consumer; + } + + protected override void DestroyListener() + { + logger.Debug("Closing NMS IMessageConsumers"); + foreach (IMessageConsumer messageConsumer in consumers) + { + NmsUtils.CloseMessageConsumer(messageConsumer); + } + logger.Debug("Closing NMS ISessions"); + foreach (ISession session in sessions) + { + NmsUtils.CloseSession(session); + } + } + + public override void AfterPropertiesSet() + { + if (this.concurrentConsumers <= 0) + { + throw new System.ArgumentException("concurrentConsumers value must be at least 1 (one)"); + } + if (SubscriptionDurable && this.concurrentConsumers != 1) + { + throw new System.ArgumentException("Only 1 concurrent consumer supported for durable subscription"); + } + + base.AfterPropertiesSet(); + } + + protected IMessageConsumer CreateConsumer(ISession session, IDestination destination) + { + // Only pass in the NoLocal flag in case of a Topic: + // Some NMS providers, such as WebSphere MQ 6.0, throw IllegalStateException + // in case of the NoLocal flag being specified for a Queue. + if (PubSubDomain) + { + if (SubscriptionDurable && destination is ITopic) + { + return session.CreateDurableConsumer( + (ITopic) destination, DurableSubscriptionName, MessageSelector, PubSubNoLocal); + } + else + { + return session.CreateConsumer(destination, MessageSelector, PubSubNoLocal); + } + } + else + { + return session.CreateConsumer(destination, MessageSelector); + } + } + } + + internal class SimpleMessageListener : IMessageListener + { + private SimpleMessageListenerContainer container; + private ISession session; + + public SimpleMessageListener(SimpleMessageListenerContainer container, ISession session) + { + this.container = container; + this.session = session; + } + + public void OnMessage(IMessage message) + { + container.ExecuteListener(session, message); + } + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/MessageCreatorDelegate.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/MessageCreatorDelegate.cs new file mode 100644 index 00000000..80ee0db2 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/MessageCreatorDelegate.cs @@ -0,0 +1,35 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using NMS; + +namespace Spring.Messaging.Nms +{ + /// + /// Delegate that creates a NMS message given a ISession + /// + /// the NMS ISession to be used to create the + /// IMessage (never null) + /// + /// the IMessage to be sent + /// + /// NMSException if thrown by NMS API methods + public delegate IMessage IMessageCreatorDelegate(ISession session); +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsGatewaySupport.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsGatewaySupport.cs new file mode 100644 index 00000000..42d4bc33 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsGatewaySupport.cs @@ -0,0 +1,116 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Common.Logging; +using Spring.Objects.Factory; +using NMS; + +namespace Spring.Messaging.Nms +{ + /// + /// Convenient super class for application classes that need NMS access. + /// + /// + /// Requires a IConnectionFactory or a NmsTemplate instance to be set. + /// It will create its own NmsTemplate if a IConnectionFactory is passed in. + /// A custom NmsTemplate instance can be created for a given IConnectionFactory + /// through overriding the createNmsTemplate method. + /// + /// + public class NmsGatewaySupport : IInitializingObject + { + + #region Logging + + protected readonly ILog logger = LogManager.GetLogger(typeof(NmsGatewaySupport)); + + #endregion + + private NmsTemplate jmsTemplate; + + + /// + /// Gets or sets the NMS template for the gateway. + /// + /// The NMS template. + public NmsTemplate NmsTemplate + { + get { return jmsTemplate; } + set { jmsTemplate = value; } + } + + /// + /// Gets or sets he NMS connection factory to be used by the gateway. + /// Will automatically create a NmsTemplate for the given IConnectionFactory. + /// + /// The connection factory. + public IConnectionFactory ConnectionFactory + { + get + { + return (jmsTemplate != null ? this.jmsTemplate.ConnectionFactory : null); + } + set + { + this.jmsTemplate = CreateNmsTemplate(value); + } + } + + /// + /// Creates a NmsTemplate for the given IConnectionFactory. + /// + /// Only invoked if populating the gateway with a IConnectionFactory reference. + /// Can be overridden in subclasses to provide a different NmsTemplate instance + /// + /// + /// The connection factory. + /// + protected virtual NmsTemplate CreateNmsTemplate(IConnectionFactory connectionFactory) + { + return new NmsTemplate(connectionFactory); + } + + public void AfterPropertiesSet() + { + if (jmsTemplate == null) + { + throw new ArgumentException("connectionFactory or jmsTemplate is required"); + } + try + { + InitGateway(); + } + catch (Exception e) + { + throw new ObjectInitializationException("Initialization of the NMS gateway failed: " + e.Message, e); + } + } + + /// + /// Subclasses can override this for custom initialization behavior. + /// Gets called after population of this instance's properties. + /// + protected virtual void InitGateway() + { + + } + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs new file mode 100644 index 00000000..2074225e --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs @@ -0,0 +1,1425 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Messaging.Nms.IConnections; +using Spring.Messaging.Nms.Support; +using Spring.Messaging.Nms.Support.Converter; +using Spring.Messaging.Nms.Support.IDestinations; +using Spring.Transaction.Support; +using Spring.Util; +using NMS; + +namespace Spring.Messaging.Nms +{ + /// Helper class that simplifies NMS access code. + /// + /// If you want to use dynamic destination creation, you must specify + /// the type of NMS destination to create, using the "pubSubDomain" property. + /// For other operations, this is not necessary. + /// Point-to-Point (Queues) is the default domain. + /// + /// Default settings for NMS ISessions are "not transacted" and "auto-acknowledge". + /// + /// This template uses a DynamicDestinationResolver and a SimpleMessageConverter + /// as default strategies for resolving a destination name or converting a message, + /// respectively. + /// + /// + /// Mark Pollack + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class NmsTemplate : NmsDestinationAccessor, INmsOperations + { + #region Fields + + public static readonly long DEFAULT_RECEIVE_TIMEOUT = -1; + + + private NmsTemplateResourceFactory transactionalResourceFactory; + + + private object defaultDestination; + + private IMessageConverter messageConverter; + + + private bool messageIdEnabled = true; + + private bool messageTimestampEnabled = true; + + private bool pubSubNoLocal = false; + + private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT; + + private bool explicitQosEnabled = false; + + private byte priority = NMSConstants.defaultPriority; + + private bool persistent = NMSConstants.defaultPersistence; + + private TimeSpan timeToLive; + + private bool cacheNmsResources = true; + + private NmsResources jmsResources = new NmsResources(); + + #endregion + + #region Constructor (s) + + /// Create a new NmsTemplate. + /// + ///

    Note: The IConnectionFactory has to be set before using the instance. + /// This constructor can be used to prepare a NmsTemplate via an ObjectFactory, + /// typically setting the IConnectionFactory.

    + ///
    + public NmsTemplate() + { + transactionalResourceFactory = new NmsTemplateResourceFactory(this); + InitDefaultStrategies(); + } + + + /// Create a new NmsTemplate, given a IConnectionFactory. + /// the IConnectionFactory to obtain IConnections from + /// + public NmsTemplate(IConnectionFactory connectionFactory) + : this() + { + ConnectionFactory = connectionFactory; + AfterPropertiesSet(); + } + + #endregion + + #region Methods + + /// Initialize the default implementations for the template's strategies: + /// DynamicDestinationResolver and SimpleMessageConverter. + /// + protected virtual void InitDefaultStrategies() + { + MessageConverter = new SimpleMessageConverter(); + } + + private void CheckDefaultDestination() + { + if (defaultDestination == null) + { + throw new SystemException( + "No defaultDestination or defaultDestinationName specified. Check configuration of NmsTemplate."); + } + } + + private void CheckMessageConverter() + { + if (MessageConverter == null) + { + throw new SystemException("No messageConverter registered. Check configuration of NmsTemplate."); + } + } + + /// Execute the action specified by the given action object within a + /// NMS ISession. + /// + /// Generalized version of execute(ISessionCallback), + /// allowing the NMS IConnection to be started on the fly. + ///

    Use execute(ISessionCallback) for the general case. + /// Starting the NMS IConnection is just necessary for receiving messages, + /// which is preferably achieved through the receive methods.

    + ///
    + /// callback object that exposes the session + /// + /// Start the connection before performing callback action. + /// + /// the result object from working with the session + /// + /// NMSException if there is any problem + public virtual object Execute(ISessionCallback action, bool startConnection) + { + AssertUtils.ArgumentNotNull(action, "Callback object must not be null"); + + IConnection conToClose = null; + ISession sessionToClose = null; + try + { + ISession sessionToUse = + ConnectionFactoryUtils.DoGetTransactionalSession(ConnectionFactory, transactionalResourceFactory); + if (sessionToUse == null) + { + conToClose = CreateConnection(); + sessionToClose = CreateSession(conToClose); + if (startConnection) + { + conToClose.Start(); + } + sessionToUse = sessionToClose; + } + if (logger.IsDebugEnabled) + { + logger.Debug("Executing callback on NMS ISession [" + sessionToUse + "]"); + } + return action.DoInNms(sessionToUse); + } + //TODO make sure don't want to do exception translation. + //TODO investigate options to not close session/connection via caching since + //these objects are thread safe in tibco ems. + finally + { + if (!CacheNmsResources) + { + NmsUtils.CloseSession(sessionToClose); + NmsUtils.CloseConnection(conToClose, startConnection); + } + //TODO No IConnectionFactory interface so can't create + // non closing impl of IConnection Need NMS. + } + } + + #endregion + + #region Properties + + /// + /// Gets or sets the default destination to be used on send/receive operations that do not + /// have a destination parameter. + /// + /// Alternatively, specify a "defaultDestinationName", to be + /// dynamically resolved via the DestinationResolver. + /// The default destination. + virtual public IDestination DefaultDestination + { + get { return (defaultDestination as IDestination); } + + set { defaultDestination = value; } + } + + + /// + /// Gets or sets the name of the default destination name + /// to be used on send/receive operations that + /// do not have a destination parameter. + /// + /// + /// Alternatively, specify a NMS IDestination object as "DefaultDestination" + /// + /// The name of the default destination. + virtual public string DefaultDestinationName + { + get { return (defaultDestination as string); } + + set { defaultDestination = value; } + } + + /// + /// Gets or sets the message converter for this template. + /// + /// + /// Used to resolve + /// Object parameters to convertAndSend methods and Object results + /// from receiveAndConvert methods. + ///

    The default converter is a SimpleMessageConverter, which is able + /// to handle IBytesMessages, ITextMessages and IObjectMessages.

    + ///
    + /// The message converter. + virtual public IMessageConverter MessageConverter + { + get { return messageConverter; } + + set { messageConverter = value; } + } + + + //TODO check tibco support for message id...prob yes.. + /// + /// Gets or sets a value indicating whether IMessageIds are. + /// + /// true if [message id enabled]; otherwise, false. + virtual public bool MessageIdEnabled + { + get { return messageIdEnabled; } + + set { messageIdEnabled = value; } + } + + //TODO check tibco support for message id...prob yes.., so don't really need it then. + /// + /// Gets or sets a value indicating whether message timestamps are enabled. + /// + /// + /// true if [message timestamp enabled]; otherwise, false. + /// + virtual public bool MessageTimestampEnabled + { + get { return messageTimestampEnabled; } + + set { messageTimestampEnabled = value; } + } + + + /// + /// Gets or sets a value indicating whether to inhibit the delivery of messages published by its own connection. + /// + /// + /// true if inhibit the delivery of messages published by its own connection; otherwise, false. + virtual public bool PubSubNoLocal + { + get { return pubSubNoLocal; } + + set { pubSubNoLocal = value; } + } + + /// + /// Gets or sets the receive timeout to use for recieve calls. + /// + /// The default is -1, which means no timeout. + /// The receive timeout. + virtual public long ReceiveTimeout + { + get { return receiveTimeout; } + + set { receiveTimeout = value; } + } + + /// + /// Gets or sets a value indicating whether to use explicit Quality of Service values. + /// + /// If "true", then the values of deliveryMode, priority, and timeToLive + /// will be used when sending a message. Otherwise, the default values, + /// that may be set administratively, will be used + /// true if use explicit QoS values; otherwise, false. + virtual public bool ExplicitQosEnabled + { + get { return explicitQosEnabled; } + + set { explicitQosEnabled = value; } + } + + /// + /// Sets a value indicating whether message delivery should be persistent or non-persistent + /// + /// + /// This will set the delivery to persistent or non-persistent + ///

    Default it "true" aka delivery mode "PERSISTENT".

    + ///
    + /// true if [delivery persistent]; otherwise, false. + virtual public bool Persistent + { + get { return persistent; } + + set { persistent = value; } + } + + + //TODO verify admin... + + /// + /// Gets or sets the priority when sending. + /// + /// Since a default value may be defined administratively, + /// this is only used when "isExplicitQosEnabled" equals "true". + /// The priority. + virtual public byte Priority + { + get { return priority; } + + set { priority = value; } + } + + /// + /// Gets or sets the time to live when sending + /// + /// Since a default value may be defined administratively, + /// this is only used when "isExplicitQosEnabled" equals "true". + /// The time to live. + virtual public TimeSpan TimeToLive + { + get { return timeToLive; } + + set { timeToLive = value; } + } + + + virtual public bool CacheNmsResources + { + get { return cacheNmsResources; } + set { cacheNmsResources = value; } + } + + #endregion + + protected virtual object DoConvertFromMessage(IMessage message) + { + if (message != null) + { + return MessageConverter.FromMessage(message); + } + return null; + } + + #region NMS Factory Methods + + /// Fetch an appropriate IConnection from the given NmsResourceHolder. + /// + /// the NmsResourceHolder + /// + /// an appropriate IConnection fetched from the holder, + /// or null if none found + /// + protected virtual IConnection GetConnection(NmsResourceHolder holder) + { + return holder.GetConnection(); + } + + /// Fetch an appropriate ISession from the given NmsResourceHolder. + /// + /// the NmsResourceHolder + /// + /// an appropriate ISession fetched from the holder, + /// or null if none found + /// + protected virtual ISession GetSession(NmsResourceHolder holder) + { + return holder.GetSession(); + } + + /// Create a NMS IConnection via this template's IConnectionFactory. + /// + /// If CacheNmsResource is true, then the connection + /// will be created upon the first invocation and will retrun the same + /// connection on all subsequent calls. + /// + /// A NMS IConnection + /// + /// NMSException if thrown by NMS API methods + protected virtual IConnection CreateConnection() + { + if (this.CacheNmsResources) + { + if (jmsResources.Connection == null) + { + jmsResources.Connection = ConnectionFactory.CreateConnection(); + } + return jmsResources.Connection; + + } + else + { + return ConnectionFactory.CreateConnection(); + } + + } + + /// Create a NMS ISession for the given IConnection. + ///

    This implementation uses NMS 1.1 API.

    + ///
    + /// the NMS IConnection to create a ISession for + /// + /// the new NMS ISession + /// + /// NMSException if thrown by NMS API methods + protected virtual ISession CreateSession(IConnection con) + { + if (CacheNmsResources) + { + if (jmsResources.Session == null) + { + jmsResources.Session = jmsResources.Connection.CreateSession(SessionAcknowledgeMode); + } + return jmsResources.Session; + } + else + { + return con.CreateSession(SessionAcknowledgeMode); + } + } + + + /// Create a NMS IMessageProducer for the given ISession and IDestination, + /// configuring it to disable message ids and/or timestamps (if necessary). + ///

    Delegates to doCreateProducer for creation of the raw + /// NMS IMessageProducer, which needs to be specific to NMS 1.1 or 1.0.2.

    + ///
    + /// the NMS ISession to create a IMessageProducer for + /// + /// the NMS IDestination to create a IMessageProducer for + /// + /// the new NMS IMessageProducer + /// + /// NMSException if thrown by NMS API methods + /// + /// + /// + /// + /// + /// + protected virtual IMessageProducer CreateProducer(ISession session, IDestination destination) + { + IMessageProducer producer = DoCreateProducer(session, destination); + if (!MessageIdEnabled) + { + producer.DisableMessageID = true; + } + if (!MessageTimestampEnabled) + { + producer.DisableMessageTimestamp = true; + } + return producer; + } + + + /// Create a raw NMS IMessageProducer for the given ISession and IDestination. + ///

    This implementation uses NMS 1.1 API.

    + ///
    + /// the NMS ISession to create a IMessageProducer for + /// + /// the NMS IDestination to create a IMessageProducer for + /// + /// the new NMS IMessageProducer + /// + /// NMSException if thrown by NMS API methods + protected virtual IMessageProducer DoCreateProducer(ISession session, IDestination destination) + { + if (CacheNmsResources) + { + if (jmsResources.MessageProducer == null) + { + jmsResources.MessageProducer = session.CreateProducer(destination); + } + return jmsResources.MessageProducer; + } + else + { + return session.CreateProducer(destination); + } + } + + /// Create a NMS IMessageConsumer for the given ISession and IDestination. + ///

    This implementation uses NMS 1.1 API.

    + ///
    + /// the NMS ISession to create a IMessageConsumer for + /// + /// the NMS IDestination to create a IMessageConsumer for + /// + /// the message selector for this consumer (can be null) + /// + /// the new NMS IMessageConsumer + /// + /// NMSException if thrown by NMS API methods + protected virtual IMessageConsumer CreateConsumer(ISession session, IDestination destination, + string messageSelector) + { + // Only pass in the NoLocal flag in case of a Topic: + // Some NMS providers, such as WebSphere MQ 6.0, throw IllegalStateException + // in case of the NoLocal flag being specified for a Queue. + if (PubSubDomain) + { + return session.CreateConsumer(destination, messageSelector, PubSubNoLocal); + } + else + { + return session.CreateConsumer(destination, messageSelector); + } + } + + //TODO refactor to not pass null as a 'switch' for behavior. + + protected internal virtual void DoSend(ISession session, IDestination destination, IMessageCreatorDelegate messageCreatorDelegate) + { + AssertUtils.ArgumentNotNull(messageCreatorDelegate, "IMessageCreatorDelegate must not be null"); + DoSend(session, destination, null, messageCreatorDelegate); + } + + protected internal virtual void DoSend(ISession session, IDestination destination, IMessageCreator messageCreator) + { + AssertUtils.ArgumentNotNull(messageCreator, "IMessageCreator must not be null"); + DoSend(session, destination, messageCreator, null); + } + + /// Send the given NMS message. + /// the NMS ISession to operate on + /// + /// the NMS IDestination to send to + /// + /// callback to create a NMS IMessage + /// + /// delegate callback to create a NMS IMessage + /// + /// NMSException if thrown by NMS API methods + protected internal virtual void DoSend(ISession session, IDestination destination, IMessageCreator messageCreator, + IMessageCreatorDelegate messageCreatorDelegate) + { + + + IMessageProducer producer = CreateProducer(session, destination); + try + { + + IMessage message = null; + if (messageCreator != null) + { + message = messageCreator.CreateMessage(session) ; + } + else { + message = messageCreatorDelegate(session); + } + if (logger.IsDebugEnabled) + { + logger.Debug("Sending created message [" + message + "]"); + } + DoSend(producer, message); + + // Check commit - avoid commit call within a JTA transaction. + if (session.Transacted && !TransactionSynchronizationManager.HasResource(ConnectionFactory)) + { + // Transacted session created by this template -> commit. + NmsUtils.CommitIfNecessary(session); + } + } + finally + { + CloseProducer(producer); + } + } + + private void CloseProducer(IMessageProducer producer) + { + if (!CacheNmsResources) + { + NmsUtils.CloseMessageProducer(producer); + } + } + + /// Actually send the given NMS message. + /// the NMS IMessageProducer to send with + /// + /// the NMS IMessage to send + /// + /// NMSException if thrown by NMS API methods + protected virtual void DoSend(IMessageProducer producer, IMessage message) + { + if (ExplicitQosEnabled) + { + producer.Send(message, Persistent, Priority, TimeToLive); + } + else + { + producer.Send(message); + } + } + + + #endregion + + #region INmsOperations Implementation + + /// Execute the action specified by the given action object within + /// a NMS ISession. + ///

    Note: The value of isPubSubDomain affects the behavior of this method. + /// If isPubSubDomain equals true, then a ISession is passed to the callback. + /// If false, then a ISession is passed to the callback.

    + ///
    + /// callback object that exposes the session + /// + /// the result object from working with the session + /// + /// NMSException if there is any problem + public object Execute(ISessionCallback action) + { + return Execute(action, false); + } + + /// Send a message to a NMS destination. The callback gives access to + /// the NMS session and IMessageProducer in order to do more complex + /// send operations. + /// + /// callback object that exposes the session/producer pair + /// + /// the result object from working with the session + /// + /// NMSException if there is any problem + public object Execute(IProducerCallback action) + { + return Execute(new ProducerCreatorCallback(this, action)); + } + + /// Send a message to the default destination. + ///

    This will only work with a default destination specified!

    + ///
    + /// delegate callback to create a message + /// + /// NMSException if there is any problem + public void SendWithDelegate(IMessageCreatorDelegate messageCreatorDelegate) + { + CheckDefaultDestination(); + if (DefaultDestination != null) + { + SendWithDelegate(DefaultDestination, messageCreatorDelegate); + } + else + { + SendWithDelegate(DefaultDestinationName, messageCreatorDelegate); + } + } + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a ISession. + /// + /// the destination to send this message to + /// + /// delegate callback to create a message + /// + /// NMSException if there is any problem + public void SendWithDelegate(IDestination destination, IMessageCreatorDelegate messageCreatorDelegate) + { + Execute(new SendDestinationCallback(this, destination, messageCreatorDelegate), false); + } + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a ISession. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// delegate callback to create a message + /// + /// NMSException if there is any problem + public void SendWithDelegate(string destinationName, IMessageCreatorDelegate messageCreatorDelegate) + { + Execute(new SendDestinationCallback(this, destinationName, messageCreatorDelegate), false); + } + + /// Send a message to the default destination. + ///

    This will only work with a default destination specified!

    + ///
    + /// callback to create a message + /// + /// NMSException if there is any problem + public void Send(IMessageCreator messageCreator) + { + CheckDefaultDestination(); + if (DefaultDestination != null) + { + Send(DefaultDestination, messageCreator); + } + else + { + Send(DefaultDestinationName, messageCreator); + } + } + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a ISession. + /// + /// the destination to send this message to + /// + /// callback to create a message + /// + /// NMSException if there is any problem + public void Send(IDestination destination, IMessageCreator messageCreator) + { + + Execute(new SendDestinationCallback(this, destination, messageCreator), false); + } + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a ISession. + /// + /// the destination to send this message to + /// + /// callback to create a message + /// + /// NMSException if there is any problem + public void Send(string destinationName, IMessageCreator messageCreator) + { + Execute(new SendDestinationCallback(this, destinationName, messageCreator), false); + } + /// Send the given object to the default destination, converting the object + /// to a NMS message with a configured IMessageConverter. + ///

    This will only work with a default destination specified!

    + ///
    + /// the object to convert to a message + /// + /// NMSException if there is any problem + public void ConvertAndSend(object message) + { + CheckDefaultDestination(); + if (DefaultDestination != null) + { + ConvertAndSend(DefaultDestination, message); + } + else + { + ConvertAndSend(DefaultDestinationName, message); + } + } + + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. + /// + /// the destination to send this message to + /// + /// the object to convert to a message + /// + /// NMSException if there is any problem + public void ConvertAndSend(IDestination destination, object message) + { + CheckMessageConverter(); + Send(destination, new SimpleMessageCreator(this, message)); + } + + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the object to convert to a message + /// + /// NMSException if there is any problem + public void ConvertAndSend(string destinationName, object message) + { + Send(destinationName, new SimpleMessageCreator(this, message)); + } + + /// Send the given object to the default destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + ///

    This will only work with a default destination specified!

    + ///
    + /// the object to convert to a message + /// + /// the callback to modify the message + /// + /// NMSException if there is any problem + public void ConvertAndSend(object message, IMessagePostProcessor postProcessor) + { + CheckDefaultDestination(); + if (DefaultDestination != null) + { + ConvertAndSend(DefaultDestination, message, postProcessor); + } + else + { + ConvertAndSend(DefaultDestinationName, message, postProcessor); + } + } + + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the destination to send this message to + /// + /// the object to convert to a message + /// + /// the callback to modify the message + /// + /// NMSException if there is any problem + public void ConvertAndSend(IDestination destination, object message, IMessagePostProcessor postProcessor) + { + CheckMessageConverter(); + Send(destination, new ConvertAndSendMessageCreator(this, message, postProcessor)); + + } + + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the object to convert to a message. + /// + /// the callback to modify the message + /// + /// NMSException if there is any problem + public void ConvertAndSend(string destinationName, object message, IMessagePostProcessor postProcessor) + { + CheckMessageConverter(); + Send(destinationName, new ConvertAndSendMessageCreator(this, message, postProcessor)); + + } + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///

    This will only work with a default destination specified!

    + ///
    + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + public IMessage Receive() + { + CheckDefaultDestination(); + if (DefaultDestination != null) + { + return Receive(DefaultDestination); + } + else + { + return Receive(DefaultDestinationName); + } + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the destination to receive a message from + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + public IMessage Receive(IDestination destination) + { + return Execute(new ReceiveCallback(this, destination)) as IMessage; + } + + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + public IMessage Receive(string destinationName) + { + return Execute(new ReceiveCallback(this, destinationName)) as IMessage; + } + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///

    This will only work with a default destination specified!

    + ///
    + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + public IMessage ReceiveSelected(string messageSelector) + { + CheckDefaultDestination(); + if (DefaultDestination!= null) + { + return ReceiveSelected(DefaultDestination, messageSelector); + } + else + { + return ReceiveSelected(DefaultDestinationName, messageSelector); + } + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the destination to receive a message from + /// + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + public IMessage ReceiveSelected(IDestination destination, string messageSelector) + { + return Execute(new ReceiveSelectedCallback(this, destination, messageSelector), true) as IMessage; + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// NMSException if there is any problem + public IMessage ReceiveSelected(string destinationName, string messageSelector) + { + return Execute(new ReceiveSelectedCallback(this, destinationName, messageSelector), true) as IMessage; + + } + + protected virtual IMessage DoReceive(ISession session, IDestination destination, string messageSelector) + { + return DoReceive(session, CreateConsumer(session, destination, messageSelector)); + } + + protected virtual IMessage DoReceive(ISession session, IMessageConsumer consumer) + { + try + { + long timeout = ReceiveTimeout; + NmsResourceHolder resourceHolder = + (NmsResourceHolder)TransactionSynchronizationManager.GetResource(ConnectionFactory); + if (resourceHolder != null && resourceHolder.HasTimeout) + { + timeout = Convert.ToInt64(resourceHolder.TimeToLiveInMilliseconds); + } + IMessage message = (timeout > 0) + ? consumer.Receive(new TimeSpan(timeout)) + : consumer.Receive(); + if (session.Transacted) + { + // Commit necessary - but avoid commit call within a JTA transaction. + if (resourceHolder == null) + { + // Transacted session created by this template -> commit. + NmsUtils.CommitIfNecessary(session); + } + } + else if (ClientAcknowledge(session)) + { + // Manually acknowledge message, if any. + if (message != null) + { + message.Acknowledge(); + } + } + return message; + } + finally + { + NmsUtils.CloseMessageConsumer(consumer); + } + } + + /// + /// Returns whether the ISession is in client acknowledgement mode. + /// + /// The session. + /// true ifin client ack mode, false otherwise + protected virtual bool ClientAcknowledge(ISession session) + { + return (session.AcknowledgementMode == AcknowledgementMode.ClientAcknowledge); + } + + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///

    This will only work with a default destination specified!

    + ///
    + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + public object ReceiveAndConvert() + { + CheckMessageConverter(); + return DoConvertFromMessage(Receive()); + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the destination to receive a message from + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + public object ReceiveAndConvert(IDestination destination) + { + CheckMessageConverter(); + return DoConvertFromMessage(Receive(destination)); + } + + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + public object ReceiveAndConvert(string destinationName) + { + CheckMessageConverter(); + return DoConvertFromMessage(Receive(destinationName)); + } + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///

    This will only work with a default destination specified!

    + ///
    + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + public object ReceiveSelectedAndConvert(string messageSelector) + { + CheckMessageConverter(); + return DoConvertFromMessage(ReceiveSelected(messageSelector)); ; + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the destination to receive a message from + /// + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + public object ReceiveSelectedAndConvert(IDestination destination, string messageSelector) + { + CheckMessageConverter(); + return DoConvertFromMessage(ReceiveSelected(destination, messageSelector)); + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

    This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

    + ///
    + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the NMS message selector expression (or null if none). + /// See the NMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// NMSException if there is any problem + public object ReceiveSelectedAndConvert(string destinationName, string messageSelector) + { + CheckMessageConverter(); + return DoConvertFromMessage(ReceiveSelected(destinationName, messageSelector)); + } + + #endregion + + #region Supporting Internal Classes + + private class NmsTemplateResourceFactory : ConnectionFactoryUtils.ResourceFactory + { + private NmsTemplate enclosingTemplateInstance; + + public NmsTemplateResourceFactory(NmsTemplate enclosingInstance) + { + InitBlock(enclosingInstance); + } + + private void InitBlock(NmsTemplate enclosingInstance) + { + enclosingTemplateInstance = enclosingInstance; + } + + public NmsTemplate Enclosing_Instance + { + get { return enclosingTemplateInstance; } + } + + public virtual IConnection GetConnection(NmsResourceHolder holder) + { + return Enclosing_Instance.GetConnection(holder); + } + + public virtual ISession GetSession(NmsResourceHolder holder) + { + return Enclosing_Instance.GetSession(holder); + } + + public virtual IConnection CreateConnection() + { + return Enclosing_Instance.CreateConnection(); + } + + public virtual ISession CreateSession(IConnection con) + { + return Enclosing_Instance.CreateSession(con); + } + + public bool SynchedLocalTransactionAllowed + { + get { return Enclosing_Instance.SessionTransacted; } + } + } + + private class ProducerCreatorCallback : ISessionCallback + { + private NmsTemplate jmsTemplate; + private IProducerCallback producerCallback; + + public ProducerCreatorCallback(NmsTemplate jmsTemplate, IProducerCallback producerCallback) + { + this.jmsTemplate = jmsTemplate; + } + + public object DoInNms(ISession session) + { + IMessageProducer producer = jmsTemplate.CreateProducer(session, null); + try + { + return producerCallback.DoInNms(session, producer); + } + finally + { + NmsUtils.CloseMessageProducer(producer); + } + + } + } + + private class ReceiveCallback : ISessionCallback + { + private NmsTemplate jmsTemplate; + private IDestination destination; + private string destinationName; + + + public ReceiveCallback(NmsTemplate jmsTemplate, string destinationName) + { + this.jmsTemplate = jmsTemplate; + this.destinationName = destinationName; + } + + public ReceiveCallback(NmsTemplate jmsTemplate, IDestination destination) + { + this.jmsTemplate = jmsTemplate; + this.destination = destination; + } + + public object DoInNms(ISession session) + { + if (destination != null) + { + return jmsTemplate.DoReceive(session, destination, null); + } + else + { + return jmsTemplate.DoReceive(session, + jmsTemplate.ResolveDestinationName(session, destinationName), + null); + } + + } + } + + private class ConvertAndSendMessageCreator : IMessageCreator + { + private NmsTemplate jmsTemplate; + private object objectToConvert; + private IMessagePostProcessor messagePostProcessor; + + public ConvertAndSendMessageCreator(NmsTemplate jmsTemplate, object message, IMessagePostProcessor messagePostProcessor) + { + this.jmsTemplate = jmsTemplate; + objectToConvert = message; + this.messagePostProcessor = messagePostProcessor; + } + + public IMessage CreateMessage(ISession session) + { + IMessage msg = jmsTemplate.MessageConverter.ToMessage(objectToConvert, session); + return messagePostProcessor.PostProcessMessage(msg); + } + } + + private class ReceiveSelectedCallback : ISessionCallback + { + private NmsTemplate jmsTemplate; + private string messageSelector; + private string destinationName; + private IDestination destination; + + public ReceiveSelectedCallback(NmsTemplate jmsTemplate, + IDestination destination, + string messageSelector) + { + this.jmsTemplate = jmsTemplate; + this.destination = destination; + this.messageSelector = messageSelector; + } + public ReceiveSelectedCallback(NmsTemplate jmsTemplate, + string destinationName, + string messageSelector) + { + this.jmsTemplate = jmsTemplate; + this.destinationName = destinationName; + this.messageSelector = messageSelector; + } + + public object DoInNms(ISession session) + { + if (destination != null) + { + return jmsTemplate.DoReceive(session, destination, messageSelector); + } + else + { + return jmsTemplate.DoReceive(session, + jmsTemplate.ResolveDestinationName(session, destinationName), + messageSelector); + } + + } + + } + + #endregion + } + + /// + /// This is a TIBCO specific class so that we can reuse connections, session, and + /// message producers instead of creating/destroying them on each operation. + /// + internal class NmsResources + { + private IConnection connection; + private ISession session; + private IMessageProducer messageProducer; + + public IConnection Connection + { + get { return connection; } + set { connection = value; } + } + + public ISession Session + { + get { return session; } + set { session = value; } + } + + public IMessageProducer MessageProducer + { + get { return messageProducer; } + set { messageProducer = value; } + } + } + + + internal class SimpleMessageCreator : IMessageCreator + { + private NmsTemplate jmsTemplate; + private object objectToConvert; + + public SimpleMessageCreator(NmsTemplate jmsTemplate, object objectToConvert) + { + this.jmsTemplate = jmsTemplate; + this.objectToConvert = objectToConvert; + } + + public IMessage CreateMessage(ISession session) + { + return jmsTemplate.MessageConverter.ToMessage(objectToConvert, session); + } + + + } + + + + internal class SendDestinationCallback : ISessionCallback + { + private string destinationName; + private IDestination destination; + private NmsTemplate jmsTemplate; + private IMessageCreator messageCreator; + private IMessageCreatorDelegate messageCreatorDelegate; + + public SendDestinationCallback(NmsTemplate jmsTemplate, string destinationName, IMessageCreator messageCreator) + { + this.jmsTemplate = jmsTemplate; + this.destinationName = destinationName; + this.messageCreator = messageCreator; + } + + public SendDestinationCallback(NmsTemplate jmsTemplate, IDestination destination, IMessageCreator messageCreator) + { + this.jmsTemplate = jmsTemplate; + this.destination = destination; + this.messageCreator = messageCreator; + } + + public SendDestinationCallback(NmsTemplate jmsTemplate, string destinationName, IMessageCreatorDelegate messageCreatorDelegate) + { + this.jmsTemplate = jmsTemplate; + this.destinationName = destinationName; + this.messageCreatorDelegate = messageCreatorDelegate; + } + + public SendDestinationCallback(NmsTemplate jmsTemplate, IDestination destination, IMessageCreatorDelegate messageCreatorDelegate) + { + this.jmsTemplate = jmsTemplate; + this.destination = destination; + this.messageCreatorDelegate = messageCreatorDelegate; + } + + + public object DoInNms(ISession session) + { + if (destination == null) + { + destination = jmsTemplate.ResolveDestinationName(session, destinationName); + } + if (messageCreator != null) + { + jmsTemplate.DoSend(session, destination, messageCreator); + } + else + { + jmsTemplate.DoSend(session, destination, messageCreatorDelegate); + } + return null; + } + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/IMessageConverter.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/IMessageConverter.cs new file mode 100644 index 00000000..2453709b --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/IMessageConverter.cs @@ -0,0 +1,55 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using NMS; + +namespace Spring.Messaging.Nms.Support.Converter +{ + /// Strategy interface that specifies a IMessageConverter + /// between .NET objects and NMS messages. + /// + /// + /// Mark Pollack + /// Juergen Hoeller + /// Mark Pollack (.NET) + public interface IMessageConverter + { + /// Convert a .NET object to a NMS IMessage using the supplied session + /// to create the message object. + /// + /// the object to convert + /// + /// the ISession to use for creating a NMS IMessage + /// + /// the NMS IMessage + /// + /// NMSException if thrown by NMS API methods + /// IMessageConversionException in case of conversion failure + IMessage ToMessage(object objectToConvert, ISession session); + + /// Convert from a NMS IMessage to a .NET object. + /// the message to convert + /// + /// the converted .NET object + /// + /// IMessageConversionException in case of conversion failure + object FromMessage(IMessage message); + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/MessageConversionException.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/MessageConversionException.cs new file mode 100644 index 00000000..c7fbca9f --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/MessageConversionException.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Spring.Messaging.Nms.Support.Converter +{ + /// Thrown by IMessageConverter implementations when the conversion + /// of an object to/from a IMessage fails. + /// + /// + /// Mark Pollack + [Serializable] + public class IMessageConversionException : ApplicationException + { + //TODO add jms root exception hierarchy?.... + #region Constructor (s) / Destructor + /// Creates a new instance of the IMessageConverterException class. + public IMessageConversionException() + { + } + + /// + /// Creates a new instance of the IMessageConverterException class. with the specified message. + /// + /// + /// A message about the exception. + /// + public IMessageConversionException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of the IMessageConverterException class with the specified message + /// and root cause. + /// + /// + /// A message about the exception. + /// + /// + /// The root exception that is being wrapped. + /// + public IMessageConversionException(string message, Exception rootCause) + : base(message, rootCause) + { + } + + /// + /// Creates a new instance of the IMessageConverterException class. + /// + /// + /// The + /// that holds the serialized object data about the exception being thrown. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + protected IMessageConversionException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + #endregion + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/SimpleMessageConverter.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/SimpleMessageConverter.cs new file mode 100644 index 00000000..0fcf3c02 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/SimpleMessageConverter.cs @@ -0,0 +1,233 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Runtime.Serialization; +using NMS; + +namespace Spring.Messaging.Nms.Support.Converter +{ + /// A simple message converter that can handle ITextMessages, IBytesMessages, + /// IMapMessages, and IObjectMessages. Used as default by NmsTemplate, for + /// ConvertAndSend and ReceiveAndConvert operations. + /// + ///

    Converts a String to a NMS ITextMessage, a byte array to a NMS IBytesMessage, + /// a Map to a NMS IMapMessage, and a Serializable object to a NMS IObjectMessage + /// (or vice versa).

    + /// + /// + ///
    + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class SimpleMessageConverter : IMessageConverter + { + /// Convert a .NET object to a NMS IMessage using the supplied session + /// to create the message object. + /// + /// the object to convert + /// + /// the ISession to use for creating a NMS IMessage + /// + /// the NMS IMessage + /// + /// NMSException if thrown by NMS API methods + /// IMessageConversionException in case of conversion failure + public IMessage ToMessage(object objectToConvert, ISession session) + { + if (objectToConvert is IMessage) + { + return (IMessage) objectToConvert; + } + else if (objectToConvert is string) + { + return CreateMessageForString((string) objectToConvert, session); + } + else if (objectToConvert is sbyte[]) + { + return CreateMessageForByteArray((byte[]) objectToConvert, session); + } + else if (objectToConvert is IDictionary) + { + return CreateMessageForMap((IDictionary) objectToConvert, session); + } + else if (objectToConvert is ISerializable) + { + return + CreateMessageForSerializable(((ISerializable) objectToConvert), session); + } + else + { + throw new IMessageConversionException("Cannot convert object [" + objectToConvert + "] to NMS message"); + } + } + + /// Convert from a NMS IMessage to a .NET object. + /// the message to convert + /// + /// the converted .NET object + /// + /// IMessageConversionException in case of conversion failure + public object FromMessage(IMessage message) + { + if (message is ITextMessage) + { + return ExtractStringFromMessage((ITextMessage) message); + } + else if (message is IBytesMessage) + { + return ExtractByteArrayFromMessage((IBytesMessage) message); + } + else if (message is IMapMessage) + { + return ExtractMapFromMessage((IMapMessage) message); + } + else if (message is IObjectMessage) + { + return ExtractSerializableFromMessage((IObjectMessage) message); + } + else + { + return message; + } + } + + #region To Converter Methods + + /// Create a NMS ITextMessage for the given String. + /// the String to convert + /// + /// current NMS session + /// + /// the resulting message + /// + /// NMSException if thrown by NMS methods + protected virtual ITextMessage CreateMessageForString(string text, ISession session) + { + return session.CreateTextMessage((text)); + } + + /// Create a NMS IBytesMessage for the given byte array. + /// the byyte array to convert + /// + /// current NMS session + /// + /// the resulting message + /// + /// NMSException if thrown by NMS methods + protected virtual IBytesMessage CreateMessageForByteArray(byte[] bytes, ISession session) + { + IBytesMessage message = session.CreateBytesMessage(); + message.Content = bytes; + return message; + } + + /// Create a NMS IMapMessage for the given Map. + /// the Map to convert + /// + /// current NMS session + /// + /// the resulting message + /// + /// NMSException if thrown by NMS methods + protected virtual IMapMessage CreateMessageForMap(IDictionary map, ISession session) + { + IMapMessage mapMessage = session.CreateMapMessage(); + foreach (DictionaryEntry entry in map) + { + if (!(entry.Key is string)) + { + //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Class.getName' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'" + throw new IMessageConversionException("Cannot convert non-String key of type [" + + (entry.Key != null ? entry.Key.GetType().FullName : null) + + "] to IMapMessage entry"); + } + mapMessage.Body[entry.Key.ToString()] = entry.Value; + } + return mapMessage; + } + + /// Create a NMS IObjectMessage for the given Serializable object. + /// the Serializable object to convert + /// + /// current NMS session + /// + /// the resulting message + /// + /// NMSException if thrown by NMS methods + protected virtual IObjectMessage CreateMessageForSerializable( + ISerializable objectToSend, ISession session) + { + return session.CreateObjectMessage(objectToSend); + } + + #endregion + + #region From Converter Mehtods + + /// Extract a String from the given ITextMessage. + /// the message to convert + /// + /// the resulting String + /// + /// NMSException if thrown by NMS methods + protected virtual string ExtractStringFromMessage(ITextMessage message) + { + return message.Text; + } + + /// Extract a byte array from the given IBytesMessage. + /// the message to convert + /// + /// the resulting byte array + /// + /// NMSException if thrown by NMS methods + protected virtual byte[] ExtractByteArrayFromMessage(IBytesMessage message) + { + return message.Content; + } + + /// Extract a IDictionary from the given IMapMessage. + /// the message to convert + /// + /// the resulting Map + /// + /// NMSException if thrown by NMS methods + protected virtual IDictionary ExtractMapFromMessage(IMapMessage message) + { + IDictionary dictionary = new Hashtable(); + IEnumerator e = message.Body.Keys.GetEnumerator(); + while (e.MoveNext()) + { + String key = (String) e.Current; + dictionary.Add(key, message.Body[key]); + } + return dictionary; + } + + protected virtual object ExtractSerializableFromMessage( + IObjectMessage message) + { + return message.Body as ISerializable; + } + + #endregion + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/DynamicDestinationResolver.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/DynamicDestinationResolver.cs new file mode 100644 index 00000000..7587e40f --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/DynamicDestinationResolver.cs @@ -0,0 +1,78 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using Spring.Util; +using NMS; + +namespace Spring.Messaging.Nms.Support.IDestinations +{ + /// Simple DestinationResolver implementation resolving destination names + /// as dynamic destinations. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class DynamicDestinationResolver : IDestinationResolver + { + /// Resolve the given destination name, either as located resource + /// or as dynamic destination. + /// + /// the current NMS ISession + /// + /// the name of the destination + /// + /// true if the domain is pub-sub, false if P2P + /// + /// the NMS destination (either a topic or a queue) + /// + /// NMSException if resolution failed + public IDestination ResolveDestinationName(ISession session, string destinationName, bool pubSubDomain) + { + AssertUtils.ArgumentNotNull(session, "ISession must not be null"); + AssertUtils.ArgumentNotNull(destinationName, "IDestination name must not be null"); + if (pubSubDomain) + { + return ResolveTopic(session, destinationName); + } + else + { + return ResolveQueue(session, destinationName); + } + } + + + /// Resolve the given destination name to a Topic. + /// the current NMS ISession + /// + /// the name of the desired Topic. + /// + /// the NMS Topic name + /// + /// NMSException if resolution failed + protected internal virtual IDestination ResolveTopic(ISession session, System.String topicName) + { + return session.GetTopic(topicName); + } + + protected internal virtual IDestination ResolveQueue(ISession session, string queueName) + { + return session.GetQueue(queueName); + } + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/IDestinationResolver.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/IDestinationResolver.cs new file mode 100644 index 00000000..ea19c104 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/IDestinationResolver.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using NMS; + +namespace Spring.Messaging.Nms.Support.IDestinations +{ + /// Strategy interface for resolving NMS destinations. + /// + /// + /// Used by NmsTemplate for resolving + /// destination names from simple Strings to actual + /// IDestination implementation instances. + /// + /// + /// The default DestinationResolver implementation used by + /// NmsTemplate instances is the + /// DynamicDestinationResolver class. Consider using the + /// JndiDestinationResolver for more advanced scenarios. + /// + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + public interface IDestinationResolver + { + /// Resolve the given destination name, either as located resource + /// or as dynamic destination. + /// + /// the current NMS ISession + /// + /// the name of the destination + /// + /// true if the domain is pub-sub, false if P2P + /// + /// the NMS destination (either a topic or a queue) + /// + /// NMSException if resolution failed + IDestination ResolveDestinationName(ISession session, string destinationName, bool pubSubDomain); + + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/NmsDestinationAccessor.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/NmsDestinationAccessor.cs new file mode 100644 index 00000000..37ec23ae --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/NmsDestinationAccessor.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using Spring.Util; +using NMS; + +namespace Spring.Messaging.Nms.Support.IDestinations +{ + /// Base class for NmsTemplate} and other + /// NMS-accessing gateway helpers, adding destination-related properties to + /// NmsAccessor's common properties. + /// + /// + ///

    Not intended to be used directly. See NmsTemplate.

    + /// + ///
    + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class NmsDestinationAccessor : NmsAccessor + { + #region Fields + + private IDestinationResolver destinationResolver = new DynamicDestinationResolver(); + + private bool pubSubDomain = false; + + #endregion + + #region Properties + + /// + /// Gets or sets the destination resolver that is to be used to resolve + /// IDestination references for this accessor. + /// + /// The default resolver is a DynamicDestinationResolver. Specify a + /// JndiDestinationResolver for resolving destination names as JNDI locations. + /// + /// The destination resolver. + virtual public IDestinationResolver DestinationResolver + { + get + { + return destinationResolver; + } + + set + { + AssertUtils.ArgumentNotNull(value, "DestinationResolver must not be null"); + this.destinationResolver = value; + } + + } + + + /// + /// Gets or sets a value indicating whether Publish/Subscribe + /// domain (Topics) is used. Otherwise, the Point-to-Point domain + /// (Queues) is used. + /// + /// + /// this + /// setting tells what type of destination to create if dynamic destinations are enabled. + /// true if Publish/Subscribe domain; otherwise, false + /// for the Point-to-Point domain. + public virtual bool PubSubDomain + { + get + { + return pubSubDomain; + } + + set + { + this.pubSubDomain = value; + } + + } + + #endregion + + public virtual IDestination ResolveDestinationName(ISession session, System.String destinationName) + { + return DestinationResolver.ResolveDestinationName(session, destinationName, PubSubDomain); + } + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs new file mode 100644 index 00000000..9e47e2a4 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs @@ -0,0 +1,130 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Common.Logging; +using Spring.Objects.Factory; +using NMS; + +namespace Spring.Messaging.Nms.Support +{ + /// Base class for NmsTemplate and other + /// NMS-accessing gateway helpers + /// It defines common properties like the + /// IConnectionFactory}. The subclass + /// NmsIDestinationAccessor adds + /// further, destination-related properties. + /// + /// Not intended to be used directly. See NmsTemplate. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class NmsAccessor : IInitializingObject + { + #region Logging + + protected readonly ILog logger = LogManager.GetLogger(typeof(NmsAccessor)); + + #endregion + + #region Fields + + private IConnectionFactory connectionFactory; + private bool sessionTransacted = false; + private AcknowledgementMode sessionAcknowledgeMode = AcknowledgementMode.AutoAcknowledge; + + #endregion + + #region Properties + + + /// + /// Gets or sets the connection factory to use for obtaining NMS IConnections. + /// + /// The connection factory. + virtual public IConnectionFactory ConnectionFactory + { + get + { + return connectionFactory; + } + + set + { + this.connectionFactory = value; + } + } + + + /// + /// Gets or sets the session acknowledge mode for NMS Sessions including whether or not the session is transacted + /// + /// + /// Set the NMS acknowledgement mode that is used when creating a NMS + /// ISession to send a message. The default is ISession.AUTO_ACKNOWLEDGE. + ///

    Vendor-specific extensions to the acknowledgment mode can be set here as well.

    + ///
    + /// The session acknowledge mode. + virtual public AcknowledgementMode SessionAcknowledgeMode + { + get + { + return sessionAcknowledgeMode; + } + + set + { + this.sessionAcknowledgeMode = value; + } + + } + + /// + /// Set the transaction mode that is used when creating a NMS Session. + /// Default is "false". + /// + /// + /// Setting this flag to "true" will use a short local NMS transaction + /// when running outside of a managed transaction, and a synchronized local + /// NMS transaction in case of a managed transaction being present. + /// The latter has the effect of a local NMS + /// transaction being managed alongside the main transaction (which might + /// be a native ADO.NET transaction), with the NMS transaction committing + /// right after the main transaction. + /// + /// + public bool SessionTransacted + { + get { return sessionTransacted; } + set { sessionTransacted = value; } + } + + #endregion + + + public virtual void AfterPropertiesSet() + { + if (ConnectionFactory == null) + { + throw new ArgumentException("ConnectionFactory is required"); + } + } + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsUtils.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsUtils.cs new file mode 100644 index 00000000..b22472e8 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsUtils.cs @@ -0,0 +1,239 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Common.Logging; +using Spring.Util; +using NMS; + +namespace Spring.Messaging.Nms.Support +{ + public abstract class NmsUtils + { + #region Logging + + private static readonly ILog logger = LogManager.GetLogger(typeof(NmsUtils)); + + #endregion + + /// Close the given NMS IConnection and ignore any thrown exception. + /// This is useful for typical finally blocks in manual NMS code. + /// + /// the NMS IConnection to close (may be null) + /// + public static void CloseConnection(IConnection con) + { + CloseConnection(con, false); + } + + /// Close the given NMS IConnection and ignore any thrown exception. + /// This is useful for typical finally blocks in manual NMS code. + /// + /// the NMS IConnection to close (may be null) + /// + /// whether to call stop() before closing + /// + public static void CloseConnection(IConnection con, bool stop) + { + if (con != null) + { + try + { + if (stop) + { + try + { + con.Stop(); + } + finally + { + con.Close(); + } + } + else + { + con.Close(); + } + } + catch (NMSException ex) + { + logger.Debug("Could not close NMS IConnection", ex); + } + catch (Exception ex) + { + // We don't trust the NMS provider: It might throw another exception. + logger.Debug("Unexpected exception on closing NMS IConnection", ex); + } + } + } + + /// Close the given NMS ISession and ignore any thrown exception. + /// This is useful for typical finally blocks in manual NMS code. + /// + /// the NMS ISession to close (may be null) + /// + public static void CloseSession(ISession session) + { + if (session != null) + { + try + { + session.Close(); + } + catch (NMSException ex) + { + logger.Debug("Could not close NMS ISession", ex); + } + catch (Exception ex) + { + // We don't trust the NMS provider: It might throw RuntimeException or Error. + logger.Debug("Unexpected exception on closing NMS ISession", ex); + } + } + } + + /// Close the given NMS IMessageProducer and ignore any thrown exception. + /// This is useful for typical finally blocks in manual NMS code. + /// + /// the NMS IMessageProducer to close (may be null) + /// + public static void CloseMessageProducer(IMessageProducer producer) + { + if (producer != null) + { + try + { + producer.Dispose(); + } + catch (NMSException ex) + { + logger.Debug("Could not close NMS IMessageProducer", ex); + } + catch (Exception ex) + { + // We don't trust the NMS provider: It might throw RuntimeException or Error. + logger.Debug("Unexpected exception on closing NMS IMessageProducer", ex); + } + } + } + + /// Close the given NMS IMessageConsumer and ignore any thrown exception. + /// This is useful for typical finally blocks in manual NMS code. + /// + /// the NMS IMessageConsumer to close (may be null) + /// + public static void CloseMessageConsumer(IMessageConsumer consumer) + { + if (consumer != null) + { + try + { + consumer.Close(); + } + catch (NMSException ex) + { + logger.Debug("Could not close NMS IMessageConsumer", ex); + } + catch (Exception ex) + { + // We don't trust the NMS provider: It might throw RuntimeException or Error. + logger.Debug("Unexpected exception on closing NMS IMessageConsumer", ex); + } + } + } +/* + /// Close the given NMS QueueRequestor and ignore any thrown exception. + /// This is useful for typical finally blocks in manual NMS code. + /// + /// the NMS QueueRequestor to close (may be null) + /// + */ +// public static void CloseQueueRequestor(QueueRequestor requestor) +// { +// if (requestor != null) +// { +// try +// { +// requestor.Close(); +// } +// catch (NMSException ex) +// { +// logger.Debug("Could not close NMS QueueRequestor", ex); +// } +// catch (Exception ex) +// { +// // We don't trust the NMS provider: It might throw RuntimeException or Error. +// logger.Debug("Unexpected exception on closing NMS QueueRequestor", ex); +// } +// } +// } + + + /// Commit the ISession if not within a distributed transaction. + /// Needs investigation - no distributed tx in EMS + /// the NMS ISession to commit + /// + /// NMSException if committing failed + public static void CommitIfNecessary(ISession session) + { + AssertUtils.ArgumentNotNull(session, "ISession must not be null"); + + session.Commit(); + + // TODO Investigate + +// try { +// session.Commit(); +// } +// catch (TransactionInProgressException ex) { +// // TODO Investigate +// // Ignore -> can only happen in case of a JTA transaction. +// } +// catch (IllegalStateException ex) { +// // TODO Investigate +// // Ignore -> can only happen in case of a JTA transaction. +// } + } + + /// Rollback the ISession if not within a distributed transaction. + /// Needs investigation - no distributed tx in EMS + /// the NMS ISession to rollback + /// + /// NMSException if committing failed + public static void RollbackIfNecessary(ISession session) + { + AssertUtils.ArgumentNotNull(session, "ISession must not be null"); + session.Rollback(); + + // TODO Investigate + +// try { +// session.Rollback(); +// } +// catch (TransactionInProgressException ex) { +// // Ignore -> can only happen in case of a JTA transaction. +// } +// catch (IllegalStateException ex) { +// // Ignore -> can only happen in case of a JTA transaction. +// } + } + + } +} diff --git a/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2003.csproj b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2003.csproj new file mode 100644 index 00000000..14b503f6 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2003.csproj @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj new file mode 100644 index 00000000..ce149c7f --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj @@ -0,0 +1,100 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {AEB1578C-9018-4D49-B440-789F38DD2F29} + Library + Properties + Spring + Spring.Messaging.Nms + + + true + full + false + ..\..\..\build\VS.NET.2005\Spring.Messaging.Nms\Debug\ + DEBUG;TRACE + prompt + 4 + ..\..\..\build\VS.NET.2005\Spring.Messaging.Nms\Debug\Spring.Messaging.Nms.xml + + + pdbonly + true + ..\..\..\build\VS.NET.2005\Spring.Messaging.Nms\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\lib\net\2.0\antlr.runtime.dll + + + False + ..\..\..\lib\net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\net\2.0\NMS.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 + + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.build b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.build new file mode 100644 index 00000000..02564930 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.build @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/AdaptableJobFactory.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/AdaptableJobFactory.cs new file mode 100644 index 00000000..aea613ed --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/AdaptableJobFactory.cs @@ -0,0 +1,111 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System; +using System.Threading; + +using Quartz; +using Quartz.Spi; +using Quartz.Util; + +namespace Spring.Scheduling.Quartz +{ + /// + /// JobFactory implementation that supports + /// objects as well as standard Quartz instances. + /// + /// Juergen Hoeller + /// Marko Lahma (.NET) + /// + /// + public class AdaptableJobFactory : IJobFactory + { + /// + /// Called by the scheduler at the time of the trigger firing, in order to + /// produce a instance on which to call Execute. + /// + /// + /// It should be extremely rare for this method to throw an exception - + /// basically only the the case where there is no way at all to instantiate + /// and prepare the Job for execution. When the exception is thrown, the + /// Scheduler will move all triggers associated with the Job into the + /// state, which will require human + /// intervention (e.g. an application restart after fixing whatever + /// configuration problem led to the issue wih instantiating the Job. + /// + /// The TriggerFiredBundle from which the + /// and other info relating to the trigger firing can be obtained. + /// the newly instantiated Job + /// SchedulerException if there is a problem instantiating the Job. + public virtual IJob NewJob(TriggerFiredBundle bundle) + { + try + { + object jobObject = CreateJobInstance(bundle); + return AdaptJob(jobObject); + } + catch (Exception ex) + { + throw new SchedulerException("Job instantiation failed", ex); + } + } + + /// + /// Create an instance of the specified job class. + ///

    + /// Can be overridden to post-process the job instance. + ///

    + ///
    + /// + /// The TriggerFiredBundle from which the JobDetail + /// and other info relating to the trigger firing can be obtained. + /// + /// The job instance. + protected virtual object CreateJobInstance(TriggerFiredBundle bundle) + { + return ObjectUtils.InstantiateType(bundle.JobDetail.JobType); + } + + /// + /// Adapt the given job object to the Quartz Job interface. + /// + /// + /// The default implementation supports straight Quartz Jobs + /// as well as Runnables, which get wrapped in a DelegatingJob. + /// + /// + /// The original instance of the specified job class. + /// + /// The adapted Quartz Job instance. + /// + protected virtual IJob AdaptJob(object jobObject) + { + if (jobObject is IJob) + { + return (IJob) jobObject; + } + else if (jobObject is ThreadStart) + { + return new DelegatingJob((ThreadStart)jobObject); + } + else + { + throw new ArgumentException( + string.Format("Unable to execute job class [{0}]: only [IJob] and [ThreadStart] supported.", + jobObject.GetType().FullName)); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/CronTriggerObject.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/CronTriggerObject.cs new file mode 100644 index 00000000..e0466316 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/CronTriggerObject.cs @@ -0,0 +1,202 @@ +/* +* Copyright 2002-2007 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Collections; +using System.Reflection; + +using Quartz; +using Spring.Objects.Factory; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Convenience subclass of Quartz's CronTrigger type, making property based + /// usage easier. + /// + /// + ///

    + /// CronTrigger itself is already property based but lacks sensible defaults. + /// This class uses the Spring object name as job name, the Quartz default group + /// ("DEFAULT") as job group, the current time as start time, and indefinite + /// repetition, if not specified. + ///

    + ///

    + /// This class will also register the trigger with the job name and group of + /// a given . This allows + /// to automatically register a trigger for the corresponding JobDetail, + /// instead of registering the JobDetail separately. + ///

    + ///
    + /// Juergen Hoeller + /// + /// + /// + /// + /// + /// + /// + /// + public class CronTriggerObject : CronTrigger, IJobDetailAwareTrigger, IObjectNameAware, IInitializingObject + { + private JobDetail jobDetail; + private string objectName; + private readonly Constants constants = new Constants(typeof(MisfirePolicy.CronTrigger), typeof(MisfirePolicy)); + + + /// + /// Register objects in the JobDataMap via a given Map. + /// + /// + /// These objects will be available to this Trigger only, + /// in contrast to objects in the JobDetail's data map. + /// + /// + public virtual IDictionary JobDataAsMap + { + set { JobDataMap.PutAll(value); } + } + + /// + /// Set the misfire instruction via the name of the corresponding + /// constant in the CronTrigger class. + /// Default is MISFIRE_INSTRUCTION_SMART_POLICY. + /// + /// + /// + /// + public virtual string MisfireInstructionName + { + set + { + MisfireInstruction = constants.AsNumber(value); + } + } + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// The name of the object in the factory. + /// + ///

    + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

    + ///
    + public virtual string ObjectName + { + set { objectName = value; } + } + + /// + /// Set the JobDetail that this trigger should be associated with. + /// + /// + /// This is typically used with a object reference if the JobDetail + /// is a Spring-managed object. Alternatively, the trigger can also + /// be associated with a job by name and group. + /// + /// + /// + public virtual JobDetail JobDetail + { + get { return jobDetail; } + set { jobDetail = value; } + } + + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public virtual void AfterPropertiesSet() + { + if (Name == null) + { + Name = objectName; + } + if (Group == null) + { + Group = SchedulerConstants.DEFAULT_GROUP; + } + if (StartTimeUtc == DateTime.MinValue) + { + StartTimeUtc = DateTime.UtcNow; + } + if (TimeZone == null) + { + TimeZone = TimeZone.CurrentTimeZone; + } + if (jobDetail != null) + { + JobName = jobDetail.Name; + JobGroup = jobDetail.Group; + } + } + } + + /// + /// Helper class to map constant names to their values. + /// + internal class Constants + { + private readonly Type[] types; + + public Constants(params Type[] reflectedTypes) + { + types = reflectedTypes; + } + + public int AsNumber(string field) + { + foreach (Type type in types) + { + FieldInfo fi = type.GetField(field); + if (fi != null) + { + return Convert.ToInt32(fi.GetValue(null)); + } + } + + // not found + throw new Exception(string.Format("Unknown field '{0}'", field)); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/DelegatingJob.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/DelegatingJob.cs new file mode 100644 index 00000000..afd67c1e --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/DelegatingJob.cs @@ -0,0 +1,82 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System; +using System.Threading; + +using Quartz; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Simple Quartz IJob adapter that delegates to a + /// given instance. + /// + /// + /// Typically used in combination with property injection on the + /// Runnable instance, receiving parameters from the Quartz JobDataMap + /// that way instead of via the JobExecutionContext. + /// + /// Juergen Hoeller + /// Marko Lahma (.NET) + /// + /// + public class DelegatingJob : IJob + { + private ThreadStart delegateInstance; + + /// + /// Return the wrapped Runnable implementation. + /// + /// The delegate. + public virtual ThreadStart Delegate + { + get { return delegateInstance; } + } + + /// + /// Create a new DelegatingJob. + /// + /// + /// The Runnable implementation to delegate to. + /// + public DelegatingJob(ThreadStart delegateInstance) + { + if (delegateInstance == null) + { + throw new ArgumentException("Delegate must not be null", "delegateInstance"); + } + this.delegateInstance = delegateInstance; + } + + + /// + /// Delegates execution to the underlying ThreadStart, + /// converting any Exception thrown to a Quartz JobExecutionException + /// (as required by the Job contract). + /// + public virtual void Execute(JobExecutionContext context) + { + try + { + delegateInstance.Invoke(); + } + catch (Exception ex) + { + throw new JobExecutionException(ex); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/IJobDetailAwareTrigger.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/IJobDetailAwareTrigger.cs new file mode 100644 index 00000000..bd64614b --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/IJobDetailAwareTrigger.cs @@ -0,0 +1,49 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using Quartz; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Interface to be implemented by Quartz Triggers that are aware + /// of the JobDetail object that they are associated with. + /// + /// + ///

    + /// SchedulerFactoryObject will auto-detect Triggers that implement this + /// interface and register them for the respective JobDetail accordingly. + ///

    + /// + ///

    + /// The alternative is to configure a Trigger for a Job name and group: + /// This involves the need to register the JobDetail object separately + /// with SchedulerFactoryObject. + ///

    + ///
    + /// Juergen Hoeller + /// + /// + /// + /// + public interface IJobDetailAwareTrigger + { + /// + /// Return the JobDetail that this Trigger is associated with. + /// + /// The associated JobDetail, or null if none + JobDetail JobDetail { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ISchedulerContextAware.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ISchedulerContextAware.cs new file mode 100644 index 00000000..54c46a7d --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ISchedulerContextAware.cs @@ -0,0 +1,41 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using Quartz; +using Quartz.Spi; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Callback interface to be implemented by Spring-managed + /// Quartz artifacts that need access to the SchedulerContext + /// (without having natural access to it). + /// + /// + /// Currently only supported for custom JobFactory implementations + /// that are passed in via Spring's SchedulerFactoryObject. + /// + /// Juergen Hoeller + /// + /// + public interface ISchedulerContextAware + { + /// + /// Set the SchedulerContext of the current Quartz Scheduler. + /// + /// + SchedulerContext SchedulerContext { set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ISchedulingTaskExecutor.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ISchedulingTaskExecutor.cs new file mode 100644 index 00000000..0112e7b5 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ISchedulingTaskExecutor.cs @@ -0,0 +1,36 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +namespace Spring.Scheduling +{ + /// + /// + /// + public interface ISchedulingTaskExecutor : ITaskExecutor + { + /// + /// Gets a value indicating whether´this instance prefers short lived tasks. + /// + /// + /// true if prefers short lived tasks; otherwise, false. + /// + bool PrefersShortLivedTasks + { + get; + } + + } +} diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ITaskExecutor.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ITaskExecutor.cs new file mode 100644 index 00000000..29158527 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ITaskExecutor.cs @@ -0,0 +1,31 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System.Threading; + +namespace Spring.Scheduling +{ + /// + /// + /// + public interface ITaskExecutor + { + /// + /// Executes this instance. + /// + void Execute(ThreadStart runnable); + } +} diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/JobDetailObject.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/JobDetailObject.cs new file mode 100644 index 00000000..1b53eac2 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/JobDetailObject.cs @@ -0,0 +1,216 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System; +using System.Collections; +using Quartz; +using Spring.Context; +using Spring.Objects.Factory; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Convenience subclass of Quartz' JobDetail class that eases properties based + /// usage. + /// + /// + /// itself is already a object but lacks + /// sensible defaults. This class uses the Spring object name as job name, + /// and the Quartz default group ("DEFAULT") as job group if not specified. + /// + /// Juergen Hoeller + /// + /// + /// + public class JobDetailObject : JobDetail, IObjectNameAware, IApplicationContextAware, IInitializingObject + { + private Type actualJobType; + private string objectName; + private IApplicationContext applicationContext; + private string applicationContextJobDataKey; + + /// + /// Overridden to support any job class, to allow a custom JobFactory + /// to adapt the given job class to the Quartz Job interface. + /// + /// + public override Type JobType + { + get { return (actualJobType != null ? actualJobType : base.JobType); } + + set + { + if (value != null && !typeof (IJob).IsAssignableFrom(value)) + { + base.JobType = typeof (DelegatingJob); + actualJobType = value; + } + else + { + base.JobType = value; + } + } + } + + /// + /// Register objects in the JobDataMap via a given Map. + /// + /// + /// These objects will be available to this Job only, + /// in contrast to objects in the SchedulerContext. + ///

    + /// Note: When using persistent Jobs whose JobDetail will be kept in the + /// database, do not put Spring-managed objects or an ApplicationContext + /// reference into the JobDataMap but rather into the SchedulerContext. + ///

    + ///
    + /// + public virtual IDictionary JobDataAsMap + { + set + { + if (value == null) + { + throw new ArgumentException("Value cannot be null", "value"); + } + JobDataMap.PutAll(value); + } + } + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// The name of the object in the factory. + /// + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + /// + public virtual string ObjectName + { + set { objectName = value; } + } + + /// + /// Set the that this + /// object runs in. + /// + /// + /// + ///

    + /// Normally this call will be used to initialize the object. + ///

    + ///

    + /// Invoked after population of normal object properties but before an + /// init callback such as + /// 's + /// + /// or a custom init-method. Invoked after the setting of any + /// 's + /// + /// property. + ///

    + ///
    + /// + /// In the case of application context initialization errors. + /// + /// + /// If thrown by any application context methods. + /// + /// + public virtual IApplicationContext ApplicationContext + { + set { applicationContext = value; } + get { return applicationContext; } + } + + /// + /// Set the key of an IApplicationContext reference to expose in the JobDataMap, + /// for example "applicationContext". Default is none. + /// Only applicable when running in a Spring ApplicationContext. + /// + /// + ///

    + /// In case of a QuartzJobObject, the reference will be applied to the Job + /// instance as object property. An "applicationContext" attribute will correspond + /// to a "setApplicationContext" method in that scenario. + ///

    + ///

    + /// Note that ObjectFactory callback interfaces like IApplicationContextAware + /// are not automatically applied to Quartz Job instances, because Quartz + /// itself is responsible for the lifecycle of its Jobs. + ///

    + ///

    + /// Note: When using persistent job stores where JobDetail contents will + /// be kept in the database, do not put an IApplicationContext reference into + /// the JobDataMap but rather into the SchedulerContext. + ///

    + ///
    + /// + /// + public virtual string ApplicationContextJobDataKey + { + set { applicationContextJobDataKey = value; } + } + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public virtual void AfterPropertiesSet() + { + if (Name == null) + { + Name = objectName; + } + if (Group == null) + { + Group = SchedulerConstants.DEFAULT_GROUP; + } + if (applicationContextJobDataKey != null) + { + if (applicationContext == null) + { + throw new ArgumentException("JobDetailObject needs to be set up in an IApplicationContext " + + "to be able to handle an 'applicationContextJobDataKey'"); + } + JobDataMap.Put(applicationContextJobDataKey, applicationContext); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/LocalTaskExecutorThreadPool.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/LocalTaskExecutorThreadPool.cs new file mode 100644 index 00000000..6d3196ef --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/LocalTaskExecutorThreadPool.cs @@ -0,0 +1,135 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System.Threading; + +using Common.Logging; +using Quartz; +using Quartz.Spi; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Quartz ThreadPool adapter that delegates to a Spring-managed + /// TaskExecutor instance, specified on SchedulerFactoryObject. + /// + /// Juergen Hoeller + /// + public class LocalTaskExecutorThreadPool : IThreadPool + { + /// + /// Logger available to subclasses. + /// + protected ILog logger; + + private ITaskExecutor taskExecutor; + + /// + /// Initializes a new instance of the class. + /// + public LocalTaskExecutorThreadPool() + { + logger = LogManager.GetLogger(GetType()); + } + + /// + /// Gets the size of the pool. + /// + /// The size of the pool. + public virtual int PoolSize + { + get { return - 1; } + } + + /// + /// Called by the QuartzScheduler before the is + /// used, in order to give the it a chance to Initialize. + /// + public virtual void Initialize() + { + // Absolutely needs thread-bound TaskExecutor to Initialize. + taskExecutor = SchedulerFactoryObject.ConfigTimeTaskExecutor; + if (taskExecutor == null) + { + throw new SchedulerConfigException("No local TaskExecutor found for configuration - " + + "'taskExecutor' property must be set on SchedulerFactoryObject"); + } + } + + /// + /// Called by the QuartzScheduler to inform the + /// that it should free up all of it's resources because the scheduler is + /// shutting down. + /// + /// + public virtual void Shutdown(bool waitForJobsToComplete) + { + } + + + /// + /// Execute the given in the next + /// available . + /// + /// + /// + /// + /// The implementation of this interface should not throw exceptions unless + /// there is a serious problem (i.e. a serious misconfiguration). If there + /// are no available threads, rather it should either queue the Runnable, or + /// block until a thread is available, depending on the desired strategy. + /// + public virtual bool RunInThread(IThreadRunnable runnable) + { + if (runnable == null) + { + return false; + } + try + { + taskExecutor.Execute(new ThreadStart(runnable.Run)); + return true; + } + catch (TaskRejectedException ex) + { + logger.Error("Task has been rejected by TaskExecutor", ex); + return false; + } + } + + /// + /// Determines the number of threads that are currently available in in + /// the pool. Useful for determining the number of times + /// can be called before returning + /// false. + /// + /// + /// the number of currently available threads + /// + /// + /// The implementation of this method should block until there is at + /// least one available thread. + /// + public virtual int BlockForAvailableThreads() + { + // The present implementation always returns 1, making Quartz (1.6) + // always schedule any tasks that it feels like scheduling. + // This could be made smarter for specific TaskExecutors, + // for example calling getMaximumPoolSize() - getActiveCount() + // on a java.util.concurrent.ThreadPoolExecutor. + return 1; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/MethodInvokingJob.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/MethodInvokingJob.cs new file mode 100644 index 00000000..ba59c7c6 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/MethodInvokingJob.cs @@ -0,0 +1,89 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Reflection; + +using Common.Logging; + +using Quartz; + +using Spring.Objects.Support; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Quartz Job implementation that invokes a specified method. + /// Automatically applied by MethodInvokingJobDetailFactoryObject. + /// + public class MethodInvokingJob : QuartzJobObject + { + private static readonly ILog logger = LogManager.GetLogger(typeof(MethodInvokingJob)); + private MethodInvoker methodInvoker; + private string errorMessage; + + /// + /// Set the MethodInvoker to use. + /// + public virtual MethodInvoker MethodInvoker + { + set + { + if (value == null) + { + throw new ArgumentException("Method invoker cannot be null", "value"); + } + methodInvoker = value; + errorMessage = + string.Format("Could not invoke method '{0}' on target object [{1}]", methodInvoker.TargetMethod, + methodInvoker.TargetObject); + } + } + + + /// + /// Invoke the method via the MethodInvoker. + /// + /// + protected override void ExecuteInternal(JobExecutionContext context) + { + if (methodInvoker == null) + { + throw new JobExecutionException("Could not execute job when method invoker is null"); + } + try + { + methodInvoker.Invoke(); + } + catch (TargetInvocationException ex) + { + logger.Warn(errorMessage, ex.GetBaseException()); + if (ex.GetBaseException() is JobExecutionException) + { + throw ex.GetBaseException(); + } + throw new JobExecutionException(errorMessage, ex.GetBaseException()); + + } + catch (Exception ex) + { + logger.Warn(errorMessage, ex); + throw new JobExecutionException(errorMessage, ex); + } + } + } + +} diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/MethodInvokingJobDetailFactoryObject.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/MethodInvokingJobDetailFactoryObject.cs new file mode 100644 index 00000000..92bab59b --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/MethodInvokingJobDetailFactoryObject.cs @@ -0,0 +1,246 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; + +using Quartz; + +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Objects.Support; + +namespace Spring.Scheduling.Quartz +{ + /// + /// IFactoryObject that exposes a JobDetail object that delegates job execution + /// to a specified (static or non-static) method. Avoids the need to implement + /// a one-line Quartz Job that just invokes an existing service method. + /// + /// + ///

    + /// Derived from ArgumentConverting MethodInvoker to share common properties and behavior + /// with MethodInvokingFactoryObject. + ///

    + ///

    + /// Supports both concurrently running jobs and non-currently running + /// ones through the "concurrent" property. Jobs created by this + /// MethodInvokingJobDetailFactoryObject are by default volatile and durable + /// (according to Quartz terminology). + ///

    + ///

    NOTE: JobDetails created via this FactoryObject are not + /// serializable and thus not suitable for persistent job stores. + /// You need to implement your own Quartz Job as a thin wrapper for each case + /// where you want a persistent job to delegate to a specific service method. + ///

    + ///
    + /// Juergen Hoeller + /// Alef Arendsen + /// + /// + public class MethodInvokingJobDetailFactoryObject : ArgumentConvertingMethodInvoker, IFactoryObject, IObjectNameAware, + IInitializingObject + { + private string name; + private string group; + private bool concurrent = true; + private string[] jobListenerNames; + private string objectName; + private JobDetail jobDetail; + + /// + /// Initializes a new instance of the class. + /// + public MethodInvokingJobDetailFactoryObject() + { + group = SchedulerConstants.DEFAULT_GROUP; + } + + + /// + /// Set the name of the job. + /// Default is the object name of this FactoryObject. + /// + /// + public virtual string Name + { + set { name = value; } + } + + /// + /// Set the group of the job. + /// Default is the default group of the Scheduler. + /// + /// + /// + public virtual string Group + { + set { group = value; } + } + + /// + /// Specify whether or not multiple jobs should be run in a concurrent + /// fashion. The behavior when one does not want concurrent jobs to be + /// executed is realized through adding the interface. + /// More information on stateful versus stateless jobs can be found + /// here. + ///

    + /// The default setting is to run jobs concurrently. + ///

    + ///
    + public virtual bool Concurrent + { + set { concurrent = value; } + } + + /// + /// Set a list of JobListener names for this job, referring to + /// non-global JobListeners registered with the Scheduler. + /// + /// + /// A JobListener name always refers to the name returned + /// by the JobListener implementation. + /// + /// + /// + public virtual string[] JobListenerNames + { + set { jobListenerNames = value; } + } + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// The name of the object in the factory. + /// + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + /// + public virtual string ObjectName + { + set { objectName = value; } + } + + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + /// An instance (possibly shared or independent) of the object managed by + /// this factory. + /// + /// + /// + /// If this method is being called in the context of an enclosing IoC container and + /// returns , the IoC container will consider this factory + /// object as not being fully initialized and throw a corresponding (and most + /// probably fatal) exception. + /// + /// + public object GetObject() + { + return jobDetail; + } + + /// + /// Return the of object that this + /// creates, or + /// if not known in advance. + /// + /// + public virtual Type ObjectType + { + get { return typeof (JobDetail); } + } + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + /// + public virtual bool IsSingleton + { + get { return true; } + } + + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public virtual void AfterPropertiesSet() + { + Prepare(); + + // Use specific name if given, else fall back to object name. + string jobDetailName = (name != null ? name : objectName); + + // Consider the concurrent flag to choose between stateful and stateless job. + Type jobType = (concurrent ? typeof (MethodInvokingJob) : typeof (StatefulMethodInvokingJob)); + + // Build JobDetail instance. + jobDetail = new JobDetail(jobDetailName, group, jobType); + jobDetail.JobDataMap.Put("methodInvoker", this); + jobDetail.Volatile = true; + jobDetail.Durable = true; + + // Register job listener names. + if (jobListenerNames != null) + { + for (int i = 0; i < jobListenerNames.Length; i++) + { + jobDetail.AddJobListener(jobListenerNames[i]); + } + } + + PostProcessJobDetail(jobDetail); + } + + /// + /// Callback for post-processing the JobDetail to be exposed by this FactoryObject. + ///

    + /// The default implementation is empty. Can be overridden in subclasses. + ///

    + ///
    + /// the JobDetail prepared by this FactoryObject + protected virtual void PostProcessJobDetail(JobDetail detail) + { + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/MethodInvokingRunnable.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/MethodInvokingRunnable.cs new file mode 100644 index 00000000..39b0173f --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/MethodInvokingRunnable.cs @@ -0,0 +1,132 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System; +using System.Reflection; + +using Common.Logging; + +using Quartz; + +using Spring.Objects.Factory; +using Spring.Objects.Support; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Adapter that implements the Runnable interface as a configurable + /// method invocation based on Spring's MethodInvoker. + /// + /// + ///

    + /// Derives from ArgumentConvertingMethodInvoker, inheriting common + /// configuration properties from MethodInvoker. + ///

    + /// + ///

    + /// Useful to generically encapsulate a method invocation as timer task for + /// java.util.Timer, in combination with a DelegatingTimerTask adapter. + /// Can also be used with JDK 1.5's java.util.concurrent.Executor + /// abstraction, which works with plain Runnables. + ///

    + ///

    + /// Extended by Spring's MethodInvokingTimerTaskFactoryObject adapter + /// for TimerTask. Note that you can populate a + /// ScheduledTimerTask object with a plain MethodInvokingRunnable instance + /// as well, which will automatically get wrapped with a DelegatingTimerTask. + ///

    + ///
    + /// Juergen Hoeller + /// + /// + public class MethodInvokingRunnable : ArgumentConvertingMethodInvoker, IInitializingObject, IThreadRunnable + { + /// + /// Logger instance shared by this instance and its sub-class instances. + /// + protected ILog logger; + + /// + /// Initializes a new instance of the class. + /// + public MethodInvokingRunnable() + { + logger = LogManager.GetLogger(GetType()); + } + + + /// + /// Gets the invocation failure message. + /// + /// The invocation failure message. + protected virtual string InvocationFailureMessage + { + get { return string.Format("Invocation of method '{0}' on target object [{1}] failed", TargetMethod, TargetObject); } + } + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public virtual void AfterPropertiesSet() + { + Prepare(); + } + + /// + /// This method has to be implemented in order that starting of the thread causes the object's + /// run method to be called in that separately executing thread. + /// + public virtual void Run() + { + try + { + Invoke(); + } + catch (TargetInvocationException ex) + { + logger.Error(InvocationFailureMessage, ex); + // Do not throw exception, else the main loop of the Timer will stop! + } + catch (Exception ex) + { + logger.Error(InvocationFailureMessage, ex); + // Do not throw exception, else the main loop of the Timer will stop! + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/QuartzJobObject.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/QuartzJobObject.cs new file mode 100644 index 00000000..795457ae --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/QuartzJobObject.cs @@ -0,0 +1,83 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using Quartz; +using Spring.Objects; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Simple implementation of the Quartz Job interface, applying the + /// passed-in JobDataMap and also the SchedulerContext as object property + /// values. This is appropriate because a new Job instance will be created + /// for each execution. JobDataMap entries will override SchedulerContext + /// entries with the same keys. + /// + /// + ///

    + /// For example, let's assume that the JobDataMap contains a key + /// "myParam" with value "5": The Job implementation can then expose + /// a object property "myParam" of type int to receive such a value, + /// i.e. a method "setMyParam(int)". This will also work for complex + /// types like business objects etc. + ///

    + /// + ///

    + /// Note: The QuartzJobObject class itself only implements the standard + /// Quartz IJob interface. Let your subclass explicitly implement the + /// Quartz IStatefulJob interface to mark your concrete job object as stateful. + ///

    + ///
    + /// Juergen Hoeller + /// + /// + /// + /// + /// + /// + /// + public abstract class QuartzJobObject : IJob + { + /// + /// This implementation applies the passed-in job data map as object property + /// values, and delegates to ExecuteInternal afterwards. + /// + /// + public void Execute(JobExecutionContext context) + { + try + { + ObjectWrapper bw = new ObjectWrapper(this); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.AddAll(context.Scheduler.Context); + pvs.AddAll(context.MergedJobDataMap); + bw.SetPropertyValues(pvs, true); + } + catch (SchedulerException ex) + { + throw new JobExecutionException(ex); + } + ExecuteInternal(context); + } + + /// + /// Execute the actual job. The job data map will already have been + /// applied as object property values by execute. The contract is + /// exactly the same as for the standard Quartz execute method. + /// + /// + protected abstract void ExecuteInternal(JobExecutionContext context); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ResourceJobSchedulingDataProcessor.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ResourceJobSchedulingDataProcessor.cs new file mode 100644 index 00000000..037ebc58 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/ResourceJobSchedulingDataProcessor.cs @@ -0,0 +1,84 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System.IO; + +using Quartz.Xml; + +using Spring.Context; +using Spring.Core.IO; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Subclass of Quartz' JobSchedulingDataProcessor that considers + /// given filenames as Spring resource locations. + /// + /// Juergen Hoeller + /// + public class ResourceJobSchedulingDataProcessor : JobSchedulingDataProcessor, IResourceLoaderAware + { + private IResourceLoader resourceLoader; + + /// + /// Initializes a new instance of the class. + /// + public ResourceJobSchedulingDataProcessor() + { + resourceLoader = new ConfigurableResourceLoader(); + } + + + /// + /// Gets and sets the + /// that this object runs in. + /// + /// + /// + /// Invoked after population of normal objects properties but + /// before an init callback such as + /// 's + /// + /// or a custom init-method. Invoked before setting + /// 's + /// + /// property. + /// + public virtual IResourceLoader ResourceLoader + { + set { resourceLoader = (value != null ? value : new ConfigurableResourceLoader()); } + get { return resourceLoader; } + } + + /// + /// Returns an from the fileName as a resource. + /// + /// Name of the file. + /// + /// an from the fileName as a resource. + /// + protected override Stream GetInputStream(string fileName) + { + try + { + return resourceLoader.GetResource(fileName).InputStream; + } + catch (IOException ex) + { + throw new SchedulingException("Could not load job scheduling data XML file", ex); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SchedulerFactoryObject.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SchedulerFactoryObject.cs new file mode 100644 index 00000000..6a4aa818 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SchedulerFactoryObject.cs @@ -0,0 +1,1084 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Threading; + +using Common.Logging; + +using Quartz; +using Quartz.Impl; +using Quartz.Simpl; +using Quartz.Spi; +using Quartz.Util; +using Quartz.Xml; + +using Spring.Context; +using Spring.Core.IO; +using Spring.Objects.Factory; + +namespace Spring.Scheduling.Quartz +{ + /// + /// FactoryObject that sets up a Quartz Scheduler and exposes it for object references. + /// + /// + ///

    + /// Allows registration of JobDetails, Calendars and Triggers, automatically + /// starting the scheduler on initialization and shutting it down on destruction. + /// In scenarios that just require static registration of jobs at startup, there + /// is no need to access the Scheduler instance itself in application code. + ///

    + /// + ///

    + /// For dynamic registration of jobs at runtime, use a object reference to + /// this SchedulerFactoryObject to get direct access to the Quartz Scheduler + /// (). This allows you to create new jobs + /// and triggers, and also to control and monitor the entire Scheduler. + ///

    + /// + ///

    + /// Note that Quartz instantiates a new Job for each execution, in + /// contrast to Timer which uses a TimerTask instance that is shared + /// between repeated executions. Just JobDetail descriptors are shared. + ///

    + /// + ///

    + /// When using persistent jobs, it is strongly recommended to perform all + /// operations on the Scheduler within Spring-managed transactions. + /// Else, database locking will not properly work and might even break. + ///

    + ///

    + /// The preferred way to achieve transactional execution is to demarcate + /// declarative transactions at the business facade level, which will + /// automatically apply to Scheduler operations performed within those scopes. + /// Alternatively, define a TransactionProxyFactoryObject for the Scheduler itself. + ///

    + ///
    + /// Juergen Hoeller + /// Marko Lahma (.NET) + /// + /// + /// + public class SchedulerFactoryObject : IFactoryObject, IApplicationContextAware, IInitializingObject, IDisposable + { + private const string CONTEXT_KEY_CONFIG_TIME_TASK_EXECUTOR = "sched_factory_conf_t_t_exec"; + + /// + /// Default thread count to be set to thread pool. + /// + public const int DEFAULT_THREAD_COUNT = 10; + + /// + /// Property name for thread count in thread pool. + /// + public const string PROP_THREAD_COUNT = "quartz.threadPool.threadCount"; + + /// + /// Logger for this instance and its sub-class instances. + /// + protected ILog logger; + + private IApplicationContext applicationContext; + private string applicationContextSchedulerContextKey; + private bool autoStartup = true; + private IDictionary calendars; + private IResource configLocation; + private IJobListener[] globalJobListeners; + private ITriggerListener[] globalTriggerListeners; + private ArrayList jobDetails; + private IJobFactory jobFactory; + private IJobListener[] jobListeners; + private string[] jobSchedulingDataLocations; + private bool overwriteExistingJobs = false; + private NameValueCollection quartzProperties; + private IScheduler scheduler; + private IDictionary schedulerContextMap; + private Type schedulerFactoryType; + private ISchedulerListener[] schedulerListeners; + private string schedulerName; + private int startupDelay = 0; + private ITaskExecutor taskExecutor; + private ITriggerListener[] triggerListeners; + private ArrayList triggers; + private bool waitForJobsToCompleteOnShutdown = false; + + /// + /// Initializes a new instance of the class. + /// + public SchedulerFactoryObject() + { + logger = LogManager.GetLogger(GetType()); + schedulerFactoryType = typeof (StdSchedulerFactory); + jobFactory = new AdaptableJobFactory(); + } + + + /// + /// Set the Quartz SchedulerFactory implementation to use. + /// + /// + /// Default is StdSchedulerFactory, reading in the standard + /// quartz.properties from Quartz' dll. To use custom Quartz + /// properties, specify "configLocation" or "quartzProperties". + /// + /// The scheduler factory class. + /// + /// + /// + public virtual Type SchedulerFactoryType + { + set + { + if (value == null || !typeof (ISchedulerFactory).IsAssignableFrom(value)) + { + throw new ArgumentException("schedulerFactoryType must implement [Quartz.ISchedulerFactory]"); + } + schedulerFactoryType = value; + } + } + + /// + /// Set the name of the Scheduler to fetch from the SchedulerFactory. + /// If not specified, the default Scheduler will be used. + /// + /// The name of the scheduler. + /// + /// + public virtual string SchedulerName + { + set { schedulerName = value; } + } + + /// + /// Set the location of the Quartz properties config file, for example + /// as assembly resource "assembly:quartz.properties". + /// + /// + /// Note: Can be omitted when all necessary properties are specified + /// locally via this object, or when relying on Quartz' default configuration. + /// + /// + public virtual IResource ConfigLocation + { + set { configLocation = value; } + } + + /// + /// Set Quartz properties, like "quartz.threadPool.type". + /// + /// + /// Can be used to override values in a Quartz properties config file, + /// or to specify all necessary properties locally. + /// + /// + public virtual NameValueCollection QuartzProperties + { + set { quartzProperties = value; } + } + + /// + /// Set the Spring TaskExecutor to use as Quartz backend. + /// Exposed as thread pool through the Quartz SPI. + /// + /// + /// By default, a Quartz SimpleThreadPool will be used, configured through + /// the corresponding Quartz properties. + /// + /// The task executor. + /// + /// + public virtual ITaskExecutor TaskExecutor + { + set { taskExecutor = value; } + } + + /// + /// Register objects in the Scheduler context via a given Map. + /// These objects will be available to any Job that runs in this Scheduler. + /// + /// + /// Note: When using persistent Jobs whose JobDetail will be kept in the + /// database, do not put Spring-managed object or an ApplicationContext + /// reference into the JobDataMap but rather into the SchedulerContext. + /// + /// + /// Map with string keys and any objects as + /// values (for example Spring-managed objects) + /// + /// + public virtual IDictionary SchedulerContextAsMap + { + set { schedulerContextMap = value; } + } + + /// + /// Set the key of an IApplicationContext reference to expose in the + /// SchedulerContext, for example "applicationContext". Default is none. + /// Only applicable when running in a Spring ApplicationContext. + /// + /// + ///

    + /// Note: When using persistent Jobs whose JobDetail will be kept in the + /// database, do not put an IApplicationContext reference into the JobDataMap + /// but rather into the SchedulerContext. + ///

    + /// + ///

    + /// In case of a QuartzJobObject, the reference will be applied to the Job + /// instance as object property. An "applicationContext" attribute will + /// correspond to a "setApplicationContext" method in that scenario. + ///

    + /// + ///

    + /// Note that ObjectFactory callback interfaces like IApplicationContextAware + /// are not automatically applied to Quartz Job instances, because Quartz + /// itself is reponsible for the lifecycle of its Jobs. + ///

    + ///
    + /// The application context scheduler context key. + /// + public virtual string ApplicationContextSchedulerContextKey + { + set { applicationContextSchedulerContextKey = value; } + } + + /// + /// Set the Quartz JobFactory to use for this Scheduler. + /// + /// + ///

    + /// Default is Spring's , which supports + /// standard Quartz instances. + ///

    + ///

    + /// Specify an instance of Spring's here + /// (typically as an inner object definition) to automatically populate a + /// job's object properties from the specified job data map and scheduler + /// context. + ///

    + ///
    + /// + /// + public virtual IJobFactory JobFactory + { + set { jobFactory = value; } + } + + /// + /// Set whether any jobs defined on this SchedulerFactoryObject should overwrite + /// existing job definitions. Default is "false", to not overwrite already + /// registered jobs that have been read in from a persistent job store. + /// + public virtual bool OverwriteExistingJobs + { + set { overwriteExistingJobs = value; } + } + + /// + /// Set the location of a Quartz job definition XML file that follows the + /// "job_scheduling_data" XSD. Can be specified to automatically + /// register jobs that are defined in such a file, possibly in addition + /// to jobs defined directly on this SchedulerFactoryObject. + /// + /// + /// + public virtual string JobSchedulingDataLocation + { + set { jobSchedulingDataLocations = new string[] {value}; } + } + + /// + /// Set the locations of Quartz job definition XML files that follow the + /// "job_scheduling_data" XSD. Can be specified to automatically + /// register jobs that are defined in such files, possibly in addition + /// to jobs defined directly on this SchedulerFactoryObject. + /// + /// The job scheduling data locations. + /// + /// + public virtual string[] JobSchedulingDataLocations + { + set { jobSchedulingDataLocations = value; } + } + + /// + /// Register a list of JobDetail objects with the Scheduler that + /// this FactoryObject creates, to be referenced by Triggers. + ///

    This is not necessary when a Trigger determines the JobDetail + /// itself: In this case, the JobDetail will be implicitly registered + /// in combination with the Trigger.

    + ///
    + /// + /// + /// + /// + /// + public virtual JobDetail[] JobDetails + { + set + { + // Use modifiable ArrayList here, to allow for further adding of + // JobDetail objects during autodetection of JobDetailAwareTriggers. + jobDetails = new ArrayList(value); + } + } + + /// + /// Register a list of Quartz ICalendar objects with the Scheduler + /// that this FactoryObject creates, to be referenced by Triggers. + /// + /// The calendars. + /// + /// + public virtual IDictionary Calendars + { + set { calendars = value; } + } + + /// + /// Register a list of Trigger objects with the Scheduler that + /// this FactoryObject creates. + ///

    + /// If the Trigger determines the corresponding JobDetail itself, + /// the job will be automatically registered with the Scheduler. + /// Else, the respective JobDetail needs to be registered via the + /// "jobDetails" property of this FactoryObject. + ///

    + ///
    + /// + /// + /// + /// + public virtual Trigger[] Triggers + { + set { triggers = new ArrayList(value); } + } + + /// + /// Specify Quartz SchedulerListeners to be registered with the Scheduler. + /// + public virtual ISchedulerListener[] SchedulerListeners + { + set { schedulerListeners = value; } + } + + /// + /// Specify global Quartz JobListeners to be registered with the Scheduler. + /// Such JobListeners will apply to all Jobs in the Scheduler. + /// + public virtual IJobListener[] GlobalJobListeners + { + set { globalJobListeners = value; } + } + + /// + /// Specify named Quartz JobListeners to be registered with the Scheduler. + /// Such JobListeners will only apply to Jobs that explicitly activate + /// them via their name. + /// + /// The job listeners. + /// + /// + /// + public virtual IJobListener[] JobListeners + { + set { jobListeners = value; } + } + + /// + /// Specify global Quartz TriggerListeners to be registered with the Scheduler. + /// Such TriggerListeners will apply to all Triggers in the Scheduler. + /// + /// The global trigger listeners. + public virtual ITriggerListener[] GlobalTriggerListeners + { + set { globalTriggerListeners = value; } + } + + /// + /// Specify named Quartz TriggerListeners to be registered with the Scheduler. + /// Such TriggerListeners will only apply to Triggers that explicitly activate + /// them via their name. + /// + /// + /// + /// + /// + public virtual ITriggerListener[] TriggerListeners + { + set { triggerListeners = value; } + } + + /// + /// Set whether to automatically start the scheduler after initialization. + /// Default is "true"; set this to "false" to allow for manual startup. + /// + public virtual bool AutoStartup + { + set { autoStartup = value; } + } + + /// + /// Set the number of seconds to wait after initialization before + /// starting the scheduler asynchronously. Default is 0, meaning + /// immediate synchronous startup on initialization of this object. + /// + /// + /// Setting this to 10 or 20 seconds makes sense if no jobs + /// should be run before the entire application has started up. + /// + public virtual int StartupDelay + { + set { startupDelay = value; } + } + + /// + /// Set whether to wait for running jobs to complete on Shutdown. + /// Default is "false". + /// + /// + /// true if [wait for jobs to complete on Shutdown]; otherwise, false. + /// + /// + public virtual bool WaitForJobsToCompleteOnShutdown + { + set { waitForJobsToCompleteOnShutdown = value; } + } + + /// + /// Gets a value indicating whether this is running. + /// + /// true if running; otherwise, false. + public virtual bool Running + { + get + { + if (scheduler != null) + { + try + { + return !scheduler.InStandbyMode; + } + catch (SchedulerException) + { + return false; + } + } + return false; + } + } + + /// + /// Return the TaskExecutor for the currently configured Quartz Scheduler, + /// to be used by LocalTaskExecutorThreadPool. + /// + /// + /// This instance will be set before initialization of the corresponding + /// Scheduler, and reset immediately afterwards. It is thus only available + /// during configuration. + /// + public static ITaskExecutor ConfigTimeTaskExecutor + { + get { return (ITaskExecutor) LogicalThreadContext.GetData(CONTEXT_KEY_CONFIG_TIME_TASK_EXECUTOR); } + } + + #region IApplicationContextAware Members + + /// + /// Set the that this + /// object runs in. + /// + /// + /// + ///

    + /// Normally this call will be used to initialize the object. + ///

    + ///

    + /// Invoked after population of normal object properties but before an + /// init callback such as + /// 's + /// + /// or a custom init-method. Invoked after the setting of any + /// 's + /// + /// property. + ///

    + ///
    + /// + /// In the case of application context initialization errors. + /// + /// + /// If thrown by any application context methods. + /// + /// + public virtual IApplicationContext ApplicationContext + { + set { applicationContext = value; } + get { return applicationContext; } + } + + #endregion + + #region IDisposable Members + + /// + /// Shut down the Quartz scheduler on object factory Shutdown, + /// stopping all scheduled jobs. + /// + public virtual void Dispose() + { + logger.Info("Shutting down Quartz Scheduler"); + scheduler.Shutdown(waitForJobsToCompleteOnShutdown); + } + + #endregion + + #region IFactoryObject Members + + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + /// An instance (possibly shared or independent) of the object managed by + /// this factory. + /// + /// + /// + /// If this method is being called in the context of an enclosing IoC container and + /// returns , the IoC container will consider this factory + /// object as not being fully initialized and throw a corresponding (and most + /// probably fatal) exception. + /// + /// + public object GetObject() + { + return scheduler; + } + + /// + /// Return the of object that this + /// creates, or + /// if not known in advance. + /// + /// + public virtual Type ObjectType + { + get { return (scheduler != null) ? scheduler.GetType() : typeof (IScheduler); } + } + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + /// + public virtual bool IsSingleton + { + get { return true; } + } + + #endregion + + //--------------------------------------------------------------------- + // Implementation of IInitializingObject interface + //--------------------------------------------------------------------- + + #region IInitializingObject Members + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public virtual void AfterPropertiesSet() + { + // Create SchedulerFactory instance. + ISchedulerFactory schedulerFactory = (ISchedulerFactory) ObjectUtils.InstantiateType(schedulerFactoryType); + + InitSchedulerFactory(schedulerFactory); + + // Get Scheduler instance from SchedulerFactory. + scheduler = CreateScheduler(schedulerFactory, schedulerName); + if (jobFactory != null) + { + if (jobFactory is ISchedulerContextAware) + { + ((ISchedulerContextAware) jobFactory).SchedulerContext = scheduler.Context; + } + scheduler.JobFactory = jobFactory; + } + + + PopulateSchedulerContext(); + RegisterListeners(); + RegisterJobsAndTriggers(); + + // Start Scheduler immediately, if demanded. + if (autoStartup) + { + StartScheduler(scheduler, startupDelay); + } + } + + #endregion + + /// + /// Load and/or apply Quartz properties to the given SchedulerFactory. + /// + /// the SchedulerFactory to Initialize + private void InitSchedulerFactory(ISchedulerFactory schedulerFactory) + { + if (configLocation != null || quartzProperties != null || schedulerName != null || + taskExecutor != null) + { + if (!(schedulerFactory is StdSchedulerFactory)) + { + throw new ArgumentException("StdSchedulerFactory required for applying Quartz properties"); + } + + NameValueCollection mergedProps = new NameValueCollection(); + + // Set necessary default properties here, as Quartz will not apply + // its default configuration when explicitly given properties. + if (taskExecutor != null) + { + mergedProps[StdSchedulerFactory.PROP_THREAD_POOL_CLASS] = + typeof (LocalTaskExecutorThreadPool).FullName; + } + else + { + mergedProps.Set(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, typeof (SimpleThreadPool).Name); + mergedProps[PROP_THREAD_COUNT] = Convert.ToString(DEFAULT_THREAD_COUNT); + } + + if (configLocation != null) + { + if (logger.IsInfoEnabled) + { + logger.Info("Loading Quartz config from [" + configLocation + "]"); + } + using (StreamReader sr = new StreamReader(configLocation.InputStream)) + { + string line; + while ((line =sr.ReadLine()) != null) + { + string[] lineItems = line.Split('='); + if (lineItems.Length == 2) + { + mergedProps[lineItems[0]] = lineItems[1]; + } + } + } + + } + + MergePropertiesIntoMap(quartzProperties, mergedProps); + + + // Make sure to set the scheduler name as configured in the Spring configuration. + if (schedulerName != null) + { + mergedProps.Add(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, schedulerName); + } + + ((StdSchedulerFactory) schedulerFactory).Initialize(mergedProps); + } + } + + /// + /// Merges the properties into map. This effectively also + /// overwrites existing properties with same key in map. + /// + /// The properties to merge into given map. + /// The map to merge to. + protected virtual void MergePropertiesIntoMap(NameValueCollection properties, NameValueCollection map) + { + foreach (string key in properties.Keys) + { + map[key] = properties[key]; + } + } + + + /// + /// Create the Scheduler instance for the given factory and scheduler name. + /// Called by afterPropertiesSet. + /// + /// + /// Default implementation invokes SchedulerFactory's GetScheduler + /// method. Can be overridden for custom Scheduler creation. + /// + /// the factory to create the Scheduler with + /// the name of the scheduler to create + /// the Scheduler instance + /// + /// + protected virtual IScheduler CreateScheduler(ISchedulerFactory schedulerFactory, string schedName) + { + // StdSchedulerFactory's default "getScheduler" implementation + // uses the scheduler name specified in the Quartz properties, + // which we have set before (in "InitSchedulerFactory"). + return schedulerFactory.GetScheduler(); + } + + /// + /// Expose the specified context attributes and/or the current + /// IApplicationContext in the Quartz SchedulerContext. + /// + private void PopulateSchedulerContext() + { + // Put specified objects into Scheduler context. + if (schedulerContextMap != null) + { + scheduler.Context.PutAll(schedulerContextMap); + } + + // Register IApplicationContext in Scheduler context. + if (applicationContextSchedulerContextKey != null) + { + if (applicationContext == null) + { + throw new SystemException("SchedulerFactoryObject needs to be set up in an IApplicationContext " + + "to be able to handle an 'applicationContextSchedulerContextKey'"); + } + scheduler.Context.Put(applicationContextSchedulerContextKey, applicationContext); + } + } + + + /// + /// Register all specified listeners with the Scheduler. + /// + private void RegisterListeners() + { + if (schedulerListeners != null) + { + for (int i = 0; i < schedulerListeners.Length; i++) + { + scheduler.AddSchedulerListener(schedulerListeners[i]); + } + } + if (globalJobListeners != null) + { + for (int i = 0; i < globalJobListeners.Length; i++) + { + scheduler.AddGlobalJobListener(globalJobListeners[i]); + } + } + if (jobListeners != null) + { + for (int i = 0; i < jobListeners.Length; i++) + { + scheduler.AddJobListener(jobListeners[i]); + } + } + if (globalTriggerListeners != null) + { + for (int i = 0; i < globalTriggerListeners.Length; i++) + { + scheduler.AddGlobalTriggerListener(globalTriggerListeners[i]); + } + } + if (triggerListeners != null) + { + for (int i = 0; i < triggerListeners.Length; i++) + { + scheduler.AddTriggerListener(triggerListeners[i]); + } + } + } + + /// + /// Register jobs and triggers (within a transaction, if possible). + /// + private void RegisterJobsAndTriggers() + { + try + { + if (jobSchedulingDataLocations != null) + { + ResourceJobSchedulingDataProcessor dataProcessor = new ResourceJobSchedulingDataProcessor(); + if (applicationContext != null) + { + dataProcessor.ResourceLoader = applicationContext; + } + for (int i = 0; i < jobSchedulingDataLocations.Length; i++) + { + dataProcessor.ProcessFileAndScheduleJobs(jobSchedulingDataLocations[i], scheduler, + overwriteExistingJobs); + } + } + + // Register JobDetails. + if (jobDetails != null) + { + foreach (JobDetail jobDetail in jobDetails) + { + AddJobToScheduler(jobDetail); + } + } + else + { + // Create empty list for easier checks when registering triggers. + jobDetails = new ArrayList(); + } + + // Register Calendars. + if (calendars != null) + { + foreach (DictionaryEntry entry in calendars) + { + string calendarName = (string) entry.Key; + ICalendar calendar = (ICalendar) entry.Value; + scheduler.AddCalendar(calendarName, calendar, true, true); + } + } + + // Register Triggers. + if (triggers != null) + { + foreach (Trigger trigger in triggers) + { + AddTriggerToScheduler(trigger); + } + } + } + catch (Exception ex) + { + if (ex is SchedulerException) + { + throw; + } + throw new SchedulerException("Registration of jobs and triggers failed: " + ex.Message, ex); + } + } + + /// + /// Add the given job to the Scheduler, if it doesn't already exist. + /// Overwrites the job in any case if "overwriteExistingJobs" is set. + /// + /// the job to add + /// + /// true if the job was actually added, + /// false if it already existed before + /// + /// + private bool AddJobToScheduler(JobDetail jobDetail) + { + if (overwriteExistingJobs || scheduler.GetJobDetail(jobDetail.Name, jobDetail.Group) == null) + { + scheduler.AddJob(jobDetail, true); + return true; + } + else + { + return false; + } + } + + /// + /// Add the given trigger to the Scheduler, if it doesn't already exist. + /// Overwrites the trigger in any case if "overwriteExistingJobs" is set. + /// + /// the trigger to add + /// + /// true if the trigger was actually added, + /// false if it already existed before + /// + /// + private bool AddTriggerToScheduler(Trigger trigger) + { + bool triggerExists = (scheduler.GetTrigger(trigger.Name, trigger.Group) != null); + if (!triggerExists || overwriteExistingJobs) + { + // Check if the Trigger is aware of an associated JobDetail. + if (trigger is IJobDetailAwareTrigger) + { + JobDetail jobDetail = ((IJobDetailAwareTrigger) trigger).JobDetail; + // Automatically register the JobDetail too. + if (!jobDetails.Contains(jobDetail) && AddJobToScheduler(jobDetail)) + { + jobDetails.Add(jobDetail); + } + } + if (!triggerExists) + { + try + { + scheduler.ScheduleJob(trigger); + } + catch (ObjectAlreadyExistsException ex) + { + if (logger.IsDebugEnabled) + { + logger.Debug( + string.Format( + "Unexpectedly found existing trigger, assumably due to cluster race condition: {0} - can safely be ignored", + ex.Message)); + } + if (overwriteExistingJobs) + { + scheduler.RescheduleJob(trigger.Name, trigger.Group, trigger); + } + } + } + else + { + scheduler.RescheduleJob(trigger.Name, trigger.Group, trigger); + } + return true; + } + else + { + return false; + } + } + + + /// + /// Start the Quartz Scheduler, respecting the "startDelay" setting. + /// + /// the Scheduler to start + /// the number of seconds to wait before starting + /// the Scheduler asynchronously + protected virtual void StartScheduler(IScheduler sched, int startDelay) + { + if (startDelay <= 0) + { + logger.Info("Starting Quartz Scheduler now"); + sched.Start(); + } + else + { + if (logger.IsInfoEnabled) + { + logger.Info( + string.Format("Will start Quartz Scheduler [{0}] in {1} seconds", sched.SchedulerName, + startDelay)); + } + QuartzThread schedulerThread = new AnonymousClassThread(this); + schedulerThread.Name = "Quartz Scheduler [" + sched.SchedulerName + "]"; + schedulerThread.Start(); + } + } + + + //--------------------------------------------------------------------- + // Implementation of Lifecycle interface + //--------------------------------------------------------------------- + + /// + /// Starts this instance. + /// + public virtual void Start() + { + if (scheduler != null) + { + try + { + scheduler.Start(); + } + catch (SchedulerException ex) + { + throw new SchedulingException("Could not start Quartz Scheduler", ex); + } + } + } + + /// + /// Stops this instance. + /// + public virtual void Stop() + { + if (scheduler != null) + { + try + { + scheduler.Standby(); + } + catch (SchedulerException ex) + { + throw new SchedulingException("Could not stop Quartz Scheduler", ex); + } + } + } + + #region Nested type: AnonymousClassThread + + private class AnonymousClassThread : QuartzThread + { + private readonly SchedulerFactoryObject schedulerFactoryObject; + + public AnonymousClassThread(SchedulerFactoryObject enclosingInstance) + { + schedulerFactoryObject = enclosingInstance; + } + + + public override void Run() + { + try + { + Thread.Sleep(schedulerFactoryObject.startupDelay*1000); + } + catch (ThreadInterruptedException) + { + // simply proceed + } + if (schedulerFactoryObject.logger.IsInfoEnabled) + { + schedulerFactoryObject.logger.Info("Starting Quartz Scheduler now, after delay of " + + schedulerFactoryObject.startupDelay + + " seconds"); + } + try + { + schedulerFactoryObject.scheduler.Start(); + } + catch (SchedulerException ex) + { + throw new SchedulingException("Could not start Quartz Scheduler after delay", ex); + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SchedulingException.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SchedulingException.cs new file mode 100644 index 00000000..a5a68a92 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SchedulingException.cs @@ -0,0 +1,27 @@ +using System; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Generic scheduling exception. + /// + public class SchedulingException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// The message. + public SchedulingException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The original exception. + public SchedulingException(string message, Exception ex) : base(message, ex) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SimpleThreadPoolTaskExecutor.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SimpleThreadPoolTaskExecutor.cs new file mode 100644 index 00000000..84e335de --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SimpleThreadPoolTaskExecutor.cs @@ -0,0 +1,127 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System; +using System.Threading; + +using Quartz; +using Quartz.Simpl; +using Spring.Objects.Factory; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Subclass of Quartz's SimpleThreadPool that implements Spring's + /// TaskExecutor interface and listens to Spring lifecycle callbacks. + /// + /// Juergen Hoeller + /// + /// + /// + public class SimpleThreadPoolTaskExecutor : SimpleThreadPool, ISchedulingTaskExecutor, IInitializingObject, IDisposable + { + private bool waitForJobsToCompleteOnShutdown = false; + + /// + /// Set whether to wait for running jobs to complete on Shutdown. + /// Default is "false". + /// + /// + /// true if [wait for jobs to complete on shutdown]; otherwise, false. + /// + /// + public virtual bool WaitForJobsToCompleteOnShutdown + { + set { waitForJobsToCompleteOnShutdown = value; } + } + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public virtual void AfterPropertiesSet() + { + Initialize(); + } + + /// + /// Executes the specified task. + /// + /// The task. + public virtual void Execute(ThreadStart task) + { + if (task == null) + { + throw new ArgumentException("Runnable must not be null", "task"); + } + if (!RunInThread(new ThreadRunnableDelegate(task))) + { + throw new SchedulingException("Quartz SimpleThreadPool already shut down"); + } + } + + /// This task executor prefers short-lived work units. + public virtual bool PrefersShortLivedTasks + { + get { return true; } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public virtual void Dispose() + { + Shutdown(waitForJobsToCompleteOnShutdown); + } + + internal class ThreadRunnableDelegate : IThreadRunnable + { + private ThreadStart ts; + + + public ThreadRunnableDelegate(ThreadStart ts) + { + this.ts = ts; + } + + public void Run() + { + ts.Invoke(); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SimpleTriggerObject.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SimpleTriggerObject.cs new file mode 100644 index 00000000..b4bb4959 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SimpleTriggerObject.cs @@ -0,0 +1,195 @@ +/* +* Copyright 2002-2007 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System; +using System.Collections; +using Quartz; +using Spring.Objects.Factory; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Convenience subclass of Quartz's + /// class, making properties based usage easier. + /// + /// + ///

    + /// SimpleTrigger itself is already a PONO but lacks sensible defaults. + /// This class uses the Spring object name as job name, the Quartz default group + /// ("DEFAULT") as job group, the current time as start time, and indefinite + /// repetition, if not specified. + ///

    + /// + ///

    + /// This class will also register the trigger with the job name and group of + /// a given . This allows + /// to automatically register a trigger for the corresponding JobDetail, + /// instead of registering the JobDetail separately. + ///

    + ///
    + /// Juergen Hoeller + /// + /// + /// + /// + /// + /// + /// + /// + /// + public class SimpleTriggerObject : SimpleTrigger, IJobDetailAwareTrigger, IObjectNameAware, IInitializingObject + { + private long startDelay = 0; + private JobDetail jobDetail; + private string objectName; + private readonly Constants constants = new Constants(typeof(MisfirePolicy.SimpleTrigger), typeof(MisfirePolicy)); + + /// + /// Initializes a new instance of the class. + /// + public SimpleTriggerObject() + { + RepeatCount = REPEAT_INDEFINITELY; + } + + /// + /// Register objects in the JobDataMap via a given Map. + ///

    + /// These objects will be available to this Trigger only, + /// in contrast to objects in the JobDetail's data map. + ///

    + ///
    + /// + public virtual IDictionary JobDataAsMap + { + set { JobDataMap.PutAll(value); } + } + + /// + /// Set the misfire instruction via the name of the corresponding + /// constant in the SimpleTrigger class. + /// Default is . + /// + /// + /// + /// + /// + /// + /// + public virtual string MisfireInstructionName + { + set + { + MisfireInstruction = constants.AsNumber(value); + } + } + + /// + /// Set the delay before starting the job for the first time. + /// The given number of milliseconds will be added to the current + /// time to calculate the start time. Default is 0. + /// + /// + /// This delay will just be applied if no custom start time was + /// specified. However, in typical usage within a Spring context, + /// the start time will be the container startup time anyway. + /// Specifying a relative delay is appropriate in that case. + /// + /// + public virtual long StartDelay + { + set { startDelay = value; } + } + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// The name of the object in the factory. + /// + ///

    + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

    + ///
    + public virtual string ObjectName + { + set { objectName = value; } + } + + /// + /// Set the JobDetail that this trigger should be associated with. + ///

    + /// This is typically used with a object reference if the JobDetail + /// is a Spring-managed object. Alternatively, the trigger can also + /// be associated with a job by name and group. + ///

    + ///
    + public virtual JobDetail JobDetail + { + get { return jobDetail; } + set { jobDetail = value; } + } + + + /// + /// Invoked by an + /// after it has injected all of an object's dependencies. + /// + /// + ///

    + /// This method allows the object instance to perform the kind of + /// initialization only possible when all of it's dependencies have + /// been injected (set), and to throw an appropriate exception in the + /// event of misconfiguration. + ///

    + ///

    + /// Please do consult the class level documentation for the + /// interface for a + /// description of exactly when this method is invoked. In + /// particular, it is worth noting that the + /// + /// and + /// callbacks will have been invoked prior to this method being + /// called. + ///

    + ///
    + /// + /// In the event of misconfiguration (such as the failure to set a + /// required property) or if initialization fails. + /// + public virtual void AfterPropertiesSet() + { + if (Name == null) + { + Name = objectName; + } + if (Group == null) + { + Group = SchedulerConstants.DEFAULT_GROUP; + } + if (StartTimeUtc == DateTime.MinValue) + { + StartTimeUtc = DateTime.UtcNow.AddMilliseconds(startDelay); + } + if (jobDetail != null) + { + JobName = jobDetail.Name; + JobGroup = jobDetail.Group; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SpringObjectJobFactory.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SpringObjectJobFactory.cs new file mode 100644 index 00000000..1b513b4f --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/SpringObjectJobFactory.cs @@ -0,0 +1,120 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using Quartz; +using Quartz.Spi; +using Spring.Objects; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Subclass of AdaptableJobFactory that also supports Spring-style + /// dependency injection on object properties. This is essentially the direct + /// equivalent of Spring's QuartzJobObject in the shape of a + /// Quartz JobFactory. + /// + /// + /// Applies scheduler context, job data map and trigger data map entries + /// as object property values. If no matching object property is found, the entry + /// is by default simply ignored. This is analogous to QuartzJobObject's behavior. + /// + /// Juergen Hoeller + /// + /// + public class SpringObjectJobFactory : AdaptableJobFactory, ISchedulerContextAware + { + private string[] ignoredUnknownProperties; + private SchedulerContext schedulerContext; + + /// + /// Specify the unknown properties (not found in the object) that should be ignored. + /// + /// + /// Default is null, indicating that all unknown properties + /// should be ignored. Specify an empty array to throw an exception in case + /// of any unknown properties, or a list of property names that should be + /// ignored if there is no corresponding property found on the particular + /// job class (all other unknown properties will still trigger an exception). + /// + public virtual string[] IgnoredUnknownProperties + { + set { ignoredUnknownProperties = value; } + } + + /// + /// Set the SchedulerContext of the current Quartz Scheduler. + /// + /// + /// + public virtual SchedulerContext SchedulerContext + { + set { schedulerContext = value; } + } + + + /// + /// Create the job instance, populating it with property values taken + /// from the scheduler context, job data map and trigger data map. + /// + protected override object CreateJobInstance(TriggerFiredBundle bundle) + { + ObjectWrapper ow = new ObjectWrapper(bundle.JobDetail.JobType); + if (IsEligibleForPropertyPopulation(ow.WrappedInstance)) + { + MutablePropertyValues pvs = new MutablePropertyValues(); + if (schedulerContext != null) + { + pvs.AddAll(schedulerContext); + } + pvs.AddAll(bundle.JobDetail.JobDataMap); + pvs.AddAll(bundle.Trigger.JobDataMap); + if (ignoredUnknownProperties != null) + { + for (int i = 0; i < ignoredUnknownProperties.Length; i++) + { + string propName = ignoredUnknownProperties[i]; + if (pvs.Contains(propName)) + { + pvs.Remove(propName); + } + } + ow.SetPropertyValues(pvs); + } + else + { + ow.SetPropertyValues(pvs, true); + } + } + return ow.WrappedInstance; + } + + /// + /// Return whether the given job object is eligible for having + /// its object properties populated. + ///

    + /// The default implementation ignores QuartzJobObject instances, + /// which will inject object properties themselves. + ///

    + ///
    + /// + /// The job object to introspect. + /// + /// + protected virtual bool IsEligibleForPropertyPopulation(object jobObject) + { + return (!(jobObject is QuartzJobObject)); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/StatefulMethodInvokingJob.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/StatefulMethodInvokingJob.cs new file mode 100644 index 00000000..64a08956 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/StatefulMethodInvokingJob.cs @@ -0,0 +1,31 @@ +/* +* Copyright 2002-2006 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using Quartz; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Extension of the MethodInvokingJob, implementing the StatefulJob interface. + /// Quartz checks whether or not jobs are stateful and if so, + /// won't let jobs interfere with each other. + /// + public class StatefulMethodInvokingJob : MethodInvokingJob, IStatefulJob + { + // No implementation, just an addition of the tag interface StatefulJob + // in order to allow stateful method invoking jobs. + } +} diff --git a/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/TaskRejectedException.cs b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/TaskRejectedException.cs new file mode 100644 index 00000000..3cc03eec --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Scheduling/Quartz/TaskRejectedException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Spring.Scheduling +{ + /// + /// Summary description for TaskRejectedException. + /// + public class TaskRejectedException : ApplicationException + { + } +} diff --git a/src/Spring/Spring.Scheduling.Quartz/Spring.Scheduling.Quartz.2003.csproj b/src/Spring/Spring.Scheduling.Quartz/Spring.Scheduling.Quartz.2003.csproj new file mode 100644 index 00000000..cc9fe1c0 --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Spring.Scheduling.Quartz.2003.csproj @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Scheduling.Quartz/Spring.Scheduling.Quartz.2005.csproj b/src/Spring/Spring.Scheduling.Quartz/Spring.Scheduling.Quartz.2005.csproj new file mode 100644 index 00000000..615b42cc --- /dev/null +++ b/src/Spring/Spring.Scheduling.Quartz/Spring.Scheduling.Quartz.2005.csproj @@ -0,0 +1,79 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {E823D54C-CE82-4868-929F-5F95A999F61E} + Library + Properties + Spring + Spring.Scheduling.Quartz + + + true + full + false + ..\..\..\build\VS.NET.2005\Spring.Scheduling.Quartz\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + ..\..\..\build\VS.NET.2005\Spring.Scheduling.Quartz\Debug\Spring.Scheduling.Quartz.xml + + + pdbonly + true + ..\..\..\build\VS.NET.2005\Spring.Scheduling.Quartz\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\lib\net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\net\2.0\Quartz.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Services/App.config b/src/Spring/Spring.Services/App.config new file mode 100644 index 00000000..f93d71ae --- /dev/null +++ b/src/Spring/Spring.Services/App.config @@ -0,0 +1,54 @@ + + + + + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Services/AssemblyInfo.cs b/src/Spring/Spring.Services/AssemblyInfo.cs new file mode 100644 index 00000000..a085cc31 --- /dev/null +++ b/src/Spring/Spring.Services/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring.Net Portable Service Abstractions support")] +[assembly: AssemblyDescription("Interfaces and classes that provide portable service abstractions in Spring.Net")] \ No newline at end of file diff --git a/src/Spring/Spring.Services/EnterpriseServices/EnterpriseServices.keys b/src/Spring/Spring.Services/EnterpriseServices/EnterpriseServices.keys new file mode 100644 index 00000000..bad27a8c Binary files /dev/null and b/src/Spring/Spring.Services/EnterpriseServices/EnterpriseServices.keys differ diff --git a/src/Spring/Spring.Services/EnterpriseServices/EnterpriseServicesExporter.cs b/src/Spring/Spring.Services/EnterpriseServices/EnterpriseServicesExporter.cs new file mode 100644 index 00000000..db4b0c36 --- /dev/null +++ b/src/Spring/Spring.Services/EnterpriseServices/EnterpriseServicesExporter.cs @@ -0,0 +1,318 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if (!NET_1_0) + +#region Imports + +using System; +using System.Collections; +using System.EnterpriseServices; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Objects.Factory; +using Spring.Util; + +#endregion + +namespace Spring.EnterpriseServices +{ + /// + /// Exports specified components as ServicedComponents. + /// + /// + ///

    + /// This class will create ServicedComponent wrapper for each of the + /// specified components and register them with the Component Services. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: EnterpriseServicesExporter.cs,v 1.6 2007/05/16 10:03:24 oakinger Exp $ + public class EnterpriseServicesExporter : IInitializingObject, IObjectFactoryAware + { + #region Fields + + private IObjectFactory objectFactory; + + private IList components = new ArrayList(); + private string applicationName; + private string applicationId; + private ActivationOption activationMode = ActivationOption.Library; + private string description; + private ApplicationAccessControlAttribute accessControl; + private ApplicationQueuingAttribute applicationQueuing; + private IList roles; + + private string assemblyName; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates new enterprise services exporter. + /// + public EnterpriseServicesExporter() + { + } + + #endregion + + #region Properties + + /// + /// Gets or sets list of components to export. + /// + public IList Components + { + get { return components; } + set { components = value; } + } + + /// + /// Gets or sets COM+ application name. + /// + public string ApplicationName + { + get { return applicationName; } + set { applicationName = value; } + } + + /// + /// Gets or sets application identifier (GUID). Defaults to generated GUID if not specified. + /// + public string ApplicationId + { + get { return applicationId; } + set { applicationId = value; } + } + + /// + /// Gets or sets application activation mode, which can be either Server or Library (default). + /// + public ActivationOption ActivationMode + { + get { return activationMode; } + set { activationMode = value; } + } + + /// + /// Gets or sets application description. + /// + public string Description + { + get { return description; } + set { description = value; } + } + + /// + /// Gets or sets access control attribute. + /// + public ApplicationAccessControlAttribute AccessControl + { + get { return accessControl; } + set { accessControl = value; } + } + + /// + /// Gets or sets application queuing attribute. + /// + public ApplicationQueuingAttribute ApplicationQueuing + { + get { return applicationQueuing; } + set { applicationQueuing = value; } + } + + /// + /// Gets or sets application roles. + /// + public IList Roles + { + get { return roles; } + set { roles = value; } + } + + /// + /// Gets or sets name of the generated assembly that will contain serviced components. + /// + public string Assembly + { + get { return assemblyName; } + set { assemblyName = value; } + } + + #endregion + + #region IInitializingObject Members + + /// + /// Called by Spring container after object is configured in order to initialize it. + /// + public void AfterPropertiesSet() + { + if (roles != null && roles.Count > 0) + { + RefreshRoles(); + } + Export(); + } + + #endregion + + #region IObjectFactoryAware Members + + /// + /// Sets object factory instance. + /// + public IObjectFactory ObjectFactory + { + set { objectFactory = value; } + } + + #endregion + + #region Public Methods + + /// + /// Creates ServicedComponent wrappers for the specified components and registers + /// them with COM+ Component Services. + /// + public virtual void Export() + { + AssemblyName an = new AssemblyName(); + an.Name = assemblyName; + an.Version = new Version("1.0.0.0"); + an.KeyPair = new StrongNameKeyPair(GetKeyPair()); + AssemblyBuilder proxyAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave); + ModuleBuilder module = proxyAssembly.DefineDynamicModule(assemblyName, assemblyName + ".dll", true); + ApplyAssemblyAttributes(proxyAssembly); + + foreach (ServicedComponentExporter definition in components) + { + definition.CreateWrapperType(module, objectFactory); + } + + proxyAssembly.Save(assemblyName + ".dll"); + + RegistrationConfig config = new RegistrationConfig(); + config.Application = applicationName; + config.AssemblyFile = AppDomain.CurrentDomain.DynamicDirectory + assemblyName + ".dll"; + config.InstallationFlags = InstallationFlags.FindOrCreateTargetApplication; + + RegistrationHelper regHelper = new RegistrationHelper(); + regHelper.InstallAssemblyFromConfig(ref config); + } + + #endregion + + #region Private Methods + + /// + /// Reads key pair from embedded resource. + /// + /// Key pair as a byte array. + private byte[] GetKeyPair() + { + using (Stream keys = GetType().Assembly.GetManifestResourceStream("Spring.EnterpriseServices.EnterpriseServices.keys")) + { + byte[] bytes = new byte[keys.Length]; + keys.Read(bytes, 0, (int) keys.Length); + return bytes; + } + } + + /// + /// Applies custom attributes to generated assembly. + /// + /// Dynamic assembly to apply attributes to. + private void ApplyAssemblyAttributes(AssemblyBuilder assembly) + { + assembly.SetCustomAttribute(ReflectionUtils.CreateCustomAttribute(typeof(ApplicationNameAttribute), applicationName)); + assembly.SetCustomAttribute(ReflectionUtils.CreateCustomAttribute(typeof(ApplicationActivationAttribute), activationMode)); + if (applicationId != null) + { + assembly.SetCustomAttribute(ReflectionUtils.CreateCustomAttribute(typeof(ApplicationIDAttribute), applicationId)); + } + if (description != null) + { + assembly.SetCustomAttribute(ReflectionUtils.CreateCustomAttribute(typeof(DescriptionAttribute), description)); + assembly.SetCustomAttribute(ReflectionUtils.CreateCustomAttribute(typeof(AssemblyDescriptionAttribute), description)); + } + if (accessControl != null) + { + assembly.SetCustomAttribute(ReflectionUtils.CreateCustomAttribute(accessControl)); + } + if (applicationQueuing != null) + { + assembly.SetCustomAttribute(ReflectionUtils.CreateCustomAttribute(applicationQueuing)); + } + if (roles != null) + { + foreach (SecurityRoleAttribute role in roles) + { + assembly.SetCustomAttribute(ReflectionUtils.CreateCustomAttribute(typeof(SecurityRoleAttribute), + new object[] {role.Role}, role)); + } + } + } + + /// + /// Replaces roles expressed using string with appropriate SecurityRoleAttribute instance. + /// + private void RefreshRoles() + { + for (int i = 0; i < roles.Count; i++) + { + object role = roles[i]; + if (role is string) + { + roles[i] = ParseRole((string) role); + } + } + } + + /// + /// Parses string representation of SecurityRoleAttribute. + /// + /// Role definition string. + /// Configured SecurityRoleAttribute instance. + private SecurityRoleAttribute ParseRole(string roleString) + { + string[] parts = roleString.Split(':'); + SecurityRoleAttribute role = new SecurityRoleAttribute(parts[0].Trim()); + if (parts.Length > 1) + { + role.Description = parts[1].Trim(); + } + if (parts.Length > 2) + { + role.SetEveryoneAccess = bool.Parse(parts[2].Trim()); + } + + return role; + } + + #endregion + } +} + +#endif // (!NET_1_0) \ No newline at end of file diff --git a/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentExporter.cs b/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentExporter.cs new file mode 100644 index 00000000..cce34421 --- /dev/null +++ b/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentExporter.cs @@ -0,0 +1,225 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if (!NET_1_0) + +#region Imports + +using System; +using System.Collections; +using System.EnterpriseServices; +using System.Reflection.Emit; + +using Spring.Core.TypeResolution; +using Spring.Objects.Factory; +using Spring.Proxy; +using Spring.Util; + +#endregion + +namespace Spring.EnterpriseServices +{ + /// + /// Encapsulates information necessary to create ServicedComponent + /// wrapper around target class. + /// + /// + ///

    + /// Instances of this class should be used as elements in the Components + /// list of the class, which will + /// register them with COM+ Services. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ServicedComponentExporter.cs,v 1.9 2007/07/31 18:18:19 bbaia Exp $ + public class ServicedComponentExporter : IInitializingObject, IObjectNameAware + { + #region Fields + + private string _objectName; + private string _targetName; + private string[] _interfaces; + private IList _typeAttributes = new ArrayList(); + private IDictionary _memberAttributes = new Hashtable(); + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public ServicedComponentExporter() + {} + + #endregion + + #region Properties + + /// + /// Gets or sets name of the target object that should be exposed as a serviced component. + /// + public string TargetName + { + get { return _targetName; } + set { _targetName = value; } + } + + /// + /// Gets or sets the list of interfaces whose methods should be exported. + /// + /// + /// The default value of this property is all the interfaces + /// implemented or inherited by the target type. + /// + /// The interfaces to export. + public string[] Interfaces + { + get { return _interfaces; } + set { _interfaces = value; } + } + + /// + /// Gets or sets a list of custom attributes + /// that should be applied to a proxy class. + /// + public IList TypeAttributes + { + get { return _typeAttributes; } + set { _typeAttributes = value; } + } + + /// + /// Gets or sets a dictionary of custom attributes + /// that should be applied to proxy members. + /// + /// + /// Map key is an expression that members can be matched against. Value is a list + /// of attributes that should be applied to each member that matches expression. + /// + public IDictionary MemberAttributes + { + get { return _memberAttributes; } + set { _memberAttributes = value; } + } + + #endregion + + #region IInitializingObject Members + + /// + /// Validate configuration. + /// + public void AfterPropertiesSet() + { + ValidateConfiguration(); + } + + #endregion + + #region IObjectNameAware Members + + /// + /// Set the name of the object in the object factory + /// that created this object. + /// + public string ObjectName + { + set { _objectName = value; } + } + + #endregion + + #region Methods + + private void ValidateConfiguration() + { + if (TargetName == null) + { + throw new ArgumentException("The TargetName property is required."); + } + } + + /// + /// Creates ServicedComponent wrapper around target class. + /// + /// Dynamic module builder to use + /// Object factory to get target from. + internal Type CreateWrapperType(ModuleBuilder module, IObjectFactory objectFactory) + { + // create wrapper using appropriate proxy builder + IProxyTypeBuilder proxyBuilder = new ServicedComponentProxyTypeBuilder(module); + proxyBuilder.Name = _objectName; + proxyBuilder.TargetType = objectFactory.GetType(TargetName); + if (_interfaces != null && _interfaces.Length > 0) + { + proxyBuilder.Interfaces = TypeResolutionUtils.ResolveInterfaceArray(_interfaces); + } + proxyBuilder.TypeAttributes = TypeAttributes; + proxyBuilder.MemberAttributes = MemberAttributes; + + Type componentType = proxyBuilder.BuildProxyType(); + + // create and register client-side proxy factory for component + + return componentType; + } + + #endregion + + #region ServicedComponentProxyTypeBuilder inner class definition + + private sealed class ServicedComponentProxyTypeBuilder : CompositionProxyTypeBuilder + { + #region Fields + + private ModuleBuilder module; + + #endregion + + #region Constructor(s) / Destructor + + public ServicedComponentProxyTypeBuilder(ModuleBuilder module) + { + this.module = module; + + BaseType = typeof(ServicedComponent); + } + + #endregion + + #region Protected Methods + + protected override TypeBuilder CreateTypeBuilder(string name, Type baseType) + { + return module.DefineType(name, + System.Reflection.TypeAttributes.BeforeFieldInit | System.Reflection.TypeAttributes.Public, + baseType); + } + + #endregion + } + + #endregion + } +} + +#endif // (!NET_1_0) diff --git a/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentFactory.cs b/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentFactory.cs new file mode 100644 index 00000000..aa476b19 --- /dev/null +++ b/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentFactory.cs @@ -0,0 +1,183 @@ +#region License + +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if (!NET_1_0) + +#region Imports + +using System; + +using Spring.Util; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.EnterpriseServices +{ + /// + /// Factory Object that instantiates and configures ServicedComponent. + /// + /// + ///

    + /// This factory object should be used to instantiate and configure + /// serviced components created by . + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ServicedComponentFactory.cs,v 1.7 2007/05/16 10:03:25 oakinger Exp $ + public class ServicedComponentFactory : IConfigurableFactoryObject, IInitializingObject + { + #region Fields + + private string name; + private string server; + + private bool isSingleton; + private IObjectDefinition productTemplate; + + private Type componentType; + private object singletonInstance; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates new instance of serviced component factory. + /// + public ServicedComponentFactory() + { + this.isSingleton = false; + } + + #endregion + + #region Properties + + /// + /// Gets or sets component name, as registered with COM+ Services. + /// + public string Name + { + get { return name; } + set { name = value; } + } + + /// + /// Gets or sets name of the remote server that COM+ component is registered with. + /// + public string Server + { + get { return server; } + set { server = value; } + } + + #endregion + + #region IConfigurableFactoryObject Members + + /// + /// Returns configured instance of the serviced component. + /// + /// Configured instance of the serviced component. + public object GetObject() + { + if (IsSingleton) + { + if (singletonInstance == null) + { + singletonInstance = CreateInstance(); + } + return singletonInstance; + } + else + { + return CreateInstance(); + } + } + + /// + /// Returns type of serviced component. + /// + public Type ObjectType + { + get { return componentType; } + } + + /// + /// Gets or sets whether serviced component should be treated as singleton. Default is false. + /// + public bool IsSingleton + { + get { return isSingleton; } + set { isSingleton = value; } + } + + /// + /// Gets or sets the template object definition + /// that should be used to configure proxy instance. + /// + public IObjectDefinition ProductTemplate + { + get { return productTemplate; } + set { productTemplate = value; } + } + + #endregion + + #region IInitializingObject Members + + /// + /// Initializes factory object. + /// + public void AfterPropertiesSet() + { + ValidateConfiguration(); + componentType = Type.GetTypeFromProgID(Name, Server); + } + + #endregion + + #region Private Methods + + private void ValidateConfiguration() + { + if (Name == null) + { + throw new ArgumentException("The Name property is required."); + } + } + + /// + /// Creates new instance of serviced component. + /// + /// New instance of serviced component. + private object CreateInstance() + { + return Activator.CreateInstance(componentType); + } + + #endregion + } +} + +#endif // (!NET_1_0) diff --git a/src/Spring/Spring.Services/Remoting/CaoExporter.cs b/src/Spring/Spring.Services/Remoting/CaoExporter.cs new file mode 100644 index 00000000..8852124b --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/CaoExporter.cs @@ -0,0 +1,294 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Remoting; + +using Spring.Context; +using Spring.Core; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Support; +using Spring.Remoting.Support; +using Spring.Util; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Registers an object type on the server + /// as a Client Activated Object (CAO). + /// + /// Aleksandar Seovic + /// Mark Pollack + /// Bruno Baia + /// $Id: CaoExporter.cs,v 1.12 2007/11/07 19:03:09 bbaia Exp $ + public class CaoExporter : ConfigurableLifetime, IApplicationContextAware, IObjectFactoryAware, IInitializingObject, IDisposable + { + #region Logging + + private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(CaoExporter)); + + #endregion + + #region Fields + + private string targetName; + private string[] interfaces; + + private IApplicationContext applicationContext; + private AbstractObjectFactory objectFactory; + + private CaoRemoteFactory remoteFactory; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the class. + /// + public CaoExporter() + { + } + + #endregion + + #region Properties + + /// + /// Gets or sets the name of the target object definition. + /// + public string TargetName + { + get { return targetName; } + set { targetName = value; } + } + + /// + /// Gets or sets the list of interfaces whose methods should be exported. + /// + /// + /// The default value of this property is all the interfaces + /// implemented or inherited by the target type. + /// + /// The interfaces to export. + public string[] Interfaces + { + get { return interfaces; } + set { interfaces = value; } + } + + #endregion + + #region IApplicationContextAware Members + + /// + /// Gets or sets the that this + /// object runs in. + /// + public IApplicationContext ApplicationContext + { + get { return applicationContext; } + set { applicationContext = value; } + } + + #endregion + + #region IObjectFactoryAware Members + + /// + /// Sets object factory to use. + /// + public IObjectFactory ObjectFactory + { + set { objectFactory = (AbstractObjectFactory) value; } + } + + #endregion + + #region IInitializingObject Members + + /// + /// Publish the object + /// + public void AfterPropertiesSet() + { + ValidateConfiguration(); + Export(); + } + + #endregion + + #region IDisposable Members + + /// + /// Disconnect the remote object from the registered remoting channels. + /// + public void Dispose() + { + RemotingServices.Disconnect(remoteFactory); + } + + #endregion + + #region Private Methods + + private void ValidateConfiguration() + { + if (TargetName == null) + { + throw new ArgumentException("The TargetName property is required."); + } + } + + private void Export() + { + remoteFactory = new CaoRemoteFactory(this, targetName, interfaces, objectFactory); + + RemotingServices.Marshal(remoteFactory, targetName); + + #region Instrumentation + + if (LOG.IsDebugEnabled) + { + LOG.Debug(String.Format("Target '{0}' registered.", targetName)); + } + + #endregion + } + + #endregion + + #region BaseCao inner class definition + + /// + /// This class extends to allow CAOs + /// to be disconnect from the client. + /// + public abstract class BaseCao : BaseRemoteObject, IDisposable + { + #region IDisposable Members + + void IDisposable.Dispose() + { + RemotingServices.Disconnect(this); + } + + #endregion + } + + #endregion + + #region CaoRemoteFactory inner class definition + + private sealed class CaoRemoteFactory : MarshalByRefObject, ICaoRemoteFactory + { + #region Fields + + private AbstractObjectFactory objectFactory; + private string targetName; + private RemoteObjectFactory remoteObjectFactory; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Create a new instance of the RemoteFactory. + /// + public CaoRemoteFactory(ILifetime lifetime, string targetName, + string[] interfaces, AbstractObjectFactory objectFactory) + { + this.targetName = targetName; + this.objectFactory = objectFactory; + + this.remoteObjectFactory = new RemoteObjectFactory(); + this.remoteObjectFactory.BaseType = typeof(BaseCao); + this.remoteObjectFactory.Interfaces = interfaces; + this.remoteObjectFactory.Infinite = lifetime.Infinite; + this.remoteObjectFactory.InitialLeaseTime = lifetime.InitialLeaseTime; + this.remoteObjectFactory.RenewOnCallTime = lifetime.RenewOnCallTime; + this.remoteObjectFactory.SponsorshipTimeout = lifetime.SponsorshipTimeout; + } + + #endregion + + #region Membres de ICaoRemoteFactory + + /// + /// Returns the CAO proxy. + /// + /// The remote object. + public object GetObject() + { + remoteObjectFactory.Target = objectFactory.GetObject(targetName); + + return remoteObjectFactory.GetObject(); + } + + /// + /// Returns the CAO proxy using the + /// argument list to call the constructor. + /// + /// + /// The matching of arguments to call the constructor is done + /// by type. The alternative ways, by index and by constructor + /// name are not supported. + /// + /// Constructor + /// arguments used to create the object. + /// The remote object. + public object GetObject(object[] constructorArguments) + { + RootObjectDefinition mergedObjectDefinition = objectFactory.GetMergedObjectDefinition(targetName, false); + + if (typeof(IFactoryObject).IsAssignableFrom(mergedObjectDefinition.ObjectType)) + { + throw new NotSupportedException( + "Client activated objects with constructor arguments is not supported with IFactoryObject implementations."); + } + + remoteObjectFactory.Target = objectFactory.GetObject(targetName, constructorArguments); + + return remoteObjectFactory.GetObject(); + } + + #endregion + + #region Overrided Methods + + /// + /// Set infinite lifetime. + /// + public override object InitializeLifetimeService() + { + return null; + } + + #endregion + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Services/Remoting/CaoFactoryObject.cs b/src/Spring/Spring.Services/Remoting/CaoFactoryObject.cs new file mode 100644 index 00000000..37f9bb86 --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/CaoFactoryObject.cs @@ -0,0 +1,151 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Objects.Factory; +using Spring.Remoting.Support; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Factory for creating a reference to a + /// client activated object (CAO). + /// + /// Aleksandar Seovic + /// Mark Pollack + /// Bruno Baia + /// $Id: CaoFactoryObject.cs,v 1.4 2006/09/13 20:20:14 bbaia Exp $ + public class CaoFactoryObject : IFactoryObject, IInitializingObject + { + #region Fields + + private string remoteTargetName; + private string serviceUrl; + private object[] constructorArguments; + + #endregion + + #region Properties + + /// + /// The remote target name to activate. + /// + public string RemoteTargetName + { + get { return remoteTargetName; } + set { remoteTargetName = value; } + } + + /// + /// The Uri of the remote type. + /// + public string ServiceUrl + { + get { return serviceUrl; } + set { serviceUrl = value; } + } + + /// + /// Argument list used to call the CAO constructor. + /// + public object[] ConstructorArguments + { + get { return constructorArguments; } + set { constructorArguments = value; } + } + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the class. + /// + public CaoFactoryObject() + { + } + + #endregion + + #region IInitializingObject Members + + /// + /// Callback method called once all factory properties have been set. + /// + /// if an error occured + public void AfterPropertiesSet() + { + if (RemoteTargetName == null) + { + throw new ArgumentException("The RemoteTargetName property is required."); + } + + if (ServiceUrl == null) + { + throw new ArgumentException("The ServiceUrl property is required."); + } + } + + #endregion + + #region IFactoryObject Members + + /// + /// Always return false. + /// + public bool IsSingleton + { + get { return false; } + } + + /// + /// The type of object to be created. + /// + public Type ObjectType + { + get { return typeof(MarshalByRefObject); } + } + + /// + /// Return the CAO proxy. + /// + /// the CAO proxy + public object GetObject() + { + ICaoRemoteFactory remoteFactory = (ICaoRemoteFactory) Activator.GetObject(typeof(ICaoRemoteFactory), serviceUrl.TrimEnd('/') + '/' + remoteTargetName); + + if (constructorArguments != null) + { + return remoteFactory.GetObject(constructorArguments); + } + else + { + return remoteFactory.GetObject(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Services/Remoting/Config/RemotingNamespaceParser.cs b/src/Spring/Spring.Services/Remoting/Config/RemotingNamespaceParser.cs new file mode 100644 index 00000000..89e6f129 --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/Config/RemotingNamespaceParser.cs @@ -0,0 +1,491 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Xml; + +using Spring.Context.Support; +using Spring.Core.TypeResolution; +using Spring.Objects; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +#endregion + +namespace Spring.Remoting.Config +{ + /// + /// Implementation of the custom configuration parser for remoting definitions. + /// + /// Bruno Baia + /// $Id: RemotingNamespaceParser.cs,v 1.3 2007/10/08 14:57:34 bbaia Exp $ + [ + NamespaceParser( + Namespace = "http://www.springframework.net/remoting", + SchemaLocationAssemblyHint = typeof(RemotingNamespaceParser), + SchemaLocation = "/Spring.Remoting.Config/spring-remoting-1.1.xsd") + ] + public sealed class RemotingNamespaceParser : ObjectsNamespaceParser + { + private const string RemotingTypePrefix = "remoting: "; + + static RemotingNamespaceParser() + { + TypeRegistry.RegisterType( + RemotingTypePrefix + RemotingConfigurerConstants.RemotingConfigurerElement, + typeof(RemotingConfigurer)); + TypeRegistry.RegisterType( + RemotingTypePrefix + SaoFactoryObjectConstants.SaoFactoryObjectElement, + typeof(SaoFactoryObject)); + TypeRegistry.RegisterType( + RemotingTypePrefix + CaoFactoryObjectConstants.CaoFactoryObjectElement, + typeof(CaoFactoryObject)); + TypeRegistry.RegisterType( + RemotingTypePrefix + RemoteObjectFactoryConstants.RemoteObjectFactoryElement, + typeof(RemoteObjectFactory)); + TypeRegistry.RegisterType( + RemotingTypePrefix + SaoExporterConstants.SaoExporterElement, + typeof(SaoExporter)); + TypeRegistry.RegisterType( + RemotingTypePrefix + CaoExporterConstants.CaoExporterElement, + typeof(CaoExporter)); + } + + /// + /// Initializes a new instance of the class. + /// + public RemotingNamespaceParser() + {} + + + /// + /// Parse the specified element and register any resulting + /// IObjectDefinitions with the IObjectDefinitionRegistry that is + /// embedded in the supplied ParserContext. + /// + /// The element to be parsed into one or more IObjectDefinitions + /// The object encapsulating the current state of the parsing + /// process. + /// + /// The primary IObjectDefinition (can be null as explained above) + /// + /// + /// Implementations should return the primary IObjectDefinition + /// that results from the parse phase if they wish to used nested + /// inside (for example) a <property> tag. + /// Implementations may return null if they will not + /// be used in a nested scenario. + /// + /// + public override IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + string name = element.GetAttribute(ObjectDefinitionConstants.IdAttribute); + IConfigurableObjectDefinition remotingDefinition = ParseRemotingDefinition(element, name, parserContext); + if (!StringUtils.HasText(name)) + { + name = ObjectDefinitionReaderUtils.GenerateObjectName(remotingDefinition, parserContext.Registry); + } + parserContext.Registry.RegisterObjectDefinition(name, remotingDefinition); + + return null; + } + + + + /// + /// Parses remoting definitions. + /// + /// Validator XML element. + /// The name of the object definition. + /// The parser context. + /// A remoting object definition. + private IConfigurableObjectDefinition ParseRemotingDefinition( + XmlElement element, string name, ParserContext parserContext) + { + switch (element.LocalName) + { + case RemotingConfigurerConstants.RemotingConfigurerElement: + return ParseRemotingConfigurer(element, name, parserContext); + case SaoFactoryObjectConstants.SaoFactoryObjectElement: + return ParseSaoFactoryObject(element, name, parserContext); + case CaoFactoryObjectConstants.CaoFactoryObjectElement: + return ParseCaoFactoryObject(element, name, parserContext); + case RemoteObjectFactoryConstants.RemoteObjectFactoryElement: + return ParseRemoteObjectFactory(element, name, parserContext); + case SaoExporterConstants.SaoExporterElement: + return ParseSaoExporter(element, name, parserContext); + case CaoExporterConstants.CaoExporterElement: + return ParseCaoExporter(element, name, parserContext); + } + + return null; + } + + /// + /// Parses the RemotingConfigurer definition. + /// + /// The element to parse. + /// The name of the object definition. + /// The parser context. + /// RemotingConfigurer object definition. + private IConfigurableObjectDefinition ParseRemotingConfigurer( + XmlElement element, string name, ParserContext parserContext) + { + string typeName = GetTypeName(element); + string filename = element.GetAttribute(RemotingConfigurerConstants.FilenameAttribute); + string useConfigFile = element.GetAttribute(RemotingConfigurerConstants.UseConfigFileAttribute); + string ensureSecurity = element.GetAttribute(RemotingConfigurerConstants.EnsureSecurityAttribute); + + MutablePropertyValues properties = new MutablePropertyValues(); + if (StringUtils.HasText(filename)) + { + properties.Add("Filename", filename); + } + if (StringUtils.HasText(useConfigFile)) + { + properties.Add("UseConfigFile", useConfigFile); + } + if (StringUtils.HasText(ensureSecurity)) + { + properties.Add("EnsureSecurity", ensureSecurity); + } + + IConfigurableObjectDefinition cod = parserContext.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( + typeName, null, parserContext.ReaderContext.Reader.Domain); + cod.PropertyValues = properties; + return cod; + } + + /// + /// Parses the SaoFactoryObject definition. + /// + /// The element to parse. + /// The name of the object definition. + /// The parser context. + /// SaoFactoryObject object definition. + private IConfigurableObjectDefinition ParseSaoFactoryObject( + XmlElement element, string name, ParserContext parserContext) + { + string typeName = GetTypeName(element); + string serviceInterface = element.GetAttribute(SaoFactoryObjectConstants.ServiceInterfaceAttribute); + string serviceUrl = element.GetAttribute(SaoFactoryObjectConstants.ServiceUrlAttribute); + + MutablePropertyValues properties = new MutablePropertyValues(); + if (StringUtils.HasText(serviceInterface)) + { + properties.Add("ServiceInterface", serviceInterface); + } + if (StringUtils.HasText(serviceUrl)) + { + properties.Add("ServiceUrl", serviceUrl); + } + IConfigurableObjectDefinition cod = parserContext.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( + typeName, null, parserContext.ReaderContext.Reader.Domain); + cod.PropertyValues = properties; + return cod; + } + + /// + /// Parses the CaoFactoryObject definition. + /// + /// The element to parse. + /// The name of the object definition. + /// The parser context. + /// CaoFactoryObject object definition. + private IConfigurableObjectDefinition ParseCaoFactoryObject( + XmlElement element, string name, ParserContext parserContext) + { + string typeName = GetTypeName(element); + string remoteTargetName = element.GetAttribute(CaoFactoryObjectConstants.RemoteTargetNameAttribute); + string serviceUrl = element.GetAttribute(CaoFactoryObjectConstants.ServiceUrlAttribute); + + MutablePropertyValues properties = new MutablePropertyValues(); + if (StringUtils.HasText(remoteTargetName)) + { + properties.Add("RemoteTargetName", remoteTargetName); + } + if (StringUtils.HasText(serviceUrl)) + { + properties.Add("ServiceUrl", serviceUrl); + } + + foreach (XmlElement child in element.ChildNodes) + { + switch (child.LocalName) + { + case CaoFactoryObjectConstants.ConstructorArgumentsElement: + properties.Add("ConstructorArguments", base.GetList(child, name, parserContext.ParserHelper)); + break; + } + } + + IConfigurableObjectDefinition cod = parserContext.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( + typeName, null, parserContext.ReaderContext.Reader.Domain); + cod.PropertyValues = properties; + + return cod; + } + + /// + /// Parses the RemoteObjectFactory definition. + /// + /// The element to parse. + /// The name of the object definition. + /// The parser context. + /// RemoteObjectFactory object definition. + private IConfigurableObjectDefinition ParseRemoteObjectFactory( + XmlElement element, string name, ParserContext parserContext) + { + string typeName = GetTypeName(element); + string targetName = element.GetAttribute(RemoteObjectFactoryConstants.TargetNameAttribute); + string infinite = element.GetAttribute(RemoteObjectFactoryConstants.InfiniteAttribute); + + MutablePropertyValues properties = new MutablePropertyValues(); + if (StringUtils.HasText(targetName)) + { + properties.Add("Target", targetName); + } + if (StringUtils.HasText(infinite)) + { + properties.Add("Infinite", infinite); + } + + foreach (XmlElement child in element.ChildNodes) + { + switch (child.LocalName) + { + case LifeTimeConstants.LifeTimeElement: + ParseLifeTime(properties, child, parserContext); + break; + case InterfacesConstants.InterfacesElement: + properties.Add("Interfaces", base.GetList(child, name, parserContext.ParserHelper)); + break; + } + } + + IConfigurableObjectDefinition cod = parserContext.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( + typeName, null, parserContext.ReaderContext.Reader.Domain); + cod.PropertyValues = properties; + + return cod; + } + + /// + /// Parses the SaoExporter definition. + /// + /// The element to parse. + /// The name of the object definition. + /// The parser context. + /// SaoExporter object definition. + private IConfigurableObjectDefinition ParseSaoExporter( + XmlElement element, string name, ParserContext parserContext) + { + string typeName = GetTypeName(element); + string targetName = element.GetAttribute(SaoExporterConstants.TargetNameAttribute); + string applicationName = element.GetAttribute(SaoExporterConstants.ApplicationNameAttribute); + string serviceName = element.GetAttribute(SaoExporterConstants.ServiceNameAttribute); + string infinite = element.GetAttribute(SaoExporterConstants.InfiniteAttribute); + + MutablePropertyValues properties = new MutablePropertyValues(); + if (StringUtils.HasText(targetName)) + { + properties.Add("TargetName", targetName); + } + if (StringUtils.HasText(applicationName)) + { + properties.Add("ApplicationName", applicationName); + } + if (StringUtils.HasText(serviceName)) + { + properties.Add("ServiceName", serviceName); + } + if (StringUtils.HasText(infinite)) + { + properties.Add("Infinite", infinite); + } + + foreach (XmlElement child in element.ChildNodes) + { + switch (child.LocalName) + { + case LifeTimeConstants.LifeTimeElement: + ParseLifeTime(properties, child, parserContext); + break; + case InterfacesConstants.InterfacesElement: + properties.Add("Interfaces", base.GetList(child, name, parserContext.ParserHelper)); + break; + } + } + + IConfigurableObjectDefinition cod = parserContext.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( + typeName, null, parserContext.ReaderContext.Reader.Domain); + cod.PropertyValues = properties; + return cod; + } + + /// + /// Parses the CaoExporter definition. + /// + /// The element to parse. + /// The name of the object definition. + /// The parser context. + /// CaoExporter object definition. + private IConfigurableObjectDefinition ParseCaoExporter( + XmlElement element, string name, ParserContext parserContext) + { + string typeName = GetTypeName(element); + string targetName = element.GetAttribute(CaoExporterConstants.TargetNameAttribute); + string infinite = element.GetAttribute(CaoExporterConstants.InfiniteAttribute); + + MutablePropertyValues properties = new MutablePropertyValues(); + if (StringUtils.HasText(targetName)) + { + properties.Add("TargetName", targetName); + } + if (StringUtils.HasText(infinite)) + { + properties.Add("Infinite", infinite); + } + + foreach (XmlElement child in element.ChildNodes) + { + switch (child.LocalName) + { + case LifeTimeConstants.LifeTimeElement: + ParseLifeTime(properties, child, parserContext); + break; + case InterfacesConstants.InterfacesElement: + properties.Add("Interfaces", base.GetList(child, name, parserContext.ParserHelper)); + break; + } + } + + IConfigurableObjectDefinition cod = parserContext.ReaderContext.ObjectDefinitionFactory.CreateObjectDefinition( + typeName, null, parserContext.ReaderContext.Reader.Domain); + cod.PropertyValues = properties; + + return cod; + } + + /// + /// Parses the LifeTime definition. + /// + private void ParseLifeTime(MutablePropertyValues properties, XmlElement child, ParserContext parserContext) + { + string initialLeaseTime = child.GetAttribute(LifeTimeConstants.InitialLeaseTimeAttribute); + string renewOnCallTime = child.GetAttribute(LifeTimeConstants.RenewOnCallTimeAttribute); + string sponsorshipTimeout = child.GetAttribute(LifeTimeConstants.SponsorshipTimeoutAttribute); + + if (StringUtils.HasText(initialLeaseTime)) + { + properties.Add("InitialLeaseTime", initialLeaseTime); + } + if (StringUtils.HasText(renewOnCallTime)) + { + properties.Add("RenewOnCallTime", renewOnCallTime); + } + if (StringUtils.HasText(sponsorshipTimeout)) + { + properties.Add("SponsorshipTimeout", sponsorshipTimeout); + } + } + + /// + /// Gets the name of the object type for the specified element. + /// + /// The element. + /// The name of the object type. + private string GetTypeName(XmlElement element) + { + string typeName = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute); + if (StringUtils.IsNullOrEmpty(typeName)) + { + return RemotingTypePrefix + element.LocalName; + } + return typeName; + } + + #region Element & Attribute Name Constants + + private class RemotingConfigurerConstants + { + public const string RemotingConfigurerElement = "configurer"; + public const string FilenameAttribute = "filename"; + public const string UseConfigFileAttribute = "useConfigFile"; + public const string EnsureSecurityAttribute = "ensureSecurity"; + } + + private class SaoFactoryObjectConstants + { + public const string SaoFactoryObjectElement = "saoFactory"; + public const string ServiceInterfaceAttribute = "serviceInterface"; + public const string ServiceUrlAttribute = "serviceUrl"; + } + + private class CaoFactoryObjectConstants + { + public const string CaoFactoryObjectElement = "caoFactory"; + public const string ConstructorArgumentsElement = "constructor-args"; + public const string RemoteTargetNameAttribute = "remoteTargetName"; + public const string ServiceUrlAttribute = "serviceUrl"; + } + + private class LifeTimeConstants + { + public const string LifeTimeElement = "lifeTime"; + public const string InitialLeaseTimeAttribute = "initialLeaseTime"; + public const string RenewOnCallTimeAttribute = "renewOnCallTime"; + public const string SponsorshipTimeoutAttribute = "sponsorshipTimeout"; + } + + private class InterfacesConstants + { + public const string InterfacesElement = "interfaces"; + } + + private class RemoteObjectFactoryConstants + { + public const string RemoteObjectFactoryElement = "remoteObjectFactory"; + public const string TargetNameAttribute = "targetName"; + public const string InfiniteAttribute = "infinite"; + } + + private class SaoExporterConstants + { + public const string SaoExporterElement = "saoExporter"; + public const string TargetNameAttribute = "targetName"; + public const string ApplicationNameAttribute = "applicationName"; + public const string ServiceNameAttribute = "serviceName"; + public const string InfiniteAttribute = "infinite"; + } + + private class CaoExporterConstants + { + public const string CaoExporterElement = "caoExporter"; + public const string TargetNameAttribute = "targetName"; + public const string InfiniteAttribute = "infinite"; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Services/Remoting/Config/spring-remoting-1.1.xsd b/src/Spring/Spring.Services/Remoting/Config/spring-remoting-1.1.xsd new file mode 100644 index 00000000..16878986 --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/Config/spring-remoting-1.1.xsd @@ -0,0 +1,231 @@ + + + + + + + + Spring.NET Remoting Framework Config Schema Definition + + Author: Bruno Baia + + This file defines a configuration schema for the remoting framework + object definitions. Using elements from this schema instead of the + standard object definitions can greatly simplify remoting configuration. + + + + + + Configures the remoting infrastructure. + + + + The name of the remoting configurationn file. + + + + + Indicates whether a configuration file is used. Default is true. + + + + + Indicates whether the security is enabled. Default is false. + + + + + + + Creates a reference to a server activated object (SAO). + + + + The id of the created object. + + + + + The remote service interface. + + + + + The URI of the well known object. + + + + + + + Creates a reference to a client activated object (CAO). + + + + + The arguments used to call the CAO constructor. + + + + + + + + + The id of the created object. + + + + + The remote target name to activate. + + + + + The Uri of the remote type. + + + + + + + Defines the lifetime policy of the remoteobject. + + + + The initial time for the lease. + + + + + The amount of time to add to the current lease time after each call. + + + + + The amount of time to wait for a sponsor to respond with a lease renewal time. + + + + + + + Creates a MarshalByRefObject wrapper around target class. + + + + + + The list of interfaces to wrap. + + + + + + + + + The id of the created remote object to be referenced. + + + + + The target object. + + + + + Indicates whether the remote object has infinite lifetime. + + + + + + + Registers an object as a Server Activated Object (SAO). + + + + + + The list of interfaces whose methods should be exported. + + + + + + + + + The id of the exporter to be referenced. + + + + + The target object to export. + + + + + The name of the remote application. + + + + + The name of the exported remote service. + + + + + Indicates whether the remote object has infinite lifetime. + + + + + + + Registers an object as a Client Activated Object (CAO). + + + + + + The list of interfaces whose methods should be exported. + + + + + + + + + The id of the exporter to be referenced. + + + + + The target object to export. + + + + + Indicates whether the remote object has infinite lifetime. + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Remoting/Config/spring-remoting-1.1.xsx b/src/Spring/Spring.Services/Remoting/Config/spring-remoting-1.1.xsx new file mode 100644 index 00000000..ff71343b --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/Config/spring-remoting-1.1.xsx @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/Spring/Spring.Services/Remoting/RemoteObjectFactory.cs b/src/Spring/Spring.Services/Remoting/RemoteObjectFactory.cs new file mode 100644 index 00000000..dd8050d4 --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/RemoteObjectFactory.cs @@ -0,0 +1,184 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Proxy; +using Spring.Objects.Factory; +using Spring.Remoting.Support; +using Spring.Util; +using Spring.Core.TypeResolution; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Factory for creating MarshalByRefObject wrapper around target class. + /// + /// Bruno Baia + /// $Id: RemoteObjectFactory.cs,v 1.8 2007/07/31 18:18:19 bbaia Exp $ + public class RemoteObjectFactory : ConfigurableLifetime, IInitializingObject, IFactoryObject + { + #region Fields + + private object target; + private Type baseType = typeof(BaseRemoteObject); + private string[] interfaces; + + private ConstructorInfo proxyConstructor; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the MarshalByRefObjectFactory. + /// + public RemoteObjectFactory() + { + } + + #endregion + + #region Properties + + /// + /// Gets or sets the target object. + /// + public object Target + { + get { return target; } + set { target = value; } + } + + /// + /// Gets or sets the class or subclass + /// that the proxy must inherit from. + /// + public Type BaseType + { + get { return baseType; } + set { baseType = value; } + } + + /// + /// Gets or sets the list of interfaces to wrap. + /// + /// + /// The default value of this property is all the interfaces + /// implemented or inherited by the target type. + /// + /// The interfaces to export. + public string[] Interfaces + { + get { return interfaces; } + set { interfaces = value; } + } + + #endregion + + #region IInitializingObject Members + + /// + /// Initializes factory object. + /// + public void AfterPropertiesSet() + { + ValidateConfiguration(); + GenerateProxy(); + } + + #endregion + + #region IFactoryObject Members + + /// + /// Returns type of the remotable target proxy. + /// + public Type ObjectType + { + get + { + return (proxyConstructor != null) ? proxyConstructor.DeclaringType : target.GetType(); + } + } + + /// + /// Creates new instance of the remotable target proxy. + /// + /// New instance of the remotable target proxy. + public object GetObject() + { + if (proxyConstructor == null) + GenerateProxy(); + + return proxyConstructor.Invoke(new object[1]{ target }); + } + + /// + /// Always returns false. + /// + public bool IsSingleton + { + get + { + return false; + } + } + + #endregion + + #region Private Methods + + private void ValidateConfiguration() + { + if (Target == null) + { + throw new ArgumentException("The Target property is required."); + } + if (!typeof(BaseRemoteObject).IsAssignableFrom(BaseType)) + { + throw new ArgumentException("The type BaseRemoteObject cannot be assigned from BaseType."); + } + } + + private void GenerateProxy() + { + IProxyTypeBuilder builder = new RemoteObjectProxyTypeBuilder(this); + builder.TargetType = target.GetType(); + builder.BaseType = baseType; + if (interfaces != null && interfaces.Length > 0) + { + builder.Interfaces = TypeResolutionUtils.ResolveInterfaceArray(interfaces); + } + + Type remotableObjectType = builder.BuildProxyType(); + + proxyConstructor = remotableObjectType.GetConstructor(new Type[1] { builder.TargetType }); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Services/Remoting/RemotingConfigurer.cs b/src/Spring/Spring.Services/Remoting/RemotingConfigurer.cs new file mode 100644 index 00000000..f1d4e019 --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/RemotingConfigurer.cs @@ -0,0 +1,170 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Remoting; + +using Common.Logging; +using Spring.Core; +using Spring.Core.IO; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Convenience class to configure remoting infrastructure from the IoC container. + /// + /// Bruno Baia + /// $Id: RemotingConfigurer.cs,v 1.7 2007/10/08 14:57:34 bbaia Exp $ + public class RemotingConfigurer : IObjectFactoryPostProcessor, IOrdered + { + #region Fields + + private static readonly ILog log = LogManager.GetLogger(typeof(RemotingConfigurer)); + + private int _order = Int32.MinValue; + + private IResource _filename; + private bool _useConfigFile = true; + private bool _ensureSecurity = false; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Initializes a new instance of the class. + /// + public RemotingConfigurer() + {} + + #endregion + + #region Properties + + /// + /// Gets or sets the name of the remoting configuration file. + /// + /// + /// If filename is or not set, + /// current AppDomain's configuration file will be used. + /// + public IResource Filename + { + get { return _filename; } + set { _filename = value; } + } + + /// + /// Indicates whether a configuration file is used. + /// Default value is . + /// + /// + /// If , default remoting configuration will be used. + /// + public bool UseConfigFile + { + get { return _useConfigFile; } + set { _useConfigFile = value; } + } + + /// + /// Gets or sets if security is enabled. + /// + /// + /// This property is only available since .NET Framework 2.0. + /// + public bool EnsureSecurity + { + get { return _ensureSecurity; } + set + { +#if NET_2_0 + _ensureSecurity = value; +#else + throw new NotSupportedException( + "The EnsureSecurity property is supported only on .Net Framework 2.0."); +#endif + } + } + + #endregion + + #region IObjectFactoryPostProcessor Members + + /// + /// Modify the application context's internal object factory after its + /// standard initialization. + /// + /// + /// The object factory used by the application context. + /// + /// + public void PostProcessObjectFactory(IConfigurableListableObjectFactory factory) + { + string filename = null; + if (UseConfigFile) + { + filename = (Filename == null) + ? AppDomain.CurrentDomain.SetupInformation.ConfigurationFile + : Filename.File.FullName; + } +#if NET_2_0 + RemotingConfiguration.Configure(filename, EnsureSecurity); +#else + RemotingConfiguration.Configure(filename); +#endif + #region Instrumentation + + if (log.IsDebugEnabled) + { + if (filename == null) + { + log.Debug("Default remoting infrastructure loaded."); + } + else + { + log.Debug(String.Format("Remoting infrastructure configured using file '{0}'.", filename)); + } + } + + #endregion + } + + #endregion + + #region IOrdered Members + + /// + /// Return the order value of this object, + /// where a higher value means greater in terms of sorting. + /// + public int Order + { + get { return _order; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Services/Remoting/SaoExporter.cs b/src/Spring/Spring.Services/Remoting/SaoExporter.cs new file mode 100644 index 00000000..2b1f853d --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/SaoExporter.cs @@ -0,0 +1,341 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Remoting; + +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Proxy; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Support; +using Spring.Remoting.Support; +using Spring.Util; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Publishes an instance of an object under + /// a given url as a Server Activated Object (SAO). + /// + /// + /// Object can be exported either as SingleCall or Singleton. + /// + /// Aleksandar Seovic + /// Mark Pollack + /// Bruno Baia + /// $Id: SaoExporter.cs,v 1.18 2007/07/31 18:18:19 bbaia Exp $ + public class SaoExporter : ConfigurableLifetime, IApplicationContextAware, IObjectFactoryAware, IInitializingObject, IDisposable + { + #region Logging + + private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(SaoExporter)); + + #endregion + + #region Fields + + private string targetName; + private string applicationName; + private string serviceName; + private string[] interfaces; + + private IApplicationContext applicationContext; + private IObjectFactory objectFactory; + + private MarshalByRefObject remoteObject; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the class. + /// + public SaoExporter() + { + } + + #endregion + + #region Properties + + /// + /// Gets or sets the name of the target object definition. + /// + public string TargetName + { + get { return targetName; } + set { targetName = value; } + } + + /// + /// Gets or sets the name of the remote application. + /// + public string ApplicationName + { + get { return applicationName; } + set { applicationName = value; } + } + + /// + /// Gets or sets the name of the exported remote service. + /// + /// The name that will be used in the URI to refer to this service. + /// This will be of the form, tcp://host:port/ServiceName or + /// tcp://host:port/ApplicationName/ServiceName + /// + /// + public string ServiceName + { + get { return serviceName; } + set { serviceName = value; } + } + + /// + /// Gets or sets the list of interfaces whose methods should be exported. + /// + /// + /// The default value of this property is all the interfaces + /// implemented or inherited by the target type. + /// + /// The interfaces to export. + public string[] Interfaces + { + get { return interfaces; } + set { interfaces = value; } + } + + #endregion + + #region IApplicationContextAware Members + + /// + /// Gets or sets the that this + /// object runs in. + /// + public IApplicationContext ApplicationContext + { + get { return applicationContext; } + set { applicationContext = value; } + } + + #endregion + + #region IObjectFactoryAware Members + + /// + /// Sets object factory to use. + /// + public IObjectFactory ObjectFactory + { + set { objectFactory = value; } + } + + #endregion + + #region IInitializingObject Members + + /// + /// Publish the object + /// + public void AfterPropertiesSet() + { + ValidateConfiguration(); + Export(); + } + + #endregion + + #region IDisposable Members + + /// + /// Disconnect the remote object from the registered remoting channels. + /// + public void Dispose() + { + RemotingServices.Disconnect(remoteObject); + } + + #endregion + + #region Private Methods + + private void ValidateConfiguration() + { + if (TargetName == null) + { + throw new ArgumentException("The TargetName property is required."); + } + + if (ServiceName == null) + { + throw new ArgumentException("The ServiceName property is required."); + } + } + + private void Export() + { + IProxyTypeBuilder builder = new SaoRemoteObjectProxyTypeBuilder(applicationContext.Name, targetName, this); + builder.TargetType = this.objectFactory.GetType(targetName); + if (interfaces != null && interfaces.Length > 0) + { + builder.Interfaces = TypeResolutionUtils.ResolveInterfaceArray(interfaces); + } + + ConstructorInfo proxyConstructor = + builder.BuildProxyType().GetConstructor(Type.EmptyTypes); + + string objectUri = (applicationName != null ? applicationName + "/" + serviceName : serviceName); + + remoteObject = (MarshalByRefObject) proxyConstructor.Invoke(ObjectUtils.EmptyObjects); + + RemotingServices.Marshal(remoteObject, objectUri); + + #region Instrumentation + + if (LOG.IsDebugEnabled) + { + LOG.Debug(String.Format("Target '{0}' exported as '{1}'.", targetName, objectUri)); + } + + #endregion + } + + #endregion + + #region SaoRemoteObjectProxyTypeBuilder inner class definition + + /// + /// Builds a proxy type based on to wrap a target object + /// that is intended to be remotable. + /// + /// + /// The wrapped target object is retrieved by name from the IoC container. + /// + private sealed class SaoRemoteObjectProxyTypeBuilder : RemoteObjectProxyTypeBuilder + { + #region Fields + + private static readonly MethodInfo TimeSpan_FromTicks = + typeof(TimeSpan).GetMethod("FromTicks", BindingFlags.Public | BindingFlags.Static); + + private static readonly MethodInfo ContextRegistry_GetContext = + typeof(ContextRegistry).GetMethod("GetContext", new Type[1] { typeof(string) }); + + private static readonly MethodInfo IObjectFactory_GetObject = + typeof(IObjectFactory).GetMethod("GetObject", new Type[1] { typeof(string) }); + + private string contextName; + private string targetObjectName; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The name of the target object definition. + /// + /// + /// The name of the application context that this object runs in. + /// + /// + /// The lifetime properties to be applied to the target object. + /// + public SaoRemoteObjectProxyTypeBuilder(string contextName, string targetObjectName, ILifetime lifetime) + : base(lifetime) + { + this.contextName = contextName; + this.targetObjectName = targetObjectName; + + Name = "SaoRemoteObjectProxy"; + BaseType = typeof(BaseRemoteObject); + } + + #endregion + + #region IProxyTypeGenerator Members + + /// + /// Generates the IL instructions that pushes + /// the target instance on which calls should be delegated to. + /// + /// The IL generator to use. + public override void PushTarget(ILGenerator il) + { + il.Emit(OpCodes.Ldstr, this.contextName); + il.Emit(OpCodes.Call, ContextRegistry_GetContext); + il.Emit(OpCodes.Ldstr, this.targetObjectName); + il.Emit(OpCodes.Callvirt, IObjectFactory_GetObject); + } + + #endregion + + #region Protected Methods + + /// + /// Implements constructors for the proxy class. + /// + /// + /// The to use. + /// + protected override void ImplementConstructors(TypeBuilder builder) + { + MethodAttributes attributes = MethodAttributes.Public | + MethodAttributes.HideBySig | MethodAttributes.SpecialName | + MethodAttributes.RTSpecialName; + ConstructorBuilder cb = builder.DefineConstructor(attributes, + CallingConventions.Standard, Type.EmptyTypes); + + ILGenerator il = cb.GetILGenerator(); + GenerateRemoteObjectLifetimeInitialization(il); + il.Emit(OpCodes.Ret); + } + + /// + /// Deaclares a field that holds the target object instance. + /// + /// + /// The builder to use for code generation. + /// + protected override void DeclareTargetInstanceField(TypeBuilder builder) + { + } + + #endregion + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Services/Remoting/SaoFactoryObject.cs b/src/Spring/Spring.Services/Remoting/SaoFactoryObject.cs new file mode 100644 index 00000000..7b102bd7 --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/SaoFactoryObject.cs @@ -0,0 +1,141 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Factory for creating a reference to a + /// remote server activated object (SAO). + /// + /// + /// This is useful alternative to adminstrative type registration on + /// the client when you would like the client to have only + /// a reference to the interface that an SAO implements and not the + /// actual SAO implentation. + /// + /// Aleksandar Seovic + /// Mark Pollack + /// Bruno Baia + /// $Id: SaoFactoryObject.cs,v 1.7 2006/05/17 17:45:39 bbaia Exp $ + public class SaoFactoryObject : IFactoryObject, IInitializingObject + { + #region Fields + + private Type serviceInterface; + private string serviceUrl; + + #endregion + + #region Properties + + /// + /// The remote service interface. + /// + public Type ServiceInterface + { + get { return serviceInterface; } + set { serviceInterface = value; } + } + + /// + /// The URI of the well known object + /// + public string ServiceUrl + { + get { return serviceUrl; } + set { serviceUrl = value; } + } + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the SaoFactoryObject class. + /// + public SaoFactoryObject() + { + } + + #endregion + + #region IInitializingObject Members + + /// + /// Callback method called once all factory properties have been set. + /// + /// if an error occured + public void AfterPropertiesSet() + { + if (ServiceUrl == null) + { + throw new ArgumentException("The ServiceUrl property is required."); + } + + if (ServiceInterface == null) + { + throw new ArgumentException("The ServiceInterface property is required."); + } + + if (!ServiceInterface.IsInterface) + { + throw new ArgumentException("ServiceInterface must be an interface"); + } + } + + #endregion + + #region IFactoryObject Members + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + public bool IsSingleton + { + get { return false; } + } + + /// + /// The type of object to be created. + /// + public Type ObjectType + { + get { return serviceInterface; } + } + + /// + /// Return the SAO proxy. + /// + /// the SAO proxy + public object GetObject() + { + return Activator.GetObject(serviceInterface, serviceUrl); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Services/Remoting/Support/BaseRemoteObject.cs b/src/Spring/Spring.Services/Remoting/Support/BaseRemoteObject.cs new file mode 100644 index 00000000..f12b5141 --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/Support/BaseRemoteObject.cs @@ -0,0 +1,164 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Remoting.Lifetime; + +#endregion + +namespace Spring.Remoting.Support +{ + /// + /// This class extends to allow users + /// to define object lifecycle details by simply setting its properties. + /// + /// + ///

    + /// Remoting exporters uses this class as a base proxy class + /// in order to support lifecycle configuration when exporting + /// a remote object. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: BaseRemoteObject.cs,v 1.1 2006/09/13 20:20:16 bbaia Exp $ + public abstract class BaseRemoteObject : MarshalByRefObject + { + #region Fields + + private bool isInfinite = false; + private TimeSpan initialLeaseTime = TimeSpan.Zero; + private TimeSpan renewOnCallTime = TimeSpan.Zero; + private TimeSpan sponsorshipTimeout = TimeSpan.Zero; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Initializes a new instance of the class. + /// + public BaseRemoteObject() + {} + + #endregion + + #region Properties + + /// + /// Gets or sets a value indicating whether this instance has infinite lifetime. + /// + /// + /// if this instance has infinite lifetime; + /// otherwise, . + /// + public bool IsInfinite + { + get { return isInfinite; } + set { isInfinite = value; } + } + + /// + /// Gets or sets the initial lease time. + /// + /// The initial lease time. + public TimeSpan InitialLeaseTime + { + get { return initialLeaseTime; } + set { initialLeaseTime = value; } + } + + /// + /// Gets or sets the amount of time lease should be + /// extended for on each call to this object. + /// + /// The amount of time lease should be + /// extended for on each call to this object. + public TimeSpan RenewOnCallTime + { + get { return renewOnCallTime; } + set { renewOnCallTime = value; } + } + + /// + /// Gets or sets the amount of time lease manager will for this object's + /// sponsors to respond. + /// + /// The amount of time lease manager will for this object's + /// sponsors to respond. + public TimeSpan SponsorshipTimeout + { + get { return sponsorshipTimeout; } + set { sponsorshipTimeout = value; } + } + + #endregion + + #region Methods + + /// + /// Obtains a lifetime service object to control the lifetime policy for this instance. + /// + /// + ///

    + /// This method uses property values to configure for this object. + ///

    + ///

    + /// It is very much inspired by Ingo Rammer's example in Chapter 6 of "Advanced .NET Remoting", + /// but is modified slightly to make it more "Spring-friendly". Basically, the main difference is that + /// instead of pulling lease configuration from the .NET config file, this implementation relies + /// on Spring DI to get appropriate values injected, which makes it much more flexible. + ///

    + ///
    + /// + /// An object of type used to control the + /// lifetime policy for this instance. This is the current lifetime service object for + /// this instance if one exists; otherwise, a new lifetime service object initialized to the value + /// of the property. + /// + /// The immediate caller does not have infrastructure permission. + public override object InitializeLifetimeService() + { + if (this.isInfinite) + { + return null; + } + + ILease lease = (ILease) base.InitializeLifetimeService(); + if (this.initialLeaseTime != TimeSpan.Zero) + { + lease.InitialLeaseTime = this.initialLeaseTime; + } + if (this.renewOnCallTime != TimeSpan.Zero) + { + lease.RenewOnCallTime = this.renewOnCallTime; + } + if (this.sponsorshipTimeout != TimeSpan.Zero) + { + lease.SponsorshipTimeout = this.sponsorshipTimeout; + } + + return lease; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Services/Remoting/Support/ConfigurableLifetime.cs b/src/Spring/Spring.Services/Remoting/Support/ConfigurableLifetime.cs new file mode 100644 index 00000000..c32cdcc3 --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/Support/ConfigurableLifetime.cs @@ -0,0 +1,95 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Remoting.Support +{ + /// + /// Configurable implementation of the interface. + /// + /// Bruno Baia + /// $Id: ConfigurableLifetime.cs,v 1.1 2006/09/13 20:20:16 bbaia Exp $ + public class ConfigurableLifetime : ILifetime + { + #region Fields + + private bool infinite = true; + private TimeSpan initialLeaseTime = TimeSpan.Zero; + private TimeSpan renewOnCallTime = TimeSpan.Zero; + private TimeSpan sponsorshipTimeout = TimeSpan.Zero; + + #endregion + + #region ILifetime Members + + /// + /// Gets or sets a value indicating whether this instance has infinite lifetime. + /// + /// + /// true if this instance has infinite lifetime; otherwise, false. + /// + public bool Infinite + { + get { return infinite; } + set { infinite = value; } + } + + /// + /// Gets or sets the initial lease time. + /// + /// The initial lease time. + public TimeSpan InitialLeaseTime + { + get { return initialLeaseTime; } + set { initialLeaseTime = value; } + } + + /// + /// Gets or sets the amount of time lease + /// should be extended for on each call to this object. + /// + /// The amount of time lease should be + /// extended for on each call to this object. + public TimeSpan RenewOnCallTime + { + get { return renewOnCallTime; } + set { renewOnCallTime = value; } + } + + /// + /// Gets or sets the amount of time lease manager + /// will for this object's sponsors to respond. + /// + /// The amount of time lease manager will for this object's + /// sponsors to respond. + public TimeSpan SponsorshipTimeout + { + get { return sponsorshipTimeout; } + set { sponsorshipTimeout = value; } + } + + #endregion + } +} diff --git a/src/Spring/Spring.Services/Remoting/Support/ICaoRemoteFactory.cs b/src/Spring/Spring.Services/Remoting/Support/ICaoRemoteFactory.cs new file mode 100644 index 00000000..37dbaadc --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/Support/ICaoRemoteFactory.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + +#endregion + +namespace Spring.Remoting.Support +{ + /// + /// Interface for a CAO based object factory. + /// + /// + ///

    + /// Provides a well known location for clients to retrieve + /// references to CAO references. + ///

    + ///
    + /// Aleksandar Seovic + /// Mark Pollack + /// Bruno Baia + /// $Id: ICaoRemoteFactory.cs,v 1.1 2006/09/13 20:20:16 bbaia Exp $ + public interface ICaoRemoteFactory + { + /// + /// Returns the CAO proxy. + /// + /// The remote object. + object GetObject(); + + /// + /// Returns the CAO proxy using the + /// argument list to call the constructor. + /// + /// + /// The matching of arguments to call the constructor is done + /// by type. The alternative ways, by index and by constructor + /// name are not supported. + /// + /// Constructor + /// arguments used to create the object. + /// The remote object. + object GetObject(object[] constructorArguments); + } +} diff --git a/src/Spring/Spring.Services/Remoting/Support/ILifetime.cs b/src/Spring/Spring.Services/Remoting/Support/ILifetime.cs new file mode 100644 index 00000000..b94e25c0 --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/Support/ILifetime.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Remoting.Support +{ + /// + /// Defines lifetime's properties of remote objects that is used by Spring. + /// + /// Bruno Baia + /// $Id: ILifetime.cs,v 1.1 2006/09/13 20:20:16 bbaia Exp $ + public interface ILifetime + { + /// + /// Gets or sets a value indicating whether this instance has infinite lifetime. + /// + /// + /// true if this instance has infinite lifetime; otherwise, false. + /// + bool Infinite { get; } + + /// + /// Gets the initial lease time. + /// + /// The initial lease time. + TimeSpan InitialLeaseTime { get; } + + /// + /// Gets the amount of time lease + /// should be extended for on each call to this object. + /// + /// The amount of time lease should be + /// extended for on each call to this object. + TimeSpan RenewOnCallTime { get; } + + /// + /// Gets the amount of time lease manager + /// will for this object's sponsors to respond. + /// + /// The amount of time lease manager will for this object's + /// sponsors to respond. + TimeSpan SponsorshipTimeout { get; } + } +} diff --git a/src/Spring/Spring.Services/Remoting/Support/RemoteObjectProxyTypeBuilder.cs b/src/Spring/Spring.Services/Remoting/Support/RemoteObjectProxyTypeBuilder.cs new file mode 100644 index 00000000..89c857f6 --- /dev/null +++ b/src/Spring/Spring.Services/Remoting/Support/RemoteObjectProxyTypeBuilder.cs @@ -0,0 +1,177 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Proxy; + +#endregion + +namespace Spring.Remoting.Support +{ + /// + /// Builds a proxy type based on to wrap a target object + /// that is intended to be remotable. + /// + /// Bruno Baia + /// $Id: RemoteObjectProxyTypeBuilder.cs,v 1.3 2006/11/12 01:37:47 bbaia Exp $ + internal class RemoteObjectProxyTypeBuilder : CompositionProxyTypeBuilder + { + #region Fields + + private static readonly MethodInfo TimeSpan_FromTicks = + typeof(TimeSpan).GetMethod("FromTicks", BindingFlags.Public | BindingFlags.Static); + + private static readonly MethodInfo BaseRemoteObject_IsInfinite = + typeof(BaseRemoteObject).GetMethod("set_IsInfinite", BindingFlags.Public | BindingFlags.Instance); + + private static readonly MethodInfo BaseRemoteObject_InitialLeaseTime = + typeof(BaseRemoteObject).GetMethod("set_InitialLeaseTime", BindingFlags.Public | BindingFlags.Instance); + + private static readonly MethodInfo BaseRemoteObject_RenewOnCallTime = + typeof(BaseRemoteObject).GetMethod("set_RenewOnCallTime", BindingFlags.Public | BindingFlags.Instance); + + private static readonly MethodInfo BaseRemoteObject_SponsorshipTimeout = + typeof(BaseRemoteObject).GetMethod("set_SponsorshipTimeout", BindingFlags.Public | BindingFlags.Instance); + + private ILifetime lifetime; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The lifetime properties to be applied to the target object. + /// + public RemoteObjectProxyTypeBuilder(ILifetime lifetime) + { + this.lifetime = lifetime; + + Name = "RemoteObjectProxy"; + } + + #endregion + + #region IProxyTypeBuilder Members + + /// + /// Creates a remotable proxy type based on . + /// + /// The generated proxy class. + /// + /// If the is not + /// an instance of . + /// + public override Type BuildProxyType() + { + if (!typeof(BaseRemoteObject).IsAssignableFrom(this.BaseType)) + { + throw new ArgumentException( + "BaseRemoteObject cannot be assigned from BaseType.", "BaseType"); + } + + return base.BuildProxyType(); + } + + #endregion + + #region Protected Methods + + /// + /// Implements constructors for the proxy class. + /// + /// + /// The to use. + /// + protected override void ImplementConstructors(TypeBuilder builder) + { + MethodAttributes attributes = MethodAttributes.Public | + MethodAttributes.HideBySig | MethodAttributes.SpecialName | + MethodAttributes.RTSpecialName; + ConstructorBuilder cb = builder.DefineConstructor(attributes, + CallingConventions.Standard, + new Type[1] { TargetType }); + + ILGenerator il = cb.GetILGenerator(); + + GenerateRemoteObjectLifetimeInitialization(il); + + PushProxy(il); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Stfld, targetInstance); + + il.Emit(OpCodes.Ret); + } + + /// + /// Generate initialization code for 's lifetime properties. + /// + /// ILGenerator + protected void GenerateRemoteObjectLifetimeInitialization(ILGenerator il) + { + // Set IsInfinite Property + if (this.lifetime.Infinite) + { + PushProxy(il); + il.Emit(OpCodes.Ldc_I4_1); // true + il.Emit(OpCodes.Call, BaseRemoteObject_IsInfinite); + } + else + { + // Set InitialLeaseTime Property + if (this.lifetime.InitialLeaseTime != TimeSpan.Zero) + { + PushProxy(il); + il.Emit(OpCodes.Ldc_I8, this.lifetime.InitialLeaseTime.Ticks); + il.Emit(OpCodes.Call, TimeSpan_FromTicks); + il.Emit(OpCodes.Call, BaseRemoteObject_InitialLeaseTime); + } + + // Set RenewOnCallTime Property + if (this.lifetime.RenewOnCallTime != TimeSpan.Zero) + { + PushProxy(il); + il.Emit(OpCodes.Ldc_I8, this.lifetime.RenewOnCallTime.Ticks); + il.Emit(OpCodes.Call, TimeSpan_FromTicks); + il.Emit(OpCodes.Call, BaseRemoteObject_RenewOnCallTime); + } + + // Set SponsorshipTimeout Property + if (this.lifetime.SponsorshipTimeout != TimeSpan.Zero) + { + PushProxy(il); + il.Emit(OpCodes.Ldc_I8, this.lifetime.SponsorshipTimeout.Ticks); + il.Emit(OpCodes.Call, TimeSpan_FromTicks); + il.Emit(OpCodes.Call, BaseRemoteObject_SponsorshipTimeout); + } + } + } + + #endregion + } +} diff --git a/src/Spring/Spring.Services/ServiceModel/Activation/ServiceHostFactory.cs b/src/Spring/Spring.Services/ServiceModel/Activation/ServiceHostFactory.cs new file mode 100644 index 00000000..0e369b7e --- /dev/null +++ b/src/Spring/Spring.Services/ServiceModel/Activation/ServiceHostFactory.cs @@ -0,0 +1,78 @@ +#if NET_3_0 +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ServiceModel; +using System.ServiceModel.Activation; + +using Spring.Util; + +#endregion + +namespace Spring.ServiceModel.Activation +{ + /// + /// Factory that provides instances of + /// to host objects created by Spring's IoC container. + /// + /// Bruno Baia + /// $Id: ServiceHostFactory.cs,v 1.2 2007/05/18 21:04:37 bbaia Exp $ + public class ServiceHostFactory : ServiceHostFactoryBase + { + /// + /// Creates a for + /// a specified Spring-managed object with a specific base address. + /// + /// + /// A reference to a Spring-managed object 'contextName:objectName'. + /// + /// + /// The of type that contains + /// the base addresses for the service hosted. + /// + /// + /// A for the Spring-managed object. + /// + /// + /// If the Service attribute in the ServiceHost directive was not provided. + /// + public override ServiceHostBase CreateServiceHost(string reference, Uri[] baseAddresses) + { + if (StringUtils.IsNullOrEmpty(reference)) + { + throw new ArgumentException("The service name was not provided in the ServiceHost directive.", "Service"); + } + + string[] refSplitted = reference.Split(':'); + if (refSplitted.Length == 2) + { + return new ServiceHost(refSplitted[0], refSplitted[1], baseAddresses); + } + else + { + return new ServiceHost(reference, baseAddresses); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Services/ServiceModel/ServiceExporter.cs b/src/Spring/Spring.Services/ServiceModel/ServiceExporter.cs new file mode 100644 index 00000000..bf3449dd --- /dev/null +++ b/src/Spring/Spring.Services/ServiceModel/ServiceExporter.cs @@ -0,0 +1,222 @@ +#if NET_3_0 +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using Spring.Util; +using Spring.Context; +using Spring.Core.TypeResolution; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Support; +using Spring.ServiceModel.Support; +using Spring.Context.Support; +using Spring.Proxy; + +#endregion + +namespace Spring.ServiceModel +{ + /// + /// Registers an object type on the server as a WCF service. + /// + /// Bruno Baia + /// $Id: ServiceExporter.cs,v 1.2 2007/09/21 14:26:26 bbaia Exp $ + public class ServiceExporter : IApplicationContextAware, IObjectFactoryAware, IInitializingObject, IDisposable + { + #region Logging + + private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(ServiceExporter)); + + #endregion + + #region Fields + + private string targetName; + private string[] _contracts; + private IList _typeAttributes = new ArrayList(); + private IDictionary _memberAttributes = new Hashtable(); + + private IApplicationContext applicationContext; + private AbstractObjectFactory objectFactory; + + private System.ServiceModel.ServiceHost serviceHost; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the class. + /// + public ServiceExporter() + { + } + + #endregion + + #region Properties + + /// + /// Gets or sets the name of the target object definition. + /// + public string TargetName + { + get { return targetName; } + set { targetName = value; } + } + + /// + /// Gets or sets the list of service contracts. + /// + /// + /// If not set, all the interfaces implemented or inherited + /// by the target type will be used. + /// + /// The interfaces. + public string[] Contracts + { + get { return _contracts; } + set { _contracts = value; } + } + + /// + /// Gets or sets a list of custom attributes + /// that should be applied to the WCF service class. + /// + public IList TypeAttributes + { + get { return _typeAttributes; } + set { _typeAttributes = value; } + } + + /// + /// Gets or sets a dictionary of custom attributes + /// that should be applied to the WCF service members. + /// + /// + /// Dictionary key is an expression that members can be matched against. + /// Value is a list of attributes that should be applied + /// to each member that matches expression. + /// + public IDictionary MemberAttributes + { + get { return _memberAttributes; } + set { _memberAttributes = value; } + } + + #endregion + + #region IApplicationContextAware Members + + /// + /// Gets or sets the that this + /// object runs in. + /// + public IApplicationContext ApplicationContext + { + get { return applicationContext; } + set { applicationContext = value; } + } + + #endregion + + #region IObjectFactoryAware Members + + /// + /// Sets object factory to use. + /// + public IObjectFactory ObjectFactory + { + set { objectFactory = (AbstractObjectFactory) value; } + } + + #endregion + + #region IInitializingObject Members + + /// + /// Publish the object + /// + public void AfterPropertiesSet() + { + ValidateConfiguration(); + Export(); + } + + #endregion + + #region IDisposable Members + + /// + /// Closes the service host. + /// + public void Dispose() + { + serviceHost.Close(); + } + + #endregion + + #region Private Methods + + private void ValidateConfiguration() + { + if (TargetName == null) + { + throw new ArgumentException("The TargetName property is required."); + } + } + + private void Export() + { + string contextName = (applicationContext.Name == AbstractApplicationContext.DefaultRootContextName) ? null : applicationContext.Name; + Type targetType = objectFactory.GetType(targetName); + IProxyTypeBuilder builder = new ServiceProxyTypeBuilder(contextName, targetName, targetType); + if (Contracts != null && Contracts.Length > 0) + { + builder.Interfaces = TypeResolutionUtils.ResolveInterfaceArray(Contracts); + } + builder.TypeAttributes = TypeAttributes; + builder.MemberAttributes = MemberAttributes; + + Type serviceType = builder.BuildProxyType(); + + serviceHost = new System.ServiceModel.ServiceHost(serviceType); + serviceHost.Open(); + + #region Instrumentation + + if (LOG.IsDebugEnabled) + { + LOG.Debug(String.Format("The service '{0}' is ready and can now be accessed.", targetName)); + } + + #endregion + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Services/ServiceModel/ServiceHost.cs b/src/Spring/Spring.Services/ServiceModel/ServiceHost.cs new file mode 100644 index 00000000..23224a0b --- /dev/null +++ b/src/Spring/Spring.Services/ServiceModel/ServiceHost.cs @@ -0,0 +1,97 @@ +#if NET_3_0 +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Util; +using Spring.Context; +using Spring.Context.Support; +using Spring.ServiceModel.Support; + +#endregion + +namespace Spring.ServiceModel +{ + /// + /// Provides a host for Spring-managed services. + /// + /// Bruno Baia + /// $Id: ServiceHost.cs,v 1.1 2007/05/18 21:04:37 bbaia Exp $ + public class ServiceHost : System.ServiceModel.ServiceHost + { + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the class. + /// + /// The name of the service within Spring's IoC container. + /// The base addresses for the hosted service. + public ServiceHost(string serviceName, params Uri[] baseAddresses) + : this(null, serviceName, baseAddresses) + { + } + + /// + /// Creates a new instance of the class. + /// + /// The name of the context to use. + /// The name of the service within Spring's IoC container. + /// The base addresses for the hosted service. + public ServiceHost(string contextName, string serviceName, params Uri[] baseAddresses) + : base(CreateServiceType(contextName, serviceName), baseAddresses) + { + } + + private static Type CreateServiceType(string contextName, string serviceName) + { + if (StringUtils.IsNullOrEmpty(serviceName)) + { + throw new ArgumentException("The service name cannot be null or an empty string.", "serviceName"); + } + + IApplicationContext ctx; + if (StringUtils.IsNullOrEmpty(contextName)) + { + ctx = ContextRegistry.GetContext(); + if (ctx == null) + { + throw new ArgumentException("No context registered."); + } + } + else + { + ctx = ContextRegistry.GetContext(contextName); + if (ctx == null) + { + throw new ArgumentException(string.Format("Context '{0}' is not registered.", contextName)); + } + } + Type serviceType = ctx.GetType(serviceName); + + return new ServiceProxyTypeBuilder(contextName, serviceName, serviceType).BuildProxyType(); + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Services/ServiceModel/Support/ServiceProxyTypeBuilder.cs b/src/Spring/Spring.Services/ServiceModel/Support/ServiceProxyTypeBuilder.cs new file mode 100644 index 00000000..5dbd1730 --- /dev/null +++ b/src/Spring/Spring.Services/ServiceModel/Support/ServiceProxyTypeBuilder.cs @@ -0,0 +1,137 @@ +#if NET_3_0 +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Proxy; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.ServiceModel.Support +{ + /// + /// Builds a WCF service type. + /// + /// Bruno Baia + /// $Id: ServiceProxyTypeBuilder.cs,v 1.1 2007/05/18 21:04:37 bbaia Exp $ + public class ServiceProxyTypeBuilder : CompositionProxyTypeBuilder + { + #region Fields + + private static readonly MethodInfo GetObject = + typeof(IObjectFactory).GetMethod("GetObject", new Type[] { typeof(string) }); + + private static readonly MethodInfo GetContext = + typeof(ContextRegistry).GetMethod("GetContext", Type.EmptyTypes); + + private static readonly MethodInfo GetContextByName = + typeof(ContextRegistry).GetMethod("GetContext", new Type[] { typeof(string) }); + + private string contextName; + private string serviceName; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// The name of the context to use. + /// The name of the service within Spring's IoC container. + /// The type of the service. + public ServiceProxyTypeBuilder(string contextName, string serviceName, Type serviceType) + { + this.contextName = contextName; + this.serviceName = serviceName; + + this.Name = (contextName == null) ? serviceName : contextName + "." + serviceName; + this.TargetType = serviceType; + } + + #endregion + + #region Protected Methods + + /// + /// Implements constructors for the proxy class. + /// + /// + /// This implementation generates a constructor + /// that gets instance of the target object using + /// . + /// + /// + /// The builder to use. + /// + protected override void ImplementConstructors(TypeBuilder builder) + { + MethodAttributes attributes = MethodAttributes.Public | + MethodAttributes.HideBySig | MethodAttributes.SpecialName | + MethodAttributes.RTSpecialName; + + ConstructorBuilder cb = builder.DefineConstructor( + attributes, CallingConventions.Standard, Type.EmptyTypes); + + ILGenerator il = cb.GetILGenerator(); + + il.Emit(OpCodes.Ldarg_0); + if (contextName == null) + { + // call ContextRegistry.GetContext() + il.EmitCall(OpCodes.Call, GetContext, null); + } + else + { + // call ContextRegistry.GetContext(contextName) + il.Emit(OpCodes.Ldstr, contextName); + il.EmitCall(OpCodes.Call, GetContextByName, null); + } + il.Emit(OpCodes.Ldstr, serviceName); + il.EmitCall(OpCodes.Callvirt, GetObject, null); + il.Emit(OpCodes.Stfld, targetInstance); + + il.Emit(OpCodes.Ret); + } + + /// + /// Creates an appropriate type builder. + /// + /// The name to use for the proxy type name. + /// The type to extends if provided. + /// The type builder to use. + protected override TypeBuilder CreateTypeBuilder(string name, Type baseType) + { + return DynamicProxyManager.CreateTypeBuilder(name, baseType); + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Services/Spring.Services.2003.csproj b/src/Spring/Spring.Services/Spring.Services.2003.csproj new file mode 100644 index 00000000..9942bbd7 --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.2003.csproj @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Spring.Services.2005.csproj b/src/Spring/Spring.Services/Spring.Services.2005.csproj new file mode 100644 index 00000000..6ae0f84d --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.2005.csproj @@ -0,0 +1,149 @@ + + + Local + 8.0.50727 + 2.0 + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB} + Debug + AnyCPU + + + + + Spring.Services + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + ..\..\..\build\VS.Net.2005\Spring.Services\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + Spring.Services.xml + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\..\build\VS.Net.2005\Spring.Services\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\antlr.runtime.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + System + + + System.Data + + + system.enterpriseservices + + + system.web.services + + + System.XML + + + + + CommonAssemblyInfo.cs + Code + + + + + + + Code + + + Code + + + + + Code + + + + + + + + Code + + + Code + + + Code + + + + Designer + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Services/Spring.Services.2008.csproj b/src/Spring/Spring.Services/Spring.Services.2008.csproj new file mode 100644 index 00000000..e714fe3d --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.2008.csproj @@ -0,0 +1,156 @@ + + + Local + 8.0.50727 + 2.0 + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Debug + AnyCPU + + + + + Spring.Services + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + 3.0 + false + + + ..\..\..\build\VS.Net.2008\Spring.Services\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0;NET_3_0 + Spring.Services.xml + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\..\build\VS.Net.2008\Spring.Services\Release\ + false + 285212672 + false + + + TRACE;NET_2_0;NET_3_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + System + + + System.Data + + + system.enterpriseservices + + + + system.web.services + + + System.XML + + + + + CommonAssemblyInfo.cs + Code + + + + + + + Code + + + Code + + + + + Code + + + + + + + + Code + + + Code + + + + Code + + + Code + + + + Code + + + + Designer + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Common.2005.csproj b/src/Spring/Spring.Services/Spring.Services.WindowsService.Common.2005.csproj new file mode 100644 index 00000000..17f37da2 --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Common.2005.csproj @@ -0,0 +1,263 @@ + + + Local + 8.0.50727 + 2.0 + {B8381347-51A1-4850-828C-3170FA93846C} + Debug + AnyCPU + + + + + Spring.Services.WindowsService.Common + + + JScript + Grid + IE50 + false + Library + Spring.Services + OnBuildSuccess + + + + + + + + + ..\..\..\build\VS.Net.2005\Spring.Services.WindowsService.Common\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + Spring.Services.WindowsService.Common.xml + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\..\build\VS.Net.2005\Spring.Services.WindowsService.Common\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + Spring.Services.WindowsService.Common.xml + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + cscompmgd + + + False + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + System + + + System.Data + + + System.DirectoryServices + + + System.Drawing + + + System.Runtime.Remoting + + + System.Runtime.Serialization.Formatters.Soap + + + System.Web + + + System.Windows.Forms + + + System.XML + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + UserControl + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + ApplicationMonitor.cs + + + + + {4640D873-95DC-4130-98E2-2B931D390C20} + Spring.Threading.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Common.build b/src/Spring/Spring.Services/Spring.Services.WindowsService.Common.build new file mode 100644 index 00000000..b7864879 --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Common.build @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Common.csproj b/src/Spring/Spring.Services/Spring.Services.WindowsService.Common.csproj new file mode 100644 index 00000000..8bb7fa39 --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Common.csproj @@ -0,0 +1,359 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Gui.build b/src/Spring/Spring.Services/Spring.Services.WindowsService.Gui.build new file mode 100644 index 00000000..e50f9ce8 --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Gui.build @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Gui.csproj b/src/Spring/Spring.Services/Spring.Services.WindowsService.Gui.csproj new file mode 100644 index 00000000..901aa215 --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Gui.csproj @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Installer.2005.csproj b/src/Spring/Spring.Services/Spring.Services.WindowsService.Installer.2005.csproj new file mode 100644 index 00000000..791324de --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Installer.2005.csproj @@ -0,0 +1,119 @@ + + + Local + 8.0.50727 + 2.0 + {832EB570-71D3-4847-92CA-56E8225576D7} + Debug + AnyCPU + ..\..\..\icons\spring32-16.ico + + + Spring.Services.WindowsService.Installer + + + JScript + Grid + IE50 + false + Exe + Spring.Services + OnBuildSuccess + Spring.Services.WindowsService.Installer.ServiceInstaller + + + + + + + ..\..\..\build\VS.Net.2005\Spring.Services.WindowsService.Installer\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + Spring.Services.WindowsService.Installer.xml + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\..\build\VS.Net.2005\Spring.Services.WindowsService.Installer\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + System + + + System.Configuration.Install + + + System.Data + + + System.Runtime.Remoting + + + System.ServiceProcess + + + System.XML + + + + + + Code + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {B8381347-51A1-4850-828C-3170FA93846C} + Spring.Services.WindowsService.Common.2005 + + + + + + + copy "$(ProjectDir)WindowsService\Process\service-process-definition.xml" "$(TargetDir)" + + \ No newline at end of file diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Installer.build b/src/Spring/Spring.Services/Spring.Services.WindowsService.Installer.build new file mode 100644 index 00000000..beaa1fab --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Installer.build @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Installer.csproj b/src/Spring/Spring.Services/Spring.Services.WindowsService.Installer.csproj new file mode 100644 index 00000000..be617a8c --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Installer.csproj @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Process.2005.csproj b/src/Spring/Spring.Services/Spring.Services.WindowsService.Process.2005.csproj new file mode 100644 index 00000000..5e323f5d --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Process.2005.csproj @@ -0,0 +1,140 @@ + + + Local + 8.0.50727 + 2.0 + {36790A24-6817-4A43-AFF6-FA9E0FDEC8D3} + Debug + AnyCPU + ..\..\..\icons\spring32-16.ico + + + Spring.Services.WindowsService.Process + + + JScript + Grid + IE50 + false + WinExe + Spring.Services + OnBuildSuccess + + + + + + + + + ..\..\..\build\VS.Net.2005\Spring.Services.WindowsService.Process\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + Spring.Services.WindowsService.Process.xml + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\..\build\VS.Net.2005\Spring.Services.WindowsService.Process\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + System + + + System.Configuration.Install + + + System.Data + + + System.Runtime.Remoting + + + System.ServiceProcess + + + System.XML + + + + + + + + + Component + + + Component + + + Service.cs + + + ServiceInstaller.cs + + + + + {4640D873-95DC-4130-98E2-2B931D390C20} + Spring.Threading.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB} + Spring.Services.2005 + + + {B8381347-51A1-4850-828C-3170FA93846C} + Spring.Services.WindowsService.Common.2005 + + + + + + + copy "$(ProjectDir)WindowsService\Process\service-process-definition.xml" "$(TargetDir)" + + \ No newline at end of file diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Process.build b/src/Spring/Spring.Services/Spring.Services.WindowsService.Process.build new file mode 100644 index 00000000..e5a11f4d --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Process.build @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Spring.Services.WindowsService.Process.csproj b/src/Spring/Spring.Services/Spring.Services.WindowsService.Process.csproj new file mode 100644 index 00000000..836014b7 --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.WindowsService.Process.csproj @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Spring.Services.build b/src/Spring/Spring.Services/Spring.Services.build new file mode 100644 index 00000000..53ffbe1f --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.build @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Services/Spring.Services.xml b/src/Spring/Spring.Services/Spring.Services.xml new file mode 100644 index 00000000..2aa5d087 --- /dev/null +++ b/src/Spring/Spring.Services/Spring.Services.xml @@ -0,0 +1,1275 @@ + + + + Spring.Services + + + + + Factory Object that dynamically implements service interface for web service. + + +

    + This factory object should be used to obtain reference to a web service + that can be safely cast to a service interface, which allows client code to code + against interface, and not directly against the web service. +

    +

    + The WSDL contract needs to conform to WS-I Basic Profiles. +

    +
    + Bruno Baia + Aleksandar Seovic + $Id: WebServiceProxyFactory.cs,v 1.33 2008/02/24 19:10:12 bbaia Exp $ +
    + + + The web service proxy default constructor. + + + + + Creates a new instance of the + class. + + + + + Creates new instance of the web service proxy. + + New instance of the web service proxy. + + + + Initializes factory object. + + + + + Validates the configuration. + + + + + Generates the web service proxy type. + + + + + Gets XML Web Services documents from a Spring resource. + + + + + Gets or sets the base type that web service proxy should inherit. + + + Default is + + + + + Gets or sets the base type that web service proxy should inherit. + + + Default is + + + + + Gets or sets the URI for an + that contains the web service description (WSDL). + + + + + Gets or sets type of the proxy class to wrap. + + + + + Gets or sets service interface that proxy should implement. + + + + + Gets or sets the instance + to use when connecting to a server that requires authentication. + + + + + Gets or sets the url of the proxy server to use for retrieving the WSDL. + + +

    + This only applies when using an as uri. +

    +

    + The default is to use the system proxy setting. +

    +
    +
    + + + Gets or sets the instance + to use when connecting to a proxy server that requires authentication. + + +

    + This only applies when using an as uri. +

    +
    +
    + + + Gets or sets the web service binding name to use for the proxy. + + + + + Gets or sets a list of custom attributes + that should be applied to a proxy class. + + + + + Gets or sets a dictionary of custom attributes + that should be applied to web service members. + + + Dictionary key is an expression that members can be matched against. + Value is a list of attributes that should be applied + to each member that matches expression. + + + + + Returns type of the web service proxy. + + + + + Always returns false. + + + + + Gets or sets the template object definition + that should be used to configure proxy instance. + + + + + Proxy type builder that can be used to create a proxy for + derived classes. + + + + + Creates a new instance of the + class. + + The URI that contains the Web Service meta info (WSDL). + The XML Web Service documents to use to create the proxy. + The name of the Web Service binding to use. + + + + Creates the proxy type. + + The generated proxy class. + + + + Generates the IL instructions that pushes + the target instance on which calls should be delegated to. + + The IL generator to use. + + + + Implements constructors for the proxy class. + + + The to use. + + + + + Search and returns the binding for the specified name. + + + + + Search and returns the url for the specified binding. + + + + + Search and returns the operation that matches the specified method. + + + + + Search and returns the OperationBinding that matches the specified Operation. + + + + + Search and returns the type mapping between method parameters/return value + and the element parts of a literal-use SOAP message. + + + + + Creates a that should be applied to proxy type. + + + + + Creates a or a + that should be applied to proxy method. + + + + + Proxy method builder that can be used to create a proxy method + for web services operation invocation. + + + + + Creates a new instance of the method builder. + + The type builder to use. + + The implementation to use. + + + + + Generates the proxy method. + + The IL generator to use. + The method to proxy. + + The interface definition of the method, if applicable. + + + + + Proxy type builder that can be used to create a proxy for + .Net-generated proxy class that can be safely cast to a service interface. + + + + + Creates a new instance of the + class. + + + + + Gets the mapping of the interface to proxy + into the actual methods on the target type + that does not need to implement that interface. + + +

    + As the proxy type does not implement the interface, + we try to find matching methods. +

    +
    + + The of the target object. + + The interface to implement. + + An interface mapping for the interface to proxy. + +
    + + + Configurable implementation of the interface. + + Bruno Baia + $Id: ConfigurableLifetime.cs,v 1.1 2006/09/13 20:20:16 bbaia Exp $ + + + + Defines lifetime's properties of remote objects that is used by Spring. + + Bruno Baia + $Id: ILifetime.cs,v 1.1 2006/09/13 20:20:16 bbaia Exp $ + + + + Gets or sets a value indicating whether this instance has infinite lifetime. + + + true if this instance has infinite lifetime; otherwise, false. + + + + + Gets the initial lease time. + + The initial lease time. + + + + Gets the amount of time lease + should be extended for on each call to this object. + + The amount of time lease should be + extended for on each call to this object. + + + + Gets the amount of time lease manager + will for this object's sponsors to respond. + + The amount of time lease manager will for this object's + sponsors to respond. + + + + Gets or sets a value indicating whether this instance has infinite lifetime. + + + true if this instance has infinite lifetime; otherwise, false. + + + + + Gets or sets the initial lease time. + + The initial lease time. + + + + Gets or sets the amount of time lease + should be extended for on each call to this object. + + The amount of time lease should be + extended for on each call to this object. + + + + Gets or sets the amount of time lease manager + will for this object's sponsors to respond. + + The amount of time lease manager will for this object's + sponsors to respond. + + + + Factory for creating a reference to a + remote server activated object (SAO). + + + This is useful alternative to adminstrative type registration on + the client when you would like the client to have only + a reference to the interface that an SAO implements and not the + actual SAO implentation. + + Aleksandar Seovic + Mark Pollack + Bruno Baia + $Id: SaoFactoryObject.cs,v 1.7 2006/05/17 17:45:39 bbaia Exp $ + + + + Creates a new instance of the SaoFactoryObject class. + + + + + Callback method called once all factory properties have been set. + + if an error occured + + + + Return the SAO proxy. + + the SAO proxy + + + + The remote service interface. + + + + + The URI of the well known object + + + + + Is the object managed by this factory a singleton or a prototype? + + + + + The type of object to be created. + + + + + Exports specified components as ServicedComponents. + + +

    + This class will create ServicedComponent wrapper for each of the + specified components and register them with the Component Services. +

    +
    + Aleksandar Seovic + $Id: EnterpriseServicesExporter.cs,v 1.6 2007/05/16 10:03:24 oakinger Exp $ +
    + + + Creates new enterprise services exporter. + + + + + Called by Spring container after object is configured in order to initialize it. + + + + + Creates ServicedComponent wrappers for the specified components and registers + them with COM+ Component Services. + + + + + Reads key pair from embedded resource. + + Key pair as a byte array. + + + + Applies custom attributes to generated assembly. + + Dynamic assembly to apply attributes to. + + + + Replaces roles expressed using string with appropriate SecurityRoleAttribute instance. + + + + + Parses string representation of SecurityRoleAttribute. + + Role definition string. + Configured SecurityRoleAttribute instance. + + + + Gets or sets list of components to export. + + + + + Gets or sets COM+ application name. + + + + + Gets or sets application identifier (GUID). Defaults to generated GUID if not specified. + + + + + Gets or sets application activation mode, which can be either Server or Library (default). + + + + + Gets or sets application description. + + + + + Gets or sets access control attribute. + + + + + Gets or sets application queuing attribute. + + + + + Gets or sets application roles. + + + + + Gets or sets name of the generated assembly that will contain serviced components. + + + + + Sets object factory instance. + + + + + Implementation of the custom configuration parser for remoting definitions. + + Bruno Baia + $Id: RemotingNamespaceParser.cs,v 1.3 2007/10/08 14:57:34 bbaia Exp $ + + + + Initializes a new instance of the class. + + + + + Parse the specified element and register any resulting + IObjectDefinitions with the IObjectDefinitionRegistry that is + embedded in the supplied ParserContext. + + The element to be parsed into one or more IObjectDefinitions + The object encapsulating the current state of the parsing + process. + + The primary IObjectDefinition (can be null as explained above) + + + Implementations should return the primary IObjectDefinition + that results from the parse phase if they wish to used nested + inside (for example) a <property> tag. + Implementations may return null if they will not + be used in a nested scenario. + + + + + + Parses remoting definitions. + + Validator XML element. + The name of the object definition. + The parser context. + A remoting object definition. + + + + Parses the RemotingConfigurer definition. + + The element to parse. + The name of the object definition. + The parser context. + RemotingConfigurer object definition. + + + + Parses the SaoFactoryObject definition. + + The element to parse. + The name of the object definition. + The parser context. + SaoFactoryObject object definition. + + + + Parses the CaoFactoryObject definition. + + The element to parse. + The name of the object definition. + The parser context. + CaoFactoryObject object definition. + + + + Parses the RemoteObjectFactory definition. + + The element to parse. + The name of the object definition. + The parser context. + RemoteObjectFactory object definition. + + + + Parses the SaoExporter definition. + + The element to parse. + The name of the object definition. + The parser context. + SaoExporter object definition. + + + + Parses the CaoExporter definition. + + The element to parse. + The name of the object definition. + The parser context. + CaoExporter object definition. + + + + Parses the LifeTime definition. + + + + + Gets the name of the object type for the specified element. + + The element. + The name of the object type. + + + + Convenience class to configure remoting infrastructure from the IoC container. + + Bruno Baia + $Id: RemotingConfigurer.cs,v 1.7 2007/10/08 14:57:34 bbaia Exp $ + + + + Initializes a new instance of the class. + + + + + Modify the application context's internal object factory after its + standard initialization. + + + The object factory used by the application context. + + + + + + Gets or sets the name of the remoting configuration file. + + + If filename is or not set, + current AppDomain's configuration file will be used. + + + + + Indicates whether a configuration file is used. + Default value is . + + + If , default remoting configuration will be used. + + + + + Gets or sets if security is enabled. + + + This property is only available since .NET Framework 2.0. + + + + + Return the order value of this object, + where a higher value means greater in terms of sorting. + + + + + This class extends to allow users + to define object lifecycle details by simply setting its properties. + + +

    + Remoting exporters uses this class as a base proxy class + in order to support lifecycle configuration when exporting + a remote object. +

    +
    + Aleksandar Seovic + $Id: BaseRemoteObject.cs,v 1.1 2006/09/13 20:20:16 bbaia Exp $ +
    + + + Initializes a new instance of the class. + + + + + Obtains a lifetime service object to control the lifetime policy for this instance. + + +

    + This method uses property values to configure for this object. +

    +

    + It is very much inspired by Ingo Rammer's example in Chapter 6 of "Advanced .NET Remoting", + but is modified slightly to make it more "Spring-friendly". Basically, the main difference is that + instead of pulling lease configuration from the .NET config file, this implementation relies + on Spring DI to get appropriate values injected, which makes it much more flexible. +

    +
    + + An object of type used to control the + lifetime policy for this instance. This is the current lifetime service object for + this instance if one exists; otherwise, a new lifetime service object initialized to the value + of the property. + + The immediate caller does not have infrastructure permission. +
    + + + Gets or sets a value indicating whether this instance has infinite lifetime. + + + if this instance has infinite lifetime; + otherwise, . + + + + + Gets or sets the initial lease time. + + The initial lease time. + + + + Gets or sets the amount of time lease should be + extended for on each call to this object. + + The amount of time lease should be + extended for on each call to this object. + + + + Gets or sets the amount of time lease manager will for this object's + sponsors to respond. + + The amount of time lease manager will for this object's + sponsors to respond. + + + + Builds a proxy type based on to wrap a target object + that is intended to be remotable. + + Bruno Baia + $Id: RemoteObjectProxyTypeBuilder.cs,v 1.3 2006/11/12 01:37:47 bbaia Exp $ + + + + Creates a new instance of the + class. + + + The lifetime properties to be applied to the target object. + + + + + Creates a remotable proxy type based on . + + The generated proxy class. + + If the is not + an instance of . + + + + + Implements constructors for the proxy class. + + + The to use. + + + + + Generate initialization code for 's lifetime properties. + + ILGenerator + + + + Interface for a CAO based object factory. + + +

    + Provides a well known location for clients to retrieve + references to CAO references. +

    +
    + Aleksandar Seovic + Mark Pollack + Bruno Baia + $Id: ICaoRemoteFactory.cs,v 1.1 2006/09/13 20:20:16 bbaia Exp $ +
    + + + Returns the CAO proxy. + + The remote object. + + + + Returns the CAO proxy using the + argument list to call the constructor. + + + The matching of arguments to call the constructor is done + by type. The alternative ways, by index and by constructor + name are not supported. + + Constructor + arguments used to create the object. + The remote object. + + + + Publishes an instance of an object under + a given url as a Server Activated Object (SAO). + + + Object can be exported either as SingleCall or Singleton. + + Aleksandar Seovic + Mark Pollack + Bruno Baia + $Id: SaoExporter.cs,v 1.18 2007/07/31 18:18:19 bbaia Exp $ + + + + Creates a new instance of the class. + + + + + Publish the object + + + + + Disconnect the remote object from the registered remoting channels. + + + + + Gets or sets the name of the target object definition. + + + + + Gets or sets the name of the remote application. + + + + + Gets or sets the name of the exported remote service. + + The name that will be used in the URI to refer to this service. + This will be of the form, tcp://host:port/ServiceName or + tcp://host:port/ApplicationName/ServiceName + + + + + + Gets or sets the list of interfaces whose methods should be exported. + + + The default value of this property is all the interfaces + implemented or inherited by the target type. + + The interfaces to export. + + + + Gets or sets the that this + object runs in. + + + + + Sets object factory to use. + + + + + Builds a proxy type based on to wrap a target object + that is intended to be remotable. + + + The wrapped target object is retrieved by name from the IoC container. + + + + + Creates a new instance of the + class. + + + The name of the target object definition. + + + The name of the application context that this object runs in. + + + The lifetime properties to be applied to the target object. + + + + + Generates the IL instructions that pushes + the target instance on which calls should be delegated to. + + The IL generator to use. + + + + Implements constructors for the proxy class. + + + The to use. + + + + + Deaclares a field that holds the target object instance. + + + The builder to use for code generation. + + + + + Factory for creating a reference to a + client activated object (CAO). + + Aleksandar Seovic + Mark Pollack + Bruno Baia + $Id: CaoFactoryObject.cs,v 1.4 2006/09/13 20:20:14 bbaia Exp $ + + + + Creates a new instance of the class. + + + + + Callback method called once all factory properties have been set. + + if an error occured + + + + Return the CAO proxy. + + the CAO proxy + + + + The remote target name to activate. + + + + + The Uri of the remote type. + + + + + Argument list used to call the CAO constructor. + + + + + Always return false. + + + + + The type of object to be created. + + + + + Registers an object type on the server + as a Client Activated Object (CAO). + + Aleksandar Seovic + Mark Pollack + Bruno Baia + $Id: CaoExporter.cs,v 1.12 2007/11/07 19:03:09 bbaia Exp $ + + + + Creates a new instance of the class. + + + + + Publish the object + + + + + Disconnect the remote object from the registered remoting channels. + + + + + Gets or sets the name of the target object definition. + + + + + Gets or sets the list of interfaces whose methods should be exported. + + + The default value of this property is all the interfaces + implemented or inherited by the target type. + + The interfaces to export. + + + + Gets or sets the that this + object runs in. + + + + + Sets object factory to use. + + + + + This class extends to allow CAOs + to be disconnect from the client. + + + + + Create a new instance of the RemoteFactory. + + + + + Returns the CAO proxy. + + The remote object. + + + + Returns the CAO proxy using the + argument list to call the constructor. + + + The matching of arguments to call the constructor is done + by type. The alternative ways, by index and by constructor + name are not supported. + + Constructor + arguments used to create the object. + The remote object. + + + + Set infinite lifetime. + + + + + Encapsulates information necessary to create ServicedComponent + wrapper around target class. + + +

    + Instances of this class should be used as elements in the Components + list of the class, which will + register them with COM+ Services. +

    +
    + Aleksandar Seovic + $Id: ServicedComponentExporter.cs,v 1.9 2007/07/31 18:18:19 bbaia Exp $ +
    + + + Creates a new instance of the + class. + + + + + Validate configuration. + + + + + Creates ServicedComponent wrapper around target class. + + Dynamic module builder to use + Object factory to get target from. + + + + Gets or sets name of the target object that should be exposed as a serviced component. + + + + + Gets or sets the list of interfaces whose methods should be exported. + + + The default value of this property is all the interfaces + implemented or inherited by the target type. + + The interfaces to export. + + + + Gets or sets a list of custom attributes + that should be applied to a proxy class. + + + + + Gets or sets a dictionary of custom attributes + that should be applied to proxy members. + + + Map key is an expression that members can be matched against. Value is a list + of attributes that should be applied to each member that matches expression. + + + + + Set the name of the object in the object factory + that created this object. + + + + + Factory for creating MarshalByRefObject wrapper around target class. + + Bruno Baia + $Id: RemoteObjectFactory.cs,v 1.8 2007/07/31 18:18:19 bbaia Exp $ + + + + Creates a new instance of the MarshalByRefObjectFactory. + + + + + Initializes factory object. + + + + + Creates new instance of the remotable target proxy. + + New instance of the remotable target proxy. + + + + Gets or sets the target object. + + + + + Gets or sets the class or subclass + that the proxy must inherit from. + + + + + Gets or sets the list of interfaces to wrap. + + + The default value of this property is all the interfaces + implemented or inherited by the target type. + + The interfaces to export. + + + + Returns type of the remotable target proxy. + + + + + Always returns false. + + + + + Factory Object that instantiates and configures ServicedComponent. + + +

    + This factory object should be used to instantiate and configure + serviced components created by . +

    +
    + Aleksandar Seovic + $Id: ServicedComponentFactory.cs,v 1.7 2007/05/16 10:03:25 oakinger Exp $ +
    + + + Creates new instance of serviced component factory. + + + + + Returns configured instance of the serviced component. + + Configured instance of the serviced component. + + + + Initializes factory object. + + + + + Creates new instance of serviced component. + + New instance of serviced component. + + + + Gets or sets component name, as registered with COM+ Services. + + + + + Gets or sets name of the remote server that COM+ component is registered with. + + + + + Returns type of serviced component. + + + + + Gets or sets whether serviced component should be treated as singleton. Default is false. + + + + + Gets or sets the template object definition + that should be used to configure proxy instance. + + +
    +
    diff --git a/src/Spring/Spring.Services/Web/Services/WebServiceProxyFactory.cs b/src/Spring/Spring.Services/Web/Services/WebServiceProxyFactory.cs new file mode 100644 index 00000000..e3643fcd --- /dev/null +++ b/src/Spring/Spring.Services/Web/Services/WebServiceProxyFactory.cs @@ -0,0 +1,1178 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using System.Net; +using System.Reflection; +using System.Reflection.Emit; +using System.Web.Services; +using System.Web.Services.Description; +using System.Web.Services.Protocols; +using System.Web.Services.Discovery; + +using Spring.Core; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Proxy; +using Spring.Util; +using Spring.Objects; +using Spring.Core.IO; +using Spring.Expressions; + +#endregion + +namespace Spring.Web.Services +{ + /// + /// Factory Object that dynamically implements service interface for web service. + /// + /// + ///

    + /// This factory object should be used to obtain reference to a web service + /// that can be safely cast to a service interface, which allows client code to code + /// against interface, and not directly against the web service. + ///

    + ///

    + /// The WSDL contract needs to conform to WS-I Basic Profiles. + ///

    + ///
    + /// Bruno Baia + /// Aleksandar Seovic + /// $Id: WebServiceProxyFactory.cs,v 1.33 2008/02/24 19:10:12 bbaia Exp $ + public class WebServiceProxyFactory : IConfigurableFactoryObject, IInitializingObject + { + #region Logging + + private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(WebServiceProxyFactory)); + + #endregion + + #region Fields + + private IObjectDefinition _productTemplate; + private Type _webServiceProxyBaseType = typeof(SoapHttpClientProtocol); + private IResource _serviceUri; + private Type _proxyType; + private Type _serviceInterface; + private NetworkCredential _credential; + private string _proxyUrl; + private NetworkCredential _proxyCredential; + private string _bindingName; + private IList _typeAttributes = new ArrayList(); + private IDictionary _memberAttributes = new Hashtable(); + + /// + /// The web service proxy default constructor. + /// + protected ConstructorInfo proxyConstructor; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public WebServiceProxyFactory() + { + } + + #endregion + + #region Properties + + /// + /// Gets or sets the base type that web service proxy should inherit. + /// + /// + /// Default is + /// + [Obsolete("This property is obsolete, it has been replaced by WebServiceProxyBaseType")] + public Type ClientProtocolType + { + get { return _webServiceProxyBaseType; } + set { _webServiceProxyBaseType = value; } + } + + /// + /// Gets or sets the base type that web service proxy should inherit. + /// + /// + /// Default is + /// + public Type WebServiceProxyBaseType + { + get { return _webServiceProxyBaseType; } + set { _webServiceProxyBaseType = value; } + } + + /// + /// Gets or sets the URI for an + /// that contains the web service description (WSDL). + /// + public IResource ServiceUri + { + get { return _serviceUri; } + set { _serviceUri = value; } + } + + /// + /// Gets or sets type of the proxy class to wrap. + /// + public Type ProxyType + { + get { return _proxyType; } + set { _proxyType = value; } + } + + /// + /// Gets or sets service interface that proxy should implement. + /// + public Type ServiceInterface + { + get { return _serviceInterface; } + set { _serviceInterface = value; } + } + + /// + /// Gets or sets the instance + /// to use when connecting to a server that requires authentication. + /// + public NetworkCredential Credential + { + get { return _credential; } + set { _credential = value; } + } + + /// + /// Gets or sets the url of the proxy server to use for retrieving the WSDL. + /// + /// + ///

    + /// This only applies when using an as uri. + ///

    + ///

    + /// The default is to use the system proxy setting. + ///

    + ///
    + public string ProxyUrl + { + get { return _proxyUrl; } + set { _proxyUrl = value; } + } + + /// + /// Gets or sets the instance + /// to use when connecting to a proxy server that requires authentication. + /// + /// + ///

    + /// This only applies when using an as uri. + ///

    + ///
    + public NetworkCredential ProxyCredential + { + get { return _proxyCredential; } + set { _proxyCredential = value; } + } + + /// + /// Gets or sets the web service binding name to use for the proxy. + /// + public string BindingName + { + get { return _bindingName; } + set { _bindingName = value; } + } + + /// + /// Gets or sets a list of custom attributes + /// that should be applied to a proxy class. + /// + public IList TypeAttributes + { + get { return _typeAttributes; } + set { _typeAttributes = value; } + } + + /// + /// Gets or sets a dictionary of custom attributes + /// that should be applied to web service members. + /// + /// + /// Dictionary key is an expression that members can be matched against. + /// Value is a list of attributes that should be applied + /// to each member that matches expression. + /// + public IDictionary MemberAttributes + { + get { return _memberAttributes; } + set { _memberAttributes = value; } + } + + #endregion + + #region IConfigurableFactoryObject Members + + /// + /// Returns type of the web service proxy. + /// + public virtual Type ObjectType + { + get { return (proxyConstructor != null ? proxyConstructor.DeclaringType : ServiceInterface); } + } + + /// + /// Creates new instance of the web service proxy. + /// + /// New instance of the web service proxy. + public virtual object GetObject() + { + if (proxyConstructor == null) + { + GenerateProxy(); + } + + return ObjectUtils.InstantiateType(proxyConstructor, ObjectUtils.EmptyObjects); + } + + /// + /// Always returns false. + /// + public virtual bool IsSingleton + { + get { return false; } + } + + /// + /// Gets or sets the template object definition + /// that should be used to configure proxy instance. + /// + public virtual IObjectDefinition ProductTemplate + { + get { return _productTemplate; } + set { _productTemplate = value; } + } + + #endregion + + #region IInitializingObject Members + + /// + /// Initializes factory object. + /// + public virtual void AfterPropertiesSet() + { + ValidateConfiguration(); + } + + #endregion + + #region Protected Methods + + /// + /// Validates the configuration. + /// + protected virtual void ValidateConfiguration() + { + if (ServiceUri == null && ProxyType == null) + { + throw new ArgumentException("ServiceUri or ProxyType property is required."); + } + if (ServiceInterface == null) + { + throw new ArgumentException("The ServiceInterface property is required."); + } + if (!ServiceInterface.IsInterface) + { + throw new ArgumentException("ServiceInterface must be an interface"); + } + if (WebServiceProxyBaseType.IsSealed) + { + throw new ArgumentException("Web service client proxy cannot be created for a sealed class [" + WebServiceProxyBaseType.FullName + "]"); + } + } + + /// + /// Generates the web service proxy type. + /// + protected virtual void GenerateProxy() + { + IProxyTypeBuilder builder; + if (ProxyType != null) + { + // Wrap .NET generated proxy class or another + builder = new WebServiceProxyProxyTypeBuilder(); + builder.TargetType = ProxyType; + } + else + { + // Dynamically generates proxy class from WSDL + builder = new SoapHttpClientProxyTypeBuilder( + ServiceUri, GetWsDocuments(ServiceUri), BindingName); + builder.BaseType = WebServiceProxyBaseType; + } + + builder.Interfaces = ReflectionUtils.ToInterfaceArray(ServiceInterface); + builder.TypeAttributes = TypeAttributes; + builder.MemberAttributes = MemberAttributes; + + Type wrapper = builder.BuildProxyType(); + + proxyConstructor = wrapper.GetConstructor(Type.EmptyTypes); + + #region Instrumentation + + if (LOG.IsDebugEnabled) + { + if (ServiceUri != null) + { + LOG.Debug( + String.Format("Generated client proxy type [{0}] for web service [{1}]", wrapper.FullName, + ServiceUri.Description)); + } + else if (ProxyType != null) + { + LOG.Debug( + String.Format("Generated client proxy type [{0}] for web service based on provided proxy type [{1}]", wrapper.FullName, + ProxyType.FullName)); + } + } + + #endregion + } + + #endregion + + #region Private Methods + + /// + /// Gets XML Web Services documents from a Spring resource. + /// + private DiscoveryClientDocumentCollection GetWsDocuments(IResource resource) + { + try + { + if (ServiceUri is UrlResource || + ServiceUri is FileSystemResource) + { + DiscoveryClientProtocol dcProtocol = new DiscoveryClientProtocol(); + dcProtocol.AllowAutoRedirect = true; + dcProtocol.Credentials = CreateCredentials(); + dcProtocol.Proxy = ConfigureProxy(); + dcProtocol.DiscoverAny(resource.Uri.AbsoluteUri); + dcProtocol.ResolveAll(); + + return dcProtocol.Documents; + } + else + { + DiscoveryClientDocumentCollection dcdc = new DiscoveryClientDocumentCollection(); + dcdc[resource.Description] = ServiceDescription.Read(resource.InputStream); + return dcdc; + } + } + catch (Exception ex) + { + throw new ArgumentException(String.Format("Couldn't retrieve the description of the web service located at '{0}'.", resource.Description), ex); + } + } + + private IWebProxy ConfigureProxy() + { + IWebProxy webProxy = null; + if (ProxyUrl != null) + { + webProxy = new WebProxy(ProxyUrl); + } + if (ProxyCredential != null) + { + if (webProxy == null) + { +#if !NET_2_0 + webProxy = GlobalProxySelection.Select; +#else + webProxy = WebRequest.DefaultWebProxy; +#endif + } + webProxy.Credentials = ProxyCredential; + } + + return webProxy; + } + + private ICredentials CreateCredentials() + { + if (Credential != null) + { + CredentialCache credentialCache = new CredentialCache(); + + Uri wsUri = new Uri(ServiceUri.Uri.AbsoluteUri.Substring(0, ServiceUri.Uri.AbsoluteUri.Length - ServiceUri.Uri.AbsolutePath.Length)); + IEnumerator enumerator = AuthenticationManager.RegisteredModules; + while (enumerator.MoveNext()) + { + credentialCache.Add(wsUri, ((IAuthenticationModule)enumerator.Current).AuthenticationType, Credential); + } + + return credentialCache; + } + else + { + return CredentialCache.DefaultCredentials; + } + } + + #endregion + + #region SoapHttpClientProxyTypeBuilder inner class implementation + + /// + /// Proxy type builder that can be used to create a proxy for + /// derived classes. + /// + private sealed class SoapHttpClientProxyTypeBuilder : AbstractProxyTypeBuilder + { + #region Logging + + private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(SoapHttpClientProxyTypeBuilder)); + + #endregion + + #region Fields + + // Binding/Type related + private IResource serviceUri; + private ServiceDescriptionCollection wsDescriptions; + private XmlSchemaImporter schemaImporter; + private Binding wsBinding; + private string wsUrl; + + // Operation/Method related + private Operation operation; + private OperationBinding operationBinding; + private SoapOperationBinding soapOperationBinding; + private XmlMembersMapping inputMembersMapping; + private XmlMembersMapping outputMembersMapping; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// The URI that contains the Web Service meta info (WSDL). + /// The XML Web Service documents to use to create the proxy. + /// The name of the Web Service binding to use. + public SoapHttpClientProxyTypeBuilder(IResource serviceUri, + DiscoveryClientDocumentCollection wsDocuments, string bindingName) + { + this.serviceUri = serviceUri; + + Name = "SoapHttpClientProxy"; + ProxyTargetAttributes = false; + + Initialize(wsDocuments, bindingName); + } + + #endregion + + #region IProxyTypeBuilder Members + + /// + /// Creates the proxy type. + /// + /// The generated proxy class. + public override Type BuildProxyType() + { + if (Interfaces == null || Interfaces.Length == 0) + { + throw new ArgumentException( + "Web service client proxy must implement at least one interface."); + } + + TypeBuilder typeBuilder = CreateTypeBuilder(Name, BaseType); + + // apply custom attributes to the proxy type. + ApplyTypeAttributes(typeBuilder, TargetType); + + // create constructors + ImplementConstructors(typeBuilder); + + // implement service interfaces + foreach (Type intf in Interfaces) + { + ImplementInterface(typeBuilder, + new SoapHttpClientProxyMethodBuilder(typeBuilder, this), + intf, TargetType); + } + + return typeBuilder.CreateType(); + } + + #endregion + + #region IProxyTypeGenerator Members + + /// + /// Generates the IL instructions that pushes + /// the target instance on which calls should be delegated to. + /// + /// The IL generator to use. + public override void PushTarget(ILGenerator il) + { + PushProxy(il); + } + + #endregion + + #region Protected Methods + + /// + /// Implements constructors for the proxy class. + /// + /// + /// The to use. + /// + protected override void ImplementConstructors(TypeBuilder builder) + { + MethodAttributes attributes = MethodAttributes.Public | + MethodAttributes.HideBySig | MethodAttributes.SpecialName | + MethodAttributes.RTSpecialName; + ConstructorBuilder cb = builder.DefineConstructor(attributes, + CallingConventions.Standard, Type.EmptyTypes); + + ILGenerator il = cb.GetILGenerator(); + + PushProxy(il); + il.Emit(OpCodes.Call, this.BaseType.GetConstructor(Type.EmptyTypes)); + + // Set Url Property + PushProxy(il); + il.Emit(OpCodes.Ldstr, this.wsUrl); + il.Emit(OpCodes.Call, this.BaseType.GetMethod("set_Url", BindingFlags.Public | BindingFlags.Instance)); + + il.Emit(OpCodes.Ret); + } + + protected override void ApplyMethodAttributes( + MethodBuilder methodBuilder, MethodInfo targetMethod) + { + MoveToMethod(targetMethod); + + base.ApplyMethodAttributes(methodBuilder, targetMethod); + } + + protected override IList GetTypeAttributes(Type type) + { + IList attrs = base.GetTypeAttributes(type); + + // Add the WebServiceBindingAttribute + attrs.Add(CreateWebServiceBindingAttribute(this.wsBinding)); + + return attrs; + } + + protected override IList GetMethodAttributes(MethodInfo method) + { + IList attrs = base.GetMethodAttributes(method); + + // Add the SoapMethodAttribute + attrs.Add(CreateSoapMethodAttribute( + operation, operationBinding, soapOperationBinding, inputMembersMapping, outputMembersMapping)); + + return attrs; + } + +#if NET_2_0 + protected override IList GetMethodReturnTypeAttributes(MethodInfo method) + { + IList attrs = base.GetMethodReturnTypeAttributes(method); + + // Add the XmlElementAttribute if needed + if (operation.Messages.Output != null) + { + if (method.ReturnType != typeof(void)) + { + if (outputMembersMapping.Count > 0) + { + XmlMemberMapping outMemberMapping = outputMembersMapping[0]; + bool useMemberName = (outMemberMapping.MemberName != operation.Name + "Result"); + bool useNamespace = outMemberMapping.Namespace != outputMembersMapping.Namespace; + if (useMemberName || useNamespace) + { + ReflectionUtils.CustomAttributeBuilderBuilder cabb = + new ReflectionUtils.CustomAttributeBuilderBuilder(typeof(XmlElementAttribute)); + if (useMemberName) + { + cabb.AddPropertyValue("ElementName", outMemberMapping.MemberName); + } + if (useNamespace) + { + cabb.AddPropertyValue("Namespace", outMemberMapping.Namespace); + } + + attrs.Add(cabb.Build()); + } + } + } + } + + return attrs; + } +#endif +/* + protected override IList GetMethodParameterAttributes(MethodInfo method, ParameterInfo paramInfo) + { + IList attrs = base.GetMethodParameterAttributes(method, paramInfo); + + // Add the XmlElementAttribute if needed + XmlMemberMapping inMemberMapping = inputMembersMapping[paramInfo.Position]; + if (inMemberMapping.Namespace != inputMembersMapping.Namespace) + { + CustomAttributeBuilderBuilder cabb = + new CustomAttributeBuilderBuilder(typeof(XmlElementAttribute)); + cabb.AddPropertyValue("Namespace", inMemberMapping.Namespace); + + attrs.Add(cabb.Build()); + } + + return attrs; + } +*/ + + #endregion + + #region Private Methods + + private void Initialize(DiscoveryClientDocumentCollection wsDocuments, string bindingName) + { + // Service descriptions + this.wsDescriptions = new ServiceDescriptionCollection(); + XmlSchemas schemas = new XmlSchemas(); + foreach (DictionaryEntry entry in wsDocuments) + { + if (entry.Value is ServiceDescription) + { + this.wsDescriptions.Add((ServiceDescription)entry.Value); + } + if (entry.Value is XmlSchema) + { + schemas.Add((XmlSchema)entry.Value); + } + } + + // XmlSchemaImporter + foreach (ServiceDescription serviceDescription in this.wsDescriptions) + { + foreach (XmlSchema schema in serviceDescription.Types.Schemas) + { + if (schemas[schema.TargetNamespace] == null) + { + schemas.Add(schema); + } + } + } + this.schemaImporter = new XmlSchemaImporter(schemas); + + this.wsBinding = GetWsBinding(this.wsDescriptions, bindingName); + this.wsUrl = GetWsUrl(this.wsDescriptions, this.wsBinding); + } + + /// + /// Search and returns the binding for the specified name. + /// + private Binding GetWsBinding(ServiceDescriptionCollection serviceDescriptions, string bindingName) + { + if (bindingName != null) + { + // Search for a specific binding + foreach (ServiceDescription description in serviceDescriptions) + { + foreach (Binding binding in description.Bindings) + { + if (binding.Name == bindingName) + { + return binding; + } + } + } + throw new ApplicationException(String.Format("Binding '{0}' does not exist in the WSDL document located at '{1}'", bindingName, this.serviceUri.Description)); + } + else + { + // Use the first one + foreach (ServiceDescription description in serviceDescriptions) + { + foreach (Binding binding in description.Bindings) + { + #region Instrumentation + + if (LOG.IsInfoEnabled) + { + LOG.Info(String.Format("The binding '{0}', found in the WSDL document located at '{1}', will be use.", binding.Name, this.serviceUri.Description)); + } + + #endregion + + return binding; + } + } + throw new ApplicationException(String.Format("No bindings exists in the WSDL document located at '{0}'", this.serviceUri.Description)); + } + } + + /// + /// Search and returns the url for the specified binding. + /// + private string GetWsUrl(ServiceDescriptionCollection serviceDescriptions, Binding binding) + { + foreach (ServiceDescription description in serviceDescriptions) + { + foreach (Service service in description.Services) + { + foreach (Port port in service.Ports) + { + if (port.Binding.Name == binding.Name) + { + SoapAddressBinding soapAddress = (SoapAddressBinding)port.Extensions.Find(typeof(SoapAddressBinding)); + return soapAddress.Location; + } + } + } + } + throw new ApplicationException(String.Format("No SoapAddressBinding has been found for binding '{0}' in the WSDL document located at '{1}'.", binding.Name, serviceUri)); + } + + /// + /// Search and returns the operation that matches the specified method. + /// + private Operation GetOperation(ServiceDescriptionCollection descriptions, Binding binding, MethodInfo method) + { + PortType portType = descriptions.GetPortType(binding.Type); + foreach (Operation operation in portType.Operations) + { + if (operation.Name == method.Name) + { + return operation; + } + } + throw new ApplicationException(String.Format("No Operation has been found for the method '{0}' in the WSDL document located at '{1}'.", method.Name, serviceUri.Description)); + } + + /// + /// Search and returns the OperationBinding that matches the specified Operation. + /// + private OperationBinding GetOperationBinding(Operation operation, Binding binding) + { + foreach (OperationBinding operationBinding in binding.Operations) + { + if (operation.IsBoundBy(operationBinding)) + { + return operationBinding; + } + } + throw new ApplicationException(String.Format("No OperationBinding matches the Operation '{0}' in the WSDL document located at '{2}'.", operation.Name, this.serviceUri.Description)); + } + + /// + /// Search and returns the type mapping between method parameters/return value + /// and the element parts of a literal-use SOAP message. + /// + private XmlMembersMapping GetMembersMapping(string messageName, MessagePartCollection messageParts, SoapBodyBinding soapBodyBinding, SoapBindingStyle soapBindingStyle) + { +#if NET_2_0 + if (soapBindingStyle == SoapBindingStyle.Rpc) + { + SoapSchemaMember[] soapSchemaMembers = new SoapSchemaMember[messageParts.Count]; + for (int i = 0; i < messageParts.Count; i++) + { + SoapSchemaMember ssm = new SoapSchemaMember(); + ssm.MemberName = messageParts[i].Name; + ssm.MemberType = messageParts[i].Type; + soapSchemaMembers[i] = ssm; + } + return this.schemaImporter.ImportMembersMapping(messageName, soapBodyBinding.Namespace, soapSchemaMembers); + } + else + { + return this.schemaImporter.ImportMembersMapping(messageParts[0].Element); + } +#else + return this.schemaImporter.ImportMembersMapping(messageParts[0].Element); +#endif + } + + private void MoveToMethod(MethodInfo targetMethod) + { + operation = GetOperation(this.wsDescriptions, this.wsBinding, targetMethod); + operationBinding = GetOperationBinding(operation, this.wsBinding); + soapOperationBinding = (SoapOperationBinding)operationBinding.Extensions.Find(typeof(SoapOperationBinding)); + + string inputMessageName = (!StringUtils.IsNullOrEmpty(operationBinding.Input.Name) && (soapOperationBinding.Style != SoapBindingStyle.Rpc)) ? operationBinding.Input.Name : operation.Name; + SoapBodyBinding inputSoapBodyBinding = (SoapBodyBinding)operationBinding.Input.Extensions.Find(typeof(SoapBodyBinding)); + +#if !NET_2_0 + if (soapOperationBinding.Style == SoapBindingStyle.Rpc && + inputSoapBodyBinding.Use == SoapBindingUse.Literal) + { + throw new NotSupportedException("rpc-literal SOAP messages are not supported on .NET Framework 1.1"); + } +#endif + if (inputSoapBodyBinding.Use != SoapBindingUse.Literal) + { + throw new NotSupportedException("WebServiceProxyFactory only supports document-literal and rpc-literal SOAP messages to conform to WS-I Basic Profiles."); + } + + Message inputMessage = this.wsDescriptions.GetMessage(operation.Messages.Input.Message); + inputMembersMapping = GetMembersMapping(inputMessageName, inputMessage.Parts, inputSoapBodyBinding, soapOperationBinding.Style); + + outputMembersMapping = null; + if (operation.Messages.Output != null) + { + string outputMessageName = (!StringUtils.IsNullOrEmpty(operationBinding.Output.Name) && (soapOperationBinding.Style != SoapBindingStyle.Rpc)) ? operationBinding.Output.Name : (operation.Name + "Response"); + SoapBodyBinding outputSoapBodyBinding = (SoapBodyBinding)operationBinding.Output.Extensions.Find(typeof(SoapBodyBinding)); + Message outputMessage = this.wsDescriptions.GetMessage(operation.Messages.Output.Message); + outputMembersMapping = GetMembersMapping(outputMessageName, outputMessage.Parts, outputSoapBodyBinding, soapOperationBinding.Style); + } + } + + /// + /// Creates a that should be applied to proxy type. + /// + private CustomAttributeBuilder CreateWebServiceBindingAttribute(Binding wsBinding) + { + ReflectionUtils.CustomAttributeBuilderBuilder cabb = + new ReflectionUtils.CustomAttributeBuilderBuilder(typeof(WebServiceBindingAttribute)); + + cabb.AddContructorArgument(this.wsBinding.Name); + cabb.AddPropertyValue("Namespace", this.wsBinding.ServiceDescription.TargetNamespace); + + return cabb.Build(); + } + +#if !NET_2_0 + private static readonly PropertyInfo XmlMembersMapping_ElementName = + typeof(XmlMembersMapping).GetProperty("ElementName"); + + private static readonly PropertyInfo XmlMembersMapping_Namespace = + typeof(XmlMembersMapping).GetProperty("Namespace"); +#endif + + /// + /// Creates a or a + /// that should be applied to proxy method. + /// + private static CustomAttributeBuilder CreateSoapMethodAttribute(Operation operation, + OperationBinding operationBinding, SoapOperationBinding soapOperationBinding, + XmlMembersMapping inputMembersMapping, XmlMembersMapping outputMembersMapping) + { + ReflectionUtils.CustomAttributeBuilderBuilder cabb; + +#if !NET_2_0 + // Access XmlMembersMapping.ElementName and .Namespace property using reflection [SPRNET-520] + string inputMembersMappingElementName = XmlMembersMapping_ElementName.GetValue(inputMembersMapping, null) as string; + string inputMembersMappingNamespace = XmlMembersMapping_Namespace.GetValue(inputMembersMapping, null) as string; +#else + string inputMembersMappingElementName = inputMembersMapping.ElementName; + string inputMembersMappingNamespace = inputMembersMapping.Namespace; +#endif + + if (soapOperationBinding.Style == SoapBindingStyle.Rpc) + { + cabb = new ReflectionUtils.CustomAttributeBuilderBuilder(typeof(SoapRpcMethodAttribute)); + } + else + { + cabb = new ReflectionUtils.CustomAttributeBuilderBuilder(typeof(SoapDocumentMethodAttribute)); + cabb.AddPropertyValue("ParameterStyle", SoapParameterStyle.Wrapped); + } + + cabb.AddContructorArgument(soapOperationBinding.SoapAction); + +#if NET_2_0 + cabb.AddPropertyValue("Use", SoapBindingUse.Literal); +#else + if (soapOperationBinding.Style != SoapBindingStyle.Rpc) + { + cabb.AddPropertyValue("Use", SoapBindingUse.Literal); + } +#endif + + if (inputMembersMappingElementName.Length > 0 && + inputMembersMappingElementName != operation.Name) + { + cabb.AddPropertyValue("RequestElementName", inputMembersMappingElementName); + } + + if (inputMembersMappingNamespace != null) + { + cabb.AddPropertyValue("RequestNamespace", inputMembersMappingNamespace); + } + + if (outputMembersMapping == null) + { + cabb.AddPropertyValue("OneWay", true); + } + else + { +#if !NET_2_0 + // Access XmlMembersMapping.ElementName and .Namespace property using reflection [SPRNET-520] + string outputMembersMappingElementName = XmlMembersMapping_ElementName.GetValue(outputMembersMapping, null) as string; + string outputMembersMappingNamespace = XmlMembersMapping_Namespace.GetValue(outputMembersMapping, null) as string; +#else + string outputMembersMappingElementName = outputMembersMapping.ElementName; + string outputMembersMappingNamespace = outputMembersMapping.Namespace; +#endif + if (outputMembersMappingElementName.Length > 0 && + outputMembersMappingElementName != (operation.Name + "Response")) + { + cabb.AddPropertyValue("ResponseElementName", outputMembersMappingElementName); + } + + if (outputMembersMappingNamespace != null) + { + cabb.AddPropertyValue("ResponseNamespace", outputMembersMappingNamespace); + } + } + + return cabb.Build(); + } + + #endregion + + #region SoapHttpClientProxyMethodBuilder inner class implementation + + /// + /// Proxy method builder that can be used to create a proxy method + /// for web services operation invocation. + /// + private sealed class SoapHttpClientProxyMethodBuilder : AbstractProxyMethodBuilder + { + #region Fields + + private static readonly MethodInfo Invoke = + typeof(SoapHttpClientProtocol).GetMethod("Invoke", BindingFlags.NonPublic | BindingFlags.Instance); + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the method builder. + /// + /// The type builder to use. + /// + /// The implementation to use. + /// + public SoapHttpClientProxyMethodBuilder( + TypeBuilder typeBuilder, IProxyTypeGenerator proxyGenerator) + : base(typeBuilder, proxyGenerator, false) + { + } + + #endregion + + #region Protected Methods + + /// + /// Generates the proxy method. + /// + /// The IL generator to use. + /// The method to proxy. + /// + /// The interface definition of the method, if applicable. + /// + protected override void GenerateMethod( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + // In Parameters + ArrayList inParams = new ArrayList(); + // Ref or Out Parameters + ArrayList refOutParams = new ArrayList(); + + foreach (ParameterInfo paramInfo in interfaceMethod.GetParameters()) + { + if (paramInfo.IsRetval || paramInfo.IsOut) + { + refOutParams.Add(paramInfo); + } + else + { + inParams.Add(paramInfo); + } + } + + proxyGenerator.PushTarget(il); + + // Parameter #1 + il.Emit(OpCodes.Ldstr, interfaceMethod.Name); + + // Parameter #2 + LocalBuilder parameters = il.DeclareLocal(typeof(Object[])); + il.Emit(OpCodes.Ldc_I4, inParams.Count); + il.Emit(OpCodes.Newarr, typeof(Object)); + il.Emit(OpCodes.Stloc, parameters); + + int paramIndex = 0; + foreach (ParameterInfo paramInfo in inParams) + { + il.Emit(OpCodes.Ldloc, parameters); + il.Emit(OpCodes.Ldc_I4, paramIndex); + il.Emit(OpCodes.Ldarg, paramInfo.Position + 1); + if (paramInfo.ParameterType.IsValueType) + { + il.Emit(OpCodes.Box, paramInfo.ParameterType); + } + il.Emit(OpCodes.Stelem_Ref); + + paramIndex++; + } + + il.Emit(OpCodes.Ldloc, parameters); + + // Call Invoke method and save result + LocalBuilder results = il.DeclareLocal(typeof(Object[])); + il.EmitCall(OpCodes.Callvirt, Invoke, null); + il.Emit(OpCodes.Stloc, results); + + int resultIndex = (interfaceMethod.ReturnType == typeof(void) ? 0 : 1); + foreach (ParameterInfo paramInfo in refOutParams) + { + il.Emit(OpCodes.Ldarg, paramInfo.Position + 1); + il.Emit(OpCodes.Ldloc, results); + + // Cast / Unbox the return value + il.Emit(OpCodes.Ldc_I4, resultIndex); + il.Emit(OpCodes.Ldelem_Ref); + + Type elementType = paramInfo.ParameterType.GetElementType(); + if (elementType.IsValueType) + { + il.Emit(OpCodes.Unbox, elementType); + il.Emit(OpCodes.Ldobj, elementType); + il.Emit(OpCodes.Stobj, elementType); + } + else + { + il.Emit(OpCodes.Castclass, elementType); + il.Emit(OpCodes.Stind_Ref); + } + + resultIndex++; + } + + if (interfaceMethod.ReturnType != typeof(void)) + { + il.Emit(OpCodes.Ldloc, results); + + // Cast / Unbox the return value + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ldelem_Ref); + if (interfaceMethod.ReturnType.IsValueType) + { + il.Emit(OpCodes.Unbox, interfaceMethod.ReturnType); + il.Emit(OpCodes.Ldobj, interfaceMethod.ReturnType); + } + else + { + il.Emit(OpCodes.Castclass, interfaceMethod.ReturnType); + } + } + } + + #endregion + } + + #endregion + } + + #endregion + + #region WebServiceProxyProxyTypeBuilder inner class implementation + + /// + /// Proxy type builder that can be used to create a proxy for + /// .Net-generated proxy class that can be safely cast to a service interface. + /// + private sealed class WebServiceProxyProxyTypeBuilder : InheritanceProxyTypeBuilder + { + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public WebServiceProxyProxyTypeBuilder() + { + this.Name = "WebServiceProxyProxy"; + } + + #endregion + + #region Protected Methods + + /// + /// Gets the mapping of the interface to proxy + /// into the actual methods on the target type + /// that does not need to implement that interface. + /// + /// + ///

    + /// As the proxy type does not implement the interface, + /// we try to find matching methods. + ///

    + ///
    + /// + /// The of the target object. + /// + /// The interface to implement. + /// + /// An interface mapping for the interface to proxy. + /// + protected override InterfaceMapping GetInterfaceMapping( + Type targetType, Type intf) + { + InterfaceMapping mapping; + + mapping.TargetType = targetType; + mapping.InterfaceType = intf; + mapping.InterfaceMethods = intf.GetMethods(); + mapping.TargetMethods = ReflectionUtils.GetMatchingMethods( + targetType, mapping.InterfaceMethods, true); + + return mapping; + } + + #endregion + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Testing.NUnit/AssemblyInfo.cs b/src/Spring/Spring.Testing.NUnit/AssemblyInfo.cs new file mode 100644 index 00000000..074c740a --- /dev/null +++ b/src/Spring/Spring.Testing.NUnit/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring.Net NUnit support")] +[assembly: AssemblyDescription("Interfaces and classes that provide NUnit integration in Spring.Net")] \ No newline at end of file diff --git a/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.2003.csproj b/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.2003.csproj new file mode 100644 index 00000000..c551dce0 --- /dev/null +++ b/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.2003.csproj @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.2005.csproj b/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.2005.csproj new file mode 100644 index 00000000..3deea103 --- /dev/null +++ b/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.2005.csproj @@ -0,0 +1,73 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8} + Library + Properties + Spring + Spring.Testing.NUnit + + + true + full + false + ..\..\..\build\VS.Net.2005\Spring.Testing.NUnit\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + Spring.Testing.NUnit.xml + + + pdbonly + true + ..\..\..\build\VS.Net.2005\Spring.Testing.NUnit\Release\ + TRACE;NET_2_0 + prompt + 4 + 1587, 1591, 219, 162, 618 + Spring.Testing.NUnit.xml + + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + + + + + CommonAssemblyInfo.cs + + + + + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.build b/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.build new file mode 100644 index 00000000..e1f14052 --- /dev/null +++ b/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.build @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.xml b/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.xml new file mode 100644 index 00000000..8cc6569d --- /dev/null +++ b/src/Spring/Spring.Testing.NUnit/Spring.Testing.NUnit.xml @@ -0,0 +1,503 @@ + + + + Spring.Testing.NUnit + + + + + Convenient superclass for tests depending on a Spring context. + The test instance itself is populated by Dependency Injection. + + + +

    Really for integration testing, not unit testing. + You should not normally use the Spring container + for unit tests: simply populate your objects in plain NUnit tests!

    + +

    This supports two modes of populating the test: +

      +
    • Via Property Dependency Injection. Simply express dependencies on objects + in the test fixture, and they will be satisfied by autowiring by type.
    • +
    • Via Field Injection. Declare protected variables of the required type + which match named beans in the context. This is autowire by name, + rather than type. This approach is based on an approach originated by + Ara Abrahmian. Property Dependency Injection is the default: set the + "populateProtectedVariables" property to true in the constructor to switch + on Field Injection.
    • +

    + +

    This class will normally cache contexts based on a context key: + normally the config locations String array describing the Spring resource + descriptors making up the context. Unless the SetDirty() method + is called by a test, the context will not be reloaded, even across different + subclasses of this test. This is particularly beneficial if your context is + slow to construct, for example if you are using Hibernate and the time taken + to load the mappings is an issue.

    + +

    If you don't want this behavior, you can override the ContextKey + property, most likely to return the test class. In conjunction with this you would + probably override the GetContext method, which by default loads + the locations specified by the ConfigLocations property.

    + +

    WARNING: When doing integration tests from within VS.NET, only use + assembly resource URLs. Else, you may see misleading failures when changing + context locations.

    +
    + + Rod Johnson + Rob Harrop + Rick Evans + Aleksandar Seovic (.NET) + $Id: AbstractDependencyInjectionSpringContextTests.cs,v 1.4 2008/03/14 12:02:45 oakinger Exp $ +
    + + + Superclass for NUnit test cases using a Spring context. + + +

    Maintains a cache of contexts by key. This has significant performance + benefit if initializing the context would take time. While initializing a + Spring context itself is very quick, some objects in a context, such as + a LocalSessionFactoryObject for working with NHibernate, may take time to + initialize. Hence it often makes sense to do that initializing once.

    +

    Normally you won't extend this class directly but rather extend one + of its subclasses.

    +
    + Rod Johnson + Aleksandar Seovic (.NET) + $Id: AbstractSpringContextTests.cs,v 1.2 2007/08/20 18:38:11 markpollack Exp $ +
    + + + Map of context keys returned by subclasses of this class, to + Spring contexts. + + + + + Logger available to subclasses. + + + + + Default constructor for AbstractSpringContextTests. + + + + + Set custom locations dirty. This will cause them to be reloaded + from the cache before the next test case is executed. + + + Call this method only if you change the state of a singleton + object, potentially affecting future tests. + + Locations + + + + Returns true if context for the specified + is cached. + + Context key to check. + + true if context for the specified + is cached, + false otherwise. + + + + + Converts context key to string. + + + Subclasses can override this to return a string representation of + their contextKey for use in logging. + + Context key to convert. + + String representation of the specified . Null if + contextKey is null. + + + + + Caches application context. + + Key to use. + Context to cache. + + + + Returns cached context if present, or loads it if not. + + Context key. + Spring application context associated with the specified key. + + + + Loads application context from the specified resource locations. + + Resources to load object definitions from. + + + + Loads application context based on user-defined key. + + + Unless overriden by the user, this method will alway throw + a . + + User-defined key. + + + + Application context this test will run against. + + + + + Holds names of the fields that should be used for field injection. + + + + + Default constructor for AbstractDependencyInjectionSpringContextTests. + + + + + Called to say that the "applicationContext" instance variable is dirty and + should be reloaded. We need to do this if a test has modified the context + (for example, by replacing an object definition). + + + + + Test setup method. + + + + + Inject dependencies into 'this' instance (that is, this test instance). + + +

    The default implementation populates protected variables if the + property is set, else + uses autowiring if autowiring is switched on (which it is by default).

    +

    You can certainly override this method if you want to totally control + how dependencies are injected into 'this' instance.

    +
    +
    + + + Loads application context from the specified resource locations. + + Resources to load object definitions from. + + + + Retrieves the names of the fields that should be used for field injection. + + + + + Injects protected fields using Field Injection. + + + + + Called right before a field is being injected + + + + + Subclasses can override this method in order to + add custom test setup logic. + + + + + Test teardown method. + + + + + Subclasses can override this method in order to + add custom test teardown logic. + + + + + Gets or sets a flag specifying whether to populate protected + variables of this test case. + + + A flag specifying whether to populate protected variables of this test case. + Default is false. + + + + + Gets or sets the autowire mode for test properties set by Dependency Injection. + + + The autowire mode for test properties set by Dependency Injection. + The default is . + + + + + Gets or sets a flag specifying whether or not dependency checking + should be performed for test properties set by Dependency Injection. + + +

    A flag specifying whether or not dependency checking + should be performed for test properties set by Dependency Injection.

    +

    The default is true, meaning that tests cannot be run + unless all properties are populated.

    +
    +
    + + + Gets the current number of context load attempts. + + + + + Gets a key for this context. Usually based on config locations, but + a subclass overriding buildContext() might want to return its class. + + + + + Subclasses must implement this property to return the locations of their + config files. A plain path will be treated as a file system location. + + An array of config locations + + + + Subclass of AbstractTransactionalSpringContextTests that adds some convenience + functionality for ADO.NET access. Expects a IDbProvider object + to be defined in the Spring application context. + + + This class exposes a AdoTemplate and provides an easy way to delete from the + database in a new transaction. + + Rod Johnson + Juergen Hoeller + Mark Pollack (.NET) + $Id: AbstractTransactionalDbProviderSpringContextTests.cs,v 1.3 2007/08/03 19:51:16 markpollack Exp $ + + + + Convenient superclass for tests that should occur in a transaction, but normally + will roll the transaction back on the completion of each test. + + +

    This is useful in a range of circumstances, allowing the following benefits:

    +
      +
    • Ability to delete or insert any data in the database, without affecting other tests
    • +
    • Providing a transactional context for any code requiring a transaction
    • +
    • Ability to write anything to the database without any need to clean up.
    • +
    + +

    This class is typically very fast, compared to traditional setup/teardown scripts.

    + +

    If data should be left in the database, call the SetComplete() + method in each test. The "DefaultRollback" property, which defaults to "true", + determines whether transactions will complete by default.

    + +

    It is even possible to end the transaction early; for example, to verify lazy + loading behavior of an O/R mapping tool. (This is a valuable away to avoid + unexpected errors when testing a web UI, for example.) Simply call the + endTransaction() method. Execution will then occur without a + transactional context.

    + +

    The StartNewTransaction() method may be called after a call to + EndTransaction() if you wish to create a new transaction, quite + independent of the old transaction. The new transaction's default fate will be to + roll back, unless setComplete() is called again during the scope of the + new transaction. Any number of transactions may be created and ended in this way. + The final transaction will automatically be rolled back when the test case is + torn down.

    + +

    Transactional behavior requires a single object in the context implementing the + IPlatformTransactionManager interface. This will be set by the superclass's + Dependency Injection mechanism. If using the superclass's Field Injection mechanism, + the implementation should be named "transactionManager". This mechanism allows the + use of this superclass even when there's more than one transaction manager in the context.

    + +

    This superclass can also be used without transaction management, if no + IPlatformTransactionManager object is found in the context provided. Be careful about + using this mode, as it allows the potential to permanently modify data. + This mode is available only if dependency checking is turned off in + the AbstractDependencyInjectionSpringContextTests superclass. The non-transactional + capability is provided to enable use of the same subclass in different environments.

    + +
    + + Rod Johnson + Juergen Hoeller + Rick Evans + Mark Pollack (.NET) + $Id: AbstractTransactionalSpringContextTests.cs,v 1.3 2007/06/01 14:05:57 oakinger Exp $ +
    + + + The transaction manager to use + + + + + Should we roll back by default? + + + + + Should we commit the current transaction? + + + + + Number of transactions started + + + + + Default transaction definition is used. + Subclasses can change this to cause different behaviour. + + + + + TransactionStatus for this test. Typical subclasses won't need to use it. + + + + + Initializes a new instance of the class. + + + + + Prevents the transaction. + + + + + Creates a transaction + + + + + Callback method called before transaction is setup. + + + + + Callback method called after transaction is setup. + + + + + rollback the transaction. + + + + + Callback before rolling back the transaction. + + + + + Callback after rolling back the transaction. + + + + + Set the complete flag.. + + + + + Ends the transaction. + + + + + Starts the new transaction. + + + + + Sets the transaction manager to use. + + + + + Sets the default rollback flag. + + + + + Set the to be used + + + Defaults to + + + + + Holds the that this base class manages + + + + + Did this test delete any tables? If so, we forbid transaction completion, + and only allow rollback. + + + + + Initializes a new instance of the class. + + + + + Convenient method to delete all rows from these tables. + Calling this method will make avoidance of rollback by calling + SetComplete() impossible. + + + + + + Overridden to prevent the transaction committing if a number of tables have been + cleared, as a defensive measure against accidental permanent wiping of a database. + + + + + Counts the rows in given table. + + Name of the table to count rows in. + The number of rows in the table + + + + Sets the DbProvider, via Dependency Injection. + + The IDbProvider. + + + + Gets or sets the AdoTemplate that this base class manages. + + The ADO template. + +
    +
    diff --git a/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractDependencyInjectionSpringContextTests.cs b/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractDependencyInjectionSpringContextTests.cs new file mode 100644 index 00000000..a0d2bc04 --- /dev/null +++ b/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractDependencyInjectionSpringContextTests.cs @@ -0,0 +1,370 @@ +#region License + +/* +/// Copyright 2002-2006 the original author or authors. +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Reflection; + +using NUnit.Framework; + +using Spring.Context; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +namespace Spring.Testing.NUnit +{ + /// + /// Convenient superclass for tests depending on a Spring context. + /// The test instance itself is populated by Dependency Injection. + /// + /// + /// + ///

    Really for integration testing, not unit testing. + /// You should not normally use the Spring container + /// for unit tests: simply populate your objects in plain NUnit tests!

    + /// + ///

    This supports two modes of populating the test: + ///

      + ///
    • Via Property Dependency Injection. Simply express dependencies on objects + /// in the test fixture, and they will be satisfied by autowiring by type.
    • + ///
    • Via Field Injection. Declare protected variables of the required type + /// which match named beans in the context. This is autowire by name, + /// rather than type. This approach is based on an approach originated by + /// Ara Abrahmian. Property Dependency Injection is the default: set the + /// "populateProtectedVariables" property to true in the constructor to switch + /// on Field Injection.
    • + ///

    + /// + ///

    This class will normally cache contexts based on a context key: + /// normally the config locations String array describing the Spring resource + /// descriptors making up the context. Unless the SetDirty() method + /// is called by a test, the context will not be reloaded, even across different + /// subclasses of this test. This is particularly beneficial if your context is + /// slow to construct, for example if you are using Hibernate and the time taken + /// to load the mappings is an issue.

    + /// + ///

    If you don't want this behavior, you can override the ContextKey + /// property, most likely to return the test class. In conjunction with this you would + /// probably override the GetContext method, which by default loads + /// the locations specified by the ConfigLocations property.

    + /// + ///

    WARNING: When doing integration tests from within VS.NET, only use + /// assembly resource URLs. Else, you may see misleading failures when changing + /// context locations.

    + ///
    + /// + /// Rod Johnson + /// Rob Harrop + /// Rick Evans + /// Aleksandar Seovic (.NET) + /// $Id: AbstractDependencyInjectionSpringContextTests.cs,v 1.4 2008/03/14 12:02:45 oakinger Exp $ + public abstract class AbstractDependencyInjectionSpringContextTests : AbstractSpringContextTests + { + private bool populateProtectedVariables = false; + private AutoWiringMode autowireMode = AutoWiringMode.ByType; + private bool dependencyCheck = true; + + /// + /// Application context this test will run against. + /// + protected IConfigurableApplicationContext applicationContext; + + /// + /// Holds names of the fields that should be used for field injection. + /// + protected string[] managedVariableNames; + private int loadCount = 0; + + /// + /// Default constructor for AbstractDependencyInjectionSpringContextTests. + /// + public AbstractDependencyInjectionSpringContextTests() + {} + + /// + /// Gets or sets a flag specifying whether to populate protected + /// variables of this test case. + /// + /// + /// A flag specifying whether to populate protected variables of this test case. + /// Default is false. + /// + public bool PopulateProtectedVariables + { + get { return populateProtectedVariables; } + set { populateProtectedVariables = value; } + } + + /// + /// Gets or sets the autowire mode for test properties set by Dependency Injection. + /// + /// + /// The autowire mode for test properties set by Dependency Injection. + /// The default is . + /// + public AutoWiringMode AutowireMode + { + get { return autowireMode; } + set { autowireMode = value; } + } + + /// + /// Gets or sets a flag specifying whether or not dependency checking + /// should be performed for test properties set by Dependency Injection. + /// + /// + ///

    A flag specifying whether or not dependency checking + /// should be performed for test properties set by Dependency Injection.

    + ///

    The default is true, meaning that tests cannot be run + /// unless all properties are populated.

    + ///
    + public bool DependencyCheck + { + get { return dependencyCheck; } + set { dependencyCheck = value; } + } + + /// + /// Gets the current number of context load attempts. + /// + public int LoadCount + { + get { return loadCount; } + } + + /// + /// Called to say that the "applicationContext" instance variable is dirty and + /// should be reloaded. We need to do this if a test has modified the context + /// (for example, by replacing an object definition). + /// + public void SetDirty() + { + SetDirty(ConfigLocations); + } + + /// + /// Test setup method. + /// + [SetUp] + protected virtual void SetUp() + { + this.applicationContext = GetContext(ContextKey); + InjectDependencies(); + try + { + OnSetUp(); + } + catch (Exception ex) + { + logger.Error("Setup error", ex); + throw; + } + } + + /// + /// Inject dependencies into 'this' instance (that is, this test instance). + /// + /// + ///

    The default implementation populates protected variables if the + /// property is set, else + /// uses autowiring if autowiring is switched on (which it is by default).

    + ///

    You can certainly override this method if you want to totally control + /// how dependencies are injected into 'this' instance.

    + ///
    + protected virtual void InjectDependencies() + { + if (PopulateProtectedVariables) + { + if (this.managedVariableNames == null) + { + InitManagedVariableNames(); + } + InjectProtectedVariables(); + } + else if (AutowireMode != AutoWiringMode.No) + { + IConfigurableListableObjectFactory factory = this.applicationContext.ObjectFactory; + ((AbstractObjectFactory) factory).IgnoreDependencyType(typeof(AutoWiringMode)); + factory.AutowireObjectProperties(this, AutowireMode, DependencyCheck); + } + } + + /// + /// Gets a key for this context. Usually based on config locations, but + /// a subclass overriding buildContext() might want to return its class. + /// + protected virtual object ContextKey + { + get { return ConfigLocations; } + } + + /// + /// Loads application context from the specified resource locations. + /// + /// Resources to load object definitions from. + protected override IConfigurableApplicationContext LoadContextLocations(string[] locations) + { + ++this.loadCount; + return base.LoadContextLocations(locations); + } + + /// + /// Retrieves the names of the fields that should be used for field injection. + /// + protected virtual void InitManagedVariableNames() + { + ArrayList managedVarNames = new ArrayList(); + Type type = GetType(); + + do + { + FieldInfo[] fields = + type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Instance); + if (logger.IsDebugEnabled) + { + logger.Debug("Found " + fields.Length + " fields on " + type); + } + + for (int i = 0; i < fields.Length; i++) + { + FieldInfo field = fields[i]; + if (logger.IsDebugEnabled) + { + logger.Debug("Candidate field: " + field); + } + if (IsProtectedInstanceField(field)) + { + object oldValue = field.GetValue(this); + if (oldValue == null) + { + managedVarNames.Add(field.Name); + if (logger.IsDebugEnabled) + { + logger.Debug("Added managed variable '" + field.Name + "'"); + } + } + else + { + if (logger.IsDebugEnabled) + { + logger.Debug("Rejected managed variable '" + field.Name + "'"); + } + } + } + } + type = type.BaseType; + } while (type != typeof (AbstractDependencyInjectionSpringContextTests)); + + this.managedVariableNames = (string[]) managedVarNames.ToArray(typeof (string)); + } + + private static bool IsProtectedInstanceField(FieldInfo field) + { + return field.IsFamily; + } + + /// + /// Injects protected fields using Field Injection. + /// + protected virtual void InjectProtectedVariables() + { + for (int i = 0; i < this.managedVariableNames.Length; i++) + { + string fieldName = this.managedVariableNames[i]; + Object obj = null; + try + { + FieldInfo field = GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + if (field != null) + { + BeforeProtectedVariableInjection(field); + obj = this.applicationContext.GetObject(fieldName, field.FieldType); + field.SetValue(this, obj); + if (logger.IsDebugEnabled) + { + logger.Debug("Populated field: " + field); + } + } + else + { + if (logger.IsWarnEnabled) + { + logger.Warn("No field with name '" + fieldName + "'"); + } + } + } + catch (NoSuchObjectDefinitionException) + { + if (logger.IsWarnEnabled) + { + logger.Warn("No object definition with name '" + fieldName + "'"); + } + } + } + } + + /// + /// Called right before a field is being injected + /// + protected virtual void BeforeProtectedVariableInjection(FieldInfo fieldInfo) + { + + } + + /// + /// Subclasses can override this method in order to + /// add custom test setup logic. + /// + protected virtual void OnSetUp() + {} + + /// + /// Test teardown method. + /// + [TearDown] + protected void TearDown() + { + try + { + OnTearDown(); + } + catch (Exception ex) + { + logger.Error("OnTearDown error", ex); + } + } + + /// + /// Subclasses can override this method in order to + /// add custom test teardown logic. + /// + protected virtual void OnTearDown() + {} + + + /// + /// Subclasses must implement this property to return the locations of their + /// config files. A plain path will be treated as a file system location. + /// + /// An array of config locations + protected abstract string[] ConfigLocations { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractSpringContextTests.cs b/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractSpringContextTests.cs new file mode 100644 index 00000000..7476c857 --- /dev/null +++ b/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractSpringContextTests.cs @@ -0,0 +1,191 @@ +/* + * Copyright 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections; +using Common.Logging; +using Spring.Context; +using Spring.Context.Support; +using Spring.Util; + +namespace Spring.Testing.NUnit +{ + /// + /// Superclass for NUnit test cases using a Spring context. + /// + /// + ///

    Maintains a cache of contexts by key. This has significant performance + /// benefit if initializing the context would take time. While initializing a + /// Spring context itself is very quick, some objects in a context, such as + /// a LocalSessionFactoryObject for working with NHibernate, may take time to + /// initialize. Hence it often makes sense to do that initializing once.

    + ///

    Normally you won't extend this class directly but rather extend one + /// of its subclasses.

    + ///
    + /// Rod Johnson + /// Aleksandar Seovic (.NET) + /// $Id: AbstractSpringContextTests.cs,v 1.2 2007/08/20 18:38:11 markpollack Exp $ + public abstract class AbstractSpringContextTests + { + /// + /// Map of context keys returned by subclasses of this class, to + /// Spring contexts. + /// + private static IDictionary contextKeyToContextMap = new Hashtable(); + + /// + /// Logger available to subclasses. + /// + protected ILog logger; + + /// + /// Default constructor for AbstractSpringContextTests. + /// + public AbstractSpringContextTests() + { + logger = LogManager.GetLogger(GetType()); + } + + /// + /// Set custom locations dirty. This will cause them to be reloaded + /// from the cache before the next test case is executed. + /// + /// + /// Call this method only if you change the state of a singleton + /// object, potentially affecting future tests. + /// + /// Locations + protected void SetDirty(string[] locations) + { + String keyString = ContextKeyString(locations); + IConfigurableApplicationContext ctx = + (IConfigurableApplicationContext) contextKeyToContextMap[keyString]; + contextKeyToContextMap.Remove(keyString); + + if (ctx != null) + { + ctx.Dispose(); + } + } + + /// + /// Returns true if context for the specified + /// is cached. + /// + /// Context key to check. + /// + /// true if context for the specified + /// is cached, + /// false otherwise. + /// + protected bool HasCachedContext(object contextKey) + { + return contextKeyToContextMap.Contains(contextKey); + } + + /// + /// Converts context key to string. + /// + /// + /// Subclasses can override this to return a string representation of + /// their contextKey for use in logging. + /// + /// Context key to convert. + /// + /// String representation of the specified . Null if + /// contextKey is null. + /// + protected virtual string ContextKeyString(object contextKey) + { + if (contextKey == null) + { + return null; + } + if (contextKey is string[]) + { + return StringUtils.ArrayToCommaDelimitedString((string[]) contextKey); + } + else + { + return contextKey.ToString(); + } + } + + /// + /// Caches application context. + /// + /// Key to use. + /// Context to cache. + public void AddContext(object key, IConfigurableApplicationContext context) + { + AssertUtils.ArgumentNotNull(context, "context", "ApplicationContext must not be null"); + contextKeyToContextMap[ContextKeyString(key)] = context; + } + + /// + /// Returns cached context if present, or loads it if not. + /// + /// Context key. + /// Spring application context associated with the specified key. + protected IConfigurableApplicationContext GetContext(object key) + { + string keyString = ContextKeyString(key); + IConfigurableApplicationContext ctx = + (IConfigurableApplicationContext) contextKeyToContextMap[keyString]; + if (ctx == null) + { + if (key is string[]) + { + ctx = LoadContextLocations((string[]) key); + } + else + { + ctx = LoadContext(key); + } + contextKeyToContextMap[keyString] = ctx; + } + return ctx; + } + + + /// + /// Loads application context from the specified resource locations. + /// + /// Resources to load object definitions from. + protected virtual IConfigurableApplicationContext LoadContextLocations(string[] locations) + { + if (logger.IsInfoEnabled) + { + logger.Info("Loading config for: " + StringUtils.ArrayToCommaDelimitedString(locations)); + } + return new XmlApplicationContext(locations); + } + + /// + /// Loads application context based on user-defined key. + /// + /// + /// Unless overriden by the user, this method will alway throw + /// a . + /// + /// User-defined key. + protected virtual IConfigurableApplicationContext LoadContext(object key) + { + throw new NotSupportedException("Subclasses may override this"); + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractTransactionalDbProviderSpringContextTests.cs b/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractTransactionalDbProviderSpringContextTests.cs new file mode 100644 index 00000000..5bc0b7c0 --- /dev/null +++ b/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractTransactionalDbProviderSpringContextTests.cs @@ -0,0 +1,106 @@ +using System; +using System.Data; +using Spring.Data; +using Spring.Data.Common; +using Spring.Data.Core; + +namespace Spring.Testing.NUnit +{ + /// + /// Subclass of AbstractTransactionalSpringContextTests that adds some convenience + /// functionality for ADO.NET access. Expects a IDbProvider object + /// to be defined in the Spring application context. + /// + /// + /// This class exposes a AdoTemplate and provides an easy way to delete from the + /// database in a new transaction. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Mark Pollack (.NET) + /// $Id: AbstractTransactionalDbProviderSpringContextTests.cs,v 1.3 2007/08/03 19:51:16 markpollack Exp $ + public abstract class AbstractTransactionalDbProviderSpringContextTests : AbstractTransactionalSpringContextTests + { + /// + /// Holds the that this base class manages + /// + protected AdoTemplate adoTemplate; + + /// + /// Did this test delete any tables? If so, we forbid transaction completion, + /// and only allow rollback. + /// + private bool zappedTables; + + /// + /// Initializes a new instance of the class. + /// + public AbstractTransactionalDbProviderSpringContextTests() + { + } + + /// + /// Sets the DbProvider, via Dependency Injection. + /// + /// The IDbProvider. + public IDbProvider DbProvider + { + set { adoTemplate = new AdoTemplate(value); } + } + + /// + /// Gets or sets the AdoTemplate that this base class manages. + /// + /// The ADO template. + public AdoTemplate AdoTemplate + { + get { return adoTemplate; } + } + + /// + /// Convenient method to delete all rows from these tables. + /// Calling this method will make avoidance of rollback by calling + /// SetComplete() impossible. + /// + /// + protected void DeleteFromTables(String[] names) + { + for (int i = 0; i < names.Length; i++) + { + int rowCount = this.adoTemplate.ExecuteNonQuery(CommandType.Text, "DELETE FROM " + names[i]); + if (logger.IsInfoEnabled) + { + logger.Info("Deleted " + rowCount + " rows from table " + names[i]); + } + } + this.zappedTables = true; + } + + /// + /// Overridden to prevent the transaction committing if a number of tables have been + /// cleared, as a defensive measure against accidental permanent wiping of a database. + /// + protected override void SetComplete() + { + if (this.zappedTables) + { + throw new InvalidOperationException("Cannot set complete after deleting tables"); + } + base.SetComplete(); + } + + /// + /// Counts the rows in given table. + /// + /// Name of the table to count rows in. + /// The number of rows in the table + protected int CountRowsInTable(String tableName) + { + return (int) adoTemplate.ExecuteScalar(CommandType.Text, "SELECT COUNT(0) FROM " + tableName); + + } + + //TODO ExecuteScript... + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractTransactionalSpringContextTests.cs b/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractTransactionalSpringContextTests.cs new file mode 100644 index 00000000..a5345cc4 --- /dev/null +++ b/src/Spring/Spring.Testing.NUnit/Testing/NUnit/AbstractTransactionalSpringContextTests.cs @@ -0,0 +1,286 @@ +using System; +using Spring.Transaction; +using Spring.Transaction.Support; + +namespace Spring.Testing.NUnit +{ + /// + /// Convenient superclass for tests that should occur in a transaction, but normally + /// will roll the transaction back on the completion of each test. + /// + /// + ///

    This is useful in a range of circumstances, allowing the following benefits:

    + ///
      + ///
    • Ability to delete or insert any data in the database, without affecting other tests
    • + ///
    • Providing a transactional context for any code requiring a transaction
    • + ///
    • Ability to write anything to the database without any need to clean up.
    • + ///
    + /// + ///

    This class is typically very fast, compared to traditional setup/teardown scripts.

    + /// + ///

    If data should be left in the database, call the SetComplete() + /// method in each test. The "DefaultRollback" property, which defaults to "true", + /// determines whether transactions will complete by default.

    + /// + ///

    It is even possible to end the transaction early; for example, to verify lazy + /// loading behavior of an O/R mapping tool. (This is a valuable away to avoid + /// unexpected errors when testing a web UI, for example.) Simply call the + /// endTransaction() method. Execution will then occur without a + /// transactional context.

    + /// + ///

    The StartNewTransaction() method may be called after a call to + /// EndTransaction() if you wish to create a new transaction, quite + /// independent of the old transaction. The new transaction's default fate will be to + /// roll back, unless setComplete() is called again during the scope of the + /// new transaction. Any number of transactions may be created and ended in this way. + /// The final transaction will automatically be rolled back when the test case is + /// torn down.

    + /// + ///

    Transactional behavior requires a single object in the context implementing the + /// IPlatformTransactionManager interface. This will be set by the superclass's + /// Dependency Injection mechanism. If using the superclass's Field Injection mechanism, + /// the implementation should be named "transactionManager". This mechanism allows the + /// use of this superclass even when there's more than one transaction manager in the context.

    + /// + ///

    This superclass can also be used without transaction management, if no + /// IPlatformTransactionManager object is found in the context provided. Be careful about + /// using this mode, as it allows the potential to permanently modify data. + /// This mode is available only if dependency checking is turned off in + /// the AbstractDependencyInjectionSpringContextTests superclass. The non-transactional + /// capability is provided to enable use of the same subclass in different environments.

    + /// + ///
    + /// + /// Rod Johnson + /// Juergen Hoeller + /// Rick Evans + /// Mark Pollack (.NET) + /// $Id: AbstractTransactionalSpringContextTests.cs,v 1.3 2007/06/01 14:05:57 oakinger Exp $ + public abstract class AbstractTransactionalSpringContextTests : AbstractDependencyInjectionSpringContextTests + { + /// + /// The transaction manager to use + /// + protected IPlatformTransactionManager transactionManager; + + /// + /// Should we roll back by default? + /// + private bool defaultRollback = true; + + /// + /// Should we commit the current transaction? + /// + private bool complete = false; + + /// + /// Number of transactions started + /// + private int transactionsStarted = 0; + + /// + /// Default transaction definition is used. + /// Subclasses can change this to cause different behaviour. + /// + private ITransactionDefinition transactionDefinition = new DefaultTransactionDefinition(); + + /// + /// TransactionStatus for this test. Typical subclasses won't need to use it. + /// + protected ITransactionStatus transactionStatus; + + + /// + /// Initializes a new instance of the class. + /// + public AbstractTransactionalSpringContextTests() + { + } + + /// + /// Sets the transaction manager to use. + /// + public IPlatformTransactionManager TransactionManager + { + set { transactionManager = value; } + } + + /// + /// Sets the default rollback flag. + /// + public bool DefaultRollback + { + set { defaultRollback = value; } + } + + /// + /// Set the to be used + /// + /// + /// Defaults to + /// + protected ITransactionDefinition TransactionDefinition + { + set { transactionDefinition = value; } + } + + /// + /// Prevents the transaction. + /// + protected virtual void PreventTransaction() + { + this.transactionDefinition = null; + } + + + /// + /// Creates a transaction + /// + protected override void OnSetUp() + { + this.complete = !this.defaultRollback; + + if (this.transactionManager == null) + { + logger.Info("No transaction manager set: test will NOT run within a transaction"); + } + else if (this.transactionDefinition == null) + { + logger.Info("No transaction definition set: test will NOT run within a transaction"); + } + else + { + OnSetUpBeforeTransaction(); + StartNewTransaction(); + try + { + OnSetUpInTransaction(); + } + catch (Exception) + { + EndTransaction(); + throw; + } + } + } + + + /// + /// Callback method called before transaction is setup. + /// + protected virtual void OnSetUpBeforeTransaction() + { + } + + /// + /// Callback method called after transaction is setup. + /// + protected virtual void OnSetUpInTransaction() + { + } + + /// + /// rollback the transaction. + /// + protected override void OnTearDown() + { + // Call onTearDownInTransaction and end transaction if the transaction is still active. + if (this.transactionStatus != null && !this.transactionStatus.Completed) + { + try + { + OnTearDownInTransaction(); + } + finally + { + EndTransaction(); + } + } + // Call onTearDownAfterTransaction if there was at least one transaction, + // even if it has been completed early through an endTransaction() call. + if (this.transactionsStarted > 0) + { + OnTearDownAfterTransaction(); + } + } + + /// + /// Callback before rolling back the transaction. + /// + protected virtual void OnTearDownInTransaction() + { + } + + /// + /// Callback after rolling back the transaction. + /// + protected virtual void OnTearDownAfterTransaction() + { + } + + /// + /// Set the complete flag.. + /// + protected virtual void SetComplete() + { + if (this.transactionManager == null) + { + throw new InvalidOperationException("No transaction manager set"); + } + this.complete = true; + } + + /// + /// Ends the transaction. + /// + protected virtual void EndTransaction() + { + if (this.transactionStatus != null) + { + try + { + if (!this.complete) + { + this.transactionManager.Rollback(this.transactionStatus); + logger.Info("Rolled back transaction after test execution"); + } + else + { + this.transactionManager.Commit(this.transactionStatus); + logger.Info("Committed transaction after test execution"); + } + } + finally + { + this.transactionStatus = null; + } + } + } + + /// + /// Starts the new transaction. + /// + protected void StartNewTransaction() + { + if (this.transactionStatus != null) + { + throw new InvalidOperationException("Cannot start new transaction without ending existing transaction: " + + "Invoke endTransaction() before startNewTransaction()"); + } + if (this.transactionManager == null) + { + throw new InvalidOperationException("No transaction manager set"); + } + + this.transactionStatus = this.transactionManager.GetTransaction(this.transactionDefinition); + ++this.transactionsStarted; + this.complete = !this.defaultRollback; + + if (logger.IsInfoEnabled) + { + logger.Info("Began transaction (" + this.transactionsStarted + "): transaction manager [" + + this.transactionManager + "]; default rollback = " + this.defaultRollback); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web.Extensions/AssemblyInfo.cs b/src/Spring/Spring.Web.Extensions/AssemblyInfo.cs new file mode 100644 index 00000000..9dc950a5 --- /dev/null +++ b/src/Spring/Spring.Web.Extensions/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Spring.Net ASP.NET AJAX 1.0 support")] +[assembly: AssemblyDescription("Interfaces and classes that provide ASP.NET AJAX 1.0 support in Spring.Net")] \ No newline at end of file diff --git a/src/Spring/Spring.Web.Extensions/Spring.Web.Extensions.2005.csproj b/src/Spring/Spring.Web.Extensions/Spring.Web.Extensions.2005.csproj new file mode 100644 index 00000000..46fb6f94 --- /dev/null +++ b/src/Spring/Spring.Web.Extensions/Spring.Web.Extensions.2005.csproj @@ -0,0 +1,69 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {E5E287D2-EE2C-4C99-87CA-EB27B35ABF7B} + Library + Properties + Spring + Spring.Web.Extensions + + + true + full + false + ..\..\..\build\VS.Net.2005\Spring.Web.Extensions\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + Spring.Web.Extensions.xml + + + pdbonly + true + ..\..\..\build\VS.Net.2005\Spring.Web.Extensions\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\lib\net\2.0\Common.Logging.dll + + + + + False + ..\..\..\lib\Net\2.0\System.Web.Extensions.dll + + + + + + CommonAssemblyInfo.cs + + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {BA4789EB-281A-48EA-8763-28B9F0596A18} + Spring.Web.2005 + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Web.Extensions/Spring.Web.Extensions.build b/src/Spring/Spring.Web.Extensions/Spring.Web.Extensions.build new file mode 100644 index 00000000..f00086cf --- /dev/null +++ b/src/Spring/Spring.Web.Extensions/Spring.Web.Extensions.build @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Web.Extensions/Spring.Web.Extensions.xml b/src/Spring/Spring.Web.Extensions/Spring.Web.Extensions.xml new file mode 100644 index 00000000..65f389c0 --- /dev/null +++ b/src/Spring/Spring.Web.Extensions/Spring.Web.Extensions.xml @@ -0,0 +1,40 @@ + + + + Spring.Web.Extensions + + + + + An implementation that + creates a handler object for either ASP.NET AJAX 1.0 or Spring web services. + + Bruno Baia + Thomas Broyer + $Id: ScriptHandlerFactory.cs,v 1.2 2008/03/27 11:11:22 bbaia Exp $ + + + + Creates a new instance of the class. + + + + + Retrieves an instance of the + implementation for handling web service requests + for both Spring and ASP.NET AJAX 1.0 web services. + + The current HTTP context. + The type of HTTP request (GET or POST). + The url of the web service. + The physical application path for the web service. + The web service handler object. + + + + Enables a factory to reuse an existing handler instance. + + The object to reuse. + + + diff --git a/src/Spring/Spring.Web.Extensions/Web/Script/Services/ScriptHandlerFactory.cs b/src/Spring/Spring.Web.Extensions/Web/Script/Services/ScriptHandlerFactory.cs new file mode 100644 index 00000000..bc3d7cf9 --- /dev/null +++ b/src/Spring/Spring.Web.Extensions/Web/Script/Services/ScriptHandlerFactory.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Web; +using System.Web.Script.Services; + +using Spring.Context; +using Spring.Context.Support; +using Spring.Util; +using Spring.Web.Services; +using Spring.Web.Support; + +#endregion + +namespace Spring.Web.Script.Services +{ + /// + /// An implementation that + /// creates a handler object for either ASP.NET AJAX 1.0 or Spring web services. + /// + /// Bruno Baia + /// Thomas Broyer + /// $Id: ScriptHandlerFactory.cs,v 1.2 2008/03/27 11:11:22 bbaia Exp $ + public class ScriptHandlerFactory : AbstractHandlerFactory, IHttpHandlerFactory + { + private static readonly Type scriptHandlerFactoryType = + typeof(ScriptServiceAttribute).Assembly.GetType("System.Web.Script.Services.ScriptHandlerFactory"); + + private static readonly FieldInfo scriptHandlerFactory_webServiceHandlerFactory = + scriptHandlerFactoryType.GetField("_webServiceHandlerFactory", BindingFlags.NonPublic | BindingFlags.Instance); + + private static readonly Type webServiceDataType = + typeof(ScriptServiceAttribute).Assembly.GetType("System.Web.Script.Services.WebServiceData"); + + private static readonly ConstructorInfo webServiceData_ctor = + webServiceDataType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(Type), typeof(bool) }, null); + + private static readonly MethodInfo webServiceData_GetCacheKey = + webServiceDataType.GetMethod("GetCacheKey", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(string) }, null); + + + private readonly IHttpHandlerFactory scriptHandlerFactory = + (IHttpHandlerFactory)Activator.CreateInstance(scriptHandlerFactoryType, true); + + /// + /// Creates a new instance of the class. + /// + public ScriptHandlerFactory() + : base() + { + scriptHandlerFactory_webServiceHandlerFactory.SetValue( + this.scriptHandlerFactory, + new WebServiceHandlerFactory()); + } + + /// + /// Retrieves an instance of the + /// implementation for handling web service requests + /// for both Spring and ASP.NET AJAX 1.0 web services. + /// + /// The current HTTP context. + /// The type of HTTP request (GET or POST). + /// The url of the web service. + /// The physical application path for the web service. + /// The web service handler object. + public override IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) + { + string filename = VirtualPathUtility.ToAbsolute(context.Request.FilePath); + string cacheKey = (string)webServiceData_GetCacheKey.Invoke( + null, new object[] { VirtualPathUtility.ToAbsolute(context.Request.FilePath) }); + object webServiceData = context.Cache.Get(cacheKey); + if (webServiceData == null) + { + IConfigurableApplicationContext appContext = + WebApplicationContext.GetContext(url) as IConfigurableApplicationContext; + + if (appContext == null) + { + throw new InvalidOperationException( + "Implementations of IApplicationContext must also implement IConfigurableApplicationContext"); + } + + string appRelativeVirtualPath = WebUtils.GetAppRelativePath(url); + NamedObjectDefinition nod = FindWebObjectDefinition(appRelativeVirtualPath, appContext.ObjectFactory); + + if (nod != null) + { + Type serviceType = appContext.GetType(nod.Name); + object[] attrs = serviceType.GetCustomAttributes(typeof(ScriptServiceAttribute), false); + if (attrs.Length > 0) + { + webServiceData = webServiceData_ctor.Invoke(new object[] { serviceType, false }); + context.Cache.Insert(cacheKey, webServiceData); + } + } + } + + return this.scriptHandlerFactory.GetHandler(context, requestType, url, pathTranslated); + } + + /// + /// Enables a factory to reuse an existing handler instance. + /// + /// The object to reuse. + public override void ReleaseHandler(IHttpHandler handler) + { + this.scriptHandlerFactory.ReleaseHandler(handler); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/AssemblyInfo.cs b/src/Spring/Spring.Web/AssemblyInfo.cs new file mode 100644 index 00000000..0b12a9a4 --- /dev/null +++ b/src/Spring/Spring.Web/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; +using System.Web.UI; + +[assembly: AssemblyTitle("Spring.Net Web Application Support")] +[assembly: AssemblyDescription("Interfaces and classes that provide web application support in Spring.Net")] +[assembly: TagPrefix("Spring.Web.UI.Controls", "spring")] +//[assembly: AssemblyKeyFile(@"C:\users\aseovic\projects\OpenSource\Spring.Net\Spring.Net.PrivateKey.keys")] \ No newline at end of file diff --git a/src/Spring/Spring.Web/Caching/AspNetCache.cs b/src/Spring/Spring.Web/Caching/AspNetCache.cs new file mode 100644 index 00000000..911b5c17 --- /dev/null +++ b/src/Spring/Spring.Web/Caching/AspNetCache.cs @@ -0,0 +1,310 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Web; +using System.Web.Caching; +using Common.Logging; +using Spring.Util; + +namespace Spring.Caching +{ + /// + /// An implementation backed by ASP.NET Cache (see ). + /// + /// + /// + /// Because ASP.NET Cache uses strings as cache keys, you need to ensure + /// that the key object type has properly implemented ToString method. + /// + /// + /// Despite the shared underlying , it is possible to use more than + /// one instance of without interfering each other. + /// + /// + /// Aleksandar Seovic + /// Erich Eichinger + /// $Id: AspNetCache.cs,v 1.6 2007/09/14 18:26:26 oakinger Exp $ + public class AspNetCache : AbstractCache + { + #region Internal Abstractions + + /// + /// Abstracts the underlying runtime cache + /// + public interface IRuntimeCache : IEnumerable + { + /// + /// Insert an item into the cache. + /// + void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, + TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback); + + /// + /// Removes an item from the cache. + /// + /// The key of the item to remove + /// The object that has been removed from the cache + object Remove(string key); + + /// + /// Retrieve an item with the specified key from the cache. + /// + /// The key of the item to be retrieved + /// The item, if found. null otherwise + object Get(string key); + } + + /// + /// Actually delegates all calls to the underlying + /// + private class RuntimeCache : IRuntimeCache + { + private readonly Cache _runtimeCache = HttpRuntime.Cache; + + public object Remove(string key) + { + return _runtimeCache.Remove(key); + } + + public void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback ) + { + _runtimeCache.Insert(key, value, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback); + } + + public object Get(string key) + { + return _runtimeCache.Get(key); + } + + public IEnumerator GetEnumerator() + { + return _runtimeCache.GetEnumerator(); + } + } + + #endregion + + #region Fields + + // logger instance for this class + private static readonly ILog Log = LogManager.GetLogger(typeof(AspNetCache)); + // the concrete cache implementation + private readonly IRuntimeCache _cache; + // the (unique!) name of this particular cache instance. + private readonly string _cacheName; + + // hold default values for this cache instance. + private CacheItemPriority _priority = CacheItemPriority.Default; + private bool _slidingExpiration = false; + + #endregion + + /// + /// Initializes a new instance of + /// + public AspNetCache() + :this(new RuntimeCache()) + { + } + + /// + /// Initializes a new instance of + /// with the specified implementation. + /// + /// + public AspNetCache(IRuntimeCache runtimeCache) + { + _cache = runtimeCache; + // noop + _cacheName = typeof(AspNetCache).FullName + "[" + this.GetHashCode() + "]."; + } + + #region Configurable Properties + + /// + /// Gets/Sets a flag, whether to use sliding expiration policy. + /// + public bool SlidingExpiration + { + get { return _slidingExpiration; } + set { _slidingExpiration = value; } + } + + /// + /// Gets/Sets a default priority to be applied to all items inserted into this cache. + /// + public CacheItemPriority Priority + { + get { return _priority; } + set { _priority = value; } + } + + #endregion + + /// + /// Gets a collection of all cache item keys. + /// + public override ICollection Keys + { + get + { + ArrayList keys = new ArrayList(); + + foreach (DictionaryEntry entry in _cache) + { + string key = (string) entry.Key; + if (key.StartsWith(_cacheName)) + { + keys.Add(key.Substring(_cacheName.Length)); + } + } + + return keys; + } + } + + /// + /// Retrieves an item from the cache. + /// + /// + /// Item key. + /// + /// + /// Item for the specified , or null. + /// + public override object Get(object key) + { + return key == null ? null : _cache.Get(GenerateKey(key)); + } + + /// + /// Removes an item from the cache. + /// + /// + /// Item key. + /// + public override void Remove(object key) + { + if (key != null) + { + if (Log.IsDebugEnabled) Log.Debug(string.Format("removing item '{0}' from cache '{1}'", key, this._cacheName)); + _cache.Remove(GenerateKey(key)); + } + } + + /// + /// Inserts an item into the cache. + /// + /// + /// Items inserted using this method have default and default + /// + /// + /// Item key. + /// + /// + /// Item value. + /// + /// + /// Item's time-to-live (TTL). + /// + protected override void DoInsert(object key, object value, TimeSpan timeToLive) + { + this.Insert( key, value, timeToLive, _slidingExpiration, _priority ); + } + + /// + /// Inserts an item into the cache. + /// + /// + /// Item key. + /// + /// + /// Item value. + /// + /// + /// Item's time-to-live. + /// + /// + /// Flag specifying whether the item's time-to-live should be reset + /// when the item is accessed. + /// + public void Insert(object key, object value, TimeSpan timeToLive, bool slidingExpiration) + { + this.Insert(key, value, timeToLive, slidingExpiration, _priority); + } + + /// + /// Inserts an item into the cache. + /// + /// + /// Item key. + /// + /// + /// Item value. + /// + /// + /// Item's time-to-live. + /// + /// + /// Flag specifying whether the item's time-to-live should be reset + /// when the item is accessed. + /// + /// + /// Item priority. + /// + public void Insert(object key, object value, TimeSpan timeToLive, bool slidingExpiration, CacheItemPriority itemPriority) + { + AssertUtils.ArgumentNotNull(key, "key"); + AssertUtils.State( TimeSpan.Zero <= timeToLive, "timeToLive" ); + + if (Log.IsDebugEnabled) Log.Debug(string.Format("adding item '{0}' to cache '{1}'", key, this._cacheName)); + + if (TimeSpan.Zero < timeToLive) + { + if (slidingExpiration) + { + TimeSpan slidingExpirationSpan = timeToLive; + _cache.Insert(GenerateKey(key), value, null, Cache.NoAbsoluteExpiration, slidingExpirationSpan, itemPriority, null); + } + else + { + DateTime absoluteExpiration = DateTime.Now.Add(timeToLive); + _cache.Insert(GenerateKey(key), value, null, absoluteExpiration, Cache.NoSlidingExpiration, itemPriority, null); + } + } + else + { + _cache.Insert(GenerateKey(key), value, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, itemPriority, null); + } + } + + /// + /// Generate a key to be used for the underlying implementation. + /// + /// + /// + public string GenerateKey( object key ) + { + return _cacheName + key; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Context/Support/HttpApplicationConfigurer.cs b/src/Spring/Spring.Web/Context/Support/HttpApplicationConfigurer.cs new file mode 100644 index 00000000..42339d19 --- /dev/null +++ b/src/Spring/Spring.Web/Context/Support/HttpApplicationConfigurer.cs @@ -0,0 +1,153 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Web; +using Common.Logging; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: HttpApplicationConfigurer.cs,v 1.2 2007/07/29 19:39:42 markpollack Exp $ + public class HttpApplicationConfigurer + { + private static readonly ILog Log = LogManager.GetLogger(typeof(HttpApplicationConfigurer)); + + #region ModuleDefinitionsTable class + + private class ModuleDefinitionsTable : Hashtable + { + public void Add(string key, IObjectDefinition value) + { + lock(SyncRoot) + { + base[key] = value; + } + } + + public IObjectDefinition this[string key] + { + get + { + lock (SyncRoot) + { + return (IObjectDefinition) base[key]; + } + } + } + + public override void Add(object key, object value) + { + AssertUtils.AssertArgumentType(key, "key", typeof(string), "Key must be a string"); + AssertUtils.AssertArgumentType(value, "value", typeof(IObjectDefinition), "Key must be a string"); + this.Add((string)key, (IObjectDefinition)value); + } + + public override object this[object key] + { + get + { + return this[(string)key]; + } + } + } + + #endregion + + /// + /// Holds shared application Template + /// + private static volatile IObjectDefinition s_applicationDefinition = null; + /// + /// Holds shared modules Templates + /// + private static readonly ModuleDefinitionsTable s_moduleDefinitions = new ModuleDefinitionsTable(); + + /// + /// Gets or Sets the shared application template + /// + public IObjectDefinition ApplicationTemplate + { + // no need for lock because of "volatile" + get { return s_applicationDefinition; } + set { s_applicationDefinition = value; } + } + + /// + /// Gets the dictionary of shared module templates + /// + /// + /// to synchronize access to the dictionary, use property. + /// + public IDictionary ModuleTemplates + { + get { return s_moduleDefinitions; } + } + + /// + /// Configures the instance and its modules. + /// + /// + /// When called, configures using the instance + /// provided in and the templates available in + /// and . + /// + /// the application context instance to be used for resolving object references. + /// the instance to be configured. + public static void Configure(IConfigurableApplicationContext appContext, HttpApplication app) + { + IObjectDefinition applicationDefinition = s_applicationDefinition; + if (s_applicationDefinition != null) + { + appContext.ObjectFactory.ConfigureObject(app, "ApplicationTemplate", applicationDefinition); + } + + lock(s_moduleDefinitions.SyncRoot) + { + HttpModuleCollection modules = app.Modules; + foreach(DictionaryEntry moduleEntry in s_moduleDefinitions) + { + string moduleName = (string) moduleEntry.Key; + IObjectDefinition od = s_moduleDefinitions[moduleName]; + IHttpModule module = modules[moduleName]; + if (module != null) + { + appContext.ObjectFactory.ConfigureObject(module, moduleName, od); + } + else + { + throw ConfigurationUtils.CreateConfigurationException(string.Format("failed applying module template '{0}' - no matching module found", moduleName)); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Context/Support/WebApplicationContext.cs b/src/Spring/Spring.Web/Context/Support/WebApplicationContext.cs new file mode 100644 index 00000000..ac47dd89 --- /dev/null +++ b/src/Spring/Spring.Web/Context/Support/WebApplicationContext.cs @@ -0,0 +1,377 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Reflection; +using System.Web; +using System.Web.Hosting; +using Common.Logging; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Web application context, taking the context definition files + /// from the file system or from URLs. + /// + /// Treats resource paths as web resources, when using + /// IApplicationContext.GetResource. Resource paths are considered relative + /// to the virtual directory. + /// + /// Note: In case of multiple config locations, later object definitions will + /// override ones defined in earlier loaded files. This can be leveraged to + /// deliberately override certain object definitions via an extra XML file. + /// + /// Aleksandar Seovic + public class WebApplicationContext : AbstractXmlApplicationContext + { + /// + /// The instance for this class. + /// + private static readonly ILog log = LogManager.GetLogger(typeof(WebApplicationContext)); + + // holds construction info for debugging output + private DateTime _constructionTimeStamp; + private string _constructionUrl; + + private readonly string[] _configurationLocations; + + /// + /// Create a new WebApplicationContext, loading the definitions + /// from the given XML resource. + /// + /// Names of configuration resources. + public WebApplicationContext(params string[] configurationLocations) + : this(null, false, null, configurationLocations) + { + } + + /// + /// Create a new WebApplicationContext, loading the definitions + /// from the given XML resource. + /// + /// The application context name. + /// Flag specifying whether to make this context case sensitive or not. + /// Names of configuration resources. + public WebApplicationContext(string name, bool caseSensitive, params string[] configurationLocations) + : this(name, caseSensitive, null, configurationLocations) + { + } + + /// + /// Create a new WebApplicationContext with the given parent, + /// loading the definitions from the given XML resources. + /// + /// The application context name. + /// Flag specifying whether to make this context case sensitive or not. + /// The parent context. + /// Names of configuration resources. + public WebApplicationContext(string name, bool caseSensitive, IApplicationContext parentContext, + params string[] configurationLocations) : base(name, caseSensitive, parentContext) + { + _configurationLocations = configurationLocations; + DefaultResourceProtocol = WebUtils.DEFAULT_RESOURCE_PROTOCOL; + Refresh(); + + // remember creation info for debug output + this._constructionTimeStamp = DateTime.Now; + if (HttpContext.Current != null) + { + this._constructionUrl = HttpContext.Current.Request.RawUrl; + } + if (log.IsDebugEnabled) + { + log.Debug("created instance " + this.ToString()); + } + } + + /// + /// returns detailed instance information for debugging + /// + /// + public override string ToString() + { + //return base.ToString() + " - created on " + _constructionTimeStamp + "\n\ncreated by:" + _constructionUrl + "\n" + _constructionStackTrace.ToString(); + return string.Format("[{0}]:{1}({2})", this.Name, this.GetType().Name, base.GetHashCode().ToString()); + } + + + /// + /// Since the HttpRuntime discards it's configurationsection cache, we maintain our own context cache. + /// Despite it really speeds up context-lookup, since we don't have to go through the whole HttpConfigurationSystem + /// + private static readonly Hashtable s_webContextCache = CollectionsUtil.CreateCaseInsensitiveHashtable(); + + static WebApplicationContext() + { + // register for ContextRegistry.Cleared event - we need to discard our cache in this case + ContextRegistry.Cleared += new EventHandler(OnContextRegistryCleared); + +#if NET_2_0 && !MONO + if (HttpRuntime.AppDomainAppVirtualPath != null) // check if we're within an ASP.NET AppDomain! + { + // ensure HttpRuntime has been fully initialized! + // this is a problem,.if ASP.NET Web Administration Application is used. This app does not fully set up the AppDomain... + HttpRuntime runtime = + (HttpRuntime) + typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); + + bool beforeFirstRequest = false; + lock (runtime) + { + beforeFirstRequest = + (bool) + typeof(HttpRuntime).GetField("_beforeFirstRequest", BindingFlags.Instance | BindingFlags.NonPublic). + GetValue(runtime); + } + log.Debug("BeforeFirstRequest:" + beforeFirstRequest); + if (beforeFirstRequest) + { + try + { + string firstRequestPath = HttpRuntime.AppDomainAppVirtualPath.TrimEnd('/') + "/dummy.context"; + log.Info("Forcing first request " + firstRequestPath); + HttpRuntime.ProcessRequest( + new SimpleWorkerRequest(firstRequestPath, string.Empty, new StringWriter())); + log.Info("Successfully processed first request!"); + } + catch (Exception ex) + { + log.Error("Failed processing first request", ex); + throw; + } + } + } +#endif + } + + /// + /// EventHandler for ContextRegistry.Cleared event. Discards webContextCache. + /// + private static void OnContextRegistryCleared(object sender, EventArgs ev) + { + lock (s_webContextCache) + { + if (log.IsDebugEnabled) + { + log.Debug("received ContextRegistry.Cleared event - clearing webContextCache"); + } + s_webContextCache.Clear(); + } + } + + /// + /// Returns the root context of this web application + /// + public static IApplicationContext GetRootContext() + { + return GetContextInternal( ("" + HttpRuntime.AppDomainAppVirtualPath).TrimEnd('/') + "/dummy.context"); + } + + /// + /// Returns the web application context for the given (absolute!) virtual path + /// + public static IApplicationContext GetContext(string virtualPath) + { + return GetContextInternal(virtualPath); + } + + /// + /// Returns the web application context for the current request's filepath + /// + public static IApplicationContext Current + { + get + { + string requestUrl = HttpContext.Current.Request.FilePath; + return GetContextInternal(requestUrl); + } + } + + private static IApplicationContext GetContextInternal(string virtualPath) + { + string virtualDirectory = WebUtils.GetVirtualDirectory(virtualPath); + string contextName = virtualDirectory; + if ( 0 == string.Compare( contextName , ("" + HttpRuntime.AppDomainAppVirtualPath).TrimEnd('/') + "/", true) ) + { + contextName = DefaultRootContextName; + } + + lock (s_webContextCache) + { + if (log.IsDebugEnabled) + { + log.Debug(string.Format("looking up web context '{0}' in WebContextCache", contextName)); + } + // first lookup in our own cache + IApplicationContext context = (IApplicationContext) s_webContextCache[contextName]; + if (context != null) + { + // found - nothing to do anymore + if (log.IsDebugEnabled) + { + log.Debug( + string.Format("returning WebContextCache hit '{0}' for vpath '{1}' ", context, contextName)); + } + return context; + } + + // lookup ContextRegistry + lock (ContextRegistry.SyncRoot) + { + if (log.IsDebugEnabled) + { + log.Debug(string.Format("looking up web context '{0}' in ContextRegistry", contextName)); + } + + if (ContextRegistry.IsContextRegistered(contextName)) + { + context = ContextRegistry.GetContext(contextName); + } + + if (context == null) + { + // finally ask HttpConfigurationSystem for the requested context + try + { + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + "web context for vpath '{0}' not found. Force creation using filepath '{1}'", + contextName, virtualPath)); + } + + // assure context is resolved to the given virtualDirectory + using (new HttpContextSwitch(virtualDirectory)) + { + context = (IApplicationContext)ConfigurationUtils.GetSection(ContextSectionName); + } + + if (context != null) + { + if (log.IsDebugEnabled) + log.Debug(string.Format("got context '{0}' for vpath '{1}'", context, contextName)); + } + else + { + if (log.IsDebugEnabled) + log.Debug(string.Format("no context defined for vpath '{0}'", contextName)); + } + } + catch (Exception ex) + { + if (log.IsErrorEnabled) + { + log.Error(string.Format("failed creating context '{0}'", contextName), ex); + } +#if NET_1_1 + if (ConfigurationUtils.IsConfigurationException(ex)) + { + if (ex.InnerException != null) + { + throw ex.InnerException; + } + } +#endif + throw; + } + } + } + + // add it to the cache + // Note: use 'contextName' not 'context.Name' here - the same context may be used for different paths! + s_webContextCache.Add(contextName, context); + if (log.IsDebugEnabled) + { + log.Debug( + string.Format("added context '{0}' to WebContextCache for vpath '{1}'", context, contextName)); + } + + if (context != null) + { + // register this context and all ParentContexts by their name - parent contexts may be additionally created by the HttpRuntime + IApplicationContext parentContext = context; + while (parentContext != null) + { + if (!s_webContextCache.ContainsKey(parentContext.Name)) + { + s_webContextCache.Add(parentContext.Name, parentContext); + if (log.IsDebugEnabled) + { + log.Debug( + string.Format("added parent context '{0}' to WebContextCache for vpath '{1}'", + parentContext, parentContext.Name)); + } + } + parentContext = parentContext.ParentContext; + } + } + return context; + } // lock(s_webContextCache) + } + + /// + /// Initializes object definition reader. + /// + /// Reader to initialize. + protected override void InitObjectDefinitionReader(XmlObjectDefinitionReader objectDefinitionReader) + { + NamespaceParserRegistry.RegisterParser(typeof(WebObjectsNamespaceParser)); + } + + /// + /// Return an array of resource locations, referring to the XML object + /// definition files that this context should be built with. + /// + /// an array of resource locations, or null if none + protected override string[] ConfigurationLocations + { + get { return _configurationLocations; } + } + + /// + /// Creates web object factory for this context using parent context's factory as a parent. + /// + /// Web object factory to use. + protected override DefaultListableObjectFactory CreateObjectFactory() + { + string contextPath = this.Name; + if (contextPath == DefaultRootContextName) + { + contextPath = "/"; + } + else + { + contextPath = contextPath + "/"; + } + return new WebObjectFactory(contextPath, this.CaseSensitive, GetInternalParentObjectFactory()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Context/Support/WebContextHandler.cs b/src/Spring/Spring.Web/Context/Support/WebContextHandler.cs new file mode 100644 index 00000000..18eba512 --- /dev/null +++ b/src/Spring/Spring.Web/Context/Support/WebContextHandler.cs @@ -0,0 +1,135 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Reflection; +using System.Web; +using System.Web.Configuration; +using System.Web.Hosting; +using System.Xml; +using Common.Logging; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Creates an instance + /// using context definitions supplied in a custom configuration and + /// configures the with that instance. + /// + /// + /// This class extends ContextHandler with + /// web specific behaviour. It uses WebApplicationContext + /// as default Context-Type. + /// + /// Erich Eichinger + /// $Id: WebContextHandler.cs,v 1.8 2008/03/13 20:07:43 bbaia Exp $ + public class WebContextHandler : ContextHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(WebContextHandler)); + + /// + /// Sets default context type to + /// + protected override Type DefaultApplicationContextType + { + get { return typeof(WebApplicationContext); } + } + + /// + /// Sets default case-sensitivity to 'false' for web-applications + /// + protected override bool DefaultCaseSensitivity + { + get { return false; } + } + + /// + /// Gets the context name from the given + /// + protected override string GetContextName(object configContext, XmlElement contextElement) + { + string contextName = ((HttpConfigurationContext) configContext).VirtualPath; + // NET 2.0 returns "/" for root path + if (contextName == "/") + { + contextName = String.Empty; + } + return contextName; + } + + /// + /// Throws a configuration exception, if child contexts are specified. + /// + /// + /// Nesting contexts in webapplications is done by explicitly declaring + /// spring context sections for each directory. + /// + protected override void CreateChildContexts(IApplicationContext parentContext, object configContext, + XmlNode[] childContexts) + { + // disable child contexts in webapps + if (childContexts.Length > 0) + { + throw ConfigurationUtils.CreateConfigurationException( + String.Format("Nested Child Contexts are not allowed in Web Applications. Use Web.config hierarchy instead."), childContexts[0]); + } + } + + /// + /// Handles web specific details of context instantiation. + /// + protected override IApplicationContext InstantiateContext(IApplicationContext parent, object configContext, + string contextName, Type contextType, + bool caseSensitive, string[] resources) + { + HttpConfigurationContext httpConfigurationContext = (HttpConfigurationContext) configContext; + + // ASP.NET may scavenge it's configuration section cache if memory usage is too high. + // Thus a handler may be called more than once for the same context. + // Return registered context in this case. + if (ContextRegistry.IsContextRegistered(contextName)) + { + IApplicationContext ctx = ContextRegistry.GetContext(contextName); + if (Log.IsDebugEnabled) + { + Log.Debug( + string.Format("web context '{0}' already registered - returning existing instance {1}", + contextName, ctx)); + } + return ctx; + } + + // for rewriting path during context instantiation + string vpath = httpConfigurationContext.VirtualPath; + if (!vpath.EndsWith("/")) vpath = vpath + "/"; + using (new HttpContextSwitch(vpath)) + { + return + base.InstantiateContext(parent, configContext, contextName, contextType, caseSensitive, resources); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Context/Support/WebSupportModule.cs b/src/Spring/Spring.Web/Context/Support/WebSupportModule.cs new file mode 100644 index 00000000..26311495 --- /dev/null +++ b/src/Spring/Spring.Web/Context/Support/WebSupportModule.cs @@ -0,0 +1,268 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Reflection; +using System.Web; +using System.Web.Caching; +using System.Web.SessionState; + +using Common.Logging; +using Spring.Core.IO; +using Spring.Core.TypeConversion; +using Spring.Core.TypeResolution; +using Spring.Expressions; +using Spring.Objects.Factory.Support; +using Spring.Threading; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Provides various support for proper handling requests. + /// + /// Erich Eichinger + /// $Id: WebSupportModule.cs,v 1.15 2007/08/08 17:48:19 bbaia Exp $ + public class WebSupportModule : IHttpModule + { + private static readonly ILog s_log; + + private static bool s_isInitialized = false; + + // Required for Session End event handling + private static int CACHEKEYPREFIXLENGTH = 0; + private static CacheItemRemovedCallback s_originalCallback; + + /// + /// For webapplications always + ///
      + ///
    • convert IResources using the current context.
    • + ///
    • use "web" as default resource protocol
    • + ///
    • use as default threading storage
    • + ///
    + ///
    + static WebSupportModule() + { + s_log = LogManager.GetLogger(typeof(WebSupportModule)); + + // register additional resource handler + ResourceHandlerRegistry.RegisterResourceHandler(WebUtils.DEFAULT_RESOURCE_PROTOCOL, typeof(WebResource)); + // replace default IResource converter + TypeConverterRegistry.RegisterConverter(typeof(IResource), + new ResourceConverter( + new ConfigurableResourceLoader(WebUtils.DEFAULT_RESOURCE_PROTOCOL))); + // default to hybrid thread storage implementation + LogicalThreadContext.SetStorage(new HybridContextStorage()); + + s_log.Debug("Set default resource protocol to 'web' and installed HttpContext-aware HybridContextStorage"); + } + + /// + /// Registers this module for all events required by the Spring.Web framework + /// + public virtual void Init(HttpApplication app) + { + lock (typeof(WebSupportModule)) + { + s_log.Debug("Initializing Application instance"); + if (!s_isInitialized) + { + HttpModuleCollection modules = app.Modules; + foreach (string moduleKey in modules.AllKeys) + { + if (modules[moduleKey] is SessionStateModule) + { +#if !NET_1_1 + HookSessionEvent((SessionStateModule) modules[moduleKey]); +#else + HookSessionEvent11(); +#endif + } + } + } + s_isInitialized = true; + + // signal, that VirtualEnvironment is ready to accept + // handler registrations for EndRequest and EndSession events + VirtualEnvironment.SetInitialized(); + } + + app.EndRequest += new EventHandler(VirtualEnvironment.RaiseEndRequest); + + // ensure context is instantiated + IConfigurableApplicationContext appContext = WebApplicationContext.GetRootContext() as IConfigurableApplicationContext; + // configure this app + it's module instances + if (appContext == null) + { + throw new InvalidOperationException("Implementations of IApplicationContext must also implement IConfigurableApplicationContext"); + } + HttpApplicationConfigurer.Configure(appContext, app); + } + + /// + /// Disposes this instance + /// + public virtual void Dispose() + { + // noop + } + + #region Session Handling Stuff + + private static void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) + { + s_log.Debug("end session " + key + " because of " + reason); + + try + { + HttpSessionState ss = CreateSessionState(key, value); + + VirtualEnvironment.RaiseEndSession(ss, reason); + } + catch (Exception ex) + { + string msg = "Failure during EndSession event handling"; + // are we on a current request? + if (HttpContext.Current != null) + { + s_log.Error(msg, ex); + } + else + { + // this is an async session timout - log as fatal since this is the thread's exit point! + s_log.Fatal(msg, ex); + } + } + finally + { + if (s_originalCallback != null) + { + s_originalCallback(key, value, reason); + } + } + } + +#if !NET_1_1 + private static void HookSessionEvent(SessionStateModule sessionStateModule) + { + // Hook only into InProcState - all others ignore SessionEnd anyway + object store = ExpressionEvaluator.GetValue(sessionStateModule, "_store"); + if ((store != null) && store.GetType().Name == "InProcSessionStateStore") + { + s_log.Debug("attaching to InProcSessionStateStore"); + s_originalCallback = (CacheItemRemovedCallback) ExpressionEvaluator.GetValue(store, "_callback"); + ExpressionEvaluator.SetValue(store, "_callback", new CacheItemRemovedCallback(OnCacheItemRemoved)); + + CACHEKEYPREFIXLENGTH = (int) ExpressionEvaluator.GetValue(store, "CACHEKEYPREFIXLENGTH"); + } + } + + private static HttpSessionState CreateSessionState(string key, object state) + { + string id = key.Substring(CACHEKEYPREFIXLENGTH); + ISessionStateItemCollection sessionItems = + (ISessionStateItemCollection) ExpressionEvaluator.GetValue(state, "_sessionItems"); + HttpStaticObjectsCollection staticObjects = + (HttpStaticObjectsCollection) ExpressionEvaluator.GetValue(state, "_staticObjects"); + int timeout = (int) ExpressionEvaluator.GetValue(state, "_timeout"); + TypeRegistry.RegisterType("SessionStateModule", typeof(SessionStateModule)); + HttpCookieMode cookieMode = + (HttpCookieMode) ExpressionEvaluator.GetValue(null, "SessionStateModule.s_configCookieless"); + SessionStateMode stateMode = + (SessionStateMode) ExpressionEvaluator.GetValue(null, "SessionStateModule.s_configMode"); + HttpSessionStateContainer container = new HttpSessionStateContainer( + id + , sessionItems + , staticObjects + , timeout + , false + , cookieMode + , stateMode + , true + ); + + return (HttpSessionState) Activator.CreateInstance( + typeof(HttpSessionState) + , BindingFlags.Instance | BindingFlags.NonPublic + , null + , new object[] {container} + , CultureInfo.InvariantCulture + ); + } +#else + private static Type t_SessionDictionary; + + private static void HookSessionEvent11() + { + // Hook only into InProcState - all others ignore SessionEnd anyway + t_SessionDictionary = typeof(HttpSessionState).Assembly.GetType("System.Web.SessionState.SessionDictionary"); + + Type t_InProcStateClientManager = + typeof(HttpSessionState).Assembly.GetType("System.Web.SessionState.InProcStateClientManager"); + + TypeRegistry.RegisterType( "InProcStateClientManager", t_InProcStateClientManager ); + + CacheItemRemovedCallback circ = new CacheItemRemovedCallback( OnCacheItemRemoved ); + CACHEKEYPREFIXLENGTH = (int)ExpressionEvaluator.GetValue(null, "InProcStateClientManager.CACHEKEYPREFIXLENGTH"); + s_originalCallback = (CacheItemRemovedCallback)ExpressionEvaluator.GetValue(null, "InProcStateClientManager.s_callback"); + ExpressionEvaluator.SetValue(null, "InProcStateClientManager.s_callback", circ); + } + + private static HttpSessionState CreateSessionState(string key, object state1) + { + string id = key.Substring(CACHEKEYPREFIXLENGTH); + object dict = ExpressionEvaluator.GetValue(state1, "dict"); + if (dict == null) + { + dict = Activator.CreateInstance(t_SessionDictionary, true); + } + object staticObjects = ExpressionEvaluator.GetValue(state1, "staticObjects"); + int timeout = (int)ExpressionEvaluator.GetValue(state1, "timeout"); + bool isCookieless = (bool)ExpressionEvaluator.GetValue(state1, "isCookieless"); + + HttpSessionState state2 = (HttpSessionState)Activator.CreateInstance( + typeof(HttpSessionState) + , BindingFlags.Instance|BindingFlags.NonPublic + , null + , new object[] + { + id, + dict, + staticObjects, + timeout, + false, + isCookieless, + SessionStateMode.InProc, + true + } + , CultureInfo.InvariantCulture + ); + return state2; + } +#endif + + #endregion Session Handling Stuff + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Core/IO/WebResource.cs b/src/Spring/Spring.Web/Core/IO/WebResource.cs new file mode 100644 index 00000000..3a628bfe --- /dev/null +++ b/src/Spring/Spring.Web/Core/IO/WebResource.cs @@ -0,0 +1,197 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.IO; +using Spring.Core.IO; +using Spring.Util; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// implementation specifically + /// for resources served up from a web server. + /// + /// + ///

    + /// Uses the System.Web.HttpContext.Current.Server.MapPath + /// method to resolve the file name for a given resource. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: WebResource.cs,v 1.14 2008/03/14 12:02:45 oakinger Exp $ + public class WebResource : FileSystemResource + { + private string absolutePath; + + /// + /// Creates a new instance of the class. + /// + /// + /// The name of the file system resource (on the server). + /// + public WebResource(string resourceName) + : base(resourceName) + { + } + + /// + /// Gets those characters that are valid path separators for the + /// resource type. + /// + /// + /// Those characters that are valid path separators for the resource + /// type. + /// + /// + protected override char[] PathSeparatorChars + { + get { return new char[] {'/'}; } + } + + /// + /// Resolves the handle + /// for the supplied . + /// + /// + /// The name of the file system resource. + /// + /// + /// The handle for this resource. + /// + protected override FileInfo ResolveFileHandle(string resourceName) + { + this.absolutePath = ResolveResourceNameWithoutProtocol(resourceName); + if(!this.absolutePath.StartsWith("/")) + { + string currentPath = VirtualEnvironment.CurrentVirtualFilePath; + int n = currentPath.LastIndexOfAny(new char[] {'/', '\\'}); + if(n >= 0) + { + currentPath = currentPath.Substring(0, n); + } + this.absolutePath = currentPath + '/' + this.absolutePath; + } + + return new FileInfo(VirtualEnvironment.MapPath(this.absolutePath)); + } + + /// + /// Resolves the root location for the supplied . + /// + /// + /// The name of the file system resource. + /// + /// + /// The root location of the resource. + /// + protected override string ResolveRootLocation(string resourceName) + { + return string.Empty; + } + + /// + /// Resolves the path for the supplied . + /// + /// + /// The name of the file system resource. + /// + /// + /// The current path of the resource. + /// + protected override string ResolveResourcePath(string resourceName) + { + string path = this.absolutePath.TrimStart(PathSeparatorChars); + int n = path.LastIndexOfAny(PathSeparatorChars); + if(n > 0) + { + path = path.Substring(0, n); + } + else + { + path = string.Empty; + } + + return path; + } + + /// + /// Resolves the presence of the + /// value + /// in the supplied into a path. + /// + /// + /// The name of the resource. + /// + /// + /// The string that is a placeholder for a base path. + /// + /// + /// The name of the resource with any + /// value having been resolved into an actual path. + /// + protected override string ResolveBasePathPlaceHolder( + string resourceName, string basePathPlaceHolder) + { + if(StringUtils.HasText(resourceName) + && resourceName.TrimStart().StartsWith(basePathPlaceHolder)) + { + return resourceName.Replace(basePathPlaceHolder, VirtualEnvironment.ApplicationVirtualPath.TrimEnd('/')); + } + return resourceName; + } + + /// + /// Does the supplied relative ? + /// + /// + /// The name of the resource to test. + /// + /// + /// if resource name is relative; + /// otherwise . + /// + protected override bool IsRelativeResource(string resourceName) + { + return (! + (resourceName.StartsWith("/") || + resourceName.StartsWith(BasePathPlaceHolder))); + } + + /// + /// Factory Method. Create a new instance of the current resource type using the given resourceName + /// + protected override IResource CreateResourceInstance(string resourceName) + { + return new WebResource(resourceName); + } + + /// + /// The ResourceLoader to be used for resolving relative resources + /// + protected override IResourceLoader GetResourceLoader() + { + return new ConfigurableResourceLoader(WebUtils.DEFAULT_RESOURCE_PROTOCOL); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/DataBinding/DataSourceItemFormatter.cs b/src/Spring/Spring.Web/DataBinding/DataSourceItemFormatter.cs new file mode 100644 index 00000000..ad80c4c9 --- /dev/null +++ b/src/Spring/Spring.Web/DataBinding/DataSourceItemFormatter.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections; +using System.Web.UI; + +namespace Spring.DataBinding +{ + /// + /// This formatter acts as an adapter to a datasource. It parses a given datasource into + /// a table to allow converting back and forth between objects and their keys during formatting. + /// + /// + /// + /// The DataSourceItemFormatter expects the "source" to have 2 properties named "DataSourceField" and "DataValueField". + /// + /// + public class DataSourceItemFormatter : IBindingAwareFormatter + { + private string _dataSourceFieldName; + private string _dataValueFieldName; + + [ThreadStatic] + private Hashtable _dataItemsByKey; + [ThreadStatic] + private string _dataItemKeyField; + + /// + /// Initialize this instance. + /// + /// The name of the "source"s property containing the dataSource + /// The name of the "source"s property containing the name of the key property + public DataSourceItemFormatter(string dataSourceField, string dataValueField) + { + this._dataSourceFieldName = dataSourceField; + if(dataValueField == null) + { + throw new ArgumentNullException("DataValueField must not be null."); + } + this._dataValueFieldName = dataValueField; + } + + /// + /// Sets the bindingContext for the current thread. + /// + /// The source object + /// The target object + /// Variables to be used during binding + /// The current binding's direction + public void SetBindingContext(object source, object target, IDictionary variables, BindingDirection direction) + { + // Extract dataSource from source object + IEnumerable dataSource = DataBinder.GetPropertyValue(source, this._dataSourceFieldName) as IEnumerable; + // Extract dataItemKeyField from source object + string dataItemKeyField = DataBinder.GetPropertyValue(source, this._dataValueFieldName) as String; + + Initialize(dataSource, dataItemKeyField, direction); + } + + /// + /// Reset the current thread's binding context. + /// + public void ClearBindingContext() + { + _dataItemsByKey = null; + _dataItemKeyField = null; + } + + /// + /// Initialize a new instance. + /// + /// The datasource containing list items + /// The name of the listitem's property that evaluates to the item's key + /// The direction of the current binding + private void Initialize(IEnumerable dataSource, string dataItemKeyField, BindingDirection direction) + { + _dataItemKeyField = dataItemKeyField; + + // if we are binding from target to source only, we can save some performance + // here because only Format() will be called. + if (direction != (direction&BindingDirection.TargetToSource)) + { + if(dataSource == null) + { + throw new ArgumentNullException( + string.Format("DataSource must not be null.")); + } + + _dataItemsByKey = new Hashtable(); + foreach(object dataItem in dataSource) + { + string key = Format(dataItem); + this._dataItemsByKey.Add(key, dataItem); + } + } + } + + /// + /// Returns the key of the specified value object. + /// + /// The value to extract the key from. + /// Key extracted from . + public string Format(object value) + { + return DataBinder.GetPropertyValue(value, this._dataItemKeyField, null); + } + + /// + /// Return the object with the specified key from the datasource. + /// + /// The key of the object. + /// Parsed . + public object Parse(string key) + { + object item = this._dataItemsByKey[key]; + if (item==null) + { + throw new ArgumentOutOfRangeException("key", key, string.Format("Item with key {0} does not exist", key)); + } + return item; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/DataBinding/HttpRequestBindingContainer.cs b/src/Spring/Spring.Web/DataBinding/HttpRequestBindingContainer.cs new file mode 100644 index 00000000..9ecf3339 --- /dev/null +++ b/src/Spring/Spring.Web/DataBinding/HttpRequestBindingContainer.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections; +using System.Reflection; +using System.Web; +using Spring.Core; +using Spring.Expressions; +using Spring.Globalization; +using Spring.Objects; +using Spring.Util; +using Spring.Validation; + +namespace Spring.DataBinding +{ + /// + /// Binds agroup of HTTP request multi-valued items to the data model. + /// + /// + /// Due to the fact, that browsers don't send the values of unchecked checkboxes, you + /// can't use for binding to checkboxes. + /// + /// Aleksandar Seovic + /// $Id: HttpRequestBindingContainer.cs,v 1.6 2008/02/05 20:40:26 aseovic Exp $ + public class HttpRequestListBindingContainer : BaseBindingContainer + { + private string[] requestParams; + private ConstructorInfo itemCtor; + private IExpression targetExpression; + + /// + /// Creates a new instance of . + /// + /// + /// Comma separated list of multi-valued request parameters that + /// should be used to create a target item. + /// + /// + /// Expression that identifies a target list items should be added to. + /// + /// + /// The type of the target item. + /// + public HttpRequestListBindingContainer(string requestParams, string targetList, Type itemType) + { + this.requestParams = requestParams.Split(','); + this.targetExpression = Expression.Parse(targetList); + this.itemCtor = itemType.GetConstructor(Type.EmptyTypes); + if (this.itemCtor == null) + { + throw new ArgumentException("Type [{0}] does not have a default constructor. " + + "A default constructor for the item type is required for collection data binding.", + itemType.FullName); + } + } + + /// + /// Adds the binding. + /// + /// + /// This is a convinience method for adding SimpleExpressionBinding, + /// one of the most often used binding types, to the bindings list. + /// + /// + /// The source expression. + /// + /// + /// The target expression. + /// + /// + /// Binding direction. + /// + /// + /// to use for value formatting and parsing. + /// + /// + /// Added instance. + /// + public override IBinding AddBinding(string sourceExpression, string targetExpression, BindingDirection direction, + IFormatter formatter) + { + // setup variable for each HTTP request parameter + return base.AddBinding("#" + sourceExpression, targetExpression, direction, formatter); + } + + /// + /// Binds source object to target object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + public override void BindSourceToTarget(object source, object target, IValidationErrors validationErrors, IDictionary variables) + { + HttpRequest request; + if (HttpContext.Current == null || (HttpContext.Current.Request) == null) + { + throw new InvalidOperationException("Cannot perform Request data binding without a valid HTTP request."); + } + request = HttpContext.Current.Request; + IList targetList = targetExpression.GetValue(target) as IList; + if (targetList == null) + { + throw new ArgumentException("Target property has to be initialized to an instance of IList interface."); + } + + if (request.Params.GetValues(requestParams[0]) != null) + { + int valueCount = request.Params.GetValues(requestParams[0]).Length; + for (int i = 0; i < valueCount; i++) + { + IDictionary vars = new Hashtable(); + foreach (string paramName in requestParams) + { + vars[paramName] = request.Params.GetValues(paramName)[i]; + } + + object targetItem; + bool addToList; + if (i < targetList.Count) + { + targetItem = targetList[i]; + addToList = false; + } + else + { + targetItem = itemCtor.Invoke(ObjectUtils.EmptyObjects); + addToList = true; + } + + foreach (IBinding binding in Bindings) + { + binding.BindSourceToTarget(null, targetItem, validationErrors, vars); + } + + if (addToList) + { + targetList.Add(targetItem); + } + } + } + } + + /// + /// Binds target object to source object. + /// + /// + /// The source object. + /// + /// + /// The target object. + /// + /// + /// Validation errors collection that type conversion errors should be added to. + /// + /// + /// Variables that should be used during expression evaluation. + /// + public override void BindTargetToSource(object source, object target, IValidationErrors validationErrors, IDictionary variables) + { + // can't bind to a read-only Request object... + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/DataBinding/IBindingAwareFormatter.cs b/src/Spring/Spring.Web/DataBinding/IBindingAwareFormatter.cs new file mode 100644 index 00000000..2f5b4bad --- /dev/null +++ b/src/Spring/Spring.Web/DataBinding/IBindingAwareFormatter.cs @@ -0,0 +1,24 @@ +using System.Collections; +using Spring.Globalization; + +namespace Spring.DataBinding +{ + /// + /// + /// + public interface IBindingAwareFormatter : IFormatter + { + /// + /// Sets the binding context of this formatter. + /// + /// + /// + /// + /// + void SetBindingContext(object source, object target, IDictionary variables, BindingDirection direction); + /// + /// Clears the binding context of this formatter. + /// + void ClearBindingContext(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/DataBinding/IWebDataBound.cs b/src/Spring/Spring.Web/DataBinding/IWebDataBound.cs new file mode 100644 index 00000000..bc09c068 --- /dev/null +++ b/src/Spring/Spring.Web/DataBinding/IWebDataBound.cs @@ -0,0 +1,31 @@ +using System; +using Spring.Context; +using Spring.Globalization; + +namespace Spring.DataBinding +{ + /// + /// A page or control must implement this interface to support spring's databinding infrastructure. + /// + /// + /// + /// + /// + /// + public interface IWebDataBound : IDataBound + { + /// + /// Return the UniqueID of this page or control. + /// + string UniqueID { get; } + /// + /// Return the where + /// instances should be optained from. + /// + IApplicationContext ApplicationContext { get; } + /// + /// This event is raised to initialize bindings for . + /// + event EventHandler DataBindingsInitialized; + } +} diff --git a/src/Spring/Spring.Web/DataBinding/MultipleSelectionListControlBinding.cs b/src/Spring/Spring.Web/DataBinding/MultipleSelectionListControlBinding.cs new file mode 100644 index 00000000..916b9cec --- /dev/null +++ b/src/Spring/Spring.Web/DataBinding/MultipleSelectionListControlBinding.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections; +using System.Web.UI.WebControls; +using Spring.DataBinding; +using Spring.Globalization; + +namespace Spring.DataBinding +{ + /// + /// Handles binding of a multiple selection ListControl to a target's IList property. + /// + public class MultipleSelectionListControlBinding : SimpleExpressionBinding + { + /// + /// Creates a new instance of this binding. + /// + ///The expression that will evaluate to the acting as the source + ///The expression that will evaluate to an property acting as the target + /// Binding's direction + ///An optional formatter converting target items into and back + /// + /// If no formatter is specified, a will be used. + /// + public MultipleSelectionListControlBinding(string sourceExpression, string targetExpression, BindingDirection direction, + IFormatter itemFormatter) + :base(sourceExpression, targetExpression, itemFormatter) + { + this.Direction = direction; + if (itemFormatter == null) + { + this.Formatter = new DataSourceItemFormatter("DataSource", "DataValueField"); + } + } + + /// + /// Actually performs unbinding the ListControl's selected values into the target's + /// + protected override void DoBindSourceToTarget(object source, object target, IDictionary variables) + { + // retrieve targetlist + IList targetList = this.GetTargetValue(target,variables) as IList; + if(targetList == null) + { + throw new ArgumentException(string.Format("Target property must evaluate to an instance of {0}.", typeof(IList).FullName)); + } + + // retrieve ListControl to be unbound + source = this.GetSourceValue(source, variables); + ListControl listControl = source as ListControl; + if(listControl == null) + { + throw new ArgumentException(string.Format("Source property must evaluate to an instance of {0}.", typeof(ListControl).FullName)); + } + + // reset target list + targetList.Clear(); + + // set binding context if applicable + IBindingAwareFormatter bindingAwareFormatter = this.Formatter as IBindingAwareFormatter; + if (bindingAwareFormatter != null) + { + bindingAwareFormatter.SetBindingContext(source, target, variables, BindingDirection.SourceToTarget); + } + + // (re-)add selected items to the target list + try + { + IFormatter formatter = this.Formatter; + ListItemCollection listItems = listControl.Items; + foreach(ListItem listItem in listControl.Items) + { + if (listItem.Selected) + { + // formatter must return an object applicable to the targetlist. + object item = formatter.Parse(listItem.Value); + targetList.Add(item); + } + } + } + finally + { + if(bindingAwareFormatter != null) + { + bindingAwareFormatter.ClearBindingContext(); + } + } + } + + /// + /// Actually performs binding the targetlist to the ListControl. + /// + protected override void DoBindTargetToSource(object source, object target, IDictionary variables) + { + // retrieve targetlist + IList targetList = this.GetTargetValue(target, variables) as IList; + if (targetList == null) + { + throw new ArgumentException("Target property has to be initialized to an instance of IList interface."); + } + + // retrieve ListControl to be bound + source = this.GetSourceValue(source, variables); + ListControl listControl = source as ListControl; + if(listControl == null) + { + throw new ArgumentException(string.Format("Source property has to be initialized to an instance of {0}.", typeof(ListControl).FullName)); + } + + // clear control's selection + listControl.ClearSelection(); + ListItemCollection listItems = listControl.Items; + + // set binding context if applicable + IBindingAwareFormatter bindingAwareFormatter = this.Formatter as IBindingAwareFormatter; + if(bindingAwareFormatter != null) + { + bindingAwareFormatter.SetBindingContext(source, target, variables, BindingDirection.TargetToSource); + } + + // select all items in ListControl matching keys in targetlist + try + { + IFormatter formatter = this.Formatter; + foreach(object objectItem in targetList) + { + string key = formatter.Format(objectItem); + ListItem item = listItems.FindByValue(key); + if (item != null) + { + item.Selected = true; + } + else + { + throw new ArgumentOutOfRangeException("value", key, string.Format("Value '{0}' does not exist", key )); + } + } + } + finally + { + if(bindingAwareFormatter != null) + { + bindingAwareFormatter.ClearBindingContext(); + } + } + } + + /// + /// Setting source value is not allowed in this bindingType. + /// + protected override void SetSourceValue(object source, object value, IDictionary variables) + { + throw new InvalidOperationException(string.Format("Setting source value in '{0}' is not allowed.",this.GetType().FullName)); + } + + /// + /// Setting target value is not allowed in this bindingType. + /// + protected override void SetTargetValue(object target, object value, IDictionary variables) + { + throw new InvalidOperationException(string.Format("Setting target value in '{0}' is not allowed.", this.GetType().FullName)); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Globalization/AspNetResourceCache.cs b/src/Spring/Spring.Web/Globalization/AspNetResourceCache.cs new file mode 100644 index 00000000..213143d1 --- /dev/null +++ b/src/Spring/Spring.Web/Globalization/AspNetResourceCache.cs @@ -0,0 +1,54 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Web; +using System.Web.Caching; + +namespace Spring.Globalization +{ + /// + /// Resource cache implementation that uses ASP.NET to cache resources. + /// + /// Aleksandar Seovic + /// $Id: AspNetResourceCache.cs,v 1.6 2007/08/03 06:08:59 oakinger Exp $ + internal class AspNetResourceCache : AbstractResourceCache + { + /// + /// Gets the list of resources from cache. + /// + /// Cache key to use for lookup. + /// A list of cached resources for the specified target object and culture. + protected override IList GetResources(string cacheKey) + { + return (IList)HttpRuntime.Cache[cacheKey]; + } + + /// + /// Puts the list of resources in the cache. + /// + /// Cache key to use for the specified resources. + /// A list of resources to cache. + protected override void PutResources(string cacheKey, IList resources) + { + HttpRuntime.Cache[cacheKey] = resources; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Globalization/Resolvers/CookieCultureResolver.cs b/src/Spring/Spring.Web/Globalization/Resolvers/CookieCultureResolver.cs new file mode 100644 index 00000000..5fd45e42 --- /dev/null +++ b/src/Spring/Spring.Web/Globalization/Resolvers/CookieCultureResolver.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using System.Web; + +namespace Spring.Globalization.Resolvers +{ + /// + /// Culture resolver that uses cookie to store culture information. + /// + /// Aleksandar Seovic + /// $Id: CookieCultureResolver.cs,v 1.4 2007/08/25 14:26:35 oakinger Exp $ + public class CookieCultureResolver : DefaultWebCultureResolver + { + private const string CultureKey = "Spring.UserLocale"; + + /// + /// Resolves the culture from the request. + /// + /// + /// If the culture cookie doesn't exist, this method will return + /// the value of the 'Accept-Language' request header, or if no + /// headers are specified, the culture of the current server thread. + /// + /// Culture that should be used to render view. + public override CultureInfo ResolveCulture() + { + HttpCookie cookie = HttpContext.Current.Request.Cookies[CultureKey]; + + if (cookie != null) + { + return base.GetCulture(cookie.Value); + } + else + { + return base.GetDefaultLocale(); + } + } + + /// + /// Sets the culture. + /// + /// The new culture or null to clear the culture. + public override void SetCulture(CultureInfo culture) + { + HttpCookie cookie = CreateCookie(culture); + HttpContext.Current.Request.Cookies.Set(cookie); + HttpContext.Current.Response.Cookies.Add(cookie); + } + + /// + /// Creates cookie for the specified culture. + /// + /// Culture to store in a cookie. + /// Created cookie. + private HttpCookie CreateCookie(CultureInfo culture) + { + HttpCookie cookie = new HttpCookie(CultureKey); + cookie.Domain = HttpContext.Current.Request.Url.Host; + cookie.Expires = DateTime.MaxValue; + cookie.Value = culture.Name; + + return cookie; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Globalization/Resolvers/DefaultWebCultureResolver.cs b/src/Spring/Spring.Web/Globalization/Resolvers/DefaultWebCultureResolver.cs new file mode 100644 index 00000000..59418ac5 --- /dev/null +++ b/src/Spring/Spring.Web/Globalization/Resolvers/DefaultWebCultureResolver.cs @@ -0,0 +1,101 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using System.Threading; +using System.Web; +using Spring.Util; + +namespace Spring.Globalization.Resolvers +{ + /// + /// Default culture resolver for web applications. Contains some common utility methods for web culture resolvers. + /// + /// Aleksandar Seovic + /// $Id: DefaultWebCultureResolver.cs,v 1.5 2007/08/25 14:26:35 oakinger Exp $ + public class DefaultWebCultureResolver : DefaultCultureResolver + { + /// + /// Returns default culture. If property is not set, + /// it tries to get culture from the request headers + /// and falls back to a current thread's culture if no headers are available. + /// + /// Default culture to use. + protected override CultureInfo GetDefaultLocale() + { + if (DefaultCulture != null) + { + return base.DefaultCulture; + } + + CultureInfo culture = GetCulture(GetRequestLanguage()); + if (culture != null) + { + return culture; + } + + return Thread.CurrentThread.CurrentUICulture; + } + + /// + /// Extracts the users favorite language from "accept-language" header of the current request. + /// + /// a language string if any or null, if no languages have been sent with the request + protected virtual string GetRequestLanguage() + { + HttpContext context = HttpContext.Current; + if (context != null && context.Request != null && ArrayUtils.HasLength(context.Request.UserLanguages)) + { + return context.Request.UserLanguages[0]; + } + return null; + } + + /// + /// Resolves a culture by name. + /// + /// the name of the culture to get + /// a (possible neutral!) or null, if culture could not be resolved + public CultureInfo GetCulture(string cultureName) + { + try { return new CultureInfo(cultureName.Split(';')[0]); } catch { } + return null; + } + + /// + /// Resolves the culture from the context. + /// + /// Culture that should be used to render view. + public override CultureInfo ResolveCulture() + { + return GetDefaultLocale(); + } + + /// + /// Not supported for this implementation. + /// + /// The new culture or null to clear the culture. + public override void SetCulture(CultureInfo culture) + { + throw new NotSupportedException("Cannot change a default culture in a web application - use a different culture resolution strategy."); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Globalization/Resolvers/RequestCultureResolver.cs b/src/Spring/Spring.Web/Globalization/Resolvers/RequestCultureResolver.cs new file mode 100644 index 00000000..932f5e42 --- /dev/null +++ b/src/Spring/Spring.Web/Globalization/Resolvers/RequestCultureResolver.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using System.Threading; +using System.Web; +using Spring.Util; + +namespace Spring.Globalization.Resolvers +{ + /// + /// Culture resolver that uses request headers to determine culture. If no languages + /// are specified in the request headers, it returns default culture specifed, and + /// if no default culture was specifed it returns current culture for the executing + /// server thread. + /// + /// + /// Note: This culture resolver cannot be used to change the culture + /// because request headers cannot be modified. In order to change the culture + /// when using this culture resolver user has to change language settings in + /// the web browser. + /// + /// Aleksandar Seovic + /// $Id: RequestCultureResolver.cs,v 1.4 2007/08/25 14:26:35 oakinger Exp $ + public class RequestCultureResolver : DefaultWebCultureResolver + { + /// + /// Tries to determine culture from the request headers. If no languages + /// are specified in the request headers, it returns default culture specifed, and + /// if no default culture was specifed it returns current culture for the executing + /// server thread. + /// + /// Culture that should be used to render view. + public override CultureInfo ResolveCulture() + { + HttpContext context = HttpContext.Current; + + CultureInfo culture = GetCulture(GetRequestLanguage()); + if (culture != null) + { + return culture; + } + + if (DefaultCulture != null) + { + return base.DefaultCulture; + } + + return Thread.CurrentThread.CurrentUICulture; + } + + /// + /// Not supported for this resolver implementation + /// + /// The new culture or null to clear the culture. + public override void SetCulture(CultureInfo culture) + { + throw new NotSupportedException("Cannot change HTTP request headers - use a different culture resolution strategy."); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Globalization/Resolvers/SessionCultureResolver.cs b/src/Spring/Spring.Web/Globalization/Resolvers/SessionCultureResolver.cs new file mode 100644 index 00000000..592d25f6 --- /dev/null +++ b/src/Spring/Spring.Web/Globalization/Resolvers/SessionCultureResolver.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Globalization; +using System.Web; +using Spring.Util; + +namespace Spring.Globalization.Resolvers +{ + /// + /// Culture resolver that uses HTTP session to store culture information. + /// + /// Aleksandar Seovic + /// $Id: SessionCultureResolver.cs,v 1.3 2007/08/25 14:26:35 oakinger Exp $ + public class SessionCultureResolver : DefaultWebCultureResolver + { + private const string CultureKey = "Spring.UserLocale"; + + /// + /// Resolves the culture from the request. + /// + /// + /// If culture information doesn't exist in the session, it will be created and its value will + /// be set to the value of the 'Accept-Language' request header, or if no + /// headers are specified to the culture of the current server thread. + /// + /// Culture that should be used to render view. + public override CultureInfo ResolveCulture() + { + CultureInfo culture = SessionCulture; + if (culture != null) + { + return culture; + } + else + { + return base.GetDefaultLocale(); + } + } + + /// + /// Sets the culture. + /// + /// The new culture or null to clear the culture. + public override void SetCulture(CultureInfo culture) + { + SessionCulture = culture; + } + + /// + /// Gets/Sets the current session's culture. + /// + protected virtual CultureInfo SessionCulture + { + set + { + AssertUtils.ArgumentNotNull(value, "SessionCulture"); + HttpContext.Current.Session[CultureKey] = value; + } + get + { + CultureInfo culture = HttpContext.Current.Session[CultureKey] as CultureInfo; + return culture; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/ChildWebObjectDefinition.cs b/src/Spring/Spring.Web/Objects/Factory/Support/ChildWebObjectDefinition.cs new file mode 100644 index 00000000..a38e163b --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Support/ChildWebObjectDefinition.cs @@ -0,0 +1,164 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using System.Web; +using Spring.Objects.Factory.Config; +using Spring.Util; + +namespace Spring.Objects.Factory.Support +{ + /// + /// Web object definitions extend + /// by adding scope property. + /// + /// + ///

    + /// This is the most common type of object definition in ASP.Net web applications + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ChildWebObjectDefinition.cs,v 1.3 2007/08/01 23:10:46 markpollack Exp $ + public class ChildWebObjectDefinition : ChildObjectDefinition, IWebObjectDefinition + { + private ObjectScope _scope = ObjectScope.Default; + private string _pageName; + + #region Constructors + + /// + /// Creates a new instance of the + /// class + /// for a singleton, providing property values and constructor arguments. + /// + /// Name of the parent object definition. + /// The class of the object to instantiate. + /// + /// The + /// to be applied to a new instance of the object. + /// + /// + /// The to be applied to + /// a new instance of the object. + /// + public ChildWebObjectDefinition(string parentName, Type type, ConstructorArgumentValues arguments, MutablePropertyValues properties) : base(parentName, type, arguments, properties) + {} + + /// + /// Creates a new instance of the + /// class + /// for a singleton, providing property values and constructor arguments. + /// + /// Name of the parent object definition. + /// The class name of the object to instantiate. + /// + /// The + /// to be applied to a new instance of the object. + /// + /// + /// The to be applied to + /// a new instance of the object. + /// + public ChildWebObjectDefinition(string parentName, string typeName, ConstructorArgumentValues arguments, MutablePropertyValues properties) : base(parentName, typeName, arguments, properties) + {} + + /// + /// Creates a new instance of the + /// class + /// for an .aspx page, providing property values. + /// + /// Name of the parent object definition. + /// Name of the .aspx page to instantiate. + /// + /// The to be applied to + /// a new instance of the object. + /// + public ChildWebObjectDefinition(string parentName, string pageName, MutablePropertyValues properties) + : base(parentName, WebObjectUtils.GetPageType(pageName), null, properties) + { + _pageName = WebUtils.CombineVirtualPaths(HttpContext.Current.Request.CurrentExecutionFilePath, pageName); + } + + #endregion + + /// + /// Object scope. + /// + public ObjectScope Scope + { + get { return _scope; } + set { _scope = value; } + } + + /// + /// Returns true if web object is .aspx page. + /// + public bool IsPage + { + get { return _pageName != null; } + } + + /// + /// Gets the rooted url of the .aspx page, if object definition represents page. + /// + public string PageName + { + get { return _pageName; } + } + + /// + /// Forces ASP pages to be treated as prototypes all the time inorder to comply with ASP.Net requirements. + /// + public override bool IsSingleton + { + get + { + if (IsPage) + { + return false; + } + else + { + return base.IsSingleton; + } + } + set { base.IsSingleton = value; } + } + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + return string.Format( + CultureInfo.InvariantCulture, + "Child web object with class [{0}] defined in {1}", + (IsPage ? PageName : ObjectTypeName), + ResourceDescription); + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/IWebObjectDefinition.cs b/src/Spring/Spring.Web/Objects/Factory/Support/IWebObjectDefinition.cs new file mode 100644 index 00000000..8a7ed3b6 --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Support/IWebObjectDefinition.cs @@ -0,0 +1,45 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Defines additional members web object definitions need to implement. + /// + /// Aleksandar Seovic + /// $Id: IWebObjectDefinition.cs,v 1.2 2007/03/26 14:58:38 oakinger Exp $ + public interface IWebObjectDefinition + { + /// + /// Gets or sets scope for the web object (application, session or request) + /// + ObjectScope Scope { get; set; } + + /// + /// Returns true if web object is .aspx page. + /// + bool IsPage { get; } + + /// + /// Gets the rooted url of the .aspx page, if object definition represents page. + /// + string PageName { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/ObjectScope.cs b/src/Spring/Spring.Web/Objects/Factory/Support/ObjectScope.cs new file mode 100644 index 00000000..9d611f9f --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Support/ObjectScope.cs @@ -0,0 +1,52 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// The possible object scope values. + /// + /// Aleksandar Seovic + /// $Id: ObjectScope.cs,v 1.1 2007/02/17 06:57:44 bbaia Exp $ + public enum ObjectScope + { + /// + /// Application scope. + /// + Application = 0, + + /// + /// Session scope. + /// + Session = 1, + + /// + /// Request scope. + /// + Request = 2, + + /// + /// Default scope (currently + /// ). + /// + /// + Default = Application + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/RootWebObjectDefinition.cs b/src/Spring/Spring.Web/Objects/Factory/Support/RootWebObjectDefinition.cs new file mode 100644 index 00000000..064749f8 --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Support/RootWebObjectDefinition.cs @@ -0,0 +1,221 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Web; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Web object definitions extend + /// by adding scope property. + /// + /// + ///

    + /// This is the most common type of object definition in ASP.Net web applications + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: RootWebObjectDefinition.cs,v 1.4 2007/08/01 23:10:46 markpollack Exp $ + public class RootWebObjectDefinition : RootObjectDefinition, IWebObjectDefinition + { + private ObjectScope _scope = ObjectScope.Default; + private string _pageName; + + #region Constructors + + + /// + /// Creates a new instance of the + /// class + /// for a singleton, providing property values and constructor arguments. + /// + /// + /// The type of the object to instantiate. + /// + /// + /// The to be applied to + /// a new instance of the object. + /// + /// + /// The + /// to be applied to a new instance of the object. + /// + public RootWebObjectDefinition( + Type type, + ConstructorArgumentValues arguments, + MutablePropertyValues properties) + : base(type, arguments, properties) + {} + + /// + /// Creates a new instance of the + /// class + /// for a singleton, providing property values and constructor arguments. + /// + /// + /// The assembly qualified of the object to instantiate. + /// + /// + /// The to be applied to + /// a new instance of the object. + /// + /// + /// The + /// to be applied to a new instance of the object. + /// + /// + ///

    + /// Takes an object class name to avoid eager loading of the object class. + ///

    + ///
    + public RootWebObjectDefinition( + string typeName, + ConstructorArgumentValues arguments, + MutablePropertyValues properties) + : base(typeName, arguments, properties) + {} + + /// + /// Creates a new instance of the + /// class + /// for an .aspx page, providing property values. + /// + /// + /// Name of the .aspx page to instantiate. + /// + /// + /// The to be applied to + /// a new instance of the object. + /// + public RootWebObjectDefinition( + string pageName, + MutablePropertyValues properties) + : base(WebObjectUtils.GetPageType(pageName), null, properties) + { + _pageName = WebUtils.CombineVirtualPaths(HttpContext.Current.Request.CurrentExecutionFilePath, pageName); + } + + /// + /// Creates a new instance of the + /// class + /// + /// + /// The definition that is to be copied. + /// + /// + ///

    + /// Deep copy constructor. + ///

    + ///
    + public RootWebObjectDefinition(IObjectDefinition other) : base(other) + { + if (other is IWebObjectDefinition) + { + this._scope = ((IWebObjectDefinition) other).Scope; + this._pageName = ((IWebObjectDefinition) other).PageName; + } + } + + #endregion + + /// + /// Object scope. + /// + public ObjectScope Scope + { + get { return _scope; } + set { _scope = value; } + } + + /// + /// Returns true if web object is .aspx page. + /// + public bool IsPage + { + get { return _pageName != null; } + } + + /// + /// Gets the rooted url of the .aspx page, if object definition represents page. + /// + public string PageName + { + get { return _pageName; } + } + + /// + /// Forces ASP pages to be treated as prototypes all the time inorder to comply with ASP.Net requirements. + /// + public override bool IsSingleton + { + get + { + if (IsPage) + { + return false; + } + else + { + return base.IsSingleton; + } + } + set { base.IsSingleton = value; } + } + + /// + /// Overrides this object's values using values from other argument. + /// + /// The object to copy values from. + public override void OverrideFrom(IObjectDefinition other) + { + base.OverrideFrom(other); + if (other is IWebObjectDefinition) + { + this._scope = ((IWebObjectDefinition) other).Scope; + this._pageName = ((IWebObjectDefinition) other).PageName; + } + } + + /// + /// A that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString() + { + return string.Format( + CultureInfo.InvariantCulture, + "Root web object with class [{0}] defined in {1}", + (IsPage ? PageName : ObjectTypeName), + ResourceDescription); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/WebInstantiationStrategy.cs b/src/Spring/Spring.Web/Objects/Factory/Support/WebInstantiationStrategy.cs new file mode 100644 index 00000000..32f1c122 --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Support/WebInstantiationStrategy.cs @@ -0,0 +1,86 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Object instantiation strategy for use in + /// . + /// + /// + ///

    + /// This strategy checks if objects id ASP.Net page and if it is uses + /// PageParser to compile and instantiate page instance. Otherwise it + /// delagates call to its parent. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: WebInstantiationStrategy.cs,v 1.4 2008/05/02 16:44:50 markpollack Exp $ + [Serializable] + public class WebInstantiationStrategy : MethodInjectingInstantiationStrategy + { + /// + /// Creates a new instance of the + /// class. + /// + public WebInstantiationStrategy() + {} + + /// + /// Instantiate an instance of the object described by the supplied + /// from the supplied . + /// + /// + /// The definition of the object that is to be instantiated. + /// + /// + /// The name associated with the object definition. The name can be the null + /// or zero length string if we're autowiring an object that doesn't belong + /// to the supplied . + /// + /// + /// The owning + /// + /// + /// An instance of the object described by the supplied + /// from the supplied . + /// + public override object Instantiate( + RootObjectDefinition definition, string name, IObjectFactory factory) + { + if (definition is IWebObjectDefinition && ((IWebObjectDefinition) definition).IsPage) + { + return WebObjectUtils.CreatePageInstance(((IWebObjectDefinition) definition).PageName); + } + else + { + return base.Instantiate(definition, name, factory); + } + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectDefinitionFactory.cs b/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectDefinitionFactory.cs new file mode 100644 index 00000000..8332f4a4 --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectDefinitionFactory.cs @@ -0,0 +1,113 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Custom implementation of + /// for web applications. + /// + /// + ///

    + /// This implementation adds support for .aspx pages and scoped objects. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: WebObjectDefinitionFactory.cs,v 1.9 2008/04/07 00:19:55 bbaia Exp $ + public class WebObjectDefinitionFactory : DefaultObjectDefinitionFactory + { + + /// + /// Factory style method for getting concrete + /// + /// instances. + /// + /// If no parent is specified, a RootWebObjectDefinition is created, otherwise a + /// ChildWebObjectDefinition. + /// The of the defined object. + /// The name of the parent object definition (if any). + /// The against which any class names + /// will be resolved into instances. + /// + /// An + /// + /// instance. + /// + public override AbstractObjectDefinition CreateObjectDefinition(string typeName, string parent, AppDomain domain) + { + Type objectType = null; + bool isPage = StringUtils.HasText(typeName) && typeName.ToLower().EndsWith(".aspx"); + bool isControl = StringUtils.HasText(typeName) && + (typeName.ToLower().EndsWith(".ascx") || typeName.ToLower().EndsWith(".master")); + + if (!(isPage || isControl) && StringUtils.HasText(typeName) && domain != null) + { + try + { + objectType = TypeResolutionUtils.ResolveType(typeName); + } + // try later.... + catch { } + } + + if (StringUtils.IsNullOrEmpty(parent)) + { + if (objectType != null) + { + return new RootWebObjectDefinition(objectType, new ConstructorArgumentValues(), new MutablePropertyValues()); + } + else if (isPage) + { + return new RootWebObjectDefinition(typeName, new MutablePropertyValues()); + } + else + { + return new RootWebObjectDefinition(typeName, new ConstructorArgumentValues(), new MutablePropertyValues()); + } + } + else + { + if (objectType != null) + { + return new ChildWebObjectDefinition(parent, objectType, new ConstructorArgumentValues(), new MutablePropertyValues()); + } + else if (isPage) + { + return new ChildWebObjectDefinition(parent, typeName, new MutablePropertyValues()); + } + else + { + return new ChildWebObjectDefinition(parent, typeName, new ConstructorArgumentValues(), new MutablePropertyValues()); + } + } + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectFactory.cs b/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectFactory.cs new file mode 100644 index 00000000..1b223e84 --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectFactory.cs @@ -0,0 +1,497 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Web; +using System.Web.Caching; +using System.Web.SessionState; +using Common.Logging; +using Spring.Context.Support; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Concrete implementation of + /// that knows + /// how to handle s. + /// + /// + ///

    + /// This class should only be used within the context of an ASP.NET web application. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: WebObjectFactory.cs,v 1.10 2008/05/29 12:13:27 oakinger Exp $ + public class WebObjectFactory : DefaultListableObjectFactory + { + private readonly static ILog log = LogManager.GetLogger(typeof(WebObjectFactory)); + + private readonly static string OBJECTTABLEKEY = "spring.objects"; + + private delegate IDictionary ObjectDictionaryCreationHandler(); + + /// + /// Holds the virtual path this factory has been created from. + /// + private readonly string contextPath; + /// + /// Holds the handler reference for creating an object dictionary + /// matching this factory's case-sensitivity + /// + private readonly ObjectDictionaryCreationHandler createObjectDictionary; + + #region Constructor (s) / Destructor + + static WebObjectFactory() + { + EnsureEventHandlersRegistered(); + } + + /// + /// Registers events handlers with to ensure + /// proper disposal of 'request'- and 'session'-scoped objects + /// + private static void EnsureEventHandlersRegistered() + { + if (log.IsDebugEnabled) log.Debug("hooking up event handlers"); + VirtualEnvironment.EndRequest += new VirtualEnvironment.RequestEventHandler(OnEndRequest); + VirtualEnvironment.EndSession += new VirtualEnvironment.SessionEventHandler(OnEndSession); + } + + /// + /// Creates a new instance of the + /// class. + /// + /// The virtual path resources will be relative resolved to. + /// Flag specifying whether to make this object factory case sensitive or not. + public WebObjectFactory(string contextPath, bool caseSensitive) + : this(contextPath, caseSensitive, null) + { + } + + /// + /// Creates a new instance of the + /// class. + /// + /// The virtual path resources will be relative resolved to. + /// Flag specifying whether to make this object factory case sensitive or not. + /// + /// The parent object factory. + /// + public WebObjectFactory(string contextPath, bool caseSensitive, IObjectFactory parentFactory) + : base(caseSensitive, parentFactory) + { + this.contextPath = contextPath; + this.createObjectDictionary = (caseSensitive) + ? new ObjectDictionaryCreationHandler(CreateCaseSensitiveDictionary) + : new ObjectDictionaryCreationHandler(CreateCaseInsensitiveDictionary); + + InstantiationStrategy = new WebInstantiationStrategy(); + } + + #endregion + + #region Convenience accessors for Http* objects + + /// + /// Convinience accessor for HttpContext + /// + private HttpContext Context + { + get { return HttpContext.Current; } + } + + /// + /// Get the table of 'request'-scoped objects. + /// + protected virtual IDictionary Request + { + get + { + IDictionary objecttable = null; + if (this.Context != null) + { + objecttable = this.Context.Items[OBJECTTABLEKEY] as IDictionary; + if (objecttable == null) + { + this.Context.Items[OBJECTTABLEKEY] = CreateObjectDictionary(); + } + } + return objecttable; + } + } + + /// + /// Get the table of 'session'-scoped objects. Returns null, if Session is disabled. + /// + protected virtual IDictionary Session + { + get + { + IDictionary objecttable = null; + if ((Context != null) && (Context.Session != null)) + { + objecttable = Context.Session[OBJECTTABLEKEY] as IDictionary; + if (objecttable == null) + { + Context.Session[OBJECTTABLEKEY] = CreateObjectDictionary(); + } + } + return objecttable; + } + } + + #endregion + + /// + /// Creates a dictionary matching this factory's case sensitivity behaviour. + /// + protected IDictionary CreateObjectDictionary() + { + return createObjectDictionary(); + } + + /// + /// Creates the root object definition. + /// + /// The template definition. + /// Root object definition. + protected override RootObjectDefinition CreateRootObjectDefinition(IObjectDefinition templateDefinition) + { + return new RootWebObjectDefinition(templateDefinition); + } + + /// + /// Tries to find cached object for the specified name. + /// + /// + /// This implementation tries to find object first in the Request scope, + /// then in the Session scope and finally in the Application scope. + /// + /// Object name to look for. + /// Cached object if found, null otherwise. + protected override object GetSingleton(string objectName) + { + object instance = null; + + instance = GetScopedSingleton(objectName, this.Request); + if (instance != null) return instance; + + instance = GetScopedSingleton(objectName, this.Session); + if (instance != null) return instance; + + instance = base.GetSingleton(objectName); + return instance; + } + + /// + /// Looks up an in the specified cache dictionary. + /// + /// the name to lookup. + /// the cache dictionary to search + /// the found instance. null otherwise + protected object GetScopedSingleton(string objectName, IDictionary scopedSingletonCache) + { + if (scopedSingletonCache == null) return null; + + lock (scopedSingletonCache.SyncRoot) + { + object instance = scopedSingletonCache[objectName]; + if (instance == TemporarySingletonPlaceHolder) + { + throw new ObjectCurrentlyInCreationException(objectName); + } + + return instance; + } + } + + /// + /// Creates a singleton instance for the specified object name and definition. + /// + /// + /// The object name (will be used as the key in the singleton cache key). + /// + /// The object definition. + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a static factory method. It is invalid to use a non-null arguments value + /// in any other case. + /// + /// The created object instance. + protected override object CreateAndCacheSingletonInstance( + string objectName, RootObjectDefinition objectDefinition, object[] arguments) + { + if (objectDefinition is IWebObjectDefinition + && ((IWebObjectDefinition)objectDefinition).Scope != ObjectScope.Application) + { + ObjectScope scope = ((IWebObjectDefinition)objectDefinition).Scope; + + if (scope == ObjectScope.Request) + { + IDictionary requestCache = this.Request; + if (requestCache == null) + { + throw new ObjectCreationException(string.Format("'request' scoped web singleton object '{0}' requires a valid Request.", objectName)); + } + + object instance = CreateAndCacheScopedSingletonInstance(objectName, objectDefinition, arguments, requestCache); + return instance; + } + else if (scope == ObjectScope.Session) + { + IDictionary sessionCache = this.Session; + if (sessionCache == null) + { + throw new ObjectCreationException(string.Format("'session' scoped web singleton object '{0}' requires a valid Session.", objectName)); + } + object instance = CreateAndCacheScopedSingletonInstance(objectName, objectDefinition, arguments, sessionCache); + return instance; + } + + throw new ObjectDefinitionException("Web singleton objects must be either request, session or application scoped."); + } + + return base.CreateAndCacheSingletonInstance(objectName, objectDefinition, arguments); + } + + /// + /// Creates a singleton instance for the specified object name and definition + /// and caches the instance in the specified dictionary + /// + /// + /// The object name (will be used as the key in the singleton cache key). + /// + /// The object definition. + /// + /// The arguments to use if creating a prototype using explicit arguments to + /// a static factory method. It is invalid to use a non-null arguments value + /// in any other case. + /// + /// the dictionary to be used for caching singleton instances + /// The created object instance. + /// + /// If the object is successfully created, + /// contains the cached instance with the key . + /// + protected virtual object CreateAndCacheScopedSingletonInstance(string objectName, RootObjectDefinition objectDefinition, object[] arguments, IDictionary scopedSingletonCache) + { + object instance; + lock (scopedSingletonCache.SyncRoot) + { + instance = scopedSingletonCache[objectName]; + if (instance == TemporarySingletonPlaceHolder) + { + throw new ObjectCurrentlyInCreationException(objectName); + } + else if (instance == null) + { + scopedSingletonCache.Add(objectName, TemporarySingletonPlaceHolder); + try + { + instance = CreateObject(objectName, objectDefinition, arguments, true); + AssertUtils.ArgumentNotNull(instance, "instance"); + scopedSingletonCache[objectName] = instance; + } + catch + { + scopedSingletonCache.Remove(objectName); + throw; + } + } + } + return instance; + } + + /// + /// Add the created, but yet unpopulated singleton to the singleton cache + /// to be able to resolve circular references + /// + /// the name of the object to add to the cache. + /// the definition used to create and populated the object. + /// the raw object instance. + /// + /// Derived classes may override this method to select the right cache based on the object definition. + /// + protected override void AddEagerlyCachedSingleton(string objectName, IObjectDefinition objectDefinition, object rawSingletonInstance) + { + if (objectDefinition is IWebObjectDefinition + && ((IWebObjectDefinition)objectDefinition).Scope != ObjectScope.Application) + { + ObjectScope scope = ((IWebObjectDefinition) objectDefinition).Scope; + if (scope == ObjectScope.Request) + { + this.Request[objectName] = rawSingletonInstance; + } + else if (scope == ObjectScope.Session) + { + this.Session[objectName] = rawSingletonInstance; + } + else + { + throw new ObjectDefinitionException("Web singleton objects must be either request, session or application scoped."); + } + } + else + { + base.AddEagerlyCachedSingleton(objectName, objectDefinition, rawSingletonInstance); + } + } + + /// + /// Remove the specified singleton from the singleton cache that has + /// been added before by a call to + /// + /// the name of the object to remove from the cache. + /// the definition used to create and populated the object. + /// + /// Derived classes may override this method to select the right cache based on the object definition. + /// + protected override void RemoveEagerlyCachedSingleton(string objectName, IObjectDefinition objectDefinition) + { + if (objectDefinition is IWebObjectDefinition + && ((IWebObjectDefinition)objectDefinition).Scope != ObjectScope.Application) + { + ObjectScope scope = ((IWebObjectDefinition) objectDefinition).Scope; + if (scope == ObjectScope.Request) + { + this.Request.Remove(objectName); + } + else if (scope == ObjectScope.Session) + { + this.Session.Remove(objectName); + } + else + { + throw new ObjectDefinitionException("Web singleton objects must be either request, session or application scoped."); + } + } + else + { + base.RemoveEagerlyCachedSingleton(objectName, objectDefinition); + } + } + + /// + /// Injects dependencies into the supplied instance + /// using the named object definition. + /// + /// + /// The object instance that is to be so configured. + /// + /// + /// The name of the object definition expressing the dependencies that are to + /// be injected into the supplied instance. + /// + /// + public override object ConfigureObject(object target, string name) + { + // always configure object relative to contextPath + using (new HttpContextSwitch(contextPath)) + { + return base.ConfigureObject(target, name); + } + } + + /// + /// Disposes all 'request'-scoped objects at the end of each Request + /// + private static void OnEndRequest(HttpContext context) + { + IDictionary items = context.Items[OBJECTTABLEKEY] as IDictionary; + + if (items != null) + { + log.Debug("disposing 'request'-scoped item cache"); + ArrayList keys = new ArrayList(items.Keys); + for (int i = 0; i < keys.Count; i++) + { + IDisposable d = items[keys[i]] as IDisposable; + if (d != null) + { + d.Dispose(); + } + } + } + } + + /// + /// Disposes all 'session'-scoped objects at the end of a session + /// + private static void OnEndSession(HttpSessionState session, CacheItemRemovedReason reason) + { + IDictionary items = null; + try + { + items = session[OBJECTTABLEKEY] as IDictionary; + } + catch + { + // ignore exceptions while accessing session + } + if (items != null) + { + log.Debug("disposing 'session'-scoped item cache"); + object key = null; + try + { + ArrayList keys = new ArrayList(items.Keys); + for (int i = 0; i < keys.Count; i++) + { + key = keys[i]; + IDisposable d = items[key] as IDisposable; + if (d != null) + { + d.Dispose(); + } + } + } + catch (Exception ex) + { + log.Fatal(string.Format("error during disposing session item with key '{0}'", key), ex); + } + } + } + + /// + /// Creates a case insensitive hashtable instance + /// + private static IDictionary CreateCaseInsensitiveDictionary() + { + return CollectionsUtil.CreateCaseInsensitiveHashtable(); + } + + /// + /// Creates a case sensitive hashtable instance + /// + private static IDictionary CreateCaseSensitiveDictionary() + { + return new Hashtable(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectUtils.cs b/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectUtils.cs new file mode 100644 index 00000000..d275a148 --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Support/WebObjectUtils.cs @@ -0,0 +1,293 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if !NET_1_1 +using System.Web.Compilation; +#endif +#if NET_1_1 +using System.Reflection; +using System.Web.UI; +#endif +using System; +using System.IO; +using System.Web; +using Common.Logging; +using Spring.Util; + +namespace Spring.Objects.Factory.Support +{ + /// + /// Miscellaneous utility methods to support web functionality within Spring.Objects + /// + /// Aleksandar Seovic + /// $Id: WebObjectUtils.cs,v 1.3 2008/01/27 23:29:55 oakinger Exp $ + public sealed class WebObjectUtils + { + private static ILog s_log = LogManager.GetLogger(typeof(WebObjectUtils)); + + // CLOVER:OFF + +#if NET_1_1 + // Required method for resolving control types + private static MethodInfo miGetCompiledUserControlType = null; + + static WebObjectUtils() + { + Type tUserControlParser = typeof(System.Web.UI.UserControl).Assembly.GetType("System.Web.UI.UserControlParser"); + miGetCompiledUserControlType = + tUserControlParser.GetMethod("GetCompiledUserControlType", BindingFlags.Static | BindingFlags.NonPublic); + } +#endif + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private WebObjectUtils() + {} + + // CLOVER:ON + + + /// + /// Creates an instance of the ASPX page + /// referred to by the supplied . + /// + /// + /// The URL of the ASPX page. + /// + /// Page instance. + /// + /// If this method is not called in the scope of an active web session + /// (i.e. the implementation this method depends on this code executing + /// in the environs of a running web server such as IIS); or if the + /// page could not be instantiated (for whatever reason, such as the + /// ASPX not actually existing). + /// + public static IHttpHandler CreatePageInstance(string pageUrl) + { + if (s_log.IsDebugEnabled) + { + s_log.Debug("creating page instance '" + pageUrl + "'"); + } + if (HttpContext.Current == null) + { + throw new ObjectCreationException( + "Unable to instantiate page. HttpContext is not defined."); + } + IHttpHandler page = null; + try + { +#if NET_1_1 + HttpContext context = HttpContext.Current; + string physicalPath = context.Server.MapPath(pageUrl); + s_log.Debug(string.Format("constructing page virtual path '{0}' from physical file '{1}'", pageUrl, physicalPath)); + page = PageParser.GetCompiledPageInstance(pageUrl, physicalPath, context); +#else + HttpContext ctx = HttpContext.Current; + if (ctx == null) + { + throw new ObjectCreationException("Unable to get page type. HttpContext is not defined."); + } + + string rootedVPath = WebUtils.CombineVirtualPaths(ctx.Request.CurrentExecutionFilePath, pageUrl); + if (s_log.IsDebugEnabled) + { + s_log.Debug("page vpath is " + rootedVPath); + } + + page = BuildManager.CreateInstanceFromVirtualPath(rootedVPath, + typeof(IHttpHandler)) as IHttpHandler; +#endif + } + catch (HttpException httpEx) + { + string msg = String.Format("Unable to instantiate page [{0}]: {1}", pageUrl, httpEx.Message); + if(httpEx.GetHttpCode() == 404) + { + throw new FileNotFoundException(msg); + } + s_log.Error(msg, httpEx); + throw new ObjectCreationException(msg, httpEx); + } + catch (Exception ex) + { + // in case of FileNotFound recreate the exception for clarity + FileNotFoundException fnfe = ex as FileNotFoundException; + if (fnfe != null) + { + string fmsg = String.Format("Unable to instantiate page [{0}]: The file '{1}' does not exist.", pageUrl, fnfe.Message); + throw new FileNotFoundException(fmsg); + } + + string msg = String.Format("Unable to instantiate page [{0}]", pageUrl); + s_log.Error(msg, ex); + throw new ObjectCreationException(msg, ex); + } + return page; + } + + /// + /// Returns the of the ASPX page + /// referred to by the supplied . + /// + /// + ///

    + /// As indicated by the exception that can be thrown by this method, + /// the ASPX page referred to by the supplied + /// does have to be instantiated in order to determine its + /// see cref="System.Type"/> + ///

    + ///
    + /// + /// The filename of the ASPX page. + /// + /// + /// The of the ASPX page + /// referred to by the supplied . + /// + /// + /// If the supplied is or + /// contains only whitespace character(s). + /// + /// + /// If this method is not called in the scope of an active web session + /// (i.e. the implementation this method depends on this code executing + /// in the environs of a running web server such as IIS); or if the + /// page could not be instantiated (for whatever reason, such as the + /// ASPX not actually existing). + /// + public static Type GetPageType(string pageUrl) + { + AssertUtils.ArgumentHasText(pageUrl, "pageUrl"); + + HttpContext ctx = HttpContext.Current; + if (ctx == null) + { + throw new ObjectCreationException("Unable to get page type. HttpContext is not defined."); + } + + try + { + Type pageType = GetCompiledPageType(pageUrl); + return pageType; + } + catch (Exception ex) + { + string msg = String.Format("Unable to get page type for url [{0}]", pageUrl); + s_log.Error(msg, ex); + throw new ObjectCreationException(msg, ex); + } + } + + /// + /// Calls the underlying ASP.NET infrastructure to obtain the compiled page type + /// relative to the current . + /// + /// + /// The filename of the ASPX page relative to the current + /// + /// + /// The of the ASPX page + /// referred to by the supplied . + /// + public static Type GetCompiledPageType(string pageUrl) + { + if (s_log.IsDebugEnabled) + { + s_log.Debug("getting page type for " + pageUrl); + } + + string rootedVPath = WebUtils.CombineVirtualPaths(HttpContext.Current.Request.CurrentExecutionFilePath, pageUrl); + if (s_log.IsDebugEnabled) + { + s_log.Debug("page vpath is " + rootedVPath); + } + + Type pageType = null; +#if NET_2_0 + pageType = BuildManager.GetCompiledType(rootedVPath); // requires rooted virtual path! +#else + pageType = CreatePageInstance(pageUrl).GetType(); +#endif + + if (s_log.IsDebugEnabled) + { + s_log.Debug(string.Format("got page type '{0}' for vpath '{1}'", pageType.FullName, rootedVPath)); + } + return pageType; + } + + + /// + /// Gets the controls type from a given filename + /// + public static Type GetControlType(string controlName) + { + AssertUtils.ArgumentHasText(controlName, "controlName"); + if (s_log.IsDebugEnabled) + { + s_log.Debug("getting control type for " + controlName); + } + + HttpContext ctx = HttpContext.Current; + if (ctx == null) + { + throw new ObjectCreationException("Unable to get control type. HttpContext is not defined."); + } + + string rootedVPath = WebUtils.CombineVirtualPaths(ctx.Request.CurrentExecutionFilePath, controlName); + + if (s_log.IsDebugEnabled) + { + s_log.Debug("control vpath is " + rootedVPath); + } + + Type controlType = null; + try + { +#if NET_2_0 + controlType = BuildManager.GetCompiledType(rootedVPath); // requires rooted virtual path! +#else + controlType = (Type) miGetCompiledUserControlType.Invoke(null, new object[] { rootedVPath, null, ctx }); +#endif + } + catch(HttpException httpEx) + { + // for better error-handling suppress 404 HttpExceptions here + if (httpEx.GetHttpCode() == 404) + { + throw new FileNotFoundException(string.Format("Control '{0}' does not exist", rootedVPath)); + } + throw; + } + + if (s_log.IsDebugEnabled) + { + s_log.Debug(string.Format("got control type '{0}' for vpath '{1}'", controlType.FullName, rootedVPath)); + } + return controlType; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectsNamespaceParser.cs b/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectsNamespaceParser.cs new file mode 100644 index 00000000..e83cb4ba --- /dev/null +++ b/src/Spring/Spring.Web/Objects/Factory/Xml/WebObjectsNamespaceParser.cs @@ -0,0 +1,206 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Web; +using System.Xml; + +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// A custom implementation of the + /// + /// interface that properly handles web application specific attributes, + /// such as object scope. + /// + /// + ///

    + /// Parses object definitions according to the standard Spring.NET schema. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: WebObjectsNamespaceParser.cs,v 1.1 2007/08/07 23:22:30 markpollack Exp $ + /// + public class WebObjectsNamespaceParser : ObjectsNamespaceParser + { + private IObjectDefinitionFactory objectDefinitionFactory; + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public WebObjectsNamespaceParser() + { + objectDefinitionFactory = new WebObjectDefinitionFactory(); + } + + #endregion + + /// + /// Parses an object definition and set various web related properties + /// if the definition is an . + /// + /// The object definition element. + /// The id / name of the object definition. + /// the parser helper + /// The object (definition). + /// + ///

    + /// The 'various web related properties' currently includes the + /// intended scope of the object. + ///

    + ///
    + /// + /// + protected override IConfigurableObjectDefinition ParseObjectDefinition( + XmlElement element, string id, ObjectDefinitionParserHelper parserHelper) + { + parserHelper.ReaderContext.ObjectDefinitionFactory = objectDefinitionFactory; + IConfigurableObjectDefinition definition = base.ParseObjectDefinition(element, id, parserHelper); + IWebObjectDefinition webDefinition = definition as IWebObjectDefinition; + + if (webDefinition != null) + { + webDefinition.Scope = GetScope(element.GetAttribute(ObjectDefinitionConstants.ScopeAttribute)); + + // force request and session scoped objects to be lazily initialized... + if (webDefinition.Scope != ObjectScope.Application) + { + definition.IsLazyInit = true; + } + + string typeName = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute); + if (typeName.EndsWith(".ascx") || typeName.EndsWith(".master")) + { + definition.IsAbstract = true; + } + } + + return definition; + } + + /// + /// Calculates an id for an object definition. + /// + /// + /// The element containing the object definition. + /// + /// + /// The list of names defined for the object; may be + /// or even empty. + /// + /// + /// A calculated object definition id. + /// + /// . + protected override string CalculateId(XmlElement element, ArrayList aliases) + { + string id = null; + string strTypeName = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute).ToLower(); + if (strTypeName.EndsWith(".aspx")) + { + string url = element.GetAttribute(ObjectDefinitionConstants.TypeAttribute); + //id = WebUtils.GetPageName(url); + //Type pageType = WebUtils.GetPageType(url); + System.Web.UI.Page page = (System.Web.UI.Page)WebObjectUtils.CreatePageInstance(url); +#if NET_2_0 + id = page.AppRelativeVirtualPath.Substring(1); +#else + string appPath = HttpContext.Current.Request.ApplicationPath.TrimEnd('\\', '/'); + id = page.TemplateSourceDirectory.TrimEnd('\\','/') + "/" + WebUtils.GetPageName(url) + ".aspx"; + if (id.ToLower().StartsWith(appPath.ToLower())) + { + id = id.Substring(appPath.Length); + } +#endif + for(int ai=0;ai + /// Gets the scope out of the supplied . + /// + /// + ///

    + /// If the supplied is invalid + /// (i.e. it does not resolve to one of the + /// values), + /// then the return value of this method call will be + /// ; + /// no exception will be raised (although the value of the invalid + /// scope will be logged). + ///

    + ///
    + /// The string containing the scope name. + /// The scope. + /// + private ObjectScope GetScope(string value) + { + ObjectScope scope = ObjectScope.Default; + if (StringUtils.HasText(value)) + { + try + { + scope = (ObjectScope) Enum.Parse(typeof(ObjectScope), value, true); + } + catch (ArgumentException ex) + { + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug(string.Format("Error while parsing object scope : '{0}' is an invalid value.", + value), ex); + } + + #endregion + } + } + return scope; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Spring.Web.2003.csproj b/src/Spring/Spring.Web/Spring.Web.2003.csproj new file mode 100644 index 00000000..fb6e4f56 --- /dev/null +++ b/src/Spring/Spring.Web/Spring.Web.2003.csproj @@ -0,0 +1,672 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Web/Spring.Web.2005.csproj b/src/Spring/Spring.Web/Spring.Web.2005.csproj new file mode 100644 index 00000000..426d2008 --- /dev/null +++ b/src/Spring/Spring.Web/Spring.Web.2005.csproj @@ -0,0 +1,290 @@ + + + Local + 8.0.50727 + 2.0 + {BA4789EB-281A-48EA-8763-28B9F0596A18} + Debug + AnyCPU + + + + + Spring.Web + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + ..\..\..\build\VS.Net.2005\Spring.Web\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + Spring.Web.xml + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\..\build\VS.Net.2005\Spring.Web\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\antlr.runtime.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + System + + + + System.Data + + + + system.enterpriseservices + + + system.web + + + system.web.services + + + System.XML + + + + + CommonAssemblyInfo.cs + Code + + + Code + + + + + Code + + + + + + + + + + + Code + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + Code + + + Code + + + Code + + + + Code + + + + + + + + + ASPXCodeBehind + + + Code + + + Code + + + Code + + + + + + + ASPXCodeBehind + + + + + Code + + + + + Code + + + Code + + + + + + + + Code + + + Code + + + + + Code + + + + + + + + + Code + + + Code + + + + + Code + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + + + + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Web/Spring.Web.build b/src/Spring/Spring.Web/Spring.Web.build new file mode 100644 index 00000000..b2b2f87e --- /dev/null +++ b/src/Spring/Spring.Web/Spring.Web.build @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Web/Spring.Web.xml b/src/Spring/Spring.Web/Spring.Web.xml new file mode 100644 index 00000000..9417e17b --- /dev/null +++ b/src/Spring/Spring.Web/Spring.Web.xml @@ -0,0 +1,7820 @@ + + + + Spring.Web + + + + + This control should be used to display field-level validation errors. + + Aleksandar Seovic + Jonathan Allenby + $Id: ValidationError.cs,v 1.11 2008/03/19 12:07:14 oakinger Exp $ + + + + Provides common functionality to all validation renderer controls. + + Erich Eichinger + $Id: AbstractValidationControl.cs,v 1.1 2008/03/19 12:07:14 oakinger Exp $ + + + + Create the default + for this ValidationControl if none is configured. + + + + + Resolves the 's list of validation errors to a list + of elements containing the error messages to be rendered. + + a list containing elements + + + + Renders error messages using the specified . + + + + + + Gets or sets the provider. + + The provider. + + + + Gets or sets the validation errors renderer to use. + + + If not explicitly specified, defaults to . + + The validation errors renderer to use. + + + + Gets the MessageSource to be used for resolve error messages + + + By default, returns 's MessageSource. + + + + + Gets the , who's + shall be rendered by this control. + + + + + Create the default + for this ValidationControl if none is configured. + + + + + Provides information about a command raised from a + + Erich Eichinger + $Id: TabCommandEventArgs.cs,v 1.1 2007/07/24 13:33:27 oakinger Exp $ + + + + Initializes a new instance. + + The name of the command raised by a . + The index of the tab that raised this event. + + + + Returns the index of the tab that raised this command. + + + + + Represents ContentPlaceHolder control that can be used to define placeholders + within the master page. + + + Any content defined within this control will be treated as a default content + for the placeholder and will be rendered unless the child page overrides it + by defining matching control. + + Aleksandar Seovic + $Id: ContentPlaceholder.cs,v 1.6 2006/09/08 06:21:31 oakinger Exp $ + + + + Represents Content control that can be used to populate or override placeholders + within the master page. + + + Any content defined within this control will override default content + in the matching control + within the master page + + Aleksandar Seovic + $Id: Content.cs,v 1.6 2007/07/24 13:33:27 oakinger Exp $ + + + + An Interface for class. + + Damjan Tomic + + + + Initializes the provider. + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + The providerId attribute is mandatory. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + Adds a new membership user to the data source. + + + + A object populated with the information for the newly created user. + + + Whether or not the new user is approved to be validated. + The password answer for the new user + The user name for the new user. + The unique identifier from the membership data source for the user. + The password for the new user. + The password question for the new user. + The e-mail address for the new user. + A enumeration value indicating whether the user was created successfully. + + + + Processes a request to update the password question and answer for a membership user. + + + + true if the password question and answer are updated successfully; otherwise, false. + + + The new password question for the specified user. + The new password answer for the specified user. + The user to change the password question and answer for. + The password for the specified user. + + + + Gets the password for the specified user name from the data source. + + + + The password for the specified user name. + + + The user to retrieve the password for. + The password answer for the user. + + + + Processes a request to update the password for a membership user. + + + + true if the password was updated successfully; otherwise, false. + + + The new password for the specified user. + The current password for the specified user. + The user to update the password for. + + + + Resets a user's password to a new, automatically generated password. + + + + The new password for the specified user. + + + The user to reset the password for. + The password answer for the specified user. + + + + Updates information about a user in the data source. + + + A object that represents the user to update and the updated information for the user. + + + + Verifies that the specified user name and password exist in the data source. + + + + true if the specified username and password are valid; otherwise, false. + + + The name of the user to validate. + The password for the specified user. + + + + Clears a lock so that the membership user can be validated. + + + + true if the membership user was successfully unlocked; otherwise, false. + + + The membership user to clear the lock status for. + + + + Gets information from the data source for a user based on the unique identifier for the membership user. Provides an option to update the last-activity date/time stamp for the user. + + + + A object populated with the specified user's information from the data source. + + + The unique identifier for the membership user to get information for. + true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. + + + + Gets information from the data source for a user. Provides an option to update the last-activity date/time stamp for the user. + + + + A object populated with the specified user's information from the data source. + + + The name of the user to get information for. + true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. + + + + Gets the user name associated with the specified e-mail address. + + + + The user name associated with the specified e-mail address. If no match is found, return null. + + + The e-mail address to search for. + + + + Removes a user from the membership data source. + + + + true if the user was successfully deleted; otherwise, false. + + + The name of the user to delete. + true to delete data related to the user from the database; false to leave data related to the user in the database. + + + + Gets a collection of all the users in the data source in pages of data. + + + + A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + + + The total number of matched users. + The index of the page of results to return. pageIndex is zero-based. + The size of the page of results to return. + + + + Gets the number of users currently accessing the application. + + + + The number of users currently accessing the application. + + + + + + Gets a collection of membership users where the user name contains the specified user name to match. + + + + A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + + + The total number of matched users. + The index of the page of results to return. pageIndex is zero-based. + The user name to search for. + The size of the page of results to return. + + + + Gets a collection of membership users where the e-mail address contains the specified e-mail address to match. + + + + A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + + + The total number of matched users. + The index of the page of results to return. pageIndex is zero-based. + The e-mail address to search for. + The size of the page of results to return. + + + + Gets the friendly name used to refer to the provider during configuration. + + + + The friendly name used to refer to the provider during configuration. + + + + + + Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + + + + A brief, friendly description suitable for display in administrative tools or other UIs. + + + + + + Indicates whether the membership provider is configured to allow users to retrieve their passwords. + + + + true if the membership provider is configured to support password retrieval; otherwise, false. The default is false. + + + + + + Indicates whether the membership provider is configured to allow users to reset their passwords. + + + + true if the membership provider supports password reset; otherwise, false. The default is true. + + + + + + Gets a value indicating whether the membership provider is configured to require the user to answer a password question for password reset and retrieval. + + + + true if a password answer is required for password reset and retrieval; otherwise, false. The default is true. + + + + + + The name of the application using the custom membership provider. + + + + The name of the application using the custom membership provider. + + + + + + Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out. + + + + The number of invalid password or password-answer attempts allowed before the membership user is locked out. + + + + + + Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. + + + + The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. + + + + + + Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name. + + + + true if the membership provider requires a unique e-mail address; otherwise, false. The default is true. + + + + + + Gets a value indicating the format for storing passwords in the membership data store. + + + + One of the values indicating the format for storing passwords in the data store. + + + + + + Gets the minimum length required for a password. + + + + The minimum length required for a password. + + + + + + Gets the minimum number of special characters that must be present in a valid password. + + + + The minimum number of special characters that must be present in a valid password. + + + + + + Gets the regular expression used to evaluate a password. + + + + A regular expression used to evaluate a password. + + + + + + + + Erich Eichinger + $Id: ConfigurableSqlProfileProvider.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ + + + + An Interface for class. + + +

    + Configuration for this provider requires providerId element set in web.config file, + as the Id of wrapped provider (defined in the Spring context). +

    +
    + Damjan Tomic +
    + + + Initializes the provider. + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + The providerId attribute is mandatory. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + Returns the collection of settings property values for the specified application instance and settings property group. + + + + A containing the values for the specified settings property group. + + + A describing the current application use. + A containing the settings property group whose values are to be retrieved.2 + + + + Sets the values of the specified group of property settings. + + + A describing the current application usage. + A representing the group of property settings to set.2 + + + + When overridden in a derived class, deletes profile properties and information for the supplied list of profiles. + + + + The number of profiles deleted from the data source. + + + A of information about profiles that are to be deleted. + + + + When overridden in a derived class, deletes profile properties and information for profiles that match the supplied list of user names. + + + + The number of profiles deleted from the data source. + + + A string array of user names for profiles to be deleted. + + + + When overridden in a derived class, deletes all user-profile data for profiles in which the last activity date occurred before the specified date. + + + + The number of profiles deleted from the data source. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are deleted. + A that identifies which user profiles are considered inactive. If the value of a user profile occurs on or before this date and time, the profile is considered inactive. + + + + When overridden in a derived class, returns the number of profiles in which the last activity date occurred on or before the specified date. + + + + The number of profiles in which the last activity date occurred on or before the specified date. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + A that identifies which user profiles are considered inactive. If the of a user profile occurs on or before this date and time, the profile is considered inactive. + + + + When overridden in a derived class, retrieves user profile data for all profiles in the data source. + + + + A containing user-profile information for all profiles in the data source. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + When this method returns, contains the total number of profiles. + The index of the page of results to return. + The size of the page of results to return. + + + + When overridden in a derived class, retrieves user-profile data from the data source for profiles in which the last activity date occurred on or before the specified date. + + + + A containing user-profile information about the inactive profiles. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + A that identifies which user profiles are considered inactive. If the of a user profile occurs on or before this date and time, the profile is considered inactive. + When this method returns, contains the total number of profiles. + The index of the page of results to return. + The size of the page of results to return. + + + + When overridden in a derived class, retrieves profile information for profiles in which the user name matches the specified user names. + + + + A containing user-profile information for profiles where the user name matches the supplied usernameToMatch parameter. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + When this method returns, contains the total number of profiles. + The index of the page of results to return. + The user name to search for. + The size of the page of results to return. + + + + When overridden in a derived class, retrieves profile information for profiles in which the last activity date occurred on or before the specified date and the user name matches the specified user name. + + + + A containing user profile information for inactive profiles where the user name matches the supplied usernameToMatch parameter. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + A that identifies which user profiles are considered inactive. If the value of a user profile occurs on or before this date and time, the profile is considered inactive. + When this method returns, contains the total number of profiles. + The index of the page of results to return. + The user name to search for. + The size of the page of results to return. + + + + Gets the friendly name used to refer to the provider during configuration. + + + + The friendly name used to refer to the provider during configuration. + + + + + Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + + + + A brief, friendly description suitable for display in administrative tools or other UIs. + + + + + Gets or sets the name of the currently running application. + + + + A that contains the application's shortened name, which does not contain a full path or extension, for example, SimpleAppSettings. + + 2 + + + + Initializes the provider. + + + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + + Values may be overridden by specifying them in list. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + The ConnectionString to be used + + + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + + + + + Culture resolver that uses request headers to determine culture. If no languages + are specified in the request headers, it returns default culture specifed, and + if no default culture was specifed it returns current culture for the executing + server thread. + + + Note: This culture resolver cannot be used to change the culture + because request headers cannot be modified. In order to change the culture + when using this culture resolver user has to change language settings in + the web browser. + + Aleksandar Seovic + $Id: RequestCultureResolver.cs,v 1.4 2007/08/25 14:26:35 oakinger Exp $ + + + + Default culture resolver for web applications. Contains some common utility methods for web culture resolvers. + + Aleksandar Seovic + $Id: DefaultWebCultureResolver.cs,v 1.5 2007/08/25 14:26:35 oakinger Exp $ + + + + Returns default culture. If property is not set, + it tries to get culture from the request headers + and falls back to a current thread's culture if no headers are available. + + Default culture to use. + + + + Extracts the users favorite language from "accept-language" header of the current request. + + a language string if any or null, if no languages have been sent with the request + + + + Resolves a culture by name. + + the name of the culture to get + a (possible neutral!) or null, if culture could not be resolved + + + + Resolves the culture from the context. + + Culture that should be used to render view. + + + + Not supported for this implementation. + + The new culture or null to clear the culture. + + + + Tries to determine culture from the request headers. If no languages + are specified in the request headers, it returns default culture specifed, and + if no default culture was specifed it returns current culture for the executing + server thread. + + Culture that should be used to render view. + + + + Not supported for this resolver implementation + + The new culture or null to clear the culture. + + + + This interface should be implemented by all validation errors renderers. + + + + Validation errors renderers are used to decouple rendering behavior from the + validation errors controls such as and + . + + + This allows users to change how validation errors are rendered by simply pluggin in + appropriate renderer implementation into the validation errors controls using + Spring.NET dependency injection. + + + Aleksandar Seovic + $Id: IValidationErrorsRenderer.cs,v 1.1 2007/08/02 19:50:28 markpollack Exp $ + + + + Renders validation errors using specified . + + Web form instance. + An HTML writer to use. + The list of validation errors. + + + + Represents Content control that can be used to populate or override placeholders + anywhere within the page. + + + + Any content defined within this control will override default content + in the matching control specified by anywhere + within the page. + + + In contrast to control, ContentReplacer can replace the content of + any control within the current page - it is not limited to replacing ContentPlaceholders on master pages. + + + This technique is useful if you want to group e.g. rendering navigation elements on 1 ascx control, but your + design requires navigation elements to be distributed across different places within the HTML code. + + + Erich Eichinger + $Id: ContentReplacer.cs,v 1.3 2007/07/24 13:33:27 oakinger Exp $ + + + + Overriden to correctly redirect rendermethod calls. + + + + + Renders child controls + + + + + Render nothing. + + + + + Specifies the unique id of the control, who's content is to be replaced. + + + + + Implementation of that retrieves + configured instances from Spring web application context. + + + + This handler factory uses the page name from the URL, without the extension, + to find the handler object in the Spring context. This means that the target object + definition doesn't need to resolve to an .aspx page -- it can be any valid + object that implements interface. + + + If the specified page is not found in the Spring application context, this + handler factory falls back to the standard ASP.NET behavior and tries + to find physical page with a given name. + + + In either case, handlers that implement + and will be provided with the references + to appropriate Spring.NET application context (based on the request URL) + and a that should be used to store the information + that needs to be shared by all instances of the handler. + + + Aleksandar Seovic + $Id: PageHandlerFactory.cs,v 1.41 2008/05/15 12:35:27 oakinger Exp $ + + + + Provides base functionality for Spring.NET context-aware + implementations. + + +

    + Provides derived classes with a default implementation of + method. +

    +
    + Aleksandar Seovic + $Id: AbstractHandlerFactory.cs,v 1.5 2008/03/19 18:05:08 oakinger Exp $ +
    + + + Creates a new instance of the + class. + + +

    + This is an abstract class and as such provides no public constructors. +

    +
    +
    + + + Returns an appropriate implementation. + + + An instance of the class that + provides references to intrinsic server objects. + + + The HTTP method of the request. + + The request URL. + + The physical path of the requested resource. + + + A new object that processes + the request. + + + + + Enables a factory to release an existing + instance. + + + The object to release. + + + + + DO NOT USE - this is subject to change! + + + + + This method requires registrars to follow the convention of registering web object definitions using their + application relative urls (~/mypath/mypage.aspx). + + + Resolve an object definition by url. + + + + + DO NOT USE - this is subject to change! + + + + + DO NOT USE + + + + + DO NOT USE + + + + + DO NOT USE + + + + + Retrieves instance of the configured page from Spring web application context, + or if page is not defined in Spring config file tries to find it using standard + ASP.Net mechanism. + + Current HttpContext + Type of HTTP request (GET, POST, etc.) + Requested page URL + Translated server path for the page + Instance of the IHttpHandler object that should be used to process request. + + + + Wrapper for handlers that do not require . + + + NOTE: This class has to extend System.Web.UI.Page instead of simply + implementing IHttpHandler in order for Server.Transfer to work properly. + This in turn requires explicit IHttpHandler implementation in order to + override non-virtual methods from the base Page class. + + + + + Initializes a new instance of the class. + + Application context instance to retrieve page from. + Name of the page object to execute. + Requested page URL. + Translated server path for the page. + + + + Initializes a new instance of the class. + + Application context instance to retrieve page from. + Name of the page object to execute. + + + + Creates a page instance corresponding to this handler's url. + + + + + Gets or - if not found - creates a process handler instance. + + + + + Apply dependency injection stuff on the handler. + + + + + + Applies to the given handler if applicable. + + + + + Checks, if page has been recompiled. Creates/discards handlerState if necessary. + + + + + + Use for sync access to this PageHandler instance. + + + + + Gets that contains handler state. + + + This will be assigned to the SharedState + property of instances that implement + interface. + + + + + Returns true because this wrapper handler can be reused. + Actual page is instantiated at the beginning of the ProcessRequest method. + + + + + Wrapper for handlers that require . + + + Delays page object instantiation until ProcessRequest is called + in order to be able to access session state. + + + + + Initializes a new instance of the class. + + Application context instance to retrieve page from. + Name of the page object to execute. + Requested page URL. + Translated server path for the page. + + + + Initializes a new instance of the class. + + Application context instance to retrieve page from. + Name of the page object to execute. + + + + An implementation that + retrieves configured WebService objects from the Spring.NET web + application context. + + + This handler factory uses web service name from the URL, without the extension, + to find web service object in the Spring context. + + Aleksandar Seovic + $Id: WebServiceHandlerFactory.cs,v 1.12 2008/03/19 18:05:08 oakinger Exp $ + + + + Retrieves instance of the page from Spring web application context. + + current HttpContext + type of HTTP request (GET, POST, etc.) + requested page URL + translated server path for the page + instance of the configured page object + + + + Interface that should be implemented by all s + that want to be aware of the they belong to. + + Aleksandar Seovic + $Id: IProcessAware.cs,v 1.2 2006/05/18 21:37:52 markpollack Exp $ + + + + Gets or sets a process instance. + + + + + An abstract base class that defines common behavior for different process implementations. + + Aleksandar Seovic + $Id: AbstractProcessHandler.cs,v 1.1 2007/08/03 08:31:25 oakinger Exp $ + + + + An interface that different process implementations need to support. + + + + + Starts the process. + + Referrer URL. + + + + Resolves view for the specified view name. + + Name of the view to go to. + + + + Ends the process. + + + + + Unique ID of this process instance. + + + + + Controller for the component. + + + Process controller will be shared by all the views + that belong to this process. + + + + + Gets the name of the current view. + + + + + Gets the the flag that indicates if selected view + has changed during the current request. + + + + + This interface should be implemented by s that want to + have access to the shared state for the handler. + + +

    + Shared state is very useful if you have data that needs to be shared by all instances + of the same page (or other ). +

    +

    + For example, class implements this interface, which allows + each page derived from it to cache localizalization resources and parsed data binding + expressions only once and then reuse the cached values, regardless of how many instances + of the page are created. +

    +
    +
    + + + Gets or sets the that should be used + to store shared state for the . + + + The that should be used + to store shared state for the . + + + + + Parameter name that is used for process ID. + + + + + Creates instance of the process and registers it with the . + + + + + Starts the process. + + Process URL. + + + + Resolves and sets the view for the specified view name. + + Name of the view to go to. + + + + Ends the process by unregistering it from the . + + + + + Method that needs to be implemented by specific process implementations + in order to navigate to the first view in the process. + + + + + Method that needs to be implemented by specific process implementations + in order to navigate to the current view. + + + + + Processes the request by delegating to appropriate view, which could be + another process. + + + + + + Unique ID of this component instance. + + + + + Gets or sets the parent process. + + + + + Returns a thread-safe dictionary that contains state that is shared by + all views of this component. + + + + + Controller for the component. + + + Process controller will be shared by all the views + that belong to this component. + + + + + Default view for the component. + + + + + Gets the name of the current view. + + + + + Gets the the flag that indicates if selected view + has changed during the current request. + + + + + Gets a map of process views. + + + + + Gets the process URL. + + + + + Returns true because this wrapper handler can be reused. + Actual page is instantiated at the beginning of the ProcessRequest method. + + + + + Gets or sets the application context. + + + + + Abstracts the underlying infrastructure of a HttpRequest + + + + + Maps a virtual path to it's physical location + + + + + The virtual (rooted) path of the current Application with trailing slash + + + For the site rooted applications, "/" will be returned, for all others "/..someappdir../" + + + + + The virtual (rooted) path of the current Request including + + + + + The virtual (rooted) path of the current Request without trailing + + + + + The virtual (rooted) path of the currently executing script + + + Normally this property is the same as . + In case of , this property returns the current script + whereas CurrentVirtualPath returns the original script path. + + + + + Helper class for easier access to reflected ControlCollection members. + + Erich Eichinger + $Id: ControlCollectionAccessor.cs,v 1.3 2008/05/13 23:23:03 oakinger Exp $ + + + + Returns the underlying ControlCollection instance. + + + + + Returns the type of the underlying ControlCollection instance. + + + + + Creates a new Accessor for a given . + + The to be accessed + + + + Gets or sets the owner of the underlying ControlCollection. + + + + + Custom implementation of + for web applications. + + +

    + This implementation adds support for .aspx pages and scoped objects. +

    +
    + Aleksandar Seovic + $Id: WebObjectDefinitionFactory.cs,v 1.9 2008/04/07 00:19:55 bbaia Exp $ +
    + + + Factory style method for getting concrete + + instances. + + If no parent is specified, a RootWebObjectDefinition is created, otherwise a + ChildWebObjectDefinition. + The of the defined object. + The name of the parent object definition (if any). + The against which any class names + will be resolved into instances. + + An + + instance. + + + + + implementation specifically + for resources served up from a web server. + + +

    + Uses the System.Web.HttpContext.Current.Server.MapPath + method to resolve the file name for a given resource. +

    +
    + Aleksandar Seovic + $Id: WebResource.cs,v 1.14 2008/03/14 12:02:45 oakinger Exp $ +
    + + + Creates a new instance of the class. + + + The name of the file system resource (on the server). + + + + + Resolves the handle + for the supplied . + + + The name of the file system resource. + + + The handle for this resource. + + + + + Resolves the root location for the supplied . + + + The name of the file system resource. + + + The root location of the resource. + + + + + Resolves the path for the supplied . + + + The name of the file system resource. + + + The current path of the resource. + + + + + Resolves the presence of the + value + in the supplied into a path. + + + The name of the resource. + + + The string that is a placeholder for a base path. + + + The name of the resource with any + value having been resolved into an actual path. + + + + + Does the supplied relative ? + + + The name of the resource to test. + + + if resource name is relative; + otherwise . + + + + + Factory Method. Create a new instance of the current resource type using the given resourceName + + + + + The ResourceLoader to be used for resolving relative resources + + + + + Gets those characters that are valid path separators for the + resource type. + + + Those characters that are valid path separators for the resource + type. + + + + + + Provides various support for proper handling requests. + + Erich Eichinger + $Id: WebSupportModule.cs,v 1.15 2007/08/08 17:48:19 bbaia Exp $ + + + + For webapplications always +
      +
    • convert IResources using the current context.
    • +
    • use "web" as default resource protocol
    • +
    • use as default threading storage
    • +
    +
    +
    + + + Registers this module for all events required by the Spring.Web framework + + + + + Disposes this instance + + + + + Web application context, taking the context definition files + from the file system or from URLs. + + Treats resource paths as web resources, when using + IApplicationContext.GetResource. Resource paths are considered relative + to the virtual directory. + + Note: In case of multiple config locations, later object definitions will + override ones defined in earlier loaded files. This can be leveraged to + deliberately override certain object definitions via an extra XML file. + + Aleksandar Seovic + + + + The instance for this class. + + + + + Create a new WebApplicationContext, loading the definitions + from the given XML resource. + + Names of configuration resources. + + + + Create a new WebApplicationContext, loading the definitions + from the given XML resource. + + The application context name. + Flag specifying whether to make this context case sensitive or not. + Names of configuration resources. + + + + Create a new WebApplicationContext with the given parent, + loading the definitions from the given XML resources. + + The application context name. + Flag specifying whether to make this context case sensitive or not. + The parent context. + Names of configuration resources. + + + + returns detailed instance information for debugging + + + + + + Since the HttpRuntime discards it's configurationsection cache, we maintain our own context cache. + Despite it really speeds up context-lookup, since we don't have to go through the whole HttpConfigurationSystem + + + + + EventHandler for ContextRegistry.Cleared event. Discards webContextCache. + + + + + Returns the root context of this web application + + + + + Returns the web application context for the given (absolute!) virtual path + + + + + Initializes object definition reader. + + Reader to initialize. + + + + Creates web object factory for this context using parent context's factory as a parent. + + Web object factory to use. + + + + Returns the web application context for the current request's filepath + + + + + Return an array of resource locations, referring to the XML object + definition files that this context should be built with. + + an array of resource locations, or null if none + + + + Specifies that page should be treated as a dialog, meaning that after processing + is over user should return to the referring page. + + +

    + Pages marked with this attribute will have "close" result predefined. +

    +

    + Developers should call SetResult("close") from the event handler + in order to return control back to the calling page. +

    +
    + Aleksandar Seovic + $Id: DialogAttribute.cs,v 1.3 2006/05/18 21:37:52 markpollack Exp $ +
    + + + Provides functions required for implementing validators + but are unfortunately not accessible from + + Erich Eichinger + $Id: AbstractBaseValidator.cs,v 1.1 2007/07/31 10:40:58 oakinger Exp $ + + + + Registers a javascript-block to be rendered. + + + + + Checks, if a certain javascript-block is already registered. + + + + + Adds an attribute to be rendered for clientside validation. + + + + + Is "XHTML 1.0 Transitional" rendering allowed? + + + + + Abstracts access to properties required for + handling validation and error rendering + + Erich Eichinger + $Id: IValidationContainer.cs,v 1.1 2008/03/19 12:07:15 oakinger Exp $ + + + + Gets the MessageSource to be used for + resolving error ids into messages + + + + + Gets the list of validation errors kept by this container. + + + + + A page or control must implement this interface to support spring's databinding infrastructure. + + + + + + + + + + Return the UniqueID of this page or control. + + + + + Return the where + instances should be optained from. + + + + + This event is raised to initialize bindings for . + + + + + The control allows you to build ASP.NET Web pages that present + the user with content arranged in tabular form. + + Erich Eichinger + $Id: TabularMultiView.cs,v 1.1 2007/07/24 13:33:28 oakinger Exp $ + + + + Initializes a new instance. + + + + + Initializes a new instance with the given container tag to be used for rendering. + + + + + Create the container for tab items. + + + + + Creates TabContainer and MultiView + + + + + keeps parsed views until multiView is created + + + + + Initialize this control. + + + + + Creates child controls. + + + + + Adds the element to the collection of child controls. + + + + + Called if ActiveViewIndex is changed + + + + + Set the style class of the panel containing the Tabs. + + + + + Set the style class of each Tab item. + + + + + Set the style class of the currently selected Tab item. + + + + + Set the style class of the panel containing all controls. + + + + + Gets or sets the index of the active View control within a control. + + + + + Occurs, if the active tab has changed. + + + + + This strategy replaces the original collection's owner with an intercepting proxy + + Erich Eichinger + $Id: InterceptControlCollectionOwnerStrategy.cs,v 1.1 2007/08/01 23:11:01 markpollack Exp $ + + + + Any concrete interception strategy must implement this interface + + Erich Eichinger + $Id: IInterceptionStrategy.cs,v 1.1 2007/08/01 23:11:00 markpollack Exp $ + + + + Any implementation must never throw an exception from this method. + Instead false must be returned to indicate interception failure. + + + true, if interception succeeded. false otherwise. + + + + + Web object definitions extend + by adding scope property. + + +

    + This is the most common type of object definition in ASP.Net web applications +

    +
    + Aleksandar Seovic + $Id: ChildWebObjectDefinition.cs,v 1.3 2007/08/01 23:10:46 markpollack Exp $ +
    + + + Defines additional members web object definitions need to implement. + + Aleksandar Seovic + $Id: IWebObjectDefinition.cs,v 1.2 2007/03/26 14:58:38 oakinger Exp $ + + + + Gets or sets scope for the web object (application, session or request) + + + + + Returns true if web object is .aspx page. + + + + + Gets the rooted url of the .aspx page, if object definition represents page. + + + + + Creates a new instance of the + class + for a singleton, providing property values and constructor arguments. + + Name of the parent object definition. + The class of the object to instantiate. + + The + to be applied to a new instance of the object. + + + The to be applied to + a new instance of the object. + + + + + Creates a new instance of the + class + for a singleton, providing property values and constructor arguments. + + Name of the parent object definition. + The class name of the object to instantiate. + + The + to be applied to a new instance of the object. + + + The to be applied to + a new instance of the object. + + + + + Creates a new instance of the + class + for an .aspx page, providing property values. + + Name of the parent object definition. + Name of the .aspx page to instantiate. + + The to be applied to + a new instance of the object. + + + + + A that represents the current + . + + + A that represents the current + . + + + + + Object scope. + + + + + Returns true if web object is .aspx page. + + + + + Gets the rooted url of the .aspx page, if object definition represents page. + + + + + Forces ASP pages to be treated as prototypes all the time inorder to comply with ASP.Net requirements. + + + + + Represents the method that will handle the TabCommand event. + + The source of the event. + A that contains the event data. + Erich Eichinger + $Id: TabCommandEventHandler.cs,v 1.1 2007/07/24 13:33:28 oakinger Exp $ + + + + This control should be used instead of standard HTML head tag + in order to render dynamicaly registered head scripts and stylesheets. + + Aleksandar Seovic + $Id: LocalizedImage.cs,v 1.4 2006/05/18 21:37:52 markpollack Exp $ + + + + Tries to determine full URL for the localized image before it's rendered. + + + + + + Name of the image file. + + + + + Gets a reference to the instance that contains the + server control. + + + + + + Displays a pop-up DHTML calendar. + + +

    + Credit: this control uses a slightly modified version of the + Dynarch.com DHTML Calendar, + written by Mihai Bazon. +

    +
    + Aleksandar Seovic + $Id: Calendar.cs,v 1.13 2007/12/07 21:06:35 oakinger Exp $ +
    + + + Registers necessary scripts and stylesheet. + + + An object that contains the event data. + + + + + Renders a hidden input field that stores the value for the radio button group. + + + to use for rendering. + + + + + Raises the SelectionChanged event. + + + + + Loads postback data into the control. + + + The key that should be used to retrieve data. + + The postback data collection. + if data has changed. + + + + The method that is called on postback if the date has changed. + + + The event argument (empty and unused). + + + + + Gets a reference to the instance that contains the + server control. + + + A reference to the instance that contains the + server control. + + + + + The selected date. + + The selected date. + + + + The date format that is to be used. + + A valid date format string. + + + + Is direct editing of the date allowed? + + + if direct editing of the date is allowed? + + + + The (CSS) style. + + The style for the calendar. + + + + Occurs when the value of the radio button group changes between postbacks to the server. + + + + + Miscellaneous web utility methods. + + Aleksandar Seovic + $Id: WebUtils.cs,v 1.32 2008/03/19 18:05:07 oakinger Exp $ + + + + Creates a new instance of the class. + + +

    + This is a utility class, and as such exposes no public constructors. +

    +
    +
    + + + Default protocol used for resolving resources in web applications + + + + + Extracts the bare ASPX page name without any extension from the + supplied . + + +

    + Examples of what would be returned from this method given a url would be: +

    +

    + + 'Login.aspx' => 'Login' + '~/Login.aspx' => 'Login' + '~/B2B/SignUp.aspx' => 'SignUp' + 'B2B/Foo/FooServices.aspx' => 'FooServices' + +

    +
    + The full URL to the ASPX page. + + The bare ASPX page name without any extension. + + + If the supplied is or + contains only whitespace character(s). + +
    + + + Returns only the directory portion of a virtual path + + + The returned path is guaranteed to always have a leading and a trailing slash.
    + If a path does not end with a file-extension it is assumed to be a directory +
    +
    + + + Returns absolute path that can be referenced within plain HTML. + + +

    + If relative path starts with '/' (forward slash), no concatenation will occur + and it will be assumed that the relative path specified is indeed the absolute path + and will be returned verbatim.

    +

    + Otherwise, relative path will be appended to the application path, while making sure that + path separators are not duplicated between the paths.

    +
    + Application path. + Relative path to combine with the application path. + Absolute path. +
    + + + Combines a rooted base path with a relative path. + + Must be a path starting with '/' + the path to be combined. May start with basepath Placeholder '~' + the combined path + + If relativePath starts with '~', rootPath is ignored and '~' resolves to the current AppDomain's application virtual path
    + If relativePath start with '/', rootPath is ignored and relativePath is returned as-is. +
    +
    + + + Gets the application-relative virtual path portion of the given absolute URL. + + the absolute url + the url relative to the current application's virtual path + + + + Gets the virtual path portion of the given absolute URL + relative to the given base path. + + + Base path comparison is done case insensitive. + + the absolute base path + the absolute url + the url relative to the given basePath + + + + Performs a . Original path will be restored on + + + Rewrites the current HttpContext's filepath to <directory>/currentcontext.dummy.
    + This affects resolving resources by calls to and
    + Original path is restored during . +
    + + + using( new HttpContextSwitch( "/path" ) ) + { + Response.Write( Request.FilePath ); // writes "/path/currentcontext.dummy" to response. + } + // Request.FilePath has been reset to original url here + + + Erich Eichinger + $Id: HttpContextSwitch.cs,v 1.1 2007/08/01 23:10:51 markpollack Exp $ +
    + + + Performs an immediate call to + + a directory path (without trailing filename!) + + + + Restores original path if necessary + + + + + The possible object scope values. + + Aleksandar Seovic + $Id: ObjectScope.cs,v 1.1 2007/02/17 06:57:44 bbaia Exp $ + + + + Application scope. + + + + + Session scope. + + + + + Request scope. + + + + + Default scope (currently + ). + + + + + + Represents a MIME media type as defined by http://www.iana.org/assignments/media-types/ + + Erich Eichinger + $Id: MimeMediaType.cs,v 1.1 2007/11/28 23:26:10 oakinger Exp $ + + + + Parses a string into a instance. + + a valid string representation of a mediaType + a new instance + + + + Creates a new media type instance representing a generic "application/octet-stream" + + + + + Creates a new media type instance representing a generic content type + with an unspecified subtype (e.g. "text/*") + + + + + Creates a new media type instance representing a particular media type + + + + + Returns a string representation of this instance. + + + + + Compares this instance to another + + another instance + + + + Determines whether the specified is equal to the current . + + + true if the specified is equal to the current ; otherwise, false. + + The to compare with the current . + + + + Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + + + A hash code for the current . + + + + + Gets the content type of this media type instance + + + + + Gets the subtype of this media type instance + + + + + Predefines common application media types + + + + + Represents "application/octet-stream" + + + + + Represents "application/pdf" + + + + + Represents "application/rtf" + + + + + Represents "application/soap+xml" + + + + + Represents "application/zip" + + + + + Predefines common image media types + + + + + Represents "image/gif" + + + + + Represents "image/jpeg" + + + + + Represents "image/tiff" + + + + + Predefines common text media types + + + + + Represents "text/html" + + + + + Represents "text/plain" + + + + + Represents "text/richtext" + + + + + Represents "text/xml" + + + + + Represents "text/javascript" + + + + + Represents "text/css" + + + + + Creates an instance + using context definitions supplied in a custom configuration and + configures the with that instance. + + + This class extends ContextHandler with + web specific behaviour. It uses WebApplicationContext + as default Context-Type. + + Erich Eichinger + $Id: WebContextHandler.cs,v 1.8 2008/03/13 20:07:43 bbaia Exp $ + + + + Gets the context name from the given + + + + + Throws a configuration exception, if child contexts are specified. + + + Nesting contexts in webapplications is done by explicitly declaring + spring context sections for each directory. + + + + + Handles web specific details of context instantiation. + + + + + Sets default context type to + + + + + Sets default case-sensitivity to 'false' for web-applications + + + + + This strategy enhances a ControlCollection's type by + dynamically implementing ISupportsWebDependencyInjection on this type + + Erich Eichinger + $Id: InterceptControlCollectionStrategy.cs,v 1.2 2008/05/13 14:22:47 oakinger Exp $ + + + + The list of methods to be intercepted for a ControlCollection + + + + + Holds a table of already known intercepted ControlCollection types + + + + + Intercepts the given by dynamically deriving + the original type and let it implement . + + the ApplicationContext to be set on the collection instance. + a wrapper around the owner control instance. + a wrapper around the collection instance. + true, if interception was successful. false otherwise + + + + Holds a reference to the static(!) callback-method to be used during generation of intercepted ControlCollection-Types + + + + + A spring configurable version of + + Erich Eichinger + $Id: ConfigurableSqlMembershipProvider.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ + + + + Initializes the provider. + + + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + + Values may be overridden by specifying them in list. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + The ConnectionString to be used + + + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + + + + + An implementation backed by ASP.NET Cache (see ). + + + + Because ASP.NET Cache uses strings as cache keys, you need to ensure + that the key object type has properly implemented ToString method. + + + Despite the shared underlying , it is possible to use more than + one instance of without interfering each other. + + + Aleksandar Seovic + Erich Eichinger + $Id: AspNetCache.cs,v 1.6 2007/09/14 18:26:26 oakinger Exp $ + + + + Initializes a new instance of + + + + + Initializes a new instance of + with the specified implementation. + + + + + + Retrieves an item from the cache. + + + Item key. + + + Item for the specified , or null. + + + + + Removes an item from the cache. + + + Item key. + + + + + Inserts an item into the cache. + + + Items inserted using this method have default and default + + + Item key. + + + Item value. + + + Item's time-to-live (TTL). + + + + + Inserts an item into the cache. + + + Item key. + + + Item value. + + + Item's time-to-live. + + + Flag specifying whether the item's time-to-live should be reset + when the item is accessed. + + + + + Inserts an item into the cache. + + + Item key. + + + Item value. + + + Item's time-to-live. + + + Flag specifying whether the item's time-to-live should be reset + when the item is accessed. + + + Item priority. + + + + + Generate a key to be used for the underlying implementation. + + + + + + + Gets/Sets a flag, whether to use sliding expiration policy. + + + + + Gets/Sets a default priority to be applied to all items inserted into this cache. + + + + + Gets a collection of all cache item keys. + + + + + Abstracts the underlying runtime cache + + + + + Insert an item into the cache. + + + + + Removes an item from the cache. + + The key of the item to remove + The object that has been removed from the cache + + + + Retrieve an item with the specified key from the cache. + + The key of the item to be retrieved + The item, if found. null otherwise + + + + Actually delegates all calls to the underlying + + + + + This panel provides the same features as , but provides additional means with regards to Spring. + + + In some cases, automatic dependency injection can cause performance problems. In this case,you can use a Panel for finer-grained control + over which controls are to be injected or not. + + Erich Eichinger + $Id: Panel.cs,v 1.3 2008/03/09 15:20:37 oakinger Exp $ + + + + "Contract"-interface of the Web-DI infrastructure. + + + This interface supports Spring's DI infrastructure and normally doesn't need to be implemented
    +
    + Any Page, Control or ControlCollection implementing this interface guarantees to + call on any control being added + before it is actually added to the child-collection. +
    + +

    The following example shows, how to make a Control support the DI-infrastructure:

    + + class MyControl : Control, ISupportsWebDependencyInjection + { + private IApplicationContext _defaultApplicationContext; + + public IApplicationContext DefaultApplicationContext + { + get { return _defaultApplicationContext; } + set { _defaultApplicationContext = value; } + } + + override protected AddedControl( Control control, int index ) + { + WebUtils.InjectDependenciesRecursive( _defaultApplicationContext, control ); + base.AddedControl( control, index ); + } + } + +
    + +

    The following example shows, how to make a ControlCollection support the DI-infrastructure:

    +

    Note, that you MUST implement the single-argument constructor ControlCollection( Control owner )!

    + + class MyControlCollection : ControlCollection, ISupportsWebDependencyInjection + { + private IApplicationContext _defaultApplicationContext; + + public MyControlCollection( Control owner ) : base( owner ) + {} + + public IApplicationContext DefaultApplicationContext + { + get { return _defaultApplicationContext; } + set { _defaultApplicationContext = value; } + } + + override public Add( Control child ) + { + WebUtils.InjectDependenciesRecursive( _defaultApplicationContext, child ); + base.Add( child ); + } + + override public AddAt( int index, Control child ) + { + WebUtils.InjectDependenciesRecursive( _defaultApplicationContext, child ); + base.AddAt( index, child ); + } + } + +
    + Erich Eichinger + $Id: ISupportsWebDependencyInjection.cs,v 1.2 2007/08/01 23:11:00 markpollack Exp $ +
    + + + Holds the default instance to be used during injecting a control-tree. + + + + + Overridden to suppress rendering this control's tag + + + + + + This method is invoked right before InitRecursive() is called for this Panel and + ensures, that dependencies get injected on child controls before their Init event is raised. + + + + + Injects dependencies into control before adding it. + + + + + Overridden to automatically aquire a reference to + root application context to use for DI. + + + + + This flag controls, whether DI on child controls will be done or not + + By default, DI will be done + + + + This flag controls, whether this Panel's tag will be rendered. + + + + + Sets / Gets the ApplicationContext instance used to configure this control + + + + + Wraps a Control to make it DI aware + + Erich Eichinger + $Id: SupportsWebDependencyInjectionOwnerProxy.cs,v 1.2 2008/05/13 14:22:47 oakinger Exp $ + + + + Wraps a control to make it DI aware + + + + + + + Performs DI before adding the control to it's parent + + + + + + + Delegates call to decorated control + + + + + + An Interface for class. + + +

    + Configuration for this provider requires providerId element set in web.config file, + as the Id of wrapped provider (defined in the Spring context). +

    +
    + Damjan Tomic +
    + + + Initializes the provider. + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + The providerId attribute is mandatory. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + Retrieves a object that represents the currently requested page using the specified object. + + + + A that represents the currently requested page; otherwise, null, if no corresponding can be found in the or if the page context is null. + + + The used to match node information with the URL of the requested page. + + + + Retrieves a object based on a specified key. + + + + A that represents the page identified by key; otherwise, null, if no corresponding is found or if security trimming is enabled and the cannot be returned for the current user. The default is null. + + + A lookup key with which a is created. + + + + When overridden in a derived class, retrieves a object that represents the page at the specified URL. + + + + A that represents the page identified by rawURL; otherwise, null, if no corresponding is found or if security trimming is enabled and the cannot be returned for the current user. + + + A URL that identifies the page for which to retrieve a . + + + + When overridden in a derived class, retrieves the child nodes of a specific . + + + + A read-only that contains the immediate child nodes of the specified ; otherwise, null or an empty collection, if no child nodes exist. + + + The for which to retrieve all child nodes. + + + + Provides an optimized lookup method for site map providers when retrieving the node for the currently requested page and fetching the parent and ancestor site map nodes for the current page. + + + + A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + + + The number of ancestor site map node generations to get. A value of -1 indicates that all ancestors might be retrieved and cached by the provider. + upLevel is less than -1. + + + + Provides an optimized lookup method for site map providers when retrieving the node for the currently requested page and fetching the site map nodes in the proximity of the current node. + + + + A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + + + The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors might be retrieved and cached by the provider. + The number of child generations to fetch. 0 indicates no descendant nodes are retrieved and a -1 indicates that all descendant nodes might be retrieved and cached by the provider. + upLevel or downLevel is less than -1. + + + + When overridden in a derived class, retrieves the parent node of a specific object. + + + + A that represents the parent of node; otherwise, null, if the has no parent or security trimming is enabled and the parent node is not accessible to the current user. + + + The for which to retrieve the parent node. + + + + Provides an optimized lookup method for site map providers when retrieving an ancestor node for the currently requested page and fetching the descendant nodes for the ancestor. + + + + A that represents an ancestor of the currently requested page; otherwise, null, if the current or ancestor is not found or cannot be returned for the current user. + + + The number of descendant node levels to retrieve from the target ancestor node. + The number of ancestor node levels to traverse when retrieving the requested ancestor node. + walkupLevels or relativeDepthFromWalkup is less than 0. + + + + Provides an optimized lookup method for site map providers when retrieving an ancestor node for the specified object and fetching its child nodes. + + + + A that represents an ancestor of node; otherwise, null, if the current or ancestor is not found or cannot be returned for the current user. + + + The number of descendant node levels to retrieve from the target ancestor node. + The that acts as a reference point for walkupLevels and relativeDepthFromWalkup. + The number of ancestor node levels to traverse when retrieving the requested ancestor node. + The value specified for walkupLevels or relativeDepthFromWalkup is less than 0. + node is null. + + + + Provides a method that site map providers can override to perform an optimized retrieval of one or more levels of parent and ancestor nodes, relative to the specified object. + + + The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors might be retrieved and cached. + The that acts as a reference point for upLevel. + upLevel is less than -1. + node is null. + + + + Provides a method that site map providers can override to perform an optimized retrieval of nodes found in the proximity of the specified node. + + + The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors (and their descendant nodes to the level of node) might be retrieved and cached. + The number of descendant generations to fetch. 0 indicates no descendant nodes are retrieved and -1 indicates that all descendant nodes might be retrieved and cached. + The that acts as a reference point for upLevel. + upLevel or downLevel is less than -1. + node is null. + + + + Retrieves a Boolean value indicating whether the specified object can be viewed by the user in the specified context. + + + + true if security trimming is enabled and node can be viewed by the user or security trimming is not enabled; otherwise, false. + + + The that contains user information. + The that is requested by the user. + context is null.- or -node is null. + + + + Gets the friendly name used to refer to the provider during configuration. + + + + The friendly name used to refer to the provider during configuration. + + + + + + Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + + + + A brief, friendly description suitable for display in administrative tools or other UIs. + + + + + + Gets the object that represents the currently requested page. + + + + A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + + + + + + Gets or sets the parent object of the current provider. + + + + The parent provider of the current . + + + + + + Gets the root object in the current provider hierarchy. + + + + An that is the top-level site map provider in the provider hierarchy that the current provider belongs to. + + + There is a circular reference to the current site map provider. + + + + Gets the root object of the site map data that the current provider represents. + + + + The root of the current site map data provider. The default implementation performs security trimming on the returned node. + + + + + + Implements by using both and and choosing dynamically between them. + + + In web applications a single Request may be executed on different threads. In this case HttpContext.Current is the only invariant.
    + This implementation dynamically chooses between System.Runtime.Remoting.Messaging.CallContext + and System.Web.HttpContext to store data. +
    + Erich Eichinger + $Id: HybridContextStorage.cs,v 1.1 2007/02/02 21:31:25 oakinger Exp $ +
    + + + Retrieves an object with the specified name. + + The name of the item. + The object in the context associated with the specified name or null if no object has been stored previously + + + + Stores a given object and associates it with the specified name. + + The name with which to associate the new item. + The object to store in the call context. + + + + Empties a data slot with the specified name. + + The name of the data slot to empty. + + + + Miscellaneous utility methods to support web functionality within Spring.Objects + + Aleksandar Seovic + $Id: WebObjectUtils.cs,v 1.3 2008/01/27 23:29:55 oakinger Exp $ + + + + Creates a new instance of the class. + + +

    + This is a utility class, and as such exposes no public constructors. +

    +
    +
    + + + Creates an instance of the ASPX page + referred to by the supplied . + + + The URL of the ASPX page. + + Page instance. + + If this method is not called in the scope of an active web session + (i.e. the implementation this method depends on this code executing + in the environs of a running web server such as IIS); or if the + page could not be instantiated (for whatever reason, such as the + ASPX not actually existing). + + + + + Returns the of the ASPX page + referred to by the supplied . + + +

    + As indicated by the exception that can be thrown by this method, + the ASPX page referred to by the supplied + does have to be instantiated in order to determine its + see cref="System.Type"/> +

    +
    + + The filename of the ASPX page. + + + The of the ASPX page + referred to by the supplied . + + + If the supplied is or + contains only whitespace character(s). + + + If this method is not called in the scope of an active web session + (i.e. the implementation this method depends on this code executing + in the environs of a running web server such as IIS); or if the + page could not be instantiated (for whatever reason, such as the + ASPX not actually existing). + +
    + + + Calls the underlying ASP.NET infrastructure to obtain the compiled page type + relative to the current . + + + The filename of the ASPX page relative to the current + + + The of the ASPX page + referred to by the supplied . + + + + + Gets the controls type from a given filename + + + + + May be used to wrap controls for databinding that don't accept unknown attributes. + + + + + Overridden to ensure only 1 control is wrapped by this adapter + + + + + + Overridden to render wrapped control only. + + + + + Returns the control wrapped by this adapter or null. + + + + + This TypeBuilder dynamically implements the contract on a given type. + + Erich Eichinger + $Id: SupportsWebDependencyInjectionTypeBuilder.cs,v 1.1 2007/08/01 23:11:01 markpollack Exp $ + + + + Creates a new TypeBuilder instance. + + The base type the proxy shall be derived from + The methods to be injected with a call to staticCallbackMethod + The static callback method to be injected into methodsToIntercept + + + + Creates a proxy that inherits the proxied object's class. + + + The generated proxy type. + + + + Actually implements the interface. + + + + + Declares field that holds the . + + + + + Determines if the specified + is one of those generated by this builder. + + The type to check. + + if the type is a SpringAwareControl-based proxy; + otherwise . + + + + + The various result modes. + + Aleksandar Seovic + $Id: ResultMode.cs,v 1.3 2007/04/19 07:49:01 oakinger Exp $ + + + + + A server-side transfer. + + +

    + Issues a server-side transfer using the + method. +

    +
    + +
    + + + A redirect. + + +

    + Issues a redirect (to the user-agent - typically a browser) using + the method. +

    +
    + +
    + + + A server-side transfer. + + +

    + Issues a server-side transfer using the + method with parameter 'preserveForm=false'. +

    +
    + +
    + + + implementation that allows users to monitor state + of the Spring.NET web application context. + + +

    + +

    +
    + Aleksandar Seovic + $Id: ContextMonitor.cs,v 1.3 2007/07/29 19:39:48 markpollack Exp $ +
    + + + Initializes a new instance of the class. + + + + + Processes HTTP request. + + An object that provides references to the intrinsic server objects (for example, , , , and ) used to service HTTP requests. + + + + Gets a value indicating whether another request can use + this instance. + + True if this handler is reusable, False otherwise. + + + + + + + + + Sets the binding context of this formatter. + + + + + + + + + Clears the binding context of this formatter. + + + + + Binds agroup of HTTP request multi-valued items to the data model. + + + Due to the fact, that browsers don't send the values of unchecked checkboxes, you + can't use for binding to checkboxes. + + Aleksandar Seovic + $Id: HttpRequestBindingContainer.cs,v 1.6 2008/02/05 20:40:26 aseovic Exp $ + + + + Creates a new instance of . + + + Comma separated list of multi-valued request parameters that + should be used to create a target item. + + + Expression that identifies a target list items should be added to. + + + The type of the target item. + + + + + Adds the binding. + + + This is a convinience method for adding SimpleExpressionBinding, + one of the most often used binding types, to the bindings list. + + + The source expression. + + + The target expression. + + + Binding direction. + + + to use for value formatting and parsing. + + + Added instance. + + + + + Binds source object to target object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Binds target object to source object. + + + The source object. + + + The target object. + + + Validation errors collection that type conversion errors should be added to. + + + Variables that should be used during expression evaluation. + + + + + Implementation of that renders + validation errors within a div element, using breaks between the + errors. + + + This renderer's behavior is consistent with standard ASP.NET behavior of + the validation summary, and is used as default renderer for Spring.NET + control. + + Aleksandar Seovic + $Id: DivValidationErrorsRenderer.cs,v 1.1 2007/08/02 19:50:28 markpollack Exp $ + + + + This class provides common members for all validation errors renderers. + + Aleksandar Seovic + $Id: AbstractValidationErrorsRenderer.cs,v 1.1 2007/08/02 19:50:28 markpollack Exp $ + + + + Renders validation errors using specified . + + Web form instance. + An HTML writer to use. + The list of validation errors. + + + + Gets or sets the name of the CSS class that should be used. + + + The name of the CSS class that should be used + + + + + Renders validation errors using specified . + + Web form instance. + An HTML writer to use. + The list of validation errors. + + + + This validator allows for validating a CheckBox's "Checked" state. + + Erich Eichinger + $Id: CheckBoxValidator.cs,v 1.1 2007/07/31 10:40:59 oakinger Exp $ + + + + Validates this validator's properties are set correctly. + + + + + + Returns the state of the checkbox's Checked property + + + + + + Adds attributes required for clientside validation + + + + + + Ensures the evaluation javascript functionblock is registered + + + + + + Web object definitions extend + by adding scope property. + + +

    + This is the most common type of object definition in ASP.Net web applications +

    +
    + Aleksandar Seovic + $Id: RootWebObjectDefinition.cs,v 1.4 2007/08/01 23:10:46 markpollack Exp $ +
    + + + Creates a new instance of the + class + for a singleton, providing property values and constructor arguments. + + + The type of the object to instantiate. + + + The to be applied to + a new instance of the object. + + + The + to be applied to a new instance of the object. + + + + + Creates a new instance of the + class + for a singleton, providing property values and constructor arguments. + + + The assembly qualified of the object to instantiate. + + + The to be applied to + a new instance of the object. + + + The + to be applied to a new instance of the object. + + +

    + Takes an object class name to avoid eager loading of the object class. +

    +
    +
    + + + Creates a new instance of the + class + for an .aspx page, providing property values. + + + Name of the .aspx page to instantiate. + + + The to be applied to + a new instance of the object. + + + + + Creates a new instance of the + class + + + The definition that is to be copied. + + +

    + Deep copy constructor. +

    +
    +
    + + + Overrides this object's values using values from other argument. + + The object to copy values from. + + + + A that represents the current + . + + + A that represents the current + . + + + + + Object scope. + + + + + Returns true if web object is .aspx page. + + + + + Gets the rooted url of the .aspx page, if object definition represents page. + + + + + Forces ASP pages to be treated as prototypes all the time inorder to comply with ASP.Net requirements. + + + + + This formatter acts as an adapter to a datasource. It parses a given datasource into + a table to allow converting back and forth between objects and their keys during formatting. + + + + The DataSourceItemFormatter expects the "source" to have 2 properties named "DataSourceField" and "DataValueField". + + + + + + Initialize this instance. + + The name of the "source"s property containing the dataSource + The name of the "source"s property containing the name of the key property + + + + Sets the bindingContext for the current thread. + + The source object + The target object + Variables to be used during binding + The current binding's direction + + + + Reset the current thread's binding context. + + + + + Initialize a new instance. + + The datasource containing list items + The name of the listitem's property that evaluates to the item's key + The direction of the current binding + + + + Returns the key of the specified value object. + + The value to extract the key from. + Key extracted from . + + + + Return the object with the specified key from the datasource. + + The key of the object. + Parsed . + + + + + + Erich Eichinger + $Id: HttpApplicationConfigurer.cs,v 1.2 2007/07/29 19:39:42 markpollack Exp $ + + + + Holds shared application Template + + + + + Holds shared modules Templates + + + + + Configures the instance and its modules. + + + When called, configures using the instance + provided in and the templates available in + and . + + the application context instance to be used for resolving object references. + the instance to be configured. + + + + Gets or Sets the shared application template + + + + + Gets the dictionary of shared module templates + + + to synchronize access to the dictionary, use property. + + + + + Wrapper for class. + + +

    + Configuration for this provider requires providerId element set in web.config file, + as the Id of wrapped provider (defined in the Spring context). +

    +
    + Damjan Tomic +
    + + + Reference to wrapped provider (defined in Spring context). + + + + + Initializes the provider. + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + The providerId attribute may be used to override the name being used for looking up an object definition. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + Retrieves a object that represents the currently requested page using the specified object. + + + + A that represents the currently requested page; otherwise, null, if no corresponding can be found in the or if the page context is null. + + + The used to match node information with the URL of the requested page. + + + + Retrieves a object based on a specified key. + + + + A that represents the page identified by key; otherwise, null, if no corresponding is found or if security trimming is enabled and the cannot be returned for the current user. The default is null. + + + A lookup key with which a is created. + + + + When overridden in a derived class, retrieves a object that represents the page at the specified URL. + + + + A that represents the page identified by rawURL; otherwise, null, if no corresponding is found or if security trimming is enabled and the cannot be returned for the current user. + + + A URL that identifies the page for which to retrieve a . + + + + When overridden in a derived class, retrieves the child nodes of a specific . + + + + A read-only that contains the immediate child nodes of the specified ; otherwise, null or an empty collection, if no child nodes exist. + + + The for which to retrieve all child nodes. + + + + Provides an optimized lookup method for site map providers when retrieving the node for the currently requested page and fetching the parent and ancestor site map nodes for the current page. + + + + A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + + + The number of ancestor site map node generations to get. A value of -1 indicates that all ancestors might be retrieved and cached by the provider. + upLevel is less than -1. + + + + Provides an optimized lookup method for site map providers when retrieving the node for the currently requested page and fetching the site map nodes in the proximity of the current node. + + + + A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + + + The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors might be retrieved and cached by the provider. + The number of child generations to fetch. 0 indicates no descendant nodes are retrieved and a -1 indicates that all descendant nodes might be retrieved and cached by the provider. + upLevel or downLevel is less than -1. + + + + When overridden in a derived class, retrieves the parent node of a specific object. + + + + A that represents the parent of node; otherwise, null, if the has no parent or security trimming is enabled and the parent node is not accessible to the current user. + + + The for which to retrieve the parent node. + + + + Provides an optimized lookup method for site map providers when retrieving an ancestor node for the currently requested page and fetching the descendant nodes for the ancestor. + + + + A that represents an ancestor of the currently requested page; otherwise, null, if the current or ancestor is not found or cannot be returned for the current user. + + + The number of descendant node levels to retrieve from the target ancestor node. + The number of ancestor node levels to traverse when retrieving the requested ancestor node. + walkupLevels or relativeDepthFromWalkup is less than 0. + + + + Provides an optimized lookup method for site map providers when retrieving an ancestor node for the specified object and fetching its child nodes. + + + + A that represents an ancestor of node; otherwise, null, if the current or ancestor is not found or cannot be returned for the current user. + + + The number of descendant node levels to retrieve from the target ancestor node. + The that acts as a reference point for walkupLevels and relativeDepthFromWalkup. + The number of ancestor node levels to traverse when retrieving the requested ancestor node. + The value specified for walkupLevels or relativeDepthFromWalkup is less than 0. + node is null. + + + + This method is marked as protected and should never be called. + + + + A that represents the root node of the set of nodes that the current provider manages. + + + + + + Provides a method that site map providers can override to perform an optimized retrieval of one or more levels of parent and ancestor nodes, relative to the specified object. + + + The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors might be retrieved and cached. + The that acts as a reference point for upLevel. + upLevel is less than -1. + node is null. + + + + Provides a method that site map providers can override to perform an optimized retrieval of nodes found in the proximity of the specified node. + + + The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors (and their descendant nodes to the level of node) might be retrieved and cached. + The number of descendant generations to fetch. 0 indicates no descendant nodes are retrieved and -1 indicates that all descendant nodes might be retrieved and cached. + The that acts as a reference point for upLevel. + upLevel or downLevel is less than -1. + node is null. + + + + Retrieves a Boolean value indicating whether the specified object can be viewed by the user in the specified context. + + + + true if security trimming is enabled and node can be viewed by the user or security trimming is not enabled; otherwise, false. + + + The that contains user information. + The that is requested by the user. + context is null.- or -node is null. + + + + Gets the friendly name used to refer to the provider during configuration. + + + + The friendly name used to refer to the provider during configuration. + + + + + + Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + + + + A brief, friendly description suitable for display in administrative tools or other UIs. + + + + + + Gets the object that represents the currently requested page. + + + + A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + + + + + + Gets or sets the parent object of the current provider. + + + + The parent provider of the current . + + + + + + Gets the root object in the current provider hierarchy. + + + + An that is the top-level site map provider in the provider hierarchy that the current provider belongs to. + + + There is a circular reference to the current site map provider. + + + + Gets the root object of the site map data that the current provider represents. + + + + The root of the current site map data provider. The default implementation performs security trimming on the returned node. + + + + + + An Interface for class. + + +

    + Configuration for this provider requires providerId element set in web.config file, + as the Id of wrapped provider (defined in the Spring context). +

    +
    + Damjan Tomic +
    + + + Initializes the provider. + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + The providerId attribute is mandatory. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + Gets a value indicating whether the specified user is in the specified role for the configured applicationName. + + + + true if the specified user is in the specified role for the configured applicationName; otherwise, false. + + + The user name to search for. + The role to search in. + + + + Gets a list of the roles that a specified user is in for the configured applicationName. + + + + A string array containing the names of all the roles that the specified user is in for the configured applicationName. + + + The user to return a list of roles for. + + + + Adds a new role to the data source for the configured applicationName. + + + The name of the role to create. + + + + Removes a role from the data source for the configured applicationName. + + + + true if the role was successfully deleted; otherwise, false. + + + If true, throw an exception if roleName has one or more members and do not delete roleName. + The name of the role to delete. + + + + Gets a value indicating whether the specified role name already exists in the role data source for the configured applicationName. + + + + true if the role name already exists in the data source for the configured applicationName; otherwise, false. + + + The name of the role to search for in the data source. + + + + Adds the specified user names to the specified roles for the configured applicationName. + + + A string array of the role names to add the specified user names to. + A string array of user names to be added to the specified roles. + + + + Removes the specified user names from the specified roles for the configured applicationName. + + + A string array of role names to remove the specified user names from. + A string array of user names to be removed from the specified roles. + + + + Gets a list of users in the specified role for the configured applicationName. + + + + A string array containing the names of all the users who are members of the specified role for the configured applicationName. + + + The name of the role to get the list of users for. + + + + Gets a list of all the roles for the configured applicationName. + + + + A string array containing the names of all the roles stored in the data source for the configured applicationName. + + + + + + Gets an array of user names in a role where the user name contains the specified user name to match. + + + + A string array containing the names of all the users where the user name matches usernameToMatch and the user is a member of the specified role. + + + The user name to search for. + The role to search in. + + + + Gets the friendly name used to refer to the provider during configuration. + + + + The friendly name used to refer to the provider during configuration. + + + + + Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + + + + A brief, friendly description suitable for display in administrative tools or other UIs. + + + + + Gets or sets the name of the application to store and retrieve role information for. + + + + The name of the application to store and retrieve role information for. + + + + + + A spring-configurable version of + + Erich Eichinger + $Id: ConfigurableSqlRoleProvider.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ + + + + Initializes the provider. + + + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + + Values may be overridden by specifying them in list. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + The ConnectionString to be used + + + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + + + + + Concrete implementation of + that knows + how to handle s. + + +

    + This class should only be used within the context of an ASP.NET web application. +

    +
    + Aleksandar Seovic + $Id: WebObjectFactory.cs,v 1.9 2007/12/16 12:43:48 oakinger Exp $ +
    + + + Holds the virtual path this factory has been created from. + + + + + Registers events handlers with to ensure + proper disposal of 'request'- and 'session'-scoped objects + + + + + Creates a new instance of the + class. + + The virtual path resources will be relative resolved to. + Flag specifying whether to make this object factory case sensitive or not. + + + + Creates a new instance of the + class. + + The virtual path resources will be relative resolved to. + Flag specifying whether to make this object factory case sensitive or not. + + The parent object factory. + + + + + Creates the root object definition. + + The template definition. + Root object definition. + + + + Tries to find cached object for the specified name. + + + This implementation tries to find object first in the Request scope, + then in the Session scope and finally in the Application scope. + + Object name to look for. + Cached object if found, null otherwise. + + + + Creates a singleton instance for the specified object name and definition. + + + The object name (will be used as the key in the singleton cache key). + + The object definition. + + The arguments to use if creating a prototype using explicit arguments to + a static factory method. It is invalid to use a non-null arguments value + in any other case. + + The created object instance. + + + + Creates hopefully unique key for the scope based on object name. + + Object name. + Generated key. + + + + Injects dependencies into the supplied instance + using the named object definition. + + + The object instance that is to be so configured. + + + The name of the object definition expressing the dependencies that are to + be injected into the supplied instance. + + + + + + Disposes all 'request'-scoped objects at the end of each Request + + + + + Disposes all 'session'-scoped objects at the end of a session + + + + + Convinience accessor for HttpContext + + + + + Get the table of 'request'-scoped objects. + + + + + Get the table of 'session'-scoped objects. Returns null, if Session is disabled. + + + + + Culture resolver that uses cookie to store culture information. + + Aleksandar Seovic + $Id: CookieCultureResolver.cs,v 1.4 2007/08/25 14:26:35 oakinger Exp $ + + + + Resolves the culture from the request. + + + If the culture cookie doesn't exist, this method will return + the value of the 'Accept-Language' request header, or if no + headers are specified, the culture of the current server thread. + + Culture that should be used to render view. + + + + Sets the culture. + + The new culture or null to clear the culture. + + + + Creates cookie for the specified culture. + + Culture to store in a cookie. + Created cookie. + + + + Handles binding of a multiple selection ListControl to a target's IList property. + + + + + Creates a new instance of this binding. + + The expression that will evaluate to the acting as the source + The expression that will evaluate to an property acting as the target + Binding's direction + An optional formatter converting target items into and back + + If no formatter is specified, a will be used. + + + + + Actually performs unbinding the ListControl's selected values into the target's + + + + + Actually performs binding the targetlist to the ListControl. + + + + + Setting source value is not allowed in this bindingType. + + + + + Setting target value is not allowed in this bindingType. + + + + + Represents an .aspx file, also known as a Web Forms page, requested from a + server that hosts an ASP.NET Web application. + + +

    + The Page class is associated with files that have an .aspx extension. + These files are compiled at run time as Page objects and cached in server memory. +

    +

    + This class extends and adds support for master + pages similar to upcoming ASP.Net 2.0 master pages feature. +

    +

    + It also adds support for automatic localization using local page resource file, and + simplifies access to global resources (resources from the message source for the + application context). +

    +
    + Aleksandar Seovic + $Id: Page.cs,v 1.89 2008/04/03 04:32:29 markpollack Exp $ +
    + + + Initializes Spring.NET page internals and raises the PreInit event. + + The instance containing the event data. + + + + Initializes the culture. + + + + + Initializes data model and controls. + + + + + Raises the event after page initialization. + + + + + Raises the PreLoadViewState event for + this page and all contained controls. + + + + + Recursively raises PreLoadViewState event. + + + + + Overridden to add support for + + + If necessary override instead of this method. + + + + + If ViewState is disabled, calls recursively for all controls. + + + If ViewState is disabled, DropDownLists etc. might not fire "Changed" events. + + + + + Calls recursively for all controls. + + + + + Recursively calls for all controls. + + + + + If necessary override this method instead of + + + + + Initializes dialog result and unbinds data from the controls + into a data model. + + Event arguments. + + + + Binds data from the data model into controls and raises + PreRender event afterwards. + + Event arguments. + + + + Raises InitializeControls event. + + Event arguments. + + + + Obtains a object from a user control file + and injects dependencies according to Spring config file. + + The virtual path to a user control file. + + Returns the specified object, with dependencies injected. + + + + + Obtains a object by type + and injects dependencies according to Spring config file. + + The type of a user control. + parameters to pass to the control + + Returns the specified object, with dependencies injected. + + + + + Initializes data model when the page is first loaded. + + + This method should be overriden by the developer + in order to initialize data model for the page. + + + + + Retrieves data model from a persistence store. + + + The default implementation uses to store and retrieve + the model for the current + + + + + Saves data model to a persistence store. + + + The default implementation uses to store and retrieve + the model for the current + + + + + Loads the saved data model on postback. + + + This method should be overriden by the developer + in order to load data model for the page. + + + + + Returns a model object that should be saved. + + + This method should be overriden by the developer + in order to save data model for the page. + + + A model object that should be saved. + + + + + Stores the controller to be returned by property. + + + The default implementation uses a field to store the reference. Derived classes may override this behaviour + but must ensure to also change the behaviour of accordingly. + + Controller for the page. + + + + Returns the controller stored by . + + + The default implementation uses a field to retrieve the reference. Derived classes may override this behaviour + but must ensure to also change the behaviour of accordingly. + + + The controller for this page. + + If this page is being part of a process, the process' is returned. + If no controller is set, a reference to the page itself is returned. + + + + + + Registers single CSS style with the page. + + Style name. + Style definition. + + + + Returns True if specified style is registered, False otherwise. + + Style name. + True if specified style is registered, False otherwise. + + + + Registers CSS file with the page. + + Style file key. + Style file name. + + + + Returns True if specified style file is registered, False otherwise. + + Style file key. + True if specified style file is registered, False otherwise. + + + + Registers script block that should be rendered within the head HTML element. + + Script key. + Script text. + + + + Registers script block that should be rendered within the head HTML element. + + Script key. + Script language. + Script text. + + + + Registers script block that should be rendered within the head HTML element. + + Script key. + Script language MIME type. + Script text. + + + + Registers script file that should be referenced within the head HTML element. + + Script key. + Script file name. + + + + Registers script file that should be referenced within the head HTML element. + + Script key. + Script language. + Script file name. + + + + Registers script file that should be referenced within the head HTML element. + + Script key. + Script language MIME type. + Script file name. + + + + Registers script block that should be rendered within the head HTML element. + + Script key. + Element ID of the event source. + Name of the event to handle. + Script text. + + + + Registers script block that should be rendered within the head HTML element. + + Script key. + Script language. + Element ID of the event source. + Name of the event to handle. + Script text. + + + + Registers script block that should be rendered within the head HTML element. + + Script key. + The scripting language's MIME type. + Element ID of the event source. + Name of the event to handle. + Script text. + + + + Returns True if specified head script is registered, False otherwise. + + Script key. + True if specified head script is registered, False otherwise. + + + + Redirects user to a URL mapped to specified result name. + + Result name. + + + + Redirects user to a URL mapped to specified result name. + + Name of the result. + The context to use for evaluating the SpEL expression in the Result. + + + + Returns a redirect url string that points to the + defined by this + result evaluated using this Page for expression + + Name of the result. + A redirect url string. + + + + Returns a redirect url string that points to the + defined by this + result evaluated using this Page for expression + + Name of the result. + The context to use for evaluating the SpEL expression in the Result + A redirect url string. + + + + Evaluates specified validators and returns True if all of them are valid. + + +

    + Each validator can itself represent a collection of other validators if it is + an instance of or one of its derived types. +

    +

    + Please see the Validation Framework section in the documentation for more info. +

    +
    + Object to validate. + Validators to evaluate. + + True if all of the specified validators are valid, False otherwise. + +
    + + + Creates the validator parameters. + + + + This method can be overriden if you want to pass additional parameters + to the validation framework, but you should make sure that you call + this base implementation in order to add page, session, application, + request, response and context to the variables collection. + + + + Dictionary containing parameters that should be passed to + the data validation framework. + + + + + Initializes the data bindings. + + + + + Returns the key to be used for looking up a cached + BindingManager instance in . + + a unique key identifying the instance in the dictionary. + + + + Creates a new instance. + + + This factory method is called if no could be found in + using the key returned by .
    +
    + +
    + a instance to be used for DataBinding +
    + + + Initializes binding manager and data bindings if necessary. + + + + + Raises the event. + + + + + Bind data from model to form. + + + + + Unbind data from form to model. + + + + + Raises DataBound event. + + Event arguments. + + + + Raises DataBound event. + + Event arguments. + + + + Initializes local message source + + + + + Creates and returns local ResourceManager for this page. + + + + In ASP.NET 1.1, this method loads local resources from the web application assembly. + + + However, in ASP.NET 2.0, local resources are compiled into a dynamic assembly, + so we need to find that assembly and load the resources from it. + + + Local ResourceManager instance. + + + + Returns message for the specified resource name. + + Resource name. + Message text. + + + + Returns message for the specified resource name. + + Resource name. + Message arguments that will be used to format return value. + Formatted message text. + + + + Returns resource object for the specified resource name. + + Resource name. + Resource object. + + + + Raises UserLocaleChanged event. + + Event arguments. + + + + Creates a key for shared state, taking into account whether + this page belongs to a process or not. + + Key suffix + Generated unique shared state key. + + + + Injects dependencies into control before adding it. + + + + + PreLoadViewState event. + + + + This event is raised if is true + immediately before state is restored from ViewState. + + + NOTE: Different from , this event + will also be raised if the control has no ViewState or ViewState is disabled. + + + + + + This event is raised before Init event and should be used to initialize + controls as necessary. + + + + + Gets or sets the process that this page belongs to. + + + + + Gets or sets controller for the page. + + + + Application pages should shadow this property and change its type + in order to make calls to controller within the page as simple as possible. + + + If external controller is not specified, page will serve as its own controller, + which will allow data binding to work properly. + + + Controller for the page. Defaults to the page itself. + + + + Returns a thread-safe dictionary that contains state that is shared by + all instances of this page. + + + + + Overrides the default PreviousPage property to return an instance of , + and to work properly during server-side transfers and executes. + + + + + Gets the master page that determines the overall look of the page. + + + + + Returns true if page uses master page, false otherwise. + + + + + Gets a dictionary of registered styles. + + Registered styles. + + + + Gets a dictionary of registered style files. + + Registered style files. + + + + Gets a dictionary of registered head scripts. + + Registered head scripts. + + + + Gets or sets the CSS root. + + The CSS root. + + + + Gets or sets the scripts root. + + The scripts root. + + + + Gets or sets the images root. + + The images root. + + + + Gets or sets map of result names to target URLs + + + + + Gets the validation errors container. + + The validation errors container. + + + + Expose BindingManager via IDataBound interface + + + + + Gets the binding manager. + + The binding manager. + + + + This event is raised after as been initialized. + + + + + This event is raised after all controls have been populated with values + from the data model. + + + + + This event is raised after data model has been populated with values from + web controls. + + + + + Gets or sets Spring application context of the page. + + + + + Gets or sets the localizer. + + The localizer. + + + + Gets or sets the culture resolver. + + The culture resolver. + + + + Gets or sets the local message source. + + The local message source. + + + + Gets or sets user's culture + + + + + This event is raised when the value of UserLocale property changes. + + + + + Holds default ApplicationContext instance to be used during DI. + + + + + This MethodBuilder emits a Callback-call before calling the base method + + Erich Eichinger + $Id: SupportsWebDependencyInjectionMethodBuilder.cs,v 1.1 2007/08/01 23:11:01 markpollack Exp $ + + + + Initializes a new instance of a SupportsWebDependencyInjectionMethodBuilder + + + + + Inserts a call to a callback-method before actually calling the base-method. + + The IL generator to use + The method to proxy + The interface definition of this method, if applicable + + + + Emits the callback invocation. + + + + + Helper class for easier access to reflected Control members. + + Erich Eichinger + $Id: ControlAccessor.cs,v 1.2 2008/05/13 14:22:47 oakinger Exp $ + + + + Instantiates a new Accessor. + + + + + + Returns the underlying ControlCollection instance. + + + + + Gets or sets the ControlCollection of the target without accessing the target's property. + + + If the underlying collection is null, it is automatically created. + + + + + Implements by using . + + Erich Eichinger + $Id: HttpContextStorage.cs,v 1.1 2007/02/02 21:31:25 oakinger Exp $ + + + + Retrieves an object with the specified name. + + The name of the item. + The object in the context associated with the specified name or null if no object has been stored previously + + + + Stores a given object and associates it with the specified name. + + The name with which to associate the new item. + The object to store in the call context. + + + + Empties a data slot with the specified name. + + The name of the data slot to empty. + + + + This control should be used instead of standard ValidationSummary control + to display validation errors identified by the Spring.NET validation framework. + + Aleksandar Seovic + Jonathan Allenby + $Id: ValidationSummary.cs,v 1.11 2008/03/19 12:07:14 oakinger Exp $ + + + + Create the default + for this ValidationControl if none is configured. + + + + + Holds the collection of controls in a . + + Erich Eichinger + $Id: TabularViewCollection.cs,v 1.1 2007/07/24 13:33:28 oakinger Exp $ + + + + Initialize a new instance. + + + + + + Add the specified control to the collection. + + + + + Add the specified control to the collection. + + + + + Obtain the specified control from the collection. + + + + + This control is responsible for rendering tabs. + + + By default, this TabContainer implementation uses controls to + render tabs. Override to change this behaviour. + + Erich Eichinger + $Id: TabContainer.cs,v 1.1 2007/07/24 13:33:28 oakinger Exp $ + + + + Represents the command name of the tab to be selected. + + + + + Key into the eventhandler table. + + + + + Catches with name '' and + raises the event. + + The source of the event. + contains event information. + + + + + Raises the event. + + + + + Creates a new tab for the specified view within the given container. + + The containing the view + The for which a new tab is to be created. + The index of the tab to be created. + + By default, controls are used for rendering tabs. Override this method to + change this behaviour. + + + + + Occurs, when a tab control is clicked. + + + + + This control allows for suppressing output of the 'action' attribute. + + + the 'action' attribute rendered by the default control causes troubles + in case of URL-rewriting. See e.g. 'thescripts.com' forum + and also JIRA SPRNET-560 for more info. + + Erich Eichinger + $Id: Form.cs,v 1.1 2007/07/24 14:32:58 oakinger Exp $ + + + + Renders attributes but performs 'action' suppressing logic. + + + + + + Sets or Gets a value indicating if the 'action' attribute shall be rendered. Defaults to 'false' + + + The following possibilites are available: + + If is 'true', rendering of the 'action' attribute is suppressed. + If is 'false' and is not set, + 'action' attribute will.be rendered to + + If is 'false' and is set, + 'action' attribute will.be rendered to + + + + + + + Sets or Gets an explicit url to be rendered + + + The url specified here is only rendered, if is true. + + + + + This wrapper suppresses output of 'action' attributes. + + + + + This is + + + + + Mark Pollack + $Id: WebDependencyInjectionUtils.cs,v 1.3 2008/05/13 14:22:47 oakinger Exp $ + + + + Injects dependencies into all controls in the hierarchy + based on template definitions in the Spring config file. + + ApplicationContext to be used + Control to inject dependencies into. + + + + Aquires an ApplicationContext according to a Control's TemplateSourceDirectory + + if availabe, the control's IApplicationContext instance - defaultContext otherwise + + + + Base class that describes client side script block or file. + + +

    + Classes that extend this class are used by Spring.Web client-side + scripting support. +

    +
    + Aleksandar Seovic + $Id: Script.cs,v 1.4 2007/11/28 23:26:10 oakinger Exp $ +
    + + + Initialize a new Script object of the specified language + + Script language. + + + + Initialize a new script object of the specified type + + a + + + + Gets or sets script language. + + + + + Gets or sets script mime type + + + + + Class that describes client side script block. + + +

    + Script blocks are used to insert script code directly into the page, + without references to an external file. +

    +
    + Aleksandar Seovic + $Id: Script.cs,v 1.4 2007/11/28 23:26:10 oakinger Exp $ +
    + + + Default constructor. + + Script language. + The script text. + + + + Initialize a new script block instance. + + the script language's + the script body + + + + Gets or sets the script text. + + The script text. + + + + Class that describes client side script file. + + +

    + Script file references script code in the external file. +

    +
    + Aleksandar Seovic + $Id: Script.cs,v 1.4 2007/11/28 23:26:10 oakinger Exp $ +
    + + + Initialize a new instance. + + Script language. + The name of the script file. + + + + Initialize a new instance. + + the script language's + the (virtual) path to the script + + + + Gets or sets the name of the script file. + + The name of the script file. + + + + Class that describes client side script file. + + +

    + Script file references script code in the external file. +

    +
    + Aleksandar Seovic + $Id: Script.cs,v 1.4 2007/11/28 23:26:10 oakinger Exp $ +
    + + + Initialize a new instance. + + Script language. + Element ID of the event source. + Name of the event to handle. + Script text. + + + + Initialize a new instance. + + the script language's + Element ID of the event source. + Name of the event to handle. + Script text. + + + + Gets or sets the element ID. + + The element ID. + + + + Gets or sets the name of the event. + + The name of the event. + + + + Singleton that keeps track of all active process instances. + + Aleksandar Seovic + $Id: ProcessManager.cs,v 1.2 2006/05/18 21:37:52 markpollack Exp $ + + + + Creates singleton instance. + + + + + Registers process instance. + + Process instance to register. + + + + Returns process with the specified ID. + + Process ID to use for lookup. + Process with the specified ID, or null if process with that ID is not registered. + + + + Unregisters process with the specified ID. + + ID of the process to unregister. + + + + Exports an object as a web service. + + +

    + The exporter will create a web service wrapper for the object that is + to be exposed and additionally enable its configuration as a web + service. +

    +

    + The exported object can be either a standard .NET web service + implementation, with methods marked using the standard + , or it can be a + plain .NET object. +

    +
    + Aleksandar Seovic + $Id: WebServiceExporter.cs,v 1.33 2008/04/07 11:00:24 bbaia Exp $ +
    + + + The name of the object in the factory. + + + + + The owning factory. + + + + + The generated web service wrapper type. + + + + + Creates a new instance of the + class. + + + + + Return an instance (possibly shared or independent) of the object + managed by this factory. + + + + If this method is being called in the context of an enclosing IoC container and + returns , the IoC container will consider this factory + object as not being fully initialized and throw a corresponding (and most + probably fatal) exception. + + + + An instance (possibly shared or independent) of the object managed by + this factory. + + + + + Exports specified object as a web service. + + + In the event of misconfiguration (such as failure to set an essential + property) or if initialization fails. + + + + + Validates the configuration. + + + + + Generates the web service wrapper type. + + + + + Gets or sets the base type that web service should inherit. + + + Default is + + + + + Gets or sets the name of the target object that should be exposed as a web service. + + + The name of the target object that should be exposed as a web service. + + + + + Gets or sets the description of the web service (optional). + + + The web service description. + + + + + Gets or sets the name of the web service (optional). + + +

    + Defaults to the value of the exporter's object ID. +

    +
    + + The web service name. + +
    + + + Gets or sets the web service namespace. + + + The web service namespace. + + + + + Gets or sets the list of interfaces whose methods should be exposed as web services. + + + If not set, all the interfaces implemented or inherited + by the target type will be used. + + The interfaces. + + + + Gets or sets a list of custom attributes + that should be applied to a proxy class. + + + + + Gets or sets a dictionary of custom attributes + that should be applied to web service members. + + + Dictionary key is an expression that members can be matched against. + Value is a list of attributes that should be applied + to each member that matches expression. + + + + + Callback that supplies the owning factory to an object instance. + + + Owning + (may not be ). The object can immediately + call methods on the factory. + + +

    + Invoked after population of normal object properties but before an init + callback like 's + + method or a custom init-method. +

    +
    + + In case of initialization errors. + +
    + + + Return the of object that this + creates, or + if not known in advance. + + + + + Is the object managed by this factory a singleton or a prototype? + + + + + Set the name of the object in the object factory that created this object. + + + The name of the object in the factory. + + +

    + Invoked after population of normal object properties but before an init + callback like 's + + method or a custom init-method. +

    +
    +
    + + + Implements constructors for the proxy class. + + + This implementation generates a constructor + that gets instance of the target object using + . + + + The builder to use. + + + + + Implementation of that renders + validation errors within a span element, using breaks between the + errors. + + + This renderer's behavior is consistent with standard ASP.NET behavior of + the control validators, and is used as the default renderer for Spring.NET + control. + + Aleksandar Seovic + $Id: SpanValidationErrorsRenderer.cs,v 1.1 2007/08/02 19:50:28 markpollack Exp $ + + + + Renders validation errors using specified . + + Web form instance. + An HTML writer to use. + The list of validation errors. + + + + Extends standard .Net user control by adding data binding and localization functionality. + + Aleksandar Seovic + $Id: UserControl.cs,v 1.54 2008/04/03 05:31:25 markpollack Exp $ + + + + Initializes user control. + + + + + Raises the event after page initialization. + + + + + This method is called during a postback if this control has been visible when being rendered to the client. + + + If the controls has been visible when being rendering to the client, + has been called during + + true if the server control's state changes as a result of the post back; otherwise false. + + + + This method is called during a postback if this control has been visible when being rendered to the client. + + true if the server control's state changes as a result of the post back; otherwise false. + + + + When implemented by a class, signals the server control object to notify the + ASP.NET application that the state of the control has changed. + + + + + When implemented by a class, signals the server control object to notify the + ASP.NET application that the state of the control has changed. + + + + + First unbinds data from the controls into a data model and + then raises Load event in order to execute all associated handlers. + + Event arguments. + + + + Binds data from the data model into controls and raises + PreRender event afterwards. + + Event arguments. + + + + Raises InitializeControls event. + + Event arguments. + + + + Obtains a object from a user control file + and injects dependencies according to Spring config file. + + The virtual path to a user control file. + + Returns the specified object, with dependencies injected. + + + + + Obtains a object by type + and injects dependencies according to Spring config file. + + The type of a user control. + parameters to pass to the control + + Returns the specified object, with dependencies injected. + + + + + Initializes data model when the page is first loaded. + + + This method should be overriden by the developer + in order to initialize data model for the page. + + + + + Retrieves data model from a persistence store. + + + The default implementation uses to store and retrieve + the model for the current + + + + + Saves data model to a persistence store. + + + The default implementation uses to store and retrieve + the model for the current + + + + + Loads the saved data model on postback. + + + This method should be overriden by the developer + in order to load data model for the page. + + + + + Returns a model object that should be saved. + + + This method should be overriden by the developer + in order to save data model for the page. + + + A model object that should be saved. + + + + + Stores the controller to be returned by property. + + + The default implementation uses a field to store the reference. Derived classes may override this behaviour + but must ensure to also change the behaviour of accordingly. + + Controller for the control. + + + + Returns the controller stored by . + + + + The default implementation uses a field to retrieve the reference. + + + If external controller is not specified, control will serve as its own controller, + which will allow data binding to work properly. + + + You may override this method e.g. to return in order to + have your control bind to the same controller as your page. When overriding this behaviour, derived classes + must ensure to also change the behaviour of accordingly. + + + + The controller for this control. + If no controller is set, a reference to the control itself is returned. + + + + + Redirects user to a URL mapped to specified result name. + + Result name. + + + + Redirects user to a URL mapped to specified result name. + + Result name. + The context to use for evaluating the SpEL expression in the Result. + + + + Evaluates specified validators and returns True if all of them are valid. + + +

    + Each validator can itself represent a collection of other validators if it is + an instance of or one of its derived types. +

    +

    + Please see the Validation Framework section in the documentation for more info. +

    +
    + Object to validate. + Validators to evaluate. + + True if all of the specified validators are valid, False otherwise. + +
    + + + Creates the validator parameters. + + + + This method can be overriden if you want to pass additional parameters + to the validation framework, but you should make sure that you call + this base implementation in order to add page, session, application, + request, response and context to the variables collection. + + + + Dictionary containing parameters that should be passed to + the data validation framework. + + + + + Initializes the data bindings. + + + + + Returns the key to be used for looking up a cached + BindingManager instance in . + + a unique key identifying the instance in the dictionary. + + + + Creates a new instance. + + + This factory method is called if no could be found in + using the key returned by .
    +
    + +
    + a instance to be used for DataBinding +
    + + + Initializes binding manager and data bindings if necessary. + + + + + Raises the event. + + + + + Bind data from model to form. + + + + + Unbind data from form to model. + + + + + Raises DataBound event. + + Event arguments. + + + + Raises DataBound event. + + Event arguments. + + + + Initializes local message source + + + + + Creates and returns local ResourceManager for this page. + + + + In ASP.NET 1.1, this method loads local resources from the web application assembly. + + + However, in ASP.NET 2.0, local resources are compiled into the dynamic assembly, + so we need to find that assembly instead and load the resources from it. + + + Local ResourceManager instance. + + + + Returns message for the specified resource name. + + Resource name. + Message text. + + + + Returns message for the specified resource name. + + Resource name. + Message arguments that will be used to format return value. + Formatted message text. + + + + Returns resource object for the specified resource name. + + Resource name. + Resource object. + + + + Creates a key for shared state, taking into account whether + this page belongs to a process or not. + + Key suffix + Generated unique shared state key. + + + + Injects dependencies into control before adding it. + + + + + PreLoadViewState event. + + + + This event is raised if is true + immediately before state is restored from ViewState. + + + NOTE: Different from , this event will always be raised! + + + + + + This event is raised before Load event and should be used to initialize + controls as necessary. + + + + + Gets or sets controller for the control. + + + + Internally calls are delegated to and . + + + Controller for the control. + + + + Returns a thread-safe dictionary that contains state that is shared by + all instances of this control. + + + + + Gets or sets map of result names to target URLs + + + + + Gets the validation errors container. + + The validation errors container. + + + + Gets the binding manager for this control. + + The binding manager. + + + + This event is raised after as been initialized. + + + + + This event is raised after all controls have been populated with values + from the data model. + + + + + This event is raised after data model has been populated with values from + web controls. + + + + + Gets or sets Spring application context of the page. + + + + + Gets or sets the localizer. + + The localizer. + + + + Gets or sets the local message source. + + The local message source. + + + + Gets or sets user's culture + + + + + Overrides Page property to return + instead of . + + + + + Holds the default ApplicationContext to be used during DI. + + + + + Represents a control that acts as a container for a group of controls within a control. + + Erich Eichinger + $Id: TabularView.cs,v 1.2 2007/07/24 14:32:58 oakinger Exp $ + + + + Indicates if this view is currently active. + + + + + Get or Set the name of the tab item associated with this view. + + + + + Get or Set the tooltip text of the tab item associated with this view. + + + + + Adds the property to the framework's control. + + + When using Spring.Web's DataBinding, you should use this control for easier binding declaration. + + Erich Eichinger + $Id: CheckBoxList.cs,v 1.2 2007/12/14 16:38:44 oakinger Exp $ + + + + Gets or Sets the list of selected values and checks the es accordingly + + + + + Convinience implementation of the wizard-like page controller. + + +

    + Wizard steps are encapsulated within custom user controls. Wizard + controller takes care of navigation through steps and loading of the + appropriate user control. +

    +

    + Developer implementing wizard needs to inherit from this class and implement + abstract, read-only StepPanel property that will be used as a container + for the wizard steps. Navigation event handlers should call Previous and Next methods + from this class to change current step. +

    +
    + Aleksandar Seovic + $Id: AbstractWizard.cs,v 1.7 2006/05/18 21:37:52 markpollack Exp $ +
    + + + Moves to the previous step in the list, if one exists. + + + + + Moves to the next step in the list, if one exists. + + + + + Initializes wizard steps. + + Event arguments. + + + + + + + + + + Loads new step into a step panel if step has changed. + + Event arguments. + + + + Initializes all the steps. + + List of step control names. + List of step control instances. + + + + Loads step control. + + + + + Gets or sets a list of user controls representing wizard steps. + + + + + Gets or sets current step using step index. + + + + + Returns true if there are no steps before the current step. + + + + + Returns true if there are no steps after the current step. + + + + + Panel that should serve as a container for wizard steps. + + + + + Represents the ASPX result page that an operation maps to. + + +

    + Parameters can reference variables accessible from the page using the + standard NAnt style property notation, namely ${varName}. +

    +

    + The result that is passed as the sole + parameter to the parameterized constructor + () must adhere to the + following format: +

    + + [<mode>:]<targetPage>[?param1,param2,...,paramN] + +

    + Only the targetPage is mandatory. +

    +
    + +

    + Examples of valid values that can be passed + to the the parameterized constructor + () include: +

    +

    + + Login.aspx + ~/Login.aspx + redirect:~/Login.aspx + transfer:~/Login.aspx + transfer:Services/Register.aspx + Login.aspx?username=springboy,password=7623AAjoe + redirect:Login.aspx?username=springboy,password=7623AAjoe + +

    +
    + Aleksandar Seovic + Matan Shapira + $Id: Result.cs,v 1.13 2008/02/02 09:12:44 oakinger Exp $ +
    + + + The default . + + +

    + Currently defaults to . +

    +
    + +
    + + + Creates a new instance of the class. + + + + + Creates a new instance of the class. + + +

    + See both the class documentation () + and the reference documentation for the Spring.Web library for a + discussion and examples of what values the supplied + can have. +

    +
    + The result descriptor. + + If the supplied is or + contains only whitespace character(s). + +
    + + + Navigates to the + defined by this result. + + + The context object for parameter resolution. This is typically + a . + + + + + Performs a server-side transfer to the + defined by this result. + + + The context object for parameter resolution. This is typically + a . + + + + + + Resolves transfer parameters and stores them into instance. + + + + + + + Performs a redirect to the + defined by this + result. + + + The context object for parameter resolution. This is typically + a . + + + + + + Returns a redirect url string that points to the + defined by this + result. + + + A redirect url string. + + + + + Extracts and sets this instance's + property from the supplied descriptor. + + +

    + The supplied descriptor is typically + something like /Foo.aspx or + redirect:http://www.springframework.net/. +

    +
    + + The result descriptor. + + + The supplied without the result mode + prefix (if any). + + + If the supplied starts with an illegal + result mode (see ). + +
    + + + Parses query parameters from the supplied . + + + The query string (may be ). + + + + + The . Defines which + method will be used to navigate to the + . + + + + + The target page. + + +

    + This is the (relative) path to the target page. +

    +
    + +

    + Examples of valid values would be: +

    +

    + + Login.aspx + ~/Login.aspx + ~/B2B/SignUp.aspx + B2B/Foo/FooServices.aspx + +

    +
    +
    + + + The parameters thar are to be passed to the + upon + navigation. + + + + + Indicates, if should be called with preserverForm='true' | 'false'. Only relevant for ResultMode.TransferXXXX modes. + + + + + Wrapper for class. + + +

    + Configuration for this provider requires providerId element set in web.config file, + as the Id of wrapped provider (defined in the Spring context). +

    +
    + Damjan Tomic +
    + + + Reference to wrapped provider (defined in Spring context). + + + + + Initializes the provider. + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + The providerId attribute may be used to override the name being used for looking up an object definition. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + Gets a value indicating whether the specified user is in the specified role for the configured applicationName. + + + + true if the specified user is in the specified role for the configured applicationName; otherwise, false. + + + The user name to search for. + The role to search in. + + + + Gets a list of the roles that a specified user is in for the configured applicationName. + + + + A string array containing the names of all the roles that the specified user is in for the configured applicationName. + + + The user to return a list of roles for. + + + + Adds a new role to the data source for the configured applicationName. + + + The name of the role to create. + + + + Removes a role from the data source for the configured applicationName. + + + + true if the role was successfully deleted; otherwise, false. + + + If true, throw an exception if roleName has one or more members and do not delete roleName. + The name of the role to delete. + + + + Gets a value indicating whether the specified role name already exists in the role data source for the configured applicationName. + + + + true if the role name already exists in the data source for the configured applicationName; otherwise, false. + + + The name of the role to search for in the data source. + + + + Adds the specified user names to the specified roles for the configured applicationName. + + + A string array of the role names to add the specified user names to. + A string array of user names to be added to the specified roles. + + + + Removes the specified user names from the specified roles for the configured applicationName. + + + A string array of role names to remove the specified user names from. + A string array of user names to be removed from the specified roles. + + + + Gets a list of users in the specified role for the configured applicationName. + + + + A string array containing the names of all the users who are members of the specified role for the configured applicationName. + + + The name of the role to get the list of users for. + + + + Gets a list of all the roles for the configured applicationName. + + + + A string array containing the names of all the roles stored in the data source for the configured applicationName. + + + + + + Gets an array of user names in a role where the user name contains the specified user name to match. + + + + A string array containing the names of all the users where the user name matches usernameToMatch and the user is a member of the specified role. + + + The user name to search for. + The role to search in. + + + + Gets the friendly name used to refer to the provider during configuration. + + + + The friendly name used to refer to the provider during configuration. + + + + + Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + + + + A brief, friendly description suitable for display in administrative tools or other UIs. + + + + + Gets or sets the name of the application to store and retrieve role information for. + + + + The name of the application to store and retrieve role information for. + + + + + + Wrapper for class. + + +

    + Configuration for this provider requires providerId element set in web.config file, + as the Id of wrapped provider (defined in the Spring context). +

    +
    + Damjan Tomic +
    + + + Reference to wrapped provider (defined in Spring context). + + + + + Initializes the provider. + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + The providerId attribute may be used to override the name being used for looking up an object definition. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + Returns the collection of settings property values for the specified application instance and settings property group. + + + + A containing the values for the specified settings property group. + + + A describing the current application use. + A containing the settings property group whose values are to be retrieved.2 + + + + Sets the values of the specified group of property settings. + + + A describing the current application usage. + A representing the group of property settings to set.2 + + + + When overridden in a derived class, deletes profile properties and information for the supplied list of profiles. + + + + The number of profiles deleted from the data source. + + + A of information about profiles that are to be deleted. + + + + When overridden in a derived class, deletes profile properties and information for profiles that match the supplied list of user names. + + + + The number of profiles deleted from the data source. + + + A string array of user names for profiles to be deleted. + + + + When overridden in a derived class, deletes all user-profile data for profiles in which the last activity date occurred before the specified date. + + + + The number of profiles deleted from the data source. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are deleted. + A that identifies which user profiles are considered inactive. If the value of a user profile occurs on or before this date and time, the profile is considered inactive. + + + + When overridden in a derived class, returns the number of profiles in which the last activity date occurred on or before the specified date. + + + + The number of profiles in which the last activity date occurred on or before the specified date. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + A that identifies which user profiles are considered inactive. If the of a user profile occurs on or before this date and time, the profile is considered inactive. + + + + When overridden in a derived class, retrieves user profile data for all profiles in the data source. + + + + A containing user-profile information for all profiles in the data source. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + When this method returns, contains the total number of profiles. + The index of the page of results to return. + The size of the page of results to return. + + + + When overridden in a derived class, retrieves user-profile data from the data source for profiles in which the last activity date occurred on or before the specified date. + + + + A containing user-profile information about the inactive profiles. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + A that identifies which user profiles are considered inactive. If the of a user profile occurs on or before this date and time, the profile is considered inactive. + When this method returns, contains the total number of profiles. + The index of the page of results to return. + The size of the page of results to return. + + + + When overridden in a derived class, retrieves profile information for profiles in which the user name matches the specified user names. + + + + A containing user-profile information for profiles where the user name matches the supplied usernameToMatch parameter. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + When this method returns, contains the total number of profiles. + The index of the page of results to return. + The user name to search for. + The size of the page of results to return. + + + + When overridden in a derived class, retrieves profile information for profiles in which the last activity date occurred on or before the specified date and the user name matches the specified user name. + + + + A containing user profile information for inactive profiles where the user name matches the supplied usernameToMatch parameter. + + + One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + A that identifies which user profiles are considered inactive. If the value of a user profile occurs on or before this date and time, the profile is considered inactive. + When this method returns, contains the total number of profiles. + The index of the page of results to return. + The user name to search for. + The size of the page of results to return. + + + + Gets the friendly name used to refer to the provider during configuration. + + + + The friendly name used to refer to the provider during configuration. + + + + + Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + + + + A brief, friendly description suitable for display in administrative tools or other UIs. + + + + + Gets or sets the name of the currently running application. + + + + A that contains the application's shortened name, which does not contain a full path or extension, for example, SimpleAppSettings. + + 2 + + + + Wrapper for class. + + +

    + Configuration for this provider requires providerId element set in web.config file, + as the Id of wrapped provider (defined in the Spring context). +

    +
    + Damjan Tomic + $Id: MembershipProviderAdapter.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ +
    + + + Reference to wrapped provider (defined in Spring context). + + + + + Initializes the provider. + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + The providerId attribute may be used to override the name being used for looking up an object definition. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + Adds a new membership user to the data source. + + + + A object populated with the information for the newly created user. + + + Whether or not the new user is approved to be validated. + The password answer for the new user + The user name for the new user. + The unique identifier from the membership data source for the user. + The password for the new user. + The password question for the new user. + The e-mail address for the new user. + A enumeration value indicating whether the user was created successfully. + + + + Processes a request to update the password question and answer for a membership user. + + + + true if the password question and answer are updated successfully; otherwise, false. + + + The new password question for the specified user. + The new password answer for the specified user. + The user to change the password question and answer for. + The password for the specified user. + + + + Gets the password for the specified user name from the data source. + + + + The password for the specified user name. + + + The user to retrieve the password for. + The password answer for the user. + + + + Processes a request to update the password for a membership user. + + + + true if the password was updated successfully; otherwise, false. + + + The new password for the specified user. + The current password for the specified user. + The user to update the password for. + + + + Resets a user's password to a new, automatically generated password. + + + + The new password for the specified user. + + + The user to reset the password for. + The password answer for the specified user. + + + + Updates information about a user in the data source. + + + A object that represents the user to update and the updated information for the user. + + + + Verifies that the specified user name and password exist in the data source. + + + + true if the specified username and password are valid; otherwise, false. + + + The name of the user to validate. + The password for the specified user. + + + + Clears a lock so that the membership user can be validated. + + + + true if the membership user was successfully unlocked; otherwise, false. + + + The membership user to clear the lock status for. + + + + Gets information from the data source for a user based on the unique identifier for the membership user. Provides an option to update the last-activity date/time stamp for the user. + + + + A object populated with the specified user's information from the data source. + + + The unique identifier for the membership user to get information for. + true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. + + + + Gets information from the data source for a user. Provides an option to update the last-activity date/time stamp for the user. + + + + A object populated with the specified user's information from the data source. + + + The name of the user to get information for. + true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. + + + + Gets the user name associated with the specified e-mail address. + + + + The user name associated with the specified e-mail address. If no match is found, return null. + + + The e-mail address to search for. + + + + Removes a user from the membership data source. + + + + true if the user was successfully deleted; otherwise, false. + + + The name of the user to delete. + true to delete data related to the user from the database; false to leave data related to the user in the database. + + + + Gets a collection of all the users in the data source in pages of data. + + + + A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + + + The total number of matched users. + The index of the page of results to return. pageIndex is zero-based. + The size of the page of results to return. + + + + Gets the number of users currently accessing the application. + + + + The number of users currently accessing the application. + + + + + + Gets a collection of membership users where the user name contains the specified user name to match. + + + + A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + + + The total number of matched users. + The index of the page of results to return. pageIndex is zero-based. + The user name to search for. + The size of the page of results to return. + + + + Gets a collection of membership users where the e-mail address contains the specified e-mail address to match. + + + + A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + + + The total number of matched users. + The index of the page of results to return. pageIndex is zero-based. + The e-mail address to search for. + The size of the page of results to return. + + + + Gets the friendly name used to refer to the provider during configuration. + + + + The friendly name used to refer to the provider during configuration. + + + + + + Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + + + + A brief, friendly description suitable for display in administrative tools or other UIs. + + + + + + Indicates whether the membership provider is configured to allow users to retrieve their passwords. + + + + true if the membership provider is configured to support password retrieval; otherwise, false. The default is false. + + + + + + Indicates whether the membership provider is configured to allow users to reset their passwords. + + + + true if the membership provider supports password reset; otherwise, false. The default is true. + + + + + + Gets a value indicating whether the membership provider is configured to require the user to answer a password question for password reset and retrieval. + + + + true if a password answer is required for password reset and retrieval; otherwise, false. The default is true. + + + + + + The name of the application using the custom membership provider. + + + + The name of the application using the custom membership provider. + + + + + + Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out. + + + + The number of invalid password or password-answer attempts allowed before the membership user is locked out. + + + + + + Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. + + + + The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. + + + + + + Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name. + + + + true if the membership provider requires a unique e-mail address; otherwise, false. The default is true. + + + + + + Gets a value indicating the format for storing passwords in the membership data store. + + + + One of the values indicating the format for storing passwords in the data store. + + + + + + Gets the minimum length required for a password. + + + + The minimum length required for a password. + + + + + + Gets the minimum number of special characters that must be present in a valid password. + + + + The minimum number of special characters that must be present in a valid password. + + + + + + Gets the regular expression used to evaluate a password. + + + + A regular expression used to evaluate a password. + + + + + + Provides platform independent access to HttpRuntime methods + + + For e.g. testing purposes, the default environment implementation may be replaced using . + + Erich Eichinger + $Id: VirtualEnvironment.cs,v 1.5 2008/03/14 12:02:45 oakinger Exp $ + + + + Replaces the current enviroment implementation. + + the new environment implementation to be used + the previously set environment instance + + + + Maps a virtual path to it's physical location + + + + + Receives EndRequest-event from an instance + and dispatches it to all handlers registered with this module. + + the HttpApplication instance sending this event + always + + + + Receives the EndSession-event and dispatches it to all handlers + registered with this module. + + + + + Signals, that VirtualEnvironment is ready to accept + handler registrations for EndRequest and EndSession events + + + + + Ensures, that WebSupportModule has been initialized. Otherwise an exception is thrown. + + + + + The virtual (rooted) path of the current Application containing a leading '/' as well as a trailing '/' + + + + + The virtual (rooted) path of the current Request including + + + + + The virtual (rooted) path of the current Request without trailing + + + + + The virtual (rooted) path of the currently executing script + + + + + Register with this event to receive any EndRequest event occuring in the current AppDomain. + + + + + Register with this event to receive any EndSession event occuring in the current AppDomain + + + This event may be raised asynchronously on it's own thread. + Don't rely on e.g. being available. + + + + + Is this VirtualEnviroment ready to accept handler registrations + for EndRequest and EndSession events ? + + + + + Represents a method that handles Request related events + + + + + Represents a method that handles Session related events + + + + + Implementation for running within HttpRuntime + + + + + Culture resolver that uses HTTP session to store culture information. + + Aleksandar Seovic + $Id: SessionCultureResolver.cs,v 1.3 2007/08/25 14:26:35 oakinger Exp $ + + + + Resolves the culture from the request. + + + If culture information doesn't exist in the session, it will be created and its value will + be set to the value of the 'Accept-Language' request header, or if no + headers are specified to the culture of the current server thread. + + Culture that should be used to render view. + + + + Sets the culture. + + The new culture or null to clear the culture. + + + + Gets/Sets the current session's culture. + + + + + Resource cache implementation that uses ASP.NET to cache resources. + + Aleksandar Seovic + $Id: AspNetResourceCache.cs,v 1.6 2007/08/03 06:08:59 oakinger Exp $ + + + + Gets the list of resources from cache. + + Cache key to use for lookup. + A list of cached resources for the specified target object and culture. + + + + Puts the list of resources in the cache. + + Cache key to use for the specified resources. + A list of resources to cache. + + + + A spring-configurable version of + + Erich Eichinger + $Id: ConfigurableXmlSiteMapProvider.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ + + + + Initializes the provider. + + + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + + Values may be overridden by specifying them in list. + + The friendly name of the provider. + The or is null. + An attempt is made to call on a provider after the provider has already been initialized. + The has a length of zero or providerId attribute is not set. + + + + The XML file to be used for reading in the sitemap + + + + + A collection of the name/value pairs representing the provider-specific + attributes specified in the configuration for this provider. + + + + + A custom implementation of the + + interface that properly handles web application specific attributes, + such as object scope. + + +

    + Parses object definitions according to the standard Spring.NET schema. +

    +
    + Aleksandar Seovic + $Id: WebObjectsNamespaceParser.cs,v 1.1 2007/08/07 23:22:30 markpollack Exp $ + +
    + + + Creates a new instance of the + class. + + + + + Parses an object definition and set various web related properties + if the definition is an . + + The object definition element. + The id / name of the object definition. + the parser helper + The object (definition). + +

    + The 'various web related properties' currently includes the + intended scope of the object. +

    +
    + + +
    + + + Calculates an id for an object definition. + + + The element containing the object definition. + + + The list of names defined for the object; may be + or even empty. + + + A calculated object definition id. + + . + + + + Gets the scope out of the supplied . + + +

    + If the supplied is invalid + (i.e. it does not resolve to one of the + values), + then the return value of this method call will be + ; + no exception will be raised (although the value of the invalid + scope will be logged). +

    +
    + The string containing the scope name. + The scope. + +
    + + + Spring.NET Master Page implementation for ASP.NET 2.0 + + Aleksandar Seovic + $Id: MasterPage.cs,v 1.15 2008/04/22 15:08:59 markpollack Exp $ + + + + Initializes user control. + + + + + Binds data from the data model into controls and raises + PreRender event afterwards. + + Event arguments. + + + + Raises InitializeControls event. + + Event arguments. + + + + Obtains a object from a user control file + and injects dependencies according to Spring config file. + + The virtual path to a user control file. + + Returns the specified object, with dependencies injected. + + + + + Obtains a object by type + and injects dependencies according to Spring config file. + + The type of a user control. + parameters to pass to the control + + Returns the specified object, with dependencies injected. + + + + + Raises DataBound event. + + Event arguments. + + + + Raises DataBound event. + + Event arguments. + + + + Initializes local message source + + + + + Creates and returns local ResourceManager for this page. + + + + In ASP.NET 1.1, this method loads local resources from the web application assembly. + + + However, in ASP.NET 2.0, local resources are compiled into the dynamic assembly, + so we need to find that assembly instead and load the resources from it. + + + Local ResourceManager instance. + + + + Returns message for the specified resource name. + + Resource name. + Message text. + + + + Returns message for the specified resource name. + + Resource name. + Message arguments that will be used to format return value. + Formatted message text. + + + + Returns resource object for the specified resource name. + + Resource name. + Resource object. + + + + Evaluates specified validators and returns True if all of them are valid. + + +

    + Each validator can itself represent a collection of other validators if it is + an instance of or one of its derived types. +

    +

    + Please see the Validation Framework section in the documentation for more info. +

    +
    + Object to validate. + Validators to evaluate. + + True if all of the specified validators are valid, False otherwise. + +
    + + + Creates the validator parameters. + + + + This method can be overriden if you want to pass additional parameters + to the validation framework, but you should make sure that you call + this base implementation in order to add page, session, application, + request, response and context to the variables collection. + + + + Dictionary containing parameters that should be passed to + the data validation framework. + + + + + Injects dependencies before adding the control. + + + + + This event is raised before Load event and should be used to initialize + controls as necessary. + + + + + This event is raised after all controls have been populated with values + from the data model. + + + + + This event is raised after data model has been populated with values from + web controls. + + + + + Gets or sets Spring application context. + + + + + Gets or sets the localizer. + + The localizer. + + + + Gets or sets the local message source. + + The local message source. + + + + Gets or sets user's culture + + + + + Gets the validation errors container. + + The validation errors container. + + + + Overrides Page property to return + instead of . + + + + + Holds the default ApplicationContext to be used during DI. + + + + + Groups radio button controls and returns the value of the selected control + + + This control alows radio buttons to be data-bound to a data model of the page. + + Aleksandar Seovic + $Id: RadioButtonGroup.cs,v 1.5 2007/03/16 22:38:55 oakinger Exp $ + + + + Overloaded to track addition of contained controls. + + + + + Overloaded to track removal of a RadioButton + + + + + + Registers the RadioButtonGroup for PostBack. + + + + + Renders only children of this control. + + HtmlTextWriter to use for rendering. + + + + Loads postback data into the control. + + Key that should be used to retrieve data. + Postback data collection. + True if data has changed, false otherwise. + + + + Raises SelectionChanged event. + + + + + Method that is called on postback if selected radio button has changed. + + Empty event argument. + + + + Gets or sets the ID of the selected radio button. + + ID of the selected radio button. + + + + Gets or sets whether form should be posted back on every selection change + within the radio group. + + + + + Occurs when the value of the radio button group changes between postbacks to the server. + + + + + Object instantiation strategy for use in + . + + +

    + This strategy checks if objects id ASP.Net page and if it is uses + PageParser to compile and instantiate page instance. Otherwise it + delagates call to its parent. +

    +
    + Aleksandar Seovic + $Id: WebInstantiationStrategy.cs,v 1.4 2008/05/02 16:44:50 markpollack Exp $ +
    + + + Creates a new instance of the + class. + + + + + Instantiate an instance of the object described by the supplied + from the supplied . + + + The definition of the object that is to be instantiated. + + + The name associated with the object definition. The name can be the null + or zero length string if we're autowiring an object that doesn't belong + to the supplied . + + + The owning + + + An instance of the object described by the supplied + from the supplied . + + + + + Resource cache implementation that uses Spring.NET page/handler state to cache resources. + + Aleksandar Seovic + $Id: SharedStateResourceCache.cs,v 1.1 2007/08/03 08:31:25 oakinger Exp $ + + + + Gets the list of resources from cache. + + Cache key to use for lookup. + A list of cached resources for the specified target object and culture. + + + + Puts the list of resources in the cache. + + Cache key to use for the specified resources. + A list of resources to cache. + + + + Implementation of that + displays an error image to let user know there is an error, and + tooltip to display actual error messages. + + + + This renderer's behavior is similar to Windows Forms error provider. + + + Aleksandar Seovic + $Id: IconValidationErrorsRenderer.cs,v 1.1 2007/08/02 19:50:28 markpollack Exp $ + + + + Renders validation errors using specified . + + Web form instance. + An HTML writer to use. + The list of validation errors. + + + + Gets or sets the name of the image file to use as an error icon. + + + + Image name should be relative to the value of the + property, and should not use leading path separator. + + + The name of the image file to use as an error icon. + + + + This control should be used instead of standard HTML head tag + in order to render dynamicaly registered head scripts and stylesheets. + + + If you need to use ASP.NETs built-in <head> tag, you should nest Spring's within ASP.NET's: + + <html> + <head> + <title>Some Title</title> + <spring:head> + <-- will render styleblocks etc. here --> + </spring:head> + </head> + </html> + + + Aleksandar Seovic + $Id: Head.cs,v 1.8 2007/11/28 23:26:19 oakinger Exp $ + + + + Sends server control content to a provided object, which writes the content to + be rendered on + the client. + + The object that receives the server control content. + + + + Gets or sets the default mimetype for the <style> element's 'type' attribute + + + Defaults to "text/css" + + + + + Gets a reference to the instance that contains the + server control. + + + + + + Any WebControl placed on a DataBindingPanel may be bound + to a model by adding an attribute "BindingTarget" + + Erich Eichinger + $Id: DataBindingPanel.cs,v 1.13 2008/03/06 20:23:44 oakinger Exp $ + + + + Registers this control for the event of it's container. + + + + + Overridden to remove custom binding attributes before rendering. + + + + + + Overriden to suppress rendering this control's tag + + + + + + Called by the containing if bindings must be initialized. + + + + + Adds all controls on this panel to the containing 's binding collection. + + the usercontrol containing this panel + the of controls to be bound + action to be performed on matching controls + + + + Retrieves custom binding attributes from a webcontrol and adds a new binding + instance to the container's if necessary. + + + + + Removes custom binding attributes from a webcontrol to avoid them being rendered. + + + + + Probe for bindingType of know controls + + the control, who's bindingType is to be determined + null, if standard binding is to be used or the fully qualified typename of the binding implementation + + + + Probes for a few know controls and their properties. + + + The 'BindingSource' expression to be used for binding this control. + + + + + Support Class providing a method to ensure a control has been intercepted + + Erich Eichinger + $Id: ControlInterceptor.cs,v 1.2 2008/05/13 14:22:47 oakinger Exp $ + + + + Holds all available interception strategies + + + + + Holds a control.GetType()->IInterceptionStrategy table. + + + + + Ensures, a control has been intercepted to support Web-DI. If not, the control will be intercepted. + + +
    +
    diff --git a/src/Spring/Spring.Web/Threading/HttpContextStorage.cs b/src/Spring/Spring.Web/Threading/HttpContextStorage.cs new file mode 100644 index 00000000..4c0d19e0 --- /dev/null +++ b/src/Spring/Spring.Web/Threading/HttpContextStorage.cs @@ -0,0 +1,41 @@ +using System.Web; + +namespace Spring.Threading +{ + /// + /// Implements by using . + /// + /// Erich Eichinger + /// $Id: HttpContextStorage.cs,v 1.1 2007/02/02 21:31:25 oakinger Exp $ + public class HttpContextStorage : IThreadStorage + { + /// + /// Retrieves an object with the specified name. + /// + /// The name of the item. + /// The object in the context associated with the specified name or null if no object has been stored previously + public object GetData(string name) + { + return HttpContext.Current.Items[name]; + } + + /// + /// Stores a given object and associates it with the specified name. + /// + /// The name with which to associate the new item. + /// The object to store in the call context. + public void SetData(string name, object value) + { + HttpContext.Current.Items[name] = value; + } + + /// + /// Empties a data slot with the specified name. + /// + /// The name of the data slot to empty. + public void FreeNamedDataSlot(string name) + { + HttpContext.Current.Items.Remove(name); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Threading/HybridContextStorage.cs b/src/Spring/Spring.Web/Threading/HybridContextStorage.cs new file mode 100644 index 00000000..7d76ea6d --- /dev/null +++ b/src/Spring/Spring.Web/Threading/HybridContextStorage.cs @@ -0,0 +1,71 @@ +using System.Runtime.Remoting.Messaging; +using System.Web; + +namespace Spring.Threading +{ + /// + /// Implements by using both and and choosing dynamically between them. + /// + /// + /// In web applications a single Request may be executed on different threads. In this case HttpContext.Current is the only invariant.
    + /// This implementation dynamically chooses between System.Runtime.Remoting.Messaging.CallContext + /// and System.Web.HttpContext to store data. + ///
    + /// Erich Eichinger + /// $Id: HybridContextStorage.cs,v 1.1 2007/02/02 21:31:25 oakinger Exp $ + public class HybridContextStorage : IThreadStorage + { + /// + /// Retrieves an object with the specified name. + /// + /// The name of the item. + /// The object in the context associated with the specified name or null if no object has been stored previously + public object GetData(string name) + { + HttpContext ctx = HttpContext.Current; + if(ctx == null) + { + return CallContext.GetData(name); + } + else + { + return ctx.Items[name]; + } + } + + /// + /// Stores a given object and associates it with the specified name. + /// + /// The name with which to associate the new item. + /// The object to store in the call context. + public void SetData(string name, object value) + { + HttpContext ctx = HttpContext.Current; + if(ctx == null) + { + CallContext.SetData(name, value); + } + else + { + ctx.Items[name] = value; + } + } + + /// + /// Empties a data slot with the specified name. + /// + /// The name of the data slot to empty. + public void FreeNamedDataSlot(string name) + { + HttpContext ctx = HttpContext.Current; + if(ctx == null) + { + CallContext.FreeNamedDataSlot(name); + } + else + { + ctx.Items.Remove(name); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Util/ControlAccessor.cs b/src/Spring/Spring.Web/Util/ControlAccessor.cs new file mode 100644 index 00000000..27a5cc20 --- /dev/null +++ b/src/Spring/Spring.Web/Util/ControlAccessor.cs @@ -0,0 +1,301 @@ +#region License +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Web.UI; +using Spring.Reflection.Dynamic; +using Spring.Web.Support; + +#endregion + +namespace Spring.Util +{ + /// + /// Helper class for easier access to reflected Control members. + /// + /// Erich Eichinger + /// $Id: ControlAccessor.cs,v 1.2 2008/05/13 14:22:47 oakinger Exp $ + internal class ControlAccessor + { + private static readonly MethodInfo s_miClear = GetMethod("Clear"); + private static MethodInfo GetMethod(string name) + { + return typeof(Control).GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + } + private static FieldInfo GetField(string name) + { + return typeof(Control).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + } + + private delegate ControlCollection CreateControlCollectionDelegate(Control target); + private delegate void AddedControlDelegate(Control target, Control control, int index); + private delegate void RemovedControlDelegate(Control target, Control control); + private delegate void VoidMethodDelegate(Control target); + +#if NET_2_0 + private static readonly CreateControlCollectionDelegate BaseCreateControlCollection = (CreateControlCollectionDelegate) Delegate.CreateDelegate(typeof(CreateControlCollectionDelegate), GetMethod("CreateControlCollection")); + private static readonly AddedControlDelegate BaseAddedControl = (AddedControlDelegate) Delegate.CreateDelegate(typeof (AddedControlDelegate), GetMethod("AddedControl")); + private static readonly RemovedControlDelegate BaseRemovedControl = (RemovedControlDelegate) Delegate.CreateDelegate(typeof (RemovedControlDelegate), GetMethod("RemovedControl")); + private static readonly VoidMethodDelegate BaseClearNamingContainer = (VoidMethodDelegate) Delegate.CreateDelegate(typeof (VoidMethodDelegate), GetMethod("ClearNamingContainer")); +#else + private class DynamicMethodWrapper + { + private IDynamicMethod _method; + + public DynamicMethodWrapper(IDynamicMethod method) + { + _method = method; + } + + public static CreateControlCollectionDelegate CreateControlCollection(MethodInfo methodInfo) + { + IDynamicMethod method = new SafeMethod(methodInfo); + return new CreateControlCollectionDelegate(new DynamicMethodWrapper(method).CreateControlCollectionInternal); + } + + public static AddedControlDelegate AddedControl(MethodInfo methodInfo) + { + IDynamicMethod method = new SafeMethod(methodInfo); + return new AddedControlDelegate(new DynamicMethodWrapper(method).AddedControlInternal); + } + + public static RemovedControlDelegate RemovedControl(MethodInfo methodInfo) + { + IDynamicMethod method = new SafeMethod(methodInfo); + return new RemovedControlDelegate(new DynamicMethodWrapper(method).RemovedControlInternal); + } + + public static VoidMethodDelegate ClearNamingContainer(MethodInfo methodInfo) + { + IDynamicMethod method = new SafeMethod(methodInfo); + return new VoidMethodDelegate(new DynamicMethodWrapper(method).ClearNamingContainerInternal); + } + + private ControlCollection CreateControlCollectionInternal(Control target) + { + return (ControlCollection) _method.Invoke(target, null); + } + + private void AddedControlInternal(Control target, Control control, int index) + { + _method.Invoke(target, new object[] { control, index }); + } + + private void RemovedControlInternal(Control target, Control control) + { + _method.Invoke(target, new object[] { control }); + } + + private void ClearNamingContainerInternal(Control target) + { + _method.Invoke(target, null); + } + } + + private static readonly CreateControlCollectionDelegate BaseCreateControlCollection = DynamicMethodWrapper.CreateControlCollection(GetMethod("CreateControlCollection")); + private static readonly AddedControlDelegate BaseAddedControl = DynamicMethodWrapper.AddedControl(GetMethod("AddedControl")); + private static readonly RemovedControlDelegate BaseRemovedControl = DynamicMethodWrapper.RemovedControl(GetMethod("RemovedControl")); + private static readonly VoidMethodDelegate BaseClearNamingContainer = DynamicMethodWrapper.ClearNamingContainer(GetMethod("ClearNamingContainer")); +#endif + + private readonly Control _targetControl; + + /// + /// Instantiates a new Accessor. + /// + /// + public ControlAccessor(Control control) + { + this._targetControl = control; + } + + /// + /// Returns the underlying ControlCollection instance. + /// + public Control GetTarget() + { + return _targetControl; + } + + /// + /// Gets or sets the ControlCollection of the target without accessing the target's property. + /// + /// + /// If the underlying collection is null, it is automatically created. + /// + public ControlCollection Controls + { + get + { + ControlCollection controls = GetChildControlCollection(); + if (controls == null) + { + controls = InterceptControlCollectionStrategy.TryCreateCollection(_targetControl); + if (controls == null) + { + controls = BaseCreateControlCollection(_targetControl); + } + SetChildControlCollection(controls); + } + return controls; + } + set { SetChildControlCollection(value); } + } + + public void AddedControl(Control control, int index) + { + BaseAddedControl(_targetControl, control, index); + } + + public void RemovedControl(Control control) + { + BaseRemovedControl(_targetControl, control); + + if (!_targetControl.HasControls()) + { + // clear naming table etc. if collection has been cleared + // this is because we can't intercept Control.ClearNamingTable(), + // which is called by ControlCollection.Clear() after removing all controls + StackFrame frame = new StackFrame(3, false); + if (frame.GetMethod() == s_miClear) + { + if (_targetControl is INamingContainer) + { + //s_miClearNamingContainer.Invoke(_targetControl, null); + BaseClearNamingContainer(_targetControl); + } + } + } + } + +#if NET_2_0 + private delegate ControlCollection GetControlsDelegate(Control target); + private delegate void SetControlsDelegate(Control target, ControlCollection controls); + + private static readonly GetControlsDelegate GetChildControlCollectionInternal = GetGetControlsDelegate(); + private static readonly SetControlsDelegate SetChildControlCollectionInternal = GetSetControlsDelegate(); + + private ControlCollection GetChildControlCollection() + { + return GetChildControlCollectionInternal(_targetControl); + } + + private void SetChildControlCollection(ControlCollection controls) + { + SetChildControlCollectionInternal(_targetControl, controls); + } + + private static GetControlsDelegate GetGetControlsDelegate() + { + FieldInfo occasionalFields = GetField("_occasionalFields"); + MethodInfo ensureOccasionalFields = GetMethod("EnsureOccasionalFields"); + FieldInfo controls = occasionalFields.FieldType.GetField("Controls"); + + System.Reflection.Emit.DynamicMethod dm = new System.Reflection.Emit.DynamicMethod("get_Controls", typeof(ControlCollection), new Type[] { typeof(Control) }, typeof(Control).Module, true); + ILGenerator il = dm.GetILGenerator(); + Label occFieldsNull = il.DefineLabel(); + Label retControls = il.DefineLabel(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, occasionalFields); + il.Emit(OpCodes.Brfalse_S, occFieldsNull); + il.MarkLabel(retControls); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, occasionalFields); + il.Emit(OpCodes.Ldfld, controls); + il.Emit(OpCodes.Ret); + il.MarkLabel(occFieldsNull); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Call, ensureOccasionalFields); + il.Emit(OpCodes.Br, retControls); + + return (GetControlsDelegate) dm.CreateDelegate(typeof(GetControlsDelegate)); + } + + private static SetControlsDelegate GetSetControlsDelegate() + { + FieldInfo occasionalFields = GetField("_occasionalFields"); + MethodInfo ensureOccasionalFields = GetMethod("EnsureOccasionalFields"); + FieldInfo controls = occasionalFields.FieldType.GetField("Controls"); + + System.Reflection.Emit.DynamicMethod dm = new System.Reflection.Emit.DynamicMethod("set_Controls", null, new Type[] { typeof(Control), typeof(ControlCollection) }, typeof(Control).Module, true); + ILGenerator il = dm.GetILGenerator(); + Label occFieldsNull = il.DefineLabel(); + Label setControls = il.DefineLabel(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, occasionalFields); + il.Emit(OpCodes.Brfalse_S, occFieldsNull); + il.MarkLabel(setControls); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, occasionalFields); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Stfld, controls); + il.Emit(OpCodes.Ret); + il.MarkLabel(occFieldsNull); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Call, ensureOccasionalFields); + il.Emit(OpCodes.Br, setControls); + + return (SetControlsDelegate) dm.CreateDelegate(typeof(SetControlsDelegate)); + } + +// private static readonly Type s_tOccasionalFields = typeof(Control).GetNestedType("OccasionalFields", BindingFlags.NonPublic); +// +// private static readonly VoidMethodDelegate EnsureOccasionalFields = (VoidMethodDelegate) Delegate.CreateDelegate(typeof (VoidMethodDelegate), GetMethod("EnsureOccasionalFields")); +// private static readonly IDynamicField _occasionalFields = SafeField.CreateFrom(GetField("_occasionalFields")); +// private static readonly IDynamicField _controls = SafeField.CreateFrom(s_tOccasionalFields.GetField("Controls")); +// +// private ControlCollection GetChildControlCollection() +// { +// // we *must not* simply call control.Controls here! +// // Some controls (e.g. Repeater overload this property and call Control.EnsureChildControls() +// // which causes Control.ChildControlsCreated flag to be set and prevents children from being created after loading viewstate! +// +// EnsureOccasionalFields(_targetControl); +// +// object occasionalFields = _occasionalFields.GetValue(_targetControl); +// object childControls = _controls.GetValue(occasionalFields); +// return (ControlCollection) childControls; +// } +// +// private void SetChildControlCollection( ControlCollection controls ) +// { +// EnsureOccasionalFields(_targetControl); +// +// object occasionalFields = _occasionalFields.GetValue(_targetControl); +// _controls.SetValue(occasionalFields, controls); +// } +#else + private static readonly IDynamicField fControls = SafeField.CreateFrom(GetField("_controls")); + + private ControlCollection GetChildControlCollection() + { + return (ControlCollection)fControls.GetValue(_targetControl); + } + + private void SetChildControlCollection(ControlCollection controls) + { + fControls.SetValue(_targetControl, controls); + } +#endif + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Util/ControlCollectionAccessor.cs b/src/Spring/Spring.Web/Util/ControlCollectionAccessor.cs new file mode 100644 index 00000000..e06dcf1d --- /dev/null +++ b/src/Spring/Spring.Web/Util/ControlCollectionAccessor.cs @@ -0,0 +1,76 @@ +#region License +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Web.UI; +using Spring.Reflection.Dynamic; + +#endregion + +namespace Spring.Util +{ + /// + /// Helper class for easier access to reflected ControlCollection members. + /// + /// Erich Eichinger + /// $Id: ControlCollectionAccessor.cs,v 1.3 2008/05/13 23:23:03 oakinger Exp $ + internal class ControlCollectionAccessor + { + private static readonly IDynamicField _owner = new SafeField(typeof (ControlCollection).GetField("_owner", BindingFlags.Instance | BindingFlags.NonPublic)); + private readonly ControlCollection _controls; + private readonly Type _controlsType; + + /// + /// Returns the underlying ControlCollection instance. + /// + public ControlCollection GetTarget() + { + return _controls; + } + + /// + /// Returns the type of the underlying ControlCollection instance. + /// + public Type GetTargetType() + { + return _controlsType; + } + + /// + /// Creates a new Accessor for a given . + /// + /// The to be accessed + public ControlCollectionAccessor(ControlCollection controls) + { + _controls = controls; + _controlsType = controls.GetType(); + } + + /// + /// Gets or sets the owner of the underlying ControlCollection. + /// + public Control Owner + { + get { return (Control) _owner.GetValue(_controls); } + set { _owner.SetValue(_controls, value); } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Util/HttpContextSwitch.cs b/src/Spring/Spring.Web/Util/HttpContextSwitch.cs new file mode 100644 index 00000000..397244aa --- /dev/null +++ b/src/Spring/Spring.Web/Util/HttpContextSwitch.cs @@ -0,0 +1,101 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web; +using Common.Logging; +using Spring.Util; + +#endregion + +namespace Spring.Util +{ + /// + /// Performs a . Original path will be restored on + /// + /// + /// Rewrites the current HttpContext's filepath to <directory>/currentcontext.dummy.
    + /// This affects resolving resources by calls to and
    + /// Original path is restored during . + ///
    + /// + /// + /// using( new HttpContextSwitch( "/path" ) ) + /// { + /// Response.Write( Request.FilePath ); // writes "/path/currentcontext.dummy" to response. + /// } + /// // Request.FilePath has been reset to original url here + /// + /// + /// Erich Eichinger + /// $Id: HttpContextSwitch.cs,v 1.1 2007/08/01 23:10:51 markpollack Exp $ + public class HttpContextSwitch : IDisposable + { + private HttpContext savedContext; + private string originalUrl; + private static readonly ILog log = LogManager.GetLogger(typeof(HttpContextSwitch)); + + /// + /// Performs an immediate call to + /// + /// a directory path (without trailing filename!) + public HttpContextSwitch( string virtualDirectory ) + { + HttpContext currentContext = HttpContext.Current; + if (currentContext == null) return; // no webrequest + + virtualDirectory = WebUtils.GetVirtualDirectory(virtualDirectory); + string currentFileDirectory = WebUtils.GetVirtualDirectory(currentContext.Request.FilePath); + // only switch path if necessary + if (string.Compare( virtualDirectory, currentFileDirectory, true ) != 0) + { + savedContext = currentContext; + originalUrl = savedContext.Request.Url.PathAndQuery; + string newPath = virtualDirectory + "currentcontext.dummy"; +#if NET_2_0 + savedContext.RewritePath( newPath, false ); +#else + savedContext.RewritePath( newPath ); +#endif + if (log.IsDebugEnabled) log.Debug("rewriting path from " + originalUrl + " to " + newPath + " results in " + savedContext.Request.FilePath); + } + } + + /// + /// Restores original path if necessary + /// + public void Dispose() + { + if (savedContext != null) + { + if (log.IsDebugEnabled) log.Debug("restoring original path from " + savedContext.Request.FilePath + " back to " + originalUrl); + HttpContext context = savedContext; + savedContext = null; +#if NET_2_0 + context.RewritePath( originalUrl, false ); +#else + context.RewritePath( originalUrl ); +#endif + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Util/IVirtualEnvironment.cs b/src/Spring/Spring.Web/Util/IVirtualEnvironment.cs new file mode 100644 index 00000000..1ef9331f --- /dev/null +++ b/src/Spring/Spring.Web/Util/IVirtualEnvironment.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Reflection; +using System.Web; +using System.Web.UI; +using Common.Logging; +using Spring.Context; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Util +{ + /// + /// Abstracts the underlying infrastructure of a HttpRequest + /// + public interface IVirtualEnvironment + { + /// + /// The virtual (rooted) path of the current Application with trailing slash + /// + /// + /// For the site rooted applications, "/" will be returned, for all others "/..someappdir../" + /// + string ApplicationVirtualPath { get; } + /// + /// The virtual (rooted) path of the current Request including + /// + string CurrentVirtualPath { get; } + /// + /// The virtual (rooted) path of the current Request without trailing + /// + string CurrentVirtualFilePath { get; } + /// + /// The virtual (rooted) path of the currently executing script + /// + /// + /// Normally this property is the same as . + /// In case of , this property returns the current script + /// whereas CurrentVirtualPath returns the original script path. + /// + string CurrentExecutionFilePath { get; } + /// + /// Maps a virtual path to it's physical location + /// + string MapPath( string virtualPath ); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Util/VirtualEnvironment.cs b/src/Spring/Spring.Web/Util/VirtualEnvironment.cs new file mode 100644 index 00000000..53443793 --- /dev/null +++ b/src/Spring/Spring.Web/Util/VirtualEnvironment.cs @@ -0,0 +1,286 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Web; +using System.Web.Caching; +using System.Web.SessionState; + +#endregion + +namespace Spring.Util +{ + /// + /// Provides platform independent access to HttpRuntime methods + /// + /// + /// For e.g. testing purposes, the default environment implementation may be replaced using . + /// + /// Erich Eichinger + /// $Id: VirtualEnvironment.cs,v 1.5 2008/03/14 12:02:45 oakinger Exp $ + public sealed class VirtualEnvironment + { + // default to standard HttpRuntime + private static IVirtualEnvironment instance = new HttpRuntimeEnvironment(); + + /// + /// Represents a method that handles Request related events + /// + public delegate void RequestEventHandler(HttpContext context); + + /// + /// Represents a method that handles Session related events + /// + public delegate void SessionEventHandler(HttpSessionState session, CacheItemRemovedReason reason); + + private static readonly object syncEndRequestEvent = new object(); + private static RequestEventHandler s_requestEvent; + private static readonly object syncEndSessionEvent = new object(); + private static SessionEventHandler s_sessionEvent; + + private static volatile bool s_isInitialized = false; + + /// + /// Replaces the current enviroment implementation. + /// + /// the new environment implementation to be used + /// the previously set environment instance + public static IVirtualEnvironment SetInstance(IVirtualEnvironment newEnvironment) + { + IVirtualEnvironment prevEnvironment = instance; + instance = newEnvironment; + return prevEnvironment; + } + + #region default IVirtualEnvironment Adapter for HttpRuntime + + /// + /// Implementation for running within HttpRuntime + /// + private class HttpRuntimeEnvironment : IVirtualEnvironment + { + public string ApplicationVirtualPath + { + get { + string appPath = HttpRuntime.AppDomainAppVirtualPath; + if (!appPath.EndsWith("/")) appPath = appPath + "/"; + return appPath; + } + } + + public string CurrentVirtualPath + { + get { return HttpContext.Current.Request.Path; } + } + + public string CurrentVirtualFilePath + { + get { return HttpContext.Current.Request.FilePath; } + } + + public string CurrentExecutionFilePath + { + get { return HttpContext.Current.Request.CurrentExecutionFilePath; } + } + + public string MapPath(string virtualPath) + { + HttpContext ctx = HttpContext.Current; + if (ctx != null) + { + return ctx.Request.MapPath(virtualPath); + } +#if NET_2_0 + + if (VirtualPathUtility.IsAbsolute(virtualPath) && virtualPath.StartsWith(HttpRuntime.AppDomainAppVirtualPath)) + { + virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); + } + if (VirtualPathUtility.IsAppRelative(virtualPath)) + { + virtualPath = virtualPath.Substring(2); // strip "~/" + string physicalPath = Path.Combine(HttpRuntime.AppDomainAppPath, virtualPath); + return physicalPath; + } +#endif + throw new ArgumentException("can't map context relative path outside a context"); + } + } + + #endregion + + /// + /// The virtual (rooted) path of the current Application containing a leading '/' as well as a trailing '/' + /// + public static string ApplicationVirtualPath + { + get { return instance.ApplicationVirtualPath; } + } + + /// + /// The virtual (rooted) path of the current Request including + /// + public static string CurrentVirtualPath + { + get { return instance.CurrentVirtualPath; } + } + + /// + /// The virtual (rooted) path of the current Request without trailing + /// + public static string CurrentVirtualFilePath + { + get { return instance.CurrentVirtualFilePath; } + } + + /// + /// The virtual (rooted) path of the currently executing script + /// + public static string CurrentExecutionFilePath + { + get { return instance.CurrentExecutionFilePath; } + } + + /// + /// Maps a virtual path to it's physical location + /// + public static string MapPath(string virtualPath) + { + return instance.MapPath(virtualPath); + } + + /// + /// Receives EndRequest-event from an instance + /// and dispatches it to all handlers registered with this module. + /// + /// the HttpApplication instance sending this event + /// always + public static void RaiseEndRequest(object sender, EventArgs e) + { + // NOTE: don't sync here for performance reasons. + // It is assumed, that all handlers are registered during application startup + if (s_requestEvent != null) + { + s_requestEvent(((HttpApplication)sender).Context); + } + } + + /// + /// Receives the EndSession-event and dispatches it to all handlers + /// registered with this module. + /// + public static void RaiseEndSession(HttpSessionState sessionState, CacheItemRemovedReason reason) + { + // NOTE: don't sync here for performance reasons. + // It is assumed, that all handlers are registered during application startup + if (s_sessionEvent != null) + { + s_sessionEvent(sessionState, reason); + } + } + + /// + /// Register with this event to receive any EndRequest event occuring in the current AppDomain. + /// + public static event RequestEventHandler EndRequest + { + add + { + AssertInitialized(); + lock (syncEndRequestEvent) + { + s_requestEvent += value; + } + } + remove + { + AssertInitialized(); + lock (syncEndRequestEvent) + { + s_requestEvent -= value; + } + } + } + + /// + /// Register with this event to receive any EndSession event occuring in the current AppDomain + /// + /// + /// This event may be raised asynchronously on it's own thread. + /// Don't rely on e.g. being available. + /// + public static event SessionEventHandler EndSession + { + add + { + AssertInitialized(); + lock (syncEndSessionEvent) + { + s_sessionEvent += value; + } + } + remove + { + AssertInitialized(); + lock (syncEndSessionEvent) + { + s_sessionEvent -= value; + } + } + } + + /// + /// Signals, that VirtualEnvironment is ready to accept + /// handler registrations for EndRequest and EndSession events + /// + public static void SetInitialized() + { + s_isInitialized = true; + } + + /// + /// Is this VirtualEnviroment ready to accept handler registrations + /// for EndRequest and EndSession events ? + /// + public static bool IsInitialized + { + get { return s_isInitialized; } + } + + /// + /// Ensures, that WebSupportModule has been initialized. Otherwise an exception is thrown. + /// + private static void AssertInitialized() + { + if (!s_isInitialized) + { + string msg = + @"WebSupportModule not initialized. Did you forget to add " + + @" " + + @"to your web.config's -section?"; + throw ConfigurationUtils.CreateConfigurationException(msg); + } + } + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Util/WebUtils.cs b/src/Spring/Spring.Web/Util/WebUtils.cs new file mode 100644 index 00000000..f0858fe5 --- /dev/null +++ b/src/Spring/Spring.Web/Util/WebUtils.cs @@ -0,0 +1,246 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + + + +#endregion + +namespace Spring.Util +{ + /// + /// Miscellaneous web utility methods. + /// + /// Aleksandar Seovic + /// $Id: WebUtils.cs,v 1.32 2008/03/19 18:05:07 oakinger Exp $ + public sealed class WebUtils + { + #region Constructor (s) / Destructor + + // CLOVER:OFF + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// This is a utility class, and as such exposes no public constructors. + ///

    + ///
    + private WebUtils() + {} + + // CLOVER:ON + + #endregion + + /// + /// Default protocol used for resolving resources in web applications + /// + internal static readonly string DEFAULT_RESOURCE_PROTOCOL = "web"; + + /// + /// Extracts the bare ASPX page name without any extension from the + /// supplied . + /// + /// + ///

    + /// Examples of what would be returned from this method given a url would be: + ///

    + ///

    + /// + /// 'Login.aspx' => 'Login' + /// '~/Login.aspx' => 'Login' + /// '~/B2B/SignUp.aspx' => 'SignUp' + /// 'B2B/Foo/FooServices.aspx' => 'FooServices' + /// + ///

    + ///
    + /// The full URL to the ASPX page. + /// + /// The bare ASPX page name without any extension. + /// + /// + /// If the supplied is or + /// contains only whitespace character(s). + /// + public static string GetPageName(string url) + { + AssertUtils.ArgumentHasText(url, "url"); + int lastSlash = url.LastIndexOf('/'); + int lastDot = url.LastIndexOf('.'); + if (lastDot < lastSlash) + { + lastDot = -1; + } + if (lastDot < 0) + { + int length = url.Length - lastSlash - 1; + return url.Substring(lastSlash + 1, length); + } + + return url.Substring(lastSlash + 1, lastDot - lastSlash - 1); + } + + /// + /// Returns only the directory portion of a virtual path + /// + /// + /// The returned path is guaranteed to always have a leading and a trailing slash.
    + /// If a path does not end with a file-extension it is assumed to be a directory + ///
    + public static string GetVirtualDirectory(string virtualPath) + { + AssertUtils.ArgumentNotNull(virtualPath, "virtualPath"); + + if (virtualPath.Length == 0) + { + return "/"; + } + + if (virtualPath[0] != '/') + { + virtualPath = "/" + virtualPath; + } + + if (virtualPath[virtualPath.Length - 1] != '/') + { + int iDot = virtualPath.LastIndexOf('.'); + int iSlash = virtualPath.LastIndexOf('/'); + if (iDot < iSlash) iDot = -1; + if (iDot > -1) + { + virtualPath = virtualPath.Substring(0, iSlash + 1); + } + else + { + virtualPath = virtualPath + "/"; + } + } + return virtualPath; + } + + /// + /// Returns absolute path that can be referenced within plain HTML. + /// + /// + ///

    + /// If relative path starts with '/' (forward slash), no concatenation will occur + /// and it will be assumed that the relative path specified is indeed the absolute path + /// and will be returned verbatim.

    + ///

    + /// Otherwise, relative path will be appended to the application path, while making sure that + /// path separators are not duplicated between the paths.

    + ///
    + /// Application path. + /// Relative path to combine with the application path. + /// Absolute path. + public static string CreateAbsolutePath(string applicationPath, string relativePath) + { + if (StringUtils.HasLength(relativePath)) + { + if (relativePath.StartsWith("/")) + { + return relativePath; + } + + if (relativePath.StartsWith("~/")) + { + relativePath = relativePath.Substring(2); + } + } + + applicationPath = (applicationPath == null) ? "" : applicationPath.TrimEnd('/'); + + return string.Format("{0}/{1}", applicationPath, relativePath); + } + + /// + /// Combines a rooted base path with a relative path. + /// + /// Must be a path starting with '/' + /// the path to be combined. May start with basepath Placeholder '~' + /// the combined path + /// + /// If relativePath starts with '~', rootPath is ignored and '~' resolves to the current AppDomain's application virtual path
    + /// If relativePath start with '/', rootPath is ignored and relativePath is returned as-is. + ///
    + public static string CombineVirtualPaths(string rootPath, string relativePath) + { + AssertUtils.ArgumentHasText(rootPath, "rootPath"); + if (rootPath[0] != '/') + { + throw new ArgumentException("RootPath must start with '/'", "rootPath"); + } + + string combinedPath = relativePath; + + if (combinedPath.StartsWith("~/")) + { + combinedPath = VirtualEnvironment.ApplicationVirtualPath.TrimEnd('/') + relativePath.Substring(1); + } + + if (!combinedPath.StartsWith("/")) + { + combinedPath = GetVirtualDirectory(rootPath).TrimEnd('/') + '/' + relativePath; + } + + // TODO: reduce contained directory upwalks here + + return combinedPath; + } + + /// + /// Gets the application-relative virtual path portion of the given absolute URL. + /// + /// the absolute url + /// the url relative to the current application's virtual path + public static string GetAppRelativePath(string url) + { + string appPath = VirtualEnvironment.ApplicationVirtualPath; + return GetRelativePath(appPath, url); + } + + /// + /// Gets the virtual path portion of the given absolute URL + /// relative to the given base path. + /// + /// + /// Base path comparison is done case insensitive. + /// + /// the absolute base path + /// the absolute url + /// the url relative to the given basePath + public static string GetRelativePath(string basePath, string url) + { + // strip application path from url + string appPath = basePath.TrimEnd('/'); + string appRelativeVirtualPath = url; + if (appRelativeVirtualPath.ToLower().StartsWith(appPath.ToLower())) + { + appRelativeVirtualPath = appRelativeVirtualPath.Substring(appPath.Length); + } + return appRelativeVirtualPath; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Process/IProcess.cs b/src/Spring/Spring.Web/Web/Process/IProcess.cs new file mode 100644 index 00000000..9c9617c5 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Process/IProcess.cs @@ -0,0 +1,52 @@ +using System; + +namespace Spring.Web.Process +{ + /// + /// An interface that different process implementations need to support. + /// + public interface IProcess + { + /// + /// Unique ID of this process instance. + /// + string Id { get; } + + /// + /// Controller for the component. + /// + /// + /// Process controller will be shared by all the views + /// that belong to this process. + /// + object Controller { get; set; } + + /// + /// Gets the name of the current view. + /// + string CurrentView { get; } + + /// + /// Gets the the flag that indicates if selected view + /// has changed during the current request. + /// + bool ViewChanged { get; } + + /// + /// Starts the process. + /// + /// Referrer URL. + void Start(string referrerUrl); + + /// + /// Resolves view for the specified view name. + /// + /// Name of the view to go to. + void SetView(string viewName); + + /// + /// Ends the process. + /// + void End(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Process/IProcessAware.cs b/src/Spring/Spring.Web/Web/Process/IProcessAware.cs new file mode 100644 index 00000000..d8076e2c --- /dev/null +++ b/src/Spring/Spring.Web/Web/Process/IProcessAware.cs @@ -0,0 +1,38 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Web; + +namespace Spring.Web.Process +{ + /// + /// Interface that should be implemented by all s + /// that want to be aware of the they belong to. + /// + /// Aleksandar Seovic + /// $Id: IProcessAware.cs,v 1.2 2006/05/18 21:37:52 markpollack Exp $ + public interface IProcessAware + { + /// + /// Gets or sets a process instance. + /// + IProcess Process { get; set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Process/ProcessManager.cs b/src/Spring/Spring.Web/Web/Process/ProcessManager.cs new file mode 100644 index 00000000..5b5ae354 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Process/ProcessManager.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; + +namespace Spring.Web.Process +{ + /// + /// Singleton that keeps track of all active process instances. + /// + /// Aleksandar Seovic + /// $Id: ProcessManager.cs,v 1.2 2006/05/18 21:37:52 markpollack Exp $ + public class ProcessManager + { + private static readonly ProcessManager instance = new ProcessManager(); + + private IDictionary processInstances = new Hashtable(); + + /// + /// Creates singleton instance. + /// + private ProcessManager() + {} + + /// + /// Registers process instance. + /// + /// Process instance to register. + public static void RegisterProcess(IProcess process) + { + instance.processInstances.Add(process.Id, process); + } + + /// + /// Returns process with the specified ID. + /// + /// Process ID to use for lookup. + /// Process with the specified ID, or null if process with that ID is not registered. + public static IProcess GetProcess(string id) + { + return (IProcess) instance.processInstances[id]; + } + + /// + /// Unregisters process with the specified ID. + /// + /// ID of the process to unregister. + public static void UnregisterProcess(string id) + { + instance.processInstances.Remove(id); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Providers/ConfigurableActiveDirectoryMembershipProvider.cs b/src/Spring/Spring.Web/Web/Providers/ConfigurableActiveDirectoryMembershipProvider.cs new file mode 100644 index 00000000..d24b7caa --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/ConfigurableActiveDirectoryMembershipProvider.cs @@ -0,0 +1,104 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections.Specialized; +using System.DirectoryServices; +using System.Security.Permissions; +using System.Web; +using System.Web.Security; + +#endregion + +namespace Spring.Web.Providers +{ +/* TBD + + /// + /// A spring configurable version of + /// + /// Erich Eichinger + /// $Id: ConfigurableActiveDirectoryMembershipProvider.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ + [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted = true)] + [DirectoryServicesPermission(SecurityAction.LinkDemand, Unrestricted = true)] + [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class ConfigurableActiveDirectoryMembershipProvider : ActiveDirectoryMembershipProvider, IMembershipProvider + { + private string connectionStringName; + private NameValueCollection parameters; + + /// + /// The ConnectionString to be used + /// + public string ConnectionStringName + { + get { return this.connectionStringName; } + set { this.connectionStringName = value; } + } + + /// + /// A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// + public NameValueCollection Parameters + { + get { return this.parameters; } + set { this.parameters = value; } + } + + /// + ///Initializes the provider. + /// + /// + /// + /// + /// A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// + /// Values may be overridden by specifying them in list. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + public override void Initialize(string name, NameValueCollection config) + { + lock (this) + { + if (this.parameters != null) + { + foreach (string key in this.parameters.Keys) + { + config[key] = this.parameters[key]; + } + } + config["connectionStringName"] = this.connectionStringName; + base.Initialize(name, config); + } + } + } +*/ +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Providers/ConfigurableSqlMembershipProvider.cs b/src/Spring/Spring.Web/Web/Providers/ConfigurableSqlMembershipProvider.cs new file mode 100644 index 00000000..97353d41 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/ConfigurableSqlMembershipProvider.cs @@ -0,0 +1,104 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections.Specialized; +using System.Security.Permissions; +using System.Web; +using System.Web.Security; + +#endregion + + +namespace Spring.Web.Providers +{ + /// + /// A spring configurable version of + /// + /// Erich Eichinger + /// $Id: ConfigurableSqlMembershipProvider.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ + [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class ConfigurableSqlMembershipProvider : SqlMembershipProvider, IMembershipProvider + { + private bool initialized = false; + private string connectionStringName; + private NameValueCollection parameters; + + /// + /// The ConnectionString to be used + /// + public string ConnectionStringName + { + get { return this.connectionStringName; } + set { this.connectionStringName = value; } + } + + /// + /// A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// + public NameValueCollection Parameters + { + get { return this.parameters; } + set { this.parameters = value; } + } + + /// + ///Initializes the provider. + /// + /// + /// + /// + /// A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// + /// Values may be overridden by specifying them in list. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + public override void Initialize(string name, NameValueCollection config) + { + lock(this) + { + if (initialized) return; + + if (parameters != null) + { + foreach(string key in parameters.Keys) + { + config[key] = parameters[key]; + } + } + config["connectionStringName"] = this.connectionStringName; + base.Initialize(name, config); + + initialized = true; + } + } + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Providers/ConfigurableSqlProfileProvider.cs b/src/Spring/Spring.Web/Web/Providers/ConfigurableSqlProfileProvider.cs new file mode 100644 index 00000000..b9907b2d --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/ConfigurableSqlProfileProvider.cs @@ -0,0 +1,105 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Collections.Specialized; +using System.Security.Permissions; +using System.Web; +using System.Web.Profile; +using System.Web.Security; + +#endregion + +namespace Spring.Web.Providers +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: ConfigurableSqlProfileProvider.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ + [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class ConfigurableSqlProfileProvider : SqlProfileProvider, IProfileProvider + { + private bool initialized = false; + private string connectionStringName; + private NameValueCollection parameters; + + /// + /// The ConnectionString to be used + /// + public string ConnectionStringName + { + get { return this.connectionStringName; } + set { this.connectionStringName = value; } + } + + /// + /// A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// + public NameValueCollection Parameters + { + get { return this.parameters; } + set { this.parameters = value; } + } + + /// + ///Initializes the provider. + /// + /// + /// + /// + /// A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// + /// Values may be overridden by specifying them in list. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + public override void Initialize(string name, NameValueCollection config) + { + lock (this) + { + if (initialized) return; + + if (parameters != null) + { + foreach (string key in parameters.Keys) + { + config[key] = parameters[key]; + } + } + config["connectionStringName"] = this.connectionStringName; + base.Initialize(name, config); + + initialized = true; + } + } + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Providers/ConfigurableSqlRoleProvider.cs b/src/Spring/Spring.Web/Web/Providers/ConfigurableSqlRoleProvider.cs new file mode 100644 index 00000000..c5d5c46a --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/ConfigurableSqlRoleProvider.cs @@ -0,0 +1,104 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Collections.Specialized; +using System.Security.Permissions; +using System.Web; +using System.Web.Security; + +#endregion + +namespace Spring.Web.Providers +{ + /// + /// A spring-configurable version of + /// + /// Erich Eichinger + /// $Id: ConfigurableSqlRoleProvider.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ + [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class ConfigurableSqlRoleProvider : SqlRoleProvider, IRoleProvider + { + private bool initialized = false; + private string connectionStringName; + private NameValueCollection parameters; + + /// + /// The ConnectionString to be used + /// + public string ConnectionStringName + { + get { return this.connectionStringName; } + set { this.connectionStringName = value; } + } + + /// + /// A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// + public NameValueCollection Parameters + { + get { return this.parameters; } + set { this.parameters = value; } + } + + /// + ///Initializes the provider. + /// + /// + /// + /// + /// A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// + /// Values may be overridden by specifying them in list. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + public override void Initialize(string name, NameValueCollection config) + { + lock (this) + { + if (initialized) return; + + if (parameters != null) + { + foreach (string key in parameters.Keys) + { + config[key] = parameters[key]; + } + } + config["connectionStringName"] = this.connectionStringName; + base.Initialize(name, config); + + initialized = true; + } + } + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Providers/ConfigurableXmlSiteMapProvider.cs b/src/Spring/Spring.Web/Web/Providers/ConfigurableXmlSiteMapProvider.cs new file mode 100644 index 00000000..3b0f4245 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/ConfigurableXmlSiteMapProvider.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections.Specialized; +using System.Security.Permissions; +using System.Web; +using System.Web.Security; + +#endregion + +namespace Spring.Web.Providers +{ + /// + /// A spring-configurable version of + /// + /// Erich Eichinger + /// $Id: ConfigurableXmlSiteMapProvider.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ + [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class ConfigurableXmlSiteMapProvider : XmlSiteMapProvider, ISiteMapProvider + { + private bool initialized; + private string siteMapFile; + private NameValueCollection parameters; + + /// + /// The XML file to be used for reading in the sitemap + /// + public string SiteMapFile + { + get { return this.siteMapFile; } + set { this.siteMapFile = value; } + } + + /// + /// A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// + public NameValueCollection Parameters + { + get { return this.parameters; } + set { this.parameters = value; } + } + + /// + ///Initializes the provider. + /// + /// + /// + /// + /// A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// + /// Values may be overridden by specifying them in list. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + public override void Initialize(string name, NameValueCollection config) + { + lock (this) + { + if (initialized) return; + + if (parameters != null) + { + foreach (string key in parameters.Keys) + { + config[key] = parameters[key]; + } + } + config["siteMapFile"] = this.siteMapFile; + base.Initialize(name, config); + + initialized = true; + } + } + } +} + +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Providers/IMembershipProvider.cs b/src/Spring/Spring.Web/Web/Providers/IMembershipProvider.cs new file mode 100644 index 00000000..24f387f9 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/IMembershipProvider.cs @@ -0,0 +1,361 @@ +#if NET_2_0 + +using System.Collections.Specialized; +using System.Web.Security; + +namespace Spring.Web.Providers +{ + /// + /// An Interface for class. + /// + /// Damjan Tomic + public interface IMembershipProvider + { + /// + ///Initializes the provider. + /// + /// + ///A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// The providerId attribute is mandatory. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + void Initialize(string name, NameValueCollection config); + + /// + ///Gets the friendly name used to refer to the provider during configuration. + /// + /// + /// + ///The friendly name used to refer to the provider during configuration. + /// + /// + string Name { get; } + + /// + ///Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + /// + /// + /// + ///A brief, friendly description suitable for display in administrative tools or other UIs. + /// + /// + string Description { get; } + + /// + ///Adds a new membership user to the data source. + /// + /// + /// + ///A object populated with the information for the newly created user. + /// + /// + ///Whether or not the new user is approved to be validated. + ///The password answer for the new user + ///The user name for the new user. + ///The unique identifier from the membership data source for the user. + ///The password for the new user. + ///The password question for the new user. + ///The e-mail address for the new user. + ///A enumeration value indicating whether the user was created successfully. + MembershipUser CreateUser(string username, string password, string email, + string passwordQuestion, string passwordAnswer, bool isApproved, + object providerUserKey, out MembershipCreateStatus status); + + /// + ///Processes a request to update the password question and answer for a membership user. + /// + /// + /// + ///true if the password question and answer are updated successfully; otherwise, false. + /// + /// + ///The new password question for the specified user. + ///The new password answer for the specified user. + ///The user to change the password question and answer for. + ///The password for the specified user. + bool ChangePasswordQuestionAndAnswer(string username, string password, + string newPasswordQuestion, string newPasswordAnswer); + + /// + ///Gets the password for the specified user name from the data source. + /// + /// + /// + ///The password for the specified user name. + /// + /// + ///The user to retrieve the password for. + ///The password answer for the user. + string GetPassword(string username, string answer); + + /// + ///Processes a request to update the password for a membership user. + /// + /// + /// + ///true if the password was updated successfully; otherwise, false. + /// + /// + ///The new password for the specified user. + ///The current password for the specified user. + ///The user to update the password for. + bool ChangePassword(string username, string oldPassword, string newPassword); + + /// + ///Resets a user's password to a new, automatically generated password. + /// + /// + /// + ///The new password for the specified user. + /// + /// + ///The user to reset the password for. + ///The password answer for the specified user. + string ResetPassword(string username, string answer); + + /// + ///Updates information about a user in the data source. + /// + /// + ///A object that represents the user to update and the updated information for the user. + void UpdateUser(MembershipUser user); + + /// + ///Verifies that the specified user name and password exist in the data source. + /// + /// + /// + ///true if the specified username and password are valid; otherwise, false. + /// + /// + ///The name of the user to validate. + ///The password for the specified user. + bool ValidateUser(string username, string password); + + /// + ///Clears a lock so that the membership user can be validated. + /// + /// + /// + ///true if the membership user was successfully unlocked; otherwise, false. + /// + /// + ///The membership user to clear the lock status for. + bool UnlockUser(string userName); + + /// + ///Gets information from the data source for a user based on the unique identifier for the membership user. Provides an option to update the last-activity date/time stamp for the user. + /// + /// + /// + ///A object populated with the specified user's information from the data source. + /// + /// + ///The unique identifier for the membership user to get information for. + ///true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. + MembershipUser GetUser(object providerUserKey, bool userIsOnline); + + /// + ///Gets information from the data source for a user. Provides an option to update the last-activity date/time stamp for the user. + /// + /// + /// + ///A object populated with the specified user's information from the data source. + /// + /// + ///The name of the user to get information for. + ///true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. + MembershipUser GetUser(string username, bool userIsOnline); + + /// + ///Gets the user name associated with the specified e-mail address. + /// + /// + /// + ///The user name associated with the specified e-mail address. If no match is found, return null. + /// + /// + ///The e-mail address to search for. + string GetUserNameByEmail(string email); + + /// + ///Removes a user from the membership data source. + /// + /// + /// + ///true if the user was successfully deleted; otherwise, false. + /// + /// + ///The name of the user to delete. + ///true to delete data related to the user from the database; false to leave data related to the user in the database. + bool DeleteUser(string username, bool deleteAllRelatedData); + + /// + ///Gets a collection of all the users in the data source in pages of data. + /// + /// + /// + ///A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + /// + /// + ///The total number of matched users. + ///The index of the page of results to return. pageIndex is zero-based. + ///The size of the page of results to return. + MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords); + + /// + ///Gets the number of users currently accessing the application. + /// + /// + /// + ///The number of users currently accessing the application. + /// + /// + int GetNumberOfUsersOnline(); + + /// + ///Gets a collection of membership users where the user name contains the specified user name to match. + /// + /// + /// + ///A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + /// + /// + ///The total number of matched users. + ///The index of the page of results to return. pageIndex is zero-based. + ///The user name to search for. + ///The size of the page of results to return. + MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, + out int totalRecords); + + /// + ///Gets a collection of membership users where the e-mail address contains the specified e-mail address to match. + /// + /// + /// + ///A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + /// + /// + ///The total number of matched users. + ///The index of the page of results to return. pageIndex is zero-based. + ///The e-mail address to search for. + ///The size of the page of results to return. + MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, + out int totalRecords); + + /// + ///Indicates whether the membership provider is configured to allow users to retrieve their passwords. + /// + /// + /// + ///true if the membership provider is configured to support password retrieval; otherwise, false. The default is false. + /// + /// + bool EnablePasswordRetrieval { get; } + + /// + ///Indicates whether the membership provider is configured to allow users to reset their passwords. + /// + /// + /// + ///true if the membership provider supports password reset; otherwise, false. The default is true. + /// + /// + bool EnablePasswordReset { get; } + + /// + ///Gets a value indicating whether the membership provider is configured to require the user to answer a password question for password reset and retrieval. + /// + /// + /// + ///true if a password answer is required for password reset and retrieval; otherwise, false. The default is true. + /// + /// + bool RequiresQuestionAndAnswer { get; } + + /// + ///The name of the application using the custom membership provider. + /// + /// + /// + ///The name of the application using the custom membership provider. + /// + /// + string ApplicationName { get; set; } + + /// + ///Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out. + /// + /// + /// + ///The number of invalid password or password-answer attempts allowed before the membership user is locked out. + /// + /// + int MaxInvalidPasswordAttempts { get; } + + /// + ///Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. + /// + /// + /// + ///The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. + /// + /// + int PasswordAttemptWindow { get; } + + /// + ///Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name. + /// + /// + /// + ///true if the membership provider requires a unique e-mail address; otherwise, false. The default is true. + /// + /// + bool RequiresUniqueEmail { get; } + + /// + ///Gets a value indicating the format for storing passwords in the membership data store. + /// + /// + /// + ///One of the values indicating the format for storing passwords in the data store. + /// + /// + MembershipPasswordFormat PasswordFormat { get; } + + /// + ///Gets the minimum length required for a password. + /// + /// + /// + ///The minimum length required for a password. + /// + /// + int MinRequiredPasswordLength { get; } + + /// + ///Gets the minimum number of special characters that must be present in a valid password. + /// + /// + /// + ///The minimum number of special characters that must be present in a valid password. + /// + /// + int MinRequiredNonAlphanumericCharacters { get; } + + /// + ///Gets the regular expression used to evaluate a password. + /// + /// + /// + ///A regular expression used to evaluate a password. + /// + /// + string PasswordStrengthRegularExpression { get; } + } +} +#endif diff --git a/src/Spring/Spring.Web/Web/Providers/IProfileProvider.cs b/src/Spring/Spring.Web/Web/Providers/IProfileProvider.cs new file mode 100644 index 00000000..93f7421d --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/IProfileProvider.cs @@ -0,0 +1,201 @@ +#if NET_2_0 + +using System; +using System.Collections.Specialized; +using System.Configuration; +using System.Web.Profile; + +namespace Spring.Web.Providers +{ + /// + /// An Interface for class. + /// + /// + ///

    + /// Configuration for this provider requires providerId element set in web.config file, + /// as the Id of wrapped provider (defined in the Spring context). + ///

    + ///
    + /// Damjan Tomic + public interface IProfileProvider + { + /// + ///Initializes the provider. + /// + /// + ///A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// The providerId attribute is mandatory. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + void Initialize(string name, NameValueCollection config); + + /// + ///Gets the friendly name used to refer to the provider during configuration. + /// + /// + /// + ///The friendly name used to refer to the provider during configuration. + /// + string Name { get; } + + /// + ///Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + /// + /// + /// + ///A brief, friendly description suitable for display in administrative tools or other UIs. + /// + string Description { get; } + + /// + ///Returns the collection of settings property values for the specified application instance and settings property group. + /// + /// + /// + ///A containing the values for the specified settings property group. + /// + /// + ///A describing the current application use. + ///A containing the settings property group whose values are to be retrieved.2 + SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, + SettingsPropertyCollection collection); + + /// + ///Sets the values of the specified group of property settings. + /// + /// + ///A describing the current application usage. + ///A representing the group of property settings to set.2 + void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection); + + /// + ///Gets or sets the name of the currently running application. + /// + /// + /// + ///A that contains the application's shortened name, which does not contain a full path or extension, for example, SimpleAppSettings. + /// + ///2 + string ApplicationName { get; set; } + + /// + ///When overridden in a derived class, deletes profile properties and information for the supplied list of profiles. + /// + /// + /// + ///The number of profiles deleted from the data source. + /// + /// + ///A of information about profiles that are to be deleted. + int DeleteProfiles(ProfileInfoCollection profiles); + + /// + ///When overridden in a derived class, deletes profile properties and information for profiles that match the supplied list of user names. + /// + /// + /// + ///The number of profiles deleted from the data source. + /// + /// + ///A string array of user names for profiles to be deleted. + int DeleteProfiles(string[] usernames); + + /// + ///When overridden in a derived class, deletes all user-profile data for profiles in which the last activity date occurred before the specified date. + /// + /// + /// + ///The number of profiles deleted from the data source. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are deleted. + ///A that identifies which user profiles are considered inactive. If the value of a user profile occurs on or before this date and time, the profile is considered inactive. + int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, + DateTime userInactiveSinceDate); + + /// + ///When overridden in a derived class, returns the number of profiles in which the last activity date occurred on or before the specified date. + /// + /// + /// + ///The number of profiles in which the last activity date occurred on or before the specified date. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + ///A that identifies which user profiles are considered inactive. If the of a user profile occurs on or before this date and time, the profile is considered inactive. + int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, + DateTime userInactiveSinceDate); + + /// + ///When overridden in a derived class, retrieves user profile data for all profiles in the data source. + /// + /// + /// + ///A containing user-profile information for all profiles in the data source. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + ///When this method returns, contains the total number of profiles. + ///The index of the page of results to return. + ///The size of the page of results to return. + ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, + int pageIndex, int pageSize, out int totalRecords); + + /// + ///When overridden in a derived class, retrieves user-profile data from the data source for profiles in which the last activity date occurred on or before the specified date. + /// + /// + /// + ///A containing user-profile information about the inactive profiles. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + ///A that identifies which user profiles are considered inactive. If the of a user profile occurs on or before this date and time, the profile is considered inactive. + ///When this method returns, contains the total number of profiles. + ///The index of the page of results to return. + ///The size of the page of results to return. + ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, + DateTime userInactiveSinceDate, int pageIndex, + int pageSize, out int totalRecords); + + /// + ///When overridden in a derived class, retrieves profile information for profiles in which the user name matches the specified user names. + /// + /// + /// + ///A containing user-profile information for profiles where the user name matches the supplied usernameToMatch parameter. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + ///When this method returns, contains the total number of profiles. + ///The index of the page of results to return. + ///The user name to search for. + ///The size of the page of results to return. + ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, + string usernameToMatch, int pageIndex, int pageSize, + out int totalRecords); + + /// + ///When overridden in a derived class, retrieves profile information for profiles in which the last activity date occurred on or before the specified date and the user name matches the specified user name. + /// + /// + /// + ///A containing user profile information for inactive profiles where the user name matches the supplied usernameToMatch parameter. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + ///A that identifies which user profiles are considered inactive. If the value of a user profile occurs on or before this date and time, the profile is considered inactive. + ///When this method returns, contains the total number of profiles. + ///The index of the page of results to return. + ///The user name to search for. + ///The size of the page of results to return. + ProfileInfoCollection FindInactiveProfilesByUserName( + ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, + int pageIndex, int pageSize, out int totalRecords); + } +} +#endif diff --git a/src/Spring/Spring.Web/Web/Providers/IRoleProvider.cs b/src/Spring/Spring.Web/Web/Providers/IRoleProvider.cs new file mode 100644 index 00000000..fce67c78 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/IRoleProvider.cs @@ -0,0 +1,188 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections.Specialized; + +#endregion + +namespace Spring.Web.Providers +{ + /// + /// An Interface for class. + /// + /// + ///

    + /// Configuration for this provider requires providerId element set in web.config file, + /// as the Id of wrapped provider (defined in the Spring context). + ///

    + ///
    + /// Damjan Tomic + public interface IRoleProvider + { + /// + ///Initializes the provider. + /// + /// + ///A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// The providerId attribute is mandatory. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + void Initialize(string name, NameValueCollection config); + + /// + ///Gets the friendly name used to refer to the provider during configuration. + /// + /// + /// + ///The friendly name used to refer to the provider during configuration. + /// + string Name { get; } + + /// + ///Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + /// + /// + /// + ///A brief, friendly description suitable for display in administrative tools or other UIs. + /// + string Description { get; } + + /// + ///Gets a value indicating whether the specified user is in the specified role for the configured applicationName. + /// + /// + /// + ///true if the specified user is in the specified role for the configured applicationName; otherwise, false. + /// + /// + ///The user name to search for. + ///The role to search in. + bool IsUserInRole(string username, string roleName); + + /// + ///Gets a list of the roles that a specified user is in for the configured applicationName. + /// + /// + /// + ///A string array containing the names of all the roles that the specified user is in for the configured applicationName. + /// + /// + ///The user to return a list of roles for. + string[] GetRolesForUser(string username); + + /// + ///Adds a new role to the data source for the configured applicationName. + /// + /// + ///The name of the role to create. + void CreateRole(string roleName); + + /// + ///Removes a role from the data source for the configured applicationName. + /// + /// + /// + ///true if the role was successfully deleted; otherwise, false. + /// + /// + ///If true, throw an exception if roleName has one or more members and do not delete roleName. + ///The name of the role to delete. + bool DeleteRole(string roleName, bool throwOnPopulatedRole); + + /// + ///Gets a value indicating whether the specified role name already exists in the role data source for the configured applicationName. + /// + /// + /// + ///true if the role name already exists in the data source for the configured applicationName; otherwise, false. + /// + /// + ///The name of the role to search for in the data source. + bool RoleExists(string roleName); + + /// + ///Adds the specified user names to the specified roles for the configured applicationName. + /// + /// + ///A string array of the role names to add the specified user names to. + ///A string array of user names to be added to the specified roles. + void AddUsersToRoles(string[] usernames, string[] roleNames); + + /// + ///Removes the specified user names from the specified roles for the configured applicationName. + /// + /// + ///A string array of role names to remove the specified user names from. + ///A string array of user names to be removed from the specified roles. + void RemoveUsersFromRoles(string[] usernames, string[] roleNames); + + /// + ///Gets a list of users in the specified role for the configured applicationName. + /// + /// + /// + ///A string array containing the names of all the users who are members of the specified role for the configured applicationName. + /// + /// + ///The name of the role to get the list of users for. + string[] GetUsersInRole(string roleName); + + /// + ///Gets a list of all the roles for the configured applicationName. + /// + /// + /// + ///A string array containing the names of all the roles stored in the data source for the configured applicationName. + /// + /// + string[] GetAllRoles(); + + /// + ///Gets an array of user names in a role where the user name contains the specified user name to match. + /// + /// + /// + ///A string array containing the names of all the users where the user name matches usernameToMatch and the user is a member of the specified role. + /// + /// + ///The user name to search for. + ///The role to search in. + string[] FindUsersInRole(string roleName, string usernameToMatch); + + /// + ///Gets or sets the name of the application to store and retrieve role information for. + /// + /// + /// + ///The name of the application to store and retrieve role information for. + /// + /// + string ApplicationName { get; set; } + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Providers/ISiteMapProvider.cs b/src/Spring/Spring.Web/Web/Providers/ISiteMapProvider.cs new file mode 100644 index 00000000..bafc8ce3 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/ISiteMapProvider.cs @@ -0,0 +1,265 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System.Collections.Specialized; +using System.Web; + +#endregion + +namespace Spring.Web.Providers +{ + /// + /// An Interface for class. + /// + /// + ///

    + /// Configuration for this provider requires providerId element set in web.config file, + /// as the Id of wrapped provider (defined in the Spring context). + ///

    + ///
    + /// Damjan Tomic + public interface ISiteMapProvider + { + /// + ///Initializes the provider. + /// + /// + ///A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// The providerId attribute is mandatory. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + void Initialize(string name, NameValueCollection config); + + /// + ///Gets the friendly name used to refer to the provider during configuration. + /// + /// + /// + ///The friendly name used to refer to the provider during configuration. + /// + /// + string Name { get; } + + /// + ///Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + /// + /// + /// + ///A brief, friendly description suitable for display in administrative tools or other UIs. + /// + /// + string Description { get; } + + /// + ///Retrieves a object that represents the currently requested page using the specified object. + /// + /// + /// + ///A that represents the currently requested page; otherwise, null, if no corresponding can be found in the or if the page context is null. + /// + /// + ///The used to match node information with the URL of the requested page. + SiteMapNode FindSiteMapNode(HttpContext context); + + /// + ///Retrieves a object based on a specified key. + /// + /// + /// + ///A that represents the page identified by key; otherwise, null, if no corresponding is found or if security trimming is enabled and the cannot be returned for the current user. The default is null. + /// + /// + ///A lookup key with which a is created. + SiteMapNode FindSiteMapNodeFromKey(string key); + + /// + ///When overridden in a derived class, retrieves a object that represents the page at the specified URL. + /// + /// + /// + ///A that represents the page identified by rawURL; otherwise, null, if no corresponding is found or if security trimming is enabled and the cannot be returned for the current user. + /// + /// + ///A URL that identifies the page for which to retrieve a . + SiteMapNode FindSiteMapNode(string rawUrl); + + /// + ///When overridden in a derived class, retrieves the child nodes of a specific . + /// + /// + /// + ///A read-only that contains the immediate child nodes of the specified ; otherwise, null or an empty collection, if no child nodes exist. + /// + /// + ///The for which to retrieve all child nodes. + SiteMapNodeCollection GetChildNodes(SiteMapNode node); + + /// + ///Provides an optimized lookup method for site map providers when retrieving the node for the currently requested page and fetching the parent and ancestor site map nodes for the current page. + /// + /// + /// + ///A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + /// + /// + ///The number of ancestor site map node generations to get. A value of -1 indicates that all ancestors might be retrieved and cached by the provider. + ///upLevel is less than -1. + SiteMapNode GetCurrentNodeAndHintAncestorNodes(int upLevel); + + /// + ///Provides an optimized lookup method for site map providers when retrieving the node for the currently requested page and fetching the site map nodes in the proximity of the current node. + /// + /// + /// + ///A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + /// + /// + ///The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors might be retrieved and cached by the provider. + ///The number of child generations to fetch. 0 indicates no descendant nodes are retrieved and a -1 indicates that all descendant nodes might be retrieved and cached by the provider. + ///upLevel or downLevel is less than -1. + SiteMapNode GetCurrentNodeAndHintNeighborhoodNodes(int upLevel, int downLevel); + + /// + ///When overridden in a derived class, retrieves the parent node of a specific object. + /// + /// + /// + ///A that represents the parent of node; otherwise, null, if the has no parent or security trimming is enabled and the parent node is not accessible to the current user. + /// + /// + ///The for which to retrieve the parent node. + SiteMapNode GetParentNode(SiteMapNode node); + + /// + ///Provides an optimized lookup method for site map providers when retrieving an ancestor node for the currently requested page and fetching the descendant nodes for the ancestor. + /// + /// + /// + ///A that represents an ancestor of the currently requested page; otherwise, null, if the current or ancestor is not found or cannot be returned for the current user. + /// + /// + ///The number of descendant node levels to retrieve from the target ancestor node. + ///The number of ancestor node levels to traverse when retrieving the requested ancestor node. + ///walkupLevels or relativeDepthFromWalkup is less than 0. + SiteMapNode GetParentNodeRelativeToCurrentNodeAndHintDownFromParent(int walkupLevels, + int relativeDepthFromWalkup); + + /// + ///Provides an optimized lookup method for site map providers when retrieving an ancestor node for the specified object and fetching its child nodes. + /// + /// + /// + ///A that represents an ancestor of node; otherwise, null, if the current or ancestor is not found or cannot be returned for the current user. + /// + /// + ///The number of descendant node levels to retrieve from the target ancestor node. + ///The that acts as a reference point for walkupLevels and relativeDepthFromWalkup. + ///The number of ancestor node levels to traverse when retrieving the requested ancestor node. + ///The value specified for walkupLevels or relativeDepthFromWalkup is less than 0. + ///node is null. + SiteMapNode GetParentNodeRelativeToNodeAndHintDownFromParent(SiteMapNode node, int walkupLevels, + int relativeDepthFromWalkup); + + /// + ///Provides a method that site map providers can override to perform an optimized retrieval of one or more levels of parent and ancestor nodes, relative to the specified object. + /// + /// + ///The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors might be retrieved and cached. + ///The that acts as a reference point for upLevel. + ///upLevel is less than -1. + ///node is null. + void HintAncestorNodes(SiteMapNode node, int upLevel); + + /// + ///Provides a method that site map providers can override to perform an optimized retrieval of nodes found in the proximity of the specified node. + /// + /// + ///The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors (and their descendant nodes to the level of node) might be retrieved and cached. + ///The number of descendant generations to fetch. 0 indicates no descendant nodes are retrieved and -1 indicates that all descendant nodes might be retrieved and cached. + ///The that acts as a reference point for upLevel. + ///upLevel or downLevel is less than -1. + ///node is null. + void HintNeighborhoodNodes(SiteMapNode node, int upLevel, int downLevel); + + /// + ///Retrieves a Boolean value indicating whether the specified object can be viewed by the user in the specified context. + /// + /// + /// + ///true if security trimming is enabled and node can be viewed by the user or security trimming is not enabled; otherwise, false. + /// + /// + ///The that contains user information. + ///The that is requested by the user. + ///context is null.- or -node is null. + bool IsAccessibleToUser(HttpContext context, SiteMapNode node); + + /// + ///Gets the object that represents the currently requested page. + /// + /// + /// + ///A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + /// + /// + SiteMapNode CurrentNode { get; } + + /// + ///Gets or sets the parent object of the current provider. + /// + /// + /// + ///The parent provider of the current . + /// + /// + System.Web.SiteMapProvider ParentProvider { get; set; } + + /// + ///Gets the root object in the current provider hierarchy. + /// + /// + /// + ///An that is the top-level site map provider in the provider hierarchy that the current provider belongs to. + /// + /// + ///There is a circular reference to the current site map provider. + System.Web.SiteMapProvider RootProvider { get; } + + /// + ///Gets the root object of the site map data that the current provider represents. + /// + /// + /// + ///The root of the current site map data provider. The default implementation performs security trimming on the returned node. + /// + /// + SiteMapNode RootNode { get; } + } +} + +#endif diff --git a/src/Spring/Spring.Web/Web/Providers/MembershipProviderAdapter.cs b/src/Spring/Spring.Web/Web/Providers/MembershipProviderAdapter.cs new file mode 100644 index 00000000..e09be99b --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/MembershipProviderAdapter.cs @@ -0,0 +1,518 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Collections.Specialized; +using System.Security.Permissions; +using System.Web; +using System.Web.Security; +using Spring.Context.Support; + +#endregion + +namespace Spring.Web.Providers +{ + /// + /// Wrapper for class. + /// + /// + ///

    + /// Configuration for this provider requires providerId element set in web.config file, + /// as the Id of wrapped provider (defined in the Spring context). + ///

    + ///
    + /// Damjan Tomic + /// $Id: MembershipProviderAdapter.cs,v 1.1 2007/07/26 14:43:56 oakinger Exp $ + [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class MembershipProviderAdapter : MembershipProvider, IMembershipProvider + { + #region Field + + /// + /// Reference to wrapped provider (defined in Spring context). + /// + private MembershipProvider wrappedProvider; + + #endregion + + #region ProviderBase members + + /// + ///Initializes the provider. + /// + /// + ///A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// The providerId attribute may be used to override the name being used for looking up an object definition. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + public override void Initialize(string name, NameValueCollection config) + { + lock (this) + { + if (config == null) throw new ArgumentNullException("config"); + + string providerId = config["providerId"]; + if (String.IsNullOrEmpty(providerId)) + providerId = name; + config.Remove("providerId"); + + this.wrappedProvider = (MembershipProvider) WebApplicationContext.GetRootContext().GetObject(providerId); + this.wrappedProvider.Initialize(name, config); + } + } + + /// + ///Gets the friendly name used to refer to the provider during configuration. + /// + /// + /// + ///The friendly name used to refer to the provider during configuration. + /// + /// + public override string Name + { + get { return this.wrappedProvider.Name; } + } + + /// + ///Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + /// + /// + /// + ///A brief, friendly description suitable for display in administrative tools or other UIs. + /// + /// + public override string Description + { + get { return this.wrappedProvider.Description; } + } + + #endregion + + #region System.Web.Security.MembershipProvider members + + /// + ///Adds a new membership user to the data source. + /// + /// + /// + ///A object populated with the information for the newly created user. + /// + /// + ///Whether or not the new user is approved to be validated. + ///The password answer for the new user + ///The user name for the new user. + ///The unique identifier from the membership data source for the user. + ///The password for the new user. + ///The password question for the new user. + ///The e-mail address for the new user. + ///A enumeration value indicating whether the user was created successfully. + public override MembershipUser CreateUser(string username, string password, string email, + string passwordQuestion, string passwordAnswer, bool isApproved, + object providerUserKey, out MembershipCreateStatus status) + { + return this.wrappedProvider.CreateUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, + out status); + } + + /// + ///Processes a request to update the password question and answer for a membership user. + /// + /// + /// + ///true if the password question and answer are updated successfully; otherwise, false. + /// + /// + ///The new password question for the specified user. + ///The new password answer for the specified user. + ///The user to change the password question and answer for. + ///The password for the specified user. + public override bool ChangePasswordQuestionAndAnswer(string username, string password, + string newPasswordQuestion, string newPasswordAnswer) + { + return this.wrappedProvider.ChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer); + } + + /// + ///Gets the password for the specified user name from the data source. + /// + /// + /// + ///The password for the specified user name. + /// + /// + ///The user to retrieve the password for. + ///The password answer for the user. + public override string GetPassword(string username, string answer) + { + return this.wrappedProvider.GetPassword(username, answer); + } + + /// + ///Processes a request to update the password for a membership user. + /// + /// + /// + ///true if the password was updated successfully; otherwise, false. + /// + /// + ///The new password for the specified user. + ///The current password for the specified user. + ///The user to update the password for. + public override bool ChangePassword(string username, string oldPassword, string newPassword) + { + return this.wrappedProvider.ChangePassword(username, oldPassword, newPassword); + } + + /// + ///Resets a user's password to a new, automatically generated password. + /// + /// + /// + ///The new password for the specified user. + /// + /// + ///The user to reset the password for. + ///The password answer for the specified user. + public override string ResetPassword(string username, string answer) + { + return this.wrappedProvider.ResetPassword(username, answer); + } + + /// + ///Updates information about a user in the data source. + /// + /// + ///A object that represents the user to update and the updated information for the user. + public override void UpdateUser(MembershipUser user) + { + this.wrappedProvider.UpdateUser(user); + } + + /// + ///Verifies that the specified user name and password exist in the data source. + /// + /// + /// + ///true if the specified username and password are valid; otherwise, false. + /// + /// + ///The name of the user to validate. + ///The password for the specified user. + public override bool ValidateUser(string username, string password) + { + return this.wrappedProvider.ValidateUser(username, password); + } + + /// + ///Clears a lock so that the membership user can be validated. + /// + /// + /// + ///true if the membership user was successfully unlocked; otherwise, false. + /// + /// + ///The membership user to clear the lock status for. + public override bool UnlockUser(string userName) + { + return this.wrappedProvider.UnlockUser(userName); + } + + /// + ///Gets information from the data source for a user based on the unique identifier for the membership user. Provides an option to update the last-activity date/time stamp for the user. + /// + /// + /// + ///A object populated with the specified user's information from the data source. + /// + /// + ///The unique identifier for the membership user to get information for. + ///true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. + public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) + { + return this.wrappedProvider.GetUser(providerUserKey, userIsOnline); + } + + /// + ///Gets information from the data source for a user. Provides an option to update the last-activity date/time stamp for the user. + /// + /// + /// + ///A object populated with the specified user's information from the data source. + /// + /// + ///The name of the user to get information for. + ///true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. + public override MembershipUser GetUser(string username, bool userIsOnline) + { + return this.wrappedProvider.GetUser(username, userIsOnline); + } + + /// + ///Gets the user name associated with the specified e-mail address. + /// + /// + /// + ///The user name associated with the specified e-mail address. If no match is found, return null. + /// + /// + ///The e-mail address to search for. + public override string GetUserNameByEmail(string email) + { + return this.wrappedProvider.GetUserNameByEmail(email); + } + + /// + ///Removes a user from the membership data source. + /// + /// + /// + ///true if the user was successfully deleted; otherwise, false. + /// + /// + ///The name of the user to delete. + ///true to delete data related to the user from the database; false to leave data related to the user in the database. + public override bool DeleteUser(string username, bool deleteAllRelatedData) + { + return this.wrappedProvider.DeleteUser(username, deleteAllRelatedData); + } + + /// + ///Gets a collection of all the users in the data source in pages of data. + /// + /// + /// + ///A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + /// + /// + ///The total number of matched users. + ///The index of the page of results to return. pageIndex is zero-based. + ///The size of the page of results to return. + public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) + { + return this.wrappedProvider.GetAllUsers(pageIndex, pageSize, out totalRecords); + } + + /// + ///Gets the number of users currently accessing the application. + /// + /// + /// + ///The number of users currently accessing the application. + /// + /// + public override int GetNumberOfUsersOnline() + { + return this.wrappedProvider.GetNumberOfUsersOnline(); + } + + /// + ///Gets a collection of membership users where the user name contains the specified user name to match. + /// + /// + /// + ///A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + /// + /// + ///The total number of matched users. + ///The index of the page of results to return. pageIndex is zero-based. + ///The user name to search for. + ///The size of the page of results to return. + public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, + out int totalRecords) + { + return this.wrappedProvider.FindUsersByName(usernameToMatch, pageIndex, pageSize, out totalRecords); + } + + /// + ///Gets a collection of membership users where the e-mail address contains the specified e-mail address to match. + /// + /// + /// + ///A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + /// + /// + ///The total number of matched users. + ///The index of the page of results to return. pageIndex is zero-based. + ///The e-mail address to search for. + ///The size of the page of results to return. + public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, + out int totalRecords) + { + return this.wrappedProvider.FindUsersByEmail(emailToMatch, pageIndex, pageSize, out totalRecords); + } + + /// + ///Indicates whether the membership provider is configured to allow users to retrieve their passwords. + /// + /// + /// + ///true if the membership provider is configured to support password retrieval; otherwise, false. The default is false. + /// + /// + public override bool EnablePasswordRetrieval + { + get { return this.wrappedProvider.EnablePasswordRetrieval; } + } + + /// + ///Indicates whether the membership provider is configured to allow users to reset their passwords. + /// + /// + /// + ///true if the membership provider supports password reset; otherwise, false. The default is true. + /// + /// + public override bool EnablePasswordReset + { + get { return this.wrappedProvider.EnablePasswordReset; } + } + + /// + ///Gets a value indicating whether the membership provider is configured to require the user to answer a password question for password reset and retrieval. + /// + /// + /// + ///true if a password answer is required for password reset and retrieval; otherwise, false. The default is true. + /// + /// + public override bool RequiresQuestionAndAnswer + { + get { return this.wrappedProvider.RequiresQuestionAndAnswer; } + } + + /// + ///The name of the application using the custom membership provider. + /// + /// + /// + ///The name of the application using the custom membership provider. + /// + /// + public override string ApplicationName + { + get { return this.wrappedProvider.ApplicationName; } + set { this.wrappedProvider.ApplicationName = value; } + } + + /// + ///Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out. + /// + /// + /// + ///The number of invalid password or password-answer attempts allowed before the membership user is locked out. + /// + /// + public override int MaxInvalidPasswordAttempts + { + get { return this.wrappedProvider.MaxInvalidPasswordAttempts; } + } + + /// + ///Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. + /// + /// + /// + ///The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. + /// + /// + public override int PasswordAttemptWindow + { + get { return this.wrappedProvider.PasswordAttemptWindow; } + } + + /// + ///Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name. + /// + /// + /// + ///true if the membership provider requires a unique e-mail address; otherwise, false. The default is true. + /// + /// + public override bool RequiresUniqueEmail + { + get { return this.wrappedProvider.RequiresUniqueEmail; } + } + + /// + ///Gets a value indicating the format for storing passwords in the membership data store. + /// + /// + /// + ///One of the values indicating the format for storing passwords in the data store. + /// + /// + public override MembershipPasswordFormat PasswordFormat + { + get { return this.wrappedProvider.PasswordFormat; } + } + + /// + ///Gets the minimum length required for a password. + /// + /// + /// + ///The minimum length required for a password. + /// + /// + public override int MinRequiredPasswordLength + { + get { return this.wrappedProvider.MinRequiredPasswordLength; } + } + + /// + ///Gets the minimum number of special characters that must be present in a valid password. + /// + /// + /// + ///The minimum number of special characters that must be present in a valid password. + /// + /// + public override int MinRequiredNonAlphanumericCharacters + { + get { return this.wrappedProvider.MinRequiredNonAlphanumericCharacters; } + } + + /// + ///Gets the regular expression used to evaluate a password. + /// + /// + /// + ///A regular expression used to evaluate a password. + /// + /// + public override string PasswordStrengthRegularExpression + { + get { return this.wrappedProvider.PasswordStrengthRegularExpression; } + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Providers/ProfileProviderAdapter.cs b/src/Spring/Spring.Web/Web/Providers/ProfileProviderAdapter.cs new file mode 100644 index 00000000..7ed7ec2a --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/ProfileProviderAdapter.cs @@ -0,0 +1,284 @@ +#if NET_2_0 + +using System; +using System.Collections.Specialized; +using System.Configuration; +using System.Security.Permissions; +using System.Web; +using System.Web.Profile; +using Spring.Context.Support; + +namespace Spring.Web.Providers +{ + /// + /// Wrapper for class. + /// + /// + ///

    + /// Configuration for this provider requires providerId element set in web.config file, + /// as the Id of wrapped provider (defined in the Spring context). + ///

    + ///
    + /// Damjan Tomic + [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class ProfileProviderAdapter : ProfileProvider, IProfileProvider + { + #region Field + + /// + /// Reference to wrapped provider (defined in Spring context). + /// + private ProfileProvider wrappedProvider; + + #endregion + + #region ProviderBase members + + /// + ///Initializes the provider. + /// + /// + ///A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// The providerId attribute may be used to override the name being used for looking up an object definition. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + public override void Initialize(string name, NameValueCollection config) + { + lock (this) + { + if (config == null) throw new ArgumentNullException("config"); + + string providerId = config["providerId"]; + if (String.IsNullOrEmpty(providerId)) + providerId = name; + config.Remove("providerId"); + + this.wrappedProvider = (ProfileProvider)WebApplicationContext.GetRootContext().GetObject(providerId); + this.wrappedProvider.Initialize(name, config); + } + } + + /// + ///Gets the friendly name used to refer to the provider during configuration. + /// + /// + /// + ///The friendly name used to refer to the provider during configuration. + /// + public override string Name + { + get { return this.wrappedProvider.Name; } + } + + /// + ///Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + /// + /// + /// + ///A brief, friendly description suitable for display in administrative tools or other UIs. + /// + public override string Description + { + get { return this.wrappedProvider.Description; } + } + + #endregion + + #region System.Web.Profile.ProfileProvider members + + /// + ///Returns the collection of settings property values for the specified application instance and settings property group. + /// + /// + /// + ///A containing the values for the specified settings property group. + /// + /// + ///A describing the current application use. + ///A containing the settings property group whose values are to be retrieved.2 + public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, + SettingsPropertyCollection collection) + { + return this.wrappedProvider.GetPropertyValues(context, collection); + } + + /// + ///Sets the values of the specified group of property settings. + /// + /// + ///A describing the current application usage. + ///A representing the group of property settings to set.2 + public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection) + { + this.wrappedProvider.SetPropertyValues(context,collection); + } + + /// + ///Gets or sets the name of the currently running application. + /// + /// + /// + ///A that contains the application's shortened name, which does not contain a full path or extension, for example, SimpleAppSettings. + /// + ///2 + public override string ApplicationName + { + get { return this.wrappedProvider.ApplicationName; } + set { this.wrappedProvider.ApplicationName = value; } + } + + /// + ///When overridden in a derived class, deletes profile properties and information for the supplied list of profiles. + /// + /// + /// + ///The number of profiles deleted from the data source. + /// + /// + ///A of information about profiles that are to be deleted. + public override int DeleteProfiles(ProfileInfoCollection profiles) + { + return this.wrappedProvider.DeleteProfiles(profiles); + } + + /// + ///When overridden in a derived class, deletes profile properties and information for profiles that match the supplied list of user names. + /// + /// + /// + ///The number of profiles deleted from the data source. + /// + /// + ///A string array of user names for profiles to be deleted. + public override int DeleteProfiles(string[] usernames) + { + return this.wrappedProvider.DeleteProfiles(usernames); + } + + /// + ///When overridden in a derived class, deletes all user-profile data for profiles in which the last activity date occurred before the specified date. + /// + /// + /// + ///The number of profiles deleted from the data source. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are deleted. + ///A that identifies which user profiles are considered inactive. If the value of a user profile occurs on or before this date and time, the profile is considered inactive. + public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, + DateTime userInactiveSinceDate) + { + return this.wrappedProvider.DeleteInactiveProfiles(authenticationOption, userInactiveSinceDate); + } + + /// + ///When overridden in a derived class, returns the number of profiles in which the last activity date occurred on or before the specified date. + /// + /// + /// + ///The number of profiles in which the last activity date occurred on or before the specified date. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + ///A that identifies which user profiles are considered inactive. If the of a user profile occurs on or before this date and time, the profile is considered inactive. + public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, + DateTime userInactiveSinceDate) + { + return this.wrappedProvider.GetNumberOfInactiveProfiles(authenticationOption, userInactiveSinceDate); + } + + /// + ///When overridden in a derived class, retrieves user profile data for all profiles in the data source. + /// + /// + /// + ///A containing user-profile information for all profiles in the data source. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + ///When this method returns, contains the total number of profiles. + ///The index of the page of results to return. + ///The size of the page of results to return. + public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, + int pageIndex, int pageSize, out int totalRecords) + { + return this.wrappedProvider.GetAllProfiles(authenticationOption, pageIndex, pageSize, out totalRecords); + } + + /// + ///When overridden in a derived class, retrieves user-profile data from the data source for profiles in which the last activity date occurred on or before the specified date. + /// + /// + /// + ///A containing user-profile information about the inactive profiles. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + ///A that identifies which user profiles are considered inactive. If the of a user profile occurs on or before this date and time, the profile is considered inactive. + ///When this method returns, contains the total number of profiles. + ///The index of the page of results to return. + ///The size of the page of results to return. + public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, + DateTime userInactiveSinceDate, int pageIndex, + int pageSize, out int totalRecords) + { + return + this.wrappedProvider.GetAllInactiveProfiles(authenticationOption, userInactiveSinceDate, pageIndex, pageSize, + out totalRecords); + } + + /// + ///When overridden in a derived class, retrieves profile information for profiles in which the user name matches the specified user names. + /// + /// + /// + ///A containing user-profile information for profiles where the user name matches the supplied usernameToMatch parameter. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + ///When this method returns, contains the total number of profiles. + ///The index of the page of results to return. + ///The user name to search for. + ///The size of the page of results to return. + public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, + string usernameToMatch, int pageIndex, int pageSize, + out int totalRecords) + { + return + this.wrappedProvider.FindProfilesByUserName(authenticationOption, usernameToMatch, pageIndex, pageSize, + out totalRecords); + } + + /// + ///When overridden in a derived class, retrieves profile information for profiles in which the last activity date occurred on or before the specified date and the user name matches the specified user name. + /// + /// + /// + ///A containing user profile information for inactive profiles where the user name matches the supplied usernameToMatch parameter. + /// + /// + ///One of the values, specifying whether anonymous, authenticated, or both types of profiles are returned. + ///A that identifies which user profiles are considered inactive. If the value of a user profile occurs on or before this date and time, the profile is considered inactive. + ///When this method returns, contains the total number of profiles. + ///The index of the page of results to return. + ///The user name to search for. + ///The size of the page of results to return. + public override ProfileInfoCollection FindInactiveProfilesByUserName( + ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, + int pageIndex, int pageSize, out int totalRecords) + { + return this.wrappedProvider.FindInactiveProfilesByUserName(authenticationOption, usernameToMatch, userInactiveSinceDate, + pageIndex, pageSize, out totalRecords); + } + + + #endregion + + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Providers/RoleProviderAdapter.cs b/src/Spring/Spring.Web/Web/Providers/RoleProviderAdapter.cs new file mode 100644 index 00000000..803394d0 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/RoleProviderAdapter.cs @@ -0,0 +1,269 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Collections.Specialized; +using System.Security.Permissions; +using System.Web; +using System.Web.Security; +using Spring.Context.Support; + +#endregion + + +namespace Spring.Web.Providers +{ + /// + /// Wrapper for class. + /// + /// + ///

    + /// Configuration for this provider requires providerId element set in web.config file, + /// as the Id of wrapped provider (defined in the Spring context). + ///

    + ///
    + /// Damjan Tomic + [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class RoleProviderAdapter : RoleProvider, IRoleProvider + { + #region Field + + /// + /// Reference to wrapped provider (defined in Spring context). + /// + private RoleProvider wrappedProvider; + + #endregion + + #region ProviderBase members + + /// + ///Initializes the provider. + /// + /// + ///A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// The providerId attribute may be used to override the name being used for looking up an object definition. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + public override void Initialize(string name, NameValueCollection config) + { + lock (this) + { + if (config == null) throw new ArgumentNullException("config"); + + string providerId = config["providerId"]; + if (String.IsNullOrEmpty(providerId)) + providerId = name; + config.Remove("providerId"); + + this.wrappedProvider = (RoleProvider)WebApplicationContext.GetRootContext().GetObject(providerId); + this.wrappedProvider.Initialize(name,config); + } + } + + /// + ///Gets the friendly name used to refer to the provider during configuration. + /// + /// + /// + ///The friendly name used to refer to the provider during configuration. + /// + public override string Name + { + get { return this.wrappedProvider.Name; } + } + + /// + ///Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + /// + /// + /// + ///A brief, friendly description suitable for display in administrative tools or other UIs. + /// + public override string Description + { + get { return this.wrappedProvider.Description; } + } + + #endregion + + #region System.Web.Security.RoleProvider members + + + /// + ///Gets a value indicating whether the specified user is in the specified role for the configured applicationName. + /// + /// + /// + ///true if the specified user is in the specified role for the configured applicationName; otherwise, false. + /// + /// + ///The user name to search for. + ///The role to search in. + public override bool IsUserInRole(string username, string roleName) + { + return this.wrappedProvider.IsUserInRole(username, roleName); + } + + /// + ///Gets a list of the roles that a specified user is in for the configured applicationName. + /// + /// + /// + ///A string array containing the names of all the roles that the specified user is in for the configured applicationName. + /// + /// + ///The user to return a list of roles for. + public override string[] GetRolesForUser(string username) + { + return this.wrappedProvider.GetRolesForUser(username); + } + + /// + ///Adds a new role to the data source for the configured applicationName. + /// + /// + ///The name of the role to create. + public override void CreateRole(string roleName) + { + this.wrappedProvider.CreateRole(roleName); + } + + /// + ///Removes a role from the data source for the configured applicationName. + /// + /// + /// + ///true if the role was successfully deleted; otherwise, false. + /// + /// + ///If true, throw an exception if roleName has one or more members and do not delete roleName. + ///The name of the role to delete. + public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) + { + return this.wrappedProvider.DeleteRole(roleName, throwOnPopulatedRole); + } + + /// + ///Gets a value indicating whether the specified role name already exists in the role data source for the configured applicationName. + /// + /// + /// + ///true if the role name already exists in the data source for the configured applicationName; otherwise, false. + /// + /// + ///The name of the role to search for in the data source. + public override bool RoleExists(string roleName) + { + return this.wrappedProvider.RoleExists(roleName); + } + + /// + ///Adds the specified user names to the specified roles for the configured applicationName. + /// + /// + ///A string array of the role names to add the specified user names to. + ///A string array of user names to be added to the specified roles. + public override void AddUsersToRoles(string[] usernames, string[] roleNames) + { + this.wrappedProvider.AddUsersToRoles(usernames,roleNames); + } + + /// + ///Removes the specified user names from the specified roles for the configured applicationName. + /// + /// + ///A string array of role names to remove the specified user names from. + ///A string array of user names to be removed from the specified roles. + public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) + { + this.wrappedProvider.RemoveUsersFromRoles(usernames,roleNames); + } + + /// + ///Gets a list of users in the specified role for the configured applicationName. + /// + /// + /// + ///A string array containing the names of all the users who are members of the specified role for the configured applicationName. + /// + /// + ///The name of the role to get the list of users for. + public override string[] GetUsersInRole(string roleName) + { + return this.wrappedProvider.GetUsersInRole(roleName); + } + + /// + ///Gets a list of all the roles for the configured applicationName. + /// + /// + /// + ///A string array containing the names of all the roles stored in the data source for the configured applicationName. + /// + /// + public override string[] GetAllRoles() + { + return this.wrappedProvider.GetAllRoles(); + } + + /// + ///Gets an array of user names in a role where the user name contains the specified user name to match. + /// + /// + /// + ///A string array containing the names of all the users where the user name matches usernameToMatch and the user is a member of the specified role. + /// + /// + ///The user name to search for. + ///The role to search in. + public override string[] FindUsersInRole(string roleName, string usernameToMatch) + { + return this.wrappedProvider.FindUsersInRole(roleName, usernameToMatch); + } + + /// + ///Gets or sets the name of the application to store and retrieve role information for. + /// + /// + /// + ///The name of the application to store and retrieve role information for. + /// + /// + public override string ApplicationName + { + get { return this.wrappedProvider.ApplicationName; } + set { this.wrappedProvider.ApplicationName = value; } + } + + #endregion + + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Providers/SiteMapProviderAdapter.cs b/src/Spring/Spring.Web/Web/Providers/SiteMapProviderAdapter.cs new file mode 100644 index 00000000..7652de4f --- /dev/null +++ b/src/Spring/Spring.Web/Web/Providers/SiteMapProviderAdapter.cs @@ -0,0 +1,368 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Collections.Specialized; +using System.Configuration.Provider; +using System.Security.Permissions; +using System.Web; +using Spring.Context.Support; + +#endregion + + +namespace Spring.Web.Providers +{ + /// + /// Wrapper for class. + /// + /// + ///

    + /// Configuration for this provider requires providerId element set in web.config file, + /// as the Id of wrapped provider (defined in the Spring context). + ///

    + ///
    + /// Damjan Tomic + [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class SiteMapProviderAdapter : SiteMapProvider, ISiteMapProvider + { + #region Field + + /// + /// Reference to wrapped provider (defined in Spring context). + /// + private SiteMapProvider wrappedProvider; + + #endregion + + #region ProviderBase members + + /// + ///Initializes the provider. + /// + /// + ///A collection of the name/value pairs representing the provider-specific + /// attributes specified in the configuration for this provider. + /// The providerId attribute may be used to override the name being used for looking up an object definition. + /// + ///The friendly name of the provider. + ///The or is null. + ///An attempt is made to call on a provider after the provider has already been initialized. + ///The has a length of zero or providerId attribute is not set. + public override void Initialize(string name, NameValueCollection config) + { + if (config == null) throw new ArgumentNullException("config"); + + string providerId = config["providerId"]; + if (String.IsNullOrEmpty(providerId)) + providerId = name; + config.Remove("providerId"); + + this.wrappedProvider = (System.Web.SiteMapProvider)WebApplicationContext.GetRootContext().GetObject(providerId); + this.wrappedProvider.Initialize(name,config); + } + + /// + ///Gets the friendly name used to refer to the provider during configuration. + /// + /// + /// + ///The friendly name used to refer to the provider during configuration. + /// + /// + public override string Name + { + get { return this.wrappedProvider.Name; } + } + + /// + ///Gets a brief, friendly description suitable for display in administrative tools or other user interfaces (UIs). + /// + /// + /// + ///A brief, friendly description suitable for display in administrative tools or other UIs. + /// + /// + public override string Description + { + get { return this.wrappedProvider.Description; } + } + + #endregion + + #region System.Web.SiteMapProvider members + + /// + ///Retrieves a object that represents the currently requested page using the specified object. + /// + /// + /// + ///A that represents the currently requested page; otherwise, null, if no corresponding can be found in the or if the page context is null. + /// + /// + ///The used to match node information with the URL of the requested page. + public override SiteMapNode FindSiteMapNode(HttpContext context) + { + return this.wrappedProvider.FindSiteMapNode(context); + } + + /// + ///Retrieves a object based on a specified key. + /// + /// + /// + ///A that represents the page identified by key; otherwise, null, if no corresponding is found or if security trimming is enabled and the cannot be returned for the current user. The default is null. + /// + /// + ///A lookup key with which a is created. + public override SiteMapNode FindSiteMapNodeFromKey(string key) + { + return this.wrappedProvider.FindSiteMapNodeFromKey(key); + } + + /// + ///When overridden in a derived class, retrieves a object that represents the page at the specified URL. + /// + /// + /// + ///A that represents the page identified by rawURL; otherwise, null, if no corresponding is found or if security trimming is enabled and the cannot be returned for the current user. + /// + /// + ///A URL that identifies the page for which to retrieve a . + public override SiteMapNode FindSiteMapNode(string rawUrl) + { + return this.wrappedProvider.FindSiteMapNode(rawUrl); + } + + /// + ///When overridden in a derived class, retrieves the child nodes of a specific . + /// + /// + /// + ///A read-only that contains the immediate child nodes of the specified ; otherwise, null or an empty collection, if no child nodes exist. + /// + /// + ///The for which to retrieve all child nodes. + public override SiteMapNodeCollection GetChildNodes(SiteMapNode node) + { + return this.wrappedProvider.GetChildNodes(node); + } + + /// + ///Provides an optimized lookup method for site map providers when retrieving the node for the currently requested page and fetching the parent and ancestor site map nodes for the current page. + /// + /// + /// + ///A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + /// + /// + ///The number of ancestor site map node generations to get. A value of -1 indicates that all ancestors might be retrieved and cached by the provider. + ///upLevel is less than -1. + public override SiteMapNode GetCurrentNodeAndHintAncestorNodes(int upLevel) + { + return this.wrappedProvider.GetCurrentNodeAndHintAncestorNodes(upLevel); + } + + /// + ///Provides an optimized lookup method for site map providers when retrieving the node for the currently requested page and fetching the site map nodes in the proximity of the current node. + /// + /// + /// + ///A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + /// + /// + ///The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors might be retrieved and cached by the provider. + ///The number of child generations to fetch. 0 indicates no descendant nodes are retrieved and a -1 indicates that all descendant nodes might be retrieved and cached by the provider. + ///upLevel or downLevel is less than -1. + public override SiteMapNode GetCurrentNodeAndHintNeighborhoodNodes(int upLevel, int downLevel) + { + return this.wrappedProvider.GetCurrentNodeAndHintNeighborhoodNodes(upLevel, downLevel); + } + + /// + ///When overridden in a derived class, retrieves the parent node of a specific object. + /// + /// + /// + ///A that represents the parent of node; otherwise, null, if the has no parent or security trimming is enabled and the parent node is not accessible to the current user. + /// + /// + ///The for which to retrieve the parent node. + public override SiteMapNode GetParentNode(SiteMapNode node) + { + return this.wrappedProvider.GetParentNode(node); + } + + /// + ///Provides an optimized lookup method for site map providers when retrieving an ancestor node for the currently requested page and fetching the descendant nodes for the ancestor. + /// + /// + /// + ///A that represents an ancestor of the currently requested page; otherwise, null, if the current or ancestor is not found or cannot be returned for the current user. + /// + /// + ///The number of descendant node levels to retrieve from the target ancestor node. + ///The number of ancestor node levels to traverse when retrieving the requested ancestor node. + ///walkupLevels or relativeDepthFromWalkup is less than 0. + public override SiteMapNode GetParentNodeRelativeToCurrentNodeAndHintDownFromParent(int walkupLevels, + int relativeDepthFromWalkup) + { + return this.wrappedProvider.GetParentNodeRelativeToCurrentNodeAndHintDownFromParent(walkupLevels, relativeDepthFromWalkup); + } + + /// + ///Provides an optimized lookup method for site map providers when retrieving an ancestor node for the specified object and fetching its child nodes. + /// + /// + /// + ///A that represents an ancestor of node; otherwise, null, if the current or ancestor is not found or cannot be returned for the current user. + /// + /// + ///The number of descendant node levels to retrieve from the target ancestor node. + ///The that acts as a reference point for walkupLevels and relativeDepthFromWalkup. + ///The number of ancestor node levels to traverse when retrieving the requested ancestor node. + ///The value specified for walkupLevels or relativeDepthFromWalkup is less than 0. + ///node is null. + public override SiteMapNode GetParentNodeRelativeToNodeAndHintDownFromParent(SiteMapNode node, int walkupLevels, + int relativeDepthFromWalkup) + { + return this.wrappedProvider.GetParentNodeRelativeToNodeAndHintDownFromParent(node, walkupLevels, relativeDepthFromWalkup); + } + + /// + /// This method is marked as protected and should never be called. + /// + /// + /// + ///A that represents the root node of the set of nodes that the current provider manages. + /// + /// + protected override SiteMapNode GetRootNodeCore() + { + throw new NotSupportedException("This method should never be called."); + } + + /// + ///Provides a method that site map providers can override to perform an optimized retrieval of one or more levels of parent and ancestor nodes, relative to the specified object. + /// + /// + ///The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors might be retrieved and cached. + ///The that acts as a reference point for upLevel. + ///upLevel is less than -1. + ///node is null. + public override void HintAncestorNodes(SiteMapNode node, int upLevel) + { + this.wrappedProvider.HintAncestorNodes(node, upLevel); + } + + /// + ///Provides a method that site map providers can override to perform an optimized retrieval of nodes found in the proximity of the specified node. + /// + /// + ///The number of ancestor generations to fetch. 0 indicates no ancestor nodes are retrieved and -1 indicates that all ancestors (and their descendant nodes to the level of node) might be retrieved and cached. + ///The number of descendant generations to fetch. 0 indicates no descendant nodes are retrieved and -1 indicates that all descendant nodes might be retrieved and cached. + ///The that acts as a reference point for upLevel. + ///upLevel or downLevel is less than -1. + ///node is null. + public override void HintNeighborhoodNodes(SiteMapNode node, int upLevel, int downLevel) + { + this.wrappedProvider.HintNeighborhoodNodes(node, upLevel, downLevel); + } + + /// + ///Retrieves a Boolean value indicating whether the specified object can be viewed by the user in the specified context. + /// + /// + /// + ///true if security trimming is enabled and node can be viewed by the user or security trimming is not enabled; otherwise, false. + /// + /// + ///The that contains user information. + ///The that is requested by the user. + ///context is null.- or -node is null. + public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node) + { + return this.wrappedProvider.IsAccessibleToUser(context, node); + } + + /// + ///Gets the object that represents the currently requested page. + /// + /// + /// + ///A that represents the currently requested page; otherwise, null, if the is not found or cannot be returned for the current user. + /// + /// + public override SiteMapNode CurrentNode + { + get { return this.wrappedProvider.CurrentNode; } + } + + /// + ///Gets or sets the parent object of the current provider. + /// + /// + /// + ///The parent provider of the current . + /// + /// + public override System.Web.SiteMapProvider ParentProvider + { + get { return this.wrappedProvider.ParentProvider; } + set { this.wrappedProvider.ParentProvider = value; } + } + + /// + ///Gets the root object in the current provider hierarchy. + /// + /// + /// + ///An that is the top-level site map provider in the provider hierarchy that the current provider belongs to. + /// + /// + ///There is a circular reference to the current site map provider. + public override System.Web.SiteMapProvider RootProvider + { + get { return this.wrappedProvider.RootProvider; } + } + + /// + ///Gets the root object of the site map data that the current provider represents. + /// + /// + /// + ///The root of the current site map data provider. The default implementation performs security trimming on the returned node. + /// + /// + public override SiteMapNode RootNode + { + get { return this.wrappedProvider.RootNode; } + } + + #endregion + + } +} +#endif \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Services/WebServiceExporter.cs b/src/Spring/Spring.Web/Web/Services/WebServiceExporter.cs new file mode 100644 index 00000000..8dee0d9d --- /dev/null +++ b/src/Spring/Spring.Web/Web/Services/WebServiceExporter.cs @@ -0,0 +1,503 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Web.Services; + +using Spring.Context; +using Spring.Context.Support; +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Util; +using Spring.Proxy; + +#endregion + +namespace Spring.Web.Services +{ + /// + /// Exports an object as a web service. + /// + /// + ///

    + /// The exporter will create a web service wrapper for the object that is + /// to be exposed and additionally enable its configuration as a web + /// service. + ///

    + ///

    + /// The exported object can be either a standard .NET web service + /// implementation, with methods marked using the standard + /// , or it can be a + /// plain .NET object. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: WebServiceExporter.cs,v 1.33 2008/04/07 11:00:24 bbaia Exp $ + public class WebServiceExporter : IInitializingObject, IObjectFactoryAware, IFactoryObject, IObjectNameAware + { + #region Fields + + private Type _webServiceBaseType = typeof(WebService); + private string _targetName; + private string _description; + private string _name; + private string _namespace = WebServiceAttribute.DefaultNamespace; + private string[] _interfaces; + private IList _typeAttributes = new ArrayList(); + private IDictionary _memberAttributes = new Hashtable(); + + /// + /// The name of the object in the factory. + /// + protected string objectName; + + /// + /// The owning factory. + /// + protected IObjectFactory objectFactory; + + /// + /// The generated web service wrapper type. + /// + protected Type proxyType; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public WebServiceExporter() + {} + + #endregion + + #region Properties + + /// + /// Gets or sets the base type that web service should inherit. + /// + /// + /// Default is + /// + public Type WebServiceBaseType + { + get { return _webServiceBaseType; } + set { _webServiceBaseType = value; } + } + + /// + /// Gets or sets the name of the target object that should be exposed as a web service. + /// + /// + /// The name of the target object that should be exposed as a web service. + /// + public string TargetName + { + get { return _targetName; } + set { _targetName = value; } + } + + /// + /// Gets or sets the description of the web service (optional). + /// + /// + /// The web service description. + /// + public string Description + { + get { return _description; } + set { _description = value; } + } + + /// + /// Gets or sets the name of the web service (optional). + /// + /// + ///

    + /// Defaults to the value of the exporter's object ID. + ///

    + ///
    + /// + /// The web service name. + /// + public string Name + { + get + { + if (_name == null) + { + _name = WebUtils.GetPageName(objectName); + } + return _name; + } + set { _name = value; } + } + + /// + /// Gets or sets the web service namespace. + /// + /// + /// The web service namespace. + /// + public string Namespace + { + get { return _namespace; } + set { _namespace = value; } + } + + /// + /// Gets or sets the list of interfaces whose methods should be exposed as web services. + /// + /// + /// If not set, all the interfaces implemented or inherited + /// by the target type will be used. + /// + /// The interfaces. + public string[] Interfaces + { + get { return _interfaces; } + set { _interfaces = value; } + } + + /// + /// Gets or sets a list of custom attributes + /// that should be applied to a proxy class. + /// + public IList TypeAttributes + { + get { return _typeAttributes; } + set { _typeAttributes = value; } + } + + /// + /// Gets or sets a dictionary of custom attributes + /// that should be applied to web service members. + /// + /// + /// Dictionary key is an expression that members can be matched against. + /// Value is a list of attributes that should be applied + /// to each member that matches expression. + /// + public IDictionary MemberAttributes + { + get { return _memberAttributes; } + set { _memberAttributes = value; } + } + + #endregion + + #region IObjectFactoryAware Members + + /// + /// Callback that supplies the owning factory to an object instance. + /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + /// + ///

    + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

    + ///
    + /// + /// In case of initialization errors. + /// + public virtual IObjectFactory ObjectFactory + { + set { this.objectFactory = value; } + } + + #endregion + + #region IFactoryObject Members + + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + /// + /// If this method is being called in the context of an enclosing IoC container and + /// returns , the IoC container will consider this factory + /// object as not being fully initialized and throw a corresponding (and most + /// probably fatal) exception. + /// + /// + /// + /// An instance (possibly shared or independent) of the object managed by + /// this factory. + /// + public virtual object GetObject() + { + // no sense to call this method, because the web service type + // will be instantiated by the .NET infrastructure. (ObjectType is used instead) + // Users should use GetObject("TargetName") instead. + return new InvalidOperationException( + "The web service instance is created and managed by the .NET infrastructure."); + } + + /// + /// Return the of object that this + /// creates, or + /// if not known in advance. + /// + public virtual Type ObjectType + { + get { return (proxyType != null ? proxyType : objectFactory.GetType(TargetName)); } + } + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + public virtual bool IsSingleton + { + get { return false; } + } + + #endregion + + #region IObjectNameAware Members + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// + /// The name of the object in the factory. + /// + /// + ///

    + /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

    + ///
    + public string ObjectName + { + set { this.objectName = value; } + } + + #endregion + + #region IInitializingObject Members + + /// + /// Exports specified object as a web service. + /// + /// + /// In the event of misconfiguration (such as failure to set an essential + /// property) or if initialization fails. + /// + public virtual void AfterPropertiesSet() + { + ValidateConfiguration(); + GenerateProxy(); + } + + #endregion + + #region Protected Methods + + /// + /// Validates the configuration. + /// + protected virtual void ValidateConfiguration() + { + if (TargetName == null) + { + throw new ArgumentException("The TargetName property is required."); + } + } + + /// + /// Generates the web service wrapper type. + /// + protected virtual void GenerateProxy() + { + IProxyTypeBuilder builder = new WebServiceProxyTypeBuilder(TargetName, Description, Name, Namespace); + builder.Name = WebUtils.GetPageName(objectName); + builder.BaseType = WebServiceBaseType; + builder.TargetType = objectFactory.GetType(TargetName); + if (Interfaces != null && Interfaces.Length > 0) + { + builder.Interfaces = TypeResolutionUtils.ResolveInterfaceArray(Interfaces); + } + builder.TypeAttributes = TypeAttributes; + builder.MemberAttributes = MemberAttributes; + + proxyType = builder.BuildProxyType(); + } + + #endregion + + #region WebServiceProxyTypeBuilder inner class implementation + + private sealed class WebServiceProxyTypeBuilder : CompositionProxyTypeBuilder + { + #region Fields + + private static readonly MethodInfo GetObject = + typeof(IObjectFactory).GetMethod("GetObject", new Type[] {typeof(string)}); + + private static readonly MethodInfo GetApplicationContext = + typeof(WebApplicationContext).GetProperty("Current", BindingFlags.Public | BindingFlags.Static, null, typeof(IApplicationContext), Type.EmptyTypes, null).GetGetMethod(); + + private string targetName; + private CustomAttributeBuilder webServiceAttribute; + + #endregion + + #region Constructor(s) / Destructor + + public WebServiceProxyTypeBuilder( + string targetName, string description, string name, string ns) + { + this.targetName = targetName; + + // Creates a WebServiceAttribute from configuration info + this.webServiceAttribute = CreateWebServiceAttribute(description, name, ns); + } + + private static CustomAttributeBuilder CreateWebServiceAttribute(string description, string name, string ns) + { + ReflectionUtils.CustomAttributeBuilderBuilder cabb = + new ReflectionUtils.CustomAttributeBuilderBuilder(typeof(WebServiceAttribute)); + if (StringUtils.HasText(description)) + { + cabb.AddPropertyValue("Description", description); + } + if (StringUtils.HasText(name)) + { + cabb.AddPropertyValue("Name", name); + } + if (StringUtils.HasText(ns)) + { + cabb.AddPropertyValue("Namespace", ns); + } + return cabb.Build(); + } + + #endregion + + #region Protected Methods + + /// + /// Implements constructors for the proxy class. + /// + /// + /// This implementation generates a constructor + /// that gets instance of the target object using + /// . + /// + /// + /// The builder to use. + /// + protected override void ImplementConstructors(TypeBuilder builder) + { + MethodAttributes attributes = MethodAttributes.Public | + MethodAttributes.HideBySig | MethodAttributes.SpecialName | + MethodAttributes.RTSpecialName; + + ConstructorBuilder cb = builder.DefineConstructor( + attributes, CallingConventions.Standard, Type.EmptyTypes); + + ILGenerator il = cb.GetILGenerator(); + + il.Emit(OpCodes.Ldarg_0); + il.EmitCall(OpCodes.Call, GetApplicationContext, null); + il.Emit(OpCodes.Ldstr, targetName); + il.EmitCall(OpCodes.Callvirt, GetObject, null); + il.Emit(OpCodes.Stfld, targetInstance); + + + il.Emit(OpCodes.Ret); + } + + protected override IList GetTypeAttributes(Type type) + { + IList attrs = base.GetTypeAttributes(type); + + for (int i = 0; i < attrs.Count; i++) + { + if (IsAttributeMatchingType(attrs[i], typeof(WebServiceAttribute))) + { + // override existing WebServiceAttribute + attrs[i] = webServiceAttribute; + + return attrs; + } + } + + // add missing WebServiceAttribute + attrs.Add(webServiceAttribute); + + return attrs; + } + + protected override IList GetMethodAttributes(MethodInfo method) + { + IList attrs = base.GetMethodAttributes(method); + + bool containsWebMethodAttribute = false; + foreach (object attr in attrs) + { + if (IsAttributeMatchingType(attr, typeof(WebMethodAttribute))) + { + containsWebMethodAttribute = true; + break; + } + } + + // Creates default WebMethodAttribute if not set yet + if (!containsWebMethodAttribute) + { + attrs.Add(ReflectionUtils.CreateCustomAttribute(typeof(WebMethodAttribute))); + } + + return attrs; + } + + protected override TypeBuilder CreateTypeBuilder(string name, Type baseType) + { + return DynamicProxyManager.CreateTypeBuilder(name, baseType); + } + + #endregion + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Services/WebServiceHandlerFactory.cs b/src/Spring/Spring.Web/Web/Services/WebServiceHandlerFactory.cs new file mode 100644 index 00000000..f25c41ee --- /dev/null +++ b/src/Spring/Spring.Web/Web/Services/WebServiceHandlerFactory.cs @@ -0,0 +1,108 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Security.Permissions; +using System.Web; +using System.Web.UI; +using System.Web.Services; + +using Spring.Context; +using Spring.Context.Support; +using Spring.Util; +using Spring.Web.Support; + +#endregion + +namespace Spring.Web.Services +{ + /// + /// An implementation that + /// retrieves configured WebService objects from the Spring.NET web + /// application context. + /// + /// + /// This handler factory uses web service name from the URL, without the extension, + /// to find web service object in the Spring context. + /// + /// Aleksandar Seovic + /// $Id: WebServiceHandlerFactory.cs,v 1.12 2008/03/19 18:05:08 oakinger Exp $ + [PermissionSet(SecurityAction.InheritanceDemand, Unrestricted=true)] + public class WebServiceHandlerFactory : System.Web.Services.Protocols.WebServiceHandlerFactory, IHttpHandlerFactory + { + private static readonly MethodInfo CoreGetHandler = + typeof(System.Web.Services.Protocols.WebServiceHandlerFactory).GetMethod("CoreGetHandler", BindingFlags.NonPublic | BindingFlags.Instance, null, + new Type[] {typeof(Type), typeof(HttpContext), typeof(HttpRequest), typeof(HttpResponse)}, null); + + /// + /// Retrieves instance of the page from Spring web application context. + /// + /// current HttpContext + /// type of HTTP request (GET, POST, etc.) + /// requested page URL + /// translated server path for the page + /// instance of the configured page object + IHttpHandler IHttpHandlerFactory.GetHandler(HttpContext context, string requestType, string url, string path) + { + new AspNetHostingPermission(AspNetHostingPermissionLevel.Minimal).Demand(); + + IConfigurableApplicationContext appContext = + WebApplicationContext.GetContext(url) as IConfigurableApplicationContext; + + if (appContext == null) + { + throw new InvalidOperationException( + "Implementations of IApplicationContext must also implement IConfigurableApplicationContext"); + } + + string appRelativeVirtualPath = WebUtils.GetAppRelativePath(url); + + AbstractHandlerFactory.NamedObjectDefinition nod = + AbstractHandlerFactory.FindWebObjectDefinition(appRelativeVirtualPath, appContext.ObjectFactory); + + Type serviceType = null; + if (nod != null) + { + serviceType = appContext.GetType(nod.Name); + + // check if the type defines a Web Service + object[] wsAttribute = serviceType.GetCustomAttributes(typeof(WebServiceAttribute), true); + if (wsAttribute.Length == 0) + { + serviceType = null; + } + } + + if (serviceType == null) + { +#if NET_2_0 + serviceType = WebServiceParser.GetCompiledType(url, context); +#else + serviceType = WebServiceParser.GetCompiledType(path, context); +#endif + } + + return (IHttpHandler) CoreGetHandler.Invoke(this, new object[] {serviceType, context, context.Request, context.Response}); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/AbstractHandlerFactory.cs b/src/Spring/Spring.Web/Web/Support/AbstractHandlerFactory.cs new file mode 100644 index 00000000..4be9f3c6 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/AbstractHandlerFactory.cs @@ -0,0 +1,183 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.IO; +using System.Web; +using Common.Logging; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Provides base functionality for Spring.NET context-aware + /// implementations. + /// + /// + ///

    + /// Provides derived classes with a default implementation of + /// method. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: AbstractHandlerFactory.cs,v 1.5 2008/03/19 18:05:08 oakinger Exp $ + public abstract class AbstractHandlerFactory : IHttpHandlerFactory + { + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// This is an abstract class and as such provides no public constructors. + ///

    + ///
    + protected AbstractHandlerFactory() + {} + + /// + /// Returns an appropriate implementation. + /// + /// + /// An instance of the class that + /// provides references to intrinsic server objects. + /// + /// + /// The HTTP method of the request. + /// + /// The request URL. + /// + /// The physical path of the requested resource. + /// + /// + /// A new object that processes + /// the request. + /// + public abstract IHttpHandler GetHandler( + HttpContext context, string requestType, string url, string pathTranslated); + + /// + /// Enables a factory to release an existing + /// instance. + /// + /// + /// The object to release. + /// + public virtual void ReleaseHandler(IHttpHandler handler) + {} + + /// + /// DO NOT USE - this is subject to change! + /// + /// + /// + /// + /// This method requires registrars to follow the convention of registering web object definitions using their + /// application relative urls (~/mypath/mypage.aspx). + /// + /// + /// Resolve an object definition by url. + /// + protected internal static NamedObjectDefinition FindWebObjectDefinition(string appRelativeVirtualPath, IConfigurableListableObjectFactory objectFactory) + { + ILog Log = LogManager.GetLogger(typeof(AbstractHandlerFactory)); + bool isDebug = Log.IsDebugEnabled; + + // lookup definition using app-relative url + if (isDebug) Log.Debug(string.Format("GetHandler():looking up definition for app-relative url '{0}'", appRelativeVirtualPath)); + string objectDefinitionName = appRelativeVirtualPath; + IObjectDefinition pageDefinition = objectFactory.GetObjectDefinition(appRelativeVirtualPath, true); + + if (pageDefinition == null) + { + // try using pagename+extension and pagename only + string pageExtension = Path.GetExtension(appRelativeVirtualPath); + string pageName = WebUtils.GetPageName(appRelativeVirtualPath); + // only looks in the specified object factory -- it will *not* search parent contexts + pageDefinition = objectFactory.GetObjectDefinition(pageName + pageExtension, false); + if (pageDefinition == null) + { + pageDefinition = objectFactory.GetObjectDefinition(pageName, false); + if (pageDefinition != null) objectDefinitionName = pageName; + } + else + { + objectDefinitionName = pageName + pageExtension; + } + + if (pageDefinition != null) + { + if (isDebug) + Log.Debug(string.Format("GetHandler():found definition for page-name '{0}'", objectDefinitionName)); + } + else + { + if (isDebug) + Log.Debug(string.Format("GetHandler():no definition found for page-name '{0}'", pageName)); + } + } + else + { + if (isDebug) Log.Debug(string.Format("GetHandler():found definition for page-url '{0}'", appRelativeVirtualPath)); + } + + return (pageDefinition == null) ? (NamedObjectDefinition)null : new NamedObjectDefinition(objectDefinitionName, pageDefinition); + } + + /// + /// DO NOT USE - this is subject to change! + /// + protected internal class NamedObjectDefinition + { + private readonly string _name; + private readonly IObjectDefinition _objectDefinition; + + /// + /// DO NOT USE + /// + public NamedObjectDefinition(string name, IObjectDefinition objectDefinition) + { + _name = name; + _objectDefinition = objectDefinition; + } + + /// + /// DO NOT USE + /// + public string Name + { + get { return _name; } + } + + /// + /// DO NOT USE + /// + public IObjectDefinition ObjectDefinition + { + get { return _objectDefinition; } + } + } + } + +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/AbstractProcessHandler.cs b/src/Spring/Spring.Web/Web/Support/AbstractProcessHandler.cs new file mode 100644 index 00000000..cc2060e0 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/AbstractProcessHandler.cs @@ -0,0 +1,289 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Web; +using System.Web.SessionState; +using Spring.Context; +using Spring.Util; +using Spring.Web.Process; +using Spring.Web.Support; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// An abstract base class that defines common behavior for different process implementations. + /// + /// Aleksandar Seovic + /// $Id: AbstractProcessHandler.cs,v 1.1 2007/08/03 08:31:25 oakinger Exp $ + public abstract class AbstractProcessHandler : IProcess, ISharedStateAware, IApplicationContextAware, IHttpHandler, IRequiresSessionState + { + /// + /// Parameter name that is used for process ID. + /// + protected internal const string ProcessIdParamName = "pid"; + + #region Fields + + private string id = Guid.NewGuid().ToString("N"); + private IProcess parent; + private object controller; + private string defaultView; + private string currentView; + private IDictionary views = CollectionsUtil.CreateCaseInsensitiveHashtable(); + private IDictionary sharedState; + private IApplicationContext applicationContext; + private string processUrl; + private bool viewChanged; + + #endregion + + #region Constructors + + /// + /// Creates instance of the process and registers it with the . + /// + public AbstractProcessHandler() + { + ProcessManager.RegisterProcess(this); + } + + #endregion + + #region Properties + + /// + /// Unique ID of this component instance. + /// + public string Id + { + get { return this.id; } + } + + /// + /// Gets or sets the parent process. + /// + internal IProcess Parent + { + get { return this.parent; } + set { this.parent = value; } + } + + /// + /// Returns a thread-safe dictionary that contains state that is shared by + /// all views of this component. + /// + public IDictionary SharedState + { + get { return this.sharedState; } + set { this.sharedState = value; } + } + + /// + /// Controller for the component. + /// + /// + /// Process controller will be shared by all the views + /// that belong to this component. + /// + public object Controller + { + get { return this.controller; } + set { this.controller = value; } + } + + /// + /// Default view for the component. + /// + public string DefaultView + { + get { return this.defaultView; } + set { this.defaultView = value; } + } + + /// + /// Gets the name of the current view. + /// + public string CurrentView + { + get + { + if (this.currentView == null) + { + this.CurrentView = this.defaultView; + } + return this.currentView; + } + set + { + string oldView = this.currentView; + if (this.views.Contains(value)) + { + this.currentView = (string) this.views[value]; + } + else + { + this.currentView = value; + } + this.viewChanged = (oldView != this.currentView); + } + } + + /// + /// Gets the the flag that indicates if selected view + /// has changed during the current request. + /// + public bool ViewChanged + { + get { return this.viewChanged; } + } + + /// + /// Gets a map of process views. + /// + public IDictionary Views + { + get { return this.views; } + } + + /// + /// Gets the process URL. + /// + protected string ProcessUrl + { + get { return this.processUrl; } + } + + #endregion + + #region Public methods + + /// + /// Starts the process. + /// + /// Process URL. + public void Start(string url) + { + this.processUrl = url; + this.NavigateToStartView(); + } + + /// + /// Resolves and sets the view for the specified view name. + /// + /// Name of the view to go to. + public virtual void SetView(string viewName) + { + this.CurrentView = viewName; + this.NavigateToCurrentView(); + } + + /// + /// Ends the process by unregistering it from the . + /// + public virtual void End() + { + ProcessManager.UnregisterProcess(this.id); + if (this.parent != null) + { + this.parent.SetView(this.parent.CurrentView); + } + } + + #endregion + + #region Abstract methods + + /// + /// Method that needs to be implemented by specific process implementations + /// in order to navigate to the first view in the process. + /// + protected abstract void NavigateToStartView(); + + /// + /// Method that needs to be implemented by specific process implementations + /// in order to navigate to the current view. + /// + protected abstract void NavigateToCurrentView(); + + #endregion + + #region IHttpHandler implementation + + /// + /// Processes the request by delegating to appropriate view, which could be + /// another process. + /// + /// + void IHttpHandler.ProcessRequest(HttpContext context) + { + IHttpHandler handler = (IHttpHandler) this.applicationContext.GetObject(WebUtils.GetPageName(this.CurrentView)); + this.viewChanged = false; + + if (handler is AbstractProcessHandler) + { + ((AbstractProcessHandler) handler).Parent = this; + // TODO: start child process + } + + if (handler is IProcessAware) + { + ((IProcessAware) handler).Process = this; + } + if (handler is ISharedStateAware) + { + ((ISharedStateAware) handler).SharedState = this.sharedState; + } + + context.Handler = handler; + handler.ProcessRequest(context); + } + + /// + /// Returns true because this wrapper handler can be reused. + /// Actual page is instantiated at the beginning of the ProcessRequest method. + /// + bool IHttpHandler.IsReusable + { + get { return false; } + } + + #endregion + + #region IApplicationContextAware implementation + + /// + /// Gets or sets the application context. + /// + public IApplicationContext ApplicationContext + { + get { return this.applicationContext; } + set { this.applicationContext = value; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/ContextMonitor.cs b/src/Spring/Spring.Web/Web/Support/ContextMonitor.cs new file mode 100644 index 00000000..66d69310 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/ContextMonitor.cs @@ -0,0 +1,140 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.IO; +using System.Web; + +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +namespace Spring.Web.Support +{ + /// + /// implementation that allows users to monitor state + /// of the Spring.NET web application context. + /// + /// + ///

    + /// + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: ContextMonitor.cs,v 1.3 2007/07/29 19:39:48 markpollack Exp $ + public class ContextMonitor : IHttpHandler + { + /// + /// Initializes a new instance of the class. + /// + public ContextMonitor() + {} + + /// + /// Processes HTTP request. + /// + /// An object that provides references to the intrinsic server objects (for example, , , , and ) used to service HTTP requests. + public void ProcessRequest(HttpContext context) + { + HttpResponse res = context.Response; + res.BufferOutput = true; + + RenderHeader(res.Output, context.Request.ApplicationPath); + + + IConfigurableApplicationContext appContext = + WebApplicationContext.Current as IConfigurableApplicationContext; + if (appContext == null) + { + throw new InvalidOperationException( + "Implementations of IApplicationContext must also implement IConfigurableApplicationContext"); + } + + string[] names = appContext.GetObjectDefinitionNames(); + foreach (string name in names) + { + RenderObjectDefinition(res.Output, name, appContext.ObjectFactory.GetObjectDefinition(name)); + } + + RenderFooter(res.Output); + } + + /// + /// Gets a value indicating whether another request can use + /// this instance. + /// + /// True if this handler is reusable, False otherwise. + public bool IsReusable + { + get { return true; } + } + + private void RenderHeader(TextWriter tw, string contextName) + { + tw.WriteLine(""); + tw.WriteLine(" "); + tw.WriteLine(" Spring.NET Context Monitor - " + contextName + ""); + tw.WriteLine(" "); + tw.WriteLine(" "); + tw.WriteLine(" "); + tw.WriteLine(" "); + RenderHeaderCell(tw, "Name"); + RenderHeaderCell(tw, "Type"); + RenderHeaderCell(tw, "Is Abstract"); + RenderHeaderCell(tw, "Is Singleton"); + RenderHeaderCell(tw, "Is Lazy Init"); + RenderHeaderCell(tw, "Scope"); + RenderHeaderCell(tw, "Page Name"); + tw.WriteLine(" "); + + } + + private void RenderHeaderCell(TextWriter tw, string text) + { + tw.WriteLine(" "); + } + + private void RenderObjectDefinition(TextWriter tw, string name, IObjectDefinition def) + { + tw.WriteLine(" "); + RenderCell(tw, name); + RenderCell(tw, def.ObjectTypeName); + RenderCell(tw, def.IsAbstract); + RenderCell(tw, def.IsSingleton); + RenderCell(tw, def.IsLazyInit); + RenderCell(tw, (def is IWebObjectDefinition ? ((IWebObjectDefinition) def).Scope.ToString() : " ")); + RenderCell(tw, (def is IWebObjectDefinition && ((IWebObjectDefinition) def).IsPage ? ((IWebObjectDefinition) def).PageName : " ")); + tw.WriteLine(" "); + } + + private void RenderCell(TextWriter tw, object value) + { + tw.WriteLine(" "); + } + + private void RenderFooter(TextWriter tw) + { + tw.WriteLine("
    " + text + "
    " + (value == null ? " " : value.ToString()) + "
    "); + tw.WriteLine(" "); + tw.WriteLine(""); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/ControlInterceptor.cs b/src/Spring/Spring.Web/Web/Support/ControlInterceptor.cs new file mode 100644 index 00000000..ea0d0419 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/ControlInterceptor.cs @@ -0,0 +1,134 @@ +#region License +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using System.Collections; +using System.Web.UI; +using Spring.Context; +using Spring.Util; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Support Class providing a method to ensure a control has been intercepted + /// + /// Erich Eichinger + /// $Id: ControlInterceptor.cs,v 1.2 2008/05/13 14:22:47 oakinger Exp $ + internal sealed class ControlInterceptor + { + /// + /// Holds all available interception strategies + /// + private static readonly IInterceptionStrategy[] s_availableInterceptionStrategies = new IInterceptionStrategy[] + { + new InterceptControlCollectionStrategy() + , new InterceptControlCollectionOwnerStrategy() + }; + + /// + /// Holds a control.GetType()->IInterceptionStrategy table. + /// + private static readonly Hashtable s_cachedInterceptionStrategies = new Hashtable(); + + private ControlInterceptor() + { + } + + /// + /// Ensures, a control has been intercepted to support Web-DI. If not, the control will be intercepted. + /// + public static void EnsureControlIntercepted(IApplicationContext defaultApplicationContext, Control control) + { + if (control is LiteralControl) + { + return; // nothing more to do + } + + // check control itself + if (IsDependencyInjectionAware(defaultApplicationContext, control)) + { + return; // nothing more to do + } + + // check control's ControlCollection + EnsureControlCollectionIntercepted(defaultApplicationContext, control); + } + + private static void EnsureControlCollectionIntercepted(IApplicationContext defaultApplicationContext, Control control) + { + // check the collection + ControlAccessor ctlAccessor = new ControlAccessor(control); + ControlCollection childControls = ctlAccessor.Controls; + if (IsDependencyInjectionAware(defaultApplicationContext, childControls)) + { + return; // nothing more to do + } + + // check, if the collection's owner has already been intercepted + ControlCollectionAccessor ctlColAccessor = new ControlCollectionAccessor(childControls); + if (IsDependencyInjectionAware(defaultApplicationContext, ctlColAccessor.Owner)) + { + return; // nothing more to do + } + + // lookup strategy in cache + IInterceptionStrategy strategy = null; + lock(s_cachedInterceptionStrategies) + { + strategy = (IInterceptionStrategy) s_cachedInterceptionStrategies[control.GetType()]; + } + + if (strategy != null) + { + strategy.Intercept(defaultApplicationContext, ctlAccessor, ctlColAccessor); + } + else + { + // probe for a strategy + for(int i=0;i + /// Any concrete interception strategy must implement this interface + /// + /// Erich Eichinger + /// $Id: IInterceptionStrategy.cs,v 1.1 2007/08/01 23:11:00 markpollack Exp $ + internal interface IInterceptionStrategy + { + /// + /// Any implementation must never throw an exception from this method. + /// Instead false must be returned to indicate interception failure. + /// + /// + /// true, if interception succeeded. false otherwise. + /// + bool Intercept(IApplicationContext defaultApplicationContext, + ControlAccessor ctlAccessor, ControlCollectionAccessor ctlColAccessor); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/ISharedStateAware.cs b/src/Spring/Spring.Web/Web/Support/ISharedStateAware.cs new file mode 100644 index 00000000..adf0c638 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/ISharedStateAware.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Web; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// This interface should be implemented by s that want to + /// have access to the shared state for the handler. + /// + /// + ///

    + /// Shared state is very useful if you have data that needs to be shared by all instances + /// of the same page (or other ). + ///

    + ///

    + /// For example, class implements this interface, which allows + /// each page derived from it to cache localizalization resources and parsed data binding + /// expressions only once and then reuse the cached values, regardless of how many instances + /// of the page are created. + ///

    + ///
    + public interface ISharedStateAware + { + /// + /// Gets or sets the that should be used + /// to store shared state for the . + /// + /// + /// The that should be used + /// to store shared state for the . + /// + IDictionary SharedState { get; set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/ISupportsWebDependencyInjection.cs b/src/Spring/Spring.Web/Web/Support/ISupportsWebDependencyInjection.cs new file mode 100644 index 00000000..ec884cf8 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/ISupportsWebDependencyInjection.cs @@ -0,0 +1,100 @@ +#region License +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using Spring.Context; +using Spring.Context.Support; +using Spring.Util; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// "Contract"-interface of the Web-DI infrastructure. + /// + /// + /// This interface supports Spring's DI infrastructure and normally doesn't need to be implemented
    + ///
    + /// Any Page, Control or ControlCollection implementing this interface guarantees to + /// call on any control being added + /// before it is actually added to the child-collection. + ///
    + /// + ///

    The following example shows, how to make a Control support the DI-infrastructure:

    + /// + /// class MyControl : Control, ISupportsWebDependencyInjection + /// { + /// private IApplicationContext _defaultApplicationContext; + /// + /// public IApplicationContext DefaultApplicationContext + /// { + /// get { return _defaultApplicationContext; } + /// set { _defaultApplicationContext = value; } + /// } + /// + /// override protected AddedControl( Control control, int index ) + /// { + /// WebUtils.InjectDependenciesRecursive( _defaultApplicationContext, control ); + /// base.AddedControl( control, index ); + /// } + /// } + /// + ///
    + /// + ///

    The following example shows, how to make a ControlCollection support the DI-infrastructure:

    + ///

    Note, that you MUST implement the single-argument constructor ControlCollection( Control owner )!

    + /// + /// class MyControlCollection : ControlCollection, ISupportsWebDependencyInjection + /// { + /// private IApplicationContext _defaultApplicationContext; + /// + /// public MyControlCollection( Control owner ) : base( owner ) + /// {} + /// + /// public IApplicationContext DefaultApplicationContext + /// { + /// get { return _defaultApplicationContext; } + /// set { _defaultApplicationContext = value; } + /// } + /// + /// override public Add( Control child ) + /// { + /// WebUtils.InjectDependenciesRecursive( _defaultApplicationContext, child ); + /// base.Add( child ); + /// } + /// + /// override public AddAt( int index, Control child ) + /// { + /// WebUtils.InjectDependenciesRecursive( _defaultApplicationContext, child ); + /// base.AddAt( index, child ); + /// } + /// } + /// + ///
    + /// Erich Eichinger + /// $Id: ISupportsWebDependencyInjection.cs,v 1.2 2007/08/01 23:11:00 markpollack Exp $ + public interface ISupportsWebDependencyInjection + { + /// + /// Holds the default instance to be used during injecting a control-tree. + /// + IApplicationContext DefaultApplicationContext { get; set; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/InterceptControlCollectionOwnerStrategy.cs b/src/Spring/Spring.Web/Web/Support/InterceptControlCollectionOwnerStrategy.cs new file mode 100644 index 00000000..ab1a4e6a --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/InterceptControlCollectionOwnerStrategy.cs @@ -0,0 +1,42 @@ +#region License +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using Spring.Context; +using Spring.Util; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// This strategy replaces the original collection's owner with an intercepting proxy + /// + /// Erich Eichinger + /// $Id: InterceptControlCollectionOwnerStrategy.cs,v 1.1 2007/08/01 23:11:01 markpollack Exp $ + internal class InterceptControlCollectionOwnerStrategy : IInterceptionStrategy + { + public bool Intercept(IApplicationContext defaultApplicationContext, ControlAccessor ctlAccessor, + ControlCollectionAccessor ctlColAccessor) + { + ctlColAccessor.Owner = new SupportsWebDependencyInjectionOwnerProxy(defaultApplicationContext, ctlAccessor.GetTarget() ); + return true; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/InterceptControlCollectionStrategy.cs b/src/Spring/Spring.Web/Web/Support/InterceptControlCollectionStrategy.cs new file mode 100644 index 00000000..cc4533b6 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/InterceptControlCollectionStrategy.cs @@ -0,0 +1,220 @@ +#region License +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Web.UI; +using Spring.Context; +using Spring.Proxy; +using Spring.Util; +using Spring.Web.Support; + +#if NET_2_0 +using System.Collections.Generic; +#else +using Spring.Reflection.Dynamic; +#endif + +#endregion + +namespace Spring.Web.Support +{ + /// + /// This strategy enhances a ControlCollection's type by + /// dynamically implementing ISupportsWebDependencyInjection on this type + /// + /// Erich Eichinger + /// $Id: InterceptControlCollectionStrategy.cs,v 1.2 2008/05/13 14:22:47 oakinger Exp $ + internal class InterceptControlCollectionStrategy : IInterceptionStrategy + { + /// + /// Holds a reference to the static(!) callback-method to be used during generation of intercepted ControlCollection-Types + /// + private delegate void InjectDependenciesCallbackHandler(IApplicationContext defaultApplicationContext, Control control); + + /// + /// The list of methods to be intercepted for a ControlCollection + /// + private static readonly MethodInfo[] s_collectionMethods = new MethodInfo[] + { + typeof(ControlCollection).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public) + , typeof (ControlCollection).GetMethod("AddAt", BindingFlags.Instance | BindingFlags.Public) + }; + + /// + /// Holds a table of already known intercepted ControlCollection types + /// + private static readonly Hashtable s_interceptedCollectionTypeCache = new Hashtable(); + + /// + /// Intercepts the given by dynamically deriving + /// the original type and let it implement . + /// + /// the ApplicationContext to be set on the collection instance. + /// a wrapper around the owner control instance. + /// a wrapper around the collection instance. + /// true, if interception was successful. false otherwise + public bool Intercept(IApplicationContext defaultApplicationContext, + ControlAccessor ctlAccessor, ControlCollectionAccessor ctlColAccessor) + { + Type collectionType = ctlColAccessor.GetTargetType(); + if (collectionType.IsSealed || + !ReflectionUtils.IsTypeVisible(collectionType, DynamicProxyManager.ASSEMBLY_NAME)) + // || (null == collectionType.GetConstructor(new Type[] {typeof (Control)})) + { + return false; + } + + // this will enhance the collection's type and create a new instance of this type with fields copied from original collection + try + { + ControlCollection childControls = InterceptCollection(ctlAccessor.GetTarget(), ctlColAccessor.GetTarget() ); + ((ISupportsWebDependencyInjection)childControls).DefaultApplicationContext = defaultApplicationContext; + ctlAccessor.Controls = childControls; + } + catch + { + // this may happen, if the ControlCollection doesn't contain a standard-ctor ControlCollection( Control owner) + return false; + } + return true; + } + + private static ControlCollection InterceptCollection(Control owner, ControlCollection originalCollection) + { + CreateControlCollectionDelegate factoryMethod = GetInterceptedCollectionFactory(owner.GetType(), originalCollection.GetType()); + ControlCollection interceptedCollection = factoryMethod(owner); + ReflectionUtils.MemberwiseCopy(originalCollection, interceptedCollection); + return interceptedCollection; + } + + internal static ControlCollection TryCreateCollection(Control owner) + { + CreateControlCollectionDelegate factoryMethod = (CreateControlCollectionDelegate)s_collectionFactoryCache[owner.GetType()]; + if (factoryMethod != null) + { + return factoryMethod(owner); + } + return null; + } + + private delegate ControlCollection CreateControlCollectionDelegate(Control owner); + private static readonly Hashtable s_collectionFactoryCache = new Hashtable(); + +#if NET_2_0 + private static CreateControlCollectionDelegate GetInterceptedCollectionFactory(Type ownerType, Type collectionType) + { + AssertUtils.State( typeof(Control).IsAssignableFrom(ownerType), "ownerType must be of type Control" ); + AssertUtils.State( typeof(ControlCollection).IsAssignableFrom(collectionType), "collectionType must be of type ControlCollection" ); + + CreateControlCollectionDelegate factoryMethod = (CreateControlCollectionDelegate)s_collectionFactoryCache[ownerType]; + if (factoryMethod == null) + { + lock (s_collectionFactoryCache) + { + factoryMethod = (CreateControlCollectionDelegate)s_collectionFactoryCache[ownerType]; + if (factoryMethod == null) + { + Type interceptedCollectionType = + GetInterceptedCollectionType(collectionType, WebDependencyInjectionUtils.InjectDependenciesRecursive); + + ConstructorInfo ctor = + interceptedCollectionType.GetConstructor(new Type[] { typeof(Control) }); + DynamicMethod dm = + new System.Reflection.Emit.DynamicMethod(string.Empty, typeof(ControlCollection), + new Type[] { typeof(Control) }); + ILGenerator il = dm.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Newobj, ctor); + il.Emit(OpCodes.Ret); + factoryMethod = (CreateControlCollectionDelegate)dm.CreateDelegate(typeof(CreateControlCollectionDelegate)); + s_collectionFactoryCache[ownerType] = factoryMethod; + } + } + } + return factoryMethod; + } +#else + private class CreateControlCollectionWrapper + { + private IDynamicConstructor _ctor; + + public CreateControlCollectionWrapper(IDynamicConstructor ctor) + { + _ctor = ctor; + } + + public ControlCollection Create(Control owner) + { + return (ControlCollection) _ctor.Invoke(new object[] {owner}); + } + } + + private static CreateControlCollectionDelegate GetInterceptedCollectionFactory(Type controlType, Type controlCollectionType) + { + AssertUtils.State( typeof(Control).IsAssignableFrom(controlType), "controlType must be of type Control" ); + AssertUtils.State( typeof(ControlCollection).IsAssignableFrom(controlCollectionType), "controlCollectionType must be of type ControlCollection" ); + + CreateControlCollectionDelegate factoryMethod = (CreateControlCollectionDelegate)s_collectionFactoryCache[controlType]; + if (factoryMethod == null) + { + lock (s_collectionFactoryCache) + { + factoryMethod = (CreateControlCollectionDelegate)s_collectionFactoryCache[controlType]; + if (factoryMethod == null) + { + Type interceptedCollectionType = GetInterceptedCollectionType(controlCollectionType, new InjectDependenciesCallbackHandler(WebDependencyInjectionUtils.InjectDependenciesRecursive)); + + ConstructorInfo ctor = interceptedCollectionType.GetConstructor(new Type[] {typeof (Control)}); + IDynamicConstructor dynCtor = new SafeConstructor(ctor); + s_collectionFactoryCache[controlType] = new CreateControlCollectionDelegate(new CreateControlCollectionWrapper(dynCtor).Create); + } + } + } + return factoryMethod; + } +#endif + + private static Type GetInterceptedCollectionType(Type controlCollectionType, InjectDependenciesCallbackHandler staticCallback) + { + AssertUtils.State( typeof(ControlCollection).IsAssignableFrom(controlCollectionType), "controlCollectionType must be of type ControlCollection" ); + + Type interceptedCollectionType = (Type)s_interceptedCollectionTypeCache[controlCollectionType]; + if (interceptedCollectionType == null) + { + lock (s_interceptedCollectionTypeCache) + { + MethodInfo callbackMethod = staticCallback.Method; + AssertUtils.State(callbackMethod.IsStatic && callbackMethod.IsPublic, "staticCallback must be a public static method"); + interceptedCollectionType = (Type)s_interceptedCollectionTypeCache[controlCollectionType]; + if (interceptedCollectionType == null) + { + SupportsWebDependencyInjectionTypeBuilder builder = new SupportsWebDependencyInjectionTypeBuilder(controlCollectionType, s_collectionMethods, callbackMethod); + interceptedCollectionType = builder.BuildProxyType(); + s_interceptedCollectionTypeCache[controlCollectionType] = interceptedCollectionType; + } + } + } + return interceptedCollectionType; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/MimeMediaType.cs b/src/Spring/Spring.Web/Web/Support/MimeMediaType.cs new file mode 100644 index 00000000..5ee08d97 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/MimeMediaType.cs @@ -0,0 +1,244 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Util; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Represents a MIME media type as defined by http://www.iana.org/assignments/media-types/ + /// + /// Erich Eichinger + /// $Id: MimeMediaType.cs,v 1.1 2007/11/28 23:26:10 oakinger Exp $ + public class MimeMediaType + { + private static readonly ArrayList ContentTypes = new ArrayList( new string[] { + "application", "audio", "example", "image", "message", + "model", "multipart", "text", "video" + }); + + #region Predefined Instances + + /// + /// Predefines common application media types + /// + public sealed class Application + { + /// + /// Represents "application/octet-stream" + /// + public static readonly MimeMediaType Octet = Parse("application/octet-stream"); + /// + /// Represents "application/pdf" + /// + public static readonly MimeMediaType Pdf = Parse("application/pdf"); + /// + /// Represents "application/rtf" + /// + public static readonly MimeMediaType Rtf = Parse("application/rtf"); + /// + /// Represents "application/soap+xml" + /// + public static readonly MimeMediaType Soap = Parse("application/soap+xml"); + /// + /// Represents "application/zip" + /// + public static readonly MimeMediaType Zip = Parse("application/zip"); + } + + /// + /// Predefines common image media types + /// + public sealed class Image + { + /// + /// Represents "image/gif" + /// + public static readonly MimeMediaType Gif = Parse("image/gif"); + /// + /// Represents "image/jpeg" + /// + public static readonly MimeMediaType Jpeg = Parse("image/jpeg"); + /// + /// Represents "image/tiff" + /// + public static readonly MimeMediaType Tiff = Parse("image/tiff"); + } + + /// + /// Predefines common text media types + /// + public sealed class Text + { + /// + /// Represents "text/html" + /// + public static readonly MimeMediaType Html = Parse("text/html"); + /// + /// Represents "text/plain" + /// + public static readonly MimeMediaType Plain = Parse("text/plain"); + /// + /// Represents "text/richtext" + /// + public static readonly MimeMediaType RichText = Parse("text/richtext"); + /// + /// Represents "text/xml" + /// + public static readonly MimeMediaType Xml = Parse("text/xml"); + /// + /// Represents "text/javascript" + /// + public static readonly MimeMediaType Javascript = Parse("text/javascript"); + /// + /// Represents "text/css" + /// + public static readonly MimeMediaType Css = Parse("text/css"); + } + + #endregion + + #region Static Methods + + /// + /// Parses a string into a instance. + /// + /// a valid string representation of a mediaType + /// a new instance + public static MimeMediaType Parse(string mediaType) + { + AssertUtils.ArgumentNotNull(mediaType, "mediaType"); + + string[] parts = mediaType.Split('/'); + if (parts.Length > 2) + { + throw new ArgumentException("invalid mediaType"); + } + + if (parts.Length == 1) + { + return new MimeMediaType(parts[0]); + } + else + { + return new MimeMediaType(parts[0], parts[1]); + } + } + + #endregion Static Methods + + private readonly string _contentType; + private readonly string _subType; + + /// + /// Creates a new media type instance representing a generic "application/octet-stream" + /// + public MimeMediaType() + :this("application", "octet-stream") + { + } + + /// + /// Creates a new media type instance representing a generic content type + /// with an unspecified subtype (e.g. "text/*") + /// + public MimeMediaType(string contentType) + : this(contentType, "*") + { + } + + /// + /// Creates a new media type instance representing a particular media type + /// + public MimeMediaType(string contentType, string subType) + { + _contentType = contentType.ToLower(); + if (!ContentTypes.Contains(_contentType)) + { + throw new ArgumentException("unknown content type", "contentType"); + } + _subType = (StringUtils.HasText(subType)) ? subType.ToLower() : "*"; + } + + /// + /// Gets the content type of this media type instance + /// + public string ContentType + { + get { return _contentType; } + } + + /// + /// Gets the subtype of this media type instance + /// + public string SubType + { + get { return _subType; } + } + + /// + /// Returns a string representation of this instance. + /// + public override string ToString() + { + return string.Format("{0}/{1}", _contentType, _subType); + } + + /// + /// Compares this instance to another + /// + /// another instance + protected bool Equals(MimeMediaType other) + { + if (other == null) return false; + return Equals(_contentType, other._contentType) && Equals(_subType, other._subType); + } + + /// + ///Determines whether the specified is equal to the current . + /// + /// + ///true if the specified is equal to the current ; otherwise, false. + /// + ///The to compare with the current . + public override bool Equals(object obj) + { + if (ReferenceEquals(this, obj)) return true; + return Equals(obj as MimeMediaType); + } + + /// + ///Serves as a hash function for a particular type. is suitable for use in hashing algorithms and data structures like a hash table. + /// + /// + ///A hash code for the current . + /// + public override int GetHashCode() + { + return _contentType.GetHashCode() + 29*_subType.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs b/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs new file mode 100644 index 00000000..ae15ced7 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs @@ -0,0 +1,424 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Reflection; +using System.Security.Permissions; +using System.Web; +using System.Web.SessionState; +using System.Web.UI; +using Common.Logging; +using Spring.Collections; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Util; +using Spring.Web.Process; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Implementation of that retrieves + /// configured instances from Spring web application context. + /// + /// + /// + /// This handler factory uses the page name from the URL, without the extension, + /// to find the handler object in the Spring context. This means that the target object + /// definition doesn't need to resolve to an .aspx page -- it can be any valid + /// object that implements interface. + /// + /// + /// If the specified page is not found in the Spring application context, this + /// handler factory falls back to the standard ASP.NET behavior and tries + /// to find physical page with a given name. + /// + /// + /// In either case, handlers that implement + /// and will be provided with the references + /// to appropriate Spring.NET application context (based on the request URL) + /// and a that should be used to store the information + /// that needs to be shared by all instances of the handler. + /// + /// + /// Aleksandar Seovic + /// $Id: PageHandlerFactory.cs,v 1.41 2008/05/15 12:35:27 oakinger Exp $ + public class PageHandlerFactory : AbstractHandlerFactory + { + private readonly ILog Log = LogManager.GetLogger(typeof(PageHandlerFactory)); + + private readonly IDictionary pageHandlerWrappers = CollectionsUtil.CreateCaseInsensitiveHashtable(); + + /// + /// Retrieves instance of the configured page from Spring web application context, + /// or if page is not defined in Spring config file tries to find it using standard + /// ASP.Net mechanism. + /// + /// Current HttpContext + /// Type of HTTP request (GET, POST, etc.) + /// Requested page URL + /// Translated server path for the page + /// Instance of the IHttpHandler object that should be used to process request. + public override IHttpHandler GetHandler(HttpContext context, string requestType, string url, string physicalPath) + { + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); + + bool isDebug = Log.IsDebugEnabled; + + if (isDebug) Log.Debug(string.Format("GetHandler():resolving url '{0}'", url)); + + IHttpHandler pageHandlerWrapper; + + lock (pageHandlerWrappers.SyncRoot) + { + pageHandlerWrapper = (PageHandlerWrapper)pageHandlerWrappers[url]; + } + + if (pageHandlerWrapper != null) + { + if (isDebug) + { + Log.Debug(string.Format("GetHandler():resolved url '{0}' from reusable handler cache", url)); + } + } + else + { + IConfigurableApplicationContext appContext = + WebApplicationContext.GetContext(url) as IConfigurableApplicationContext; + + if (appContext == null) + { + throw new InvalidOperationException( + "Implementations of IApplicationContext must also implement IConfigurableApplicationContext"); + } + + string appRelativeVirtualPath = WebUtils.GetAppRelativePath(url); + NamedObjectDefinition namedPageDefinition = FindWebObjectDefinition(appRelativeVirtualPath, appContext.ObjectFactory); + + if (namedPageDefinition != null) + { + Type pageType = namedPageDefinition.ObjectDefinition.ObjectType; + if (typeof(IRequiresSessionState).IsAssignableFrom(pageType)) + { + pageHandlerWrapper = new SessionAwarePageHandlerWrapper(appContext, namedPageDefinition.Name, url, null); + } + else + { + pageHandlerWrapper = new PageHandlerWrapper(appContext, namedPageDefinition.Name, url, null); + } + } + else + { + Type pageType = WebObjectUtils.GetCompiledPageType(url); + if (typeof(IRequiresSessionState).IsAssignableFrom(pageType)) + { + pageHandlerWrapper = new SessionAwarePageHandlerWrapper(appContext, appRelativeVirtualPath, url, physicalPath); + } + else + { + pageHandlerWrapper = new PageHandlerWrapper(appContext, appRelativeVirtualPath, url, physicalPath); + } + } + + if (pageHandlerWrapper.IsReusable) + { + lock (pageHandlerWrappers.SyncRoot) + { + pageHandlerWrappers[url] = pageHandlerWrapper; + } + } + } + + return pageHandlerWrapper; + } + } + + /// + /// Wrapper for handlers that do not require . + /// + /// + /// NOTE: This class has to extend System.Web.UI.Page instead of simply + /// implementing IHttpHandler in order for Server.Transfer to work properly. + /// This in turn requires explicit IHttpHandler implementation in order to + /// override non-virtual methods from the base Page class. + /// + internal class PageHandlerWrapper : Page, IHttpHandler + { +#if NET_2_0 + private static readonly FieldInfo fiHttpContext_CurrentHandler = + typeof(HttpContext).GetField("_currentHandler", BindingFlags.NonPublic | BindingFlags.Instance); + private static readonly MethodInfo miPage_SetPreviousPage = + typeof(System.Web.UI.Page).GetMethod("SetPreviousPage", BindingFlags.NonPublic | BindingFlags.Instance); +#endif + + private readonly IApplicationContext appContext; + private readonly string pageId; + private readonly string url; + private readonly string path; + + // cache handler if IsReusable == true + // since we don't use sync, make it volatile + private volatile IHttpHandler cachedHandler; + + // holds shared state for handlerType + private Type handlerType; + private IDictionary handlerState; + + private readonly object syncRoot = new object(); + + /// + /// Initializes a new instance of the class. + /// + /// Application context instance to retrieve page from. + /// Name of the page object to execute. + /// Requested page URL. + /// Translated server path for the page. + public PageHandlerWrapper(IApplicationContext appContext, string pageName, string url, string path) + { + this.appContext = appContext; + this.pageId = pageName; + this.url = url; + this.path = path; + } + + /// + /// Initializes a new instance of the class. + /// + /// Application context instance to retrieve page from. + /// Name of the page object to execute. + public PageHandlerWrapper(IApplicationContext appContext, string pageName) + : this(appContext, pageName, null, null) + { + } + + #region Properties + + /// + /// Use for sync access to this PageHandler instance. + /// + public object SyncRoot + { + get { return syncRoot; } + } + + /// + /// Gets that contains handler state. + /// + /// + /// This will be assigned to the SharedState + /// property of instances that implement + /// interface. + /// + public IDictionary HandlerState + { + get { return handlerState; } + } + + #endregion + + void IHttpHandler.ProcessRequest(HttpContext context) + { + IHttpHandler handler = cachedHandler; + + if (handler == null) + { + if (path != null) + { + handler = CreatePageInstance(); + } + else + { + handler = GetOrCreateProcessHandler(context); + } + + // note, that we don't care about sync here. The last call wins (it's the most current handler instance anyway) + if (handler.IsReusable) cachedHandler = handler; + } + + // replace handler proxy on context with "real" handler + if (this == context.Handler) + { + context.Handler = handler; + } + +#if NET_2_0 + // this may happen under load, if GetHandler() + // and ProcessRequest() are executed under different threads + // fix this... + if (this == context.CurrentHandler) + { + fiHttpContext_CurrentHandler.SetValue(context, handler); + } + + if (handler is System.Web.UI.Page) + { + System.Web.UI.Page page = (Page) handler; + + // During Server.Transfer/Execute() the PreviousPage property gets set + if (this.PreviousPage != null) + { + miPage_SetPreviousPage.Invoke(page, new object[] { this.PreviousPage }); + } + } +#endif + + ApplySharedState(handler); + ApplyDependencyInjection(handler); + + handler.ProcessRequest(context); + } + + /// + /// Returns true because this wrapper handler can be reused. + /// Actual page is instantiated at the beginning of the ProcessRequest method. + /// + bool IHttpHandler.IsReusable + { + get { return true; } + } + + /// + /// Creates a page instance corresponding to this handler's url. + /// + private IHttpHandler CreatePageInstance() + { + IHttpHandler handler; + handler = WebObjectUtils.CreatePageInstance(url); + if (handler is IApplicationContextAware) + { + ((IApplicationContextAware)handler).ApplicationContext = appContext; + } + return handler; + } + + /// + /// Gets or - if not found - creates a process handler instance. + /// + private IHttpHandler GetOrCreateProcessHandler(HttpContext context) + { + IHttpHandler handler = null; + string processId = context.Request[AbstractProcessHandler.ProcessIdParamName]; + if (processId != null) + { + handler = (IHttpHandler)ProcessManager.GetProcess(processId); + } + + if (handler == null) + { + handler = (IHttpHandler)this.appContext.GetObject(this.pageId); + if (handler is IProcess) + { + ((IProcess)handler).Start(url); + } + } + return handler; + } + + /// + /// Apply dependency injection stuff on the handler. + /// + /// + private void ApplyDependencyInjection(IHttpHandler handler) + { + if (handler is Control) + { + ControlInterceptor.EnsureControlIntercepted(appContext, (Control)handler); + } + else + { + if (handler is ISupportsWebDependencyInjection) + { + ((ISupportsWebDependencyInjection)handler).DefaultApplicationContext = appContext; + } + } + } + + /// + /// Applies to the given handler if applicable. + /// + private void ApplySharedState(IHttpHandler handler) + { + if (handler is ISharedStateAware) + { + CheckIfPageWasRecompiled(handler); + ((ISharedStateAware)handler).SharedState = this.handlerState; + } + } + + /// + /// Checks, if page has been recompiled. Creates/discards handlerState if necessary. + /// + /// + private void CheckIfPageWasRecompiled(IHttpHandler handler) + { + if (handlerType != handler.GetType()) + { + lock (SyncRoot) + { + if (handlerType != handler.GetType()) + { + // discard old handlerState and cache new pagetype + handlerState = new SynchronizedHashtable(); + handlerType = handler.GetType(); + } + } + } + } + } + + /// + /// Wrapper for handlers that require . + /// + /// + /// Delays page object instantiation until ProcessRequest is called + /// in order to be able to access session state. + /// + internal class SessionAwarePageHandlerWrapper : PageHandlerWrapper, IRequiresSessionState + { + /// + /// Initializes a new instance of the class. + /// + /// Application context instance to retrieve page from. + /// Name of the page object to execute. + /// Requested page URL. + /// Translated server path for the page. + public SessionAwarePageHandlerWrapper(IApplicationContext appContext, string pageName, string url, string path) + : base(appContext, pageName, url, path) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Application context instance to retrieve page from. + /// Name of the page object to execute. + public SessionAwarePageHandlerWrapper(IApplicationContext appContext, string pageName) + : base(appContext, pageName) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/Result.cs b/src/Spring/Spring.Web/Web/Support/Result.cs new file mode 100644 index 00000000..996f7d6c --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/Result.cs @@ -0,0 +1,393 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Text; +using System.Web; +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Represents the ASPX result page that an operation maps to. + /// + /// + ///

    + /// Parameters can reference variables accessible from the page using the + /// standard NAnt style property notation, namely ${varName}. + ///

    + ///

    + /// The result that is passed as the sole + /// parameter to the parameterized constructor + /// () must adhere to the + /// following format: + ///

    + /// + /// [<mode>:]<targetPage>[?param1,param2,...,paramN] + /// + ///

    + /// Only the targetPage is mandatory. + ///

    + ///
    + /// + ///

    + /// Examples of valid values that can be passed + /// to the the parameterized constructor + /// () include: + ///

    + ///

    + /// + /// Login.aspx + /// ~/Login.aspx + /// redirect:~/Login.aspx + /// transfer:~/Login.aspx + /// transfer:Services/Register.aspx + /// Login.aspx?username=springboy,password=7623AAjoe + /// redirect:Login.aspx?username=springboy,password=7623AAjoe + /// + ///

    + ///
    + /// Aleksandar Seovic + /// Matan Shapira + /// $Id: Result.cs,v 1.13 2008/02/02 09:12:44 oakinger Exp $ + [Serializable] + public class Result + { + #region Constants + + /// + /// The default . + /// + /// + ///

    + /// Currently defaults to . + ///

    + ///
    + /// + public const ResultMode DefaultResultMode = ResultMode.Transfer; + + #endregion + + #region Fields + + private ResultMode mode = DefaultResultMode; + private string targetPage; + private IDictionary parameters; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the class. + /// + public Result() + { } + + /// + /// Creates a new instance of the class. + /// + /// + ///

    + /// See both the class documentation () + /// and the reference documentation for the Spring.Web library for a + /// discussion and examples of what values the supplied + /// can have. + ///

    + ///
    + /// The result descriptor. + /// + /// If the supplied is or + /// contains only whitespace character(s). + /// + public Result(string result) + { + AssertUtils.ArgumentHasText(result, "result"); + result = ExtractAndSetResultMode(result); + int indexOfQueryStringDelimiter = result.IndexOf('?'); + if (indexOfQueryStringDelimiter > 0) + { + ParseParameters(result.Substring(indexOfQueryStringDelimiter + 1)); + result = result.Substring(0, indexOfQueryStringDelimiter); + } + targetPage = result.Trim(); + } + + #endregion + + #region Properties + + /// + /// The . Defines which + /// method will be used to navigate to the + /// . + /// + public ResultMode Mode + { + get { return mode; } + set { mode = value; } + } + + /// + /// The target page. + /// + /// + ///

    + /// This is the (relative) path to the target page. + ///

    + ///
    + /// + ///

    + /// Examples of valid values would be: + ///

    + ///

    + /// + /// Login.aspx + /// ~/Login.aspx + /// ~/B2B/SignUp.aspx + /// B2B/Foo/FooServices.aspx + /// + ///

    + ///
    + public string TargetPage + { + get { return targetPage; } + set { targetPage = value; } + } + + /// + /// The parameters thar are to be passed to the + /// upon + /// navigation. + /// + public IDictionary Parameters + { + get { return parameters; } + set { parameters = value; } + } + + /// + /// Indicates, if should be called with preserverForm='true' | 'false'. Only relevant for ResultMode.TransferXXXX modes. + /// + public bool PreserveForm + { + get { return (Mode == ResultMode.Transfer); } + } + + #endregion + + /// + /// Navigates to the + /// defined by this result. + /// + /// + /// The context object for parameter resolution. This is typically + /// a . + /// + public virtual void Navigate(object page) + { + switch (Mode) + { + case ResultMode.Redirect: + DoRedirect(page); + break; + case ResultMode.Transfer: + case ResultMode.TransferNoPreserve: + DoTransfer(page); + break; + default: + throw new ArgumentOutOfRangeException(string.Format("Unknown ResultMode {0}", Mode)); + } + } + + /// + /// Performs a server-side transfer to the + /// defined by this result. + /// + /// + /// The context object for parameter resolution. This is typically + /// a . + /// + /// + protected virtual void DoTransfer(object page) + { + HttpContext ctx = HttpContext.Current; + SetTransferParameters(ctx.Items, page); + ctx.Server.Transfer(TargetPage, PreserveForm); + } + + /// + /// Resolves transfer parameters and stores them into instance. + /// + /// + /// + protected void SetTransferParameters(IDictionary contextDictionary, object page) + { + if (this.parameters != null && this.parameters.Count > 0) + { + foreach (DictionaryEntry entry in this.parameters) + { + string value = entry.Value.ToString(); + if (IsRuntimeExpression(value)) + { + contextDictionary[entry.Key] = ResolveRuntimeExpression(page, value); + } + else + { + contextDictionary[entry.Key] = entry.Value; + } + } + } + } + + /// + /// Performs a redirect to the + /// defined by this + /// result. + /// + /// + /// The context object for parameter resolution. This is typically + /// a . + /// + /// + protected virtual void DoRedirect(object page) + { + HttpContext.Current.Response.Redirect(GetRedirectUri(page)); + } + + /// + /// Returns a redirect url string that points to the + /// defined by this + /// result. + /// + /// + /// A redirect url string. + /// + public string GetRedirectUri(object page) + { + StringBuilder url = new StringBuilder(256); + url.Append(TargetPage); + if (parameters != null && parameters.Count > 0) + { + char separator = '?'; + foreach (DictionaryEntry entry in parameters) + { + url.Append(separator); + url.Append(entry.Key).Append('='); + string value = entry.Value.ToString(); + if (IsRuntimeExpression(value)) + { + url.Append(HttpContext.Current.Server.UrlEncode( + ResolveRuntimeExpression(page, value).ToString())); + } + else + { + url.Append(HttpContext.Current.Server.UrlEncode(value)); + } + separator = '&'; + } + } + return url.ToString(); + } + + private static bool IsRuntimeExpression(string value) + { + // allow for 2 alternative prefixes (SPRNET-864) + return (value.StartsWith("${") || value.StartsWith("%{")) && value.EndsWith("}"); + } + + private static object ResolveRuntimeExpression(object page, string value) + { + return ExpressionEvaluator.GetValue(page, value.Substring(2, value.Length - 3)); + } + + /// + /// Extracts and sets this instance's + /// property from the supplied descriptor. + /// + /// + ///

    + /// The supplied descriptor is typically + /// something like /Foo.aspx or + /// redirect:http://www.springframework.net/. + ///

    + ///
    + /// + /// The result descriptor. + /// + /// + /// The supplied without the result mode + /// prefix (if any). + /// + /// + /// If the supplied starts with an illegal + /// result mode (see ). + /// + private string ExtractAndSetResultMode(string result) + { + int indexOfResultModeDelimiter = result.IndexOf(':'); + if (indexOfResultModeDelimiter > 0) + { + try + { + Mode = (ResultMode)Enum.Parse(typeof(ResultMode), + result.Substring(0, indexOfResultModeDelimiter), true); + return result.Substring(indexOfResultModeDelimiter + 1); + } + catch + { + throw new ArgumentOutOfRangeException("result", result, "Illegal result mode."); + } + } + return result; + } + + /// + /// Parses query parameters from the supplied . + /// + /// + /// The query string (may be ). + /// + private void ParseParameters(string queryString) + { + if (StringUtils.HasText(queryString)) + { + this.parameters = new Hashtable(); + string[] nameValuePairs = queryString.Split("&,".ToCharArray()); + foreach (string pair in nameValuePairs) + { + int n = pair.IndexOf('='); + if (n > 0) + { + string name = pair.Substring(0, n); + string val = pair.Substring(n + 1).Trim(); + this.parameters[name] = val; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/ResultMode.cs b/src/Spring/Spring.Web/Web/Support/ResultMode.cs new file mode 100644 index 00000000..6c0aca1e --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/ResultMode.cs @@ -0,0 +1,74 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// The various result modes. + /// + /// Aleksandar Seovic + /// $Id: ResultMode.cs,v 1.3 2007/04/19 07:49:01 oakinger Exp $ + /// + [Serializable] + public enum ResultMode + { + /// + /// A server-side transfer. + /// + /// + ///

    + /// Issues a server-side transfer using the + /// method. + ///

    + ///
    + /// + Transfer = 0, + + /// + /// A redirect. + /// + /// + ///

    + /// Issues a redirect (to the user-agent - typically a browser) using + /// the method. + ///

    + ///
    + /// + Redirect = 1, + + /// + /// A server-side transfer. + /// + /// + ///

    + /// Issues a server-side transfer using the + /// method with parameter 'preserveForm=false'. + ///

    + ///
    + /// + TransferNoPreserve = 2 + } +} diff --git a/src/Spring/Spring.Web/Web/Support/Script.cs b/src/Spring/Spring.Web/Web/Support/Script.cs new file mode 100644 index 00000000..6798eb2a --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/Script.cs @@ -0,0 +1,235 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using Spring.Util; + +namespace Spring.Web.Support +{ + /// + /// Base class that describes client side script block or file. + /// + /// + ///

    + /// Classes that extend this class are used by Spring.Web client-side + /// scripting support. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: Script.cs,v 1.4 2007/11/28 23:26:10 oakinger Exp $ + internal abstract class Script + { + internal static readonly MimeMediaType DefaultType = MimeMediaType.Text.Javascript; + + private string language; + private MimeMediaType type; + + /// + /// Initialize a new Script object of the specified language + /// + /// Script language. + public Script(string language) + { + this.language = language; + } + + /// + /// Initialize a new script object of the specified type + /// + /// a + public Script(MimeMediaType type) + { + this.Type = type; + } + + /// + /// Gets or sets script language. + /// + public string Language + { + get { return language; } + set { language = value; } + } + + /// + /// Gets or sets script mime type + /// + public MimeMediaType Type + { + get { return type; } + set { AssertUtils.ArgumentNotNull(value, "Type"); type = value; } + } + } + + /// + /// Class that describes client side script block. + /// + /// + ///

    + /// Script blocks are used to insert script code directly into the page, + /// without references to an external file. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: Script.cs,v 1.4 2007/11/28 23:26:10 oakinger Exp $ + internal class ScriptBlock : Script + { + private string script; + + /// + /// Default constructor. + /// + /// Script language. + /// The script text. + public ScriptBlock(string language, string script) : base(language) + { + this.script = script; + } + + /// + /// Initialize a new script block instance. + /// + /// the script language's + /// the script body + public ScriptBlock(MimeMediaType type, string script) : base(type) + { + this.script = script; + } + + /// + /// Gets or sets the script text. + /// + /// The script text. + public string Script + { + get { return script; } + set { script = value; } + } + } + + /// + /// Class that describes client side script file. + /// + /// + ///

    + /// Script file references script code in the external file. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: Script.cs,v 1.4 2007/11/28 23:26:10 oakinger Exp $ + internal class ScriptFile : Script + { + private string fileName; + + /// + /// Initialize a new instance. + /// + /// Script language. + /// The name of the script file. + public ScriptFile(string language, string fileName) : base(language) + { + this.fileName = fileName; + } + + /// + /// Initialize a new instance. + /// + /// the script language's + /// the (virtual) path to the script + public ScriptFile(MimeMediaType type, string fileName) + : base(type) + { + this.fileName = fileName; + } + + /// + /// Gets or sets the name of the script file. + /// + /// The name of the script file. + public string FileName + { + get { return fileName; } + set { fileName = value; } + } + } + + /// + /// Class that describes client side script file. + /// + /// + ///

    + /// Script file references script code in the external file. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: Script.cs,v 1.4 2007/11/28 23:26:10 oakinger Exp $ + internal class ScriptEvent : ScriptBlock + { + private string element; + private string eventName; + + /// + /// Initialize a new instance. + /// + /// Script language. + /// Element ID of the event source. + /// Name of the event to handle. + /// Script text. + public ScriptEvent(string language, string element, string eventName, string script) : base(language, script) + { + this.element = element; + this.eventName = eventName; + } + + /// + /// Initialize a new instance. + /// + /// the script language's + /// Element ID of the event source. + /// Name of the event to handle. + /// Script text. + public ScriptEvent(MimeMediaType type, string element, string eventName, string script) + : base(type, script) + { + this.element = element; + this.eventName = eventName; + } + + /// + /// Gets or sets the element ID. + /// + /// The element ID. + public string Element + { + get { return element; } + set { element = value; } + } + + /// + /// Gets or sets the name of the event. + /// + /// The name of the event. + public string EventName + { + get { return eventName; } + set { eventName = value; } + } + } + +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/SharedStateResourceCache.cs b/src/Spring/Spring.Web/Web/Support/SharedStateResourceCache.cs new file mode 100644 index 00000000..e9e633af --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/SharedStateResourceCache.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using Spring.Globalization; +using Spring.Util; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Resource cache implementation that uses Spring.NET page/handler state to cache resources. + /// + /// Aleksandar Seovic + /// $Id: SharedStateResourceCache.cs,v 1.1 2007/08/03 08:31:25 oakinger Exp $ + internal class SharedStateResourceCache : AbstractResourceCache + { + private readonly ISharedStateAware sharedStateHolder; + + public SharedStateResourceCache(ISharedStateAware sharedStateHolder) + { + AssertUtils.ArgumentNotNull(sharedStateHolder, "sharedStateHolder"); + this.sharedStateHolder = sharedStateHolder; + } + + /// + /// Gets the list of resources from cache. + /// + /// Cache key to use for lookup. + /// A list of cached resources for the specified target object and culture. + protected override IList GetResources(string cacheKey) + { + return (IList) this.sharedStateHolder.SharedState[cacheKey]; + } + + /// + /// Puts the list of resources in the cache. + /// + /// Cache key to use for the specified resources. + /// A list of resources to cache. + protected override void PutResources(string cacheKey, IList resources) + { + this.sharedStateHolder.SharedState[cacheKey] = resources; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/SupportsWebDependencyInjectionMethodBuilder.cs b/src/Spring/Spring.Web/Web/Support/SupportsWebDependencyInjectionMethodBuilder.cs new file mode 100644 index 00000000..c8fa570c --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/SupportsWebDependencyInjectionMethodBuilder.cs @@ -0,0 +1,94 @@ +#region License +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using System.Reflection; +using System.Reflection.Emit; +using Spring.Proxy; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// This MethodBuilder emits a Callback-call before calling the base method + /// + /// Erich Eichinger + /// $Id: SupportsWebDependencyInjectionMethodBuilder.cs,v 1.1 2007/08/01 23:11:01 markpollack Exp $ + internal class SupportsWebDependencyInjectionMethodBuilder : BaseProxyMethodBuilder + { + private FieldInfo _appContextField; + private MethodInfo _callbackMethod; + + /// + /// Initializes a new instance of a SupportsWebDependencyInjectionMethodBuilder + /// + public SupportsWebDependencyInjectionMethodBuilder(TypeBuilder typeBuilder, IProxyTypeGenerator proxyGenerator, + FieldInfo appContextField, MethodInfo callbackMethod) + : base(typeBuilder, proxyGenerator, true) + { + _appContextField = appContextField; + _callbackMethod = callbackMethod; + } + + /// + /// Inserts a call to a callback-method before actually calling the base-method. + /// + /// The IL generator to use + /// The method to proxy + /// The interface definition of this method, if applicable + protected override void GenerateMethod(ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + this.GenerateCallbackMethodCall(il, method, interfaceMethod); + base.GenerateMethod(il, method, interfaceMethod); + } + + /// + /// Emits the callback invocation. + /// + private void GenerateCallbackMethodCall(ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + // setup parameters for call + + // IApplicationContext is always first parameter! + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, _appContextField); + + // lookup parameters needed for callback - they are matched by type in order! + ParameterInfo[] callbackParams = _callbackMethod.GetParameters(); + ParameterInfo[] paramArray = method.GetParameters(); + + for (int j = 1; j < callbackParams.Length; j++) + { + for (int i = 0; i < paramArray.Length; i++) + { + if (paramArray[i].ParameterType == callbackParams[j].ParameterType) + { + il.Emit(OpCodes.Ldarg_S, i + 1); + break; + } + } + } + + // invoke static(!) callback + il.EmitCall(OpCodes.Call, _callbackMethod, null); + return; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/SupportsWebDependencyInjectionOwnerProxy.cs b/src/Spring/Spring.Web/Web/Support/SupportsWebDependencyInjectionOwnerProxy.cs new file mode 100644 index 00000000..6711a42d --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/SupportsWebDependencyInjectionOwnerProxy.cs @@ -0,0 +1,81 @@ +#region License +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using System.Web.UI; +using Spring.Context; +using Spring.Context.Support; +using Spring.Util; +using Spring.Web.Support; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Wraps a Control to make it DI aware + /// + /// Erich Eichinger + /// $Id: SupportsWebDependencyInjectionOwnerProxy.cs,v 1.2 2008/05/13 14:22:47 oakinger Exp $ + internal class SupportsWebDependencyInjectionOwnerProxy : Control, ISupportsWebDependencyInjection + { + // Control Fields and Methods + private readonly ControlAccessor _targetControl; + private IApplicationContext _defaultApplicationContext; + + /// + /// Wraps a control to make it DI aware + /// + /// + /// + public SupportsWebDependencyInjectionOwnerProxy(IApplicationContext defaultApplicationContext, Control targetControl) + { + _defaultApplicationContext = defaultApplicationContext; + _targetControl = new ControlAccessor(targetControl); + } + + public IApplicationContext DefaultApplicationContext + { + get { return this._defaultApplicationContext; } + set { this._defaultApplicationContext = value; } + } + + /// + /// Performs DI before adding the control to it's parent + /// + /// + /// + protected override void AddedControl(Control control, int index) + { + // do DI + WebDependencyInjectionUtils.InjectDependenciesRecursive(_defaultApplicationContext, control); + + _targetControl.AddedControl(control, index); + } + + /// + /// Delegates call to decorated control + /// + /// + protected override void RemovedControl(Control control) + { + _targetControl.RemovedControl(control); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/SupportsWebDependencyInjectionTypeBuilder.cs b/src/Spring/Spring.Web/Web/Support/SupportsWebDependencyInjectionTypeBuilder.cs new file mode 100644 index 00000000..e929a85e --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/SupportsWebDependencyInjectionTypeBuilder.cs @@ -0,0 +1,197 @@ +#region License +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Reflection.Emit; + +using Spring.Context; +using Spring.Proxy; +using Spring.Web.Support; +using Spring.Util; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// This TypeBuilder dynamically implements the contract on a given type. + /// + /// Erich Eichinger + /// $Id: SupportsWebDependencyInjectionTypeBuilder.cs,v 1.1 2007/08/01 23:11:01 markpollack Exp $ + internal class SupportsWebDependencyInjectionTypeBuilder : InheritanceProxyTypeBuilder + { + private const string PROXY_TYPE_NAME = "SupportsWebDependencyInjectionControlProxy"; + + private MethodInfo[] _methodsToIntercept; + private MethodInfo _staticCallbackMethod; + + /// + /// Creates a new TypeBuilder instance. + /// + /// The base type the proxy shall be derived from + /// The methods to be injected with a call to staticCallbackMethod + /// The static callback method to be injected into methodsToIntercept + public SupportsWebDependencyInjectionTypeBuilder(Type targetType, MethodInfo[] methodsToIntercept, MethodInfo staticCallbackMethod) + { + Name = PROXY_TYPE_NAME; + + base.TargetType = targetType; + + if (methodsToIntercept == null || methodsToIntercept.Length == 0) + { + throw new ArgumentException("No methods specified to be intercepted"); + } + _methodsToIntercept = methodsToIntercept; + + if (!staticCallbackMethod.IsStatic) + { + throw new ArgumentException("CallbackMethod must be static"); + } + _staticCallbackMethod = staticCallbackMethod; + } + + /// + /// Creates a proxy that inherits the proxied object's class. + /// + /// + /// The generated proxy type. + public override Type BuildProxyType() + { + BaseType = TargetType; + if (BaseType.IsSealed || + !ReflectionUtils.IsTypeVisible(BaseType, DynamicProxyManager.ASSEMBLY_NAME)) + { + throw new ArgumentException("Inheritance proxy cannot be created for a sealed or non-public class [" + + BaseType.FullName + "]"); + } + + TypeBuilder typeBuilder = CreateTypeBuilder(Name, BaseType); + + // declare field to hold reference to applicationContext + FieldBuilder appContextField = DeclareApplicationContextInstanceField(typeBuilder); + + // create constructors + ImplementConstructors(typeBuilder); + + // Implement IDependencyInjectionAware if necessary + if (!typeof (ISupportsWebDependencyInjection).IsAssignableFrom(BaseType)) + { + ImplementIDependencyInjectionAware(typeBuilder, BaseType, appContextField); + } + + // proxy base virtual methods + BaseProxyMethodBuilder proxyMethodBuilder = + new SupportsWebDependencyInjectionMethodBuilder(typeBuilder, this, appContextField, _staticCallbackMethod); + foreach (MethodInfo methodToIntercept in _methodsToIntercept) + { + proxyMethodBuilder.BuildProxyMethod(methodToIntercept, null); + } + + return typeBuilder.CreateType(); + } + + /// + /// Actually implements the interface. + /// + private void ImplementIDependencyInjectionAware(TypeBuilder typeBuilder, Type targetType, FieldInfo appContextField) + { + Type intf = typeof (ISupportsWebDependencyInjection); + + // Add interface declaration to type + typeBuilder.AddInterfaceImplementation(intf); + + // get interface property + PropertyInfo piApplicationContext = intf.GetProperty("DefaultApplicationContext"); + + // define property + string fullPropertyName = typeof (ISupportsWebDependencyInjection).FullName + "." + piApplicationContext.Name; + PropertyBuilder appContextProperty = + typeBuilder.DefineProperty(fullPropertyName, PropertyAttributes.None, typeof (IApplicationContext), null); + + MethodAttributes methodAtts = MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | + MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final; + + // implement getter + MethodInfo getApplicationContextMethod = piApplicationContext.GetGetMethod(); + string getterMethodName = typeof (ISupportsWebDependencyInjection).FullName + "." + getApplicationContextMethod.Name; + MethodBuilder mbGet = + typeBuilder.DefineMethod(getterMethodName, + methodAtts, + getApplicationContextMethod.CallingConvention, getApplicationContextMethod.ReturnType, + Type.EmptyTypes); + + + ILGenerator ilGet = mbGet.GetILGenerator(); + ilGet.Emit(OpCodes.Ldarg_0); + ilGet.Emit(OpCodes.Ldfld, appContextField); + ilGet.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(mbGet, getApplicationContextMethod); + appContextProperty.SetGetMethod(mbGet); + + // implement setter + MethodInfo setApplicationContextMethod = piApplicationContext.GetSetMethod(); + string setterMethodName = typeof (ISupportsWebDependencyInjection).FullName + "." + setApplicationContextMethod.Name; + MethodBuilder mbSet = typeBuilder.DefineMethod(setterMethodName, methodAtts, + setApplicationContextMethod.CallingConvention, + setApplicationContextMethod.ReturnType, + new Type[] {typeof (IApplicationContext)}); + + ILGenerator ilSet = mbSet.GetILGenerator(); + ilSet.Emit(OpCodes.Ldarg_0); + ilSet.Emit(OpCodes.Ldarg_1); + ilSet.Emit(OpCodes.Stfld, appContextField); + ilSet.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(mbSet, setApplicationContextMethod); + appContextProperty.SetSetMethod(mbSet); + } + + /// + /// Declares field that holds the . + /// + private FieldBuilder DeclareApplicationContextInstanceField(TypeBuilder builder) + { + FieldBuilder applicationContextField; + applicationContextField = + builder.DefineField("_defaultApplicationContext", typeof (IApplicationContext), FieldAttributes.Private); + return applicationContextField; + } + + #region Public Methods + + /// + /// Determines if the specified + /// is one of those generated by this builder. + /// + /// The type to check. + /// + /// if the type is a SpringAwareControl-based proxy; + /// otherwise . + /// + public static bool IsSpringAwareControlProxy(Type type) + { + return type.FullName.StartsWith(PROXY_TYPE_NAME); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/WebDependencyInjectionUtils.cs b/src/Spring/Spring.Web/Web/Support/WebDependencyInjectionUtils.cs new file mode 100644 index 00000000..22a70d26 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/WebDependencyInjectionUtils.cs @@ -0,0 +1,117 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Web.UI; +using Spring.Context; +using Spring.Context.Support; +using Spring.Util; + +namespace Spring.Web.Support +{ + /// + /// This is + /// + /// + /// + /// + /// Mark Pollack + /// $Id: WebDependencyInjectionUtils.cs,v 1.3 2008/05/13 14:22:47 oakinger Exp $ + public class WebDependencyInjectionUtils + { + /// + /// Injects dependencies into all controls in the hierarchy + /// based on template definitions in the Spring config file. + /// + /// ApplicationContext to be used + /// Control to inject dependencies into. + public static void InjectDependenciesRecursive(IApplicationContext applicationContext, Control control) + { + if (applicationContext != null) + { + InjectDependenciesRecursiveInternal(applicationContext, control); + } + } + + private static void InjectDependenciesRecursiveInternal(IApplicationContext appContext, Control control) + { + if (control is LiteralControl) return; // nothing to do + + ISupportsWebDependencyInjection diControl = control as ISupportsWebDependencyInjection; + if (diControl != null && diControl.DefaultApplicationContext != null) + { + return; // nothing to do anymore - control cares for its children + } + + // "intercept" Control to make it DI-aware + ControlInterceptor.EnsureControlIntercepted(appContext, control); + + // if the control is a UserControl, use ApplicationContext from it's physical location + IApplicationContext appContextToUse = appContext; + UserControl userControl = control as UserControl; + if (userControl != null) + { + appContextToUse = GetControlApplicationContext(appContext, userControl); + } + + // set ApplicationContext instance + if (control is IApplicationContextAware) + { + ((IApplicationContextAware)control).ApplicationContext = appContextToUse; + } + + // inject dependencies using control's context + appContextToUse.ConfigureObject(control, control.GetType().FullName); + + // and now go for control's children + if (control.HasControls()) + { + ControlCollection childControls = control.Controls; + int childCount = childControls.Count; + for (int i = 0; i < childCount; i++) + { + Control c = childControls[i]; + if (!(c is LiteralControl)) + { + InjectDependenciesRecursiveInternal(appContext, c); + } + } + } + } + + /// + /// Aquires an ApplicationContext according to a Control's TemplateSourceDirectory + /// + /// if availabe, the control's IApplicationContext instance - defaultContext otherwise + private static IApplicationContext GetControlApplicationContext(IApplicationContext defaultContext, Control control) + { + // use control's directory for resolving context + string srcDir = control.TemplateSourceDirectory; + if (StringUtils.HasLength(srcDir)) + { + if (!srcDir.EndsWith("/")) srcDir += "/"; + return WebApplicationContext.GetContext(srcDir); + } + else + { + return defaultContext; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/AbstractWizard.cs b/src/Spring/Spring.Web/Web/UI/AbstractWizard.cs new file mode 100644 index 00000000..e12c2a44 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/AbstractWizard.cs @@ -0,0 +1,190 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Web.UI; +using System.Web.UI.WebControls; + +namespace Spring.Web.UI +{ + /// + /// Convinience implementation of the wizard-like page controller. + /// + /// + ///

    + /// Wizard steps are encapsulated within custom user controls. Wizard + /// controller takes care of navigation through steps and loading of the + /// appropriate user control. + ///

    + ///

    + /// Developer implementing wizard needs to inherit from this class and implement + /// abstract, read-only StepPanel property that will be used as a container + /// for the wizard steps. Navigation event handlers should call Previous and Next methods + /// from this class to change current step. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: AbstractWizard.cs,v 1.7 2006/05/18 21:37:52 markpollack Exp $ + public abstract class AbstractWizard : Page + { + private IList steps; + + /// + /// Gets or sets a list of user controls representing wizard steps. + /// + public IList Steps + { + get { return steps; } + set { steps = value; } + } + + #region Wizard navigation members + + /// + /// Gets or sets current step using step index. + /// + public int CurrentStep + { + get + { + if (ViewState["__wizard.CurrentStep"] == null) + { + ViewState["__wizard.CurrentStep"] = 0; + } + return (int) ViewState["__wizard.CurrentStep"]; + } + set { ViewState["__wizard.CurrentStep"] = value; } + } + + /// + /// Returns true if there are no steps before the current step. + /// + public bool IsFirst + { + get { return CurrentStep == 0; } + } + + /// + /// Returns true if there are no steps after the current step. + /// + public bool IsLast + { + get { return CurrentStep == Steps.Count - 1; } + } + + /// + /// Moves to the previous step in the list, if one exists. + /// + public void Previous() + { + if (!IsFirst) + { + CurrentStep--; + } + } + + /// + /// Moves to the next step in the list, if one exists. + /// + public void Next() + { + if (!IsLast) + { + CurrentStep++; + } + } + + #endregion + + #region Page lifecycle methods + + /// + /// Initializes wizard steps. + /// + /// Event arguments. + protected override void OnInit(EventArgs e) + { + InitializeSteps(steps); + base.OnInit(e); + } + + /// + /// + /// + /// + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + } + + /// + /// Loads new step into a step panel if step has changed. + /// + /// Event arguments. + protected override void OnPreRender(EventArgs e) + { + for (int i = 0; i < StepPanel.Controls.Count; i++) + { + StepPanel.Controls[i].Visible = (i == CurrentStep); + } + base.OnPreRender(e); + } + + /// + /// Initializes all the steps. + /// + /// List of step control names. + /// List of step control instances. + private void InitializeSteps(IList stepNames) + { + foreach (string stepName in stepNames) + { + Control step = LoadStep(stepName); + StepPanel.Controls.Add(step); + } + } + + /// + /// Loads step control. + /// + private Control LoadStep(string stepControlName) + { + Control step = LoadControl(stepControlName); + + int start = stepControlName.LastIndexOf('/'); + int end = stepControlName.LastIndexOf('.'); + step.ID = stepControlName.Substring(start + 1, end - start - 1); + + return step; + } + + #endregion + + #region Abstract members + + /// + /// Panel that should serve as a container for wizard steps. + /// + protected abstract Panel StepPanel { get; } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/AbstractBaseValidator.cs b/src/Spring/Spring.Web/Web/UI/Controls/AbstractBaseValidator.cs new file mode 100644 index 00000000..8d7724c0 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/AbstractBaseValidator.cs @@ -0,0 +1,133 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Import + +using System; +using System.Reflection; +using System.Web.UI; +using System.Web.UI.WebControls; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// Provides functions required for implementing validators + /// but are unfortunately not accessible from + /// + /// Erich Eichinger + /// $Id: AbstractBaseValidator.cs,v 1.1 2007/07/31 10:40:58 oakinger Exp $ + public abstract class AbstractBaseValidator : System.Web.UI.WebControls.BaseValidator + { + /// + /// Registers a javascript-block to be rendered. + /// + protected void RegisterClientScriptBlock(Type type, string key, string script) + { +#if NET_2_0 + this.Page.ClientScript.RegisterClientScriptBlock( + typeof(RequiredCheckBoxValidator) + , "RequiredCheckBoxValidatorEvaluateIsChecked" + , +@" + +" + ); +#else + key = type.FullName + "." + key; + this.Page.RegisterClientScriptBlock( + key + , + @" + +" + ); +#endif + } + + /// + /// Checks, if a certain javascript-block is already registered. + /// + protected bool IsClientScriptBlockRegistered(Type type, string key) + { +#if NET_2_0 + return this.Page.ClientScript.IsClientScriptBlockRegistered(type, key); +#else + return this.Page.IsClientScriptBlockRegistered( type.FullName + "." + key ); +#endif + } + + /// + /// Adds an attribute to be rendered for clientside validation. + /// + protected void AddExpandoAttribute(HtmlTextWriter writer, string controlId, string attributeName, string attributeValue, bool encode) + { +#if NET_2_0 + + typeof(System.Web.UI.WebControls.BaseValidator).InvokeMember("AddExpandoAttribute" + , + BindingFlags.InvokeMethod | BindingFlags.NonPublic + | BindingFlags.Instance + , null + , this + , new object[] {writer, controlId, attributeName, attributeValue, encode} + ); +#else + if (writer != null) + { + writer.AddAttribute(attributeName, attributeValue); + } +#endif + } + + /// + /// Is "XHTML 1.0 Transitional" rendering allowed? + /// + protected bool EnableLegacyRendering + { + get + { +#if NET_2_0 + return + (bool) typeof(System.Web.UI.WebControls.BaseValidator).GetProperty("EnableLegacyRendering", + BindingFlags.Instance + | BindingFlags.NonPublic).GetValue(this, null); +#else + return (this.Page != null); +#endif + } + } + } +} diff --git a/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs b/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs new file mode 100644 index 00000000..42d2a1f7 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs @@ -0,0 +1,138 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Web.UI; +using Spring.Context; +using Spring.Web.UI.Validation; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// Provides common functionality to all validation renderer controls. + /// + /// Erich Eichinger + /// $Id: AbstractValidationControl.cs,v 1.1 2008/03/19 12:07:14 oakinger Exp $ + public abstract class AbstractValidationControl : Control + { + private string provider; + private IValidationErrorsRenderer renderer; + + /// + /// Gets or sets the provider. + /// + /// The provider. + public string Provider + { + get + { + if (this.provider == null) + { + return this.ID; + } + return this.provider; + } + set { this.provider = value; } + } + + /// + /// Gets or sets the validation errors renderer to use. + /// + /// + /// If not explicitly specified, defaults to . + /// + /// The validation errors renderer to use. + public IValidationErrorsRenderer Renderer + { + get + { + if (this.renderer == null) + { + this.renderer = CreateValidationErrorsRenderer(); + } + return this.renderer; + } + set { this.renderer = value; } + } + + /// + /// Gets the MessageSource to be used for resolve error messages + /// + /// + /// By default, returns 's MessageSource. + /// + protected virtual IMessageSource MessageSource + { + get { return ValidationContainer.MessageSource; } + } + + /// + /// Create the default + /// for this ValidationControl if none is configured. + /// + protected abstract IValidationErrorsRenderer CreateValidationErrorsRenderer(); + + /// + /// Gets the , who's + /// shall be rendered by this control. + /// + protected virtual IValidationContainer ValidationContainer + { + get + { + for(Control parent=this.Parent; parent!=null; parent=parent.Parent) + { + IValidationContainer container = parent as IValidationContainer; + if (container != null) + { + return container; + } + } + return null; + } + } + + /// + /// Resolves the 's list of validation errors to a list + /// of elements containing the error messages to be rendered. + /// + /// a list containing elements + protected virtual IList ResolveErrorMessages() + { + IList errorMessages; + errorMessages = this.ValidationContainer.ValidationErrors.GetResolvedErrors(this.Provider, this.MessageSource); + return errorMessages; + } + + /// + /// Renders error messages using the specified . + /// + /// + protected override void Render(HtmlTextWriter writer) + { + IList errorMessages = ResolveErrorMessages(); + Renderer.RenderErrors(Page as Spring.Web.UI.Page, writer, errorMessages); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/Calendar.cs b/src/Spring/Spring.Web/Web/UI/Controls/Calendar.cs new file mode 100644 index 00000000..d3cea716 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/Calendar.cs @@ -0,0 +1,288 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using System.IO; +using System.Threading; +using System.Web.UI; +using System.Web.UI.WebControls; +using Spring.Util; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// Displays a pop-up DHTML calendar. + /// + /// + ///

    + /// Credit: this control uses a slightly modified version of the + /// Dynarch.com DHTML Calendar, + /// written by Mihai Bazon. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: Calendar.cs,v 1.13 2007/12/07 21:06:35 oakinger Exp $ + [ValidationProperty("SelectedDate")] + public class Calendar : WebControl, IPostBackDataHandler + { + private const string AllowEditingViewStateKey = "AllowEditing"; + private const string DateFormatViewStateKey = "Format"; + private const string SelectedDateViewStateKey = "SelectedDate"; + private static readonly object EventDateChanged = new object(); + + private string skin; + + /// + /// Registers necessary scripts and stylesheet. + /// + /// + /// An object that contains the event data. + /// + protected override void OnPreRender(EventArgs e) + { + if (skin != null) + { + Page.RegisterStyleFile("CalendarStyle", WebUtils.CreateAbsolutePath(Page.ScriptsRoot, "Calendar/calendar-" + skin + ".css")); + } + Page.RegisterHeadScriptFile("Calendar", WebUtils.CreateAbsolutePath(Page.ScriptsRoot,"Calendar/calendar.js")); + Page.RegisterHeadScriptFile("CalendarLanguage", WebUtils.CreateAbsolutePath(Page.ScriptsRoot, "Calendar/lang/calendar-" + Page.UserCulture.Name + ".js")); + Page.RegisterHeadScriptFile("CalendarSetup", WebUtils.CreateAbsolutePath(Page.ScriptsRoot,"Calendar/calendar-setup.js")); + } + + /// + /// Gets a reference to the instance that contains the + /// server control. + /// + /// + /// A reference to the instance that contains the + /// server control. + /// + private new Page Page + { + get { return base.Page as Page; } + } + + /// + /// The selected date. + /// + /// The selected date. + public DateTime SelectedDate + { + get + { + if (this.ViewState[SelectedDateViewStateKey] == null) + { + return DateTime.Now; + } + return (DateTime) this.ViewState[SelectedDateViewStateKey]; + } + set { this.ViewState[SelectedDateViewStateKey] = value; } + } + + /// + /// The date format that is to be used. + /// + /// A valid date format string. + public string Format + { + get + { + if (this.ViewState[DateFormatViewStateKey] == null) + { + return Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern; + } + return (string) this.ViewState[DateFormatViewStateKey]; + } + set { this.ViewState[DateFormatViewStateKey] = value; } + } + + /// + /// Is direct editing of the date allowed? + /// + /// + /// if direct editing of the date is allowed? + public bool AllowEditing + { + get + { + if (this.ViewState[AllowEditingViewStateKey] == null) + { + return true; + } + return (bool) this.ViewState[AllowEditingViewStateKey]; + } + set { this.ViewState[AllowEditingViewStateKey] = value; } + } + + /// + /// The (CSS) style. + /// + /// The style for the calendar. + public string Skin + { + get { return skin; } + set { skin = value; } + } + + /// + /// Renders a hidden input field that stores the value for the radio button group. + /// + /// + /// to use for rendering. + /// + protected override void Render(HtmlTextWriter writer) + { + writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0"); + writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0"); + + writer.RenderBeginTag(HtmlTextWriterTag.Table); + writer.RenderBeginTag(HtmlTextWriterTag.Tr); + writer.RenderBeginTag(HtmlTextWriterTag.Td); + + RenderTextBox(writer); + + writer.RenderEndTag(); + writer.RenderBeginTag(HtmlTextWriterTag.Td); + + RenderButton(writer); + + writer.RenderEndTag(); + writer.RenderEndTag(); + writer.RenderEndTag(); + + RenderSetupScript(writer); + } + + private void RenderTextBox(HtmlTextWriter writer) + { + writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID); + writer.AddAttribute(HtmlTextWriterAttribute.Type, "text"); + writer.AddAttribute(HtmlTextWriterAttribute.Name, UniqueID); + writer.AddAttribute(HtmlTextWriterAttribute.Value, (SelectedDate != DateTime.MinValue ? SelectedDate.ToString(Format) : "")); + + if (!AllowEditing) + { + writer.AddAttribute(HtmlTextWriterAttribute.ReadOnly, "readonly"); + } + base.AddAttributesToRender(writer); + + writer.RenderBeginTag(HtmlTextWriterTag.Input); + writer.RenderEndTag(); + } + + private void RenderButton(HtmlTextWriter writer) + { + writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID + "_button"); + writer.AddAttribute(HtmlTextWriterAttribute.Src, WebUtils.CreateAbsolutePath(Page.ScriptsRoot, "Calendar/img.gif")); + writer.AddAttribute(HtmlTextWriterAttribute.Border, "0"); + + writer.RenderBeginTag(HtmlTextWriterTag.Img); + writer.RenderEndTag(); + } + + private void RenderSetupScript(HtmlTextWriter writer) + { + writer.AddAttribute("language", "javascript"); + + writer.RenderBeginTag(HtmlTextWriterTag.Script); + writer.WriteLine("Calendar.setup({"); + writer.WriteLine(" inputField : \"" + ClientID + "\","); + writer.WriteLine(" button : \"" + ClientID + "_button\""); + writer.WriteLine("});"); + writer.RenderEndTag(); + } + + #region IPostBackDataHandler Members + + /// + /// Raises the SelectionChanged event. + /// + public void RaisePostDataChangedEvent() + { + OnDateChanged(EventArgs.Empty); + } + + /// + /// Loads postback data into the control. + /// + /// + /// The key that should be used to retrieve data. + /// + /// The postback data collection. + /// if data has changed. + public bool LoadPostData(string postDataKey, NameValueCollection postCollection) + { + DateTime dateValue; + string dateString = postCollection[postDataKey]; + if (StringUtils.HasText(dateString)) + { + try + { + dateValue = DateTime.Parse(dateString); + } + catch (FormatException) + { + dateValue = DateTime.MinValue; + } + } + else + { + dateValue = DateTime.MinValue; + } + bool changed = dateValue != this.SelectedDate; + if (changed) + { + this.ViewState[SelectedDateViewStateKey] = dateValue; + } + return changed; + } + + #endregion + + /// + /// The method that is called on postback if the date has changed. + /// + /// + /// The event argument (empty and unused). + /// + protected virtual void OnDateChanged(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[EventDateChanged]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Occurs when the value of the radio button group changes between postbacks to the server. + /// + public event EventHandler DateChanged + { + add { base.Events.AddHandler(EventDateChanged, value); } + remove { base.Events.RemoveHandler(EventDateChanged, value); } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/CheckBoxList.cs b/src/Spring/Spring.Web/Web/UI/Controls/CheckBoxList.cs new file mode 100644 index 00000000..293776a5 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/CheckBoxList.cs @@ -0,0 +1,72 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Web.UI; +using System.Web.UI.WebControls; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// Adds the property to the framework's control. + /// + /// + /// When using Spring.Web's DataBinding, you should use this control for easier binding declaration. + /// + /// Erich Eichinger + /// $Id: CheckBoxList.cs,v 1.2 2007/12/14 16:38:44 oakinger Exp $ + public class CheckBoxList : System.Web.UI.WebControls.CheckBoxList + { + /// + /// Gets or Sets the list of selected values and checks the es accordingly + /// + public string[] SelectedValues + { + get + { + ArrayList vals = new ArrayList(); + foreach( ListItem item in this.Items ) + { + if (item.Selected) vals.Add(item.Value); + } + return (string[]) vals.ToArray(typeof (string)); + } + set + { + if (value == null || value.Length == 0) + { + this.ClearSelection(); + } + else + { + ArrayList vals = new ArrayList(value); + foreach(ListItem item in this.Items) + { + item.Selected = (vals.Contains(item.Value)); + } + } + } + } + } +} diff --git a/src/Spring/Spring.Web/Web/UI/Controls/CheckBoxValidator.cs b/src/Spring/Spring.Web/Web/UI/Controls/CheckBoxValidator.cs new file mode 100644 index 00000000..09f35a02 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/CheckBoxValidator.cs @@ -0,0 +1,111 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web.UI; +using System.Web.UI.WebControls; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// This validator allows for validating a CheckBox's "Checked" state. + /// + /// Erich Eichinger + /// $Id: CheckBoxValidator.cs,v 1.1 2007/07/31 10:40:59 oakinger Exp $ + [ToolboxData("<{0}:RequiredCheckBoxValidator runat=\"server\" ErrorMessage=\"RequiredCheckBoxValidator\">")] + public class RequiredCheckBoxValidator : AbstractBaseValidator + { + /// + /// Validates this validator's properties are set correctly. + /// + /// + protected override bool ControlPropertiesValid() + { + string controlName = this.ControlToValidate; + if(controlName.Length == 0) + { + return base.ControlPropertiesValid(); + } + return true; + } + + /// + /// Returns the state of the checkbox's Checked property + /// + /// + protected override bool EvaluateIsValid() + { + CheckBox controlToValidate = this.NamingContainer.FindControl(this.ControlToValidate) as CheckBox; + if(controlToValidate == null) + { + return false; + } + + return controlToValidate.Checked; + } + + /// + /// Adds attributes required for clientside validation + /// + /// + protected override void AddAttributesToRender(HtmlTextWriter writer) + { + base.AddAttributesToRender(writer); + if(base.RenderUplevel) + { + string controlId = this.ClientID; + writer = base.EnableLegacyRendering ? writer : null; + base.AddExpandoAttribute(writer, controlId, "evaluationfunction", "RequiredCheckBoxValidatorEvaluateIsChecked", false); + base.AddExpandoAttribute(writer, controlId, "initialvalue", "", true); + } + } + + /// + /// Ensures the evaluation javascript functionblock is registered + /// + /// + protected override void OnPreRender(EventArgs e) + { + base.OnPreRender(e); + if (!base.IsClientScriptBlockRegistered(typeof(RequiredCheckBoxValidator), "RequiredCheckBoxValidatorEvaluateIsChecked")) + { + base.RegisterClientScriptBlock( + typeof(RequiredCheckBoxValidator) + , "RequiredCheckBoxValidatorEvaluateIsChecked" + , +@" + +" + ); + } + } + } +} diff --git a/src/Spring/Spring.Web/Web/UI/Controls/Content.cs b/src/Spring/Spring.Web/Web/UI/Controls/Content.cs new file mode 100644 index 00000000..0186e910 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/Content.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Web.UI; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// Represents Content control that can be used to populate or override placeholders + /// within the master page. + /// + /// + /// Any content defined within this control will override default content + /// in the matching control + /// within the master page + /// + /// Aleksandar Seovic + /// $Id: Content.cs,v 1.6 2007/07/24 13:33:27 oakinger Exp $ +#if NET_2_0 + public class Content : System.Web.UI.WebControls.Content + {} +#else + [Designer("System.Web.UI.Design.ReadWriteControlDesigner, System.Design")] + [PersistChildren(true)] + [ParseChildren(false)] + public class Content : Control + { + private String contentPlaceHolderID; + + /// + /// Id of the to override. + /// + public String ContentPlaceHolderID + { + get { return contentPlaceHolderID; } + set { contentPlaceHolderID = value; } + } + } +#endif +} diff --git a/src/Spring/Spring.Web/Web/UI/Controls/ContentPlaceholder.cs b/src/Spring/Spring.Web/Web/UI/Controls/ContentPlaceholder.cs new file mode 100644 index 00000000..3cbf459e --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/ContentPlaceholder.cs @@ -0,0 +1,75 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.ComponentModel; +using System.Web.UI; + +namespace Spring.Web.UI.Controls +{ + /// + /// Represents ContentPlaceHolder control that can be used to define placeholders + /// within the master page. + /// + /// + /// Any content defined within this control will be treated as a default content + /// for the placeholder and will be rendered unless the child page overrides it + /// by defining matching control. + /// + /// Aleksandar Seovic + /// $Id: ContentPlaceholder.cs,v 1.6 2006/09/08 06:21:31 oakinger Exp $ +#if NET_2_0 + public class ContentPlaceHolder : System.Web.UI.WebControls.ContentPlaceHolder + {} +#else + [Designer("System.Web.UI.Design.ReadWriteControlDesigner, System.Design")] + [PersistChildren(true)] + [ParseChildren(false)] + public class ContentPlaceHolder : Control + { + private Content content; + + /// + /// Association to content control defined in the child page + /// + public Content Content + { + get { return content; } + set { content = value; } + } + + /// + /// Renders either its own (default) content or content defined if the associated + /// control. + /// + /// HtmlTextWriter that should be used for output + protected override void Render(HtmlTextWriter writer) + { + if (content == null) + { + base.Render(writer); + } + else + { + content.RenderControl(writer); + } + } + } +#endif +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/ContentReplacer.cs b/src/Spring/Spring.Web/Web/UI/Controls/ContentReplacer.cs new file mode 100644 index 00000000..9d74e543 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/ContentReplacer.cs @@ -0,0 +1,157 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Reflection; +using System.Web.UI; +using Common.Logging; + +namespace Spring.Web.UI.Controls +{ + /// + /// Represents Content control that can be used to populate or override placeholders + /// anywhere within the page. + /// + /// + /// + /// Any content defined within this control will override default content + /// in the matching control specified by anywhere + /// within the page. + /// + /// + /// In contrast to control, ContentReplacer can replace the content of + /// any control within the current page - it is not limited to replacing ContentPlaceholders on master pages. + /// + /// + /// This technique is useful if you want to group e.g. rendering navigation elements on 1 ascx control, but your + /// design requires navigation elements to be distributed across different places within the HTML code. + /// + /// + /// Erich Eichinger + /// $Id: ContentReplacer.cs,v 1.3 2007/07/24 13:33:27 oakinger Exp $ + public class ContentReplacer : Control + { + private static readonly ILog log = LogManager.GetLogger(typeof(ContentReplacer)); + + private string contentPlaceHolderID; + + /// + /// Specifies the unique id of the control, who's content is to be replaced. + /// + public string ContentPlaceHolderID + { + get { return contentPlaceHolderID; } + set { contentPlaceHolderID = value; } + } + + /// + /// Overriden to correctly redirect rendermethod calls. + /// + protected override void OnPreRender(EventArgs e) + { + base.OnPreRender(e); + + // if our container is not visible, don't replace placeholder's content + if (!Visible) return; + + //log.Debug(string.Format("OnPreRender Content['{0}']", this.contentPlaceHolderID)); +#if NET_1_1 + Control ctlRoot = this.Page; + if (ctlRoot is Spring.Web.UI.Page) + { + MasterPage masterPage = ((Spring.Web.UI.Page) ctlRoot).Master; + if (masterPage != null) + { + ctlRoot = masterPage; + } + } +#else + Control ctlRoot = (this.Page.Master != null) ? (Control)this.Page.Master : (Control)this.Page; +#endif + Control ctl = ctlRoot.FindControl(this.contentPlaceHolderID); + if (ctl != null) + { + log.Debug(string.Format("OnPreRender Content['{0}'] found placeholder - replacing RenderMethod",this.contentPlaceHolderID)); + + RenderMethod myRenderMethod = GetRenderMethod(); + //log.Debug(string.Format("OnPreRender Content['{0}'] renderMethod found={1}", this.contentPlaceHolderID,(myRenderMethod != null ? "true" : "false"))); + + // prevent content control from rendering itself + this.SetRenderMethodDelegate(new RenderMethod(RenderNothing)); + if (myRenderMethod == null) + { + myRenderMethod = new RenderMethod(RenderChildControls); + } + // instead replace placeholder's rendermethod to render this control's content + ctl.SetRenderMethodDelegate(myRenderMethod); + } + else + { + throw new ArgumentException(string.Format("No ContentPlaceHolder with id '{0}' defined on this page.", this.contentPlaceHolderID)); + } + } + + /// + /// Renders child controls + /// + private void RenderChildControls(HtmlTextWriter output, Control container) + { + if (this.HasControls()) + { + foreach (Control ctl in this.Controls) + { + ctl.RenderControl(output); + } + } + } + + /// + /// Render nothing. + /// + private void RenderNothing(HtmlTextWriter output, Control container) + { + // do nothing + } + +#if NET_2_0 + private static readonly PropertyInfo piRareFieldsEnsured = + typeof(Control).GetProperty("RareFieldsEnsured", BindingFlags.NonPublic | BindingFlags.Instance); + private static readonly FieldInfo fiRenderMethod = + typeof(Control).GetNestedType("ControlRareFields",BindingFlags.NonPublic).GetField("RenderMethod"); + + private RenderMethod GetRenderMethod() + { + object o = piRareFieldsEnsured.GetValue(this, null); + RenderMethod myRenderMethod = (RenderMethod) fiRenderMethod.GetValue(o); + return myRenderMethod; + } +#else + private static readonly FieldInfo fiRenderMethod = + typeof(Control).GetField("_renderMethod", BindingFlags.NonPublic | BindingFlags.Instance); + + private RenderMethod GetRenderMethod() + { + RenderMethod myRenderMethod = (RenderMethod)fiRenderMethod.GetValue(this); + return myRenderMethod; + } +#endif + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/ControlState.cs b/src/Spring/Spring.Web/Web/UI/Controls/ControlState.cs new file mode 100644 index 00000000..9aa16ec2 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/ControlState.cs @@ -0,0 +1,45 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Web.UI.Controls +{ +#if !NET_2_0 + /// + /// Enumeration representing a control's current initialization state. + /// + /// Erich Eichinger + /// $Id: ControlState.cs,v 1.1 2007/07/24 13:33:27 oakinger Exp $ + internal enum ControlState + { + Constructed, + ChildrenInitialized, + Initialized, + ViewStateLoaded, + Loaded, + PreRendered + } +#endif //!NET_2_0 +} diff --git a/src/Spring/Spring.Web/Web/UI/Controls/DataBindingAdapter.cs b/src/Spring/Spring.Web/Web/UI/Controls/DataBindingAdapter.cs new file mode 100644 index 00000000..59c234b3 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/DataBindingAdapter.cs @@ -0,0 +1,52 @@ +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; + +namespace Spring.Web.UI.Controls +{ + /// + /// May be used to wrap controls for databinding that don't accept unknown attributes. + /// + [ParseChildren(false)] + public class DataBindingAdapter : WebControl + { + private Control wrappedControl; + + /// + /// Overridden to ensure only 1 control is wrapped by this adapter + /// + /// + protected override void AddParsedSubObject(object obj) + { + if(obj is Control && (wrappedControl == null) && (!(obj is LiteralControl))) + { + wrappedControl = (Control) obj; + this.Controls.Add(wrappedControl); + } + else if(!(obj is LiteralControl)) + { + throw new HttpException( + string.Format("DataBindingAdapter can only have 1 non-literal child", new object[] { obj.GetType().Name })); + } + } + + /// + /// Returns the control wrapped by this adapter or null. + /// + public Control WrappedControl + { + get { return this.wrappedControl; } + } + + /// + /// Overridden to render wrapped control only. + /// + protected override void Render(HtmlTextWriter writer) + { + if (wrappedControl != null) + { + wrappedControl.RenderControl(writer); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/DataBindingPanel.cs b/src/Spring/Spring.Web/Web/UI/Controls/DataBindingPanel.cs new file mode 100644 index 00000000..9de9efc0 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/DataBindingPanel.cs @@ -0,0 +1,383 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Reflection; +using System.Security.Permissions; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; + +using Common.Logging; +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.DataBinding; +using Spring.Globalization; +using Spring.Objects; +using Spring.Util; +using AttributeCollection=System.Web.UI.AttributeCollection; +using BindingDirection=Spring.DataBinding.BindingDirection; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// Any WebControl placed on a DataBindingPanel may be bound + /// to a model by adding an attribute "BindingTarget" + /// + /// Erich Eichinger + /// $Id: DataBindingPanel.cs,v 1.13 2008/03/06 20:23:44 oakinger Exp $ + [PersistChildren(true), + ToolboxData("<{0}:DataBindingPanel runat=\"server\" Width=\"125px\" Height=\"50px\"> "), + ParseChildren(false), +#if NET_1_1 + Designer("System.Web.UI.Design.WebControls.PanelDesigner, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), +#else + Designer("System.Web.UI.Design.WebControls.PanelContainerDesigner, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), +#endif + AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal), + AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class DataBindingPanel : Panel + { + private const string ATTR_BINDINGTARGET = "BindingTarget"; + private const string ATTR_BINDINGSOURCE = "BindingSource"; + private const string ATTR_BINDINGDIRECTION = "BindingDirection"; + private const string ATTR_BINDINGFORMATTER = "BindingFormatter"; + private const string ATTR_BINDINGTYPE = "BindingType"; + private const string ATTR_MESSAGEID = "MessageId"; + private const string ATTR_ERRORPROVIDERS = "ErrorProviders"; + + private static readonly ILog Log = LogManager.GetLogger(typeof(DataBindingPanel)); + + private delegate void TraversalAction(IWebDataBound bindingContainer, WebControl wc); + + /// + /// Registers this control for the event of it's container. + /// + protected override void OnInit(EventArgs e) + { + RegisterDataControl(this, new EventHandler(BindingOwner_DataBindingsInitialized)); + base.OnInit(e); + } + + /// + /// Overridden to remove custom binding attributes before rendering. + /// + /// + protected override void OnPreRender(EventArgs e) + { + // remove custom attributes + if (this.HasControls()) + { + this.TraverseControls(null, this.Controls, new TraversalAction(this.RemoveBindingAttributes)); + } + + base.OnPreRender(e); + } + + /// + /// Overriden to suppress rendering this control's tag + /// + /// + protected override void Render(HtmlTextWriter writer) + { + base.RenderContents(writer); + } + + /// + /// Called by the containing if bindings must be initialized. + /// + private void BindingOwner_DataBindingsInitialized(object sender, EventArgs e) + { + IWebDataBound bindingContainer = (IWebDataBound) sender; + if (this.HasControls()) + { + this.TraverseControls(bindingContainer, this.Controls, new TraversalAction(this.BindControl)); + } + } + + /// + /// Adds all controls on this panel to the containing 's binding collection. + /// + /// the usercontrol containing this panel + /// the of controls to be bound + /// action to be performed on matching controls + private void TraverseControls(IWebDataBound bindingContainer, ControlCollection controls, TraversalAction action) + { + foreach (Control control in controls) + { + // DataBindingPanels must not be nested + if (control is DataBindingPanel) + { + throw new HttpException("Controls of type DataBindingPanel must not be nested"); + } + + // abort recursion on LiteralControl or nested UserControl + if (control is LiteralControl + || control is UserControl) + { + continue; + } + + // if it's a WebControl, check for binding-related attributes + WebControl wc = control as WebControl; + if (wc != null) + { + try + { + action(bindingContainer, wc); + } + catch (Exception ex) + { + string msg = + string.Format("Error executing action on control '{0}' of type '{1}'", wc.UniqueID, + wc.GetType().FullName); + Log.Error(msg, ex); + throw new HttpException(msg, ex); + } + } + + if (control.HasControls()) + { + this.TraverseControls(bindingContainer, control.Controls, action); + } + } + } + + /// + /// Retrieves custom binding attributes from a webcontrol and adds a new binding + /// instance to the container's if necessary. + /// + private void BindControl(IWebDataBound dataBound, WebControl theControl) + { + // special handling of adapted controls + DataBindingAdapter adapterControl = theControl as DataBindingAdapter; + + Control wc = (adapterControl != null && adapterControl.WrappedControl != null) ? adapterControl.WrappedControl : theControl; + + AttributeCollection attributeCollection = theControl.Attributes; + string bindingTarget = attributeCollection[ATTR_BINDINGTARGET]; + + // at least a BindingTarget must be specified + if (bindingTarget == null) + { + return; + } + attributeCollection.Remove(ATTR_BINDINGTARGET); + + // determine direction + BindingDirection bindingDirection = BindingDirection.Bidirectional; + string strBindingDirection = attributeCollection[ATTR_BINDINGDIRECTION]; + if (strBindingDirection != null) + { + bindingDirection = (BindingDirection) Enum.Parse(typeof(BindingDirection), strBindingDirection); + } + + // determine BindingSource + string bindingSource = attributeCollection[ATTR_BINDINGSOURCE]; + if (bindingSource == null) + { + bindingSource = AutoProbeSourceProperty(wc); + } + attributeCollection.Remove(ATTR_BINDINGSOURCE); + + // get formatter if any + IFormatter bindingFormatter = null; + string bindingFormatterName = attributeCollection[ATTR_BINDINGFORMATTER]; + if (bindingFormatterName != null) + { + bindingFormatter = (IFormatter) dataBound.ApplicationContext.GetObject(bindingFormatterName); + attributeCollection.Remove(ATTR_BINDINGFORMATTER); + } + + // determine source expression + string containerName = dataBound.UniqueID; + string controlName = wc.UniqueID; + string relativeControlName = null; + if ( dataBound is System.Web.UI.Page ) + { + relativeControlName = string.Format("FindControl('{0}')", controlName); + } + else if ( (Control)dataBound != this.NamingContainer) + { + relativeControlName = (controlName.StartsWith(containerName)) ? controlName.Substring(containerName.Length + 1) : controlName; + relativeControlName = string.Format("FindControl('{0}')", relativeControlName); + } + else + { + relativeControlName = wc.ID; + } + // if no bindingSource, expression evaluates to the bound control + bindingSource = (StringUtils.HasLength(bindingSource)) + ? relativeControlName + "." + bindingSource + : relativeControlName; + + Log.Debug( + string.Format("binding control '{0}' relative to '{1}' using expression '{2}'", controlName, + containerName, bindingSource)); + + //get bindingType if any + IBinding binding = null; + string bindingTypeName = attributeCollection[ATTR_BINDINGTYPE]; + if (bindingTypeName == null) + { + bindingTypeName = AutoProbeBindingType(wc); + } + + // get messageId and errorProviders list + string messageId = attributeCollection[ATTR_MESSAGEID]; + string errorProvidersText = attributeCollection[ATTR_ERRORPROVIDERS]; + string[] errorProviders = null; + if (StringUtils.HasLength(errorProvidersText)) + { + errorProviders = (string[])new Spring.Core.TypeConversion.StringArrayConverter().ConvertFrom(errorProvidersText); + } + + // add binding to BindingManager + if (bindingTypeName != null) + { + binding = CreateBindingInstance(bindingTypeName, bindingSource, bindingTarget, bindingDirection, bindingFormatter); + binding = dataBound.BindingManager.AddBinding(binding); + } + else + { + binding = dataBound.BindingManager.AddBinding(bindingSource, bindingTarget, bindingDirection, bindingFormatter); + } + + // set error message + if (StringUtils.HasLength(messageId)) + { + binding.SetErrorMessage( messageId, errorProviders ); + } + } + + /// + /// Removes custom binding attributes from a webcontrol to avoid them being rendered. + /// + private void RemoveBindingAttributes(IWebDataBound dataBound, WebControl wc) + { + AttributeCollection attributeCollection = wc.Attributes; + attributeCollection.Remove(ATTR_BINDINGTARGET); + attributeCollection.Remove(ATTR_BINDINGSOURCE); + attributeCollection.Remove(ATTR_BINDINGTYPE); + attributeCollection.Remove(ATTR_BINDINGFORMATTER); + } + + /// + /// Probe for bindingType of know controls + /// + /// the control, who's bindingType is to be determined + /// null, if standard binding is to be used or the fully qualified typename of the binding implementation + protected virtual string AutoProbeBindingType(Control wc) + { + if (wc is ListBox && ((ListBox) wc).SelectionMode == ListSelectionMode.Multiple) + { + return typeof(MultipleSelectionListControlBinding).FullName; + } + return null; + } + + /// + /// Probes for a few know controls and their properties. + /// + /// + /// The 'BindingSource' expression to be used for binding this control. + /// + protected virtual string AutoProbeSourceProperty(Control wc) + { + if (wc is ListBox && ((ListBox) wc).SelectionMode == ListSelectionMode.Multiple) + { + return null; // force evaluate to control itself + } + else if (wc is CheckBoxList) + { + return "SelectedValues"; + } + else if (wc is ListControl) + { + return "SelectedValue"; + } + else if (wc is CheckBox) + { + return "Checked"; + } + else if (wc is TextBox) + { + return "Text"; + } +#if NET_2_0 + else if (wc is HiddenField) + { + return "Value"; + } +#endif + else if (wc is RadioButtonGroup) + { + return "Value"; + } + else if (wc is DataBindingAdapter) + { + throw new ArgumentNullException("Attribute 'BindingSource' is mandatory when using DataBindingAdapter"); + } + + throw new ArgumentNullException("Attribute 'BindingSource' is missing and control is of unknown type"); + } + + private static IBinding CreateBindingInstance(string bindingTypeName, string bindingSource, string bindingTarget, BindingDirection bindingDirection, IFormatter bindingFormatter) + { + IBinding binding; + Type bindingType = TypeResolutionUtils.ResolveType(bindingTypeName); + ConstructorInfo ctor = + bindingType.GetConstructor(new Type[] {typeof(string), typeof(string), typeof(BindingDirection), typeof(IFormatter)}); + + if (ctor == null) + { + throw new ArgumentException(string.Format("Specified BindingType '{0}' does not implement constructor (string,string,BindingDirection,IFormatter)",bindingTypeName)); + } + binding = + (IBinding) + ObjectUtils.InstantiateType(ctor, new object[] {bindingSource, bindingTarget, bindingDirection, bindingFormatter}); + return binding; + } + + private static void RegisterDataControl(Control control, EventHandler initializeBindingHandler) + { + GetBindingContainerControl(control).DataBindingsInitialized += initializeBindingHandler; + } + + private static IWebDataBound GetBindingContainerControl(Control control) + { + Control parent = control; + while (parent != null) + { + if (parent is IWebDataBound) + { + return (IWebDataBound) parent; + } + parent = parent.Parent; + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/Form.cs b/src/Spring/Spring.Web/Web/UI/Controls/Form.cs new file mode 100644 index 00000000..37dca733 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/Form.cs @@ -0,0 +1,118 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web; +using System.Web.UI; +using System.Web.UI.HtmlControls; +using Spring.Util; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// This control allows for suppressing output of the 'action' attribute. + /// + /// + /// the 'action' attribute rendered by the default control causes troubles + /// in case of URL-rewriting. See e.g. 'thescripts.com' forum + /// and also JIRA SPRNET-560 for more info. + /// + /// Erich Eichinger + /// $Id: Form.cs,v 1.1 2007/07/24 14:32:58 oakinger Exp $ + public class Form : HtmlForm + { + private bool suppressAction = false; + private string action = null; + + /// + /// Sets or Gets a value indicating if the 'action' attribute shall be rendered. Defaults to 'false' + /// + /// + /// The following possibilites are available: + /// + /// If is 'true', rendering of the 'action' attribute is suppressed. + /// If is 'false' and is not set, + /// 'action' attribute will.be rendered to + /// + /// If is 'false' and is set, + /// 'action' attribute will.be rendered to + /// + /// + /// + public bool SuppressAction + { + get { return this.suppressAction; } + set { this.suppressAction = value; } + } + + /// + /// Sets or Gets an explicit url to be rendered + /// + /// + /// The url specified here is only rendered, if is true. + /// + public string Action + { + get { return this.action; } + set { this.action = value; } + } + + /// + /// Renders attributes but performs 'action' suppressing logic. + /// + /// + protected override void RenderAttributes(HtmlTextWriter writer) + { + base.RenderAttributes(new ActionSupressingHtmlTextWriter(writer)); + if (!this.suppressAction) + { + string url = (StringUtils.HasText(this.action)) ? this.action : Context.Request.RawUrl; + writer.WriteAttribute("action", url, true); + } + } + + #region Nested type: ActionSupressingHtmlTextWriter + + /// + /// This wrapper suppresses output of 'action' attributes. + /// + private class ActionSupressingHtmlTextWriter : HtmlTextWriter + { + public ActionSupressingHtmlTextWriter(HtmlTextWriter wrappedWriter) + : base(wrappedWriter.InnerWriter) + { + } + + public override void WriteAttribute(string name, string value, bool fEncode) + { + if (string.Compare(name, "action", true) != 0) + { + base.WriteAttribute(name, value, fEncode); + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/Head.cs b/src/Spring/Spring.Web/Web/UI/Controls/Head.cs new file mode 100644 index 00000000..fc55867c --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/Head.cs @@ -0,0 +1,189 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.IO; +using System.Web.UI; +using Spring.Util; +using Spring.Web.Support; + +namespace Spring.Web.UI.Controls +{ + /// + /// This control should be used instead of standard HTML head tag + /// in order to render dynamicaly registered head scripts and stylesheets. + /// + /// + /// If you need to use ASP.NETs built-in <head> tag, you should nest Spring's within ASP.NET's: + /// + /// <html> + /// <head> + /// <title>Some Title</title> + /// <spring:head> + /// <-- will render styleblocks etc. here --> + /// </spring:head> + /// </head> + /// </html> + /// + /// + /// Aleksandar Seovic + /// $Id: Head.cs,v 1.8 2007/11/28 23:26:19 oakinger Exp $ + public class Head : Control + { + private string _defaultStyleType = "text/css"; + + /// + /// Gets or sets the default mimetype for the <style> element's 'type' attribute + /// + /// + /// Defaults to "text/css" + /// + public string DefaultStyleType + { + get { return _defaultStyleType; } + set { _defaultStyleType = value; } + } + + /// + /// Gets a reference to the instance that contains the + /// server control. + /// + /// + private new Page Page + { + get { return base.Page as Page; } + } + + /// + /// Sends server control content to a provided object, which writes the content to + /// be rendered on + /// the client. + /// + /// The object that receives the server control content. + protected override void Render(HtmlTextWriter writer) + { +#if NET_1_1 + bool hasIntrinsicHead = false; +#else + bool hasIntrinsicHead = (this.Page.Header != null); +#endif + // don't render begin/end element if we are nested within an ASP.NET control + if (!hasIntrinsicHead) + { + writer.RenderBeginTag(HtmlTextWriterTag.Head); + } + + RenderChildren(writer); + RenderStyleBlocks(writer); + RenderStyleFiles(writer); + RenderHeadScripts(writer); + + if (!hasIntrinsicHead) + { + writer.RenderEndTag(); + } + } + + private void RenderStyleBlocks(HtmlTextWriter writer) + { + if (Page.Styles.Count > 0) + { + writer.AddAttribute(HtmlTextWriterAttribute.Type, _defaultStyleType); + writer.RenderBeginTag(HtmlTextWriterTag.Style); + + foreach (DictionaryEntry style in Page.Styles) + { + writer.WriteLine(style.Key + " { " + style.Value + " }"); + } + writer.RenderEndTag(); + } + } + + private void RenderStyleFiles(HtmlTextWriter writer) + { + foreach (DictionaryEntry file in Page.StyleFiles) + { + writer.AddAttribute("rel", "stylesheet"); + writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/css"); + writer.AddAttribute(HtmlTextWriterAttribute.Href, WebUtils.CreateAbsolutePath(Page.CssRoot, (string)file.Value)); + + writer.RenderBeginTag(HtmlTextWriterTag.Link); + writer.RenderEndTag(); + } + } + + private void RenderHeadScripts(HtmlTextWriter writer) + { + foreach (DictionaryEntry scriptEntry in Page.HeadScripts) + { + object script = scriptEntry.Value; + if (script is ScriptEvent) + { + RenderScriptEvent(writer, script as ScriptEvent); + } + else if (script is ScriptFile) + { + RenderScriptFile(writer, script as ScriptFile); + } + else if (script is ScriptBlock) + { + RenderScriptBlock(writer, script as ScriptBlock); + } + } + } + + private void RenderScriptBlock(HtmlTextWriter writer, ScriptBlock script) + { + RenderCommonScriptAttributes(writer, script); + writer.RenderBeginTag(HtmlTextWriterTag.Script); + writer.WriteLine(script.Script); + writer.RenderEndTag(); + } + + private void RenderScriptFile(HtmlTextWriter writer, ScriptFile script) + { + RenderCommonScriptAttributes(writer, script); + writer.AddAttribute(HtmlTextWriterAttribute.Src, WebUtils.CreateAbsolutePath(Page.ScriptsRoot, script.FileName)); + + writer.RenderBeginTag(HtmlTextWriterTag.Script); + writer.RenderEndTag(); + } + + private void RenderScriptEvent(HtmlTextWriter writer, ScriptEvent script) + { + RenderCommonScriptAttributes(writer, script); + writer.AddAttribute(HtmlTextWriterAttribute.For, script.Element); + writer.AddAttribute("event", script.EventName); + + writer.RenderBeginTag(HtmlTextWriterTag.Script); + writer.WriteLine(script.Script); + writer.RenderEndTag(); + } + + private void RenderCommonScriptAttributes(HtmlTextWriter writer, Script script) + { + if (StringUtils.HasLength(script.Language)) + { + writer.AddAttribute("language", script.Language); + } + writer.AddAttribute("type", script.Type.ToString()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/LocalizedImage.cs b/src/Spring/Spring.Web/Web/UI/Controls/LocalizedImage.cs new file mode 100644 index 00000000..8977b0d3 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/LocalizedImage.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.IO; + +namespace Spring.Web.UI.Controls +{ + /// + /// This control should be used instead of standard HTML head tag + /// in order to render dynamicaly registered head scripts and stylesheets. + /// + /// Aleksandar Seovic + /// $Id: LocalizedImage.cs,v 1.4 2006/05/18 21:37:52 markpollack Exp $ + public class LocalizedImage : System.Web.UI.WebControls.Image + { + private string imageName; + + /// + /// Tries to determine full URL for the localized image before it's rendered. + /// + /// + protected override void OnPreRender(EventArgs e) + { + base.OnPreRender(e); + this.ImageUrl = DetermineLocalizedUrl(); + } + + /// + /// Name of the image file. + /// + public string ImageName + { + get { return imageName; } + set { imageName = value; } + } + + /// + /// Gets a reference to the instance that contains the + /// server control. + /// + /// + new private Page Page + { + get { return base.Page as Page; } + } + + private string DetermineLocalizedUrl() + { + ArrayList localeParts = new ArrayList(Page.UserCulture.Name.Split('-')); + while (localeParts.Count > 0 && !FileExists(localeParts)) + { + localeParts.RemoveAt(localeParts.Count - 1); + } + + string locale = String.Join("-", (string[]) localeParts.ToArray(typeof(string))); + return Page.ImagesRoot + (locale.Length > 0 ? "/" + locale : "") + "/" + this.ImageName; + } + + private bool FileExists(ArrayList localeParts) + { + string locale = String.Join("-", (string[]) localeParts.ToArray(typeof(string))); + string url = Page.ImagesRoot + "/" + locale + "/" + this.ImageName; + return File.Exists(Page.Server.MapPath(url)); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/MultiView.cs b/src/Spring/Spring.Web/Web/UI/Controls/MultiView.cs new file mode 100644 index 00000000..e6cf94bb --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/MultiView.cs @@ -0,0 +1,448 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using System.Security.Permissions; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; + +#endregion + +namespace Spring.Web.UI.Controls +{ +#if !NET_2_0 + /// + /// This controls provides MultiView introduced with ASP.NET 2.0 for NET 1.1 Platform + /// + /// Erich Eichinger + /// $Id: MultiView.cs,v 1.1 2007/07/24 13:33:27 oakinger Exp $ + [ + DefaultEvent("ActiveViewChanged") + , ToolboxData("<{0}:MultiView runat=\"server\">") + , ParseChildren(false, null) + , AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal) + , AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)] + public class MultiView : Control + { + #region Constants + + /// + /// Represents the command name associated with the next View control to display in a MultiView control. This field is read-only. + /// + public static readonly string NextViewCommandName = "NextView"; + /// + /// Represents the command name associated with the previous View control to display in a MultiView control. This field is read-only. + /// + public static readonly string PreviousViewCommandName = "PrevView"; + /// + /// Represents the command name associated with changing the active View control in a MultiView control, based on a specified View id. This field is read-only. + /// + public static readonly string SwitchViewByIDCommandName = "SwitchViewByID"; + /// + /// Represents the command name associated with changing the active View control in a MultiView control based on a specified View index. This field is read-only. + /// + public static readonly string SwitchViewByIndexCommandName = "SwitchViewByIndex"; + + #endregion + + #region Fields + + private static readonly FieldInfo s_fiControlState = + typeof(Control).GetField("_controlState", BindingFlags.Instance | BindingFlags.NonPublic); + + + private int _activeViewIndex = -1; + private int _cachedActiveViewIndex = -1; + private bool _controlStateApplied; + private static readonly object _eventActiveViewChanged = new object(); + private bool _ignoreBubbleEvents; + + #endregion + + #region Construction + + /// + /// Initializes a new instance of the class. + /// + public MultiView() + { + // intentionally left empty + } + + #endregion + + #region Events + + /// + /// Occurs when the active View control of a MultiView control changes between posts to the server. + /// + public event EventHandler ActiveViewChanged + { + add { base.Events.AddHandler(_eventActiveViewChanged, value); } + remove { base.Events.RemoveHandler(_eventActiveViewChanged, value); } + } + + #endregion + + #region Properties + + /// + /// Gets or sets the index of the active View control within a MultiView control. + /// + [DefaultValue(-1)] + public virtual int ActiveViewIndex + { + get + { + if (_cachedActiveViewIndex > -1) + { + return _cachedActiveViewIndex; + } + return _activeViewIndex; + } + set + { + if (value < -1) + { + throw new ArgumentOutOfRangeException("value", + string.Format( + "MultiView_ActiveViewIndex_less_than_minus_one", + new object[] {value})); + } + if ((Views.Count == 0) && (ControlState < ControlState.ChildrenInitialized)) + { + _cachedActiveViewIndex = value; + } + else + { + if (value >= Views.Count) + { + throw new ArgumentOutOfRangeException("value", + string.Format( + "MultiView_ActiveViewIndex_equal_or_greater_than_count", + new object[] {value, Views.Count})); + } + int num = (_cachedActiveViewIndex != -1) ? -1 : _activeViewIndex; + _activeViewIndex = value; + _cachedActiveViewIndex = -1; + if (((num != value) && (num != -1)) && (num < Views.Count)) + { + Views[num].Active = false; + if (ShouldTriggerViewEvent) + { + Views[num].OnDeactivate(EventArgs.Empty); + } + } + if (((num != value) && (Views.Count != 0)) && (value != -1)) + { + Views[value].Active = true; + if (ShouldTriggerViewEvent) + { + Views[value].OnActivate(EventArgs.Empty); + OnActiveViewChanged(EventArgs.Empty); + } + } + } + } + } + + + /// + /// Gets the collection of View controls in the MultiView control. + /// + [ + PersistenceMode(PersistenceMode.InnerDefaultProperty) + , Browsable(false) + ] + public virtual ViewCollection Views + { + get { return (ViewCollection) Controls; } + } + + /// + /// Return the current lifecycle state of this control. + /// + private ControlState ControlState + { + get { return (ControlState) s_fiControlState.GetValue(this); } + } + + #endregion + + /// + /// Sets the specified View control to the active view within a MultiView control. + /// + /// A View control to set as the active view within a MultiView control. + public void SetActiveView(View view) + { + int index = Views.IndexOf(view); + if (index < 0) + { + throw new HttpException( + string.Format("MultiView_view_not_found", new object[] {(view == null) ? "null" : view.ID, ID})); + } + ActiveViewIndex = index; + } + + /// + /// Returns the current active View control within a MultiView control. + /// + /// A View control that represents the active view within a MultiView control. + public View GetActiveView() + { + int activeViewIndex = ActiveViewIndex; + if (activeViewIndex >= Views.Count) + { + throw new Exception("MultiView_ActiveViewIndex_out_of_range"); + } + if (activeViewIndex < 0) + { + return null; + } + View view = Views[activeViewIndex]; + if (!view.Active) + { + UpdateActiveView(activeViewIndex); + } + return view; + } + + /// + /// Mark this control to ignore + /// + internal void IgnoreBubbleEvents() + { + _ignoreBubbleEvents = true; + } + + /// + /// Adds the element to the collection of child controls. + /// + protected override void AddParsedSubObject(object obj) + { + if (obj is View) + { + Controls.Add((Control) obj); + } + else if (!(obj is LiteralControl)) + { + throw new HttpException( + string.Format("MultiView_cannot_have_children_of_type", new object[] {obj.GetType().Name})); + } + } + + /// + /// Creates a new instance. + /// + /// + protected override ControlCollection CreateControlCollection() + { + return new ViewCollection(this); + } + + /// + /// Restores view-state information from a previous page request. + /// + protected override void LoadViewState(object savedState) + { + base.LoadViewState(savedState); + ActiveViewIndex = (int) ViewState["_activeViewIndex"]; + _controlStateApplied = true; + } + + /// + /// Saves view-state information to the page response. + /// + protected override object SaveViewState() + { + ViewState["_activeViewIndex"] = ActiveViewIndex; + return base.SaveViewState(); + } + + /// + /// Raises the event. + /// + protected virtual void OnActiveViewChanged(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[_eventActiveViewChanged]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Captures know commands. + /// + protected override bool OnBubbleEvent(object source, EventArgs e) + { + if (!_ignoreBubbleEvents && (e is CommandEventArgs)) + { + CommandEventArgs args = (CommandEventArgs) e; + string commandName = args.CommandName; + if (commandName == NextViewCommandName) + { + if (ActiveViewIndex < (Views.Count - 1)) + { + ActiveViewIndex++; + } + else + { + ActiveViewIndex = -1; + } + return true; + } + if (commandName == PreviousViewCommandName) + { + if (ActiveViewIndex > -1) + { + ActiveViewIndex--; + } + return true; + } + if (commandName == SwitchViewByIDCommandName) + { + View view = FindControl((string) args.CommandArgument) as View; + if ((view == null) || (view.Parent != this)) + { + throw new HttpException( + string.Format("MultiView_invalid_view_id", + new object[] + {ID, (string) args.CommandArgument, SwitchViewByIDCommandName})); + } + SetActiveView(view); + return true; + } + if (commandName == SwitchViewByIndexCommandName) + { + int num; + try + { + num = int.Parse((string) args.CommandArgument, CultureInfo.InvariantCulture); + } + catch (FormatException) + { + throw new FormatException( + string.Format("MultiView_invalid_view_index_format", + new object[] {(string) args.CommandArgument, SwitchViewByIndexCommandName})); + } + ActiveViewIndex = num; + return true; + } + } + return false; + } + + /// + /// Initialize this control. + /// + /// + protected override void OnInit(EventArgs e) + { + base.OnInit(e); + if (_cachedActiveViewIndex > -1) + { + ActiveViewIndex = _cachedActiveViewIndex; + _cachedActiveViewIndex = -1; + GetActiveView(); + } + } + + /// + /// Handle removal of a view. + /// + /// + protected override void RemovedControl(Control ctl) + { + if (((View) ctl).Active && (ActiveViewIndex < Views.Count)) + { + GetActiveView(); + } + base.RemovedControl(ctl); + } + + /// + /// Render the currently active view. + /// + /// + protected override void Render(HtmlTextWriter writer) + { + View activeView = GetActiveView(); + if (activeView != null) + { + activeView.RenderControl(writer); + } + } + + /// + /// Switches from the currently active view to the specified view. + /// + /// + private void UpdateActiveView(int activeViewIndex) + { + for (int i = 0; i < Views.Count; i++) + { + View view = Views[i]; + if (i == activeViewIndex) + { + view.Active = true; + if (ShouldTriggerViewEvent) + { + view.OnActivate(EventArgs.Empty); + } + } + else if (view.Active) + { + view.Active = false; + if (ShouldTriggerViewEvent) + { + view.OnDeactivate(EventArgs.Empty); + } + } + } + } + + /// + /// Indicates if view events are ready to be raised. + /// + private bool ShouldTriggerViewEvent + { + get + { + if (_controlStateApplied) + { + return true; + } + if (this.Page != null) + { + return !this.Page.IsPostBack; + } + return false; + } + } + } +#endif // !NET_2_0 +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/Panel.cs b/src/Spring/Spring.Web/Web/UI/Controls/Panel.cs new file mode 100644 index 00000000..cd189094 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/Panel.cs @@ -0,0 +1,191 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.Diagnostics; +using System.Reflection; +using System.Security.Permissions; +using System.Web; +using System.Web.UI; +using System.Web.UI.HtmlControls; +using Spring.Context; +using Spring.Context.Support; +using Spring.Util; +using Spring.Web.Support; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// This panel provides the same features as , but provides additional means with regards to Spring. + /// + /// + /// In some cases, automatic dependency injection can cause performance problems. In this case,you can use a Panel for finer-grained control + /// over which controls are to be injected or not. + /// + /// Erich Eichinger + /// $Id: Panel.cs,v 1.3 2008/03/09 15:20:37 oakinger Exp $ + [PersistChildren(true), + ToolboxData("<{0}:Panel runat=\"server\" Width=\"125px\" Height=\"50px\"> "), + ParseChildren(false), +#if NET_1_1 + Designer("System.Web.UI.Design.WebControls.PanelDesigner, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), +#else + Designer("System.Web.UI.Design.WebControls.PanelContainerDesigner, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), +#endif + AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal), + AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class Panel : System.Web.UI.WebControls.Panel, ISupportsWebDependencyInjection + { + // this helper class intercepts the first call to the child controls + // collection from within InitRecursive(). + // InitRecursive() is called right after the control has been added to it's parent's children. + private class InitRecursiveInterceptingControlsCollection : ControlCollection + { + private bool _passedInitRecursiveCheck = false; + + public InitRecursiveInterceptingControlsCollection(Control owner) : base(owner) + { + } + + public override Control this[int index] + { + get + { + // the following check relies on the fact, that ControlCollection is set readonly + // right before InitRecursive() is called on each child control + if (!_passedInitRecursiveCheck + && this.IsReadOnly) + { + _passedInitRecursiveCheck = true; + ((Panel)this.Owner).OnPreInitRecursive(EventArgs.Empty); + } + return base[index]; + } + } + } + + private bool _suppressDependencyInjection; + private bool _renderContainerTag; + + /// + /// This flag controls, whether DI on child controls will be done or not + /// + /// By default, DI will be done + public bool SuppressDependencyInjection + { + get { return _suppressDependencyInjection; } + set { _suppressDependencyInjection = value; } + } + + /// + /// This flag controls, whether this Panel's tag will be rendered. + /// + public bool RenderContainerTag + { + get { return _renderContainerTag; } + set { _renderContainerTag = value; } + } + + /// + /// Overridden to suppress rendering this control's tag + /// + /// + protected override void Render(HtmlTextWriter writer) + { + if (_renderContainerTag) + { + base.Render(writer); + } + else + { + base.RenderContents(writer); + } + } + + /// + /// This method is invoked right before InitRecursive() is called for this Panel and + /// ensures, that dependencies get injected on child controls before their Init event is raised. + /// + protected virtual void OnPreInitRecursive(EventArgs e) + { + if (!_suppressDependencyInjection + && _defaultApplicationContext == null) + { + // obtain appContext of the closed parent template (.ascx/.aspx) control + IApplicationContext appCtx = WebApplicationContext.GetContext(this.TemplateSourceDirectory.TrimEnd('/') + "/"); + // NOTE: _defaultApplicationContext will be set during DI! + WebDependencyInjectionUtils.InjectDependenciesRecursive(appCtx, this); + } + } + + #region Dependency Injection Support + + private IApplicationContext _defaultApplicationContext; + + /// + /// Sets / Gets the ApplicationContext instance used to configure this control + /// + IApplicationContext ISupportsWebDependencyInjection.DefaultApplicationContext + { + get { return _defaultApplicationContext; } + set { _defaultApplicationContext = value; } + } + + /// + /// Injects dependencies into control before adding it. + /// + protected override void AddedControl(Control control, int index) + { + if (!_suppressDependencyInjection + && _defaultApplicationContext != null) + { + WebDependencyInjectionUtils.InjectDependenciesRecursive(_defaultApplicationContext, control); + } + base.AddedControl(control, index); + } + + /// + /// Overridden to automatically aquire a reference to + /// root application context to use for DI. + /// + protected override ControlCollection CreateControlCollection() + { + // set root application context to signal, that we care + // for our children ourselves + if (_suppressDependencyInjection) + { + _defaultApplicationContext = WebApplicationContext.GetRootContext(); + return base.CreateControlCollection(); + } + else + { + return new InitRecursiveInterceptingControlsCollection(this); + } + } + + #endregion Dependency Injection Support + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/RadioButtonGroup.cs b/src/Spring/Spring.Web/Web/UI/Controls/RadioButtonGroup.cs new file mode 100644 index 00000000..d44be09b --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/RadioButtonGroup.cs @@ -0,0 +1,198 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Web.UI; +using System.Web.UI.WebControls; +using Spring.Util; + +namespace Spring.Web.UI.Controls +{ + /// + /// Groups radio button controls and returns the value of the selected control + /// + /// + /// This control alows radio buttons to be data-bound to a data model of the page. + /// + /// Aleksandar Seovic + /// $Id: RadioButtonGroup.cs,v 1.5 2007/03/16 22:38:55 oakinger Exp $ + [ParseChildren(false)] + public class RadioButtonGroup : WebControl, IPostBackDataHandler + { + private static readonly object EventSelectionChanged = new object(); + + private ArrayList options = new ArrayList(); + + /// + /// Overloaded to track addition of contained controls. + /// + protected override void AddedControl(Control control, int index) + { + if (control is RadioButton) + { + RadioButton option = (RadioButton)control; + option.GroupName = this.ID + "Group"; + option.AutoPostBack = this.AutoPostBack; + if(option.Attributes["value"] == null) option.Attributes["value"] = option.ID; + option.Checked = (0 == string.Compare(this.Value, option.Attributes["value"])); + options.Add(control); + } + base.AddedControl (control, index); + } + + /// + /// Overloaded to track removal of a RadioButton + /// + /// + protected override void RemovedControl(Control control) + { + int index = -1; + for(int i=0;i -1) options.RemoveAt(index); + + base.RemovedControl (control); + } + + /// + /// Gets or sets the ID of the selected radio button. + /// + /// ID of the selected radio button. + public string Value + { + get { return (string) this.ViewState["value"]; } + set + { + this.ViewState["value"] = value; + foreach(RadioButton option in this.options) + { + option.Checked = (0==string.Compare(value, option.Attributes["value"])); + } + } + } + + /// + /// Gets or sets whether form should be posted back on every selection change + /// within the radio group. + /// + [DefaultValue(false)] + public virtual bool AutoPostBack + { + get + { + object autoPostBack = this.ViewState["AutoPostBack"]; + if (autoPostBack != null) + { + return (bool) autoPostBack; + } + return false; + } + set + { + this.ViewState["AutoPostBack"] = value; + foreach(RadioButton option in this.options) + { + option.AutoPostBack = value; + } + } + } + + /// + /// Registers the RadioButtonGroup for PostBack. + /// + protected override void OnPreRender(EventArgs e) + { + base.OnPreRender(e); + if (this.Visible) + { + Page.RegisterRequiresPostBack(this); + } + } + + /// + /// Renders only children of this control. + /// + /// HtmlTextWriter to use for rendering. + protected override void Render(HtmlTextWriter writer) + { + this.RenderChildren(writer); + } + + #region IPostBackDataHandler Members + + /// + /// Loads postback data into the control. + /// + /// Key that should be used to retrieve data. + /// Postback data collection. + /// True if data has changed, false otherwise. + public bool LoadPostData(string postDataKey, NameValueCollection postCollection) + { + string newValue = postCollection[postDataKey+"Group"]; + + if (0 != string.Compare(newValue,this.Value)) + { + this.ViewState["value"] = newValue; + return true; + } + + return false; + } + + /// + /// Raises SelectionChanged event. + /// + public void RaisePostDataChangedEvent() + { + OnSelectionChanged(EventArgs.Empty); + } + + #endregion + + /// + /// Method that is called on postback if selected radio button has changed. + /// + /// Empty event argument. + protected virtual void OnSelectionChanged(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[RadioButtonGroup.EventSelectionChanged]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Occurs when the value of the radio button group changes between postbacks to the server. + /// + public event EventHandler SelectionChanged + { + add { base.Events.AddHandler(RadioButtonGroup.EventSelectionChanged, value); } + remove { base.Events.RemoveHandler(RadioButtonGroup.EventSelectionChanged, value); } + } + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/TabCommandEventArgs.cs b/src/Spring/Spring.Web/Web/UI/Controls/TabCommandEventArgs.cs new file mode 100644 index 00000000..725f14ca --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/TabCommandEventArgs.cs @@ -0,0 +1,55 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +using System.Web.UI.WebControls; + +namespace Spring.Web.UI.Controls +{ + /// + /// Provides information about a command raised from a + /// + /// Erich Eichinger + /// $Id: TabCommandEventArgs.cs,v 1.1 2007/07/24 13:33:27 oakinger Exp $ + public class TabCommandEventArgs : CommandEventArgs + { + /// + /// Initializes a new instance. + /// + /// The name of the command raised by a . + /// The index of the tab that raised this event. + public TabCommandEventArgs(string commandName, int tabIndex) : base(commandName, tabIndex) + { + } + + /// + /// Returns the index of the tab that raised this command. + /// + public int TabIndex + { + get { return (int) base.CommandArgument; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/TabCommandEventHandler.cs b/src/Spring/Spring.Web/Web/UI/Controls/TabCommandEventHandler.cs new file mode 100644 index 00000000..db0ad439 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/TabCommandEventHandler.cs @@ -0,0 +1,37 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// Represents the method that will handle the TabCommand event. + /// + /// The source of the event. + /// A that contains the event data. + /// Erich Eichinger + /// $Id: TabCommandEventHandler.cs,v 1.1 2007/07/24 13:33:28 oakinger Exp $ + public delegate void TabCommandEventHandler(object sender, TabCommandEventArgs e); +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/TabContainer.cs b/src/Spring/Spring.Web/Web/UI/Controls/TabContainer.cs new file mode 100644 index 00000000..82ae2fab --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/TabContainer.cs @@ -0,0 +1,118 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web.UI; +using System.Web.UI.WebControls; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// This control is responsible for rendering tabs. + /// + /// + /// By default, this TabContainer implementation uses controls to + /// render tabs. Override to change this behaviour. + /// + /// Erich Eichinger + /// $Id: TabContainer.cs,v 1.1 2007/07/24 13:33:28 oakinger Exp $ + public class TabContainer : Control + { + /// + /// Represents the command name of the tab to be selected. + /// + public static readonly string SelectTabCommandName = "SelectTab"; + + /// + /// Key into the eventhandler table. + /// + private static readonly object _eventClick = new object(); + + /// + /// Occurs, when a tab control is clicked. + /// + public event TabCommandEventHandler Click + { + add { base.Events.AddHandler(_eventClick, value); } + remove { base.Events.RemoveHandler(_eventClick, value); } + } + + /// + /// Catches with name '' and + /// raises the event. + /// + /// The source of the event. + /// contains event information. + /// + protected override bool OnBubbleEvent(object source, EventArgs args) + { + CommandEventArgs cea = args as CommandEventArgs; + if (cea != null && cea.CommandName == SelectTabCommandName) + { + this.OnTabClickedCommand(source, (CommandEventArgs) args); + return true; + } + + return base.OnBubbleEvent(source, args); + } + + /// + /// Raises the event. + /// + protected virtual void OnTabClickedCommand(object sender, CommandEventArgs args) + { + TabCommandEventHandler handler = (TabCommandEventHandler) base.Events[_eventClick]; + if (handler != null) + { + handler(sender, new TabCommandEventArgs(args.CommandName, Int32.Parse((string) args.CommandArgument))); + } + } + + /// + /// Creates a new tab for the specified view within the given container. + /// + /// The containing the view + /// The for which a new tab is to be created. + /// The index of the tab to be created. + /// + /// By default, controls are used for rendering tabs. Override this method to + /// change this behaviour. + /// + protected internal virtual void CreateTab(TabularMultiView container, TabularView view, int index) + { + LinkButton btnTab = new LinkButton(); + btnTab.Text = view.TabName; + btnTab.CommandName = "SelectTab"; + btnTab.CommandArgument = "" + index; + btnTab.ID = "Tab" + index; + btnTab.ToolTip = view.TabToolTip; + btnTab.Enabled = !view.Active; + + WebControl span = new WebControl(HtmlTextWriterTag.Span); + span.CssClass = (view.Active) ? container.TabularMenuSelectedItemCSS : container.TabularMenuItemCSS; + span.Controls.Add(btnTab); + this.Controls.Add(span); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/TabularMultiView.cs b/src/Spring/Spring.Web/Web/UI/Controls/TabularMultiView.cs new file mode 100644 index 00000000..f06ae0ae --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/TabularMultiView.cs @@ -0,0 +1,300 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// The control allows you to build ASP.NET Web pages that present + /// the user with content arranged in tabular form. + /// + /// Erich Eichinger + /// $Id: TabularMultiView.cs,v 1.1 2007/07/24 13:33:28 oakinger Exp $ + [ToolboxData("<{0}:TabularMultiView runat=\"server\">")] + [ParseChildren(false)] + public class TabularMultiView : WebControl + { + #region Style Properties + + private string m_MenuStyle = "TabMenu"; + private string m_BodyStyle = "TabBody"; + private string m_TabItemStyle = "TabItem"; + private string m_TabSelectedItemStyle = "TabSelectedItem"; + + /// + /// Set the style class of the panel containing the Tabs. + /// + [Bindable(true), Category("Appearance"), DefaultValue(typeof(string), "TabMenu")] + public string TabularMenuCSS + { + get { return m_MenuStyle; } + set { m_MenuStyle = value; } + } + + /// + /// Set the style class of each Tab item. + /// + [Bindable(true), DefaultValue(typeof(string), "TabItem"), Category("Appearance")] + public string TabularMenuItemCSS + { + get { return (m_TabItemStyle); } + set { m_TabItemStyle = value; } + } + + /// + /// Set the style class of the currently selected Tab item. + /// + [Bindable(true), DefaultValue(typeof(string), "TabSelectedItem"), Category("Appearance")] + public string TabularMenuSelectedItemCSS + { + get { return m_TabSelectedItemStyle; } + set { m_TabSelectedItemStyle = value; } + } + + /// + /// Set the style class of the panel containing all controls. + /// + [Bindable(true), Category("Appearance"), DefaultValue(typeof(string), "TabBody")] + public string TabularBodyCSS + { + get { return m_BodyStyle; } + set { m_BodyStyle = value; } + } + + #endregion + + #region Public Members + + /// + /// Initializes a new instance. + /// + public TabularMultiView() + : this(HtmlTextWriterTag.Div) + { + } + + /// + /// Initializes a new instance with the given container tag to be used for rendering. + /// + protected TabularMultiView(HtmlTextWriterTag containerTag) + : base(containerTag) + { + } + + /// + /// Gets or sets the index of the active View control within a control. + /// + public int ActiveTabIndex + { + get + { + if (!_controlStateInitialized) + { + return _activeViewIndexCached; + } + else + { + return _multiView.ActiveViewIndex; + } + } + set + { + if (!_controlStateInitialized) + { + _activeViewIndexCached = value; + } + else + { + _multiView.ActiveViewIndex = value; + } + } + } + + /// + /// Occurs, if the active tab has changed. + /// + public event EventHandler ActiveTabChanged + { + add { base.Events.AddHandler(_eventActiveTabChanged, value); } + remove { base.Events.RemoveHandler(_eventActiveTabChanged, value); } + } + + #endregion Public Members + + #region Customizable Members + + /// + /// Create the container for tab items. + /// + protected virtual TabContainer CreateTabContainer() + { + return new TabContainer(); + } + + /// + /// Creates TabContainer and MultiView + /// + protected virtual Control CreateContent(TabContainer menu, MultiView body) + { + Control content = new Control(); + + WebControl menuPanel = new WebControl(HtmlTextWriterTag.Div); + menuPanel.CssClass = TabularMenuCSS; + menuPanel.Controls.Add(menu); + content.Controls.Add(menuPanel); + + WebControl bodyPanel = new WebControl(HtmlTextWriterTag.Div); + bodyPanel.CssClass = TabularBodyCSS; + bodyPanel.Controls.Add(body); + content.Controls.Add(bodyPanel); + + return content; + } + + #endregion + + #region Fields + + private static readonly object _eventActiveTabChanged = new object(); + + private int _activeViewIndexCached = -1; + private bool _controlStateInitialized = false; + + private TabContainer _tabContainer; + private MultiView _multiView; + + /// + /// keeps parsed views until multiView is created + /// + private ArrayList _parsedViews = new ArrayList(); + + #endregion + + /// + /// Initialize this control. + /// + protected override void OnInit(EventArgs e) + { + BuildControlTree(); + + base.OnInit(e); + } + + private void BuildControlTree() + { + _controlStateInitialized = true; + + EnsureChildControls(); + } + + /// + /// Creates child controls. + /// + protected override void CreateChildControls() + { + // create menu tabstrip + _tabContainer = CreateTabContainer(); + _tabContainer.Click += new TabCommandEventHandler(OnSelectTabCommand); + + // create multiview container + _multiView = new MultiView(); + _multiView.ActiveViewChanged += new EventHandler(OnActiveViewChanged); + + // add views previously parsed + for (int i = 0; i < _parsedViews.Count; i++) _multiView.Controls.Add((Control) _parsedViews[i]); + _parsedViews = null; + + // select defined view + if (_activeViewIndexCached != -1) + { + _multiView.ActiveViewIndex = _activeViewIndexCached; + _activeViewIndexCached = -1; + } + + // create content pane + Control content = CreateContent(_tabContainer, _multiView); + Controls.Add(content); + + RebuildTabs(); + } + + /// + /// Adds the element to the collection of child controls. + /// + protected override void AddParsedSubObject(object obj) + { + // remember parsed views for later + if (obj is TabularView) + { + _parsedViews.Add(obj); + } + else if (!(obj is LiteralControl)) + { + throw new HttpException( + string.Format("TabularMultiView_cannot_have_children_of_type '{0}'", obj.GetType().FullName)); + } + } + + /// + /// Called if ActiveViewIndex is changed + /// + private void RebuildTabs() + { + _tabContainer.Controls.Clear(); + + ViewCollection views = _multiView.Views; + for (int i = 0; i < views.Count; i++) + { + TabularView view = (TabularView) views[i]; + _tabContainer.CreateTab(this, view, i); + } + } + + private void OnSelectTabCommand(object sender, TabCommandEventArgs e) + { + int selectedIndex = e.TabIndex; + if (selectedIndex != _multiView.ActiveViewIndex) + { + // will trigger OnActiveViewChanged + _multiView.ActiveViewIndex = selectedIndex; + RebuildTabs(); + } + } + + private void OnActiveViewChanged(object sender, EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[_eventActiveTabChanged]; + if (handler != null) + { + handler(this, e); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/TabularView.cs b/src/Spring/Spring.Web/Web/UI/Controls/TabularView.cs new file mode 100644 index 00000000..0ad74d99 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/TabularView.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Reflection; +using System.ComponentModel; +using System.Web.UI; +using System.Web.UI.WebControls; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// Represents a control that acts as a container for a group of controls within a control. + /// + /// Erich Eichinger + /// $Id: TabularView.cs,v 1.2 2007/07/24 14:32:58 oakinger Exp $ + [ToolboxData("<{0}:TabularView runat=\"server\">")] + public class TabularView : View + { +#if NET_2_0 + private static readonly PropertyInfo s_fiActive = + typeof(View).GetProperty("Active", BindingFlags.Instance | BindingFlags.NonPublic); + + /// + /// Indicates if this view is currently active. + /// + public bool Active + { + get { return (bool) s_fiActive.GetValue(this, null); } + } +#endif + private string m_TabToolTip; + + /// + /// Get or Set the name of the tab item associated with this view. + /// + [Description("Name of the tab.") + , NotifyParentProperty(true) + , DefaultValue("Tab") + , Category("Behavior")] + public string TabName + { + get + { + string str = (string) ViewState["m_TabName"]; + return str; + } + set { ViewState["m_TabName"] = value; } + } + + /// + /// Get or Set the tooltip text of the tab item associated with this view. + /// + [Description("Tooltip of the tab.") + , NotifyParentProperty(true) + , DefaultValue("") + , Category("Behavior")] + public string TabToolTip + { + get { return m_TabToolTip; } + set { m_TabToolTip = value; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/TabularViewCollection.cs b/src/Spring/Spring.Web/Web/UI/Controls/TabularViewCollection.cs new file mode 100644 index 00000000..f1535767 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/TabularViewCollection.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Security.Permissions; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// Holds the collection of controls in a . + /// + /// Erich Eichinger + /// $Id: TabularViewCollection.cs,v 1.1 2007/07/24 13:33:28 oakinger Exp $ + [ + AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal) + , AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)] + public class TabularViewCollection : ViewCollection + { + /// + /// Initialize a new instance. + /// + /// + public TabularViewCollection(Control owner) : base(owner) + { + } + + /// + /// Add the specified control to the collection. + /// + public override void Add(Control v) + { + if (!(v is TabularView)) + { + throw new ArgumentException("ViewCollection_must_contain_view"); + } + base.Add(v); + } + + /// + /// Add the specified control to the collection. + /// + public override void AddAt(int index, Control v) + { + if (!(v is TabularView)) + { + throw new ArgumentException("ViewCollection_must_contain_view"); + } + base.AddAt(index, v); + } + + /// + /// Obtain the specified control from the collection. + /// + public new TabularView this[int i] + { + get { return (TabularView) base[i]; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/ValidationError.cs b/src/Spring/Spring.Web/Web/UI/Controls/ValidationError.cs new file mode 100644 index 00000000..6c035010 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/ValidationError.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Web.UI.Validation; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// This control should be used to display field-level validation errors. + /// + /// Aleksandar Seovic + /// Jonathan Allenby + /// $Id: ValidationError.cs,v 1.11 2008/03/19 12:07:14 oakinger Exp $ + public class ValidationError : AbstractValidationControl + { + /// + /// Create the default + /// for this ValidationControl if none is configured. + /// + protected override IValidationErrorsRenderer CreateValidationErrorsRenderer() + { + return new SpanValidationErrorsRenderer(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/ValidationSummary.cs b/src/Spring/Spring.Web/Web/UI/Controls/ValidationSummary.cs new file mode 100644 index 00000000..3f56d6d7 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/ValidationSummary.cs @@ -0,0 +1,47 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Web.UI.Validation; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// This control should be used instead of standard ValidationSummary control + /// to display validation errors identified by the Spring.NET validation framework. + /// + /// Aleksandar Seovic + /// Jonathan Allenby + /// $Id: ValidationSummary.cs,v 1.11 2008/03/19 12:07:14 oakinger Exp $ + public class ValidationSummary : AbstractValidationControl + { + /// + /// Create the default + /// for this ValidationControl if none is configured. + /// + protected override IValidationErrorsRenderer CreateValidationErrorsRenderer() + { + return new DivValidationErrorsRenderer(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/View.cs b/src/Spring/Spring.Web/Web/UI/Controls/View.cs new file mode 100644 index 00000000..9fe2b6e9 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/View.cs @@ -0,0 +1,136 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Security.Permissions; +using System.Web; +using System.Web.UI; + +#endregion + +namespace Spring.Web.UI.Controls +{ +#if !NET_2_0 + /// + /// Represents a control that acts as a container for a group of controls within a control. + /// + [ParseChildren(false, null) + , ToolboxData("<{0}:View runat=\"server\">") + , AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal) + , AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)] + public class View : Control + { + private bool _active; + private static readonly object _eventActivate = new object(); + private static readonly object _eventDeactivate = new object(); + + /// + /// Occurs when the current View control becomes the active view. + /// + public event EventHandler Activate + { + add { base.Events.AddHandler(_eventActivate, value); } + remove { base.Events.RemoveHandler(_eventActivate, value); } + } + + /// + /// Occurs when the current active View control becomes inactive. + /// + public event EventHandler Deactivate + { + add { base.Events.AddHandler(_eventDeactivate, value); } + remove { base.Events.RemoveHandler(_eventDeactivate, value); } + } + + /// + /// Raises the event of the View control. + /// + protected internal virtual void OnActivate(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[_eventActivate]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Raises the event of the View control. + /// + protected internal virtual void OnDeactivate(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[_eventDeactivate]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Gets a value that indicates, if this control has been instantiated by a designer. + /// + protected bool DesignMode + { + get { return Context == null; } + } + + /// + /// Gets or Sets a value that indicates, if this control is currently active. + /// + internal bool Active + { + get { return _active; } + set + { + _active = value; + base.Visible = value; + } + } + + /// + /// Gets a value that indicates, whether this view will be rendered as UI. + /// + public override bool Visible + { + get + { + if (Parent == null) + { + return Active; + } + if (Active) + { + return Parent.Visible; + } + return false; + } + set + { + if (!DesignMode) + { + throw new InvalidOperationException("View_CannotSetVisible"); + } + } + } + } +#endif // !NET_2_0 +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Controls/ViewCollection.cs b/src/Spring/Spring.Web/Web/UI/Controls/ViewCollection.cs new file mode 100644 index 00000000..81ffb7de --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Controls/ViewCollection.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Security.Permissions; +using System.Web; +using System.Web.UI; + +#endregion + +namespace Spring.Web.UI.Controls +{ +#if !NET_2_0 + /// + /// Represents a collection container that enables a control to maintain a list of its child controls. + /// + /// Erich Eichinger + /// $Id: ViewCollection.cs,v 1.1 2007/07/24 13:33:28 oakinger Exp $ + [ + AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal) + , AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)] + public class ViewCollection : ControlCollection + { + /// + /// Initializes a new instance. + /// + /// + public ViewCollection(Control owner) : base(owner) + { + } + + /// + /// Add the specified control to the collection. + /// + public override void Add(Control v) + { + if (!(v is View)) + { + throw new ArgumentException("ViewCollection_must_contain_view"); + } + base.Add(v); + } + + /// + /// Add the specified control to the collection. + /// + public override void AddAt(int index, Control v) + { + if (!(v is View)) + { + throw new ArgumentException("ViewCollection_must_contain_view"); + } + base.AddAt(index, v); + } + + /// + /// Obtain the specified control from the collection. + /// + public new View this[int i] + { + get { return (View) base[i]; } + } + } +#endif // !NET_2_0 +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/DialogAttribute.cs b/src/Spring/Spring.Web/Web/UI/DialogAttribute.cs new file mode 100644 index 00000000..0cafaa86 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/DialogAttribute.cs @@ -0,0 +1,44 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Web.UI +{ + /// + /// Specifies that page should be treated as a dialog, meaning that after processing + /// is over user should return to the referring page. + /// + /// + ///

    + /// Pages marked with this attribute will have "close" result predefined. + ///

    + ///

    + /// Developers should call SetResult("close") from the event handler + /// in order to return control back to the calling page. + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: DialogAttribute.cs,v 1.3 2006/05/18 21:37:52 markpollack Exp $ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] + public class DialogAttribute : Attribute + { + } +} diff --git a/src/Spring/Spring.Web/Web/UI/IValidationContainer.cs b/src/Spring/Spring.Web/Web/UI/IValidationContainer.cs new file mode 100644 index 00000000..019d8363 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/IValidationContainer.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Context; +using Spring.Validation; + +#endregion + +namespace Spring.Web.UI +{ + /// + /// Abstracts access to properties required for + /// handling validation and error rendering + /// + /// Erich Eichinger + /// $Id: IValidationContainer.cs,v 1.1 2008/03/19 12:07:15 oakinger Exp $ + public interface IValidationContainer + { + /// + /// Gets the MessageSource to be used for + /// resolving error ids into messages + /// + IMessageSource MessageSource { get; } + /// + /// Gets the list of validation errors kept by this container. + /// + IValidationErrors ValidationErrors { get; } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/MasterPage.cs b/src/Spring/Spring.Web/Web/UI/MasterPage.cs new file mode 100644 index 00000000..e1ec4937 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/MasterPage.cs @@ -0,0 +1,513 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Web.UI; +using Spring.Validation; +using Spring.Web.UI.Controls; +using IValidator = Spring.Validation.IValidator; + +#if NET_2_0 +using Spring.Context; +using Spring.Context.Support; +using Spring.Globalization; +using Spring.Util; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using System.Resources; +using System.Web; +using System.Web.Compilation; +using Spring.Web.Support; +#endif + +#endregion Imports + +namespace Spring.Web.UI +{ +#if !NET_2_0 + + #region ASP.NET 1.1 Spring Master Page Implementation + + /// + /// Spring.NET Master Page implementation for ASP.NET 1.1 + /// + /// Aleksandar Seovic + /// $Id: MasterPage.cs,v 1.15 2008/04/22 15:08:59 markpollack Exp $ + public class MasterPage : UserControl + { + /// + /// Initializes master page. + /// + public void Initialize(Page childPage) + { + InitializeAsUserControl(childPage); + this.ID = "masterPage"; + + Control[] controls = new Control[childPage.Controls.Count]; + childPage.Controls.CopyTo(controls, 0); + + for (int i = 0; i < controls.Length; i++) + { + if (controls[i] is Content) + { + Content content = (Content) controls[i]; + ContentPlaceHolder placeholder = (ContentPlaceHolder) this.FindControl(content.ContentPlaceHolderID); + if (placeholder == null) + { + throw new ArgumentException("Content placeholder " + content.ContentPlaceHolderID + " does not exist in the master page."); + } + + placeholder.Content = content; + } + } + + childPage.Controls.AddAt(0, this); + } + + + /// + /// Delegate validation errors to the owning page. + /// + public override IValidationErrors ValidationErrors + { + get { return Page.ValidationErrors; } + } + } + + #endregion + +#else + + #region ASP.NET 2.0 Spring Master Page Implementation + + + /// + /// Spring.NET Master Page implementation for ASP.NET 2.0 + /// + /// Aleksandar Seovic + /// $Id: MasterPage.cs,v 1.15 2008/04/22 15:08:59 markpollack Exp $ + public class MasterPage : System.Web.UI.MasterPage, IApplicationContextAware, ISupportsWebDependencyInjection + { + #region Instance Fields + + private ILocalizer localizer; + private IValidationErrors validationErrors = new ValidationErrors(); + private IMessageSource messageSource; + private IApplicationContext applicationContext; + private IApplicationContext defaultApplicationContext; + + #endregion + + #region Control lifecycle methods + + /// + /// Initializes user control. + /// + protected override void OnInit(EventArgs e) + { + InitializeMessageSource(); + + base.OnInit(e); + + // initialize controls + OnInitializeControls(EventArgs.Empty); + } + + /// + /// Binds data from the data model into controls and raises + /// PreRender event afterwards. + /// + /// Event arguments. + protected override void OnPreRender(EventArgs e) + { + if (localizer != null) + { + localizer.ApplyResources(this, messageSource, UserCulture); + } + else if (Page.Localizer != null) + { + Page.Localizer.ApplyResources(this, messageSource, UserCulture); + } + + base.OnPreRender(e); + } + + /// + /// This event is raised before Load event and should be used to initialize + /// controls as necessary. + /// + public event EventHandler InitializeControls; + + /// + /// Raises InitializeControls event. + /// + /// Event arguments. + protected virtual void OnInitializeControls(EventArgs e) + { + if (InitializeControls != null) + { + InitializeControls(this, e); + } + } + + /// + /// Obtains a object from a user control file + /// and injects dependencies according to Spring config file. + /// + /// The virtual path to a user control file. + /// + /// Returns the specified object, with dependencies injected. + /// + protected new Control LoadControl(string virtualPath) + { + Control control = base.LoadControl(virtualPath); + WebDependencyInjectionUtils.InjectDependenciesRecursive(defaultApplicationContext,control); + return control; + } + + /// + /// Obtains a object by type + /// and injects dependencies according to Spring config file. + /// + /// The type of a user control. + /// parameters to pass to the control + /// + /// Returns the specified object, with dependencies injected. + /// + protected new Control LoadControl( Type t, params object[] parameters) + { + Control control = base.LoadControl( t, parameters ); + WebDependencyInjectionUtils.InjectDependenciesRecursive(defaultApplicationContext,control); + return control; + } + + #endregion Control lifecycle methods + + #region Data binding events + + /// + /// This event is raised after all controls have been populated with values + /// from the data model. + /// + public event EventHandler DataBound; + + /// + /// Raises DataBound event. + /// + /// Event arguments. + protected virtual void OnDataBound(EventArgs e) + { + if (DataBound != null) + { + DataBound(this, e); + } + } + + /// + /// This event is raised after data model has been populated with values from + /// web controls. + /// + public event EventHandler DataUnbound; + + /// + /// Raises DataBound event. + /// + /// Event arguments. + protected virtual void OnDataUnbound(EventArgs e) + { + if (DataUnbound != null) + { + DataUnbound(this, e); + } + } + + #endregion + + #region Application context support + + /// + /// Gets or sets Spring application context. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual IApplicationContext ApplicationContext + { + get { return applicationContext; } + set { applicationContext = value; } + } + + #endregion + + #region Message source and localization support + + /// + /// Gets or sets the localizer. + /// + /// The localizer. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ILocalizer Localizer + { + get { return localizer; } + set + { + localizer = value; + if (localizer.ResourceCache is NullResourceCache) + { + localizer.ResourceCache = new AspNetResourceCache(); + } + } + } + + /// + /// Gets or sets the local message source. + /// + /// The local message source. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IMessageSource MessageSource + { + get { return messageSource; } + set + { + messageSource = value; + if (messageSource != null && messageSource is AbstractMessageSource) + { + ((AbstractMessageSource) messageSource).ParentMessageSource = applicationContext; + } + } + } + + /// + /// Initializes local message source + /// + protected void InitializeMessageSource() + { + if (MessageSource == null) + { + string key = GetType().FullName + ".MessageSource"; + MessageSource = (IMessageSource) Context.Cache.Get(key); + + if (MessageSource == null) + { + ResourceSetMessageSource defaultMessageSource = new ResourceSetMessageSource(); + ResourceManager rm = GetLocalResourceManager(); + if (rm != null) + { + defaultMessageSource.ResourceManagers.Add(rm); + } + Context.Cache.Insert(key, defaultMessageSource); + MessageSource = defaultMessageSource; + } + } + } + + /// + /// Creates and returns local ResourceManager for this page. + /// + /// + /// + /// In ASP.NET 1.1, this method loads local resources from the web application assembly. + /// + /// + /// However, in ASP.NET 2.0, local resources are compiled into the dynamic assembly, + /// so we need to find that assembly instead and load the resources from it. + /// + /// + /// Local ResourceManager instance. + private ResourceManager GetLocalResourceManager() + { + object resourceProvider = Page.GetLocalResourceProvider.Invoke(typeof(ResourceExpressionBuilder), new object[] {this}); + MethodInfo GetLocalResourceAssembly = + resourceProvider.GetType().GetMethod("GetLocalResourceAssembly", BindingFlags.NonPublic | BindingFlags.Instance); + Assembly localResourceAssembly = (Assembly) GetLocalResourceAssembly.Invoke(resourceProvider, null); + if (localResourceAssembly != null) + { + return new ResourceManager(VirtualPathUtility.GetFileName(this.AppRelativeVirtualPath), localResourceAssembly); + } + return null; + } + + /// + /// Returns message for the specified resource name. + /// + /// Resource name. + /// Message text. + public string GetMessage(string name) + { + return messageSource.GetMessage(name, UserCulture); + } + + /// + /// Returns message for the specified resource name. + /// + /// Resource name. + /// Message arguments that will be used to format return value. + /// Formatted message text. + public string GetMessage(string name, params object[] args) + { + return messageSource.GetMessage(name, UserCulture, args); + } + + /// + /// Returns resource object for the specified resource name. + /// + /// Resource name. + /// Resource object. + public object GetResourceObject(string name) + { + return messageSource.GetResourceObject(name, UserCulture); + } + + /// + /// Gets or sets user's culture + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual CultureInfo UserCulture + { + get { return Page.UserCulture; } + set { Page.UserCulture = value; } + } + + #endregion + + #region Validation support + + /// + /// Evaluates specified validators and returns True if all of them are valid. + /// + /// + ///

    + /// Each validator can itself represent a collection of other validators if it is + /// an instance of or one of its derived types. + ///

    + ///

    + /// Please see the Validation Framework section in the documentation for more info. + ///

    + ///
    + /// Object to validate. + /// Validators to evaluate. + /// + /// True if all of the specified validators are valid, False otherwise. + /// + public virtual bool Validate(object validationContext, params IValidator[] validators) + { + IDictionary contextParams = CreateValidatorParameters(); + bool result = true; + foreach (IValidator validator in validators) + { + if (validator == null) + { + throw new ArgumentException("Validator is not defined."); + } + result = validator.Validate(validationContext, contextParams, this.validationErrors) && result; + } + + return result; + } + + /// + /// Gets the validation errors container. + /// + /// The validation errors container. + public virtual IValidationErrors ValidationErrors + { + get { return validationErrors; } + } + + /// + /// Creates the validator parameters. + /// + /// + /// + /// This method can be overriden if you want to pass additional parameters + /// to the validation framework, but you should make sure that you call + /// this base implementation in order to add page, session, application, + /// request, response and context to the variables collection. + /// + /// + /// + /// Dictionary containing parameters that should be passed to + /// the data validation framework. + /// + protected virtual IDictionary CreateValidatorParameters() + { + IDictionary parameters = new ListDictionary(); + parameters["page"] = this.Page; + parameters["usercontrol"] = this; + parameters["session"] = this.Session; + parameters["application"] = this.Application; + parameters["request"] = this.Request; + parameters["response"] = this.Response; + parameters["context"] = this.Context; + + return parameters; + } + + #endregion + + #region Spring Page support + + /// + /// Overrides Page property to return + /// instead of . + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new Page Page + { + get { return (Page) base.Page; } + } + + #endregion + + #region Dependency Injection Support + + /// + /// Holds the default ApplicationContext to be used during DI. + /// + IApplicationContext ISupportsWebDependencyInjection.DefaultApplicationContext + { + get { return defaultApplicationContext; } + set { defaultApplicationContext = value; } + } + + /// + /// Injects dependencies before adding the control. + /// + protected override void AddedControl(Control control,int index) + { + WebDependencyInjectionUtils.InjectDependenciesRecursive(defaultApplicationContext,control); + base.AddedControl(control,index); + } + + #endregion Dependency Injection Support + } + + #endregion + +#endif +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Page.cs b/src/Spring/Spring.Web/Web/UI/Page.cs new file mode 100644 index 00000000..b8f34263 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Page.cs @@ -0,0 +1,1667 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using System.Resources; +using System.Security.Permissions; +using System.Threading; +using System.Web; +using System.Web.UI; +using Spring.Context; +using Spring.Context.Support; +using Spring.Core; +using Spring.DataBinding; +using Spring.Globalization; +using Spring.Globalization.Resolvers; +using Spring.Util; +using Spring.Validation; +using Spring.Web.Process; +using Spring.Web.Support; +#if NET_2_0 +using System.Web.Compilation; +#endif +using IValidator=Spring.Validation.IValidator; + +#endregion + +namespace Spring.Web.UI +{ + /// + /// Represents an .aspx file, also known as a Web Forms page, requested from a + /// server that hosts an ASP.NET Web application. + /// + /// + ///

    + /// The Page class is associated with files that have an .aspx extension. + /// These files are compiled at run time as Page objects and cached in server memory. + ///

    + ///

    + /// This class extends and adds support for master + /// pages similar to upcoming ASP.Net 2.0 master pages feature. + ///

    + ///

    + /// It also adds support for automatic localization using local page resource file, and + /// simplifies access to global resources (resources from the message source for the + /// application context). + ///

    + ///
    + /// Aleksandar Seovic + /// $Id: Page.cs,v 1.89 2008/04/03 04:32:29 markpollack Exp $ + [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + public class Page : System.Web.UI.Page, IHttpHandler, IApplicationContextAware, ISharedStateAware, IProcessAware, + ISupportsWebDependencyInjection, IWebDataBound, IValidationContainer + { + #region Constants + + private static readonly object EventInitializeControls = new object(); + private static readonly object EventPreLoadViewState = new object(); + private static readonly object EventDataBindingsInitialized = new object(); + private static readonly object EventDataBound = new object(); + private static readonly object EventDataUnbound = new object(); +#if !NET_2_0 + private static readonly object EventPreInit = new object(); + private static readonly object EventInitComplete = new object(); +#else + internal static readonly MethodInfo GetLocalResourceProvider = + typeof(ResourceExpressionBuilder).GetMethod("GetLocalResourceProvider", BindingFlags.NonPublic | BindingFlags.Static, null, + new Type[] {typeof(TemplateControl)}, null); +#endif + + #endregion + + #region Instance Fields + +#if !NET_2_0 + private static readonly FieldInfo _fiRequestValueCollection = + typeof(System.Web.UI.Page).GetField("_requestValueCollection", + BindingFlags.Instance | BindingFlags.NonPublic); + + private bool passedPreInit = false; + private bool passedInit = false; + private bool passedTrackViewState = false; + + private MasterPage master; + private String masterPageFile; +#endif + private IProcess process; + private object controller; + private IDictionary sharedState; + + private ILocalizer localizer; + private ICultureResolver cultureResolver = new DefaultWebCultureResolver(); + private IMessageSource messageSource; + private IBindingContainer bindingManager; + private IValidationErrors validationErrors = new ValidationErrors(); + + private IDictionary results; + private IApplicationContext applicationContext; + private IApplicationContext defaultApplicationContext; + private string traceCategory = "Spring.Page"; + + private IDictionary styles = new ListDictionary(); + private IDictionary styleFiles = new ListDictionary(); + private IDictionary headScripts = new ListDictionary(); + private string cssRoot = "CSS"; + private string scriptsRoot = "Scripts"; + private string imagesRoot = "Images"; + + #endregion + + #region Page lifecycle methods + +#if !NET_2_0 + /// + /// Determines the type of request made for the Page class. + /// + /// + /// If the post back used the POST method, the form information is returned from the Context object. + /// If the postback used the GET method, the query string information is returned. + /// If the page is being requested for the first time, a null reference (Nothing in Visual Basic) is returned. + /// + /// + /// Adds support for OnPreInit() in NET 1.1.
    + /// Overriding this method is the closest match to NET 2.0 OnPreInit()-behaviour. + ///
    + protected override NameValueCollection DeterminePostBackMode() + { + NameValueCollection requestValueCollection = base.DeterminePostBackMode(); + if (!passedPreInit) + { + _fiRequestValueCollection.SetValue(this, requestValueCollection); + passedPreInit = true; + OnPreInit(EventArgs.Empty); + } + return requestValueCollection; + } + + /// + /// Overridden to add support for . + /// + protected override void TrackViewState() + { + base.TrackViewState(); + if (passedInit && !passedTrackViewState) + { + passedTrackViewState = true; + OnInitComplete(EventArgs.Empty); + } + } + + /// + /// Gets a value indicating whether view state is enabled for this page. + /// + protected internal bool IsViewStateEnabled + { + get + { + for (Control parent = this; parent != null; parent = parent.Parent) + { + if (!parent.EnableViewState) + { + return false; + } + } + return true; + } + } +#endif + + /// + /// Initializes Spring.NET page internals and raises the PreInit event. + /// + /// The instance containing the event data. +#if !NET_2_0 + protected virtual void OnPreInit(EventArgs e) +#else + protected override void OnPreInit(EventArgs e) +#endif + { + InitializeCulture(); + InitializeMessageSource(); +#if !NET_2_0 + EventHandler handler = (EventHandler) base.Events[EventPreInit]; + if (handler != null) + { + handler(this, e); + } +#else + base.OnPreInit(e); +#endif + } + +#if !NET_2_0 + /// + /// PreInit event. + /// + public event EventHandler PreInit + { + add { base.Events.AddHandler(EventPreInit, value); } + remove { base.Events.RemoveHandler(EventPreInit, value); } + } +#endif + + /// + /// Initializes the culture. + /// +#if !NET_2_0 + protected virtual void InitializeCulture() +#else + protected override void InitializeCulture() +#endif + { + CultureInfo userCulture = this.UserCulture; + Thread.CurrentThread.CurrentUICulture = userCulture; + if (userCulture.IsNeutralCulture) + { + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(userCulture.Name); + } + else + { + Thread.CurrentThread.CurrentCulture = userCulture; + } + } + +#if !NET_2_0 + /// + /// Gets a value indicating whether this instance is in design mode. + /// + /// + /// true if this instance is in design mode; otherwise, false. + /// + protected bool DesignMode + { + get { return this.Context == null; } + } +#endif + + /// + /// Initializes data model and controls. + /// + protected override void OnInit(EventArgs e) + { + InitializeBindingManager(); + + if (!IsPostBack) + { + InitializeModel(); + } + else + { + LoadModel(LoadModelFromPersistenceMedium()); + } + +#if !NET_2_0 + if (HasMaster) + { + Trace.Write(traceCategory, "Initialize Master"); + master = (MasterPage) this.LoadControl(MasterPageFile); + master.Initialize(this); + } +#endif + base.OnInit(e); + + // initialize controls + Trace.Write(traceCategory, "Initialize Controls"); + OnInitializeControls(EventArgs.Empty); + +#if !NET_2_0 + passedInit = true; +#endif + } + + +#if !NET_2_0 + /// + /// Raises the event after page initialization. + /// + protected virtual void OnInitComplete(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[EventInitComplete]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// InitComplete event. + /// + public event EventHandler InitComplete + { + add { base.Events.AddHandler(EventInitComplete, value); } + remove { base.Events.RemoveHandler(EventInitComplete, value); } + } +#endif + /// + /// Raises the event after page initialization. + /// + protected virtual void OnPreLoadViewState(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[EventPreLoadViewState]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// PreLoadViewState event. + /// + /// + /// + /// This event is raised if is true + /// immediately before state is restored from ViewState. + /// + /// + /// NOTE: Different from , this event + /// will also be raised if the control has no ViewState or ViewState is disabled. + /// + /// + public event EventHandler PreLoadViewState + { + add { base.Events.AddHandler(EventPreLoadViewState, value); } + remove { base.Events.RemoveHandler(EventPreLoadViewState, value); } + } + + /// + /// Raises the PreLoadViewState event for + /// this page and all contained controls. + /// + private void RaisePreLoadViewStateEvent() + { + this.OnPreLoadViewState(EventArgs.Empty); + if (this.HasControls()) + { + PreLoadViewStateRecursive(this.Controls); + } + } + + /// + /// Recursively raises PreLoadViewState event. + /// + private void PreLoadViewStateRecursive(ControlCollection controls) + { + for (int i = 0; i < controls.Count; i++) + { + Control control = controls[i]; + + if (control is UserControl) + { + ((UserControl) control).OnPreLoadViewState(EventArgs.Empty); + } + + if (control.HasControls()) + { + PreLoadViewStateRecursive(control.Controls); + } + } + } + + /// + /// Overridden to add support for + /// + /// + /// If necessary override instead of this method. + /// + protected override object LoadPageStateFromPersistenceMedium() + { + RaisePreLoadViewStateEvent(); + + // If ViewState is disabled, use BindFormData() to populate controls. + BindFormDataIfNecessary(); + + // continue with default behaviour + return LoadPageStateFromPersistenceMediumBase(); + } + + /// + /// If ViewState is disabled, calls recursively for all controls. + /// + /// + /// If ViewState is disabled, DropDownLists etc. might not fire "Changed" events. + /// + protected virtual void BindFormDataIfNecessary() + { + if (!IsViewStateEnabled) + { + BindFormDataRecursive(); + } + } + + /// + /// Calls recursively for all controls. + /// + protected void BindFormDataRecursive() + { + this.BindFormData(); + if (this.HasControls()) + { + BindFormDataRecursive(this.Controls); + } + } + + /// + /// Recursively calls for all controls. + /// + private void BindFormDataRecursive(ControlCollection controls) + { + for(int i = 0; i < controls.Count; i++) + { + Control control = controls[i]; + + if(control is UserControl) + { + ((UserControl)control).BindFormData(); + } + + if(control.HasControls()) + { + BindFormDataRecursive(control.Controls); + } + } + } + + /// + /// If necessary override this method instead of + /// + protected virtual object LoadPageStateFromPersistenceMediumBase() + { + return base.LoadPageStateFromPersistenceMedium(); + } + + /// + /// Initializes dialog result and unbinds data from the controls + /// into a data model. + /// + /// Event arguments. + protected override void OnLoad(EventArgs e) + { + // create dialog result if necessary +// if (GetType().IsDefined(typeof(DialogAttribute), true)) +// { +// if (!IsPostBack) +// { +// ViewState["__dialogResult"] = "redirect:" + Request.UrlReferrer.AbsoluteUri; +// } +// Results["close"] = new Result((string) ViewState["__dialogResult"]); +// } + + if (IsPostBack) + { + // unbind form data + UnbindFormData(); + } + + + Trace.Write(traceCategory, "Execute Handlers for Load Event"); + base.OnLoad(e); + } + + /// + /// Binds data from the data model into controls and raises + /// PreRender event afterwards. + /// + /// Event arguments. + protected override void OnPreRender(EventArgs e) + { + if (Process == null || !Process.ViewChanged) + { + // bind data from model to form + BindFormData(); + + if (localizer != null) + { + Trace.Write(traceCategory, "Apply Localized Resources"); + localizer.ApplyResources(this, messageSource, UserCulture); + } + } + + base.OnPreRender(e); + + object modelToSave = SaveModel(); + if (modelToSave != null) + { + SaveModelToPersistenceMedium(modelToSave); + } + } + + /// + /// This event is raised before Init event and should be used to initialize + /// controls as necessary. + /// + public event EventHandler InitializeControls + { + add { base.Events.AddHandler(EventInitializeControls, value); } + remove { base.Events.RemoveHandler(EventInitializeControls, value); } + } + + /// + /// Raises InitializeControls event. + /// + /// Event arguments. + protected virtual void OnInitializeControls(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[EventInitializeControls]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Obtains a object from a user control file + /// and injects dependencies according to Spring config file. + /// + /// The virtual path to a user control file. + /// + /// Returns the specified object, with dependencies injected. + /// + protected new Control LoadControl(string virtualPath) + { + Control control = base.LoadControl(virtualPath); + WebDependencyInjectionUtils.InjectDependenciesRecursive(defaultApplicationContext, control); + return control; + } + +#if NET_2_0 + /// + /// Obtains a object by type + /// and injects dependencies according to Spring config file. + /// + /// The type of a user control. + /// parameters to pass to the control + /// + /// Returns the specified object, with dependencies injected. + /// + protected new Control LoadControl(Type t, params object[] parameters) + { + Control control = base.LoadControl(t, parameters); + WebDependencyInjectionUtils.InjectDependenciesRecursive(defaultApplicationContext, control); + return control; + } +#endif + + #endregion + + #region Model Management Support + + /// + /// Initializes data model when the page is first loaded. + /// + /// + /// This method should be overriden by the developer + /// in order to initialize data model for the page. + /// + protected virtual void InitializeModel() + { + } + + /// + /// Retrieves data model from a persistence store. + /// + /// + /// The default implementation uses to store and retrieve + /// the model for the current + /// + protected virtual object LoadModelFromPersistenceMedium() + { + return Session[Request.CurrentExecutionFilePath + ".Model"]; + } + + /// + /// Saves data model to a persistence store. + /// + /// + /// The default implementation uses to store and retrieve + /// the model for the current + /// + protected virtual void SaveModelToPersistenceMedium(object modelToSave) + { + Session[Request.CurrentExecutionFilePath + ".Model"] = modelToSave; + } + + /// + /// Loads the saved data model on postback. + /// + /// + /// This method should be overriden by the developer + /// in order to load data model for the page. + /// + protected virtual void LoadModel(object savedModel) + { + } + + /// + /// Returns a model object that should be saved. + /// + /// + /// This method should be overriden by the developer + /// in order to save data model for the page. + /// + /// + /// A model object that should be saved. + /// + protected virtual object SaveModel() + { + return null; + } + + #endregion + + #region Process and Controller support + + /// + /// Gets or sets the process that this page belongs to. + /// + public IProcess Process + { + get { return this.process; } + set { this.process = value; } + } + + /// + /// Gets or sets controller for the page. + /// + /// + /// + /// Application pages should shadow this property and change its type + /// in order to make calls to controller within the page as simple as possible. + /// + /// + /// If external controller is not specified, page will serve as its own controller, + /// which will allow data binding to work properly. + /// + /// + /// Controller for the page. Defaults to the page itself. + public object Controller + { + get { return GetController(); } + set { SetController(value); } + } + + /// + /// Stores the controller to be returned by property. + /// + /// + /// The default implementation uses a field to store the reference. Derived classes may override this behaviour + /// but must ensure to also change the behaviour of accordingly. + /// + /// Controller for the page. + protected virtual void SetController(object controller) + { + this.controller = controller; + } + + /// + /// Returns the controller stored by . + /// + /// + /// The default implementation uses a field to retrieve the reference. Derived classes may override this behaviour + /// but must ensure to also change the behaviour of accordingly. + /// + /// + /// The controller for this page. + /// + /// If this page is being part of a process, the process' is returned. + /// If no controller is set, a reference to the page itself is returned. + /// + /// + protected virtual object GetController() + { + if (controller == null) + { + if (process != null) + { + return process.Controller; + } + else + { + return this; + } + } + return controller; + } + + #endregion + + #region Shared State support + + /// + /// Returns a thread-safe dictionary that contains state that is shared by + /// all instances of this page. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IDictionary SharedState + { + get { return this.sharedState; } + set { this.sharedState = value; } + } + +#if NET_2_0 + /// + /// Overrides the default PreviousPage property to return an instance of , + /// and to work properly during server-side transfers and executes. + /// + public new Page PreviousPage + { + get { return this.Context.PreviousHandler as Page; } + } +#endif + + #endregion + + #region Master Page support + +#if !NET_2_0 + /// + /// Reference to a master page template. + /// + /// + ///

    + /// Master page can be any user control that defines form element and one or more <spring:ContentPlaceHolder/> controls. + /// Placeholders in the master page can contain default content that will be replaced by the content defined in child pages. + ///

    + ///

    + /// Child pages should only define content they want to override using appropriate <spring:Content/> control + /// that references appropriate content placeholder in the master page. Child pages don't have to define + /// content elements for every placeholder in the master page. In that case, child page will simply inherit + /// default content from the placeholder. + ///

    + ///
    + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public MasterPage Master + { + get { return master; } + } + + /// + /// Convinience property that allows users to specify master page using its file name + /// + public String MasterPageFile + { + get { return masterPageFile; } + set { masterPageFile = value; } + } + + /// + /// Renders this page, taking master page into account. + /// + /// The object that receives the server control content. + protected override void Render(HtmlTextWriter writer) + { + if (HasMaster) + { + Master.RenderControl(writer); + } + else + { + base.Render(writer); + } + } +#else + /// + /// Gets the master page that determines the overall look of the page. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new MasterPage Master + { + get { return (MasterPage) base.Master; } + } + +#endif + + /// + /// Returns true if page uses master page, false otherwise. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool HasMaster + { + get { return Master != null || MasterPageFile != null; } + } + + #endregion + + #region CSS support + + /// + /// Gets a dictionary of registered styles. + /// + /// Registered styles. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IDictionary Styles + { + get { return styles; } + } + + /// + /// Gets a dictionary of registered style files. + /// + /// Registered style files. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IDictionary StyleFiles + { + get { return styleFiles; } + } + + /// + /// Registers single CSS style with the page. + /// + /// Style name. + /// Style definition. + public void RegisterStyle(string name, string style) + { + styles[name] = style; + } + + /// + /// Returns True if specified style is registered, False otherwise. + /// + /// Style name. + /// True if specified style is registered, False otherwise. + public bool IsStyleRegistered(string name) + { + return styles.Contains(name); + } + + /// + /// Registers CSS file with the page. + /// + /// Style file key. + /// Style file name. + public void RegisterStyleFile(string key, string fileName) + { + styleFiles[key] = fileName; + } + + /// + /// Returns True if specified style file is registered, False otherwise. + /// + /// Style file key. + /// True if specified style file is registered, False otherwise. + public bool IsStyleFileRegistered(string key) + { + return styleFiles.Contains(key); + } + + #endregion + + #region Client script support + + /// + /// Gets a dictionary of registered head scripts. + /// + /// Registered head scripts. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IDictionary HeadScripts + { + get { return headScripts; } + } + + /// + /// Registers script block that should be rendered within the head HTML element. + /// + /// Script key. + /// Script text. + public void RegisterHeadScriptBlock(string key, string script) + { + RegisterHeadScriptBlock(key, Script.DefaultType, script); + } + + /// + /// Registers script block that should be rendered within the head HTML element. + /// + /// Script key. + /// Script language. + /// Script text. + [Obsolete("The 'language' attribute is deprecated. Please use RegisterHeadScriptBlock(string key, MimeMediaType type, string script) instead", false)] + public void RegisterHeadScriptBlock(string key, string language, string script) + { + headScripts[key] = new ScriptBlock(language, script); + } + + /// + /// Registers script block that should be rendered within the head HTML element. + /// + /// Script key. + /// Script language MIME type. + /// Script text. + public void RegisterHeadScriptBlock(string key, MimeMediaType type, string script) + { + headScripts[key] = new ScriptBlock(type, script); + } + + /// + /// Registers script file that should be referenced within the head HTML element. + /// + /// Script key. + /// Script file name. + public void RegisterHeadScriptFile(string key, string fileName) + { + RegisterHeadScriptFile(key, Script.DefaultType, fileName); + } + + /// + /// Registers script file that should be referenced within the head HTML element. + /// + /// Script key. + /// Script language. + /// Script file name. + [Obsolete("The 'language' attribute is deprecated. Please use RegisterHeadScriptFile(string key, MimeMediaType type, string filename) instead", false)] + public void RegisterHeadScriptFile(string key, string language, string fileName) + { + headScripts[key] = new ScriptFile(language, fileName); + } + + /// + /// Registers script file that should be referenced within the head HTML element. + /// + /// Script key. + /// Script language MIME type. + /// Script file name. + public void RegisterHeadScriptFile(string key, MimeMediaType type, string fileName) + { + headScripts[key] = new ScriptFile(type, fileName); + } + + /// + /// Registers script block that should be rendered within the head HTML element. + /// + /// Script key. + /// Element ID of the event source. + /// Name of the event to handle. + /// Script text. + public void RegisterHeadScriptEvent(string key, string element, string eventName, string script) + { + RegisterHeadScriptEvent(key, Script.DefaultType, element, eventName, script); + } + + /// + /// Registers script block that should be rendered within the head HTML element. + /// + /// Script key. + /// Script language. + /// Element ID of the event source. + /// Name of the event to handle. + /// Script text. + [Obsolete("The 'language' attribute is deprecated. Please use RegisterHeadScriptEvent(string key, MimeMediaType mimeType, string element, string eventName, string script) instead")] + public void RegisterHeadScriptEvent(string key, string language, string element, string eventName, string script) + { + headScripts[key] = new ScriptEvent(language, element, eventName, script); + } + + /// + /// Registers script block that should be rendered within the head HTML element. + /// + /// Script key. + /// The scripting language's MIME type. + /// Element ID of the event source. + /// Name of the event to handle. + /// Script text. + public void RegisterHeadScriptEvent(string key, MimeMediaType mimeType, string element, string eventName, string script) + { + headScripts[key] = new ScriptEvent(mimeType, element, eventName, script); + } + + /// + /// Returns True if specified head script is registered, False otherwise. + /// + /// Script key. + /// True if specified head script is registered, False otherwise. + public bool IsHeadScriptRegistered(string key) + { + return headScripts.Contains(key); + } + + #endregion + + #region Well-known folders support + + /// + /// Gets or sets the CSS root. + /// + /// The CSS root. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public string CssRoot + { + get { return WebUtils.CreateAbsolutePath(Request.ApplicationPath, cssRoot); } + set { cssRoot = value; } + } + + /// + /// Gets or sets the scripts root. + /// + /// The scripts root. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public string ScriptsRoot + { + get { return WebUtils.CreateAbsolutePath(Request.ApplicationPath, scriptsRoot); } + set { scriptsRoot = value; } + } + + /// + /// Gets or sets the images root. + /// + /// The images root. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public string ImagesRoot + { + get { return WebUtils.CreateAbsolutePath(Request.ApplicationPath, imagesRoot); } + set { imagesRoot = value; } + } + + #endregion + + #region Result support + + /// + /// Gets or sets map of result names to target URLs + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IDictionary Results + { + get + { + if (results == null) + { + results = new Hashtable(); + } + return results; + } + set + { + results = new Hashtable(); + foreach (DictionaryEntry entry in value) + { + if (entry.Value is Result) + { + results[entry.Key] = entry.Value; + } + else if (entry.Value is String) + { + results[entry.Key] = new Result((string) entry.Value); + } + else + { + throw new TypeMismatchException( + "Unable to create result object. Please use either String or Result instances to define results."); + } + } + } + } + + /// + /// Redirects user to a URL mapped to specified result name. + /// + /// Result name. + protected internal void SetResult(string resultName) + { + GetResult(resultName).Navigate(this); + } + + + /// + /// Redirects user to a URL mapped to specified result name. + /// + /// Name of the result. + /// The context to use for evaluating the SpEL expression in the Result. + protected internal void SetResult(string resultName, object context) + { + GetResult(resultName).Navigate(context); + } + + + /// + /// Returns a redirect url string that points to the + /// defined by this + /// result evaluated using this Page for expression + /// + /// Name of the result. + /// A redirect url string. + protected internal string GetResultUrl(string resultName) + { + Result result = GetResult(resultName); + return ResolveUrl(result.GetRedirectUri(this)); + } + + /// + /// Returns a redirect url string that points to the + /// defined by this + /// result evaluated using this Page for expression + /// + /// Name of the result. + /// The context to use for evaluating the SpEL expression in the Result + /// A redirect url string. + protected internal string GetResultUrl(string resultName, object context) + { + Result result = GetResult(resultName); + return ResolveUrl(result.GetRedirectUri(context)); + } + + + private Result GetResult(string resultName) + { + Result result = (Result)Results[resultName]; + if (result == null) + { + throw new ArgumentException( + string.Format("No URL mapping found for the specified result '{0}'.", resultName), "resultName"); + } + return result; + } + + + + #endregion + + #region Validation support + + /// + /// Evaluates specified validators and returns True if all of them are valid. + /// + /// + ///

    + /// Each validator can itself represent a collection of other validators if it is + /// an instance of or one of its derived types. + ///

    + ///

    + /// Please see the Validation Framework section in the documentation for more info. + ///

    + ///
    + /// Object to validate. + /// Validators to evaluate. + /// + /// True if all of the specified validators are valid, False otherwise. + /// + public bool Validate(object validationContext, params IValidator[] validators) + { + IDictionary contextParams = CreateValidatorParameters(); + bool result = true; + foreach (IValidator validator in validators) + { + if (validator == null) + { + throw new ArgumentException("Validator is not defined."); + } + result = validator.Validate(validationContext, contextParams, this.validationErrors) && result; + } + + return result; + } + + /// + /// Gets the validation errors container. + /// + /// The validation errors container. + public virtual IValidationErrors ValidationErrors + { + get { return validationErrors; } + } + + /// + /// Creates the validator parameters. + /// + /// + /// + /// This method can be overriden if you want to pass additional parameters + /// to the validation framework, but you should make sure that you call + /// this base implementation in order to add page, session, application, + /// request, response and context to the variables collection. + /// + /// + /// + /// Dictionary containing parameters that should be passed to + /// the data validation framework. + /// + protected virtual IDictionary CreateValidatorParameters() + { + IDictionary parameters = new ListDictionary(); + parameters["page"] = this; + parameters["session"] = this.Session; + parameters["application"] = this.Application; + parameters["request"] = this.Request; + parameters["response"] = this.Response; + parameters["context"] = this.Context; + + return parameters; + } + + #endregion + + #region Data binding support + + /// + /// Initializes the data bindings. + /// + protected virtual void InitializeDataBindings() + { + } + + /// + /// Returns the key to be used for looking up a cached + /// BindingManager instance in . + /// + /// a unique key identifying the instance in the dictionary. + protected virtual string GetBindingManagerKey() + { + return CreateSharedStateKey("DataBindingManager"); + } + + /// + /// Creates a new instance. + /// + /// + /// This factory method is called if no could be found in + /// using the key returned by .
    + ///
    + /// + ///
    + /// a instance to be used for DataBinding + protected virtual IBindingContainer CreateBindingManager() + { + return new BaseBindingManager(); + } + + /// + /// Expose BindingManager via IDataBound interface + /// + IBindingContainer IDataBound.BindingManager + { + get { return this.BindingManager; } + } + + /// + /// Gets the binding manager. + /// + /// The binding manager. + protected IBindingContainer BindingManager + { + get { return this.bindingManager; } + } + + /// + /// Initializes binding manager and data bindings if necessary. + /// + private void InitializeBindingManager() + { + IDictionary sharedState = this.SharedState; + + string key = GetBindingManagerKey(); + this.bindingManager = sharedState[key] as BaseBindingManager; + if (this.bindingManager == null) + { + // access to shared state must be synchronized + lock (this.SharedState.SyncRoot) + { + this.bindingManager = sharedState[key] as BaseBindingManager; + if (this.bindingManager == null) + { + Trace.Write(traceCategory, "Initialize Data Bindings"); + this.bindingManager = CreateBindingManager(); + if (this.bindingManager == null) + { + throw new ArgumentNullException("bindingManager", + "CreateBindingManager() must not return null"); + } + InitializeDataBindings(); + sharedState[key] = this.bindingManager; + OnDataBindingsInitialized(EventArgs.Empty); + } + } + } + } + + /// + /// Raises the event. + /// + protected virtual void OnDataBindingsInitialized(EventArgs e) + { + EventHandler handler = (EventHandler)base.Events[EventDataBindingsInitialized]; + + if(handler != null) + { + handler(this, e); + } + } + + /// + /// This event is raised after as been initialized. + /// + public event EventHandler DataBindingsInitialized + { + add + { + base.Events.AddHandler(EventDataBindingsInitialized, value); + } + remove + { + base.Events.RemoveHandler(EventDataBindingsInitialized, value); + } + } + + /// + /// Bind data from model to form. + /// + protected virtual void BindFormData() + { + if (BindingManager.HasBindings) + { + Trace.Write(traceCategory, "Bind Data Model onto Controls"); + + BindingManager.BindTargetToSource(this, Controller, ValidationErrors); + } + OnDataBound(EventArgs.Empty); + } + + /// + /// Unbind data from form to model. + /// + protected virtual void UnbindFormData() + { + if (BindingManager.HasBindings) + { + Trace.Write(traceCategory, "Unbind Controls into Data Model"); + + BindingManager.BindSourceToTarget(this, Controller, ValidationErrors); + } + OnDataUnbound(EventArgs.Empty); + } + + /// + /// This event is raised after all controls have been populated with values + /// from the data model. + /// + public event EventHandler DataBound + { + add { base.Events.AddHandler(EventDataBound, value); } + remove { base.Events.RemoveHandler(EventDataBound, value); } + } + + /// + /// Raises DataBound event. + /// + /// Event arguments. + protected virtual void OnDataBound(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[EventDataBound]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// This event is raised after data model has been populated with values from + /// web controls. + /// + public event EventHandler DataUnbound + { + add { base.Events.AddHandler(EventDataUnbound, value); } + remove { base.Events.RemoveHandler(EventDataUnbound, value); } + } + + /// + /// Raises DataBound event. + /// + /// Event arguments. + protected virtual void OnDataUnbound(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[EventDataUnbound]; + if (handler != null) + { + handler(this, e); + } + } + + #endregion + + #region Application context support + + /// + /// Gets or sets Spring application context of the page. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual IApplicationContext ApplicationContext + { + get { return applicationContext; } + set { applicationContext = value; } + } + + #endregion + + #region Message source and localization support + + /// + /// Gets or sets the localizer. + /// + /// The localizer. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ILocalizer Localizer + { + get { return this.localizer; } + set + { + this.localizer = value; + if (this.localizer.ResourceCache is NullResourceCache) + { + this.localizer.ResourceCache = new SharedStateResourceCache(this); + } + } + } + + /// + /// Gets or sets the culture resolver. + /// + /// The culture resolver. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ICultureResolver CultureResolver + { + get + { +// if (cultureResolver == null) +// { +// cultureResolver = new DefaultWebCultureResolver(); +// } + return cultureResolver; + } + set { cultureResolver = value; } + } + + /// + /// Gets or sets the local message source. + /// + /// The local message source. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IMessageSource MessageSource + { + get { return messageSource; } + set + { + messageSource = value; + if (messageSource != null && messageSource is AbstractMessageSource) + { + ((AbstractMessageSource) messageSource).ParentMessageSource = applicationContext; + } + } + } + + /// + /// Initializes local message source + /// + private void InitializeMessageSource() + { + if (MessageSource == null) + { + string MessageSourceKey = CreateSharedStateKey("MessageSource"); + if (this.SharedState[MessageSourceKey] == null) + { + lock (this.SharedState.SyncRoot) + { + if (this.SharedState[MessageSourceKey] == null) + { + ResourceSetMessageSource defaultMessageSource = new ResourceSetMessageSource(); + defaultMessageSource.UseCodeAsDefaultMessage = true; + ResourceManager rm = GetLocalResourceManager(); + if (rm != null) + { + defaultMessageSource.ResourceManagers.Add(rm); + } + this.SharedState[MessageSourceKey] = defaultMessageSource; + } + } + } + + MessageSource = (IMessageSource) this.SharedState[MessageSourceKey]; + } + } + + /// + /// Creates and returns local ResourceManager for this page. + /// + /// + /// + /// In ASP.NET 1.1, this method loads local resources from the web application assembly. + /// + /// + /// However, in ASP.NET 2.0, local resources are compiled into a dynamic assembly, + /// so we need to find that assembly and load the resources from it. + /// + /// + /// Local ResourceManager instance. + private ResourceManager GetLocalResourceManager() + { +#if !NET_2_0 + return new ResourceManager(GetType().BaseType); +#else + object resourceProvider = GetLocalResourceProvider.Invoke(typeof(ResourceExpressionBuilder), new object[] {this}); + MethodInfo GetLocalResourceAssembly = + resourceProvider.GetType().GetMethod("GetLocalResourceAssembly", BindingFlags.NonPublic | BindingFlags.Instance); + Assembly localResourceAssembly = (Assembly) GetLocalResourceAssembly.Invoke(resourceProvider, null); + if (localResourceAssembly != null) + { + return new ResourceManager(VirtualPathUtility.GetFileName(this.AppRelativeVirtualPath), localResourceAssembly); + } + + return null; +#endif + } + + /// + /// Returns message for the specified resource name. + /// + /// Resource name. + /// Message text. + public string GetMessage(string name) + { + return messageSource.GetMessage(name, UserCulture); + } + + /// + /// Returns message for the specified resource name. + /// + /// Resource name. + /// Message arguments that will be used to format return value. + /// Formatted message text. + public string GetMessage(string name, params object[] args) + { + return messageSource.GetMessage(name, UserCulture, args); + } + + /// + /// Returns resource object for the specified resource name. + /// + /// Resource name. + /// Resource object. + public object GetResourceObject(string name) + { + return messageSource.GetResourceObject(name, UserCulture); + } + + /// + /// Gets or sets user's culture + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual CultureInfo UserCulture + { + get { return CultureResolver.ResolveCulture(); } + set + { + CultureResolver.SetCulture(value); + Thread.CurrentThread.CurrentUICulture = value; + if (value.IsNeutralCulture) + { + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(value.Name); + } + else + { + Thread.CurrentThread.CurrentCulture = value; + } + OnUserCultureChanged(EventArgs.Empty); + } + } + + /// + /// This event is raised when the value of UserLocale property changes. + /// + public event EventHandler UserCultureChanged; + + /// + /// Raises UserLocaleChanged event. + /// + /// Event arguments. + protected virtual void OnUserCultureChanged(EventArgs e) + { + if (UserCultureChanged != null) + { + UserCultureChanged(this, e); + } + } + + #endregion + + #region Helper methods + + /// + /// Creates a key for shared state, taking into account whether + /// this page belongs to a process or not. + /// + /// Key suffix + /// Generated unique shared state key. + protected string CreateSharedStateKey(string key) + { + if (this.Process != null) + { + return this.Process.CurrentView + "." + key; + } + else + { + return key; + } + } + + #endregion + + #region Dependency Injection Support + + /// + /// Holds default ApplicationContext instance to be used during DI. + /// + IApplicationContext ISupportsWebDependencyInjection.DefaultApplicationContext + { + get { return defaultApplicationContext; } + set { defaultApplicationContext = value; } + } + + /// + /// Injects dependencies into control before adding it. + /// + protected override void AddedControl(Control control, int index) + { + WebDependencyInjectionUtils.InjectDependenciesRecursive(defaultApplicationContext, control); + base.AddedControl(control, index); + } + + #endregion Dependency Injection Support + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/UserControl.cs b/src/Spring/Spring.Web/Web/UI/UserControl.cs new file mode 100644 index 00000000..7306003d --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/UserControl.cs @@ -0,0 +1,1066 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using System.Resources; +using System.Web; +#if NET_2_0 +using System.Web.Compilation; +#endif +using System.Web.UI; +using Spring.Collections; +using Spring.Context; +using Spring.Context.Support; +using Spring.Core; +using Spring.DataBinding; +using Spring.Globalization; +using Spring.Validation; +using Spring.Web.Support; +using IValidator = Spring.Validation.IValidator; + +#endregion + +namespace Spring.Web.UI +{ + /// + /// Extends standard .Net user control by adding data binding and localization functionality. + /// + /// Aleksandar Seovic + /// $Id: UserControl.cs,v 1.54 2008/04/03 05:31:25 markpollack Exp $ + public class UserControl : System.Web.UI.UserControl, IApplicationContextAware, IWebDataBound, ISupportsWebDependencyInjection, + IPostBackDataHandler,IValidationContainer + { + #region Static fields + + private static readonly object EventPreLoadViewState = new object(); + private static readonly object EventDataBindingsInitialized = new object(); + private static readonly object EventDataBound = new object(); + private static readonly object EventDataUnbound = new object(); + + #endregion + + #region Instance Fields + + private object controller; + private ILocalizer localizer; + private IMessageSource messageSource; + private IDictionary sharedState; + private IBindingContainer bindingManager; + private IValidationErrors validationErrors = new ValidationErrors(); + private IDictionary results; + private IApplicationContext applicationContext; + private IApplicationContext defaultApplicationContext; + private bool needsUnbind = false; + + #endregion + + #region Control lifecycle methods + +#if !NET_2_0 + /// + /// Gets a value indicating whether this instance is in design mode. + /// + /// + /// true if this instance is in design mode; otherwise, false. + /// + protected bool DesignMode + { + get { return this.Context == null; } + } + + /// + /// Gets a value indicating whether view state is enabled for this page. + /// + protected internal bool IsViewStateEnabled + { + get + { + for (Control parent = this; parent != null; parent = parent.Parent) + { + if (!parent.EnableViewState) + { + return false; + } + } + return true; + } + } +#endif + + /// + /// Initializes user control. + /// + protected override void OnInit(EventArgs e) + { + InitializeMessageSource(); + InitializeBindingManager(); + + if (!IsPostBack) + { + InitializeModel(); + } + else + { + LoadModel(LoadModelFromPersistenceMedium()); + } + + base.OnInit(e); + + OnInitializeControls(EventArgs.Empty); + } + + /// + /// Raises the event after page initialization. + /// + protected internal virtual void OnPreLoadViewState(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[EventPreLoadViewState]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// PreLoadViewState event. + /// + /// + /// + /// This event is raised if is true + /// immediately before state is restored from ViewState. + /// + /// + /// NOTE: Different from , this event will always be raised! + /// + /// + public event EventHandler PreLoadViewState + { + add { base.Events.AddHandler(EventPreLoadViewState, value); } + remove { base.Events.RemoveHandler(EventPreLoadViewState, value); } + } + + /// + /// This method is called during a postback if this control has been visible when being rendered to the client. + /// + /// + /// If the controls has been visible when being rendering to the client, + /// has been called during + /// + /// true if the server control's state changes as a result of the post back; otherwise false. + bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) + { + return LoadPostData(postDataKey, postCollection); + } + + /// + /// This method is called during a postback if this control has been visible when being rendered to the client. + /// + /// true if the server control's state changes as a result of the post back; otherwise false. + protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) + { + // mark this control for unbinding form data during OnLoad() + this.needsUnbind = true; + return false; + } + + /// + /// When implemented by a class, signals the server control object to notify the + /// ASP.NET application that the state of the control has changed. + /// + void IPostBackDataHandler.RaisePostDataChangedEvent() + { + RaisePostDataChangedEvent(); + } + + /// + /// When implemented by a class, signals the server control object to notify the + /// ASP.NET application that the state of the control has changed. + /// + protected virtual void RaisePostDataChangedEvent() + { + return; + } + + + /// + /// First unbinds data from the controls into a data model and + /// then raises Load event in order to execute all associated handlers. + /// + /// Event arguments. + protected override void OnLoad(EventArgs e) + { + if (IsPostBack && needsUnbind) + { + // unbind form data + UnbindFormData(); + } + base.OnLoad(e); + } + + /// + /// Binds data from the data model into controls and raises + /// PreRender event afterwards. + /// + /// Event arguments. + protected override void OnPreRender(EventArgs e) + { + if (Visible) + { + // causes IPostBackDataHandler.LoadPostData() to be called on next postback. + // this is used for indicating a required call to UnbindFormData() + Page.RegisterRequiresPostBack(this); + + BindFormData(); + + if (localizer != null) + { + localizer.ApplyResources(this, messageSource, UserCulture); + } + else if (Page.Localizer != null) + { + Page.Localizer.ApplyResources(this, messageSource, UserCulture); + } + } + + base.OnPreRender(e); + + object modelToSave = SaveModel(); + if (modelToSave != null) + { + SaveModelToPersistenceMedium(modelToSave); + } + } + + /// + /// This event is raised before Load event and should be used to initialize + /// controls as necessary. + /// + public event EventHandler InitializeControls; + + /// + /// Raises InitializeControls event. + /// + /// Event arguments. + protected virtual void OnInitializeControls(EventArgs e) + { + if (InitializeControls != null) + { + InitializeControls(this, e); + } + } + + /// + /// Obtains a object from a user control file + /// and injects dependencies according to Spring config file. + /// + /// The virtual path to a user control file. + /// + /// Returns the specified object, with dependencies injected. + /// + protected new Control LoadControl(string virtualPath) + { + Control control = base.LoadControl(virtualPath); + WebDependencyInjectionUtils.InjectDependenciesRecursive(defaultApplicationContext, control); + return control; + } + +#if NET_2_0 + /// + /// Obtains a object by type + /// and injects dependencies according to Spring config file. + /// + /// The type of a user control. + /// parameters to pass to the control + /// + /// Returns the specified object, with dependencies injected. + /// + protected new Control LoadControl( Type t, params object[] parameters) + { + Control control = base.LoadControl( t, parameters ); + WebDependencyInjectionUtils.InjectDependenciesRecursive(defaultApplicationContext, control); + return control; + } +#endif + + #endregion + + #region Model Management Support + + /// + /// Initializes data model when the page is first loaded. + /// + /// + /// This method should be overriden by the developer + /// in order to initialize data model for the page. + /// + protected virtual void InitializeModel() + { + } + + /// + /// Retrieves data model from a persistence store. + /// + /// + /// The default implementation uses to store and retrieve + /// the model for the current + /// + protected virtual object LoadModelFromPersistenceMedium() + { + return Session[Request.CurrentExecutionFilePath + this.UniqueID + ".Model"]; + } + + /// + /// Saves data model to a persistence store. + /// + /// + /// The default implementation uses to store and retrieve + /// the model for the current + /// + protected virtual void SaveModelToPersistenceMedium(object modelToSave) + { + Session[Request.CurrentExecutionFilePath + this.UniqueID + ".Model"] = modelToSave; + } + + /// + /// Loads the saved data model on postback. + /// + /// + /// This method should be overriden by the developer + /// in order to load data model for the page. + /// + protected virtual void LoadModel(object savedModel) + { + } + + /// + /// Returns a model object that should be saved. + /// + /// + /// This method should be overriden by the developer + /// in order to save data model for the page. + /// + /// + /// A model object that should be saved. + /// + protected virtual object SaveModel() + { + return null; + } + + #endregion< + + #region Controller Support + + /// + /// Gets or sets controller for the control. + /// + /// + /// + /// Internally calls are delegated to and . + /// + /// + /// Controller for the control. + public object Controller + { + get { return GetController(); } + set { SetController(value); } + } + + /// + /// Stores the controller to be returned by property. + /// + /// + /// The default implementation uses a field to store the reference. Derived classes may override this behaviour + /// but must ensure to also change the behaviour of accordingly. + /// + /// Controller for the control. + protected virtual void SetController(object controller) + { + this.controller = controller; + } + + /// + /// Returns the controller stored by . + /// + /// + /// + /// The default implementation uses a field to retrieve the reference. + /// + /// + /// If external controller is not specified, control will serve as its own controller, + /// which will allow data binding to work properly. + /// + /// + /// You may override this method e.g. to return in order to + /// have your control bind to the same controller as your page. When overriding this behaviour, derived classes + /// must ensure to also change the behaviour of accordingly. + /// + /// + /// + /// The controller for this control. + /// If no controller is set, a reference to the control itself is returned. + /// + protected virtual object GetController() + { + if(controller == null) + { + return this; + } + return controller; + } + + #endregion Controller Support + + #region Shared State support + + /// + /// Returns a thread-safe dictionary that contains state that is shared by + /// all instances of this control. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + protected IDictionary SharedState + { + get + { + if (sharedState == null) + { + string thisTypeKey = this.GetType().FullName + this.GetType().GetHashCode() + ".SharedState"; + + sharedState = Application[thisTypeKey] as IDictionary; + if (sharedState==null) + { + Application.Lock(); + try + { + sharedState = Application[thisTypeKey] as IDictionary; + if (sharedState == null) + { + sharedState = new SynchronizedHashtable(); + Application.Add(thisTypeKey, sharedState); + } + } + finally + { + Application.UnLock(); + } + } + } + return sharedState; + } + } + + #endregion Shared State support + + #region Result support + + /// + /// Gets or sets map of result names to target URLs + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IDictionary Results + { + get + { + if (results == null) + { + results = new Hashtable(); + } + return results; + } + set + { + results = new Hashtable(); + foreach (DictionaryEntry entry in value) + { + if (entry.Value is Result) + { + results[entry.Key] = entry.Value; + } + else if (entry.Value is String) + { + results[entry.Key] = new Result((string)entry.Value); + } + else + { + throw new TypeMismatchException( + "Unable to create result object. Please use either String or Result instances to define results."); + } + } + } + } + + /// + /// Redirects user to a URL mapped to specified result name. + /// + /// Result name. + protected void SetResult(string resultName) + { + Result result = (Result)Results[resultName]; + if (result == null) + { + // bubble up result in the hierarchy + Control parent = this.Parent; + while(parent != null) + { + if (parent is UserControl) + { + ((UserControl)parent).SetResult(resultName); + return; + } + else if (parent is Page) + { + ((Page) parent).SetResult(resultName); + return; + } + parent = parent.Parent; + } + throw new ArgumentException(string.Format("No URL mapping found for the specified result '{0}'.", resultName), "resultName"); + } + + result.Navigate(this); + } + + /// + /// Redirects user to a URL mapped to specified result name. + /// + /// Result name. + /// The context to use for evaluating the SpEL expression in the Result. + protected void SetResult(string resultName, object context) + { + Result result = (Result)Results[resultName]; + if (result == null) + { + // bubble up result in the hierarchy + Control parent = this.Parent; + while (parent != null) + { + if (parent is UserControl) + { + ((UserControl)parent).SetResult(resultName, context); + return; + } + else if (parent is Page) + { + ((Page)parent).SetResult(resultName, context); + return; + } + parent = parent.Parent; + } + throw new ArgumentException(string.Format("No URL mapping found for the specified result '{0}'.", resultName), "resultName"); + } + + result.Navigate(context); + } + + #endregion + + #region Validation support + + /// + /// Evaluates specified validators and returns True if all of them are valid. + /// + /// + ///

    + /// Each validator can itself represent a collection of other validators if it is + /// an instance of or one of its derived types. + ///

    + ///

    + /// Please see the Validation Framework section in the documentation for more info. + ///

    + ///
    + /// Object to validate. + /// Validators to evaluate. + /// + /// True if all of the specified validators are valid, False otherwise. + /// + public bool Validate(object validationContext, params IValidator[] validators) + { + IDictionary contextParams = CreateValidatorParameters(); + bool result = true; + foreach (IValidator validator in validators) + { + if (validator == null) + { + throw new ArgumentException("Validator is not defined."); + } + result = validator.Validate(validationContext, contextParams, this.validationErrors) && result; + } + + return result; + } + + /// + /// Gets the validation errors container. + /// + /// The validation errors container. + public virtual IValidationErrors ValidationErrors + { + get { return validationErrors; } + } + + /// + /// Creates the validator parameters. + /// + /// + /// + /// This method can be overriden if you want to pass additional parameters + /// to the validation framework, but you should make sure that you call + /// this base implementation in order to add page, session, application, + /// request, response and context to the variables collection. + /// + /// + /// + /// Dictionary containing parameters that should be passed to + /// the data validation framework. + /// + protected virtual IDictionary CreateValidatorParameters() + { + IDictionary parameters = new ListDictionary(); + parameters["page"] = this.Page; + parameters["usercontrol"] = this; + parameters["session"] = this.Session; + parameters["application"] = this.Application; + parameters["request"] = this.Request; + parameters["response"] = this.Response; + parameters["context"] = this.Context; + + return parameters; + } + + #endregion + + #region Data binding support + + /// + /// Initializes the data bindings. + /// + protected virtual void InitializeDataBindings() + {} + + /// + /// Returns the key to be used for looking up a cached + /// BindingManager instance in . + /// + /// a unique key identifying the instance in the dictionary. + protected virtual string GetBindingManagerKey() + { + return "DataBindingManager"; + } + + /// + /// Creates a new instance. + /// + /// + /// This factory method is called if no could be found in + /// using the key returned by .
    + ///
    + /// + ///
    + /// a instance to be used for DataBinding + protected virtual IBindingContainer CreateBindingManager() + { + return new BaseBindingManager(); + } + + /// + /// Gets the binding manager for this control. + /// + /// The binding manager. + public IBindingContainer BindingManager + { + get { return this.bindingManager; } + } + + /// + /// Initializes binding manager and data bindings if necessary. + /// + private void InitializeBindingManager() + { + IDictionary sharedState = this.SharedState; + + string key = GetBindingManagerKey(); + this.bindingManager = sharedState[key] as BaseBindingManager; + if (this.bindingManager == null) + { + lock(sharedState.SyncRoot) + { + this.bindingManager = sharedState[key] as BaseBindingManager; + if (this.bindingManager == null) + { + try + { + this.bindingManager = CreateBindingManager(); + if(bindingManager == null) + { + throw new ArgumentNullException("bindingManager", + "CreateBindingManager() must not return null"); + } + InitializeDataBindings(); + } + catch + { + this.bindingManager = null; + throw; + } + sharedState[key] = this.bindingManager; + this.OnDataBindingsInitialized(EventArgs.Empty); + } + } + } + } + + /// + /// Raises the event. + /// + protected virtual void OnDataBindingsInitialized(EventArgs e) + { + EventHandler handler = (EventHandler)base.Events[EventDataBindingsInitialized]; + + if(handler != null) + { + handler(this, e); + } + } + + /// + /// This event is raised after as been initialized. + /// + public event EventHandler DataBindingsInitialized + { + add + { + base.Events.AddHandler(EventDataBindingsInitialized, value); + } + remove + { + base.Events.RemoveHandler(EventDataBindingsInitialized, value); + } + } + + /// + /// Bind data from model to form. + /// + protected internal virtual void BindFormData() + { + if(BindingManager.HasBindings) + { + BindingManager.BindTargetToSource(this, Controller, Page.ValidationErrors); + } + OnDataBound(EventArgs.Empty); + } + + /// + /// Unbind data from form to model. + /// + protected internal virtual void UnbindFormData() + { + if(BindingManager.HasBindings) + { + BindingManager.BindSourceToTarget(this, Controller, Page.ValidationErrors); + } + OnDataUnbound(EventArgs.Empty); + } + + /// + /// This event is raised after all controls have been populated with values + /// from the data model. + /// + public event EventHandler DataBound + { + add + { + base.Events.AddHandler(EventDataBound, value); + } + remove + { + base.Events.RemoveHandler(EventDataBound, value); + } + } + + /// + /// Raises DataBound event. + /// + /// Event arguments. + protected virtual void OnDataBound(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[EventDataBound]; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// This event is raised after data model has been populated with values from + /// web controls. + /// + public event EventHandler DataUnbound + { + add + { + base.Events.AddHandler(EventDataUnbound, value); + } + remove + { + base.Events.RemoveHandler(EventDataUnbound, value); + } + } + + /// + /// Raises DataBound event. + /// + /// Event arguments. + protected virtual void OnDataUnbound(EventArgs e) + { + EventHandler handler = (EventHandler) base.Events[EventDataUnbound]; + if (handler != null) + { + handler(this, e); + } + } + + #endregion + + #region Application context support + + /// + /// Gets or sets Spring application context of the page. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual IApplicationContext ApplicationContext + { + get { return applicationContext; } + set { applicationContext = value; } + } + + #endregion + + #region Message source and localization support + + /// + /// Gets or sets the localizer. + /// + /// The localizer. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ILocalizer Localizer + { + get { return localizer; } + set + { + localizer = value; + if (localizer.ResourceCache is NullResourceCache) + { + localizer.ResourceCache = new AspNetResourceCache(); + } + } + } + + /// + /// Gets or sets the local message source. + /// + /// The local message source. + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IMessageSource MessageSource + { + get { return messageSource; } + set + { + messageSource = value; + if (messageSource != null && messageSource is AbstractMessageSource) + { + ((AbstractMessageSource) messageSource).ParentMessageSource = applicationContext; + } + } + } + + /// + /// Initializes local message source + /// + protected void InitializeMessageSource() + { + if (this.MessageSource == null) + { + string key = CreateSharedStateKey("MessageSource"); + IDictionary sharedState = this.SharedState; + IMessageSource messageSource = sharedState[key] as IMessageSource; + if (messageSource == null) + { + lock(sharedState.SyncRoot) + { + messageSource = sharedState[key] as IMessageSource; + if (messageSource == null) + { + ResourceSetMessageSource defaultMessageSource = new ResourceSetMessageSource(); + defaultMessageSource.UseCodeAsDefaultMessage = true; + ResourceManager rm = GetLocalResourceManager(); + if (rm != null) + { + defaultMessageSource.ResourceManagers.Add(rm); + } + sharedState[key] = defaultMessageSource; + messageSource = defaultMessageSource; + } + } + } + this.MessageSource = messageSource; + } + } + + /// + /// Creates and returns local ResourceManager for this page. + /// + /// + /// + /// In ASP.NET 1.1, this method loads local resources from the web application assembly. + /// + /// + /// However, in ASP.NET 2.0, local resources are compiled into the dynamic assembly, + /// so we need to find that assembly instead and load the resources from it. + /// + /// + /// Local ResourceManager instance. + private ResourceManager GetLocalResourceManager() +#if !NET_2_0 + { + return new ResourceManager(GetType().BaseType); + } +#else + { + object resourceProvider = Page.GetLocalResourceProvider.Invoke(typeof(ResourceExpressionBuilder), new object[] {this}); + MethodInfo GetLocalResourceAssembly = + resourceProvider.GetType().GetMethod("GetLocalResourceAssembly", BindingFlags.NonPublic | BindingFlags.Instance); + Assembly localResourceAssembly = (Assembly) GetLocalResourceAssembly.Invoke(resourceProvider, null); + if (localResourceAssembly != null) + { + return new ResourceManager(VirtualPathUtility.GetFileName(this.AppRelativeVirtualPath), localResourceAssembly); + } + return null; + } +#endif + + /// + /// Returns message for the specified resource name. + /// + /// Resource name. + /// Message text. + public string GetMessage(string name) + { + return messageSource.GetMessage(name, UserCulture); + } + + /// + /// Returns message for the specified resource name. + /// + /// Resource name. + /// Message arguments that will be used to format return value. + /// Formatted message text. + public string GetMessage(string name, params object[] args) + { + return messageSource.GetMessage(name, UserCulture, args); + } + + /// + /// Returns resource object for the specified resource name. + /// + /// Resource name. + /// Resource object. + public object GetResourceObject(string name) + { + return messageSource.GetResourceObject(name, UserCulture); + } + + /// + /// Gets or sets user's culture + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual CultureInfo UserCulture + { + get { return Page.UserCulture; } + set { Page.UserCulture = value; } + } + + #endregion + + #region Spring Page support + + /// + /// Overrides Page property to return + /// instead of . + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new Page Page + { + get { return (Page) base.Page; } + } + + #endregion + + #region Helper Methods + + /// + /// Creates a key for shared state, taking into account whether + /// this page belongs to a process or not. + /// + /// Key suffix + /// Generated unique shared state key. + protected string CreateSharedStateKey(string key) + { + return key; + } + + #endregion + + #region Dependency Injection Support + + /// + /// Holds the default ApplicationContext to be used during DI. + /// + IApplicationContext ISupportsWebDependencyInjection.DefaultApplicationContext + { + get { return defaultApplicationContext; } + set { defaultApplicationContext = value; } + } + + /// + /// Injects dependencies into control before adding it. + /// + protected override void AddedControl(Control control,int index) + { + WebDependencyInjectionUtils.InjectDependenciesRecursive( defaultApplicationContext, control ); + base.AddedControl(control,index); + } + + #endregion Dependency Injection Support + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Validation/AbstractValidationErrorsRenderer.cs b/src/Spring/Spring.Web/Web/UI/Validation/AbstractValidationErrorsRenderer.cs new file mode 100644 index 00000000..fea552bf --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Validation/AbstractValidationErrorsRenderer.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Web.UI; +using Page=Spring.Web.UI.Page; + +namespace Spring.Web.UI.Validation +{ + /// + /// This class provides common members for all validation errors renderers. + /// + /// Aleksandar Seovic + /// $Id: AbstractValidationErrorsRenderer.cs,v 1.1 2007/08/02 19:50:28 markpollack Exp $ + public abstract class AbstractValidationErrorsRenderer : IValidationErrorsRenderer + { + private string cssClass; + + /// + /// Gets or sets the name of the CSS class that should be used. + /// + /// + /// The name of the CSS class that should be used + /// + public string CssClass + { + get { return this.cssClass; } + set { this.cssClass = value; } + } + + /// + /// Renders validation errors using specified . + /// + /// Web form instance. + /// An HTML writer to use. + /// The list of validation errors. + public abstract void RenderErrors(Page page, HtmlTextWriter writer, IList errors); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Validation/DivValidationErrorsRenderer.cs b/src/Spring/Spring.Web/Web/UI/Validation/DivValidationErrorsRenderer.cs new file mode 100644 index 00000000..faac260e --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Validation/DivValidationErrorsRenderer.cs @@ -0,0 +1,69 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Web.UI; +using Spring.Web.UI.Validation; +using Page=Spring.Web.UI.Page; + +namespace Spring.Web.UI.Validation +{ + /// + /// Implementation of that renders + /// validation errors within a div element, using breaks between the + /// errors. + /// + /// + /// This renderer's behavior is consistent with standard ASP.NET behavior of + /// the validation summary, and is used as default renderer for Spring.NET + /// control. + /// + /// Aleksandar Seovic + /// $Id: DivValidationErrorsRenderer.cs,v 1.1 2007/08/02 19:50:28 markpollack Exp $ + public class DivValidationErrorsRenderer : AbstractValidationErrorsRenderer + { + /// + /// Renders validation errors using specified . + /// + /// Web form instance. + /// An HTML writer to use. + /// The list of validation errors. + public override void RenderErrors(Page page, HtmlTextWriter writer, IList errors) + { + if (CssClass != null) + { + writer.AddAttribute(HtmlTextWriterAttribute.Class, CssClass); + } + + writer.AddAttribute(HtmlTextWriterAttribute.Id, "ctl00_body_validationSummary"); + + writer.RenderBeginTag(HtmlTextWriterTag.Div); + if (errors != null && errors.Count > 0) + { + foreach (string error in errors) + { + writer.WriteLine(error); + writer.WriteFullBeginTag("br /"); + } + } + writer.RenderEndTag(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Validation/IValidationErrorsRenderer.cs b/src/Spring/Spring.Web/Web/UI/Validation/IValidationErrorsRenderer.cs new file mode 100644 index 00000000..ea335b1c --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Validation/IValidationErrorsRenderer.cs @@ -0,0 +1,57 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Web.UI; + +using Spring.Web.UI.Controls; + +using Page=Spring.Web.UI.Page; + +namespace Spring.Web.UI.Validation +{ + /// + /// This interface should be implemented by all validation errors renderers. + /// + /// + /// + /// Validation errors renderers are used to decouple rendering behavior from the + /// validation errors controls such as and + /// . + /// + /// + /// This allows users to change how validation errors are rendered by simply pluggin in + /// appropriate renderer implementation into the validation errors controls using + /// Spring.NET dependency injection. + /// + /// + /// Aleksandar Seovic + /// $Id: IValidationErrorsRenderer.cs,v 1.1 2007/08/02 19:50:28 markpollack Exp $ + public interface IValidationErrorsRenderer + { + /// + /// Renders validation errors using specified . + /// + /// Web form instance. + /// An HTML writer to use. + /// The list of validation errors. + void RenderErrors(Page page, HtmlTextWriter writer, IList errors); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Validation/IconValidationErrorsRenderer.cs b/src/Spring/Spring.Web/Web/UI/Validation/IconValidationErrorsRenderer.cs new file mode 100644 index 00000000..0ff2b409 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Validation/IconValidationErrorsRenderer.cs @@ -0,0 +1,86 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Text; +using System.Web.UI; +using Spring.Web.UI.Validation; +using Page=Spring.Web.UI.Page; + +namespace Spring.Web.UI.Validation +{ + /// + /// Implementation of that + /// displays an error image to let user know there is an error, and + /// tooltip to display actual error messages. + /// + /// + /// + /// This renderer's behavior is similar to Windows Forms error provider. + /// + /// + /// Aleksandar Seovic + /// $Id: IconValidationErrorsRenderer.cs,v 1.1 2007/08/02 19:50:28 markpollack Exp $ + public class IconValidationErrorsRenderer : AbstractValidationErrorsRenderer + { + private string iconSrc; + + /// + /// Gets or sets the name of the image file to use as an error icon. + /// + /// + /// + /// Image name should be relative to the value of the + /// property, and should not use leading path separator. + /// + /// + /// The name of the image file to use as an error icon. + public string IconSrc + { + get { return this.iconSrc; } + set { this.iconSrc = value; } + } + + /// + /// Renders validation errors using specified . + /// + /// Web form instance. + /// An HTML writer to use. + /// The list of validation errors. + public override void RenderErrors(Page page, HtmlTextWriter writer, IList errors) + { + if (errors != null && errors.Count > 0) + { + StringBuilder sb = new StringBuilder(); + string separator = ""; + foreach (string error in errors) + { + sb.Append(separator).Append(error); + separator = "\n"; + } + writer.AddAttribute(HtmlTextWriterAttribute.Src, page.ImagesRoot + "/" + IconSrc); + writer.AddAttribute(HtmlTextWriterAttribute.Title, sb.ToString()); + + writer.RenderBeginTag(HtmlTextWriterTag.Img); + writer.RenderEndTag(); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/UI/Validation/SpanValidationErrorsRenderer.cs b/src/Spring/Spring.Web/Web/UI/Validation/SpanValidationErrorsRenderer.cs new file mode 100644 index 00000000..98aaab46 --- /dev/null +++ b/src/Spring/Spring.Web/Web/UI/Validation/SpanValidationErrorsRenderer.cs @@ -0,0 +1,71 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using System.Web.UI; + +using Spring.Web.UI.Controls; +using Spring.Web.UI.Validation; +using Page=Spring.Web.UI.Page; + +namespace Spring.Web.UI.Validation +{ + /// + /// Implementation of that renders + /// validation errors within a span element, using breaks between the + /// errors. + /// + /// + /// This renderer's behavior is consistent with standard ASP.NET behavior of + /// the control validators, and is used as the default renderer for Spring.NET + /// control. + /// + /// Aleksandar Seovic + /// $Id: SpanValidationErrorsRenderer.cs,v 1.1 2007/08/02 19:50:28 markpollack Exp $ + public class SpanValidationErrorsRenderer : AbstractValidationErrorsRenderer + { + /// + /// Renders validation errors using specified . + /// + /// Web form instance. + /// An HTML writer to use. + /// The list of validation errors. + public override void RenderErrors(Page page, HtmlTextWriter writer, IList errors) + { + if (errors != null && errors.Count > 0) + { + if (CssClass != null) + { + writer.AddAttribute(HtmlTextWriterAttribute.Class, CssClass); + } + + writer.RenderBeginTag(HtmlTextWriterTag.Span); + + foreach (string error in errors) + { + writer.Write(error); + writer.Write(" "); + } + writer.RenderEndTag(); + + } + } + } +} \ No newline at end of file diff --git a/src/Spring/readme.txt b/src/Spring/readme.txt new file mode 100644 index 00000000..5a8a6a85 --- /dev/null +++ b/src/Spring/readme.txt @@ -0,0 +1,25 @@ +This folder is where source code for all subprojects of Spring.Net are located. + +There should be one subproject under this directory for each of the assemblies +that will be produced when Spring.Net is built. Each subproject should be named +exactly how the target assembly should be named, without .dll at the end. + +Currently identified subprojects are: + +Each subproject should set root namespace to "Spring". Source code should always be in subdirectories matching +the namespace. For example, Spring.Aop subproject will have following hierarchy: + +\Spring.Aop + \Aop + \Framework + \AutoProxy + ... + \Interceptor + \Support + \Target + ... + \Spring.Aop.2003.csproj (project file) + \AssemblyInfo.cs + +Other subprojects should have similar structure. + diff --git a/test/Spring/CommonAssemblyInfo.cs b/test/Spring/CommonAssemblyInfo.cs new file mode 100644 index 00000000..9833ea63 --- /dev/null +++ b/test/Spring/CommonAssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Spring.Net Unit Tests")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.1.1.0")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. + +// *IMPORTANT*: for Mono compatibility one must not use these attributes if strong name is not used! + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] +//[assembly: AssemblyKeyName("")] diff --git a/test/Spring/Spring.Aop.Tests/Aop/Advice/DebugAdvice.cs b/test/Spring/Spring.Aop.Tests/Aop/Advice/DebugAdvice.cs new file mode 100644 index 00000000..41e2c090 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Advice/DebugAdvice.cs @@ -0,0 +1,92 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Advice +{ + /// + /// Convenience + /// implementation that displays verbose information about intercepted + /// invocations to the system console. + /// + /// + ///

    + /// Can be introduced into an interceptor chain to serve as a useful low + /// level debugging aid. + ///

    + ///
    + /// Rod Johnson + /// Federico Spinazzi (.NET) + /// Aleksandar Seovic (.NET) + /// $Id: DebugAdvice.cs,v 1.1 2007/08/08 07:45:36 markpollack Exp $ + /// + public sealed class DebugAdvice : IMethodInterceptor + { + private int _count; + + /// + /// Gets the count of the number of times this interceptor has been + /// invoked. + /// + /// + /// The count of the number of times this interceptor has been invoked. + /// + public int Count + { + get { return _count; } + } + + /// + /// Displays verbose information about intercepted invocations to the + /// system console. + /// + /// + /// The method invocation that is being intercepted. + /// + /// + /// The result of the call to the + /// method of + /// the supplied ; this return value may + /// well have been intercepted by the interceptor. + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + /// + /// + public object Invoke(IMethodInvocation invocation) + { + ++_count; + Console.Out.WriteLine("{0} [count={1}, invocation='{2}']", + typeof(DebugAdvice).Name, _count, invocation); + object returnValue = invocation.Proceed(); + Console.Out.WriteLine("{0} ['{1}' invocation returned '{2}']", + typeof(DebugAdvice).Name, invocation.Method.Name, returnValue); + return returnValue; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Config/AopNamespaceParserTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Config/AopNamespaceParserTests.cs new file mode 100644 index 00000000..bd590ca6 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Config/AopNamespaceParserTests.cs @@ -0,0 +1,102 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Aop.Framework; +using Spring.Aop.Framework.DynamicProxy; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Aop.Config +{ + /// + /// This class contains tests for the custom aop namespace. + /// + /// Mark Pollack + /// $Id: AopNamespaceParserTests.cs,v 1.7 2007/08/09 03:06:37 markpollack Exp $ + [TestFixture] + public class AopNamespaceParserTests + { + + private IApplicationContext ctx; + + [SetUp] + public void Setup() + { + NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser)); + //ctx = new XmlApplicationContext( "assembly://Spring.Aop.Tests/Spring.Aop.Config/AopNamespaceParserTests.xml"); + ctx = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("AopNamespaceParserTests.xml", this.GetType())); + } + + + + [Test] + public void Registered() + { + Assert.IsNotNull(NamespaceParserRegistry.GetParser("http://www.springframework.net/aop")); + + + IPointcut pointcut = ctx["getDescriptionCalls"] as IPointcut; + Assert.IsNotNull(pointcut); + Assert.IsFalse(AopUtils.IsAopProxy(pointcut)); + + + ITestObject testObject = ctx["testObject"] as ITestObject; + Assert.IsNotNull(testObject); + Assert.IsTrue(AopUtils.IsAopProxy(testObject), "Object should be an AOP proxy"); + + IAdvised advised = testObject as IAdvised; + Assert.IsNotNull(advised); + IAdvisor[] advisors = advised.Advisors; + Assert.IsTrue(advisors.Length > 0, "Advisors should not be empty"); + + + } + + [Test] + public void AdviceInvokedCorrectly() + { + CountingBeforeAdvice getDescriptionCounter = ctx.GetObject("getDescriptionCounter") as CountingBeforeAdvice; + Assert.IsNotNull(getDescriptionCounter); + + ITestObject testObject = GetTestObject(); + + Assert.AreEqual(0,getDescriptionCounter.GetCalls("GetDescription"),"Incorrect initial getDescription count"); + + testObject.GetDescription(); + + Assert.AreEqual(1, getDescriptionCounter.GetCalls("GetDescription"), "Incorrect getDescription count"); + + } + + private ITestObject GetTestObject() + { + return ctx.GetObject("testObject", typeof (ITestObject)) as ITestObject; + } + + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Config/AopNamespaceParserTests.xml b/test/Spring/Spring.Aop.Tests/Aop/Config/AopNamespaceParserTests.xml new file mode 100644 index 00000000..33d3bf00 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Config/AopNamespaceParserTests.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + .*GetDescription.* + + + + + + + + + + + diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AbstractMethodInvocationTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AbstractMethodInvocationTests.cs new file mode 100644 index 00000000..9e6e3032 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AbstractMethodInvocationTests.cs @@ -0,0 +1,248 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using System.Reflection; + +using AopAlliance.Intercept; +using DotNetMock.Dynamic; +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Unit tests for the AbstractMethodInvocation class. + /// + /// Rick Evans + /// Bruno Baia + /// $Id: AbstractMethodInvocationTests.cs,v 1.2 2008/02/06 18:29:05 bbaia Exp $ + [TestFixture] + public abstract class AbstractMethodInvocationTests + { + protected abstract AbstractMethodInvocation CreateMethodInvocation( + object proxy, object target, MethodInfo method, MethodInfo onProxyMethod, + object[] arguments, Type targetType, IList interceptors); + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullMethod() + { + CreateMethodInvocation(null, this, null, null, null, GetType(), null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullTarget() + { + CreateMethodInvocation(null, null, null, null, null, GetType(), null); + } + + [Test] + public void ProceedWithNullInterceptorChain() + { + Target target = new Target(); + AbstractMethodInvocation join = CreateMethodInvocation( + null, target, target.GetTargetMethodNoArgs(), null, null, target.GetType(), null); + string score = (string) join.Proceed(); + Assert.AreEqual(Target.DefaultScore + Target.Suffix, score); + } + + [Test] + public void ProceedWithEmptyInterceptorChain() + { + Target target = new Target(); + AbstractMethodInvocation join = CreateMethodInvocation( + null, target, target.GetTargetMethodNoArgs(), null, null, target.GetType(), new ArrayList()); + string score = (string) join.Proceed(); + Assert.AreEqual(Target.DefaultScore + Target.Suffix, score); + } + + [Test] + public void ToStringWithoutArguments() + { + Target target = new Target(); + AbstractMethodInvocation join = CreateMethodInvocation( + null, target, target.GetTargetMethodNoArgs(), null, null, target.GetType(), new ArrayList()); + CheckToStringDoesntThrowAnException(join); + } + + [Test] + public void ToStringWithArguments() + { + Target target = new Target(); + AbstractMethodInvocation join = CreateMethodInvocation( + null, target, target.GetTargetMethod(), null, new string[] { "Five" }, target.GetType(), new ArrayList()); + CheckToStringDoesntThrowAnException(join); + } + + [Test] + public void ToStringMustNotInvokeToStringOnTarget() + { + Target target = new TargetWithBadToString(); + AbstractMethodInvocation join = CreateMethodInvocation( + null, target, target.GetTargetMethodNoArgs(), null, null, target.GetType(), new ArrayList()); + // if it hits the target the test will fail with NotSupportedException... + CheckToStringDoesntThrowAnException(join); + } + + private static string CheckToStringDoesntThrowAnException(AbstractMethodInvocation join) + { + return join.ToString(); + } + + public class Target + { + public const string Suffix = "!!!"; + public const string DefaultScore = "ONE HUNDRED AND EIGHTY"; + + public MethodInfo GetTargetMethodNoArgs() + { + return GetType().GetMethod("BullseyeMethod", Type.EmptyTypes); + } + + public MethodInfo GetTargetMethod() + { + return GetType().GetMethod("BullseyeMethod", new Type[] {typeof (string)}); + } + + public string BullseyeMethod() + { + return BullseyeMethod(DefaultScore); + } + + public string BullseyeMethod(string score) + { + return score + Suffix; + } + } + + public interface ICommand + { + void Execute(); + } + + public sealed class BadCommand : ICommand + { + public void Execute() + { + throw new NotImplementedException(); + } + + public MethodInfo GetTargetMethod() + { + return GetType().GetMethod("Execute", Type.EmptyTypes); + } + } + + private sealed class TargetWithBadToString : Target + { + public override string ToString() + { + throw new NotSupportedException("ToString"); + } + } + + [Test] + public void ValidInvocation() + { + Target target = new Target(); + IDynamicMock mock = new DynamicMock(typeof (IMethodInterceptor)); + AbstractMethodInvocation join = CreateMethodInvocation( + null, target, target.GetTargetMethodNoArgs(), null, null, target.GetType(), new object[] { mock.Object }); + mock.ExpectAndReturn("Invoke", target.BullseyeMethod().ToLower(CultureInfo.InvariantCulture)); + + string score = (string) join.Proceed(); + Assert.AreEqual(Target.DefaultScore.ToLower(CultureInfo.InvariantCulture) + Target.Suffix, score); + mock.Verify(); + } + + [Test] + public void UnwrapsTargetInvocationException_NoInterceptors() + { + BadCommand target = new BadCommand(); + AbstractMethodInvocation join = CreateMethodInvocation( + null, target, target.GetTargetMethod(), null, null, target.GetType(), new object[] { }); + try + { + join.Proceed(); + } + catch (NotImplementedException) + { + // this is good, we want this exception to bubble up... + } + catch (TargetInvocationException) + { + Assert.Fail("Must have unwrapped this."); + } + } + + [Test] + public void UnwrapsTargetInvocationException_WithInterceptor() + { + BadCommand target = new BadCommand(); + IDynamicMock mock = new DynamicMock(typeof (IMethodInterceptor)); + AbstractMethodInvocation join = CreateMethodInvocation( + null, target, target.GetTargetMethod(), null, null, target.GetType(), new object[] { mock.Object }); + mock.ExpectAndReturn("Invoke", null); + try + { + join.Proceed(); + } + catch (NotImplementedException) + { + // this is good, we want this exception to bubble up... + } + catch (TargetInvocationException) + { + Assert.Fail("Must have unwrapped this."); + } + mock.Verify(); + } + + [Test] + public void UnwrapsTargetInvocationException_WithInterceptorThatThrowsAnException() + { + BadCommand target = new BadCommand(); + IDynamicMock mock = new DynamicMock(typeof (IMethodInterceptor)); + AbstractMethodInvocation join = CreateMethodInvocation( + null, target, target.GetTargetMethod(), null, null, target.GetType(), new object[] { mock.Object }); + mock.ExpectAndThrow("Invoke", new NotImplementedException()); + try + { + join.Proceed(); + } + catch (NotImplementedException) + { + // this is good, we want this exception to bubble up... + } + catch (TargetInvocationException) + { + Assert.Fail("Must have unwrapped this."); + } + mock.Verify(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/AdvisorAdapterRegistrationTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/AdvisorAdapterRegistrationTests.cs new file mode 100644 index 00000000..a20e60c3 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/AdvisorAdapterRegistrationTests.cs @@ -0,0 +1,86 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports +using System; +using System.Text; + +using NUnit.Framework; + +using Spring.Aop; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects; +using Spring.Objects.Factory.Xml; +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// TestCase for AdvisorAdapterRegistrationManager mechanism. + /// + /// Dmitriy Kopylenko + /// Simon White (.NET) + [TestFixture] + public class AdvisorAdapterRegistrationTests + { + [Test] + public void AdvisorAdapterRegistrationManagerNotPresentInContext() + { + string configLocation = ReadOnlyXmlTestResource.GetFilePath("withoutBPPContext.xml", typeof(AdvisorAdapterRegistrationTests)); + IApplicationContext ctx = new XmlApplicationContext(configLocation); + ITestObject to = (ITestObject) ctx.GetObject("testObject"); + // just invoke any method to see if advice fired + try + { + to.ReturnsThis(); + Assert.Fail("Should throw UnknownAdviceTypeException"); + } + catch (UnknownAdviceTypeException) + { + // expected + Assert.AreEqual(0, GetAdviceImpl(to).InvocationCounter); + } + } + + [Test] + public void AdvisorAdapterRegistrationManagerPresentInContext() + { + string configLocation = ReadOnlyXmlTestResource.GetFilePath("withBPPContext.xml", typeof(AdvisorAdapterRegistrationTests)); + IApplicationContext ctx = new XmlApplicationContext(configLocation); + ITestObject to = (ITestObject) ctx.GetObject("testObject"); + // just invoke any method to see if advice fired + try + { + to.ReturnsThis(); + Assert.AreEqual(1, GetAdviceImpl(to).InvocationCounter); + } + catch (UnknownAdviceTypeException) + { + Assert.Fail("Should not throw UnknownAdviceTypeException"); + } + } + + private SimpleBeforeAdviceImpl GetAdviceImpl(ITestObject to) + { + IAdvised advised = (IAdvised) to; + IAdvisor advisor = advised.Advisors[0]; + return (SimpleBeforeAdviceImpl) advisor.Advice; + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/AfterReturningAdviceInterceptorTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/AfterReturningAdviceInterceptorTests.cs new file mode 100644 index 00000000..bc50b3c5 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/AfterReturningAdviceInterceptorTests.cs @@ -0,0 +1,96 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using AopAlliance.Intercept; +using DotNetMock.Dynamic; +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// Unit tests for the AfterReturningAdviceInterceptor class. + /// + /// Rod Johnson + /// Simon White (.NET) + [TestFixture] + public sealed class AfterReturningAdviceInterceptorTests + { + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void PassNullAdviceToCtor() + { + new AfterReturningAdviceInterceptor(null); + } + + [Test] + public void IsNotInvokedIfServiceObjectThrowsException() + { + IDynamicMock mockAdvice = new DynamicMock(typeof (IAfterReturningAdvice)); + IAfterReturningAdvice afterAdvice = (IAfterReturningAdvice) mockAdvice.Object; + + IDynamicMock mockInvocation = new DynamicMock(typeof (IMethodInvocation)); + IMethodInvocation invocation = (IMethodInvocation) mockInvocation.Object; + mockInvocation.ExpectAndThrow("Proceed", new FormatException(), null); + + try + { + AfterReturningAdviceInterceptor interceptor = new AfterReturningAdviceInterceptor(afterAdvice); + interceptor.Invoke(invocation); + Assert.Fail("Must have thrown a FormatException by this point."); + } + catch (FormatException) + { + } + + mockAdvice.Verify(); // must not have been called... + mockInvocation.Verify(); + } + + [Test] + public void JustPassesAfterReturningAdviceExceptionUpWithoutAnyWrapping() + { + IDynamicMock mockAdvice = new DynamicMock(typeof (IAfterReturningAdvice)); + IAfterReturningAdvice afterAdvice = (IAfterReturningAdvice) mockAdvice.Object; + mockAdvice.ExpectAndThrow("AfterReturning", new FormatException(), new object[] { null, null, null, null}); + + IDynamicMock mockInvocation = new DynamicMock(typeof (IMethodInvocation)); + IMethodInvocation invocation = (IMethodInvocation) mockInvocation.Object; + mockInvocation.ExpectAndReturn("Proceed", null); + + try + { + AfterReturningAdviceInterceptor interceptor = new AfterReturningAdviceInterceptor(afterAdvice); + interceptor.Invoke(invocation); + Assert.Fail("Must have thrown a FormatException by this point."); + } + catch (FormatException) + { + } + + mockAdvice.Verify(); + mockInvocation.Verify(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/ThrowsAdviceInterceptorTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/ThrowsAdviceInterceptorTests.cs new file mode 100644 index 00000000..4eb9c444 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/ThrowsAdviceInterceptorTests.cs @@ -0,0 +1,288 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Runtime.Remoting; +using System.Web; +using AopAlliance.Intercept; +using DotNetMock.Dynamic; +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// Unit tests for the ThrowsAdviceInterceptor class. + /// + /// Rod Johnson + /// Simon White (.NET) + [TestFixture] + public sealed class ThrowsAdviceInterceptorTests + { + [Test] + [ExpectedException(typeof (ArgumentException))] + public void NoHandlerMethods() + { + new ThrowsAdviceInterceptor(new object()); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void PassNullAdviceToCtor() + { + new ThrowsAdviceInterceptor(null); + } + + [Test] + public void NotInvoked() + { + MyThrowsHandler th = new MyThrowsHandler(); + ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th); + object ret = new object(); + + IDynamicMock mc = new DynamicMock(typeof (IMethodInvocation)); + IMethodInvocation mi = (IMethodInvocation) mc.Object; + mi.Proceed(); + mc.SetValue("Proceed", ret); + Assert.AreEqual(ret, ti.Invoke(mi)); + Assert.AreEqual(0, th.GetCalls()); + mc.Verify(); + } + + [Test] + public void NoHandlerMethodForThrowable() + { + MyThrowsHandler th = new MyThrowsHandler(); + ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th); + Assert.AreEqual(2, ti.HandlerMethodCount); + Exception ex = new Exception(); + IDynamicMock mc = new DynamicMock(typeof (IMethodInvocation)); + IMethodInvocation mi = (IMethodInvocation) mc.Object; + mi.Proceed(); + mc.ExpectAndThrow("Proceed", ex, null); + try + { + ti.Invoke(mi); + Assert.Fail(); + } + catch (Exception caught) + { + Assert.AreEqual(ex, caught); + } + Assert.AreEqual(0, th.GetCalls()); + mc.Verify(); + } + + [Test] + public void CorrectHandlerUsed() + { + MyThrowsHandler th = new MyThrowsHandler(); + ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th); + HttpException ex = new HttpException(); + IDynamicMock mc = new DynamicMock(typeof (IMethodInvocation)); + IMethodInvocation mi = (IMethodInvocation) mc.Object; + mi.Proceed(); + mc.ExpectAndThrow("Proceed", ex, null); + try + { + ti.Invoke(mi); + Assert.Fail(); + } + catch (Exception caught) + { + Assert.AreEqual(ex, caught); + } + Assert.AreEqual(1, th.GetCalls()); + Assert.AreEqual(1, th.GetCalls("HttpException")); + mc.Verify(); + } + + [Test] + public void NestedInnerExceptionsAreNotPickedUp() + { + MyThrowsHandler throwsHandler = new MyThrowsHandler(); + ThrowsAdviceInterceptor throwsInterceptor = new ThrowsAdviceInterceptor(throwsHandler); + // nest the exceptions; make sure the advice gets applied because of the inner exception... + Exception exception = new FormatException("Parent", new HttpException("Inner")); + IDynamicMock mockInvocation = new DynamicMock(typeof (IMethodInvocation)); + IMethodInvocation invocation = (IMethodInvocation) mockInvocation.Object; + invocation.Proceed(); + mockInvocation.ExpectAndThrow("Proceed", exception, null); + try + { + throwsInterceptor.Invoke(invocation); + Assert.Fail("Must have failed (by throwing an exception by this point - check the mock)."); + } + catch (Exception caught) + { + Assert.AreEqual(exception, caught); + } + Assert.AreEqual(0, throwsHandler.GetCalls(), + "Must NOT have been handled, 'cos the HttpException was wrapped by " + + "another Exception that did not have a handler."); + Assert.AreEqual(0, throwsHandler.GetCalls("HttpException"), + "Similarly, must NOT have been handled, 'cos the HttpException was wrapped by " + + "another Exception that did not have a handler."); + mockInvocation.Verify(); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ChokesOnHandlerWhereMultipleMethodsAreApplicable() + { + object throwsHandler = new MultipleMethodsAreApplicableThrowsHandler(); + new ThrowsAdviceInterceptor(throwsHandler); + } + + [Test] + public void CorrectHandlerUsedForSubclass() + { + MyThrowsHandler th = new MyThrowsHandler(); + ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th); + // Extends RemotingException + RemotingTimeoutException ex = new RemotingTimeoutException(); + IDynamicMock mc = new DynamicMock(typeof (IMethodInvocation)); + IMethodInvocation mi = (IMethodInvocation) mc.Object; + mi.Proceed(); + mc.ExpectAndThrow("Proceed", ex, null); + try + { + ti.Invoke(mi); + Assert.Fail(); + } + catch (Exception caught) + { + Assert.AreEqual(ex, caught); + } + Assert.AreEqual(1, th.GetCalls()); + Assert.AreEqual(1, th.GetCalls("RemotingException")); + mc.Verify(); + } + + [Test] + public void HandlerMethodThrowsException() + { + Exception exception = new Exception(); + MyThrowsHandler handler = new ThrowingMyHandler(exception); + ThrowsAdviceInterceptor interceptor = new ThrowsAdviceInterceptor(handler); + // extends RemotingException... + RemotingTimeoutException ex = new RemotingTimeoutException(); + IDynamicMock mc = new DynamicMock(typeof (IMethodInvocation)); + IMethodInvocation invocation = (IMethodInvocation) mc.Object; + invocation.Proceed(); + mc.ExpectAndThrow("Proceed", ex, null); + try + { + interceptor.Invoke(invocation); + Assert.Fail("Should not have reached this point, should have thrown an exception."); + } + catch (Exception caught) + { + Assert.AreEqual(exception, caught); + } + Assert.AreEqual(1, handler.GetCalls()); + Assert.AreEqual(1, handler.GetCalls("RemotingException")); + mc.Verify(); + } + + #region Helper Classes + + private sealed class MultipleMethodsAreApplicableThrowsHandler + { + public void AfterThrowing( + MethodInfo method, object[] args, object target, RemotingException ex) + { + } + + public void AfterThrowing(RemotingException ex) + { + } + } + + private class ThrowingMyHandler : MyThrowsHandler + { + private Exception exception; + + public ThrowingMyHandler(Exception ex) + { + this.exception = ex; + } + + public override void AfterThrowing(RemotingException ex) + { + base.AfterThrowing(ex); + throw exception; + } + } + + public class MyThrowsHandler : MethodCounter, IThrowsAdvice + { + public void AfterThrowing( + MethodInfo m, object[] args, object target, HttpException ex) + { + Count("HttpException"); + } + + public virtual void AfterThrowing(RemotingException ex) + { + Count("RemotingException"); + } + + // not valid, wrong number of arguments... + public void AfterThrowing(MethodInfo m, Exception ex) + { + throw new NotSupportedException("Shouldn't be called"); + } + } + + public interface IEcho + { + int A { get; set; } + + int EchoException(int i, Exception t); + } + + public class Echo : IEcho + { + private int a; + + public int A + { + get { return a; } + set { a = value; } + } + + public virtual int EchoException(int i, Exception ex) + { + if (ex != null) + { + throw ex; + } + return i; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/UnknownAdviceTypeExceptionTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/UnknownAdviceTypeExceptionTests.cs new file mode 100644 index 00000000..22637562 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/Adapter/UnknownAdviceTypeExceptionTests.cs @@ -0,0 +1,45 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Framework.Adapter +{ + /// + /// Unit tests for the UnknownAdviceTypeException class. + /// + /// Rick Evans + /// $Id: UnknownAdviceTypeExceptionTests.cs,v 1.2 2006/04/09 07:19:05 markpollack Exp $ + [TestFixture] + public sealed class UnknownAdviceTypeExceptionTests + { + [Test] + public void InstantiationWithNullAdviceDoesNotThrowAnotherException() + { + new UnknownAdviceTypeException(null); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AopContextTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AopContextTests.cs new file mode 100644 index 00000000..e682ad8e --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AopContextTests.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Threading; + +using NUnit.Framework; + +using Spring.Objects; +using AopAlliance.Intercept; +using Spring.Threading; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Unit tests for the AopContext class. + /// + /// Rick Evans + /// $Id: AopContextTests.cs,v 1.6 2007/05/30 21:06:25 oakinger Exp $ + [TestFixture] + public sealed class AopContextTests + { + [SetUp] + public void SetUp() + { + // makes sure the context is always empty before any unit test... + try + { + do + { + AopContext.PopProxy(); + } while (true); + } + catch (AopConfigException) + { + } + } + + [Test] + [ExpectedException(typeof (AopConfigException))] + public void CurrentProxyChokesIfNoAopProxyIsOnTheStack() + { + AopContext.CurrentProxy.ToString(); + } + + [Test] + public void CurrentProxyStackJustPeeksItDoesntPop() + { + string foo = "Foo"; + AopContext.PushProxy(foo); + object fooref = AopContext.CurrentProxy; + Assert.IsTrue(ReferenceEquals(foo, fooref), + "Not the exact same instance (must be)."); + // must not have been popped off the stack by looking at it... + object foorefref = AopContext.CurrentProxy; + Assert.IsTrue(ReferenceEquals(fooref, foorefref), + "Not the exact same instance (must be)."); + } + + [Test] + [ExpectedException(typeof (AopConfigException))] + public void PopProxyWithNothingOnStack() + { + AopContext.PopProxy(); + } + + #region CurrentProxyIsThreadSafe + + [Test(Description = "http://opensource.atlassian.com/projects/spring/browse/SPRNET-341")] + public void CurrentProxyIsThreadSafe() + { + AsyncTestMethod t1 = new AsyncTestMethod(100, new ThreadStart(ProxyTestObjectAndExposeProxy)); + AsyncTestMethod t2 = new AsyncTestMethod(100, new ThreadStart(ProxyTestObjectAndExposeProxy)); + t1.Start(); + t2.Start(); + + t1.AssertNoException(); + t2.AssertNoException(); + } + + private void ProxyTestObjectAndExposeProxy() + { + TestObject target = new TestObject(); + target.Age = 26; + + ProxyFactory pf = new ProxyFactory(); + pf.ExposeProxy = true; + pf.Target = target; + pf.AddAdvice(new TestAopContextInterceptor()); + + ITestObject proxy = pf.GetProxy() as ITestObject; + Assert.IsNotNull(proxy); + Assert.AreEqual(target.Age, proxy.Age, "Incorrect age"); + } + + private class TestAopContextInterceptor : IMethodInterceptor + { + public object Invoke(IMethodInvocation invocation) + { + Assert.IsNotNull(AopContext.CurrentProxy); + Object ret = invocation.Proceed(); + Assert.IsNotNull(AopContext.CurrentProxy); + return ret; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/AdvisorAutoProxyCreatorCircularReferencesTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/AdvisorAutoProxyCreatorCircularReferencesTests.cs new file mode 100644 index 00000000..f0ff1d50 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/AdvisorAutoProxyCreatorCircularReferencesTests.cs @@ -0,0 +1,146 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; +using Spring.Aop.Framework.DynamicProxy; +using Spring.Aop.Support; +using Spring.Context.Support; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// Tests for auto proxy creation combined with factory object and circular references. + /// + /// Erich Eichinger (.NET) + /// $Id: AdvisorAutoProxyCreatorCircularReferencesTests.cs,v 1.4 2007/09/07 01:53:01 markpollack Exp $ + [TestFixture] + public class AdvisorAutoProxyCreatorCircularReferencesTests + { + [Test] + public void TestAutoProxyCreation() + { + XmlApplicationContext context = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("advisorAutoProxyCreatorCircularReferencesTests.xml", typeof(AdvisorAutoProxyCreatorCircularReferencesTests))); + // direct deps of AutoProxyCreator are not eligable for proxying + Assert.IsFalse(AopUtils.IsAopProxy(context.GetObject("aapc"))); + Assert.IsFalse(AopUtils.IsAopProxy(context.GetObject("testAdvisor"))); + Assert.IsFalse(AopUtils.IsAopProxy(context.GetObject("&testObjectFactory"))); + Assert.IsFalse(AopUtils.IsAopProxy(context.GetObject("someOtherObject"))); + + // this one is completely independent + Assert.IsTrue(AopUtils.IsAopProxy(context.GetObject("independentObject"))); + + // products of the factory created at runtime should be proxied + Assert.IsTrue(AopUtils.IsAopProxy(context.GetObject("testObjectFactory"))); + } + } + + #region Support Classes + + public class TestAdvisor : StaticMethodMatcherPointcutAdvisor + { + private ITestObject testObject; + + public ITestObject TestObject + { + get { return this.testObject; } + set { this.testObject = value; } + } + + public override bool Matches(MethodInfo method, Type targetType) + { + return true; + } + } + + public class SomeOtherObject + {} + + public class IndependentObject + { } + + public class TestObjectFactoryObject : IFactoryObject, IInitializingObject + { + private bool initialized = false; + private ITestObject testObject; + private SomeOtherObject someOtherObject; + + public TestObjectFactoryObject() + { + } + + public SomeOtherObject SomeOtherObject + { + get { return this.someOtherObject; } + set { this.someOtherObject = value; } + } + + public object GetObject() + { + // return product only, if factory has been fully initialized! + if (!initialized) + { + return null; + } + else + { + return testObject; + } + } + + public Type ObjectType + { + get + { + // return type only if we are ready to deliver our product! + if (!initialized) + { + return null; + } + else + { + return typeof(ITestObject); + } + } + } + + public bool IsSingleton + { + get { return true; } + } + + public void AfterPropertiesSet() + { + Assert.IsNotNull(someOtherObject); + testObject = new TestObject(); + initialized = true; + } + } + + #endregion +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/AdvisorAutoProxyCreatorTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/AdvisorAutoProxyCreatorTests.cs new file mode 100644 index 00000000..f5d3f30c --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/AdvisorAutoProxyCreatorTests.cs @@ -0,0 +1,121 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Aop.Framework.DynamicProxy; +using Spring.Context.Support; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; +using Spring.Threading; + +#endregion#region License + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// Tests for auto proxy creation by advisor recognition. + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// $Id: AdvisorAutoProxyCreatorTests.cs,v 1.4 2007/08/01 17:56:25 markpollack Exp $ + [TestFixture] + public class AdvisorAutoProxyCreatorTests + { + private static string ADVISOR_APC_OBJECT_NAME = "aapc"; + + protected virtual IObjectFactory ObjectFactory + { + get + { + string configLocation = ReadOnlyXmlTestResource.GetFilePath("advisorAutoProxyCreator.xml", typeof(AdvisorAutoProxyCreatorTests)); + return new XmlApplicationContext(configLocation); + } + } + + [Test] + public void DefaultExclusionPrefix() + { + DefaultAdvisorAutoProxyCreator aapc = (DefaultAdvisorAutoProxyCreator)ObjectFactory.GetObject(ADVISOR_APC_OBJECT_NAME); + Assert.AreEqual(ADVISOR_APC_OBJECT_NAME + DefaultAdvisorAutoProxyCreator.SEPARATOR, aapc.AdvisorObjectNamePrefix); + Assert.IsFalse(aapc.UsePrefix); + } + + /// + /// No pointcuts match the methods on NoSetterProperties therefore + /// there should be proxying. + /// + [Test] + public void NoProxy() + { + IObjectFactory of = ObjectFactory; + object o = of.GetObject("noSetterPropertiesObject"); + Assert.IsFalse(AopUtils.IsAopProxy(o)); + } + + /// + /// A pointcut matches the property (i.e. method set_Age) on TestObject + /// therefore there should be proxying. + /// + [Test] + public void HasProxy() + { + IObjectFactory of = ObjectFactory; + object o = of.GetObject("testObject"); + Assert.IsTrue(AopUtils.IsAopProxy(o), "Expected TestObject to be proxied"); + } + + [Test] + public void RegexpApplied() + { + IObjectFactory of = ObjectFactory; + ITestObject testObject = (ITestObject)of.GetObject("testObject"); + MethodCounter counter = (MethodCounter)of.GetObject("CountingAdvice"); + Assert.AreEqual(0,counter.GetCalls()); + testObject.Spouse = new TestObject("Daniela", 23); + Assert.AreEqual(0, counter.GetCalls()); + testObject.Name = "foo"; + Assert.AreEqual(1, counter.GetCalls()); + + } + + [Test] + public void SetLTCValue() + { + IObjectFactory of = ObjectFactory; + ITestObject testObject = (ITestObject)of.GetObject("testObject"); + OrderedLogicalThreadContextCheckAdvisor orderedBeforeLTCSet = + (OrderedLogicalThreadContextCheckAdvisor)of.GetObject("orderedBeforeLTCSet"); + Assert.AreEqual(0, orderedBeforeLTCSet.CountingBeforeAdvice.GetCalls()); + + Assert.IsNull(LogicalThreadContext.GetData(LogicalThreadContextAdvice.ORDERING_SLOT)); + Assert.AreEqual(4, testObject.Age, "Initial value of age for test object is not correct."); + int newAge = 5; + testObject.Age = newAge; + Assert.AreEqual(1, orderedBeforeLTCSet.CountingBeforeAdvice.GetCalls()); + + Assert.AreEqual(newAge, testObject.Age, "Assigned value of age for test object is not correct."); + Assert.IsNotNull(LogicalThreadContext.GetData(LogicalThreadContextAdvice.ORDERING_SLOT)); + + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/CreatesTestObject.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/CreatesTestObject.cs new file mode 100644 index 00000000..5863c088 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/CreatesTestObject.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Objects; +using Spring.Objects.Factory; + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// This is simple implementation of IFactoryObject that creates a TestObject. + /// + /// Mark Pollack + /// $Id: CreatesTestObject.cs,v 1.1 2007/09/07 01:53:02 markpollack Exp $ + public class CreatesTestObject : IFactoryObject, IInitializingObject + { + private bool initialized = false; + private ITestObject testObject; + + public CreatesTestObject() + { + } + + + public object GetObject() + { + // return product only, if factory has been fully initialized! + if (!initialized) + { + return null; + } + else + { + return testObject; + } + } + + public Type ObjectType + { + get + { + // return type only if we are ready to deliver our product! + if (!initialized) + { + return null; + } + else + { + return typeof(ITestObject); + } + } + } + + public bool IsSingleton + { + get { return true; } + } + + public void AfterPropertiesSet() + { + testObject = new TestObject(); + initialized = true; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/LogicalThreadContextAdvice.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/LogicalThreadContextAdvice.cs new file mode 100644 index 00000000..234fd3db --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/LogicalThreadContextAdvice.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Reflection; +using Spring.Threading; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + public class LogicalThreadContextAdvice : IMethodBeforeAdvice + { + public static string ORDERING_SLOT = "ordering_slot"; + + public void Before(MethodInfo method, object[] args, object target) + { + LogicalThreadContext.SetData(ORDERING_SLOT, new object()); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/NoSetterProperties.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/NoSetterProperties.cs new file mode 100644 index 00000000..2d67b5d4 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/NoSetterProperties.cs @@ -0,0 +1,44 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Text; + +namespace Spring.Aop.Framework.AutoProxy +{ + public class NoSetterProperties + { + public string FancyName + { + get + { + return "Joe Suave"; + } + } + + public void DoWork() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 100; i++) + { + sb.Append(i); + } + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/ObjectNameAutoProxyCreatorTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/ObjectNameAutoProxyCreatorTests.cs new file mode 100644 index 00000000..996001f7 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/ObjectNameAutoProxyCreatorTests.cs @@ -0,0 +1,162 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Aop.Interceptor; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects; +using Spring.Objects.Factory.Xml; + +#endregion#region License + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// Tests for ObjectNameAutoProxyCreator functionality + /// + /// Mark Pollack + /// $Id: ObjectNameAutoProxyCreatorTests.cs,v 1.6 2007/09/07 01:53:01 markpollack Exp $ + [TestFixture] + public class ObjectNameAutoProxyCreatorTests + { + private IApplicationContext ctx; + + [SetUp] + public void SetUp() + { + string configLocation = + ReadOnlyXmlTestResource.GetFilePath("objectNameAutoProxyCreatorTests.xml", + typeof (ObjectNameAutoProxyCreatorTests)); + ctx = new XmlApplicationContext(configLocation); + } + + [Test] + public void NoProxy() + { + ITestObject testObject = (ITestObject) ctx.GetObject("noproxy"); + Assert.IsFalse(AopUtils.IsAopProxy(testObject), testObject + " is not an AOP proxy"); + Assert.AreEqual("noproxy", testObject.Name); + } + + [Test] + public void ProxyWithExactNameMatch() + { + ITestObject testObject = (ITestObject) ctx.GetObject("testObject"); + ProxyAssertions(testObject, 1); + Assert.AreEqual("SimpleTestObject", testObject.Name); + } + + [Test] + public void ProxyWithDoubleProxying() + { + ITestObject testObject = (ITestObject)ctx.GetObject("doubleProxy"); + ProxyAssertions(testObject, 2); + Assert.AreEqual("doubleProxy", testObject.Name); + } + + [Test] + public void ProxyWithWildcardMatchSuffix() + { + ITestObject testObject = (ITestObject) ctx.GetObject("SmithFamilyMember"); + ProxyAssertions(testObject, 1); + Assert.AreEqual("John Smith", testObject.Name); + } + + [Test] + public void ProxyWithTwoWildcardsMatch() + { + ITestObject testObject = (ITestObject)ctx.GetObject("twoWildcardsTestObject"); + ProxyAssertions(testObject, 1); + Assert.AreEqual("Damjan Tomic", testObject.Name); + } + + [Test] + public void AppliesToCreatedObjectsNotFactoryObject() + { + ITestObject testObject = (ITestObject) ctx.GetObject("factoryObject"); + ProxyAssertions(testObject, 1); + } + + [Test] + public void DecoratorProxyWithWildcardMatch() + { + ITestObject testObject = (ITestObject)ctx.GetObject("decoratorProxy"); + DecoratorProxyAssertions(testObject); + Assert.AreEqual("decoratorProxy", testObject.Name); + } + + [Test] + public void FrozenProxy() + { + ITestObject testObject = (ITestObject)ctx.GetObject("frozen"); + Assert.IsTrue( ((IAdvised)testObject).IsFrozen); + } + + [Test] + public void Introduction() + { + object obj = ctx.GetObject("introductionUsingDecorator"); + Assert.IsNotNull(obj as IIsModified); + ITestObject testObject = (ITestObject) obj; + NopInterceptor nop = (NopInterceptor)ctx.GetObject("introductionNopInterceptor"); + Assert.AreEqual(0, nop.Count); + Assert.IsTrue(AopUtils.IsCompositionAopProxy(testObject), testObject + " is not an Composition AOP Proxy"); + int age = 5; + testObject.Age = age; + Assert.AreEqual(age, testObject.Age); + Assert.IsNotNull(testObject as IIsModified); + Assert.IsTrue(((IIsModified)testObject).IsModified); + Assert.AreEqual(3, nop.Count); + Assert.AreEqual("introductionUsingDecorator", testObject.Name); + } + + + private void ProxyAssertions(ITestObject testObject, int nopInterceptorCount) + { + NopInterceptor nop = (NopInterceptor) ctx.GetObject("nopInterceptor"); + Assert.AreEqual(0, nop.Count); + Assert.IsTrue(AopUtils.IsCompositionAopProxy(testObject), testObject + " is not an AOP Proxy"); + int age = 5; + testObject.Age = age; + Assert.AreEqual(age, testObject.Age); + Assert.AreEqual(2 * nopInterceptorCount, nop.Count); + } + + private void DecoratorProxyAssertions(ITestObject testObject) + { + CountingBeforeAdvice cba = (CountingBeforeAdvice) ctx.GetObject("countingBeforeAdvice"); + NopInterceptor nop = (NopInterceptor)ctx.GetObject("nopInterceptor"); + Assert.AreEqual(0, cba.GetCalls()); + Assert.AreEqual(0, nop.Count); + Assert.IsTrue(AopUtils.IsDecoratorAopProxy(testObject), testObject + " is not an AOP Proxy"); + //extra advice calls are due to test IsDecoratorAopProxy and call to .GetType in impl + Assert.AreEqual(1, nop.Count); + Assert.AreEqual(1, cba.GetCalls()); + int age = 5; + testObject.Age = age; + Assert.AreEqual(age, testObject.Age); + Assert.AreEqual(3, nop.Count); + Assert.AreEqual(3, cba.GetCalls()); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/OrderedLogicalThreadContextCheckAdvisor.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/OrderedLogicalThreadContextCheckAdvisor.cs new file mode 100644 index 00000000..9c77cfa5 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/OrderedLogicalThreadContextCheckAdvisor.cs @@ -0,0 +1,110 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Aop.Support; +using Spring.Objects.Factory; +using Spring.Threading; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// Before advisor that allow us to manipulate ordering to check + /// that superclass sorting works correctly. + /// + /// + /// It doesn't actually do anything except count + /// method invocations and check for presence of a value in the + /// LogicalThreadContext. + /// + /// Mark Pollack (.NET) + /// $Id: OrderedLogicalThreadContextCheckAdvisor.cs,v 1.2 2006/09/15 21:25:17 markpollack Exp $ + public class OrderedLogicalThreadContextCheckAdvisor : StaticMethodMatcherPointcutAdvisor, IInitializingObject + { + + public virtual bool RequireLTCHasValue + { + get { return requireLtcHasValue; } + + set { requireLtcHasValue = value; } + } + + public virtual CountingBeforeAdvice CountingBeforeAdvice + { + get { return (CountingBeforeAdvice) Advice; } + } + + /// Should we insist on the presence of a transaction attribute or refuse to accept one? + private bool requireLtcHasValue = false; + + + public virtual void AfterPropertiesSet() + { + Advice = new LTCCountingBeforeAdvice(this); + } + + public override bool Matches(MethodInfo method, Type targetClass) + { + return method.Name.StartsWith("set_Age"); + } + + + private class LTCCountingBeforeAdvice : CountingBeforeAdvice + { + private OrderedLogicalThreadContextCheckAdvisor enclosingInstance; + + public LTCCountingBeforeAdvice(OrderedLogicalThreadContextCheckAdvisor enclosingInstance) + { + this.enclosingInstance = enclosingInstance; + } + + + public OrderedLogicalThreadContextCheckAdvisor EnclosingInstance + { + get { return enclosingInstance; } + } + + public override void Before(MethodInfo method, object[] args, object target) + { + // do check for presence of LTC value.... + if (EnclosingInstance.requireLtcHasValue) + { + if (LogicalThreadContext.GetData(LogicalThreadContextAdvice.ORDERING_SLOT) == null) + { + throw new SystemException("Expected object in LTC ORDERING_SLOT"); + } + } + else + { + if (LogicalThreadContext.GetData(LogicalThreadContextAdvice.ORDERING_SLOT) != null) + { + throw new SystemException("Expected no object in LTC ORDERING_SLOT"); + } + } + base.Before(method, args, target); + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/advisorAutoProxyCreatorCircularReferencesTests.xml b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/advisorAutoProxyCreatorCircularReferencesTests.xml new file mode 100644 index 00000000..a3ec4d4e --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/advisorAutoProxyCreatorCircularReferencesTests.xml @@ -0,0 +1,35 @@ + + + + Reproduces a problem with AutoProxyCreators, IFactoryObjects, + circular dependencies and a certain order of object definitions + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingAfterReturningAdvice.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingAfterReturningAdvice.cs new file mode 100644 index 00000000..4f16260c --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingAfterReturningAdvice.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports +using System; +using System.Reflection; +using Spring.Aop.Framework; +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Simple after returning advice example that we can use for counting checks. + /// + /// Rod Johnson + /// Bruno Baia (.NET) + /// $Id: CountingAfterReturningAdvice.cs,v 1.2 2007/03/16 04:01:47 aseovic Exp $ + [Serializable] + public class CountingAfterReturningAdvice : MethodCounter, IAfterReturningAdvice + { + public void AfterReturning(object returnValue, MethodInfo method, object[] args, object target) + { + Count(method); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingBeforeAdvice.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingBeforeAdvice.cs new file mode 100644 index 00000000..51e58546 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingBeforeAdvice.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports +using System; +using System.Reflection; +using Spring.Aop.Framework; +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Simple before advice example that we can use for counting checks. + /// + /// Rod Johnson + /// Choy Rim (.NET) + /// $Id: CountingBeforeAdvice.cs,v 1.6 2007/03/16 04:01:47 aseovic Exp $ + [Serializable] + public class CountingBeforeAdvice : MethodCounter, IMethodBeforeAdvice + { + public virtual void Before(MethodInfo method, object[] args, object target) + { + Count(method); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingMultiAdvice.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingMultiAdvice.cs new file mode 100644 index 00000000..2a3b5e18 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingMultiAdvice.cs @@ -0,0 +1,55 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Aop.Framework; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Advice object that implements multiple Advice interfaces. + /// + /// Juergen Hoeller + /// Bruno Baia (.NET) + /// $Id: CountingMultiAdvice.cs,v 1.1 2006/08/10 18:45:57 bbaia Exp $ + public class CountingMultiAdvice : MethodCounter, + IMethodBeforeAdvice, IAfterReturningAdvice, IThrowsAdvice + { + public void Before(MethodInfo method, object[] args, object target) + { + Count(method); + } + + public void AfterReturning(object returnValue, MethodInfo method, object[] args, object target) + { + Count(method); + } + + public void AfterThrowing(ApplicationException aex) + { + Count(aex.GetType().Name); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingThrowsAdvice.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingThrowsAdvice.cs new file mode 100644 index 00000000..6e08fe16 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/CountingThrowsAdvice.cs @@ -0,0 +1,50 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using Spring.Aop.Framework; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Simple throw advice example that we can use for counting checks. + /// + /// Rod Johnson + /// Bruno Baia (.NET) + /// $Id: CountingThrowsAdvice.cs,v 1.2 2007/03/16 04:01:47 aseovic Exp $ + [Serializable] + public class CountingThrowsAdvice : MethodCounter, IThrowsAdvice + { + public void AfterThrowing(Exception ex) + { + Count(ex.GetType().Name); + } + + public void AfterThrowing(ApplicationException aex) + { + Count(aex.GetType().Name); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicMethodInvocationTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicMethodInvocationTests.cs new file mode 100644 index 00000000..1374af7e --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicMethodInvocationTests.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Collections; + +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Unit tests for the DynamicMethodInvocation class. + /// + /// Bruno Baia + /// $Id: DynamicMethodInvocationTests.cs,v 1.2 2008/02/06 18:29:05 bbaia Exp $ + [TestFixture] + public class DynamicMethodInvocationTests : AbstractMethodInvocationTests + { + protected override AbstractMethodInvocation CreateMethodInvocation(object proxy, object target, MethodInfo method, MethodInfo onProxyMethod, object[] arguments, Type targetType, IList interceptors) + { + return new DynamicMethodInvocation(proxy, target, method, onProxyMethod, arguments, targetType, interceptors); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/AbstractAopProxyTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/AbstractAopProxyTests.cs new file mode 100644 index 00000000..82e25e26 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/AbstractAopProxyTests.cs @@ -0,0 +1,2490 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Threading; +using System.Reflection; +using System.Collections; +using System.Web; +using System.Diagnostics; +using System.Runtime.Remoting; + +using AopAlliance.Aop; +using AopAlliance.Intercept; +using Spring.Aop.Target; +using Spring.Aop.Framework; +using Spring.Aop.Framework.Adapter; +using Spring.Aop.Interceptor; +using Spring.Aop.Support; +using Spring.Expressions; +using Spring.Objects; +using Spring.Proxy; +using Spring.Threading; +using Spring.Util; + +using DotNetMock.Dynamic; +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Useful base class for Aop proxy test cases. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Bruno Baia (.NET) + /// $Id: AbstractAopProxyTests.cs,v 1.21 2008/05/21 08:04:52 bbaia Exp $ + public abstract class AbstractAopProxyTests + { + protected MockTargetSource mockTargetSource = new MockTargetSource(); + + /* + * Make a clean target source available if code wants to use it. + * The target must be set. Verification will be automatic in tearDown + * to ensure that it was used appropriately by code. + */ + + [TestFixtureSetUp] + public void FixtureSetUp() + { + SystemUtils.RegisterLoadedAssemblyResolver(); + } + + [SetUp] + protected void SetUp() + { + mockTargetSource.Reset(); + } + + [TearDown] + protected void TearDown() + { + mockTargetSource.Verify(); + } + + protected abstract object CreateProxy(AdvisedSupport advisedSupport); + + protected abstract Type CreateAopProxyType(AdvisedSupport advisedSupport); + + protected virtual IAopProxy CreateAopProxy(AdvisedSupport advisedSupport) + { + Type proxyType = CreateAopProxyType(advisedSupport); + ConstructorInfo proxyCtorInfo = proxyType.GetConstructor(new Type[] { typeof(IAdvised) }); + + ExpressionEvaluator.GetValue(advisedSupport, "Activate()"); + return (IAopProxy)proxyCtorInfo.Invoke(new object[] { advisedSupport }); + } + + [Test(Description = "Simple test that if we set values we can get them out again.")] + public void ValuesStick() + { + int age1 = 33; + int age2 = 37; + string name = "tony"; + + TestObject target = new TestObject(); + target.Age = age1; + ProxyFactory pf1 = new ProxyFactory(target); + pf1.AddAdvisor(new DefaultPointcutAdvisor(new NopInterceptor())); + pf1.AddAdvisor(new DefaultPointcutAdvisor(new TimestampIntroductionInterceptor())); + ITestObject to = target; + + Assert.AreEqual(age1, to.Age); + to.Age = age2; + Assert.AreEqual(age2, to.Age); + Assert.IsNull(to.Name); + to.Name = name; + Assert.AreEqual(name, to.Name); + } + + [Test] + public void InterceptorInvokedWithNoTarget() + { + int age = 26; + DynamicMock mock = new DynamicMock(typeof(IMethodInterceptor)); + mock.SetValue("Invoke", age); + IMethodInterceptor mi = (IMethodInterceptor)mock.Object; + + AdvisedSupport advised = new AdvisedSupport(); + advised.AddAdvice(mi); + advised.Interfaces = new Type[] { typeof(ITestObject) }; + + ITestObject to = CreateProxy(advised) as ITestObject; + Assert.IsNotNull(to); + Assert.IsTrue(to.Age == age, "Incorrect age"); + mock.Verify(); + } + + [Test] + public void ProxyAProxy() + { + ITestObject target = new TestObject(); + target.Age = 26; + + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = target; + advised.AddAdvice(new NopInterceptor()); + IAopProxy aop = CreateAopProxy(advised); + ITestObject proxy1 = (ITestObject)aop.GetProxy(); + Assert.AreEqual(target.Age, proxy1.Age, "Incorrect age"); + + advised = new AdvisedSupport(); + advised.Target = proxy1; + advised.AddAdvice(new NopInterceptor()); + aop = CreateAopProxy(advised); + ITestObject proxy2 = (ITestObject)aop.GetProxy(); + Assert.AreEqual(target.Age, proxy2.Age, "Incorrect age"); + } + + #region ProxyAProxyWhereClassExplicitlyImplementsInterfacesWithSameMethodNamesAndSignatures + + // SPRNET-655 + [Test] + public void ProxyAProxyWhereClassExplicitlyImplementsInterfacesWithSameMethodNamesAndSignatures() + { + TheCommand target = new TheCommand(); + + // proxy + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = target; + advised.AddAdvice(new NopInterceptor()); + object proxy = CreateProxy(advised); + + // proxy again + advised = new AdvisedSupport(); + advised.Target = proxy; + advised.AddAdvice(new NopInterceptor()); + proxy = CreateAopProxy(advised); + + IServiceCommand serviceCommand = proxy as IServiceCommand; + Assert.IsNotNull(serviceCommand); + serviceCommand.Execute(); + + IBusinessCommand businessCommand = proxy as IBusinessCommand; + Assert.IsNotNull(businessCommand); + businessCommand.Execute(); + + Assert.AreEqual(1, serviceCommand.ServiceCount); + Assert.AreEqual(1, businessCommand.BusinessCount); + } + + public interface IServiceCommand + { + int ServiceCount { get; } + + void Execute(); + } + + public interface IBusinessCommand + { + int BusinessCount { get; } + + void Execute(); + } + + public class TheCommand : IServiceCommand, IBusinessCommand + { + #region IServiceCommand Members + + private int _serviceCount = 0; + int IServiceCommand.ServiceCount + { + get { return _serviceCount; } + } + + void IServiceCommand.Execute() + { + _serviceCount++; + } + + #endregion + + #region IBusinessCommand Members + + private int _businessCount = 0; + int IBusinessCommand.BusinessCount + { + get { return _businessCount; } + } + + void IBusinessCommand.Execute() + { + _businessCount++; + } + + #endregion + } + + #endregion + + [Test] + public void EqualsMethod() + { + TestObject target = new TestObject(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.Target = target; + IAopProxy aopProxy = CreateAopProxy(advised); + object proxy = aopProxy.GetProxy(); + + Assert.IsNotNull(proxy); + Assert.AreEqual(target, proxy, "Equals() returned false"); + } + + [Test] + public void GetHashCodeMethod() + { + TestObject target = new TestObject(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.Target = target; + IAopProxy aopProxy = CreateAopProxy(advised); + object proxy = aopProxy.GetProxy(); + + Assert.IsNotNull(proxy); + Assert.AreEqual(target.GetHashCode(), proxy.GetHashCode(), "GetHashCode() not equal"); + } + + #region ProxyMethodWithRefOutParameters + + [Test] + public void ProxyMethodWithRefOutParametersWithDirectCall() + { + PublicRefOutTestObject target = new PublicRefOutTestObject(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IRefOutTestObject) }); + advised.Target = target; + + IAopProxy aopProxy = CreateAopProxy(advised); + IRefOutTestObject proxy = aopProxy.GetProxy() as IRefOutTestObject; + Assert.IsNotNull(proxy); + + TestsProxyMethodWithRefOutParameters(proxy); + } + + [Test] + public void ProxyMethodWithRefOutParametersWithDynamicReflection() + { + PublicRefOutTestObject target = new PublicRefOutTestObject(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IRefOutTestObject) }); + advised.Target = target; + NopInterceptor ni = new NopInterceptor(); + advised.AddAdvice(ni); + + IAopProxy aopProxy = CreateAopProxy(advised); + IRefOutTestObject proxy = aopProxy.GetProxy() as IRefOutTestObject; + Assert.IsNotNull(proxy); + + TestsProxyMethodWithRefOutParameters(proxy); + + Assert.AreEqual(1, ni.Count); + } + + [Test] + public virtual void ProxyMethodWithRefOutParametersWithStandardReflection() + { + InternalRefOutTestObject target = new InternalRefOutTestObject(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IRefOutTestObject) }); + advised.Target = target; + NopInterceptor ni = new NopInterceptor(); + advised.AddAdvice(ni); + + IAopProxy aopProxy = CreateAopProxy(advised); + IRefOutTestObject proxy = aopProxy.GetProxy() as IRefOutTestObject; + Assert.IsNotNull(proxy); + + TestsProxyMethodWithRefOutParameters(proxy); + + Assert.AreEqual(1, ni.Count); + } + + private void TestsProxyMethodWithRefOutParameters(IRefOutTestObject instance) + { + int valueType = 0; + TestObject obj = null; + EnumValue enumValue = EnumValue.Initial; + bool refValueType = false; + int outValueType; + String refObj = null; + TestObject outObj; + EnumValue refEnum = EnumValue.Initial; + EnumValue outEnum = EnumValue.Initial; + Guid refGuid = Guid.NewGuid(); + Guid outGuid = Guid.NewGuid(); + + instance.DoIt(valueType, obj, enumValue, ref refValueType, out outValueType, ref refObj, out outObj, ref refEnum, out outEnum, ref refGuid, out outGuid); + + Assert.AreEqual(0, valueType); + Assert.AreEqual(null, obj); + Assert.AreEqual(EnumValue.Initial, enumValue); + Assert.AreEqual(true, refValueType); + Assert.AreEqual(1, outValueType); + Assert.AreEqual("RefObj", refObj); + Assert.IsNotNull(outObj); + Assert.AreEqual("OutObj", outObj.Name); + Assert.AreEqual(EnumValue.Ref, refEnum); + Assert.AreEqual(EnumValue.Out, outEnum); + Assert.AreEqual(Guid.Empty, refGuid); + Assert.AreEqual(Guid.Empty, outGuid); + } + + public enum EnumValue + { + Initial, + Ref, + Out, + Another + } + + public interface IRefOutTestObject + { + int DoIt(int valueType, TestObject obj, EnumValue enumValue, + ref bool refValueType, out int outValueType, + ref String refObj, out TestObject outObj, + ref EnumValue refEnum, out EnumValue outEnum, + ref Guid refGuid, out Guid outGuid); + } + + public class PublicRefOutTestObject : IRefOutTestObject + { + public int DoIt(int valueType, TestObject obj, EnumValue enumValue, + ref bool refValueType, out int outValueType, + ref String refObj, out TestObject outObj, + ref EnumValue refEnum, out EnumValue outEnum, + ref Guid refGuid, out Guid outGuid) + { + valueType++; + obj = new TestObject("Bruno", 27); + enumValue = EnumValue.Another; + refValueType = true; + outValueType = 1; + refObj += "RefObj"; + outObj = new TestObject("OutObj", 27); + refEnum = EnumValue.Ref; + outEnum = EnumValue.Out; + refGuid = Guid.Empty; + outGuid = Guid.Empty; + + return 0; + } + } + + internal class InternalRefOutTestObject : PublicRefOutTestObject + { + } + + #endregion + + #region ProxyGenericMethodWithRefOutParameters + +#if NET_2_0 + [Test] + public void ProxyGenericMethodWithRefOutParametersWithDirectCall() + { + PublicRefOutGenericTestObject target = new PublicRefOutGenericTestObject(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IRefOutGenericTestObject) }); + advised.Target = target; + + IAopProxy aopProxy = CreateAopProxy(advised); + IRefOutGenericTestObject proxy = aopProxy.GetProxy() as IRefOutGenericTestObject; + Assert.IsNotNull(proxy); + + TestsProxyGenericMethodWithRefOutParameters(proxy); + } + + [Test] + public void ProxyGenericMethodWithRefOutParametersWithDynamicReflection() + { + PublicRefOutGenericTestObject target = new PublicRefOutGenericTestObject(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IRefOutGenericTestObject) }); + advised.Target = target; + NopInterceptor ni = new NopInterceptor(); + advised.AddAdvice(ni); + + IAopProxy aopProxy = CreateAopProxy(advised); + IRefOutGenericTestObject proxy = aopProxy.GetProxy() as IRefOutGenericTestObject; + Assert.IsNotNull(proxy); + + TestsProxyGenericMethodWithRefOutParameters(proxy); + + Assert.AreEqual(3, ni.Count); + } + + [Test] + public virtual void ProxyGenericMethodWithRefOutParametersWithStandardReflection() + { + InternalRefOutGenericTestObject target = new InternalRefOutGenericTestObject(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IRefOutGenericTestObject) }); + advised.Target = target; + NopInterceptor ni = new NopInterceptor(); + advised.AddAdvice(ni); + + IAopProxy aopProxy = CreateAopProxy(advised); + IRefOutGenericTestObject proxy = aopProxy.GetProxy() as IRefOutGenericTestObject; + Assert.IsNotNull(proxy); + + TestsProxyGenericMethodWithRefOutParameters(proxy); + + Assert.AreEqual(3, ni.Count); + } + + private void TestsProxyGenericMethodWithRefOutParameters(IRefOutGenericTestObject instance) + { + EnumValue enumValue = EnumValue.Another; + EnumValue refEnum = EnumValue.Ref; + EnumValue outEnum = EnumValue.Out; + EnumValue enumResult = instance.DoIt(enumValue, ref refEnum, out outEnum); + Assert.AreEqual(EnumValue.Another, refEnum); + Assert.AreEqual(EnumValue.Another, outEnum); + Assert.AreEqual(EnumValue.Another, enumResult); + + TestObject objectValue = new TestObject("Value", 28); + TestObject refObject = new TestObject("Ref", 28); + TestObject outObject = new TestObject("Out", 28); + TestObject objectResult = instance.DoIt(objectValue, ref refObject, out outObject); + Assert.AreEqual("Value", refObject.Name); + Assert.AreEqual("Value", outObject.Name); + Assert.AreEqual("Value", objectResult.Name); + + int intValue = 1; + int refInt = 2; + int outInt = 3; + int intResult = instance.DoIt(intValue, ref refInt, out outInt); + Assert.AreEqual(1, refInt); + Assert.AreEqual(1, outInt); + Assert.AreEqual(1, intResult); + } + + public interface IRefOutGenericTestObject + { + T DoIt(T param, ref T refParam, out T outParam); + } + + public class PublicRefOutGenericTestObject : IRefOutGenericTestObject + { + public T DoIt(T paramValue, ref T refParam, out T outParam) + { + refParam = paramValue; + outParam = paramValue; + + return paramValue; + } + } + + internal class InternalRefOutGenericTestObject : PublicRefOutGenericTestObject + { + } +#endif + #endregion + + [Test] + public void ToStringMethod() + { + TestObject target = new TestObject(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.Target = target; + IAopProxy aopProxy = CreateAopProxy(advised); + object proxy = aopProxy.GetProxy(); + + Assert.IsNotNull(proxy); + Assert.AreEqual(target.ToString(), proxy.ToString(), "ToString() not equal"); + } + +#if NET_2_0 + [Test] + public void InterceptGenericMethod() + { + AbstractProxyTypeBuilderTests.ClassWithGenericMethod target = + new AbstractProxyTypeBuilderTests.ClassWithGenericMethod(); + mockTargetSource.SetTarget(target); + + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.TargetSource = mockTargetSource; + advised.AddAdvice(ni); + + AbstractProxyTypeBuilderTests.InterfaceWithGenericMethod proxy = + CreateProxy(advised) as AbstractProxyTypeBuilderTests.InterfaceWithGenericMethod; + Assert.IsNotNull(proxy); + + proxy.PolymorphicMethod(); + proxy.PolymorphicMethod(); + + string result1 = proxy.PolymorphicMethod("coucou"); + Assert.AreEqual("coucou", result1); + + proxy.WithGenericParameter(); + + proxy.WithGenericParameterAndGenericArgument("ola"); + + string result2 = proxy.WithGenericParameterAndGenericArgumentAndGenericReturnType("ola"); + Assert.AreEqual("ola", result2); + + proxy.WithInterfaceConstraint(); + + proxy.WithBaseTypeConstraint(); + + proxy.WithBaseTypeAndInterfaceConstraints(); + + proxy.WithMixedConstraint(); + Assert.AreEqual(10, ni.Count); + } + + [Test] + public void InterceptGenericInterface() + { + AbstractProxyTypeBuilderTests.ClassThatImplementsGenericInterface target = + new AbstractProxyTypeBuilderTests.ClassThatImplementsGenericInterface(); + mockTargetSource.SetTarget(target); + + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.TargetSource = mockTargetSource; + advised.AddAdvice(ni); + + AbstractProxyTypeBuilderTests.GenericInterface proxy = + CreateProxy(advised) as AbstractProxyTypeBuilderTests.GenericInterface; + Assert.IsNotNull(proxy); + + TestObject to1 = proxy.Create(); + Assert.IsNotNull(to1); + + TestObject to2 = proxy.Populate(new TestObject()); + Assert.IsNotNull(to2); + Assert.AreEqual("Populated", to2.Name); + + Assert.AreEqual(2, ni.Count); + } +#endif + + #region MultiThreadedProxyCreation + + [Test(Description = "http://opensource.atlassian.com/projects/spring/browse/SPRNET-340")] + public void MultiThreadedProxyCreation() + { + MultiThreadedProxyCreation(5); + } + + private void MultiThreadedProxyCreation(int howMany) + { + AsyncTestMethod[] threads = new AsyncTestMethod[howMany]; + for (int i = 0; i < howMany; i++) + threads[i] = new AsyncTestMethod(10, new ThreadStart(ProxyTestObject)); + for (int i = 0; i < howMany; i++) + threads[i].Start(); + for (int i = 0; i < howMany; i++) + threads[i].AssertNoException(); + } + + private void ProxyTestObject() + { + TestObject target = new TestObject(); + target.Age = 26; + + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = target; + advised.AddAdvice(new NopInterceptor()); + + ITestObject proxy = CreateProxy(advised) as ITestObject; + Assert.IsNotNull(proxy); + Assert.AreEqual(target.Age, proxy.Age, "Incorrect age"); + } + + #endregion + + #region Attributes + + [Test] + public void ProxyTargetTypeAttributes() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + object[] attrs = proxy.GetType().GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type."); + } + + [Test] + public void DoesNotProxyTargetTypeAttributesWithProxyTargetAttributesEqualsFalse() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + advised.ProxyTargetAttributes = false; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + object[] attrs = proxy.GetType().GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the target type."); + } + + [Test] + public void ProxyTargetMethodAttributes() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("Spring.Aop.Framework.DynamicProxy.AbstractAopProxyTests+IMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetType().GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method."); + } + + [Test] + public void DoesNotProxyTargetMethodAttributesWithProxyTargetAttributesEqualsFalse() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + advised.ProxyTargetAttributes = false; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("Spring.Aop.Framework.DynamicProxy.AbstractAopProxyTests+IMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetType().GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the target method."); + } + + [Test] + public void ProxyTargetMethodParameterAttributes() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("Spring.Aop.Framework.DynamicProxy.AbstractAopProxyTests+IMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetType().GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + + object[] attrs = method.GetParameters()[1].GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's parameter."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's parameter."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's parameter."); + } + + [Test] + public void DoesNotProxyTargetMethodParameterAttributesWithProxyTargetAttributesEqualsFalse() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + advised.ProxyTargetAttributes = false; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("Spring.Aop.Framework.DynamicProxy.AbstractAopProxyTests+IMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetType().GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + + object[] attrs = method.GetParameters()[1].GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's parameter."); + } + +#if NET_2_0 + [Test] + public void ProxyTargetMethodReturnValueAttributes() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("Spring.Aop.Framework.DynamicProxy.AbstractAopProxyTests+IMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetType().GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + + object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's return value."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's return value."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's return value."); + } + + [Test] + public void DoesNotProxyTargetMethodReturnValueAttributesWithProxyTargetAttributesEqualsFalse() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + advised.ProxyTargetAttributes = false; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("Spring.Aop.Framework.DynamicProxy.AbstractAopProxyTests+IMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetType().GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + + object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's return value."); + } +#endif + + public sealed class MarkerAttribute : Attribute + { + } + + public interface IMarkerInterface + { + void MarkerMethod(int param1, string param2); + } + + [Marker] + public class MarkerClass : IMarkerInterface + { + [Marker] + [return: Marker] + public void MarkerMethod(int param1, [Marker]string param2) + { + } + + [Marker] + [return: Marker] + public virtual void MarkerVirtualMethod(int param1, [Marker]string param2) + { + } + } + + #endregion + + [Test] + public void AddAdviceAtRuntime() + { + TestObject target = new TestObject(); + CountingBeforeAdvice cba = new CountingBeforeAdvice(); + + ProxyFactory pf = new ProxyFactory(target); + ITestObject proxy = (ITestObject)CreateProxy(pf); + + proxy.Age.ToString(); + Assert.AreEqual(0, cba.GetCalls()); + ((IAdvised)proxy).AddAdvice(cba); + proxy.Age.ToString(); + Assert.AreEqual(1, cba.GetCalls()); + } + + [Test] + public void SerializationAdviceAndTargetNotSerializable() + { + TestObject to = new TestObject(); + Assert.IsFalse(SerializationTestUtils.IsSerializable(to)); + + ProxyFactory pf = new ProxyFactory(to); + + pf.AddAdvice(new NopInterceptor()); + ITestObject proxy = (ITestObject)CreateAopProxy(pf).GetProxy(); + + Assert.IsFalse(SerializationTestUtils.IsSerializable(proxy)); + } + + [Test] + public void SerializationAdviceNotSerializable() + { + SerializablePerson sp = new SerializablePerson(); + Assert.IsTrue(SerializationTestUtils.IsSerializable(sp)); + + ProxyFactory pf = new ProxyFactory(sp); + + // This isn't serializable + IAdvice i = new NopInterceptor(); + pf.AddAdvice(i); + Assert.IsFalse(SerializationTestUtils.IsSerializable(i)); + Object proxy = CreateAopProxy(pf).GetProxy(); + + Assert.IsFalse(SerializationTestUtils.IsSerializable(proxy)); + } + + [Test] + public void SerializationSerializableTargetAndAdvice() + { + SerializablePerson personTarget = new SerializablePerson(); + personTarget.Name = "jim"; + personTarget.Age = 26; + + Assert.IsTrue(SerializationTestUtils.IsSerializable(personTarget)); + + ProxyFactory pf = new ProxyFactory(personTarget); + + CountingThrowsAdvice cta = new CountingThrowsAdvice(); + + pf.AddAdvice(new SerializableNopInterceptor()); + // Try various advice types + pf.AddAdvice(new CountingBeforeAdvice()); + pf.AddAdvice(new CountingAfterReturningAdvice()); + pf.AddAdvice(cta); + IPerson p = (IPerson) CreateAopProxy(pf).GetProxy(); + + p.Echo(null); + Assert.AreEqual(0, cta.GetCalls()); + try + { + p.Echo(new Exception()); + } + catch (Exception) + { + } + Assert.AreEqual(1, cta.GetCalls()); + + // Will throw exception if it fails + IPerson p2 = (IPerson) SerializationTestUtils.SerializeAndDeserialize(p); + Assert.AreNotSame(p, p2); + Assert.AreEqual(p.GetName(), p2.GetName()); + Assert.AreEqual(p.GetAge(), p2.GetAge()); + Assert.IsTrue(AopUtils.IsAopProxy(p2), "Deserialized object is an AOP proxy"); + + IAdvised a1 = (IAdvised)p; + IAdvised a2 = (IAdvised)p2; + // Check we can manipulate state of p2 + Assert.AreEqual(a1.Advisors.Length, a2.Advisors.Length); + + // This should work as SerializablePerson is equal + Assert.AreEqual(p, p2, "Proxies should be equal, even after one was serialized"); + + // Check we can add a new advisor to the target + NopInterceptor ni = new NopInterceptor(); + p2.GetAge(); + Assert.AreEqual(0, ni.Count); + a2.AddAdvice(ni); + p2.GetAge(); + Assert.AreEqual(1, ni.Count); + + cta = (CountingThrowsAdvice) a2.Advisors[3].Advice; + p2.Echo(null); + Assert.AreEqual(1, cta.GetCalls()); + try + { + p2.Echo(new Exception()); + } + catch (Exception) + { + + } + Assert.AreEqual(2, cta.GetCalls()); + } + + #region OneAdvisedObjectCallsAnother + + /// + /// Check that the two MethodInvocations necessary are independent + /// and don't conflict. + /// + [Test] + public void OneAdvisedObjectCallsAnother() + { + int age1 = 33; + int age2 = 37; + + TestObject target1 = new TestObject(); + ProxyFactory pf1 = new ProxyFactory(target1); + // Permit proxy and invocation checkers to get context from AopContext + pf1.ExposeProxy = true; + NopInterceptor di1 = new NopInterceptor(); + pf1.AddAdvice(0, di1); + pf1.AddAdvice(1, new ProxyMatcherInterceptor()); + pf1.AddAdvice(2, new MethodInvocationMatcherInterceptor()); + ITestObject advised1 = (ITestObject) pf1.GetProxy(); + advised1.Age = age1; // = 1 invocation + + TestObject target2 = new TestObject(); + ProxyFactory pf2 = new ProxyFactory(target2); + pf2.ExposeProxy = true; + NopInterceptor di2 = new NopInterceptor(); + pf2.AddAdvice(0, di2); + pf2.AddAdvice(1, new ProxyMatcherInterceptor()); + pf2.AddAdvice(2, new MethodInvocationMatcherInterceptor()); + ITestObject advised2 = (ITestObject) CreateProxy(pf2); + advised2.Age = age2; + advised1.Spouse = advised2; // = 2 invocations + + Assert.AreEqual(age1, advised1.Age, "Advised one has correct age"); // = 3 invocations + Assert.AreEqual(age2, advised2.Age, "Advised two has correct age"); + // Means extra call on advised 2 + Assert.AreEqual(age2, advised1.Spouse.Age, "Advised one spouse has correct age"); // = 4 invocations on 1 and another one on 2 + Assert.AreEqual(4, di1.Count, "one was invoked correct number of times"); + // Got hit by call to advised1.getSpouse().Age + Assert.AreEqual(3, di2.Count, "one was invoked correct number of times"); + } + + private class MethodInvocationMatcherInterceptor : IMethodInterceptor + { + public object Invoke(IMethodInvocation invocation) + { + MethodInfo m = invocation.Method; + Object retval = invocation.Proceed(); + Assert.AreEqual(m, invocation.Method, "Method invocation should have same method on way back"); + return retval; + } + } + + private class ProxyMatcherInterceptor : IMethodInterceptor + { + public object Invoke(IMethodInvocation invocation) + { + Object proxy = AopContext.CurrentProxy; + Object ret = invocation.Proceed(); + Assert.IsTrue(proxy == AopContext.CurrentProxy, "Proxy should be the same on way back"); + return ret; + } + } + + #endregion + + [Test] + public void Reentrance() + { + int age1 = 33; + + TestObject target1 = new TestObject(); + ProxyFactory pf1 = new ProxyFactory(target1); + NopInterceptor di1 = new NopInterceptor(); + pf1.AddAdvice(0, di1); + ITestObject advised1 = (ITestObject)CreateProxy(pf1); + + advised1.Age = age1; // = 1 invocation + advised1.Spouse = advised1; // = 2 invocations + + Assert.AreEqual(2, di1.Count, "one was invoked correct number of times"); + + Assert.AreEqual(age1, advised1.Age, "Advised one has correct age"); // = 3 invocations + Assert.AreEqual(3, di1.Count, "one was invoked correct number of times"); + + // = 5 invocations, as reentrant call to spouse is advised also + Assert.AreEqual(age1, advised1.Spouse.Age, "Advised spouse has correct age"); + + Assert.AreEqual(5, di1.Count, "one was invoked correct number of times"); + } + + [Test] + public void TargetCanGetProxy() + { + NopInterceptor di = new NopInterceptor(); + INeedsToSeeProxy target = new TargetChecker(); + ProxyFactory proxyFactory = new ProxyFactory(target); + proxyFactory.ExposeProxy = true; + Assert.IsTrue(proxyFactory.ExposeProxy); + + proxyFactory.AddAdvice(0, di); + INeedsToSeeProxy proxied = (INeedsToSeeProxy)CreateProxy(proxyFactory); + + Assert.AreEqual(0, di.Count); + Assert.AreEqual(0, target.Count); + proxied.IncrementViaThis(); + Assert.AreEqual(1, target.Count, "Increment happened"); + + Assert.AreEqual(1, di.Count, "Only one invocation via AOP as use of this wasn't proxied"); + // 1 invocation + Assert.AreEqual(1, proxied.Count, "Increment happened"); + proxied.IncrementViaProxy(); // 2 invoocations + Assert.AreEqual(2, target.Count, "Increment happened"); + Assert.AreEqual(4, di.Count, "3 more invocations via AOP as the first call was reentrant through the proxy"); + } + + /// + /// Check that although a method is eligible for advice chain optimization and + /// direct reflective invocation, it doesn't happen if we've asked to see the proxy, + /// so as to guarantee a consistent programming model. + /// + [Test] + public void TargetCanGetProxyEvenIfNoAdviceChain() + { + NeedsToSeeProxy target = new NeedsToSeeProxy(); + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(INeedsToSeeProxy) }); + advised.Target = target; + advised.ExposeProxy = true; + + // Now let's try it with the special target + IAopProxy aop = CreateAopProxy(advised); + INeedsToSeeProxy proxied = (INeedsToSeeProxy)aop.GetProxy(); + + // It will complain if it can't get the proxy + proxied.IncrementViaProxy(); + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void TargetCantGetProxyByDefault() + { + NeedsToSeeProxy et = new NeedsToSeeProxy(); + ProxyFactory pf1 = new ProxyFactory(et); + Assert.IsFalse(pf1.ExposeProxy); + INeedsToSeeProxy proxied = (INeedsToSeeProxy)CreateProxy(pf1); + proxied.IncrementViaProxy(); + } + + #region TargetReturnsThis + + [Test(Description = "Test that the proxy returns itself when the target returns 'this'.")] + public void TargetReturnsThis() + { + // Test return value + TestObject raw = new OwnSpouse(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.Target = raw; + + ITestObject to = (ITestObject) CreateProxy(advised); + Assert.IsTrue(to.Spouse == to, "this return is wrapped in proxy"); + } + + public class OwnSpouse : TestObject + { + public override ITestObject Spouse + { + get { return this; } + } + } + + #endregion + + [Test] + public void TargetThrowsException() + { + Exception expectedException = new ApplicationException(); + + // Test return value + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.Target = new TestObject(); + IAopProxy aop = CreateAopProxy(advised); + + try + { + ITestObject to = (ITestObject) aop.GetProxy(); + to.Exceptional(expectedException); + Assert.Fail("Should have thrown exception raised by target"); + } + catch (Exception ex) + { + Assert.AreEqual(expectedException, ex, "exception matches"); + } + } + + #region InterceptorThrowsException + + [Test] + public void InterceptorThrowsException() + { + Exception expectedException = new ApplicationException(); + + IMethodInterceptor mi = new ThrowExceptionInterceptor(expectedException); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.Target = new TestObject(); + advised.AddAdvice(mi); + IAopProxy aop = CreateAopProxy(advised); + + try + { + ITestObject to = (ITestObject)aop.GetProxy(); + to.Name = "Bruno"; + Assert.Fail("Should have thrown exception raised by interceptor"); + } + catch (Exception ex) + { + Assert.AreEqual(expectedException, ex, "exception matches"); + } + } + + private class ThrowExceptionInterceptor : IMethodInterceptor + { + private Exception _exception; + + public ThrowExceptionInterceptor(Exception ex) + { + _exception = ex; + } + + public object Invoke(IMethodInvocation invocation) + { + throw _exception; + } + } + + #endregion + + // TODO : Introduction tests +/* + [Test(Description = "Test stateful interceptor")] + public void MixinWithIntroductionAdvisor() + { + TestObject to = new TestObject(); + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.AddAdvisor(new LockMixinAdvisor()); + advised.Target = to; + + CheckTestObjectIntroduction(advised); + } + + [Test] + public void MixinWithIntroductionInfo() + { + TestObject to = new TestObject(); + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.AddAdvice(new LockMixin()); + advised.Target = to; + + CheckTestObjectIntroduction(advised); + } + + private void CheckTestObjectIntroduction(AdvisedSupport advised) + { + int newAge = 65; + + ITestObject ito = (ITestObject) CreateProxy(advised); + ito.Age = newAge; + Assert.IsTrue(ito.Age == newAge); + + ILockable lockable = (ILockable) ito; + Assert.IsFalse(lockable.Locked()); + lockable.DoLock(); + + Assert.IsTrue(ito.Age == newAge); + try + { + ito.Age = 1; + Assert.Fail("Setters should fail when locked"); + } + catch (LockedException) + { + // ok + } + Assert.IsTrue(ito.Age == newAge); + + // Unlock + Assert.IsTrue(lockable.Locked()); + lockable.Unlock(); + ito.Age = 1; + Assert.IsTrue(ito.Age == 1); + } +*/ + + #region MultipleProceedCalls + + [Test] + public void MultipleProceedCalls() + { + TestObject to = new TestObject(); + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.Target = to; + advised.AddAdvice(new RetryAdvice()); + NopInterceptor ni = new NopInterceptor(); + advised.AddAdvice(ni); + + ITestObject ito = (ITestObject)CreateProxy(advised); + ito.Age = 27; + + Assert.AreEqual(2, ni.Count); + } + + public class RetryAdvice : IMethodInterceptor + { + public object Invoke(IMethodInvocation invocation) + { + invocation.Proceed(); + return invocation.Proceed(); + } + } + + #endregion + + #region ReplaceArgument + + [Test] + public void ReplaceArgument() + { + TestObject to = new TestObject(); + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.Target = to; + advised.AddAdvisor(new StringSetterNullReplacementAdvisor(new StringSetterNullReplacementAdvice())); + + ITestObject ito = (ITestObject)CreateProxy(advised); + int newAge = 5; + ito.Age = newAge; + Assert.IsTrue(ito.Age == newAge); + String newName = "greg"; + ito.Name = newName; + Assert.AreEqual(newName, ito.Name); + + ito.Name = null; + + // Null replacement magic should work + Assert.IsTrue(ito.Name.Equals("")); + } + + [Test] + public void ReplaceArgumentDescendingPropagation() + { + TestObject to = new TestObject(); + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.Target = to; + advised.AddAdvisor(new StringSetterNullReplacementAdvisor(new StringSetterNullReplacementAdvice())); + NopInterceptor ni = new NopInterceptor(); + advised.AddAdvisor(new StringSetterNullReplacementAdvisor(ni)); + + ITestObject ito = (ITestObject)CreateProxy(advised); + int newAge = 5; + ito.Age = newAge; + Assert.IsTrue(ito.Age == newAge); + String newName = "greg"; + ito.Name = newName; + Assert.AreEqual(newName, ito.Name); + + ito.Name = null; + + // Null replacement magic should work + Assert.IsTrue(ito.Name.Equals("")); + + // StringSetterNullReplacementAdvisor should not be fire twice + Assert.AreEqual(0, ni.Count); + } + + [Test] + public void ReplaceArgumentAscendingPropagation() + { + TestObject to = new TestObject(); + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.Target = to; + advised.AddAdvice(new CheckArgumentsAfterProceedAdvice()); + advised.AddAdvisor(new StringSetterNullReplacementAdvisor(new StringSetterNullReplacementAdvice())); + + ITestObject ito = (ITestObject)CreateProxy(advised); + ito.Name = null; + } + + /// + /// Fires on setter methods that take a string. + /// + public class StringSetterNullReplacementAdvisor : DynamicMethodMatcherPointcutAdvisor + { + public StringSetterNullReplacementAdvisor(IAdvice advice) + : base(advice) + { + } + + public override bool Matches(MethodInfo method, Type targetType, object[] args) + { + return (args[0] == null); + } + + public override bool Matches(MethodInfo method, Type targetType) + { + return method.Name.StartsWith("set_") && + method.GetParameters().Length == 1 && + method.GetParameters()[0].ParameterType == typeof(string); + } + } + + /// + /// Replaces null arg with "". + /// + public class StringSetterNullReplacementAdvice : IMethodInterceptor + { + public object Invoke(IMethodInvocation invocation) + { + // We know it can only be invoked if there's a single parameter of type string + invocation.Arguments[0] = ""; + return invocation.Proceed(); + } + } + + public class CheckArgumentsAfterProceedAdvice : IMethodInterceptor + { + public object Invoke(IMethodInvocation invocation) + { + object returnValue = invocation.Proceed(); + + // Null replacement magic should work + Assert.IsNotNull(invocation.Arguments[0]); + + return returnValue; + } + } + + #endregion + + /// + /// http://forum.springframework.net/showthread.php?t=504 + /// + [Test] + public void CanCastProxyToIAdvised() + { + TestObject to = new TestObject(); + AdvisedSupport advisedSupport = new AdvisedSupport(); + advisedSupport.Target = to; + NopInterceptor ni = new NopInterceptor(); + advisedSupport.AddAdvice(0, ni); + + ITestObject ito = (ITestObject)CreateProxy(advisedSupport); + Assert.AreEqual(0, ni.Count); + ito.Age = 23; + Assert.AreEqual(23, ito.Age); + Assert.AreEqual(2, ni.Count); + + IAdvised advised = (IAdvised)ito; + Assert.AreEqual(1, advised.Advisors.Length, "Have 1 advisor"); + Assert.AreEqual(ni, advised.Advisors[0].Advice); + NopInterceptor ni2 = new NopInterceptor(); + advised.AddAdvice(1, ni2); + ito.Name = "Bruno"; + Assert.AreEqual(3, ni.Count); + Assert.AreEqual(1, ni2.Count); + // will remove ni + advised.RemoveAdvisor(0); + Assert.IsNotNull(ito.Age); + // Unchanged + Assert.AreEqual(3, ni.Count); + Assert.AreEqual(2, ni2.Count); + + CountingBeforeAdvice cba = new CountingBeforeAdvice(); + Assert.AreEqual(0, cba.GetCalls()); + advised.AddAdvice(cba); + ito.Age = 16; + Assert.AreEqual(16, ito.Age); + Assert.AreEqual(2, cba.GetCalls()); + } + + [Test] + public void CannotAddInterceptorWhenFrozen() + { + TestObject target = new TestObject(); + target.Age = 21; + ProxyFactory pf = new ProxyFactory(target); + Assert.IsFalse(pf.IsFrozen); + pf.AddAdvice(new NopInterceptor()); + ITestObject proxied = (ITestObject)CreateProxy(pf); + pf.IsFrozen = true; + try + { + pf.AddAdvice(0, new NopInterceptor()); + Assert.Fail("Shouldn't be able to add interceptor when frozen"); + } + catch (AopConfigException ex) + { + Assert.IsTrue(ex.Message.IndexOf("frozen") > -1); + } + + // Check it still works: proxy factory state shouldn't have been corrupted + Assert.AreEqual(target.Age, proxied.Age); + Assert.AreEqual(1, ((IAdvised)proxied).Advisors.Length); + } + + [Test(Description = "Check that casting to Advised can't get around advice freeze.")] + public void CannotAddAdvisorWhenFrozenUsingCast() + { + TestObject target = new TestObject(); + target.Age = 21; + ProxyFactory pf = new ProxyFactory(target); + Assert.IsFalse(pf.IsFrozen); + pf.AddAdvice(new NopInterceptor()); + ITestObject proxied = (ITestObject)CreateProxy(pf); + pf.IsFrozen = true; + IAdvised advised = (IAdvised)proxied; + + Assert.IsTrue(pf.IsFrozen); + try + { + advised.AddAdvisor(new DefaultPointcutAdvisor(new NopInterceptor())); + Assert.Fail("Shouldn't be able to add Advisor when frozen"); + } + catch (AopConfigException ex) + { + Assert.IsTrue(ex.Message.IndexOf("frozen") > -1); + } + + // Check it still works: proxy factory state shouldn't have been corrupted + Assert.AreEqual(target.Age, proxied.Age); + Assert.AreEqual(1, advised.Advisors.Length); + } + + [Test] + public void CannotRemoveAdvisorWhenFrozen() + { + TestObject target = new TestObject(); + target.Age = 21; + ProxyFactory pf = new ProxyFactory(target); + Assert.IsFalse(pf.IsFrozen); + pf.AddAdvice(new NopInterceptor()); + ITestObject proxied = (ITestObject)CreateProxy(pf); + pf.IsFrozen = true; + IAdvised advised = (IAdvised)proxied; + + Assert.IsTrue(pf.IsFrozen); + try + { + advised.RemoveAdvisor(0); + Assert.Fail("Shouldn't be able to remove Advisor when frozen"); + } + catch (AopConfigException ex) + { + Assert.IsTrue(ex.Message.IndexOf("frozen") > -1); + } + + // Didn't get removed + Assert.AreEqual(1, advised.Advisors.Length); + pf.IsFrozen = false; + // Can now remove it + advised.RemoveAdvisor(0); + // Check it still works: proxy factory state shouldn't have been corrupted + Assert.AreEqual(target.Age, proxied.Age); + Assert.AreEqual(0, advised.Advisors.Length); + } + + [Test(Description = "Check that the string is informative.")] + public void ProxyConfigString() + { + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + pf.Interfaces = new Type[] { typeof(ITestObject) }; + pf.AddAdvice(new NopInterceptor()); + IMethodBeforeAdvice mba = new CountingBeforeAdvice(); + IAdvisor advisor = new DefaultPointcutAdvisor(new NameMatchMethodPointcut(), mba); + pf.AddAdvisor(advisor); + ITestObject proxied = (ITestObject)CreateProxy(pf); + + String proxyConfigString = ((IAdvised)proxied).ToProxyConfigString(); + Assert.IsTrue(proxyConfigString.IndexOf(advisor.ToString()) != -1); + Assert.IsTrue(proxyConfigString.IndexOf("1 interface") != -1); + } + + // TODO : Opaque can be implemented if really usefull (To increase performance) +/* + public void testCanPreventCastToAdvisedUsingOpaque() + { + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + pf.Interfaces = new Type[] { typeof(ITestObject) }; + pf.AddAdvice(new NopInterceptor()); + CountingBeforeAdvice mba = new CountingBeforeAdvice(); + NameMatchMethodPointcut nmmp = new NameMatchMethodPointcut(); + nmmp.MappedName = "set_Age"; + IAdvisor advisor = new DefaultPointcutAdvisor(nmmp, mba); + pf.AddAdvisor(advisor); + Assert.IsFalse(pf.Opaque, "Opaque defaults to false"); + pf.Opaque = true; + Assert.IsTrue(pf.Opaque, "Opaque now true for this config"); + ITestObject proxied = (ITestObject) CreateProxy(pf); + proxied.Age = 10; + Assert.AreEqual(10, proxied.Age); + Assert.AreEqual(1, mba.GetCalls()); + + Assert.IsFalse(proxied is IAdvised, "Cannot be cast to Advised", ); + } + */ + + // TODO AdviceSupportListeners test + #region AdviceSupportListeners +/* + [Test] + public void AdviceSupportListeners() + { + TestObject target = new TestObject(); + target.Age = 21; + + ProxyFactory pf = new ProxyFactory(target); + CountingAdvisorListener l = new CountingAdvisorListener(pc); + pf.AddListener(l); + RefreshCountingAdvisorChainFactory acf = new RefreshCountingAdvisorChainFactory(); + // Should be automatically added as a listener + pf.AdvisorChainFactory(acf); + Assert.IsFalse(pf.isActive()); + Assert.AreEqual(0, l.activates); + Assert.AreEqual(0, acf.refreshes); + ITestObject proxied = (ITestObject)CreateProxy(pf); + Assert.AreEqual(1, acf.refreshes); + Assert.AreEqual(1, l.activates); + Assert.IsTrue(pc.isActive()); + Assert.AreEqual(target.Age, proxied.Age); + Assert.AreEqual(0, l.adviceChanges); + NopInterceptor ni = new NopInterceptor(); + pf.addAdvice(0, ni); + Assert.AreEqual(1, l.adviceChanges); + Assert.AreEqual(2, acf.refreshes); + Assert.AreEqual(target.Age, proxied.Age); + pf.removeAdvice(ni); + Assert.AreEqual(2, l.adviceChanges); + Assert.AreEqual(3, acf.refreshes); + Assert.AreEqual(target.Age, proxied.Age); + pf.getProxy(); + Assert.AreEqual(1, l.activates); + + pf.RemoveListener(l); + Assert.AreEqual(2, l.adviceChanges); + pf.AddAdvisor(new DefaultPointcutAdvisor(new NopInterceptor())); + // No longer counting + Assert.AreEqual(2, l.adviceChanges); + } + + public class CountingAdvisorListener : IAdvisedSupportListener + { + public int adviceChanges; + public int activates; + private AdvisedSupport expectedSource; + + public CountingAdvisorListener(AdvisedSupport expectedSource) + { + this.expectedSource = expectedSource; + } + + public void AdviceChanged(AdvisedSupport source) + { + Assert.AreEqual(expectedSource, source); + ++adviceChanges; + } + + public void Activated(AdvisedSupport source) + { + Assert.AreEqual(expectedSource,source); + ++activates; + } + } + + public class RefreshCountingAdvisorChainFactory : IAdvisorChainFactory + { + public int refreshes; + + public void AdviceChanged(AdvisedSupport source) + { + ++refreshes; + } + + public IList GetInterceptors(IAdvised advised, object proxy, string methodId, MethodInfo method, Type targetType) + { + return AdvisorChainFactoryUtils.CalculateInterceptors(advised, proxy, method, targetType); + } + + public void Activated(AdvisedSupport source) + { + ++refreshes; + } + } +*/ + #endregion + + #region DynamicMethodPointcut + + [Test] + public void DynamicMethodPointcutThatAlwaysAppliesStatically() + { + TestObject to = new TestObject(); + ProxyFactory pf = new ProxyFactory(new Type[] { typeof(ITestObject) }); + TestDynamicPointcutAdvisor dp = new TestDynamicPointcutAdvisor(new NopInterceptor(), "get_Age"); + pf.AddAdvisor(dp); + pf.Target = to; + ITestObject it = (ITestObject) CreateProxy(pf); + Assert.AreEqual(dp.count, 0); + int age = it.Age; + Assert.AreEqual(dp.count, 1); + it.Age = 11; + Assert.AreEqual(it.Age, 11); + Assert.AreEqual(dp.count, 2); + } + + [Test] + public void DynamicMethodPointcutThatAppliesStaticallyOnlyToSetters() + { + TestObject to = new TestObject(); + ProxyFactory pf = new ProxyFactory(new Type[] { typeof(ITestObject) }); + // Could apply dynamically to property Age but not to property Name + ForSettersOnlyPointcutAdvisor dp = new ForSettersOnlyPointcutAdvisor(new NopInterceptor(), "Age"); + pf.AddAdvisor(dp); + this.mockTargetSource.SetTarget(to); + pf.TargetSource = mockTargetSource; + ITestObject it = (ITestObject)CreateProxy(pf); + Assert.AreEqual(dp.count, 0); + int age = it.Age; + // Statically vetoed + Assert.AreEqual(0, dp.count); + it.Age = 11; + Assert.AreEqual(it.Age, 11); + Assert.AreEqual(dp.count, 1); + // Applies statically but not dynamically + it.Name = "joe"; + Assert.AreEqual(dp.count, 1); + } + + private class TestDynamicPointcutAdvisor : DynamicMethodMatcherPointcutAdvisor + { + private string pattern; + public int count = 0; + + public TestDynamicPointcutAdvisor(IAdvice advice, string pattern) + : + base(advice) + { + this.pattern = pattern; + } + + public override bool Matches(MethodInfo method, Type targetType, object[] args) + { + bool run = method.Name.IndexOf(pattern) != -1; + if (run) ++count; + return run; + } + } + + private class ForSettersOnlyPointcutAdvisor : TestDynamicPointcutAdvisor + { + public ForSettersOnlyPointcutAdvisor(IAdvice advice, string pattern) + : + base(advice, pattern) + { + } + + public override bool Matches(MethodInfo method, Type targetType) + { + return method.Name.StartsWith("set_"); + } + } + + #endregion + + #region StaticMethodPointcut + + [Test] + public void StaticMethodPointcut() + { + TestObject to = new TestObject(); + ProxyFactory pf = new ProxyFactory(new Type[] { typeof(ITestObject) }); + NopInterceptor ni = new NopInterceptor(); + TestStaticPointcutAdvisor sp = new TestStaticPointcutAdvisor(ni, "get_Age"); + pf.AddAdvisor(sp); + pf.Target = to; + ITestObject it = (ITestObject) CreateProxy(pf); + Assert.AreEqual(ni.Count, 0); + int age = it.Age; + Assert.AreEqual(ni.Count, 1); + it.Age = 11; + Assert.AreEqual(it.Age, 11); + Assert.AreEqual(ni.Count, 2); + } + + private class TestStaticPointcutAdvisor : StaticMethodMatcherPointcutAdvisor + { + private string pattern; + private int count; + + public TestStaticPointcutAdvisor(IAdvice advice, String pattern) + : + base(advice) + { + this.pattern = pattern; + } + + public override bool Matches(MethodInfo method, Type targetType) + { + bool run = method.Name.IndexOf(pattern) != -1; + if (run) ++count; + return run; + } + } + + #endregion + + #region CloneInvocationToProceedThreeTimes + + // TODO ? ReflectiveMethodInvocation is not Cloneable +/* + [Test(Description="There are times when we want to call proceed() twice.")] + public void CloneInvocationToProceedThreeTimes() + { + //We can do this if we clone the invocation. + + TestObject to = new TestObject(); + ProxyFactory pf = new ProxyFactory(to); + pf.AddInterface(typeof(ITestObject)); + + TwoBirthdayAdvice twoBirthdayAdvice = new TwoBirthdayAdvice(); + + TwoBirthdayPointcutAdvisor sp = new TwoBirthdayPointcutAdvisor(twoBirthdayAdvice); + pf.AddAdvisor(sp); + ITestObject ito = (ITestObject)CreateProxy(pf); + + int age = 20; + ito.Age = age; + Assert.AreEqual(age, ito.Age); + // Should return the age before the third, AOP-induced birthday + Assert.AreEqual(age + 2, ito.haveBirthday()); + // Return the final age produced by 3 birthdays + Assert.AreEqual(age + 3, ito.Age); + } + + private class TwoBirthdayPointcutAdvisor : StaticMethodMatcherPointcutAdvisor + { + public TwoBirthdayPointcutAdvisor(IAdvice advice) + : base(advice) + { + } + + public override bool Matches(MethodInfo method, Type targetType) + { + return "haveBirthday".Equals(method.Name); + } + } + + private class TwoBirthdayAdvice : IMethodInterceptor + { + public object Invoke(IMethodInvocation invocation) + { + // Clone the invocation to proceed three times + // "The Moor's Last Sigh": this technology can cause premature aging + IMethodInvocation clone1 = ((ReflectiveMethodInvocation)invocation).InvocableClone(); + IMethodInvocation clone2 = ((ReflectiveMethodInvocation)invocation).InvocableClone(); + clone1.Proceed(); + clone2.Proceed(); + return invocation.Proceed(); + } + } + +// // We want to change the arguments on a clone: it shouldn't affect the original. +// public void testCanChangeArgumentsIndependentlyOnClonedInvocation() throws Throwable +// { +// TestObject to = new TestObject(); +// ProxyFactory pc = new ProxyFactory(to); +// pc.addInterface(typeof(ITestObject)); + +// // Changes the name, then changes it back. +// MethodInterceptor nameReverter = new MethodInterceptor() { +// public Object invoke(MethodInvocation mi) throws Throwable { +// MethodInvocation clone = ((ReflectiveMethodInvocation) mi).invocableClone(); +// String oldName = ((ITestObject) mi.getThis()).Name; +// clone.getArguments()[0] = oldName; +// // Original method invocation should be unaffected by changes to argument list of clone +// mi.proceed(); +// return clone.proceed(); +// } +// }; + +// class NameSaver implements MethodInterceptor { +// private List names = new LinkedList(); + +// public Object invoke(MethodInvocation mi) throws Throwable { +// names.add(mi.getArguments()[0]); +// return mi.proceed(); +// } +// } + +// NameSaver saver = new NameSaver(); + +// pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, nameReverter)); +// pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, saver)); +// ITestObject it = (ITestObject) createProxy(pc); + +// String name1 = "tony"; +// String name2 = "gordon"; + +// to.setName(name1); +// Assert.AreEqual(name1, to.Name); + +// it.setName(name2); +// // NameReverter saved it back +// Assert.AreEqual(name1, it.Name); +// Assert.AreEqual(2, saver.names.size()); +// Assert.AreEqual(name2, saver.names.get(0)); +// Assert.AreEqual(name1, saver.names.get(1)); +// } +*/ + #endregion + + #region OverloadedMethodsWithDifferentAdvice + + [Test] + public void OverloadedMethodsWithDifferentAdvice() + { + Overloads target = new Overloads(); + ProxyFactory pf = new ProxyFactory(target); + NopInterceptor overloadVoid = new NopInterceptor(); + pf.AddAdvisor(new OverloadVoidPointcutAdvisor(overloadVoid)); + NopInterceptor overloadInt = new NopInterceptor(); + pf.AddAdvisor(new OverloadIntPointcutAdvisor(overloadInt)); + + IOverloads proxy = (IOverloads) CreateProxy(pf); + + Assert.AreEqual(0, overloadInt.Count); + Assert.AreEqual(0, overloadVoid.Count); + proxy.Overload(); + Assert.AreEqual(0, overloadInt.Count); + Assert.AreEqual(1, overloadVoid.Count); + Assert.AreEqual(25, proxy.Overload(25)); + Assert.AreEqual(1, overloadInt.Count); + Assert.AreEqual(1, overloadVoid.Count); + proxy.NoAdvice(); + Assert.AreEqual(1, overloadInt.Count); + Assert.AreEqual(1, overloadVoid.Count); + } + + public interface IOverloads + { + void Overload(); + int Overload(int i); + string Overload(string foo); + void NoAdvice(); + } + + public class Overloads : IOverloads + { + public void Overload() + { + } + + public int Overload(int i) + { + return i; + } + + public string Overload(string foo) + { + return foo; + } + + public void NoAdvice() + { + } + } + + private class OverloadVoidPointcutAdvisor : StaticMethodMatcherPointcutAdvisor + { + public OverloadVoidPointcutAdvisor(IAdvice advice) + : base(advice) + { + } + + public override bool Matches(MethodInfo method, Type targetType) + { + return method.Name.Equals("Overload") && method.GetParameters().Length == 0; + } + } + + private class OverloadIntPointcutAdvisor : StaticMethodMatcherPointcutAdvisor + { + public OverloadIntPointcutAdvisor(IAdvice advice) + : base(advice) + { + } + + public override bool Matches(MethodInfo method, Type targetType) + { + return method.Name.Equals("Overload") && method.GetParameters().Length == 1 && + method.GetParameters()[0].ParameterType == typeof(int); + } + } + + #endregion + + // TODO : IAdvised.TargetSource is read only (no setter) +/* + [Test] + public void ExistingProxyChangesTarget() + { + TestObject to1 = new TestObject(); + to1.Age = 33; + + TestObject to2 = new TestObject(); + to2.Age = 26; + to2.Name = "Juergen"; + TestObject to3 = new TestObject(); + to3.Age = 37; + ProxyFactory pf = new ProxyFactory(to1); + NopInterceptor nop = new NopInterceptor(); + pf.AddAdvice(nop); + ITestObject proxy = (ITestObject)CreateProxy(pf); + Assert.AreEqual(nop.Count, 0); + Assert.AreEqual(to1.Age, proxy.Age); + Assert.AreEqual(nop.Count, 1); + // Change to a new static target + pf.Target = to2; + Assert.AreEqual(to2.Age, proxy.Age); + Assert.AreEqual(nop.Count, 2); + + // Change to a new dynamic target + HotSwappableTargetSource hts = new HotSwappableTargetSource(to3); + pf.TargetSource = hts; + Assert.AreEqual(to3.Age, proxy.Age); + Assert.AreEqual(nop.Count, 3); + hts.Swap(to1); + Assert.AreEqual(to1.Age, proxy.Age); + to1.Name = "Colin"; + Assert.AreEqual(to1.Name, proxy.Name); + Assert.AreEqual(nop.Count, 5); + + // Change back, relying on casting to Advised + IAdvised advised = (IAdvised)proxy; + Assert.AreSame(hts, advised.TargetSource); + SingletonTargetSource sts = new SingletonTargetSource(to2); + advised.TargetSource = sts; + Assert.AreEqual(to2.Name, proxy.Name); + Assert.AreSame(sts, advised.TargetSource); + Assert.AreEqual(to2.Age, proxy.Age); + } + + [Test] + public void ProxyIsBoundBeforeTargetSourceInvoked() + { + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(new DebugInterceptor()); + pf.ExposeProxy = true; + ITestObject proxy = (ITestObject) CreateProxy(pf); + IAdvised config = (IAdvised) proxy; + // This class just checks proxy is bound before getTarget() call + config.setTargetSource(new TargetSource() { + public Class getTargetClass() { + return TestObject.class; + } + + public boolean isStatic() { + return false; + } + + public Object getTarget() throws Exception { + Assert.AreEqual(proxy, AopContext.currentProxy()); + return target; + } + + public void releaseTarget(Object target) throws Exception { + } + }); + + // Just test anything: it will fail if context wasn't found + Assert.AreEqual(0, proxy.Age); + } +*/ + + #region BeforeAdvisorIsInvoked + + [Test] + public void BeforeAdvisorIsInvoked() + { + CountingBeforeAdvice cba = new CountingBeforeAdvice(); + IAdvisor matchesNoArgsAdvisor = new NoArgsMethodPointcutAdvisor(cba); + TestObject target = new TestObject(); + target.Age = 80; + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(new NopInterceptor()); + pf.AddAdvisor(matchesNoArgsAdvisor); + Assert.AreEqual(matchesNoArgsAdvisor, pf.Advisors[1], "Advisor was added"); + ITestObject proxied = (ITestObject)CreateProxy(pf); + Assert.AreEqual(0, cba.GetCalls()); + Assert.AreEqual(0, cba.GetCalls("get_Age")); + Assert.AreEqual(target.Age, proxied.Age); + Assert.AreEqual(1, cba.GetCalls()); + Assert.AreEqual(1, cba.GetCalls("get_Age")); + Assert.AreEqual(0, cba.GetCalls("set_Age")); + // Won't be advised + proxied.Age = 26; + Assert.AreEqual(1, cba.GetCalls()); + Assert.AreEqual(26, proxied.Age); + } + + private class NoArgsMethodPointcutAdvisor : StaticMethodMatcherPointcutAdvisor + { + public NoArgsMethodPointcutAdvisor(IAdvice advice) + : base(advice) + { + } + + public override bool Matches(MethodInfo method, Type targetType) + { + return method.GetParameters().Length == 0; + } + } + + #endregion + + // TODO : Multi advice not supported ? + #region MultiAdvice + + [Test] + [Ignore("Multi advice not supported for now.")] + public void MultiAdvice() + { + CountingMultiAdvice cma = new CountingMultiAdvice(); + IAdvisor matchesNoArgsAdvisor = new NoArgsOrNotExceptionalMethodPointcutAdvisor(cma); + TestObject target = new TestObject(); + target.Age = 80; + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(new NopInterceptor()); + pf.AddAdvisor(matchesNoArgsAdvisor); + Assert.AreEqual(matchesNoArgsAdvisor, pf.Advisors[1], "Advisor was added"); + ITestObject proxied = (ITestObject)CreateProxy(pf); + + Assert.AreEqual(0, cma.GetCalls()); + Assert.AreEqual(0, cma.GetCalls("get_Age")); + Assert.AreEqual(target.Age, proxied.Age); + Assert.AreEqual(2, cma.GetCalls()); + Assert.AreEqual(2, cma.GetCalls("get_Age")); + Assert.AreEqual(0, cma.GetCalls("set_Age")); + // Won't be advised + proxied.Age = 26; + Assert.AreEqual(2, cma.GetCalls()); + Assert.AreEqual(26, proxied.Age); + Assert.AreEqual(4, cma.GetCalls()); + try + { + proxied.Exceptional(new ApplicationException("foo")); + Assert.Fail("Should have thrown ApplicationException"); + } + catch (ApplicationException) + { + // expected + } + Assert.AreEqual(6, cma.GetCalls()); + } + + private class NoArgsOrNotExceptionalMethodPointcutAdvisor : StaticMethodMatcherPointcutAdvisor + { + public NoArgsOrNotExceptionalMethodPointcutAdvisor(IAdvice advice) + : base(advice) + { + } + + public override bool Matches(MethodInfo method, Type targetType) + { + return method.GetParameters().Length == 0 || method.Name == "Exceptional"; + } + } + + #endregion + + #region BeforeAdviceThrowsException + + [Test] + public void BeforeAdviceThrowsException() + { + ApplicationException aex = new ApplicationException(); + CountingBeforeNonSetterAdvice ba = new CountingBeforeNonSetterAdvice(aex); + + TestObject target = new TestObject(); + target.Age = 80; + NopInterceptor nop1 = new NopInterceptor(); + NopInterceptor nop2 = new NopInterceptor(2); + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(nop1); + pf.AddAdvice(ba); + pf.AddAdvice(nop2); + ITestObject proxied = (ITestObject)CreateProxy(pf); + // Won't throw an exception + Assert.AreEqual(target.Age, proxied.Age); + Assert.AreEqual(1, ba.GetCalls()); + Assert.AreEqual(1, ba.GetCalls("get_Age")); + Assert.AreEqual(1, nop1.Count); + Assert.AreEqual(1, nop2.Count); + // Will fail, after invoking Nop1 + try + { + proxied.Age = 26; + Assert.Fail("before advice should have ended chain"); + } + catch (ApplicationException ex) + { + Assert.AreEqual(aex, ex); + } + Assert.AreEqual(2, ba.GetCalls()); + Assert.AreEqual(2, nop1.Count); + // Nop2 didn't get invoked when the exception was thrown + Assert.AreEqual(1, nop2.Count); + // Shouldn't have changed value in joinpoint + Assert.AreEqual(target.Age, proxied.Age); + } + + private class CountingBeforeNonSetterAdvice : CountingBeforeAdvice + { + private Exception _exception; + + public CountingBeforeNonSetterAdvice(Exception ex) + { + _exception = ex; + } + + public override void Before(MethodInfo method, object[] args, object target) + { + base.Before(method, args, target); + + if (method.Name.StartsWith("set_")) + throw _exception; + } + } + + #endregion + + #region AfterReturningAdvisorIsInvoked + + [Test] + public void AfterReturningAdvisorIsInvoked() + { + SummingAfterAdvice aa = new SummingAfterAdvice(); + IAdvisor matchesIntAdvisor = new ReturnsIntPointcutAdvisor(aa); + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(new NopInterceptor()); + pf.AddAdvisor(matchesIntAdvisor); + Assert.AreEqual(matchesIntAdvisor, pf.Advisors[1], "Advisor was added"); + ITestObject proxied = (ITestObject)CreateProxy(pf); + Assert.AreEqual(0, aa.sum); + int i1 = 12; + int i2 = 13; + + // Won't be advised + proxied.Age = i1; + Assert.AreEqual(i1, proxied.Age); + Assert.AreEqual(i1, aa.sum); + proxied.Age = i2; + Assert.AreEqual(i2, proxied.Age); + Assert.AreEqual(i1 + i2, aa.sum); + Assert.AreEqual(i2, proxied.Age); + } + + private class SummingAfterAdvice : IAfterReturningAdvice + { + public int sum; + + public void AfterReturning(Object returnValue, MethodInfo method, Object[] args, Object target) + { + sum += ((int)returnValue); + } + } + + private class ReturnsIntPointcutAdvisor : StaticMethodMatcherPointcutAdvisor + { + public ReturnsIntPointcutAdvisor(IAdvice advice) + : base(advice) + { + } + + public override bool Matches(MethodInfo method, Type targetType) + { + return method.ReturnType == typeof(int); + } + } + + #endregion + + [Test] + public void AfterReturningAdvisorIsNotInvokedOnException() + { + CountingAfterReturningAdvice car = new CountingAfterReturningAdvice(); + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(new NopInterceptor()); + pf.AddAdvice(car); + Assert.AreEqual(car, pf.Advisors[1].Advice, "Advice was wrapped in Advisor and added"); + ITestObject proxied = (ITestObject)CreateProxy(pf); + Assert.AreEqual(0, car.GetCalls()); + int age = 10; + proxied.Age = age; + Assert.AreEqual(age, proxied.Age); + Assert.AreEqual(2, car.GetCalls()); + Exception ex = new Exception(); + // On exception it won't be invoked + try + { + proxied.Exceptional(ex); + Assert.Fail("Should have thrown Exception"); + } + catch (Exception caught) + { + Assert.AreSame(ex, caught); + } + Assert.AreEqual(2, car.GetCalls()); + } + + #region ThrowsAdvisorIsInvoked + + [Test] + public void ThrowsAdvisorIsInvoked() + { + // Reacts to HttpException and RemotingException + ThrowsAdviceInterceptorTests.MyThrowsHandler th = new ThrowsAdviceInterceptorTests.MyThrowsHandler(); + IAdvisor matchesEchoAdvisor = new EchoPointcutAdvisor(th); + ThrowsAdviceInterceptorTests.Echo target = new ThrowsAdviceInterceptorTests.Echo(); + target.A = 16; + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(new NopInterceptor()); + pf.AddAdvisor(matchesEchoAdvisor); + Assert.AreEqual(matchesEchoAdvisor, pf.Advisors[1], "Advisor was added"); + ThrowsAdviceInterceptorTests.IEcho proxied = (ThrowsAdviceInterceptorTests.IEcho)CreateProxy(pf); + Assert.AreEqual(0, th.GetCalls()); + Assert.AreEqual(target.A, proxied.A); + Assert.AreEqual(0, th.GetCalls()); + Exception ex = new Exception(); + // Will be advised but doesn't match + try + { + proxied.EchoException(1, ex); + Assert.Fail("Should have thrown Exception"); + } + catch (Exception caught) + { + Assert.AreEqual(ex, caught); + } + + ex = new HttpException(); + try + { + proxied.EchoException(1, ex); + Assert.Fail("Should have thrown HttpException"); + } + catch (HttpException caught) + { + Assert.AreEqual(ex, caught); + } + Assert.AreEqual(1, th.GetCalls("HttpException")); + } + + private class EchoPointcutAdvisor : StaticMethodMatcherPointcutAdvisor + { + public EchoPointcutAdvisor(IAdvice advice) + : base(advice) + { + } + + public override bool Matches(MethodInfo method, Type targetType) + { + return method.Name.StartsWith("Echo"); + } + } + + #endregion + + [Test] + public void AddThrowsAdviceWithoutAdvisor() + { + // Reacts to ServletException and RemoteException + ThrowsAdviceInterceptorTests.MyThrowsHandler th = new ThrowsAdviceInterceptorTests.MyThrowsHandler(); + + ThrowsAdviceInterceptorTests.Echo target = new ThrowsAdviceInterceptorTests.Echo(); + target.A = 16; + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(new NopInterceptor()); + pf.AddAdvice(th); + ThrowsAdviceInterceptorTests.IEcho proxied = (ThrowsAdviceInterceptorTests.IEcho)CreateProxy(pf); + Assert.AreEqual(0, th.GetCalls()); + Assert.AreEqual(target.A, proxied.A); + Assert.AreEqual(0, th.GetCalls()); + Exception ex = new Exception(); + // Will be advised but doesn't match + try + { + proxied.EchoException(1, ex); + Assert.Fail("Should have thrown Exception"); + } + catch (Exception caught) + { + Assert.AreEqual(ex, caught); + } + + // Subclass of RemoteException + ex = new RemotingTimeoutException(); + try + { + proxied.EchoException(1, ex); + Assert.Fail("Should have thrown RemotingTimeoutException"); + } + catch (RemotingTimeoutException caught) + { + Assert.AreEqual(ex, caught); + } + Assert.AreEqual(1, th.GetCalls("RemotingException")); + } + + #region Arguments + + [Test] + public void ArgumentsModification() + { + } + + + public interface ITargetClass + { + string TargetMethod(string arg); + } + + public class TargetClass : ITargetClass + { + public string TargetMethod(string arg) + { + if (arg == null) + { + throw new ArgumentException(); + } + return arg; + } + } + + public class Interceptor1 : IMethodInterceptor + { + #region IMethodInterceptor Members + + public object Invoke(IMethodInvocation invocation) + { + invocation.Proceed(); + + return invocation.Arguments[0]; + } + + #endregion + } + + public class Interceptor2 : IMethodInterceptor + { + #region IMethodInterceptor Members + + public object Invoke(IMethodInvocation invocation) + { + invocation.Arguments[0] = null; + + return invocation.Proceed(); + } + + #endregion + } + +#endregion + + + #region Helper classes definitions + + public interface INeedsToSeeProxy + { + int Count { get; } + + void IncrementViaThis(); + + void IncrementViaProxy(); + + void Increment(); + } + + public class NeedsToSeeProxy : INeedsToSeeProxy + { + private int _count; + public int Count + { + get { return _count; } + } + + public void IncrementViaThis() + { + this.Increment(); + } + + public void IncrementViaProxy() + { + INeedsToSeeProxy thisViaProxy = (INeedsToSeeProxy)AopContext.CurrentProxy; + thisViaProxy.Increment(); + IAdvised advised = (IAdvised)thisViaProxy; + CheckAdvised(advised); + } + + protected virtual void CheckAdvised(IAdvised advised) + { + } + + public void Increment() + { + ++_count; + } + } + + public class TargetChecker : NeedsToSeeProxy + { + protected override void CheckAdvised(IAdvised advised) + { + // TODO replace this check: no longer possible + //Assert.AreEqual(advised.getTarget(), this); + } + } + + #endregion + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/AopUtilsTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/AopUtilsTests.cs new file mode 100644 index 00000000..1a3fbb87 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/AopUtilsTests.cs @@ -0,0 +1,223 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Aop.Framework; +using Spring.Aop.Interceptor; +using Spring.Aop.Support; +using Spring.Collections; +using Spring.Objects; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Unit tests for the AopUtils class. + /// + /// Rod Johnson + /// Choy Rim (.NET) + /// $Id: AopUtilsTests.cs,v 1.2 2008/03/21 10:49:37 oakinger Exp $ + [TestFixture] + public sealed class AopUtilsTests + { + [Test] + public void GetAllInterfaces() + { + DerivedTestObject testObject = new DerivedTestObject(); + IList interfaces = new ArrayList(testObject.GetType().GetInterfaces()); + Assert.AreEqual(8, interfaces.Count, "Incorrect number of interfaces"); + Assert.IsTrue(interfaces.Contains(typeof (ITestObject)), "Does not contain ITestObject"); + Assert.IsTrue(interfaces.Contains(typeof (IOther)), "Does not contain IOther"); + } + + [Test] + public void PointcutCanNeverApply() + { + IPointcut pointcut = new NeverMatchesPointcut(); + Assert.IsFalse(AopUtils.CanApply(pointcut, typeof (Object), null)); + } + + [Test] + public void PointcutAlwaysApplies() + { + Assert.IsTrue(AopUtils.CanApply(new DefaultPointcutAdvisor(new NopInterceptor()), typeof (Object), null)); + Assert.IsTrue(AopUtils.CanApply(new DefaultPointcutAdvisor(new NopInterceptor()), typeof (TestObject), new Type[] {typeof (ITestObject)})); + } + + [Test] + public void PointcutAppliesToOneMethodOnObject() + { + IPointcut pointcut = new OneMethodTestPointcut(); + Assert.IsTrue(AopUtils.CanApply(pointcut, typeof (object), null), + "Must return true if we're not proxying interfaces."); + Assert.IsFalse(AopUtils.CanApply(pointcut, typeof (object), + new Type[] {typeof (ITestObject)}), + "Must return false if we're proxying interfaces."); + } + + [Test] + public void PointcutAppliesToOneInterfaceOfSeveral() + { + IPointcut pointcut = new OneInterfaceTestPointcut(); + + // Will return true if we're proxying interfaces including ITestObject + Assert.IsTrue(AopUtils.CanApply(pointcut, typeof (TestObject), new Type[] {typeof (ITestObject), typeof (IComparable)})); + + // Will return true if we're proxying interfaces including ITestObject + Assert.IsFalse(AopUtils.CanApply(pointcut, typeof (TestObject), new Type[] {typeof (IComparable)})); + } + + [Test] + public void CanApplyWithAdvisorYieldsTrueIfAdvisorIsNotKnownAdvisorType() + { + IAdvisor advisor = (IAdvisor) new DynamicMock(typeof (IAdvisor)).Object; + Assert.IsTrue(AopUtils.CanApply(advisor, typeof (TestObject), null)); + } + + [Test] + public void CanApplyWithAdvisorYieldsTrueIfAdvisorIsNull() + { + Assert.IsTrue(AopUtils.CanApply((IAdvisor) null, typeof (TestObject), null)); + } + + [Test] + public void GetAllInterfacesWithNull() + { + Type[] interfaces = AopUtils.GetAllInterfaces(null); + Assert.IsNotNull(interfaces, + "Must never return null, even if the argument is null."); + Assert.AreEqual(0, interfaces.Length, + "Must return an empty array is the argument is null."); + } + + [Test] + public void GetAllInterfacesWithObjectThatDoesntImpementAnything() + { + ImplementsNothing instance = new ImplementsNothing(); + Type[] interfaces = AopUtils.GetAllInterfaces(instance); + Assert.IsNotNull(interfaces, + "Must never return null, even if the argument doesn't implement any interfaces."); + Assert.AreEqual(0, interfaces.Length, + "Must return an empty array is the argument doesn't implement any interfaces."); + } + + [Test] + public void GetAllInterfacesSunnyDay() + { + ImplementsTwoInterfaces instance = new ImplementsTwoInterfaces(); + Type[] interfaces = AopUtils.GetAllInterfaces(instance); + Assert.IsNotNull(interfaces, "Must never return null."); + Assert.AreEqual(2, interfaces.Length, + "Implements two interfaces."); + ISet ifaces = new ListSet(interfaces); + Assert.IsTrue( + ifaces.ContainsAll( + new Type [] {typeof(IDisposable), typeof(ICloneable)}), + "Did not find the correct interfaces."); + } + + [Test] + public void GetAllInterfacesWithObjectThatInheritsInterfaces() + { + InheritsOneInterface instance = new InheritsOneInterface(); + Type[] interfaces = AopUtils.GetAllInterfaces(instance); + Assert.IsNotNull(interfaces, "Must never return null."); + Assert.AreEqual(1, interfaces.Length, + "Inherited one interface from superclass."); + Type iface = interfaces[0]; + Assert.IsNotNull(iface, "Returned interface cannot be null."); + Assert.AreEqual(typeof(IDisposable), iface, "Wrong interface returned."); + } + + [Test] + public void CanApplyWithTrueIntroductionAdvisor() + { + DynamicMock mockIntroAdvisor = new DynamicMock(typeof (IIntroductionAdvisor)); + mockIntroAdvisor.ExpectAndReturn("TypeFilter", TrueTypeFilter.True); + IAdvisor advisor = (IAdvisor) mockIntroAdvisor.Object; + Assert.IsTrue(AopUtils.CanApply(advisor, typeof (TestObject), null)); + mockIntroAdvisor.Verify(); + } + + #region Helper Classes + + private sealed class OneMethodTestPointcut : StaticMethodMatcherPointcut + { + public override bool Matches(MethodInfo m, Type targetClass) + { + return m.Name.Equals("GetHashCode"); + } + } + + private sealed class OneInterfaceTestPointcut : StaticMethodMatcherPointcut + { + public override bool Matches(MethodInfo m, Type targetClass) + { + return m.Name.Equals("ReturnsThis"); + } + } + + private sealed class NeverMatchesPointcut : StaticMethodMatcherPointcut + { + public override bool Matches(MethodInfo m, Type targetClass) + { + return false; + } + } + + private sealed class ImplementsNothing + { + } + + private abstract class ImplementsOneInterface : IDisposable + { + public void Dispose() + { + throw new NotImplementedException(); + } + } + + private sealed class InheritsOneInterface : ImplementsOneInterface + { + } + + private sealed class ImplementsTwoInterfaces : IDisposable, ICloneable + { + public void Dispose() + { + throw new NotImplementedException(); + } + + public object Clone() + { + throw new NotImplementedException(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/CachedAopProxyFactoryTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/CachedAopProxyFactoryTests.cs new file mode 100644 index 00000000..06eb0bfb --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/CachedAopProxyFactoryTests.cs @@ -0,0 +1,181 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Collections; + +using NUnit.Framework; +using Spring.Objects; +using Spring.Aop.Support; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Unit tests for the CachedAopProxyFactoryTests class. + /// + /// Bruno Baia + /// $Id: CachedAopProxyFactoryTests.cs,v 1.1 2007/08/04 01:20:11 bbaia Exp $ + [TestFixture] + public sealed class CachedAopProxyFactoryTests : DefaultAopProxyFactoryTests + { + protected override IAopProxy CreateAopProxy(AdvisedSupport advisedSupport) + { + IAopProxyFactory apf = new CachedAopProxyFactory(); + return apf.CreateAopProxy(advisedSupport); + } + + [SetUp] + public void SetUp() + { + // Clear Aop proxy type cache + Assert.IsNotNull(TypeCacheField); + TypeCacheField.SetValue(null, new Hashtable()); + } + + [Test] + public void DoesNotCacheWithDifferentBaseType() + { + // Decorated-based proxy (BaseType == TargetType) + AdvisedSupport advisedSupport = new AdvisedSupport(); + advisedSupport.ProxyTargetType = true; + advisedSupport.Target = new TestObject(); + CreateAopProxy(advisedSupport); + + // Composition-based proxy (BaseType = BaseCompositionAopProxy) + advisedSupport = new AdvisedSupport(); + advisedSupport.ProxyTargetType = false; + advisedSupport.Target = new TestObject(); + CreateAopProxy(advisedSupport); + + AssertAopProxyTypeCacheCount(2); + } + + [Test] + public void DoesNotCacheWithDifferentTargetType() + { + AdvisedSupport advisedSupport = new AdvisedSupport(); + advisedSupport.Target = new BadCommand(); + CreateAopProxy(advisedSupport); + + advisedSupport = new AdvisedSupport(); + advisedSupport.Target = new GoodCommand(); + CreateAopProxy(advisedSupport); + + AssertAopProxyTypeCacheCount(2); + } + + [Test] + public void DoesNotCacheWithDifferentInterfaces() + { + AdvisedSupport advisedSupport = new AdvisedSupport(); + advisedSupport.Target = new TestObject(); + CreateAopProxy(advisedSupport); + + advisedSupport = new AdvisedSupport(); + advisedSupport.Target = new TestObject(); + advisedSupport.AddInterface(typeof(IPerson)); + CreateAopProxy(advisedSupport); + + AssertAopProxyTypeCacheCount(2); + + // Same with Introductions + advisedSupport = new AdvisedSupport(); + advisedSupport.Target = new TestObject(); + TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(); + ti.TimeStamp = new DateTime(666L); + IIntroductionAdvisor introduction = new DefaultIntroductionAdvisor(ti, typeof(ITimeStamped)); + advisedSupport.AddIntroduction(introduction); + CreateAopProxy(advisedSupport); + + AssertAopProxyTypeCacheCount(3); + } + + [Test] + public void DoesCacheWithTwoDecoratorBasedProxy() + { + AdvisedSupport advisedSupport = new AdvisedSupport(); + advisedSupport.ProxyTargetType = true; + advisedSupport.Target = new TestObject(); + CreateAopProxy(advisedSupport); + + advisedSupport = new AdvisedSupport(); + advisedSupport.ProxyTargetType = true; + advisedSupport.Target = new TestObject(); + CreateAopProxy(advisedSupport); + + AssertAopProxyTypeCacheCount(1); + } + + [Test] + public void DoesCacheWithTwoCompositionBasedProxy() + { + AdvisedSupport advisedSupport = new AdvisedSupport(); + advisedSupport.Target = new TestObject(); + CreateAopProxy(advisedSupport); + + advisedSupport = new AdvisedSupport(); + advisedSupport.Target = new TestObject(); + CreateAopProxy(advisedSupport); + + AssertAopProxyTypeCacheCount(1); + } + + + private static readonly FieldInfo TypeCacheField = + typeof(CachedAopProxyFactory).GetField("typeCache", BindingFlags.Static | BindingFlags.NonPublic); + + private void AssertAopProxyTypeCacheCount(int count) + { + Assert.IsNotNull(TypeCacheField); + Hashtable cache = TypeCacheField.GetValue(null) as Hashtable; + Assert.IsNotNull(cache); + Assert.AreEqual(count, cache.Count); + } + + #region Helper classes definitions + + public interface ICommand + { + void Execute(); + } + + public sealed class BadCommand : ICommand + { + public void Execute() + { + throw new NotImplementedException(); + } + } + + public sealed class GoodCommand : ICommand + { + public void Execute() + { + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/CompositionAopProxyTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/CompositionAopProxyTests.cs new file mode 100644 index 00000000..10ea964e --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/CompositionAopProxyTests.cs @@ -0,0 +1,114 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Additional and overridden tests for the composition-based proxy. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Bruno Baia (.NET) + /// $Id: CompositionAopProxyTests.cs,v 1.4 2007/08/02 04:15:37 markpollack Exp $ + [TestFixture] + public class CompositionAopProxyTests : AbstractAopProxyTests + { + protected override object CreateProxy(AdvisedSupport advisedSupport) + { + Assert.IsFalse(advisedSupport.ProxyTargetType, "Not forcible decorator-based proxy"); + object proxy = CreateAopProxy(advisedSupport).GetProxy(); + Assert.IsTrue(AopUtils.IsCompositionAopProxy(proxy), "Should be a composition-based proxy: " + proxy.GetType()); + return proxy; + } + + protected override Type CreateAopProxyType(AdvisedSupport advisedSupport) + { + return new CompositionAopProxyTypeBuilder(advisedSupport).BuildProxyType(); + } + + [Test] + public void ProxyIsJustInterface() + { + TestObject target = new TestObject(); + target.Age = 32; + + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = target; + advised.Interfaces = new Type[] { typeof(ITestObject) }; + + object proxy = CreateProxy(advised); + + Assert.IsTrue(proxy is ITestObject); + Assert.IsFalse(proxy is TestObject); + } + + #region ReturnsThisWhenProxyIsIncompatible + + [Test] + public void ReturnsThisWhenProxyIsIncompatible() + { + FooBar obj = new FooBar(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = obj; + advised.Interfaces = new Type[] { typeof(IFoo) }; + + IFoo proxy = (IFoo)CreateProxy(advised); + + Assert.AreSame(obj, proxy.GetBarThis(), + "Target should be returned when return types are incompatible"); + Assert.AreSame(proxy, proxy.GetFooThis(), + "Proxy should be returned when return types are compatible"); + } + + public interface IFoo + { + IBar GetBarThis(); + IFoo GetFooThis(); + } + + public interface IBar + { + } + + public class FooBar : IFoo, IBar + { + public IBar GetBarThis() + { + return this; + } + + public IFoo GetFooThis() + { + return this; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/DecoratorAopProxyTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/DecoratorAopProxyTests.cs new file mode 100644 index 00000000..7c76c3b3 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/DecoratorAopProxyTests.cs @@ -0,0 +1,351 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; +using Spring.Aop.Interceptor; +using Spring.Objects; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Additional and overridden tests for the decorator-based proxy. + /// + /// Rod Johnson + /// Juergen Hoeller + /// Bruno Baia (.NET) + /// $Id: DecoratorAopProxyTests.cs,v 1.6 2008/05/21 08:04:52 bbaia Exp $ + [TestFixture] + public class DecoratorAopProxyTests : AbstractAopProxyTests + { + protected override object CreateProxy(AdvisedSupport advisedSupport) + { + advisedSupport.ProxyTargetType = true; + object proxy = CreateAopProxy(advisedSupport).GetProxy(); + Assert.IsTrue(AopUtils.IsDecoratorAopProxy(proxy)); + return proxy; + } + + protected override Type CreateAopProxyType(AdvisedSupport advisedSupport) + { + return new DecoratorAopProxyTypeBuilder(advisedSupport).BuildProxyType(); + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void CannotProxySealedClass() + { + SealedTestObject target = new SealedTestObject(); + mockTargetSource.SetTarget(target); + AdvisedSupport advised = new AdvisedSupport(new Type[] { }); + advised.TargetSource = mockTargetSource; + + IAopProxy aop = CreateAopProxy(advised); + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void CannotProxyNonPublicClass() + { + NonPublicTestObject target = new NonPublicTestObject(); + mockTargetSource.SetTarget(target); + AdvisedSupport advised = new AdvisedSupport(new Type[] { }); + advised.TargetSource = mockTargetSource; + + IAopProxy aop = CreateAopProxy(advised); + } + + [Test] + public void ProxyCanBeClassAndInterface() + { + TestObject target = new TestObject(); + target.Age = 32; + mockTargetSource.SetTarget(target); + AdvisedSupport advised = new AdvisedSupport(); + advised.TargetSource = mockTargetSource; + IAopProxy aop = CreateAopProxy(advised); + + object proxy = aop.GetProxy(); + Assert.IsTrue(AopUtils.IsDecoratorAopProxy(proxy), "Should be a decorator-based proxy"); + Assert.IsTrue(proxy is ITestObject); + Assert.IsTrue(proxy is TestObject); + + ITestObject itb = (ITestObject)proxy; + Assert.AreEqual(32, itb.Age, "Incorrect age"); + TestObject tb = (TestObject)proxy; + Assert.AreEqual(32, tb.Age, "Incorrect age"); + } + + [Test] + public void InterceptVirtualMethod() + { + DoesNotImplementInterfaceTestObject target = new DoesNotImplementInterfaceTestObject(); + target.Name = "Bruno"; + mockTargetSource.SetTarget(target); + + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.TargetSource = mockTargetSource; + advised.AddAdvice(ni); + + DoesNotImplementInterfaceTestObject proxy = CreateProxy(advised) as DoesNotImplementInterfaceTestObject; + Assert.IsNotNull(proxy); + + Assert.AreEqual(target.Name, proxy.Name, "Incorrect name"); + proxy.Name = "Bruno Baia"; + Assert.AreEqual("Bruno Baia", proxy.Name, "Incorrect name"); + + Assert.AreEqual(3, ni.Count); + } + + [Test] + public void CannotInterceptFinalMethodThatDoesNotBelongToAnInterface() + { + DoesNotImplementInterfaceTestObject target = new DoesNotImplementInterfaceTestObject(); + target.Location = "Paris"; + mockTargetSource.SetTarget(target); + + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.TargetSource = mockTargetSource; + advised.AddAdvice(ni); + + DoesNotImplementInterfaceTestObject proxy = CreateProxy(advised) as DoesNotImplementInterfaceTestObject; + Assert.IsNotNull(proxy); + + // Location is final and doesn't belong to an interface so can't proxy. + // method call goes directly to the proxy + // and will not have access to the valid _location field + Assert.IsNull(proxy.Location); + + Assert.AreEqual(0, ni.Count); + } + + [Test] + public void InterceptFinalMethodThatBelongsToAnInterface() + { + TestObject target = new TestObject(); + target.Name = "Bruno"; + mockTargetSource.SetTarget(target); + + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.TargetSource = mockTargetSource; + advised.AddAdvice(ni); + + // Cast to the interface that method belongs to + ITestObject proxy = CreateProxy(advised) as ITestObject; + Assert.IsNotNull(proxy); + + Assert.AreEqual(target.Name, proxy.Name, "Incorrect name"); + proxy.Name = "Bruno Baia"; + Assert.AreEqual("Bruno Baia", proxy.Name, "Incorrect name"); + + Assert.AreEqual(3, ni.Count); + } + + [Test] + [ExpectedException(typeof(AopConfigException), "Cannot create decorator-based IAopProxy for a non visible class [Spring.Aop.Framework.DynamicProxy.AbstractAopProxyTests+InternalRefOutTestObject]")] + public override void ProxyMethodWithRefOutParametersWithStandardReflection() + { + base.ProxyMethodWithRefOutParametersWithStandardReflection(); + } + +#if NET_2_0 + [Test] + [ExpectedException(typeof(AopConfigException), "Cannot create decorator-based IAopProxy for a non visible class [Spring.Aop.Framework.DynamicProxy.AbstractAopProxyTests+InternalRefOutGenericTestObject]")] + public override void ProxyGenericMethodWithRefOutParametersWithStandardReflection() + { + base.ProxyGenericMethodWithRefOutParametersWithStandardReflection(); + } +#endif + + #region Attributes + + [Test] + public void ProxyTargetVirtualMethodAttributes() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method."); + } + + [Test] + public void DoesNotProxyTargetVirtualMethodAttributesWithProxyTargetAttributesEqualsFalse() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + advised.ProxyTargetAttributes = false; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the target method."); + } + + [Test] + public void ProxyTargetVirtualMethodParameterAttributes() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetParameters()[1].GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's parameter."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's parameter."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's parameter."); + } + + [Test] + public void DoesNotProxyTargetVirtualMethodParameterAttributesWithProxyTargetAttributesEqualsFalse() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + advised.ProxyTargetAttributes = false; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetParameters()[1].GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's parameter."); + } + +#if NET_2_0 + [Test] + public void ProxyTargetVirtualMethodReturnValueAttributes() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's return value."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's return value."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's return value."); + } + + [Test] + public void DoesNotProxyTargetVirtualMethodReturnValueAttributesWithProxyTargetAttributesEqualsFalse() + { + MarkerClass target = new MarkerClass(); + + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(IMarkerInterface) }); + advised.Target = target; + advised.ProxyTargetAttributes = false; + IAopProxy aopProxy = CreateAopProxy(advised); + + object proxy = aopProxy.GetProxy(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to GetProxy() was null."); + + MethodInfo method = proxy.GetType().GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's return value."); + } +#endif + + #endregion + + #region Helper classes definitions + + internal class NonPublicTestObject + { + } + + public sealed class SealedTestObject + { + } + + public class DoesNotImplementInterfaceTestObject + { + // virtual property + private string _name; + public virtual string Name + { + get { return _name; } + set { _name = value; } + } + + // final method + private string _location; + public string Location + { + get { return _location; } + set { _location = value; } + } + } + + #endregion + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/DefaultAopProxyFactoryTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/DefaultAopProxyFactoryTests.cs new file mode 100644 index 00000000..143f572b --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/DefaultAopProxyFactoryTests.cs @@ -0,0 +1,108 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Unit tests for the DefaultAopProxyFactory class. + /// + /// Bruno Baia + /// $Id: DefaultAopProxyFactoryTests.cs,v 1.1 2007/08/04 01:20:11 bbaia Exp $ + [TestFixture] + public class DefaultAopProxyFactoryTests + { + protected virtual IAopProxy CreateAopProxy(AdvisedSupport advisedSupport) + { + IAopProxyFactory apf = new DefaultAopProxyFactory(); + return apf.CreateAopProxy(advisedSupport); + } + + [Test] + [ExpectedException(typeof(AopConfigException), "Cannot create IAopProxy with null ProxyConfig")] + public void NullConfig() + { + CreateAopProxy(null); + } + + [Test] + [ExpectedException(typeof(AopConfigException), "Cannot create IAopProxy with no advisors and no target source")] + public void NoInterceptorsAndNoTarget() + { + AdvisedSupport advisedSupport = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + CreateAopProxy(advisedSupport); + } + + [Test] + public void TargetDoesNotImplementAnyInterfaces() + { + AdvisedSupport advisedSupport = new AdvisedSupport(); + advisedSupport.ProxyTargetType = false; + advisedSupport.Target = new DoesNotImplementAnyInterfacesTestObject(); + + IAopProxy aopProxy = CreateAopProxy(advisedSupport); + Assert.IsNotNull(aopProxy); + Assert.IsTrue(AopUtils.IsDecoratorAopProxy(aopProxy)); + } + + [Test] + public void TargetImplementsAnInterface() + { + AdvisedSupport advisedSupport = new AdvisedSupport(); + advisedSupport.Target = new TestObject(); + + IAopProxy aopProxy = CreateAopProxy(advisedSupport); + Assert.IsNotNull(aopProxy); + + Assert.IsTrue(AopUtils.IsCompositionAopProxy(aopProxy)); + } + + [Test] + public void TargetImplementsAnInterfaceWithProxyTargetTypeSetToTrue() + { + AdvisedSupport advisedSupport = new AdvisedSupport(); + advisedSupport.ProxyTargetType = true; + advisedSupport.Target = new TestObject(); + + IAopProxy aopProxy = CreateAopProxy(advisedSupport); + Assert.IsNotNull(aopProxy); + Assert.IsTrue(AopUtils.IsDecoratorAopProxy(aopProxy)); + } + + #region Helper classes definitions + + public class DoesNotImplementAnyInterfacesTestObject + { + public virtual void SomeMethod() + { + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/InheritanceAopProxyTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/InheritanceAopProxyTests.cs new file mode 100644 index 00000000..15bcba58 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/InheritanceAopProxyTests.cs @@ -0,0 +1,343 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +using Spring.Objects; +using Spring.Aop.Interceptor; +using Spring.Proxy; +using System.Reflection; +using Spring.Expressions; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Additional and overridden tests for the inheritance-based proxy. + /// + /// Bruno Baia + /// $Id: InheritanceAopProxyTests.cs,v 1.2 2008/03/03 09:29:17 bbaia Exp $ + [TestFixture] + public class InheritanceAopProxyTests + { + protected object CreateProxy(AdvisedSupport advisedSupport) + { + Type proxyType = new InheritanceAopProxyTypeBuilder(advisedSupport).BuildProxyType(); + ConstructorInfo proxyCtorInfo = proxyType.GetConstructor(new Type[] { typeof(IAdvised) }); + + ExpressionEvaluator.GetValue(advisedSupport, "Activate()"); + return ((IAopProxy)proxyCtorInfo.Invoke(new object[] { advisedSupport })).GetProxy(); + } + + [Test] + public void DirectCall() + { + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = new InheritanceTestObject(); + + object proxy = CreateProxy(advised); + //DynamicProxyManager.SaveAssembly(); + + Assert.IsTrue(proxy is InheritanceTestObject); + InheritanceTestObject proxiedClass = proxy as InheritanceTestObject; + proxiedClass.Name = "DirectCall"; + Assert.AreEqual("DirectCall", proxiedClass.Name); + + Assert.IsTrue(proxy is IIncrementable); + IIncrementable proxiedIntf = proxy as IIncrementable; + proxiedIntf.Increment(); + Assert.AreEqual(1, proxiedIntf.Value); + } + + [Test] + public void ProxyTargetTypeOnly() + { + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = new InheritanceTestObject("ProxyTargetTypeOnly"); + + object proxy = CreateProxy(advised); + //DynamicProxyManager.SaveAssembly(); + + Assert.IsTrue(proxy is InheritanceTestObject); + InheritanceTestObject proxiedClass = proxy as InheritanceTestObject; + Assert.AreNotEqual("ProxyTargetTypeOnly", proxiedClass.Name); + } + + [Test] + public void CallBaseConstructor() + { + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = new InheritanceTestObject(); + + object proxy = CreateProxy(advised); + //DynamicProxyManager.SaveAssembly(); + + Assert.IsTrue(proxy is InheritanceTestObject); + InheritanceTestObject proxiedClass = proxy as InheritanceTestObject; + Assert.AreEqual("InheritanceTestObject", proxiedClass.Name); + } + + [Test] + public void InterceptVirtualMethod() + { + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = new InheritanceTestObject(); + advised.AddAdvice(ni); + + object proxy = CreateProxy(advised); + //DynamicProxyManager.SaveAssembly(); + + Assert.IsTrue(proxy is InheritanceTestObject); + InheritanceTestObject proxiedClass = proxy as InheritanceTestObject; + proxiedClass.Name = "InterceptVirtualMethod"; + Assert.AreEqual("InterceptVirtualMethod", proxiedClass.Name); + Assert.AreEqual(2, ni.Count); + } + +#if NET_2_0 + [Test] + public void InterceptVirtualGenericMethod() + { + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = new AnotherTestObject(); + advised.AddAdvice(ni); + + object proxy = CreateProxy(advised); + //DynamicProxyManager.SaveAssembly(); + + Assert.IsTrue(proxy is AnotherTestObject); + AnotherTestObject proxiedClass = proxy as AnotherTestObject; + Assert.AreEqual(typeof(int), proxiedClass.GenericMethod()); + Assert.AreEqual(1, ni.Count); + } + + public class AnotherTestObject + { + public virtual Type GenericMethod() + { + return typeof(T); + } + + // Test ambiguous match + public virtual Type GenericMethod() + { + return typeof(string); + } + } +#endif + + [Test] + public void DoesNotInterceptFinalMethod() + { + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = new InheritanceTestObject(); + advised.AddAdvice(ni); + + object proxy = CreateProxy(advised); + //DynamicProxyManager.SaveAssembly(); + + Assert.IsTrue(proxy is InheritanceTestObject); + InheritanceTestObject proxiedClass = proxy as InheritanceTestObject; + proxiedClass.Name = "DoesNotInterceptFinalMethod"; + Assert.AreEqual("DoesNotInterceptFinalMethod", proxiedClass.Name); + Assert.AreEqual(2, ni.Count); + + proxiedClass.Reset(); + Assert.AreEqual(2, ni.Count); + Assert.AreEqual("InheritanceTestObject", proxiedClass.Name); + Assert.AreEqual(3, ni.Count); + } + + [Test] + public void InterceptVirtualMethodThatBelongsToAnInterface() + { + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = new InheritanceTestObject(); + advised.AddAdvice(ni); + + object proxy = CreateProxy(advised); + //DynamicProxyManager.SaveAssembly(); + + Assert.IsTrue(proxy is InheritanceTestObject); + InheritanceTestObject proxiedClass = proxy as InheritanceTestObject; + proxiedClass.Increment(); + Assert.AreEqual(1, ni.Count); + + Assert.IsTrue(proxy is IIncrementable); + IIncrementable proxiedInterface = proxy as IIncrementable; + proxiedInterface.Increment(); + Assert.AreEqual(2, ni.Count); + } + + [Test] + public void InterceptNonVirtualMethodThatBelongsToAnInterface() + { + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = new InheritanceTestObject(); + advised.AddAdvice(ni); + + object proxy = CreateProxy(advised); + //DynamicProxyManager.SaveAssembly(); + + Assert.IsTrue(proxy is InheritanceTestObject); + InheritanceTestObject proxiedClass = proxy as InheritanceTestObject; + Assert.AreEqual(0, proxiedClass.Value); + Assert.AreEqual(0, ni.Count); + + Assert.IsTrue(proxy is IIncrementable); + IIncrementable proxiedInterface = proxy as IIncrementable; + proxiedInterface.Increment(); + Assert.AreEqual(1, proxiedInterface.Value); + Assert.AreEqual(2, ni.Count); + } + + [Test] + public void InterceptThisCalls() + { + NopInterceptor ni = new NopInterceptor(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.Target = new InheritanceTestObject(); + advised.AddAdvice(ni); + + object proxy = CreateProxy(advised); + //DynamicProxyManager.SaveAssembly(); + + Assert.IsTrue(proxy is InheritanceTestObject); + InheritanceTestObject proxiedClass = proxy as InheritanceTestObject; + proxiedClass.IncrementTwice(); + Assert.AreEqual(2, ni.Count); + Assert.AreEqual(2, proxiedClass.Value); + } + + //[Test] + //public void InterceptProtectedMethod() + //{ + // NopInterceptor ni = new NopInterceptor(); + + // AdvisedSupport advised = new AdvisedSupport(); + // advised.Target = new InheritanceTestObject(); + // advised.AddAdvice(ni); + + // object proxy = CreateProxy(advised); + // //DynamicProxyManager.SaveAssembly(); + + // Assert.IsTrue(proxy is InheritanceTestObject); + // InheritanceTestObject proxiedClass = proxy as InheritanceTestObject; + // proxiedClass.Todo(); + // Assert.AreEqual(1, ni.Count); + //} + } + + #region Helper Classes + + public interface IIncrementable + { + int Value {get; set;} + void Increment(); + } + + public class InheritanceTestObject : IIncrementable + { + // virtual method + private string _name; + public virtual string Name + { + get { return _name; } + set { _name = value; } + } + + // many constructors + public InheritanceTestObject() : this("InheritanceTestObject", 0) + { + } + + public InheritanceTestObject(string name) : this(name, 0) + { + } + + public InheritanceTestObject(string name, int value) + { + this._name = name; + this._value = value; + } + + #region IIncrementable Members + + // non virtual method that belongs to an interface + private int _value; + public int Value + { + get { return _value; } + set { _value = value; } + } + + // virtual method that belongs to an interface too + public virtual void Increment() + { + // this call + this._value++; + } + + #endregion + + // final method + public void Reset() + { + this._name = "InheritanceTestObject"; + this._value = 0; + } + + // this call + public void IncrementTwice() + { + this.Increment(); + this.Increment(); + } + + // protected method call + public void Todo() + { + ProtectedTodo(); + } + + protected virtual void ProtectedTodo() + { + + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/MockTargetSource.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/MockTargetSource.cs new file mode 100644 index 00000000..a304066d --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/MockTargetSource.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Aop.Target; + +#endregion + +namespace Spring.Aop.Framework.DynamicProxy +{ + /// + /// Useful implementation + /// that checks calls to GetTarget and ReleaseTarget. + /// + /// Rod Johnson + /// Bruno Baia (.NET) + /// $Id: MockTargetSource.cs,v 1.1 2006/08/10 18:45:58 bbaia Exp $ + public class MockTargetSource : ITargetSource + { + private object _target; + + public int gets; + public int releases; + + public void Reset() + { + this._target = null; + gets = releases = 0; + } + + public void SetTarget(Object target) + { + this._target = target; + } + + public void Verify() + { + if (gets != releases) + throw new Exception("Expectation failed: " + gets + " gets and " + releases + " releases"); + } + + #region ITargetSource Members + + public Type TargetType + { + get { return _target.GetType(); } + } + + public bool IsStatic + { + get { return false; } + } + + public object GetTarget() + { + ++gets; + return _target; + } + + public void ReleaseTarget(object target) + { + if (target != this._target) + throw new Exception("Released wrong target"); + ++releases; + } + + #endregion + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/HashtableCachingAdvisorChainFactoryTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/HashtableCachingAdvisorChainFactoryTests.cs new file mode 100644 index 00000000..29479b95 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/HashtableCachingAdvisorChainFactoryTests.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Unit tests for the HashtableCachingAdvisorChainFactory class. + /// + /// Rick Evans + /// $Id: HashtableCachingAdvisorChainFactoryTests.cs,v 1.2 2006/04/09 07:19:05 markpollack Exp $ + [TestFixture] + public sealed class HashtableCachingAdvisorChainFactoryTests + { + #region SetUp + + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp() + { + } + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp() + { + } + + #endregion + + #region TearDown + + /// + /// The tear down logic executed after the execution of each individual test. + /// + [TearDown] + public void TearDown() + { + } + + /// + /// The tear down logic executed after the entire test fixture has executed. + /// + [TestFixtureTearDown] + public void FixtureTearDown() + { + } + + #endregion + + [Test] + public void Instantiation() { + + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/IIsModified.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/IIsModified.cs new file mode 100644 index 00000000..2a2f4dde --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/IIsModified.cs @@ -0,0 +1,27 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Aop.Framework +{ + public interface IIsModified + { + bool IsModified { get; set; } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/ITimeStamped.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/ITimeStamped.cs new file mode 100644 index 00000000..09be9cfd --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/ITimeStamped.cs @@ -0,0 +1,39 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion +#region Imports +using System; +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// This interface can be implemented by cacheable objects + /// or cache entries, to enable the freshness of objects + /// to be checked. + /// + /// Rod Johnson + /// Choy Rim (.NET) + /// $Id: ITimeStamped.cs,v 1.1 2004/08/01 10:20:20 choyrim Exp $ + public interface ITimeStamped + { + /// + /// Return the timestamp for this object. + /// + DateTime TimeStamp { get; } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/IsModifiedMixin.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/IsModifiedMixin.cs new file mode 100644 index 00000000..d3a31fbd --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/IsModifiedMixin.cs @@ -0,0 +1,43 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using AopAlliance.Aop; +using Spring.Aop.Support; + +namespace Spring.Aop.Framework +{ + public class IsModifiedMixin : IIsModified, IAdvice + { + private bool isModified = true; + + public virtual bool IsModified + { + get { return isModified; } + set { isModified = value; } + } + } + + public class IsModifiedAdvisor : DefaultIntroductionAdvisor + { + public IsModifiedAdvisor() + : base(new IsModifiedMixin()) + {} + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/MethodCounter.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/MethodCounter.cs new file mode 100644 index 00000000..04fbadba --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/MethodCounter.cs @@ -0,0 +1,67 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion +#region Imports +using System; +using System.Collections; +using System.Reflection; +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Useful base class for counting advices etc. + /// + /// Rod Johnson + /// Choy Rim (.NET) + /// $Id: MethodCounter.cs,v 1.2 2007/03/16 04:01:47 aseovic Exp $ + [Serializable] + public class MethodCounter + { + /// Method name --> count, does not understand overloading + private Hashtable map = new Hashtable(); + private int allCount; + + protected internal virtual void Count(MethodBase m) + { + Count(m.Name); + } + + protected internal virtual void Count(string methodName) + { + int count = GetCalls(methodName); + ++count; + map[methodName] = count; + ++allCount; + } + + public virtual int GetCalls(string methodName) + { + int count = 0; + if ( map.ContainsKey(methodName) ) + { + count = (int) map[methodName]; + } + return count; + } + + public virtual int GetCalls() + { + return allCount; + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/PrototypeTargetTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/PrototypeTargetTests.cs new file mode 100644 index 00000000..21fa9e44 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/PrototypeTargetTests.cs @@ -0,0 +1,101 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports +using System; + +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; +using AopAlliance.Intercept; + +using NUnit.Framework; +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// + /// + /// Juergen Hoeller + /// Simon White (.NET) + [TestFixture] + public class PrototypeTargetTests + { + [Test] + public void PrototypeProxyWithPrototypeTarget() + { + TestObjectImpl.constructionCount = 0; + IObjectFactory iof = new XmlObjectFactory(new ReadOnlyXmlTestResource("prototypeTarget.xml", GetType())); + for (int i = 0 ; i < 10 ; i++) + { + int crap = TestObjectImpl.constructionCount; + TestObject to = (TestObject) iof.GetObject("testObjectPrototype"); + crap = TestObjectImpl.constructionCount; + to.DoSomething(); + } + TestInterceptor interceptor = (TestInterceptor) iof.GetObject("testInterceptor"); + Assert.AreEqual(10, TestObjectImpl.constructionCount); + Assert.AreEqual(10, interceptor.invocationCount); + } + + [Test] + public void SingletonProxyWithPrototypeTarget() + { + TestObjectImpl.constructionCount = 0; + IObjectFactory iof = new XmlObjectFactory(new ReadOnlyXmlTestResource("prototypeTarget.xml", GetType())); + for (int i = 0; i < 10; i++) + { + TestObject to = (TestObject) iof.GetObject("testObjectSingleton"); + to.DoSomething(); + } + TestInterceptor interceptor = (TestInterceptor) iof.GetObject("testInterceptor"); + Assert.AreEqual(1, TestObjectImpl.constructionCount); + Assert.AreEqual(10, interceptor.invocationCount); + } + + public interface TestObject + { + void DoSomething(); + } + + public class TestObjectImpl : TestObject + { + public static int constructionCount = 0; + + public TestObjectImpl() + { + constructionCount++; + } + + public void DoSomething() + { + } + } + + public class TestInterceptor : IMethodInterceptor + { + public int invocationCount = 0; + + public object Invoke(IMethodInvocation methodInvocation) + { + invocationCount++; + return methodInvocation.Proceed(); + } + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/ProxyConfigTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/ProxyConfigTests.cs new file mode 100644 index 00000000..d982d3bf --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/ProxyConfigTests.cs @@ -0,0 +1,44 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Unit tests for the ProxyConfig class. + /// + /// Rick Evans + /// $Id: ProxyConfigTests.cs,v 1.2 2006/04/09 07:19:05 markpollack Exp $ + [TestFixture] + public sealed class ProxyConfigTests + { + [Test] + public void Instantiation() + { + + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/ProxyFactoryObjectTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/ProxyFactoryObjectTests.cs new file mode 100644 index 00000000..1d45e7dd --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/ProxyFactoryObjectTests.cs @@ -0,0 +1,1095 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Security.Policy; +using System.Collections; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Web; + +using AopAlliance.Aop; +using AopAlliance.Intercept; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Aop.Advice; +using Spring.Aop.Framework.Adapter; +using Spring.Aop.Framework.DynamicProxy; +using Spring.Aop.Interceptor; +using Spring.Aop.Support; +using Spring.Aop.Target; +using Spring.Context; +using Spring.Core.IO; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Proxy; +using Spring.Threading; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Integration test cases for the ProxyFactoryObject, using an XML object factory. + /// + /// Rod Johnson + /// Federico Spinazzi (.NET) + /// Choy Rim (.NET) + /// Aleksandar Seovic (.NET) + /// $Id: ProxyFactoryObjectTests.cs,v 1.35 2007/12/07 17:59:31 bbaia Exp $ + [TestFixture] + public sealed class ProxyFactoryObjectTests + { + private IObjectFactory factory; + + [SetUp] + public void SetUp() + { + this.factory = new XmlObjectFactory(new ReadOnlyXmlTestResource("proxyFactoryTests.xml", GetType())); + } + + + [Test] + public void TargetThrowsInvalidCastException() + { + Exception expectedException = new InvalidCastException(); + ITestObject test1 = (ITestObject)factory.GetObject("test1"); + try + { + test1.Exceptional(expectedException); + Assert.Fail("Should have thrown exception raised by target"); + } + catch (Exception ex) + { + Assert.AreEqual(expectedException, ex, "exception matches"); + Assert.AreEqual(1, test1.ExceptionMethodCallCount); + } + } + + [Test] + public void IsCompositionProxy() + { + ITestObject test1 = (ITestObject)factory.GetObject("test1"); + Assert.IsTrue(AopUtils.IsCompositionAopProxy(test1), "test1 is a composition proxy"); + } + + [Test] + public void GetObjectTypeWithDirectTarget() + { + IObjectFactory bf = new XmlObjectFactory( + new ReadOnlyXmlTestResource("proxyFactoryTargetSourceTests.xml", + GetType())); + + // We have a counting before advice here + CountingBeforeAdvice cba = (CountingBeforeAdvice)bf.GetObject("countingBeforeAdvice"); + Assert.AreEqual(0, cba.GetCalls()); + + ITestObject tb = (ITestObject)bf.GetObject("directTarget"); + Assert.IsTrue(tb.Name.Equals("Adam")); + Assert.AreEqual(1, cba.GetCalls()); + + ProxyFactoryObject pfb = (ProxyFactoryObject)bf.GetObject("&directTarget"); + Assert.IsTrue(typeof(ITestObject).IsAssignableFrom(pfb.ObjectType), "Has correct object type"); + } + + [Test] + public void GetObjectTypeWithTargetViaTargetSource() + { + IObjectFactory bf = new XmlObjectFactory( + new ReadOnlyXmlTestResource("proxyFactoryTargetSourceTests.xml", + GetType())); + ITestObject tb = (ITestObject)bf.GetObject("viaTargetSource"); + Assert.IsTrue(tb.Name.Equals("Adam")); + ProxyFactoryObject pfb = (ProxyFactoryObject)bf.GetObject("&viaTargetSource"); + Assert.IsTrue(typeof(ITestObject).IsAssignableFrom(pfb.ObjectType), "Has correct object type"); + } + + [Test] + public void GetObjectTypeWithNoTargetOrTargetSource() + { + IObjectFactory bf = + new XmlObjectFactory(new ReadOnlyXmlTestResource("proxyFactoryTargetSourceTests.xml", GetType())); + bf.GetObject("noTarget"); + IFactoryObject pfb = (ProxyFactoryObject)bf.GetObject("&noTarget"); + Assert.IsTrue(typeof(ITestObject).IsAssignableFrom(pfb.ObjectType), "Has correct object type"); + } + + /// + /// The instances are equal, but do not have object identity. + /// Interceptors and interfaces and the target are the same. + /// + [Test] + public void SingletonInstancesAreEqual() + { + ITestObject test1 = (ITestObject)factory.GetObject("test1"); + ITestObject test1_1 = (ITestObject)factory.GetObject("test1"); + Assert.AreEqual(test1, test1_1, "Singleton instances =="); + test1.Age = 25; + Assert.AreEqual(test1.Age, test1_1.Age); + test1.Age = 250; + Assert.AreEqual(test1.Age, test1_1.Age); + IAdvised pc1 = (IAdvised)test1; + IAdvised pc2 = (IAdvised)test1_1; + Assert.AreEqual(pc1.Advisors, pc2.Advisors); + int oldLength = pc1.Advisors.Length; + NopInterceptor di = new NopInterceptor(); + pc1.AddAdvice(1, di); + Assert.AreEqual(pc1.Advisors, pc2.Advisors); + Assert.AreEqual(oldLength + 1, pc2.Advisors.Length, "Now have one more advisor"); + Assert.AreEqual(di.Count, 0); + test1.Age = (5); + Assert.AreEqual(test1_1.Age, test1.Age); + Assert.AreEqual(di.Count, 3); + } + + [Test] + public void PrototypeInstancesAreNotEqual() + { + ITestObject test2 = (ITestObject)factory.GetObject("prototype"); + ITestObject test2_1 = (ITestObject)factory.GetObject("prototype"); + Assert.IsTrue(test2 != test2_1, "Prototype instances !="); + Assert.IsTrue(TestObject.Equals(test2, test2_1), "Prototype instances equal"); + } + + [Test] + public void PrototypeInstancesAreIndependent() + { + IObjectFactory objectFactory = new XmlObjectFactory(new ReadOnlyXmlTestResource("prototypeTests.xml", GetType())); + // Initial count value set in object factory XML + int INITIAL_COUNT = 10; + + + // Check it works without AOP + ISideEffectObject raw = (ISideEffectObject)objectFactory.GetObject("prototypeTarget"); + Assert.AreEqual(INITIAL_COUNT, raw.Count); + raw.doWork(); + Assert.AreEqual(INITIAL_COUNT + 1, raw.Count); + raw = (ISideEffectObject)objectFactory.GetObject("prototypeTarget"); + Assert.AreEqual(INITIAL_COUNT, raw.Count); + + // Now try with advised instances + ISideEffectObject prototype2FirstInstance = (ISideEffectObject)objectFactory.GetObject("prototype"); + Assert.AreEqual(INITIAL_COUNT, prototype2FirstInstance.Count); + prototype2FirstInstance.doWork(); + Assert.AreEqual(INITIAL_COUNT + 1, prototype2FirstInstance.Count); + + ISideEffectObject prototype2SecondInstance = (ISideEffectObject)objectFactory.GetObject("prototype"); + Assert.IsFalse(prototype2FirstInstance == prototype2SecondInstance, "Prototypes are not =="); + Assert.AreEqual(INITIAL_COUNT, prototype2SecondInstance.Count); + Assert.AreEqual(INITIAL_COUNT + 1, prototype2FirstInstance.Count); + + + } + + + /// Test invoker is automatically added to manipulate target + [Test] + public void AutoInvoker() + { + String name = "Hieronymous"; + TestObject target = (TestObject)factory.GetObject("test"); + target.Name = name; + ITestObject autoInvoker = (ITestObject)factory.GetObject("autoInvoker"); + Assert.IsTrue(autoInvoker.Name.Equals(name)); + } + + [Test] + public void CanGetFactoryReferenceAndManipulate() + { + ITestObject to = (ITestObject)factory.GetObject("test1"); + // no exception + string dummy = to.Name; + + IAdvised config = (IAdvised)to; + Assert.AreEqual(1, config.Advisors.Length, "Object should have only one advisors"); + + Exception ex = new NotSupportedException("Invoke"); + // Add evil interceptor to head of list + config.AddAdvice(0, new EvilMethodInterceptor(ex)); + Assert.AreEqual(2, config.Advisors.Length, "The advisor count is wrong after adding an advisor programmatically."); + + try + { + // evil interceptor should throw exception + dummy = to.Name; + Assert.Fail("Evil interceptor added programmatically should fail all method calls, but it didn't"); + } + catch (Exception thrown) + { + Assert.AreEqual(thrown, ex, "The thrown exception is not the one we were looking for."); + } + } + + /// + /// Must see effect immediately on behaviour. + /// + [Test] + public void CanAddAndRemoveIntroductionsOnSingleton() + { + try + { + ITimeStamped ts = (ITimeStamped)factory.GetObject("test1"); + Assert.Fail("Shouldn't implement ITimeStamped before manipulation"); + } + catch (InvalidCastException) + { + } + + ProxyFactoryObject config = (ProxyFactoryObject)factory.GetObject("&test1"); + long time = 666L; + TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(); + ti.TimeStamp = new DateTime(time); + IIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(ti, typeof(ITimeStamped)); + + // add to front of introduction chain + int oldCount = config.Introductions.Length; + config.AddIntroduction(0, advisor); + Assert.IsTrue(config.Introductions.Length == oldCount + 1); + + ITimeStamped ts2 = (ITimeStamped)factory.GetObject("test1"); + Assert.IsTrue(ts2.TimeStamp == new DateTime(time)); + + // Can remove + config.RemoveIntroduction(advisor); + Assert.IsTrue(config.Introductions.Length == oldCount); + + // Existing reference will still work + object o = ts2.TimeStamp; + + // But new proxies should not implement ITimeStamped + try + { + ts2 = (ITimeStamped)factory.GetObject("test1"); + Assert.Fail("Should no longer implement ITimeStamped"); + } + catch (InvalidCastException) + { + // expected... + } + + // Now check non-effect of removing interceptor that isn't there + oldCount = config.Advisors.Length; + config.RemoveAdvice(new DebugAdvice()); + Assert.IsTrue(config.Advisors.Length == oldCount); + + ITestObject it = (ITestObject)ts2; + DebugAdvice debugInterceptor = new DebugAdvice(); + config.AddAdvice(0, debugInterceptor); + object foo = it.Spouse; + Assert.AreEqual(1, debugInterceptor.Count); + config.RemoveAdvice(debugInterceptor); + foo = it.Spouse; + // not invoked again + Assert.IsTrue(debugInterceptor.Count == 1); + } + + /// Try adding and removing interfaces and interceptors on prototype. + /// Changes will only affect future references obtained from the factory. + /// Each instance will be independent. + /// + [Test] + public void CanAddAndRemoveAspectInterfacesOnPrototype() + { + try + { + ITimeStamped ts = (ITimeStamped)factory.GetObject("test2"); + Assert.Fail("Shouldn't implement ITimeStamped before manipulation"); + } + catch (InvalidCastException) + { + } + + IAdvised config = (IAdvised)factory.GetObject("&test2"); + long time = 666L; + TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(); + ti.TimeStamp = new DateTime(time); + IIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(ti, typeof(ITimeStamped)); + + // add to front of introduction chain + int oldCount = config.Introductions.Length; + config.AddIntroduction(0, advisor); + Assert.IsTrue(config.Introductions.Length == oldCount + 1); + + ITimeStamped ts2 = (ITimeStamped)factory.GetObject("test2"); + Assert.IsTrue(ts2.TimeStamp == new DateTime(time)); + + // Can remove + config.RemoveIntroduction(advisor); + Assert.IsTrue(config.Introductions.Length == oldCount); + + // Existing reference will still work + object o = ts2.TimeStamp; + + // But new proxies should not implement ITimeStamped + try + { + ts2 = (ITimeStamped)factory.GetObject("test2"); + Assert.Fail("Should no longer implement ITimeStamped"); + } + catch (InvalidCastException) + { + } + + // Now check non-effect of removing interceptor that isn't there + ITestObject it = (ITestObject)factory.GetObject("test2"); + config = (IAdvised)it; + + oldCount = config.Advisors.Length; + config.RemoveAdvice(new DebugAdvice()); + Assert.IsTrue(config.Advisors.Length == oldCount); + + DebugAdvice debugInterceptor = new DebugAdvice(); + config.AddAdvice(0, debugInterceptor); + object foo = it.Spouse; + Assert.AreEqual(1, debugInterceptor.Count); + config.RemoveAdvice(debugInterceptor); + foo = it.Spouse; + // not invoked again + Assert.IsTrue(debugInterceptor.Count == 1); + } + + /// + /// Note that we can't add or remove interfaces without reconfiguring the + /// singleton. + /// + [Test] + public void CanAddAndRemoveAspectInterfacesOnSingletonByCasting() + { + ITestObject it = (ITestObject)factory.GetObject("test1"); + IAdvised pc = (IAdvised)it; + object name = it.Age; + NopInterceptor di = new NopInterceptor(); + pc.AddAdvice(0, di); + Assert.AreEqual(0, di.Count); + it.Age = 25; + Assert.AreEqual(25, it.Age); + Assert.AreEqual(2, di.Count); + } + + [Test] + public void MethodPointcuts() + { + ITestObject tb = (ITestObject)factory.GetObject("pointcuts"); + PointcutForVoid.Reset(); + Assert.IsTrue((PointcutForVoid.methodNames.Count == 0), "No methods intercepted"); + object o = tb.Age; + Assert.IsTrue((PointcutForVoid.methodNames.Count == 0), "Not void: shouldn't have intercepted"); + tb.Age = 1; + o = tb.Age; + tb.Name = "Tristan"; + tb.ToString(); + Assert.AreEqual(2, PointcutForVoid.methodNames.Count, "Recorded wrong number of invocations"); + Assert.AreEqual("set_Age", PointcutForVoid.methodNames[0]); + Assert.AreEqual("set_Name", PointcutForVoid.methodNames[1]); + } + + [Test] + public void CanAddThrowsAdviceWithoutAdvisor() + { + IObjectFactory f = new XmlObjectFactory(new ReadOnlyXmlTestResource("throwsAdvice.xml", GetType())); + ThrowsAdviceInterceptorTests.MyThrowsHandler th = + (ThrowsAdviceInterceptorTests.MyThrowsHandler)f.GetObject("throwsAdvice"); + CountingBeforeAdvice cba = (CountingBeforeAdvice)f.GetObject("countingBeforeAdvice"); + Assert.AreEqual(0, cba.GetCalls()); + Assert.AreEqual(0, th.GetCalls()); + ThrowsAdviceInterceptorTests.IEcho echo = (ThrowsAdviceInterceptorTests.IEcho)f.GetObject("throwsAdvised"); + echo.A = 12; + Assert.AreEqual(1, cba.GetCalls()); + Assert.AreEqual(0, th.GetCalls()); + Exception expected = new Exception(); + try + { + echo.EchoException(1, expected); + Assert.Fail(); + } + catch (Exception ex) + { + Assert.AreEqual(expected, ex); + } + // No throws handler method: count should still be 0 + Assert.AreEqual(0, th.GetCalls()); + + // Handler knows how to handle this exception + expected = new HttpException(); + try + { + echo.EchoException(1, expected); + Assert.Fail(); + } + catch (HttpException ex) + { + Assert.AreEqual(expected, ex); + } + // One match + Assert.AreEqual(1, th.GetCalls("HttpException")); + } + + /// Checks that globals get invoked, + /// and that they can add aspect interfaces unavailable + /// to other objects. These interfaces don't need + /// to be included in proxiedInterface []. + /// + [Test] + [ExpectedException(typeof(InvalidCastException))] + public void GlobalsCanAddAspectInterfaces() + { + IAddedGlobalInterface agi = (IAddedGlobalInterface)factory.GetObject("autoInvoker"); + Assert.IsTrue(agi.GlobalsAdded == -1); + + ProxyFactoryObject pfb = (ProxyFactoryObject)factory.GetObject("&validGlobals"); + Assert.AreEqual(2, pfb.Advisors.Length, "Proxy should have 1 global and 1 explicit advisor"); + Assert.AreEqual(1, pfb.Introductions.Length, "Proxy should have 1 global introduction"); + + agi.GlobalsAdded = ((IAdvised)agi).Introductions.Length; + Assert.IsTrue(agi.GlobalsAdded == 1); + + IApplicationEventListener l = (IApplicationEventListener)factory.GetObject("validGlobals"); + agi = (IAddedGlobalInterface)l; + Assert.IsTrue(agi.GlobalsAdded == -1); + agi = (IAddedGlobalInterface)factory.GetObject("test1"); + } + + [Test] + public void IsSingletonFalseReturnsNew_ProxyInstance_NotNewProxyTargetSource() + { + IDynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + GoodCommand target = new GoodCommand(); + mock.ExpectAndReturn("GetObject", target, "singleton"); + mock.ExpectAndReturn("GetObject", target, "singleton"); + IObjectFactory factory = (IObjectFactory)mock.Object; + + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { typeof(ICommand).FullName }; + fac.IsSingleton = false; + fac.TargetName = "singleton"; + fac.ObjectFactory = factory; + fac.AddAdvice(new NopInterceptor()); + + ICommand one = (ICommand)fac.GetObject(); + ICommand two = (ICommand)fac.GetObject(); + Assert.IsFalse(ReferenceEquals(one, two)); + + mock.Verify(); + } + + [Test] + public void IsSingletonTrueReturnsNew_ProxyInstance_NotNewProxyTargetSource() + { + IDynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + GoodCommand target = new GoodCommand(); + mock.ExpectAndReturn("GetObject", target, "singleton"); + IObjectFactory factory = (IObjectFactory)mock.Object; + + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { typeof(ICommand).FullName }; + fac.IsSingleton = true; // default, just being explicit... + fac.TargetName = "singleton"; + fac.ObjectFactory = factory; + fac.AddAdvice(new NopInterceptor()); + + ICommand one = (ICommand)fac.GetObject(); + ICommand two = (ICommand)fac.GetObject(); + Assert.IsTrue(ReferenceEquals(one, two)); + + mock.Verify(); + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void AddAdvisorWhenConfigIsFrozen() + { + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.IsFrozen = true; + fac.AddAdvisor(new PointcutForVoid()); + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void RemoveAdvisorWhenConfigIsFrozen() + { + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.IsFrozen = true; + fac.RemoveAdvisor(new PointcutForVoid()); + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void ReplaceAdvisorWhenConfigIsFrozen() + { + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.IsFrozen = true; + fac.ReplaceAdvisor(new PointcutForVoid(), new PointcutForVoid()); + } + + [Test] + public void TargetAtEndOfInterceptorList() + { + GoodCommand target = new GoodCommand(); + NopInterceptor advice = new NopInterceptor(); + + IDynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("GetObject", advice, "advice"); + mock.ExpectAndReturn("GetObject", target, "singleton"); + mock.ExpectAndReturn("GetType", typeof(GoodCommand), "singleton"); + IObjectFactory factory = (IObjectFactory)mock.Object; + + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { typeof(ICommand).FullName }; + fac.IsSingleton = true; // default, just being explicit... + fac.InterceptorNames = new string[] { "advice", "singleton" }; + fac.ObjectFactory = factory; + + ICommand one = (ICommand)fac.GetObject(); + ICommand two = (ICommand)fac.GetObject(); + Assert.IsTrue(ReferenceEquals(one, two)); + one.Execute(); + Assert.AreEqual(1, advice.Count); + two.Execute(); + Assert.AreEqual(2, advice.Count); + + mock.Verify(); + } + + [Test] + public void MakeSurePrototypeTargetIsNotNeedlesslyCreatedDuringInitialization_Unit() + { + GoodCommand target = new GoodCommand(); + NopInterceptor advice = new NopInterceptor(); + + IDynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("IsSingleton", true); // advice is a singleton... + mock.ExpectAndReturn("GetObject", advice, "advice"); + mock.ExpectAndReturn("GetType", typeof(GoodCommand), "prototype"); + + mock.ExpectAndReturn("GetObject", advice, "advice"); + mock.ExpectAndReturn("GetObject", target, "prototype"); + + IObjectFactory factory = (IObjectFactory)mock.Object; + + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { typeof(ICommand).FullName }; + fac.IsSingleton = false; + fac.InterceptorNames = new string[] { "advice", "prototype" }; + fac.ObjectFactory = factory; + + fac.GetObject(); + mock.Verify(); + } + + [Test] + public void MakeSurePrototypeTargetIsNotNeedlesslyCreatedDuringInitialization_Integration() + { + try + { + RootObjectDefinition advice = new RootObjectDefinition(typeof(NopInterceptor)); + // prototype target... + RootObjectDefinition target = new RootObjectDefinition(typeof(InstantiationCountingCommand), false); + + DefaultListableObjectFactory ctx = new DefaultListableObjectFactory(); + ctx.RegisterObjectDefinition("advice", advice); + ctx.RegisterObjectDefinition("prototype", target); + + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { typeof(ICommand).FullName }; + fac.IsSingleton = false; + fac.InterceptorNames = new string[] { "advice", "prototype" }; + fac.ObjectFactory = ctx; + + Assert.AreEqual(0, InstantiationCountingCommand.NumberOfInstantiations, + "Prototype target instance is being (needlessly) created during PFO initialization."); + fac.GetObject(); + Assert.AreEqual(1, InstantiationCountingCommand.NumberOfInstantiations, "Expected 1 inst"); + fac.GetObject(); + Assert.AreEqual(2, InstantiationCountingCommand.NumberOfInstantiations); + } + finally + { + InstantiationCountingCommand.NumberOfInstantiations = 0; + } + } + + [Test] + public void SingletonProxyWithPrototypeTarget() + { + try + { + RootObjectDefinition advice = new RootObjectDefinition(typeof(NopInterceptor)); + // prototype target... + RootObjectDefinition target = new RootObjectDefinition(typeof(InstantiationCountingCommand), false); + + DefaultListableObjectFactory ctx = new DefaultListableObjectFactory(); + ctx.RegisterObjectDefinition("advice", advice); + ctx.RegisterObjectDefinition("prototype", target); + + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { typeof(ICommand).FullName }; + fac.IsSingleton = true; + fac.InterceptorNames = new string[] { "advice", "prototype" }; + fac.ObjectFactory = ctx; + + Assert.AreEqual(1, InstantiationCountingCommand.NumberOfInstantiations, "First Call"); + fac.GetObject(); + Assert.AreEqual(1, InstantiationCountingCommand.NumberOfInstantiations, "Second Call"); + fac.GetObject(); + Assert.AreEqual(1, InstantiationCountingCommand.NumberOfInstantiations, "Third Call"); + } + + finally + { + InstantiationCountingCommand.NumberOfInstantiations = 0; + } + } + + public class InstantiationCountingCommand : ICommand + { + private static int numberOfInstantiations = 0; + + public InstantiationCountingCommand() + { + ++numberOfInstantiations; + } + + public static int NumberOfInstantiations + { + get { return numberOfInstantiations; } + set { numberOfInstantiations = value; } + } + + public void Execute() + { + } + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void NullNameInInterceptorNamesArray() + { + IObjectFactory factory = (IObjectFactory)new DynamicMock(typeof(IObjectFactory)).Object; + + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { typeof(ICommand).FullName }; + fac.IsSingleton = false; + fac.InterceptorNames = new string[] { null, null }; + fac.ObjectFactory = factory; + } + + [Test] + public void PassEmptyInterceptorNamesArray_WithTargetThatImplementsAnInterfaceCanBeCastToSaidInterface() + { + IObjectFactory factory = (IObjectFactory)new DynamicMock(typeof(IObjectFactory)).Object; + + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { }; + fac.Target = new GoodCommand(); + fac.ObjectFactory = factory; + + IAdvised advised = fac.GetObject() as IAdvised; + Assert.IsNotNull(advised); + + ICommand cmd = fac.GetObject() as ICommand; + Assert.IsNotNull(cmd); + + DoesntImplementAnyInterfaces obj = fac.GetObject() as DoesntImplementAnyInterfaces; + Assert.IsNull(obj); + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void PassNullToTheProxyInterfacesProperty() + { + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = null; + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void PassClassNotInterfaceNameToTheProxyInterfacesProperty() + { + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { typeof(GoodCommand).FullName }; + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void PassRubbishNameToTheProxyInterfacesProperty() + { + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { "Hey" }; + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void PassNullElementListToTheProxyInterfacesProperty() + { + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.ProxyInterfaces = new string[] { null }; + } + + [Test] + public void ProxiedObjectUnwrapsTargetInvocationException() + { + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.AddInterface(typeof(ICommand)); + fac.AddAdvice(new NopInterceptor()); + fac.Target = new BadCommand(); + + ICommand cmd = (ICommand)fac.GetObject(); + try + { + cmd.Execute(); + } + catch (NotImplementedException) + { + // this is good, we want this exception to bubble up... + } + catch (TargetInvocationException) + { + Assert.Fail("Must have unwrapped this."); + } + } + + [Test] + public void FactoryWrapsObjectInSingletonTargetSource() + { + IObjectFactory bf = new XmlObjectFactory( + new ReadOnlyXmlTestResource("proxyFactoryTargetSourceTests.xml", + GetType())); + ITestObject tb = (ITestObject)bf.GetObject("viaTargetSource"); + Assert.IsTrue(tb.Name.Equals("Adam")); + ProxyFactoryObject pfb = (ProxyFactoryObject)bf.GetObject("&viaTargetSource"); + Assert.IsTrue(typeof(ITestObject).IsAssignableFrom(pfb.ObjectType), "Has correct object type"); + Assert.AreEqual(typeof(SingletonTargetSource), pfb.TargetSource.GetType(), "Incorrect target source, expected singleton"); + } + + [Test(Description = "http://opensource.atlassian.com/projects/spring/browse/SPRNET-293")] + public void SupportsTransparentProxyAsTarget() + { + AppDomain domain = null; + try + { + AppDomainSetup setup = new AppDomainSetup(); + setup.ApplicationBase = Environment.CurrentDirectory; + domain = AppDomain.CreateDomain("Spring", new Evidence(AppDomain.CurrentDomain.Evidence), setup); + object command = domain.CreateInstanceAndUnwrap(GetType().Assembly.FullName, typeof(RemotableCommand).FullName); + + ProxyFactoryObject fac = new ProxyFactoryObject(); + fac.AddInterface(typeof(ICommand)); + fac.AddAdvice(new NopInterceptor()); + fac.Target = command; + + IAdvised advised = fac.GetObject() as IAdvised; + Assert.IsNotNull(advised); + + ICommand cmd = fac.GetObject() as ICommand; + Assert.IsNotNull(cmd); + + cmd.Execute(); + } + finally + { + AppDomain.Unload(domain); + } + } + + [Test(Description = "http://opensource.atlassian.com/projects/spring/browse/SPRNET-500")] + public void NotAccessibleInterfaceProxying() + { + const string xml = @" + + + + + Spring.Aop.Framework.HelperInterface2, Spring.Aop.Tests + + + + + + MyInterceptor + + + + + + "; + + + MemoryStream stream = new MemoryStream(new UTF8Encoding().GetBytes(xml)); + IResource resource = new InputStreamResource(stream, "Test ProxyFactoryObject"); + XmlObjectFactory objectFactory = new XmlObjectFactory(resource, null); + + HelperInterface2 hc = (HelperInterface2)objectFactory.GetObject("MyProxy"); + Console.WriteLine(hc.SecondDoSomething()); + } + + [Test] + public void ProxyFactoryObjectIsThreadSafe() + { + const int WORKERS = 10; + + AsyncTestMethod[] workers = new AsyncTestMethod[WORKERS]; + for(int i=0;i + /// Fires only on void methods. Saves list of methods intercepted. + /// + public class PointcutForVoid : DynamicMethodMatcherPointcutAdvisor + { + private class AnonymousClassMethodInterceptor1 : IMethodInterceptor + { + public Object Invoke(IMethodInvocation invocation) + { + methodNames.Add(invocation.Method.Name); + return invocation.Proceed(); + } + } + + public static IList methodNames; + + public static void Reset() + { + methodNames.Clear(); + } + + public PointcutForVoid() + : base(new AnonymousClassMethodInterceptor1()) + { + } + + /// + /// Must fire only if it returns void. + /// + public override bool Matches(MethodInfo method, Type targetType, object[] args) + { + return method.ReturnType == typeof(void); + } + + static PointcutForVoid() + { + methodNames = new ArrayList(); + } + } + + /// Aspect interface + public interface IAddedGlobalInterface + { + int GlobalsAdded { get; set; } + } + + /// Use as a global interceptor. Checks that + /// global interceptors can add aspect interfaces. + /// NB: Add only via global interceptors in XML file. + /// + public class GlobalIntroduction : IAdvice, IAddedGlobalInterface + { + private int globalsAdded = -1; + + public int GlobalsAdded + { + get { return globalsAdded; } + set { globalsAdded = value; } + } + } + + private class EvilMethodInterceptor : IMethodInterceptor + { + private Exception ex; + + public EvilMethodInterceptor(Exception ex) + { + this.ex = ex; + } + + public object Invoke(IMethodInvocation invocation) + { + throw ex; + } + } + + #endregion + } + + #region HelpersForNotAccessibleInterfaceProxyingTest + + public class HelperClassForNotAccessibleInterfaceProxyingTest : HelperInterface1, HelperInterface2 + { + public string FirstDoSomething() + { + return "FirstDoSomething (internal interface implementation)"; + } + + public string SecondDoSomething() + { + return "SecondDoSomething (public interface implementation)"; + } + } + + internal interface HelperInterface1 + { + string FirstDoSomething(); + } + + public interface HelperInterface2 + { + string SecondDoSomething(); + } + + #endregion + + +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/ProxyFactoryTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/ProxyFactoryTests.cs new file mode 100644 index 00000000..b8e09904 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/ProxyFactoryTests.cs @@ -0,0 +1,608 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; +using AopAlliance.Aop; +using AopAlliance.Intercept; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Aop.Interceptor; +using Spring.Aop.Support; +using Spring.Objects; +using Spring.Proxy; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Unit tests for the ProxyFactory class. + /// + /// Rod Johnson + /// Choy Rim (.NET) + /// Rick Evans (.NET) + /// $Id: ProxyFactoryTests.cs,v 1.22 2008/03/21 10:49:38 oakinger Exp $ + [TestFixture] + public sealed class ProxyFactoryTests + { + public interface IDoubleClickable + { + event EventHandler DoubleClick; + + void FireDoubleClickEvent(); + } + + public interface IDoubleClickableIntroduction : + IDoubleClickable, IAdvice + { + } + + private class DoubleClickableIntroduction : + IDoubleClickableIntroduction + { + public event EventHandler DoubleClick; + + public void FireDoubleClickEvent() + { + if (DoubleClick != null) + { + DoubleClick(this, EventArgs.Empty); + } + } + } + + [Test] + public void AddAndRemoveEventHandlerThroughIntroduction() + { + TestObject target = new TestObject(); + DoubleClickableIntroduction dci = new DoubleClickableIntroduction(); + DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(dci); + CountingBeforeAdvice countingBeforeAdvice = new CountingBeforeAdvice(); + target.Name = "SOME-NAME"; + ProxyFactory pf = new ProxyFactory(target); + pf.AddIntroduction(advisor); + pf.AddAdvisor(new DefaultPointcutAdvisor(countingBeforeAdvice)); + object proxy = pf.GetProxy(); + ITestObject to = proxy as ITestObject; + Assert.IsNotNull(to); + Assert.AreEqual("SOME-NAME", to.Name); + IDoubleClickable doubleClickable = proxy as IDoubleClickable; + // add event handler through introduction + doubleClickable.DoubleClick += new EventHandler(OnClick); + OnClickWasCalled = false; + doubleClickable.FireDoubleClickEvent(); + Assert.IsTrue(OnClickWasCalled); + Assert.AreEqual(3, countingBeforeAdvice.GetCalls()); + // remove event handler through introduction + doubleClickable.DoubleClick -= new EventHandler(OnClick); + OnClickWasCalled = false; + doubleClickable.FireDoubleClickEvent(); + Assert.IsFalse(OnClickWasCalled); + Assert.AreEqual(5, countingBeforeAdvice.GetCalls()); + } + + private bool OnClickWasCalled = false; + + private void OnClick(object sender, EventArgs e) + { + OnClickWasCalled = true; + } + + [Test] + public void CacheTest() + { + + for (int i = 0; i < 2; i++) + { + TestObject target = new TestObject(); + NopInterceptor nopInterceptor = new NopInterceptor(); + CountingBeforeAdvice countingBeforeAdvice = new CountingBeforeAdvice(); + ProxyFactory pf = new ProxyFactory(); + pf.Target = target; + pf.AddAdvice(nopInterceptor); + pf.AddAdvisor(new DefaultPointcutAdvisor(countingBeforeAdvice)); + object proxy = pf.GetProxy(); + } + + // fails when running in resharper/testdriven.net + // DynamicProxyManager.SaveAssembly(); + + } + + [Test] + public void AddAndRemoveEventHandlerThroughInterceptor() + { + TestObject target = new TestObject(); + NopInterceptor nopInterceptor = new NopInterceptor(); + CountingBeforeAdvice countingBeforeAdvice = new CountingBeforeAdvice(); + target.Name = "SOME-NAME"; + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(nopInterceptor); + pf.AddAdvisor(new DefaultPointcutAdvisor(countingBeforeAdvice)); + object proxy = pf.GetProxy(); + ITestObject to = proxy as ITestObject; + // add event handler through proxy + to.Click += new EventHandler(OnClick); + OnClickWasCalled = false; + to.OnClick(); + Assert.IsTrue(OnClickWasCalled); + Assert.AreEqual(2, countingBeforeAdvice.GetCalls()); + // remove event handler through proxy + to.Click -= new EventHandler(OnClick); + OnClickWasCalled = false; + to.OnClick(); + Assert.IsFalse(OnClickWasCalled); + Assert.AreEqual(4, countingBeforeAdvice.GetCalls()); + } + + private class TestObject2 : TestObject + { + public bool EqualsOverrideWasCalled = false; + + public override bool Equals(object obj) + { + EqualsOverrideWasCalled = true; + return true; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + [Test] + public void CallsEqualsOverride() + { + TestObject2 target = new TestObject2(); + target.Name = "SOME-NAME"; + ProxyFactory pf = new ProxyFactory(target); + object proxy = pf.GetProxy(); + ITestObject to = proxy as ITestObject; + Assert.IsNotNull(to); + Assert.AreEqual("SOME-NAME", to.Name); + + target.EqualsOverrideWasCalled = false; + Assert.IsTrue(to.Equals(proxy)); + Assert.IsTrue(target.EqualsOverrideWasCalled); + + target.EqualsOverrideWasCalled = false; + Assert.IsTrue(proxy.Equals(to)); + Assert.IsTrue(target.EqualsOverrideWasCalled); + + target.EqualsOverrideWasCalled = false; + Assert.IsTrue(target.Equals(to)); + Assert.IsTrue(target.EqualsOverrideWasCalled); + + target.EqualsOverrideWasCalled = false; + Assert.IsTrue(to.Equals(target)); + Assert.IsTrue(target.EqualsOverrideWasCalled); + } + + [Test] + public void CreateProxyFactoryWithoutTargetThenSetTarget() + { + TestObject target = new TestObject(); + target.Name = "Adam"; + NopInterceptor nopInterceptor = new NopInterceptor(); + CountingBeforeAdvice countingBeforeAdvice = new CountingBeforeAdvice(); + ProxyFactory pf = new ProxyFactory(); + pf.Target = target; + pf.AddAdvice(nopInterceptor); + pf.AddAdvisor(new DefaultPointcutAdvisor(countingBeforeAdvice)); + object proxy = pf.GetProxy(); + ITestObject to = (ITestObject) proxy; + Assert.AreEqual("Adam", to.Name); + Assert.AreEqual(1, countingBeforeAdvice.GetCalls()); + } + + [Test] + [ExpectedException(typeof (AopConfigException))] + public void InstantiateWithNullTarget() + { + new ProxyFactory((object) null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddNullInterface() + { + new ProxyFactory().AddInterface(null); + } + + [Test] + [ExpectedException(typeof (AopConfigException))] + public void AddInterfaceWhenConfigurationIsFrozen() + { + ProxyFactory factory = new ProxyFactory(); + factory.IsFrozen = true; + factory.AddInterface(typeof(ITestObject)); + } + + [Test] + public void IndexOfMethods() + { + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + NopInterceptor nop = new NopInterceptor(); + IAdvisor advisor = new DefaultPointcutAdvisor(new CountingBeforeAdvice()); + IAdvised advised = (IAdvised) pf.GetProxy(); + // Can use advised and ProxyFactory interchangeably + advised.AddAdvice(nop); + pf.AddAdvisor(advisor); + Assert.AreEqual(- 1, pf.IndexOf((IInterceptor) null)); + Assert.AreEqual(- 1, pf.IndexOf(new NopInterceptor())); + Assert.AreEqual(0, pf.IndexOf(nop)); + Assert.AreEqual(- 1, advised.IndexOf((IAdvisor) null)); + Assert.AreEqual(1, pf.IndexOf(advisor)); + Assert.AreEqual(- 1, advised.IndexOf(new DefaultPointcutAdvisor(null))); + } + + [Test] + public void RemoveAdvisorByReference() + { + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + NopInterceptor nop = new NopInterceptor(); + CountingBeforeAdvice cba = new CountingBeforeAdvice(); + IAdvisor advisor = new DefaultPointcutAdvisor(cba); + pf.AddAdvice(nop); + pf.AddAdvisor(advisor); + ITestObject proxied = (ITestObject) pf.GetProxy(); + proxied.Age = 5; + Assert.AreEqual(1, cba.GetCalls()); + Assert.AreEqual(1, nop.Count); + Assert.IsFalse(pf.RemoveAdvisor(null)); + Assert.IsTrue(pf.RemoveAdvisor(advisor)); + Assert.AreEqual(5, proxied.Age); + Assert.AreEqual(1, cba.GetCalls()); + Assert.AreEqual(2, nop.Count); + Assert.IsFalse(pf.RemoveAdvisor(new DefaultPointcutAdvisor(null))); + } + + [Test] + public void RemoveAdvisorByIndex() + { + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + NopInterceptor nop = new NopInterceptor(); + CountingBeforeAdvice cba = new CountingBeforeAdvice(); + IAdvisor advisor = new DefaultPointcutAdvisor(cba); + pf.AddAdvice(nop); + pf.AddAdvisor(advisor); + NopInterceptor nop2 = new NopInterceptor(2); // make instance unique (see SPRNET-847) + pf.AddAdvice(nop2); + ITestObject proxied = (ITestObject) pf.GetProxy(); + proxied.Age = 5; + Assert.AreEqual(1, cba.GetCalls()); + Assert.AreEqual(1, nop.Count); + Assert.AreEqual(1, nop2.Count); + // Removes counting before advisor + pf.RemoveAdvisor(1); + Assert.AreEqual(5, proxied.Age); + Assert.AreEqual(1, cba.GetCalls()); + Assert.AreEqual(2, nop.Count); + Assert.AreEqual(2, nop2.Count); + // Removes Nop1 + pf.RemoveAdvisor(0); + Assert.AreEqual(5, proxied.Age); + Assert.AreEqual(1, cba.GetCalls()); + Assert.AreEqual(2, nop.Count); + Assert.AreEqual(3, nop2.Count); + + // Check out of bounds + try + { + pf.RemoveAdvisor(- 1); + Assert.Fail("Supposed to throw exception"); + } + catch (AopConfigException) + { + // Ok + } + + try + { + pf.RemoveAdvisor(2); + Assert.Fail("Supposed to throw exception"); + } + catch (AopConfigException) + { + // Ok + } + + Assert.AreEqual(5, proxied.Age); + Assert.AreEqual(4, nop2.Count); + } + + [Test] + public void TryRemoveNonProxiedInterface() + { + ProxyFactory factory = new ProxyFactory(new TestObject ()); + Assert.IsFalse(factory.RemoveInterface(typeof(IServiceProvider))); + } + + [Test] + public void RemoveProxiedInterface() + { + ProxyFactory factory = new ProxyFactory(new TestObject ()); + Assert.IsTrue(factory.RemoveInterface(typeof(ITestObject))); + } + + [Test] + public void ReplaceAdvisor() + { + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + NopInterceptor nop = new NopInterceptor(); + CountingBeforeAdvice cba1 = new CountingBeforeAdvice(); + CountingBeforeAdvice cba2 = new CountingBeforeAdvice(); + IAdvisor advisor1 = new DefaultPointcutAdvisor(cba1); + IAdvisor advisor2 = new DefaultPointcutAdvisor(cba2); + pf.AddAdvisor(advisor1); + pf.AddAdvice(nop); + ITestObject proxied = (ITestObject) pf.GetProxy(); + // Use the type cast feature + // Replace etc methods on advised should be same as on ProxyFactory + IAdvised advised = (IAdvised) proxied; + proxied.Age = 5; + Assert.AreEqual(1, cba1.GetCalls()); + Assert.AreEqual(0, cba2.GetCalls()); + Assert.AreEqual(1, nop.Count); + Assert.IsFalse(advised.ReplaceAdvisor(null, null)); + Assert.IsFalse(advised.ReplaceAdvisor(null, advisor2)); + Assert.IsFalse(advised.ReplaceAdvisor(advisor1, null)); + Assert.IsTrue(advised.ReplaceAdvisor(advisor1, advisor2)); + Assert.AreEqual(advisor2, pf.Advisors[0]); + Assert.AreEqual(5, proxied.Age); + Assert.AreEqual(1, cba1.GetCalls()); + Assert.AreEqual(2, nop.Count); + Assert.AreEqual(1, cba2.GetCalls()); + Assert.IsFalse(pf.ReplaceAdvisor(new DefaultPointcutAdvisor(null), advisor1)); + } + + [Test] + public void IgnoresAdvisorDuplicates() + { + CountingBeforeAdvice cba1 = new CountingBeforeAdvice(); + IAdvisor advisor1 = new DefaultPointcutAdvisor(cba1); + + AdvisedSupport advSup = new AdvisedSupport(); + advSup.AddAdvisor(advisor1); + advSup.AddAdvisor(advisor1); + + Assert.AreEqual(1, advSup.Advisors.Length); + } + + private class AnonymousClassTimeStamped : ITimeStamped + { + public AnonymousClassTimeStamped(ProxyFactoryTests enclosingInstance) + { + InitBlock(enclosingInstance); + } + + private void InitBlock(ProxyFactoryTests enclosingInstance) + { + this.enclosingInstance = enclosingInstance; + } + + private ProxyFactoryTests enclosingInstance; + + public ProxyFactoryTests Enclosing_Instance + { + get { return enclosingInstance; } + + } + + public DateTime TimeStamp + { + get { throw new NotSupportedException("TimeStamp"); } + } + } + + [Test] + public void AddRepeatedInterface() + { + ITimeStamped tst = new AnonymousClassTimeStamped(this); + ProxyFactory pf = new ProxyFactory(tst); + // We've already implicitly added this interface. + // This call should be ignored without error + pf.AddInterface(typeof (ITimeStamped)); + // All cool + ITimeStamped ts = (ITimeStamped) pf.GetProxy(); + } + + internal class TestObjectSubclass : TestObject, IComparable + { + public override int CompareTo(Object arg0) + { + throw new NotSupportedException("compareTo"); + } + } + + [Test] + public void GetsAllInterfaces() + { + // Extend to get new interface + TestObjectSubclass raw = new TestObjectSubclass(); + ProxyFactory factory = new ProxyFactory(raw); + Assert.AreEqual(7, factory.Interfaces.Length, "Found correct number of interfaces"); + //System.out.println("Proxied interfaces are " + StringUtils.arrayToDelimitedString(factory.getProxiedInterfaces(), ",")); + ITestObject tb = (ITestObject) factory.GetProxy(); + Assert.IsTrue(tb is IOther, "Picked up secondary interface"); + + raw.Age = 25; + Assert.IsTrue(tb.Age == raw.Age); + + DateTime t = new DateTime(2004, 8, 1); + TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(t); + + Console.WriteLine(StringUtils.ArrayToDelimitedString(factory.Interfaces, "/")); + + //factory.addAdvisor(0, new DefaultIntroductionAdvisor(ti, typeof(ITimeStamped))); + factory.AddIntroduction( + new DefaultIntroductionAdvisor(ti, typeof (ITimeStamped)) + ); + + Console.WriteLine(StringUtils.ArrayToDelimitedString(factory.Interfaces, "/")); + + ITimeStamped ts = (ITimeStamped) factory.GetProxy(); + Assert.IsTrue(ts.TimeStamp == t); + // Shouldn't fail; + ((IOther) ts).Absquatulate(); + } + + private class AnonymousClassInterceptor : IInterceptor + { + } + + [Test] + public void CanOnlyAddMethodInterceptors() + { + ProxyFactory factory = new ProxyFactory(new TestObject()); + factory.AddAdvice(0, new NopInterceptor()); + try + { + factory.AddAdvice(0, new AnonymousClassInterceptor()); + Assert.Fail("Should only be able to add MethodInterceptors"); + } + catch (AopConfigException) + { + } + + // Check we can still use it + IOther other = (IOther) factory.GetProxy(); + other.Absquatulate(); + } + + [Test] + public void InterceptorInclusionMethods() + { + NopInterceptor di = new NopInterceptor(); + NopInterceptor diUnused = new NopInterceptor(1); // // make instance unique (see SPRNET-847) + ProxyFactory factory = new ProxyFactory(new TestObject()); + factory.AddAdvice(0, di); + ITestObject tb = (ITestObject) factory.GetProxy(); + Assert.IsTrue(factory.AdviceIncluded(di)); + Assert.IsTrue(!factory.AdviceIncluded(diUnused)); + Assert.IsTrue(factory.CountAdviceOfType(typeof (NopInterceptor)) == 1); + + factory.AddAdvice(0, diUnused); + Assert.IsTrue(factory.AdviceIncluded(diUnused)); + Assert.IsTrue(factory.CountAdviceOfType(typeof (NopInterceptor)) == 2); + } + + [Test] + public void AddAdvisedSupportListener() + { + IDynamicMock mock = new DynamicMock(typeof(IAdvisedSupportListener)); + IAdvisedSupportListener listener = (IAdvisedSupportListener) mock.Object; + + mock.Expect("Activated"); + ProxyFactory factory = new ProxyFactory(new TestObject()); + factory.AddListener(listener); + factory.GetProxy(); + mock.Verify(); + } + + [Test] + public void AdvisedSupportListenerMethodsAreCalledAppropriately() + { + IDynamicMock mock = new DynamicMock(typeof(IAdvisedSupportListener)); + IAdvisedSupportListener listener = (IAdvisedSupportListener) mock.Object; + + mock.Expect("Activated"); + mock.Expect("AdviceChanged"); + mock.Expect("InterfacesChanged"); + + ProxyFactory factory = new ProxyFactory(new TestObject()); + factory.AddListener(listener); + + // must fire the Activated callback... + factory.GetProxy(); + // must fire the AdviceChanged callback... + factory.AddAdvice(new NopInterceptor()); + // must fire the InterfacesChanged callback... + factory.AddInterface(typeof(ISerializable)); + + mock.Verify(); + } + + [Test] + public void AdvisedSupportListenerMethodsAre_NOT_CalledIfProxyHasNotBeenCreated() + { + IDynamicMock mock = new DynamicMock(typeof(IAdvisedSupportListener)); + IAdvisedSupportListener listener = (IAdvisedSupportListener) mock.Object; + + ProxyFactory factory = new ProxyFactory(new TestObject()); + factory.AddListener(listener); + + // must not fire the AdviceChanged callback... + factory.AddAdvice(new NopInterceptor()); + // must not fire the InterfacesChanged callback... + factory.AddInterface(typeof(ISerializable)); + + mock.Verify(); + } + + [Test] + public void AddNullAdvisedSupportListenerIsOk() + { + ProxyFactory factory = new ProxyFactory(new TestObject()); + factory.AddListener(null); + } + + [Test] + public void RemoveNullAdvisedSupportListenerIsOk() + { + ProxyFactory factory = new ProxyFactory(new TestObject()); + factory.RemoveListener(null); + } + + [Test] + public void RemoveAdvisedSupportListener() + { + IDynamicMock mock = new DynamicMock(typeof(IAdvisedSupportListener)); + IAdvisedSupportListener listener = (IAdvisedSupportListener) mock.Object; + + ProxyFactory factory = new ProxyFactory(new TestObject()); + factory.AddListener(listener); + factory.RemoveListener(listener); + + factory.GetProxy(); + + // check that no lifecycle callback methods were invoked on the listener... + mock.Verify(); + } + + [Test] + [ExpectedException(typeof(AopConfigException))] + public void Frozen_RemoveAdvisor() + { + ProxyFactory factory = new ProxyFactory(); + factory.IsFrozen = true; + factory.RemoveAdvisor(null); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/ReflectiveMethodInvocationTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/ReflectiveMethodInvocationTests.cs new file mode 100644 index 00000000..2cb5fb4f --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/ReflectiveMethodInvocationTests.cs @@ -0,0 +1,47 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Collections; + +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Unit tests for the ReflectiveMethodInvocation class. + /// + /// Rick Evans + /// Bruno Baia + /// $Id: ReflectiveMethodInvocationTests.cs,v 1.8 2008/02/06 18:29:05 bbaia Exp $ + [TestFixture] + public class ReflectiveMethodInvocationTests : AbstractMethodInvocationTests + { + protected override AbstractMethodInvocation CreateMethodInvocation(object proxy, object target, MethodInfo method, MethodInfo onProxyMethod, object[] arguments, Type targetType, IList interceptors) + { + return new ReflectiveMethodInvocation(proxy, target, method, onProxyMethod, arguments, targetType, interceptors); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/TimestampIntroductionInterceptor.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/TimestampIntroductionInterceptor.cs new file mode 100644 index 00000000..03818c48 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/TimestampIntroductionInterceptor.cs @@ -0,0 +1,67 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion +#region Imports +using System; + +using AopAlliance.Aop; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Test introduction ported from Spring.Java. + /// + /// + /// The name is deceptive because it isn't implemented as an interceptor. + /// It's an introduction which is handled differently in Spring.NET. + /// Keeping the name is useful as a placeholder for future porting work. + /// + /// Spring.Java Folks + /// Choy Rim (.NET) + public class TimestampIntroductionInterceptor : IAdvice, ITimeStamped, IInterceptor + { + private DateTime ts; + + public TimestampIntroductionInterceptor() + { + } + + public TimestampIntroductionInterceptor(DateTime ts) + { + this.ts = ts; + } + + #region ITimeStamped Members + + public DateTime TimeStamp + { + get + { + return this.ts; + } + set + { + this.ts = value; + } + } + + #endregion + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/UnsupportedInterceptor.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/UnsupportedInterceptor.cs new file mode 100644 index 00000000..7929424b --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/UnsupportedInterceptor.cs @@ -0,0 +1,13 @@ +using System; +using AopAlliance.Intercept; + +namespace Spring.Aop.Framework +{ + public class UnsupportedInterceptor : IMethodInterceptor + { + public object Invoke(IMethodInvocation invocation) + { + throw new NotImplementedException(invocation.Method.Name); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/ISimpleBeforeAdvice.cs b/test/Spring/Spring.Aop.Tests/Aop/ISimpleBeforeAdvice.cs new file mode 100644 index 00000000..9f44ea21 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/ISimpleBeforeAdvice.cs @@ -0,0 +1,34 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports +using System; +#endregion + +namespace Spring.Aop +{ + /// + /// Simple BeforeAdvice targeted for testing + /// + /// Dmitriy Kopylenko + /// Simon White (.NET) + public interface ISimpleBeforeAdvice : IBeforeAdvice + { + void Before(); + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Interceptor/NopInterceptor.cs b/test/Spring/Spring.Aop.Tests/Aop/Interceptor/NopInterceptor.cs new file mode 100644 index 00000000..05087806 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Interceptor/NopInterceptor.cs @@ -0,0 +1,86 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring.Aop.Interceptor +{ + /// + /// Trivial interceptor that can be introduced into an interceptor chain to + /// aid in debugging. + /// + /// Rod Johnson + /// Choy Rim (.NET) + /// $Id: NopInterceptor.cs,v 1.6 2008/01/14 20:49:47 oakinger Exp $ + public class NopInterceptor : IMethodBeforeAdvice + { + protected int instanceId; + protected int count; + + + public NopInterceptor() : this(0) + { + } + + public NopInterceptor(int instanceId) + { + this.instanceId = instanceId; + } + + public int InstanceId + { + get { return instanceId; } + } + + public int Count + { + get { return this.count; } + } + + public void Before(MethodInfo method, object[] args, object target) + { + ++count; + } + + public override bool Equals(Object other) + { + if (!(other is NopInterceptor)) + { + return false; + } + if (this == other) + { + return true; + } + return (instanceId == ((NopInterceptor) other).InstanceId) + && (count == ((NopInterceptor) other).count); + } + + public override int GetHashCode() + { + return instanceId + 13 * count; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Interceptor/SerializableNopInterceptor.cs b/test/Spring/Spring.Aop.Tests/Aop/Interceptor/SerializableNopInterceptor.cs new file mode 100644 index 00000000..b25ad5a8 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Interceptor/SerializableNopInterceptor.cs @@ -0,0 +1,52 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports +using System; +using System.Runtime.Serialization; +#endregion + +namespace Spring.Aop.Interceptor +{ + /// + /// Subclass of NopInterceptor that is serializable and + /// can be used to test proxy serialization. + /// + /// Rod Johnson + /// Simon White (.NET) + [Serializable] + public sealed class SerializableNopInterceptor : NopInterceptor, ISerializable + { + public SerializableNopInterceptor() + { + } + + public SerializableNopInterceptor(SerializationInfo info, StreamingContext ctxt) + { + this.instanceId = (int) info.GetValue("InstanceId", typeof(int)); + this.count = (int)info.GetValue("Count", typeof(int)); + } + + public void GetObjectData(SerializationInfo info, StreamingContext ctxt) + { + info.AddValue("InstanceId", InstanceId); + info.AddValue("Count", Count); + } + + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/SimpleBeforeAdviceAdapter.cs b/test/Spring/Spring.Aop.Tests/Aop/SimpleBeforeAdviceAdapter.cs new file mode 100644 index 00000000..96e09301 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/SimpleBeforeAdviceAdapter.cs @@ -0,0 +1,51 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports +using System; + +using AopAlliance.Aop; +using AopAlliance.Intercept; + +using Spring.Aop.Framework.Adapter; +#endregion + + +namespace Spring.Aop +{ + /// + /// + /// + /// Dmitriy Kopylenko + /// Simon White (.NET) + public class SimpleBeforeAdviceAdapter : IAdvisorAdapter + { + #region IAdvisorAdapter Members + public bool SupportsAdvice(IAdvice advice) + { + return advice is ISimpleBeforeAdvice; + } + + public IInterceptor GetInterceptor(IAdvisor advisor) + { + ISimpleBeforeAdvice advice = (ISimpleBeforeAdvice) advisor.Advice; + return new SimpleBeforeAdviceInterceptor(advice) ; + } + #endregion + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/SimpleBeforeAdviceImpl.cs b/test/Spring/Spring.Aop.Tests/Aop/SimpleBeforeAdviceImpl.cs new file mode 100644 index 00000000..a39c8856 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/SimpleBeforeAdviceImpl.cs @@ -0,0 +1,57 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports +using System; +#endregion + +namespace Spring.Aop +{ + /// + /// + /// + /// Dmitriy Kopylenko + /// Simon White (.NET) + public class SimpleBeforeAdviceImpl : ISimpleBeforeAdvice + { + private int _invocationCounter; + + #region Properties + public int InvocationCounter + { + get + { + return _invocationCounter; + } + } + #endregion + + #region Constructors + public SimpleBeforeAdviceImpl() + { + } + #endregion + + #region ISimpleBeforeAdvice Members + public void Before() + { + ++_invocationCounter; + } + #endregion + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/SimpleBeforeAdviceInterceptor.cs b/test/Spring/Spring.Aop.Tests/Aop/SimpleBeforeAdviceInterceptor.cs new file mode 100644 index 00000000..e97328a7 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/SimpleBeforeAdviceInterceptor.cs @@ -0,0 +1,51 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports +using System; + +using AopAlliance.Intercept; +#endregion + +namespace Spring.Aop +{ + /// + /// + /// + /// Dmitriy Kopylenko + /// Simon White (.NET) + public class SimpleBeforeAdviceInterceptor : IMethodInterceptor + { + private ISimpleBeforeAdvice _advice; + + #region Constructors + public SimpleBeforeAdviceInterceptor(ISimpleBeforeAdvice advice) + { + this._advice = advice; + } + #endregion + + #region IMethodInterceptor Members + public object Invoke(IMethodInvocation mi) + { + _advice.Before(); + return mi.Proceed(); + } + #endregion + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Support/AbstractRegularExpressionMethodPointcutTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Support/AbstractRegularExpressionMethodPointcutTests.cs new file mode 100644 index 00000000..cc1684f9 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Support/AbstractRegularExpressionMethodPointcutTests.cs @@ -0,0 +1,109 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports +using System; +using System.Reflection; + +using Spring.Util; + +using NUnit.Framework; +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Unit tests for the AbstractRegularExpressionMethodPointcut class. + /// + /// Rod Johnson + /// Dmitriy Kopylenko + /// Simon White (.NET) + public abstract class AbstractRegularExpressionMethodPointcutTests + { + private AbstractRegularExpressionMethodPointcut pointcut; + + [SetUp] + protected void SetUp() + { + pointcut = GetRegexpMethodPointcut(); + } + + protected abstract AbstractRegularExpressionMethodPointcut GetRegexpMethodPointcut(); + + [Test] + public void NoPatternSupplied() + { + NoPatternSuppliedTests(pointcut); + } + + [Test] + public void SerializationWithNoPatternSupplied() + { + pointcut = (AbstractRegularExpressionMethodPointcut) SerializationTestUtils.SerializeAndDeserialize(pointcut); + NoPatternSuppliedTests(pointcut); + } + + protected void NoPatternSuppliedTests(AbstractRegularExpressionMethodPointcut rpc) + { + Assert.IsFalse(rpc.Matches(typeof(object).GetMethod("GetHashCode"), typeof(int))); + Assert.IsFalse(rpc.Matches(typeof(object).GetMethod("GetType"), typeof(Type))); + Assert.AreEqual(0, rpc.Patterns.Length); + } + + [Test] + public void ExactMatch() + { + pointcut.Pattern = "System.Object.GetHashCode"; + ExactMatchTests(pointcut); + pointcut = (AbstractRegularExpressionMethodPointcut) SerializationTestUtils.SerializeAndDeserialize(pointcut); + ExactMatchTests(pointcut); + } + + protected void ExactMatchTests(AbstractRegularExpressionMethodPointcut rpc) + { + // assumes rpc.setPattern("java.lang.Object.hashCode"); + Assert.IsTrue(rpc.Matches(typeof(object).GetMethod("GetHashCode"), typeof(int))); + Assert.IsFalse(rpc.Matches(typeof(object).GetMethod("GetType"), typeof(Type))); + } + + [Test] + public void Wildcard() + { + pointcut.Pattern = ".*Object.GetHashCode"; + Assert.IsTrue(pointcut.Matches(typeof(object).GetMethod("GetHashCode"), typeof(int))); + Assert.IsFalse(pointcut.Matches(typeof(object).GetMethod("GetType"), typeof(Type))); + } + + [Test] + public void WildcardForOneClass() + { + pointcut.Pattern = "System.Object.*"; + Assert.IsTrue(pointcut.Matches(typeof(object).GetMethod("GetHashCode"), typeof(int))); + Assert.IsTrue(pointcut.Matches(typeof(object).GetMethod("GetType"), typeof(Type))); + } + + [Test] + public void MatchesObjectClass() + { + pointcut.Pattern = "System.Object.*"; + Assert.IsTrue(pointcut.Matches(typeof(Exception).GetMethod("GetHashCode"), typeof(TargetException))); + // Doesn't match + Assert.IsFalse(pointcut.Matches(typeof(Exception).GetMethod("ToString"), typeof(Exception))); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Support/AttributeMatchMethodPointcutTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Support/AttributeMatchMethodPointcutTests.cs new file mode 100644 index 00000000..560bd9aa --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Support/AttributeMatchMethodPointcutTests.cs @@ -0,0 +1,229 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using System.Reflection; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Unit tests for the AttributeMatchMethodPointcut class. + /// + /// Rick Evans + /// Ronald Wildenberg + /// $Id: AttributeMatchMethodPointcutTests.cs,v 1.3 2007/05/21 16:44:08 bbaia Exp $ + [TestFixture] + public sealed class AttributeMatchMethodPointcutTests + { + [Test] + public void InstantiationWithASunnyDayAttributeType() + { + new AttributeMatchMethodPointcut(typeof(SerializableAttribute)); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void InstantiationWithNonAttributeType() + { + new AttributeMatchMethodPointcut(GetType()); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void AttributeSetterWithNonAttributeType() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = GetType(); + } + + [Test] + public void AttributeSetterWithNullType() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = null; // must allow this (no Exception)... + } + + [Test] + public void AttributeSetterWithASunnyDayAttributeType() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = typeof(SerializableAttribute); // must allow this too (no Exception)... + } + + [Test] + public void MatchesWithASunnyDayAttributeTypeAndNoInheritance() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = typeof(MarkupAttribute); + bool matches = cut.Matches(typeof(WithMarkup).GetMethod("Bing"), null); + Assert.IsTrue(matches, "Method was decorated with the target attribute, so this must match."); + } + + [Test] + public void MatchesWithASunnyDayAttributeTypeAndInheritance() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = typeof(MarkupAttribute); + bool matches = cut.Matches(typeof(InheritedWithMarkup).GetMethod("Bing"), null); + Assert.IsTrue(matches, "Inherited method was decorated with the target attribute, so this must match."); + } + + [Test] + public void MatchesWithAMethodThatDontMatchTheAttributeTypeAndNoInheritance() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = typeof(MarkupAttribute); + bool matches = cut.Matches(typeof(WithMarkup).GetMethod("RiloKiley"), null); + Assert.IsFalse(matches, "Method was not decorated with the target attribute, so this must not match."); + } + + [Test] + public void MatchesWithAnInheritedMethodThatDontMatchTheAttributeTypeAndNoInheritance() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = typeof(MarkupAttribute); + bool matches = cut.Matches(typeof(InheritedWithMarkup).GetMethod("RiloKiley"), null); + Assert.IsFalse(matches, "Inherited method was not decorated with the target attribute, so this must not match."); + } + + /// + /// Confirms that without interfaces checking, a method that is implemented from an interface, will not match. + /// + [Test] + public void MatchesWithAnInterfaceMethodThatMatchesTheAttributeTypeAndNoCheckInterfaces() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = typeof(MarkupAttribute); + cut.CheckInterfaces = false; + bool matches = cut.Matches(typeof(ImplementingClass).GetMethod("OtherTestMethod"), null); + Assert.IsFalse(matches, "Implementing method was not decorated with the target attribute, so this must not match since CheckInterfaces is false."); + } + + /// + /// Confirms that with interface checking, a method that is implementing an interface method + /// where the attribute is defined will match. + /// + [Test] + public void MatchesWithAnInterfaceMethodThatMatchesTheAttributeTypeAndCheckInterfaces() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = typeof(MarkupAttribute); + cut.CheckInterfaces = true; + bool matches = cut.Matches(typeof(ImplementingClass).GetMethod("OtherTestMethod"), null); + Assert.IsTrue(matches, "Implementing method was not decorated with the target attribute, " + + "but the method from the interface was, so this must match."); + } + + /// + /// Confirms that with interfaces checking, a method that is indirectly implementing an interface method + /// where the attribute is defined will match. + /// + [Test] + public void MatchesWithAnIndirectInterfaceMethodThatMatchesTheAttributeTypeAndCheckInterfaces() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = typeof(MarkupAttribute); + cut.CheckInterfaces = true; + bool matches = cut.Matches(typeof(ImplementingClass).GetMethod("TestMethod", new Type[] { }), null); + Assert.IsTrue(matches, "Implementing method was not decorated with the target attribute, but the" + + " method from an indirectly implemented interface was, so this must match."); + } + + /// + /// Confirms that overloading methods do not match, whatever the attributes. + /// + [Test] + public void MatchesWithAnOverloadedInterfaceMethod() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = typeof(MarkupAttribute); + cut.CheckInterfaces = true; + bool matches = cut.Matches(typeof(ImplementingClass).GetMethod("TestMethod", new Type[] { typeof(string) }), null); + Assert.IsFalse(matches, "Overloaded method from an implemented interface is not decorated with" + + " the attribute, so should not match."); + } + + /// + /// Confirms that methods, defined in a subclass of a class that implements an interface that + /// has methods that have been decorated with an attribute, match. + /// + [Test] + public void MatchesWithAnIndirectInterfaceMethodFromSubclassThatMatchesTheAttributeTypeAndCheckInterfaces() + { + AttributeMatchMethodPointcut cut = new AttributeMatchMethodPointcut(); + cut.Attribute = typeof(MarkupAttribute); + cut.CheckInterfaces = true; + bool matches = cut.Matches(typeof(InheritedImplementingClass).GetMethod("TestMethod", new Type[] { }), null); + Assert.IsTrue(matches, "Implementing method from subclass was not decorated with the target attribute " + + "but the method from an indirectly implemented interface was, so this must match."); + } + + #region Helper classes definitions + + [AttributeUsage(AttributeTargets.Method)] + private sealed class MarkupAttribute : Attribute {} + + private class WithMarkup + { + [Markup] + public void Bing() {} + + public void RiloKiley() {} + } + + private sealed class InheritedWithMarkup : WithMarkup + { + } + + private interface SuperInterface + { + [Markup] + void TestMethod(); + + void TestMethod(string param); + } + + private interface SubInterface : SuperInterface + { + [Markup] + void OtherTestMethod(); + } + + private class ImplementingClass : SubInterface + { + public void TestMethod() {} + + public void TestMethod(string param) {} + + public void OtherTestMethod() {} + } + + private sealed class InheritedImplementingClass : ImplementingClass + { + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Support/ControlFlowPointcutTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Support/ControlFlowPointcutTests.cs new file mode 100644 index 00000000..75f6ede7 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Support/ControlFlowPointcutTests.cs @@ -0,0 +1,228 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using NUnit.Framework; +using Spring.Aop.Framework; +using Spring.Aop.Interceptor; +using Spring.Objects; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Unit tests for the ControlFlowPointcut class. + /// + /// Rod Johnson + /// Simon White (.NET) + [TestFixture] + public sealed class ControlFlowPointcutTests + { + [Test] + [Category("Integration")] + public void Matches() + { + SerializablePerson target = new SerializablePerson(); + target.SetAge(27); + ControlFlowPointcut cflow = new ControlFlowPointcut(typeof(One), "GetAge"); + ProxyFactory factory = new ProxyFactory(target); + NopInterceptor nop = new NopInterceptor(); + IPerson proxied = (IPerson) factory.GetProxy(); + factory.AddAdvisor(new DefaultPointcutAdvisor(cflow, nop)); + + // not advised, not under One... + Assert.AreEqual(target.GetAge(), proxied.GetAge()); + Assert.AreEqual(0, nop.Count, "Whoops, appear to be advising when not under One's cflow."); + + // will be advised... + One one = new One(); + Assert.AreEqual(27, one.GetAge(proxied)); + Assert.AreEqual(1, nop.Count, "Not advising when under One's cflow (must be)."); + + // won't be advised... + Assert.AreEqual(target.GetAge(), new One().NoMatch(proxied)); + Assert.AreEqual(1, nop.Count, "Whoops, appear to be advising when under One's cflow scope, BUT NOT under a target method's cflow scope."); + Assert.AreEqual(3, cflow.EvaluationCount, "Pointcut not invoked the correct number of times."); + } + + private sealed class One + { + // attribute is required so that the delegated call is NOT jitted away... + [MethodImpl(MethodImplOptions.NoInlining)] + public int GetAge(IPerson proxied) + { + return proxied.GetAge(); + } + + public int NoMatch(IPerson proxied) + { + return proxied.GetAge(); + } + + // similarly, attribute is required so that the delegated call is NOT jitted away... + [MethodImpl(MethodImplOptions.NoInlining)] + public void Set(IPerson proxied) + { + proxied.SetAge(5); + } + } + + /// + /// Check that we can use a cflow pointcut only in conjunction with + /// a static pointcut: e.g. all setter methods that are invoked under + /// a particular class. + /// + /// + /// This greatly reduces the number of calls to the cflow pointcut, + /// meaning that it's not so prohibitively expensive. + /// + [Test] + [Category("Integration")] + public void SelectiveApplication() + { + SerializablePerson target = new SerializablePerson(); + target.SetAge(27); + NopInterceptor nop = new NopInterceptor(); + ControlFlowPointcut cflow = new ControlFlowPointcut(typeof (One)); + IPointcut settersUnderOne = Pointcuts.Intersection(SetterPointcut.Instance, cflow); + ProxyFactory pf = new ProxyFactory(target); + IPerson proxied = (IPerson) pf.GetProxy(); + pf.AddAdvisor(new DefaultPointcutAdvisor(settersUnderOne, nop)); + + // Not advised, not under One + target.SetAge(16); + Assert.AreEqual(0, nop.Count); + + // Not advised; under One but not a setter + Assert.AreEqual(16, new One().GetAge(proxied)); + Assert.AreEqual(0, nop.Count); + + // Won't be advised + new One().Set(proxied); + Assert.AreEqual(1, nop.Count); + + // We saved most evaluations + Assert.AreEqual(1, cflow.EvaluationCount); + } + + [Test] + public void EvaluationCountIncrementedEvenIfPointcutDoesNotMatch() + { + ControlFlowPointcut cut = new ControlFlowPointcut(typeof(One)); + cut.Matches(null, null, null); // args are ingored in this impl... + Assert.AreEqual(1, cut.EvaluationCount); + cut.Matches(null, null, null); // args are ingored in this impl... + Assert.AreEqual(2, cut.EvaluationCount); + } + + [Test] + public void EvaluationCountIncrementedOnEveryMatch() + { + Type oneType = typeof(One); + ControlFlowPointcut cut = new ControlFlowPointcut(oneType); + MethodInfo method = oneType.GetMethod("GetAge"); + cut.Matches(method, oneType, null); + Assert.AreEqual(1, cut.EvaluationCount); + cut.Matches(method, oneType, null); + Assert.AreEqual(2, cut.EvaluationCount); + } + + [Test] + public void DefaultClassFilterImplAlwaysMatchesRegardless() + { + Type oneType = typeof(One); + ControlFlowPointcut cut = new ControlFlowPointcut(oneType); + ITypeFilter filter = cut.TypeFilter; + Assert.IsTrue(filter.Matches(oneType), + "Must always match regardless of the supplied argument Type."); + Assert.IsTrue(filter.Matches(GetType()), + "Must always match even if the supplied argument Type is not " + + "a match for the Type supplied in the ctor."); + Assert.IsTrue(filter.Matches(null), // args are ingored in this impl... + "Must always match even if the supplied argument Type is null"); + } + + [Test] + public void StaticMethodMatchImplAlwaysMatchesRegardless() + { + Type oneType = typeof(One); + ControlFlowPointcut cut = new ControlFlowPointcut(oneType); + IMethodMatcher filter = cut.MethodMatcher; + MethodInfo method = oneType.GetMethod("GetAge"); + Assert.IsTrue(filter.Matches(method, oneType), + "Must always match regardless of the supplied arguments."); + Assert.IsTrue(filter.Matches(method, GetType()), + "Must always match even if the supplied argument method and Type are not " + + "a match for the name and Type supplied in the ctor."); + Assert.IsTrue(filter.Matches(null, null), // args are ingored in this impl... + "Must always match even if the supplied arguments are null"); + } + + [Test] + public void DynamicMethodMatchWithJustTypeSpecifiedInCtor() + { + ControlFlowPointcut cut = new ControlFlowPointcut(GetType()); + IMethodMatcher filter = cut.MethodMatcher; + Assert.IsTrue(filter.Matches(null, null, null), // args are ingored in this impl... + "Must match - under cflow of Type specified in ctor"); + } + + [Test] + public void DynamicMethodMatchWithTypeAndMethodNameSpecifiedInCtor() + { + ControlFlowPointcut cut = new ControlFlowPointcut( + GetType(), "DynamicMethodMatchWithTypeAndMethodNameSpecifiedInCtor"); + IMethodMatcher filter = cut.MethodMatcher; + Assert.IsTrue(filter.Matches(null, null, null), // args are ingored in this impl... + "Must match - under cflow of Type specified in ctor"); + } + + [Test] + public void DynamicMethodMatchWithTypeAndMethodNameSpecifiedInCtorNoMatch() + { + ControlFlowPointcut cut = new ControlFlowPointcut(GetType(), "KiloRiley"); + IMethodMatcher filter = cut.MethodMatcher; + Assert.IsFalse(filter.Matches(null, null, null), // args are ingored in this impl... + "Must not match - under cflow of Type specified in ctor, but no match on method name."); + } + + #region Helper Classes + + /// + /// Pointcut to catch all methods beginning with 'Set'. + /// + private class SetterPointcut : StaticMethodMatcherPointcut + { + public static SetterPointcut Instance = new SetterPointcut(); + + public override bool Matches(MethodInfo methodBase, Type targetType) + { + return methodBase.Name.StartsWith("Set"); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Support/DelegatingIntroductionInterceptorTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Support/DelegatingIntroductionInterceptorTests.cs new file mode 100644 index 00000000..9b401c2e --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Support/DelegatingIntroductionInterceptorTests.cs @@ -0,0 +1,190 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion +#region Imports +using System; +using System.Collections; + +using NUnit.Framework; +using DotNetMock.Dynamic; + +using AopAlliance.Aop; +using Spring.Aop.Framework; +using Spring.Objects; +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Translation of DelegatingIntroductionInterceptor unit tests to Spring.NET. + /// + /// + /// Spring.NET doesn't have a DelegatingIntroductionInterceptor because it handles + /// introductions without using interception. So all the unit tests show how similar + /// things can be done in Spring.NET. + /// + /// Rod Johnson + /// Choy Rim (.NET) + /// $Id: DelegatingIntroductionInterceptorTests.cs,v 1.6 2004/10/10 08:01:49 gcaprio Exp $ + [TestFixture] + public class DelegatingIntroductionInterceptorTests + { + private static readonly DateTime EXPECTED_TIMESTAMP = new DateTime(2004,8,1); + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void testNullTarget() + { + DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(null, typeof(ITimeStamped)); + } + + public interface ITimeStampedIntroduction: ITimeStamped, IAdvice + { + } + + [Test] + public void testIntroductionInterceptorWithDelegation() + { + TestObject raw = new TestObject(); + Assert.IsTrue(! (raw is ITimeStamped)); + ProxyFactory factory = new ProxyFactory(raw); + + IDynamicMock tsControl = new DynamicMock(typeof(ITimeStampedIntroduction)); + ITimeStampedIntroduction ts = (ITimeStampedIntroduction) tsControl.Object; + tsControl.ExpectAndReturn("TimeStamp", EXPECTED_TIMESTAMP); + + DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(ts); + factory.AddIntroduction(advisor); + + ITimeStamped tsp = (ITimeStamped) factory.GetProxy(); + Assert.IsTrue(tsp.TimeStamp == EXPECTED_TIMESTAMP); + + tsControl.Verify(); + } + + // we have to mark the ISubTimeStamped interface with the IAdvice marker + // in order to use it as an introduction. + public interface ISubTimeStampedIntroduction: ISubTimeStamped, IAdvice + { + } + + public void testIntroductionInterceptorWithInterfaceHierarchy() + { + TestObject raw = new TestObject(); + Assert.IsTrue(! (raw is ISubTimeStamped)); + ProxyFactory factory = new ProxyFactory(raw); + + IDynamicMock tsControl = new DynamicMock(typeof(ISubTimeStampedIntroduction)); + ISubTimeStampedIntroduction ts = (ISubTimeStampedIntroduction) tsControl.Object; + tsControl.ExpectAndReturn("TimeStamp", EXPECTED_TIMESTAMP); + + DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(ts); + // we must add introduction, not an advisor + factory.AddIntroduction(advisor); + + object proxy = factory.GetProxy(); + ISubTimeStamped tsp = (ISubTimeStamped) proxy; + Assert.IsTrue(tsp.TimeStamp == EXPECTED_TIMESTAMP); + + tsControl.Verify(); + } + + public void testIntroductionInterceptorWithSuperInterface() + { + TestObject raw = new TestObject(); + Assert.IsTrue(! (raw is ITimeStamped)); + ProxyFactory factory = new ProxyFactory(raw); + + IDynamicMock tsControl = new DynamicMock(typeof(ISubTimeStampedIntroduction)); + ISubTimeStamped ts = (ISubTimeStamped) tsControl.Object; + tsControl.ExpectAndReturn("TimeStamp", EXPECTED_TIMESTAMP); + + factory.AddIntroduction(0, new DefaultIntroductionAdvisor( + (ISubTimeStampedIntroduction)ts, + typeof(ITimeStamped)) + ); + + ITimeStamped tsp = (ITimeStamped) factory.GetProxy(); + Assert.IsTrue(!(tsp is ISubTimeStamped)); + Assert.IsTrue(tsp.TimeStamp == EXPECTED_TIMESTAMP); + + tsControl.Verify(); + } + + /// + /// test introduction. + /// It must include the IAdvice marker interface to be a + /// valid introduction. + /// + private class Test : ITimeStamped, ITest, IAdvice + { + private DateTime _timestamp; + + public Test(DateTime timestamp) + { + _timestamp = timestamp; + } + public void foo() + { + } + public DateTime TimeStamp + { + get + { + return _timestamp; + } + } + } + + public void testAutomaticInterfaceRecognitionInDelegate() + { + IIntroductionAdvisor ia = new DefaultIntroductionAdvisor(new Test(EXPECTED_TIMESTAMP)); + + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + pf.AddIntroduction(0, ia); + + ITimeStamped ts = (ITimeStamped) pf.GetProxy(); + + Assert.IsTrue(ts.TimeStamp == EXPECTED_TIMESTAMP); + ((ITest) ts).foo(); + + int age = ((ITestObject) ts).Age; + } + + /* + * The rest of the tests in the original tested subclassing the + * DelegatingIntroductionInterceptor. + * + * Since we don't need to subclass anything to make a delegating + * introduction, the rest of the tests are not necessary. + */ + + // must be public to be used for AOP + // AOP creates a new assembly which must have access to the + // interfaces that it intends to expose. + public interface ITest + { + void foo(); + } + + public interface ISubTimeStamped : ITimeStamped + { + } + + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Support/RegularExpressionMethodPointcutAdvisorTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Support/RegularExpressionMethodPointcutAdvisorTests.cs new file mode 100644 index 00000000..11a00b7e --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Support/RegularExpressionMethodPointcutAdvisorTests.cs @@ -0,0 +1,135 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports +using System; + +using Spring.Aop.Framework; +using Spring.Aop.Interceptor; +using Spring.Context.Support; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +using NUnit.Framework; +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Unit tests for RegularExpressionMethodPointcutAdvisorTests. + /// + /// Rod Johnson + /// Simon White (.NET) + [TestFixture] + public class RegularExpressionMethodPointcutAdvisorTests + { + [TestFixtureSetUp] + public void FixtureSetUp() + { + SystemUtils.RegisterLoadedAssemblyResolver(); + } + + /// + /// Basic use case, a single pattern defined. + /// + [Test] + public void SinglePattern() + { + IObjectFactory iof = new XmlObjectFactory(new ReadOnlyXmlTestResource("RegularExpressionSetterTests.xml", GetType())); + IPerson advised = (IPerson) iof.GetObject("SettersAdvised"); + // Interceptor behind regexp advisor + NopInterceptor nop = (NopInterceptor) iof.GetObject("NopInterceptor"); + Assert.AreEqual(0, nop.Count); + + int newAge = 12; + // Not advised + advised.Exceptional(null); + Assert.AreEqual(0, nop.Count); + advised.SetAge(newAge); + Assert.AreEqual(newAge, advised.GetAge()); + // Only setter fired + Assert.AreEqual(1, nop.Count); + } + + /// + /// Multiple patterns defined within a single advisor. + /// + [Test] + public void MultiplePatterns() + { + IObjectFactory iof = new XmlObjectFactory(new ReadOnlyXmlTestResource("RegularExpressionSetterTests.xml", GetType())); + IPerson advised = (IPerson) iof.GetObject("SettersAndReturnsThisAdvised"); + + // Interceptor behind regexp advisor + NopInterceptor nop = (NopInterceptor) iof.GetObject("NopInterceptor"); + Assert.AreEqual(0, nop.Count); + + int newAge = 12; + // Not advised + advised.Exceptional(null); + Assert.AreEqual(0, nop.Count); + + // This is proxied + advised.ReturnsThis(); + Assert.AreEqual(1, nop.Count); + + // Only setter is advised + advised.SetAge(newAge); + Assert.AreEqual(2, nop.Count); + + Assert.AreEqual(newAge, advised.GetAge()); + Assert.AreEqual(2, nop.Count); + } + + [Test] + public void Serialization() + { + IObjectFactory iof = new XmlObjectFactory(new ReadOnlyXmlTestResource("RegularExpressionSetterTests.xml", GetType())); + IPerson p = (IPerson) iof.GetObject("SerializableSettersAdvised"); + // Interceptor behind regexp advisor + NopInterceptor nop = (NopInterceptor) iof.GetObject("NopInterceptor"); + Assert.AreEqual(0, nop.Count); + + int newAge = 12; + // Not advised + Assert.AreEqual(0, p.GetAge()); + Assert.AreEqual(0, nop.Count); + + // This is proxied + p.SetAge(newAge); + Assert.AreEqual(1, nop.Count); + p.SetAge(newAge); + Assert.AreEqual(newAge, p.GetAge()); + // Only setter fired + Assert.AreEqual(2, nop.Count); + + // Serialize and continue... + p = (IPerson) SerializationTestUtils.SerializeAndDeserialize(p); + Assert.AreEqual(newAge, p.GetAge()); + // Remembers count, but we need to get a new reference to nop... + nop = (SerializableNopInterceptor) ((IAdvised) p).Advisors[0].Advice; + Assert.AreEqual(2, nop.Count); + Assert.AreEqual("SerializableSettersAdvised", p.GetName()); + p.SetAge(newAge + 1); + Assert.AreEqual(3, nop.Count); + Assert.AreEqual(newAge + 1, p.GetAge()); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Support/RootTypeFilterTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Support/RootTypeFilterTests.cs new file mode 100644 index 00000000..445935ed --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Support/RootTypeFilterTests.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Unit tests for the RootTypeFilter class. + /// + /// Rick Evans + /// $Id: RootTypeFilterTests.cs,v 1.2 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public sealed class RootTypeFilterTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void InstantiationWithNullRootType() + { + new RootTypeFilter(null); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Support/SdkRegularExpressionMethodPointcutTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Support/SdkRegularExpressionMethodPointcutTests.cs new file mode 100644 index 00000000..b113491f --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Support/SdkRegularExpressionMethodPointcutTests.cs @@ -0,0 +1,139 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports +using System; +using System.Text.RegularExpressions; +using Common.Logging; +using Common.Logging.Simple; +using NUnit.Framework; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Unit tests for the SdkRegularExpressionMethodPointcut class. + /// + /// Dmitriy Kopylenko + /// Rick Evans (.NET) + [TestFixture] + public sealed class SdkRegularExpressionMethodPointcutTests : AbstractRegularExpressionMethodPointcutTests + { + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp() + { + // enable (null appender) logging, to ensure that the logging code is exercised + LogManager.Adapter = new NoOpLoggerFactoryAdapter(); + } + + /// + /// Returns the method pointcut implementation to be tested. + /// + /// The implementation. + protected override AbstractRegularExpressionMethodPointcut GetRegexpMethodPointcut() + { + return new SdkRegularExpressionMethodPointcut(); + } + + [Test] + public void InstantiationViaSerialization() + { + SdkRegularExpressionMethodPointcut initial = new SdkRegularExpressionMethodPointcut(); + initial.Pattern = "Foo"; + SdkRegularExpressionMethodPointcut pcut = (SdkRegularExpressionMethodPointcut) SerializationTestUtils.SerializeAndDeserialize(initial); + Assert.IsNotNull(pcut, "Deserialized instance must (obviously) not be null."); + Assert.AreEqual(initial.Pattern, pcut.Pattern, "Pattern property not deserialized correctly."); + } + + /// + /// This exercises the logger after deserialization. + /// + [Test] + public void TryMatchesAfterSerialization() + { + SdkRegularExpressionMethodPointcut initial = new SdkRegularExpressionMethodPointcut(); + initial.Pattern = "Foo"; + SdkRegularExpressionMethodPointcut pcut = (SdkRegularExpressionMethodPointcut) SerializationTestUtils.SerializeAndDeserialize(initial); + Assert.IsNotNull(pcut, "Deserialized instance must (obviously) not be null."); + Type type = GetType(); + bool isMatch = pcut.Matches(type.GetMethod("ForMatchingPurposesOnly"), type); + Assert.IsFalse(isMatch, "Whoops, should not be matching here at all."); + } + + public void ForMatchingPurposesOnly () + { + } + + [Test] + public void MixedPatternsAndDefaultOptions() + { + Type type = GetType(); + SdkRegularExpressionMethodPointcut pcut = new SdkRegularExpressionMethodPointcut(); + + pcut.DefaultOptions = RegexOptions.None; + pcut.Patterns = new object[] {"forMatching*", new Regex("xyz*", RegexOptions.Compiled)}; + Assert.IsFalse(pcut.Matches(type.GetMethod("ForMatchingPurposesOnly"), type)); + + pcut.DefaultOptions = RegexOptions.IgnoreCase; + pcut.Patterns = new object[] { "forMatching*", new Regex("xyz*", RegexOptions.Compiled) }; + Assert.IsTrue(pcut.Matches(type.GetMethod("ForMatchingPurposesOnly"), type)); + + pcut.DefaultOptions = RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace; + pcut.Patterns = new object[] { "for matc \nhing*", new Regex("xyz*", RegexOptions.Compiled) }; + Assert.IsTrue(pcut.Matches(type.GetMethod("ForMatchingPurposesOnly"), type)); + } + + [ExpectedException(typeof(ArgumentNullException))] + [Test] + public void SetPatternToNull() + { + SdkRegularExpressionMethodPointcut pcut = new SdkRegularExpressionMethodPointcut(); + pcut.Pattern = null; + } + + [ExpectedException(typeof(ArgumentNullException))] + [Test] + public void SetPatternsPluralToNull() + { + SdkRegularExpressionMethodPointcut pcut = new SdkRegularExpressionMethodPointcut(); + pcut.Patterns = null; + } + + [ExpectedException(typeof(ArgumentNullException))] + [Test] + public void SetPatternsPluralToStringArrayWithNullValue() + { + SdkRegularExpressionMethodPointcut pcut = new SdkRegularExpressionMethodPointcut(); + pcut.Patterns = new string[] { null }; + } + + [Test] + public void InstantiationWithSuppliedPattern() + { + SdkRegularExpressionMethodPointcut pcut = new SdkRegularExpressionMethodPointcut("Foo"); + Assert.AreEqual("Foo", pcut.Pattern, "Pattern supplied via the ctor was not set."); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Support/TypeFiltersTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Support/TypeFiltersTests.cs new file mode 100644 index 00000000..5a2fc844 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Support/TypeFiltersTests.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Unit tests for the TypeFilters class. + /// + /// Rod Johnson + /// Choy Rim (.NET) + /// $Id: TypeFiltersTests.cs,v 1.2 2005/07/21 16:01:12 springboy Exp $ + [TestFixture] + public sealed class TypeFiltersTests + { + private ITypeFilter exceptionFilter = new RootTypeFilter(typeof (Exception)); + private ITypeFilter itoFilter = new RootTypeFilter(typeof (ITestObject)); + private ITypeFilter hasRootCauseFilter = new RootTypeFilter(typeof (StackOverflowException)); + + [Test] + public void Union() + { + Assert.IsTrue(exceptionFilter.Matches(typeof (SystemException))); + Assert.IsFalse(exceptionFilter.Matches(typeof (TestObject))); + Assert.IsFalse(itoFilter.Matches(typeof (Exception))); + Assert.IsTrue(itoFilter.Matches(typeof (TestObject))); + ITypeFilter union = TypeFilters.Union(exceptionFilter, itoFilter); + Assert.IsTrue(union.Matches(typeof (SystemException))); + Assert.IsTrue(union.Matches(typeof (TestObject))); + } + + [Test] + public void Intersection() + { + Assert.IsTrue(exceptionFilter.Matches(typeof (SystemException))); + Assert.IsTrue(hasRootCauseFilter.Matches(typeof (StackOverflowException))); + + ITypeFilter intersection = TypeFilters.Intersection(exceptionFilter, hasRootCauseFilter); + Assert.IsFalse(intersection.Matches(typeof (SystemException))); + Assert.IsFalse(intersection.Matches(typeof (TestObject))); + Assert.IsTrue(intersection.Matches(typeof (StackOverflowException))); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Target/EmptyTargetSourceTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Target/EmptyTargetSourceTests.cs new file mode 100644 index 00000000..048a63ba --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Target/EmptyTargetSourceTests.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// Unit tests for the EmptyTargetSource class. + /// + /// Rick Evans + /// $Id: EmptyTargetSourceTests.cs,v 1.2 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public sealed class EmptyTargetSourceTests + { + [Test] + public void Deserialization() + { + ITargetSource deserializedVersion + = (ITargetSource) SerializationTestUtils.SerializeAndDeserialize( + EmptyTargetSource.Empty); + Assert.IsTrue(Object.ReferenceEquals(EmptyTargetSource.Empty, deserializedVersion), + "Singleton instance not being deserialized correctly"); + } + + [Test] + public void IsSerializable() + { + Assert.IsTrue(SerializationTestUtils.IsSerializable(EmptyTargetSource.Empty), + "EmptyTargetSource.Empty must be serializable."); + } + + [Test] + public void IsStatic() + { + Assert.IsTrue(EmptyTargetSource.Empty.IsStatic, "Must be static."); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/Target/HotSwappableTargetSourceTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Target/HotSwappableTargetSourceTests.cs new file mode 100644 index 00000000..76141c8e --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Target/HotSwappableTargetSourceTests.cs @@ -0,0 +1,124 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// Unit tests for the HotSwappableTargetSource class. + /// + /// Rod Johnson + /// Federico Spinazzi (.NET) + /// $Id: HotSwappableTargetSourceTests.cs,v 1.4 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public sealed class HotSwappableTargetSourceTests + { + /// Initial count value set in Object factory XML + private const int INITIAL_COUNT = 10; + + private IObjectFactory ObjectFactory; + + [SetUp] + public void SetUp() + { + this.ObjectFactory = new XmlObjectFactory( + new ReadOnlyXmlTestResource("hotSwapTests.xml", GetType())); + } + + /// + /// We must simulate container shutdown, which should clear threads. + /// + [TearDown] + public void TearDown() + { + this.ObjectFactory.Dispose(); + } + + [Test] + [Category("Integration")] + public void ValidSwaps() + { + ISideEffectObject target1 = (ISideEffectObject) ObjectFactory.GetObject("target1"); + ISideEffectObject target2 = (ISideEffectObject) ObjectFactory.GetObject("target2"); + + ISideEffectObject proxied = (ISideEffectObject) ObjectFactory.GetObject("swappable"); + // assertEquals(target1, ((Advised) proxied).getTarget()); + Assert.AreEqual(target1.Count, proxied.Count); + proxied.doWork(); + Assert.AreEqual(INITIAL_COUNT + 1, proxied.Count); + + HotSwappableTargetSource swapper = (HotSwappableTargetSource) ObjectFactory.GetObject("swapper"); + Object old = swapper.Swap(target2); + Assert.AreEqual(target1, old, "Correct old target was returned"); + + // TODO should be able to make this assertion: need to fix target handling + // in AdvisedSupport + //assertEquals(target2, ((Advised) proxied).getTarget()); + + Assert.AreEqual(20, proxied.Count); + proxied.doWork(); + Assert.AreEqual(21, target2.Count); + + // Swap it back + swapper.Swap(target1); + Assert.AreEqual(target1.Count, proxied.Count); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void RejectsSwapToNull() + { + HotSwappableTargetSource source = new HotSwappableTargetSource(null); + source.Swap(null); + } + + [Test] + public void SwapDoesIndeedReturnTheOldTarget() + { + HotSwappableTargetSource source = new HotSwappableTargetSource(this); + object foo = source.Swap(new SideEffectObject()); + Assert.IsTrue(object.ReferenceEquals(this, foo), + "Swap() is not returning the old target."); + } + + [Test] + public void InstantiationWithNullIsOk() + { + new HotSwappableTargetSource(null); + } + + [Test] + public void InstantiationWithInitialTarget() + { + HotSwappableTargetSource source = new HotSwappableTargetSource(this); + object foo = source.GetTarget(); + Assert.IsTrue(object.ReferenceEquals(this, foo), + "Ctor is not storing the supplied target."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Target/PrototypeTargetSourceTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Target/PrototypeTargetSourceTests.cs new file mode 100644 index 00000000..c0d21c45 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Target/PrototypeTargetSourceTests.cs @@ -0,0 +1,141 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using Common.Logging.Simple; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// Unit tests for the PrototypeTargetSource class. + /// + /// Rod Johnson + /// Federico Spinazzi + /// $Id: PrototypeTargetSourceTests.cs,v 1.8 2007/07/28 07:33:22 markpollack Exp $ + [TestFixture] + public sealed class PrototypeTargetSourceTests + { + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp() + { + // enable (null appender) logging, just to ensure that the logging code is correct + LogManager.Adapter = new NoOpLoggerFactoryAdapter(); + } + + /// + /// Test that multiple invocations of the prototype object will result + /// in no change to visible state, as a new instance is used. + /// With the singleton, there will be change. + /// + [Test] + public void PrototypeAndSingletonBehaveDifferently() + { + int initialCount = 10; + IObjectFactory of = new XmlObjectFactory(new ReadOnlyXmlTestResource("prototypeTargetSourceTests.xml", GetType())); + ISideEffectObject singleton = (ISideEffectObject) of.GetObject("singleton"); + Assert.AreEqual(initialCount, singleton.Count); + singleton.doWork(); + Assert.AreEqual(initialCount + 1, singleton.Count); + + ISideEffectObject prototype = (ISideEffectObject) of.GetObject("prototype"); + Assert.AreEqual(initialCount, prototype.Count); + singleton.doWork(); + Assert.AreEqual(initialCount, prototype.Count); + + ISideEffectObject prototypeByName = (ISideEffectObject) of.GetObject("prototypeByName"); + Assert.AreEqual(initialCount, prototypeByName.Count); + singleton.doWork(); + Assert.AreEqual(initialCount, prototypeByName.Count); + } + + [Test] + public void TargetType() + { + SideEffectObject target = new SideEffectObject(); + IDynamicMock mock = new DynamicMock(typeof (IObjectFactory)); + mock.ExpectAndReturn("IsPrototype", true); + mock.ExpectAndReturn("GetType", typeof(SideEffectObject)); + PrototypeTargetSource source = new PrototypeTargetSource(); + source.ObjectFactory = (IObjectFactory) mock.Object; + Assert.AreEqual(target.GetType(), source.TargetType, "Wrong TargetType being returned."); + mock.Verify(); + } + + [Test] + public void IsStatic() + { + PrototypeTargetSource source = new PrototypeTargetSource(); + Assert.IsFalse(source.IsStatic, "Must not be static."); + } + + [Test] + public void WithNonSingletonTargetObject() + { + IDynamicMock mock = new DynamicMock(typeof (IObjectFactory)); + const string objectName = "Foo"; + mock.ExpectAndReturn("IsPrototype", false, objectName); + PrototypeTargetSource source = new PrototypeTargetSource(); + source.TargetObjectName = objectName; + try + { + source.ObjectFactory = (IObjectFactory) mock.Object; + Assert.Fail("Should have thrown an ObjectDefinitionStoreException by this point."); + } + catch (ObjectDefinitionStoreException) + { + mock.Verify(); + } + } + + [Test] + public void GetTarget() + { + SideEffectObject target = new SideEffectObject(); + IDynamicMock mock = new DynamicMock(typeof (IObjectFactory)); + mock.ExpectAndReturn("IsPrototype", true); + mock.ExpectAndReturn("GetObject", target); + PrototypeTargetSource source = new PrototypeTargetSource(); + source.ObjectFactory = (IObjectFactory) mock.Object; + Assert.IsTrue(object.ReferenceEquals(source.GetTarget(), target), + "Initial target source reference not being returned by GetTarget()."); + mock.Verify(); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void AfterPropertiesSetWithoutTargetObjectNameBeingSet() + { + PrototypeTargetSource source = new PrototypeTargetSource(); + source.AfterPropertiesSet(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Target/SimplePoolTargetSourceTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Target/SimplePoolTargetSourceTests.cs new file mode 100644 index 00000000..9426db2d --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Target/SimplePoolTargetSourceTests.cs @@ -0,0 +1,96 @@ +using NUnit.Framework; +using Spring.Aop.Target; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +namespace Spring.Aop.Target +{ + + /// Tests for pooling invoker interceptor + /// TODO need to make these tests stronger: it's hard to + /// make too many assumptions about a pool + /// + /// Rod Johnson + /// Federico Spinazzi (.Net) + /// $Id: SimplePoolTargetSourceTests.cs,v 1.3 2006/02/08 08:31:05 aseovic Exp $ + /// + [TestFixture] + public class SimplePoolTargetSourceTests + { + + /// Initial count value set in Object factory XML + private const int INITIAL_COUNT = 10; + + private XmlObjectFactory objectFactory; + + [SetUp] + public void SetUp() + { + objectFactory = new XmlObjectFactory(new ReadOnlyXmlTestResource("simplePoolTests.xml", GetType())); + } + + /// We must simulate container shutdown, which should clear threads. + [TearDown] + public void TearDown() + { + // Will call pool.close() + this.objectFactory.Dispose(); + } + + private void Functionality(System.String name) + { + ISideEffectObject pooled = (ISideEffectObject) objectFactory.GetObject(name); + Assert.AreEqual(INITIAL_COUNT, pooled.Count); + pooled.doWork(); + Assert.AreEqual(INITIAL_COUNT + 1, pooled.Count); + + pooled = (ISideEffectObject) objectFactory.GetObject(name); + // Just check that it works--we can't make assumptions + // about the count + pooled.doWork(); + //Assert.AreEqual(INITIAL_COUNT + 1, pooled.Count ); + } + + [Test] + public virtual void Functionality() + { + Functionality("pooled"); + } + + [Test] + public virtual void FunctionalityWithNoInterceptors() + { + Functionality("pooledNoInterceptors"); + } + + [Test] + public virtual void ConfigMixin() + { + ISideEffectObject pooled = (ISideEffectObject) objectFactory.GetObject("pooledWithMixin"); + Assert.AreEqual(INITIAL_COUNT, pooled.Count); + PoolingConfig conf = (PoolingConfig) objectFactory.GetObject("pooledWithMixin"); + // TODO one invocation from setup + // assertEquals(1, conf.getInvocations()); + pooled.doWork(); + // assertEquals("No objects active", 0, conf.getActive()); + Assert.AreEqual(25, conf.MaxSize, "Correct target source"); + // assertTrue("Some free", conf.getFree() > 0); + //assertEquals(2, conf.getInvocations()); + Assert.AreEqual(25, conf.MaxSize); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Target/SingletonTargetSourceTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Target/SingletonTargetSourceTests.cs new file mode 100644 index 00000000..1ead5fa5 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Target/SingletonTargetSourceTests.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Aop.Target +{ + /// + /// Unit tests for the SingletonTargetSource class. + /// + /// Rick Evans + /// $Id: SingletonTargetSourceTests.cs,v 1.2 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public sealed class SingletonTargetSourceTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void InstantiationWithNullTargetSource() + { + new SingletonTargetSource(null); + } + + [Test] + public void TargetType() + { + SingletonTargetSource source = new SingletonTargetSource(this); + Assert.AreEqual(GetType(), source.TargetType, "Wrong TargetType being returned."); + } + + [Test] + public void IsStatic() + { + SingletonTargetSource source = new SingletonTargetSource(this); + Assert.IsTrue(source.IsStatic, "Must be static."); + } + + [Test] + public void GetTarget() + { + SingletonTargetSource source = new SingletonTargetSource(this); + Assert.IsTrue(object.ReferenceEquals(source.GetTarget(), this), + "Same target source reference not being returned by GetTarget()."); + } + + [Test] + public void EqualsSameInstance() + { + SingletonTargetSource lhs = new SingletonTargetSource(this); + SingletonTargetSource rhs = new SingletonTargetSource(this); + Assert.AreEqual(lhs, rhs, "Equals() not correct for same instance comparison."); + } + + [Test] + public void EqualsNullInstance() + { + SingletonTargetSource lhs = new SingletonTargetSource(this); + Assert.IsFalse(lhs.Equals(null), "Equals() not correct for null instance comparison."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Target/ThreadLocalTargetSourceTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Target/ThreadLocalTargetSourceTests.cs new file mode 100644 index 00000000..90898a57 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Target/ThreadLocalTargetSourceTests.cs @@ -0,0 +1,158 @@ +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System; +using System.IO; +using System.Reflection; +using System.Threading; +using Common.Logging; +using NUnit.Framework; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; + +namespace Spring.Aop.Target +{ + /// Rod Johnson + /// Federico Spinazzi + /// $Id: ThreadLocalTargetSourceTests.cs,v 1.5 2006/11/13 07:04:51 markpollack Exp $ + /// + [TestFixture] + public class ThreadLocalTargetSourceTests + { + /// Initial count value set in Object factory XML + private const int INITIAL_COUNT = 10; + + private XmlObjectFactory ObjectFactory; + + private ILog log; + + [SetUp] + public void SetUp () + { + this.ObjectFactory = new XmlObjectFactory ( + new ReadOnlyXmlTestResource ("threadLocalTests.xml", GetType ())); + //TODO-LOGGING XmlConfigurator.Configure (new FileInfo ("Spring.Aop.Tests.dll.config")); + log = LogManager.GetLogger (MethodBase.GetCurrentMethod ().DeclaringType); + } + + /// We must simulate container shutdown, which should clear threads. + [TearDown] + public void TearDown () + { + this.ObjectFactory.Dispose (); + } + + /// Check we can use two different ThreadLocalTargetSources + /// managing objects of different types without them interfering + /// with one another. + /// + [Test] + public virtual void UseDifferentManagedInstancesInSameThread () + { + ISideEffectObject apartment = (ISideEffectObject) ObjectFactory.GetObject ("apartment"); + Assert.AreEqual (INITIAL_COUNT, apartment.Count); + apartment.doWork (); + Assert.AreEqual (INITIAL_COUNT + 1, apartment.Count); + + ITestObject test = (ITestObject) ObjectFactory.GetObject ("threadLocal2"); + Assert.AreEqual ("Rod", test.Name); + Assert.AreEqual ("Kerry", test.Spouse.Name); + } + + [Test] + public virtual void ReuseInSameThread () + { + ISideEffectObject apartment = (ISideEffectObject) ObjectFactory.GetObject ("apartment"); + Assert.AreEqual (INITIAL_COUNT, apartment.Count); + apartment.doWork (); + Assert.AreEqual (INITIAL_COUNT + 1, apartment.Count); + + apartment = (ISideEffectObject) ObjectFactory.GetObject ("apartment"); + Assert.AreEqual (INITIAL_COUNT + 1, apartment.Count); + } + + /// Relies on introduction + /// + /// + [Test] + public virtual void CanGetStatsViaMixinIfThereIsAnInterceptorTakingCareOfThem () + { + IThreadLocalTargetSourceStats stats = (IThreadLocalTargetSourceStats) ObjectFactory.GetObject ("apartment"); + Assert.AreEqual (0, stats.Invocations); + ISideEffectObject apartment = (ISideEffectObject) ObjectFactory.GetObject ("apartment"); + apartment.doWork (); + Assert.AreEqual (1, stats.Invocations); + Assert.AreEqual (0, stats.Hits); + apartment.doWork (); + Assert.AreEqual (2, stats.Invocations); + Assert.AreEqual (1, stats.Hits); + // Only one thread so only one object can have been bound + Assert.AreEqual (1, stats.Objects); + } + + + public class Runner + { + private ILog log = LogManager.GetLogger (MethodBase.GetCurrentMethod ().DeclaringType); + private ThreadLocalTargetSourceTests factory; + public ISideEffectObject mine; + + public Runner (ThreadLocalTargetSourceTests enclosingInstance) + { + this.factory = enclosingInstance; + } + + public virtual void Run () + { + log.Debug ("getting object"); + this.mine = (ISideEffectObject) factory.ObjectFactory.GetObject ("apartment"); + log.Debug (String.Format ("got object; hash code: {0}", this.mine.GetHashCode ())); + Assert.AreEqual (ThreadLocalTargetSourceTests.INITIAL_COUNT, mine.Count); + mine.doWork (); + Assert.AreEqual (ThreadLocalTargetSourceTests.INITIAL_COUNT + 1, mine.Count); + } + } + + [Test] + public virtual void NewThreadHasOwnInstance () + { + ISideEffectObject apartment = (ISideEffectObject) ObjectFactory.GetObject ("apartment"); + log.Debug (String.Format ("got object; hash code: {0}", apartment.GetHashCode ())); + Assert.AreEqual (INITIAL_COUNT, apartment.Count); + apartment.doWork (); + apartment.doWork (); + apartment.doWork (); + Assert.AreEqual (INITIAL_COUNT + 3, apartment.Count); + + Runner r = new Runner (this); + Thread t = new Thread (new ThreadStart (r.Run)); + t.Start (); + t.Join (); + + Assert.IsNotNull (r); + + // Check it didn't affect the other thread's copy + Assert.AreEqual (INITIAL_COUNT + 3, apartment.Count); + + // When we use other thread's copy in this thread + // it should behave like ours + Assert.AreEqual (INITIAL_COUNT + 3, r.mine.Count); + + // Bound to two threads + Assert.AreEqual (2, ((IThreadLocalTargetSourceStats) apartment).Objects); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/TrueMethodMatcherTests.cs b/test/Spring/Spring.Aop.Tests/Aop/TrueMethodMatcherTests.cs new file mode 100644 index 00000000..0522e392 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/TrueMethodMatcherTests.cs @@ -0,0 +1,78 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; +using Spring.Util; + +#endregion + +namespace Spring.Aop +{ + /// + /// Unit tests for the TrueMethodMatcher class. + /// + /// Rick Evans + /// $Id: TrueMethodMatcherTests.cs,v 1.3 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public sealed class TrueMethodMatcherTests + { + [Test] + public void Deserialization() + { + IMethodMatcher deserializedVersion + = (IMethodMatcher) SerializationTestUtils.SerializeAndDeserialize( + TrueMethodMatcher.True); + Assert.IsTrue(Object.ReferenceEquals(TrueMethodMatcher.True, deserializedVersion), + "Singleton instance not being deserialized correctly"); + } + + [Test] + public void IsSerializable() + { + Assert.IsTrue(SerializationTestUtils.IsSerializable(TrueMethodMatcher.True), + "TrueMethodMatcher must be serializable."); + } + + [Test] + public void AlwaysMatchesEvenOnNullArguments() + { + Assert.IsTrue(TrueMethodMatcher.True.Matches(null, null), + "Must always match (return true)."); + } + + [Test] + public void AlwaysMatches() + { + Assert.IsTrue(TrueMethodMatcher.True.Matches( + (MethodInfo) MethodBase.GetCurrentMethod(), GetType()), + "Must always match (return true)."); + } + + [Test] + public void IsRuntime() + { + Assert.IsFalse(TrueMethodMatcher.True.IsRuntime, "Must NOT be runtime."); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/TruePointcutTests.cs b/test/Spring/Spring.Aop.Tests/Aop/TruePointcutTests.cs new file mode 100644 index 00000000..92710dc8 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/TruePointcutTests.cs @@ -0,0 +1,57 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; +using Spring.Util; + +#endregion + +namespace Spring.Aop +{ + /// + /// Unit tests for the TruePointcut class. + /// + /// Rick Evans + /// $Id: TruePointcutTests.cs,v 1.2 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public sealed class TruePointcutTests + { + [Test] + public void Deserialization() + { + IPointcut deserializedVersion + = (IPointcut) SerializationTestUtils.SerializeAndDeserialize( + TruePointcut.True); + Assert.IsTrue(Object.ReferenceEquals(TruePointcut.True, deserializedVersion), + "Singleton instance not being deserialized correctly"); + } + + [Test] + public void IsSerializable() + { + Assert.IsTrue(SerializationTestUtils.IsSerializable(TruePointcut.True), + "TruePointcut must be serializable."); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aop/TrueTypeFilterTests.cs b/test/Spring/Spring.Aop.Tests/Aop/TrueTypeFilterTests.cs new file mode 100644 index 00000000..79848407 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/TrueTypeFilterTests.cs @@ -0,0 +1,75 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Util; + +#endregion + +namespace Spring.Aop +{ + /// + /// Unit tests for the TrueTypeFilter class. + /// + /// Rick Evans + /// $Id: TrueTypeFilterTests.cs,v 1.3 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public sealed class TrueTypeFilterTests + { + [Test] + public void Deserialization() + { + ITypeFilter deserializedVersion + = (ITypeFilter) SerializationTestUtils.SerializeAndDeserialize( + TrueTypeFilter.True); + Assert.IsTrue(Object.ReferenceEquals(TrueTypeFilter.True, deserializedVersion), + "Singleton instance not being deserialized correctly"); + } + + [Test] + public void IsSerializable() + { + Assert.IsTrue(SerializationTestUtils.IsSerializable(TrueTypeFilter.True), + "TrueClassFilter must be serializable."); + } + + [Test] + public void AlwaysMatchesEvenOnNullArgument() + { + Assert.IsTrue(TrueTypeFilter.True.Matches(null), + "Must always match (return true)."); + } + + [Test] + public void AlwaysMatches() + { + Assert.IsTrue(TrueTypeFilter.True.Matches(GetType()), + "Must always match (return true)."); + } + [Test] + public void ToStringAlwaysTrue() + { + Assert.AreEqual("TrueTypeFilter.True", TrueTypeFilter.True.ToString() ); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/AopExceptionTests.cs b/test/Spring/Spring.Aop.Tests/AopExceptionTests.cs new file mode 100644 index 00000000..0637d707 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/AopExceptionTests.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using NUnit.Framework; + +#endregion + +namespace Spring +{ + /// + /// Unit tests for all of the exception classes in the Spring.Aop library... + /// + /// Rick Evans + /// $Id: AopExceptionTests.cs,v 1.3 2006/04/09 07:19:04 markpollack Exp $ + [TestFixture] + public sealed class AopExceptionTests : ExceptionsTest + { + [TestFixtureSetUp] + public void FixtureSetUp () + { + AssemblyToCheck = Assembly.GetAssembly (typeof (Spring.Aop.TruePointcut)); + } + } +} diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheAspectIntegrationTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheAspectIntegrationTests.cs new file mode 100644 index 00000000..41ec6a54 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheAspectIntegrationTests.cs @@ -0,0 +1,140 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using NUnit.Framework; +using Spring.Aop.Framework; +using Spring.Caching; +using Spring.Context; +using Spring.Context.Support; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Unit tests for the CacheParameterAdvice class. + /// + /// Aleksandar Seovic + /// $Id: CacheAspectIntegrationTests.cs,v 1.4 2007/08/03 14:38:39 markpollack Exp $ + [TestFixture] + public sealed class CacheAspectIntegrationTests + { + private IApplicationContext context; + private CacheAspect cacheAspect; + private ICache cache; + + [SetUp] + public void SetUp() + { + cache = new NonExpiringCache(); + + context = new XmlApplicationContext(); + ((IConfigurableApplicationContext) context).ObjectFactory.RegisterSingleton("inventors", cache); + + cacheAspect = new CacheAspect(); + cacheAspect.ApplicationContext = context; + } + + [Test] + public void TestCaching() + { + ProxyFactory pf = new ProxyFactory(new InventorStore()); + pf.AddAdvisors(cacheAspect); + + IInventorStore store = (IInventorStore) pf.GetProxy(); + + Assert.AreEqual(0, cache.Count); + + IList inventors = store.GetAll(); + Assert.AreEqual(2, cache.Count); + + store.Delete((Inventor) inventors[0]); + Assert.AreEqual(1, cache.Count); + + Inventor tesla = store.Load("Nikola Tesla"); + Assert.AreEqual(2, cache.Count); + + store.Save(tesla); + Assert.AreEqual(2, cache.Count); + Assert.AreEqual("Serbian", ((Inventor)cache.Get("Nikola Tesla")).Nationality); + + store.DeleteAll(); + Assert.AreEqual(0, cache.Count); + } + } + + #region Inner Class : CacheParameterTarget + + public interface IInventorStore + { + IList GetAll(); + Inventor Load(string name); + void Save(Inventor inventor); + void Delete(Inventor inventor); + void DeleteAll(); + } + + public sealed class InventorStore : IInventorStore + { + private IDictionary inventors = new ListDictionary(); + + public InventorStore() + { + Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), null); + Inventor pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), null); + inventors.Add("Nikola Tesla", tesla); + inventors.Add("Mihajlo Pupin", pupin); + } + + [CacheResultItems("inventors", "Name")] + public IList GetAll() + { + return new ArrayList(inventors.Values); + } + + [CacheResult("inventors", "#name")] + public Inventor Load(string name) + { + return (Inventor) inventors[name]; + } + + public void Save([CacheParameter("inventors", "Name")] Inventor inventor) + { + inventor.Nationality = "Serbian"; + } + + [InvalidateCache("inventors", Keys = "#inventor.Name")] + public void Delete(Inventor inventor) + { + } + + [InvalidateCache("inventors")] + public void DeleteAll() + { + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheParameterAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheParameterAdviceTests.cs new file mode 100644 index 00000000..890768bc --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheParameterAdviceTests.cs @@ -0,0 +1,143 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Caching; +using Spring.Context; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Unit tests for the CacheParameterAdvice class. + /// + /// Aleksandar Seovic + /// $Id: CacheParameterAdviceTests.cs,v 1.6 2007/04/01 15:05:33 bbaia Exp $ + [TestFixture] + public sealed class CacheParameterAdviceTests + { + private IDynamicMock mockContext; + private CacheParameterAdvice advice; + private ICache cache; + + [SetUp] + public void SetUp() + { + mockContext = new DynamicMock(typeof (IApplicationContext)); + + advice = new CacheParameterAdvice(); + advice.ApplicationContext = (IApplicationContext) mockContext.Object; + + cache = new NonExpiringCache(); + } + + [Test] + public void TestSimpleParameterCaching() + { + MethodInfo method = typeof(SimpleCacheParameterTarget).GetMethod("Save"); + object[] args = new object[] {new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian")}; + + ExpectCacheInstanceRetrieval("cache", cache); + + // parameter value should be added to cache + advice.AfterReturning(null, method, args, null); + Assert.AreEqual(1, cache.Count); + Assert.AreEqual(args[0], cache.Get("Nikola Tesla")); + + mockContext.Verify(); + } + + [Test] + public void TestMultipleParameterCaching() + { + MethodInfo method = typeof(MultipleCacheParameterTarget).GetMethod("Save"); + object[] args = new object[] { new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian") }; + + ExpectCacheInstanceRetrieval("cache", cache); + ExpectCacheInstanceRetrieval("cache", cache); + + // parameter value should be added to both cache + advice.AfterReturning(null, method, args, null); + Assert.AreEqual(2, cache.Count); + Assert.AreEqual(args[0], cache.Get("Nikola Tesla")); + Assert.AreEqual(args[0], cache.Get("Serbian")); + + mockContext.Verify(); + } + + [Test] + public void TestConditionParameterCaching() + { + MethodInfo method = typeof(ConditionCacheParameterTarget).GetMethod("Save"); + object[] args = new object[] { new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian") }; + + // parameter value should not be added to cache + advice.AfterReturning(null, method, args, null); + Assert.AreEqual(0, cache.Count); + + mockContext.Verify(); + } + + #region Helper methods + + private void ExpectCacheInstanceRetrieval(string cacheName, ICache cache) + { + mockContext.ExpectAndReturn("GetObject", cache, cacheName); + } + + #endregion + } + + #region Inner Class : CacheParameterTarget + + public interface ICacheParameterTarget + { + void Save(Inventor inventor); + } + + public sealed class SimpleCacheParameterTarget : ICacheParameterTarget + { + public void Save([CacheParameter("cache", "Name")] Inventor inventor) + { + } + } + + public sealed class MultipleCacheParameterTarget : ICacheParameterTarget + { + public void Save([CacheParameter("cache", "Name")][CacheParameter("cache", "Nationality")] Inventor inventor) + { + } + } + + public sealed class ConditionCacheParameterTarget : ICacheParameterTarget + { + public void Save([CacheParameter("cache", "Name", Condition = "Nationality == 'French'")] Inventor inventor) + { + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs new file mode 100644 index 00000000..b0076038 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs @@ -0,0 +1,394 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Reflection; +using AopAlliance.Intercept; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Caching; +using Spring.Context; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Unit tests for the CacheResultAdvice class. + /// + /// Aleksandar Seovic + /// $Id: CacheResultAdviceTests.cs,v 1.4 2007/08/22 20:16:51 oakinger Exp $ + [TestFixture] + public sealed class CacheResultAdviceTests + { + private IDynamicMock mockInvocation; + private IDynamicMock mockContext; + private CacheResultAdvice advice; + private ICache resultCache; + private ICache itemCache; + + [SetUp] + public void SetUp() + { + mockInvocation = new DynamicMock(typeof(IMethodInvocation)); + mockContext = new DynamicMock(typeof(IApplicationContext)); + + advice = new CacheResultAdvice(); + advice.ApplicationContext = (IApplicationContext) mockContext.Object; + + resultCache = new NonExpiringCache(); + itemCache = new NonExpiringCache(); + } + + /// + /// Change History: + /// 2007-08-22 (oakinger): changed behaviour to cache null values as well + /// + [Test] + public void CacheResultOfMethodThatReturnsNull() + { + MethodInfo method = typeof(CacheResultTarget).GetMethod("ReturnsNothing"); + object expectedReturnValue = null; + + ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, null); + ExpectCacheInstanceRetrieval("results", resultCache); + ExpectCallToProceed(expectedReturnValue); + + // check that the null retVal is cached as well - it might be + // the result of an expensive webservice/database call etc. + object returnValue = advice.Invoke((IMethodInvocation) mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, returnValue); + Assert.AreEqual(1, resultCache.Count); + + mockInvocation.Verify(); + mockContext.Verify(); + } + + [Test] + public void CacheResultOfMethodThatReturnsObject() + { + MethodInfo method = typeof(CacheResultTarget).GetMethod("ReturnsScalar"); + object expectedReturnValue = CacheResultTarget.Scalar; + + ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, null); + ExpectCacheInstanceRetrieval("results", resultCache); + ExpectCallToProceed(expectedReturnValue); + + // return value should be added to cache + object returnValue = advice.Invoke((IMethodInvocation) mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, returnValue); + Assert.AreEqual(1, resultCache.Count); + + // and again, but without Proceed()... + ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, null); + ExpectCacheInstanceRetrieval("results", resultCache); + + // cached value should be returned + object cachedValue = advice.Invoke((IMethodInvocation) mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, cachedValue); + Assert.AreEqual(1, resultCache.Count); + Assert.AreSame(returnValue, cachedValue); + + mockInvocation.Verify(); + mockContext.Verify(); + } + + [Test] + public void CacheResultOfMethodThatReturnsCollection() + { + MethodInfo method = typeof(CacheResultTarget).GetMethod("ReturnsCollection"); + object expectedReturnValue = new object[] {"one", "two", "three"}; + + ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, 5, expectedReturnValue); + ExpectCacheInstanceRetrieval("results", resultCache); + ExpectCallToProceed(new object[] { "one", "two", "three" }); + + // return value should be added to cache + object returnValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, returnValue); + Assert.AreEqual(1, resultCache.Count); + + // and again, but without Proceed()... + ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, 5, expectedReturnValue); + ExpectCacheInstanceRetrieval("results", resultCache); + + // cached value should be returned + object cachedValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, cachedValue); + Assert.AreNotSame(expectedReturnValue, cachedValue); + Assert.AreEqual(expectedReturnValue, resultCache.Get(5)); + Assert.AreEqual(1, resultCache.Count); + Assert.AreSame(returnValue, cachedValue); + Assert.AreSame(cachedValue, resultCache.Get(5)); + + mockInvocation.Verify(); + mockContext.Verify(); + } + + [Test] + public void CacheResultAndItemsOfMethodThatReturnsCollection() + { + MethodInfo method = typeof(CacheResultTarget).GetMethod("ReturnsCollectionAndItems"); + object expectedReturnValue = new object[] { "one", "two", "three" }; + + ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, 5, expectedReturnValue); + ExpectCacheInstanceRetrieval("results", resultCache); + ExpectCallToProceed(new object[] { "one", "two", "three" }); + ExpectCacheInstanceRetrieval("items", itemCache); + + // return value should be added to result cache and each item to item cache + object returnValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, returnValue); + Assert.AreEqual(1, resultCache.Count); + Assert.AreEqual(3, itemCache.Count); + + // and again, but without Proceed() and item cache access... + ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, 5, expectedReturnValue); + ExpectCacheInstanceRetrieval("results", resultCache); + + // cached value should be returned + object cachedValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, cachedValue); + Assert.AreNotSame(expectedReturnValue, cachedValue); + Assert.AreEqual(expectedReturnValue, resultCache.Get(5)); + Assert.AreEqual(1, resultCache.Count); + Assert.AreEqual(3, itemCache.Count); + Assert.AreSame(returnValue, cachedValue); + Assert.AreSame(cachedValue, resultCache.Get(5)); + + mockInvocation.Verify(); + mockContext.Verify(); + } + + [Test] + public void CacheOnlyItemsOfMethodThatReturnsCollection() + { + MethodInfo method = typeof(CacheResultTarget).GetMethod("ReturnsItems"); + object expectedReturnValue = new object[] { "one", "two", "three" }; + + ExpectAttributeRetrieval(method); + ExpectCallToProceed(new object[] { "one", "two", "three" }); + ExpectCacheInstanceRetrieval("items", itemCache); + + // return value should be added to result cache and each item to item cache + object returnValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, returnValue); + Assert.AreEqual(0, resultCache.Count); + Assert.AreEqual(3, itemCache.Count); + + // and again, but without Proceed() and item cache access... + ExpectAttributeRetrieval(method); + ExpectCallToProceed(new object[] { "one", "two", "three" }); + ExpectCacheInstanceRetrieval("items", itemCache); + + // new return value should be returned + object newReturnValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, newReturnValue); + Assert.AreEqual(0, resultCache.Count); + Assert.AreEqual(3, itemCache.Count); + Assert.AreEqual("two", itemCache.Get("two")); + Assert.AreNotSame(returnValue, newReturnValue); + + mockInvocation.Verify(); + mockContext.Verify(); + } + + [Test] + public void CacheOnlyItemsOfMethodThatReturnsCollectionWithinTwoDifferentCaches() + { + MethodInfo method = typeof(CacheResultTarget).GetMethod("MultipleCacheResultItems"); + object expectedReturnValue = new object[] { "one", "two", "three" }; + + ExpectAttributeRetrieval(method); + ExpectCallToProceed(new object[] { "one", "two", "three" }); + ExpectCacheInstanceRetrieval("items", itemCache); + ExpectCacheInstanceRetrieval("items", itemCache); + + // return value should be added to result cache and each item to item cache + object returnValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, returnValue); + Assert.AreEqual(0, resultCache.Count); + Assert.AreEqual(6, itemCache.Count); + + // and again, but without Proceed() and item cache access... + ExpectAttributeRetrieval(method); + ExpectCallToProceed(new object[] { "one", "two", "three" }); + ExpectCacheInstanceRetrieval("items", itemCache); + ExpectCacheInstanceRetrieval("items", itemCache); + + // new return value should be returned + object newReturnValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, newReturnValue); + Assert.AreEqual(0, resultCache.Count); + Assert.AreEqual(6, itemCache.Count); + Assert.AreEqual("two", itemCache.Get("two")); + Assert.AreEqual("two", itemCache.Get("TWO")); + Assert.AreNotSame(returnValue, newReturnValue); + + mockInvocation.Verify(); + mockContext.Verify(); + } + + [Test] + public void CacheOnlyItemsOfMethodThatReturnsCollectionOnCondition() + { + MethodInfo method = typeof(CacheResultTarget).GetMethod("CacheResultItemsWithCondition"); + object expectedReturnValue = new object[] { "one", "two", "three" }; + + ExpectAttributeRetrieval(method); + ExpectCallToProceed(new object[] { "one", "two", "three" }); + ExpectCacheInstanceRetrieval("items", itemCache); + + // return value should be added to result cache and each item to item cache + object returnValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, returnValue); + Assert.AreEqual(2, itemCache.Count); + Assert.AreEqual("two", itemCache.Get("two")); + Assert.AreEqual("three", itemCache.Get("three")); + + mockInvocation.Verify(); + mockContext.Verify(); + } + + [Test] + public void CacheResultOfMethodThatReturnsCollectionOnCondition() + { + MethodInfo method = typeof(CacheResultTarget).GetMethod("CacheResultWithCondition"); + object expectedReturnValue = new object[] { }; + + ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, 5, expectedReturnValue); + ExpectCacheInstanceRetrieval("results", resultCache); + ExpectCallToProceed(new object[] { }); + + // return value should not be added to cache + object returnValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, returnValue); + Assert.AreEqual(0, resultCache.Count); + + mockInvocation.Verify(); + mockContext.Verify(); + } + + + #region Helper methods + + private void ExpectAttributeRetrieval(MethodInfo method) + { + mockInvocation.ExpectAndReturn("Method", method); + mockInvocation.ExpectAndReturn("Method", method); + } + + private void ExpectCacheKeyGeneration(MethodInfo method, params object[] arguments) + { + mockInvocation.ExpectAndReturn("Method", method); + mockInvocation.ExpectAndReturn("Arguments", arguments); + } + + private void ExpectCacheInstanceRetrieval(string cacheName, ICache cache) + { + mockContext.ExpectAndReturn("GetObject", cache, cacheName); + } + + private void ExpectCallToProceed(object expectedReturnValue) + { + mockInvocation.ExpectAndReturn("Proceed", expectedReturnValue); + } + + #endregion + + } + + #region Inner Class : CacheResultTarget + + public interface ICacheResultTarget + { + void ReturnsNothing(); + int ReturnsScalar(); + ICollection ReturnsCollection(int key, params object[] elements); + ICollection ReturnsCollectionAndItems(int key, params object[] elements); + ICollection ReturnsItems(int key, params object[] elements); + } + + public sealed class CacheResultTarget : ICacheResultTarget + { + public const int Scalar = int.MaxValue; + + [CacheResult("results", "'key'")] + public void ReturnsNothing() + { + } + + [CacheResult("results", "'key'")] + public int ReturnsScalar() + { + return Scalar; + } + + [CacheResult("results", "#key")] + public ICollection ReturnsCollection(int key, params object[] elements) + { + return elements; + } + + [CacheResult("results", "#key")] + [CacheResultItems("items", "#this")] + public ICollection ReturnsCollectionAndItems(int key, params object[] elements) + { + return elements; + } + + [CacheResultItems("items", "#this")] + public ICollection ReturnsItems(int key, params object[] elements) + { + return elements; + } + + [CacheResultItems("items", "#this")] + [CacheResultItems("items", "#this.ToUpper()")] + public ICollection MultipleCacheResultItems(int key, params object[] elements) + { + return elements; + } + + [CacheResultItems("items", "#this", Condition="#this.StartsWith('t')")] + public ICollection CacheResultItemsWithCondition(int key, params object[] elements) + { + return elements; + } + + [CacheResult("results", "#key", Condition="#this.Length > 0")] + public ICollection CacheResultWithCondition(int key, params object[] elements) + { + return elements; + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Cache/InvalidateCacheAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Cache/InvalidateCacheAdviceTests.cs new file mode 100644 index 00000000..c1c4f814 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aspects/Cache/InvalidateCacheAdviceTests.cs @@ -0,0 +1,189 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Caching; +using Spring.Context; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Unit tests for the InvalidateCacheAdvice class. + /// + /// Aleksandar Seovic + /// $Id: InvalidateCacheAdviceTests.cs,v 1.4 2007/04/01 15:05:34 bbaia Exp $ + [TestFixture] + public sealed class InvalidateCacheAdviceTests + { + private IDynamicMock mockContext; + private InvalidateCacheAdvice advice; + private ICache cache; + + [SetUp] + public void SetUp() + { + mockContext = new DynamicMock(typeof (IApplicationContext)); + + advice = new InvalidateCacheAdvice(); + advice.ApplicationContext = (IApplicationContext) mockContext.Object; + + cache = new NonExpiringCache(); + cache.Insert(1, "one"); + cache.Insert(2, "two"); + cache.Insert(3, "three"); + } + + [Test] + public void TestSingleKeyInvalidation() + { + MethodInfo method = typeof(InvalidateCacheTarget).GetMethod("InvalidateSingle"); + object[] args = new object[] { 2 }; + + ExpectCacheInstanceRetrieval("cache", cache); + + Assert.AreEqual(3, cache.Count); + + // item "two" should be removed from cache + advice.AfterReturning(null, method, args, null); + Assert.AreEqual(2, cache.Count); + Assert.IsNull(cache.Get(2)); + + mockContext.Verify(); + } + + [Test] + public void TestMultiKeyInvalidation() + { + MethodInfo method = typeof(InvalidateCacheTarget).GetMethod("InvalidateMulti"); + object[] args = new object[] { 2 }; + + ExpectCacheInstanceRetrieval("cache", cache); + + Assert.AreEqual(3, cache.Count); + + // all items except item "two" should be removed from cache + advice.AfterReturning(null, method, args, null); + Assert.AreEqual(1, cache.Count); + Assert.AreEqual("two", cache.Get(2)); + + mockContext.Verify(); + } + + [Test] + public void TestWholeCacheInvalidation() + { + MethodInfo method = typeof(InvalidateCacheTarget).GetMethod("InvalidateAll"); + + ExpectCacheInstanceRetrieval("cache", cache); + + Assert.AreEqual(3, cache.Count); + + // all items should be removed from cache + advice.AfterReturning(null, method, null, null); + Assert.AreEqual(0, cache.Count); + + mockContext.Verify(); + } + + [Test] + public void TestMultipleCachesInvalidation() + { + MethodInfo method = typeof(InvalidateCacheTarget).GetMethod("InvalidateMultipleCaches"); + object[] args = new object[] { 2 }; + + ExpectCacheInstanceRetrieval("cache", cache); + ExpectCacheInstanceRetrieval("cache", cache); + + Assert.AreEqual(3, cache.Count); + + // item "two" should be removed from cache + // all items except item "two" should be removed from cache + advice.AfterReturning(null, method, args, null); + Assert.AreEqual(0, cache.Count); + + mockContext.Verify(); + } + + [Test] + public void TestConditionInvalidation() + { + MethodInfo method = typeof(InvalidateCacheTarget).GetMethod("InvalidateWithCondition"); + object[] args = new object[] { 3 }; + + Assert.AreEqual(3, cache.Count); + + // no items should be removed from cache + advice.AfterReturning(null, method, args, null); + Assert.AreEqual(3, cache.Count); + Assert.AreEqual("three", cache.Get(3)); + + mockContext.Verify(); + } + + #region Helper methods + + private void ExpectCacheInstanceRetrieval(string cacheName, ICache cache) + { + mockContext.ExpectAndReturn("GetObject", cache, cacheName); + } + + #endregion + } + + #region Inner Class : InvalidateCacheTarget + + public sealed class InvalidateCacheTarget + { + [InvalidateCache("cache", Keys = "#key")] + public void InvalidateSingle(int key) + { + } + + [InvalidateCache("cache", Keys = "{1, 2, 3} - { #key }")] + public void InvalidateMulti(int key) + { + } + + [InvalidateCache("cache")] + public void InvalidateAll() + { + } + + [InvalidateCache("cache", Keys = "#key")] + [InvalidateCache("cache", Keys = "{1, 2, 3} - { #key }")] + public void InvalidateMultipleCaches(int key) + { + } + + [InvalidateCache("cache", Keys = "{1, 2, 3} - { #key }", Condition="#key != 3")] + public void InvalidateWithCondition(int key) + { + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Exception/ExceptionHandlerAspectIntegrationTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Exception/ExceptionHandlerAspectIntegrationTests.cs new file mode 100644 index 00000000..834efa56 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aspects/Exception/ExceptionHandlerAspectIntegrationTests.cs @@ -0,0 +1,336 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using Common.Logging; +using Common.Logging.Simple; +using NUnit.Framework; +using Spring.Aop.Framework; +using Spring.Aspects.Exceptions; +using Spring.Expressions; +using Spring.Objects; + +#endregion + +namespace Spring.Aspects.Exceptions +{ + /// + /// This class contains tests for ExceptionHandlerAdvice + /// + /// Mark Pollack + /// $Id: ExceptionHandlerAspectIntegrationTests.cs,v 1.6 2008/02/26 00:03:43 markpollack Exp $ + [TestFixture] + public class ExceptionHandlerAspectIntegrationTests + { + private ExceptionHandlerAdvice exceptionHandlerAdvice; + + [SetUp] + public void Setup() + { + LogManager.Adapter = new ConsoleOutLoggerFactoryAdapter(new NameValueCollection()); + exceptionHandlerAdvice = new ExceptionHandlerAdvice(); + } + + [Test] + public void LoggingTest() + { + + + LogExceptionHandler logHandler = new LogExceptionHandler(); + string testText = @"#log.Debug('Hello World, exception message = ' + #e.Message + ', target method = ' + #method.Name)"; + logHandler.SourceExceptionNames.Add("ArithmeticException"); + logHandler.ActionExpressionText = testText; + + exceptionHandlerAdvice.ExceptionHandlers.Add(logHandler); + + ProxyFactory pf = new ProxyFactory(new TestObject()); + pf.AddAdvice(exceptionHandlerAdvice); + ITestObject to = (ITestObject) pf.GetProxy(); + + try + { + to.Exceptional(new ArithmeticException()); + Assert.Fail("Should have thrown exception when only logging"); + } catch (ArithmeticException) + { + //TODO need to create adapter implementation to replay logged text. + } + + } + + [Test] + public void LoggingTestWithString() + { + string logHandlerText = "on exception name ArithmeticException log 'My Message, Method Name ' + #method.Name"; + + ExecuteLoggingHandler(logHandlerText); + } + + + [Test] + public void LoggingTestWithConstraintExpression() + { + string logHandlerText = "on exception (#e is T(System.ArithmeticException)) log 'My Message, Method Name ' + #method.Name"; + + ExecuteLoggingHandler(logHandlerText); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void LoggingTestWithBadString() + { + string logHandlerText = "on foobar name ArithmeticException log 'My Message, Method Name ' + #method.Name"; + + ExecuteLoggingHandler(logHandlerText); + } + + [Test] + public void LoggingTestWithInvalidConstraintExpression() + { + string logHandlerText = "on exception (#e is System.FooBar) log 'My Message, Method Name ' + #method.Name"; + + ExecuteLoggingHandler(logHandlerText); + + //No exception is expected. + + //TODO need to make sure log statement was executed. + } + + [Test] + public void LoggingTestWithNonBooleanConstraintExpression() + { + string logHandlerText = "on exception (1+1) log 'My Message, Method Name ' + #method.Name"; + + ExecuteLoggingHandler(logHandlerText); + + //No exception is expected. + + //TODO need to make sure log statement was executed. + } + + private void ExecuteLoggingHandler(string logHandlerText) + { + ITestObject to = CreateTestObjectProxy(logHandlerText); + + try + { + to.Exceptional(new ArithmeticException()); + } + catch (ArithmeticException) + { + //TODO assert logging occured. + } + } + + + + [Test] + public void TranslationWithString() + { + string translationHandlerText = + "on exception name ArithmeticException translate new System.InvalidOperationException('My Message, Method Name ' + #method.Name, #e)"; + + ITestObject to = CreateTestObjectProxy(translationHandlerText); + try + { + to.Exceptional(new ArithmeticException("Bad Math")); + Assert.Fail("Should have thrown exception"); + } + catch (InvalidOperationException e) + { + Assert.IsInstanceOfType(typeof(ArithmeticException), e.InnerException, "Inner exception."); + Assert.AreEqual("My Message, Method Name Exceptional", e.Message); + } catch (Exception e) + { + Assert.IsInstanceOfType(typeof(InvalidOperationException), e, "wrong exception type thrown."); + } + } + + [Test] + public void WrapWithString() + { + string translationHandlerText = + "on exception name ArithmeticException wrap System.InvalidOperationException 'My Message'"; + + ITestObject to = CreateTestObjectProxy(translationHandlerText); + try + { + to.Exceptional(new ArithmeticException("Bad Math")); + Assert.Fail("Should have thrown exception"); + } + catch (InvalidOperationException e) + { + Assert.IsInstanceOfType(typeof(ArithmeticException), e.InnerException); + Assert.AreEqual("My Message", e.Message); + } + catch (Exception e) + { + Assert.IsInstanceOfType(typeof(InvalidOperationException), e); + } + } + + [Test] + public void WrapWithStringDefaultMessage() + { + string translationHandlerText = + "on exception name ArithmeticException wrap System.InvalidOperationException"; + + ITestObject to = CreateTestObjectProxy(translationHandlerText); + try + { + to.Exceptional(new ArithmeticException("Bad Math")); + Assert.Fail("Should have thrown exception"); + } + catch (InvalidOperationException e) + { + Assert.IsInstanceOfType(typeof(ArithmeticException), e.InnerException); + Assert.AreEqual("Wrapped ArithmeticException", e.Message); + } + catch (Exception e) + { + Assert.IsInstanceOfType(typeof(InvalidOperationException), e); + } + } + + + [Test] + public void ReplaceWithString() + { + string translationHandlerText = + "on exception name ArithmeticException replace System.InvalidOperationException 'My Message'"; + + ITestObject to = CreateTestObjectProxy(translationHandlerText); + try + { + to.Exceptional(new ArithmeticException("Bad Math")); + Assert.Fail("Should have thrown exception"); + } + catch (InvalidOperationException e) + { + Assert.IsNull(e.InnerException); + Assert.AreEqual("My Message", e.Message); + } + catch (Exception e) + { + Assert.IsInstanceOfType(typeof(InvalidOperationException), e); + } + } + + [Test] + public void ReplaceWithStringDefaultMessage() + { + string translationHandlerText = + "on exception name ArithmeticException replace System.InvalidOperationException"; + + ITestObject to = CreateTestObjectProxy(translationHandlerText); + try + { + to.Exceptional(new ArithmeticException("Bad Math")); + Assert.Fail("Should have thrown exception"); + } + catch (InvalidOperationException e) + { + Assert.IsNull(e.InnerException); + Assert.AreEqual("Replaced ArithmeticException", e.Message); + } + catch (Exception e) + { + Assert.IsInstanceOfType(typeof(InvalidOperationException), e); + } + } + + [Test] + public void SwallowWithString() + { + string returnHandlerText = "on exception name ArithmeticException swallow"; + ITestObject to = CreateTestObjectProxy(returnHandlerText); + try + { + to.Exceptional(new ArithmeticException("Bad Math")); + } catch (Exception) + { + Assert.Fail("Should not have thrown exception"); + } + } + + + [Test] + public void ReturnWithString() + { + string returnHandlerText = "on exception name ArithmeticException return 12"; + ITestObject to = CreateTestObjectProxy(returnHandlerText); + try + { + int retVal = to.ExceptionalWithReturnValue(new ArithmeticException("Bad Math")); + Assert.AreEqual(12, retVal); + } + catch (Exception) + { + Assert.Fail("Should not have thrown exception"); + } + } + + [Test] + public void ChainLogAndWrap() + { + string logHandlerText = "on exception name ArithmeticException log 'My Message, Method Name ' + #method.Name"; + string translationHandlerText = "on exception name ArithmeticException wrap System.InvalidOperationException 'My Message'"; + exceptionHandlerAdvice.ExceptionHandlers.Add(logHandlerText); + exceptionHandlerAdvice.ExceptionHandlers.Add(translationHandlerText); + exceptionHandlerAdvice.AfterPropertiesSet(); + ProxyFactory pf = new ProxyFactory(new TestObject()); + pf.AddAdvice(exceptionHandlerAdvice); + ITestObject to = (ITestObject)pf.GetProxy(); + try + { + to.Exceptional(new ArithmeticException("Bad Math")); + Assert.Fail("Should have thrown exception"); + } + catch (InvalidOperationException e) + { + Assert.IsNotNull(e.InnerException); + Exception innerEx = e.InnerException; + Assert.AreEqual("My Message", e.Message); + Assert.AreEqual("Bad Math", innerEx.Message); + } + catch (Exception e) + { + Assert.IsInstanceOfType(typeof(InvalidOperationException), e); + } + + + } + + private ITestObject CreateTestObjectProxy(string logHandlerText) + { + exceptionHandlerAdvice.ExceptionHandlers.Add(logHandlerText); + exceptionHandlerAdvice.AfterPropertiesSet(); + + ProxyFactory pf = new ProxyFactory(new TestObject()); + pf.AddAdvice(exceptionHandlerAdvice); + return (ITestObject)pf.GetProxy(); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Logging/SimpleLoggingAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Logging/SimpleLoggingAdviceTests.cs new file mode 100644 index 00000000..bfb1983c --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aspects/Logging/SimpleLoggingAdviceTests.cs @@ -0,0 +1,190 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Text; +using AopAlliance.Intercept; +using Common.Logging; +using NUnit.Framework; +using Rhino.Mocks; + +#endregion + +namespace Spring.Aspects.Logging +{ + /// + /// This class contains tests for SimpleLoggingAdvice + /// + /// Mark Pollack + /// $Id: SimpleLoggingAdviceTests.cs,v 1.4 2008/04/04 14:59:47 bbaia Exp $ + [TestFixture] + public class SimpleLoggingAdviceTests + { + private MockRepository mocks; + + [SetUp] + public void Setup() + { + mocks = new MockRepository(); + } + + [Test] + public void SunnyDayLoggingCorrectly() + { + ILog log = (ILog)mocks.CreateMock(typeof(ILog)); + IMethodInvocation methodInvocation = (IMethodInvocation)mocks.CreateMock(typeof(IMethodInvocation)); + + MethodInfo mi = typeof(string).GetMethod("ToString", Type.EmptyTypes); + //two additional calls the method are to retrieve the method name on entry/exit... + Expect.Call(methodInvocation.Method).Return(mi).Repeat.Any(); + + Expect.Call(log.IsTraceEnabled).Return(true).Repeat.Any(); + log.Trace("Entering ToString"); + + Expect.Call(methodInvocation.Proceed()).Return(null); + + log.Trace("Exiting ToString"); + + mocks.ReplayAll(); + + TestableSimpleLoggingAdvice loggingAdvice = new TestableSimpleLoggingAdvice(true); + loggingAdvice.CallInvokeUnderLog(methodInvocation, log); + + mocks.VerifyAll(); + + } + + [Test] + public void SunnyDayLoggingCorrectlyDebugLevel() + { + ILog log = (ILog)mocks.CreateMock(typeof(ILog)); + IMethodInvocation methodInvocation = (IMethodInvocation)mocks.CreateMock(typeof(IMethodInvocation)); + + MethodInfo mi = typeof(string).GetMethod("ToString", Type.EmptyTypes); + //two additional calls the method are to retrieve the method name on entry/exit... + Expect.Call(methodInvocation.Method).Return(mi).Repeat.Any(); + + Expect.Call(log.IsTraceEnabled).Return(false).Repeat.Any(); + Expect.Call(log.IsDebugEnabled).Return(true).Repeat.Any(); + log.Debug("Entering ToString"); + + Expect.Call(methodInvocation.Proceed()).Return(null); + + log.Debug("Exiting ToString"); + + mocks.ReplayAll(); + + TestableSimpleLoggingAdvice loggingAdvice = new TestableSimpleLoggingAdvice(true); + loggingAdvice.LogLevel = LogLevel.Debug; + Assert.IsTrue(loggingAdvice.CallIsInterceptorEnabled(methodInvocation, log)); + loggingAdvice.CallInvokeUnderLog(methodInvocation, log); + + mocks.VerifyAll(); + + } + + + [Test] + public void ExceptionPathStillLogsCorrectly() + { + ILog log = (ILog)mocks.CreateMock(typeof(ILog)); + IMethodInvocation methodInvocation = (IMethodInvocation)mocks.CreateMock(typeof(IMethodInvocation)); + + MethodInfo mi = typeof(string).GetMethod("ToString", Type.EmptyTypes); + //two additional calls the method are to retrieve the method name on entry/exit... + Expect.Call(methodInvocation.Method).Return(mi).Repeat.Any(); + + Expect.Call(log.IsTraceEnabled).Return(true).Repeat.Any(); + log.Trace("Entering..."); + + LastCall.On(log).IgnoreArguments(); + + Exception e = new ArgumentException("bad value"); + Expect.Call(methodInvocation.Proceed()).Throw(e); + + log.Trace("Exception...", e); + LastCall.On(log).IgnoreArguments(); + + mocks.ReplayAll(); + + TestableSimpleLoggingAdvice loggingAdvice = new TestableSimpleLoggingAdvice(true); + try + { + loggingAdvice.CallInvokeUnderLog(methodInvocation, log); + Assert.Fail("Must have propagated the IllegalArgumentException."); + } + catch (ArgumentException) + { + + } + + mocks.VerifyAll(); + + } + + + [Test] + public void SunnyDayLoggingAllOptionalInformationCorrectly() + { + ILog log = (ILog)mocks.CreateMock(typeof(ILog)); + IMethodInvocation methodInvocation = (IMethodInvocation)mocks.CreateMock(typeof(IMethodInvocation)); + + MethodInfo mi = typeof(Dog).GetMethod("Bark"); + //two additional calls the method are to retrieve the method name on entry/exit... + Expect.Call(methodInvocation.Method).Return(mi).Repeat.Any(); + int[] luckyNumbers = new int[]{1, 2, 3}; + object[] args = new object[] {"hello", luckyNumbers}; + + + Expect.Call(methodInvocation.Arguments).Return(args); + + Expect.Call(log.IsTraceEnabled).Return(true).Repeat.Any(); + log.Trace("Entering..."); + LastCall.IgnoreArguments(); + + Expect.Call(methodInvocation.Proceed()).Return(4); + + log.Trace("Exiting..."); + LastCall.IgnoreArguments(); + + mocks.ReplayAll(); + + TestableSimpleLoggingAdvice loggingAdvice = new TestableSimpleLoggingAdvice(true); + loggingAdvice.LogExecutionTime = true; + loggingAdvice.LogMethodArguments = true; + loggingAdvice.LogUniqueIdentifier = true; + + loggingAdvice.CallInvokeUnderLog(methodInvocation, log); + + mocks.VerifyAll(); + } + } + + public class Dog + { + public int Bark(string message, int[] luckyNumbers) + { + return 4; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Logging/TestableSimpleLoggingAdvice.cs b/test/Spring/Spring.Aop.Tests/Aspects/Logging/TestableSimpleLoggingAdvice.cs new file mode 100644 index 00000000..c83fea8b --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aspects/Logging/TestableSimpleLoggingAdvice.cs @@ -0,0 +1,71 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using AopAlliance.Intercept; +using Common.Logging; + +namespace Spring.Aspects.Logging +{ + /// + /// This is simple wrapper to expose the protected methood InvokeUnderLog in the class + /// SimpleLoggingAdvice for testing purposes. + /// + /// Mark Pollack + /// $Id: TestableSimpleLoggingAdvice.cs,v 1.2 2007/12/06 17:17:24 markpollack Exp $ + public class TestableSimpleLoggingAdvice : SimpleLoggingAdvice + { + /// + /// Initializes a new instance of the class. + /// + /// if set to true [use dynamic logger]. + public TestableSimpleLoggingAdvice(bool useDynamicLogger) : base(useDynamicLogger) + { + } + + /// + /// Initializes a new instance of the class. + /// + public TestableSimpleLoggingAdvice() + { + } + + /// + /// Calls the protected InvokeUnderLog method + /// + /// The invocation. + /// The log. + /// The result of the call to IMethodInvocation.Proceed() + public object CallInvokeUnderLog(IMethodInvocation invocation, ILog log) + { + return InvokeUnderLog(invocation, log); + } + + /// + /// Calls the IsInterceptorEnabled method. + /// + /// The invocation. + /// The log. + /// The result of the protected method IsInterceptorEnabled + public bool CallIsInterceptorEnabled(IMethodInvocation invocation, ILog log) + { + return IsInterceptorEnabled(invocation, log); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aspects/RetryAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/RetryAdviceTests.cs new file mode 100644 index 00000000..326a597a --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aspects/RetryAdviceTests.cs @@ -0,0 +1,176 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Aop.Framework; + +#endregion + +namespace Spring.Aspects +{ + /// + /// This class contains tests for RetryAdvice + /// + /// Mark Pollack + /// $Id: RetryAdviceTests.cs,v 1.3 2008/03/17 20:25:41 markpollack Exp $ + [TestFixture] + public class RetryAdviceTests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void TestSunnyDay() + { + InvokeOncePassOnceFail(false, false); + InvokeOncePassOnceFail(false, true); + InvokeOncePassOnceFail(true, false); + InvokeOncePassOnceFail(true, true); + + } + + [Test] + public void TestUnexpectedException() + { + InvokeOnceFailWithUnexceptedException(false, false); + } + + private static void InvokeOncePassOnceFail(bool useExceptionName, bool isDelay) + { + ITestRemoteService rs = GetRemoteService(2, useExceptionName, isDelay); + + rs.DoTransfer(); + + rs = GetRemoteService(3, useExceptionName, isDelay); + try + { + rs.DoTransfer(); + Assert.Fail("Should have failed."); + } catch (ArithmeticException) + { + + } + } + private static void InvokeOnceFailWithUnexceptedException(bool useExceptionName, bool isDelay) + { + ITestRemoteService rs = GetRemoteService(3, useExceptionName, isDelay); + try + { + rs.DoTransfer2(); + Assert.Fail("Should have failed."); + } + catch (ArgumentException) + { + + } + } + + private static ITestRemoteService GetRemoteService(int numFailures, bool usingExceptionName, bool isDelay) + { + TestRemoteService remoteService = new TestRemoteService(); + remoteService.NumFailures = numFailures; + ProxyFactory factory = new ProxyFactory(remoteService); + RetryAdvice retryAdvice = new RetryAdvice(); + if (usingExceptionName) + { + if (isDelay) + { + retryAdvice.RetryExpression = "on exception name ArithmeticException retry 3x delay 1s"; + } + else + { + retryAdvice.RetryExpression = "on exception name ArithmeticException retry 3x rate (1*#n + 0.5)"; + } + } + else + { + if (isDelay) + { + retryAdvice.RetryExpression = "on exception (#e is T(System.ArithmeticException)) retry 3x delay 1s"; + } + else + { + retryAdvice.RetryExpression = "on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)"; + } + } + retryAdvice.AfterPropertiesSet(); + factory.AddAdvice(retryAdvice); + ITestRemoteService rs = factory.GetProxy() as ITestRemoteService; + Assert.IsNotNull(rs); + return rs; + } + } + + public interface ITestRemoteService + { + void DoTransfer(); + void DoTransfer2(); + } + + public class TestRemoteService : ITestRemoteService + { + private int numFailures; + private int count = 0; + private bool throwArithmeticException = false; + + + public int NumFailures + { + get { return numFailures; } + set { numFailures = value; } + } + + public bool ThrowArithmeticException + { + get + { + + if (count < NumFailures) + { + count++; + return true; + } + else + { + return throwArithmeticException; + } + } + set { throwArithmeticException = value; } + } + + public void DoTransfer() + { + if (ThrowArithmeticException) + { + throw new ArithmeticException("can't do the math"); + } + } + + public void DoTransfer2() + { + throw new ArgumentException("bad argument"); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Validation/ParameterValidationAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Validation/ParameterValidationAdviceTests.cs new file mode 100644 index 00000000..e34b9a8b --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aspects/Validation/ParameterValidationAdviceTests.cs @@ -0,0 +1,113 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Context; +using Spring.Validation; +using Spring.Validation.Actions; + +#endregion + +namespace Spring.Aspects.Validation +{ + /// + /// Unit tests for the CacheParameterAdvice class. + /// + /// Aleksandar Seovic + /// $Id: ParameterValidationAdviceTests.cs,v 1.2 2008/04/02 23:00:46 markpollack Exp $ + [TestFixture] + public sealed class ParameterValidationAdviceTests + { + private IDynamicMock mockContext; + private ParameterValidationAdvice advice; + private RequiredValidator requiredValidator; + + [SetUp] + public void SetUp() + { + mockContext = new DynamicMock(typeof (IApplicationContext)); + + advice = new ParameterValidationAdvice(); + advice.ApplicationContext = (IApplicationContext) mockContext.Object; + + requiredValidator = new RequiredValidator(); + requiredValidator.Actions.Add(new ErrorMessageAction("error.required", "errors")); + } + + [Test] + public void TestValidArgument() + { + MethodInfo method = typeof(ValidationTarget).GetMethod("Save"); + Inventor inventor = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + ValidationTarget target = new ValidationTarget(); + object[] args = new object[] {inventor}; + + ExpectValidatorRetrieval("required", requiredValidator); + advice.Before(method, args, target); + method.Invoke(target, args); + + Assert.AreEqual("NIKOLA TESLA", inventor.Name); + + mockContext.Verify(); + } + + [Test] + [ExpectedException(typeof(ValidationException))] + public void TestInvalidArgument() + { + MethodInfo method = typeof(ValidationTarget).GetMethod("Save"); + + ExpectValidatorRetrieval("required", requiredValidator); + advice.Before(method, new object[] { null }, new ValidationTarget()); + mockContext.Verify(); + } + + #region Helper methods + + private void ExpectValidatorRetrieval(string validatorName, IValidator validator) + { + mockContext.ExpectAndReturn("GetObject", validator, validatorName); + } + + #endregion + } + + #region Inner Class : ValidationTarget + + public interface IValidationTarget + { + void Save(Inventor inventor); + } + + public sealed class ValidationTarget : IValidationTarget + { + public void Save([Validated("required")] Inventor inventor) + { + inventor.Name = inventor.Name.ToUpper(); + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/AssemblyInfo.cs b/test/Spring/Spring.Aop.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..95b35428 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Spring.Aop Tests")] +[assembly: AssemblyDescription("Unit tests for Spring.Aop assembly")] diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/AutoProxy/advisorAutoProxyCreator.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/AutoProxy/advisorAutoProxyCreator.xml new file mode 100644 index 00000000..b77334a8 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/AutoProxy/advisorAutoProxyCreator.xml @@ -0,0 +1,70 @@ + + + + + Matches all Advisors in the factory: we don't use a prefix + + + + + + + + + + + + + + + + + + + Don't set order value: should remain int.MAXVALUE, so it's non-ordered + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/AutoProxy/objectNameAutoProxyCreatorTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/AutoProxy/objectNameAutoProxyCreatorTests.xml new file mode 100644 index 00000000..8f36c728 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/AutoProxy/objectNameAutoProxyCreatorTests.xml @@ -0,0 +1,153 @@ + + + + + + + frozen + + + + + nopInterceptor + + + + + + + + + *Wildcards* + testObject + myTestObj* + *FamilyMember + doubleProxy + + + + + nopInterceptor + + + + + + + + factoryObject + + + + + nopInterceptor + + + + + + + + doubleProxy + + + + + nopInterceptor + + + + + + + + decoratorProx* + + + + + + nopInterceptor + countingBeforeAdvice + + + + + + + + + *introductionUsingDecorator + + + + + introductionNopInterceptor + isModifiedAdvisor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/adapter/withBPPContext.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/adapter/withBPPContext.xml new file mode 100644 index 00000000..ede07cad --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/adapter/withBPPContext.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + testObjectTarget + Spring.Objects.ITestObject + simpleBeforeAdviceAdvisor + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/adapter/withoutBPPContext.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/adapter/withoutBPPContext.xml new file mode 100644 index 00000000..80b5a46a --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/adapter/withoutBPPContext.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + testObjectTarget + Spring.Objects.ITestObject + simpleBeforeAdviceAdvisor + + + + + diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/innerBeanTarget.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/innerBeanTarget.xml new file mode 100644 index 00000000..e757d0c8 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/innerBeanTarget.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + innerObjectTarget + + + + + nopInterceptor + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/prototypeTarget.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/prototypeTarget.xml new file mode 100644 index 00000000..1eb013fb --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/prototypeTarget.xml @@ -0,0 +1,44 @@ + + + + + + + + + + testObjectTarget + + + Spring.Aop.Framework.PrototypeTargetTests+TestObject + + + false + + + + testInterceptor + + + + + + testObjectTarget + + Spring.Aop.Framework.PrototypeTargetTests+TestObject + + + true + + + + testInterceptor + + + + + diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/prototypeTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/prototypeTests.xml new file mode 100644 index 00000000..8583e242 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/prototypeTests.xml @@ -0,0 +1,46 @@ + + + + + + + + + + 10 + + + + 10 + + + + + + + test + debugInterceptor + + + + prototypeTarget + debugInterceptor + false + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/proxyFactoryDoubleTargetSourceTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/proxyFactoryDoubleTargetSourceTests.xml new file mode 100644 index 00000000..e5debf88 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/proxyFactoryDoubleTargetSourceTests.xml @@ -0,0 +1,56 @@ + + + + + + + + + + Eve + + + + + + Adam + + + + + + + + + + + + Spring.Objects.ITestObject + + adamTargetSource + countingBeforeAdvice + + + + + + + Spring.Objects.ITestObject + + adam + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/proxyFactoryTargetSourceTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/proxyFactoryTargetSourceTests.xml new file mode 100644 index 00000000..a9f66538 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/proxyFactoryTargetSourceTests.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spring.Objects.ITestObject + nopInterceptor,unsupportedInterceptor + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/proxyFactoryTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/proxyFactoryTests.xml new file mode 100644 index 00000000..cbc06c6c --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/proxyFactoryTests.xml @@ -0,0 +1,136 @@ + + + + + + custom + 666 + + + + + + Spring.Objects.ITestObject + + debugInterceptor + + + + + + Spring.Objects.ITestObject + test + global* + global* + + + + Spring.Objects.ITestObject + false + test + + + + custom + 666 + + + + + + + + Spring.Objects.ITestObject + false + false + concurrentPrototypeTarget + global* + + + + Spring.Objects.ITestObject + false + test + + + + Spring.Objects.ITestObject + test + pointcutForVoid + + + + + + + + + + + + + + + + Spring.Context.IApplicationEventListener + target2 + debugInterceptor,global* + global* + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/serializationTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/serializationTests.xml new file mode 100644 index 00000000..99b2811c --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/serializationTests.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + serializableNopInterceptor + Spring.Objects.Person + + + serializableSingleton + + + + + + + serializablePrototype + + + + serializableNopInterceptor,prototypeTarget + Spring.Objects.Person + + false + + + + + nopInterceptor + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/throwsAdvice.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/throwsAdvice.xml new file mode 100644 index 00000000..80f3c092 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Framework/throwsAdvice.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Support/RegularExpressionSetterTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Support/RegularExpressionSetterTests.xml new file mode 100644 index 00000000..347f0f1b --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Support/RegularExpressionSetterTests.xml @@ -0,0 +1,57 @@ + + + + + + custom + 666 + + + + + + + + .*Set.* + + + + Spring.Objects.IPerson + + SettersAdvisor + + + + Spring.Objects.IPerson + + + SerializableSettersAdvised + + + SettersAdvisor + + + + + + + + .*Set.* + .*ReturnsThis + + + + + + Spring.Objects.IPerson + + true + + SettersAndReturnsThisAdvisor + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/commonsPoolTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/commonsPoolTests.xml new file mode 100644 index 00000000..f34d925f --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/commonsPoolTests.xml @@ -0,0 +1,54 @@ + + + + + 10 + + + + + prototypeTest + + + 25 + + + + + + + + GetPoolingConfigMixin + + + + + + + + + + nop + + + + + + + + + + + + + poolConfigAdvisor + + + + true + + + diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/hotSwapTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/hotSwapTests.xml new file mode 100644 index 00000000..3e3a2b35 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/hotSwapTests.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/prototypeTargetSourceTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/prototypeTargetSourceTests.xml new file mode 100644 index 00000000..6ffa6d5e --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/prototypeTargetSourceTests.xml @@ -0,0 +1,41 @@ + + + + + + + + + 10 + + + + 10 + + + + prototypeTest + + + + + + test + debugInterceptor + + + + + debugInterceptor + + + + prototypeTest + debugInterceptor + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/simplePoolTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/simplePoolTests.xml new file mode 100644 index 00000000..94253981 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/simplePoolTests.xml @@ -0,0 +1,54 @@ + + + + + 10 + + + + + prototypeTest + + + 25 + + + + + + + + GetPoolingConfigMixin + + + + + + + + + + nop + + + + + + + + + + + + + poolConfigAdvisor + + + + true + + + diff --git a/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/threadLocalTests.xml b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/threadLocalTests.xml new file mode 100644 index 00000000..3629123b --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Data/Spring/Aop/Target/threadLocalTests.xml @@ -0,0 +1,72 @@ + + + + + 10 + + + + + prototypeTest + + + + + + + + + + GetStatsMixin + + + + + + + statsAdvisor + + + debugInterceptor + + + + + + + true + + + + + + Kerry + + + + + Rod + + + + + + + + test + + + + + + + + diff --git a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2002.csproj b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2002.csproj new file mode 100644 index 00000000..1e805fd9 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2002.csproj @@ -0,0 +1,516 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2003.csproj b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2003.csproj new file mode 100644 index 00000000..f55936fe --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2003.csproj @@ -0,0 +1,554 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2005.csproj b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2005.csproj new file mode 100644 index 00000000..2bb99b17 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2005.csproj @@ -0,0 +1,306 @@ + + + Local + 8.0.50727 + 2.0 + {2111596A-0327-4C9D-8919-294FBD988A23} + Debug + AnyCPU + + + + + Spring.Aop.Tests + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + ..\..\..\build\VS.Net.2005\Spring.Aop.Tests\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0;DEBUG_DYNAMIC + Spring.Aop.Tests.xml + true + 4096 + false + 0618 + false + false + false + false + 3 + full + prompt + true + + + ..\..\..\build\VS.Net.2005\Spring.Aop.Tests\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + 618 + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\antlr.runtime.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\Net\2.0\DotNetMock.dll + + + False + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + False + ..\..\..\lib\Net\2.0\Rhino.Mocks.dll + + + System + + + System.Data + + + System.Web + + + System.XML + + + + + Code + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + + Code + + + + + + + + + + + + + Code + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904} + Spring.Core.Tests.2005 + + + + + + + echo "Copying .xml files for tests" +xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2005\Spring.Aop.Tests\$(ConfigurationName)\ /y /s /q /d +xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2005\Spring.Aop.Tests\$(ConfigurationName)\ /y /s /q + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.build b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.build new file mode 100644 index 00000000..b4883cc4 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.build @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.dll.config b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.dll.config new file mode 100644 index 00000000..7aec1f96 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.dll.config @@ -0,0 +1,30 @@ + + + + + +
    + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.xml b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.xml new file mode 100644 index 00000000..fdc729b2 --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.xml @@ -0,0 +1,854 @@ + + + + Spring.Aop.Tests + + + + + This class contains tests for RetryAdvice + + Mark Pollack + $Id: RetryAdviceTests.cs,v 1.3 2008/03/17 20:25:41 markpollack Exp $ + + + + Unit tests for the CacheParameterAdvice class. + + Aleksandar Seovic + $Id: CacheParameterAdviceTests.cs,v 1.6 2007/04/01 15:05:33 bbaia Exp $ + + + + Unit tests for the TrueMethodMatcher class. + + Rick Evans + $Id: TrueMethodMatcherTests.cs,v 1.3 2006/04/09 07:19:06 markpollack Exp $ + + + + Useful base class for counting advices etc. + + Rod Johnson + Choy Rim (.NET) + $Id: MethodCounter.cs,v 1.2 2007/03/16 04:01:47 aseovic Exp $ + + + Method name --> count, does not understand overloading + + + + Unit tests for the TruePointcut class. + + Rick Evans + $Id: TruePointcutTests.cs,v 1.2 2006/04/09 07:19:06 markpollack Exp $ + + + + This interface can be implemented by cacheable objects + or cache entries, to enable the freshness of objects + to be checked. + + Rod Johnson + Choy Rim (.NET) + $Id: ITimeStamped.cs,v 1.1 2004/08/01 10:20:20 choyrim Exp $ + + + + Return the timestamp for this object. + + + + + Unit tests for the CachedAopProxyFactoryTests class. + + Bruno Baia + $Id: CachedAopProxyFactoryTests.cs,v 1.1 2007/08/04 01:20:11 bbaia Exp $ + + + + Unit tests for the DefaultAopProxyFactory class. + + Bruno Baia + $Id: DefaultAopProxyFactoryTests.cs,v 1.1 2007/08/04 01:20:11 bbaia Exp $ + + + + Unit tests for the AbstractMethodInvocation class. + + Rick Evans + Bruno Baia + $Id: AbstractMethodInvocationTests.cs,v 1.2 2008/02/06 18:29:05 bbaia Exp $ + + + + Unit tests for the TypeFilters class. + + Rod Johnson + Choy Rim (.NET) + $Id: TypeFiltersTests.cs,v 1.2 2005/07/21 16:01:12 springboy Exp $ + + + + Unit tests for the ControlFlowPointcut class. + + Rod Johnson + Simon White (.NET) + + + + Check that we can use a cflow pointcut only in conjunction with + a static pointcut: e.g. all setter methods that are invoked under + a particular class. + + + This greatly reduces the number of calls to the cflow pointcut, + meaning that it's not so prohibitively expensive. + + + + + Pointcut to catch all methods beginning with 'Set'. + + + + + Unit tests for the AttributeMatchMethodPointcut class. + + Rick Evans + Ronald Wildenberg + $Id: AttributeMatchMethodPointcutTests.cs,v 1.3 2007/05/21 16:44:08 bbaia Exp $ + + + + Confirms that without interfaces checking, a method that is implemented from an interface, will not match. + + + + + Confirms that with interface checking, a method that is implementing an interface method + where the attribute is defined will match. + + + + + Confirms that with interfaces checking, a method that is indirectly implementing an interface method + where the attribute is defined will match. + + + + + Confirms that overloading methods do not match, whatever the attributes. + + + + + Confirms that methods, defined in a subclass of a class that implements an interface that + has methods that have been decorated with an attribute, match. + + + + + Integration test cases for the ProxyFactoryObject, using an XML object factory. + + Rod Johnson + Federico Spinazzi (.NET) + Choy Rim (.NET) + Aleksandar Seovic (.NET) + $Id: ProxyFactoryObjectTests.cs,v 1.35 2007/12/07 17:59:31 bbaia Exp $ + + + + The instances are equal, but do not have object identity. + Interceptors and interfaces and the target are the same. + + + + Test invoker is automatically added to manipulate target + + + + Must see effect immediately on behaviour. + + + + Try adding and removing interfaces and interceptors on prototype. + Changes will only affect future references obtained from the factory. + Each instance will be independent. + + + + + Note that we can't add or remove interfaces without reconfiguring the + singleton. + + + + Checks that globals get invoked, + and that they can add aspect interfaces unavailable + to other objects. These interfaces don't need + to be included in proxiedInterface []. + + + + + Fires only on void methods. Saves list of methods intercepted. + + + + + Must fire only if it returns void. + + + + Aspect interface + + + Use as a global interceptor. Checks that + global interceptors can add aspect interfaces. + NB: Add only via global interceptors in XML file. + + + + + Tests for auto proxy creation combined with factory object and circular references. + + Erich Eichinger (.NET) + $Id: AdvisorAutoProxyCreatorCircularReferencesTests.cs,v 1.4 2007/09/07 01:53:01 markpollack Exp $ + + + + Unit tests for the AfterReturningAdviceInterceptor class. + + Rod Johnson + Simon White (.NET) + + + + This class contains tests for SimpleLoggingAdvice + + Mark Pollack + $Id: SimpleLoggingAdviceTests.cs,v 1.4 2008/04/04 14:59:47 bbaia Exp $ + + + + Unit tests for the ProxyConfig class. + + Rick Evans + $Id: ProxyConfigTests.cs,v 1.2 2006/04/09 07:19:05 markpollack Exp $ + + + + Before advisor that allow us to manipulate ordering to check + that superclass sorting works correctly. + + + It doesn't actually do anything except count + method invocations and check for presence of a value in the + LogicalThreadContext. + + Mark Pollack (.NET) + $Id: OrderedLogicalThreadContextCheckAdvisor.cs,v 1.2 2006/09/15 21:25:17 markpollack Exp $ + + + Should we insist on the presence of a transaction attribute or refuse to accept one? + + + + Simple before advice example that we can use for counting checks. + + Rod Johnson + Choy Rim (.NET) + $Id: CountingBeforeAdvice.cs,v 1.6 2007/03/16 04:01:47 aseovic Exp $ + + + + This is simple wrapper to expose the protected methood InvokeUnderLog in the class + SimpleLoggingAdvice for testing purposes. + + Mark Pollack + $Id: TestableSimpleLoggingAdvice.cs,v 1.2 2007/12/06 17:17:24 markpollack Exp $ + + + + Initializes a new instance of the class. + + if set to true [use dynamic logger]. + + + + Initializes a new instance of the class. + + + + + Calls the protected InvokeUnderLog method + + The invocation. + The log. + The result of the call to IMethodInvocation.Proceed() + + + + Calls the IsInterceptorEnabled method. + + The invocation. + The log. + The result of the protected method IsInterceptorEnabled + + + + Simple throw advice example that we can use for counting checks. + + Rod Johnson + Bruno Baia (.NET) + $Id: CountingThrowsAdvice.cs,v 1.2 2007/03/16 04:01:47 aseovic Exp $ + + + + Tests for ObjectNameAutoProxyCreator functionality + + Mark Pollack + $Id: ObjectNameAutoProxyCreatorTests.cs,v 1.6 2007/09/07 01:53:01 markpollack Exp $ + + + + TestCase for AdvisorAdapterRegistrationManager mechanism. + + Dmitriy Kopylenko + Simon White (.NET) + + + + + + Dmitriy Kopylenko + Simon White (.NET) + + + + Subclass of NopInterceptor that is serializable and + can be used to test proxy serialization. + + Rod Johnson + Simon White (.NET) + + + + Trivial interceptor that can be introduced into an interceptor chain to + aid in debugging. + + Rod Johnson + Choy Rim (.NET) + $Id: NopInterceptor.cs,v 1.6 2008/01/14 20:49:47 oakinger Exp $ + + + + Unit tests for the ProxyFactory class. + + Rod Johnson + Choy Rim (.NET) + Rick Evans (.NET) + $Id: ProxyFactoryTests.cs,v 1.22 2008/03/21 10:49:38 oakinger Exp $ + + + + Simple after returning advice example that we can use for counting checks. + + Rod Johnson + Bruno Baia (.NET) + $Id: CountingAfterReturningAdvice.cs,v 1.2 2007/03/16 04:01:47 aseovic Exp $ + + + + Convenience + implementation that displays verbose information about intercepted + invocations to the system console. + + +

    + Can be introduced into an interceptor chain to serve as a useful low + level debugging aid. +

    +
    + Rod Johnson + Federico Spinazzi (.NET) + Aleksandar Seovic (.NET) + $Id: DebugAdvice.cs,v 1.1 2007/08/08 07:45:36 markpollack Exp $ + +
    + + + Displays verbose information about intercepted invocations to the + system console. + + + The method invocation that is being intercepted. + + + The result of the call to the + method of + the supplied ; this return value may + well have been intercepted by the interceptor. + + + If any of the interceptors in the chain or the target object itself + throws an exception. + + + + + + + Gets the count of the number of times this interceptor has been + invoked. + + + The count of the number of times this interceptor has been invoked. + + + + + Unit tests for the CacheResultAdvice class. + + Aleksandar Seovic + $Id: CacheResultAdviceTests.cs,v 1.4 2007/08/22 20:16:51 oakinger Exp $ + + + + Change History: + 2007-08-22 (oakinger): changed behaviour to cache null values as well + + + + + Unit tests for the PrototypeTargetSource class. + + Rod Johnson + Federico Spinazzi + $Id: PrototypeTargetSourceTests.cs,v 1.8 2007/07/28 07:33:22 markpollack Exp $ + + + + The setup logic executed before the execution of this test fixture. + + + + + Test that multiple invocations of the prototype object will result + in no change to visible state, as a new instance is used. + With the singleton, there will be change. + + + + + Unit tests for the EmptyTargetSource class. + + Rick Evans + $Id: EmptyTargetSourceTests.cs,v 1.2 2006/04/09 07:19:06 markpollack Exp $ + + + + Simple BeforeAdvice targeted for testing + + Dmitriy Kopylenko + Simon White (.NET) + + + + Additional and overridden tests for the decorator-based proxy. + + Rod Johnson + Juergen Hoeller + Bruno Baia (.NET) + $Id: DecoratorAopProxyTests.cs,v 1.6 2008/05/21 08:04:52 bbaia Exp $ + + + + Useful base class for Aop proxy test cases. + + Rod Johnson + Juergen Hoeller + Bruno Baia (.NET) + $Id: AbstractAopProxyTests.cs,v 1.21 2008/05/21 08:04:52 bbaia Exp $ + + + + Check that the two MethodInvocations necessary are independent + and don't conflict. + + + + + Check that although a method is eligible for advice chain optimization and + direct reflective invocation, it doesn't happen if we've asked to see the proxy, + so as to guarantee a consistent programming model. + + + + + http://forum.springframework.net/showthread.php?t=504 + + + + + Fires on setter methods that take a string. + + + + + Replaces null arg with "". + + + + + Unit tests for the AopUtils class. + + Rod Johnson + Choy Rim (.NET) + $Id: AopUtilsTests.cs,v 1.2 2008/03/21 10:49:37 oakinger Exp $ + + + + This class contains tests for ExceptionHandlerAdvice + + Mark Pollack + $Id: ExceptionHandlerAspectIntegrationTests.cs,v 1.6 2008/02/26 00:03:43 markpollack Exp $ + + + + Unit tests for the CacheParameterAdvice class. + + Aleksandar Seovic + $Id: CacheAspectIntegrationTests.cs,v 1.4 2007/08/03 14:38:39 markpollack Exp $ + + + Tests for pooling invoker interceptor + TODO need to make these tests stronger: it's hard to + make too many assumptions about a pool + + Rod Johnson + Federico Spinazzi (.Net) + $Id: SimplePoolTargetSourceTests.cs,v 1.3 2006/02/08 08:31:05 aseovic Exp $ + + + + Initial count value set in Object factory XML + + + We must simulate container shutdown, which should clear threads. + + + + + + Dmitriy Kopylenko + Simon White (.NET) + + + + Additional and overridden tests for the composition-based proxy. + + Rod Johnson + Juergen Hoeller + Bruno Baia (.NET) + $Id: CompositionAopProxyTests.cs,v 1.4 2007/08/02 04:15:37 markpollack Exp $ + + + + This class contains tests for the custom aop namespace. + + Mark Pollack + $Id: AopNamespaceParserTests.cs,v 1.7 2007/08/09 03:06:37 markpollack Exp $ + + + + Unit tests for the HotSwappableTargetSource class. + + Rod Johnson + Federico Spinazzi (.NET) + $Id: HotSwappableTargetSourceTests.cs,v 1.4 2006/04/09 07:19:06 markpollack Exp $ + + + Initial count value set in Object factory XML + + + + We must simulate container shutdown, which should clear threads. + + + + + + + Dmitriy Kopylenko + Simon White (.NET) + + + + Unit tests for the HashtableCachingAdvisorChainFactory class. + + Rick Evans + $Id: HashtableCachingAdvisorChainFactoryTests.cs,v 1.2 2006/04/09 07:19:05 markpollack Exp $ + + + + The setup logic executed before the execution of this test fixture. + + + + + The setup logic executed before the execution of each individual test. + + + + + The tear down logic executed after the execution of each individual test. + + + + + The tear down logic executed after the entire test fixture has executed. + + + + + Advice object that implements multiple Advice interfaces. + + Juergen Hoeller + Bruno Baia (.NET) + $Id: CountingMultiAdvice.cs,v 1.1 2006/08/10 18:45:57 bbaia Exp $ + + + Rod Johnson + Federico Spinazzi + $Id: ThreadLocalTargetSourceTests.cs,v 1.5 2006/11/13 07:04:51 markpollack Exp $ + + + + Initial count value set in Object factory XML + + + We must simulate container shutdown, which should clear threads. + + + Check we can use two different ThreadLocalTargetSources + managing objects of different types without them interfering + with one another. + + + + Relies on introduction + + + + + + Translation of DelegatingIntroductionInterceptor unit tests to Spring.NET. + + + Spring.NET doesn't have a DelegatingIntroductionInterceptor because it handles + introductions without using interception. So all the unit tests show how similar + things can be done in Spring.NET. + + Rod Johnson + Choy Rim (.NET) + $Id: DelegatingIntroductionInterceptorTests.cs,v 1.6 2004/10/10 08:01:49 gcaprio Exp $ + + + + test introduction. + It must include the IAdvice marker interface to be a + valid introduction. + + + + + Useful implementation + that checks calls to GetTarget and ReleaseTarget. + + Rod Johnson + Bruno Baia (.NET) + $Id: MockTargetSource.cs,v 1.1 2006/08/10 18:45:58 bbaia Exp $ + + + + Additional and overridden tests for the inheritance-based proxy. + + Bruno Baia + $Id: InheritanceAopProxyTests.cs,v 1.2 2008/03/03 09:29:17 bbaia Exp $ + + + + Unit tests for the UnknownAdviceTypeException class. + + Rick Evans + $Id: UnknownAdviceTypeExceptionTests.cs,v 1.2 2006/04/09 07:19:05 markpollack Exp $ + + + + Unit tests for the CacheParameterAdvice class. + + Aleksandar Seovic + $Id: ParameterValidationAdviceTests.cs,v 1.2 2008/04/02 23:00:46 markpollack Exp $ + + + + Unit tests for the AbstractRegularExpressionMethodPointcut class. + + Rod Johnson + Dmitriy Kopylenko + Simon White (.NET) + + + + Unit tests for the ReflectiveMethodInvocation class. + + Rick Evans + Bruno Baia + $Id: ReflectiveMethodInvocationTests.cs,v 1.8 2008/02/06 18:29:05 bbaia Exp $ + + + + Unit tests for the DynamicMethodInvocation class. + + Bruno Baia + $Id: DynamicMethodInvocationTests.cs,v 1.2 2008/02/06 18:29:05 bbaia Exp $ + + + + Unit tests for the ThrowsAdviceInterceptor class. + + Rod Johnson + Simon White (.NET) + + + + Unit tests for the SdkRegularExpressionMethodPointcut class. + + Dmitriy Kopylenko + Rick Evans (.NET) + + + + The setup logic executed before the execution of this test fixture. + + + + + Returns the method pointcut implementation to be tested. + + The implementation. + + + + This exercises the logger after deserialization. + + + + + Test introduction ported from Spring.Java. + + + The name is deceptive because it isn't implemented as an interceptor. + It's an introduction which is handled differently in Spring.NET. + Keeping the name is useful as a placeholder for future porting work. + + Spring.Java Folks + Choy Rim (.NET) + + + + This is simple implementation of IFactoryObject that creates a TestObject. + + Mark Pollack + $Id: CreatesTestObject.cs,v 1.1 2007/09/07 01:53:02 markpollack Exp $ + + + + Tests for auto proxy creation by advisor recognition. + + Rod Johnson + Mark Pollack (.NET) + $Id: AdvisorAutoProxyCreatorTests.cs,v 1.4 2007/08/01 17:56:25 markpollack Exp $ + + + + No pointcuts match the methods on NoSetterProperties therefore + there should be proxying. + + + + + A pointcut matches the property (i.e. method set_Age) on TestObject + therefore there should be proxying. + + + + + + + Juergen Hoeller + Simon White (.NET) + + + + Unit tests for all of the exception classes in the Spring.Aop library... + + Rick Evans + $Id: AopExceptionTests.cs,v 1.3 2006/04/09 07:19:04 markpollack Exp $ + + + + Unit tests for the SingletonTargetSource class. + + Rick Evans + $Id: SingletonTargetSourceTests.cs,v 1.2 2006/04/09 07:19:06 markpollack Exp $ + + + + Unit tests for the AopContext class. + + Rick Evans + $Id: AopContextTests.cs,v 1.6 2007/05/30 21:06:25 oakinger Exp $ + + + + Unit tests for the InvalidateCacheAdvice class. + + Aleksandar Seovic + $Id: InvalidateCacheAdviceTests.cs,v 1.4 2007/04/01 15:05:34 bbaia Exp $ + + + + Unit tests for the TrueTypeFilter class. + + Rick Evans + $Id: TrueTypeFilterTests.cs,v 1.3 2006/04/09 07:19:06 markpollack Exp $ + + + + Unit tests for RegularExpressionMethodPointcutAdvisorTests. + + Rod Johnson + Simon White (.NET) + + + + Basic use case, a single pattern defined. + + + + + Multiple patterns defined within a single advisor. + + + + + Unit tests for the RootTypeFilter class. + + Rick Evans + $Id: RootTypeFilterTests.cs,v 1.2 2006/04/09 07:19:06 markpollack Exp $ + +
    +
    diff --git a/test/Spring/Spring.Core.Tests/AssemblyInfo.cs b/test/Spring/Spring.Core.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..2e09197e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Spring.Core Tests")] +[assembly: AssemblyDescription("Unit tests for Spring.Core assembly")] diff --git a/test/Spring/Spring.Core.Tests/Caching/AbstractCacheTests.cs b/test/Spring/Spring.Core.Tests/Caching/AbstractCacheTests.cs new file mode 100644 index 00000000..80ece55e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Caching/AbstractCacheTests.cs @@ -0,0 +1,181 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Threading; +using NUnit.Framework; +using Rhino.Mocks; + +#endregion + +namespace Spring.Caching +{ + /// + /// Tests behaviour to ensure, + /// that derived classes maybe rely on this default behaviour + /// + /// Erich Eichinger + /// $Id: AbstractCacheTests.cs,v 1.1 2007/08/25 14:26:36 oakinger Exp $ + [TestFixture] + public class AbstractCacheTests + { + #region ExposingAbstractCache utility class + + /// + /// Exposes DoInsert() method for testing + /// + public abstract class ExposingAbstractCache : AbstractCache + { + protected override void DoInsert(object key, object value, TimeSpan timeToLive) + { + DoInsertExposed(key, value, timeToLive); + } + + public abstract void DoInsertExposed(object key, object value, TimeSpan timeToLive); + } + + #endregion + + TimeSpan expectedPerItemTTL; + TimeSpan expectedPerCacheTTL; + MockRepository mocks; + ExposingAbstractCache cache; + string[] KEYS = new string[] { "keyA", "keyB" }; + + [SetUp] + public void SetUp() + { + mocks = new MockRepository(); + cache = (ExposingAbstractCache)mocks.PartialMock(typeof(ExposingAbstractCache)); + + expectedPerItemTTL = new TimeSpan(0, 0, 10); + expectedPerCacheTTL = new TimeSpan(0, 0, 20); + + cache.TimeToLive = expectedPerCacheTTL; + } + + [Test] + public void TestDefaults() + { + ExposingAbstractCache localCache = (ExposingAbstractCache)mocks.PartialMock(typeof(ExposingAbstractCache)); + Assert.AreEqual(TimeSpan.Zero, localCache.TimeToLive); + Assert.AreEqual(false, localCache.EnforceTimeToLive); + } + + [Test] + public void AppliesPerCacheDefaultsIfNoPerItemValuesGiven() + { + // set expectations + cache.DoInsertExposed("key", "value", expectedPerCacheTTL); + mocks.ReplayAll(); + // verify + cache.Insert("key", "value", expectedPerCacheTTL); + mocks.VerifyAll(); + } + + [Test] + public void AppliesPerCacheDefaultsIfTTLLessThanZero() + { + // set expectations + cache.DoInsertExposed("key", "value", expectedPerCacheTTL); + cache.DoInsertExposed("key", "value", expectedPerCacheTTL); + cache.DoInsertExposed("key", "value", expectedPerCacheTTL); + cache.DoInsertExposed("key", "value", expectedPerCacheTTL); + mocks.ReplayAll(); + // verify + cache.Insert("key", "value", new TimeSpan(Timeout.Infinite)); + cache.Insert("key", "value", new TimeSpan(-1)); + cache.Insert("key", "value", new TimeSpan(Int64.MinValue)); + cache.Insert("key", "value", TimeSpan.MinValue); + mocks.VerifyAll(); + } + + [Test] + public void AppliesPerCacheDefaultsIfEnfored() + { + // set expectations + cache.DoInsertExposed("key", "value", expectedPerCacheTTL); + mocks.ReplayAll(); + // verify + cache.EnforceTimeToLive = true; + cache.Insert("key", "value", expectedPerItemTTL); + mocks.VerifyAll(); + } + + [Test] + public void AppliesZeroTTLIfTTLIsZero() + { + // set expectations + cache.DoInsertExposed("key", "value", TimeSpan.Zero); + mocks.ReplayAll(); + // verify + cache.Insert("key", "value", TimeSpan.Zero); + mocks.VerifyAll(); + } + + [Test] + public void AppliesPerItemTTLIfTTLGreaterZero() + { + // set expectations + cache.DoInsertExposed("key", "value", expectedPerItemTTL); + mocks.ReplayAll(); + // verify + cache.Insert("key", "value", expectedPerItemTTL); + mocks.VerifyAll(); + } + + [Test] + public void RemoveAllCausesCallsToRemove() + { + // set expectations + cache.Remove(KEYS[0]); + cache.Remove(KEYS[1]); + mocks.ReplayAll(); + // verify + cache.RemoveAll(KEYS); + mocks.VerifyAll(); + } + + [Test] + public void ClearCausesCallToRemoveAllUsingKeys() + { + // set expectations + Expect.Call(cache.Keys).Return(KEYS); + cache.RemoveAll(KEYS); + mocks.ReplayAll(); + // verify + cache.Clear(); + mocks.VerifyAll(); + } + + [Test] + public void CountUsingKeys() + { + // set expectations + Expect.Call(cache.Keys).Return(this.KEYS); + mocks.ReplayAll(); + // verify + Assert.AreEqual( this.KEYS.Length, cache.Count ); + mocks.VerifyAll(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Caching/BaseCacheAttributeTests.cs b/test/Spring/Spring.Core.Tests/Caching/BaseCacheAttributeTests.cs new file mode 100644 index 00000000..5d50d5b7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Caching/BaseCacheAttributeTests.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Caching +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: BaseCacheAttributeTests.cs,v 1.1 2007/08/29 17:30:00 oakinger Exp $ + [TestFixture] + public class BaseCacheAttributeTests + { + private BaseCacheAttribute att; + + private class DerivedCacheAttribute : BaseCacheAttribute + { + } + + public string TestProperty + { + get { return "OK"; } + } + + [SetUp] + public void SetUp() + { + att = new DerivedCacheAttribute(); + } + + [Test] + public void CacheNameIsSet() + { + att.CacheName = "MyCache"; + Assert.AreEqual("MyCache", att.CacheName); + } + + [Test] + public void KeyIsParsed() + { + att.Key = "TestProperty"; + string result = att.KeyExpression.GetValue(this) as string; + Assert.AreEqual("OK", result); + } + + [Test] + public void ConditionIsParsed() + { + att.Condition = "TestProperty"; + string result = att.ConditionExpression.GetValue(this) as string; + Assert.AreEqual("OK", result); + } + + [Test] + public void AllowsForExtendedTimeSpanConverterSyntax() + { + att.TimeToLive = "5ms"; + Assert.AreEqual( new TimeSpan(0,0,0,0,5), att.TimeToLiveTimeSpan ); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Collections/AbstractQueueTests.cs b/test/Spring/Spring.Core.Tests/Collections/AbstractQueueTests.cs new file mode 100644 index 00000000..0039b9cf --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Collections/AbstractQueueTests.cs @@ -0,0 +1,225 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; + +#endregion + +namespace Spring.Collections +{ + /// + /// Unit tests for the AbstractQueue class. + /// + /// Griffin Caprio + /// $Id: AbstractQueueTests.cs,v 1.7 2006/09/30 18:39:24 gcaprio Exp $ + [TestFixture] + public sealed class AbstractQueueTests + { + private sealed class SucceedQueue : AbstractQueue + { + + public override int Count + { + get { return 0; } + } + public override int Capacity + { + get + { + return 0; + } + } + public override bool Offer(object x) + { + if (x == null) + { + throw new NullReferenceException(); + } + return true; + } + + public override object Peek() + { + return Int32.Parse("1"); + } + + public override object Poll() + { + return Int32.Parse("1"); + } + + public override IEnumerator GetEnumerator() + { + return null; + } + + public override void CopyTo(Array array, Int32 index) + { + } + + public override object SyncRoot + { + get { return null; } + + } + + public override Boolean IsSynchronized + { + get { return false; } + + } + + public override bool IsEmpty + { + get { throw new NotImplementedException(); } + } + } + + private sealed class FailQueue : AbstractQueue + { + public override int Count + { + get { return 0; } + } + public override int Capacity + { + get + { + return 0; + } + } + public override bool Offer(object x) + { + if (x == null) + { + throw new NullReferenceException(); + } + return false; + } + + public override object Peek() + { + return null; + } + + public override object Poll() + { + return null; + } + + public override IEnumerator GetEnumerator() + { + return null; + } + + public override void CopyTo(Array array, Int32 index) + { + } + + public override object SyncRoot + { + get { return null; } + } + + public override Boolean IsSynchronized + { + get { return false; } + } + + public override bool IsEmpty + { + get { throw new NotImplementedException(); } + } + } + + [Test] + public void AddSucceed() + { + SucceedQueue q = new SucceedQueue(); + Assert.IsTrue(q.Add(Int32.Parse("2"))); + } + + [Test] + [ExpectedException(typeof (InvalidOperationException))] + public void AddFail() + { + FailQueue q = new FailQueue(); + q.Add(Int32.Parse("1")); + } + + [Test] + [ExpectedException(typeof (NullReferenceException))] + public void AddNPE() + { + SucceedQueue q = new SucceedQueue(); + q.Add(null); + } + + [Test] + public void RemoveSucceed() + { + SucceedQueue q = new SucceedQueue(); + q.Remove(); + } + + [Test] + [ExpectedException(typeof (NoElementsException))] + public void RemoveFail() + { + FailQueue q = new FailQueue(); + q.Remove(); + } + + [Test] + public void ElementSucceed() + { + SucceedQueue q = new SucceedQueue(); + q.Element(); + } + + [Test] + [ExpectedException(typeof (NoElementsException))] + public void ElementF() + { + FailQueue q = new FailQueue(); + q.Element(); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddAll1() + { + SucceedQueue q = new SucceedQueue(); + q.AddAll(null); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void AddAllSelf() + { + SucceedQueue q = new SucceedQueue(); + q.AddAll(q); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Collections/HashedSetTests.cs b/test/Spring/Spring.Core.Tests/Collections/HashedSetTests.cs new file mode 100644 index 00000000..52fffe4f --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Collections/HashedSetTests.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Collections +{ + /// + /// Unit tests for the HashedSet class. + /// + /// Rick Evans + /// $Id: HashedSetTests.cs,v 1.3 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public class HashedSetTests : SetTests + { + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public override void SetUp () + { + Set = new HashedSet (); + SetForSetOps = new HashedSet (); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Collections/HybridSetTests.cs b/test/Spring/Spring.Core.Tests/Collections/HybridSetTests.cs new file mode 100644 index 00000000..894e5078 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Collections/HybridSetTests.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Collections +{ + /// + /// Unit tests for the HybridSet class. + /// + /// Rick Evans + /// $Id: HybridSetTests.cs,v 1.3 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public class HybridSetTests : SetTests + { + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public override void SetUp () + { + Set = new HybridSet (); + SetForSetOps = new HybridSet (); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Collections/ImmutableSetTests.cs b/test/Spring/Spring.Core.Tests/Collections/ImmutableSetTests.cs new file mode 100644 index 00000000..8804c55a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Collections/ImmutableSetTests.cs @@ -0,0 +1,206 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Collections +{ + /// + /// Unit tests for the ImmutableSet wrapper class. + /// + /// Rick Evans + /// $Id: ImmutableSetTests.cs,v 1.2 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public class ImmutableSetTests + { + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp () { + Set = new ImmutableSet (new HybridSet (new object [] {1, 2, 3})); + } + + /// + /// The tear down logic executed after the execution of each individual test. + /// + [TearDown] + public void TearDown () { + Set = null; + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void Add() + { + Set.Add (1); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void AddAll() + { + Set.AddAll (new int [] {4, 5, 6}); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void Remove() + { + Set.Remove (1); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void RemoveAll() + { + object [] removed = new object [] {1, 3}; + Set.RemoveAll (removed); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void RetainAll() + { + Set.RetainAll (new object [] {1, 9}); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void Clear() + { + Set.Clear (); + } + + [Test] + public void IsEmpty() { + Assert.IsFalse (Set.IsEmpty); + ISet mySet = new ImmutableSet (new HybridSet ()); + Assert.IsTrue (mySet.IsEmpty); + } + + [Test] + public void Contains() { + Assert.IsFalse (Set.Contains ("Funk")); + Assert.IsTrue (Set.Contains (1)); + } + + [Test] + public void ContainsAll() { + Assert.IsFalse (Set.ContainsAll (new object [] {"Funk", 1, 2, 3})); + Assert.IsTrue (Set.ContainsAll (new object [] {1, 3, 2})); + } + + [Test] + public void CopyTo() { + int [] expected = new int [] {1, 2, 3}; + int [] actual = new int [Set.Count]; + Set.CopyTo (actual, 0); + Assert.AreEqual (expected.Length, actual.Length); + for (int i = 0; i < expected.Length; ++i) { + Assert.AreEqual (expected [i], actual [i]); + } + } + + [Test] + public void EnumeratesOk () { + int [] expected = new int [] {1, 2, 3}; + int i = 0; + foreach (int actual in Set) { + Assert.AreEqual (expected [i++], actual); + } + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ClonedInstanceMustStillBeImmutable () + { + ISet clone = (ISet) Set.Clone (); + clone.Add ("bad chair, bad chair"); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void MinusYieldsImmutableCone () + { + ISet mySet = new ListSet (new int [] {1, 2}); + ISet clone = Set.Minus (mySet); + Assert.IsNotNull (clone); + Assert.AreEqual (1, clone.Count); + clone.Add ("bad chair, bad chair"); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void UnionYieldsImmutableCone () + { + ISet mySet = new ListSet (new int [] {1, 4}); + ISet clone = Set.Union (mySet); + Assert.IsNotNull (clone); + Assert.AreEqual (4, clone.Count); + clone.Add ("bad chair, bad chair"); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void IntersectionYieldsImmutableCone () + { + ISet mySet = new ListSet (new int [] {1, 4}); + ISet clone = Set.Intersect (mySet); + Assert.IsNotNull (clone); + Assert.AreEqual (1, clone.Count); + clone.Add ("bad chair, bad chair"); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ExclusiveOrYieldsImmutableCone () + { + ISet mySet = new ListSet (new int [] {1, 4}); + ISet clone = Set.ExclusiveOr (mySet); + Assert.IsNotNull (clone); + Assert.AreEqual (3, clone.Count); + clone.Add ("bad chair, bad chair"); + } + + /// + /// The ISet being tested. + /// + protected virtual ISet Set + { + get + { + return _set; + } + set + { + _set = value; + } + } + + private ISet _set; + } +} diff --git a/test/Spring/Spring.Core.Tests/Collections/LinkedListTests.cs b/test/Spring/Spring.Core.Tests/Collections/LinkedListTests.cs new file mode 100644 index 00000000..ddbe82f1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Collections/LinkedListTests.cs @@ -0,0 +1,250 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using NUnit.Framework; + +#endregion + +namespace Spring.Collections +{ + /// + /// Set of tests for Spring.Util.LinkedList. + /// + /// Simon White + [TestFixture] + public sealed class LinkedListTests + { + [Test] + public void AddMultipleObjects() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + ll.Add("item2"); + ll.Add("item3"); + Assert.IsTrue(ll.Count == 3, "Expected 3 items not " + ll.Count); + Assert.IsTrue(ll[0].Equals("item1"), "Expected first element to be \"item1\" not " + ll[0]); + Assert.IsTrue(ll[1].Equals("item2"), "Expected second element to be \"item2\" not " + ll[1]); + } + + [Test] + public void Insert() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + ll.Add("item2"); + ll.Insert(1, "item1andahalf"); + Assert.IsTrue(ll.Count == 3, "Expected 3 items not " + ll.Count); + Assert.IsTrue(ll[0].Equals("item1"), "Expected first element to be \"item1\" not " + ll[0]); + Assert.IsTrue(ll[1].Equals("item1andahalf"), "Expected second element to be \"item1andahalf\" not " + ll[1]); + Assert.IsTrue(ll[2].Equals("item2"), "Expected third element to be \"item2\" not " + ll[0]); + } + + [Test] + public void RemoveAt() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + ll.Add("item2"); + ll.Add("item3"); + ll.RemoveAt(1); + Assert.IsTrue(ll.Count == 2, "Expected 2 items not " + ll.Count); + Assert.IsTrue(ll[0].Equals("item1"), "Expected first element to be \"item1\" not " + ll[0]); + Assert.IsTrue(ll[1].Equals("item3"), "Expected second element to be \"item3\" not " + ll[1]); + } + + [Test] + public void RemoveObject() + { + string item1 = "item1"; + string item2 = "item2"; + string item3 = "item3"; + LinkedList ll = new LinkedList(); + ll.Add(item1); + ll.Add(item2); + ll.Add(item3); + ll.Remove(item2); + Assert.IsTrue(ll.Count == 2, "Expected 2 items not " + ll.Count); + Assert.IsTrue(ll[0].Equals("item1"), "Expected first element to be \"item1\" not " + ll[0]); + Assert.IsTrue(ll[1].Equals("item3"), "Expected second element to be \"item3\" not " + ll[1]); + } + + [Test] + [ExpectedException(typeof(System.ArgumentOutOfRangeException))] + public void RemoveAtOnEmptyLinkedList() + { + LinkedList ll = new LinkedList(); + ll.RemoveAt(0); + } + + [Test] + public void Enumerator() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + ll.Add("item2"); + ll.Add("item3"); + IEnumerator ienum = ll.GetEnumerator(); + Assert.IsTrue(ienum.MoveNext()); + Assert.IsTrue(ienum.Current.Equals("item1"), "Expected first element to be \"item1\" not " + ienum.Current); + Assert.IsTrue(ienum.MoveNext()); + Assert.IsTrue(ienum.Current.Equals("item2"), "Expected second element to be \"item2\" not " + ienum.Current); + } + + [Test] + [ExpectedException(typeof(System.InvalidOperationException))] + public void EnumeratorModification() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + ll.Add("item2"); + ll.Add("item3"); + IEnumerator ienum = ll.GetEnumerator(); + Assert.IsTrue(ienum.MoveNext()); + ll.RemoveAt(0); + ienum.MoveNext(); + Assert.Fail("Expected enumerator to fail with InvalidOperationException"); + } + + [Test] + public void ConstructorWithIList() + { + ArrayList al = new ArrayList(); + al.Add("al1"); + al.Add("al2"); + al.Add("al3"); + LinkedList ll = new LinkedList(al); + Assert.IsTrue(ll.Count == 3, "Expected 3 items not " + ll.Count); + Assert.IsTrue(ll[0].Equals("al1"), "Expected first element to be \"al1\" not " + ll[0]); + Assert.IsTrue(ll[1].Equals("al2"), "Expected second element to be \"al2\" not " + ll[1]); + } + + [Test] + public void IndexOfObject() + { + string item1 = "item1"; + string item2 = "item2"; + string item3 = "item3"; + LinkedList ll = new LinkedList(); + ll.Add(item1); + ll.Add(item2); + ll.Add(item3); + int index = ll.IndexOf(item2); + Assert.IsTrue(index == 1, "Expected index of 1 not " + index); + } + + [Test] + public void CopyToWithZeroIndex() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + ll.Add("item2"); + ll.Add("item3"); + string[] strings = new string[3]; + ll.CopyTo(strings, 0); + Assert.IsTrue(strings[0].Equals("item1"), "Expected first element to be \"item1\" not " + strings[0]); + Assert.IsTrue(strings[1].Equals("item2"), "Expected second element to be \"item2\" not " + strings[1]); + Assert.IsTrue(strings[2].Equals("item3"), "Expected third element to be \"item3\" not " + strings[2]); + } + + [Test] + public void CopyToWithNonZeroIndex() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + ll.Add("item2"); + ll.Add("item3"); + string[] strings = new string[5]; + strings[0] = "string1"; + strings[1] = "string2"; + ll.CopyTo(strings, 2); + Assert.IsTrue(strings[0].Equals("string1"), "Expected first element to be \"string1\" not " + strings[0]); + Assert.IsTrue(strings[1].Equals("string2"), "Expected second element to be \"string2\" not " + strings[1]); + Assert.IsTrue(strings[2].Equals("item1"), "Expected third element to be \"item1\" not " + strings[2]); + Assert.IsTrue(strings[3].Equals("item2"), "Expected fourth element to be \"item2\" not " + strings[3]); + Assert.IsTrue(strings[4].Equals("item3"), "Expected fifth element to be \"item3\" not " + strings[4]); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void CopyToWithNullArray() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + ll.CopyTo(null, 0); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void CopyToWithNegativeIndex() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + string[] strings = new string[1]; + ll.CopyTo(strings, -1); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void CopyToWithIndexGreaterThanArrayLength() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + string[] strings = new string[1]; + ll.CopyTo(strings, 2); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void CopyToWithInsufficientSizeArray() + { + LinkedList ll = new LinkedList(); + ll.Add("item1"); + ll.Add("item2"); + string[] strings = new string[2]; + ll.CopyTo(strings, 1); + } + + [Test] + public void Contains() + { + LinkedList ll = new LinkedList(); + Assert.IsFalse(ll.Contains("Foo")); + + ll = new LinkedList(); + Assert.IsFalse(ll.Contains(null)); + ll.Add("Foo"); + ll.Add(null); + ll.Add("Bar"); + Assert.IsTrue(ll.Contains(null)); + Assert.IsTrue(ll.Contains("Bar")); + Assert.IsTrue(ll.Contains("Foo")); + + ll = new LinkedList(); + ll.Add("Foo"); + ll.Add("Bar"); + Assert.IsFalse(ll.Contains(null)); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Collections/ListSetTests.cs b/test/Spring/Spring.Core.Tests/Collections/ListSetTests.cs new file mode 100644 index 00000000..ff61f180 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Collections/ListSetTests.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Collections +{ + /// + /// Unit tests for the ListSet class. + /// + /// Rick Evans + /// $Id: ListSetTests.cs,v 1.3 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public class ListSetTests : SetTests + { + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public override void SetUp () + { + Set = new ListSet (); + SetForSetOps = new ListSet (); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Collections/PriorityQueueTests.cs b/test/Spring/Spring.Core.Tests/Collections/PriorityQueueTests.cs new file mode 100644 index 00000000..096e032c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Collections/PriorityQueueTests.cs @@ -0,0 +1,463 @@ +using System; +using System.Collections; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using NUnit.Framework; +using Spring.Util; + +namespace Spring.Collections +{ + [TestFixture] + public class PriorityQueueTests + { + protected static Int32 zero = Int32.Parse("0"); + protected static Int32 one = Int32.Parse("1"); + protected static Int32 two = Int32.Parse("2"); + protected static Int32 three = Int32.Parse("3"); + protected static Int32 four = Int32.Parse("4"); + protected static Int32 five = Int32.Parse("5"); + protected static Int32 six = Int32.Parse("6"); + protected static Int32 seven = Int32.Parse("7"); + protected static Int32 eight = Int32.Parse("8"); + protected static Int32 nine = Int32.Parse("9"); + protected static Int32 m1 = Int32.Parse("-1"); + protected static Int32 m2 = Int32.Parse("-2"); + protected static Int32 m3 = Int32.Parse("-3"); + protected static Int32 m4 = Int32.Parse("-4"); + protected static Int32 m5 = Int32.Parse("-5"); + protected static Int32 m10 = Int32.Parse("-10"); + private int SIZE = 20; + + internal class MyReverseComparator : IComparer + { + public virtual int Compare(Object x, Object y) + { + int i = (int)x; + int j = (int)y; + if (i < j) + return 1; + if (i > j) + return - 1; + return 0; + } + } + + private PriorityQueue populatedQueue(int n) + { + PriorityQueue q = new PriorityQueue(n); + Assert.IsTrue((q.Count == 0)); + for (int i = n - 1; i >= 0; i -= 2) + Assert.IsTrue(q.Offer(i)); + for (int i = (n & 1); i < n; i += 2) + Assert.IsTrue(q.Offer(i)); + Assert.IsFalse((q.Count == 0)); + Assert.AreEqual(n, q.Count); + return q; + } + + [Test] + public void CreateUnboundedQueue() + { + Assert.AreEqual(0, new PriorityQueue(SIZE).Count); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void ThrowsArgumentExceptionForZeroCapacity() + { + new PriorityQueue(0); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void ThrowsArgumentNullExceptionForNullCollection() + { + PriorityQueue q = new PriorityQueue((ICollection) null); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ThrowsArgumentNullExceptionForNullCollectionElements() + { + object[] ints = new object[SIZE]; + PriorityQueue q = new PriorityQueue(new ArrayList(ints)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void ThrowsArgumentNullExceptionForSomeNullCollectionElements() + { + object[] ints = new object[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = i; + PriorityQueue q = new PriorityQueue(new ArrayList(ints)); + } + + [Test] + public void ConstructorFromExistingCollection() + { + Int32[] ints = new Int32[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = i; + PriorityQueue q = new PriorityQueue(new ArrayList(ints)); + for (int i = 0; i < SIZE; ++i) + Assert.AreEqual(ints[i], q.Poll()); + Assert.IsTrue( q.IsEmpty ); + } + + [Test] + public void ConstructorUsingComparator() + { + MyReverseComparator cmp = new MyReverseComparator(); + PriorityQueue q = new PriorityQueue(SIZE, cmp); + Assert.AreEqual(cmp, q.Comparator()); + Int32[] ints = new Int32[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = i; + q.AddAll(new ArrayList(ints)); + for (int i = SIZE - 1; i >= 0; --i) + Assert.AreEqual(ints[i], q.Poll()); + Assert.IsTrue( q.IsEmpty ); + } + + [Test] + public void IsEmpty() + { + PriorityQueue q = new PriorityQueue(2); + Assert.IsTrue( q.IsEmpty ); + q.Add(1); + Assert.IsFalse( q.IsEmpty ); + q.Add(2); + q.Remove(); + q.Remove(); + Assert.IsTrue( q.IsEmpty ); + } + + [Test] + public void Size() + { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) + { + Assert.AreEqual(SIZE - i, q.Count); + q.Remove(); + } + for (int i = 0; i < SIZE; ++i) + { + Assert.AreEqual(i, q.Count); + q.Add(i); + } + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void OfferWithNullObject() + { + PriorityQueue q = new PriorityQueue(1); + q.Offer(null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddWithNullObject() + { + PriorityQueue q = new PriorityQueue(1); + q.Add(null); + } + + [Test] + public void OfferWithComparableElements() + { + PriorityQueue q = new PriorityQueue(1); + Assert.IsTrue(q.Offer(zero)); + Assert.IsTrue(q.Offer(one)); + } + + [Test] + [ExpectedException(typeof (InvalidCastException))] + public void OfferNonComparable() + { + PriorityQueue q = new PriorityQueue(1); + q.Offer(new Object()); + q.Offer(new Object()); + q.Offer(new Object()); + } + + [Test] + public void AddWithComparableElements() + { + PriorityQueue q = new PriorityQueue(SIZE); + for (int i = 0; i < SIZE; ++i) + { + Assert.AreEqual(i, q.Count); + Assert.IsTrue(q.Add(i)); + } + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddAllWithNullElements() + { + PriorityQueue q = new PriorityQueue(1); + q.AddAll(null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddAllWithCollectionWithNullElements() + { + PriorityQueue q = new PriorityQueue(SIZE); + object[] ints = new object[SIZE]; + q.AddAll(new ArrayList(ints)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddAllWithCollectionWithSomeNullElements() + { + PriorityQueue q = new PriorityQueue(SIZE); + object[] ints = new object[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = i; + q.AddAll(new ArrayList(ints)); + } + + [Test] + public void AddAllWithCollection() + { + Int32[] empty = new Int32[0]; + Int32[] ints = new Int32[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = (SIZE - 1 - i); + PriorityQueue q = new PriorityQueue(SIZE); + Assert.IsFalse(q.AddAll(new ArrayList(empty))); + Assert.IsTrue(q.AddAll(new ArrayList(ints))); + for (int i = 0; i < SIZE; ++i) + Assert.AreEqual(i, q.Poll()); + } + + [Test] + public void Poll() + { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) + { + Assert.AreEqual(i, (q.Poll())); + } + Assert.IsNull(q.Poll()); + } + + [Test] + public void Peek() + { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) + { + Assert.AreEqual(i, (q.Peek())); + q.Poll(); + Assert.IsTrue(q.Peek() == null || i != (int)(q.Peek())); + } + Assert.IsNull(q.Peek()); + } + + [Test] + [ExpectedException(typeof (NoElementsException))] + public void Element() + { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) + { + Assert.AreEqual(i, (q.Element())); + q.Poll(); + } + q.Element(); + } + + [Test] + [ExpectedException(typeof (NoElementsException))] + public void Remove() + { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) + { + Assert.AreEqual(i, (q.Remove())); + } + q.Remove(); + } + + [Test] + public void RemoveElement() + { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 1; i < SIZE; i += 2) + { + Assert.IsTrue(q.Remove(i)); + } + for (int i = 0; i < SIZE; i += 2) + { + Assert.IsTrue(q.Remove(i)); + Assert.IsFalse(q.Remove((i + 1))); + } + Assert.IsTrue((q.Count == 0)); + } + + [Test] + public void Contains() + { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) + { + Assert.IsTrue(CollectionUtils.Contains(q, i)); + q.Poll(); + Assert.IsFalse(CollectionUtils.Contains(q, i)); + } + } + + [Test] + public void Clear() + { + PriorityQueue q = populatedQueue(SIZE); + q.Clear(); + Assert.IsTrue((q.Count == 0)); + Assert.AreEqual(0, q.Count); + q.Add(1); + Assert.IsFalse((q.Count == 0)); + q.Clear(); + Assert.IsTrue((q.Count == 0)); + } + + [Test] + public void ContainsAll() + { + PriorityQueue q = populatedQueue(SIZE); + PriorityQueue p = new PriorityQueue(SIZE); + for (int i = 0; i < SIZE; ++i) + { + Assert.IsTrue(CollectionUtils.ContainsAll(q, p)); + Assert.IsFalse(CollectionUtils.ContainsAll(p, q)); + p.Add(i); + } + Assert.IsTrue(CollectionUtils.ContainsAll(p, q)); + } + + [Test] + public void RemoveAll() + { + for (int i = 1; i < SIZE; ++i) + { + PriorityQueue q = populatedQueue(SIZE); + PriorityQueue p = populatedQueue(i); + CollectionUtils.RemoveAll(q, p); + Assert.AreEqual(SIZE - i, q.Count); + for (int j = 0; j < i; ++j) + { + Int32 I = (int) p.Remove(); + Assert.IsFalse(CollectionUtils.Contains(q, I)); + } + } + } + + [Test] + public void ToArrayObject() + { + PriorityQueue q = populatedQueue(SIZE); + Object[] o = (Object[]) CollectionUtils.ToArrayList(q).ToArray(typeof (object)); + Array.Sort(o); + for (int i = 0; i < o.Length; i++) + Assert.AreEqual(o[i], q.Poll()); + } + + [Test] + public void ToArrayNonObject() + { + PriorityQueue q = populatedQueue(SIZE); + Int32[] ints = (Int32[]) CollectionUtils.ToArrayList(q).ToArray(typeof (int)); + Array.Sort(ints); + for (int i = 0; i < ints.Length; i++) + Assert.AreEqual(ints[i], q.Poll()); + } + + [Test] + public void Iterator() + { + PriorityQueue q = populatedQueue(SIZE); + int i = 0; + IEnumerator it = q.GetEnumerator(); + while (it.MoveNext()) + { + Assert.IsTrue(CollectionUtils.Contains(q, it.Current)); + ++i; + } + Assert.AreEqual(i, SIZE); + } + + [Test] + public void Serialization() + { + PriorityQueue q = populatedQueue(SIZE); + MemoryStream bout = new MemoryStream(10000); + + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(bout, q); + + MemoryStream bin = new MemoryStream(bout.ToArray()); + BinaryFormatter formatter2 = new BinaryFormatter(); + + PriorityQueue r = (PriorityQueue) formatter2.Deserialize(bin); + + Assert.AreEqual(q.Count, r.Count); + while (!(q.Count == 0)) + Assert.AreEqual(q.Remove(), r.Remove()); + } + [Test] + [ExpectedException(typeof(ArgumentException))] + public void QueueCopyToArrayWithSmallerDestinationArray() + { + PriorityQueue source = populatedQueue(SIZE); + object[] dest = new object[SIZE/2]; + source.CopyTo(dest); + } + [Test] + [ExpectedException(typeof(ArgumentException))] + public void QueueCopyToArrayWithIndexOutOfDestinationArrayRange() + { + PriorityQueue source = populatedQueue(SIZE); + object[] dest = new object[SIZE/2]; + source.CopyTo(dest, 11); + } + [Test] + [ExpectedException(typeof(IndexOutOfRangeException))] + public void QueueCopyToArrayWithValidSizeButInvalidStartingIndex() + { + PriorityQueue source = populatedQueue(SIZE); + object[] dest = new object[SIZE]; + source.CopyTo(dest, 10); + } + [Test] + public void CompleteQueueCopyToArrayWithValidSize() + { + PriorityQueue source = populatedQueue(SIZE); + object[] dest = new object[SIZE]; + source.CopyTo(dest); + for ( int i = 0; i < SIZE; i++ ) + { + Assert.AreEqual(source.Poll(), dest[i]); + } + } + [Test] + public void PartialQueueCopyToArrayWithValidSize() + { + PriorityQueue source = populatedQueue(SIZE); + object[] dest = new object[SIZE*2]; + source.CopyTo(dest, SIZE / 2); + for ( int i = 0; i < SIZE; i++ ) + { + Assert.IsNull(dest[i]); + } + for ( int i = SIZE; i < SIZE / 2; i++ ) + { + Assert.AreEqual(dest[i], i - SIZE / 2 ); + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Collections/SetTests.cs b/test/Spring/Spring.Core.Tests/Collections/SetTests.cs new file mode 100644 index 00000000..75f5925d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Collections/SetTests.cs @@ -0,0 +1,341 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using Spring.Collections; + +using NUnit.Framework; + +#endregion + +namespace Spring.Collections +{ + /// + /// Base class for tests on the + /// interface. + /// + /// + ///

    + /// This defines a set of tests that exercise the contract of the set interface. + /// Subclasses need only supply a concrete ISET imnplementation instance... this + /// instance must be set on the Set property exposed by this class in the + /// SetUp method. The Set instance must be empty for the start of each new test. + ///

    + ///

    + /// The SetForSetOps needs to be set during the SetUp method too. + ///

    + ///
    + /// Rick Evans + /// $Id: SetTests.cs,v 1.3 2006/04/09 07:19:06 markpollack Exp $ + public abstract class SetTests + { + protected static readonly object StuffOne = "This Is"; + + protected static readonly object StuffTwo = "Uncle"; + + protected static readonly object StuffThree = "Bob"; + + protected readonly object [] UniqueStuff = new object [] {StuffOne, StuffTwo, StuffThree}; + + protected readonly object [] DuplicatedStuff = new object [] {StuffOne, StuffTwo, StuffTwo}; + + /// + /// The setup logic executed before the execution of each individual test. + /// + public abstract void SetUp (); + + /// + /// The tear down logic executed after the execution of each individual test. + /// + [TearDown] + public virtual void TearDown () { + Set = null; + SetForSetOps = null; + } + + [Test] + public void Union() + { + Set.AddAll (UniqueStuff); + SetForSetOps.AddAll (new object [] {"Bar", StuffOne}); + ISet union = Set.Union (SetForSetOps); + Assert.IsTrue (union.Count == UniqueStuff.Length + 1); + Assert.IsFalse (object.ReferenceEquals (union, Set), "Got back same instance after set operation."); + Assert.IsTrue (Set.Count == UniqueStuff.Length); + Assert.IsTrue (SetForSetOps.Count == 2); + } + + [Test] + public void Intersect() + { + Set.AddAll (UniqueStuff); + SetForSetOps.AddAll (new object [] {"Bar", StuffOne}); + ISet intersection = Set.Intersect (SetForSetOps); + Assert.IsTrue (intersection.Count == 1); + Assert.IsFalse (object.ReferenceEquals (intersection, Set), "Got back same instance after set operation."); + Assert.IsTrue (Set.Count == UniqueStuff.Length); + Assert.IsTrue (SetForSetOps.Count == 2); + } + + [Test] + public void Minus() + { + Set.AddAll (UniqueStuff); + SetForSetOps.AddAll (new object [] {"Bar", StuffOne}); + ISet minus = Set.Minus (SetForSetOps); + Assert.IsTrue (minus.Count == UniqueStuff.Length - 1); + Assert.IsFalse (object.ReferenceEquals (minus, Set), "Got back same instance after set operation."); + Assert.IsTrue (Set.Count == UniqueStuff.Length); + Assert.IsTrue (SetForSetOps.Count == 2); + } + + [Test] + public void ExclusiveOr() + { + Set.AddAll (UniqueStuff); + SetForSetOps.AddAll (new object [] {"Bar", StuffOne}); + ISet xor = Set.ExclusiveOr (SetForSetOps); + Assert.IsTrue (xor.Count == 3); + Assert.IsTrue (xor.ContainsAll (new object [] {"Bar", StuffTwo, StuffThree})); + Assert.IsFalse (object.ReferenceEquals (xor, Set), "Got back same instance after set operation."); + Assert.IsTrue (Set.Count == UniqueStuff.Length); + Assert.IsTrue (SetForSetOps.Count == 2); + } + + [Test] + public void Contains() + { + Set.AddAll (UniqueStuff); + Assert.IsTrue (Set.Contains (StuffThree)); + Assert.IsFalse (Set.Contains ("SourDough")); + } + + [Test] + public void ContainsAll() + { + Set.AddAll (UniqueStuff); + Assert.IsTrue (Set.ContainsAll (new object [] {StuffThree, StuffTwo, StuffOne})); + } + + [Test] + public void IsEmpty () + { + Set.Add (StuffOne); + Set.Remove (StuffOne); + Assert.IsTrue (Set.IsEmpty); + } + + [Test] + public void Add() + { + Assert.IsTrue (Set.Add (StuffOne)); + Assert.IsTrue (Set.Count == 1, "Added 1 unique item, but the Count property wasn't sitting at 1."); + } + + [Test] + public void AddNull() + { + if (SupportsNull) + { + Assert.IsTrue (Set.Add (StuffOne)); + Assert.IsTrue (Set.Add (null)); + Assert.IsTrue (Set.Count == 2, "Added 2 unique item (one null), but the Count property wasn't sitting at 2."); + Assert.IsFalse (Set.Add (null)); + Assert.IsTrue (Set.Count == 2, "Added null to a set already containing null, but the Count property changed."); + } + } + + [Test] + public void EnumeratesNull() + { + if (SupportsNull) + { + Set.AddAll (new object [] {StuffOne, null, StuffTwo}); + bool gotNull = false; + foreach (object o in Set) + { + if (o == null) + { + gotNull = true; + break; + } + } + Assert.IsTrue (gotNull, "Stuffed a null value into the set but didn't get it back when enumerating over the set."); + } + } + + [Test] + public void CopiesNull () + { + if (SupportsNull) + { + object [] expected = new object [] {StuffOne, null, StuffTwo}; + Set.AddAll (expected); + object [] actual = new object [expected.Length]; + Set.CopyTo (actual, 0); + bool gotNull = false; + foreach (object o in actual) + { + if (o == null) + { + gotNull = true; + break; + } + } + Assert.IsTrue (gotNull, "Copied a set with a null value into an array, but the resulting array did not contain a a null."); + } + } + + [Test] + public void AddDuplicate() + { + Set.Add (StuffOne); + Assert.IsFalse (Set.Add (StuffOne)); + Assert.IsTrue (Set.Count == 1, "Added 2 duplicate items, but the Count property wasn't sitting at 1."); + } + + [Test] + public void AddAll() + { + Assert.IsTrue (Set.AddAll (UniqueStuff)); + Assert.IsTrue (Set.Count == UniqueStuff.Length, "Added 3 unique items, but the Count property wasn't sitting at 3."); + } + + [Test] + public void AddAllDuplicated() + { + Assert.IsTrue (Set.AddAll (DuplicatedStuff)); + Assert.IsTrue (Set.Count == 2, "Added 3 (2 duplicated) items, but the Count property wasn't sitting at 2."); + } + + [Test] + public void Remove() + { + Set.AddAll (UniqueStuff); + Set.Remove (StuffOne); + Assert.IsTrue (Set.Count == (UniqueStuff.Length - 1)); + Assert.IsFalse (Set.Contains (StuffOne)); + Assert.IsTrue (Set.Contains (StuffTwo)); + Assert.IsTrue (Set.Contains (StuffThree)); + } + + [Test] + public void RemoveNull() + { + if (SupportsNull) + { + Set.AddAll (UniqueStuff); + Assert.IsFalse (Set.Remove (null)); + Set.Add (null); + Assert.IsTrue (Set.Remove (null)); + } + } + + [Test] + public void RemoveAll() + { + Set.AddAll (UniqueStuff); + object [] removed = new object [] {StuffOne, StuffTwo}; + Set.RemoveAll (removed); + Assert.IsTrue (Set.Count == (UniqueStuff.Length - removed.Length)); + Assert.IsFalse (Set.Contains (StuffOne)); + Assert.IsFalse (Set.Contains (StuffTwo)); + Assert.IsTrue (Set.Contains (StuffThree)); + } + + [Test] + public void RetainAll() + { + Set.AddAll (UniqueStuff); + Set.RetainAll (new object [] {StuffOne, StuffTwo}); + Assert.IsTrue (Set.Count == 2); + Assert.IsTrue (Set.Contains (StuffOne)); + Assert.IsTrue (Set.Contains (StuffTwo)); + Assert.IsFalse (Set.Contains (StuffThree)); + } + + [Test] + public void Clear() + { + Set.AddAll (UniqueStuff); + Set.Clear (); + Assert.IsTrue (Set.Count == 0, "Calling Clear () did not remove all of the elements."); + } + + /// + /// The ISet being tested. + /// + protected virtual ISet Set + { + get + { + return _set; + } + set + { + _set = value; + } + } + + /// + /// An extra ISet instance for use during the set operation tests. + /// + protected virtual ISet SetForSetOps + { + get + { + return _setForSetOps; + } + set + { + _setForSetOps = value; + } + } + + /// + /// Does the Set being tested support the addition of null values? + /// + /// + ///

    + /// If true, then the tests that test the handling of the null value + /// will be executed. + ///

    + ///
    + protected bool SupportsNull + { + get + { + return _setSupportsNullValue; + } + set + { + _setSupportsNullValue = value; + } + } + + private ISet _set; + private ISet _setForSetOps; + private bool _setSupportsNullValue = true; + } +} diff --git a/test/Spring/Spring.Core.Tests/Collections/SortedSetTests.cs b/test/Spring/Spring.Core.Tests/Collections/SortedSetTests.cs new file mode 100644 index 00000000..7cd619eb --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Collections/SortedSetTests.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Collections +{ + /// + /// Unit tests for the SortedSet class. + /// + /// Rick Evans + /// $Id: SortedSetTests.cs,v 1.3 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public class SortedSetTests : SetTests + { + [TestFixtureSetUp] + public void Init () + { + SupportsNull = false; + } + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public override void SetUp () + { + Set = new SortedSet (); + SetForSetOps = new SortedSet (); + } + + [Test] + public void IsOrdered () + { + ISet mySet = new SortedSet (new int [] {2, 4, 5, 1, 3, 0}); + int j = 0; + foreach (object o in mySet) + { + Assert.AreEqual (j++, o, "Found element out of order while iterating over SortedSet"); + } + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Collections/StaticSetTests.cs b/test/Spring/Spring.Core.Tests/Collections/StaticSetTests.cs new file mode 100644 index 00000000..558bd9cc --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Collections/StaticSetTests.cs @@ -0,0 +1,129 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Collections +{ + /// + /// Unit tests for the static methods of the Set class. + /// + /// Rick Evans + /// $Id: StaticSetTests.cs,v 1.3 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public sealed class StaticSetTests + { + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp () + { + one = new ListSet (new object [] {1, "Foo", 2}); + two = new ListSet (new object [] {1, 3}); + } + + [Test] + public void Xor () + { + ISet actual = Set.ExclusiveOr (one, two); + Assert.IsNotNull (actual); + Assert.AreEqual (3, actual.Count); + Assert.IsTrue (actual.ContainsAll (new object [] {"Foo", 2, 3})); + CheckThatOriginalsHaveNotBeenModified (); + + Assert.IsNull (Set.ExclusiveOr (null, null)); + + actual = Set.ExclusiveOr (null, two); + Assert.AreEqual (two, actual); + + actual = Set.ExclusiveOr (one, null); + Assert.AreEqual (one, actual); + } + + [Test] + public void Minus () + { + ISet actual = Set.Minus (one, two); + Assert.IsNotNull (actual); + Assert.AreEqual (2, actual.Count); + Assert.IsTrue (actual.ContainsAll (new object [] {"Foo", 2})); + CheckThatOriginalsHaveNotBeenModified (); + + Assert.IsNull (Set.Minus (null, two)); + } + + [Test] + public void Union () + { + ISet actual = Set.Union (one, two); + Assert.IsNotNull (actual); + Assert.AreEqual (4, actual.Count); + Assert.IsTrue (actual.ContainsAll (new object [] {1, "Foo", 2, 3})); + CheckThatOriginalsHaveNotBeenModified (); + + Assert.IsNull (Set.Union (null, null)); + + actual = Set.Union (null, two); + Assert.AreEqual (two, actual); + + actual = Set.Union (one, null); + Assert.AreEqual (one, actual); + } + + [Test] + public void Intersect () + { + ISet actual = Set.Intersect (one, two); + Assert.IsNotNull (actual); + Assert.AreEqual (1, actual.Count); + Assert.IsTrue (actual.Contains (1)); + CheckThatOriginalsHaveNotBeenModified (); + + Assert.IsNull (Set.Intersect (null, null)); + + actual = Set.Intersect (null, two); + Assert.AreEqual (0, actual.Count); + + actual = Set.Intersect (one, null); + Assert.AreEqual (0, actual.Count); + } + + private void CheckThatOriginalsHaveNotBeenModified () + { + Assert.IsNotNull (one, "Set operation modified the original sets."); + Assert.AreEqual (3, one.Count, "Set operation modified the original sets."); + Assert.IsTrue (one.ContainsAll (new object [] {1, "Foo", 2}), "Set operation modified the original sets."); + + Assert.IsNotNull (two, "Set operation modified the original sets."); + Assert.AreEqual (2, two.Count, "Set operation modified the original sets."); + Assert.IsTrue (two.ContainsAll (new object [] {1, 3}), "Set operation modified the original sets."); + } + + private ISet one; + private ISet two; + } +} diff --git a/test/Spring/Spring.Core.Tests/CommonTypes.cs b/test/Spring/Spring.Core.Tests/CommonTypes.cs new file mode 100644 index 00000000..7f64d128 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/CommonTypes.cs @@ -0,0 +1,367 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.ComponentModel; +using DotNetMock; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; + +namespace Spring +{ + public class MockConfigurableObjectFactory : MockObject, IConfigurableObjectFactory + { + private ExpectationCounter _destroyCalls = new ExpectationCounter("MockConfigurableObjectFactory.DestroySingletonCalls"); + + public void SetDisposeCalls(int expectedCalls) + { + _destroyCalls.Expected = expectedCalls; + } + + #region IConfigurableObjectFactory Members + + public void RegisterSingleton(string objectName, object singletonObject) + { + } + + public void Dispose() + { + _destroyCalls.Inc(); + } + + public void IgnoreDependencyType(Type type) + { + } + + + public bool IsCurrentlyInCreation(string objectName) + { + throw new NotImplementedException(); + } + + public void AddObjectPostProcessor(IObjectPostProcessor objectPostProcessor) + { + } + + public int ObjectPostProcessorCount + { + get { throw new NotImplementedException(); } + } + + public void RegisterAlias(string objectName, string alias) + { + } + + public void RegisterCustomConverter(Type requiredType, TypeConverter converter) + { + } + + public bool ContainsSingleton(string name) + { + throw new NotImplementedException(); + } + + public IObjectDefinition GetObjectDefinition(string objectName) + { + return null; + } + + public IObjectFactory ParentObjectFactory + { + set + { + } + } + + #endregion + + #region IHierarchicalObjectFactory Members + + IObjectFactory IHierarchicalObjectFactory.ParentObjectFactory + { + get { return null; } + } + + #endregion + + #region IObjectFactory Members + + public object this[string name] + { + get { return null; } + } + + public bool ContainsObject(string name) + { + return false; + } + + public string[] GetAliases(string name) + { + return null; + } + + public object GetObject(string name, Type requiredType) + { + return null; + } + + object IObjectFactory.GetObject(string name) + { + return null; + } + + public object GetObject(string name, object[] arguments) + { + return null; + } + + public object GetObject(string name, Type requiredType, object[] arguments) + { + return null; + } + + public bool IsSingleton(string name) + { + return false; + } + + + public bool IsPrototype(string name) + { + return false; + } + + public Type GetType(string name) + { + return null; + } + + + + public bool IsTypeMatch(string name, Type targetType) + { + return false; + } + + public object ConfigureObject(object target) + { + return null; + } + + public object ConfigureObject(object target, string name) + { + return null; + } + + #endregion + } + + public class MockObjectFactory : MockObject, IObjectFactory + { + private Exception _exceptionToThrow = null; + + public Exception ExceptionToThrow + { + get { return _exceptionToThrow; } + set { _exceptionToThrow = value; } + } + + #region IObjectFactory Members + + public object this[string name] + { + get + { + innerExecute(); + return null; + } + } + + public bool ContainsObject(string name) + { + innerExecute(); + return false; + } + + public string[] GetAliases(string name) + { + innerExecute(); + return null; + } + + public object GetObject(string name, Type requiredType) + { + innerExecute(); + return null; + } + + object IObjectFactory.GetObject(string name) + { + innerExecute(); + return null; + } + + public object GetObject(string name, object[] arguments) + { + innerExecute(); + return null; + } + + public object GetObject(string name, Type requiredType, object[] arguments) + { + innerExecute(); + return null; + } + + public bool IsSingleton(string name) + { + innerExecute(); + return false; + } + + + public bool IsPrototype(string name) + { + innerExecute(); + return false; + } + + public Type GetType(string name) + { + innerExecute(); + return null; + } + + + public bool IsTypeMatch(string name, Type targetType) + { + innerExecute(); + return false; + } + + public object ConfigureObject(object target) + { + return null; + } + + public object ConfigureObject(object target, string name) + { + return null; + } + + #endregion + + private void innerExecute() + { + if (_exceptionToThrow != null) + { + throw _exceptionToThrow; + } + } + + public void Dispose() + { + } + } + + public class Inventor + { + public string Name; + public string Nationality; + public string[] Inventions; +#if NET_2_0 + public DateTime? DateOfGraduation; +#endif + private DateTime dob; + private Place pob; + + public Inventor() : this(null, DateTime.MinValue, null) + {} + + public Inventor(string name, DateTime dateOfBirth, string nationality) + { + this.Name = name; + this.dob = dateOfBirth; + this.Nationality = nationality; + this.pob = new Place(); + } + + public DateTime DOB + { + get { return dob; } + set { dob = value; } + } + + public Place PlaceOfBirth + { + get { return pob; } + } + + public int GetAge(DateTime on) + { + // not very accurate, but it will do the job ;-) + return on.Year - dob.Year; + } + } + + public class Place + { + public string City; + public string Country; + } + + public class Society + { + public string Name = "League of Extraordinary Gentlemen"; + public static string Advisors = "advisors"; + public static string President = "president"; + + private IList members = new ArrayList(); + private IDictionary officers = new Hashtable(); + + public IList Members + { + get { return members; } + } + + public IDictionary Officers + { + get { return officers; } + } + + public bool IsMember(string name) + { + bool found = false; + foreach (Inventor inventor in members) + { + if (inventor.Name == name) + { + found = true; + break; + } + } + return found; + } + } + + +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/ApplicationEventArgsTests.cs b/test/Spring/Spring.Core.Tests/Context/ApplicationEventArgsTests.cs new file mode 100644 index 00000000..63536cd6 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/ApplicationEventArgsTests.cs @@ -0,0 +1,35 @@ +using System; +using NUnit.Framework; + +namespace Spring.Context +{ + [TestFixture] + public class ApplicationEventArgsTests + { + private ApplicationEventArgs _args; + + [SetUp] + public void Init() + { + _args = new ApplicationEventArgs(); + } + + [TearDown] + public void Destroy() + { + _args = null; + } + + [Test] + public void ArgsTimeStamp() + { + Assert.IsTrue(_args.TimeStamp.ToString("MM/dd/yyyy").Equals(DateTime.Now.ToString("MM/dd/yyyy"))); + } + + [Test] + public void ArgsMilliTimestamp() + { + Assert.IsTrue((_args.TimeStamp.Ticks - 621355968000000000)/10000 == _args.EventTimeMilliseconds); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/CommonTypes.cs b/test/Spring/Spring.Core.Tests/Context/CommonTypes.cs new file mode 100644 index 00000000..584cd96f --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/CommonTypes.cs @@ -0,0 +1,584 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using DotNetMock; +using Spring.Context.Support; +using Spring.Core.IO; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Context +{ + /// + /// This class contains common mock implementations of Context interfaces, + /// used for testing. + /// + public class MockMessageSource : MockObject, IMessageSource + { + private ExpectationCounter _getMessageCalls = new ExpectationCounter("MockMessageSource.GetMessageCounter"); + private ExpectationString _getMessageCode = new ExpectationString("MockMessageSource.GetMessageCode"); + private ExpectationString _getMessageDefaultMessage = new ExpectationString("MockMessageSource.GetMessageDefaultMessage"); + private ExpectationArray _getMessageArguments = new ExpectationArray("MockMessageSource.GetMessageArguments"); + + private string _getMessageReturn = null; + + + public MockMessageSource() + { + // this is to ensure, that no expectations may be violated + _getMessageCalls.Expected = -1; + _getMessageCode.Expected = "something very, very unlikely " + Guid.NewGuid(); + _getMessageDefaultMessage.Expected = "something very, very unlikely " + Guid.NewGuid(); + _getMessageArguments.Expected = new object[] { "something very, very unlikely " + Guid.NewGuid() }; + } + + public void SetExpectedGetMessageCalls(int calls) + { + _getMessageCalls.Expected = calls; + } + + public void SetExpectedGetMessageDefaultMessage(string defaultMessage) + { + _getMessageDefaultMessage.Expected = defaultMessage; + } + + public void SetExpectedGetMessageArguments(object[] arguments) + { + _getMessageArguments.Expected = arguments; + } + + public void SetExpectedGetMessageReturn(string message) + { + _getMessageReturn = message; + } + + public void SetExpectedGetMessageCode(string code) + { + _getMessageCode.Expected = code; + } + + #region IMessageSource Members + + public string GetMessage(IMessageSourceResolvable resolvable, CultureInfo culture) + { + _getMessageCalls.Inc(); + return _getMessageReturn; + } + + public string GetMessage(string name, CultureInfo culture, params object[] args) + { + _getMessageCalls.Inc(); + _getMessageCode.Actual = name; + _getMessageArguments.Actual = args; + return _getMessageReturn; + } + + public string GetMessage(string name) + { + _getMessageCalls.Inc(); + _getMessageCode.Actual = name; + return _getMessageReturn; + } + + public string GetMessage(string name, params object[] args) + { + _getMessageCalls.Inc(); + _getMessageCode.Actual = name; + _getMessageArguments.Actual = args; + return _getMessageReturn; + } + + + public string GetMessage(string name, string defaultMessage, CultureInfo culture, params object[] arguments) + { + _getMessageCalls.Inc(); + _getMessageCode.Actual = name; + _getMessageDefaultMessage.Actual = defaultMessage; + _getMessageArguments.Actual = arguments; + return _getMessageReturn; + } + + public string GetMessage(string name, CultureInfo cultureInfo) + { + _getMessageCalls.Inc(); + _getMessageCode.Actual = name; + return _getMessageReturn; + } + + public object GetResourceObject(string name, CultureInfo culture) + { + return null; + } + + public object GetResourceObject(string name) + { + return null; + } + + public void ApplyResources(object value, string objectName, CultureInfo cultureInfo) + { + } + + #endregion + } + + public class MockMessageResolvable : MockObject, IMessageSourceResolvable + { + private string[] _codes; + private string _defaultMessage; + private object[] _arguments; + private ExpectationCounter _getCodesCalls = new ExpectationCounter("MockMessageSource.Codes"); + + public void SetExpectedCodesCalls(int calls) + { + _getCodesCalls.Expected = calls; + } + + public void SetCode(string code) + { + SetCodes(new string[] {code}); + } + + public void SetCodes(string[] codes) + { + _codes = codes; + } + + public void SetDefaultMessage(String defaultMessage) + { + _defaultMessage = defaultMessage; + } + + public void SetArguments(object[] arguments) + { + _arguments = arguments; + } + + #region IMessageSourceResolvable Members + + public object[] GetArguments() + { + return _arguments; + } + + public string[] GetCodes() + { + _getCodesCalls.Inc(); + return _codes; + } + + public string DefaultMessage + { + get { return _defaultMessage; } + } + + #endregion + } + + [Serializable] + public class MockContextAwareObject : MarshalByRefObject, + IApplicationContextAware, IMessageSourceAware, IResourceLoaderAware + { + private IApplicationContext _applicationContext; + private IResourceLoader _resourceLoader; + private IMessageSource _messageSource; + + public IApplicationContext GetApplicationContext() + { + return _applicationContext; + } + + public IApplicationContext ApplicationContext + { + set { _applicationContext = value; } + get { return _applicationContext; } + } + + public IResourceLoader ResourceLoader + { + set { _resourceLoader = value; } + get { return _resourceLoader; } + } + + public IMessageSource MessageSource + { + get { return _messageSource; } + set { _messageSource = value; } + } + } + + public class MockApplicationContext : AbstractApplicationContext, IMockObject + { + private string _mockName; + private bool _isVerified; + private DefaultListableObjectFactory factory; + private ExpectationCounter _closeCalls = new ExpectationCounter("MockConfigurableApplicationContext.CloseCalls"); + + public void SetCloseCalls(int expectedCalls) + { + _closeCalls.Expected = expectedCalls; + } + + public MockApplicationContext() : this(null, null) + { + } + + public MockApplicationContext(string name) : this(name, null) + { + _mockName = name; + factory = new DefaultListableObjectFactory(); +// factory.AddObjectPostProcessor(new ApplicationContextAwareProcessor(this)); + } + + public MockApplicationContext(string name, IApplicationContext parentContext) : base(name, true, parentContext) + { + _mockName = name; + factory = new DefaultListableObjectFactory(); +// factory.AddObjectPostProcessor(new ApplicationContextAwareProcessor(this)); + } + + public override IConfigurableListableObjectFactory ObjectFactory + { + get { return factory; } + } + + protected override void RefreshObjectFactory() + { + } + + public void RegisterSingleton() + { + RootObjectDefinition mcaoDef = new RootObjectDefinition(typeof (MockContextAwareObject), new MutablePropertyValues(), true); + factory.RegisterObjectDefinition("mcao-single", mcaoDef); + } + + public void RegisterObject() + { + RootObjectDefinition mcaoDef = new RootObjectDefinition(typeof (MockContextAwareObject), new MutablePropertyValues(), false); + factory.RegisterObjectDefinition("mcao-proto", mcaoDef); + } + + public override void Dispose() + { + _closeCalls.Inc(); + } + + #region IMockObject Members + + public void NotImplemented(string methodName) + { + throw new NotImplementedException(methodName + " is not currently implemented"); + } + + public string MockName + { + get { return _mockName; } + set { _mockName = value; } + } + + #endregion + + #region IVerifiable Members + + public void Verify() + { + Verifier.Verify(this); + _isVerified = true; + } + + public bool IsVerified + { + get { return _isVerified; } + } + + #endregion + } + + public class MockDefaultApplicationContext : MockObject, IApplicationContext + { + public void Dispose() + { + } + + #region IApplicationContext Members + + public DateTime StartupDate + { + get { return new DateTime(); } + } + + public event ApplicationEventHandler ContextEvent; + + public long StartupDateMilliseconds + { + get { return 0; } + } + + public string Name + { + get { return AbstractApplicationContext.DefaultRootContextName; } + set + { + } + } + + public IApplicationContext ParentContext + { + get { return null; } + set + { + } + } + + #endregion + + #region IListableObjectFactory Members + + public string[] GetObjectDefinitionNames(Type type) + { + return null; + } + + public IObjectDefinition GetObjectDefinition(string name) + { + return null; + } + + public IObjectDefinition GetObjectDefinition(string name, bool includeAncestors) + { + return null; + } + + string[] IListableObjectFactory.GetObjectDefinitionNames() + { + return null; + } + + public string[] GetObjectNamesForType(Type type) + { + return null; + } + + public string[] GetObjectNamesForType( + Type type, bool includePrototypes, bool includeFactoryObjects) + { + return null; + } + + public IDictionary GetObjectsOfType(Type type) + { + return null; + } + + public IDictionary GetObjectsOfType( + Type type, bool includePrototypes, bool includeFactoryObjects) + { + return null; + } + + public int ObjectDefinitionCount + { + get { return 0; } + } + + public bool ContainsObjectDefinition(string name) + { + return false; + } + + #endregion + + #region IObjectFactory Members + + public object this[string name] + { + get { return null; } + } + + public bool ContainsObject(string name) + { + return false; + } + + public string[] GetAliases(string name) + { + return null; + } + + public object GetObject(string name, Type requiredType) + { + return null; + } + + object IObjectFactory.GetObject(string name) + { + return null; + } + + public object GetObject(string name, object[] arguments) + { + return null; + } + + public object GetObject(string name, Type requiredType, object[] arguments) + { + return null; + } + + public bool IsSingleton(string name) + { + return false; + } + + + public bool IsPrototype(string name) + { + return false; + } + + public Type GetType(string name) + { + return null; + } + + + public bool IsTypeMatch(string name, Type targetType) + { + return false; + } + + public object ConfigureObject(object target) + { + return null; + } + + public object ConfigureObject(object target, string name) + { + return null; + } + + public object ConfigureObject(object target, string name, IObjectDefinition definition) + { + return null; + } + + #endregion + + #region IHierarchicalObjectFactory Members + + public IObjectFactory ParentObjectFactory + { + get { return null; } + } + + #endregion + + #region IMessageSource Members + + string IMessageSource.GetMessage(IMessageSourceResolvable resolvable, CultureInfo culture) + { + return null; + } + + string IMessageSource.GetMessage(string name, CultureInfo culture, params object[] args) + { + return null; + } + + string IMessageSource.GetMessage(string name) + { + return null; + } + + string IMessageSource.GetMessage(string name, params object[] args) + { + return null; + } + + string IMessageSource.GetMessage(string name, CultureInfo cultureInfo) + { + return null; + } + + object IMessageSource.GetResourceObject(string name, CultureInfo culture) + { + return null; + } + + object IMessageSource.GetResourceObject(string name) + { + return null; + } + + + public string GetMessage(string name, string defaultMessage, CultureInfo culture, params object[] arguments) + { + return null; + } + + void IMessageSource.ApplyResources(object value, string objectName, CultureInfo cultureInfo) + { + } + + #endregion + + #region IResourceLoader Members + + public IResource GetResource(string location) + { + return null; + } + + #endregion + + #region IEventRegistry Members + + public void PublishEvents(object sourceObject) + { + throw new NotImplementedException(); + } + + public void Subscribe(object subscriber) + { + throw new NotImplementedException(); + } + + public void Subscribe(object subscriber, Type targetSourceType) + { + throw new NotImplementedException(); + } + + #endregion + + public void PublishEvent(object sender, ApplicationEventArgs e) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/ContextExceptionTests.cs b/test/Spring/Spring.Core.Tests/Context/ContextExceptionTests.cs new file mode 100644 index 00000000..3d5a6028 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/ContextExceptionTests.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using NUnit.Framework; + +#endregion + +namespace Spring.Context +{ + /// + /// Unit tests for all of the exception classes in the Spring.Context library... + /// + /// Rick Evans + /// $Id: ContextExceptionTests.cs,v 1.2 2006/04/09 07:19:06 markpollack Exp $ + [TestFixture] + public sealed class ContextExceptionTests : ExceptionsTest + { + [TestFixtureSetUp] + public void FixtureSetUp () + { + AssemblyToCheck = Assembly.GetAssembly (typeof (Spring.Context.ApplicationContextException)); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/ContextListenerObject.cs b/test/Spring/Spring.Core.Tests/Context/ContextListenerObject.cs new file mode 100644 index 00000000..9844a6a3 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/ContextListenerObject.cs @@ -0,0 +1,104 @@ +using System; +using Spring.Context.Events; + +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Context +{ + /// + /// Test object for receiving application context events. + /// + /// Mark Pollack + public sealed class ContextListenerObject : IApplicationContextAware, IApplicationEventListener + { + private IApplicationContext _ctx; + private bool _appListenerContextRefreshed = false; + private bool _appListenerContextClosed = false; + private bool _ctxRefreshed = false; + private bool _ctxClosed = false; + + public ContextListenerObject() + { + } + + public IApplicationContext ApplicationContext + { + set + { + _ctx = value; + _ctx.ContextEvent += new ApplicationEventHandler(ContextRefreshedHandler); + } + get { return _ctx; } + } + + public bool AppListenerContextRefreshed + { + get { return _appListenerContextRefreshed; } + } + + public bool AppListenerContextClosed + { + get { return _appListenerContextClosed; } + } + + public bool CtxRefreshed + { + get { return _ctxRefreshed; } + } + + public bool CtxClosed + { + get { return _ctxClosed; } + } + + public void HandleApplicationEvent(object source, ApplicationEventArgs e) + { + ContextEventArgs ctxArgs = e as ContextEventArgs; + if (ctxArgs != null) + { + if (ctxArgs.Event == ContextEventArgs.ContextEvent.Refreshed) + { + _appListenerContextRefreshed = true; + } + if (ctxArgs.Event == ContextEventArgs.ContextEvent.Closed) + { + _appListenerContextClosed = true; + } + } + } + + private void ContextRefreshedHandler(object sender, ApplicationEventArgs e) + { + ContextEventArgs args = e as ContextEventArgs; + if (args != null) + { + if (args.Event == ContextEventArgs.ContextEvent.Refreshed) + { + _ctxRefreshed = true; + } + else if (args.Event == ContextEventArgs.ContextEvent.Closed) + { + _ctxClosed = true; + } + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/EventListenerAttributeTests.cs b/test/Spring/Spring.Core.Tests/Context/EventListenerAttributeTests.cs new file mode 100644 index 00000000..52be8067 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/EventListenerAttributeTests.cs @@ -0,0 +1,44 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using NUnit.Framework; + +namespace Spring.Context +{ + [TestFixture] + public sealed class EventListenerAttributeTests + { + [Test] + public void ReadEventListenerAttribute() + { + Attribute[] attributes = Attribute.GetCustomAttributes(typeof (IApplicationEventListener)); + bool found = false; + foreach (Attribute attribute in attributes) + { + if (attribute is EventListenerAttribute) + { + found = true; + } + } + Assert.IsTrue(found, "EventListener Attribute not found"); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Events/ConsoleListenerTests.cs b/test/Spring/Spring.Core.Tests/Context/Events/ConsoleListenerTests.cs new file mode 100644 index 00000000..fd83b843 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Events/ConsoleListenerTests.cs @@ -0,0 +1,36 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using NUnit.Framework; + +namespace Spring.Context.Events +{ + [TestFixture] + public class ConsoleListenerTests + { + [Test] + public void ConsoleOnApplicationEvent() + { + ConsoleListener listener = new ConsoleListener(); + listener.HandleApplicationEvent( this, new ApplicationEventArgs() ); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Support/AbstractApplicationContextTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/AbstractApplicationContextTests.cs new file mode 100644 index 00000000..488deec1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/AbstractApplicationContextTests.cs @@ -0,0 +1,314 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Context.Support +{ + [TestFixture] + public class AbstractApplicationContextTests + { + private MockApplicationContext _context; + + [SetUp] + public void Init() + { + EverythingAwareObject.InstanceCount = 0; + EverythingAwareObjectFactoryPostProcessor.InstanceCount = 0; + EverythingAwareObjectPostProcessor.InstanceCount = 0; + _context = new MockApplicationContext("MockApplicationContextName"); + } + + [TearDown] + public void Destroy() + { + _context.Dispose(); + _context = null; + } + + + public void ContextAwareDisplayName() + { + + } + /// + /// Tests the case where there is an object in the context with the name of the + /// default message source name, that is NOT an IMessageSource. + /// + [Test] + public void InvalidMessageSourceObject() + { + RootObjectDefinition def = new RootObjectDefinition(typeof (TestObject)); + ((DefaultListableObjectFactory) _context.ObjectFactory) + .RegisterObjectDefinition(AbstractApplicationContext.MessageSourceObjectName, def); + _context.Refresh(); + object foo = _context + .GetObject(AbstractApplicationContext.MessageSourceObjectName); + Assert.IsTrue(foo is ITestObject, + "Registered non-IMessageSource object under the default message source name, but " + + "failed to get it out of the context. Object retrieved is of type [ " + foo.GetType() + "]."); + } + + /// + /// Tests the case where there is an object in the context with the name of the + /// default event registry name, that is NOT an IEventRegistry. + /// + [Test] + public void InvalidEventRegistryObject() + { + RootObjectDefinition def = new RootObjectDefinition(typeof (TestObject)); + ((DefaultListableObjectFactory) _context.ObjectFactory) + .RegisterObjectDefinition(AbstractApplicationContext.EventRegistryObjectName, def); + _context.Refresh(); + object foo = _context + .GetObject(AbstractApplicationContext.EventRegistryObjectName); + Assert.IsTrue(foo is ITestObject, + "Registered non-IEventRegistry object under the default message source name, but " + + "failed to get it out of the context. Object retrieved is of type [ " + foo.GetType() + "]."); + } + + [Test] + public void ContextAwareSingletonWasCalledBack() + { + _context.RegisterSingleton(); + _context.Refresh(); + MockContextAwareObject mcao1 = (MockContextAwareObject)_context.GetObject("mcao-single"); + Assert.IsTrue(mcao1.ApplicationContext == _context, "context"); + object mcao2 = _context.GetObject("mcao-single"); + Assert.IsTrue(mcao1 == mcao2, "same"); + Assert.IsTrue(_context.IsSingleton("mcao-single"), "singleton?"); + } + + [Test] + public void ContextAwarePrototypeWasCalledBack() + { + _context.RegisterObject(); + _context.Refresh(); + MockContextAwareObject mcao1 = (MockContextAwareObject)_context.GetObject("mcao-proto"); + Assert.IsTrue(mcao1.ApplicationContext == _context, "context"); + Assert.IsTrue(! _context.IsSingleton("mcao-proto"), "singleton"); + MockContextAwareObject mcao2 = (MockContextAwareObject) _context.GetObject("mcao-proto"); + Assert.IsTrue(mcao1 != mcao2, "instance"); + + } + + [Test] + public void ContextAwareSingletonGetName() + { + _context.RegisterSingleton(); + _context.Refresh(); + MockContextAwareObject mcao1 = (MockContextAwareObject)_context.GetObject("mcao-single"); + Assert.AreEqual(mcao1.ApplicationContext.Name, "MockApplicationContextName"); + } + + [Test] + public void ContextAwarePrototypeGetName() + { + _context.RegisterObject(); + _context.Refresh(); + MockContextAwareObject mcao1 = (MockContextAwareObject)_context.GetObject("mcao-proto"); + Assert.AreEqual(mcao1.ApplicationContext.Name, "MockApplicationContextName"); + } + + [Test] + public void ParentNull() + { + Assert.IsNull(_context.ParentContext, "parent is not null"); + } + + [Test] + public void ParentNotNullGrandparentNull() + { + IApplicationContext parentContext = new MockApplicationContext("MockApplicationContextParent"); + _context = new MockApplicationContext("MockApplicationContextName", parentContext); + Assert.IsNotNull(_context.ParentContext, "parent is null"); + Assert.IsNull(_context.ParentContext.ParentContext, "parent is null"); + } + + #region OrderOfKnownProcessorInterfaces Utility Classes + + public enum ObjectProcessingState + { + SetObjectName, + SetObjectFactory, + SetApplicationContext, + PostProcessObjectFactory, + ObjectPostProcessorBeforeInitialization, + ObjectPostProcessorAfterInitialization, + } + + public class EverythingAwareObject : + IObjectNameAware, + IObjectFactoryAware, + IApplicationContextAware + { + public static int InstanceCount = 0; + + private ObjectProcessingState currentState; + + public EverythingAwareObject() + { + InstanceCount++; + } + + public EverythingAwareObject(int expectObjectPostProcessorInstances) + :this() + { + // ensure postprocessor has been instantiated *before* this object + Assert.AreEqual(expectObjectPostProcessorInstances, EverythingAwareObjectPostProcessor.InstanceCount); + } + + public ObjectProcessingState CurrentState + { + get { return currentState;} + set { currentState = value; } + } + + public IApplicationContext ApplicationContext + { + get { throw new NotImplementedException(); } + set + { + Assert.AreEqual(ObjectProcessingState.SetApplicationContext, this.CurrentState); + this.CurrentState++; + } + } + + public string ObjectName + { + set + { + Assert.AreEqual(ObjectProcessingState.SetObjectName, this.CurrentState); + this.CurrentState++; + } + } + + public IObjectFactory ObjectFactory + { + set + { + Assert.AreEqual(ObjectProcessingState.SetObjectFactory, this.CurrentState); + this.CurrentState++; + } + } + } + + public class EverythingAwareObjectFactoryPostProcessor : EverythingAwareObject, + IObjectFactoryPostProcessor + { + public new static int InstanceCount = 0; + + public EverythingAwareObjectFactoryPostProcessor() + { + InstanceCount++; + } + + public void PostProcessObjectFactory(IConfigurableListableObjectFactory factory) + { + Assert.AreEqual(ObjectProcessingState.PostProcessObjectFactory, CurrentState); + CurrentState++; + } + } + + public class EverythingAwareObjectPostProcessor : EverythingAwareObjectFactoryPostProcessor, + IObjectPostProcessor + { + public new static int InstanceCount = 0; + + public EverythingAwareObjectPostProcessor() + { + InstanceCount++; + } + + public EverythingAwareObjectPostProcessor(int expectObjectFactoryPostProcessorInstances) + :this() + { + // ensure factorypostprocessor has been instantiated *before* this object + Assert.AreEqual(expectObjectFactoryPostProcessorInstances, EverythingAwareObjectFactoryPostProcessor.InstanceCount); + } + + public object PostProcessBeforeInitialization(object instance, string name) + { + Assert.AreNotEqual(this, instance); + Assert.AreEqual(ObjectProcessingState.ObjectPostProcessorBeforeInitialization, CurrentState); + CurrentState++; + return instance; + } + + public object PostProcessAfterInitialization(object instance, string objectName) + { + Assert.AreNotEqual(this, instance); + Assert.AreEqual(ObjectProcessingState.ObjectPostProcessorAfterInitialization, CurrentState); + CurrentState++; + return instance; + } + } + + #endregion + + [Test] + public void OrderOfKnownProcessorInterfaces() + { + DefaultListableObjectFactory objectFactory = (DefaultListableObjectFactory)this._context.ObjectFactory; + RootObjectDefinition def; + def = new RootObjectDefinition(typeof(EverythingAwareObjectPostProcessor)); + objectFactory.RegisterObjectDefinition("everythingAwareObjectPostProcessor", def); + _context.Refresh(); + } + + [Test] + public void OrderOfKnownProcessorInstantiation() + { + DefaultListableObjectFactory objectFactory = (DefaultListableObjectFactory)this._context.ObjectFactory; + RootObjectDefinition def; + // note the order of registration (checks instantiation does not occur in order of registration) + def = new RootObjectDefinition(typeof(EverythingAwareObject)); + def.ConstructorArgumentValues.AddIndexedArgumentValue(0, 1); + objectFactory.RegisterObjectDefinition("everythingAwareObject", def); + def = new RootObjectDefinition(typeof(EverythingAwareObjectPostProcessor)); + def.ConstructorArgumentValues.AddIndexedArgumentValue(0, 1); + objectFactory.RegisterObjectDefinition("everythingAwareObjectPostProcessor", def); + def = new RootObjectDefinition(typeof(EverythingAwareObjectFactoryPostProcessor)); + objectFactory.RegisterObjectDefinition("everythingAwareObjectFactoryPostProcessor", def); + _context.Refresh(); + } + + [Test] + public void DefaultObjectFactoryProcessorsDontGetAddedTwice() + { + MockApplicationContext myContext = new MockApplicationContext("myContext"); + DefaultListableObjectFactory objectFactory = (DefaultListableObjectFactory)myContext.ObjectFactory; + Assert.AreEqual(0, objectFactory.ObjectPostProcessorCount); + myContext.Refresh(); + int defaultProcessors = objectFactory.ObjectPostProcessors.Count; + myContext.Refresh(); + Assert.AreEqual(defaultProcessors, objectFactory.ObjectPostProcessors.Count); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/AbstractMessageSourceTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/AbstractMessageSourceTests.cs new file mode 100644 index 00000000..9e444438 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/AbstractMessageSourceTests.cs @@ -0,0 +1,225 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using NUnit.Framework; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// $Id: AbstractMessageSourceTests.cs,v 1.6 2007/08/23 14:31:18 oakinger Exp $ + [TestFixture] + public sealed class AbstractMessageSourceTests : AbstractMessageSource + { + [SetUp] + public void Init() + { + resetMe(); + } + + [Test] + [ExpectedException(typeof (NoSuchMessageException))] + public void GetResolvableNullCodes() + { + MockMessageResolvable res = new MockMessageResolvable(); + res.SetExpectedCodesCalls(1); + GetMessage(res, CultureInfo.CurrentCulture); + res.Verify(); + } + + [Test] + public void GetResolvableDefaultsToParentMessageSource() + { + string MSGCODE = "nullCode"; + object[] MSGARGS = new object[] { "arg1", "arg2" }; + + MockMessageResolvable res = new MockMessageResolvable(); + res.SetExpectedCodesCalls(1); + res.SetArguments(MSGARGS); + res.SetCode(MSGCODE); + + MockMessageSource parentSource = new MockMessageSource(); + parentSource.SetExpectedGetMessageCalls(1); + parentSource.SetExpectedGetMessageCode(MSGCODE); + parentSource.SetExpectedGetMessageDefaultMessage(null); + parentSource.SetExpectedGetMessageArguments(res.GetArguments()); + parentSource.SetExpectedGetMessageReturn("MockMessageSource"); + ParentMessageSource = parentSource; + + Assert.AreEqual("MockMessageSource", GetMessage(res, CultureInfo.CurrentCulture), "My Message"); + parentSource.Verify(); + res.Verify(); + } + + [Test] + public void GetMessageParentMessageSource() + { + object[] args = new object[] {"arguments"}; + MockMessageSource parentSource = new MockMessageSource(); + parentSource.SetExpectedGetMessageCalls(1); + parentSource.SetExpectedGetMessageCode("null"); + parentSource.SetExpectedGetMessageDefaultMessage(null); + parentSource.SetExpectedGetMessageArguments(args); + parentSource.SetExpectedGetMessageReturn("my parent message"); + ParentMessageSource = parentSource; + Assert.AreEqual("my parent message", GetMessage("null", "message", CultureInfo.CurrentCulture, args[0])); + parentSource.Verify(); + } + + [Test] + public void GetMessageResolvableDefaultMessage() + { + MockMessageResolvable res = new MockMessageResolvable(); + res.SetDefaultMessage("MyDefaultMessage"); + res.SetCode(null); + Assert.AreEqual("MyDefaultMessage", GetMessage(res, CultureInfo.CurrentCulture), "Default"); + res.Verify(); + } + + [Test] + public void GetMessageResolvableReturnsFirstCode() + { + MockMessageResolvable res = new MockMessageResolvable(); + UseCodeAsDefaultMessage = true; + res.SetCode("null"); + Assert.AreEqual("null", GetMessage(res, CultureInfo.CurrentCulture), "Code"); + res.Verify(); + } + + [Test] + [ExpectedException(typeof (NoSuchMessageException))] + public void GetMessageResolvableNoValidMessage() + { + MockMessageResolvable res = new MockMessageResolvable(); + res.SetCode(null); + GetMessage(res, CultureInfo.CurrentCulture); + } + + [Test] + public void GetMessageResolvableValidMessageAndCode() + { + MockMessageResolvable res = new MockMessageResolvable(); + res.SetCode("code1"); + res.SetArguments(new object[] {"my", "arguments"}); + Assert.AreEqual("my arguments", GetMessage(res, CultureInfo.CurrentCulture), "Resolve"); + res.Verify(); + } + + [Test] + public void GetMessageResolvableValidMessageAndCodeNullCulture() + { + MockMessageResolvable res = new MockMessageResolvable(); + res.SetCode("code1"); + res.SetArguments(new object[] {"my", "arguments"}); + Assert.AreEqual("my arguments", GetMessage(res, null), "Resolve"); + res.Verify(); + } + + [Test] + [ExpectedException(typeof(NoSuchMessageException))] + public void GetMessageNullCode() + { + Assert.IsNull(GetMessage(null)); + } + + [Test] + public void GetMessageValidMessageAndCode() + { + Assert.AreEqual("my arguments", GetMessage("code1", new object[] {"my", "arguments"}), "Resolve"); + } + + [Test] + public void GetMessageValidMessageAndCodeNullCulture() + { + Assert.AreEqual("my arguments", GetMessage("code1", null, new object[] {"my", "arguments"}), "Resolve"); + } + + [Test] + public void GetMessageUseDefaultCode() + { + UseCodeAsDefaultMessage = true; + Assert.AreEqual("null", GetMessage("null", new object[] {"arguments"}), "message"); + Assert.IsTrue(UseCodeAsDefaultMessage, "default"); + } + + [Test] + [ExpectedException(typeof (NoSuchMessageException))] + public void GetMessageNoValidMessage() + { + GetMessage("null", new object[] {"arguments"}); + } + + [Test] + public void GetMessageWithResolvableArguments() + { + MockMessageResolvable res = new MockMessageResolvable(); + res.SetCode("code1"); + res.SetArguments(new object[] {"my", "resolvable"}); + Assert.AreEqual("spring my resolvable", GetMessage("code2", CultureInfo.CurrentCulture, new object[] {"spring", res}), "Resolve"); + res.Verify(); + } + + [Test] + public void GetMessageResolvableValidMessageAndCodNullMessageFormat() + { + MockMessageResolvable res = new MockMessageResolvable(); + res.SetDefaultMessage("myDefaultMessage"); + res.SetCode("nullCode"); + Assert.AreEqual("myDefaultMessage", GetMessage(res, null), "Resolve"); + res.Verify(); + } + + private void resetMe() + { + ParentMessageSource = null; + UseCodeAsDefaultMessage = false; + } + + protected override string ResolveMessage(string code, CultureInfo cultureInfo) + { + if (code.Equals("null")) + { + return null; + } + else if (code.Equals("nullCode")) + { + return null; + } + else + { + return "{0} {1}"; + } + } + + protected override object ResolveObject(string code, CultureInfo cultureInfo) + { + return null; + } + + protected override void ApplyResourcesToObject(object value, string objectName, CultureInfo cultureInfo) + { + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/ApplicationContextAwareProcessorTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/ApplicationContextAwareProcessorTests.cs new file mode 100644 index 00000000..533430f7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/ApplicationContextAwareProcessorTests.cs @@ -0,0 +1,151 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Security.Policy; +using NUnit.Framework; + +namespace Spring.Context.Support +{ + [TestFixture] + public sealed class ApplicationContextAwareProcessorTests + { + [Test] + public void AttachResourceLoader() + { + MockApplicationContext ctx = new MockApplicationContext("MockApplicationContext"); + ApplicationContextAwareProcessor processor = new ApplicationContextAwareProcessor(ctx); + MockContextAwareObject obj = new MockContextAwareObject(); + Assert.IsNull(obj.ResourceLoader, "ResourceLoader Does Not Equal"); + MockContextAwareObject obj2 = (MockContextAwareObject) processor.PostProcessBeforeInitialization(obj, "MyContextAwareObject"); + Assert.AreEqual(ctx, obj2.ResourceLoader, "ResourceLoader Does Not Equal"); + } + + [Test] + public void DoNotAttachResourceLoaderForRegularObject() + { + MockApplicationContext ctx = new MockApplicationContext(); + ApplicationContextAwareProcessor processor = new ApplicationContextAwareProcessor(ctx); + object obj = new object(); + object obj1 = processor.PostProcessBeforeInitialization(obj, "MyContextAwareObject"); + Assert.AreEqual(obj, obj1, "Objects don't equal"); + } + + [Test] + public void AttachContext() + { + MockApplicationContext ctx = new MockApplicationContext(); + ApplicationContextAwareProcessor processor = new ApplicationContextAwareProcessor(ctx); + MockContextAwareObject obj = new MockContextAwareObject(); + Assert.IsNull(obj.GetApplicationContext(), "Context Does Not Equal"); + MockContextAwareObject obj2 = (MockContextAwareObject) processor.PostProcessBeforeInitialization(obj, "MyContextAwareObject"); + Assert.AreEqual(ctx, obj2.GetApplicationContext(), "Context Does Not Equal"); + } + + [Test] + public void DoNotAttachContextForRegularObject() + { + MockApplicationContext ctx = new MockApplicationContext(); + ApplicationContextAwareProcessor processor = new ApplicationContextAwareProcessor(ctx); + object obj = new object(); + object obj1 = processor.PostProcessBeforeInitialization(obj, "MyContextAwareObject"); + Assert.AreEqual(obj, obj1, "Objects don't equal"); + } + + [Test] + public void AfterInitReturnsSameInstanceAsWasPassedIn() + { + MockApplicationContext ctx = new MockApplicationContext(); + ApplicationContextAwareProcessor processor = new ApplicationContextAwareProcessor(ctx); + object obj = new object(); + object obj1 = processor.PostProcessAfterInitialization(obj, "MyContextAwareObject"); + Assert.AreEqual(obj, obj1, "Objects don't equal"); + } + + [Test] + public void AlwaysIgnoresProxiedMessageSourceAwareObjects() + { + PostProcessTProxiedObject(new ProcessedObjectChecker( + _AlwaysIgnoresProxiedMessageSourceAwareObjects)); + } + + private void _AlwaysIgnoresProxiedMessageSourceAwareObjects(MockContextAwareObject obj) + { + Assert.IsNull(obj.MessageSource, + "Transparent proxy IMessageSourceAware object was not ignored (must be)."); + } + + [Test] + public void AlwaysIgnoresProxiedResourceLoaderAwareObjects() + { + PostProcessTProxiedObject(new ProcessedObjectChecker( + _AlwaysIgnoresProxiedResourceLoaderAwareObjects)); + } + + private void _AlwaysIgnoresProxiedResourceLoaderAwareObjects(MockContextAwareObject obj) + { + Assert.IsNull(obj.ResourceLoader, + "Transparent proxy IResourceLoaderAware object was not ignored (must be)."); + } + + [Test] + public void AlwaysIgnoresProxiedApplicationContextAwareAwareObjects() + { + PostProcessTProxiedObject(new ProcessedObjectChecker( + _AlwaysIgnoresProxiedApplicationContextAwareAwareObjects)); + } + + private void _AlwaysIgnoresProxiedApplicationContextAwareAwareObjects(MockContextAwareObject obj) + { + Assert.IsNull(obj.ApplicationContext, + "Transparent proxy IApplicationContextAwareAware object was not ignored (must be)."); + } + + private void PostProcessTProxiedObject(ProcessedObjectChecker test) + { + AppDomain domain = null; + try + { + AppDomainSetup setup = new AppDomainSetup(); + setup.ApplicationBase = Environment.CurrentDirectory; + domain = AppDomain.CreateDomain("Spring", new Evidence(AppDomain.CurrentDomain.Evidence), setup); + object foo = domain.CreateInstanceAndUnwrap(GetType().Assembly.FullName, typeof(MockContextAwareObject).FullName); + + MockApplicationContext ctx = new MockApplicationContext(); + ApplicationContextAwareProcessor processor = new ApplicationContextAwareProcessor(ctx); + MockContextAwareObject afterFoo = (MockContextAwareObject) processor.PostProcessBeforeInitialization(foo, "MyContextAwareObject"); + test(afterFoo); + } + finally + { + try + { + AppDomain.Unload(domain); + } + catch (Exception ex) + { + Console.Write("Error unloading AppDomain used during testing : " + ex); + } + } + } + + private delegate void ProcessedObjectChecker(MockContextAwareObject obj); + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/ApplicationContextExtensionTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/ApplicationContextExtensionTests.cs new file mode 100644 index 00000000..5e2dbf01 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/ApplicationContextExtensionTests.cs @@ -0,0 +1,66 @@ +using NUnit.Framework; + +using Spring.Objects; + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Test configuration of the application context with custom parsers, + /// resource handlers, and type aliases. + /// + [TestFixture] + public class ApplicationContextExtensionTests + { + /// + /// Create an instance of the test. + /// + public ApplicationContextExtensionTests() + {} + + /// + /// Test using a custom parser to create our familiar "TestObject" + /// + [Test] + public void UsingCustomParsers() + { + ContextRegistry.Clear(); + IApplicationContext ctx = ContextRegistry.GetContext(); + Assert.IsNotNull(ctx); + + IApplicationContext parentCtx = ContextRegistry.GetContext("Parent"); + Assert.IsNotNull(parentCtx, "Parent context not registered."); + + TestObject to = (TestObject) ctx.GetObject("Parent"); + Assert.IsNotNull(to); + Assert.IsTrue(TestObjectConfigParser.ParseElementCalled); + + TestObject to2 = (TestObject) ctx.GetObject("testObject"); + Assert.AreEqual(12, to2.Age); + Assert.AreEqual("John", to2.Name); + + Assert.AreEqual(2, ctx.ObjectDefinitionCount); + + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/ApplicationObjectSupportTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/ApplicationObjectSupportTests.cs new file mode 100644 index 00000000..c5643d17 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/ApplicationObjectSupportTests.cs @@ -0,0 +1,405 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Context.Support +{ + [TestFixture] + public class ApplicationObjectSupportTests + { + internal class MyApplicationObjectSupport : ApplicationObjectSupport + { + private bool _init = false; + + public MyApplicationObjectSupport() : base() + { + } + + public MyApplicationObjectSupport(IApplicationContext applicationContext) : base(applicationContext) + { + } + + protected override Type RequiredType + { + get { return typeof (MockApplicationContext); } + } + + protected override void InitApplicationContext() + { + _init = true; + } + + public bool Init + { + get { return _init; } + } + } + + internal class MyContext2 : IApplicationContext + { + public void Dispose() + { + } + + #region IApplicationContext Members + + public IApplicationContext ParentContext + { + get { return null; } + set + { + } + } + + public DateTime StartupDate + { + get { return new DateTime(); } + } + + public event ApplicationEventHandler ContextEvent; + + public long StartupDateMilliseconds + { + get { return 0; } + } + + public string Name + { + get { return AbstractApplicationContext.DefaultRootContextName; } + set + { + } + } + + #endregion + + #region IListableObjectFactory Members + + public IObjectDefinition GetObjectDefinition(string name) + { + return null; + } + + public IObjectDefinition GetObjectDefinition(string name, bool includeAncestors) + { + return null; + } + + public string[] GetObjectDefinitionNames(Type type) + { + return null; + } + + public string[] GetObjectNamesForType(Type type) + { + return null; + } + + public string[] GetObjectNamesForType( + Type type, bool includePrototypes, bool includeFactoryObjects) + { + return null; + } + + string[] IListableObjectFactory.GetObjectDefinitionNames() + { + return null; + } + + public IDictionary GetObjectsOfType(Type type) + { + return null; + } + + public IDictionary GetObjectsOfType(Type type, bool includePrototypes, bool includeFactoryObjects) + { + return null; + } + + public int ObjectDefinitionCount + { + get { return 0; } + } + + public bool ContainsObjectDefinition(string name) + { + return false; + } + + #endregion + + #region IObjectFactory Members + + public object this[string name] + { + get { return null; } + } + + public bool ContainsObject(string name) + { + return false; + } + + public string[] GetAliases(string name) + { + return null; + } + + public object GetObject(string name, Type requiredType) + { + return null; + } + + object IObjectFactory.GetObject(string name) + { + return null; + } + + public object GetObject(string name, object[] arguments) + { + return null; + } + + public object GetObject(string name, Type requiredType, object[] arguments) + { + return null; + } + + public bool IsSingleton(string name) + { + return false; + } + + + public bool IsPrototype(string name) + { + return false; + } + + public Type GetType(string name) + { + return null; + } + + + public bool IsTypeMatch(string name, Type targetType) + { + return false; + } + + public object ConfigureObject(object target) + { + return null; + } + + public object ConfigureObject(object target, string name) + { + return null; + } + + public object ConfigureObject(object target, string name, IObjectDefinition definition) + { + return null; + } + + #endregion + + #region IHierarchicalObjectFactory Members + + public IObjectFactory ParentObjectFactory + { + get { return null; } + } + + #endregion + + #region IMessageSource Members + + public string GetMessage(IMessageSourceResolvable resolvable, CultureInfo culture) + { + return null; + } + + string IMessageSource.GetMessage(string name, CultureInfo culture, params object[] args) + { + return null; + } + + string IMessageSource.GetMessage(string name) + { + return null; + } + + string IMessageSource.GetMessage(string name, params object[] args) + { + return null; + } + + string IMessageSource.GetMessage(string name, CultureInfo cultureInfo) + { + return null; + } + + public string GetMessage(string name, string defaultMessage, CultureInfo culture, params object[] arguments) + { + return null; + } + + object IMessageSource.GetResourceObject(string name, CultureInfo culture) + { + return null; + } + + object IMessageSource.GetResourceObject(string name) + { + return null; + } + + void IMessageSource.ApplyResources(object value, string objectName, CultureInfo cultureInfo) + { + } + + #endregion + + #region IResourceLoader Members + + public IResource GetResource(string location) + { + return null; + } + + #endregion + + #region IEventRepository Members + + public void PublishEvents(object sourceObject) + { + throw new NotImplementedException(); + } + + public void Subscribe(object subscriber) + { + throw new NotImplementedException(); + } + + public void Subscribe(object subscriber, Type targetSourceType) + { + throw new NotImplementedException(); + } + + #endregion + + public void PublishEvent(object sender, ApplicationEventArgs e) + { + throw new NotImplementedException(); + } + } + + internal class MyContext2Subclass : MyContext2 + { + } + + internal class MyApplicationObjectSupportConcrete : ApplicationObjectSupport + { + private bool _init; + + public MyApplicationObjectSupportConcrete() : base() + { + } + + public MyApplicationObjectSupportConcrete(IApplicationContext applicationContext) : base(applicationContext) + { + } + + protected override void InitApplicationContext() + { + base.InitApplicationContext(); + _init = true; + } + + public bool Init + { + get { return _init; } + } + } + + + [Test] + [ExpectedException(typeof (ApplicationContextException))] + public void InvalidContextSubclass() + { + ApplicationObjectSupport support = new MyApplicationObjectSupport(); + support.ApplicationContext = new MyContext2(); + } + + [Test] + public void ValidContextSubClassOfAContext() + { + MyApplicationObjectSupportConcrete support = new MyApplicationObjectSupportConcrete(); + support.ApplicationContext = new MyContext2(); + } + + [Test] + public void ValidContextIApplicationContext() + { + MyApplicationObjectSupportConcrete support = new MyApplicationObjectSupportConcrete(); + support.ApplicationContext = new MyContext2(); + Assert.IsTrue(support.Init); + Assert.IsNotNull(support.MessageSourceAccessor); + } + + [Test] + public void ValidContextSubClass() + { + MyApplicationObjectSupport support = new MyApplicationObjectSupport(); + support.ApplicationContext = new MockApplicationContext(); + Assert.IsTrue(support.Init); + } + + [Test] + public void ReinitWithSameContext() + { + MockApplicationContext ctx = new MockApplicationContext(); + ApplicationObjectSupport support = new MyApplicationObjectSupport(ctx); + support.ApplicationContext = ctx; + Assert.AreEqual(ctx, support.ApplicationContext); + } + + [Test] + [ExpectedException(typeof (ApplicationContextException))] + public void ReinitWithDiffContext() + { + MockApplicationContext ctx = new MockApplicationContext(); + ApplicationObjectSupport support = new MyApplicationObjectSupport(ctx); + support.ApplicationContext = new MockApplicationContext(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/Assembler.cs b/test/Spring/Spring.Core.Tests/Context/Support/Assembler.cs new file mode 100644 index 00000000..c38a4c77 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/Assembler.cs @@ -0,0 +1,57 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Context.Support +{ + #region Test Utility Classes + + #endregion + + + /// Mark Pollack + /// $Id: Assembler.cs,v 1.2 2007/07/17 14:55:47 oakinger Exp $ + public class Assembler + { + private Service service; + private Logic logic; + private string name; + + public Logic Logic + { + set { logic = value; } + } + + public Service Service + { + set { service = value; } + } + + + public string ObjectName + { + set { name = value; } + } + + public void Test() + { + + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/ContextLocatorHandlerTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/ContextLocatorHandlerTests.cs new file mode 100644 index 00000000..0b040436 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/ContextLocatorHandlerTests.cs @@ -0,0 +1,172 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Configuration; +using System.IO; +using System.Security.Policy; +using System.Xml; +using NUnit.Framework; +using Spring.Objects; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Unit tests for the ContextHandler class. + /// + /// Mark Pollack + /// Rick Evans + [TestFixture] + public sealed class ContextHandlerTests + { + private XmlElement configurationElement; + + [SetUp] + public void SetUp() + { + ContextRegistry.Clear(); + } + + [Test] + public void CreateContextSuccessful() + { + const string xmlData = + @" + +"; + CreateConfigurationElement(xmlData); + + ContextHandler ctxHandler = new ContextHandler(); + IApplicationContext ctx = (IApplicationContext) ctxHandler.Create(null, null, configurationElement); + Assert.AreEqual(ctx, ContextRegistry.GetContext()); + Assert.AreEqual(1, ContextRegistry.GetContext().ObjectDefinitionCount); + } + + /// + /// Expect failure when using a type that does not inherit from IApplicationContext + /// + [Test] +#if NET_2_0 + [ExpectedException(typeof(ConfigurationErrorsException))] +#else + [ExpectedException(typeof(ConfigurationException))] +#endif + public void ContextNotOfCorrectType() + { + const string xmlData = + @" + +"; + CreateConfigurationElement(xmlData); + ContextHandler ctxHandler = new ContextHandler(); + ctxHandler.Create(null, null, configurationElement); + } + + [Test] +#if NET_2_0 + [ExpectedException(typeof(ConfigurationErrorsException))] +#else + [ExpectedException(typeof(ConfigurationException))] +#endif + public void CreatedFromNullXmlElement() + { + ContextHandler ctxHandler = new ContextHandler(); + ctxHandler.Create(null, null, null); + } + + [Test] + public void DefaultsToXmlApplicationContextType() + { + const string xmlData = + @" + +"; + CreateConfigurationElement(xmlData); + ContextHandler ctxHandler = new ContextHandler(); + IApplicationContext ctx = (IApplicationContext) ctxHandler.Create(null, null, configurationElement); + Assert.AreEqual(typeof (XmlApplicationContext), ctx.GetType(), + "Default type is not the XmlApplicationContext type; it must be."); + } + +#if NET_2_0 + [ExpectedException(typeof(ConfigurationErrorsException))] +#else + [ExpectedException(typeof(ConfigurationException))] +#endif + [Test(Description="SPRNET-105")] + public void ChokesIfChildContextsUseTheSameName() + { + const string xmlData = + @" + + +"; + CreateConfigurationElement(xmlData); + ContextHandler ctxHandler = new ContextHandler(); + ctxHandler.Create(null, null, configurationElement); + } + + private void CreateConfigurationElement(string xmlData) + { + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(new StringReader(xmlData)); + configurationElement = xmlDoc.DocumentElement; + } + + // integration test; touches just about every class in the Spring.NET core... + [Test] + public void LoadParentChildContextsHierarchy() + { + //need a second section for another independent test as CongfigurationSettings.GetConfig will + //not be called twice by .NET + IApplicationContext ctx + = (IApplicationContext) ConfigurationUtils.GetSection("spring2/context"); + + Assert.IsNotNull(ctx); + IApplicationContext parentCtx = ContextRegistry.GetContext("Parent"); + Assert.IsNotNull(parentCtx, "Parent context not registered."); + Assert.AreEqual("Parent", parentCtx.Name, "Parent's DisplayName property not picked up from config file."); + IApplicationContext childCtx = ContextRegistry.GetContext("Child"); + Assert.IsNotNull(childCtx, "Child context not registered."); + Assert.AreEqual("Child", childCtx.Name, "Child's DisplayName property not picked up from config file."); + Assert.AreEqual("Parent", childCtx.ParentContext.Name); + IApplicationContext grandchildCtx = ContextRegistry.GetContext("Grandchild"); + Assert.IsNotNull(grandchildCtx, "Grandchild context not registered."); + Assert.AreEqual("Grandchild", grandchildCtx.Name, "Grandchild's DisplayName property not picked up from config file."); + Assert.AreEqual("Child", grandchildCtx.ParentContext.Name); + + // ensure proper objects have been loaded into the correct context... + Assert.IsTrue(parentCtx.ContainsObjectDefinition("Parent"), "Parent context object not present (must be)."); + Assert.IsFalse(parentCtx.ContainsObjectDefinition("Child"), "Wrong (child context) object present in Parent context."); + + Assert.IsTrue(childCtx.ContainsObjectDefinition("Child"), "Child context object not present (must be)."); + Assert.IsFalse(childCtx.ContainsObjectDefinition("Parent"), "Wrong (parent context) object present in Child context."); + + Assert.IsTrue(grandchildCtx.ContainsObjectDefinition("Grandchild"), "Grandchild context object not present (must be)."); + Assert.IsFalse(grandchildCtx.ContainsObjectDefinition("Child"), "Wrong (parent context) object present in Grandchild context."); + Assert.IsFalse(grandchildCtx.ContainsObjectDefinition("Parent"), "Wrong (parent context) object present in Grandchild context."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/ContextRegistryTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/ContextRegistryTests.cs new file mode 100644 index 00000000..64f96534 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/ContextRegistryTests.cs @@ -0,0 +1,157 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Xml; +using NUnit.Framework; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Unit tests for the ContextRegistry class. + /// + /// Rick Evans + /// $Id: ContextRegistryTests.cs,v 1.9 2008/03/21 10:49:38 oakinger Exp $ + [TestFixture] + public sealed class ContextRegistryTests + { + [SetUp] + public void SetUp() + { + ContextRegistry.Clear(); + } + + + /// + /// This handler simulates calls to ContextRegistry during context creation + /// + private static object GetContextRecursive(object parent, object context, XmlNode section) + { + return ContextRegistry.GetContext(); // this must fail! + } + + [Test] + public void ThrowsInvalidOperationExceptionOnRecursiveCallsToGetContext() + { + HookableContextHandler.CreateContextFromSectionHandler prevInst = HookableContextHandler.SetSectionHandler( + new HookableContextHandler.CreateContextFromSectionHandler(GetContextRecursive)); + try + { + ContextRegistry.GetContext("somename"); + } + catch(Exception ex) + { + InvalidOperationException rootCause = ex.GetBaseException() as InvalidOperationException; + Assert.IsNotNull(rootCause); + Assert.AreEqual("root context is currently in creation.", rootCause.Message.Substring(0, 38)); + } + finally + { + HookableContextHandler.SetSectionHandler(prevInst); + } + } + + [Test] + public void RegisterRootContext() + { + MockApplicationContext ctx = new MockApplicationContext(); + ContextRegistry.RegisterContext(ctx); + IApplicationContext context = ContextRegistry.GetContext(); + Assert.IsNotNull(context, + "Root context is null even though a context has been registered."); + Assert.IsTrue(Object.ReferenceEquals(ctx, context), + "Root context was not the same as the first context registered (it must be)."); + } + + [Test] + public void RegisterNamedRootContext() + { + const string ctxName = "bingo"; + MockApplicationContext ctx = new MockApplicationContext(ctxName); + ContextRegistry.RegisterContext(ctx); + IApplicationContext rootContext = ContextRegistry.GetContext(); + Assert.IsNotNull(rootContext, + "Root context is null even though a context has been registered."); + Assert.AreEqual(ctxName, rootContext.Name, + "Root context name is different even though the root context has been registered under the lookup name."); + } + + [Test] + public void RegisterNamedContext() + { + const string ctxName = "bingo"; + MockApplicationContext ctx = new MockApplicationContext(ctxName); + ContextRegistry.RegisterContext(ctx); + IApplicationContext context = ContextRegistry.GetContext(ctxName); + Assert.IsNotNull(context, + "Named context is null even though a context has been registered under the lookup name."); + Assert.IsTrue(Object.ReferenceEquals(ctx, context), + "Named context was not the same as the registered context (it must be)."); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void GetContextWithNullName() + { + ContextRegistry.GetContext(null); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void GetContextWithEmptyName() + { + ContextRegistry.GetContext(""); + } + + [Test] + public void GetContextNotRegisteredReturnsNull() + { + IApplicationContext context = ContextRegistry.GetContext("bingo"); + Assert.IsNull(context, + "Named context is not null even though a context has not been registered under the lookup name."); + } +#if NET_2_0 + // TODO : Add support for .NET 1.x + [Test] + public void ClearWithConfigurationSection() + { + IApplicationContext ctx1 = ContextRegistry.GetContext(); + ContextRegistry.Clear(); + IApplicationContext ctx2 = ContextRegistry.GetContext(); + + Assert.AreNotSame(ctx1, ctx2); + } +#endif + + [Test(Description="SPRNET-105")] + [ExpectedException(typeof(ApplicationContextException))] + public void ChokesIfChildContextRegisteredUnderNameOfAnExistingContext() + { + MockApplicationContext original = new MockApplicationContext("original"); + ContextRegistry.RegisterContext(original); + MockApplicationContext duplicate = new MockApplicationContext("original"); + ContextRegistry.RegisterContext(duplicate); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/DefaultMessageSourceResolvableTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/DefaultMessageSourceResolvableTests.cs new file mode 100644 index 00000000..ce4327d6 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/DefaultMessageSourceResolvableTests.cs @@ -0,0 +1,109 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text; +using NUnit.Framework; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Unit tests for the DefaultMessageSourceResolvable class. + /// + [TestFixture] + public sealed class DefaultMessageSourceResolvableTests + { + [Test] + public void InstantiationWithASingleCodeDefaultsToEmptyDefaultMessage() + { + DefaultMessageSourceResolvable dmr = new DefaultMessageSourceResolvable("foo"); + Assert.AreEqual(string.Empty, dmr.DefaultMessage, + "Not defaulting to non null empty string value (it must)."); + } + + [Test] + public void NullLastCode() + { + DefaultMessageSourceResolvable dmr = new DefaultMessageSourceResolvable(null, null); + Assert.IsNull(dmr.LastCode); + } + + [Test] + public void LastCode() + { + DefaultMessageSourceResolvable dmr = new DefaultMessageSourceResolvable(new string[] {"code1"}, null); + Assert.AreEqual("code1", dmr.LastCode); + } + + [Test] + public void ResolvableToString() + { + string[] codes = new string[] {"code1", "code2"}; + object[] arguments = new object[] {"argument1", "argument2"}; + string defaultMessage = "defaultMessage"; + DefaultMessageSourceResolvable dmr = new DefaultMessageSourceResolvable(codes, arguments, defaultMessage); + Assert.AreEqual(getResolvableString(), dmr.ToString(), "to string"); + } + + [Test] + public void ResolvableToStringNullArguments() + { + string[] codes = new string[] {"code1", "code2"}; + string defaultMessage = "defaultMessage"; + DefaultMessageSourceResolvable dmr = new DefaultMessageSourceResolvable(codes, null, defaultMessage); + Assert.AreEqual(getResolvableStringNull(), dmr.ToString(), "to string"); + } + + [Test] + public void DefaultResolvableFromExistingResolvable() + { + MockMessageResolvable mockResolvable = new MockMessageResolvable(); + mockResolvable.SetExpectedCodesCalls(1); + mockResolvable.SetCode("code1FromMock"); + mockResolvable.SetDefaultMessage("defaultMessageFromMock"); + mockResolvable.SetArguments(new object[] {"ArgumentFromMock"}); + DefaultMessageSourceResolvable dmr = new DefaultMessageSourceResolvable(mockResolvable); + Assert.AreEqual("defaultMessageFromMock", dmr.DefaultMessage, "default"); + Assert.AreEqual("code1FromMock", dmr.LastCode, "codes"); + Assert.AreEqual("ArgumentFromMock", (dmr.GetArguments())[0], "arguments"); + mockResolvable.Verify(); + } + + private string getResolvableString() + { + StringBuilder builder = new StringBuilder(); + builder.Append("codes=[code1,code2]; arguments=[(String)[argument1], (String)[argument2]]; "); + builder.Append("defaultMessage=[defaultMessage]"); + return builder.ToString(); + } + + private string getResolvableStringNull() + { + StringBuilder builder = new StringBuilder(); + builder.Append("codes=[code1,code2]; arguments=[null]; "); + builder.Append("defaultMessage=[defaultMessage]"); + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/DelegatingMessageSourceTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/DelegatingMessageSourceTests.cs new file mode 100644 index 00000000..e12bc6ad --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/DelegatingMessageSourceTests.cs @@ -0,0 +1,268 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using DotNetMock.Dynamic; +using NUnit.Framework; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Unit tests for the DelegatingMessageSource class. + /// + /// Rick Evans + /// $Id: DelegatingMessageSourceTests.cs,v 1.3 2006/04/09 07:19:07 markpollack Exp $ + [TestFixture] + public sealed class DelegatingMessageSourceTests + { + private const string LookupKey = "rick"; + private DynamicMock _mock; + private IMessageSource _messageSource; + + private DynamicMock TheMock + { + get { return _mock; } + } + + private IMessageSource MockMessageSource + { + get { return _messageSource; } + } + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp() + { + _mock = new DynamicMock(typeof(IMessageSource)); + _messageSource = (IMessageSource) _mock.Object; + } + + [Test] + public void Instantiation() + { + DelegatingMessageSource source = new DelegatingMessageSource(); + Assert.IsNotNull(source.ParentMessageSource, + "ParentMessageSource property must *never* be null."); + } + + [Test] + public void InstantiationWithSuppliedParentMessageSource() + { + DelegatingMessageSource source + = new DelegatingMessageSource(MockMessageSource); + Assert.IsNotNull(source.ParentMessageSource, + "ParentMessageSource property must *never* be null."); + Assert.IsTrue(Object.ReferenceEquals(source.ParentMessageSource, MockMessageSource)); + } + + [Test] + public void GetMessage() + { + const string expectedName = "Rick Evans"; + TheMock.ExpectAndReturn("GetMessage", expectedName, LookupKey); + DelegatingMessageSource source + = new DelegatingMessageSource(MockMessageSource); + string name = source.GetMessage(LookupKey); + Assert.AreEqual(expectedName, name); + TheMock.Verify(); + } + + [Test] + [ExpectedException(typeof(NoSuchMessageException))] + public void GetMessageNoDelegateTarget() + { + DelegatingMessageSource source = new DelegatingMessageSource(); + source.GetMessage(LookupKey); + } + + [Test] + public void GetMessageWithCulture() + { + const string expectedName = "Rick Evans"; + TheMock.ExpectAndReturn("GetMessage", expectedName, LookupKey, CultureInfo.InvariantCulture); + DelegatingMessageSource source + = new DelegatingMessageSource(MockMessageSource); + string name = source.GetMessage(LookupKey, CultureInfo.InvariantCulture); + Assert.AreEqual(expectedName, name); + TheMock.Verify(); + } + + [Test] + [ExpectedException(typeof(NoSuchMessageException))] + public void GetMessageWithCultureNoDelegateTarget() + { + DelegatingMessageSource source = new DelegatingMessageSource(); + source.GetMessage(LookupKey, CultureInfo.InvariantCulture); + } + + [Test] + public void GetMessageWithParams() + { + const string expectedName = "Rick Evans"; + TheMock.ExpectAndReturn("GetMessage", expectedName, LookupKey, new string[] { "Rick", "Evans" }); + DelegatingMessageSource source + = new DelegatingMessageSource(MockMessageSource); + string name = source.GetMessage(LookupKey, "Rick", "Evans"); + Assert.AreEqual(expectedName, name); + TheMock.Verify(); + } + + [Test] + [ExpectedException(typeof(NoSuchMessageException))] + public void GetMessageWithParamsNoDelegateTarget() + { + DelegatingMessageSource source = new DelegatingMessageSource(); + source.GetMessage(LookupKey, "Rick", "Evans"); + } + + [Test] + public void GetMessageWithCultureAndParams() + { + const string expectedName = "Rick Evans"; + TheMock.ExpectAndReturn("GetMessage", expectedName, LookupKey, CultureInfo.InvariantCulture, new string[] { "Rick", "Evans" }); + DelegatingMessageSource source + = new DelegatingMessageSource(MockMessageSource); + string name = source.GetMessage(LookupKey, CultureInfo.InvariantCulture, "Rick", "Evans"); + Assert.AreEqual(expectedName, name); + TheMock.Verify(); + } + + [Test] + [ExpectedException(typeof(NoSuchMessageException))] + public void GetMessageWithCultureAndParamsNoDelegateTarget() + { + DelegatingMessageSource source = new DelegatingMessageSource(); + source.GetMessage(LookupKey, CultureInfo.InvariantCulture, "Rick", "Evans"); + } + + [Test] + public void GetMessageWithMessageSourceResolvableAndCulture() + { + const string expectedName = "Rick Evans"; + DelegatingMessageSource source + = new DelegatingMessageSource(MockMessageSource); + TheMock.ExpectAndReturn("GetMessage", expectedName, null, CultureInfo.InvariantCulture); + string name = source.GetMessage( + (IMessageSourceResolvable) null, CultureInfo.InvariantCulture); + Assert.AreEqual(expectedName, name); + TheMock.Verify(); + } + + [Test] + public void GetMessageWithNoParentMessageSourceAndMessageSourceResolvableAndCulture() + { + const string expectedName = "Rick Evans"; + DynamicMock mock = new DynamicMock(typeof(IMessageSourceResolvable)); + IMessageSourceResolvable resolvable = (IMessageSourceResolvable) mock.Object; + mock.ExpectAndReturn("DefaultMessage", "Rick Evans"); + mock.ExpectAndReturn("DefaultMessage", "Rick Evans"); + DelegatingMessageSource source = new DelegatingMessageSource(); + string name = source.GetMessage(resolvable, CultureInfo.InvariantCulture); + Assert.AreEqual(expectedName, name); + mock.Verify(); + } + + [Test] + [ExpectedException(typeof(NoSuchMessageException))] + public void GetMessageWithNoParentMessageSourceAndNullDefaultMessageSourceResolvableWithNoCodesAndCulture() + { + IMessageSourceResolvable resolver = new DefaultMessageSourceResolvable( + new string[] {}, new object[] {}, string.Empty); + DelegatingMessageSource source = new DelegatingMessageSource(); + source.GetMessage(resolver, CultureInfo.InvariantCulture); + } + + [Test] + [ExpectedException(typeof(NoSuchMessageException))] + public void GetMessageWithNoParentMessageSourceAndNullDefaultMessageSourceResolvableAndCulture() + { + IMessageSourceResolvable resolver = new DefaultMessageSourceResolvable( + new string[] {"foo"}, new object[] {}, string.Empty); + DelegatingMessageSource source = new DelegatingMessageSource(); + source.GetMessage(resolver, CultureInfo.InvariantCulture); + } + + [Test] + public void GetResourceObject() + { + const string expectedName = "Rick Evans"; + TheMock.ExpectAndReturn("GetResourceObject", expectedName, LookupKey); + DelegatingMessageSource source + = new DelegatingMessageSource(MockMessageSource); + string name = (string) source.GetResourceObject(LookupKey); + Assert.AreEqual(expectedName, name); + TheMock.Verify(); + } + + [Test] + [ExpectedException(typeof(ApplicationContextException))] + public void GetResourceObjectWithNoParentMessageSource() + { + DelegatingMessageSource source = new DelegatingMessageSource(); + source.GetResourceObject(LookupKey); + } + + [Test] + public void GetResourceObjectWithCulture() + { + const string expectedName = "Rick Evans"; + TheMock.ExpectAndReturn("GetResourceObject", expectedName, LookupKey, CultureInfo.InvariantCulture); + DelegatingMessageSource source + = new DelegatingMessageSource(MockMessageSource); + string name = (string) source.GetResourceObject(LookupKey, CultureInfo.InvariantCulture); + Assert.AreEqual(expectedName, name); + TheMock.Verify(); + } + + [Test] + [ExpectedException(typeof(ApplicationContextException))] + public void GetResourceObjectWithNoParentMessageSourceWithCulture() + { + DelegatingMessageSource source = new DelegatingMessageSource(); + source.GetResourceObject(LookupKey, CultureInfo.InvariantCulture); + } + + [Test] + public void ApplyResources() + { + const string expectedName = "Rick Evans"; + TheMock.ExpectAndReturn("ApplyResources", expectedName, 12, "rick", CultureInfo.InvariantCulture); + DelegatingMessageSource source + = new DelegatingMessageSource(MockMessageSource); + source.ApplyResources(12, "rick", CultureInfo.InvariantCulture); + TheMock.Verify(); + } + + [Test] + [ExpectedException(typeof(ApplicationContextException))] + public void ApplyResourcesWithNoParentMessageSource() + { + DelegatingMessageSource source = new DelegatingMessageSource(); + source.ApplyResources(12, "rick", CultureInfo.InvariantCulture); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Support/Logic.cs b/test/Spring/Spring.Core.Tests/Context/Support/Logic.cs new file mode 100644 index 00000000..74cc23a2 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/Logic.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using Spring.Objects.Factory; + +namespace Spring.Context.Support +{ + /// Mark Pollack + /// $Id: Logic.cs,v 1.1 2007/07/10 05:48:37 markpollack Exp $ + public class Logic : IObjectNameAware + { + + private string objectName; + private Assembler assembler; + + + public Assembler Assembler + { + set { assembler = value; } + } + + #region IObjectNameAware Members + + public string ObjectName + { + set { objectName = value; } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/MessageSourceAccessorTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/MessageSourceAccessorTests.cs new file mode 100644 index 00000000..36dace6b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/MessageSourceAccessorTests.cs @@ -0,0 +1,138 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Globalization; + +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Globalization; + +namespace Spring.Context.Support +{ + [TestFixture] + public class MessageSourceAccessorTests + { + [TestFixtureSetUp] + public void TestFixtureSetUp() + { + CultureTestScope.Set(); + } + + [TestFixtureTearDown] + public void TestFixtureTearDown() + { + CultureTestScope.Reset(); + } + + private readonly string MSGCODE = "code1"; + private readonly CultureInfo MSGCULTURE = new CultureInfo("fr"); + private readonly object[] MSGARGS = new object[] { "argument1" }; + private readonly string MSGRESULT = "my message"; + + + private MockRepository mocks; + private IMessageSource mockMsgSource; + private IMessageSourceResolvable mockMsgSourceResolvable; + + [SetUp] + public void SetUp() + { + mocks = new MockRepository(); + mockMsgSource = (IMessageSource) mocks.CreateMock(typeof(IMessageSource)); + mockMsgSourceResolvable = (IMessageSourceResolvable) mocks.CreateMock(typeof(IMessageSourceResolvable)); + } + + [TearDown] + public void TearDown() + { + mocks.VerifyAll(); + } + + [Test] + public void GetMessageCodeCultureArgs() + { + Expect.Call(mockMsgSource.GetMessage(MSGCODE, MSGCULTURE, MSGARGS)).Return(MSGRESULT); + mocks.ReplayAll(); + + MessageSourceAccessor msgSourceAccessor = new MessageSourceAccessor(mockMsgSource); + Assert.AreEqual(MSGRESULT, msgSourceAccessor.GetMessage(MSGCODE, MSGCULTURE, MSGARGS)); + } + + [Test] + public void GetMessageCodeArgs() + { + Expect.Call(mockMsgSource.GetMessage(MSGCODE, MSGCULTURE, MSGARGS)).Return(MSGRESULT); + mocks.ReplayAll(); + + MessageSourceAccessor msgSourceAccessor = new MessageSourceAccessor(mockMsgSource, MSGCULTURE); + Assert.AreEqual(MSGRESULT, msgSourceAccessor.GetMessage(MSGCODE, MSGARGS)); + } + + [Test] + public void GetMessageCodeArgsDefaultsToCurrentUICulture() + { + Expect.Call(mockMsgSource.GetMessage(MSGCODE, CultureInfo.CurrentUICulture, MSGARGS)).Return(MSGRESULT); + mocks.ReplayAll(); + + MessageSourceAccessor msgSourceAccessor = new MessageSourceAccessor(mockMsgSource); + Assert.AreEqual(MSGRESULT, msgSourceAccessor.GetMessage(MSGCODE, MSGARGS)); + } + + [Test] + public void GetMessageCodeCulture() + { + Expect.Call(mockMsgSource.GetMessage(MSGCODE, MSGCULTURE)).Return(MSGRESULT); + mocks.ReplayAll(); + + MessageSourceAccessor msgSourceAccessor = new MessageSourceAccessor(mockMsgSource); + Assert.AreEqual(MSGRESULT, msgSourceAccessor.GetMessage(MSGCODE, MSGCULTURE)); + } + + [Test] + public void GetMessageCodeDefaultsToCurrentUICulture() + { + Expect.Call(mockMsgSource.GetMessage(MSGCODE, CultureInfo.CurrentUICulture)).Return(MSGRESULT); + mocks.ReplayAll(); + + MessageSourceAccessor msgSourceAccessor = new MessageSourceAccessor(mockMsgSource); + Assert.AreEqual(MSGRESULT, msgSourceAccessor.GetMessage(MSGCODE)); + } + + [Test] + public void GetMessageResolvableCulture() + { + Expect.Call(mockMsgSource.GetMessage(mockMsgSourceResolvable, MSGCULTURE)).Return(MSGRESULT); + mocks.ReplayAll(); + + MessageSourceAccessor msgSourceAccessor = new MessageSourceAccessor(mockMsgSource); + Assert.AreEqual(MSGRESULT, msgSourceAccessor.GetMessage(mockMsgSourceResolvable, MSGCULTURE)); + } + + [Test] + public void GetMessageResolvableDefaultsToCurrentUICulture() + { + Expect.Call(mockMsgSource.GetMessage(mockMsgSourceResolvable, CultureInfo.CurrentUICulture)).Return(MSGRESULT); + mocks.ReplayAll(); + + MessageSourceAccessor msgSourceAccessor = new MessageSourceAccessor(mockMsgSource); + Assert.AreEqual(MSGRESULT, msgSourceAccessor.GetMessage(mockMsgSourceResolvable)); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/NamespaceParsersSectionHandlerTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/NamespaceParsersSectionHandlerTests.cs new file mode 100644 index 00000000..cb9f6d17 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/NamespaceParsersSectionHandlerTests.cs @@ -0,0 +1,126 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Configuration; +using System.Xml; +using NUnit.Framework; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Unit tests for the NamespaceParsersSectionHandler class. + /// + /// Rick Evans + [TestFixture] + public sealed class NamespaceParsersSectionHandlerTests + { + [Test] + public void ParseSectionSunnyDay() + { + const string xml = @" + + +"; + + NamespaceParsersSectionHandler handler = new NamespaceParsersSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void ParseSectionWithHandlerThatDoesNotImplement_IXmlObjectDefinitionParser() + { + const string xml = @" + + +"; + + NamespaceParsersSectionHandler handler = new NamespaceParsersSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + [ExpectedException(typeof (TypeLoadException))] + public void ParseSectionWithBadTypeForHandler() + { + const string xml = @" + + +"; + + NamespaceParsersSectionHandler handler = new NamespaceParsersSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + public void ParseSectionWithNoChildParserNamespaceElements() + { + const string xml = @" + + +"; + + NamespaceParsersSectionHandler handler = new NamespaceParsersSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void ParseSectionWithEmptyType() + { + const string xml = @" + + +"; + + NamespaceParsersSectionHandler handler = new NamespaceParsersSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] +#if NET_2_0 + [ExpectedException(typeof(ConfigurationErrorsException))] +#else + [ExpectedException(typeof (ConfigurationException))] +#endif + public void WithParserElementThatIsMissingTheTypeAttribute() + { + const string xml = @" + + +"; + + NamespaceParsersSectionHandler handler = new NamespaceParsersSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + private static XmlNode BuildConfigurationSection(string xml) + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + return doc.DocumentElement; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/NullMessageSourceTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/NullMessageSourceTests.cs new file mode 100644 index 00000000..8def2dad --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/NullMessageSourceTests.cs @@ -0,0 +1,61 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using NUnit.Framework; + +namespace Spring.Context.Support +{ + /// + /// Unit tests for the NullMessageSource class. + /// + /// + /// $Id: NullMessageSourceTests.cs,v 1.3 2007/07/02 21:24:48 markpollack Exp $ + [TestFixture] + public sealed class NullMessageSourceTests + { + [Test] + public void CanonicalInstanceIsNotNull() + { + Assert.IsNotNull(NullMessageSource.Null); + } + + [Test] + public void ResolveMessageSpitsbackWhatItWasGiven() + { + const string expected = "foo"; + string message = NullMessageSource.Null.GetMessage(expected); + Assert.AreEqual(expected, message); + } + + [Test] + public void ResolveObjectReturnsNull() + { + object anObject = NullMessageSource.Null.GetResourceObject(""); + Assert.IsNull(anObject); + } + + [Test] + public void ApplyResourcesDoesNothing() + { + NullMessageSource.Null.ApplyResources("", "foo", null); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/ResourceSetMessageSourceTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/ResourceSetMessageSourceTests.cs new file mode 100644 index 00000000..8f1a5087 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/ResourceSetMessageSourceTests.cs @@ -0,0 +1,425 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Resources; +using System.Text; +using NUnit.Framework; +using Spring.Globalization; +using Spring.Objects; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Unit tests for the ResourceSetMessageSource class. + /// + /// + /// $Id: ResourceSetMessageSourceTests.cs,v 1.13 2007/07/18 21:26:07 oakinger Exp $ + [TestFixture] + public sealed class ResourceSetMessageSourceTests + { + private ResourceSetMessageSource messageSource; + + private const string ResourceNamespace = "Spring.Resources"; + private const string ResourceFileName = "Spring.Context.Tests"; + // The namespace is added during the build... + private const string ResourceBaseName = ResourceNamespace + "." + ResourceFileName; + private IList resourceManagerList; + + [TestFixtureSetUp] + public void TestFixtureSetUp() + { + CultureTestScope.Set(); + } + + [TestFixtureTearDown] + public void TestFixtureTearDown() + { + CultureTestScope.Reset(); + } + + /// + /// Populate a known ResourceManager in the resourceManagerList. + /// + [SetUp] + public void Init() + { + messageSource = new ResourceSetMessageSource(); + resourceManagerList = new ArrayList(); + Assembly ass = this.GetType().Assembly; + resourceManagerList.Add(new ResourceManager(ResourceBaseName, ass)); + } + + [TearDown] + public void Destroy() + { + messageSource = null; + } + + public void DoTestMessageAccess(bool hasParentContext, bool useCodeAsDefaultMessage) + { + StaticApplicationContext ac = new StaticApplicationContext(); + if (hasParentContext) + { + StaticApplicationContext parent = new StaticApplicationContext(); + parent.Refresh(); + ac.ParentContext = parent; + } + + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("resourceManagers", resourceManagerList); + if (useCodeAsDefaultMessage) + { + pvs.Add("UseCodeAsDefaultMessage", true); + } + ac.RegisterSingleton("messageSource", typeof(ResourceSetMessageSource), pvs); + ac.Refresh(); + + + // Localizaiton fallbacks + GetMessageLocalizaitonFallbacks(ac); + + // MessageSourceAccessor functionality + MessageSourceAccessor accessor = new MessageSourceAccessor(ac); + Assert.AreEqual("message3", accessor.GetMessage("code3", CultureInfo.CurrentUICulture, (object[])null)); + + // IMessageSourceResolveable + Assert.AreEqual("message3", ac.GetMessage("code3", CultureInfo.CurrentUICulture, (object[]) null)); + IMessageSourceResolvable resolvable = new DefaultMessageSourceResolvable("code3"); + + Assert.AreEqual("message3", ac.GetMessage(resolvable, CultureInfo.CurrentUICulture)); + resolvable = new DefaultMessageSourceResolvable(new string[] {"code4", "code3"}); + Assert.AreEqual("message3", ac.GetMessage(resolvable, CultureInfo.CurrentUICulture)); + + Assert.AreEqual("message3", ac.GetMessage("code3", CultureInfo.CurrentUICulture, (object[])null)); + resolvable = new DefaultMessageSourceResolvable(new string[] { "code4", "code3" }); + Assert.AreEqual("message3", ac.GetMessage(resolvable, CultureInfo.CurrentUICulture)); + + object[] arguments = new object[] { "Hello", new DefaultMessageSourceResolvable(new string[]{"code1"}) }; + Assert.AreEqual("Hello, message1", ac.GetMessage("hello", CultureInfo.CurrentUICulture, arguments)); + + + // test default message without and with args + Assert.AreEqual("default", ac.GetMessage(null, "default", CultureInfo.CurrentUICulture, null)); + Assert.AreEqual("default", ac.GetMessage(null, "default", CultureInfo.CurrentUICulture, arguments)); + + /* not supported + Assert.AreEqual("{0}, default", ac.GetMessage(null, "{0}, default", CultureInfo.CurrentUICulture, null)); + */ + + Assert.AreEqual("Hello, default", ac.GetMessage(null, "{0}, default", CultureInfo.CurrentUICulture, arguments)); + + // test resolvable with default message, without and with args + resolvable = new DefaultMessageSourceResolvable(null, null, "default"); + Assert.AreEqual("default", ac.GetMessage(resolvable, CultureInfo.CurrentUICulture)); + resolvable = new DefaultMessageSourceResolvable(null, arguments, "default"); + Assert.AreEqual("default", ac.GetMessage(resolvable, CultureInfo.CurrentUICulture)); + + /* not supported + resolvable = new DefaultMessageSourceResolvable(null, null, "{0}, default"); + Assert.AreEqual("{0}, default", ac.GetMessage(resolvable, CultureInfo.CurrentUICulture)); + */ + + resolvable = new DefaultMessageSourceResolvable(null, arguments, "{0}, default"); + Assert.AreEqual("Hello, default", ac.GetMessage(resolvable, CultureInfo.CurrentUICulture)); + + + // test message args + Assert.AreEqual("Arg1, Arg2", ac.GetMessage("hello", CultureInfo.CurrentUICulture, new object[]{"Arg1", "Arg2"})); + + /* not supported + Assert.AreEqual("{0}, {1}", ac.GetMessage("hello", CultureInfo.CurrentUICulture, null)); + */ + + + /* not supported + Assert.AreEqual("Hello\nWorld", ac.GetMessage("escaped")); + } + else + { + Assert.AreEqual("Hello\\nWorld", ac.GetMessage("escaped")); + } + */ + + + try + { + Assert.AreEqual("MyNonExistantMessage", ac.GetMessage("MyNonExistantMessage")); + if (!useCodeAsDefaultMessage) + { + Assert.Fail("Should have thrown NoSuchMessagException"); + } + } + catch (NoSuchMessageException e) + { + if (useCodeAsDefaultMessage) + { + Assert.Fail("Should have returned code as default message."); + e.ToString(); + } + } + + } + + [Test] + public void SimpleFormat() + { + string msg = String.Format(CultureInfo.CurrentUICulture, "\"Hello World\""); + Assert.AreEqual("\"Hello World\"", msg); + } + + [Test] + public void MessageAccessFallbackTurnedOff() + { + DoTestMessageAccess(false, false); + } + + + [Test] + public void MessageAccessFallbackTurnedOn() + { + DoTestMessageAccess(false, true); + } + + + [Test] + public void MessageAccessWithParentAndFallbackTurnedOff() + { + DoTestMessageAccess(true, false); + } + + + /// + /// Test to string shows expected message with base name listing. + /// + [Test] + public void ResourceSetMessageSourceToString() + { + messageSource.ResourceManagers = resourceManagerList; + Assert.AreEqual("ResourceSetMessageSource with ResourceManagers of base names = [Spring.Resources.Spring.Context.Tests]", + messageSource.ToString(), "ToString not as expected"); + } + + /// + /// Happy day scenario where the requested message key is found and substitutions are made. + /// + [Test] + public void ResourceSetMessageSourceGetMessage() + { + messageSource.ResourceManagers = resourceManagerList; + GetMessageLocalizaitonFallbacks(messageSource); + } + + private void GetMessageLocalizaitonFallbacks(IMessageSource msgSource) + { + Assert.AreEqual("This is Spring.NET", + msgSource.GetMessage("MyMessage", new object[] { "Spring", ".NET" }), "message not as expected"); + + Assert.AreEqual("Isso e Spring.NET", + msgSource.GetMessage("MyMessage", new CultureInfo("pt-BR"), new object[] { "Spring", ".NET" }), "message not as expected"); + + Assert.AreEqual("Visual Studio loves Spring.NET", + msgSource.GetMessage("MyNewMessage", new object[] { "Spring", ".NET" }), "message not as expected"); + + // test localization fallbacks + Assert.AreEqual("Visual Studio loves Spring.NET", + msgSource.GetMessage("MyNewMessage", new CultureInfo("pt-BR"), new object[] { "Spring", ".NET" }), "message not as expected"); + + Assert.AreEqual("Ovo je Spring.NET", + msgSource.GetMessage("MyMessage", new CultureInfo(CultureInfoUtils.SerbianLatinCultureName), new object[] { "Spring", ".NET" }), "message not as expected"); + + Assert.AreEqual("Ово је Spring.NET", + msgSource.GetMessage("MyMessage", new CultureInfo(CultureInfoUtils.SerbianCyrillicCultureName), + new object[] { "Spring", ".NET" }), "message not as expected"); + + Assert.AreEqual("Visual Studio voli Spring.NET", + msgSource.GetMessage("MyNewMessage", new CultureInfo(CultureInfoUtils.SerbianCyrillicCultureName), new object[] { "Spring", ".NET" }), "message not as expected"); + + Assert.AreEqual("First name", + msgSource.GetMessage("field.firstname", new CultureInfo(CultureInfoUtils.SerbianCyrillicCultureName)), "message not as expected"); + } + + /// + /// Test the happy day scenario of returning an object + /// + [Test] + public void GetResource() + { + //Add another resource manager to the list + Assembly ass = Assembly.LoadWithPartialName("Spring.Core.Tests"); + string baseName = "Spring" + "." + "Resources.Images"; + + resourceManagerList.Add(new ResourceManager(baseName, ass)); + messageSource.ResourceManagers = resourceManagerList; + object obj = messageSource.GetResourceObject("bubblechamber", CultureInfo.CurrentCulture); + Assert.IsNotNull(obj, "expected to retrieve object form resource set"); + Bitmap bitMap = null; + + //.NET 1.0 returns this as a base64 string while .NET 1.1 returns it as a Bitmap + //There are some isues with resx compatability between framework versions. + if (obj is string) + { + //thanks dotnetdave for the string to bitmap cookbook. + //http://www.vsdntips.com/Tips/VS.NET/Csharp/76.aspx + string ImageText = obj as string; + Byte[] bitmapData = new Byte[ImageText.Length]; + bitmapData = Convert.FromBase64String(FixBase64ForImage(ImageText)); + MemoryStream streamBitmap = new MemoryStream(bitmapData); + bitMap = new Bitmap((Bitmap)Image.FromStream(streamBitmap)); + + } + else + { + bitMap = obj as Bitmap; + } + Assert.IsNotNull(bitMap, "expected to retrieve BitMap. Instead Type = " + obj.GetType()); + Assert.AreEqual(146, bitMap.Size.Width, "Width of image wrong"); + Assert.AreEqual(119, bitMap.Size.Height, "Height of image wrong"); + + object obj2 = messageSource.GetResourceObject("notExist", CultureInfo.CurrentCulture); + Assert.IsNull(obj2, "non existent resourse found"); + } + + private string FixBase64ForImage(string Image) + { + StringBuilder sbText = new StringBuilder(Image, Image.Length); + sbText.Replace("\r\n", String.Empty); + sbText.Replace(" ", String.Empty); + return sbText.ToString(); + } + +#if ! NET_1_0 + /// + /// Test applying a family of resources to an object. + /// + [Test] + public void ApplyResources() + { + //Add another resource manager to the list + TestObject to = new TestObject(); + ComponentResourceManager mgr = new ComponentResourceManager(to.GetType()); + resourceManagerList.Add(mgr); + messageSource.ResourceManagers = resourceManagerList; + + messageSource.ApplyResources(to, "testObject", CultureInfo.CurrentCulture); + Assert.AreEqual("Mark", to.Name); + Assert.AreEqual(35, to.Age); + } +#endif + + /// + /// Test when the code being resolves itself implements IMessageResolvable. + /// + [Test] + public void MessageResolveableGetMessage() + { + string[] codes = { "field.firstname" }; + DefaultMessageSourceResolvable dmr = new DefaultMessageSourceResolvable(codes, null); + + messageSource.ResourceManagers = resourceManagerList; + Assert.AreEqual(messageSource.GetMessage("error.required", CultureInfo.CurrentCulture, dmr, "dude!"), "First name is required dude!", "message not as expected"); + } + + /// + /// Get exception when resource doesn't exist. + /// + [Test] + [ExpectedException(typeof(NoSuchMessageException))] + public void ResourceSetMessageSourceGetNonExistantResource() + { + messageSource.ResourceManagers = resourceManagerList; + messageSource.GetMessage("MyNonExistantMessage", CultureInfo.CurrentCulture, new object[] { "Spring", ".NET" }); + } + +#if !NET_2_0 + /// + /// Test resource whose value is null + /// + [Test] + public void ResourceSetMessageSourceGetNullResource() + { + messageSource.ResourceManagers = resourceManagerList; + object resource = messageSource.GetResourceObject("MyNullMessage", CultureInfo.InvariantCulture); + Assert.AreEqual(String.Empty, resource, "should've returned empty string"); + } +#endif + +#if NET_2_0 + /// + /// Test resource whose value is null. + /// + [Test] + [Ignore("See SPRNET-246")] + public void GetNullResourceIn2PointOh() + { + messageSource.ResourceManagers = resourceManagerList; + object resource = messageSource.GetResourceObject("MyNullMessage", CultureInfo.InvariantCulture); + Assert.AreEqual(String.Empty, resource, "should've returned empty string"); + } +#endif + + /// + /// Exercise repeated requests to hit the cache of MessageSets. Note order of test is not assured + /// in NUnit. + /// + [Test] + public void ResourceSetMessageSourceCachedMessage() + { + messageSource.ResourceManagers = resourceManagerList; + Assert.AreEqual("This is Spring.NET", messageSource.GetMessage("MyMessage", CultureInfo.InvariantCulture, new object[] { "Spring", ".NET" }), "message"); + Assert.AreEqual("Visual Studio loves Spring.NET", messageSource.GetMessage("MyNewMessage", CultureInfo.InvariantCulture, "Spring", ".NET"), "message"); + Assert.AreEqual("This is Spring.NET", messageSource.GetMessage("MyMessage", CultureInfo.InvariantCulture, new object[] { "Spring", ".NET" }), "message"); + } + + /// + /// Test when there are two resource managers listed. + /// + [Test] + public void TwoResourceManagers() + { + //Add another resource manager to the list + Assembly ass = Assembly.LoadWithPartialName("Spring.Core.Tests"); + string baseName = "Spring" + "." + "Resources.SampleResources"; + resourceManagerList.Add(new ResourceManager(baseName, ass)); + + messageSource.ResourceManagers = resourceManagerList; + + //Repeat the test for the first resource manager + + Assert.AreEqual("This is Spring.NET", + messageSource.GetMessage("MyMessage", new object[] { "Spring", ".NET" }), "message not as expected"); + + //Now with the newly added one + Assert.AreEqual("Hello Mr. Anderson", + messageSource.GetMessage("message", + new object[] { "Mr.", "Anderson" }), "message not as expected"); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/Service.cs b/test/Spring/Spring.Core.Tests/Context/Support/Service.cs new file mode 100644 index 00000000..8fefee3d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/Service.cs @@ -0,0 +1,75 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Context.Support +{ + /// Mark Pollack + /// $Id: Service.cs,v 1.1 2007/07/10 05:48:37 markpollack Exp $ + public class Service : IApplicationContextAware, IMessageSourceAware, IDisposable + { + private IApplicationContext applicationContext; + private IMessageSource messageSource; + private IResourceLoaderAware[] resources; + private bool properlyDestroyed = false; + + public IApplicationContext ApplicationContext + { + get { return applicationContext; } + set { applicationContext = value; } + } + + public IMessageSource MessageSource + { + set + { + if (messageSource != null) + { + throw new InvalidOperationException("MessageSource should not be set twice"); + } + messageSource = value; + } + get + { + return messageSource; + } + + } + + + public IResourceLoaderAware[] Resources + { + get { return resources; } + set { resources = value; } + } + + public bool ProperlyDestroyed + { + get { return properlyDestroyed; } + } + + public void Dispose() + { + properlyDestroyed = true; + //TODO - try to get object while destroying, expect ObjectCreationNotAllowedException + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/StaticApplicationContextTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/StaticApplicationContextTests.cs new file mode 100644 index 00000000..4c750c88 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/StaticApplicationContextTests.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Globalization; +using NUnit.Framework; + +namespace Spring.Context.Support +{ + [TestFixture] + public class StaticApplicationContextTests + { + [Test] + public void AddMessageTest() + { + StaticApplicationContext ctx = new StaticApplicationContext(); + ctx.Refresh(); + ctx.AddMessage("code1", CultureInfo.CurrentUICulture, "this is {0}"); + Assert.AreEqual("this is Spring.NET", ctx.GetMessage("code1", "Spring.NET")); + } + + [Test] + public void RegisterObjectPrototype() + { + StaticApplicationContext ctx = new StaticApplicationContext(); + ctx.RegisterPrototype("my object", typeof(MockContextAwareObject), null); + MockContextAwareObject ctx1 = (MockContextAwareObject) ctx.GetObject("my object"); + MockContextAwareObject ctx2 = (MockContextAwareObject) ctx.GetObject("my object"); + Assert.IsTrue(ctx1 != ctx2); + Assert.IsTrue(!ctx.IsSingleton("my object")); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/StaticMessageSourceTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/StaticMessageSourceTests.cs new file mode 100644 index 00000000..3755b8e9 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/StaticMessageSourceTests.cs @@ -0,0 +1,219 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using NUnit.Framework; +using Spring.Globalization; +using Spring.Objects; + +namespace Spring.Context.Support +{ + /// + /// Unit tests for the StaticMessageSource class. + /// + /// + /// $Id: StaticMessageSourceTests.cs,v 1.9 2007/07/24 17:26:40 oakinger Exp $ + [TestFixture] + public sealed class StaticMessageSourceTests + { + [TestFixtureSetUp] + public void TestFixtureSetUp() + { + CultureTestScope.Set(); + } + + [TestFixtureTearDown] + public void TestFixtureTearDown() + { + CultureTestScope.Reset(); + } + + [Test] + public void GetMessageDefaultsToCurrentUICulture() + { + StaticMessageSource msgSource = new StaticMessageSource(); + msgSource.AddMessage("code", CultureInfo.CurrentUICulture, "{0}"); + Assert.AreEqual("my message", msgSource.GetMessage("code", "my message"), "message"); + + try + { + msgSource.GetMessage("code", CultureInfo.CurrentCulture); + Assert.Fail("message"); + } + catch(NoSuchMessageException) + {} + } + + [Test] + public void GetMessageCode() + { + StaticMessageSource msgSource = new StaticMessageSource(); + msgSource.AddMessage("code", CultureInfo.CurrentUICulture, "{0} {1}"); + Assert.AreEqual("my message", + msgSource.GetMessage("code", CultureInfo.CurrentUICulture, new object[] {"my", "message"}), + "message"); + } + + [Test] + public void StaticMessageSourceToString() + { + StaticMessageSource msgSource = new StaticMessageSource(); + msgSource.AddMessage("code1", CultureInfo.CurrentUICulture, "{0} {1}"); + Assert.AreEqual("StaticMessageSource : ['code1_" + CultureInfo.CurrentUICulture.Name + "' : '{0} {1}']", + msgSource.ToString()); + } + + + [Test] + public void ApplyResources() + { + TestObject value = new TestObject(); + StaticMessageSource msgSource = new StaticMessageSource(); +#if ! NET_1_0 + msgSource.ApplyResources(value, "testObject", CultureInfo.InvariantCulture); + Assert.AreEqual("Mark", value.Name, "Name property value not applied."); + Assert.AreEqual(35, value.Age, "Age property value not applied."); +#else + try + { + msgSource.ApplyResources(value, "testObject", CultureInfo.InvariantCulture); + Assert.Fail("Should not be supported in 1.0"); + } catch (NotSupportedException e) + { + Assert.AreEqual("Operation not supported in .NET 1.0 Release.", + e.Message); + } +#endif + } + + [Test] + public void ApplyResourcesWithNullObject() + { + TestObject value = new TestObject(); + StaticMessageSource msgSource = new StaticMessageSource(); +#if ! NET_1_0 + msgSource.ApplyResources(null, "testObject", CultureInfo.InvariantCulture); + Assert.AreEqual(null, value.Name); + Assert.AreEqual(0, value.Age); +#else + try + { + msgSource.ApplyResources(null, "testObject", CultureInfo.InvariantCulture); + Assert.Fail("Should not be supported in 1.0"); + } catch (NotSupportedException e) + { + Assert.AreEqual("Operation not supported in .NET 1.0 Release.", + e.Message); + } +#endif + } + + [Test] + public void ApplyResourcesWithNullLookupKey() + { + TestObject value = new TestObject(); + StaticMessageSource msgSource = new StaticMessageSource(); +#if ! NET_1_0 + try + { + msgSource.ApplyResources(value, null, CultureInfo.InvariantCulture); + Assert.Fail("ArgumentNullException was expected"); + } catch (ArgumentNullException e) + { + Assert.IsNotNull(e); + } + Assert.AreEqual(null, value.Name); + Assert.AreEqual(0, value.Age); +#else + try + { + msgSource.ApplyResources(value, null, CultureInfo.InvariantCulture); + Assert.Fail("Should not be supported in 1.0"); + } + catch (NotSupportedException e) + { + Assert.AreEqual("Operation not supported in .NET 1.0 Release.", + e.Message); + } +#endif + } + + [Test] + public void GetResourceObjectWithCodeAndCulture() + { + TestObject value = new TestObject("Rick", 30); + StaticMessageSource msgSource = new StaticMessageSource(); + msgSource.AddObject("rick", CultureInfo.InstalledUICulture, value); + + TestObject retrieved = (TestObject) + msgSource.GetResourceObject("rick", CultureInfo.InstalledUICulture); + Assert.IsNotNull(retrieved, + "Object previously added to StaticMessageSource was not retrieved " + + "when using same lookup code and CultureInfo."); + Assert.IsTrue(ReferenceEquals(value, retrieved), + "Object returned from StaticMessageSource was not the same one " + + "that was previously added (it must be)."); + } + + [Test] + public void GetResourceObjectWithCode() + { + TestObject value = new TestObject("Rick", 30); + StaticMessageSource msgSource = new StaticMessageSource(); + msgSource.AddObject("rick", CultureInfo.CurrentUICulture, value); + + TestObject retrieved = (TestObject) + msgSource.GetResourceObject("rick"); + Assert.IsNotNull(retrieved, + "Object previously added to StaticMessageSource was not retrieved " + + "when using same lookup code."); + Assert.IsTrue(ReferenceEquals(value, retrieved), + "Object returned from StaticMessageSource was not the same one " + + "that was previously added (it must be)."); + } + + [Test] + public void GetResourceObjectThatAintPreviouslyBeenAddedDoesntYieldAnything() + { + StaticMessageSource msgSource = new StaticMessageSource(); + TestObject retrieved = (TestObject) + msgSource.GetResourceObject("rick"); + Assert.IsNull(retrieved, + "Getting 'some (?) weird object out of an empty StaticMessageSource"); + } + + [Test] + public void GetResourceObjectWithCodeAssumesCurrentUICulture() + { + TestObject value = new TestObject("Rick", 30); + StaticMessageSource msgSource = new StaticMessageSource(); + msgSource.AddObject("rick", CultureInfo.InvariantCulture, value); + + // assumes object was previously added using CultureInfo.CurrentUICulture + TestObject retrieved = (TestObject) + msgSource.GetResourceObject("rick"); + Assert.IsNull(retrieved, + "Object previously added to StaticMessageSource " + + "(using CultureInfo.InvariantCulture) was (wrongly) retrieved " + + "when using same lookup code."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/TestObjectConfigParser.cs b/test/Spring/Spring.Core.Tests/Context/Support/TestObjectConfigParser.cs new file mode 100644 index 00000000..2b8b1e7c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/TestObjectConfigParser.cs @@ -0,0 +1,100 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Xml; +using Spring.Objects; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Support +{ + /// + /// A simple parser to create TestObjects. + /// + public class TestObjectConfigParser : ObjectsNamespaceParser + { + /// + /// Has the method been called? + /// + public static bool ParseElementCalled = false; + + /// + /// Create an instance of the parser + /// + public TestObjectConfigParser() + { + } + + /// + /// Parse the specified element and register any resulting + /// IObjectDefinitions with the IObjectDefinitionRegistry that is + /// embedded in the supplied ParserContext. + /// + /// The element to be parsed into one or more IObjectDefinitions + /// The object encapsulating the current state of the parsing + /// process. + /// + /// The primary IObjectDefinition (can be null as explained above) + /// + /// + /// Implementations should return the primary IObjectDefinition + /// that results from the parse phase if they wish to used nested + /// inside (for example) a <property> tag. + /// Implementations may return null if they will not + /// be used in a nested scenario. + /// + /// + public override IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + ParseElementCalled = true; + ObjectDefinitionHolder holder = ParseTestObjectDefinition(element, parserContext); + ObjectDefinitionReaderUtils.RegisterObjectDefinition(holder, parserContext.Registry); + return null; + } + + + private ObjectDefinitionHolder ParseTestObjectDefinition(XmlElement rootElement, ParserContext parserContext) + { + MutablePropertyValues properties = new MutablePropertyValues(); + + XmlNodeList childNodes = rootElement.ChildNodes; + + //Get all properties (from non whitespace nodes) + foreach (XmlNode childNode in childNodes) + { + if (XmlNodeType.Whitespace != childNode.NodeType) + { + properties.Add(new PropertyValue(childNode.LocalName, childNode.InnerText)); + } + } + IConfigurableObjectDefinition od = new RootObjectDefinition(typeof (TestObject), null, properties); + od.IsSingleton = false; + + //HardCoded for now. + string id = "testObject"; + //id = ObjectDefinitionReaderUtils.GenerateObjectName(od, reader.ObjectReader.Registry); + + return new ObjectDefinitionHolder(od, id); + + + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/TypeAliasesSectionHandlerTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/TypeAliasesSectionHandlerTests.cs new file mode 100644 index 00000000..62a99918 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/TypeAliasesSectionHandlerTests.cs @@ -0,0 +1,222 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Configuration; +using System.Xml; +using NUnit.Framework; +using Spring.Core.TypeResolution; +using Spring.Objects; +using Spring.Util; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// Unit tests for the TypeAliasesSectionHandler class. + /// + [TestFixture] + public sealed class TypeAliasesSectionHandlerTests + { + [Test] + public void ParseSectionSunnyDay() + { + const string xml = @" + + +"; + + Assert.IsNull(TypeRegistry.ResolveType("fiona.apple"), "TypeRegistry already contains an alias for the type being tested (it must not)."); + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + + Type type = TypeRegistry.ResolveType("fiona.apple"); + Assert.AreEqual(typeof (TestObject), type, "The type alias was not registered by the TypeAliasesSectionHandler."); + } + +#if NET_2_0 + [Test] + public void WithGenericType() + { + const string xml = @" + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + public void WithGenericTypeDefinition() + { + const string xml = @" + + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } +#endif + + [Test] + [ExpectedException(typeof (TypeLoadException))] + public void WithNonExistentType() + { + const string xml = @" + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void WithEmptyTypeName() + { + const string xml = @" + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void WithWhitespacedTypeName() + { + const string xml = @" + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void WithEmptyAlias() + { + const string xml = @" + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void WithWhitespacedAlias() + { + const string xml = @" + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + public void ParseSectionWithNoChildParserNamespaceElements() + { + const string xml = @" + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] + public void ParseSectionWithGuffChildParserNamespaceElementsIsAllowed() + { + const string xml = @" + + + + + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] +#if NET_2_0 + [ExpectedException(typeof(ConfigurationErrorsException))] +#else + [ExpectedException(typeof (ConfigurationException))] +#endif + public void WithAliasElementThatIsMissingTheNameAttribute() + { + const string xml = @" + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + [Test] +#if NET_2_0 + [ExpectedException(typeof(ConfigurationErrorsException))] +#else + [ExpectedException(typeof (ConfigurationException))] +#endif + public void WithAliasElementThatIsMissingTheTypeAttribute() + { + const string xml = @" + + +"; + + TypeAliasesSectionHandler handler = new TypeAliasesSectionHandler(); + handler.Create(null, null, BuildConfigurationSection(xml)); + } + + private static XmlNode BuildConfigurationSection(string xml) + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + return doc.DocumentElement; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/XmlApplicationContextTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/XmlApplicationContextTests.cs new file mode 100644 index 00000000..48da4adc --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/XmlApplicationContextTests.cs @@ -0,0 +1,351 @@ +#region License + +/* + * Copyright 2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using NUnit.Framework; +using Spring.Core; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Support +{ + /// + /// Test creation of application context from XML. + /// + /// Mark Pollack + [TestFixture] + public sealed class XmlApplicationContextTests + { + [Test] + public void SingleConfigLocation() + { + XmlApplicationContext ctx = + new XmlApplicationContext(false, "assembly://Spring.Core.Tests/Spring.Context.Support/simpleContext.xml"); + Assert.IsTrue(ctx.ContainsObject("someMessageSource")); + ctx.Dispose(); + } + + [Test] + public void MultipleConfigLocations() + { + XmlApplicationContext ctx = + new XmlApplicationContext(false, "assembly://Spring.Core.Tests/Spring.Context.Support/contextB.xml", + "assembly://Spring.Core.Tests/Spring.Context.Support/contextC.xml", + "assembly://Spring.Core.Tests/Spring.Context.Support/contextA.xml"); + Assert.IsTrue(ctx.ContainsObject("service")); + Assert.IsTrue(ctx.ContainsObject("logicOne")); + Assert.IsTrue(ctx.ContainsObject("logicTwo")); + Service service = (Service) ctx.GetObject("service"); + ctx.Refresh(); + Assert.IsTrue(service.ProperlyDestroyed); + service = (Service) ctx.GetObject("service"); + ctx.Dispose(); + Assert.IsTrue(service.ProperlyDestroyed); + } + + [Test] + public void ContextWithInvalidValueType() + { + try + { + XmlApplicationContext ctx = + new XmlApplicationContext(false, + "assembly://Spring.Core.Tests/Spring.Context.Support/invalidValueType.xml"); + Assert.Fail("Should have thrown ObjectCreationException for context", ctx); + } + catch (ObjectCreationException ex) + { + Assert.IsTrue(ex.Message.IndexOf((typeof (TypeMismatchException).Name)) != -1); + Assert.IsTrue(ex.Message.IndexOf(("UseCodeAsDefaultMessage")) != -1); + } + } + + [Test] + [Ignore("Need to add Spring.TypeLoadException")] + public void ContextWithInvalidLazyType() + { + XmlApplicationContext ctx = + new XmlApplicationContext(false, + "assembly://Spring.Core.Tests/Spring.Context.Support/invalidType.xml"); + Assert.IsTrue(ctx.ContainsObject("someMessageSource")); + ctx.GetObject("someMessageSource"); + } + + [Test] + public void CaseInsensitiveContext() + { + XmlApplicationContext ctx = + new XmlApplicationContext(false, "assembly://Spring.Core.Tests/Spring.Context.Support/objects.xml"); + Assert.IsTrue(ctx.ContainsObject("goran")); + Assert.IsTrue(ctx.ContainsObject("Goran")); + Assert.IsTrue(ctx.ContainsObject("GORAN")); + Assert.AreEqual(ctx.GetObject("goran"), ctx.GetObject("GORAN")); + } + + [Test] + public void FactoryObjectsAreNotInstantiatedBeforeObjectFactoryPostProcessorsAreApplied() + { + XmlApplicationContext ctx = new XmlApplicationContext("Spring/Context/Support/SPRNET-192.xml"); + LogFactoryObject logFactory = (LogFactoryObject) ctx["&log"]; + Assert.AreEqual("foo", logFactory.LogName); + } + + /// + /// Make sure that if an IObjectPostProcessor is defined as abstract + /// the creation of an IApplicationContext will not try to instantiate it. + /// + [Test] + public void ContextWithPostProcessors() + { + CountingObjectPostProcessor.Count = 0; + CoutingObjectFactoryPostProcessor.Count = 0; + + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Core.Tests/Spring.Context.Support/objects.xml"); + + Assert.IsTrue(ctx.ContainsObject("abstractObjectProcessorPrototype")); + Assert.IsTrue(ctx.ContainsObject("abstractFactoryProcessorPrototype")); + + Assert.AreEqual(0, CountingObjectPostProcessor.Count); + Assert.AreEqual(0, CoutingObjectFactoryPostProcessor.Count); + } + + /// + /// Make sure that ConfigureObject() completly configures target + /// object (goes through whole lifecycle of object creation and + /// applies all processors). + /// + [Test] + public void ConfigureObject() + { + const string objDefLocation = "assembly://Spring.Core.Tests/Spring.Context.Support/objects.xml"; + + XmlApplicationContext xmlContext = new XmlApplicationContext(new string[] {objDefLocation}); + + object objGoran = xmlContext.GetObject("goran"); + Assert.IsTrue(objGoran is TestObject); + TestObject fooGet = objGoran as TestObject; + + TestObject fooConfigure = new TestObject(); + xmlContext.ConfigureObject(fooConfigure, "goran"); + Assert.IsNotNull(fooGet); + Assert.AreEqual(fooGet.Name, fooConfigure.Name); + Assert.AreEqual(fooGet.Age, fooConfigure.Age); + Assert.AreEqual(fooGet.ObjectName, fooConfigure.ObjectName); + Assert.IsNotNull(fooGet.ObjectName); + Assert.AreEqual(xmlContext, fooGet.ApplicationContext); + Assert.AreEqual(xmlContext, fooConfigure.ApplicationContext); + } + + + [Test] + public void ContextLifeCycle() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Core.Tests/Spring.Context/contextlifecycle.xml"); + IConfigurableApplicationContext configCtx = ctx as IConfigurableApplicationContext; + Assert.IsNotNull(configCtx); + ContextListenerObject clo; + using (configCtx) + { + clo = configCtx["contextListenerObject"] as ContextListenerObject; + Assert.IsNotNull(clo); + Assert.IsTrue(clo.AppListenerContextRefreshed, + "Object did not receive context refreshed event via IApplicationListener"); + Assert.IsTrue(clo.CtxRefreshed, "Object did not receive context refreshed event via direct wiring"); + } + Assert.IsTrue(clo.AppListenerContextClosed, + "Object did not receive context closed event via IApplicationContextListener"); + Assert.IsTrue(clo.CtxClosed, "Object did not receive context closed event via direct wiring."); + } + + [Test] + public void RefreshDisposesExistingObjectFactory_SPRNET479() + { + string tmp = typeof (DestroyTester).FullName; + Console.WriteLine(tmp); + + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Core.Tests/Spring.Context.Support/objects.xml"); + + DestroyTester destroyTester = (DestroyTester) ctx.GetObject("destroyTester"); + DisposeTester disposeTester = (DisposeTester) ctx.GetObject("disposeTester"); + Assert.IsFalse(destroyTester.IsDestroyed); + Assert.IsFalse(disposeTester.IsDisposed); + + ((IConfigurableApplicationContext) ctx).Refresh(); + + Assert.IsTrue(destroyTester.IsDestroyed); + Assert.IsTrue(disposeTester.IsDisposed); + } + + [Test] + public void GenericApplicationContextWithXmlObjectDefinitions() + { + GenericApplicationContext ctx = new GenericApplicationContext(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(ctx); + reader.LoadObjectDefinitions("assembly://Spring.Core.Tests/Spring.Context.Support/contextB.xml"); + reader.LoadObjectDefinitions("assembly://Spring.Core.Tests/Spring.Context.Support/contextC.xml"); + reader.LoadObjectDefinitions("assembly://Spring.Core.Tests/Spring.Context.Support/contextA.xml"); + ctx.Refresh(); + + Assert.IsTrue(ctx.ContainsObject("service")); + Assert.IsTrue(ctx.ContainsObject("logicOne")); + Assert.IsTrue(ctx.ContainsObject("logicTwo")); + ctx.Dispose(); + + } + + [Test] + public void GenericApplicationContextConstructorTests() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Core.Tests/Spring.Context/contextlifecycle.xml"); + GenericApplicationContext genericCtx = new GenericApplicationContext(ctx); + genericCtx = new GenericApplicationContext("test", true, ctx); + } + + #region Helper classes + + public class DisposeTester : IDisposable + { + private bool isDisposed = false; + + public bool IsDisposed + { + get { return isDisposed; } + } + + public void Dispose() + { + if (isDisposed) throw new InvalidOperationException("must not be disposed twice"); + isDisposed = true; + } + } + + public class DestroyTester + { + private bool isDestroyed = false; + + public bool IsDestroyed + { + get { return isDestroyed; } + } + + public void DestroyMe() + { + if (isDestroyed) throw new InvalidOperationException("must not be destroyed twice"); + isDestroyed = true; + } + } + + /// + /// Utility class to keep track of object construction. + /// + public class CountingObjectPostProcessor : IObjectPostProcessor + { + private static int count; + + /// + /// Property Count (int) + /// + public static int Count + { + get { return count; } + set { count = value; } + } + + /// + /// Create an instance and increment the counter + /// + public CountingObjectPostProcessor() + { + count++; + } + + #region IObjectPostProcessor Members + + /// + /// No op implementation + /// + /// object to process + /// name of object + /// processed object + public object PostProcessAfterInitialization(object obj, string objectName) + { + return obj; + } + + /// + /// No op implementation + /// + /// object to process + /// name of object + /// processed object + public object PostProcessBeforeInitialization(object obj, string name) + { + return obj; + } + + #endregion + } + + + /// + /// Utility class to keep track of object construction. + /// + public class CoutingObjectFactoryPostProcessor : IObjectFactoryPostProcessor + { + private static int count; + + /// + /// Property Count (int) + /// + public static int Count + { + get { return count; } + set { count = value; } + } + + /// + /// Create an instance and increment the counter + /// + public CoutingObjectFactoryPostProcessor() + { + count++; + } + + #region IObjectFactoryPostProcessor Members + + /// + /// no op + /// + /// factory to post process + public void PostProcessObjectFactory(IConfigurableListableObjectFactory factory) + { + } + + #endregion + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/contextA.xml b/test/Spring/Spring.Core.Tests/Context/Support/contextA.xml new file mode 100644 index 00000000..fc0aa240 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/contextA.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Support/contextB.xml b/test/Spring/Spring.Core.Tests/Context/Support/contextB.xml new file mode 100644 index 00000000..a3b6568e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/contextB.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Support/contextC.xml b/test/Spring/Spring.Core.Tests/Context/Support/contextC.xml new file mode 100644 index 00000000..4c9abac8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/contextC.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/import1.xml b/test/Spring/Spring.Core.Tests/Context/Support/import1.xml new file mode 100644 index 00000000..dabd9953 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/import1.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/invalidType.xml b/test/Spring/Spring.Core.Tests/Context/Support/invalidType.xml new file mode 100644 index 00000000..5e0032b1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/invalidType.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Support/invalidValueType.xml b/test/Spring/Spring.Core.Tests/Context/Support/invalidValueType.xml new file mode 100644 index 00000000..3cb3307c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/invalidValueType.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Support/objects.xml b/test/Spring/Spring.Core.Tests/Context/Support/objects.xml new file mode 100644 index 00000000..d31ba0bb --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/objects.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Support/simpleContext.xml b/test/Spring/Spring.Core.Tests/Context/Support/simpleContext.xml new file mode 100644 index 00000000..48161fe4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/simpleContext.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Support/testobject.xsd b/test/Spring/Spring.Core.Tests/Context/Support/testobject.xsd new file mode 100644 index 00000000..242311ae --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/testobject.xsd @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/testobject.xsx b/test/Spring/Spring.Core.Tests/Context/Support/testobject.xsx new file mode 100644 index 00000000..ff71343b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/testobject.xsx @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/contextlifecycle.xml b/test/Spring/Spring.Core.Tests/Context/contextlifecycle.xml new file mode 100644 index 00000000..553f7c8c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/contextlifecycle.xml @@ -0,0 +1,11 @@ + + + + + Tests for notificaton on application lifecycle events. + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/ComposedCriteriaTests.cs b/test/Spring/Spring.Core.Tests/Core/ComposedCriteriaTests.cs new file mode 100644 index 00000000..2a5aa955 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/ComposedCriteriaTests.cs @@ -0,0 +1,78 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Text.RegularExpressions; +using NUnit.Framework; + +#endregion + +namespace Spring.Core +{ + [TestFixture] + public class ComposedCriteriaTests : ComposedCriteria + { + public ComposedCriteriaTests() : base() {} + public ComposedCriteriaTests( ICriteria criteria ) : base( criteria ) {} + [Test] + public void IsSatisfiedWithNoCriteria() + { + ComposedCriteriaTests composedCriteria = new ComposedCriteriaTests(); + Assert.IsTrue(composedCriteria.IsSatisfied("foo")); + } + + [Test] + public void SatifiesMyUpperCaseCriteria() + { + ComposedCriteriaTests composedCriteria = new ComposedCriteriaTests(new MyUpperCaseCriteria()); + Assert.IsTrue(composedCriteria.IsSatisfied("HELLO")); + Assert.IsFalse(composedCriteria.IsSatisfied("hello")); + } + + [Test] + public void SatifiesTwoCriteria() + { + ComposedCriteriaTests composedCriteria = new ComposedCriteriaTests(); + composedCriteria.Add(new MyUpperCaseCriteria()); + composedCriteria.Add(new MyStringCriteria()); + Assert.IsTrue(composedCriteria.IsSatisfied("HELLO")); + Assert.IsFalse(composedCriteria.IsSatisfied("GOODBYE")); + Assert.IsTrue( composedCriteria.Criteria.Count == 2 ); + } + + internal class MyUpperCaseCriteria : ICriteria + { + public bool IsSatisfied(object datum) + { + return Regex.Match((string) datum, "[A-Z]").Success; + } + } + + internal class MyStringCriteria : ICriteria + { + public bool IsSatisfied(object datum) + { + return datum.ToString().ToLower() == "hello"; + } + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/ControlFlowFactoryTests.cs b/test/Spring/Spring.Core.Tests/Core/ControlFlowFactoryTests.cs new file mode 100644 index 00000000..3c8a12bb --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/ControlFlowFactoryTests.cs @@ -0,0 +1,123 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Core +{ + /// + /// Unit tests for the default IControlFlow implementation returned by the + /// ControlFlowFactory class. + /// + /// Rick Evans + /// $Id: ControlFlowFactoryTests.cs,v 1.2 2006/04/09 07:24:48 markpollack Exp $ + [TestFixture] + public sealed class ControlFlowFactoryTests + { + [Test] + public void CreateControlFlow() + { + IControlFlow cflow = ControlFlowFactory.CreateControlFlow(); + Assert.IsNotNull(cflow, "The ControlFlowFactory factory class is returning a " + + "null IControlFlow instance (it, obviously, must not)"); + } + + [Test] + public void CreateControlFlowReturnsDistinctInstance() + { + IControlFlow cflow1 = ControlFlowFactory.CreateControlFlow(); + IControlFlow cflow2 = ControlFlowFactory.CreateControlFlow(); + Assert.IsFalse(Object.ReferenceEquals(cflow1, cflow2), "The ControlFlowFactory " + + "factory class is not returning distinct IControlFlow instances (its always " + + "the same instance)"); + } + + [Test] + public void DefaultCflowUnderThisTest() + { + IControlFlow cflow = ControlFlowFactory.CreateControlFlow(); + bool isUnder = cflow.Under(GetType()); + Assert.IsTrue(isUnder, string.Format( + "The IControlFlow implementation created by the ControlFlowFactory factory class " + + "would appear to have a faulty Under(Type) implementation : [{0}]", cflow.GetType())); + } + + [Test] + public void DefaultCflowIsNotUnderSomeArbitraryClass() + { + IControlFlow cflow = ControlFlowFactory.CreateControlFlow(); + bool isUnder = cflow.Under(typeof (TestObject)); + Assert.IsFalse(isUnder, string.Format( + "The IControlFlow implementation created by the ControlFlowFactory factory class " + + "would appear to have a faulty Under(Type) implementation : [{0}]", cflow.GetType())); + } + + [Test] + public void DefaultCflowUnderThisTestAndTestMethodName() + { + IControlFlow cflow = ControlFlowFactory.CreateControlFlow(); + bool isUnder = cflow.Under(GetType(), MethodBase.GetCurrentMethod().Name); + Assert.IsTrue(isUnder, string.Format( + "The IControlFlow implementation created by the ControlFlowFactory factory class " + + "would appear to have a faulty Under(Type,string) implementation : [{0}]", + cflow.GetType())); + } + + [Test] + public void DefaultCflowIsNotUnderThisTestAndSomeRandomMethodName() + { + IControlFlow cflow = ControlFlowFactory.CreateControlFlow(); + bool isUnder = cflow.Under(GetType(), "PlayingYouALikeTheFoolYouAre"); + Assert.IsFalse(isUnder, string.Format( + "The IControlFlow implementation created by the ControlFlowFactory factory class " + + "would appear to have a faulty Under(Type,string) implementation : [{0}]", + cflow.GetType())); + } + + [Test] + public void DefaultCflowUnderToken() + { + IControlFlow cflow = ControlFlowFactory.CreateControlFlow(); + bool isUnder = cflow.UnderToken("Cflow"); + Assert.IsTrue(isUnder, string.Format( + "The IControlFlow implementation created by the ControlFlowFactory factory " + + "class would appear to have a faulty UnderToken(string) implementation : [{0}]", + cflow.GetType())); + } + + [Test] + public void DefaultCflowIsNotUnderSomeArbitraryToken() + { + IControlFlow cflow = ControlFlowFactory.CreateControlFlow(); + bool isUnder = cflow.UnderToken("GoatsCheeseAndSoda"); + Assert.IsFalse(isUnder, string.Format( + "The IControlFlow implementation created by the ControlFlowFactory factory class " + + "would appear to have a faulty UnderToken(string) implementation : [{0}]", + cflow.GetType())); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/IO/AssemblyResourceTest.cs b/test/Spring/Spring.Core.Tests/Core/IO/AssemblyResourceTest.cs new file mode 100644 index 00000000..a4e95777 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/IO/AssemblyResourceTest.cs @@ -0,0 +1,252 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Unit tests for AssemblyResource + /// + /// Aleksandar Seovic + /// Federico Spinazzi + /// $Id: AssemblyResourceTest.cs,v 1.10 2007/08/08 17:49:59 bbaia Exp $ + [TestFixture] + public class AssemblyResourceTest + { + #region SetUp/TearDown + + [SetUp] + public void SetUp() + {} + + [TearDown] + public void TearDown() + {} + + #endregion + + /// + /// Use incorrect format for an assembly resource. Using + /// comma delimited instead of '/'. + /// + [Test] + [ExpectedException(typeof(UriFormatException))] + public void CreateWithMalformedResourceName() + { + new AssemblyResource("assembly://Spring.Core.Tests,Spring.TestResource.txt"); + } + + /// + /// Use old format, no longer supported (actually never publicly released) + /// that used 'dot' notation to seperate the namespace and resource name. + /// + [Test] + [ExpectedException(typeof(UriFormatException))] + public void CreateWithObsoleteResourceName() + { + new AssemblyResource("assembly://Spring.Core.Tests/Spring.TestResource.txt"); + } + + /// + /// Use the correct format but with an invalid assembly name. + /// + [Test] + [ExpectedException(typeof(FileNotFoundException))] + public void CreateFromInvalidAssembly() + { + new AssemblyResource("assembly://Xyz.Invalid.Assembly/Spring/TestResource.txt"); + } + + /// + /// Sunny day scenario that creates IResources and ensures the + /// correct contents can be read from them. + /// + [Test] + public void CreateValidAssemblyResource() + { + IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring/TestResource.txt"); + AssertResourceContent(res, "Spring.TestResource.txt"); + IResource res2 = new AssemblyResource("assembly://Spring.Core.Tests/Spring.Core.IO/TestResource.txt"); + AssertResourceContent(res2, "Spring.Core.IO.TestResource.txt"); + } + + /// + /// Use correct assembly name, but incorrect namespace and resource name. + /// + [Test] + public void CreateInvalidAssemblyResource() + { + IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Xyz/InvalidResource.txt"); + Assert.IsFalse(res.Exists, "Exists should return false"); + Assert.IsNull(res.InputStream, "Stream should be null"); + } + + [Test] + public void CreateRelativeWhenNotRelative() + { + IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring/TestResource.txt"); + IResource res2 = res.CreateRelative("Spring.Core.Tests/Spring.Core.IO/TestResource.txt"); + AssertResourceContent(res2, "Spring.Core.IO.TestResource.txt"); + } + + /// + /// Test creating a resource relative to the location of the first. + /// The first resource is physically located in the root of the project since + /// the default namespace of the Spring.Core.Tests project is + /// 'Spring'. The notation './IO/TestResource.txt' will navigate + /// down to the 'Spring.Core.IO' namespace and CreateRelative will + /// then retrieve the similarly named TestResource.txt located there. + /// + [Test] + public void CreateRelativeInChildNamespace() + { + IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring/TestResource.txt"); + IResource res2 = res.CreateRelative("./Core.IO/TestResource.txt"); + AssertResourceContent(res2, "Spring.Core.IO.TestResource.txt"); + } + + /// + /// Test creating a resource relative to the location of the first. + /// The first resource is physically located in the root of the project since + /// the default namespace of the Spring.Core.Tests project is + /// 'Spring'. The notation 'IO/TestResource.txt' will navigate + /// down to the 'Spring.Core.IO' namespace and CreateRelative will + /// then retrieve the similarly named TestResource.txt located there. + /// + [Test] + public void CreateRelativeInChildNamespaceWithoutPrefix() + { + IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring/TestResource.txt"); + IResource res2 = res.CreateRelative("Core.IO/TestResource.txt"); + AssertResourceContent(res2, "Spring.Core.IO.TestResource.txt"); + } + + /// + /// Test creating a resource relative to the root of the assembly. + /// The first resource is physically located in the root of the project since + /// the default namespace of the Spring.Core.Tests project is + /// 'Spring'. The notation '/Spring.Core.IO/TestResource.txt' will navigate + /// down to the 'Spring.Core.IO' namespace and CreateRelative will + /// then retrieve the similarly named TestResource.txt located there. + /// + [Test] + public void CreateRelativeToRoot() + { + IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring/TestResource.txt"); + IResource res2 = res.CreateRelative("/Spring.Core.IO/TestResource.txt"); + AssertResourceContent(res2, "Spring.Core.IO.TestResource.txt"); + } + + /// + /// Test creating a resource relative to the location of the first. + /// The first resource is physically located in the Spring.Core.IO directory + /// of the project and corresponds to the namespace 'Spring.Core.IO'. + /// The notation '../../TestResource.txt' will navigate up to the + /// root 'Spring' namespace and CreateRelative will then + /// retrieve the similarly named TestResource.txt located there + /// + [Test] + public void CreateRelativeInParentNamespace() + { + IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring.Core.IO/TestResource.txt"); + IResource res2 = res.CreateRelative("../../TestResource.txt"); + AssertResourceContent(res2, "Spring.TestResource.txt"); + } + + /// + /// Test creating a resource relative to the location of the first. + /// The first resource is physically located in the Spring.Core.IO + /// directory of the project and corresponds to the namespace + /// 'Spring.Core.IO'. The notation '../../Objects/Factory/TestResource.txt' + /// will navigate up to the root 'Spring' namespace and then down + /// into the 'Spring.Object.Factory' namespace and CreateRelative + /// will then retrieve the similarly named TestResource.txt located there. + /// + [Test] + public void CreateRelativeInNotStraightParentNamespace() + { + IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring.Core.IO/TestResource.txt"); + IResource res2 = res.CreateRelative("../../Objects/Factory/TestResource.txt"); + AssertResourceContent(res2, "Spring.Objects.Factory.TestResource.txt"); + } + + /// + /// Test creating a resource relative to the location of the first. + /// In this case the first resource is an assembly and the second is + /// uses the file URI. + /// The file URI used three slashes '///' which is interpreted to + /// mean the root of where the assembly is located on the file system. + /// The file 'abstract.xml' is located under Spring.Data in the VS.NET project + /// but a build-event copies these files under the location + /// of Spring.Core.Tests.dll. + /// + [Test] + public void CreateRelativeWithAReferenceToAFileResource() + { + IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring.Core.IO/TestResource.txt"); + string path = "Spring/Objects/Factory/Xml/abstract.xml"; + IResource res2 = res.CreateRelative("file://~/" + path); + using (StreamReader r = File.OpenText(path)) + { + string content = r.ReadToEnd(); + using (StreamReader reader = new StreamReader(res2.InputStream)) + { + Assert.AreEqual(content, reader.ReadToEnd(), "Resource content is not as expected"); + } + } + } + + /// + /// Try to create a relative resource, but use too many '..' to navigate + /// past the root namespace, off into la-la land. + /// + [Test] + [ExpectedException(typeof(UriFormatException))] + public void TooMuchParentNamespacesAbove() + { + IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring.Core.IO/TestResource.txt"); + IResource res2 = res.CreateRelative("../../../../TestResource.txt"); + AssertResourceContent(res2, "Spring.TestResource.txt"); + } + + /// + /// Utility method to compare a resource that contains a single string with + /// an exemplar. + /// + /// The resource to read a line from + /// the expected value of the line. + private void AssertResourceContent(IResource res, string expectedContent) + { + Assert.IsTrue(res.Exists); + using (StreamReader reader = new StreamReader(res.InputStream)) + { + Assert.AreEqual(expectedContent, reader.ReadLine(), "Resource content is not as expected"); + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/IO/ConfigurableResourceLoaderTests.cs b/test/Spring/Spring.Core.Tests/Core/IO/ConfigurableResourceLoaderTests.cs new file mode 100644 index 00000000..4fad0307 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/IO/ConfigurableResourceLoaderTests.cs @@ -0,0 +1,187 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Unit tests for the ConfigurableResourceLoader class. + /// + /// Aleksandar Seovic + /// $Id: ConfigurableResourceLoaderTests.cs,v 1.6 2007/08/08 17:49:59 bbaia Exp $ + [TestFixture] + public sealed class ConfigurableResourceLoaderTests + { + private ConfigurableResourceLoader loader; + + [SetUp] + public void SetUp() + { + loader = new ConfigurableResourceLoader(); + } + + #region ConfigurableResourceLoader.GetResource Tests + + /// + /// Tests that loader correctly loads files specified by absolute name, regardless + /// of the fact whether protocol name is specified or not. + /// + [Test] + public void GetAbsoluteFileSystemResource() + { + string fileName = Path.GetTempFileName(); + try + { + IResource withoutProtocol = loader.GetResource(fileName); + Assert.IsNotNull(withoutProtocol, "Resource should not be null"); + Assert.IsTrue(withoutProtocol is FileSystemResource, "Expected FileSystemResource"); + Assert.IsTrue(withoutProtocol.Exists, "Resource should exist but it does not"); + + IResource withProtocol = loader.GetResource("file:///" + fileName); + Assert.IsNotNull(withProtocol, "Resource should not be null"); + Assert.IsTrue(withProtocol is FileSystemResource, "Expected FileSystemResource"); + Assert.IsTrue(withProtocol.Exists, "Resource should exist but it does not"); + } + finally + { + new FileInfo(fileName).Delete(); + } + } + + [Test] + public void GetResourceThatSupportsTheSpecialHomeCharacter() + { + string filename = "foo.txt"; + FileInfo expectedFile = + new FileInfo(Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filename))); + StreamWriter writer = expectedFile.CreateText(); + FileSystemResource res = (FileSystemResource) loader.GetResource("~/" + filename); + Assert.AreEqual(expectedFile.FullName, res.File.FullName); + try + { + writer.Close(); + } + catch (IOException) + { + } + try + { + expectedFile.Delete(); + } + catch (IOException) + { + } + } + + [Test] + public void GetResourceThatSupportsTheSpecialHomeCharacter_WithLeadingWhitespace() + { + string filename = "foo.txt"; + FileInfo expectedFile = + new FileInfo(Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filename))); + StreamWriter writer = expectedFile.CreateText(); + FileSystemResource res = (FileSystemResource) loader.GetResource(" ~/" + filename); + Assert.AreEqual(expectedFile.FullName, res.File.FullName); + try + { + writer.Close(); + } + catch (IOException) + { + } + try + { + expectedFile.Delete(); + } + catch (IOException) + { + } + } + + /// + /// Tests that loader correctly loads files specified by relative name, regardless + /// of the fact whether protocol name is specified or not. + /// + [Test] + public void GetRelativeFileSystemResource() + { + string fileName = "test.tmp"; + FileInfo fi = new FileInfo(fileName); + FileStream fs = fi.Create(); + fs.Close(); + + try + { + IResource withoutProtocol = loader.GetResource(fileName); + Assert.IsNotNull(withoutProtocol, "Resource should not be null"); + Assert.IsTrue(withoutProtocol is FileSystemResource, "Expected FileSystemResource"); + Assert.IsTrue(withoutProtocol.Exists, "Resource should exist but it does not"); + + IResource withProtocol = loader.GetResource("file://" + fileName); + Assert.IsNotNull(withProtocol, "Resource should not be null"); + Assert.IsTrue(withProtocol is FileSystemResource, "Expected FileSystemResource"); + Assert.IsTrue(withProtocol.Exists, "Resource should exist but it does not"); + } + finally + { + fi.Delete(); + } + } + + /// + /// Tests that loader can load UrlResource over HTTP protocol + /// + [Test] + [Explicit] + public void GetHttpUrlResource() + { + IResource res = loader.GetResource("http://www.springframework.net/license.html"); + Assert.IsNotNull(res, "Resource should not be null"); + Assert.AreEqual(typeof(UrlResource), res.GetType()); + } + + /// + /// Tests that loader can load UrlResource over assembly pseudo protocol + /// + [Test] + public void GetAssemblyResource() + { + IResource res = loader.GetResource("assembly://Spring.Core.Tests/Spring/TestResource.txt"); + Assert.IsNotNull(res, "Resource should not be null"); + Assert.AreEqual(typeof(AssemblyResource), res.GetType()); + } + + #endregion + + [Test] + [ExpectedException(typeof(UriFormatException))] + public void GetResourceForNonMappedProtocol() + { + new ConfigurableResourceLoader().GetResource("beep://foo.xml"); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/IO/FileSystemResourceCommonTests.cs b/test/Spring/Spring.Core.Tests/Core/IO/FileSystemResourceCommonTests.cs new file mode 100644 index 00000000..39015805 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/IO/FileSystemResourceCommonTests.cs @@ -0,0 +1,325 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Reflection; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Common Unit tests for all FileSystemResource derived classes. + /// + /// Erich Eichinger + /// $Id: FileSystemResourceCommonTests.cs,v 1.4 2007/08/08 17:49:59 bbaia Exp $ + public abstract class FileSystemResourceCommonTests + { + protected const string TemporaryFileName = "temp.file"; + + protected abstract FileSystemResource CreateResourceInstance(string resourceName); + + /// + /// Creates a FileInfo instance representing the original location of the given assembly + /// + /// + /// Use this instead of the "Assembly.Location" property to get the original location before shadow copying! + /// + protected static FileInfo GetAssemblyLocation(Assembly assembly) + { + return new FileInfo(new Uri(assembly.CodeBase).LocalPath); + } + + protected static FileInfo CreateFileForTheCurrentDirectory() + { + return new FileInfo(Path.GetFullPath( + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, TemporaryFileName))); + } + + [Test] + public void CreateFileSystemResourceWithPathName() + { + FileSystemResource fileSystemResource = CreateResourceInstance(TemporaryFileName); + Assert.AreEqual(TemporaryFileName, fileSystemResource.File.Name); + } + + [Test] + public void FileSystemResourceExists() + { + FileInfo file = GetAssemblyLocation(Assembly.GetExecutingAssembly()); + FileSystemResource fileSystemResource = CreateResourceInstance("~/" + file.Name); + Assert.IsTrue(fileSystemResource.Exists); + } + + [Test] + public void FileSystemResourceNotExists() + { + Assert.IsFalse(CreateResourceInstance("asdfasfadf").Exists); + } + + [Test] + [ExpectedException(typeof(FileNotFoundException))] + public void FileSystemResourceOpenNonExistanceFile() + { + FileSystemResource fileSystemResource = CreateResourceInstance(TemporaryFileName); + Stream inputStream = fileSystemResource.InputStream; + inputStream.Close(); + } + + [Test] + public void FileSystemResourceValidInputStream() + { + FileInfo file = GetAssemblyLocation(Assembly.GetExecutingAssembly()); + FileSystemResource fileSystemResource = CreateResourceInstance("~/" + file.Name); + using(Stream inputStream = fileSystemResource.InputStream) + { + Assert.IsNotNull(inputStream); + } + } + + [Test] + public void FileSystemResourceGivesOpenedInputStream() + { + FileInfo file = GetAssemblyLocation(Assembly.GetExecutingAssembly()); + FileSystemResource fileSystemResource = CreateResourceInstance("~/" + file.Name); + using(Stream inputStream = fileSystemResource.InputStream) + { + Assert.IsTrue(inputStream.CanRead); + } + } + + [Test] + public void GetDescription() + { + FileSystemResource fileSystemResource = CreateResourceInstance(TemporaryFileName); + string expectedDescription = "file [" + fileSystemResource.File.FullName + "]"; + Assert.AreEqual(expectedDescription, fileSystemResource.Description); + } + + [Test] + public void GetURL() + { + FileSystemResource fileSystemResource = CreateResourceInstance(TemporaryFileName); + Assert.IsNotNull(fileSystemResource.Uri); + } + + /// + /// Even though the 'root' resource points to a nonexistent subdirectory, surfing 'up' to the parent + /// via a relative path should still work... + /// + [Test] + public void CreateRelativeFromNonExistentOriginalResource() + { + // a suitable subdirectory of total pish + IResource resource = CreateResourceInstance("dork/muller.venken"); + Assert.IsFalse(resource.Exists, + "This test needs to feed off of a base resource that explicitly *doesn't* exist; but the resource seems to exist anyway."); + IResource relative = + resource.CreateRelative("../" + GetAssemblyLocation(Assembly.GetExecutingAssembly()).Name); + Assert.IsTrue(relative.Exists); + } + + [Test] + public void CreateRelativeResourceIsEqualToOriginalAfterBouncingUpAndDownTheDirectoryTree() + { + IResource resource = new FileSystemResource(GetAssemblyLocation(Assembly.GetExecutingAssembly()).FullName); + IResource relative = + resource.CreateRelative("foo/bar/../../" + GetAssemblyLocation(Assembly.GetExecutingAssembly()).Name); + Assert.IsTrue(relative.Exists); + Assert.IsTrue(resource.Equals(relative)); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void CreateRelativeWithNullRelativePath() + { + IResource resource = CreateResourceInstance("."); + resource.CreateRelative(null); + } + + [Test] + public void CreateRelativeWithEmptyRelativePath() + { + IResource resource = CreateResourceInstance("boba.licious"); + IResource relative = resource.CreateRelative(string.Empty); + Assert.IsFalse(relative.Exists); + } + + [Test] + public void RelativeResourceWhenNotRelative() + { + IResource res = CreateResourceInstance("dummy.txt"); + IResource res2 = CreateResourceInstance("/index.html"); + + IResource rel0 = res.CreateRelative("/index.html"); + Assert.IsTrue(rel0 is FileSystemResource); + Assert.AreEqual(res2.File.FullName, rel0.File.FullName); + } + + [Test] + public void RelativeResourceFromRoot() + { + FileSystemResource res = CreateResourceInstance(@"/dummy.txt"); + FileSystemResource res2; + + IResource rel0 = res.CreateRelative("/index.html"); + Assert.IsTrue(rel0 is FileSystemResource); + res2 = CreateResourceInstance("/index.html"); + Assert.AreEqual(res2.File.FullName, rel0.File.FullName); + + IResource rel1 = res.CreateRelative(@"index.html"); + Assert.IsTrue(rel1 is FileSystemResource); + res2 = CreateResourceInstance("/index.html"); + Assert.AreEqual(res2.File.FullName, rel1.File.FullName); + + IResource rel2 = res.CreateRelative(@"samples/artfair/index.html"); + Assert.IsTrue(rel2 is FileSystemResource); + res2 = CreateResourceInstance("/samples/artfair/index.html"); + Assert.AreEqual(res2.File.FullName, rel2.File.FullName); + + IResource rel3 = res.CreateRelative(@"./samples/artfair/index.html"); + Assert.IsTrue(rel3 is FileSystemResource); + res2 = CreateResourceInstance("/samples/artfair/index.html"); + Assert.AreEqual(res2.File.FullName, rel3.File.FullName); + } + + [Test] + public void RelativeResourceFromSubfolder() + { + FileSystemResource res = CreateResourceInstance(@"/samples/artfair/dummy.txt"); + FileSystemResource resExpected; + + IResource rel0 = res.CreateRelative(@"/index.html"); + Assert.IsTrue(rel0 is FileSystemResource); + resExpected = CreateResourceInstance("/index.html"); + Assert.AreEqual(resExpected.File.FullName, rel0.File.FullName); + + IResource rel1 = res.CreateRelative(@"index.html"); + Assert.IsTrue(rel1 is FileSystemResource); + resExpected = CreateResourceInstance("/samples/artfair/index.html"); + Assert.AreEqual(resExpected.File.FullName, rel1.File.FullName); + + IResource rel2 = res.CreateRelative(@"demo\index.html"); + Assert.IsTrue(rel2 is FileSystemResource); + resExpected = CreateResourceInstance("/samples/artfair/demo/index.html"); + Assert.AreEqual(resExpected.File.FullName, rel2.File.FullName); + + IResource rel3 = res.CreateRelative(@"./demo/index.html"); + Assert.IsTrue(rel3 is FileSystemResource); + resExpected = CreateResourceInstance("/samples/artfair/demo/index.html"); + Assert.AreEqual(resExpected.File.FullName, rel3.File.FullName); + + IResource rel4 = res.CreateRelative(@"../calculator/index.html"); + Assert.IsTrue(rel4 is FileSystemResource); + resExpected = CreateResourceInstance("/samples/calculator/index.html"); + Assert.AreEqual(resExpected.File.FullName, rel4.File.FullName); + + IResource rel5 = res.CreateRelative(@"..\..\index.html"); + resExpected = CreateResourceInstance("/index.html"); + Assert.AreEqual(resExpected.File.FullName, rel5.File.FullName); + } + + [Test] + [ExpectedException(typeof(UriFormatException))] + public void RelativeResourceTooManyBackLevels() + { + FileSystemResource res = CreateResourceInstance("/samples/artfair/dummy.txt"); + res.CreateRelative("../../../index.html"); + } + + [Test] + public void SupportsAndResolvesTheSpecialHomeCharacter_SunnyDay() + { + FileInfo file = + new FileInfo(Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, TemporaryFileName))); + StreamWriter writer = file.CreateText(); + FileSystemResource res = CreateResourceInstance("~/" + TemporaryFileName); + Assert.IsTrue(res.File.Exists); + Assert.AreEqual(file.FullName.ToLower(), res.File.FullName.ToLower()); + try + { + writer.Close(); + } + catch(IOException) + {} + try + { + file.Delete(); + } + catch(IOException) + {} + } + + [Test] + public void SupportsAndResolvesTheSpecialHomeCharacter_SunnyDayEvenWithLeadingWhitespace() + { + FileInfo file = + new FileInfo(Path.GetFullPath( + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, TemporaryFileName))); + StreamWriter writer = file.CreateText(); + FileSystemResource res = CreateResourceInstance(" ~/" + TemporaryFileName); + Assert.AreEqual(file.FullName.ToLower(), res.File.FullName.ToLower()); + try + { + writer.Close(); + } + catch(IOException) + {} + try + { + file.Delete(); + } + catch(IOException) + {} + } + + [Test] + public void SupportsAndResolvesTheSpecialHomeCharacter_OnlyIfSpecialHomeCharacterIsFirstCharacter() + { + FileSystemResource res = CreateResourceInstance("foo~.txt"); + // must not have replaced ~; its only valid at the start of a resource name... + Assert.AreEqual("foo~.txt", res.File.Name); + } + + [Test] + public void Resolution_PlainVanilla() + { + FileInfo file = CreateFileForTheCurrentDirectory(); + IResource res = CreateResourceInstance(TemporaryFileName); + Assert.AreEqual(file.FullName.ToLower(), res.File.FullName.ToLower(), + "The bare file name all by itself must have resolved to a file in the current " + + "directory of the currently executing domain."); + } + + [Test] + public void Resolution_WithSpecialHomeCharacter() + { + FileInfo file = CreateFileForTheCurrentDirectory(); + IResource res = CreateResourceInstance("~/" + TemporaryFileName); + Assert.AreEqual(file.FullName.ToLower(), res.File.FullName.ToLower(), + "The file name with ~/ must have resolved to a file " + + "in the current directory of the currently executing domain."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/IO/FileSystemResourceTests.cs b/test/Spring/Spring.Core.Tests/Core/IO/FileSystemResourceTests.cs new file mode 100644 index 00000000..3f4e4642 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/IO/FileSystemResourceTests.cs @@ -0,0 +1,320 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Unit tests for the FileSystemResourceTest class. + /// + /// Rick Evans + /// $Id: FileSystemResourceTests.cs,v 1.9 2007/08/08 17:49:59 bbaia Exp $ + [TestFixture] + public class FileSystemResourceTests : FileSystemResourceCommonTests + { + protected override FileSystemResource CreateResourceInstance( string resourceName ) + { + return new FileSystemResource(resourceName); + } + + [Test] + [ExpectedException(typeof (UriFormatException))] + public void RelativeUncResourceTooManyBackLevels() + { + FileSystemResource res = new FileSystemResource(@"\\server\share\samples\artfair\dummy.txt"); + res.CreateRelative(@"..\..\..\index.html"); + } + + [Test(Description="SPRNET-89")] + public void SPRNET_89_SupportUrlEncodedLocationsWithSpaces() + { + string path = Path.GetFullPath("spaced dir"); + Directory.CreateDirectory(path); + string filePath = Path.Combine(path, "spaced file.txt"); + using (StreamWriter text = File.CreateText(filePath)) + { + text.WriteLine("hello world"); + } + FileSystemResource res = new FileSystemResource(new Uri(filePath).AbsoluteUri); + using (Stream s = res.InputStream) + { + using (TextReader reader = new StreamReader(s)) + { + Assert.AreEqual("hello world", reader.ReadLine()); + } + } + } + + [Test(Description = "http://opensource.atlassian.com/projects/spring/browse/SPRNET-320")] + public void SupportsSpecialUriCharacter() + { + string path = Path.GetFullPath("dir#"); + Directory.CreateDirectory(path); + string filePath = Path.Combine(path, "file.txt"); + using (StreamWriter text = File.CreateText(filePath)) + { + text.WriteLine("hello world"); + } + FileSystemResource res = new FileSystemResource(new Uri(filePath).AbsoluteUri); + using (Stream s = res.InputStream) + { + using (TextReader reader = new StreamReader(s)) + { + Assert.AreEqual("hello world", reader.ReadLine()); + } + } + } + + [Test] + public void Resolution_WithProtocolAndSpecialHomeCharacter() + { + FileInfo file = CreateFileForTheCurrentDirectory(); + IResource res = new FileSystemResource("file://~/" + TemporaryFileName); + Assert.AreEqual(file.FullName, res.File.FullName, + "The file name with file://~ must have resolved to a file " + + "in the current directory of the currently executing domain."); + } + + [Test] + public void Resolution_WithProtocolAndSpecialHomeCharacterParentDirectory() + { + FileInfo file = new FileInfo(Path.GetFullPath( + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, TemporaryFileName))); + IResource res = new FileSystemResource("file://~/../" + TemporaryFileName); + string fileNameOneDirectoryUp = file.Directory.Parent.FullName + "\\" + TemporaryFileName; + Assert.AreEqual(fileNameOneDirectoryUp, res.File.FullName, + "The file name with file://~/.. must have resolved to a file " + + "in the parent directory of the currently executing domain."); + } + + [Test] + public void CreateRelativeWithParent() + { + // use the filename of the declaring assembly as the root... + string rootpath = GetType().Assembly.Location; + DirectoryInfo rootdir = new DirectoryInfo(Path.GetDirectoryName(rootpath)); + _testCreateRelative(rootpath, rootdir.Parent.FullName, @"../"); + } + + [Test] + public void CreateRelativeWithSuperParent() + { + // use the filename of the declaring assembly as the root... + string rootpath = GetType().Assembly.Location; + DirectoryInfo rootdir = new DirectoryInfo(Path.GetDirectoryName(rootpath)); + _testCreateRelative(rootpath, rootdir.Parent.Parent.FullName, @"..\..\"); + } + + [Test] + public void CreateRelativeInSameDirectory() + { + // use the filename of the declaring assembly as the root... + string rootpath = GetType().Assembly.Location; + DirectoryInfo rootdir = new DirectoryInfo(Path.GetDirectoryName(rootpath)); + _testCreateRelative(rootpath, rootdir.FullName, @".\"); + } + + [Test] + public void CreateRelativeInSubdirectoryDirectory() + { + string subdirname = "Stuff"; + // use the filename of the declaring assembly as the root... + string rootpath = GetType().Assembly.Location; + DirectoryInfo rootdir = new DirectoryInfo(Path.GetDirectoryName(rootpath)); + DirectoryInfo subdir = rootdir.CreateSubdirectory(subdirname); + try + { + _testCreateRelative(rootpath, subdir.FullName, subdirname + "/"); + // let's get obtuse... specify the parent directory of the subdirectory (i.e. the root directory again) + _testCreateRelative(rootpath, rootdir.FullName, subdirname + "/../"); + } + finally + { + if (subdir.Exists) + { + try + { + subdir.Delete(); + } + catch (IOException) + { + } + } + } + } + + /// + /// Helper method... + /// + private void _testCreateRelative(string rootPath, string targetPath, string relativePath) + { + string filename = "stuff.txt"; + FileInfo file = new FileInfo(Path.GetFullPath(Path.Combine(targetPath, filename))); + // create a temporary file in whatever 'targetpath' dir we've been passed... + StreamWriter writer = file.CreateText(); + // test that the CreateRelative () method works with the supplied 'relativePath' + IResource resource = new FileSystemResource(rootPath); + IResource relative = resource.CreateRelative(relativePath + filename); + Assert.IsTrue(relative.Exists); + if (file.Exists) + { + try + { + writer.Close(); + file.Delete(); + } + catch (IOException) + { + } + } + } + + [Test] + public void RelativeLocalFileSystemResourceWhenNotRelative() + { + FileSystemResource res = new FileSystemResource(@"C:\dummy.txt"); + + IResource rel0 = res.CreateRelative(@"c:\index.html"); + Assert.IsTrue(rel0 is FileSystemResource); + Assert.AreEqual(@"file [c:\index.html]", rel0.Description); + } + + [Test] + public void RelativeLocalFileSystemResourceFromRoot() + { + FileSystemResource res = new FileSystemResource(@"c:\dummy.txt"); + + IResource rel0 = res.CreateRelative(@"\index.html"); + Assert.IsTrue(rel0 is FileSystemResource); + Assert.AreEqual(@"file [c:\index.html]", rel0.Description); + + IResource rel1 = res.CreateRelative(@"index.html"); + Assert.IsTrue(rel1 is FileSystemResource); + Assert.AreEqual(@"file [c:\index.html]", rel1.Description); + + IResource rel2 = res.CreateRelative(@"samples/artfair/index.html"); + Assert.IsTrue(rel2 is FileSystemResource); + Assert.AreEqual(@"file [c:\samples\artfair\index.html]", rel2.Description); + + IResource rel3 = res.CreateRelative(@".\samples\artfair\index.html"); + Assert.IsTrue(rel3 is FileSystemResource); + Assert.AreEqual(@"file [c:\samples\artfair\index.html]", rel3.Description); + } + + [Test] + public void RelativeLocalFileSystemResourceFromSubfolder() + { + FileSystemResource res = new FileSystemResource(@"c:\samples\artfair\dummy.txt"); + + IResource rel0 = res.CreateRelative(@"/index.html"); + Assert.IsTrue(rel0 is FileSystemResource); + Assert.AreEqual(@"file [c:\index.html]", rel0.Description); + + IResource rel1 = res.CreateRelative(@"index.html"); + Assert.IsTrue(rel1 is FileSystemResource); + Assert.AreEqual(@"file [c:\samples\artfair\index.html]", rel1.Description); + + IResource rel2 = res.CreateRelative(@"demo\index.html"); + Assert.IsTrue(rel2 is FileSystemResource); + Assert.AreEqual(@"file [c:\samples\artfair\demo\index.html]", rel2.Description); + + IResource rel3 = res.CreateRelative(@"./demo/index.html"); + Assert.IsTrue(rel3 is FileSystemResource); + Assert.AreEqual(@"file [c:\samples\artfair\demo\index.html]", rel3.Description); + + IResource rel4 = res.CreateRelative(@"../calculator/index.html"); + Assert.IsTrue(rel4 is FileSystemResource); + Assert.AreEqual(@"file [c:\samples\calculator\index.html]", rel4.Description); + + IResource rel5 = res.CreateRelative(@"..\..\index.html"); + Assert.IsTrue(rel5 is FileSystemResource); + Assert.AreEqual(@"file [c:\index.html]", rel5.Description); + } + + [Test] + public void RelativeUncResourceWhenNotRelative() + { + FileSystemResource res = new FileSystemResource(@"\\server\share\dummy.txt"); + + IResource rel0 = res.CreateRelative(@"\\server\share\index.html"); + Assert.IsTrue(rel0 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\index.html]", rel0.Description); + } + + [Test] + public void RelativeUncResourceFromRoot() + { + FileSystemResource res = new FileSystemResource(@"\\server\share\dummy.txt"); + + IResource rel0 = res.CreateRelative(@"\index.html"); + Assert.IsTrue(rel0 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\index.html]", rel0.Description); + + IResource rel1 = res.CreateRelative(@"index.html"); + Assert.IsTrue(rel1 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\index.html]", rel1.Description); + + IResource rel2 = res.CreateRelative(@"samples/artfair/index.html"); + Assert.IsTrue(rel2 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\samples\artfair\index.html]", rel2.Description); + + IResource rel3 = res.CreateRelative(@".\samples\artfair\index.html"); + Assert.IsTrue(rel3 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\samples\artfair\index.html]", rel3.Description); + } + + [Test] + public void RelativeUncResourceFromSubfolder() + { + FileSystemResource res = new FileSystemResource(@"\\server\share\samples\artfair\dummy.txt"); + + IResource rel0 = res.CreateRelative(@"/index.html"); + Assert.IsTrue(rel0 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\index.html]", rel0.Description); + + IResource rel1 = res.CreateRelative(@"index.html"); + Assert.IsTrue(rel1 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\samples\artfair\index.html]", rel1.Description); + + IResource rel2 = res.CreateRelative(@"demo\index.html"); + Assert.IsTrue(rel2 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\samples\artfair\demo\index.html]", rel2.Description); + + IResource rel3 = res.CreateRelative(@"./demo/index.html"); + Assert.IsTrue(rel3 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\samples\artfair\demo\index.html]", rel3.Description); + + IResource rel4 = res.CreateRelative(@"../calculator/index.html"); + Assert.IsTrue(rel4 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\samples\calculator\index.html]", rel4.Description); + + IResource rel5 = res.CreateRelative(@"..\..\index.html"); + Assert.IsTrue(rel5 is FileSystemResource); + Assert.AreEqual(@"file [\\server\share\index.html]", rel5.Description); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/IO/InputStreamResourceTests.cs b/test/Spring/Spring.Core.Tests/Core/IO/InputStreamResourceTests.cs new file mode 100644 index 00000000..951e1d6b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/IO/InputStreamResourceTests.cs @@ -0,0 +1,111 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Unit tests for the InputStreamResource class. + /// + /// Rick Evans + /// $Id: InputStreamResourceTests.cs,v 1.5 2007/08/08 17:49:59 bbaia Exp $ + [TestFixture] + public sealed class InputStreamResourceTests + { + [Test] + public void Instantiation () + { + FileInfo file = null; + Stream stream = null; + try + { + file = new FileInfo ("Instantiation"); + stream = file.Create (); + InputStreamResource res = new InputStreamResource (stream, "A temporary resource."); + Assert.IsTrue (res.IsOpen); + Assert.IsTrue (res.Exists); + Assert.IsNotNull (res.InputStream); + } + finally + { + try + { + if (stream != null) + { + stream.Close (); + } + if (file != null + && file.Exists) + { + file.Delete (); + } + } + catch {} + } + } + + [Test] + [ExpectedException (typeof (ArgumentNullException))] + public void InstantiationWithNull () { + new InputStreamResource (null, "A null resource."); + } + + [Test] + [ExpectedException (typeof (InvalidOperationException))] + public void ReadStreamMultipleTimes () + { + FileInfo file = null; + Stream stream = null; + try + { + file = new FileInfo ("ReadStreamMultipleTimes"); + stream = file.Create (); + // attempting to read this stream twice is an error... + InputStreamResource res = new InputStreamResource (stream, "A temporary resource."); + Stream streamOne = res.InputStream; + Stream streamTwo = res.InputStream; // should bail here + } + finally + { + try + { + if (stream != null) + { + stream.Close (); + } + if (file != null + && file.Exists) + { + file.Delete (); + } + } + catch {} + } + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/IO/ResourceConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/IO/ResourceConverterTests.cs new file mode 100644 index 00000000..e10d7009 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/IO/ResourceConverterTests.cs @@ -0,0 +1,116 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using Common.Logging.Simple; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Unit tests for the ResourceConverter class. + /// + /// Rick Evans + /// $Id: ResourceConverterTests.cs,v 1.7 2007/08/08 17:49:59 bbaia Exp $ + [TestFixture] + public sealed class ResourceConverterTests + { + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp() + { + // enable (null appender) logging, just to ensure that the logging code is correct + LogManager.Adapter = new NoOpLoggerFactoryAdapter(); + } + + [Test] + public void CanConvertFrom() + { + ResourceConverter vrt = new ResourceConverter(); + Assert.IsTrue(vrt.CanConvertFrom(typeof (string)), "Conversion from a string instance must be supported."); + Assert.IsFalse(vrt.CanConvertFrom(typeof (int))); + } + + [Test] + public void ConvertFrom() + { + ResourceConverter vrt = new ResourceConverter(); + object actual = vrt.ConvertFrom("file://localhost/"); + Assert.IsNotNull(actual); + IResource res = actual as IResource; + Assert.IsNotNull(res); + Assert.IsFalse(res.Exists); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromNullReference() + { + ResourceConverter vrt = new ResourceConverter(); + vrt.ConvertFrom(null); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromNonSupportedOptionBails() + { + ResourceConverter vrt = new ResourceConverter(); + vrt.ConvertFrom(new TestFixtureAttribute()); + } + + [Test] + public void ConvertFromWithEnvironmentVariableExpansion() + { + // TODO : won't pass on anything other than Win9x boxes... + ResourceConverter vrt = new ResourceConverter(); + string path = @"${userprofile}\foo.txt"; + IResource resource = (IResource) vrt.ConvertFrom(path); + + string userprofile = Environment.GetEnvironmentVariable("userprofile"); + + Assert.IsFalse(resource.Exists, + "Darn. Should be supplying a rubbish non-existant resource. " + + "You don't actually have a file called 'foo.txt' in your user directory do you?"); + Assert.IsTrue(resource.Description.IndexOf(userprofile) > -1, + "Environment variable not expanded."); + } + + [Test] + public void DoesNotChokeOnUnresolvableEnvironmentVariableExpansion() + { + ResourceConverter vrt = new ResourceConverter(); + string path = @"${_go_ahead_I_wish_you_would_}\foo.txt"; + IResource resource = (IResource) vrt.ConvertFrom(path); + + Assert.IsFalse(resource.Exists, + "Darn. Should be supplying a rubbish non-existant resource. " + + "You don't actually have a file called 'foo.txt' in your user directory do you?"); + Assert.IsTrue(resource.Description.IndexOf("_go_ahead_I_wish_you_would_") > -1, + "Rubbish environment variable not preserved as-is."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/IO/ResourceHandlerRegistryTests.cs b/test/Spring/Spring.Core.Tests/Core/IO/ResourceHandlerRegistryTests.cs new file mode 100644 index 00000000..079ebacb --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/IO/ResourceHandlerRegistryTests.cs @@ -0,0 +1,129 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Unit tests for the ConfigurableResourceLoader class. + /// + /// Aleksandar Seovic + /// $Id: ResourceHandlerRegistryTests.cs,v 1.4 2007/08/08 17:49:59 bbaia Exp $ + [TestFixture] + public sealed class ResourceHandlerRegistryTests + { + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void WithNullProtocolName() + { + ResourceHandlerRegistry.RegisterResourceHandler(null, GetType()); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void WithNullIResourceHandlerType() + { + ResourceHandlerRegistry.RegisterResourceHandler("beep", (Type) null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void WithWhitespacedProtocolName() + { + ResourceHandlerRegistry.RegisterResourceHandler("\t ", GetType()); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void WithNonIResourceHandlerType() + { + ResourceHandlerRegistry.RegisterResourceHandler("beep", GetType()); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void WithIResourceHandlerTypeWithNoValidCtor() + { + ResourceHandlerRegistry.RegisterResourceHandler("beep", typeof(IncompatibleResource)); + } + + [Test] + public void AddProtocolMappingSilentlyOverwritesExistingProtocol() + { + ResourceHandlerRegistry.RegisterResourceHandler("beep", typeof(FileSystemResource)); + // overwrite, must not complain... + ResourceHandlerRegistry.RegisterResourceHandler("beep", typeof(AssemblyResource)); + IResource res = new ConfigurableResourceLoader().GetResource("beep://Spring.Core.Tests/Spring/TestResource.txt"); + Assert.IsNotNull(res, "Resource must not be null"); + Assert.AreEqual(typeof(AssemblyResource), res.GetType(), + "The original IResource Type associated with the 'beep' protocol " + + "must have been overwritten; expecting an AssemblyResource 'cos " + + "we registered it last under the 'beep' protocol."); + } + + /// + /// Deso not expose a constructor that takes a single string argument. + /// + private sealed class IncompatibleResource : IResource + { + public bool IsOpen + { + get { throw new NotImplementedException(); } + } + + public Uri Uri + { + get { throw new NotImplementedException(); } + } + + public FileInfo File + { + get { throw new NotImplementedException(); } + } + + public string Description + { + get { throw new NotImplementedException(); } + } + + public bool Exists + { + get { throw new NotImplementedException(); } + } + + public IResource CreateRelative(string relativePath) + { + throw new NotImplementedException(); + } + + public Stream InputStream + { + get { throw new NotImplementedException(); } + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/IO/TestResource.txt b/test/Spring/Spring.Core.Tests/Core/IO/TestResource.txt new file mode 100644 index 00000000..eb53c8a8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/IO/TestResource.txt @@ -0,0 +1 @@ +Spring.Core.IO.TestResource.txt diff --git a/test/Spring/Spring.Core.Tests/Core/IO/UrlResourceTest.cs b/test/Spring/Spring.Core.Tests/Core/IO/UrlResourceTest.cs new file mode 100644 index 00000000..e986f107 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/IO/UrlResourceTest.cs @@ -0,0 +1,166 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Unit tests for the UrlResource class. + /// + [TestFixture] + public sealed class UrlResourceTests + { + private const string FILE_PROTOCOL_PREFIX = "file:///"; + + [Test] + public void CreateUrlResourceWithGivenPath() + { + UrlResource urlResource = new UrlResource(FILE_PROTOCOL_PREFIX + "C:/temp"); + Assert.AreEqual("C:/temp", urlResource.Uri.AbsolutePath); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void CreateInvalidUrlResource() + { + string uri = null; + new UrlResource(uri); + } + + [Test] + public void GetValidFileInfo() + { + UrlResource urlResource = new UrlResource(FILE_PROTOCOL_PREFIX + "C:/temp"); + Assert.AreEqual("C:\\temp", urlResource.File.FullName); + } + + [Test] + public void ExistsValidHttp() + { + UrlResource urlResource = new UrlResource("http://www.springframework.net/"); + Assert.IsTrue(urlResource.Exists); + } + + [Test] + [ExpectedException(typeof(FileNotFoundException))] + public void GetInvalidFileInfo() + { + UrlResource urlResource = new UrlResource("http://www.springframework.net/"); + FileInfo file = urlResource.File; + file.GetType(); + } + + [Test] + [ExpectedException(typeof(FileNotFoundException))] + public void GetInvalidFileInfoWithOddPort() + { + UrlResource urlResource = new UrlResource("http://www.springframework.net:76/"); + FileInfo file = urlResource.File; + file.GetType(); + } + + [Test] + public void GetDescription() + { + UrlResource urlResource = new UrlResource(FILE_PROTOCOL_PREFIX + "C:/temp"); + Assert.AreEqual("URL [file:///C:/temp]", urlResource.Description); + } + + [Test] + public void GetValidInputStreamForFileProtocol() + { + string fileName = Path.GetTempFileName(); + FileStream fs = File.Create(fileName); + fs.Close(); + using (Stream inputStream = new UrlResource(FILE_PROTOCOL_PREFIX + fileName).InputStream) + { + Assert.IsTrue(inputStream.CanRead); + } + } + + [Test] + public void RelativeResourceFromRoot() + { + UrlResource res = new UrlResource("http://www.springframework.net/documentation.html"); + + IResource rel0 = res.CreateRelative("/index.html"); + Assert.IsTrue(rel0 is UrlResource); + Assert.AreEqual("URL [http://www.springframework.net/index.html]", rel0.Description); + + IResource rel1 = res.CreateRelative("index.html"); + Assert.IsTrue(rel1 is UrlResource); + Assert.AreEqual("URL [http://www.springframework.net/index.html]", rel1.Description); + + IResource rel2 = res.CreateRelative("samples/artfair/index.html"); + Assert.IsTrue(rel2 is UrlResource); + Assert.AreEqual("URL [http://www.springframework.net/samples/artfair/index.html]", rel2.Description); + + IResource rel3 = res.CreateRelative("./samples/artfair/index.html"); + Assert.IsTrue(rel3 is UrlResource); + Assert.AreEqual("URL [http://www.springframework.net/samples/artfair/index.html]", rel3.Description); + } + + [Test] + public void RelativeResourceFromSubfolder() + { + UrlResource res = new UrlResource("http://www.springframework.net/samples/artfair/download.html"); + + IResource rel0 = res.CreateRelative("/index.html"); + Assert.IsTrue(rel0 is UrlResource); + Assert.AreEqual("URL [http://www.springframework.net/index.html]", rel0.Description); + + IResource rel1 = res.CreateRelative("index.html"); + Assert.IsTrue(rel1 is UrlResource); + Assert.AreEqual("URL [http://www.springframework.net/samples/artfair/index.html]", rel1.Description); + + IResource rel2 = res.CreateRelative("demo/index.html"); + Assert.IsTrue(rel2 is UrlResource); + Assert.AreEqual("URL [http://www.springframework.net/samples/artfair/demo/index.html]", rel2.Description); + + IResource rel3 = res.CreateRelative("./demo/index.html"); + Assert.IsTrue(rel3 is UrlResource); + Assert.AreEqual("URL [http://www.springframework.net/samples/artfair/demo/index.html]", rel3.Description); + + IResource rel4 = res.CreateRelative("../calculator/index.html"); + Assert.IsTrue(rel4 is UrlResource); + Assert.AreEqual("URL [http://www.springframework.net/samples/calculator/index.html]", rel4.Description); + + IResource rel5 = res.CreateRelative("../../index.html"); + Assert.IsTrue(rel5 is UrlResource); + Assert.AreEqual("URL [http://www.springframework.net/index.html]", rel5.Description); + } + + [Test] + [ExpectedException(typeof(UriFormatException))] + public void RelativeResourceTooManyBackLevels() + { + UrlResource res = new UrlResource("http://www.springframework.net/samples/artfair/download.html"); + res.CreateRelative("../../../index.html"); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/MethodGenericArgumentsCountCriteriaTests.cs b/test/Spring/Spring.Core.Tests/Core/MethodGenericArgumentsCountCriteriaTests.cs new file mode 100644 index 00000000..b551bbbf --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/MethodGenericArgumentsCountCriteriaTests.cs @@ -0,0 +1,104 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Reflection; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core +{ + /// + /// Unit tests for the MethodGenericArgumentsCountCriteria class. + /// + /// Bruno Baia + /// $Id: MethodGenericArgumentsCountCriteriaTests.cs,v 1.1 2007/08/04 01:05:52 bbaia Exp $ + [TestFixture] + public sealed class MethodGenericArgumentsCountCriteriaTests + { + [Test] + public void Instantiation () + { + MethodGenericArgumentsCountCriteria criteria = new MethodGenericArgumentsCountCriteria(); + Assert.AreEqual (0, criteria.ExpectedGenericArgumentCount); + criteria = new MethodGenericArgumentsCountCriteria(10); + Assert.AreEqual(10, criteria.ExpectedGenericArgumentCount); + } + + [Test] + [ExpectedException (typeof (ArgumentOutOfRangeException))] + public void InstantiationBailsWithGenericArgumentCountSetToLessThanZero () + { + MethodGenericArgumentsCountCriteria criteria = new MethodGenericArgumentsCountCriteria(-10); + } + + [Test] + [ExpectedException (typeof (ArgumentOutOfRangeException))] + public void BailsWhenExpectedGenericArgumentCountSetToLessThanZero() + { + MethodGenericArgumentsCountCriteria criteria = new MethodGenericArgumentsCountCriteria(); + criteria.ExpectedGenericArgumentCount = -12; + } + + [Test] + public void IsSatisfied () + { + MethodGenericArgumentsCountCriteria criteria = new MethodGenericArgumentsCountCriteria(); + MethodInfo method = GetType().GetMethod("NoGenericArgument", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue(criteria.IsSatisfied(method)); + + criteria = new MethodGenericArgumentsCountCriteria(1); + method = GetType().GetMethod("OneGenericArgument", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue (criteria.IsSatisfied (method)); + + criteria = new MethodGenericArgumentsCountCriteria(2); + method = GetType().GetMethod("TwoGenericArguments", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue (criteria.IsSatisfied (method)); + } + + [Test] + public void IsNotSatisfiedWithNull () + { + MethodGenericArgumentsCountCriteria criteria = new MethodGenericArgumentsCountCriteria(); + Assert.IsFalse (criteria.IsSatisfied (null)); + } + + // some methods for testing signatures... + public void NoGenericArgument() + { + } + + public void OneGenericArgument() + { + } + + public void TwoGenericArguments() + { + } + } +} + +#endif diff --git a/test/Spring/Spring.Core.Tests/Core/MethodParametersCountCriteriaTests.cs b/test/Spring/Spring.Core.Tests/Core/MethodParametersCountCriteriaTests.cs new file mode 100644 index 00000000..1a65be37 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/MethodParametersCountCriteriaTests.cs @@ -0,0 +1,141 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core +{ + /// + /// Unit tests for the MethodParametersCountCriteria class. + /// + /// Rick Evans + /// $Id: MethodParametersCountCriteriaTests.cs,v 1.3 2007/09/20 14:35:29 bbaia Exp $ + [TestFixture] + public sealed class MethodParametersCountCriteriaTests + { + [Test] + public void Instantiation () + { + MethodParametersCountCriteria criteria = new MethodParametersCountCriteria (); + Assert.AreEqual (0, criteria.ExpectedParameterCount); + criteria = new MethodParametersCountCriteria (10); + Assert.AreEqual (10, criteria.ExpectedParameterCount); + } + + [Test] + [ExpectedException (typeof (ArgumentOutOfRangeException))] + public void InstantiationBailsWithParameterCountSetToLessThanZero () + { + MethodParametersCountCriteria criteria = new MethodParametersCountCriteria (-10); + } + + [Test] + [ExpectedException (typeof (ArgumentOutOfRangeException))] + public void BailsWhenExpectedParameterCountSetToLessThanZero () + { + MethodParametersCountCriteria criteria = new MethodParametersCountCriteria (); + criteria.ExpectedParameterCount = -12; + } + + [Test] + public void IsSatisfiedWithNoParameter () + { + MethodParametersCountCriteria criteria = new MethodParametersCountCriteria (); + MethodInfo method = GetType ().GetMethod ("NoParameter", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue (criteria.IsSatisfied (method)); + + criteria = new MethodParametersCountCriteria(0); + method = GetType().GetMethod("NoParameter", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue(criteria.IsSatisfied(method)); + + criteria = new MethodParametersCountCriteria(); + criteria.ExpectedParameterCount = 0; + method = GetType().GetMethod("NoParameter", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue(criteria.IsSatisfied(method)); + } + + [Test] + public void IsSatisfiedWithOneParameter () + { + MethodParametersCountCriteria criteria = new MethodParametersCountCriteria (1); + MethodInfo method = GetType().GetMethod("OneParameter", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue (criteria.IsSatisfied (method)); + } + + [Test] + public void IsSatisfiedWithTwoParameters () + { + MethodParametersCountCriteria criteria = new MethodParametersCountCriteria(2); + MethodInfo method = GetType().GetMethod("TwoParameters", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue (criteria.IsSatisfied (method)); + } + + [Test] + public void IsSatisfiedWithParamsParameters () + { + MethodParametersCountCriteria criteria = new MethodParametersCountCriteria(1); + MethodInfo method = GetType().GetMethod("ParamsParameters", BindingFlags.Public | BindingFlags.Instance); + Assert.IsFalse(criteria.IsSatisfied(method)); + + criteria = new MethodParametersCountCriteria(2); + method = GetType().GetMethod("ParamsParameters", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue(criteria.IsSatisfied(method)); + + criteria = new MethodParametersCountCriteria (3); + method = GetType().GetMethod("ParamsParameters", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue (criteria.IsSatisfied (method)); + + criteria = new MethodParametersCountCriteria(5); + method = GetType().GetMethod("ParamsParameters", BindingFlags.Public | BindingFlags.Instance); + Assert.IsTrue(criteria.IsSatisfied(method)); + } + + [Test] + public void IsNotSatisfiedWithNull () + { + MethodParametersCountCriteria criteria = new MethodParametersCountCriteria (); + Assert.IsFalse (criteria.IsSatisfied (null)); + } + + // some methods for testing signatures... + public void NoParameter () + { + } + + public void OneParameter (int foo) + { + } + + public void TwoParameters (int foo, int bar) + { + } + + public void ParamsParameters(int foo, int bar, params string[] strs) + { + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/MethodParametersCriteriaTests.cs b/test/Spring/Spring.Core.Tests/Core/MethodParametersCriteriaTests.cs new file mode 100644 index 00000000..69551603 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/MethodParametersCriteriaTests.cs @@ -0,0 +1,124 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Core +{ + /// + /// Unit tests for the MethodParametersCriteria class. + /// + /// Rick Evans + /// $Id: MethodParametersCriteriaTests.cs,v 1.2 2007/09/20 14:20:47 bbaia Exp $ + [TestFixture] + public sealed class MethodParametersCriteriaTests + { + [Test] + public void IsNotSatisfiedWithNull () { + MethodParametersCriteria criteria = new MethodParametersCriteria (); + Assert.IsFalse (criteria.IsSatisfied (null), "Was satisified with null."); + } + + [Test] + public void IsSatisfiedWithNoParametersByDefault () + { + MethodParametersCriteria criteria = new MethodParametersCriteria (); + MethodInfo method = GetType ().GetMethod ("Foo"); + Assert.IsTrue (criteria.IsSatisfied (method), "Was not satisified with a method that takes no parameters by default."); + } + + [Test] + public void IsSatisfied () + { + MethodParametersCriteria criteria = new MethodParametersCriteria ( + new Type [] {typeof (string), typeof (DBNull), typeof (TestObject)}); + MethodInfo method = GetType ().GetMethod ("BoJangles"); + Assert.IsTrue (criteria.IsSatisfied (method), "Was not satisified with a method that takes a whole buncha parameters."); + + method = GetType ().GetMethod ("BadBobbyBoJangles"); + Assert.IsFalse (criteria.IsSatisfied (method), "Was satisified with a (bad) method that takes a whole buncha parameters."); + } + + [Test] + public void IsSatisfiedIsPolymorphic () + { + // i.e. derived types satisfy the criteria if a base type or interface is + // specified as one of the parameter types + MethodParametersCriteria criteria + = new MethodParametersCriteria (new Type [] {typeof (TestObject)}); + MethodInfo method = GetType ().GetMethod ("Diddly"); + Assert.IsTrue (criteria.IsSatisfied (method), "Was not satisified with a method that takes a base class as a parameter."); + } + + [Test] + public void IsSatisfiedWithParamsParameters() + { + MethodParametersCriteria criteria = new MethodParametersCriteria(new Type[] { typeof(int), typeof(string) }); + MethodInfo method = GetType().GetMethod("ParamsParameters"); + Assert.IsTrue(criteria.IsSatisfied(method), "Was not satisified with a method that takes a parameter array ('params') as a parameter."); + + criteria = new MethodParametersCriteria(new Type[] { typeof(int), typeof(string[]) }); + method = GetType().GetMethod("ParamsParameters"); + Assert.IsTrue(criteria.IsSatisfied(method), "Was not satisified with a method that takes a parameter array ('params') as a parameter."); + + criteria = new MethodParametersCriteria(new Type[] { typeof(int) }); + method = GetType().GetMethod("ParamsParameters"); + Assert.IsTrue(criteria.IsSatisfied(method), "Was not satisified with a method that takes a parameter array ('params') as a parameter."); + + criteria = new MethodParametersCriteria(new Type[] { typeof(int), typeof(string), typeof(string), typeof(string) }); + method = GetType().GetMethod("ParamsParameters"); + Assert.IsTrue(criteria.IsSatisfied(method), "Was not satisified with a method that takes a parameter array ('params') as a parameter."); + + criteria = new MethodParametersCriteria(new Type[] { typeof(int), typeof(string[]), typeof(string) }); + method = GetType().GetMethod("ParamsParameters"); + Assert.IsFalse(criteria.IsSatisfied(method), "Was not satisified with a method that takes a parameter array ('params') as a parameter."); + } + + // some methods for testing signatures... + public void Foo () + { + } + + public DateTime BoJangles (string one, DBNull two, TestObject three) + { + return DateTime.Now; + } + + public void BadBobbyBoJangles (string one, DBNull two, string bing) + { + } + + public void Diddly (ITestObject three) + { + } + + public void ParamsParameters(int foo, params string[] strs) + { + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/MethodReturnTypeCriteriaTests.cs b/test/Spring/Spring.Core.Tests/Core/MethodReturnTypeCriteriaTests.cs new file mode 100644 index 00000000..7714385a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/MethodReturnTypeCriteriaTests.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Core +{ + /// + /// Unit tests for the MethodReturnTypeCriteria class. + /// + /// $Id: MethodReturnTypeCriteriaTests.cs,v 1.1 2007/08/04 01:05:52 bbaia Exp $ + [TestFixture] + public sealed class MethodReturnTypeCriteriaTests + { + [Test] + public void IsSatisfied () { + MethodReturnTypeCriteria criteria = new MethodReturnTypeCriteria (typeof (bool)); + MethodInfo method = GetType ().GetMethod ("SomeKindOfWonderful", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsTrue (criteria.IsSatisfied (method)); + } + + private bool SomeKindOfWonderful () + { + return true; + } + + [Test] + public void IsSatisfiedWithVoidByDefault () + { + MethodReturnTypeCriteria criteria = new MethodReturnTypeCriteria (); + MethodInfo method = GetType ().GetMethod ("IsSatisfied"); + Assert.IsTrue (criteria.IsSatisfied (method)); + } + + [Test] + public void IsNotSatisfiedWithNull () { + MethodReturnTypeCriteria criteria = new MethodReturnTypeCriteria (); + Assert.IsFalse (criteria.IsSatisfied (null)); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/OrderComparatorTests.cs b/test/Spring/Spring.Core.Tests/Core/OrderComparatorTests.cs new file mode 100644 index 00000000..e0c691d3 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/OrderComparatorTests.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core +{ + /// + /// Unit tests for the OrderComparator class. + /// + /// Rick Evans + /// $Id: OrderComparatorTests.cs,v 1.2 2006/04/09 07:24:48 markpollack Exp $ + [TestFixture] + public sealed class OrderComparatorTests + { + [Test] + public void OrdersCorrectly () + { + Ordered one = new Ordered (1); + Ordered fifty = new Ordered (50); + string max = "Max"; // should be stuck at the end 'cos it doesnt implement IOrdered + object [] list = new object [] {max, one, fifty}; + Array.Sort (list, new OrderComparator ()); + Assert.AreEqual (one, list [0]); + Assert.AreEqual (fifty, list [1]); + Assert.AreEqual (max, list [2]); + } + + [Test] + public void DoesntBailWhenFedNulls () + { + Ordered one = new Ordered (1); + object [] list = new object [] {null, one, null}; + Array.Sort (list, new OrderComparator ()); + Assert.AreEqual (one, list [0]); + Assert.AreEqual (null, list [1]); + Assert.AreEqual (null, list [2]); + } + + internal sealed class Ordered : IOrdered + { + public Ordered (int order) + { + _order = order; + } + + public int Order + { + get + { + return _order; + } + } + + private int _order; + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/RegularExpressionEventNameCriteriaTests.cs b/test/Spring/Spring.Core.Tests/Core/RegularExpressionEventNameCriteriaTests.cs new file mode 100644 index 00000000..d5a89b60 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/RegularExpressionEventNameCriteriaTests.cs @@ -0,0 +1,71 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text.RegularExpressions; +using System.Reflection; + +using Spring.Objects; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core +{ + /// + /// Unit tests for the RegularExpressionEventNameCriteria class. + /// + [TestFixture] + public class RegularExpressionEventNameCriteriaTests + { + [Test] + public void IsSatisfied () { + RegularExpressionEventNameCriteria criteria = new RegularExpressionEventNameCriteria("Click"); + EventInfo evt = typeof(TestObject).GetEvent("Click"); + Assert.IsTrue (criteria.IsSatisfied (evt)); + } + + [Test] + public void IsNotSatisfiedWithGarbage () + { + RegularExpressionEventNameCriteria criteria = new RegularExpressionEventNameCriteria ("BingoBango"); + EventInfo evt = typeof(TestObject).GetEvent("Click"); + Assert.IsFalse (criteria.IsSatisfied (evt)); + } + + [Test] + public void IsNotSatisfiedWithNull () + { + RegularExpressionEventNameCriteria criteria = new RegularExpressionEventNameCriteria("Click"); + Assert.IsFalse (criteria.IsSatisfied (null)); + } + + [Test] + public void IsSatisfiedWithAnythingByDefault () + { + RegularExpressionEventNameCriteria criteria = new RegularExpressionEventNameCriteria (); + EventInfo evt = typeof (TestObject).GetEvent ("Click"); + Assert.IsTrue (criteria.IsSatisfied (evt)); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/RegularExpressionMethodNameCriteriaTests.cs b/test/Spring/Spring.Core.Tests/Core/RegularExpressionMethodNameCriteriaTests.cs new file mode 100644 index 00000000..44a667c1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/RegularExpressionMethodNameCriteriaTests.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using Spring.Objects; +using NUnit.Framework; + +#endregion + +namespace Spring.Core +{ + /// + /// Unit tests for the RegularExpressionMethodNameCriteria class. + /// + [TestFixture] + public class RegularExpressionMethodNameCriteriaTests + { + [Test] + public void IsSatisfied () + { + RegularExpressionMethodNameCriteria criteria = new RegularExpressionMethodNameCriteria ("Click"); + MethodInfo method = typeof(TestObject).GetMethod("OnClick"); + Assert.IsTrue (criteria.IsSatisfied (method)); + } + + [Test] + public void IsNotSatisfiedWithGarbage () + { + RegularExpressionMethodNameCriteria criteria = new RegularExpressionMethodNameCriteria ("BingoBango"); + MethodInfo method = typeof(TestObject).GetMethod("OnClick"); + Assert.IsFalse (criteria.IsSatisfied (method)); + } + + [Test] + public void IsNotSatisfiedWithNull () + { + RegularExpressionMethodNameCriteria criteria = new RegularExpressionMethodNameCriteria("OnClick"); + Assert.IsFalse (criteria.IsSatisfied (null)); + } + + [Test] + public void IsSatisfiedWithAnythingByDefault () + { + RegularExpressionMethodNameCriteria criteria = new RegularExpressionMethodNameCriteria (); + MethodInfo method = typeof(TestObject).GetMethod("OnClick"); + Assert.IsTrue (criteria.IsSatisfied (method)); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/CredentialConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/CredentialConverterTests.cs new file mode 100644 index 00000000..12c8d1ee --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/CredentialConverterTests.cs @@ -0,0 +1,144 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Net; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the CredentialConverter class. + /// + /// Bruno Baia + /// $Id: CredentialConverterTests.cs,v 1.1 2007/07/31 18:21:01 bbaia Exp $ + [TestFixture] + public sealed class CredentialConverterTests + { + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void ConvertFromNullReference() + { + CredentialConverter cc = new CredentialConverter(); + cc.ConvertFrom(null); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void ConvertFromNonSupportedOptionBails() + { + CredentialConverter cc = new CredentialConverter(); + cc.ConvertFrom(12); + } + + [Test] + public void ConvertFrom() + { + CredentialConverter cc = new CredentialConverter(); + object credential = cc.ConvertFrom(@"Spring\bbaia:sprnet"); + Assert.IsNotNull(credential); + Assert.IsTrue(credential is NetworkCredential); + + NetworkCredential nc = (NetworkCredential)credential; + Assert.AreEqual("Spring", nc.Domain); + Assert.AreEqual("bbaia", nc.UserName); + Assert.AreEqual("sprnet", nc.Password); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ConvertFromEmptyString() + { + CredentialConverter cc = new CredentialConverter(); + cc.ConvertFrom(string.Empty); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ConvertFromMalformedString() + { + CredentialConverter cc = new CredentialConverter(); + object credential = cc.ConvertFrom(@"Spring:bbaia\sprnet"); + } + + [Test] + public void ConvertFromStringWithoutDomain() + { + CredentialConverter cc = new CredentialConverter(); + object credential = cc.ConvertFrom(@"bbaia:sprnet"); + Assert.IsNotNull(credential); + Assert.IsTrue(credential is NetworkCredential); + + NetworkCredential nc = (NetworkCredential)credential; + Assert.AreEqual(string.Empty, nc.Domain); + Assert.AreEqual("bbaia", nc.UserName); + Assert.AreEqual("sprnet", nc.Password); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ConvertFromStringWithIncorrectDomain() + { + CredentialConverter cc = new CredentialConverter(); + object credential = cc.ConvertFrom(@"\bbaia:sprnet"); + } + + [Test] + public void ConvertFromStringWithoutPassword() + { + CredentialConverter cc = new CredentialConverter(); + object credential = cc.ConvertFrom(@"Spring\bbaia"); + Assert.IsNotNull(credential); + Assert.IsTrue(credential is NetworkCredential); + + NetworkCredential nc = (NetworkCredential)credential; + Assert.AreEqual("Spring", nc.Domain); + Assert.AreEqual("bbaia", nc.UserName); + Assert.AreEqual(string.Empty, nc.Password); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ConvertFromStringWithIncorrectPassword() + { + CredentialConverter cc = new CredentialConverter(); + object credential = cc.ConvertFrom(@"\bbaia:"); + } + + [Test] + public void ConvertFromStringWithUsernameOnly() + { + CredentialConverter cc = new CredentialConverter(); + object credential = cc.ConvertFrom(@"bbaia"); + Assert.IsNotNull(credential); + Assert.IsTrue(credential is NetworkCredential); + + NetworkCredential nc = (NetworkCredential)credential; + Assert.AreEqual(string.Empty, nc.Domain); + Assert.AreEqual("bbaia", nc.UserName); + Assert.AreEqual(string.Empty, nc.Password); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/CustomNumberConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/CustomNumberConverterTests.cs new file mode 100644 index 00000000..4453bbd2 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/CustomNumberConverterTests.cs @@ -0,0 +1,135 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the CustomNumberConverter class. + /// + /// $Id: CustomNumberConverterTests.cs,v 1.1 2007/07/31 18:21:01 bbaia Exp $ + [TestFixture] + public class CustomNumberConverterTests + { + [Test] + public void Instantiation () + { + CustomNumberConverter verter + = new CustomNumberConverter (typeof (int), null, true); + // mmm, this should still pass... it aint a number though + verter + = new CustomNumberConverter (typeof (bool), null, true); + } + + [Test] + [ExpectedException (typeof (ArgumentException))] + public void InstantiationWithNonPrimitiveType () + { + CustomNumberConverter verter + = new CustomNumberConverter ( + typeof (CustomNumberConverterTests), null, true); + } + + [Test] + public void CanConvertFromString () + { + CustomNumberConverter verter + = new CustomNumberConverter (typeof (int), null, true); + Assert.IsTrue (verter.CanConvertFrom (typeof (string))); + Assert.IsFalse (verter.CanConvertFrom (null)); + } + + [Test] + public void ConvertsEmptyStringToZeroWhenAllowed () + { + CustomNumberConverter verter + = new CustomNumberConverter (typeof (int), null, true); + int actual = (int) verter.ConvertFrom ( + null, CultureInfo.CurrentUICulture, string.Empty); + Assert.AreEqual (0, actual); + } + + [Test] + public void ConvertFromSupportedNumericType () + { + Type [] numTypes = new Type [] { + typeof (int), + typeof (uint), + typeof (short), + typeof (ushort), + typeof (long), + typeof (ulong), + typeof (float), + typeof (double), + }; + int expected = 12; + foreach (Type numType in numTypes) + { + try + { + CustomNumberConverter verter + = new CustomNumberConverter (numType, null, true); + object actual = verter.ConvertFrom ( + null, CultureInfo.CurrentUICulture, expected.ToString ()); + Assert.AreEqual (expected, actual); + } + catch (Exception ex) + { + Assert.Fail ("Bailed when converting type '" + numType + "' : " + ex); + } + } + } + + [Test] + [ExpectedException (typeof (FormatException))] + public void BailsOnEmptyStringWhenNotAllowed () + { + CustomNumberConverter verter + = new CustomNumberConverter (typeof (int), null, false); + verter.ConvertFrom (null, CultureInfo.CurrentUICulture, string.Empty); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ConvertFromNonSupportedNumericTypeOptionBails () + { + CustomNumberConverter verter + = new CustomNumberConverter (typeof (char), null, false); + object foo = verter.ConvertFrom ("12"); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ConvertFromNonSupportedOptionBails () + { + CustomNumberConverter verter + = new CustomNumberConverter (typeof (int), null, false); + object foo = verter.ConvertFrom (12); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/FileInfoConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/FileInfoConverterTests.cs new file mode 100644 index 00000000..d4a28e15 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/FileInfoConverterTests.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the FileInfoConverter class. + /// + /// Rick Evans + /// $Id: FileInfoConverterTests.cs,v 1.1 2007/07/31 18:21:01 bbaia Exp $ + [TestFixture] + public sealed class FileInfoConverterTests + { + [Test] + public void CanConvertFrom() + { + FileInfoConverter vrt = new FileInfoConverter(); + Assert.IsTrue(vrt.CanConvertFrom(typeof (string)), "Conversion from a string instance must be supported."); + Assert.IsFalse(vrt.CanConvertFrom(typeof (int))); + } + + [Test] + public void ConvertFrom() + { + FileInfoConverter vrt = new FileInfoConverter(); + object file = vrt.ConvertFrom("././ManAhShoahDoLoveThoseGrits"); + Assert.IsNotNull(file); + Assert.AreEqual(typeof (FileInfo), file.GetType()); + } + + [Test] + public void FileConverter() + { + FileInfoConverter converter = new FileInfoConverter(); + object file = converter.ConvertFrom("C:/test/myfile.txt"); + Assert.IsNotNull(file); + Assert.IsTrue(file is FileInfo); + Assert.AreEqual(new FileInfo("C:/test/myfile.txt").FullName, ((FileInfo) file).FullName); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromNullReference() + { + FileInfoConverter vrt = new FileInfoConverter(); + vrt.ConvertFrom(null); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromNonSupportedOptionBails() + { + FileInfoConverter vrt = new FileInfoConverter(); + vrt.ConvertFrom(12); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/NameValueConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/NameValueConverterTests.cs new file mode 100644 index 00000000..16383aaf --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/NameValueConverterTests.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; + +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the NameValueConverter class. + /// + /// Rick Evans + /// $Id: NameValueConverterTests.cs,v 1.1 2007/07/31 18:21:01 bbaia Exp $ + [TestFixture] + public sealed class NameValueConverterTests + { + [Test] + public void CanConvertFromString () + { + NameValueConverter vrt = new NameValueConverter (); + Assert.IsTrue (vrt.CanConvertFrom (typeof (string)), "Conversion from a string instance must be supported."); + } + + [Test] + public void CanConvertOnlyFromString() + { + NameValueConverter vrt = new NameValueConverter (); + Assert.IsFalse(vrt.CanConvertFrom(typeof(TestObject)), + "Seem to be able to convert from non-supported Type."); + } + + [Test] + public void ConvertFrom () + { + string xml = + "" + + " " + + " " + + ""; + NameValueConverter vrt = new NameValueConverter (); + NameValueCollection actual = vrt.ConvertFrom (xml) as NameValueCollection; + Assert.IsNotNull (actual); + Assert.AreEqual (2, actual.Count); + Assert.AreEqual ("one", actual.GetKey (0)); + Assert.AreEqual ("two", actual.GetKey (1)); + Assert.AreEqual ("1", actual ["one"]); + Assert.AreEqual ("2", actual ["two"]); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ConvertFromNullReference () + { + NameValueConverter vrt = new NameValueConverter (); + vrt.ConvertFrom (null); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ConvertFromNonSupportedOptionBails () + { + NameValueConverter vrt = new NameValueConverter (); + vrt.ConvertFrom (true); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/RGBColorConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/RGBColorConverterTests.cs new file mode 100644 index 00000000..a3602220 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/RGBColorConverterTests.cs @@ -0,0 +1,130 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Drawing; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the RGBColorConverter class. + /// + /// Rick Evans + /// $Id: RGBColorConverterTests.cs,v 1.1 2007/07/31 18:21:01 bbaia Exp $ + [TestFixture] + public sealed class RGBColorConverterTests + { + [Test] + public void ConvertFromRGB() + { + Color expected = Color.BlanchedAlmond; + RGBColorConverter converter = new RGBColorConverter(); + Color actual = + (Color) converter.ConvertFrom(String.Format("{0}, {1}, {2}", expected.R, expected.G, expected.B)); + Assert.AreEqual(expected.A, actual.A); + Assert.AreEqual(expected.R, actual.R); + Assert.AreEqual(expected.G, actual.G); + Assert.AreEqual(expected.B, actual.B); + } + + [Test] + [ExpectedException(typeof (FormatException))] + public void ConvertFromCommaSeparatedListWithNotEnoughValues() + { + RGBColorConverter converter = new RGBColorConverter(); + converter.ConvertFrom("255, 235"); + } + + [Test] + [ExpectedException(typeof (FormatException))] + public void ConvertFromCommaSeparatedListWithOutOfRangeValue() + { + RGBColorConverter converter = new RGBColorConverter(); + converter.ConvertFrom("255, 235, 4567"); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromNullReference() + { + RGBColorConverter vrt = new RGBColorConverter(); + vrt.ConvertFrom(null); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromEmptyString() + { + RGBColorConverter vrt = new RGBColorConverter(); + vrt.ConvertFrom(string.Empty); + } + + [Test] + [ExpectedException(typeof (FormatException))] + public void ConvertFromGarbageBails() + { + RGBColorConverter vrt = new RGBColorConverter(); + vrt.ConvertFrom("*&&%%^£"); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromNonSupportedOptionBails() + { + RGBColorConverter vrt = new RGBColorConverter(); + vrt.ConvertFrom(12); + } + + [Test] + public void ConvertFromName() + { + Color expected = Color.BlanchedAlmond; + RGBColorConverter vrt = new RGBColorConverter(); + Color actual = (Color) vrt.ConvertFrom(expected.Name); + Assert.AreEqual(expected, actual); + } + + [Test] + public void ConvertFromARGB() + { + Color expected = Color.BlanchedAlmond; + RGBColorConverter converter = new RGBColorConverter(); + Color actual = + (Color) converter.ConvertFrom(String.Format("{0}, {1}, {2}, {3}", expected.A, expected.R, expected.G, expected.B)); + Assert.AreEqual(expected.A, actual.A); + Assert.AreEqual(expected.R, actual.R); + Assert.AreEqual(expected.G, actual.G); + Assert.AreEqual(expected.B, actual.B); + } + + [Test] + public void CanConvertFrom() + { + RGBColorConverter vrt = new RGBColorConverter(); + Assert.IsTrue(vrt.CanConvertFrom(typeof (string)), "Conversion from a string instance must be supported."); + Assert.IsFalse(vrt.CanConvertFrom(typeof (int))); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/RegexConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/RegexConverterTests.cs new file mode 100644 index 00000000..c0452df3 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/RegexConverterTests.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text.RegularExpressions; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the RegexConverter class. + /// + /// Bruno Baia + /// $Id: RegexConverterTests.cs,v 1.1 2007/07/31 18:21:01 bbaia Exp $ + [TestFixture] + public sealed class RegexConverterTests + { + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void ConvertFromNullReference() + { + RegexConverter rc = new RegexConverter(); + rc.ConvertFrom(null); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void ConvertFromNonSupportedOptionBails() + { + RegexConverter rc = new RegexConverter(); + rc.ConvertFrom(12); + } + + [Test] + public void ConvertFrom() + { + RegexConverter rc = new RegexConverter(); + object regex = rc.ConvertFrom("[a-z]"); + Assert.IsNotNull(regex); + Assert.IsTrue(regex is Regex); + Assert.IsFalse(((Regex)regex).IsMatch("2")); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/RegistryKeyConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/RegistryKeyConverterTests.cs new file mode 100644 index 00000000..0d27226a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/RegistryKeyConverterTests.cs @@ -0,0 +1,91 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Microsoft.Win32; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the RegistryKeyConverter class. + /// + /// Aleksandar Seovic + /// $Id: RegistryKeyConverterTests.cs,v 1.1 2007/07/31 18:21:01 bbaia Exp $ + [TestFixture] + public sealed class RegistryKeyConverterTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ConvertFromNullReference() + { + RegistryKeyConverter rkc = new RegistryKeyConverter(); + rkc.ConvertFrom(null); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void ConvertFromNonSupportedOptionBails() + { + RegistryKeyConverter rkc = new RegistryKeyConverter(); + rkc.ConvertFrom(12); + } + + [Test] + public void ConvertFrom() + { + RegistryKeyConverter rkc = new RegistryKeyConverter(); + Assert.AreEqual(Registry.CurrentUser, rkc.ConvertFrom("HKEY_CURRENT_USER")); + Assert.AreEqual(Registry.CurrentUser.OpenSubKey("Software").Name, + ((RegistryKey) rkc.ConvertFrom(@"HKEY_CURRENT_USER\Software")).Name); + Assert.AreEqual(Registry.CurrentUser.OpenSubKey("Software").OpenSubKey("Microsoft").Name, + ((RegistryKey) rkc.ConvertFrom(@"HKEY_CURRENT_USER\Software\Microsoft")).Name); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ConvertFromEmptyString() + { + RegistryKeyConverter rkc = new RegistryKeyConverter(); + rkc.ConvertFrom(string.Empty); + } + + [Test] + [ExpectedException(typeof(ArgumentException), @"Registry key [HKEY_CURRENT_USER\sdgsdfgsdfgxadas] does not exist.")] + public void ConvertFromBadKeyString() + { + RegistryKeyConverter rkc = new RegistryKeyConverter(); + rkc.ConvertFrom(@"HKEY_CURRENT_USER\sdgsdfgsdfgxadas\Xyz\Abc"); + } + + [Test] + [ExpectedException(typeof(ArgumentException), "Invalid root hive name [HKEY_ERROR].")] + public void ConvertFromBadHiveString() + { + RegistryKeyConverter rkc = new RegistryKeyConverter(); + rkc.ConvertFrom(@"HKEY_ERROR\sdgsdfgsdfgxadas"); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/ResourceManagerTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/ResourceManagerTests.cs new file mode 100644 index 00000000..bd38da6b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/ResourceManagerTests.cs @@ -0,0 +1,136 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using System.Resources; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the ResourceManagerConverter class. + /// + /// Mark Pollack + [TestFixture] + public sealed class ResourceManagerConverterTests + { + /// + /// Test that we indicate we can convert from strings only. + /// + [Test] + public void CanConvertFrom () + { + ResourceManagerConverter cvt = new ResourceManagerConverter (); + Assert.IsTrue (cvt.CanConvertFrom (typeof (string)), "Conversion from a string instance must be supported."); + Assert.IsFalse (cvt.CanConvertFrom (null)); + } + + /// + /// Test sunny day scenario to convert from resource name, assembly name string pair + /// + [Test] + public void ConvertFrom () + { + + ResourceManagerConverter cvt = new ResourceManagerConverter(); + object actual = cvt.ConvertFrom ("Spring.TestResource.txt, Spring.Core.Tests"); + Assert.IsNotNull (actual); + Assert.AreEqual (typeof (ResourceManager), actual.GetType()); + + } + + /// + /// Test passing a null instance and see if expected exception is raised + /// + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ConvertFromNullReference () + { + ResourceManagerConverter cvt = new ResourceManagerConverter(); + object foo = cvt.ConvertFrom (null); + } + + /// + /// Test passing a single string with no ',' + /// + [Test] + [ExpectedException (typeof (ArgumentException))] + public void ConvertFromSingleString () + { + ResourceManagerConverter cvt = new ResourceManagerConverter(); + object foo = cvt.ConvertFrom ("Spring.TestResource.txt"); + } + + /// + /// Test passing a single ',' + /// + [Test] + [ExpectedException (typeof (ArgumentException))] + public void ConvertFromSingleComma () + { + ResourceManagerConverter cvt = new ResourceManagerConverter(); + object foo = cvt.ConvertFrom (","); + } + + /// + /// Test passing only assembly name + /// + [Test] + [ExpectedException (typeof (ArgumentException))] + public void ConvertFromOnlyAssemblyNAme () + { + ResourceManagerConverter cvt = new ResourceManagerConverter(); + object foo = cvt.ConvertFrom (",Spring.Core.Tests"); + } + + /// + /// Test passing only assembly name + /// + [Test] + [ExpectedException (typeof (ArgumentException))] + public void ConvertFromOnlyResourceName() + { + ResourceManagerConverter cvt = new ResourceManagerConverter(); + object foo = cvt.ConvertFrom ("Spring.TestResource.txt,"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ConvertFromBadAssembly() + { + ResourceManagerConverter cvt = new ResourceManagerConverter(); + object foo = cvt.ConvertFrom ("Spring.TestResource.txt, FooAssembly"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ConvertFromBad_App_GlobalResources() + { + ResourceManagerConverter cvt = new ResourceManagerConverter(); + object foo = cvt.ConvertFrom("Spring.TestResource.txt, "+ResourceManagerConverter.APP_GLOBALRESOURCES_ASSEMBLYNAME); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/RuntimeTypeConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/RuntimeTypeConverterTests.cs new file mode 100644 index 00000000..ac50caeb --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/RuntimeTypeConverterTests.cs @@ -0,0 +1,88 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the RuntimeTypeConverter class. + /// + /// $Id: RuntimeTypeConverterTests.cs,v 1.1 2007/07/31 18:21:01 bbaia Exp $ + [TestFixture] + public class RuntimeTypeConverterTests + { + [Test] + public void CanConvertFrom () + { + RuntimeTypeConverter cnv = new RuntimeTypeConverter (); + Assert.IsTrue (cnv.CanConvertFrom (typeof (string)), "Mmm... I can't convert from a string to a Type."); + Assert.IsFalse (cnv.CanConvertFrom (GetType ()), "Mmm... managed to convert to a Type from a Type of this test. Boing!"); + } + + [Test] + public void CanConvertTo () + { + RuntimeTypeConverter cnv = new RuntimeTypeConverter (); + Assert.IsTrue (cnv.CanConvertTo (typeof (Type)), "Mmm... I can't convert to a Type at all."); + Assert.IsFalse (cnv.CanConvertTo (typeof (void)), "Mmm... managed to convert to a Type from a bad type. Boing!"); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ConvertFromNonSupportedType () + { + RuntimeTypeConverter cnv = new RuntimeTypeConverter (); + object foo = cnv.ConvertFrom (12); + } + + [Test] + public void ConvertFromString () + { + RuntimeTypeConverter cnv = new RuntimeTypeConverter (); + object foo = cnv.ConvertFrom ("System.String"); + Assert.IsNotNull (foo); + Assert.AreEqual ("System.RuntimeType", foo.GetType ().FullName); + } + + [Test] + public void ConvertToString () + { + RuntimeTypeConverter cnv = new RuntimeTypeConverter (); + object foo = cnv.ConvertTo (typeof (string), typeof (string)); + Assert.IsNotNull (foo); + Assert.AreEqual (typeof (string).AssemblyQualifiedName, foo); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ConvertToStringFromNonSupportedType () + { + RuntimeTypeConverter cnv = new RuntimeTypeConverter (); + object foo = cnv.ConvertTo (typeof (string), GetType ()); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/StreamConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/StreamConverterTests.cs new file mode 100644 index 00000000..f8943bad --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/StreamConverterTests.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Net; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the StreamConverter class. + /// + /// Rick Evans + /// $Id: StreamConverterTests.cs,v 1.1 2007/07/31 18:21:01 bbaia Exp $ + [TestFixture] + public sealed class StreamConverterTests + { + [Test] + public void CanConvertFrom() + { + StreamConverter vrt = new StreamConverter(); + Assert.IsTrue(vrt.CanConvertFrom(typeof (string)), + "Conversion from a string instance must be supported."); + Assert.IsFalse(vrt.CanConvertFrom(typeof (int))); + } + + [Test] + [Explicit] // requires one to be connected to the 'net... + public void ConvertFrom() + { + StreamConverter vrt = new StreamConverter(); + Stream actual = vrt.ConvertFrom("http://www.springframework.net/") as Stream; + Assert.IsNotNull(actual); + } + + [Test] + [Explicit] // fails if there is a transparent proxy that redirects to error page for non existing URL + [ExpectedException(typeof (WebException))] + public void ConvertFromValidButNonExistingStreamResource() + { + new StreamConverter().ConvertFrom("http://www.aaaabbbbccccddd.com"); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromNullReference() + { + new StreamConverter().ConvertFrom(null); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromNonSupportedOptionBails() + { + new StreamConverter().ConvertFrom(12); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/StringArrayConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/StringArrayConverterTests.cs new file mode 100644 index 00000000..7e82ca8e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/StringArrayConverterTests.cs @@ -0,0 +1,156 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Threading; +using NUnit.Framework; +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the StringArrayConverter class. + /// + /// Rick Evans + /// $Id: StringArrayConverterTests.cs,v 1.1 2007/07/31 18:21:02 bbaia Exp $ + [TestFixture] + public sealed class StringArrayConverterTests + { + [Test] + public void CanConvertFrom() + { + StringArrayConverter vrt = new StringArrayConverter(); + Assert.IsTrue(vrt.CanConvertFrom(typeof (string)), "Conversion from a string instance must be supported."); + Assert.IsFalse(vrt.CanConvertFrom(null)); + } + + [Test] + public void ConvertFrom() + { + object[] expected = new object[] {"1", "Foo", "3"}; + StringArrayConverter vrt = new StringArrayConverter(); + object actual = vrt.ConvertFrom("1,Foo,3"); + Assert.IsNotNull(actual); + Assert.AreEqual(typeof (string[]), actual.GetType()); + Assert.AreEqual(3, ((string[]) actual).Length, "Wrong number of elements in the resulting array."); + Assert.IsTrue(ArrayUtils.AreEqual(expected, (string[]) actual), + "Individual array elements not correctly converted."); + } + + [Test] + public void ConvertFromPreservesExtraneousWhitespace() + { + object[] expected = new object[] {"1 ", " Foo ", " 3"}; + StringArrayConverter vrt = new StringArrayConverter(); + object actual = vrt.ConvertFrom("1 , Foo , 3"); + Assert.IsNotNull(actual); + Assert.AreEqual(typeof (string[]), actual.GetType()); + Assert.IsTrue(ArrayUtils.AreEqual(expected, (string[]) actual), + "Individual array elements not correctly converted (check the whitespace?)."); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromNullReference() + { + StringArrayConverter vrt = new StringArrayConverter(); + vrt.ConvertFrom(null); + } + + [Test] + [ExpectedException(typeof (NotSupportedException))] + public void ConvertFromNonSupportedOptionBails() + { + StringArrayConverter vrt = new StringArrayConverter(); + vrt.ConvertFrom(12); + } + + [Test] + public void EnsureCultureListSeparatorIsIgnored() + { + CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture; + try + { + CultureInfo frenchCulture = new CultureInfo("fr-FR"); + Thread.CurrentThread.CurrentCulture = frenchCulture; + object[] expected = new object[] {"1", "Foo", "3"}; + StringArrayConverter vrt = new StringArrayConverter(); + // France uses the ';' (semi-colon) to separate list items... + object actual = vrt.ConvertFrom("1,Foo,3"); + Assert.IsNotNull(actual); + Assert.AreEqual(typeof (string[]), actual.GetType()); + Assert.IsTrue(ArrayUtils.AreEqual(expected, (string[]) actual), + "Individual array elements not correctly converted."); + } + finally + { + Thread.CurrentThread.CurrentCulture = originalCulture; + } + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void EmptyListSeparator() + { + StringArrayConverter vrt = new StringArrayConverter(); + vrt.ListSeparator = string.Empty; + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TooLongListSeparator() + { + StringArrayConverter vrt = new StringArrayConverter(); + vrt.ListSeparator = " "; + } + + [Test] + public void CustomListSeparator() + { + object[] expected = new object[] {"1", "Foo", "3"}; + StringArrayConverter vrt = new StringArrayConverter(); + const string customSeparator = "#"; + vrt.ListSeparator = customSeparator; + object actual = vrt.ConvertFrom(string.Format("1{0}Foo{0}3", customSeparator)); + Assert.IsNotNull(actual); + Assert.AreEqual(typeof (string[]), actual.GetType()); + Assert.IsTrue(ArrayUtils.AreEqual(expected, (string[]) actual), + "Individual array elements not correctly converted."); + } + + [Test] + public void NullingTheListSeparatorMakesItRevertToTheDefault() + { + object[] expected = new object[] {"1", "Foo", "3"}; + StringArrayConverter vrt = new StringArrayConverter(); + vrt.ListSeparator = null; + object actual = vrt.ConvertFrom("1,Foo,3"); + Assert.IsNotNull(actual); + Assert.AreEqual(typeof (string[]), actual.GetType()); + Assert.IsTrue(ArrayUtils.AreEqual(expected, (string[]) actual), + "Individual array elements not correctly converted."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/TimeSpanConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/TimeSpanConverterTests.cs new file mode 100644 index 00000000..7ca68e7c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/TimeSpanConverterTests.cs @@ -0,0 +1,133 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the TimeSpanConverter class. + /// + /// Bruno Baia + /// $Id: TimeSpanConverterTests.cs,v 1.2 2007/08/29 17:29:41 oakinger Exp $ + [TestFixture] + public sealed class TimeSpanConverterTests + { + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void ConvertFromNullReference() + { + TimeSpanConverter tsc = new TimeSpanConverter(); + tsc.ConvertFrom(null); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void ConvertFromNonSupportedOptionBails() + { + TimeSpanConverter tsc = new TimeSpanConverter(); + tsc.ConvertFrom(12); + } + + [Test] + [ExpectedException(typeof(FormatException))] + public void ConvertFromStringMalformed() + { + TimeSpanConverter tsc = new TimeSpanConverter(); + tsc.ConvertFrom("15a"); + } + + [Test] + public void BaseConvertFrom() + { + TimeSpanConverter tsc = new TimeSpanConverter(); + object timeSpan = tsc.ConvertFrom("00:00:10"); + Assert.IsNotNull(timeSpan); + Assert.IsTrue(timeSpan is TimeSpan); + Assert.AreEqual(TimeSpan.FromSeconds(10), (TimeSpan)timeSpan); + } + + [Test] + public void ConvertFrom() + { + TimeSpanConverter tsc = new TimeSpanConverter(); + object timeSpan = tsc.ConvertFrom("1000"); + Assert.IsNotNull(timeSpan); + Assert.IsTrue(timeSpan is TimeSpan); + Assert.AreEqual(TimeSpan.Parse("1000"), (TimeSpan)timeSpan); + } + + [Test] + public void ConvertFromStringWithMilliSecondSpecifier() + { + TimeSpanConverter tsc = new TimeSpanConverter(); + object timeSpan = tsc.ConvertFrom("100ms"); + Assert.IsNotNull(timeSpan); + Assert.IsTrue(timeSpan is TimeSpan); + Assert.AreEqual(TimeSpan.FromMilliseconds(100), (TimeSpan)timeSpan); + } + + [Test] + public void ConvertFromStringWithSecondSpecifier() + { + TimeSpanConverter tsc = new TimeSpanConverter(); + object timeSpan = tsc.ConvertFrom("10s"); + Assert.IsNotNull(timeSpan); + Assert.IsTrue(timeSpan is TimeSpan); + Assert.AreEqual(TimeSpan.FromSeconds(10), (TimeSpan)timeSpan); + } + + [Test] + public void ConvertFromStringWithMinuteSpecifier() + { + TimeSpanConverter tsc = new TimeSpanConverter(); + object timeSpan = tsc.ConvertFrom("2m"); + Assert.IsNotNull(timeSpan); + Assert.IsTrue(timeSpan is TimeSpan); + Assert.AreEqual(TimeSpan.FromMinutes(2), (TimeSpan)timeSpan); + } + + [Test] + public void ConvertFromStringWithHourSpecifier() + { + TimeSpanConverter tsc = new TimeSpanConverter(); + object timeSpan = tsc.ConvertFrom("1H"); + Assert.IsNotNull(timeSpan); + Assert.IsTrue(timeSpan is TimeSpan); + Assert.AreEqual(TimeSpan.FromHours(1), (TimeSpan)timeSpan); + } + + [Test] + public void ConvertFromStringWithDaySpecifier() + { + TimeSpanConverter tsc = new TimeSpanConverter(); + object timeSpan = tsc.ConvertFrom("1d"); + Assert.IsNotNull(timeSpan); + Assert.IsTrue(timeSpan is TimeSpan); + Assert.AreEqual(TimeSpan.FromDays(1), (TimeSpan)timeSpan); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/TypeConverterRegistryTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/TypeConverterRegistryTests.cs new file mode 100644 index 00000000..b7a4cc72 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/TypeConverterRegistryTests.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the TypeConverterRegistry class. + /// + /// Bruno Baia + /// $Id: TypeConverterRegistryTests.cs,v 1.1 2007/07/31 18:21:02 bbaia Exp $ + [TestFixture] + public sealed class TypeConverterRegistryTests + { + [Test] + public void GetConverterForEnums() + { + TypeConverter converter = TypeConverterRegistry.GetConverter(typeof(DayOfWeek)); + Assert.IsTrue(converter is EnumConverter); + } + + [Test] + public void GetInternalConverter() + { + TypeConverter converter = TypeConverterRegistry.GetConverter(typeof(int)); + Assert.IsTrue(converter is Int32Converter); + } + + [Test] + public void GetSpringConverter() + { + TypeConverter converter = TypeConverterRegistry.GetConverter(typeof(string[])); + Assert.IsTrue(converter is StringArrayConverter); + } + + [Test] + public void RegisterConverter() + { + TypeConverterRegistry.RegisterConverter("System.DateTime", "System.ComponentModel.DateTimeConverter"); + } + + [Test] + [ExpectedException(typeof(TypeLoadException))] + public void RegisterConverterWithNonResolvableType() + { + TypeConverterRegistry.RegisterConverter("Systemm.DateTime", "System.ComponentModel.DateTimeConverter"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void RegisterConverterWithNonTypeConverter() + { + TypeConverterRegistry.RegisterConverter("System.DateTime", "System.DateTime"); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/UniqueKeyConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/UniqueKeyConverterTests.cs new file mode 100644 index 00000000..77df652b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/UniqueKeyConverterTests.cs @@ -0,0 +1,125 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ComponentModel; +using NUnit.Framework; +using Spring.Util; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Tests functionality. + /// + /// Erich Eichinger + /// $Id: UniqueKeyConverterTests.cs,v 1.1 2007/08/22 20:17:05 oakinger Exp $ + [TestFixture] + public class UniqueKeyConverterTests + { + [Test] + public void CanConvertFromStringOnly() + { + TypeConverter c = new UniqueKeyConverter(); + Assert.IsTrue(c.CanConvertFrom(typeof(string))); + Assert.IsFalse(c.CanConvertFrom(typeof(object))); + } + + [Test] + public void CanConvertToStringOnly() + { + TypeConverter c = new UniqueKeyConverter(); + Assert.IsTrue(c.CanConvertTo(typeof(string))); + Assert.IsFalse(c.CanConvertTo(typeof(object))); + } + + [Test] + public void ConvertToStringOrUniqueKeyOnly() + { + TypeConverter c = new UniqueKeyConverter(); + UniqueKey key = UniqueKey.GetInstanceScoped(new object(), "PartialKey"); + + c.ConvertTo(key, typeof(UniqueKey)); + c.ConvertTo(key, typeof(string)); + try + { + c.ConvertTo(key, typeof(object)); + Assert.Fail(); + } + catch(NotSupportedException) {} + } + + [Test] + public void ConvertFromStringOrUniqueKeyOnly() + { + TypeConverter c = new UniqueKeyConverter(); + UniqueKey key = UniqueKey.GetInstanceScoped(new object(), "PartialKey"); + c.ConvertFrom(key); + c.ConvertFrom(key.ToString()); + try + { + c.ConvertFrom(new object()); + Assert.Fail(); + } + catch(NotSupportedException) {} + } + + [Test] + public void ConvertFromReturnsNullIfInputNull() + { + TypeConverter c = new UniqueKeyConverter(); + Assert.IsNull(c.ConvertFrom(null)); + } + + [Test] + public void ConvertToReturnsNullIfInputNull() + { + TypeConverter c = new UniqueKeyConverter(); + Assert.IsNull(c.ConvertTo(null, typeof(string))); + } + + [Test] + public void ConvertToEqualsToString() + { + object testObject = new object(); + UniqueKey key = UniqueKey.GetInstanceScoped(testObject, "PartialKey"); + + TypeConverter c = new UniqueKeyConverter(); + string stringKey = (string) c.ConvertTo(key, typeof(string)); + Assert.AreEqual(key.ToString(), stringKey); + } + + [Test] + public void ConvertToAndFromAreInSync() + { + object testObject = new object(); + UniqueKey expectedKey = UniqueKey.GetInstanceScoped(testObject, "PartialKey"); + + TypeConverter c = new UniqueKeyConverter(); + string stringKey = (string)c.ConvertTo(expectedKey, typeof(string)); + UniqueKey key2 = (UniqueKey) c.ConvertFrom(stringKey); + Assert.AreEqual( expectedKey, key2 ); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeConversion/UriConverterTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeConversion/UriConverterTests.cs new file mode 100644 index 00000000..5043ddc9 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeConversion/UriConverterTests.cs @@ -0,0 +1,87 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeConversion +{ + /// + /// Unit tests for the UriConverter class. + /// + /// Rick Evans + /// $Id: UriConverterTests.cs,v 1.1 2007/07/31 18:21:02 bbaia Exp $ + [TestFixture] + public sealed class UriConverterTests + { + [Test] + public void CanConvertFrom () + { + UriConverter vrt = new UriConverter (); + Assert.IsTrue (vrt.CanConvertFrom (typeof (string)), "Conversion from a string instance must be supported."); + Assert.IsFalse (vrt.CanConvertFrom (typeof (int))); + } + + [Test] + public void ConvertFrom () + { + UriConverter vrt = new UriConverter (); + object actual = vrt.ConvertFrom ("svn://localhost/Spring/trunk/"); + Assert.IsNotNull (actual); + Assert.AreEqual (typeof (System.Uri), actual.GetType ()); + } + + [Test] + public void ConvertFromMalformedUriBails () + { + try + { + UriConverter vrt = new UriConverter (); + object actual = vrt.ConvertFrom ("$TheAngelGang"); + } + catch (Exception ex) + { + // check that the inner exception was doe to the malformed URL + Assert.IsTrue (ex.InnerException is UriFormatException); + } + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ConvertFromNullReference () + { + UriConverter vrt = new UriConverter (); + object actual = vrt.ConvertFrom (null); + } + + [Test] + [ExpectedException (typeof (NotSupportedException))] + public void ConvertFromNonSupportedOptionBails () + { + UriConverter vrt = new UriConverter (); + object actual = vrt.ConvertFrom (12); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/TypeResolution/CachedTypeResolverTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeResolution/CachedTypeResolverTests.cs new file mode 100644 index 00000000..48555ea6 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeResolution/CachedTypeResolverTests.cs @@ -0,0 +1,57 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using DotNetMock.Dynamic; +using NUnit.Framework; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Unit tests for the CachedTypeResolver class. + /// + /// Rick Evans + /// $Id: CachedTypeResolverTests.cs,v 1.1 2007/07/31 18:21:02 bbaia Exp $ + [TestFixture] + public sealed class CachedTypeResolverTests + { + [Test] + [ExpectedException(typeof(TypeLoadException))] + public void ResolveWithNullTypeName() { + + IDynamicMock mock = new DynamicMock(typeof(ITypeResolver)); + ITypeResolver mockResolver = (ITypeResolver) mock.Object; + + CachedTypeResolver resolver = new CachedTypeResolver(mockResolver); + resolver.Resolve(null); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void InstantiateWithNullTypeResolver() + { + new CachedTypeResolver(null); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/TypeResolution/GenericTypeResolverTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeResolution/GenericTypeResolverTests.cs new file mode 100644 index 00000000..7e22d854 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeResolution/GenericTypeResolverTests.cs @@ -0,0 +1,94 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Data; + +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Unit tests for the GenericTypeResolver class. + /// + /// Bruno Baia + /// $Id: GenericTypeResolverTests.cs,v 1.2 2007/08/16 05:42:33 markpollack Exp $ + [TestFixture] + public class GenericTypeResolverTests : TypeResolverTests + { + protected override ITypeResolver GetTypeResolver() + { + return new GenericTypeResolver(); + } + + [Test] + public void ResolveLocalAssemblyGenericType() + { + Type t = GetTypeResolver().Resolve("Spring.Objects.TestGenericObject< int, string>"); + Assert.AreEqual(typeof(TestGenericObject), t); + } + + [Test] + public void ResolveLocalAssemblyGenericTypeDefinition() + { + // CLOVER:ON + Type t = GetTypeResolver().Resolve("Spring.Objects.TestGenericObject< ,>"); + // CLOVER:OFF + Assert.AreEqual(typeof(TestGenericObject<,>), t); + } + + [Test] + [ExpectedException(typeof(TypeLoadException))] + public void ResolveLocalAssemblyGenericTypeOpen() + { + GetTypeResolver().Resolve("Spring.Objects.TestGenericObject"); + } + + [Test] + public void ResolveGenericTypeWithAssemblyName() + { + Type t = GetTypeResolver().Resolve("System.Collections.Generic.Stack, System"); + Assert.AreEqual(typeof(System.Collections.Generic.Stack), t); + } + + [Test] + [ExpectedException(typeof(TypeLoadException))] + public void ResolveAmbiguousGenericTypeWithAssemblyName() + { + Type t = GetTypeResolver().Resolve("Spring.Objects.TestGenericObject, System, string>"); + } + + [Test] + [ExpectedException(typeof(TypeLoadException))] + public void ResolveMalformedGenericType() + { + Type t = GetTypeResolver().Resolve("Spring.Objects.TestGenericObject>"); + } + } +} + +#endif diff --git a/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeRegistryTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeRegistryTests.cs new file mode 100644 index 00000000..8631e5ff --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeRegistryTests.cs @@ -0,0 +1,738 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Util; +using IBar=Spring.Objects.Factory.IBar; +using Spring.Context; +using Spring.Context.Support; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Unit tests for the TypeRegistry class. + /// + /// Aleksandar Seovic + /// Rick Evans + [TestFixture] + public sealed class TypeRegistryTests + { + [Test] + public void TestAliasResolution() + { + TypeRegistry.RegisterType("Foo", typeof (Foo)); + TypeRegistry.RegisterType("Bar", "Spring.Objects.Factory.Bar, Spring.Core.Tests"); + + Assert.AreEqual(TypeRegistry.ResolveType("Foo"), typeof (Foo)); + Assert.AreEqual(TypeRegistry.ResolveType("Bar"), typeof (Bar)); + + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Core.Tests/Spring.Core.TypeResolution/aliasedObjects.xml"); + + Foo foo = ctx.GetObject("aliasedType") as Foo; + Assert.IsNotNull(foo); + Assert.IsNotNull(foo.Bar); + Assert.AreEqual(foo.Bar, typeof (Bar)); + Assert.IsTrue(typeof (IBar).IsAssignableFrom(foo.Bar)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void ResolveTypeWithNullAliasArg() + { + TypeRegistry.ResolveType(null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void ResolveTypeWithEmptyAliasArg() + { + TypeRegistry.ResolveType(string.Empty); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void ResolveTypeWithWhitespacedAliasArg() + { + TypeRegistry.ResolveType(" "); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterTypeWithNullAliasArg() + { + TypeRegistry.RegisterType(null, typeof (TestObject)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterTypeWithEmptyAliasArg() + { + TypeRegistry.RegisterType(string.Empty, typeof (TestObject)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterTypeWithWhitespacedAliasArg() + { + TypeRegistry.RegisterType(" ", typeof (TestObject)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterTypeWithNullTypeArg() + { + TypeRegistry.RegisterType("foo", (Type) null); + } + + public void RegisterTypeWithNullTypeStringArg() + { + TypeRegistry.RegisterType("foo", (String) null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterTypeWithEmptyTypeStringArg() + { + TypeRegistry.RegisterType("foo", string.Empty); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterTypeWithWhitespacedTypeStringArg() + { + TypeRegistry.RegisterType("foo", " "); + } + + [Test] + public void ReturnsNullIfNoTypeAliasRegistered() + { + Type type = TypeRegistry.ResolveType("panko"); + Assert.IsNull(type, "Must return null if no Type is registered under the supplied alias."); + } + + [Test] + public void RegisteringAnAliasTwiceDoesNotThrowException() + { + const string Alias = "foo"; + + TypeRegistry.RegisterType(Alias, typeof (TestObject)); + TypeRegistry.RegisterType(Alias, GetType()); + + Type type = TypeRegistry.ResolveType(Alias); + Assert.AreEqual(GetType(), type, "Overriding Type was not registered."); + } + + [Test] + public void ResolveIntegerByName() + { + Assert.AreEqual(typeof (int), + TypeRegistry.ResolveType("int")); + } + + [Test] + public void ResolveChar() + { + Assert.AreEqual(typeof (char), + TypeRegistry.ResolveType(TypeRegistry.CharAlias)); + } + + [Test] + public void ResolveInteger() + { + Assert.AreEqual(typeof (int), + TypeRegistry.ResolveType(TypeRegistry.Int32Alias)); + } + + [Test] + public void ResolveDecimal() + { + Assert.AreEqual(typeof (decimal), + TypeRegistry.ResolveType(TypeRegistry.DecimalAlias)); + } + + [Test] + public void ResolveUnsignedIntegerByName() + { + Assert.AreEqual(typeof (uint), + TypeRegistry.ResolveType("uint")); + } + + [Test] + public void ResolveUnsignedInteger() + { + Assert.AreEqual(typeof (uint), + TypeRegistry.ResolveType(TypeRegistry.UInt32Alias)); + } + + [Test] + public void ResolveFloatByName() + { + Assert.AreEqual(typeof (float), + TypeRegistry.ResolveType("float")); + } + + [Test] + public void ResolveFloat() + { + Assert.AreEqual(typeof (float), + TypeRegistry.ResolveType(TypeRegistry.FloatAlias)); + } + + [Test] + public void ResolveDoubleByName() + { + Assert.AreEqual(typeof (double), + TypeRegistry.ResolveType("double")); + } + + [Test] + public void ResolveDouble() + { + Assert.AreEqual(typeof (double), + TypeRegistry.ResolveType(TypeRegistry.DoubleAlias)); + } + + [Test] + public void ResolveLongByName() + { + Assert.AreEqual(typeof (long), + TypeRegistry.ResolveType("long")); + } + + [Test] + public void ResolveLong() + { + Assert.AreEqual(typeof (long), + TypeRegistry.ResolveType(TypeRegistry.Int64Alias)); + } + + [Test] + public void ResolveUnsignedLongByName() + { + Assert.AreEqual(typeof (ulong), + TypeRegistry.ResolveType("ulong")); + } + + [Test] + public void ResolveUnsignedLong() + { + Assert.AreEqual(typeof (ulong), + TypeRegistry.ResolveType(TypeRegistry.UInt64Alias)); + } + + [Test] + public void ResolveShortByName() + { + Assert.AreEqual(typeof (short), + TypeRegistry.ResolveType("short")); + } + + [Test] + public void ResolveShort() + { + Assert.AreEqual(typeof (short), + TypeRegistry.ResolveType(TypeRegistry.Int16Alias)); + } + + [Test] + public void ResolveUnsignedShortByName() + { + Assert.AreEqual(typeof (ushort), + TypeRegistry.ResolveType("ushort")); + } + + [Test] + public void ResolveUnsignedShort() + { + Assert.AreEqual(typeof (ushort), + TypeRegistry.ResolveType(TypeRegistry.UInt16Alias)); + } + + [Test] + public void ResolveDate() + { + Assert.AreEqual(typeof (DateTime), + TypeRegistry.ResolveType(TypeRegistry.DateAlias)); + } + + [Test] + public void ResolveBool() + { + Assert.AreEqual(typeof (bool), + TypeRegistry.ResolveType(TypeRegistry.BoolAlias)); + } + + [Test] + public void ResolveIntegerByVBName() + { + Assert.AreEqual(typeof (int), + TypeRegistry.ResolveType("Integer")); + } + + [Test] + public void ResolveVBInteger() + { + Assert.AreEqual(typeof (int), + TypeRegistry.ResolveType(TypeRegistry.Int32AliasVB)); + } + + [Test] + public void ResolveVBDecimal() + { + Assert.AreEqual(typeof (decimal), + TypeRegistry.ResolveType(TypeRegistry.DecimalAliasVB)); + } + + [Test] + public void ResolveSingleByName() + { + Assert.AreEqual(typeof (float), + TypeRegistry.ResolveType("Single")); + } + + [Test] + public void ResolveSingle() + { + Assert.AreEqual(typeof (float), + TypeRegistry.ResolveType(TypeRegistry.SingleAlias)); + } + + [Test] + public void ResolveVBDouble() + { + Assert.AreEqual(typeof (double), + TypeRegistry.ResolveType(TypeRegistry.DoubleAliasVB)); + } + + [Test] + public void ResolveVBLong() + { + Assert.AreEqual(typeof (long), + TypeRegistry.ResolveType(TypeRegistry.Int64AliasVB)); + } + + [Test] + public void ResolveVBShort() + { + Assert.AreEqual(typeof (short), + TypeRegistry.ResolveType(TypeRegistry.Int16AliasVB)); + } + + [Test] + public void ResolveVBDate() + { + Assert.AreEqual(typeof (DateTime), + TypeRegistry.ResolveType(TypeRegistry.DateAliasVB)); + } + + [Test] + public void ResolveVBBool() + { + Assert.AreEqual(typeof (bool), + TypeRegistry.ResolveType(TypeRegistry.BoolAliasVB)); + } + + [Test] + public void ResolveString() + { + Assert.AreEqual(typeof(string), + TypeRegistry.ResolveType(TypeRegistry.StringAlias)); + } + + [Test] + public void ResolveVBString() + { + Assert.AreEqual(typeof(string), + TypeRegistry.ResolveType(TypeRegistry.StringAliasVB)); + } + + [Test] + public void ResolveStringArray() + { + Assert.AreEqual(typeof (string[]), + TypeRegistry.ResolveType(TypeRegistry.StringArrayAlias)); + } + + [Test] + public void ResolveVBStringArray() + { + Assert.AreEqual(typeof (string[]), + TypeRegistry.ResolveType(TypeRegistry.StringArrayAliasVB)); + } + + [Test] + public void ResolveObject() + { + Assert.AreEqual(typeof(object), + TypeRegistry.ResolveType(TypeRegistry.ObjectAlias)); + } + + [Test] + public void ResolveVBObject() + { + Assert.AreEqual(typeof(object), + TypeRegistry.ResolveType(TypeRegistry.ObjectAliasVB)); + } + + [Test] + public void ResolveObjectArray() + { + Assert.AreEqual(typeof(object[]), + TypeRegistry.ResolveType(TypeRegistry.ObjectArrayAlias)); + } + + [Test] + public void ResolveVBObjectArray() + { + Assert.AreEqual(typeof(object[]), + TypeRegistry.ResolveType(TypeRegistry.ObjectArrayAliasVB)); + } + + [Test] + public void ResolveCharArray() + { + Assert.AreEqual(typeof (char[]), + TypeRegistry.ResolveType(TypeRegistry.CharArrayAlias)); + } + + [Test] + public void ResolveVBCharArray() + { + Assert.AreEqual(typeof (char[]), + TypeRegistry.ResolveType(TypeRegistry.CharArrayAliasVB)); + } + + [Test] + public void ResolveInt32Array() + { + Assert.AreEqual(typeof (int[]), + TypeRegistry.ResolveType(TypeRegistry.Int32ArrayAlias)); + } + + [Test] + public void ResolveVBInt32Array() + { + Assert.AreEqual(typeof (int[]), + TypeRegistry.ResolveType(TypeRegistry.Int32ArrayAliasVB)); + } + + [Test] + public void ResolveInt16Array() + { + Assert.AreEqual(typeof (short[]), + TypeRegistry.ResolveType(TypeRegistry.Int16ArrayAlias)); + } + + [Test] + public void ResolveVBInt16Array() + { + Assert.AreEqual(typeof (short[]), + TypeRegistry.ResolveType(TypeRegistry.Int16ArrayAliasVB)); + } + + [Test] + public void ResolveInt64Array() + { + Assert.AreEqual(typeof (long[]), + TypeRegistry.ResolveType(TypeRegistry.Int64ArrayAlias)); + } + + [Test] + public void ResolveVBInt64Array() + { + Assert.AreEqual(typeof (long[]), + TypeRegistry.ResolveType(TypeRegistry.Int64ArrayAliasVB)); + } + + [Test] + public void ResolveUInt16Array() + { + Assert.AreEqual(typeof (ushort[]), + TypeRegistry.ResolveType(TypeRegistry.UInt16ArrayAlias)); + } + + [Test] + public void ResolveUInt32Array() + { + Assert.AreEqual(typeof (uint[]), + TypeRegistry.ResolveType(TypeRegistry.UInt32ArrayAlias)); + } + + [Test] + public void ResolveUInt64Array() + { + Assert.AreEqual(typeof (ulong[]), + TypeRegistry.ResolveType(TypeRegistry.UInt64ArrayAlias)); + } + + [Test] + public void ResolveBoolArray() + { + Assert.AreEqual(typeof (bool[]), + TypeRegistry.ResolveType(TypeRegistry.BoolArrayAlias)); + } + + [Test] + public void ResolveVBBoolArray() + { + Assert.AreEqual(typeof (bool[]), + TypeRegistry.ResolveType(TypeRegistry.BoolArrayAliasVB)); + } + + [Test] + public void ResolveDateArray() + { + Assert.AreEqual(typeof (DateTime[]), + TypeRegistry.ResolveType(TypeRegistry.DateTimeArrayAlias)); + } + + [Test] + public void ResolveVBDateArray() + { + Assert.AreEqual(typeof (DateTime[]), + TypeRegistry.ResolveType(TypeRegistry.DateTimeArrayAliasVB)); + } + + [Test] + public void ResolveFloatArray() + { + Assert.AreEqual(typeof (float[]), + TypeRegistry.ResolveType(TypeRegistry.FloatArrayAlias)); + } + + [Test] + public void ResolveVBSingleArray() + { + Assert.AreEqual(typeof (float[]), + TypeRegistry.ResolveType(TypeRegistry.SingleArrayAliasVB)); + } + + [Test] + public void ResolveDoubleArray() + { + Assert.AreEqual(typeof (double[]), + TypeRegistry.ResolveType(TypeRegistry.DoubleArrayAlias)); + } + + [Test] + public void ResolveVBDoubleArray() + { + Assert.AreEqual(typeof (double[]), + TypeRegistry.ResolveType(TypeRegistry.DoubleArrayAliasVB)); + } + +#if NET_2_0 + [Test] + public void ResolveNullableChar() + { + Assert.AreEqual(typeof(char?), + TypeRegistry.ResolveType(TypeRegistry.NullableCharAlias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableCharAlias)); + } + + [Test] + public void ResolveNullableInteger() + { + Assert.AreEqual(typeof(int?), + TypeRegistry.ResolveType(TypeRegistry.NullableInt32Alias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableInt32Alias)); + } + + [Test] + public void ResolveNullableDecimal() + { + Assert.AreEqual(typeof(decimal?), + TypeRegistry.ResolveType(TypeRegistry.NullableDecimalAlias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableDecimalAlias)); + } + + [Test] + public void ResolveNullableUnsignedInteger() + { + Assert.AreEqual(typeof(uint?), + TypeRegistry.ResolveType(TypeRegistry.NullableUInt32Alias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableUInt32Alias)); + } + + [Test] + public void ResolveNullableFloat() + { + Assert.AreEqual(typeof(float?), + TypeRegistry.ResolveType(TypeRegistry.NullableFloatAlias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableFloatAlias)); + } + + [Test] + public void ResolveNullableDouble() + { + Assert.AreEqual(typeof(double?), + TypeRegistry.ResolveType(TypeRegistry.NullableDoubleAlias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableDoubleAlias)); + } + + [Test] + public void ResolveNullableLong() + { + Assert.AreEqual(typeof(long?), + TypeRegistry.ResolveType(TypeRegistry.NullableInt64Alias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableInt64Alias)); + } + + [Test] + public void ResolveNullableUnsignedLong() + { + Assert.AreEqual(typeof(ulong?), + TypeRegistry.ResolveType(TypeRegistry.NullableUInt64Alias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableUInt64Alias)); + } + + [Test] + public void ResolveNullableShort() + { + Assert.AreEqual(typeof(short?), + TypeRegistry.ResolveType(TypeRegistry.NullableInt16Alias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableInt16Alias)); + } + + [Test] + public void ResolveNullableUnsignedShort() + { + Assert.AreEqual(typeof(ushort?), + TypeRegistry.ResolveType(TypeRegistry.NullableUInt16Alias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableUInt16Alias)); + } + + [Test] + public void ResolveNullableBool() + { + Assert.AreEqual(typeof(bool?), + TypeRegistry.ResolveType(TypeRegistry.NullableBoolAlias)); + Assert.AreEqual(typeof(Nullable), + TypeRegistry.ResolveType(TypeRegistry.NullableBoolAlias)); + } + + [Test] + public void ResolveNullableCharArray() + { + Assert.AreEqual(typeof(char?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableCharArrayAlias)); + } + + [Test] + public void ResolveNullableInt32Array() + { + Assert.AreEqual(typeof(int?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableInt32ArrayAlias)); + } + + [Test] + public void ResolveNullableDecimalArray() + { + Assert.AreEqual(typeof(decimal?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableDecimalArrayAlias)); + } + + [Test] + public void ResolveNullableInt16Array() + { + Assert.AreEqual(typeof(short?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableInt16ArrayAlias)); + } + + [Test] + public void ResolveNullableInt64Array() + { + Assert.AreEqual(typeof(long?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableInt64ArrayAlias)); + } + + [Test] + public void ResolveNullableUInt16Array() + { + Assert.AreEqual(typeof(ushort?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableUInt16ArrayAlias)); + } + + [Test] + public void ResolveNullableUInt32Array() + { + Assert.AreEqual(typeof(uint?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableUInt32ArrayAlias)); + } + + [Test] + public void ResolveNullableUInt64Array() + { + Assert.AreEqual(typeof(ulong?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableUInt64ArrayAlias)); + } + + [Test] + public void ResolveNullableBoolArray() + { + Assert.AreEqual(typeof(bool?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableBoolArrayAlias)); + } + + [Test] + public void ResolveNullableFloatArray() + { + Assert.AreEqual(typeof(float?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableFloatArrayAlias)); + } + + [Test] + public void ResolveNullableDoubleArray() + { + Assert.AreEqual(typeof(double?[]), + TypeRegistry.ResolveType(TypeRegistry.NullableDoubleArrayAlias)); + } +#endif + } + + internal class Foo + { + private Type bar; + + public Type Bar + { + get { return bar; } + set { bar = value; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeResolutionUtilsTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeResolutionUtilsTests.cs new file mode 100644 index 00000000..bcfa2a02 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeResolutionUtilsTests.cs @@ -0,0 +1,122 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using NUnit.Framework; + +using Spring.Objects; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Unit tests for the TypeResolutionUtils class. + /// + /// $Id: TypeResolutionUtilsTests.cs,v 1.2 2007/08/02 14:19:03 markpollack Exp $ + [TestFixture] + public sealed class TypeResolutionUtilsTests + { + [Test] + public void ResolveFromAssemblyQualifiedName() + { + Type testObjectType = TypeResolutionUtils.ResolveType("Spring.Objects.TestObject, Spring.Core.Tests"); + Assert.IsNotNull(testObjectType); + Assert.IsTrue(testObjectType.Equals(typeof (TestObject))); + } + + [Test] + [ExpectedException(typeof(TypeLoadException))] + public void ResolveFromBadAssemblyQualifiedName() + { + TypeResolutionUtils.ResolveType("Spring.Objects.TestObject, Spring.Core.FooTests"); + } + + [Test] + public void ResolveFromShortName() + { + Type testObjectType = TypeResolutionUtils.ResolveType("Spring.Objects.TestObject"); + Assert.IsNotNull(testObjectType); + Assert.IsTrue(testObjectType.Equals(typeof (TestObject))); + } + + [Test] + [ExpectedException(typeof(TypeLoadException))] + public void ResolveFromBadShortName() + { + TypeResolutionUtils.ResolveType("Spring.Objects.FooBarTestObject"); + } + + + + + [Test] + public void ResolveInterfaceArrayFromStringArray() + { + Type[] expected = new Type[] { typeof(IFoo) }; + string[] input = new string[] { typeof(IFoo).AssemblyQualifiedName }; + Type[] actual = TypeResolutionUtils.ResolveInterfaceArray(input); + Assert.IsNotNull(actual); + Assert.AreEqual(expected.Length, actual.Length); + Assert.AreEqual(expected[0], actual[0]); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ResolveInterfaceArrayFromStringArrayWithNonInterfaceTypes() + { + string[] input = new string[] { GetType().AssemblyQualifiedName }; + TypeResolutionUtils.ResolveInterfaceArray(input); + } + + [Test] + public void MethodMatch() + { + MethodInfo absquatulateMethod = typeof(TestObject).GetMethod("Absquatulate"); + Assert.IsTrue(TypeResolutionUtils.MethodMatch("*", absquatulateMethod), "Should match '*'"); + Assert.IsTrue(TypeResolutionUtils.MethodMatch("*tulate", absquatulateMethod), "Should match '*tulate'"); + Assert.IsTrue(TypeResolutionUtils.MethodMatch("Absqua*", absquatulateMethod), "Should match 'Absqua*'"); + Assert.IsTrue(TypeResolutionUtils.MethodMatch("*quatul*", absquatulateMethod), "Should match '*quatul*'"); + Assert.IsTrue(TypeResolutionUtils.MethodMatch("Absquatulate", absquatulateMethod), "Should match 'Absquatulate'"); + Assert.IsTrue(TypeResolutionUtils.MethodMatch("Absquatulate()", absquatulateMethod), "Should match 'Absquatulate()'"); + Assert.IsTrue(TypeResolutionUtils.MethodMatch("Absquatulate()", absquatulateMethod), "Should match 'Absquatulate()'"); + Assert.IsFalse(TypeResolutionUtils.MethodMatch("Absquatulate(string)", absquatulateMethod), "Should not match 'Absquatulate(string)'"); + + MethodInfo addPeriodicElementMethod = typeof(TestObject).GetMethod("AddPeriodicElement"); + Assert.IsTrue(TypeResolutionUtils.MethodMatch("AddPeriodicElement", addPeriodicElementMethod), "Should match 'AddPeriodicElement'"); + Assert.IsFalse(TypeResolutionUtils.MethodMatch("AddPeriodicElement()", addPeriodicElementMethod), "Should not match 'AddPeriodicElement()'"); + Assert.IsFalse(TypeResolutionUtils.MethodMatch("AddPeriodicElement(string)", addPeriodicElementMethod), "Should not match 'AddPeriodicElement(string)'"); + Assert.IsTrue(TypeResolutionUtils.MethodMatch("AddPeriodicElement(string, string)", addPeriodicElementMethod), "Should match 'AddPeriodicElement(string, string)'"); + } + + #region Helper classes + + internal interface IFoo + { + bool Spanglish(string foo, object[] args); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeResolverTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeResolverTests.cs new file mode 100644 index 00000000..b072338d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeResolverTests.cs @@ -0,0 +1,102 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Core.TypeResolution +{ + /// + /// Unit tests for the TypeResolver class. + /// + /// Rick Evans + /// Bruno Baia + /// $Id: TypeResolverTests.cs,v 1.1 2007/07/31 18:21:02 bbaia Exp $ + [TestFixture] + public class TypeResolverTests + { + protected virtual ITypeResolver GetTypeResolver() + { + return new TypeResolver(); + } + + [Test] + public void ResolveLocalAssemblyType() + { + Type t = GetTypeResolver().Resolve("Spring.Objects.TestObject"); + Assert.AreEqual(typeof (TestObject), t); + } + + [Test] + public void ResolveWithPartialAssemblyName() + { + Type t = GetTypeResolver().Resolve("System.Data.IDbConnection, System.Data"); + Assert.AreEqual(typeof (IDbConnection), t); + } + + /// + /// Tests that the resolve method throws the correct exception + /// when supplied a load of old rubbish as a type name. + /// + [Test] + [ExpectedException(typeof (TypeLoadException))] + public void ResolveWithNonExistentTypeName() + { + GetTypeResolver().Resolve("RaskolnikovsDilemma, System.StPetersburg"); + } + + [Test] + [ExpectedException(typeof(TypeLoadException))] + public void ResolveBadArgs() + { + GetTypeResolver().Resolve(null); + } + + [Test] + public void ResolveLocalAssemblyTypeWithFullAssemblyQualifiedName() + { + Type t = GetTypeResolver().Resolve(typeof(TestObject).AssemblyQualifiedName); + Assert.AreEqual(typeof (TestObject), t); + } + + [Test] + [ExpectedException(typeof(TypeLoadException))] + public void LoadTypeFromSystemAssemblySpecifyingOnlyTheAssemblyDisplayName() + { + string stringType = typeof(string).FullName + ", System"; + Type t = GetTypeResolver().Resolve(stringType); + Assert.AreEqual(typeof(string), t); + } + + [Test] + public void LoadTypeFromSystemAssemblySpecifyingTheFullAssemblyName() + { + string stringType = typeof(string).AssemblyQualifiedName; + Type t = GetTypeResolver().Resolve(stringType); + Assert.AreEqual(typeof(string), t); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Core/TypeResolution/aliasedObjects.xml b/test/Spring/Spring.Core.Tests/Core/TypeResolution/aliasedObjects.xml new file mode 100644 index 00000000..7c826c49 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Core/TypeResolution/aliasedObjects.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/CoreExceptionTests.cs b/test/Spring/Spring.Core.Tests/CoreExceptionTests.cs new file mode 100644 index 00000000..47d84c23 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/CoreExceptionTests.cs @@ -0,0 +1,45 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Reflection; +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring +{ + /// + /// Unit tests for all of the exception classes in the Spring.Core library... + /// + /// Rick Evans + /// $Id: CoreExceptionTests.cs,v 1.3 2007/01/03 06:27:43 aseovic Exp $ + [TestFixture] + public sealed class CoreExceptionTests : ExceptionsTest + { + [TestFixtureSetUp] + public void FixtureSetUp() + { + AssemblyToCheck = Assembly.GetAssembly(typeof (ObjectsException)); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/PathMatcher/EmptyPattern.test b/test/Spring/Spring.Core.Tests/Data/PathMatcher/EmptyPattern.test new file mode 100644 index 00000000..62820826 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/PathMatcher/EmptyPattern.test @@ -0,0 +1,14 @@ +--pattern-- + +--match-- +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/spaced dir/db/foo.db +c:/spring/service/deploy/app/db/backup/foo +c:/spring/service/deploy/app/ab/backup/foo +//server/share/service/deploy/app/db/backup/foo + +## Bug fix +--pattern-- +C:\Documents and Settings\fede\Desktop\Spring.Net\build\VS.Net\Spring.Services.WindowsService.Process\Debug\deploy\copia di cassini/** +--match-- +C:\Documents and Settings\fede\Desktop\Spring.Net\build\VS.Net\Spring.Services.WindowsService.Process\Debug\deploy\copia di cassini\service.xml diff --git a/test/Spring/Spring.Core.Tests/Data/PathMatcher/Examples.test b/test/Spring/Spring.Core.Tests/Data/PathMatcher/Examples.test new file mode 100644 index 00000000..a2863e6d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/PathMatcher/Examples.test @@ -0,0 +1,128 @@ +# + +## +## +## +## +## +## +## +## + +# +# +foo?bar.* +# +# +fooAbar.txt +foo1bar.txt +foo_bar.txt +foo-bar.txt +# +# +foo.bar.txt +foo/bar.txt +foo\bar.txt +# +# + +# +# +*.* +# +# +foo.db +.db +foo +foo.bar.db +foo.db.db +db.db.db +# +# +c:/ +c:/foo.db +c:/foo +c:/.db +c:/foo.foo.db +//server/foo +# +# + +# +# +spring/foo.bar +# +# +c:\spring\foo.bar +c:/spring\foo.bar +c:/spring/foo.bar +/spring/foo.bar +\spring\foo.bar +# +# +# +# + +# +# +**/db/**/*.DB +# +# +c:/spring/service/deploy/app/db/foo.DB +# +# +c:/spring/service/deploy/app/DB/foo.DB +c:spring/service/deploy/app/spaced dir/DB/foo.DB +//server/share/service/deploy/app/DB/backup/foo.db +# +# + +# +# +**/bin/**/tmp/** +# +# +c:/spring/foo/bin/bar/tmp/a +c:/spring/foo/bin/tmp/a/b.c +# +# +c:/spring/foo/bin/bar/temp/a +c:/tmp/foo/bin/bar/a/b.c +# +# + +# +# +**/db/** +# +# +/db +//server/db +c:/db +c:/spring/app/db/foo.db +//Program Files/App/spaced dir/db/foo.db +/home/spring/spaced dir/db/v1/foo.db +# +# +c:/spring/app/db-v1/foo.db +/home/spring/spaced dir/db-v1/foo.db +# +# + +# +# +**/.spring-assemblies*/** +# +# +c:/.spring-assemblies +c:/.spring-assembliesabcd73xs +c:/app/.spring-assembliesabcd73xs +c:/app/.spring-assembliesabcd73xs/foo.dll +//server/app/.spring-assembliesabcd73xs +# +# +c:/app/.spring-assemblie +# +# + +# diff --git a/test/Spring/Spring.Core.Tests/Data/PathMatcher/InBetween.test b/test/Spring/Spring.Core.Tests/Data/PathMatcher/InBetween.test new file mode 100644 index 00000000..2cb6ba3d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/PathMatcher/InBetween.test @@ -0,0 +1,64 @@ +# +# +--pattern-- +# +**/db/**/*.db +# +--match-- +# +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/spaced dir/db/foo.db +# +--dont-match-- +# +c:/spring/service/deploy/app/db/backup/foo +c:/spring/service/deploy/app/ab/backup/foo +//server/share/service/deploy/app/db/backup/foo +# +# + +--pattern-- +**/app/db/**/*.db +--match-- +c:/spring/service/deploy/app/db/foo.db +--dont-match-- +c:/spring/service/deploy/app/spaced dir/db/foo.db +c:/spring/service/deploy/app/db/backup/foo +c:/spring/service/deploy/app/ab/backup/foo +//server/share/service/deploy/app/db/backup/foo + +--pattern-- +**/app/db/** +--match-- +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/db/backup/foo +//server/share/service/deploy/app/db/backup/foo +c:/spring/service/deploy/app/db +--dont-match-- +c:/spring/service/deploy/app/spaced dir/db/foo.db +c:/spring/service/deploy/app/ab/backup/foo +c:/spring/service/deploy/application/spaced dir/db/foo.db + +--pattern-- +**/*b/** +--match-- +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/db/backup/foo +//server/share/service/deploy/app/db/backup/foo +c:/spring/service/deploy/app/spaced dir/db/foo.db +c:/spring/service/deploy/app/ab/backup/foo +c:/spring/service/deploy/app/db + +--pattern-- +**/app/*b/** +--match-- +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/db/backup/foo +//server/share/service/deploy/app/db/backup/foo +c:/spring/service/deploy/app/ab/backup/foo +c:/spring/service/deploy/app/db +--dont-match-- +c:/spring/service/deploy/app/spaced dir/db/foo.db +c:/spring/service/deploy/application/spaced dir/db/foo.db + +# \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/PathMatcher/MatchAll.test b/test/Spring/Spring.Core.Tests/Data/PathMatcher/MatchAll.test new file mode 100644 index 00000000..37d17c1d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/PathMatcher/MatchAll.test @@ -0,0 +1,20 @@ +--pattern-- +**/** +--match-- +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/db/backup/foo +//server/share/service/deploy/app/db/backup/foo + +--pattern-- +**/**/** +--match-- +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/db/backup/foo +//server/share/service/deploy/app/db/backup/foo + +--pattern-- +**/*.* +--match-- +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/db/backup/foo +//server/share/service/deploy/app/db/backup/foo diff --git a/test/Spring/Spring.Core.Tests/Data/PathMatcher/PatternsAreCaseSensitive.test b/test/Spring/Spring.Core.Tests/Data/PathMatcher/PatternsAreCaseSensitive.test new file mode 100644 index 00000000..3027f242 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/PathMatcher/PatternsAreCaseSensitive.test @@ -0,0 +1,30 @@ +--pattern-- +**/DB/**/*.db +--match-- +c:/spring/service/deploy/app/DB/foo.db +c:/spring/service/deploy/app/spaced dir/DB/foo.db +--dont-match-- +c:/spring/service/deploy/app/db/foo.DB +c:/spring/service/deploy/app/DB/foo.DB +c:/spring/service/deploy/app/db/backup/foo +//server/share/service/deploy/app/db/backup/foo + +--pattern-- +**/db/**/*.DB +--match-- +c:/spring/service/deploy/app/db/foo.DB +--dont-match-- +c:/spring/service/deploy/app/DB/foo.DB +c:/spring/service/deploy/app/DB/backup/foo +c:/spring/service/deploy/app/spaced dir/DB/foo.DB +//server/share/service/deploy/app/DB/backup/foo + +--pattern-- +**/DB/**/*.DB +--match-- +c:/spring/service/deploy/app/DB/foo.DB +c:/spring/service/deploy/app/spaced dir/DB/foo.DB +--dont-match-- +c:/spring/service/deploy/app/db/foo.DB +c:/spring/service/deploy/app/DB/backup/foo +//server/share/service/deploy/app/DB/backup/foo diff --git a/test/Spring/Spring.Core.Tests/Data/PathMatcher/SingleCaracters.test b/test/Spring/Spring.Core.Tests/Data/PathMatcher/SingleCaracters.test new file mode 100644 index 00000000..eb362d3f --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/PathMatcher/SingleCaracters.test @@ -0,0 +1,21 @@ +# +# this is a comment line for the test +# +--pattern-- +**/a??/db/** +--match-- +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/db/backup/foo +//server/share/service/deploy/app/db/backup/foo +--dont-match-- +c:/spring/service/deploy/app/spaced dir/db/foo.db +c:/spring/service/deploy/app/ab/backup/foo +c:/spring/service/deploy/application/ab/backup/foo + +--pattern-- +c:\fo?\*.bat +--match-- +c:/fo1/autoexec.bat +--dont-match-- +c:/fo/autoexec.bat +c:/foob/autoexec.bat \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/PathMatcher/SlurpPrependedDirectories.test b/test/Spring/Spring.Core.Tests/Data/PathMatcher/SlurpPrependedDirectories.test new file mode 100644 index 00000000..08fd8199 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/PathMatcher/SlurpPrependedDirectories.test @@ -0,0 +1,94 @@ +--pattern-- +**/db/** +--match-- +/db +//server/db +c:/db +c:/spring/service/deploy/app/db/foo +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/spaced dir/db +c:/spring/service/deploy/app/db/backup/foo +--dont-match-- +c:/spring/service/deploy/app/adb/foo.db +c:/spring/service/deploy/app/dba/foo.db +c:/spring/service/deploy/app/adba/foo.db + +--pattern-- +c:/bin/**/*.* +--match-- +c:/bin/foo/foo.log +--dont-match-- +c:/bar/bin/foo.log + +--pattern-- +c:/**/bin/**/*.* +--match-- +c:/bin/foo/foo.log +c:/bar/bin/foo.log + +--pattern-- +**/db*/** +--match-- +/db +//server/db +c:/db +c:/spring/service/deploy/app/db/foo +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/spaced dir/db +c:/spring/service/deploy/app/db/backup/foo +c:/spring/service/deploy/app/dba/foo.db +--dont-match-- +c:/spring/service/deploy/app/adba/foo.db +c:/spring/service/deploy/app/adb/foo.db + +--pattern-- +**/*db*/** +--match-- +/db +//server/db +c:/db +c:/spring/service/deploy/app/db/foo +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/spaced dir/db +c:/spring/service/deploy/app/db/backup/foo +c:/spring/service/deploy/app/dba/foo.db +c:/spring/service/deploy/app/adba/foo.db +c:/spring/service/deploy/app/adb/foo.db +--dont-match-- +x:/qwrty/zxpv + +--pattern-- +**/db/foo +--match-- +c:/spring/service/deploy/app/db/foo +--dont-match-- +c:/spring/service/deploy/app/db/foo.db +c:/spring/service/deploy/app/spaced dir/db/foo.db +c:/spring/service/deploy/app/db/backup/foo +//server/share/service/deploy/app/db/backup/foo + +--pattern-- +**/*.log +--match-- +c:/spring/service/deploy/app/db/foo.log +c:/foo.log +c:/foolog.log +c:/foo.bar.log +c:/foo.log.log +--dont-match-- +c:/foo.db +c:/foo.log.db + +--pattern-- +**/fo?.log +--match-- +c:/spring/service/deploy/app/db/foo.log +c:/foo.log +--dont-match-- +c:/foo.db +c:/foo.log.db +c:/fo.log +c:/orfo.log +c:/foolog.log +c:/foo.bar.log +c:/foo.log.log diff --git a/test/Spring/Spring.Core.Tests/Data/PathMatcher/SpringAssembliesPrefix.test b/test/Spring/Spring.Core.Tests/Data/PathMatcher/SpringAssembliesPrefix.test new file mode 100644 index 00000000..9bf763af --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/PathMatcher/SpringAssembliesPrefix.test @@ -0,0 +1,11 @@ +--pattern-- +**/.spring-assemblies*/** +--match-- +c:/.spring-assembliesabcd73xs +c:/app/.spring-assembliesabcd73xs +c:/app/.spring-assembliesabcd73xs/foo.dll +//server/app/.spring-assembliesabcd73xs +--dont-match-- +c:/spring/service/deploy/app/db/backup/foo +c:/spring/service/deploy/app/ab/backup/foo +//server/share/service/deploy/app/db/backup/foo diff --git a/test/Spring/Spring.Core.Tests/Data/PathMatcher/ThisDirectory.test b/test/Spring/Spring.Core.Tests/Data/PathMatcher/ThisDirectory.test new file mode 100644 index 00000000..b754fa77 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/PathMatcher/ThisDirectory.test @@ -0,0 +1,39 @@ +--pattern-- +*.* +--match-- +foo.db +.db +foo +foo.bar.db +foo.db.db +db.db.db +--dont-match-- +c:/foo.db +c:/foo +c:/.db +c:/foo.foo.db +c:/ +d:/spring/service/deploy/app/db/foo.db +d:/spring/service/deploy/app/spaced dir/db/foo.db +d:/spring/service/deploy/app/db/backup/foo +d:/spring/service/deploy/app/ab/backup/foo +//server/share/service/deploy/app/db/backup/foo + + +--pattern-- +c:/*.db +--match-- +c:/foo.db +c:/foo.foo.db +c:/.db +c:/.foo.db +--dont-match-- +c:/ +c:/foo.db.foo +c:/.db.foo +c:/foo/bar.db +d:/spring/service/deploy/app/db/foo.db +d:/spring/service/deploy/app/spaced dir/db/foo.db +d:/spring/service/deploy/app/db/backup/foo +d:/spring/service/deploy/app/ab/backup/foo +//server/share/service/deploy/app/db/backup/foo diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Context/Support/SPRNET-192.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Context/Support/SPRNET-192.xml new file mode 100644 index 00000000..035714e7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Context/Support/SPRNET-192.xml @@ -0,0 +1,17 @@ + + + + + ${log} + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/AnotherDaoConfig.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/AnotherDaoConfig.xml new file mode 100644 index 00000000..ad89d3d6 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/AnotherDaoConfig.xml @@ -0,0 +1,11 @@ + + + +
    + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/DaoConfig.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/DaoConfig.xml new file mode 100644 index 00000000..2d9a1861 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/DaoConfig.xml @@ -0,0 +1,11 @@ + + + +
    + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/DatabaseConfig.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/DatabaseConfig.xml new file mode 100644 index 00000000..e519cf1e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/DatabaseConfig.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPC-SPRNET-55.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPC-SPRNET-55.xml new file mode 100644 index 00000000..6fec83b4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPC-SPRNET-55.xml @@ -0,0 +1,57 @@ + + + + + + ${connection.string} + + + + + + + + + + + + + config:// + file://Spring/Objects/Factory/Config/DaoConfig.xml + file://Spring/Objects/Factory/Config/AnotherDaoConfig.xml + + + + + + + + + + + + + config:// + file://Spring/Objects/Factory/Config/DaoConfig.xml + file://Spring/Objects/Factory/Config/AnotherDaoConfig.xml + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPCTwoLocationsOneSectionTests.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPCTwoLocationsOneSectionTests.xml new file mode 100644 index 00000000..56c7a129 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPCTwoLocationsOneSectionTests.xml @@ -0,0 +1,46 @@ + + + + + + + ${maxResults} + + + + + + ${connection.string} + + + + + + + config:// + file://Spring/Objects/Factory/Config/DaoConfig.xml + + + + DaoConfiguration + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPCTwoLocationsTwoSectionsTests.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPCTwoLocationsTwoSectionsTests.xml new file mode 100644 index 00000000..b107cca2 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPCTwoLocationsTwoSectionsTests.xml @@ -0,0 +1,45 @@ + + + + + + + ${maxResults} + + + + + + ${connection.string} + + + + + + + config:// + file://Spring/Objects/Factory/Config/DatabaseConfig.xml + + + + DaoConfiguration,DatabaseConfiguration + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPCWithTypesTests.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPCWithTypesTests.xml new file mode 100644 index 00000000..731cdff2 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PPCWithTypesTests.xml @@ -0,0 +1,52 @@ + + + + + + + + Gribouille + Blutch + + + + + + + + + + + mine + your + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PropertyPlaceholderConfigurerTests.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PropertyPlaceholderConfigurerTests.xml new file mode 100644 index 00000000..ffbe0665 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PropertyPlaceholderConfigurerTests.xml @@ -0,0 +1,39 @@ + + + + + + + ${maxResults} + + + + + + ${connection.string} + + + + + + DaoConfiguration,DatabaseConfiguration + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PropertyResourceConfigurerTests.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PropertyResourceConfigurerTests.xml new file mode 100644 index 00000000..0b687a98 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/PropertyResourceConfigurerTests.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + A ${DefaultName} + + + + + + Start Name + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/TypeAliases.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/TypeAliases.xml new file mode 100644 index 00000000..6f905130 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/TypeAliases.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/one.properties b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/one.properties new file mode 100644 index 00000000..e9414362 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/one.properties @@ -0,0 +1,2 @@ +name=Aleks Seovic +age=32 \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/two.properties b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/two.properties new file mode 100644 index 00000000..a32a1994 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Config/two.properties @@ -0,0 +1,2 @@ +name=Aleksandar Seovic +family=Marija,Ana,Nadja \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/Resources/resource-imports.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/Resources/resource-imports.xml new file mode 100644 index 00000000..a83af33e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/Resources/resource-imports.xml @@ -0,0 +1,11 @@ + + + + + + Rick + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/abstract.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/abstract.xml new file mode 100644 index 00000000..815d854b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/abstract.xml @@ -0,0 +1,6 @@ + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/autowire.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/autowire.xml new file mode 100644 index 00000000..14fdf8fa --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/autowire.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /Spring.Objects/Factory.Xmlcollections.xml + + + + + + + /Spring.Objects/Factory.Xmlconstructor-arg.xml + /Spring.Objects/Factory.Xmlinitializers.xml + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/bad-external-resources.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/bad-external-resources.xml new file mode 100644 index 00000000..a2aa53b4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/bad-external-resources.xml @@ -0,0 +1,13 @@ + + + + + + + + Summat + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/bad-named-constructor-arg.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/bad-named-constructor-arg.xml new file mode 100644 index 00000000..2e03c6aa --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/bad-named-constructor-arg.xml @@ -0,0 +1,15 @@ + + + + + + Isaac Newton + + + 87 + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/child.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/child.xml new file mode 100644 index 00000000..80fd52f7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/child.xml @@ -0,0 +1,53 @@ + + + + + + + override + + + + + + + prototypeOverridesInheritedSingleton + + + + + + + prototype-override + + + + + + + prototype-override + + + + + + + overrideParentObject + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/classnotfound.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/classnotfound.xml new file mode 100644 index 00000000..4b43cd02 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/classnotfound.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collections.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collections.xml new file mode 100644 index 00000000..b0ff4f72 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collections.xml @@ -0,0 +1,549 @@ + + + + Tests for list and map handling in an XML object definition file. + + + + + + + rick + + Rick Evans + + + + uncleelvis + + Elvis Orten + + + + + + + + + Rick Evans + + + Elvis Orten + + + + + + + + + + + + Rick Evans + + + + + + Elvis Orten + + + + + + + I have no properties and I'm happy without them. + + + + + + + 1 + + + 12 + + + + + + + Mark + + + 35 + + + + Dell + IBM T41 + + + + + + Aleks + + + 30 + + + + Nadja + + + + + + Jenny + + + 30 + + + + + + + + Simple object, without any collections. + + + The name of the user + David + + + 27 + + + + + Rod + + + 32 + + + List of Rod's friends + + + + + + + + + Jenny + + + 30 + + + + + + + + David + + + 27 + + + + + Rod + + + 32 + + + + + + + + + + + loner + + + 26 + + + + + + + + + + + + literal + + + + + + + + + + + + + + + + + + + + + + + verbose + + + + + + + + + + + + + + + aliased + + + + + aliased + + + + + aliased + + + + + + + + + + + + + + + + + bar + + + fum + + + + + + + + + + + + bar + + + + + + + + + + + + + + + bar + + + + + + + + + + + + bar + + + + + + + zero + + + bar + + + + + + + + ba + + + + + + + + bar + + + + + + + + + + + + + + + + + + bar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + one + + + + + + + + System.String + System.Exception + + + + + + + bar + jenny + + + + Spring.Collections.LinkedList + + + + + + bar + jenny + + + + Spring.Collections.LinkedList + + + true + + + + + + bar + jenny + + + + Spring.Collections.HybridSet + + + + + + bar + jenny + + + + Spring.Collections.HybridSet + + + true + + + + + + + bar + + + jenny + + + + + System.Collections.Hashtable + + + + + + + bar + + + jenny + + + + + System.Collections.Hashtable + + + true + + + + + + + + + + + + + + + + + + + + Rick Evans + 30 + + + Uncle Elvis + 47 + + + + + + + 1 + 1 + date('1974-8-24').Month + 'Aleksandar Seovic'.ToUpper() + DateTime.Today > date('1974-8-24') + + + + + + 1 + 1 + + + date('1974-8-24').Month + + + 'Aleksandar Seovic'.ToUpper() + + + DateTime.Today > date('1974-8-24') + + + + + + + + 1 + 1 + date('1974-8-24').Month + 'Aleksandar Seovic'.ToUpper() + DateTime.Today > date('1974-8-24') + + + + + + 1 + 1 + + + date('1974-8-24').Month + + + 'Aleksandar Seovic'.ToUpper() + + + DateTime.Today > date('1974-8-24') + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/constructor-arg.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/constructor-arg.xml new file mode 100644 index 00000000..cc909e98 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/constructor-arg.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bird + + + + + + + wife + + + + mmm + 99 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/default-autowire.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/default-autowire.xml new file mode 100644 index 00000000..da455470 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/default-autowire.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + Kerry + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/default-lazy-init.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/default-lazy-init.xml new file mode 100644 index 00000000..ffd66205 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/default-lazy-init.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/dependenciesmaterializethis.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/dependenciesmaterializethis.xml new file mode 100644 index 00000000..b69aba9a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/dependenciesmaterializethis.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/enums.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/enums.xml new file mode 100644 index 00000000..23fa1a72 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/enums.xml @@ -0,0 +1,23 @@ + + + + + + + Rod + + + Create + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/event-wiring-prototypes.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/event-wiring-prototypes.xml new file mode 100644 index 00000000..c70b4b92 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/event-wiring-prototypes.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/event-wiring.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/event-wiring.xml new file mode 100644 index 00000000..aa22b70b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/event-wiring.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/expressions.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/expressions.xml new file mode 100644 index 00000000..55dbe364 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/expressions.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/external-resources.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/external-resources.xml new file mode 100644 index 00000000..8f4db0bf --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/external-resources.xml @@ -0,0 +1,10 @@ + + + + + + Jenny + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/factory-methods.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/factory-methods.xml new file mode 100644 index 00000000..2335252b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/factory-methods.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/factorycircle.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/factorycircle.xml new file mode 100644 index 00000000..1cb38d5b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/factorycircle.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/field-props-factory.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/field-props-factory.xml new file mode 100644 index 00000000..519bbfd5 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/field-props-factory.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + System.Globalization.CultureInfo.CurrentUICulture, Mscorlib + + + + + + + + + + + + + + DefauLT + + + + + + + + + + + + System.Type, Mscorlib + + + EmPTytypeS + + + + + + + + + + + + + + MyDefaultCulture + + + + + + + + + + + + System.Globalization.CultureInfo.LittleBobbyBoJangles, Mscorlib + + + + + + + + + + + System.Type.Foo, Mscorlib + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/initializers.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/initializers.xml new file mode 100644 index 00000000..b3e4ba81 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/initializers.xml @@ -0,0 +1,41 @@ + + + + + + + 7 + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/invalid-factory.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/invalid-factory.xml new file mode 100644 index 00000000..0562f3e8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/invalid-factory.xml @@ -0,0 +1,13 @@ + + + + + + + false + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/invalid.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/invalid.xml new file mode 100644 index 00000000..69245f80 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/invalid.xml @@ -0,0 +1,25 @@ + + + + + + + + Jenny + + + 30 + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/invoke-factory.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/invoke-factory.xml new file mode 100644 index 00000000..4110be9c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/invoke-factory.xml @@ -0,0 +1,19 @@ + + + + + + System.Globalization.CultureInfo, Mscorlib + + + InstalledUICulture + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/lazy-init-multithreaded.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/lazy-init-multithreaded.xml new file mode 100644 index 00000000..cb51e834 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/lazy-init-multithreaded.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/locale.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/locale.xml new file mode 100644 index 00000000..0942242f --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/locale.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/no-objects.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/no-objects.xml new file mode 100644 index 00000000..68baf049 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/no-objects.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/notfullyspecified.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/notfullyspecified.xml new file mode 100644 index 00000000..96307ff9 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/notfullyspecified.xml @@ -0,0 +1,13 @@ + + + + + + + + Provider=SQLOLEDB + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/parent.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/parent.xml new file mode 100644 index 00000000..82ab464c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/parent.xml @@ -0,0 +1,27 @@ + + + + + + + parent + 1 + + + + parent + 1 + + + + parent + 2 + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/reftypes.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/reftypes.xml new file mode 100644 index 00000000..9f4c4b71 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/reftypes.xml @@ -0,0 +1,185 @@ + + + + + + + Jenny + + + 30 + + + + + + + + + + + + 27 + + + + + + + + + Andrew + + + 36 + + + + + + + + + Emma + + + 31 + + + + + + + + + Georgia + + + 33 + + + + + + + + + ego + + + 1 + + + + + + + + + hasInner + + + 5 + + + + + inner1 + + + 6 + + + + + + + + inner2 + + + 7 + + + + + + + + + + + inner3 + + + 8 + + + + + + + + + + hasInner + + + 5 + + + + + inner1 + + + 6 + + + + + + + + inner2 + + + 7 + + + + + + innerFriendOfAFriend + + + 7 + + + + + + + + + + + + + + inner3 + + + 8 + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/resource.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/resource.xml new file mode 100644 index 00000000..2750ff7e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/resource.xml @@ -0,0 +1,20 @@ + + + + + + file:///temp/spring-test.properties + + + file:///temp/spring-test.properties + + + + + file:///temp/spring-test.properties + + + file:///temp/spring-test.properties + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/satisfiedalldependencycheck.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/satisfiedalldependencycheck.xml new file mode 100644 index 00000000..5fcd9bba --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/satisfiedalldependencycheck.xml @@ -0,0 +1,23 @@ + + + + + + + + + + 33 + + + Rod + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/satisfiedobjectdependencycheck.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/satisfiedobjectdependencycheck.xml new file mode 100644 index 00000000..78b77126 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/satisfiedobjectdependencycheck.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/satisfiedsimpledependencycheck.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/satisfiedsimpledependencycheck.xml new file mode 100644 index 00000000..57c7b142 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/satisfiedsimpledependencycheck.xml @@ -0,0 +1,18 @@ + + + + + + + 33 + + + Rod + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/schema-validation.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/schema-validation.xml new file mode 100644 index 00000000..1abbfb88 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/schema-validation.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/test.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/test.xml new file mode 100644 index 00000000..a17a8609 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/test.xml @@ -0,0 +1,112 @@ + + + + + + + Rod + + + 31 + + + + + + + + + Roderick + + + + + + + Kerry + + + 34 + + + + + + + + + Kathy + + + 28 + + + + + + + + + typeMismatch + + + 34x + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + listenerVeto + + + 66 + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedAllDependencyCheckMissingSimple.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedAllDependencyCheckMissingSimple.xml new file mode 100644 index 00000000..0845836c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedAllDependencyCheckMissingSimple.xml @@ -0,0 +1,19 @@ + + + + + + + tony + + + --> + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedalldependencycheckmissingobjects.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedalldependencycheckmissingobjects.xml new file mode 100644 index 00000000..fa622191 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedalldependencycheckmissingobjects.xml @@ -0,0 +1,19 @@ + + + + + + + 33 + + + Rod + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedobjectdependencycheck.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedobjectdependencycheck.xml new file mode 100644 index 00000000..8c076799 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedobjectdependencycheck.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedsimpledependencycheck.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedsimpledependencycheck.xml new file mode 100644 index 00000000..aa9b87b7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/unsatisfiedsimpledependencycheck.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/wellformed-but-bad.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/wellformed-but-bad.xml new file mode 100644 index 00000000..1b469413 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/wellformed-but-bad.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/concurrent.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/concurrent.xml new file mode 100644 index 00000000..48863bbf --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/concurrent.xml @@ -0,0 +1,21 @@ + + + + + + + 2004/08/08 + + + + + 2000/02/02 + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/leaf.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/leaf.xml new file mode 100644 index 00000000..2c48f507 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/leaf.xml @@ -0,0 +1,18 @@ + + + + + + + custom + + + 25 + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/middle.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/middle.xml new file mode 100644 index 00000000..c817458c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/middle.xml @@ -0,0 +1,21 @@ + + + + + + + + custom + + + 666 + + + + + diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/root.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/root.xml new file mode 100644 index 00000000..8cdb7ee8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/root.xml @@ -0,0 +1,22 @@ + + + + + + + + custom + + + 25 + + + + + + false + + + diff --git a/test/Spring/Spring.Core.Tests/DataBinding/BaseBindingManagerTests.cs b/test/Spring/Spring.Core.Tests/DataBinding/BaseBindingManagerTests.cs new file mode 100644 index 00000000..af7c08ed --- /dev/null +++ b/test/Spring/Spring.Core.Tests/DataBinding/BaseBindingManagerTests.cs @@ -0,0 +1,326 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +using NUnit.Framework; +using Spring.Core; +using Spring.Globalization.Formatters; +using Spring.Objects; +using Spring.Validation; + +namespace Spring.DataBinding +{ + /// + /// Test cases for the BaseBindingManager. + /// + /// Aleksandar Seovic + /// $Id: BaseBindingManagerTests.cs,v 1.6 2008/03/20 14:02:47 oakinger Exp $ + [TestFixture] + public class BaseBindingManagerTests + { + private BaseBindingManager mgr; + + [TestFixtureSetUp] + public void SetUp() + { + mgr = new BaseBindingManager(); + mgr.AddBinding("['name']", "Name"); + mgr.AddBinding("['dob']", "DOB"); +#if NET_2_0 // test nullable type + mgr.AddBinding("['dateofgraduation']", "DateOfGraduation"); +#endif + mgr.AddBinding("['inventions']", "Inventions"); + mgr.AddBinding("['cityOfBirth']", "PlaceOfBirth.City"); + mgr.AddBinding("['countryOfBirth']", "PlaceOfBirth.Country"); + } + + [Test] + public void WithoutTypeConversion() + { + Hashtable source = new Hashtable(); + Inventor target = new Inventor(); + + source["name"] = "Nikola Tesla"; + source["dob"] = new DateTime(1856, 7, 9); +#if NET_2_0 + // I know this is pupin's graduation year but I need a date here + source["dateofgraduation"] = new DateTime(1883,1,1); +#endif + source["inventions"] = new string[] {"One", "Two"}; + + mgr.BindSourceToTarget(source, target, null); + + Assert.IsTrue(mgr.HasBindings); + Assert.AreEqual("Nikola Tesla", target.Name); + Assert.AreEqual(new DateTime(1856, 7, 9), target.DOB); + Assert.AreEqual(new string[] {"One", "Two"}, target.Inventions); + Assert.IsNull(target.PlaceOfBirth.City); + Assert.IsNull(target.PlaceOfBirth.Country); + + target.Name = "Tesla, Nikola"; + target.DOB = DateTime.Today; + target.PlaceOfBirth.City = "Smiljan"; + target.PlaceOfBirth.Country = "Lika"; + + mgr.BindTargetToSource(source, target, null); + Assert.AreEqual("Tesla, Nikola", source["name"]); + Assert.AreEqual(DateTime.Today, source["dob"]); + Assert.AreEqual("One", ((string[]) source["inventions"])[0]); + Assert.AreEqual("Smiljan", source["cityOfBirth"]); + Assert.AreEqual("Lika", source["countryOfBirth"]); + } + + [Test] + public void WithTypeConversion() + { + Hashtable source = new Hashtable(); + Inventor target = new Inventor(); + + source["name"] = "Nikola Tesla"; + source["dob"] = "1856-7-9"; + source["inventions"] = "One,Two"; +#if NET_2_0 + // I know this is pupin's graduation year but I need a date here + source["dateofgraduation"] = "1883-1-1"; +#endif + + mgr.BindSourceToTarget(source, target, null); + + Assert.IsTrue(mgr.HasBindings); + Assert.AreEqual("Nikola Tesla", target.Name); + Assert.AreEqual(new DateTime(1856, 7, 9), target.DOB); + Assert.AreEqual(new string[] {"One", "Two"}, target.Inventions); + Assert.IsNull(target.PlaceOfBirth.City); + Assert.IsNull(target.PlaceOfBirth.Country); + + target.Name = "Tesla, Nikola"; + target.DOB = DateTime.Today; + target.PlaceOfBirth.City = "Smiljan"; + target.PlaceOfBirth.Country = "Lika"; + + mgr.BindTargetToSource(source, target, null); + Assert.AreEqual("Tesla, Nikola", source["name"]); + Assert.AreEqual(DateTime.Today, source["dob"]); + Assert.AreEqual("One", ((string[]) source["inventions"])[0]); + Assert.AreEqual("Smiljan", source["cityOfBirth"]); + Assert.AreEqual("Lika", source["countryOfBirth"]); + + } + +#if NET_2_0 + [Test] + public void BindNullValues() + { + Hashtable source; + Inventor target; + + target = new Inventor(); + source = new Hashtable(); + + // this is legal (dog is nullable) + BaseBindingManager mgr = new BaseBindingManager(); + mgr.AddBinding("['dateofgraduation']", "DateOfGraduation"); + + source["dateofgraduation"] = null; + target.DateOfGraduation = DateTime.Now; + mgr.BindSourceToTarget(source, target, null); + Assert.IsNull(target.DateOfGraduation); + + source["dateofgraduation"] = DateTime.Now; + mgr.BindTargetToSource(source, target, null); + Assert.IsNull(source["dateofgraduation"]); + } + + [Test] + public void BindNullValuesWithFormatter() + { + Hashtable source; + Inventor target; + + target = new Inventor(); + source = new Hashtable(); + + // this is legal (dog is nullable) + BaseBindingManager mgr = new BaseBindingManager(); + mgr.AddBinding("['dateofgraduation']", "DateOfGraduation", new HasTextFilteringFormatter(null, null)); + + source["dateofgraduation"] = string.Empty; + target.DateOfGraduation = DateTime.Now; + mgr.BindSourceToTarget(source, target, null); + Assert.IsNull(target.DateOfGraduation); + } +#endif + + [Test] + public void UnhandledTypeConversionExceptionSourceToTarget() + { + BaseBindingManager dbm = new BaseBindingManager(); + Hashtable source = new Hashtable(); + source["boolValue"] = false; + Inventor target = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + + dbm.AddBinding("['boolValue']", "DOB"); + + try + { + dbm.BindSourceToTarget(source, target, null); + Assert.Fail("Binding boolean to date should throw an exception."); + } + catch (TypeMismatchException) + {} + + // make sure that the old value doesn't override current invalid value + dbm.BindTargetToSource(source, target, null); + Assert.AreEqual(false, source["boolValue"]); + } + + [Test] + public void UnhandledTypeConversionExceptionTargetToSource() + { + BaseBindingManager dbm = new BaseBindingManager(); + Inventor st = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + st.Inventions = new string[] {"Invention One", "Invention Two"}; + + dbm.AddBinding("pob", "DOB"); + + try + { + dbm.BindTargetToSource(st, st, null); + Assert.Fail("Binding date to custom Place type should throw an exception."); + } + catch (TypeMismatchException) + {} + + // make sure that the old value doesn't override current invalid value + dbm.BindSourceToTarget(st, st, null); + Assert.AreEqual(new DateTime(1856, 7, 9), st.DOB); + } + + [Test] + public void HandledTypeConversionExceptionSourceToTarget() + { + BaseBindingManager dbm = new BaseBindingManager(); + IValidationErrors errors = new ValidationErrors(); + Hashtable source = new Hashtable(); + source["boolValue"] = false; + Inventor target = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + + SimpleExpressionBinding binding = new SimpleExpressionBinding("['boolValue']", "DOB"); + binding.SetErrorMessage("error", "errors"); + dbm.AddBinding(binding); + + dbm.BindSourceToTarget(source, target, errors); + Assert.IsFalse(binding.IsValid); + Assert.IsFalse(errors.IsEmpty); + Assert.AreEqual(1, errors.GetErrors("errors").Count); + + // make sure that the old value doesn't override current invalid value + dbm.BindTargetToSource(source, target, errors); + Assert.AreEqual(false, source["boolValue"]); + } + + [Test] + public void HandledTypeConversionExceptionTargetToSource() + { + BaseBindingManager dbm = new BaseBindingManager(); + IValidationErrors errors = new ValidationErrors(); + Inventor st = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + st.Inventions = new string[] {"Invention One", "Invention Two"}; + + SimpleExpressionBinding binding = new SimpleExpressionBinding("pob", "DOB"); + binding.SetErrorMessage("error", "errors"); + dbm.AddBinding(binding); + + dbm.BindTargetToSource(st, st, errors); + Assert.IsFalse(binding.IsValid); + Assert.IsFalse(errors.IsEmpty); + Assert.AreEqual(1, errors.GetErrors("errors").Count); + + // make sure that the old value doesn't override current invalid value + dbm.BindSourceToTarget(st, st, errors); + Assert.AreEqual(new DateTime(1856, 7, 9), st.DOB); + } + + [Test] + public void DirectionSourceToTarget() + { + BaseBindingManager dbm = new BaseBindingManager(); + Inventor source = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + Hashtable target = new Hashtable(); + + dbm.AddBinding("Name", "['name']", BindingDirection.SourceToTarget); + dbm.BindSourceToTarget(source, target, null); + Assert.AreEqual("Nikola Tesla", target["name"]); + + target["name"] = "Mihajlo Pupin"; + dbm.BindTargetToSource(source, target, null); + Assert.AreEqual("Nikola Tesla", source.Name); + } + + [Test] + public void DirectionTargetToSource() + { + BaseBindingManager dbm = new BaseBindingManager(); + Inventor source = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + Hashtable target = new Hashtable(); + + dbm.AddBinding("Name", "['name']", BindingDirection.TargetToSource); + dbm.BindSourceToTarget(source, target, null); + Assert.IsNull(target["name"]); + + target["name"] = "Mihajlo Pupin"; + dbm.BindTargetToSource(source, target, null); + Assert.AreEqual("Mihajlo Pupin", source.Name); + } + +// [Test] +// public void ResetBindingStates() +// { +// BaseBindingManager dbm = new BaseBindingManager(); +// Inventor source = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); +// Hashtable target = new Hashtable(); +// +// dbm.AddBinding("Name", "['name']"); +// dbm.AddBinding("WrongProperty", "['wrongproperty']"); +// +// try +// { +// dbm.BindSourceToTarget(source, target, null); +// throw new AssertionException("should throw InvalidPropertyException"); +// } catch(Spring.Objects.InvalidPropertyException) {} // ok +// +// // will ignore previously failed bindings - bindingState is false +// dbm.BindSourceToTarget(source, target, null); +// +// // now reset states +// dbm.ResetBindingStates(); +// +// // now throws again +// try +// { +// dbm.BindSourceToTarget(source, target, null); +// throw new AssertionException("should throw InvalidPropertyException"); +// } +// catch(Spring.Objects.InvalidPropertyException) {} // ok +// } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/DataBinding/SimpleExpressionBindingTests.cs b/test/Spring/Spring.Core.Tests/DataBinding/SimpleExpressionBindingTests.cs new file mode 100644 index 00000000..75f122b7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/DataBinding/SimpleExpressionBindingTests.cs @@ -0,0 +1,71 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using NUnit.Framework; + +namespace Spring.DataBinding +{ + /// + /// Test cases for the SimpleExpressionBinding class. + /// + /// Aleksandar Seovic + /// $Id: SimpleExpressionBindingTests.cs,v 1.1 2006/11/21 05:47:00 aseovic Exp $ + [TestFixture] + public class SimpleExpressionBindingTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void WithNullMesageId() + { + new SimpleExpressionBinding("exp", "exp").SetErrorMessage(null, "errors"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void WithEmptyMesageId() + { + new SimpleExpressionBinding("exp", "exp").SetErrorMessage("", "errors"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void WithWhitespaceMesageId() + { + new SimpleExpressionBinding("exp", "exp").SetErrorMessage("\t ", "errors"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WithNullProviders() + { + new SimpleExpressionBinding("exp", "exp").SetErrorMessage("error", null); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WithEmptyProviders() + { + new SimpleExpressionBinding("exp", "exp").SetErrorMessage("error", new string[0]); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/ExceptionsTest.cs b/test/Spring/Spring.Core.Tests/ExceptionsTest.cs new file mode 100644 index 00000000..506f5f2a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/ExceptionsTest.cs @@ -0,0 +1,434 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using NUnit.Framework; + +#endregion + +namespace Spring +{ + /// + /// Tests the various exception classes. + /// + /// + /// + /// Shamelessly lifted from the NAnt test suite. + /// + /// + public abstract class ExceptionsTest : StandardsComplianceTest + { + protected ExceptionsTest() + { + CheckedType = typeof (Exception); + } + + #region Tests + + [Test] + public void TestStandardsCompliance() + { + ProcessAssembly(AssemblyToCheck); + } + + [Test] + public void TestThisTest() + { + ProcessAssembly(Assembly.GetAssembly(GetType())); + } + + #endregion + + #region Methods + + protected override void CheckStandardsCompliance(Assembly assembly, Type t) + { + // check to see that the exception is correctly named, with "Exception" at the end + bool nameIsValid = t.Name.EndsWith("Exception"); + Assert.IsTrue(nameIsValid, t.Name + " class name must end with Exception."); + if (t.IsAbstract) + { + return; + } + // Does the exception have the 3 standard constructors? + // Default constructor + CheckPublicConstructor(t, "()"); + // Constructor with a single string parameter + CheckPublicConstructor(t, "(string message)", typeof (String)); + // Constructor with a string and an inner exception + CheckPublicConstructor(t, "(string message, Exception inner)", + typeof (String), typeof (Exception)); + // check to see if the serialization constructor is present + // if exception is sealed, constructor should be private + // if exception is not sealed, constructor should be protected + if (t.IsSealed) + { + // check to see if the private serialization constructor is present... + CheckPrivateConstructor(t, "(SerializationInfo info, StreamingContext context)", + typeof (SerializationInfo), + typeof (StreamingContext)); + } + else + { + // check to see if the protected serialization constructor is present... + CheckProtectedConstructor(t, "(SerializationInfo info, StreamingContext context)", + typeof (SerializationInfo), + typeof (StreamingContext)); + } + // check to see if the type is marked as serializable + Assert.IsTrue(t.IsSerializable, t.Name + " is not serializable, missing [Serializable]?"); + // check to see if there are any public fields. These should be properties instead... + FieldInfo[] publicFields = t.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance); + if (publicFields.Length != 0) + { + foreach (FieldInfo fieldInfo in publicFields) + { + Assert.Fail(t.Name + "." + fieldInfo.Name + " is a public field, should be exposed through property instead."); + } + } + // If this exception has any fields, check to make sure it has a + // version of GetObjectData. If not, it does't serialize those fields. + FieldInfo[] fields = + t.GetFields(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + if (fields.Length != 0) + { + if (t.GetMethod("GetObjectData", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) == null) + { + Assert.Fail(t.Name + " does not implement GetObjectData but has private fields."); + } + } + if (!t.IsAbstract) + { + CheckInstantiation(t); + } + } + + private void CheckPublicConstructor(Type t, string description, params Type[] parameters) + { + // locate constructor + ConstructorInfo ci = + t.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, parameters, null); + // fail if constructor does not exist + Assert.IsNotNull(ci, t.Name + description + " is a required constructor."); + // fail if constructor is private + Assert.IsFalse(ci.IsPrivate, t.Name + description + " is private, must be public."); + // fail if constructor is protected + Assert.IsFalse(ci.IsFamily, t.Name + description + " is internal, must be public."); + // fail if constructor is internal + Assert.IsFalse(ci.IsAssembly, t.Name + description + " is internal, must be public."); + // fail if constructor is protected internal + Assert.IsFalse(ci.IsFamilyOrAssembly, t.Name + description + " is protected internal, must be public."); + // sanity check to make sure the constructor is public + Assert.IsTrue(ci.IsPublic, t.Name + description + " is not public, must be public."); + } + + private void CheckProtectedConstructor(Type t, string description, params Type[] parameters) + { + // locate constructor + ConstructorInfo ci = + t.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, parameters, null); + // fail if constructor does not exist + Assert.IsNotNull(ci, t.Name + description + " is a required constructor."); + // fail if constructor is public + Assert.IsFalse(ci.IsPublic, t.Name + description + " is public, must be protected."); + // fail if constructor is private + Assert.IsFalse(ci.IsPrivate, t.Name + description + " is private, must be public or protected."); + // fail if constructor is internal + Assert.IsFalse(ci.IsAssembly, t.Name + description + " is internal, must be protected."); + // fail if constructor is protected internal + Assert.IsFalse(ci.IsFamilyOrAssembly, t.Name + description + " is protected internal, must be protected."); + // sanity check to make sure the constructor is protected + Assert.IsTrue(ci.IsFamily, t.Name + description + " is not protected, must be protected."); + } + + private void CheckPrivateConstructor(Type t, string description, params Type[] parameters) + { + // locate constructor + ConstructorInfo ci = + t.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, parameters, null); + // fail if constructor does not exist + Assert.IsNotNull(ci, t.Name + description + " is a required constructor."); + // fail if constructor is public + Assert.IsFalse(ci.IsPublic, t.Name + description + " is public, must be private."); + // fail if constructor is protected + Assert.IsFalse(ci.IsFamily, t.Name + description + " is protected, must be private."); + // fail if constructor is internal + Assert.IsFalse(ci.IsAssembly, t.Name + description + " is internal, must be private."); + // fail if constructor is protected internal + Assert.IsFalse(ci.IsFamilyOrAssembly, t.Name + description + " is protected internal, must be private."); + // sanity check to make sure the constructor is private + Assert.IsTrue(ci.IsPrivate, t.Name + description + " is not private, must be private."); + } + + /// + /// If we've got here, then the standards compliance tests have passed... + /// + /// The exception type being tested. + private void CheckInstantiation(Type t) + { + // attempt to instantiate the 3 standard ctors... + ConstructorInfo ctor = t.GetConstructor(Type.EmptyTypes); + try + { + ctor.Invoke(null); + } + catch (Exception ex) + { + Assert.Fail("Ctor () for '" + t.Name + "' threw an exception : " + ex.Message); + } + ctor = t.GetConstructor(new Type[] {typeof (string)}); + try + { + Exception ex = (Exception) ctor.Invoke(new object[] {"My Fingers Turn To Fists"}); + Assert.IsNotNull(ex.Message, t.Name + "'s Message was null."); + } + catch (Exception ex) + { + Assert.Fail("Ctor (string) for '" + t.Name + "' threw an exception : " + ex.Message); + } + ctor = t.GetConstructor(new Type[] {typeof (string), typeof (Exception)}); + try + { + Exception ex = (Exception) ctor.Invoke(new object[] {"My Fingers Turn To Fists", new FormatException("Bing")}); + Assert.IsNotNull(ex.Message, t.Name + "'s Message was null."); + Assert.IsNotNull(ex.InnerException, t.Name + "'s InnerException was null."); + Assert.AreEqual("Bing", ex.InnerException.Message); + } + catch (Exception ex) + { + Assert.Fail("Ctor (string, Exception) for '" + t.Name + "' threw an exception : " + ex.Message); + } + // test the serialization ctor + try + { + ctor = t.GetConstructor(new Type[] {typeof (string)}); + Exception ex = (Exception) ctor.Invoke(new object[] {"HungerHurtsButStarvingWorks"}); + BinaryFormatter bf = new BinaryFormatter(); + MemoryStream ms = new MemoryStream(); + bf.Serialize(ms, ex); + ms.Seek(0,0); + Exception inex = (Exception)bf.Deserialize(ms); + Assert.IsNotNull(inex); + } + catch (Exception ex) + { + Assert.Fail("Ctor (Serialization) for '" + t.Name + "' threw an exception : " + ex.Message); + } + + } + + #endregion + + #region Properties + + /// + /// We will test all of the exceptions in this assembly for their correctness. + /// + protected Assembly AssemblyToCheck + { + get { return _assemblyToCheck; } + set { _assemblyToCheck = value; } + } + + #endregion + + private Assembly _assemblyToCheck = null; + } + + #region Inner Class : SimpleTestException + + /// + /// Do nothing exception to verify that the exception tester + /// is working correctly. + /// + [Serializable] + public class SimpleTestException : ApplicationException + { + #region Public Instance Constructors + + public SimpleTestException() + { + } + + public SimpleTestException(string message) : base(message) + { + } + + public SimpleTestException(string message, Exception inner) + : base(message, inner) + { + } + + #endregion Public Instance Constructors + + #region Protected Instance Constructors + + // deserialization constructor + protected SimpleTestException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + #endregion Protected Instance Constructors + } + + #endregion + + #region Inner Class : TestException + + /// + /// Exception to verify that the exception tester is working correctly. + /// + [Serializable] + public class TestException : ApplicationException, ISerializable + { + #region Private Instance Fields + + private int _value; + + #endregion Private Instance Fields + + #region Public Instance Constructors + + public TestException() + { + } + + public TestException(string message) : base(message) + { + } + + public TestException(string message, Exception inner) + : base(message, inner) + { + } + + // constructors that take the added value + public TestException(string message, int value) : base(message) + { + _value = value; + } + + #endregion Public Instance Constructors + + #region Protected Instance Constructors + + // deserialization constructor + protected TestException(SerializationInfo info, StreamingContext context) : base(info, context) + { + _value = info.GetInt32("Value"); + } + + #endregion Protected Instance Constructors + + #region Public Instance Properties + + public int Value + { + get { return _value; } + } + + #endregion Public Instance Properties + + #region Override implementation of ApplicationException + + // Called by the frameworks during serialization + // to fetch the data from an object. + public override void GetObjectData( + SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("Value", _value); + } + + // overridden Message property. This will give the + // proper textual representation of the exception, + // with the added field value. + public override string Message + { + get + { + // NOTE: should be localized... + string s = + string.Format( + CultureInfo.InvariantCulture, "Value: {0}", _value); + return base.Message + Environment.NewLine + s; + } + } + + #endregion Override implementation of ApplicationException + } + + #endregion + + #region Inner Class : SealedTestException + + /// + /// Exception to verify that the exception tester is working on + /// sealed exception. + /// + [Serializable] + public sealed class SealedTestException : TestException + { + #region Public Instance Constructors + + public SealedTestException() + { + } + + public SealedTestException(string message) : base(message) + { + } + + public SealedTestException(string message, Exception inner) + : base(message, inner) + { + } + + // constructors that take the added value + public SealedTestException(string message, int value) + : base(message, value) + { + } + + #endregion Public Instance Constructors + + #region Private Instance Constructors + + // deserialization constructor + private SealedTestException( + SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + #endregion Private Instance Constructors + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Expressions/ExpressionEvaluatorTests.cs b/test/Spring/Spring.Core.Tests/Expressions/ExpressionEvaluatorTests.cs new file mode 100644 index 00000000..627d6f70 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Expressions/ExpressionEvaluatorTests.cs @@ -0,0 +1,2940 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Diagnostics; +using System.EnterpriseServices; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Runtime.Serialization.Formatters.Soap; +using System.Text; +using System.Threading; +using System.Web.Services; +using antlr.collections; +using NUnit.Framework; +using Spring.Collections; +using Spring.Context; +using Spring.Context.Support; +using Spring.Core; +using Spring.Core.TypeResolution; +using Spring.Expressions; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Threading; +using Spring.Util; +using Foo = Spring.Expressions.Foo; + +#endregion + +namespace Spring.Expressions +{ + /// + /// This class contains tests for ExpressionEvaluator. + /// + /// Aleksandar Seovic + /// $Id: ExpressionEvaluatorTests.cs,v 1.72 2008/03/20 23:58:16 oakinger Exp $ + [TestFixture] + public sealed class ExpressionEvaluatorTests + { + #region Helper classes for threading tests + + public class AsyncTestExpressionEvaluation : AsyncTestTask + { + private IExpression exp; + private object rootContext; + private object expected; + private IDictionary variables; + + public AsyncTestExpressionEvaluation(int iterations, IExpression exp, object rootContext, object expected, + IDictionary variables) + : base(iterations) + { + this.exp = exp; + this.rootContext = rootContext; + this.expected = expected; + this.variables = variables; + } + + public override void DoExecute() + { + object result = exp.GetValue(rootContext, variables); + Assert.AreEqual(expected, result); + } + } + + #endregion + + private Inventor tesla; + private Inventor pupin; + private Society ieee; + + #region SetUp and TearDown + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp() + { + ContextRegistry.Clear(); + tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + tesla.Inventions = new string[] + { + "Telephone repeater", "Rotating magnetic field principle", + "Polyphase alternating-current system", "Induction motor", + "Alternating-current power transmission", "Tesla coil transformer", + "Wireless communication", "Radio", "Fluorescent lights" + }; + tesla.PlaceOfBirth.City = "Smiljan"; + + pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), "Serbian"); + pupin.Inventions = + new string[] { "Long distance telephony & telegraphy", "Secondary X-Ray radiation", "Sonar" }; + pupin.PlaceOfBirth.City = "Idvor"; + pupin.PlaceOfBirth.Country = "Serbia"; + + ieee = new Society(); + ieee.Members.Add(tesla); + ieee.Members.Add(pupin); + ieee.Officers["president"] = pupin; + ieee.Officers["advisors"] = new Inventor[] { tesla, pupin }; + // not historically accurate, but I need an array in the map ;-) + + TypeRegistry.RegisterType("Society", typeof(Society)); + } + + [TestFixtureTearDown] + public void TearDown() + { + //DynamicCodeManager.SaveAssembly(); + } + + #endregion + + #region Serialization Tests + + /// + /// GetObjectData() is not overridden on purpose !!! + /// + [Serializable] + private class SerializationTestExpression : BaseNode + { + private int testValue = 0; + + public int TestValue + { + get { return testValue; } + } + + public SerializationTestExpression(int testValue) + { + this.testValue = testValue; + } + + protected SerializationTestExpression(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + protected override object Get(object context, EvaluationContext evalContext) + { + throw new NotImplementedException(); + } + } + + /// + /// Tests serialization + deserialization of all BaseNode derived types + /// + [Test] + public void AllExpressionNodeTypesAreSerializable() + { + Type[] possibleTypes = typeof(BaseNode).Assembly.GetTypes(); + + BinaryFormatter formatter = new BinaryFormatter(); + + // look for all BaseNode derived types defined in assembly Spring.Core + for (int i = 0; i < possibleTypes.Length; i++) + { + Type t = possibleTypes[i]; + if (t != typeof(BaseNode) + && typeof(BaseNode).IsAssignableFrom(t) + && (!t.IsAbstract) + ) + { + //Console.WriteLine("testing " + t.FullName); + + // create using for public default ctor + IExpression exp = (IExpression)Activator.CreateInstance(t, true); + // serialize and deserialize it + exp = SerializeDeserializeExpression(exp); + exp = SerializeDeserializeExpressionUsingSoap(exp); + } + } + } + + /// + /// implements . + /// Thus members in derived classes won't get automatically serialized. + /// + [Test] + public void MembersDontGetSerializedByDefault() + { + SerializationTestExpression exp = new SerializationTestExpression(5); + Assert.AreEqual(5, exp.TestValue); + SerializationTestExpression exp2 = (SerializationTestExpression)SerializeDeserializeExpression(exp); + Assert.AreEqual(0, exp2.TestValue); + } + + /// + /// This test ensures, that the default node-type is serializable. + /// + /// + /// date() is parsed into DateLiteralNode( down:<default node type> ). + /// Normally antlr.CommonAST is the default node used by antlr. To enable serialization, Spring + /// uses a custom ASTFactory in + /// + [Test] + public void ExpressionDateLiteralNodeMaintainsStateAfterSerialization() + { + IExpression exp = Expression.Parse("date('08-24-1974', 'MM-dd-yyyy')"); + + Assert.AreEqual(new DateTime(1974, 8, 24), exp.GetValue(null)); + + exp = SerializeDeserializeExpression(exp); + + Assert.AreEqual(new DateTime(1974, 8, 24), exp.GetValue(null)); + } + + private static IExpression SerializeDeserializeExpression(IExpression exp) + { + byte[] data; + BinaryFormatter formatter = new BinaryFormatter(); + using (MemoryStream ms = new MemoryStream()) + { + formatter.Serialize(ms, exp); + ms.Flush(); + data = ms.ToArray(); + } + + using (MemoryStream ms = new MemoryStream(data)) + { + exp = (IExpression)formatter.Deserialize(ms); + } + + return exp; + } + + private static IExpression SerializeDeserializeExpressionUsingSoap(IExpression exp) + { + string xml; + SoapFormatter formatter = new SoapFormatter(); + using (MemoryStream ms = new MemoryStream()) + { + formatter.Serialize(ms, exp); + ms.Position = 0; + byte[] b = new byte[ms.Length]; + ms.Read(b, 0, (int)ms.Length); + xml = Encoding.ASCII.GetString(b, 0, b.Length); + } + using (StringReader sr = new StringReader(xml)) + { + byte[] b = Encoding.ASCII.GetBytes(xml); + Stream stream = new MemoryStream(b); + exp = (IExpression)formatter.Deserialize(stream); + } + return exp; + } + + #endregion Serialization Tests + + /// + /// Should throw exception for null root object + /// + [Test] + [ExpectedException(typeof(NullValueInNestedPathException))] + public void NullRoot() + { + ExpressionEvaluator.GetValue(null, "dummy.expression"); + } + + /// + /// Should throw exception for null root object + /// + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void TryingToSetTheValueOfNonSettableNode() + { + ExpressionEvaluator.SetValue(null, "10", 5); + } + + /// + /// Should return root itself for empty expression + /// + [Test] + public void GetNullOrEmptyExpression() + { + DateTime now = DateTime.Now; + Assert.AreEqual(ExpressionEvaluator.GetValue(now, null), now); + Assert.AreEqual(ExpressionEvaluator.GetValue(now, ""), now); + } + + /// + /// Should fail when setting value for the empty expression + /// + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void SetNullOrEmptyExpression() + { + ExpressionEvaluator.SetValue("xyz", null, "abc"); + } + + /// + /// Tests null literal. + /// + [Test] + public void TestNullLiteral() + { + Assert.IsNull(ExpressionEvaluator.GetValue(null, "null")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "'xyz' == null")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "null != 'xyz'")); + } + + [Test] + public void TestUnicode() + { + Assert.AreEqual("\u6f22\u5b57", ExpressionEvaluator.GetValue(null, "'\u6f22\u5b57'")); + } + /// + /// Tests string literals. + /// + [Test] + public void TestStringLiterals() + { + Assert.AreEqual("literal string", ExpressionEvaluator.GetValue(null, "'literal string'")); + Assert.AreEqual("literal 'string", ExpressionEvaluator.GetValue(null, "'literal ''string'")); + Assert.AreEqual(string.Empty, ExpressionEvaluator.GetValue(null, "''")); + Assert.AreEqual("escaped \t string \n", ExpressionEvaluator.GetValue(null, "'escaped \t string \n'")); + //Debug.Write(ExpressionEvaluator.GetValue(null, "'escaped\tstring\nsecond line\n\nfourth line'")); + } + + /// + /// Tests integer literals. + /// + [Test] + public void TestIntLiterals() + { + object int32 = ExpressionEvaluator.GetValue(null, Int32.MaxValue.ToString()); + object int64 = ExpressionEvaluator.GetValue(null, Int64.MaxValue.ToString()); + Assert.AreEqual(int32, Int32.MaxValue); + Assert.AreEqual(int64, Int64.MaxValue); + Assert.IsTrue(int32 is Int32); + Assert.IsTrue(int64 is Int64); + Assert.AreEqual(Int64.MaxValue.ToString(), + ExpressionEvaluator.GetValue(null, Int64.MaxValue.ToString() + ".ToString()")); + Assert.AreEqual(Int64.MaxValue.ToString(), ExpressionEvaluator.GetValue(null, "long.MaxValue.ToString()")); + Assert.AreEqual(32, ExpressionEvaluator.GetValue(null, "0x20")); + } + + /// + /// Tests hexadecimal integer literals. + /// + [Test] + public void TestHexLiterals() + { + IExpression exp = Expression.Parse("0x20"); + Assert.AreEqual(32, exp.GetValue()); + Assert.AreEqual(32, exp.GetValue()); + Assert.AreEqual(255, ExpressionEvaluator.GetValue(null, "0xFF")); + Assert.AreEqual(Int32.MaxValue, ExpressionEvaluator.GetValue(null, "0x7FFFFFFF")); + Assert.AreEqual(Int64.MaxValue, ExpressionEvaluator.GetValue(null, "0x7FFFFFFFFFFFFFFF")); + Assert.AreEqual(Int32.MinValue, ExpressionEvaluator.GetValue(null, "0x80000000")); + Assert.AreEqual(Int64.MinValue, ExpressionEvaluator.GetValue(null, "0x8000000000000000")); + } + + /// + /// Tests real literals. + /// + [Test] + public void TestRealLiterals() + { + IExpression exp = Expression.Parse("3.402823E+38"); + exp.GetValue(); + object s = exp.GetValue(); + object d = ExpressionEvaluator.GetValue(null, "1.797693E+308"); + object dec = ExpressionEvaluator.GetValue(null, "1000.00m"); + + + Assert.IsTrue(s is Double); + Assert.IsTrue(d is Double); + Assert.IsTrue(dec is Decimal); + + Assert.AreEqual(s, 3.402823E+38); + Assert.AreEqual(d, 1.797693E+308); + + Assert.AreEqual(5.25F, ExpressionEvaluator.GetValue(null, "5.25f")); + Assert.AreEqual(0.75d, ExpressionEvaluator.GetValue(null, "0.75D")); + + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "1000 == 1e3 and 1e+4 != 1000")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "100 < 1000.00m and 10000.00 > 1000")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "100 < 1000.00 and 10000.00m > 1e2")); + } + + /// + /// Tests boolean literals. + /// + [Test] + public void TestBooleanLiterals() + { + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false")); + } + + /// + /// Tests date literals. + /// + [Test] + public void TestDateLiterals() + { + IExpression exp = Expression.Parse("date('1974/08/24')"); + Assert.AreEqual(exp.GetValue(), new DateTime(1974, 8, 24)); + Assert.AreEqual(exp.GetValue(), new DateTime(1974, 8, 24)); + Assert.AreEqual(ExpressionEvaluator.GetValue(null, "date('1974-08-24')"), new DateTime(1974, 8, 24)); + Assert.AreEqual(ExpressionEvaluator.GetValue(null, "date('08-24-1974', 'MM-dd-yyyy')"), + new DateTime(1974, 8, 24)); + Assert.AreEqual(ExpressionEvaluator.GetValue(null, "date('08/24/1974', 'MM/dd/yyyy')"), + new DateTime(1974, 8, 24)); + Assert.AreEqual(ExpressionEvaluator.GetValue(null, "date('1974-08-24 12:35:06Z', 'u')"), + new DateTime(1974, 8, 24, 12, 35, 6)); + Assert.AreEqual(ExpressionEvaluator.GetValue(null, "date('1974/08/24').Year"), 1974); + Assert.AreEqual(ExpressionEvaluator.GetValue(null, "date('1974/08/24').AddYears(31).Year"), 2005); + } + + /// + /// Tests simple property and field accessors and mutators + /// + [Test] + public void TestSimplePropertyAccess() + { + Assert.AreEqual(DateTime.Today, ExpressionEvaluator.GetValue(null, "DateTime.Today")); + Assert.AreEqual("Nikola Tesla", ExpressionEvaluator.GetValue(tesla, "Name")); + Assert.AreEqual("Idvor", ExpressionEvaluator.GetValue(pupin, "PlaceOfBirth.City")); + ExpressionEvaluator.SetValue(tesla, "PlaceOfBirth.Country", "Croatia"); + Assert.AreEqual("Croatia", ExpressionEvaluator.GetValue(tesla, "PlaceOfBirth.Country")); + ExpressionEvaluator.SetValue(pupin, "Name", "Michael Pupin"); + Assert.AreEqual("Michael Pupin", ExpressionEvaluator.GetValue(pupin, "Name")); + Assert.AreEqual(new DateTime(1856, 7, 9), ExpressionEvaluator.GetValue(tesla, "DOB")); + Assert.AreEqual(1856, ExpressionEvaluator.GetValue(tesla, "DOB.Year")); + } + + /// + /// Tests that simple property and field accessors and mutators are case-insensitive. + /// + [Test] + public void SimplePropertyAccessIsCaseInsensitive() + { + Assert.AreEqual("Nikola Tesla", ExpressionEvaluator.GetValue(tesla, "nAme")); + Assert.AreEqual("Idvor", ExpressionEvaluator.GetValue(pupin, "Placeofbirth.city")); + ExpressionEvaluator.SetValue(tesla, "PlaceOfBirth.CountRY", "Croatia"); + Assert.AreEqual("Croatia", ExpressionEvaluator.GetValue(tesla, "Placeofbirth.COUNtry")); + ExpressionEvaluator.SetValue(pupin, "NAME", "Michael Pupin"); + Assert.AreEqual("Michael Pupin", ExpressionEvaluator.GetValue(pupin, "name")); + Assert.AreEqual(new DateTime(1856, 7, 9), ExpressionEvaluator.GetValue(tesla, "dob")); + Assert.AreEqual(1856, ExpressionEvaluator.GetValue(tesla, "DOb.YEar")); + } + + /// + /// Tests setting and getting shadowed properties + /// + [Test] + public void TestShadowedPropertyAccess() + { + ShadowingTestsMostSpezializedClass o; + + // test read + o = new ShadowingTestsMostSpezializedClass(); + o.SomeValue = "SomeString"; + Assert.AreEqual("SomeString", ExpressionEvaluator.GetValue(o, "SomeValue")); + + // test write + o = new ShadowingTestsMostSpezializedClass(); + ExpressionEvaluator.SetValue(o, "SomeValue", "SomeOtherString"); + Assert.AreEqual("SomeOtherString", o.SomeValue); + + // test readonly shadowed + o = new ShadowingTestsMostSpezializedClass(); + ((ShadowingTestsBaseClass)o).ReadonlyShadowedValue = "SomeString1"; + Assert.AreEqual("SomeString1", ExpressionEvaluator.GetValue(o, "ReadonlyShadowedValue")); + try + { + ExpressionEvaluator.SetValue(o, "ReadonlyShadowedValue", "SomeString2"); + Assert.Fail("Setting readonly property should throw NotWritablePropertyException"); + } + catch (NotWritablePropertyException) + { } + Assert.AreEqual("SomeString1", ExpressionEvaluator.GetValue(o, "ReadonlyShadowedValue")); + + // test writeonly shadowed + o = new ShadowingTestsMostSpezializedClass(); + ExpressionEvaluator.SetValue(o, "WriteonlyShadowedValue", "SomeString3"); + Assert.AreEqual("SomeString3", ((ShadowingTestsBaseClass)o).WriteonlyShadowedValue); + try + { + ExpressionEvaluator.GetValue(o, "WriteonlyShadowedValue"); + Assert.Fail("Getting writeonly property should throw NotReadablePropertyException"); + } + catch (NotReadablePropertyException) + { } + } + + /// + /// Tests indexed property and field accessors and mutators + /// + [Test] + public void TestIndexedPropertyAccess() + { + TypeRegistry.RegisterType("Society", typeof(Society)); + + // arrays and lists + Assert.AreEqual("Induction motor", ExpressionEvaluator.GetValue(tesla, "Inventions[3]")); + Assert.AreEqual("Nikola Tesla", ExpressionEvaluator.GetValue(ieee, "Members[0].Name")); + Assert.AreEqual("Wireless communication", ExpressionEvaluator.GetValue(ieee, "Members[0].Inventions[6]")); + + // maps + Assert.AreEqual(pupin, ExpressionEvaluator.GetValue(ieee, "Officers['president']")); + Assert.AreEqual("Idvor", ExpressionEvaluator.GetValue(ieee, "Officers['president'].PlaceOfBirth.City")); + Assert.AreEqual(tesla, ExpressionEvaluator.GetValue(ieee, "Officers['advisors'][0]")); + Assert.AreEqual("Polyphase alternating-current system", + ExpressionEvaluator.GetValue(ieee, "Officers['advisors'][0].Inventions[2]")); + + // maps with non-literal parameters + IDictionary vars = new Hashtable(); + vars["prez"] = "president"; + Assert.AreEqual(pupin, ExpressionEvaluator.GetValue(ieee, "Officers[#prez]", vars)); + + Assert.AreEqual(pupin, ExpressionEvaluator.GetValue(ieee, "Officers[Society.President]")); + Assert.AreEqual("Idvor", ExpressionEvaluator.GetValue(ieee, "Officers[Society.President].PlaceOfBirth.City")); + Assert.AreEqual(tesla, ExpressionEvaluator.GetValue(ieee, "Officers[Society.Advisors][0]")); + Assert.AreEqual("Polyphase alternating-current system", + ExpressionEvaluator.GetValue(ieee, "Officers[Society.Advisors][0].Inventions[2]")); + + // try to set some values + ExpressionEvaluator.SetValue(ieee, "Officers['advisors'][0].PlaceOfBirth.Country", "Croatia"); + Assert.AreEqual("Croatia", ExpressionEvaluator.GetValue(tesla, "PlaceOfBirth.Country")); + ExpressionEvaluator.SetValue(ieee, "Officers['president'].Name", "Michael Pupin"); + Assert.AreEqual("Michael Pupin", ExpressionEvaluator.GetValue(pupin, "Name")); + ExpressionEvaluator.SetValue(ieee, "Officers['advisors']", new Inventor[] { pupin, tesla }); + Assert.AreEqual(pupin, ExpressionEvaluator.GetValue(ieee, "Officers['advisors'][0]")); + Assert.AreEqual(tesla, ExpressionEvaluator.GetValue(ieee, "Officers['advisors'][1]")); + + // generic indexer + Bar bar = new Bar(); + Foo foo = new Foo(); + IExpression exp = Expression.Parse("[1]"); + Assert.AreEqual(2, exp.GetValue(bar)); + Assert.AreEqual(2, exp.GetValue(bar)); + Assert.AreEqual("test_1", ExpressionEvaluator.GetValue(foo, "[1, 'test']")); + } + + /// + /// Tests indexer access with invalid number of indices + /// + [Test] + [ExpectedException(typeof(InvalidPropertyException))] + public void TestIndexedPropertyAccessWithInvalidNumberOfIndices() + { + ExpressionEvaluator.GetValue(tesla, "Inventions[3, 2]"); + } + + /// + /// Tests method accessors + /// + [Test] + public void TestMethodAccess() + { + Guid guid = Guid.NewGuid(); + + TypeRegistry.RegisterType("Guid", typeof(Guid)); + Assert.AreEqual(guid.ToString(), ExpressionEvaluator.GetValue(guid, "ToString()")); + Assert.AreEqual(guid.ToString("n"), ExpressionEvaluator.GetValue(guid, "ToString('n')")); + Assert.AreEqual(16, ExpressionEvaluator.GetValue(null, "Guid.NewGuid().ToByteArray().Length")); + + Assert.AreEqual(2005 - tesla.DOB.Year, + ExpressionEvaluator.GetValue(ieee, "Members[0].GetAge(date('2005-01-01'))")); + } + + [Test] + public void TestMethodEvaluationOnDifferentContextType() + { + IExpression expression = Expression.Parse("ToString('dummy', null)"); + Assert.AreEqual("dummy", expression.GetValue(0m, null)); + Assert.AreEqual("dummy", expression.GetValue(0, null)); + } + + [Test] + public void TestMethodEvaluationOnDifferentArgumentTypes() + { + IExpression expression = Expression.Parse("Foo(#var1)"); + MethodInvokationCases testContext = new MethodInvokationCases(); + Hashtable args = new Hashtable(); + args["var1"] = "myString"; + Assert.AreEqual("myString", expression.GetValue(testContext, args)); + args["var1"] = 12; + Assert.AreEqual(12, expression.GetValue(testContext, args)); + } + + /// + /// Tests missing method accessors + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestMissingMethodAccess() + { + ExpressionEvaluator.GetValue("xyz", "ToStringilyLingily()"); + } + + /// + /// Tests projection node + /// + [Test] + public void TestProjection() + { + IList placesOfBirth = (IList)ExpressionEvaluator.GetValue(ieee, "Members.!{PlaceOfBirth.City}"); + + Assert.AreEqual(2, placesOfBirth.Count); + Assert.AreEqual("Smiljan", placesOfBirth[0]); + Assert.AreEqual("Idvor", placesOfBirth[1]); + + IList names = (IList)ExpressionEvaluator.GetValue(ieee, "Officers['advisors'].!{Name}"); + Assert.AreEqual(2, names.Count); + Assert.AreEqual("Nikola Tesla", names[0]); + Assert.AreEqual("Mihajlo Pupin", names[1]); + } + + /// + /// Tests selection node + /// + [Test] + public void TestSelection() + { + IList memberSelection = + (IList)ExpressionEvaluator.GetValue(ieee, "Members.?{PlaceOfBirth.City == 'Smiljan'}"); + + Assert.AreEqual(1, memberSelection.Count); + Assert.AreEqual("Nikola Tesla", ((Inventor)memberSelection[0]).Name); + + IList serbianOfficers = + (IList)ExpressionEvaluator.GetValue(ieee, "Officers['advisors'].?{Nationality == 'Serbian'}"); + Assert.AreEqual(2, serbianOfficers.Count); + Assert.AreEqual("Nikola Tesla", ((Inventor)serbianOfficers[0]).Name); + Assert.AreEqual("Mihajlo Pupin", ((Inventor)serbianOfficers[1]).Name); + + Inventor first = + (Inventor)ExpressionEvaluator.GetValue(ieee, "Officers['advisors'].^{Nationality == 'Serbian'}"); + Assert.AreEqual("Nikola Tesla", first.Name); + + Inventor last = + (Inventor)ExpressionEvaluator.GetValue(ieee, "Officers['advisors'].${Nationality == 'Serbian'}"); + Assert.AreEqual("Mihajlo Pupin", last.Name); + } + + /// + /// Tests type node + /// + [Test] + public void TestTypeNode() + { + IExpression exp = Expression.Parse("T(DateTime)"); + exp.GetValue(); + Assert.AreEqual(typeof(DateTime), exp.GetValue()); + + Assert.AreEqual(typeof(DateTime), ExpressionEvaluator.GetValue(null, "T(System.DateTime)")); + Assert.AreEqual(typeof(DateTime[]), ExpressionEvaluator.GetValue(null, "T(System.DateTime[], mscorlib)")); + Assert.AreEqual(typeof(ExpressionEvaluator), ExpressionEvaluator.GetValue(null, "T(Spring.Expressions.ExpressionEvaluator, Spring.Core)")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(tesla, "T(System.DateTime) == DOB.GetType()")); + } + + /// + /// Tests type node + /// + [Test] + [Ignore("does not work yet due to parser expecting ID production within T(ID)")] + public void TestTypeNodeWithAssemblyQualifiedName() + { + Assert.AreEqual(typeof(ExpressionEvaluator), ExpressionEvaluator.GetValue(null, string.Format("T({0})", typeof(ExpressionEvaluator).AssemblyQualifiedName))); + } + + /// + /// Tests constructor node + /// + [Test] + public void TestConstructor() + { + TypeRegistry.RegisterType(typeof(Inventor)); + + IExpression exp = Expression.Parse("new System.DateTime(2004, 8, 14)"); + Assert.AreEqual(1000, ExpressionEvaluator.GetValue(null, "new Decimal(1000)")); + Assert.AreEqual(new DateTime(2004, 8, 14), exp.GetValue(null)); + Assert.AreEqual(new DateTime(2004, 8, 14), exp.GetValue("xyz")); + Assert.AreEqual(new DateTime(1974, 8, 24), + ExpressionEvaluator.GetValue(null, "new DateTime(2004, 8, 14).AddDays(10).AddYears(-30)")); + + // test named arguments + Inventor ana = + (Inventor) + ExpressionEvaluator.GetValue(null, + "new Inventor(Name = 'Ana Maria Seovic', DOB = date('2004-08-14'), Nationality = 'American')"); + Assert.AreEqual("Ana Maria Seovic", ana.Name); + Assert.AreEqual(new DateTime(2004, 8, 14), ana.DOB); + Assert.AreEqual("American", ana.Nationality); + + Inventor aleks = + (Inventor) + ExpressionEvaluator.GetValue(null, + "new Inventor('Aleksandar Seovic', date('1974-08-24'), 'Serbian', Inventions = {'SPELL'})"); + Assert.AreEqual("Aleksandar Seovic", aleks.Name); + Assert.AreEqual(new DateTime(1974, 8, 24), aleks.DOB); + Assert.AreEqual("Serbian", aleks.Nationality); + Assert.AreEqual(1, aleks.Inventions.Length); + Assert.AreEqual("SPELL", aleks.Inventions[0]); + } + + /// + /// Tests missing constructor + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestMissingConstructor() + { + ExpressionEvaluator.GetValue(null, "new Decimal('xyz')"); + } + + /// + /// Tests expression list node + /// + [Test] + public void TestExpressionList() + { + TypeRegistry.RegisterType("Inventor", typeof(Inventor)); + Assert.AreEqual(3, + ExpressionEvaluator.GetValue(ieee.Members, + "(Add(new Inventor('Aleksandar Seovic', date('1974-08-24'), 'Serbian')); Count)")); + Assert.AreEqual(3, + ExpressionEvaluator.GetValue(ieee, + "Members.(Add(new Inventor('Ana Maria Seovic', date('2004-08-14'), 'Serbian')); RemoveAt(1); Count)")); + Assert.AreEqual("Aleksandar Seovic", + ExpressionEvaluator.GetValue(ieee.Members, + "([1].PlaceOfBirth.City = 'Beograd'; [1].PlaceOfBirth.Country = 'Serbia'; [1].Name)")); + Assert.AreEqual("Beograd", ((Inventor)ieee.Members[1]).PlaceOfBirth.City); + } + + /// + /// Tests assignment node + /// + [Test] + public void TestAssignNode() + { + Inventor inventor = new Inventor(); + Assert.AreEqual("Aleksandar Seovic", ExpressionEvaluator.GetValue(inventor, "Name = 'Aleksandar Seovic'")); + Assert.AreEqual(new DateTime(1974, 8, 24), + ExpressionEvaluator.GetValue(inventor, "DOB = date('1974-08-24')")); + Assert.AreEqual("Serbian", ExpressionEvaluator.GetValue(inventor, "Nationality = 'Serbian'")); + Assert.AreEqual("Ana Maria Seovic", + ExpressionEvaluator.GetValue(inventor, + "(DOB = date('2004-08-14'); Name = 'Ana Maria Seovic')")); + Assert.AreEqual(new DateTime(2004, 8, 14), inventor.DOB); + ExpressionEvaluator.GetValue(ieee, "Officers['vp'] = Members[0]"); + Assert.AreEqual("Nikola Tesla", ((Inventor)ieee.Officers["vp"]).Name); + } + + /// + /// Tests default node + /// + [Test] + public void TestDefaultNode() + { + Assert.AreEqual("default", ExpressionEvaluator.GetValue(null, "null ?? 'default'")); + Assert.AreEqual(1, ExpressionEvaluator.GetValue(null, "null ?? 2 * 2 - 3")); + Assert.AreEqual("Nikola Tesla", ExpressionEvaluator.GetValue(tesla, "null ?? #root.Name")); + + Assert.AreEqual("default", ExpressionEvaluator.GetValue(null, "'default' ?? 'xyz'")); + Assert.AreEqual(1, ExpressionEvaluator.GetValue(null, "2 * 2 - 3 ?? 5")); + Assert.AreEqual("Nikola Tesla", ExpressionEvaluator.GetValue(tesla, "#root.Name ?? 'Pupin'")); + } + + /// + /// Tests variable node + /// + [Test] + public void TestVariableNode() + { + IDictionary vars = new Hashtable(); + vars["newName"] = "Aleksandar Seovic"; + Assert.AreEqual("Ana Maria Seovic", + ExpressionEvaluator.GetValue(null, "#newName = 'Ana Maria Seovic'", vars)); + Assert.AreEqual("Ana Maria Seovic", ExpressionEvaluator.GetValue(tesla, "Name = #newName", vars)); + Assert.AreEqual("Nikola Tesla", + ExpressionEvaluator.GetValue(tesla, "(#oldName = Name; Name = 'Nikola Tesla')", vars)); + Assert.AreEqual("Nikola Tesla", ((Inventor)ExpressionEvaluator.GetValue(tesla, "#this", vars)).Name); + Assert.AreEqual("Nikola Tesla", + ExpressionEvaluator.GetValue(tesla, "(Nationality = 'Srbin'; #this).Name", vars)); + Assert.AreEqual("Nikola Tesla", tesla.Name); + Assert.AreEqual("Srbin", tesla.Nationality); + Assert.AreEqual("Ana Maria Seovic", vars["oldName"]); + Assert.AreEqual(tesla, ExpressionEvaluator.GetValue(tesla, "#root", vars)); + } + + + /// + /// Try to set 'this' variable + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TryToSetThis() + { + ExpressionEvaluator.SetValue(null, "#this", "xyz"); + } + + /// + /// Try to set 'root' variable + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TryToSetRoot() + { + ExpressionEvaluator.SetValue(null, "#root", "xyz"); + } + + /// + /// Tests ternary node + /// + [Test] + public void TestTernaryNode() + { + IExpression exp = Expression.Parse("true ? 'trueExp' : 'falseExp'"); + exp.GetValue(); + + Assert.AreEqual("trueExp", exp.GetValue()); + Assert.AreEqual("falseExp", ExpressionEvaluator.GetValue(null, "false ? 'trueExp' : 'falseExp'")); + Assert.AreEqual("trueExp", ExpressionEvaluator.GetValue(null, "(true ? 'trueExp' : 'falseExp')")); + Assert.AreEqual("falseExp", ExpressionEvaluator.GetValue(null, "(false ? 'trueExp' : 'falseExp')")); + + ExpressionEvaluator.SetValue(ieee, "Name", "IEEE"); + IDictionary vars = new Hashtable(); + vars["queryName"] = "Nikola Tesla"; + string expression = + @"IsMember(#queryName) + ? #queryName + ' is a member of the ' + Name + ' Society' + : #queryName + ' is not a member of ' + Name + ' Society'"; + Assert.AreEqual("Nikola Tesla is a member of the IEEE Society", + ExpressionEvaluator.GetValue(ieee, expression, vars)); + } + + /// + /// Tests logical OR operator + /// + [Test] + public void TestLogicalOrOperator() + { + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true or true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false or false")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true or false")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "false or true")); + string expression = @"IsMember('Nikola Tesla') or IsMember('Albert Einstien')"; + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(ieee, expression)); + } + + /// + /// Tests logical AND operator + /// + [Test] + public void TestLogicalAndOperator() + { + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true and true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false and false")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "true and false")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false and true")); + string expression = @"IsMember('Nikola Tesla') and IsMember('Mihajlo Pupin')"; + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(ieee, expression)); + } + + /// + /// Tests logical NOT operator + /// + [Test] + public void TestLogicalNotOperator() + { + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!true")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "!false")); + string expression = @"IsMember('Nikola Tesla') and !IsMember('Mihajlo Pupin')"; + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(ieee, expression)); + } + + /// + /// Tests logical operator presedance + /// + [Test] + public void TestLogicalOperatorPresedance() + { + // NOT over AND + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!false and false")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "!false and true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!true and false")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!true and true")); + + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "!(false and false)")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "!(false and true)")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "!(true and false)")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!(true and true)")); + + // NOT over OR + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "!false or false")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "!false or true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!true or false")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "!true or true")); + + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "!(false or false)")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!(false or true)")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!(true or false)")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!(true or true)")); + + // AND over OR + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false and false or false")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "false and false or true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false and true or false")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "false and true or true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "true and false or false")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true and false or true")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true and true or false")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true and true or true")); + + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false and (false or false)")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false and (false or true)")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false and (true or false)")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false and (true or true)")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "true and (false or false)")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true and (false or true)")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true and (true or false)")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true and (true or true)")); + } + + /// + /// Tests equality operator. + /// + [Test] + public void TestEqualityOperator() + { + // Null + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "null == null")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null == 'xyz'")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "123 == null")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null == 123")); + + // Bool + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "false == false")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true == true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false == true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "true == false")); + + // Int + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "2 == 2")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "-5 == -5")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "2 == -5")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "-5 == 2")); + + // String + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'test' == 'test'")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "'Test' == 'test'")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "'test' == 'Test'")); + + // DateTime + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') == date('1974-08-24')")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "DateTime.Today == DateTime.Today")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "DateTime.Today == date('1974-08-24')")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') == DateTime.Today")); + + // Enums + Foo foo = new Foo(FooType.One); + TypeRegistry.RegisterType("FooType", typeof(FooType)); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(foo, "Type == FooType.One")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(foo, "Type == 'One'")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(foo, "Type == 'Two'")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(foo, "FooType.One == Type")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(foo, "'One' == Type")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(foo, "'Two' == Type")); + } + + /// + /// Tests inequality operator. + /// + [Test] + public void TestInqualityOperator() + { + // Null + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null != null")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "123 != null")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "null != 'xyz'")); + + // Bool + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false != false")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "true != true")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "false != true")); + + // Int + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "2 != 2.0")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "-5.0 != -5")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "2.0 != -5")); + + // String + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "'test' != 'test'")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'Test' != 'test'")); + + // DateTime + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') != date('1974-08-24')")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "DateTime.Today != DateTime.Today")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "DateTime.Today != date('1974-08-24')")); + } + + /// + /// Tests less than operator. + /// + [Test] + public void TestLessThanOperator() + { + // Null + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null < null")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "123 < null")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "null < 'xyz'")); + + // Bool + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "false < true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "true < true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "true < false")); + + // Int + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "2 < 2.0")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "-5.0 < 2")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "2 < -5.0")); + + // String + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "'test' < 'test'")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "'Test' < 'test'")); + + // DateTime + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') < date('1974-08-24')")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') < DateTime.Today")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "DateTime.Today < date('1974-08-24')")); + } + + /// + /// Tests less than or equal operator. + /// + [Test] + public void TestLessThanOrEqualOperator() + { + // Null + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "null <= null")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "123 <= null")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "null <= 'xyz'")); + + // Bool + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "false <= true")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true <= true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "true <= false")); + + // Int + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "2 <= 2.0")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "-5.0 <= 2")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "2.0 <= -5")); + + // String + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'test' <= 'test'")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "'Test' <= 'test'")); + + // DateTime + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') <= date('1974-08-24')")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') <= DateTime.Today")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "DateTime.Today <= date('1974-08-24')")); + } + + /// + /// Tests greater than operator. + /// + [Test] + public void TestGreaterThanOperator() + { + // Null + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null > null")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "123 > null")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null > 'xyz'")); + + // Bool + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false > true")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "true > true")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true > false")); + + // Int + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "2 > 2.0")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "-5.0 > 2")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "2 > -5.0")); + + // String + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "'test' > 'test'")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'Test' > 'test'")); + + // DateTime + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') > date('1974-08-24')")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') > DateTime.Today")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "DateTime.Today > date('1974-08-24')")); + } + + /// + /// Tests greater than or equal operator. + /// + [Test] + public void TestGreaterThanOrEqualOperator() + { + // Null + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "null >= null")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "123 >= null")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null >= 'xyz'")); + + // Bool + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "false >= true")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true >= true")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "true >= false")); + + // Int + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "2.0 >= 2")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "-5 >= 2.0")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "2.0 >= -5")); + + // String + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'test' >= 'test'")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'Test' >= 'test'")); + + // DateTime + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') >= date('1974-08-24')")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "date('1974-08-24') >= DateTime.Today")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "DateTime.Today >= date('1974-08-24')")); + } + + /// + /// Tests IN operator. + /// + [Test] + public void TestInOperator() + { + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null in null")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "3 in {1, 2, 3, 4, 5}")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!(3 in {1, 2, 3, 4, 5})")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'xyz' in new string[] {'abc', 'xyz'}")); + Assert.IsTrue( + (bool)ExpressionEvaluator.GetValue(null, "'xyz' in #{'abc' : 'Value 1', 'xyz' : DateTime.Today}")); + } + + /// + /// Tests IS operator. + /// + [Test] + public void TestIsOperator() + { + TypeRegistry.RegisterType(typeof(IList)); + TypeRegistry.RegisterType(typeof(IDictionary)); + + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null is null")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "5 is null")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null is int")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "5 is int")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "!(5 is int)")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "{1, 2, 3, 4, 5} is IList")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "new string[] {'abc', 'xyz'} is T(string[])")); + Assert.IsTrue( + (bool)ExpressionEvaluator.GetValue(null, "#{'abc' : 'Value 1', 'xyz' : DateTime.Today} is IDictionary")); + } + + /// + /// Tests BETWEEN operator. + /// + [Test] + public void TestBetweenOperator() + { + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "null between {1, 5}")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "0 between {1, 5}")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "1 between {1, 5}")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "3 between {1, 5}")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "5 between {1, 5}")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "6 between {1, 5}")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "!(6 between {1, 5})")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'efg' between {'abc', 'xyz'}")); + Assert.IsTrue( + (bool)ExpressionEvaluator.GetValue(null, "DateTime.Today between {DateTime.Today, DateTime.Now}")); + Assert.IsFalse( + (bool)ExpressionEvaluator.GetValue(null, "DateTime.Today between {DateTime.Now, DateTime.Now}")); + } + + /// + /// Tests LIKE operator. + /// + [Test] +#if !NET_2_0 + [ExpectedException(typeof(NotSupportedException))] +#endif + public void TestLikeOperator() + { + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'A' like '?'")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "'Abc' like '?'")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'Abc' like '[A-Z]b?'")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'Abc' like '*'")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'Aleksandar' like 'Aleks*'")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'Ana Maria Seovic' like '*Maria*'")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, "'Marija Seovic' like '*Seovic'")); + } + + /// + /// Tests MATCHES operator. + /// + [Test] + public void TestMatchesOperator() + { + string emailCheck = + @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"; + + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(emailCheck, "'A' matches #this")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(emailCheck, "'aleks@seovic.com' matches #this")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(emailCheck, "'@seovic.com' matches #this")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(emailCheck, "'seovic.com' matches #this")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(emailCheck, "'aleks' matches #this")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(emailCheck, "'aleks@' matches #this")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(emailCheck, "'aleks@seovic' matches #this")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(null, @"'5.00' matches '^-?\d+(\.\d{2})?$'")); + } + + /// + /// Type coercion failure. + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestTypeCoercionForUncoercableTypes() + { + ExpressionEvaluator.GetValue(null, "'xyz' > 123"); + } + + /// + /// Type comparison failure. + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestComparisonOfInstancesThatDoNotImplementIComparable() + { + IDictionary vars = new Hashtable(); + vars["tesla"] = tesla; + vars["pupin"] = pupin; + ExpressionEvaluator.GetValue(null, "#tesla > #pupin", vars); + } + + /// + /// Tests addition operator. + /// + [Test] + public void TestAddOperator() + { + // numbers + Assert.AreEqual(2, ExpressionEvaluator.GetValue(null, "1 + 1")); + Assert.AreEqual(2, ExpressionEvaluator.GetValue(null, "1e0 + 1")); + Assert.AreEqual(0, ExpressionEvaluator.GetValue(null, "-1e0 + 1")); + Assert.AreEqual(-2, ExpressionEvaluator.GetValue(null, "-1e0 + -1")); + Assert.AreEqual(Decimal.Parse("2.0", NumberFormatInfo.InvariantInfo), + ExpressionEvaluator.GetValue(null, "1.0m + 1.0")); + Assert.AreEqual(9, ExpressionEvaluator.GetValue(null, "2.0 + 3e0 + 4")); + + // strings + Assert.AreEqual("test string", ExpressionEvaluator.GetValue(null, "'test' + ' ' + 'string'")); + Assert.AreEqual("test 2", ExpressionEvaluator.GetValue(null, "'test' + ' ' + 2")); + Assert.AreEqual("test " + DateTime.Today.ToString(), + ExpressionEvaluator.GetValue(null, "'test' + ' ' + DateTime.Today")); + + // dates + DateTime anaDOB = new DateTime(2004, 8, 14); + DateTime aleksDOB = new DateTime(1974, 8, 24); + TimeSpan diff = anaDOB - aleksDOB; + IDictionary vars = new Hashtable(); + vars["ts"] = diff; + + Assert.AreEqual(anaDOB, ExpressionEvaluator.GetValue(null, "date('1974-08-24') + #ts", vars)); + Assert.AreEqual(new DateTime(1974, 8, 29), ExpressionEvaluator.GetValue(null, "date('1974-08-24') + 5")); + Assert.AreEqual(new DateTime(1974, 8, 29), + ExpressionEvaluator.GetValue(null, "date('1974-08-24') + '5.0:0'")); + } + + /// + /// Tests addition operator with invalid arguments. + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestAddOperatorWithInvalidArguments() + { + ExpressionEvaluator.GetValue(null, "DateTime.Today + false"); + } + + /// + /// Tests subtraction operator. + /// + [Test] + public void TestSubtractOperator() + { + // numbers + Assert.AreEqual(2, ExpressionEvaluator.GetValue(null, "3 - 1")); + Assert.AreEqual(-2, ExpressionEvaluator.GetValue(null, "1 - 3")); + Assert.AreEqual(4, ExpressionEvaluator.GetValue(null, "1 - -3")); + Assert.AreEqual(Decimal.Parse("-9000.00", NumberFormatInfo.InvariantInfo), + ExpressionEvaluator.GetValue(null, "1000.00m - 1e4")); + Assert.AreEqual(-5, ExpressionEvaluator.GetValue(null, "2.0 - 3e0 - 4")); + + // dates + DateTime anaDOB = new DateTime(2004, 8, 14); + DateTime aleksDOB = new DateTime(1974, 8, 24); + TimeSpan diff = anaDOB - aleksDOB; + IDictionary vars = new Hashtable(); + vars["ts"] = diff; + + Assert.AreEqual(aleksDOB, ExpressionEvaluator.GetValue(null, "date('2004-08-14') - #ts", vars)); + Assert.AreEqual(diff, ExpressionEvaluator.GetValue(null, "date('2004-08-14') - date('1974-08-24')")); + Assert.AreEqual(new DateTime(1974, 8, 19), ExpressionEvaluator.GetValue(null, "date('1974-08-24') - 5")); + Assert.AreEqual(new DateTime(1974, 8, 19), + ExpressionEvaluator.GetValue(null, "date('1974-08-24') - '5.0:0'")); + } + + /// + /// Tests subtraction operator with invalid arguments. + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestSubtractOperatorWithInvalidArguments() + { + ExpressionEvaluator.GetValue(null, "DateTime.Today - false"); + } + + /// + /// Tests multiplication operator. + /// + [Test] + public void TestMultiplyOperator() + { + Assert.AreEqual(4, ExpressionEvaluator.GetValue(null, "2 * 2")); + Assert.AreEqual(-6, ExpressionEvaluator.GetValue(null, "2 * -3")); + Assert.AreEqual(6, ExpressionEvaluator.GetValue(null, "-2 * -3")); + Assert.AreEqual(Decimal.Parse("1000000.00", NumberFormatInfo.InvariantInfo), + ExpressionEvaluator.GetValue(null, "1000.00m * 1e3")); + Assert.AreEqual(24, ExpressionEvaluator.GetValue(null, "2.0 * 3e0 * 4")); + } + + /// + /// Tests multiplication operator with invalid arguments. + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestMultiplyOperatorWithInvalidArguments() + { + ExpressionEvaluator.GetValue(null, "DateTime.Today * false"); + } + + /// + /// Tests division operator. + /// + [Test] + public void TestDivideOperator() + { + Assert.AreEqual(1, ExpressionEvaluator.GetValue(null, "2 / 2")); + Assert.AreEqual(-2, ExpressionEvaluator.GetValue(null, "6 / -3")); + Assert.AreEqual(2, ExpressionEvaluator.GetValue(null, "-6 / -3")); + Assert.AreEqual(Decimal.Parse("1.00", NumberFormatInfo.InvariantInfo), + ExpressionEvaluator.GetValue(null, "1000.00m / 1e3")); + Assert.AreEqual(1, ExpressionEvaluator.GetValue(null, "8.0 / 4e0 / 2")); + } + + /// + /// Tests division operator with invalid arguments. + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestDivideOperatorWithInvalidArguments() + { + ExpressionEvaluator.GetValue(null, "DateTime.Today / false"); + } + + /// + /// Tests modulus operator. + /// + [Test] + public void TestModulusOperator() + { + Assert.AreEqual(0, ExpressionEvaluator.GetValue(null, "2 % 2")); + Assert.AreEqual(2, ExpressionEvaluator.GetValue(null, "6 % -4")); + Assert.AreEqual(-1, ExpressionEvaluator.GetValue(null, "-6 % -5")); + Assert.AreEqual(Decimal.Parse("5.00", NumberFormatInfo.InvariantInfo), + ExpressionEvaluator.GetValue(null, "1005.00m % 1e3")); + Assert.AreEqual(1, ExpressionEvaluator.GetValue(null, "8.0 % 5e0 % 2")); + } + + /// + /// Tests modulus operator with invalid arguments. + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestModulusOperatorWithInvalidArguments() + { + ExpressionEvaluator.GetValue(null, "DateTime.Today % false"); + } + + /// + /// Tests power operator. + /// + [Test] + public void TestPowerOperator() + { + Assert.AreEqual(8, ExpressionEvaluator.GetValue(null, "2 ^ +3")); + Assert.AreEqual(16, ExpressionEvaluator.GetValue(null, "-2 ^ 4")); + Assert.AreEqual(-32, ExpressionEvaluator.GetValue(null, "-2 ^ 5")); + Assert.AreEqual(.0625, ExpressionEvaluator.GetValue(null, "4 ^ -2")); + Assert.AreEqual(2, ExpressionEvaluator.GetValue(null, "+4 ^ .5")); + } + + /// + /// Tests power operator with invalid arguments. + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestPowerOperatorWithInvalidArguments() + { + ExpressionEvaluator.GetValue(null, "DateTime.Today ^ false"); + } + + /// + /// Tests unary minus operator with invalid argument. + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestUnaryMinusOperatorWithInvalidArguments() + { + ExpressionEvaluator.GetValue(null, "-false"); + } + + /// + /// Tests unary plus operator with invalid argument. + /// + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestUnaryPlusOperatorWithInvalidArguments() + { + ExpressionEvaluator.GetValue(null, "+false"); + } + + /// + /// Tests operator precedence. + /// + [Test] + public void TestOperatorPrecedence() + { + Assert.AreEqual(-3, ExpressionEvaluator.GetValue(null, "1+2-3*8/2/2")); + Assert.AreEqual(-45, ExpressionEvaluator.GetValue(null, "1+2-3*8^2/2/2")); + Assert.AreEqual(0, ExpressionEvaluator.GetValue(null, "1+2-3*8/2^2/2")); + Assert.AreEqual(-4.5, ExpressionEvaluator.GetValue(null, "1+(2-3*8)/2.0/2")); + } + + /// + /// Tests Spring reference when reference to a non-existant object is specified. + /// + [Test] + [ExpectedException(typeof(NoSuchObjectDefinitionException))] + public void TestReferenceForNonExistantObject() + { + ContextRegistry.RegisterContext( + new XmlApplicationContext(false, "assembly://Spring.Core.Tests/Spring.Context.Support/objects.xml")); + ExpressionEvaluator.GetValue(null, "@(dummyRef)"); + } + + /// + /// Tests Spring reference. + /// + [Test] + public void TestReference() + { + ContextRegistry.RegisterContext( + new XmlApplicationContext(false, "assembly://Spring.Core.Tests/Spring.Context.Support/objects.xml")); + Assert.AreEqual(typeof(TestObject), ExpressionEvaluator.GetValue(null, "@(goran)").GetType()); + + IApplicationContext ctx = + new XmlApplicationContext(false, "assembly://Spring.Core.Tests/Spring.Context.Support/objects.xml"); + ctx.Name = "myContext"; + ContextRegistry.RegisterContext(ctx); + + Assert.AreEqual(typeof(TestObject), + ExpressionEvaluator.GetValue(null, "@(myContext:goran)").GetType()); + } + + /// + /// Since Expression-References require the context to be added to ContextRegistry, + /// they work only if the objectdefinition's "lazy-init" is true. + /// + [Test] + public void TestReferenceByExpression() + { + //TODO: write a test showing that expressions don't work without "lazy-init": + /* + + + + + */ + + ContextRegistry.RegisterContext( + new XmlApplicationContext(false, "assembly://Spring.Core.Tests/Spring.Context.Support/objects.xml")); + + TestObject testObject = ExpressionEvaluator.GetValue(null, "@(goran)") as TestObject; + + TestObjectContainer testObjectContainer = + ExpressionEvaluator.GetValue(null, "@(testObjectContainer_lazy)") as TestObjectContainer; + + Assert.IsNotNull(testObject); + Assert.AreSame(testObject, testObjectContainer.TestObject); + } + + /// + /// Ensure, e.g. context-names my contain a dot + /// + [Test] + public void TestQualifiedNameMayContainDots() + { + IApplicationContext ctx = + new XmlApplicationContext(false, "assembly://Spring.Core.Tests/Spring.Context.Support/objects.xml"); + ctx.Name = "my.Context"; + ContextRegistry.RegisterContext(ctx); + + Assert.AreEqual(typeof(TestObject), + ExpressionEvaluator.GetValue(null, "@(my\\.Context:goran)").GetType()); + } + + /// + /// Tests attribute expression. + /// + [Test] + public void TestAttribute() + { + TypeRegistry.RegisterType("WebMethod", typeof(WebMethodAttribute)); + TypeRegistry.RegisterType("TransactionOption", typeof(TransactionOption)); + + Assert.IsInstanceOfType(typeof(SerializableAttribute), + ExpressionEvaluator.GetValue(null, "@[System.Serializable]")); + Assert.IsInstanceOfType(typeof(SerializableAttribute), + ExpressionEvaluator.GetValue(null, "@[System.Serializable()]")); + Assert.IsInstanceOfType(typeof(WebMethodAttribute), ExpressionEvaluator.GetValue(null, "@[WebMethod]")); + + WebMethodAttribute webMethod = (WebMethodAttribute)ExpressionEvaluator.GetValue(null, "@[WebMethod(true)]"); + Assert.IsTrue(webMethod.EnableSession); + + webMethod = (WebMethodAttribute) + ExpressionEvaluator.GetValue( + null, + "@[WebMethod(false, CacheDuration = 60, Description = 'my web method', TransactionOption = TransactionOption.Required)]"); + Assert.AreEqual(60, webMethod.CacheDuration); + Assert.AreEqual("my web method", webMethod.Description); + Assert.AreEqual(TransactionOption.Required, webMethod.TransactionOption); + } + + /// + /// Type lambda expressions. + /// + [Test] + public void TestLambdaExpressions() + { + TypeRegistry.RegisterType(typeof(Math)); + + // simple function + Assert.AreEqual(4, + ExpressionEvaluator.GetValue(null, "(#add = {|x, y| $x + $y}; #add(2, 2))", new Hashtable())); + Assert.AreEqual(25, + ExpressionEvaluator.GetValue(null, "(#max = {|x, y| $x > $y ? $x : $y }; #max(5,25))", + new Hashtable())); + + // recursive function + Assert.AreEqual(120, + ExpressionEvaluator.GetValue(null, + "(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))", + new Hashtable())); + + // function invoked within projection expression + string expr = "(#upper = {|txt| $txt.ToUpper() }; !{ #upper(Name) })"; + IList upperNames = (IList)ExpressionEvaluator.GetValue(ieee.Members, expr, new Hashtable()); + Assert.AreEqual("NIKOLA TESLA", upperNames[0]); + Assert.AreEqual("MIHAJLO PUPIN", upperNames[1]); + + // function that delegates to a function passed as a parameter + IDictionary vars = new Hashtable(); + Expression.RegisterFunction("sqrt", "{|n| Math.Sqrt($n)}", vars); + Expression.RegisterFunction("fact", "{|n| $n <= 1 ? 1 : $n * #fact($n-1)}", vars); + string expr2 = + @"( + #delegate = {|f,n| $f($n) }; + #d = #delegate; + + #result = { #delegate(#sqrt, 4), #d(#fact, 5), #delegate({|n| $n ^ 2 }, 5) } + )"; + IList results = (IList)ExpressionEvaluator.GetValue(null, expr2, vars); + Assert.AreEqual(2, results[0]); + Assert.AreEqual(120, results[1]); + Assert.AreEqual(25, results[2]); + + // function assignment + Assert.AreEqual(120, + ExpressionEvaluator.GetValue(null, + "(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #f = #fact; #f(5))", + new Hashtable())); + } + + #region Collection Processor and Aggregator tests + + [Test] + public void TestCountAggregator() + { + int[] arr = new int[] { 24, 8, 14, 8 }; + Assert.AreEqual(4, ExpressionEvaluator.GetValue(arr, "count()")); + Assert.AreEqual(3, ExpressionEvaluator.GetValue(null, "{1, 5, -3}.count()")); + Assert.AreEqual(0, ExpressionEvaluator.GetValue(null, "count()")); + } + + [Test] + public void TestSumAggregator() + { + int[] arr = new int[] { 24, 8, 14, 8 }; + Assert.AreEqual(54, ExpressionEvaluator.GetValue(arr, "sum()")); + Assert.AreEqual(13, ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.sum()")); + + object[] arr2 = new object[] { 5, 5.8, 12.2, 1 }; + object result = ExpressionEvaluator.GetValue(arr2, "sum()"); + Assert.IsInstanceOfType(typeof(double), result); + Assert.AreEqual(24, result); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestSumAggregatorWithNonNumber() + { + object[] arr = new object[] { 5, "ana", 12.2, 1 }; + ExpressionEvaluator.GetValue(arr, "sum()"); + } + + [Test] + public void TestAverageAggregator() + { + int[] arr = new int[] { 24, 8, 16, 8 }; + Assert.AreEqual(14, ExpressionEvaluator.GetValue(arr, "average()")); + Assert.AreEqual(3, ExpressionEvaluator.GetValue(null, "{1, 5, -4, 10}.average()")); + Assert.AreEqual(3.5, ExpressionEvaluator.GetValue(null, "{1, 5, -2, 10}.average()")); + + object[] arr2 = new object[] { 5, 5.8, 12.2, 1 }; + object result = ExpressionEvaluator.GetValue(arr2, "average()"); + Assert.IsInstanceOfType(typeof(double), result); + Assert.AreEqual(6, result); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestAverageAggregatorWithNonNumber() + { + object[] arr = new object[] { 5, "ana", 12.2, 1 }; + ExpressionEvaluator.GetValue(arr, "average()"); + } + + [Test] + public void TestMinAggregator() + { + int[] arr = new int[] { 24, 8, 14, 8 }; + Assert.AreEqual(8, ExpressionEvaluator.GetValue(arr, "min()")); + Assert.AreEqual(-3, ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.min()")); + + object[] arr2 = new object[] { 5, 5.8, 12.2, 1 }; + object result = ExpressionEvaluator.GetValue(arr2, "min()"); + Assert.IsInstanceOfType(typeof(int), result); + Assert.AreEqual(1, result); + + Assert.IsNull(ExpressionEvaluator.GetValue(ObjectUtils.EmptyObjects, "min()")); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestMinAggregatorWithNonComparable() + { + object[] arr = new object[] { new Object(), new Object() }; + ExpressionEvaluator.GetValue(arr, "min()"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestMinAggregatorWithMixedTypes() + { + object[] arr = new object[] { 5, "ana", 12.2, 1 }; + ExpressionEvaluator.GetValue(arr, "min()"); + } + + [Test] + public void TestMaxAggregator() + { + int[] arr = new int[] { 24, 8, 14, 8 }; + Assert.AreEqual(24, ExpressionEvaluator.GetValue(arr, "max()")); + Assert.AreEqual(10, ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.max()")); + + object[] arr2 = new object[] { 5, 5.8, 12.2, 1 }; + object result = ExpressionEvaluator.GetValue(arr2, "max()"); + Assert.IsInstanceOfType(typeof(double), result); + Assert.AreEqual(12.2, result); + + Assert.IsNull(ExpressionEvaluator.GetValue(ObjectUtils.EmptyObjects, "max()")); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestMaxAggregatorWithNonComparable() + { + object[] arr = new object[] { new Object(), new Object() }; + ExpressionEvaluator.GetValue(arr, "max()"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestMaxAggregatorWithMixedTypes() + { + object[] arr = new object[] { 5, "ana", 12.2, 1 }; + ExpressionEvaluator.GetValue(arr, "max()"); + } + + [Test] + public void TestSortProcessor() + { + int[] arr = new int[] { 24, 8, 14, 6 }; + Assert.AreEqual(new int[] { 6, 8, 14, 24 }, ExpressionEvaluator.GetValue(arr, "sort()")); + Assert.AreEqual(new int[] { 6, 8, 14, 24 }, ExpressionEvaluator.GetValue(arr, "sort(true)")); + Assert.AreEqual(new int[] { 24, 14, 8, 6 }, ExpressionEvaluator.GetValue(arr, "sort(false)")); + + string[] arr2 = new string[] { "abc", "xyz", "stuv", "efg", "dcb" }; + Assert.AreEqual(new string[] { "abc", "dcb", "efg", "stuv", "xyz" }, + ExpressionEvaluator.GetValue(arr2, "sort()")); + + DateTime[] arr3 = new DateTime[] { DateTime.Today, DateTime.MaxValue, DateTime.MinValue }; + Assert.AreEqual(new DateTime[] { DateTime.MinValue, DateTime.Today, DateTime.MaxValue }, + ExpressionEvaluator.GetValue(arr3, "sort()")); + + Assert.AreEqual(new object[] { -3.3, 1.2, 5.5 }, ExpressionEvaluator.GetValue(null, "{1.2, 5.5, -3.3}.sort()")); + Assert.IsNull(ExpressionEvaluator.GetValue(null, "sort()")); + + ISet set = new ListSet(arr); + Assert.AreEqual(new int[] { 6, 8, 14, 24 }, ExpressionEvaluator.GetValue(set, "sort()")); + } + + [Test(Description="sort supports any ICollection containing elements of uniform type")] + public void TestSortProcessorWithSimpleICollectionType() + { + Stack stack = new Stack(new int[] { 24, 8, 14, 6 }); + ExpressionEvaluator.GetValue(stack, "sort()"); + } + + [Test] + public void TestNonNullProcessor() + { + string[] arr2 = new string[] { "abc", "xyz", null, "abc", "def", null }; + Assert.AreEqual(new string[] { "abc", "xyz", "abc", "def" }, + ExpressionEvaluator.GetValue(arr2, "nonNull()")); + Assert.AreEqual(new string[] { "abc", "abc", "def", "xyz" }, + ExpressionEvaluator.GetValue(arr2, "nonNull().sort()")); + } + + [Test] + public void TestDistinctProcessor() + { + int[] arr = new int[] { 24, 8, 8, 6, 24, 6, 8, 6 }; + Assert.AreEqual(new int[] { 6, 8, 24 }, ExpressionEvaluator.GetValue(arr, "distinct().sort()")); + + string[] arr2 = new string[] { "abc", "xyz", "abc", "def", null, "def" }; + Assert.AreEqual(new string[] { null, "abc", "def", "xyz" }, + ExpressionEvaluator.GetValue(arr2, "distinct(true).sort()")); + Assert.AreEqual(new string[] { "abc", "def", "xyz" }, + ExpressionEvaluator.GetValue(arr2, "distinct(false).sort()")); + Assert.AreEqual(new string[] { "abc", "def", "xyz" }, + ExpressionEvaluator.GetValue(arr2, "distinct().sort()")); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestDistinctProcessorWithInvalidArgumentType() + { + int[] arr = new int[] { 24, 8, 8, 6, 24, 6, 8, 6 }; + ExpressionEvaluator.GetValue(arr, "distinct(6)"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestDistinctProcessorWithInvalidNumberOfArguments() + { + int[] arr = new int[] { 24, 8, 8, 6, 24, 6, 8, 6 }; + ExpressionEvaluator.GetValue(arr, "distinct(true, 4, 'xyz')"); + } + + [Test] + public void TestConversionProcessor() + { + object[] arr = new object[] { "0", 1, 1.1m, "1.1", 1.1f }; + decimal[] result = (decimal[]) ExpressionEvaluator.GetValue(arr, "convert(decimal)"); + Assert.AreEqual( 0.0m, result[0] ); + Assert.AreEqual(1.0m, result[1]); + Assert.AreEqual(1.1m, result[2]); + Assert.AreEqual(1.1m, result[3]); + Assert.AreEqual(1.1m, result[4]); + } + + [Test] + public void TestReverseProcessor() + { + object[] arr = new object[] { "0", 1, 2.1m, "3", 4.1f }; + object[] result = new ArrayList( (ICollection) ExpressionEvaluator.GetValue(arr, "reverse()") ).ToArray(); + Assert.AreEqual(new object[] { 4.1f, "3", 2.1m, 1, "0" }, result); + } + + #endregion + + /// + /// Type SetValue. + /// + [Test] + public void TestSetValue() + { + IDictionary vars = new Hashtable(); + vars["tesla"] = tesla; + vars["pupin"] = pupin; + ExpressionEvaluator.SetValue(null, "#tesla.Name", vars, "Tesla, Nikola"); + Assert.AreEqual("Tesla, Nikola", tesla.Name); + } + + /// + /// Tests property access with null in the path. + /// + [Test] + [ExpectedException(typeof(NullValueInNestedPathException))] + public void TestPropertyGetWithNullInThePath() + { + ExpressionEvaluator.GetValue(new Inventor(), "Name.Length"); + } + + /// + /// Tests property set with null in the path. + /// + [Test] + [ExpectedException(typeof(NullValueInNestedPathException))] + public void TestPropertySetWithNullInThePath() + { + ExpressionEvaluator.SetValue(new Inventor(), "Name.Length", 20); + } + + /// + /// Tries to set value of the PropertyOrFieldNode that represents type. + /// + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void TestTypeSet() + { + ExpressionEvaluator.SetValue(null, "DateTime", 20); + } + +#if NET_2_0 + + /// + /// Reproduce SPRNET-408. + /// + /// + /// http://opensource.atlassian.com/projects/spring/browse/SPRNET-408 + /// http://forum.springframework.net/showthread.php?t=933 + /// + [Test(Description = "Test to reproduce SPRNET-408")] + public void TestNullableTypes() + { + Foo foo = new Foo(); + Assert.IsNull(ExpressionEvaluator.GetValue(foo, "NullableDate")); + Assert.IsNull(ExpressionEvaluator.GetValue(foo, "NullableInt")); + + ExpressionEvaluator.SetValue(foo, "NullableDate", DateTime.Today); + ExpressionEvaluator.SetValue(foo, "NullableDate", null); + ExpressionEvaluator.SetValue(foo, "NullableDate", "2004-08-14"); + ExpressionEvaluator.SetValue(foo, "NullableDate", DateTime.Today); + ExpressionEvaluator.SetValue(foo, "NullableInt", 1); + ExpressionEvaluator.SetValue(foo, "NullableInt", null); + ExpressionEvaluator.SetValue(foo, "NullableInt", "5"); + ExpressionEvaluator.SetValue(foo, "NullableInt", 1); + + Assert.IsInstanceOfType(typeof(DateTime?), ExpressionEvaluator.GetValue(foo, "NullableDate")); + Assert.IsInstanceOfType(typeof(Int32?), ExpressionEvaluator.GetValue(foo, "NullableInt")); + + Assert.AreEqual(DateTime.Today, ExpressionEvaluator.GetValue(foo, "NullableDate")); + Assert.AreEqual(1, ExpressionEvaluator.GetValue(foo, "NullableInt")); + + int? test = 1; + Assert.IsInstanceOfType(typeof(Int32?), ExpressionEvaluator.GetValue(test, "#root")); + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(test, "#root != null")); + Assert.AreEqual(1, ExpressionEvaluator.GetValue(test, "#root")); + + test = null; + Assert.IsTrue((bool)ExpressionEvaluator.GetValue(test, "#root == null")); + Assert.IsNull(ExpressionEvaluator.GetValue(test, "#root")); + } +#endif + + /// + /// Reproduce SPRNET-462. + /// + /// + /// http://opensource.atlassian.com/projects/spring/browse/SPRNET-462 + /// http://forum.springframework.net/showthread.php?t=1515 + /// + [Test(Description = "Test to reproduce SPRNET-462")] + public void TestMethodResolutionWithNullArguments() + { + DateTime today = DateTime.Today; + Assert.AreEqual(today.ToString("d"), ExpressionEvaluator.GetValue(today, "ToString('d')")); + + TypeRegistry.RegisterType(typeof(TypeRegistry)); + try + { + ExpressionEvaluator.GetValue(null, "TypeRegistry.RegisterType(null)"); + Assert.Fail("Should throw ArgumentNullException"); + } + catch (ArgumentNullException) + { } + + try + { + ExpressionEvaluator.GetValue(null, "TypeRegistry.RegisterType(null, 'System.Object')"); + Assert.Fail("Should throw ArgumentNullException"); + } + catch (ArgumentNullException) + { } + + try + { + ExpressionEvaluator.GetValue(null, "TypeRegistry.RegisterType(null, null)"); + Assert.Fail("Should throw AmbiguousMatchException"); + } + catch (AmbiguousMatchException) + { } + + try + { + ExpressionEvaluator.GetValue(null, "TypeRegistry.RegisterType(null, null, null)"); + Assert.Fail("Should throw ArgumentException"); + } + catch (ArgumentException) + { } + + try + { + ExpressionEvaluator.GetValue(null, "TypeRegistry.RegisterType(int, string)"); + Assert.Fail("Should throw ArgumentException"); + } + catch (ArgumentException) + { } + } + + /// + /// Reproduce SPRNET-464. + /// + /// + /// http://opensource.atlassian.com/projects/spring/browse/SPRNET-464 + /// http://forum.springframework.net/showthread.php?t=1515 + /// + [Test(Description = "Test to reproduce SPRNET-464")] + public void TestMethodResolutionWithParamArray() + { + Foo foo = new Foo(); + Assert.AreEqual("a|b|c", foo.MethodWithArrayArgument(new string[] { "a", "b", "c" })); + Assert.AreEqual("a||c", foo.MethodWithArrayArgument(new string[] { "a", null, "c" })); + Assert.AreEqual("a|b|c", foo.MethodWithParamArray("a", "b", "c")); + Assert.AreEqual("a||c", foo.MethodWithParamArray("a", null, "c")); + + Assert.AreEqual("a|b|c", ExpressionEvaluator.GetValue(foo, "MethodWithArrayArgument(new string[] { 'a', 'b', 'c' })")); + Assert.AreEqual("a||c", ExpressionEvaluator.GetValue(foo, "MethodWithArrayArgument(new string[] { 'a', null, 'c' })")); + Assert.AreEqual("a|b|c", ExpressionEvaluator.GetValue(foo, "MethodWithParamArray('a', 'b', 'c')")); + Assert.AreEqual("a||c", ExpressionEvaluator.GetValue(foo, "MethodWithParamArray('a', null, 'c')")); + + Assert.AreEqual("a|b|c", ExpressionEvaluator.GetValue(foo, "MethodWithParamArray(false, 'a', 'b', 'c')")); + Assert.AreEqual("a||c", ExpressionEvaluator.GetValue(foo, "MethodWithParamArray(false, 'a', null, 'c')")); + Assert.AreEqual("A|B|C", ExpressionEvaluator.GetValue(foo, "MethodWithParamArray(true, 'a', 'b', 'c')")); + Assert.AreEqual("A||C", ExpressionEvaluator.GetValue(foo, "MethodWithParamArray(true, 'a', null, 'c')")); + } + + [Test] + public void TestMethodResolutionResolvesToExactMatchOfArgumentTypes() + { + Hashtable args = new Hashtable(); + args["bars"] = new Bar[] { new Bar() }; + Foo foo = new Foo(); + + // ensure noone changed our test class + Assert.AreEqual("ExactMatch", foo.MethodWithSimilarArguments(1, (Bar[])args["bars"])); + Assert.AreEqual("AssignableMatch", foo.MethodWithSimilarArguments(1, (ICollection)args["bars"])); + + Assert.AreEqual("ExactMatch", ExpressionEvaluator.GetValue(foo, "MethodWithSimilarArguments(1, #bars)", args)); + } + + [Test] + public void TestIndexerResolutionResolvesToExactMatchOfArgumentTypes() + { + Hashtable args = new Hashtable(); + args["bars"] = new Bar[] { new Bar() }; + Foo foo = new Foo(); + + // ensure noone changed our test class + Assert.AreEqual("ExactMatch", foo[(Bar[])args["bars"]]); + Assert.AreEqual("AssignableMatch", foo[(ICollection)args["bars"]]); + + Assert.AreEqual("ExactMatch", ExpressionEvaluator.GetValue(foo, "#root[#bars]", args)); + } + + + [Test] + public void TestCtorResolutionResolvesToExactMatchOfArgumentTypes() + { + TypeRegistry.RegisterType(typeof(Foo)); + Hashtable args = new Hashtable(); + args["bars"] = new Bar[] { new Bar() }; + + // ensure noone changed our test class + Foo foo1 = new Foo(1, (Bar[])args["bars"]); + try + { + Foo foo2 = new Foo(1, (ICollection)args["bars"]); + } + catch (InvalidOperationException) { } + + Assert.IsNotNull(ExpressionEvaluator.GetValue(null, "new Foo(1, #bars)", args)); + } + + [Test] + public void TestCtorResolutionWithParamArray() + { + TypeRegistry.RegisterType(typeof(Foo)); + Assert.IsNotNull(ExpressionEvaluator.GetValue(null, "new Foo('a', 'b', 'c')")); + Assert.IsNotNull(ExpressionEvaluator.GetValue(null, "new Foo('a', null, 'c')")); + Assert.IsNotNull(ExpressionEvaluator.GetValue(null, "new Foo(false, 'a', 'b', 'c')")); + Assert.IsNotNull(ExpressionEvaluator.GetValue(null, "new Foo(false, 'a', null, 'c')")); + } + + /// + /// Reproduce SPRNET-470. + /// + /// + /// http://opensource.atlassian.com/projects/spring/browse/SPRNET-470 + /// http://forum.springframework.net/showthread.php?t=1574 + /// + [Test(Description = "Test to reproduce SPRNET-470")] + public void TestPropertyAccessForTypes() + { + Assert.AreEqual(Int32.MaxValue, ExpressionEvaluator.GetValue(null, "Int32.MaxValue")); + Assert.AreEqual(Int32.MaxValue, ExpressionEvaluator.GetValue(null, "T(System.Int32).MaxValue")); + Assert.AreEqual(typeof(Int32).FullName, ExpressionEvaluator.GetValue(null, "Int32.FullName")); + Assert.AreEqual(typeof(Int32).FullName, ExpressionEvaluator.GetValue(null, "T(System.Int32).FullName")); + Assert.IsFalse((bool)ExpressionEvaluator.GetValue(null, "Int32.IsSubclassOf(Int64)")); + + TypeRegistry.RegisterType(typeof(FooType)); + Assert.AreEqual(FooType.One, ExpressionEvaluator.GetValue(null, "FooType.One")); + Assert.AreEqual(typeof(FooType).FullName, ExpressionEvaluator.GetValue(null, "FooType.FullName")); + } + + /// + /// Reproduce SPRNET-450 + /// Try to set/get numeric value from enum type property/field + /// + /// + /// http://opensource.atlassian.com/projects/spring/browse/SPRNET-450 + /// http://forum.springframework.net/showthread.php?t=1353 + /// + [Test] + public void TestSetEnumTypePropertyOrFieldFromNumeric() + { + //object val = Convert.ChangeType((Int16) 1, typeof(Int32)); + //Assert.AreEqual( typeof(Int32), val.GetType() ); + + IExpression expField = Expression.Parse("SampleEnumField"); + IExpression expProperty = Expression.Parse("SampleEnumProperty"); + IExpression expFlagsField = Expression.Parse("SampleFlagsEnumField"); + IExpression expFlagsProperty = Expression.Parse("SampleFlagsEnumProperty"); + + TestEnumTypePropertyClass o = new TestEnumTypePropertyClass(); + + // test field set operations + expField.SetValue(o, TestEnumTypePropertyClass.ESampleEnumType.Trunk); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, o.SampleEnumField); + + expField.SetValue(o, ((Int16)1)); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, o.SampleEnumField); + + expField.SetValue(o, ((Int32)1)); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, o.SampleEnumField); + + expField.SetValue(o, ((Int64)1)); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, o.SampleEnumField); + + // test property set operations + expProperty.SetValue(o, TestEnumTypePropertyClass.ESampleEnumType.Trunk); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, o.SampleEnumField); + + expProperty.SetValue(o, ((Int16)1)); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, o.SampleEnumField); + + expProperty.SetValue(o, ((Int32)1)); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, o.SampleEnumField); + + expProperty.SetValue(o, ((Int64)1)); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, o.SampleEnumField); + + expProperty.SetValue(o, "Trunk"); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, o.SampleEnumField); + + try + { + expProperty.SetValue(o, ((double)1.0)); + Assert.Fail("should throw"); + } + catch (TypeMismatchException e) + { + Assert.IsTrue(e.Message.StartsWith("Cannot convert property value of type [System.Double]")); + } + + try + { + expProperty.SetValue(o, ((float)1.0)); + Assert.Fail("should throw"); + } + catch (TypeMismatchException e) + { + Assert.IsTrue(e.Message.StartsWith("Cannot convert property value of type [System.Single]")); + } + + // test get operations + object val = expField.GetValue(o); + Assert.AreEqual(typeof(TestEnumTypePropertyClass.ESampleEnumType), val.GetType()); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, val); + + val = expProperty.GetValue(o); + Assert.AreEqual(typeof(TestEnumTypePropertyClass.ESampleEnumType), val.GetType()); + Assert.AreEqual(TestEnumTypePropertyClass.ESampleEnumType.Trunk, val); + + // test bitwise combined enum set + try + { + // not allowed since -1 is not defined in enum + expField.SetValue(o, -1); + Assert.Fail("should throw"); + } + catch (TypeMismatchException ex) + { + Assert.IsTrue( + ex.Message.StartsWith("Cannot convert property value of type [System.Int32] to required type")); + } + + try + { + // not allowed since -1 is not a representation of any bitwise combination of the enum's values. + expFlagsField.SetValue(o, -1); + Assert.Fail("should throw"); + } + catch (TypeMismatchException ex) + { + Assert.IsTrue( + ex.Message.StartsWith("Cannot convert property value of type [System.Int32] to required type")); + } + + expFlagsField.SetValue(o, + TestEnumTypePropertyClass.ESampleFlagsEnumType.SOME | + TestEnumTypePropertyClass.ESampleFlagsEnumType.SOMEOTHER); + Assert.AreEqual( + TestEnumTypePropertyClass.ESampleFlagsEnumType.SOME | + TestEnumTypePropertyClass.ESampleFlagsEnumType.SOMEOTHER, o.SampleFlagsEnumField); + } + + /// + /// Test to reproduce SPRNET-342, provided on the forum. + /// + /// + /// http://opensource.atlassian.com/projects/spring/browse/SPRNET-342 + /// http://forum.springframework.net/showthread.php?t=614 + /// + [Test] + public void ForumTestThread614() + { + TypeRegistry.RegisterType(typeof(Sample)); + + IExpression e = Expression.Parse("new Sample(O, T, H)"); + + Sample d1 = new Sample("A", "B", "C"); + Sample d2 = new Sample("A", "B", "Z"); + + Sample tmp1 = (Sample)e.GetValue(d1); + Assert.AreEqual("A", tmp1.O); + Assert.AreEqual("B", tmp1.T); + Assert.AreEqual("C", tmp1.H); + + Sample tmp2 = (Sample)e.GetValue(d2); + Assert.AreEqual("A", tmp2.O); + Assert.AreEqual("B", tmp2.T); + Assert.AreEqual("Z", tmp2.H); + } + + /// + /// More "to the point" test to reproduce SPRNET-342 + /// + /// + /// http://opensource.atlassian.com/projects/spring/browse/SPRNET-342 + /// http://forum.springframework.net/showthread.php?t=614 + /// + [Test] + public void RootContextChangeTest() + { + IExpression e = Expression.Parse("#root"); + + Assert.AreEqual("RootA", e.GetValue("RootA")); + Assert.AreEqual("RootB", e.GetValue("RootB")); + } + + /// + /// Hopefully detects threading issues during Expression evaluation + /// + /// + /// A single expression instance may be evaluated against different contexts on different threads. + /// + [Test] + public void ExpressionEvaluationIsThreadSafe() + { + IExpression exp = Expression.Parse("PlaceOfBirth.Country"); + + Inventor seovic = new Inventor("Aleksandar Seovic", new DateTime(1974, 08, 24), "Serbian"); + + AsyncTestTask t1 = new AsyncTestExpressionEvaluation(2000, exp, tesla, tesla.PlaceOfBirth.Country, null).Start(); + AsyncTestTask t2 = new AsyncTestExpressionEvaluation(2000, exp, pupin, pupin.PlaceOfBirth.Country, null).Start(); + AsyncTestTask t3 = new AsyncTestExpressionEvaluation(2000, exp, seovic, seovic.PlaceOfBirth.Country, null).Start(); + + IExpression exp2 = Expression.Parse("(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(#root))"); + + AsyncTestTask t4 = new AsyncTestExpressionEvaluation(2000, exp2, 5, 120, new Hashtable()).Start(); + AsyncTestTask t5 = new AsyncTestExpressionEvaluation(2000, exp2, 6, 720, new Hashtable()).Start(); + + t1.AssertNoException(); + t2.AssertNoException(); + t3.AssertNoException(); + t4.AssertNoException(); + t5.AssertNoException(); + } + + /// + /// Hopefully detects threading issues during Expression parsing + /// + [Test] + public void ExpressionParserIsThreadSafe() + { + AsyncTestTask t1 = new AsyncTestMethod(200, new ThreadStart(TestOperatorPrecedence)).Start(); + AsyncTestTask t2 = new AsyncTestMethod(200, new ThreadStart(TestOperatorPrecedence)).Start(); + AsyncTestTask t3 = new AsyncTestMethod(200, new ThreadStart(TestOperatorPrecedence)).Start(); + + t1.AssertNoException(); + t2.AssertNoException(); + t3.AssertNoException(); + } + + + /// + /// Checks if Expression Language array initializers work properly. + /// + [Test] + public void TestArrayConstructor() + { + object obj = ExpressionEvaluator.GetValue(null, "new int[] {3, 4, 5, 6}"); + Assert.IsNotNull(obj); + Assert.IsInstanceOfType(typeof(int[]), obj); + int[] intarray = (int[])obj; + Assert.AreEqual(4, intarray.Length); + for (int i = 0; i < intarray.Length; i++) + { + Assert.AreEqual(i + 3, intarray[i]); + } + + obj = ExpressionEvaluator.GetValue(null, "new long[5]"); + Assert.IsNotNull(obj); + Assert.IsInstanceOfType(typeof(long[]), obj); + long[] longarray = obj as long[]; + Assert.AreEqual(5, longarray.Length); + for (int i = 0; i < longarray.Length; i++) + { + Assert.AreEqual(0, longarray[i]); + } + + obj = ExpressionEvaluator.GetValue(null, "new double[4, 5]"); + Assert.IsNotNull(obj); + Assert.IsInstanceOfType(typeof(double[,]), obj); + double[,] twodimarray = obj as double[,]; + Assert.AreEqual(4 * 5, twodimarray.Length); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 5; j++) + { + Assert.AreEqual(0.0, twodimarray[i, j]); + } + } + + obj = ExpressionEvaluator.GetValue(null, "new int[Int32.Parse('11')]"); + Assert.IsNotNull(obj); + Assert.IsInstanceOfType(typeof(int[]), obj); + intarray = obj as int[]; + Assert.AreEqual(11, intarray.Length); + } + + [Test] + public void TestMethodArgumentNodesResolveAgainstThisContext() + { + IExpression exp; + + // case #root == #this - ToString() will be applied to #this + exp = Expression.Parse("long.Parse(ToString())"); + object result = exp.GetValue(100); + Assert.AreEqual((long)100, result); + + // case #root != #this in Projection - ToString() will be applied to #this + exp = Expression.Parse("(ToString(); #noop ={|val| $val}; !{#noop(ToString()) } )"); + result = exp.GetValue(new int[] { 100, 200 }, new Hashtable()); + Assert.AreEqual(new string[] { "100", "200" }, result); + + // case #root != #this in Selection - ToString() will be applied to #this + exp = Expression.Parse("(#noop ={|val| $val}; ?{#noop(ToString()=='100')} )"); + result = exp.GetValue(new int[] { 100, 200 }, new Hashtable()); + IList list = new ArrayList(); + list.Add(100); + Assert.AreEqual(list, result); + } + + [Test] + public void TestAccessVisibility() + { + AccessVisibilityCases cases = new AccessVisibilityCases(); + + try + { + ExpressionEvaluator.SetValue(cases, "_privateReadonlyField", "notsoreadonly"); + Assert.Fail("writing to readonly field should throw " + typeof(NotWritablePropertyException).FullName); + } + catch (NotWritablePropertyException) { } + + try + { + ExpressionEvaluator.SetValue(cases, "PrivateReadonlyProperty", "notsoreadonly"); + Assert.Fail("writing to readonly field should throw " + typeof(NotWritablePropertyException).FullName); + } + catch (NotWritablePropertyException) { } + + Assert.AreEqual("_privateField", ExpressionEvaluator.GetValue(cases, "_privateField")); + Assert.AreEqual("_protectedField", ExpressionEvaluator.GetValue(cases, "_protectedField")); + Assert.AreEqual("_publicField", ExpressionEvaluator.GetValue(cases, "_publicField")); + + Assert.AreEqual("PrivateProperty", ExpressionEvaluator.GetValue(cases, "PrivateProperty")); + Assert.AreEqual("ProtectedProperty", ExpressionEvaluator.GetValue(cases, "ProtectedProperty")); + Assert.AreEqual("PublicProperty", ExpressionEvaluator.GetValue(cases, "PublicProperty")); + + Assert.AreEqual("PrivateIndexer", ExpressionEvaluator.GetValue(cases, "#root[1]")); + Assert.AreEqual("ProtectedIndexer", ExpressionEvaluator.GetValue(cases, "#root[1.0]")); + Assert.AreEqual("PublicIndexer", ExpressionEvaluator.GetValue(cases, "#root['']")); + + Assert.AreEqual("PrivateMethod", ExpressionEvaluator.GetValue(cases, "GetPrivateMethod()")); + Assert.AreEqual("ProtectedMethod", ExpressionEvaluator.GetValue(cases, "GetProtectedMethod()")); + Assert.AreEqual("PublicMethod", ExpressionEvaluator.GetValue(cases, "GetPublicMethod()")); + } + + #region TestAccessVisibility Classes + + internal class AccessVisibilityCases + { + private readonly string _privateReadonlyField = "_privateReadonlyField"; + private string _privateField = "_privateField"; + protected string _protectedField = "_protectedField"; + public string _publicField = "_publicField"; + + + public AccessVisibilityCases() + { + Assert.IsTrue(_privateReadonlyField != string.Empty); + Assert.IsTrue(_privateField != string.Empty); + } + + private string PrivateReadonlyProperty { get { return "PrivateReadonlyProperty"; } } + private string PrivateProperty { get { return "PrivateProperty"; } } + protected string ProtectedProperty { get { return "ProtectedProperty"; } } + public string PublicProperty { get { return "PublicProperty"; } } + + private string this[int intindex] { get { return "PrivateIndexer"; } } + protected string this[double strindex] { get { return "ProtectedIndexer"; } } + public string this[string strindex] { get { return "PublicIndexer"; } } + + private string GetPrivateMethod() { return "PrivateMethod"; } + protected string GetProtectedMethod() { return "ProtectedMethod"; } + public string GetPublicMethod() { return "PublicMethod"; } + } + + #endregion + + #region TestMethodInvocation Classes + + class MethodInvokationCases + { + public string Foo(string stringArg) { return stringArg; } + public int Foo(int intArg) { return intArg; } + } + + #endregion + + #region Set operations tests + + [Test] + public void TestUnionOperator() + { + object o = ExpressionEvaluator.GetValue(null, "{1,2,3} + {3,4,5}"); + Assert.IsInstanceOfType(typeof(ISet), o); + ISet union = (ISet)o; + Assert.AreEqual(5, union.Count); + Assert.IsTrue(union.Contains(1)); + Assert.IsTrue(union.Contains(3)); + Assert.IsTrue(union.Contains(5)); + + o = ExpressionEvaluator.GetValue(null, "{1,2,3} + {3,4,5} + {'ivan', 'gox', 'damjao', 5}"); + Assert.IsInstanceOfType(typeof(ISet), o); + union = (ISet)o; + Assert.AreEqual(8, union.Count); + Assert.IsTrue(union.Contains(1)); + Assert.IsTrue(union.Contains("ivan")); + + ISet testset = new ListSet(); + testset.AddAll(new int[] { 1, 2, 3, 5, 8 }); + o = ExpressionEvaluator.GetValue(testset, "#this + {1, 2, 13, 15}"); + Assert.IsInstanceOfType(typeof(ISet), o); + union = (ISet)o; + Assert.AreEqual(7, union.Count); + Assert.IsTrue(union.Contains(1)); + Assert.IsTrue(union.Contains(15)); + + o = ExpressionEvaluator.GetValue(null, "#{1:'one', 2:'two', 3:'three'} + #{1:'ivan', 5:'five'}"); + Assert.IsInstanceOfType(typeof(IDictionary), o); + IDictionary result = (IDictionary)o; + Assert.AreEqual(4, result.Count); + Assert.AreEqual("one", result[1]); + Assert.AreEqual("five", result[5]); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestUnionOperatorBad() + { + object o = ExpressionEvaluator.GetValue(null, "#{1:'one', 2:'two', 3:'three'} + {1, 5}"); + Assert.IsInstanceOfType(typeof(IDictionary), o); + } + + [Test] + public void TestIntersectionOperator() + { + object o = ExpressionEvaluator.GetValue(null, "{111, 'ivan', 23, 24} * {111, 11, 'ivan'}"); + Assert.IsInstanceOfType(typeof(ISet), o); + ISet intersection = (ISet)o; + Assert.AreEqual(2, intersection.Count); + Assert.IsTrue(intersection.Contains(111)); + Assert.IsTrue(intersection.Contains("ivan")); + + o = ExpressionEvaluator.GetValue(null, "{24, 25, 'aaa' + 'bb'} * {date('2007/2/5').day * 5, 24 - 1}"); + Assert.IsInstanceOfType(typeof(ISet), o); + intersection = (ISet)o; + Assert.AreEqual(1, intersection.Count); + Assert.IsTrue(intersection.Contains(25)); + + ISet testset = new ListSet(); + testset.AddAll(new int[] { 1, 2, 3, 5, 8 }); + o = ExpressionEvaluator.GetValue(testset, "#this * #{1:'one', 10:'ten'}"); + Assert.IsInstanceOfType(typeof(ISet), o); + intersection = (ISet)o; + Assert.AreEqual(1, intersection.Count); + Assert.IsTrue(intersection.Contains(1)); + + o = ExpressionEvaluator.GetValue(null, "#{1:'one', 2:'two', 3:'three'} * #{1:'ivan', 5:'five'}"); + Assert.IsInstanceOfType(typeof(IDictionary), o); + IDictionary result = (IDictionary)o; + Assert.AreEqual(1, result.Count); + Assert.AreEqual("one", result[1]); + + o = ExpressionEvaluator.GetValue(null, "#{1:'one', 2:'two', 3:'three'} * {1, 2, 5, 7}"); + Assert.IsInstanceOfType(typeof(IDictionary), o); + result = (IDictionary)o; + Assert.AreEqual(2, result.Count); + Assert.AreEqual("one", result[1]); + Assert.AreEqual("two", result[2]); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestIntersectionOperatorBad() + { + object o = ExpressionEvaluator.GetValue(null, "#{1:'one', 2:'two', 3:'three'} * 'something'"); + Assert.IsInstanceOfType(typeof(IDictionary), o); + } + + [Test] + public void TestDifferenceOperator() + { + object o = ExpressionEvaluator.GetValue(null, "{111, 11} - {14, 12, 11}"); + Assert.IsInstanceOfType(typeof(ISet), o); + ISet diff = (ISet)o; + Assert.AreEqual(1, diff.Count); + Assert.IsTrue(diff.Contains(111)); + + o = ExpressionEvaluator.GetValue(null, "{111, 11} - {14, 12, 11} - {111}"); + Assert.IsInstanceOfType(typeof(ISet), o); + diff = (ISet)o; + Assert.AreEqual(0, diff.Count); + + ISet testset = new ListSet(); + testset.AddAll(new int[] { 1, 2, 3, 5, 8 }); + o = ExpressionEvaluator.GetValue(testset, "#this - #{1:'one', 10:'ten'}"); + Assert.IsInstanceOfType(typeof(ISet), o); + diff = (ISet)o; + Assert.AreEqual(4, diff.Count); + Assert.IsFalse(diff.Contains(1)); + + o = ExpressionEvaluator.GetValue(null, "#{1:'one', 2:'two', 3:'three'} - #{1:'ivan', 5:'five'}"); + Assert.IsInstanceOfType(typeof(IDictionary), o); + IDictionary result = (IDictionary)o; + Assert.AreEqual(2, result.Count); + Assert.IsNull(result[1]); + Assert.AreEqual("three", result[3]); + + o = ExpressionEvaluator.GetValue(null, "#{1:'one', 2:'two', 3:'three'} - {1, 2, 3, 5, 7}"); + Assert.IsInstanceOfType(typeof(IDictionary), o); + result = (IDictionary)o; + Assert.AreEqual(0, result.Count); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestDifferenceOperatorBad() + { + object o = ExpressionEvaluator.GetValue(null, "#{1:'one', 2:'two', 3:'three'} - 'something'"); + Assert.IsInstanceOfType(typeof(IDictionary), o); + } + + #endregion + + #region Performance tests + + private DateTime start, stop; + + //[Test] + public void PerformanceTests() + { + int n = 10000000; + object x = ""; + IDictionary vars = new Hashtable(); + + // tesla.PlaceOfBirth + start = DateTime.Now; + for (int i = 0; i < n; i++) + { + x = tesla.PlaceOfBirth; + } + stop = DateTime.Now; + PrintTest("tesla.PlaceOfBirth (direct)", n, Elapsed); + + // start = DateTime.Now; + // for (int i = 0; i < n; i++) + // { + // x = ExpressionEvaluator.GetValue(tesla, "PlaceOfBirth", vars); + // } + // stop = DateTime.Now; + // PrintTest("tesla.PlaceOfBirth (multi-parse)", n, Elapsed); + + start = DateTime.Now; + IExpression exp = Expression.Parse("PlaceOfBirth"); + for (int i = 0; i < n; i++) + { + x = exp.GetValue(tesla, vars); + } + stop = DateTime.Now; + PrintTest("tesla.PlaceOfBirth (single-parse)", n, Elapsed); + + // ieee.Officers['advisors'][0].Inventions[2] + start = DateTime.Now; + for (int i = 0; i < n; i++) + { + x = ((Inventor)((IList)ieee.Officers["advisors"])[0]).Inventions[2]; + } + stop = DateTime.Now; + PrintTest("ieee.Officers['advisors'][0].Inventions[2] (direct)", n, Elapsed); + + // start = DateTime.Now; + // for (int i = 0; i < n / 10; i++) + // { + // x = ExpressionEvaluator.GetValue(ieee, "Officers['advisors'][0].Inventions[2]", vars); + // } + // stop = DateTime.Now; + // PrintTest("ieee.Officers['advisors'][0].Inventions[2] (multi-parse)", n / 10, Elapsed); + + start = DateTime.Now; + exp = Expression.Parse("Officers['advisors'][0].Inventions[2]"); + for (int i = 0; i < n; i++) + { + x = exp.GetValue(ieee, vars); + } + stop = DateTime.Now; + PrintTest("ieee.Officers['advisors'][0].Inventions[2] (single-parse)", n, Elapsed); + + x.ToString(); + } + + private double Elapsed + { + get { return (stop.Ticks - start.Ticks) / 10000000f; } + } + + private static void PrintTest(string name, int iterations, double duration) + { + Debug.WriteLine( + String.Format("{0,-60} {1,12:#,###} {2,12:##0.000} {3,12:#,###}", name, iterations, duration, + iterations / duration)); + } + + #endregion + + #region Method Inheritance tests + + [Test] + public void TestInheritedMethodInvocation() + { + DerivedSingleMethodTestClass testClass = new DerivedSingleMethodTestClass(); + Assert.AreEqual("Hello World", ExpressionEvaluator.GetValue(testClass, "#root.GetString()")); + } + + [Test] + public void TestStaticInheritedMethodInvocation() + { + Assert.AreEqual("Hello Static World from SingleMethodTestClass", DerivedSingleMethodTestClass.StaticMethod()); + Assert.AreEqual("Hello Static World from SingleMethodTestClass", ExpressionEvaluator.GetValue(null, string.Format("T({0}).StaticMethod()", typeof(DerivedSingleMethodTestClass).FullName))); + + Assert.AreEqual("SingleMethodTestClass.ShadowedStaticMethod", SingleMethodTestClass.ShadowedStaticMethod()); + Assert.AreEqual("DerivedSingleMethodTestClass.ShadowedStaticMethod", DerivedSingleMethodTestClass.ShadowedStaticMethod()); + Assert.AreEqual("SingleMethodTestClass.ShadowedStaticMethod", ExpressionEvaluator.GetValue(null, string.Format("T({0}).ShadowedStaticMethod()", typeof(SingleMethodTestClass).FullName))); + Assert.AreEqual("DerivedSingleMethodTestClass.ShadowedStaticMethod", ExpressionEvaluator.GetValue(null, string.Format("T({0}).ShadowedStaticMethod()", typeof(DerivedSingleMethodTestClass).FullName))); + } + + #endregion + + private static void DumpNode(AST rootNode, int level) + { + Trace.WriteLine(new string(' ', level) + rootNode.ToString()); + + int numberOfChildren = rootNode.getNumberOfChildren(); + if (numberOfChildren > 0) + { + AST node = rootNode.getFirstChild(); + while (node != null) + { + DumpNode(node, level + 2); + node = node.getNextSibling(); + } + } + } + } + + #region Helper classes + + internal class SingleMethodTestClass + { + protected static string GetMethodName(MethodBase method) + { + return method.DeclaringType.Name + "." + method.Name; + } + + public static string StaticMethod() + { + return "Hello Static World from SingleMethodTestClass"; + } + + public static string ShadowedStaticMethod() + { + return GetMethodName(MethodInfo.GetCurrentMethod()); + } + + public string GetString() + { + return "Hello World"; + } + } + + internal class DerivedSingleMethodTestClass : SingleMethodTestClass + { + public new static string ShadowedStaticMethod() + { + return GetMethodName(MethodInfo.GetCurrentMethod()); + } + } + + internal class TestObjectContainer + { + private TestObject testObject; + + public TestObject TestObject + { + get { return this.testObject; } + set { this.testObject = value; } + } + } + + internal class TestEnumTypePropertyClass + { + [Flags] + internal enum ESampleFlagsEnumType : int + { + NONE = 0, + SOME = 1, + SOMEOTHER = 2, + } + + internal enum ESampleEnumType : int + { + Van = 0, + Trunk = 1, + Air = 2 + } + + public ESampleFlagsEnumType SampleFlagsEnumField; + public ESampleEnumType SampleEnumField; + + public ESampleFlagsEnumType SampleFlagsEnumProperty + { + get { return SampleFlagsEnumField; } + set { SampleFlagsEnumField = value; } + } + + public ESampleEnumType SampleEnumProperty + { + get { return SampleEnumField; } + set { SampleEnumField = value; } + } + } + + internal sealed class Bar + { + private int[] numbers = new int[] { 1, 2, 3 }; + + public int this[int index] + { + get { return numbers[index]; } + } + } + + internal class Foo + { + private FooType type; +#if NET_2_0 + private Nullable nullableDate; + private Nullable nullableInt; +#endif + public Foo() + : this(FooType.One) + { + } + + public Foo(FooType type) + { + this.type = type; + } + + public Foo(params string[] values) + { + } + + public Foo(bool flag, params string[] values) + { + } + + public Foo(int flag, Bar[] bars) + { + } + + public Foo(int flag, ICollection bars) + { + throw new InvalidOperationException("should have selected ctor(int, Bar[])"); + } + + public string this[Bar[] bars] + { + get { return "ExactMatch"; } + } + + public string this[ICollection bars] + { + get { return "AssignableMatch"; } + } + + public object this[int foo, string key] + { + get { return key + "_" + foo; } + } + + public FooType Type + { + get { return type; } + } + +#if NET_2_0 + + public DateTime? NullableDate + { + get { return nullableDate; } + set { nullableDate = value; } + } + + public int? NullableInt + { + get { return nullableInt; } + set { nullableInt = value; } + } +#endif + + public string MethodWithSimilarArguments(int flags, Bar[] bars) + { + return "ExactMatch"; + } + + public string MethodWithSimilarArguments(int flags, ICollection bar) + { + return "AssignableMatch"; + } + + public string MethodWithArrayArgument(string[] values) + { + return string.Join("|", values); + } + + public string MethodWithParamArray(params string[] values) + { + return string.Join("|", values); + } + + public string MethodWithParamArray(bool uppercase, params string[] values) + { + string ret = string.Join("|", values); + return (uppercase ? ret.ToUpper() : ret); + } + } + + internal enum FooType + { + One, + Two, + Three + } + + internal class Sample + { + public string O; + public string T; + public string H; + + public Sample(string o, string t, string h) + { + O = o; + T = t; + H = h; + } + } + + #endregion + + #region Shadowing Test Helper Classes + + internal class ShadowingTestsBaseClass + { + private object _someValue; + private object _readonlyShadowedValue; + private object _writeonlyShadowedValue; + + public object SomeValue + { + get { return _someValue; } + set { _someValue = value; } + } + + public object ReadonlyShadowedValue + { + get { return _readonlyShadowedValue; } + set { _readonlyShadowedValue = value; } + } + + public object WriteonlyShadowedValue + { + get { return _writeonlyShadowedValue; } + set { _writeonlyShadowedValue = value; } + } + } + + internal class ShadowingTestsSpezializedClass : ShadowingTestsBaseClass + { + public new string SomeValue + { + get { return (string)base.SomeValue; } + set { base.SomeValue = value; } + } + + public new string ReadonlyShadowedValue + { + get { return (string)base.ReadonlyShadowedValue; } + } + + public new string WriteonlyShadowedValue + { + set { base.WriteonlyShadowedValue = value; } + } + } + + internal class ShadowingTestsMoreSpezializedClass : ShadowingTestsSpezializedClass + { + } + + internal class ShadowingTestsMostSpezializedClass : ShadowingTestsMoreSpezializedClass + { + } + + #endregion // Shadowing Test Helper Classes + + +} diff --git a/test/Spring/Spring.Core.Tests/Expressions/FunctionNodeTests.cs b/test/Spring/Spring.Core.Tests/Expressions/FunctionNodeTests.cs new file mode 100644 index 00000000..da59a1ab --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Expressions/FunctionNodeTests.cs @@ -0,0 +1,77 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; + +#endregion + +namespace Spring.Expressions +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: FunctionNodeTests.cs,v 1.1 2008/03/20 23:58:16 oakinger Exp $ + [TestFixture] + public class FunctionNodeTests + { + [Test] + public void ExecutesLambdaFunction() + { + Hashtable vars = new Hashtable(); + Expression.RegisterFunction("ident", "{|n| $n}", vars); + + FunctionNode fn = new FunctionNode(); + fn.Text = "ident"; + StringLiteralNode str = new StringLiteralNode(); + str.Text = "theValue"; + fn.addChild(str); + + IExpression exp = fn; + Assert.AreEqual(str.Text, exp.GetValue(null, vars)); + } + + [Test] + public void ExecutesDelegate() + { + Hashtable vars = new Hashtable(); + vars["ident"] = new IdentityCallback(Identity); + + FunctionNode fn = new FunctionNode(); + fn.Text = "ident"; + StringLiteralNode str = new StringLiteralNode(); + str.Text = "theValue"; + fn.addChild(str); + + IExpression exp = fn; + Assert.AreEqual(str.Text, exp.GetValue(null, vars)); + } + + private delegate object IdentityCallback(object arg); + private object Identity(object arg) + { + return arg; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Expressions/MethodNodeTests.cs b/test/Spring/Spring.Core.Tests/Expressions/MethodNodeTests.cs new file mode 100644 index 00000000..a9e886fa --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Expressions/MethodNodeTests.cs @@ -0,0 +1,144 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; +using Spring.Expressions.Processors; + +#endregion + +namespace Spring.Expressions +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: MethodNodeTests.cs,v 1.2 2008/03/20 23:58:16 oakinger Exp $ + [TestFixture] + public class MethodNodeTests + { + private class MyTestCollectionProcessor : ICollectionProcessor + { + public object Process(ICollection source, object[] args) + { + return source; + } + } + + [Test] + public void CallCustomCollectionProcessor() + { + Hashtable vars = new Hashtable(); + vars["myCollProc"] = new MyTestCollectionProcessor(); + + MethodNode mn = new MethodNode(); + mn.Text = "myCollProc"; + + IExpression exp = mn; + int[] input = new int[] {1, 2, 3}; + Assert.AreSame(input, exp.GetValue(input, vars)); + } + + [Test, Explicit] + public void PerformanceOfMethodEvaluationOnDifferentContextTypes() + { + MethodNode mn = new MethodNode(); + mn.Text = "ToString"; + + TypeNode nln = new TypeNode(); + nln.Text = "System.Globalization.CultureInfo"; + + PropertyOrFieldNode pn = new PropertyOrFieldNode(); + pn.Text = "InvariantCulture"; + + + Expression exp = new Expression(); + exp.addChild(nln); + exp.addChild(pn); + + StringLiteralNode sln = new StringLiteralNode(); + sln.Text = "dummy"; + + mn.addChild(sln); + mn.addChild(exp); + + IExpression mnExp = mn; + Assert.AreEqual("dummy", mnExp.GetValue(0m, null)); + + int runs = 10000000; + + StopWatch watch = new StopWatch(); + using (watch.Start("Duration: {0}")) + { + for (int i = 0; i < runs; i++) + { + mnExp.GetValue(0m, null); + } + } + } + + #region StopWatch + + private class StopWatch + { + private DateTime _startTime; + private TimeSpan _elapsed; + + private class Stopper : IDisposable + { + private readonly StopWatch _owner; + private readonly string _format; + public Stopper(StopWatch owner, string format) { _owner = owner; _format = format; } + public void Dispose() { _owner.Stop(_format); GC.SuppressFinalize(this); } + } + + public IDisposable Start(string outputFormat) + { + Stopper stopper = new Stopper(this, outputFormat); + _startTime = DateTime.Now; + return stopper; + } + + private void Stop(string outputFormat) + { + _elapsed = DateTime.Now.Subtract(_startTime); + if (outputFormat != null) + { + Console.WriteLine(outputFormat, _elapsed); + } + } + + public DateTime StartTime + { + get { return _startTime; } + } + + public TimeSpan Elapsed + { + get { return _elapsed; } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Expressions/Processors/ConversionProcessorTests.cs b/test/Spring/Spring.Core.Tests/Expressions/Processors/ConversionProcessorTests.cs new file mode 100644 index 00000000..82cc91c6 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Expressions/Processors/ConversionProcessorTests.cs @@ -0,0 +1,97 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; +using Spring.Core.TypeConversion; +using Spring.Globalization; + +#endregion + +namespace Spring.Expressions.Processors +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: ConversionProcessorTests.cs,v 1.1 2008/03/20 23:58:17 oakinger Exp $ + [TestFixture] + public class ConversionProcessorTests + { + [TestFixtureSetUp] + public void FixtureSetUp() + { + CultureTestScope.Set(); + } + + [TestFixtureTearDown] + public void FixtureTearDown() + { + CultureTestScope.Reset(); + } + + [Test] + public void RequiresTypeArgument() + { + ConversionProcessor cp = new ConversionProcessor(); + try + { + cp.Process(new object[] {0, 1}, null); + Assert.Fail("should throw"); + } + catch (ArgumentNullException) + { + } + } + + [Test] + public void ReturnsListAsIsIfNullOrEmpty() + { + ConversionProcessor cp = new ConversionProcessor(); + object result; + result = cp.Process(null, new object[] { typeof(int) }); + Assert.IsNull(result); + ICollection input = new object[] {}; + result = cp.Process(input, new object[] { typeof(int) }); + Assert.AreEqual( input, result ); + } + + [Test] + public void ReturnsTypedArray() + { + ConversionProcessor cp = new ConversionProcessor(); + object result = cp.Process( new object[] { 0, 1 }, new object[] { typeof(int) }); + Assert.IsTrue( result is int[] ); + } + + [Test] + public void UsesTypeConverterRegistryForConversion() + { + ConversionProcessor cp = new ConversionProcessor(); + ICollection result = (ICollection) cp.Process(new object[] { "0", 1, 1.1m, "1.1", 1.1f }, new object[] { typeof(decimal) }); + decimal sum = 0; + foreach (decimal element in result) sum += element; + Assert.AreEqual( 4.3f, sum ); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Expressions/Processors/OrderByProcessorTests.cs b/test/Spring/Spring.Core.Tests/Expressions/Processors/OrderByProcessorTests.cs new file mode 100644 index 00000000..5f9d5f9a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Expressions/Processors/OrderByProcessorTests.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; + +#endregion + +namespace Spring.Expressions.Processors +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: OrderByProcessorTests.cs,v 1.1 2008/03/20 23:58:17 oakinger Exp $ + [TestFixture] + public class OrderByProcessorTests + { + [Test] + public void OrderByExpressionString() + { + IExpression exp = Expression.Parse("orderBy('ToString()')"); + object[] input = new object[] { 'b', 1, 2.0, "a" }; + + Assert.AreEqual( new object[] { 1,2.0,"a",'b' }, exp.GetValue(input) ); + } + + [Test] + public void OrderByLambdaFunction() + { + IExpression exp = Expression.Parse("orderBy({|a,b| $a.ToString().CompareTo($b.ToString())})"); + object[] input = new object[] { 'b', 1, 2.0, "a" }; + + Assert.AreEqual(new object[] { 1, 2.0, "a", 'b' }, exp.GetValue(input)); + + Hashtable vars = new Hashtable(); + Expression.RegisterFunction( "compare", "{|a,b| $a.ToString().CompareTo($b.ToString())}", vars); + exp = Expression.Parse("orderBy(#compare)"); + Assert.AreEqual(new object[] { 1, 2.0, "a", 'b' }, exp.GetValue(input, vars)); + } + + [Test] + public void OrderByDelegate() + { + Hashtable vars = new Hashtable(); + vars["compare"] = new CompareCallback(CompareObjects); + + IExpression exp = Expression.Parse("orderBy(#compare)"); + object[] input = new object[] { 'b', 1, 2.0, "a" }; + + Assert.AreEqual(new object[] { 1, 2.0, "a", 'b' }, exp.GetValue(input, vars)); + } + + private delegate int CompareCallback(object x, object y); + private int CompareObjects(object x, object y) + { + if (x == y) return 0; + return x.ToString().CompareTo(""+y); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Expressions/ReferenceNodeTests.cs b/test/Spring/Spring.Core.Tests/Expressions/ReferenceNodeTests.cs new file mode 100644 index 00000000..0cff296f --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Expressions/ReferenceNodeTests.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Expressions +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: ReferenceNodeTests.cs,v 1.1 2008/03/20 10:35:55 oakinger Exp $ + [TestFixture] + public class ReferenceNodeTests + { + public class MyTestObject + { + public object MyField; + } + + [Test] + public void DoesntCallContextRegistryForLocalObjectFactoryReferences() + { + string xml = string.Format( + @" + + + + +" + , typeof(MyTestObject).AssemblyQualifiedName + ); + + XmlObjectFactory of = new XmlObjectFactory(new StringResource(xml, Encoding.UTF8)); + object theObject = new object(); + of.RegisterSingleton("theObject", theObject); + + MyTestObject to = (MyTestObject) of.GetObject("foo"); + Assert.AreSame( theObject, to.MyField ); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Expressions/SelectionNodeTests.cs b/test/Spring/Spring.Core.Tests/Expressions/SelectionNodeTests.cs new file mode 100644 index 00000000..3621f721 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Expressions/SelectionNodeTests.cs @@ -0,0 +1,65 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; + +#endregion + +namespace Spring.Expressions +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: SelectionNodeTests.cs,v 1.1 2008/03/25 22:33:04 oakinger Exp $ + [TestFixture] + public class SelectionNodeTests + { + [Test] + public void RespectsLimits() + { + char[] input = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n' }; + char[] result; + + result = Evaluate("?{ true }", input); + Assert.AreEqual(input, result); + result = Evaluate("?{ true, 5, 10 }", input); + Assert.AreEqual( new char[] { 'f', 'g', 'h', 'i', 'j', 'k' }, result ); + result = Evaluate("?{ true, 5 }", input); + Assert.AreEqual(new char[] { 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n' }, result); + result = Evaluate("?{ true, 0, 10 }", input); + Assert.AreEqual(new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k' }, result); + + result = Evaluate("?{ T(System.Convert).ToInt32(#this) % 2 == 0, 1, 3 }", input); + Assert.AreEqual(new char[] { 'd', 'f', 'h' }, result); + } + + private char[] Evaluate(string expression, char[] input) + { + IExpression expr = Expression.Parse(expression); + ICollection collection = (ICollection)expr.GetValue(input); + return (char[]) new ArrayList(collection).ToArray(typeof(char)); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Globalization/AbstractLocalizerTests.cs b/test/Spring/Spring.Core.Tests/Globalization/AbstractLocalizerTests.cs new file mode 100644 index 00000000..4852377b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/AbstractLocalizerTests.cs @@ -0,0 +1,154 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; + +using NUnit.Framework; + +using Spring.Context; + +#endregion + +namespace Spring.Globalization +{ + /// + /// Unit tests for the localizers. + /// + /// Aleksandar Seovic + /// $Id: AbstractLocalizerTests.cs,v 1.5 2007/07/24 17:26:47 oakinger Exp $ + public abstract class AbstractLocalizerTests + { + private ILocalizer localizer; + + [TestFixtureSetUp] + public void TestFixtureSetUp() + { + CultureTestScope.Set("de-AT", "sr"); + } + + [TestFixtureTearDown] + public void TestFixtureTearDown() + { + CultureTestScope.Reset(); + } + + [SetUp] + public void Init() + { + localizer = CreateLocalizer(); + } + + [Test] + public void TestInvariantCulture() + { + Inventor tesla = CreateInventor(CultureInfo.InvariantCulture); + Assert.AreEqual("Nikola Tesla", tesla.Name); + Assert.AreEqual("Serbian", tesla.Nationality); + Assert.AreEqual(new DateTime(1856, 7, 9), tesla.DOB); + Assert.AreEqual("Croatia", tesla.PlaceOfBirth.Country); + Assert.AreEqual("Smiljan", tesla.PlaceOfBirth.City); + } + + [Test] + public void TestSerbianLatin() + { + Inventor tesla = CreateInventor(new CultureInfo(CultureInfoUtils.SerbianLatinCultureName)); + Assert.AreEqual("Nikola Tesla", tesla.Name); + Assert.AreEqual("Srbin", tesla.Nationality); + Assert.AreEqual(new DateTime(1856, 7, 9), tesla.DOB); + Assert.AreEqual("Hrvatska", tesla.PlaceOfBirth.Country); + Assert.AreEqual("Smiljan", tesla.PlaceOfBirth.City); + } + + [Test] + public void TestSerbianCyrillic() + { + Inventor tesla = CreateInventor(new CultureInfo(CultureInfoUtils.SerbianCyrillicCultureName)); + Assert.AreEqual("Ðикола ТеÑла", tesla.Name); + Assert.AreEqual("Србин", tesla.Nationality); + Assert.AreEqual(new DateTime(1856, 7, 9), tesla.DOB); + Assert.AreEqual("ХрватÑка", tesla.PlaceOfBirth.Country); + Assert.AreEqual("Смиљан", tesla.PlaceOfBirth.City); + } + + [Test] + public void NullReferenceHandling() + { +// ResourceSetMessageSource messageSource = new ResourceSetMessageSource(); +// messageSource.ResourceManagers.Add(new ResourceManager("Spring.Resources.Tesla", GetType().Assembly)); +// ResourceSetLocalizer localizer = new ResourceSetLocalizer(); + IMessageSource messageSource = CreateMessageSource(); + + // target must not be null + try + { + localizer.ApplyResources(null, messageSource, CultureInfo.InvariantCulture); + Assert.Fail(); + } + catch (ArgumentNullException) { } + try + { + localizer.ApplyResources(null, messageSource); + Assert.Fail(); + } + catch (ArgumentNullException) { } + + // messageSource may be null + localizer.ApplyResources(new object(), null); + localizer.ApplyResources(new object(), null, CultureInfo.InvariantCulture); + + try + { + localizer.ApplyResources(new object(), messageSource, null); + Assert.Fail(); + } + catch (ArgumentNullException) { } + } + + [Test] + public void DefaultResolvesUsingCurrentUICulture() + { + Inventor tesla = CreateInventor(null); + Assert.AreEqual("Nikola Tesla", tesla.Name); + Assert.AreEqual("Srbin", tesla.Nationality); + } + + private Inventor CreateInventor(CultureInfo culture) + { + Inventor inventor = new Inventor(); + IMessageSource messageSource = CreateMessageSource(); + if (culture == null) + { + localizer.ApplyResources(inventor, messageSource); + } + else + { + localizer.ApplyResources(inventor, messageSource, culture); + } + return inventor; + } + + protected abstract ILocalizer CreateLocalizer(); + protected abstract IMessageSource CreateMessageSource(); + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Globalization/CultureInfoUtils.cs b/test/Spring/Spring.Core.Tests/Globalization/CultureInfoUtils.cs new file mode 100644 index 00000000..eec624ae --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/CultureInfoUtils.cs @@ -0,0 +1,61 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Globalization; + +namespace Spring.Globalization +{ + /// + /// Helper class to pick the appropriate culture name for serbian due to changes of the CultureInfo classes + /// in recent releases of the .NET framework. + /// + /// In short, sr-SP-Latn->sr-Latn-CS and sr-SP-Cyrl->sr-Cyrl-CS. See http://blogs.msdn.com/kierans/archive/2006/08/02/687267.aspx + /// and http://blogs.msdn.com/shawnste/archive/2006/11/14/problems-compiling-resources-in-net-2-0-apps-after-updates.aspx for + /// additional information. + /// Mark Pollack + public class CultureInfoUtils + { + private static string srLatn = "sr-SP-Latn"; + private static string srCyrl = "sr-SP-Cyrl"; + + static CultureInfoUtils() + { + foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures)) + { + if (ci.Name.Equals("sr-Latn-CS")) + { + srLatn = "sr-Latn-CS"; + srCyrl = "sr-Cyrl-CS"; + break; + } + } + } + + public static string SerbianCyrillicCultureName + { + get { return srCyrl; } + } + + public static string SerbianLatinCultureName + { + get { return srLatn; } + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Globalization/CultureTestScope.cs b/test/Spring/Spring.Core.Tests/Globalization/CultureTestScope.cs new file mode 100644 index 00000000..e6fbd5ca --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/CultureTestScope.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Threading; + +#endregion + +namespace Spring.Globalization +{ + /// + /// Helps setting/resetting current thread cultures. + /// + /// Erich Eichinger + /// $Id: CultureTestScope.cs,v 1.1 2007/07/24 17:26:47 oakinger Exp $ + public class CultureTestScope : IDisposable + { + [ThreadStatic] + private static CultureTestScope s_currentScope; + + public static void Set() + { + Set("en-GB", "de-DE"); + } + + public static void Set(string culture, string uiCulture) + { + s_currentScope = new CultureTestScope(culture, uiCulture); + } + + public static void Reset() + { + CultureTestScope scope = s_currentScope; s_currentScope = null; + scope.Dispose(); + } + + private readonly CultureInfo _prevCulture; + private readonly CultureInfo _prevUICulture; + + private CultureTestScope(string culture, string uiCulture) + { + this._prevCulture = Thread.CurrentThread.CurrentCulture; + this._prevUICulture = Thread.CurrentThread.CurrentUICulture; + + Thread.CurrentThread.CurrentCulture = new CultureInfo(culture); + Thread.CurrentThread.CurrentUICulture = new CultureInfo(uiCulture); + } + + void IDisposable.Dispose() + { + this.Dispose(); + } + + private void Dispose() + { + Thread.CurrentThread.CurrentCulture = this._prevCulture; + Thread.CurrentThread.CurrentUICulture = this._prevUICulture; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Globalization/Formatters/BooleanFormatterTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Formatters/BooleanFormatterTests.cs new file mode 100644 index 00000000..e69de29b diff --git a/test/Spring/Spring.Core.Tests/Globalization/Formatters/CurrencyFormatterTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Formatters/CurrencyFormatterTests.cs new file mode 100644 index 00000000..ec9f4b09 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/Formatters/CurrencyFormatterTests.cs @@ -0,0 +1,160 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using NUnit.Framework; + +namespace Spring.Globalization.Formatters +{ + /// + /// Unit tests for CurrencyFormatter class. + /// + /// Aleksandar Seovic + /// $Id: CurrencyFormatterTests.cs,v 1.3 2007/05/04 18:13:22 markpollack Exp $ + [TestFixture] + public class CurrencyFormatterTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void FormatNullValue() + { + CurrencyFormatter fmt = new CurrencyFormatter(); + fmt.Format(null); + } + + [Test] + public void ParseNullOrEmptyValue() + { + CurrencyFormatter fmt = new CurrencyFormatter(); + Assert.AreEqual(0, fmt.Parse(null)); + Assert.IsTrue(fmt.Parse("") is double); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void FormatNonNumber() + { + CurrencyFormatter fmt = new CurrencyFormatter(); + fmt.Format("not a number"); + } + + [Test] + public void FormatUsingDefaults() + { + CurrencyFormatter fmt = new CurrencyFormatter("en-US"); + Assert.AreEqual("$1,234.00", fmt.Format(1234)); + Assert.AreEqual("$1,234.56", fmt.Format(1234.56)); + Assert.AreEqual("($1,234.00)", fmt.Format(-1234)); + Assert.AreEqual("($1,234.56)", fmt.Format(-1234.56)); + + fmt = new CurrencyFormatter(CultureInfoUtils.SerbianLatinCultureName); + Assert.AreEqual("1.234,00 Din.", fmt.Format(1234)); + Assert.AreEqual("1.234,56 Din.", fmt.Format(1234.56)); + Assert.AreEqual("-1.234,00 Din.", fmt.Format(-1234)); + Assert.AreEqual("-1.234,56 Din.", fmt.Format(-1234.56)); + + fmt = new CurrencyFormatter(CultureInfoUtils.SerbianCyrillicCultureName); + Assert.AreEqual("1.234,00 Дин.", fmt.Format(1234)); + Assert.AreEqual("1.234,56 Дин.", fmt.Format(1234.56)); + Assert.AreEqual("-1.234,00 Дин.", fmt.Format(-1234)); + Assert.AreEqual("-1.234,56 Дин.", fmt.Format(-1234.56)); + } + + [Test] + public void ParseUsingDefaults() + { + CurrencyFormatter fmt = new CurrencyFormatter("en-US"); + Assert.AreEqual(1234, fmt.Parse("$1,234.00")); + Assert.AreEqual(1234.56, fmt.Parse("$1,234.56")); + Assert.AreEqual(-1234, fmt.Parse("($1,234.00)")); + Assert.AreEqual(-1234.56, fmt.Parse("($1,234.56)")); + + fmt = new CurrencyFormatter(CultureInfoUtils.SerbianLatinCultureName); + Assert.AreEqual(1234, fmt.Parse("1.234,00 Din.")); + Assert.AreEqual(1234.56, fmt.Parse("1.234,56 Din.")); + Assert.AreEqual(-1234, fmt.Parse("-1.234,00 Din.")); + Assert.AreEqual(-1234.56, fmt.Parse("-1.234,56 Din.")); + + fmt = new CurrencyFormatter(CultureInfoUtils.SerbianCyrillicCultureName); + Assert.AreEqual(1234, fmt.Parse("1.234,00 Дин.")); + Assert.AreEqual(1234.56, fmt.Parse("1.234,56 Дин.")); + Assert.AreEqual(-1234, fmt.Parse("-1.234,00 Дин.")); + Assert.AreEqual(-1234.56, fmt.Parse("-1.234,56 Дин.")); + } + + [Test] + public void FormatUsingCustomSettings() + { + CurrencyFormatter fmt = new CurrencyFormatter("en-US"); + fmt.DecimalDigits = 0; + fmt.NegativePattern = 1; + Assert.AreEqual("$1,234", fmt.Format(1234)); + Assert.AreEqual("$1,235", fmt.Format(1234.56)); + Assert.AreEqual("-$1,234", fmt.Format(-1234)); + Assert.AreEqual("-$1,235", fmt.Format(-1234.56)); + + fmt = new CurrencyFormatter(CultureInfoUtils.SerbianLatinCultureName); + fmt.PositivePattern = 1; + fmt.CurrencySymbol = "din"; + Assert.AreEqual("1.234,00din", fmt.Format(1234)); + Assert.AreEqual("1.234,56din", fmt.Format(1234.56)); + Assert.AreEqual("-1.234,00 din", fmt.Format(-1234)); + Assert.AreEqual("-1.234,56 din", fmt.Format(-1234.56)); + + fmt = new CurrencyFormatter(CultureInfoUtils.SerbianCyrillicCultureName); + fmt.GroupSizes = new int[] {1, 2}; + fmt.GroupSeparator = "'"; + Assert.AreEqual("1'23'4,00 Дин.", fmt.Format(1234)); + Assert.AreEqual("1'23'4,56 Дин.", fmt.Format(1234.56)); + Assert.AreEqual("-1'23'4,00 Дин.", fmt.Format(-1234)); + Assert.AreEqual("-1'23'4,56 Дин.", fmt.Format(-1234.56)); + } + + [Test] + public void ParseUsingCustomSettings() + { + CurrencyFormatter fmt = new CurrencyFormatter("en-US"); + fmt.DecimalDigits = 0; + fmt.NegativePattern = 1; + Assert.AreEqual(1234, fmt.Parse("$1,234")); + Assert.AreEqual(1234.56, fmt.Parse("$1,234.56")); + Assert.AreEqual(-1234, fmt.Parse("-$1,234")); + Assert.AreEqual(-1234.56, fmt.Parse("-$1,234.56")); + + fmt = new CurrencyFormatter("sr-SP-Latn"); + fmt.PositivePattern = 1; + fmt.CurrencySymbol = "din"; + Assert.AreEqual(1234, fmt.Parse("1.234,00din")); + Assert.AreEqual(1234.56, fmt.Parse("1.234,56din")); + Assert.AreEqual(-1234, fmt.Parse("-1.234,00 din")); + Assert.AreEqual(-1234.56, fmt.Parse("-1.234,56 din")); + + fmt = new CurrencyFormatter(CultureInfoUtils.SerbianCyrillicCultureName); + fmt.GroupSizes = new int[] {1, 2}; + fmt.GroupSeparator = "'"; + Assert.AreEqual(1234, fmt.Parse("1'23'4,00 Дин.")); + Assert.AreEqual(1234.56, fmt.Parse("1'23'4,56 Дин.")); + Assert.AreEqual(-1234, fmt.Parse("-1'23'4,00 Дин.")); + Assert.AreEqual(-1234.56, fmt.Parse("-1'23'4,56 Дин.")); + } + + } +} diff --git a/test/Spring/Spring.Core.Tests/Globalization/Formatters/DateTimeFormatterTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Formatters/DateTimeFormatterTests.cs new file mode 100644 index 00000000..287d52e8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/Formatters/DateTimeFormatterTests.cs @@ -0,0 +1,99 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using NUnit.Framework; + +namespace Spring.Globalization.Formatters +{ + /// + /// Unit tests for DateTimeFormatter class. + /// + /// Aleksandar Seovic + /// $Id: DateTimeFormatterTests.cs,v 1.3 2007/05/04 18:13:22 markpollack Exp $ + [TestFixture] + public class DateTimeFormatterTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void FormatNullValue() + { + DateTimeFormatter fmt = new DateTimeFormatter("d"); + fmt.Format(null); + } + + [Test] + public void ParseNullOrEmptyValue() + { + DateTimeFormatter fmt = new DateTimeFormatter("d"); + Assert.AreEqual(DateTime.MinValue, fmt.Parse(null)); + Assert.IsTrue(fmt.Parse("") is DateTime); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void FormatNonDate() + { + DateTimeFormatter fmt = new DateTimeFormatter("d"); + fmt.Format("not a date"); + } + + [Test] + public void FormatUsingDefaults() + { + DateTimeFormatter fmt = new DateTimeFormatter("d", "en-US"); + Assert.AreEqual("8/14/2004", fmt.Format(new DateTime(2004, 8, 14))); + Assert.AreEqual("8/24/1974", fmt.Format(new DateTime(1974, 8, 24))); + + fmt = new DateTimeFormatter("dd-MMM-yyyy", "en-US"); + Assert.AreEqual("14-Aug-2004", fmt.Format(new DateTime(2004, 8, 14))); + Assert.AreEqual("24-Aug-1974", fmt.Format(new DateTime(1974, 8, 24))); + + fmt = new DateTimeFormatter("D", CultureInfoUtils.SerbianLatinCultureName); + Assert.AreEqual("14. avgust 2004", fmt.Format(new DateTime(2004, 8, 14))); + Assert.AreEqual("24. avgust 1974", fmt.Format(new DateTime(1974, 8, 24))); + + fmt = new DateTimeFormatter("dd-MMM-yyyy", CultureInfoUtils.SerbianCyrillicCultureName); + Assert.AreEqual("14-авг-2004", fmt.Format(new DateTime(2004, 8, 14))); + Assert.AreEqual("24-авг-1974", fmt.Format(new DateTime(1974, 8, 24))); + } + + [Test] + public void ParseUsingDefaults() + { + DateTimeFormatter fmt = new DateTimeFormatter("d", "en-US"); + Assert.AreEqual(new DateTime(2004, 8, 14), fmt.Parse("8/14/2004")); + Assert.AreEqual(new DateTime(1974, 8, 24), fmt.Parse("8/24/1974")); + + fmt = new DateTimeFormatter("dd-MMM-yyyy", "en-US"); + Assert.AreEqual(new DateTime(2004, 8, 14), fmt.Parse("14-Aug-2004")); + Assert.AreEqual(new DateTime(1974, 8, 24), fmt.Parse("24-Aug-1974")); + + fmt = new DateTimeFormatter("D", CultureInfoUtils.SerbianLatinCultureName); + Assert.AreEqual(new DateTime(2004, 8, 14), fmt.Parse("14. avgust 2004")); + Assert.AreEqual(new DateTime(1974, 8, 24), fmt.Parse("24. avgust 1974")); + + fmt = new DateTimeFormatter("dd-MMM-yyyy", CultureInfoUtils.SerbianCyrillicCultureName); + Assert.AreEqual(new DateTime(2004, 8, 14), fmt.Parse("14-авг-2004")); + Assert.AreEqual(new DateTime(1974, 8, 24), fmt.Parse("24-авг-1974")); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Globalization/Formatters/FilteringFormatterTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Formatters/FilteringFormatterTests.cs new file mode 100644 index 00000000..d647a10e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/Formatters/FilteringFormatterTests.cs @@ -0,0 +1,95 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Rhino.Mocks; + +#endregion + +namespace Spring.Globalization.Formatters +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: FilteringFormatterTests.cs,v 1.1 2008/03/20 13:19:47 oakinger Exp $ + [TestFixture] + public class FilteringFormatterTests + { + public class TestFilteringFormatter : FilteringFormatter + { + public TestFilteringFormatter(IFormatter underlyingFormatter) : base(underlyingFormatter) + { + } + + protected override string FilterValueToParse(string value) + { + return this.DoFilterValueToParse(value); + } + + public virtual string DoFilterValueToParse(string value) + { + return base.FilterValueToParse(value); + } + + protected override object FilterValueToFormat(object value) + { + return this.DoFilterValueToFormat(value); + } + + public virtual object DoFilterValueToFormat(object value) + { + return base.FilterValueToFormat(value); + } + + } + + [Test] + public void FiltersOnParseAndFormat() + { + MockRepository mocks = new MockRepository(); + IFormatter underlyingFormatter = (IFormatter) mocks.CreateMock(typeof (IFormatter)); + TestFilteringFormatter formatter = (TestFilteringFormatter) mocks.PartialMock(typeof (TestFilteringFormatter), underlyingFormatter); + + string inputText = "inputText"; + string filteredInputText = "filteredInputText"; + object outputValue = new object(); + object filteredOutputValue = new object(); + + using(mocks.Ordered()) + { + Expect.Call(formatter.DoFilterValueToParse(inputText)).Return(filteredInputText); + Expect.Call(underlyingFormatter.Parse(filteredInputText)).Return(outputValue); + + Expect.Call(formatter.DoFilterValueToFormat(outputValue)).Return(filteredOutputValue); + Expect.Call(underlyingFormatter.Format(filteredOutputValue)).Return(inputText); + } + mocks.ReplayAll(); + + Assert.AreSame(outputValue, formatter.Parse(inputText)); + Assert.AreEqual(inputText, formatter.Format(outputValue)); + + mocks.VerifyAll(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Globalization/Formatters/FloatFormatterTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Formatters/FloatFormatterTests.cs new file mode 100644 index 00000000..0474bc7f --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/Formatters/FloatFormatterTests.cs @@ -0,0 +1,97 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using NUnit.Framework; + +namespace Spring.Globalization.Formatters +{ + /// + /// Unit tests for FloatFormatter class. + /// + /// Aleksandar Seovic + /// $Id: FloatFormatterTests.cs,v 1.3 2006/04/09 07:24:49 markpollack Exp $ + [TestFixture] + public class FloatFormatterTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void FormatNullValue() + { + FloatFormatter fmt = new FloatFormatter(); + fmt.Format(null); + } + + [Test] + public void ParseNullOrEmptyValue() + { + FloatFormatter fmt = new FloatFormatter(); + Assert.AreEqual(0, fmt.Parse(null)); + Assert.IsTrue(fmt.Parse("") is double); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void FormatNonNumber() + { + FloatFormatter fmt = new FloatFormatter(); + fmt.Format("not a number"); + } + + [Test] + public void FormatUsingDefaults() + { + FloatFormatter fmt = new FloatFormatter(FloatFormatter.DefaultFormat, "en-US"); + Assert.AreEqual("1234.00", fmt.Format(1234)); + Assert.AreEqual("-1234.00", fmt.Format(-1234)); + + fmt = new FloatFormatter(FloatFormatter.DefaultFormat, "sr-SP-Latn"); + Assert.AreEqual("1234,00", fmt.Format(1234)); + Assert.AreEqual("-1234,00", fmt.Format(-1234)); + } + + [Test] + public void ParseUsingDefaults() + { + FloatFormatter fmt = new FloatFormatter(FloatFormatter.DefaultFormat, "en-US"); + Assert.AreEqual(1234.56, fmt.Parse("1234.56")); + Assert.AreEqual(-1234, fmt.Parse("-1234")); + Assert.AreEqual(1234.56, fmt.Parse("1.23456e+003")); + Assert.AreEqual(-1234, fmt.Parse("-1.234e+003")); + + fmt = new FloatFormatter(FloatFormatter.DefaultFormat, "sr-SP-Cyrl"); + Assert.AreEqual(1234.56, fmt.Parse("1234,56")); + Assert.AreEqual(-1234, fmt.Parse("-1234")); + Assert.AreEqual(1234.56, fmt.Parse("1,23456e+003")); + Assert.AreEqual(-1234, fmt.Parse("-1,234e+003")); + } + + [Test] + public void FormatUsingCustomSettings() + { + FloatFormatter fmt = new FloatFormatter("{0:e3}", "en-US"); + Assert.AreEqual("1.234e+003", fmt.Format(1234)); + Assert.AreEqual("-1.234e+003", fmt.Format(-1234)); + Assert.AreEqual("1.235e+003", fmt.Format(1234.56)); + Assert.AreEqual("-1.235e+003", fmt.Format(-1234.56)); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Globalization/Formatters/HasTextFilteringFormatterTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Formatters/HasTextFilteringFormatterTests.cs new file mode 100644 index 00000000..93551515 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/Formatters/HasTextFilteringFormatterTests.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Globalization.Formatters +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: HasTextFilteringFormatterTests.cs,v 1.1 2008/03/20 13:19:47 oakinger Exp $ + [TestFixture] + public class HasTextFilteringFormatterTests + { + [Test] + public void ReplacesNullAndWhitespacesByDefaultValue() + { + string defaultValue = "theDefaultValue"; + HasTextFilteringFormatter fmt = new HasTextFilteringFormatter(defaultValue, null); + + Assert.AreEqual( defaultValue, fmt.Parse(null)); + Assert.AreEqual(defaultValue, fmt.Parse(string.Empty)); + Assert.AreEqual( defaultValue, fmt.Parse("\t \n\r")); + Assert.AreEqual(" text \n", fmt.Parse(" text \n")); + } + + [Test] + public void DoesntAffectFormat() + { + string defaultValue = "theDefaultValue"; + HasTextFilteringFormatter fmt = new HasTextFilteringFormatter(defaultValue, null); + + Assert.AreEqual(null, fmt.Format(null)); + Assert.AreEqual(string.Empty, fmt.Format(string.Empty)); + Assert.AreEqual("\t \n\r", fmt.Format("\t \n\r")); + object o = new object(); + Assert.AreEqual(o.ToString(), fmt.Format(o)); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Globalization/Formatters/IntegerFormatterTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Formatters/IntegerFormatterTests.cs new file mode 100644 index 00000000..efb79d9e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/Formatters/IntegerFormatterTests.cs @@ -0,0 +1,92 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using NUnit.Framework; + +namespace Spring.Globalization.Formatters +{ + /// + /// Unit tests for IntegerFormatter class. + /// + /// Aleksandar Seovic + /// $Id: IntegerFormatterTests.cs,v 1.3 2007/06/01 08:53:06 oakinger Exp $ + [TestFixture] + public class IntegerFormatterTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void FormatNullValue() + { + IntegerFormatter fmt = new IntegerFormatter(); + fmt.Format(null); + } + + [Test] + public void ParseNullOrEmptyValue() + { + IntegerFormatter fmt = new IntegerFormatter(); + Assert.AreEqual( 0, fmt.Parse(null)); + Assert.AreEqual( 0, fmt.Parse(string.Empty) ); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void FormatNonNumber() + { + IntegerFormatter fmt = new IntegerFormatter(); + fmt.Format("not a number"); + } + + [Test] + public void FormatUsingDefaults() + { + IntegerFormatter fmt = new IntegerFormatter(); + Assert.AreEqual("1234", fmt.Format(1234)); + Assert.AreEqual("-1234", fmt.Format(-1234)); + } + + [Test] + public void ParseUsingDefaults() + { + IntegerFormatter fmt = new IntegerFormatter(); + Assert.AreEqual(1234, fmt.Parse("1234")); + Assert.AreEqual(-1234, fmt.Parse("-1234")); + } + + [Test] + public void FormatUsingCustomSettings() + { + IntegerFormatter fmt = new IntegerFormatter("{0:00000}"); + Assert.AreEqual("01234", fmt.Format(1234)); + Assert.AreEqual("-01234", fmt.Format(-1234)); + + fmt = new IntegerFormatter("{0,10}"); + Assert.AreEqual(" 1234", fmt.Format(1234)); + + fmt = new IntegerFormatter("{0,-10}"); + Assert.AreEqual("1234 ", fmt.Format(1234)); + + fmt = new IntegerFormatter("{0:(###) ###-####}"); + Assert.AreEqual("(813) 555-4034", fmt.Format(8135554034)); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Globalization/Formatters/NumberFormatterTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Formatters/NumberFormatterTests.cs new file mode 100644 index 00000000..9db42539 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/Formatters/NumberFormatterTests.cs @@ -0,0 +1,132 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using NUnit.Framework; + +namespace Spring.Globalization.Formatters +{ + /// + /// Unit tests for NumberFormatter class. + /// + /// Aleksandar Seovic + /// $Id: NumberFormatterTests.cs,v 1.2 2006/04/09 07:24:49 markpollack Exp $ + [TestFixture] + public class NumberFormatterTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void FormatNullValue() + { + NumberFormatter fmt = new NumberFormatter(); + fmt.Format(null); + } + + [Test] + public void ParseNullOrEmptyValue() + { + NumberFormatter fmt = new NumberFormatter(); + Assert.AreEqual(0, fmt.Parse(null)); + Assert.IsTrue(fmt.Parse("") is double); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void FormatNonNumber() + { + NumberFormatter fmt = new NumberFormatter(); + fmt.Format("not a number"); + } + + [Test] + public void FormatUsingDefaults() + { + NumberFormatter fmt = new NumberFormatter("en-US"); + Assert.AreEqual("1,234.00", fmt.Format(1234)); + Assert.AreEqual("1,234.56", fmt.Format(1234.56)); + Assert.AreEqual("-1,234.00", fmt.Format(-1234)); + Assert.AreEqual("-1,234.56", fmt.Format(-1234.56)); + + fmt = new NumberFormatter("sr-SP-Latn"); + Assert.AreEqual("1.234,00", fmt.Format(1234)); + Assert.AreEqual("1.234,56", fmt.Format(1234.56)); + Assert.AreEqual("-1.234,00", fmt.Format(-1234)); + Assert.AreEqual("-1.234,56", fmt.Format(-1234.56)); + } + + [Test] + public void ParseUsingDefaults() + { + NumberFormatter fmt = new NumberFormatter("en-US"); + Assert.AreEqual(1234, fmt.Parse("1,234.00")); + Assert.AreEqual(1234.56, fmt.Parse("1,234.56")); + Assert.AreEqual(-1234, fmt.Parse("-1,234.00")); + Assert.AreEqual(-1234.56, fmt.Parse("-1,234.56")); + + fmt = new NumberFormatter("sr-SP-Latn"); + Assert.AreEqual(1234, fmt.Parse("1.234,00")); + Assert.AreEqual(1234.56, fmt.Parse("1.234,56")); + Assert.AreEqual(-1234, fmt.Parse("-1.234,00")); + Assert.AreEqual(-1234.56, fmt.Parse("-1.234,56")); + } + + [Test] + public void FormatUsingCustomSettings() + { + NumberFormatter fmt = new NumberFormatter("en-US"); + fmt.DecimalDigits = 0; + fmt.NegativePattern = 0; + Assert.AreEqual("1,234", fmt.Format(1234)); + Assert.AreEqual("1,235", fmt.Format(1234.56)); + Assert.AreEqual("(1,234)", fmt.Format(-1234)); + Assert.AreEqual("(1,235)", fmt.Format(-1234.56)); + + fmt = new NumberFormatter("sr-SP-Cyrl"); + fmt.GroupSizes = new int[] {1, 2}; + fmt.GroupSeparator = "'"; + Assert.AreEqual("1'23'4,00", fmt.Format(1234)); + Assert.AreEqual("1'23'4,56", fmt.Format(1234.56)); + Assert.AreEqual("-1'23'4,00", fmt.Format(-1234)); + Assert.AreEqual("-1'23'4,56", fmt.Format(-1234.56)); + } + + [Test] + public void ParseUsingCustomSettings() + { + NumberFormatter fmt = new NumberFormatter("en-US"); + fmt.DecimalDigits = 0; + fmt.NegativePattern = 0; + Assert.AreEqual(1234, fmt.Parse("1,234")); + Assert.AreEqual(1234.56, fmt.Parse("1,234.56")); + Assert.AreEqual(-1234, fmt.Parse("(1,234)")); + Assert.AreEqual(-1234.56, fmt.Parse("(1,234.56)")); + + fmt = new NumberFormatter("sr-SP-Cyrl"); + fmt.GroupSizes = new int[] {1, 2}; + fmt.GroupSeparator = "'"; + Assert.AreEqual(1234, fmt.Parse("1'23'4,00")); + Assert.AreEqual(1234.56, fmt.Parse("1'23'4,56")); + Assert.AreEqual(-1234, fmt.Parse("-1'23'4,00")); + Assert.AreEqual(-1234.56, fmt.Parse("-1'23'4,56")); + } + + } +} diff --git a/test/Spring/Spring.Core.Tests/Globalization/Formatters/PercentFormatterTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Formatters/PercentFormatterTests.cs new file mode 100644 index 00000000..185e5208 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/Formatters/PercentFormatterTests.cs @@ -0,0 +1,113 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using NUnit.Framework; + +namespace Spring.Globalization.Formatters +{ + /// + /// Unit tests for PercentFormatter class. + /// + /// Aleksandar Seovic + /// $Id: PercentFormatterTests.cs,v 1.2 2006/04/09 07:24:49 markpollack Exp $ + [TestFixture] + public class PercentFormatterTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void FormatNullValue() + { + PercentFormatter fmt = new PercentFormatter(); + fmt.Format(null); + } + + [Test] + public void ParseNullOrEmptyValue() + { + PercentFormatter fmt = new PercentFormatter(); + Assert.AreEqual(0, fmt.Parse(null)); + Assert.IsTrue(fmt.Parse("") is double); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void FormatNonNumber() + { + PercentFormatter fmt = new PercentFormatter(); + fmt.Format("not a number"); + } + + [Test] + public void FormatUsingDefaults() + { + PercentFormatter fmt = new PercentFormatter("en-US"); + Assert.AreEqual("25.00 %", fmt.Format(0.25)); + Assert.AreEqual("25.34 %", fmt.Format(0.2534)); + + fmt = new PercentFormatter("sr-SP-Latn"); + Assert.AreEqual("25,00%", fmt.Format(0.25)); + Assert.AreEqual("25,34%", fmt.Format(0.2534)); + } + + [Test] + public void ParseUsingDefaults() + { + PercentFormatter fmt = new PercentFormatter("en-US"); + Assert.AreEqual(0.25, fmt.Parse("25.00 %")); + Assert.AreEqual(0.2534, fmt.Parse("25.34 %")); + + fmt = new PercentFormatter("sr-SP-Latn"); + Assert.AreEqual(0.25, fmt.Parse("25,00%")); + Assert.AreEqual(0.2534, fmt.Parse("25,34%")); + } + + [Test] + public void FormatUsingCustomSettings() + { + PercentFormatter fmt = new PercentFormatter("en-US"); + fmt.DecimalDigits = 0; + fmt.PositivePattern = 1; + Assert.AreEqual("25%", fmt.Format(0.25)); + Assert.AreEqual("25%", fmt.Format(0.2534)); + + fmt = new PercentFormatter("sr-SP-Latn"); + fmt.DecimalDigits = 1; + Assert.AreEqual("25,0%", fmt.Format(0.25)); + Assert.AreEqual("25,3%", fmt.Format(0.2534)); + } + + [Test] + public void ParseUsingCustomSettings() + { + PercentFormatter fmt = new PercentFormatter("en-US"); + fmt.DecimalDigits = 0; + fmt.PositivePattern = 1; + Assert.AreEqual(0.25, fmt.Parse("25%")); + Assert.AreEqual(0.2534, fmt.Parse("25.34%")); + + fmt = new PercentFormatter("sr-SP-Latn"); + fmt.DecimalDigits = 1; + Assert.AreEqual(0.25, fmt.Parse("25,0%")); + Assert.AreEqual(0.253, fmt.Parse("25,3%")); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Globalization/Localizers/ResourceSetLocalizerTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Localizers/ResourceSetLocalizerTests.cs new file mode 100644 index 00000000..23b81b7b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/Localizers/ResourceSetLocalizerTests.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Resources; + +using NUnit.Framework; + +using Spring.Context; +using Spring.Context.Support; + +#endregion + +namespace Spring.Globalization.Localizers +{ + /// + /// Unit tests for the ResourceSetLocalizer class. + /// + /// Aleksandar Seovic + /// $Id: ResourceSetLocalizerTests.cs,v 1.4 2007/07/24 17:26:47 oakinger Exp $ + +#if ! NET_1_0 + [TestFixture] +#endif + public sealed class ResourceSetLocalizerTests : AbstractLocalizerTests + { + protected override ILocalizer CreateLocalizer() + { + return new ResourceSetLocalizer(); + } + + protected override IMessageSource CreateMessageSource() + { + ResourceSetMessageSource messageSource = new ResourceSetMessageSource(); + messageSource.ResourceManagers.Add(new ResourceManager("Spring.Resources.Tesla", GetType().Assembly)); + return messageSource; + } + + [Test] + public void DoesNotThrowOnMissingResource() + { + ResourceSetMessageSource messageSource = new ResourceSetMessageSource(); + messageSource.ResourceManagers.Add(new ResourceManager("do not exist", GetType().Assembly)); + ResourceSetLocalizer localizer = new ResourceSetLocalizer(); + localizer.ApplyResources(new Inventor(), messageSource, CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Globalization/Resolvers/DefaultCultureResolverTests.cs b/test/Spring/Spring.Core.Tests/Globalization/Resolvers/DefaultCultureResolverTests.cs new file mode 100644 index 00000000..3a2541e7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Globalization/Resolvers/DefaultCultureResolverTests.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Globalization; +using System.Threading; + +using NUnit.Framework; + +#endregion + +namespace Spring.Globalization.Resolvers +{ + /// + /// Unit tests for the DefaultCultureResolver class. + /// + /// Rick Evans + /// $Id: DefaultCultureResolverTests.cs,v 1.2 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class DefaultCultureResolverTests + { + [Test] + public void ResolveCultureYieldsThreadsCultureAfterInitialization() + { + DefaultCultureResolver resolver = new DefaultCultureResolver(); + Assert.AreEqual(Thread.CurrentThread.CurrentUICulture, resolver.ResolveCulture(), + "Not defaulting to the culture of the current thread."); + } + + [Test] + public void DefaultCultureIsNullAfterInitialization() + { + DefaultCultureResolver resolver = new DefaultCultureResolver(); + Assert.IsNull(resolver.DefaultCulture, "Must be null until explicitly set."); + } + + [Test] + public void DefaultCultureIsYieldedForResolveCulture() + { + DefaultCultureResolver resolver = new DefaultCultureResolver(); + resolver.DefaultCulture = CultureInfo.InvariantCulture; + Assert.AreEqual(CultureInfo.InvariantCulture, resolver.ResolveCulture(), + "Not returning the DefaultCulture (it must if the DefaultCulture " + + "property has been set explicitly)."); + } + + [Test] + public void NullingDefaultCultureYieldsThreadsCultureForResolveCulture() + { + DefaultCultureResolver resolver = new DefaultCultureResolver(); + resolver.DefaultCulture = CultureInfo.InvariantCulture; + Assert.AreEqual(CultureInfo.InvariantCulture, resolver.ResolveCulture(), + "Not returning the DefaultCulture (it must if the DefaultCulture " + + "property has been set explicitly)."); + resolver.DefaultCulture = null; + Assert.AreEqual(Thread.CurrentThread.CurrentUICulture, resolver.ResolveCulture(), + "Not falling back to the culture of the current thread after " + + "DefaultCulture property is nulled out (it must)."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/HookableContextHandler.cs b/test/Spring/Spring.Core.Tests/HookableContextHandler.cs new file mode 100644 index 00000000..f8c0add1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/HookableContextHandler.cs @@ -0,0 +1,76 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Configuration; +using System.Xml; +using Spring.Context.Support; + +#endregion + +namespace Spring +{ + /// + /// Replace the original context handler with this hookable version for testing ContextRegistry + /// + /// Erich Eichinger + /// $Id: HookableContextHandler.cs,v 1.1 2008/03/21 10:49:38 oakinger Exp $ + public class HookableContextHandler : ContextHandler, IConfigurationSectionHandler + { + public delegate object CreateContextFromSectionHandler(object parent, object configContext, XmlNode section); + + private static CreateContextFromSectionHandler s_callback; + + public static CreateContextFromSectionHandler callback + { + get { return s_callback; } + } + + public static CreateContextFromSectionHandler SetSectionHandler(CreateContextFromSectionHandler sectionHandler) + { + CreateContextFromSectionHandler prevInstance = s_callback; + s_callback = sectionHandler; + return prevInstance; + } + + /// + ///Creates a configuration section handler. + /// + /// + /// + ///The created section handler object. + /// + /// + ///Parent object. + ///Section XML node. + ///Configuration context object.2 + new virtual public object Create(object parent, object configContext, XmlNode section) + { + if (s_callback != null) + { + return s_callback(parent, configContext, section); + } + + return base.Create(parent, configContext, section); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/DerivedTestObject.cs b/test/Spring/Spring.Core.Tests/Objects/DerivedTestObject.cs new file mode 100644 index 00000000..19b74b5b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/DerivedTestObject.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects +{ + /// + /// Adds some extra interfaces and properties to the base clas + /// so that we can test extra odd corner cases and suchlike. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + public class DerivedTestObject : TestObject, IDisposable + { + public const string NicknamePrefix = "#"; + + private bool destroyed; + private string nickers; + + public virtual void Dispose() + { + destroyed = true; + } + + public virtual bool WasDestroyed() + { + return destroyed; + } + + public new string Nickname + { + get { return this.nickers; } + set { this.nickers = NicknamePrefix + value; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Events/Support/EventManipulationUtilsTests.cs b/test/Spring/Spring.Core.Tests/Objects/Events/Support/EventManipulationUtilsTests.cs new file mode 100644 index 00000000..fab7e53a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Events/Support/EventManipulationUtilsTests.cs @@ -0,0 +1,147 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Events.Support.Tests +{ + /// + /// Unit tests for the EventManipulationUtils class. + /// + [TestFixture] + public sealed class EventManipulationUtilsTests + { + [Test] + public void FoundEventHandlers() + { + Type pubType = typeof (SimpleEventPublisher); + EventInfo[] events = pubType.GetEvents(); + + foreach (EventInfo currentEvent in events) + { + Type eventHandlerType = currentEvent.EventHandlerType; + MethodInfo invoke = eventHandlerType.GetMethod("Invoke"); + Assert.IsNotNull(EventManipulationUtils.GetMethodInfoMatchingSignature(invoke, typeof (SimpleEventSubscriber))); + } + } + + [Test] + public void FoundSomeEventHandlers() + { + Type pubType = typeof (SimpleEventPublisher); + EventInfo[] events = pubType.GetEvents(); + foreach (EventInfo currentEvent in events) + { + Type eventHandlerType = currentEvent.EventHandlerType; + MethodInfo invoke = eventHandlerType.GetMethod("Invoke"); + if (currentEvent.Name == "MyFirstEvent" || currentEvent.Name == "MySecondEvent") + { + Assert.IsNotNull(EventManipulationUtils.GetMethodInfoMatchingSignature(invoke, typeof (SomeEventSubscriber))); + } + else + { + Assert.IsNull(EventManipulationUtils.GetMethodInfoMatchingSignature(invoke, typeof (SomeEventSubscriber))); + } + } + } + + [Test] + public void FoundNoHandlers() + { + Type pubType = typeof (SimpleEventPublisher); + EventInfo[] events = pubType.GetEvents(); + + foreach (EventInfo currentEvent in events) + { + Type eventHandlerType = currentEvent.EventHandlerType; + MethodInfo invoke = eventHandlerType.GetMethod("Invoke"); + Assert.IsNull(EventManipulationUtils.GetMethodInfoMatchingSignature(invoke, typeof (NoEventSubscriber))); + } + } + + #region Helper Classes + + internal delegate void Delegate1(object source, string name); + + internal delegate string Delegate2(string name, bool flag); + + internal delegate bool Delegate3(string name, bool flag); + + internal class SimpleEventPublisher + { + public event Delegate1 MyFirstEvent; + public event Delegate2 MySecondEvent; + public event Delegate3 MyThirdEvent; + } + + internal class SimpleEventSubscriber + { + public void MyFirstEvent_Handler(object source, string name) + { + } + + public string MySecondEvent_Handler(string name, bool flag) + { + return String.Empty; + } + + public bool MyThirdEvent_Handler(string name, bool flag) + { + return false; + } + } + + internal class NoEventSubscriber + { + public void MyFirstEvent_Handler(object source, string name, bool flag) + { + } + + public void MySecondEvent_Handler(string name, bool flag) + { + } + + public string MyThirdEvent_Handler(string name) + { + return String.Empty; + } + } + + internal class SomeEventSubscriber + { + public void MyFirstEvent_Handler(object source, string name) + { + } + + public string MySecondEvent_Handler(string name, bool flag) + { + return String.Empty; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Events/Support/EventRegistryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Events/Support/EventRegistryTests.cs new file mode 100644 index 00000000..9de96b59 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Events/Support/EventRegistryTests.cs @@ -0,0 +1,314 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using NUnit.Framework; + +namespace Spring.Objects.Events.Support.Tests +{ + [TestFixture] + public class EventRegistryTests + { + #region Helper Classes + + internal delegate void SimpleClientEvent(object sender, MyClientEventArgs args); + + internal delegate string SimpleClientEvent2(MyClientEventArgs args); + + internal class SimpleClient2 + { + public event SimpleClientEvent MyClientEvent; + + public SimpleClient2() + { + } + + public void ClientMethodThatTriggersEvent() + { + if (MyClientEvent != null) + { + MyClientEvent(this, new MyClientEventArgs("Event raised")); + } + } + } + + internal interface ISimpleClient + { + event SimpleClientEvent MyClientEvent1; + event SimpleClientEvent MyClientEvent2; + event SimpleClientEvent2 MyClientEvent3; + } + + internal class SimpleClient : ISimpleClient + { + private string _clientName; + + public event SimpleClientEvent MyClientEvent1; + public event SimpleClientEvent MyClientEvent2; + public event SimpleClientEvent2 MyClientEvent3; + + public SimpleClient(string clientName) + { + _clientName = clientName; + } + + public void ClientMethodThatTriggersEvent() + { + if (MyClientEvent1 != null) + { + MyClientEvent1(this, new MyClientEventArgs("Event raised from " + _clientName)); + } + } + + public void ClientMethodThatTriggersEvent2() + { + if (MyClientEvent2 != null) + { + MyClientEvent2(this, new MyClientEventArgs("Event raised from " + _clientName)); + } + } + + public void ClientMethodThatTriggersEvent3() + { + if (MyClientEvent3 != null) + { + MyClientEvent3(new MyClientEventArgs("Event raised from " + _clientName)); + } + } + } + + internal class MyClientEventArgs : EventArgs + { + private string _eventMessage; + + public MyClientEventArgs(string eventMessage) + { + _eventMessage = eventMessage; + } + + public string EventMessage + { + get { return _eventMessage; } + } + } + + internal class SimpleSubscriber + { + protected bool _eventRaised = false; + + public SimpleSubscriber() + { + } + + public bool EventRaised + { + get { return _eventRaised; } + } + } + + internal class EventSubscriber : SimpleSubscriber + { + public EventSubscriber() + { + } + + public void HandleClientEvents(object sender, MyClientEventArgs args) + { + _eventRaised = true; + } + } + + internal class NoEventSubscriber : SimpleSubscriber + { + public NoEventSubscriber() + { + } + + public void HandleClientEvents(object sender) + { + _eventRaised = true; + } + } + + internal class OtherEventSubscriber : SimpleSubscriber + { + public OtherEventSubscriber() + { + } + + public string HandleClientEvents(MyClientEventArgs args) + { + _eventRaised = true; + return String.Empty; + } + } + + #endregion + + [Test] + public void RespectsInheritance() + { + SimpleClient source = new SimpleClient("foo"); + + IEventRegistry registry = new EventRegistry(); + registry.PublishEvents(source); + + EventSubscriber sub = new EventSubscriber(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + + source.ClientMethodThatTriggersEvent(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + + registry.Subscribe(sub, typeof(ISimpleClient)); + source.ClientMethodThatTriggersEvent(); + Assert.IsTrue(sub.EventRaised, "Event Not Raised"); + } + + [Test] + public void PublishAllEvents() + { + IEventRegistry registry = new EventRegistry(); + SimpleClient client = new SimpleClient("PublishAllEvents"); + registry.PublishEvents(client); + EventSubscriber sub = new EventSubscriber(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + + client.ClientMethodThatTriggersEvent(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + + registry.Subscribe(sub); + client.ClientMethodThatTriggersEvent(); + Assert.IsTrue(sub.EventRaised, "Event Not Raised"); + } + + [Test] + public void PublishAllEventsMultipleSubscribers() + { + IEventRegistry registry = new EventRegistry(); + SimpleClient client = new SimpleClient("PublishAllEvents"); + registry.PublishEvents(client); + EventSubscriber sub = new EventSubscriber(); + EventSubscriber sub2 = new EventSubscriber(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + Assert.IsFalse(sub2.EventRaised, "Event raised"); + + client.ClientMethodThatTriggersEvent(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + Assert.IsFalse(sub2.EventRaised, "Event raised"); + + registry.Subscribe(sub); + registry.Subscribe(sub2); + client.ClientMethodThatTriggersEvent(); + Assert.IsTrue(sub.EventRaised, "Event Not Raised"); + Assert.IsTrue(sub2.EventRaised, "Event Not Raised"); + } + + [Test] + public void PublishAllEventsSubscribeToNamedEvents() + { + IEventRegistry registry = new EventRegistry(); + SimpleClient client = new SimpleClient("PublishAllEvents"); + SimpleClient2 client2 = new SimpleClient2(); + + registry.PublishEvents(client); + registry.PublishEvents(client2); + + EventSubscriber sub = new EventSubscriber(); + EventSubscriber sub2 = new EventSubscriber(); + + Assert.IsFalse(sub.EventRaised, "Event raised"); + Assert.IsFalse(sub2.EventRaised, "Event raised"); + + client.ClientMethodThatTriggersEvent(); + client2.ClientMethodThatTriggersEvent(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + Assert.IsFalse(sub2.EventRaised, "Event raised"); + + registry.Subscribe(sub, typeof (SimpleClient)); + registry.Subscribe(sub2, typeof (SimpleClient2)); + + client.ClientMethodThatTriggersEvent(); + Assert.IsTrue(sub.EventRaised, "Event Not Raised"); + Assert.IsFalse(sub2.EventRaised, "Event raised"); + + client2.ClientMethodThatTriggersEvent(); + Assert.IsTrue(sub.EventRaised, "Event Not Raised"); + Assert.IsTrue(sub2.EventRaised, "Event Not Raised"); + } + + [Test] + public void NoValidEventHandlersOrEventsToSubscribeto() + { + IEventRegistry registry = new EventRegistry(); + SimpleClient client = new SimpleClient("PublishAllEvents"); + NoEventSubscriber sub = new NoEventSubscriber(); + registry.PublishEvents(client); + + Assert.IsFalse(sub.EventRaised, "Event raised"); + client.ClientMethodThatTriggersEvent(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + + registry.Subscribe(sub); + client.ClientMethodThatTriggersEvent(); + Assert.IsFalse(sub.EventRaised, "Event Raised"); + } + + [Test] + public void NoPublishers() + { + IEventRegistry registry = new EventRegistry(); + SimpleClient client = new SimpleClient("PublishAllEvents"); + SimpleSubscriber sub = new SimpleSubscriber(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + + client.ClientMethodThatTriggersEvent(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + + registry.Subscribe(sub); + client.ClientMethodThatTriggersEvent(); + Assert.IsFalse(sub.EventRaised, "Event Raised"); + } + + [Test] + public void PublishAllEventsAndSubscribeToSome() + { + IEventRegistry registry = new EventRegistry(); + SimpleClient client = new SimpleClient("PublishAllEventsAndSubscribeToSome"); + registry.PublishEvents(client); + EventSubscriber sub = new EventSubscriber(); + OtherEventSubscriber sub2 = new OtherEventSubscriber(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + Assert.IsFalse(sub2.EventRaised, "Event raised"); + + client.ClientMethodThatTriggersEvent(); + Assert.IsFalse(sub.EventRaised, "Event raised"); + + client.ClientMethodThatTriggersEvent3(); + Assert.IsFalse(sub2.EventRaised, "Event raised"); + + registry.Subscribe(sub); + registry.Subscribe(sub2); + client.ClientMethodThatTriggersEvent(); + client.ClientMethodThatTriggersEvent3(); + Assert.IsTrue(sub.EventRaised, "Event Not Raised"); + Assert.IsTrue(sub2.EventRaised, "Event Not Raised"); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/ExpressionTestObject.cs b/test/Spring/Spring.Core.Tests/Objects/ExpressionTestObject.cs new file mode 100644 index 00000000..dd994577 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/ExpressionTestObject.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections; +using Spring.Expressions; + +namespace Spring.Objects +{ + public class ExpressionTestObject + { + private IExpression expressionOne; + private IExpression expressionTwo; + private string someString; + private DateTime someDate; + private IDictionary someDictionary; + + public ExpressionTestObject(string someString) + { + this.someString = someString; + } + + public IExpression ExpressionOne + { + get { return expressionOne; } + set { expressionOne = value; } + } + + public IExpression ExpressionTwo + { + get { return expressionTwo; } + set { expressionTwo = value; } + } + + public string SomeString + { + get { return someString; } + } + + public DateTime SomeDate + { + get { return someDate; } + set { someDate = value; } + } + + public IDictionary SomeDictionary + { + get { return someDictionary; } + set { someDictionary = value; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/AbstractListableObjectFactoryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/AbstractListableObjectFactoryTests.cs new file mode 100644 index 00000000..8e084fff --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/AbstractListableObjectFactoryTests.cs @@ -0,0 +1,117 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory { + + /// + /// Unit tests for the AbstractListableObjectFactory class. + /// + /// Rod Johnson + /// Rick Evans (.NET) + public abstract class AbstractListableObjectFactoryTests : + AbstractObjectFactoryTests { + + /// + /// Subclasses must initialize this (via the derived ObjectFactory property). + /// + protected internal virtual IListableObjectFactory ListableObjectFactory + { + get + { + if (!(ObjectFactory is IListableObjectFactory)) + { + throw new SystemException ("IListableObjectFactory required..."); + } + return (IListableObjectFactory) ObjectFactory; + } + } + + /// + /// Subclasses can override this. + /// + [Test] + public virtual void Count () + { + AssertCount (13); + } + + protected internal void AssertCount (int count) + { + string [] defnames = ListableObjectFactory.GetObjectDefinitionNames (); + Assert.IsTrue ( + defnames.Length == count, + string.Format ("We should have {0} objects, not {1}.", count, defnames.Length)); + } + + [Test] + public virtual void ObjectCount () + { + AssertTestObjectCount (9); + } + + public virtual void AssertTestObjectCount (int count) + { + string [] defnames = + ListableObjectFactory.GetObjectNamesForType (typeof (TestObject)); + Assert.IsTrue ( + defnames.Length == count, + string.Format ("We should have {0} objects for class {1}, not {2}.", count, typeof (TestObject).FullName, defnames.Length)); + } + + [Test] + public virtual void GetDefinitionsForNoSuchClass () + { + string[] defnames = + ListableObjectFactory.GetObjectNamesForType (typeof (string)); + Assert.IsTrue (defnames.Length == 0, "No string definitions"); + } + + /// + /// Check that count refers to factory class, not + /// object class (we don't know what type factories may return, + /// and it may even change over time). + /// + [Test] + public virtual void GetCountForFactoryClass () + { + int count = + ListableObjectFactory.GetObjectNamesForType ( + typeof (IFactoryObject)).Length; + Assert.IsTrue ( + count == 2, + string.Format ("Should have 2 factories, not {0}.", count)); + } + + [Test] + public virtual void ContainsObjectDefinition () + { + Assert.IsTrue (ListableObjectFactory.ContainsObjectDefinition ("rod")); + Assert.IsTrue (ListableObjectFactory.ContainsObjectDefinition ("roderick")); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/AbstractObjectFactoryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/AbstractObjectFactoryTests.cs new file mode 100644 index 00000000..df71117a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/AbstractObjectFactoryTests.cs @@ -0,0 +1,425 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Subclasses must override SetUp () to initialize the object factory + /// and any other variables they need. + /// + /// Rod Johnson + /// Rick Evans (.NET) + public abstract class AbstractObjectFactoryTests + { + #region Properties + + protected internal abstract IObjectFactory ObjectFactory { get; } + + #endregion + + #region Tests + + /// + /// Roderick objects inherits from rod, overriding name only. + /// + [Test] + public void Inheritance() + { + Assert.IsTrue(ObjectFactory.ContainsObject("rod")); + Assert.IsTrue(ObjectFactory.ContainsObject("roderick")); + TestObject rod = (TestObject) ObjectFactory["rod"]; + TestObject roderick = (TestObject) ObjectFactory["roderick"]; + Assert.IsTrue(rod != roderick, "not == "); + Assert.IsTrue(rod.Name.Equals("Rod"), "rod.name is Rod"); + Assert.IsTrue(rod.Age == 31, "rod.age is 31"); + Assert.IsTrue(roderick.Name.Equals("Roderick"), "roderick.name is Roderick"); + Assert.IsTrue(roderick.Age == rod.Age, "roderick.age was inherited"); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public virtual void GetObjectWithNullName() + { + ObjectFactory.GetObject(null); + } + + /// + /// Test that InitializingObject objects receive the AfterPropertiesSet () callback. + /// + [Test] + public void InitializingObjectCallback() + { + MustBeInitialized mbi = + (MustBeInitialized) ObjectFactory["mustBeInitialized"]; + // The dummy business method will throw an exception if the + // AfterPropertiesSet () callback wasn't invoked + mbi.BusinessMethod(); + } + + /// + /// Test that InitializingObject/ObjectFactoryAware/DisposableObject objects + /// receive the AfterPropertiesSet () callback before ObjectFactoryAware + /// callbacks. + /// + [Test] + public void LifecycleCallbacks() + { + LifecycleObject lb = (LifecycleObject) ObjectFactory.GetObject("lifecycle"); + Assert.AreEqual("lifecycle", lb.ObjectName); + // The dummy business method will throw an exception if the + // necessary callbacks weren't invoked in the right order + lb.BusinessMethod(); + Assert.IsFalse(lb.Destroyed, "Was destroyed"); + } + + [Test] + public void FindsValidInstance() + { + object o = ObjectFactory.GetObject("rod"); + Assert.IsTrue(o is TestObject, "Rod object is a TestObject"); + TestObject rod = (TestObject) o; + Assert.IsTrue(rod.Name.Equals("Rod"), "rod.name is Rod"); + Assert.IsTrue(rod.Age == 31, "rod.age is 31"); + } + + [Test] + public void GetInstanceByMatchingClass() + { + object o = ObjectFactory.GetObject("rod", typeof (TestObject)); + Assert.IsTrue(o is TestObject, "Rod object is a TestObject"); + } + + [Test] + public void GetInstanceByNonmatchingClass() + { + try + { + ObjectFactory.GetObject("rod", typeof (IObjectFactory)); + Assert.Fail("Rod object is not of type IObjectFactory; GetObjectInstance(rod, typeof (IObjectFactory)) should throw ObjectNotOfRequiredTypeException"); + } + catch (ObjectNotOfRequiredTypeException ex) + { + Assert.IsTrue(ex.ObjectName.Equals("rod"), "Exception has correct object name"); + Assert.IsTrue(ex.RequiredType.Equals(typeof (IObjectFactory)), "Exception requiredType must be ObjectFactory.class"); + Assert.IsTrue(typeof (TestObject).IsAssignableFrom(ex.ActualType), "Exception actualType as TestObject.class"); + Assert.IsTrue(ex.ActualInstance == ObjectFactory.GetObject("rod"), "Actual instance is correct"); + } + } + + [Test] + public virtual void GetSharedInstanceByMatchingClass() + { + object o = ObjectFactory.GetObject("rod", typeof (TestObject)); + Assert.IsTrue(o is TestObject, "Rod object is a TestObject"); + } + + [Test] + public virtual void GetSharedInstanceByMatchingClassNoCatch() + { + object o = ObjectFactory.GetObject("rod", typeof (TestObject)); + Assert.IsTrue(o is TestObject, "Rod object is a TestObject"); + } + + [Test] + public void GetSharedInstanceByNonmatchingClass() + { + try + { + ObjectFactory.GetObject("rod", typeof (IObjectFactory)); + Assert.Fail("Rod object is not of type ObjectFactory; getObjectInstance(rod, ObjectFactory.class) should throw ObjectNotOfRequiredTypeException"); + } + catch (ObjectNotOfRequiredTypeException ex) + { + // So far, so good + Assert.IsTrue(ex.ObjectName.Equals("rod"), "Exception has correct object name"); + Assert.IsTrue(ex.RequiredType.Equals(typeof (IObjectFactory)), "Exception requiredType must be IObjectFactory class"); + Assert.IsTrue(typeof (TestObject).IsAssignableFrom(ex.ActualType), "Exception actualType as TestObject class"); + } + catch (Exception ex) + { + Assert.Fail("Shouldn't throw exception on getting valid instance with matching class : " + ex.Message); + } + } + + [Test] + public virtual void SharedInstancesAreEqual() + { + try + { + object o = ObjectFactory.GetObject("rod"); + Assert.IsTrue(o is TestObject, "Rod object1 is a TestObject"); + object o1 = ObjectFactory.GetObject("rod"); + Assert.IsTrue(o1 is TestObject, "Rod object2 is a TestObject"); + Assert.IsTrue(o == o1, "Object equals applies"); + } + catch + { + Assert.Fail("Shouldn't throw exception on getting valid instance"); + } + } + + [Test] + [ExpectedException(typeof (NoSuchObjectDefinitionException))] + public void NotThere() + { + Assert.IsFalse(ObjectFactory.ContainsObject("Mr Squiggle")); + ObjectFactory.GetObject("Mr Squiggle"); + } + + [Test] + public void ValidEmpty() + { + try + { + object o = ObjectFactory.GetObject("validEmpty"); + Assert.IsTrue(o is TestObject, "validEmpty object is a TestObject"); + TestObject ve = (TestObject) o; + Assert.IsTrue(ve.Name == null && ve.Age == 0 && ve.Spouse == null, "Valid empty has defaults"); + } + catch + { + Assert.Fail("Shouldn't throw exception on valid empty"); + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void RegisterNullCustomTypeConverter() + { + AbstractObjectFactory fac = ObjectFactory as AbstractObjectFactory; + if(fac != null) + { + fac.RegisterCustomConverter(null, null); + } + } + + [Test] + public virtual void TypeMismatch() + { + try + { + ObjectFactory.GetObject("typeMismatch"); + Assert.Fail("Shouldn't succeed with type mismatch"); + } + catch (ObjectCreationException wex) + { + Assert.IsTrue(wex.InnerException is PropertyAccessExceptionsException); + PropertyAccessExceptionsException ex = (PropertyAccessExceptionsException) wex.InnerException; + // Furthers + Assert.IsTrue(ex.ExceptionCount == 1, "Has one error"); + Assert.IsTrue(ex.GetPropertyAccessException("age") != null, "Error is for field age"); + + TestObject tb = (TestObject) ex.ObjectWrapper.WrappedInstance; + Assert.IsTrue(tb.Age == 0, "Age still has default"); + Assert.IsTrue(ex.GetPropertyAccessException("age").PropertyChangeArgs.NewValue.Equals("34x"), "We have rejected age in exception"); + Assert.IsTrue(tb.Name.Equals("typeMismatch"), "valid name stuck"); + Assert.IsTrue(tb.Spouse.Name.Equals("Rod"), "valid spouse stuck"); + } + } + + [Test] + public virtual void GrandParentDefinitionFoundInObjectFactory() + { + TestObject dad = (TestObject) ObjectFactory.GetObject("father"); + Assert.IsTrue(dad.Name.Equals("Albert"), "Dad has correct name"); + } + + [Test] + public virtual void GrandParentDefinitionFoundInObjectFactoryWithType() + { + TestObject dad = (TestObject)ObjectFactory.GetObject("typedfather", typeof(TestObject)); + Assert.AreEqual(null, dad.Name, "Dad has not correct name"); + } + + [Test] + public virtual void GrandParentDefinitionFoundInObjectFactoryWithArguments() + { + TestObject dad = (TestObject)ObjectFactory.GetObject("namedfather", new object[] { "Hugo", 65 } ); + Assert.AreEqual("Hugo", dad.Name, "Dad has not correct name"); + Assert.AreEqual(65, dad.Age, "Dad has not correct age"); + } + + [Test] + public virtual void GrandParentDefinitionFoundInObjectFactoryWithTypeAndArguments() + { + TestObject dad = (TestObject)ObjectFactory.GetObject("typedfather", typeof(TestObject), new object[] { "Chris", 66 }); + Assert.AreEqual("Chris", dad.Name, "Dad has not correct name"); + Assert.AreEqual(66, dad.Age, "Dad has not correct age"); + } + + [Test(Description="Extra check that the type is really passed on to the parent factory")] + public virtual void GrandParentDefinitionFoundInObjectFactoryWithTypeAndArgumentsWithWrongType() + { + try + { + TestObject dad = (TestObject)ObjectFactory.GetObject("typedfather", typeof(string), new object[] { "Chris", 66 }); + Assert.Fail("should throw ObjectNotOfRequiredTypeException"); + } + catch (ObjectNotOfRequiredTypeException) + { + } + } + + [Test] + public virtual void FactorySingleton() + { + Assert.IsTrue(ObjectFactory.IsSingleton("&singletonFactory")); + Assert.IsTrue(ObjectFactory.IsSingleton("singletonFactory")); + TestObject tb = (TestObject) ObjectFactory.GetObject("singletonFactory"); + Assert.IsTrue(tb.Name.Equals(DummyFactory.SINGLETON_NAME), "Singleton from factory has correct name, not " + tb.Name); + DummyFactory factory = (DummyFactory) ObjectFactory.GetObject("&singletonFactory"); + TestObject tb2 = (TestObject) ObjectFactory.GetObject("singletonFactory"); + Assert.IsTrue(tb == tb2, "Singleton references =="); + Assert.IsTrue(factory.ObjectFactory != null, "FactoryObject is ObjectFactoryAware"); + } + + [Test] + public virtual void FactoryPrototype() + { + Assert.IsTrue(ObjectFactory.IsSingleton("&prototypeFactory")); + Assert.IsFalse(ObjectFactory.IsSingleton("prototypeFactory")); + TestObject tb = (TestObject) ObjectFactory.GetObject("prototypeFactory"); + Assert.IsTrue(!tb.Name.Equals(DummyFactory.SINGLETON_NAME)); + TestObject tb2 = (TestObject) ObjectFactory.GetObject("prototypeFactory"); + Assert.IsTrue(tb != tb2, "Prototype references !="); + } + + /// + /// Check that we can get the factory object itself. + /// This is only possible if we're dealing with a factory + /// + [Test] + public virtual void GetFactoryItself() + { + DummyFactory factory = (DummyFactory) ObjectFactory.GetObject("&singletonFactory"); + Assert.IsTrue(factory != null); + } + + /// Check that AfterPropertiesSet gets called on factory. + [Test] + public virtual void FactoryIsInitialized() + { + TestObject tb = (TestObject) ObjectFactory.GetObject("singletonFactory"); + DummyFactory factory = (DummyFactory) ObjectFactory.GetObject("&singletonFactory"); + Assert.IsTrue(factory.WasInitialized, "Factory was not initialized even though it implemented IInitializingObject"); + } + + /// + /// It should be illegal to dereference a normal object as a factory. + /// + [Test] + [ExpectedException(typeof (ObjectIsNotAFactoryException))] + public virtual void RejectsFactoryGetOnNormalObject() + { + ObjectFactory.GetObject("&rod"); + } + + [Test] + [ExpectedException(typeof (ObjectDefinitionStoreException))] + public virtual void Aliasing() + { + if (!(ObjectFactory is AbstractObjectFactory)) + { + return; + } + + string alias = "rods alias"; + try + { + ObjectFactory.GetObject(alias); + Assert.Fail("Shouldn't permit factory get on normal object"); + } + catch (NoSuchObjectDefinitionException ex) + { + Assert.IsTrue(alias.Equals(ex.ObjectName)); + } + + // Create alias + ((AbstractObjectFactory) ObjectFactory).RegisterAlias("rod", alias); + object rod = ObjectFactory.GetObject("rod"); + object aliasRod = ObjectFactory.GetObject(alias); + Assert.IsTrue(rod == aliasRod); + ((AbstractObjectFactory) ObjectFactory).RegisterAlias("father", alias); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void RegisterSingletonWithEmptyName() + { + ((AbstractObjectFactory) ObjectFactory) + .RegisterSingleton(Environment.NewLine, DBNull.Value); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void RegisterSingletonWithNullName() + { + ((AbstractObjectFactory) ObjectFactory) + .RegisterSingleton(null, DBNull.Value); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ContainsSingletonWithEmptyName() + { + ((AbstractObjectFactory) ObjectFactory) + .ContainsSingleton(Environment.NewLine); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ContainsSingletonWithNullName() + { + ((AbstractObjectFactory) ObjectFactory) + .ContainsSingleton(null); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void AliasWithEmptyName() + { + ((AbstractObjectFactory) ObjectFactory).RegisterAlias(Environment.NewLine, "the whipping boy"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void AliasWithEmptyAlias() + { + ((AbstractObjectFactory) ObjectFactory).RegisterAlias("rick", Environment.NewLine); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void ChokesIfNotGivenSupportedIObjectDefinitionImplementation() + { + ObjectFactory.GetObject("unsupportedDefinition"); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/MyRequiredAttribute.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/MyRequiredAttribute.cs new file mode 100644 index 00000000..9a5bfcc0 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/MyRequiredAttribute.cs @@ -0,0 +1,35 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Objects.Factory.Attributes +{ + /// + /// Custom attribute to indicate required setter property + /// + /// Mark Pollack + /// $Id: MyRequiredAttribute.cs,v 1.1 2008/04/02 18:02:31 markpollack Exp $ + [AttributeUsage(AttributeTargets.Property)] + public class MyRequiredAttribute : Attribute + { + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredAttributeObjectPostProcessorTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredAttributeObjectPostProcessorTests.cs new file mode 100644 index 00000000..2cb91ec0 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredAttributeObjectPostProcessorTests.cs @@ -0,0 +1,129 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Reflection; +using NUnit.Framework; +using Spring.Context.Support; + +#endregion + +namespace Spring.Objects.Factory.Attributes +{ + /// + /// This class contains tests for RequiredAttributeObjectPostProcessor. + /// + /// Mark Pollack + /// $Id: RequiredAttributeObjectPostProcessorTests.cs,v 1.1 2008/04/02 18:02:31 markpollack Exp $ + [TestFixture] + public class RequiredAttributeObjectPostProcessorTests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void WithRequiredPropertyOmitted() + { + try + { + XmlApplicationContext ctx = + new XmlApplicationContext(false, + "assembly://Spring.Core.Tests/Spring.Objects.Factory.Attributes/RequiredWithOneRequiredPropertyOmitted.xml"); + Assert.Fail("Should have thrown ObjectCreationException"); + } + catch (ObjectCreationException ex) + { + string message = ex.InnerException.Message; + Assert.IsTrue(message.IndexOf("Property") > -1); + Assert.IsTrue(message.IndexOf("Age") > -1); + Assert.IsTrue(message.IndexOf("testObject") > -1); + } + } + + [Test] + public void WithThreeRequiredPropertiesOmitted() + { + try + { + XmlApplicationContext ctx = + new XmlApplicationContext(false, + "assembly://Spring.Core.Tests/Spring.Objects.Factory.Attributes/RequiredWithThreeRequiredPropertiesOmitted.xml"); + Assert.Fail("Should have thrown ObjectCreationException"); + } + catch (ObjectCreationException ex) + { + string message = ex.InnerException.Message; + Assert.IsTrue(message.IndexOf("Properties") > -1); + Assert.IsTrue(message.IndexOf("Age") > -1); + Assert.IsTrue(message.IndexOf("FavoriteColor") > -1); + Assert.IsTrue(message.IndexOf("JobTitle") > -1); + Assert.IsTrue(message.IndexOf("testObject") > -1); + } + } + + [Test] + public void WithOnlyRequiredPropertiesSpecified() + { + XmlApplicationContext ctx = + new XmlApplicationContext(false, + "assembly://Spring.Core.Tests/Spring.Objects.Factory.Attributes/RequiredWithAllRequiredPropertiesProvided.xml"); + RequiredTestObject to = (RequiredTestObject) ctx.GetObject("testObject"); + Assert.AreEqual(24, to.Age); + Assert.AreEqual("Blue", to.GetFavoriteColor()); + } + + [Test] + public void Reflection() + { + foreach (PropertyInfo pi in typeof(RequiredTestObject).GetProperties(BindingFlags.Instance | BindingFlags.Public)) + { + if (pi.Name.Equals("Age")) + { + object[] attribs = pi.GetCustomAttributes(typeof (RequiredAttribute), true); + Assert.Greater(attribs.Length, 0); + } + } + + } + + [Test] + public void WithCustomAttribute() + { + try + { + XmlApplicationContext ctx = + new XmlApplicationContext(false, + "assembly://Spring.Core.Tests/Spring.Objects.Factory.Attributes/RequiredWithCustomAttribute.xml"); + Assert.Fail("Should have thrown ObjectCreationException"); + } + catch (ObjectCreationException ex) + { + string message = ex.InnerException.Message; + Assert.IsTrue(message.IndexOf("Property") > -1); + Assert.IsTrue(message.IndexOf("Name") > -1); + Assert.IsTrue(message.IndexOf("testObject") > -1); + } + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredTestObject.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredTestObject.cs new file mode 100644 index 00000000..5fe6e0be --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredTestObject.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +namespace Spring.Objects.Factory.Attributes +{ + /// + /// Test object for testing 'required' attribute functionality. + /// + /// Mark Pollack + /// $Id: RequiredTestObject.cs,v 1.1 2008/04/02 18:02:31 markpollack Exp $ + public class RequiredTestObject : IObjectNameAware, IObjectFactoryAware + { + private string name; + + private int age; + + private string favoriteColor; + + private string jobTitle; + + [Required] + public int Age + { + get { return age; } + set { age = value; } + } + + [MyRequired] + public string Name + { + get { return name; } + set { name = value; } + } + + + [Required] + public string FavoriteColor + { + set { favoriteColor = value; } + } + + public string GetFavoriteColor() + { + return favoriteColor; + } + + [Required] + public string JobTitle + { + get { return jobTitle; } + set { jobTitle = value; } + } + + [Required] + public string ObjectName + { + set { } + } + + [Required] + public IObjectFactory ObjectFactory + { + set { } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithAllRequiredPropertiesProvided.xml b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithAllRequiredPropertiesProvided.xml new file mode 100644 index 00000000..78138a6e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithAllRequiredPropertiesProvided.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithCustomAttribute.xml b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithCustomAttribute.xml new file mode 100644 index 00000000..d1f311a5 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithCustomAttribute.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithOneRequiredPropertyOmitted.xml b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithOneRequiredPropertyOmitted.xml new file mode 100644 index 00000000..b7206973 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithOneRequiredPropertyOmitted.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithThreeRequiredPropertiesOmitted.xml b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithThreeRequiredPropertiesOmitted.xml new file mode 100644 index 00000000..3ae41a51 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Attributes/RequiredWithThreeRequiredPropertiesOmitted.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/AbstractFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/AbstractFactoryObjectTests.cs new file mode 100644 index 00000000..55050f32 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/AbstractFactoryObjectTests.cs @@ -0,0 +1,86 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using DotNetMock.Dynamic; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the basic functionality of the AbstractFactoryObject class. + /// + /// Rick Evans + /// $Id: AbstractFactoryObjectTests.cs,v 1.3 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class AbstractFactoryObjectTests + { + [Test] + public void DisposeCallbackIsNotInvokedOnDisposeIfInPrototypeMode() + { + IDynamicMock mock = new DynamicMock(typeof(IDisposable)); + DummyFactoryObject factory = new DummyFactoryObject(mock.Object); + factory.IsSingleton = false; + factory.GetObject(); + factory.Dispose(); + // in prototype mode, so the Dispose() method of the object must not be called... + mock.Verify(); + } + + [Test] + public void DisposeCallbackIsInvokedOnDispose() + { + IDynamicMock mock = new DynamicMock(typeof(IDisposable)); + mock.Expect("Dispose"); + DummyFactoryObject factory = new DummyFactoryObject(mock.Object); + factory.AfterPropertiesSet(); + factory.Dispose(); + mock.Verify(); + } + + private sealed class DummyFactoryObject : AbstractFactoryObject + { + public object theObject; + + public DummyFactoryObject() : this (new object()) + { + } + + public DummyFactoryObject(object theObject) + { + this.theObject = theObject; + } + + public override Type ObjectType + { + get { return typeof(object); } + } + + protected override object CreateInstance() + { + return theObject; + } + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/CommandLineArgsVariableSourceTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/CommandLineArgsVariableSourceTests.cs new file mode 100644 index 00000000..539ac1e8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/CommandLineArgsVariableSourceTests.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the CommandLineArgsVariableSource class. + /// + /// Aleksandar Seovic + /// $Id: CommandLineArgsVariableSourceTests.cs,v 1.1 2007/03/10 01:44:40 aseovic Exp $ + [TestFixture] + public sealed class CommandLineArgsVariableSourceTests + { + [Test] + public void TestVariablesResolution() + { + CommandLineArgsVariableSource vs = new CommandLineArgsVariableSource( + new string[] {"program.exe", "file.txt", "/name:Aleks Seovic", "/framework:Spring.NET"}); + + // existing vars + Assert.AreEqual("Spring.NET", vs.ResolveVariable("FRAMEWORK")); + Assert.AreEqual("Spring.NET", vs.ResolveVariable("framework")); + Assert.AreEqual("Aleks Seovic", vs.ResolveVariable("name")); + Assert.AreEqual("Aleks Seovic", vs.ResolveVariable("NAME")); + + // non-existant variable + Assert.IsNull(vs.ResolveVariable("dummy")); + } + + [Test] + public void TestVariablesResolutionWithCustomPrefixAndSeparator() + { + CommandLineArgsVariableSource vs = new CommandLineArgsVariableSource( + new string[] { "program.exe", "file.txt", "--Name=Aleks Seovic", "--Framework=Spring.NET" }); + vs.ArgumentPrefix = "--"; + vs.ValueSeparator = "="; + + // existing vars + Assert.AreEqual("Spring.NET", vs.ResolveVariable("FRAMEWORK")); + Assert.AreEqual("Spring.NET", vs.ResolveVariable("framework")); + Assert.AreEqual("Aleks Seovic", vs.ResolveVariable("name")); + Assert.AreEqual("Aleks Seovic", vs.ResolveVariable("NAME")); + + // non-existant variable + Assert.IsNull(vs.ResolveVariable("dummy")); + } + + [Test] + [Explicit] + public void TestLiveVariablesResolutionWithTestDriven() + { + CommandLineArgsVariableSource vs = new CommandLineArgsVariableSource(); + Assert.IsTrue(((string) vs.ResolveVariable("AssemblyName")).StartsWith("TestDriven.TestRunner.Server")); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConfigSectionVariableSourceTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConfigSectionVariableSourceTests.cs new file mode 100644 index 00000000..3ccbb3b8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConfigSectionVariableSourceTests.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Core.IO; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the ConfigSectionVariableSource class. + /// + /// Aleksandar Seovic + /// $Id: ConfigSectionVariableSourceTests.cs,v 1.3 2007/08/08 17:49:04 bbaia Exp $ + [TestFixture] + public sealed class ConfigSectionVariableSourceTests + { + [Test] + public void TestVariablesResolutionWithSingleSection() + { + ConfigSectionVariableSource vs = new ConfigSectionVariableSource(); + vs.SectionName = "DaoConfiguration"; + + // existing vars + Assert.AreEqual("1000", vs.ResolveVariable("maxResults")); + Assert.AreEqual("1000", vs.ResolveVariable("MAXResults")); + + // non-existant variable + Assert.IsNull(vs.ResolveVariable("dummy")); + } + + [Test] + public void TestVariablesResolutionWithTwoSections() + { + ConfigSectionVariableSource vs = new ConfigSectionVariableSource(); + vs.SectionNames = new string[] { "DaoConfiguration", "DatabaseConfiguration" }; + + // existing vars + Assert.AreEqual("1000", vs.ResolveVariable("maxResults")); + Assert.AreEqual("1000", vs.ResolveVariable("MAXResults")); + Assert.AreEqual(@"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=c:\Northwind.mdb;User ID=Admin;Password=;", + vs.ResolveVariable("connection.string")); + Assert.AreEqual(@"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=c:\Northwind.mdb;User ID=Admin;Password=;", + vs.ResolveVariable("Connection.String")); + + // non-existant variable + Assert.IsNull(vs.ResolveVariable("dummy")); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConfigurationReaderTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConfigurationReaderTests.cs new file mode 100644 index 00000000..13246d8a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConfigurationReaderTests.cs @@ -0,0 +1,164 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections.Specialized; +using System.Configuration; +using System.IO; +using System.Text; + +using NUnit.Framework; + +using Spring.Core.IO; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the ConfigurationReader class. + /// + /// Rick Evans + /// $Id: ConfigurationReaderTests.cs,v 1.7 2007/08/08 17:49:04 bbaia Exp $ + [TestFixture] + public sealed class ConfigurationReaderTests + { + private const string SunnyDayXml = @" + + +
    + + + + + +"; + + [Test] + public void ReadSunnyDay() + { + new StreamHelperDecorator(new StreamHelperCallback(_ReadSunnyDay)).Run(); + } + + private void _ReadSunnyDay(out Stream stream) + { + stream = new MemoryStream(Encoding.UTF8.GetBytes(SunnyDayXml)); + NameValueCollection props + = ConfigurationReader.Read(new InputStreamResource(stream, ""), "foo"); + Assert.IsNotNull(props, "Failed to read in any properties at all (props is null)."); + Assert.AreEqual(2, props.Count, "Wrong number of properties read in."); + Assert.AreEqual("kiley", props["rilo"], "Wrong value for second property"); + Assert.AreEqual("lewis", props["jenny"], "Wrong value for second property"); + } + + [Test] + public void ReadWithOverrideOfPreviouslyExistingValues() + { + new StreamHelperDecorator(new StreamHelperCallback(_ReadWithOverrideOfPreviouslyExistingValues)).Run(); + } + + private void _ReadWithOverrideOfPreviouslyExistingValues(out Stream stream) + { + stream = new MemoryStream(Encoding.UTF8.GetBytes(SunnyDayXml)); + NameValueCollection defaults = new NameValueCollection(); + defaults.Add("jenny", "agutter"); + NameValueCollection props + = ConfigurationReader.Read(new InputStreamResource(stream, ""), "foo", defaults); + Assert.IsTrue(ReferenceEquals(defaults, props), "Must have got same collection as was passed in."); + Assert.AreEqual("lewis", props["jenny"], "Wrong value for overridden property (was not overridden"); + } + + [Test] + public void ReadWithOverrideOfPreviouslyExistingValuesButWithOverrideSwitchedOff() + { + new StreamHelperDecorator(new StreamHelperCallback(_ReadWithOverrideOfPreviouslyExistingValuesButWithOverrideSwitchedOff)).Run(); + } + + private void _ReadWithOverrideOfPreviouslyExistingValuesButWithOverrideSwitchedOff(out Stream stream) + { + stream = new MemoryStream(Encoding.UTF8.GetBytes(SunnyDayXml)); + NameValueCollection defaults = new NameValueCollection(); + defaults.Add("jenny", "agutter"); + NameValueCollection props + = ConfigurationReader.Read(new InputStreamResource(stream, ""), "foo", defaults, false); + Assert.IsTrue(ReferenceEquals(defaults, props), "Must have got same collection as was passed in."); + Assert.AreEqual("agutter,lewis", props["jenny"], "Wrong value for overridden property (was not overridden"); + } + + [Test] + public void ReadWithNullExistingValuesPassedIn() + { + new StreamHelperDecorator(new StreamHelperCallback(_ReadWithNullExistingValuesPassedIn)).Run(); + } + + private void _ReadWithNullExistingValuesPassedIn(out Stream stream) + { + stream = new MemoryStream(Encoding.UTF8.GetBytes(SunnyDayXml)); + NameValueCollection props + = ConfigurationReader.Read(new InputStreamResource(stream, ""), "foo", null); + Assert.IsNotNull(props, "Failed to read in any properties at all (props is null)."); + Assert.AreEqual(2, props.Count, "Wrong number of properties read in."); + Assert.AreEqual("kiley", props["rilo"], "Wrong value for second property"); + Assert.AreEqual("lewis", props["jenny"], "Wrong value for second property"); + } + + [Test] + public void ReadWithNoConfigSectionSectionDefaultsToNameValueSectionHandler() + { + new StreamHelperDecorator(new StreamHelperCallback(_ReadWithNoConfigSectionSectionDefaultsToNameValueSectionHandler)).Run(); + } + + private void _ReadWithNoConfigSectionSectionDefaultsToNameValueSectionHandler(out Stream stream) + { + const string NoConfigSectionXml = @" + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(NoConfigSectionXml)); + NameValueCollection props + = ConfigurationReader.Read(new InputStreamResource(stream, ""), "foo", null); + Assert.IsNotNull(props, "Failed to read in any properties at all (props is null)."); + Assert.AreEqual(2, props.Count, "Wrong number of properties read in."); + Assert.AreEqual("kiley", props["rilo"], "Wrong value for second property"); + Assert.AreEqual("lewis", props["jenny"], "Wrong value for second property"); + } + + [Test] +#if !NET_2_0 + [ExpectedException(typeof(ConfigurationException), "Cannot read properties; config section 'ELNOMBRE' not found.")] +#else + [ExpectedException(typeof(ConfigurationErrorsException), "Cannot read properties; config section 'ELNOMBRE' not found.")] +#endif + public void TryReadFromNonExistantConfigSection() + { + new StreamHelperDecorator(new StreamHelperCallback(_TryReadFromNonExistantConfigSection)).Run(); + } + + private void _TryReadFromNonExistantConfigSection(out Stream stream) + { + stream = new MemoryStream(Encoding.UTF8.GetBytes(SunnyDayXml)); + ConfigurationReader.Read(new InputStreamResource(stream, ""), "ELNOMBRE", null); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConnectionStringsVariableSourceTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConnectionStringsVariableSourceTests.cs new file mode 100644 index 00000000..1ef5622a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConnectionStringsVariableSourceTests.cs @@ -0,0 +1,56 @@ +#if NET_2_0 + +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the ConnectionStringsVariableSource class. + /// + /// Aleksandar Seovic + /// $Id: ConnectionStringsVariableSourceTests.cs,v 1.2 2007/05/30 17:32:37 oakinger Exp $ + [TestFixture] + public sealed class ConnectionStringsVariableSourceTests + { + [Test] + public void TestVariablesResolution() + { + ConnectionStringsVariableSource vs = new ConnectionStringsVariableSource(); + + // existing vars + Assert.AreEqual("mySqlServerConnectionString", vs.ResolveVariable("mySqlDataSource.connectionString")); + Assert.AreEqual("System.Data.SqlClient", vs.ResolveVariable("mySqlDataSource.providerName")); + Assert.AreEqual("myOracleConnectionString", vs.ResolveVariable("myOracleDataSource.connectionString")); + Assert.AreEqual("System.Data.OracleClient", vs.ResolveVariable("myOracleDataSource.providerName")); + + // non-existant variable + Assert.IsNull(vs.ResolveVariable("dummy")); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConstructorArgumentValuesTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConstructorArgumentValuesTests.cs new file mode 100644 index 00000000..5c43570a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ConstructorArgumentValuesTests.cs @@ -0,0 +1,296 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Collections; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the ConstructorArgumentValues class. + /// + /// Rick Evans (.NET) + [TestFixture] + public sealed class ConstructorArgumentValuesTests + { + [Test] + public void Instantiation() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + Assert.IsNotNull(values.GenericArgumentValues, "The 'GenericArgumentValues' property was not initialised."); + Assert.IsNotNull(values.IndexedArgumentValues, "The 'IndexedArgumentValues' property was not initialised."); + Assert.IsNotNull(values.NamedArgumentValues, "The 'NamedArgumentValues' property was not initialised."); + Assert.AreEqual(0, values.ArgumentCount, "There were some arguments in a newly initialised instance."); + Assert.IsTrue(values.Empty, "A newly initialised instance was not initially empty."); + } + + [Test] + public void GetGenericArgumentValueIgnoresAlreadyUsedValues() + { + ISet used = new ListSet(); + + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddGenericArgumentValue(1); + values.AddGenericArgumentValue(2); + values.AddGenericArgumentValue(3); + + Type intType = typeof (int); + ConstructorArgumentValues.ValueHolder one = values.GetGenericArgumentValue(intType, used); + Assert.AreEqual(1, one.Value); + used.Add(one); + ConstructorArgumentValues.ValueHolder two = values.GetGenericArgumentValue(intType, used); + Assert.AreEqual(2, two.Value); + used.Add(two); + ConstructorArgumentValues.ValueHolder three = values.GetGenericArgumentValue(intType, used); + Assert.AreEqual(3, three.Value); + used.Add(three); + ConstructorArgumentValues.ValueHolder four = values.GetGenericArgumentValue(intType, used); + Assert.IsNull(four); + } + + [Test] + public void GetGeneric_Untyped_ArgumentValue() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + const string expectedValue = "Rick"; + values.AddGenericArgumentValue(expectedValue); + + ConstructorArgumentValues.ValueHolder name = values.GetGenericArgumentValue(null, null); + Assert.IsNotNull(name, + "Must get non-null valueholder back if no required type is specified."); + Assert.AreEqual(expectedValue, name.Value); + } + + [Test] + public void GetGeneric_Untyped_ArgumentValueWithOnlyStronglyTypedValuesInTheCtorValueList() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + const string expectedValue = "Rick"; + values.AddGenericArgumentValue(expectedValue, typeof(string).FullName); + + ConstructorArgumentValues.ValueHolder name = values.GetGenericArgumentValue(null, null); + Assert.IsNull(name, + "Must get null valueholder back if no required type is specified but only " + + "strongly typed values are present in the ctor values list."); + } + + [Test] + public void GetArgumentValueIgnoresAlreadyUsedValues() + { + ISet used = new ListSet(); + + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddGenericArgumentValue(1); + values.AddNamedArgumentValue("2", 2); + values.AddIndexedArgumentValue(3, 3); + + Type intType = typeof (int); + ConstructorArgumentValues.ValueHolder one = values.GetArgumentValue(10, string.Empty, intType, used); + Assert.AreEqual(1, one.Value); + used.Add(one); + ConstructorArgumentValues.ValueHolder two = values.GetArgumentValue(10, "2", intType, used); + Assert.AreEqual(2, two.Value); + used.Add(two); + ConstructorArgumentValues.ValueHolder three = values.GetArgumentValue(3, string.Empty, intType, used); + Assert.AreEqual(3, three.Value); + used.Add(three); + ConstructorArgumentValues.ValueHolder four = values.GetArgumentValue(10, string.Empty, intType, used); + Assert.IsNull(four); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddNamedArgumentWithNullName() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddNamedArgumentValue(null, 1); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddNamedArgumentWithEmptyStringName() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddNamedArgumentValue(string.Empty, 1); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddNamedArgumentWithWhitespaceStringName() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddNamedArgumentValue(Environment.NewLine + " ", 1); + } + + [Test] + public void AddIndexedArgumentValue() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddIndexedArgumentValue(1, DBNull.Value); + Assert.IsFalse(values.Empty, "Added one value, but the collection is sayin' it's empty."); + Assert.AreEqual(1, values.ArgumentCount, "Added one value, but the collection ain't sayin' that it's got a single element in it."); + Assert.AreEqual(1, values.IndexedArgumentValues.Count, "Added one indexed value, but the collection of indexed values ain't sayin' that it's got a single element in it."); + } + + [Test] + public void AddGenericArgumentValue() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddGenericArgumentValue(DBNull.Value); + Assert.IsFalse(values.Empty, "Added one value, but the collection is sayin' it's empty."); + Assert.AreEqual(1, values.ArgumentCount, "Added one value, but the collection ain't sayin' that it's got a single element in it."); + Assert.AreEqual(1, values.GenericArgumentValues.Count, "Added one generic value, but the collection of indexed values ain't sayin' that it's got a single element in it."); + } + + [Test] + public void GetIndexedArgumentValue() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + Assert.IsNull(values.GetIndexedArgumentValue(0, typeof (object)), "Mmm... managed to get a non null instance back from an empty instance."); + values.AddIndexedArgumentValue(16, DBNull.Value, typeof (DBNull).FullName); + Assert.IsNull(values.GetIndexedArgumentValue(0, typeof (object)), "Mmm... managed to get a non null instance back from an instance that should have now't at the specified index."); + ConstructorArgumentValues.ValueHolder value = + values.GetIndexedArgumentValue(16, typeof (DBNull)); + Assert.IsNotNull(value, "Stored a value at a specified index, but got null when retrieving it."); + Assert.AreSame(DBNull.Value, value.Value, "The value stored at the specified index was not the exact same instance as was added."); + ConstructorArgumentValues.ValueHolder wrongValue = + values.GetIndexedArgumentValue(16, typeof (string)); + Assert.IsNull(wrongValue, "Stored a value at a specified index, and got it (or rather something) back when retrieving it with the wrong Type specified."); + } + + [Test] + public void GetGenericArgumentValue() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + Assert.IsNull(values.GetGenericArgumentValue(typeof (object)), "Mmm... managed to get a non null instance back from an empty instance."); + values.AddGenericArgumentValue(DBNull.Value, typeof (DBNull).FullName); + Assert.IsNull(values.GetGenericArgumentValue(typeof (string)), "Mmm... managed to get a non null instance back from an instance that should have now't with the specified Type."); + ConstructorArgumentValues.ValueHolder value = + values.GetGenericArgumentValue(typeof (DBNull)); + Assert.IsNotNull(value, "Stored a value of a specified Type, but got null when retrieving it using said Type."); + Assert.AreSame(DBNull.Value, value.Value, "The value stored at the specified index was not the exact same instance as was added."); + } + + [Test] + public void GetArgumentValue() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + Assert.IsNull(values.GetArgumentValue(0, typeof (object)), "Mmm... managed to get a non null instance back from an empty instance."); + values.AddGenericArgumentValue(DBNull.Value, typeof (DBNull).FullName); + values.AddNamedArgumentValue("foo", DBNull.Value); + values.AddIndexedArgumentValue(16, DBNull.Value, typeof (DBNull).FullName); + Assert.IsNull(values.GetArgumentValue(100, typeof (string)), "Mmm... managed to get a non null instance back from an instance that should have now't with the specified Type."); + ConstructorArgumentValues.ValueHolder value = + values.GetArgumentValue(-3, typeof (DBNull)); + Assert.IsNotNull(value, "Stored a value of a specified Type at a specified index, but got null when retrieving it using the wrong index but the correct Type."); + Assert.AreSame(DBNull.Value, value.Value, "The retrieved value was not the exact same instance as was added."); + + value = values.GetArgumentValue("foo", typeof (DBNull)); + Assert.IsNotNull(value, "Stored a value of a specified Type under a name, but got null when retrieving it using the wrong name but the correct Type."); + Assert.AreSame(DBNull.Value, value.Value, "The retrieved value was not the exact same instance as was added."); + } + + [Test] + public void AddAllDoesntChokeOnNullArgument() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddAll(null); + } + + [Test] + public void AddAllFromOther() + { + ConstructorArgumentValues other = new ConstructorArgumentValues(); + other.AddIndexedArgumentValue(1, DBNull.Value); + other.AddIndexedArgumentValue(2, "Foo"); + other.AddIndexedArgumentValue(3, 3); + + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddAll(other); + Assert.AreEqual(other.ArgumentCount, values.ArgumentCount, + "Must have been the same since one was filled up with the values in the other."); + } + + [Test] + public void AddRangeOfIndexedArgumentValues() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddIndexedArgumentValue(1, DBNull.Value); + values.AddIndexedArgumentValue(2, "Foo"); + values.AddIndexedArgumentValue(3, 3); + new ConstructorArgumentValues(values); + Assert.IsFalse(values.Empty, "Added three indexed values(as a range), but the collection is sayin' it's empty."); + Assert.AreEqual(3, values.ArgumentCount, "Added three indexed values(as a range), but the collection ain't sayin' that it's got 3 elements in it."); + Assert.AreEqual(3, values.IndexedArgumentValues.Count, "Added three indexed values(as a range), but the collection of indexed values ain't sayin' that it's got 3 elements in it."); + } + + [Test] + public void NamedArgumentsAreCaseInsensitive() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddNamedArgumentValue("foo", "sball"); + Assert.AreEqual(1, values.NamedArgumentValues.Count, "Added one named argument but it doesn't seem to have been added to the named arguments collection."); + Assert.AreEqual(1, values.ArgumentCount, "Added one named argument but it doesn't seem to be reflected in the overall argument count."); + Assert.IsTrue(values.ContainsNamedArgument("FOo"), "Mmm, the ContainsNamedArgument() method eveidently IS case sensitive (which is wrong)."); + ConstructorArgumentValues.ValueHolder arg = values.GetNamedArgumentValue("fOo"); + Assert.IsNotNull(arg, "The named argument previously added could not be pulled from the ctor arg collection."); + Assert.AreEqual("sball", arg.Value, "The value of the named argument passed in is not the same as the one that was pulled out."); + } + + [Test] + public void AddNamedArgument() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddNamedArgumentValue("foo", "sball"); + Assert.AreEqual(1, values.NamedArgumentValues.Count, "Added one named argument but it doesn't seem to have been added to the named arguments collection."); + Assert.AreEqual(1, values.ArgumentCount, "Added one named argument but it doesn't seem to be reflected in the overall argument count."); + ConstructorArgumentValues.ValueHolder arg = values.GetNamedArgumentValue("foo"); + Assert.IsNotNull(arg, "The named argument previously added could not be pulled from the ctor arg collection."); + Assert.AreEqual("sball", arg.Value, "The value of the named argument passed in is not the same as the one that was pulled out."); + } + + [Test] + public void AddNamedArgumentFromAotherCtorArgCollection() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddNamedArgumentValue("foo", "sball"); + ConstructorArgumentValues copy = new ConstructorArgumentValues(values); + Assert.AreEqual(1, copy.NamedArgumentValues.Count, "Added one named argument but it doesn't seem to have been added to the named arguments collection."); + Assert.AreEqual(1, copy.ArgumentCount, "Added one named argument but it doesn't seem to be reflected in the overall argument count."); + ConstructorArgumentValues.ValueHolder arg = copy.GetNamedArgumentValue("foo"); + Assert.IsNotNull(arg, "The named argument previously added could not be pulled from the ctor arg collection."); + Assert.AreEqual("sball", arg.Value, "The value of the named argument passed in is not the same as the one that was pulled out."); + } + + [Test] + public void ValueHolderToStringsNicely() + { + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddGenericArgumentValue(1, typeof(int).FullName); + ConstructorArgumentValues.ValueHolder vh = values.GetGenericArgumentValue(typeof(int)); + Assert.AreEqual("'1' [System.Int32]", vh.ToString()); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/CustomConverterConfigurerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/CustomConverterConfigurerTests.cs new file mode 100644 index 00000000..82b15cf3 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/CustomConverterConfigurerTests.cs @@ -0,0 +1,126 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using DotNetMock.Dynamic; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the CustomConverterConfigurer class. + /// + /// Rick Evans + /// $Id: CustomConverterConfigurerTests.cs,v 1.6 2007/05/11 02:32:31 markpollack Exp $ + [TestFixture] + public sealed class CustomConverterConfigurerTests + { + [Test] + [ExpectedException(typeof(ObjectInitializationException))] + public void UseInvalidKeyForConverterMapKey() + { + IDictionary converters = new Hashtable(); + converters.Add(12, typeof(DateTimeConverter)); + + DynamicMock mock = new DynamicMock(typeof(IConfigurableListableObjectFactory)); + IConfigurableListableObjectFactory factory + = (IConfigurableListableObjectFactory) mock.Object; + + CustomConverterConfigurer config = new CustomConverterConfigurer(); + config.CustomConverters = converters; + config.PostProcessObjectFactory(factory); + } + + [Test] + [ExpectedException(typeof(ObjectInitializationException))] + public void UseNonTypeConverterForConverterMapValue() + { + IDictionary converters = new Hashtable(); + converters.Add( typeof(DateTime), null); + + DynamicMock mock = new DynamicMock(typeof(IConfigurableListableObjectFactory)); + IConfigurableListableObjectFactory factory + = (IConfigurableListableObjectFactory) mock.Object; + + CustomConverterConfigurer config = new CustomConverterConfigurer(); + config.CustomConverters = converters; + config.PostProcessObjectFactory(factory); + } + + [Test] + [ExpectedException(typeof(ObjectInitializationException))] + public void UseNonResolvableTypeForConverterMapKey() + { + IDictionary converters = new Hashtable(); + // purposely misspelled... :D + converters.Add("Systemm.Date", typeof(DateTimeConverter)); + + DynamicMock mock = new DynamicMock(typeof(IConfigurableListableObjectFactory)); + IConfigurableListableObjectFactory factory + = (IConfigurableListableObjectFactory) mock.Object; + + CustomConverterConfigurer config = new CustomConverterConfigurer(); + config.CustomConverters = converters; + config.PostProcessObjectFactory(factory); + } + + /// + /// Just tests that the configurer doesn't blow up and + /// doesn't register anything ('cos we ain't supplied anything). + /// + [Test] + public void DontSupplyAnyCustomConverters() + { + DynamicMock mock = new DynamicMock(typeof(IConfigurableListableObjectFactory)); + IConfigurableListableObjectFactory factory + = (IConfigurableListableObjectFactory) mock.Object; + CustomConverterConfigurer config = new CustomConverterConfigurer(); + config.CustomConverters = null; + config.PostProcessObjectFactory(factory); + mock.Verify(); + } + + [Test] + public void SunnyDayScenario() + { + IDictionary converters = new Hashtable(); + converters.Add( typeof(DateTime), new DateTimeConverter()); + converters.Add( typeof(Color), new ColorConverter()); + + DynamicMock mock = new DynamicMock(typeof(IConfigurableListableObjectFactory)); + mock.Expect("RegisterCustomConverter"); + mock.Expect("RegisterCustomConverter"); + IConfigurableListableObjectFactory mockFactory + = (IConfigurableListableObjectFactory) mock.Object; + + CustomConverterConfigurer config = new CustomConverterConfigurer(); + config.CustomConverters = converters; + config.PostProcessObjectFactory(mockFactory); + mock.Verify(); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/DelegateFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/DelegateFactoryObjectTests.cs new file mode 100644 index 00000000..e361bc3a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/DelegateFactoryObjectTests.cs @@ -0,0 +1,180 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the DelegateFactoryObject class. + /// + /// Rick Evans + /// $Id: DelegateFactoryObjectTests.cs,v 1.5 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class DelegateFactoryObjectTests + { + [Test] + [ExpectedException(typeof (ArgumentException))] + public void StaticDelegateWithInstanceMethod() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.DelegateType = typeof (PopHandler); + fob.TargetType = typeof (OneThirstyDude); + fob.MethodName = "HandlePop"; + fob.IsSingleton = false; + fob.AfterPropertiesSet(); + fob.GetObject(); + } + + [Test] + public void StaticDelegate() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.DelegateType = typeof (PopHandler); + fob.TargetType = typeof (OneThirstyDude); + fob.MethodName = "StaticHandlePop"; + fob.IsSingleton = false; + fob.AfterPropertiesSet(); + PopHandler popper = (PopHandler) fob.GetObject(); + Assert.IsNotNull(popper); + Assert.AreEqual(fob.MethodName, popper.Method.Name); + } + + [Test] + public void InstanceSingletonDelegate() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.DelegateType = typeof (PopHandler); + OneThirstyDude dude = new OneThirstyDude(); + fob.TargetObject = dude; + fob.MethodName = "HandlePop"; + fob.AfterPropertiesSet(); + PopHandler popper = (PopHandler) fob.GetObject(); + Assert.IsNotNull(popper); + Assert.AreEqual(fob.MethodName, popper.Method.Name); + string soda = "The Drink Of Champions"; + popper(this, soda); + Assert.AreEqual(soda, dude.Soda); + PopHandler other = (PopHandler) fob.GetObject(); + Assert.IsTrue(ReferenceEquals(popper, other)); + } + + [Test] + public void InstancePrototypeDelegate() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.IsSingleton = false; + fob.DelegateType = typeof (PopHandler); + OneThirstyDude dude = new OneThirstyDude(); + fob.TargetObject = dude; + fob.MethodName = "HandlePop"; + fob.IsSingleton = false; + fob.AfterPropertiesSet(); + PopHandler one = (PopHandler) fob.GetObject(); + PopHandler two = (PopHandler) fob.GetObject(); + Assert.IsFalse(ReferenceEquals(one, two)); + } + + [Test] + public void ObjectType() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + Assert.IsNotNull(fob.ObjectType, "Should never be null (should default to typeof(Delegate)) "); + Assert.AreEqual(typeof (Delegate), fob.ObjectType, "Not defaulting to typeof(Delegate)."); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void MissingDelegateType() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void BadDelegateType() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.DelegateType = DBNull.Value.GetType(); + fob.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void NullMethodName() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.DelegateType = typeof (EventHandler); + fob.TargetType = typeof (OneThirstyDude); + fob.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void EmptyMethodName() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.DelegateType = typeof (EventHandler); + fob.TargetType = typeof (OneThirstyDude); + fob.MethodName = string.Empty; + fob.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void WhitespacedMethodName() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.DelegateType = typeof (EventHandler); + fob.TargetType = typeof (OneThirstyDude); + fob.MethodName = "\n"; + fob.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void MissingATarget() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.DelegateType = typeof (EventHandler); + fob.MethodName = "I Love You Laura Palmer, I Really Do"; + fob.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void ChokesIfBothTargetTypeAndTargetObjectSupplied() + { + DelegateFactoryObject fob = new DelegateFactoryObject(); + fob.DelegateType = typeof (PopHandler); + fob.TargetType = typeof (OneThirstyDude); + fob.TargetObject = new OneThirstyDude(); + fob.MethodName = "I Love You Laura Palmer, I Really Do"; + fob.AfterPropertiesSet(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/DictionaryFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/DictionaryFactoryObjectTests.cs new file mode 100644 index 00000000..bda44582 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/DictionaryFactoryObjectTests.cs @@ -0,0 +1,122 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the DictionaryFactoryObject class. + /// + /// Rick Evans + /// $Id: DictionaryFactoryObjectTests.cs,v 1.2 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class DictionaryFactoryObjectTests + { + [Test] + [ExpectedException(typeof (ArgumentException), "The Type passed to the TargetDictionaryType property must implement the 'System.Collections.IDictionary' interface.")] + public void SetTargetDictionaryTypeToNonDictionaryType() + { + DictionaryFactoryObject dfo = new DictionaryFactoryObject(); + dfo.TargetDictionaryType = typeof (ICollection); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The Type passed to the TargetDictionaryType property cannot be an interface; it must be a concrete class that implements the 'System.Collections.IDictionary' interface.")] + public void SetTargetDictionaryTypeToDerivedIDictionaryInterfaceType() + { + DictionaryFactoryObject dfo = new DictionaryFactoryObject(); + dfo.TargetDictionaryType = typeof (IExtendedDictionary); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The Type passed to the TargetDictionaryType property cannot be abstract (MustInherit in VisualBasic.NET); it must be a concrete class that implements the 'System.Collections.IDictionary' interface.")] + public void SetTargetDictionaryTypeToAbstractIDictionaryInterfaceType() + { + DictionaryFactoryObject dfo = new DictionaryFactoryObject(); + dfo.TargetDictionaryType = typeof (AbstractDictionary); + } + + private interface IExtendedDictionary : IDictionary + { + } + + private abstract class AbstractDictionary : Hashtable + { + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void SetTargetDictionaryTypeToNull() + { + DictionaryFactoryObject dfo = new DictionaryFactoryObject(); + dfo.TargetDictionaryType = null; + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The 'SourceDictionary' property cannot be null (Nothing in Visual Basic.NET).")] + public void GetObjectWithoutSupplyingASourceDictionary() + { + DictionaryFactoryObject dfo = new DictionaryFactoryObject(); + dfo.IsSingleton = false; + dfo.GetObject(); + } + + [Test] + public void ObjectTypeReallyIsIDictionary() + { + Assert.AreEqual(typeof (IDictionary), new DictionaryFactoryObject().ObjectType); + } + + [Test] + public void GetObjectReallyDoesPopulateANewIDictionaryInstanceWithTheElementsOfTheSourceDictionary() + { + // man, that has got to be my longest ever test name :D ... + IDictionary source = new Hashtable(); + TestObject rick = new TestObject("Rick", 30); + source.Add(rick.Name, rick); + TestObject mark = new TestObject("Mark", 35); + source.Add(mark.Name, mark); + TestObject griffin = new TestObject("Griffin", 21); + source.Add(griffin.Name, griffin); + + DictionaryFactoryObject dfo = new DictionaryFactoryObject(); + dfo.SourceDictionary = source; + dfo.AfterPropertiesSet(); + + IDictionary dic = (IDictionary) dfo.GetObject(); + Assert.IsNotNull(dic); + Assert.AreEqual(source.Count, dic.Count); + foreach (DictionaryEntry entry in dic) + { + string name = (string) entry.Key; + TestObject dude = (TestObject) entry.Value; + TestObject originalDude = (TestObject) source[name]; + Assert.IsTrue(object.ReferenceEquals(dude, originalDude)); + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/EnvironmentVariableSourceTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/EnvironmentVariableSourceTests.cs new file mode 100644 index 00000000..6f267340 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/EnvironmentVariableSourceTests.cs @@ -0,0 +1,53 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Microsoft.Win32; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the EnvironmentVariableSource class. + /// + /// Aleksandar Seovic + /// $Id: EnvironmentVariableSourceTests.cs,v 1.1 2007/03/10 01:44:40 aseovic Exp $ + [TestFixture] + public sealed class EnvironmentVariableSourceTests + { + [Test] + public void TestVariablesResolution() + { + EnvironmentVariableSource vs = new EnvironmentVariableSource(); + + // existing vars + Assert.AreEqual(Environment.GetEnvironmentVariable("path"), vs.ResolveVariable("PATH")); + Assert.AreEqual(Environment.GetEnvironmentVariable("PATH"), vs.ResolveVariable("path")); + Assert.AreEqual(Environment.GetEnvironmentVariable("ComputerName"), vs.ResolveVariable("computerName")); + + // non-existant variable + Assert.IsNull(vs.ResolveVariable("dummy")); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/EventValuesTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/EventValuesTests.cs new file mode 100644 index 00000000..92ad6b00 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/EventValuesTests.cs @@ -0,0 +1,147 @@ +using NUnit.Framework; + +namespace Spring.Objects.Factory.Config +{ + [TestFixture] + public class EventValuesTests + { + [Test] + public void EmptyEventValues() + { + EventValues eventValues = new EventValues(); + Assert.AreEqual( 0, eventValues.Events.Count ); + } + [Test] + public void OneEventValue() + { + EventValues eventValues = new EventValues(); + eventValues.AddHandler( new MyEventHandler()); + Assert.AreEqual( 1, eventValues.Events.Count ); + } + [Test] + public void TwoEventHandlersForSameEvent() + { + EventValues eventValues = new EventValues(); + eventValues.AddHandler( new MyEventHandler()); + eventValues.AddHandler( new MyEventHandler()); + Assert.AreEqual( 1, eventValues.Events.Count ); + } + [Test] + public void CopyEventHandlerValues() + { + EventValues eventValues = new EventValues(); + eventValues.AddHandler( new MyEventHandler()); + eventValues.AddHandler( new MyEventHandler()); + Assert.AreEqual( 1, eventValues.Events.Count ); + EventValues eventValues2 = new EventValues(eventValues); + Assert.AreEqual( 1, eventValues2.Events.Count ); + } + [Test] + public void NullEventHandlerValue() + { + EventValues eventValues = new EventValues(); + eventValues.AddAll( null ); + Assert.AreEqual( 0, eventValues.Events.Count ); + } + internal class MyEventHandler : IEventHandlerValue + { + #region IEventHandlerValue Members + + private object _source = new object(); + private string _eventName = "MyEvent"; + private string _methodName = "MyEventHandlerMethod"; + public object Source + { + get + { + return _source; + } + set + { + _source = value; + } + } + + public string EventName + { + get + { + return _eventName; + } + set + { + _eventName = value; + } + } + + public string MethodName + { + get + { + return _methodName; + } + set + { + _methodName = value; + } + } + + public void Wire(object source, object handler) + { + } + + #endregion + + } + internal class MyEventHandler2 : IEventHandlerValue + { + #region IEventHandlerValue Members + + private object _source = new object(); + private string _eventName = "MyEvent"; + private string _methodName = "MyEventHandlerMethod"; + public object Source + { + get + { + return _source; + } + set + { + _source = value; + } + } + + public string EventName + { + get + { + return _eventName; + } + set + { + _eventName = value; + } + } + + public string MethodName + { + get + { + return _methodName; + } + set + { + _methodName = value; + } + } + + public void Wire(object source, object handler) + { + } + + #endregion + + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/FieldRetrievingFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/FieldRetrievingFactoryObjectTests.cs new file mode 100644 index 00000000..00e9a5e1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/FieldRetrievingFactoryObjectTests.cs @@ -0,0 +1,232 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the FieldRetrievingFactoryObject class. + /// + /// Juergen Hoeller + /// Rick Evans + /// $Id: FieldRetrievingFactoryObjectTests.cs,v 1.5 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class FieldRetrievingFactoryObjectTests + { + [Test] + public void StaticField() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.StaticField = "Spring.Objects.Factory.Config.FinalFielder.Name, Spring.Core.Tests"; + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (string), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(FinalFielder.Name, actual); + } + + [Test] + public void StaticFieldWithWhitespace() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.StaticField = "\tSpring.Objects.Factory.Config.FinalFielder.Name, Spring.Core.Tests "; + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (string), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(FinalFielder.Name, actual); + } + + [Test] + public void StaticFieldThatAintAssemblyQualifiedShouldStillBeResolved() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.StaticField = "Spring.Objects.Factory.Config.FinalFielder.Name"; + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (string), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(FinalFielder.Name, actual); + } + + [Test] + public void StaticFieldSystemClass() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.StaticField = "System.DBNull.Value"; + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (DBNull), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(DBNull.Value, actual); + } + + [Test] + public void UsesObjectNameForStaticField() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.ObjectName = "System.Type.Delimiter"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (char), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(Type.Delimiter, actual); + } + + [Test] + public void OnlyUsesObjectNameForStaticFieldIfTheStaticFieldHasNotBeenSet() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.ObjectName = "System.DBNull.Value"; + fac.StaticField = "Spring.Objects.Factory.Config.FinalFielder.Name"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (string), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(FinalFielder.Name, actual); + } + + [Test] + public void StaticFieldViaClassAndFieldName() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.TargetField = "Name"; + fac.TargetType = typeof (FinalFielder); + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + object actual = fac.GetObject(); + Assert.AreEqual(FinalFielder.Name, actual); + } + + [Test] + public void IsSingleton() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + Assert.IsTrue(fac.IsSingleton); + } + + [Test] + public void InstanceField() + { + FinalFielder expected = new FinalFielder(); + expected.Age = 56; + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.TargetObject = expected; + fac.TargetField = "Age"; + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + object actual = fac.GetObject(); + Assert.AreEqual(expected.Age, actual); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void NothingSet() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void JustTargetField() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.TargetField = "Space"; + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void JustTargetType() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.TargetType = GetType(); + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void JustTargetObject() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.TargetObject = this; + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void BailsWhenStaticFieldPassedGumpfh() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.StaticField = "Goob"; // no field specified + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + } + + [Test] + public void StaticFieldIsCaseINsenSiTiVE() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.StaticField = "System.Type.emptytyPES, Mscorlib"; + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (Type[]), fac.ObjectType); + Type[] actual = fac.GetObject() as Type[]; + Assert.IsNotNull(actual); + Assert.AreEqual(Type.EmptyTypes.Length, actual.Length); + } + + [Test] + [ExpectedException(typeof (ObjectDefinitionStoreException))] + public void BailsOnNonExistantField() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.StaticField = "Spring.Objects.Factory.Config.FinalFielder.Rubbish, Spring.Core.Tests"; + fac.ObjectName = "foo"; + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void BailsWhenBothTargetTypeAndTargetObjectPropsAreSet() + { + FieldRetrievingFactoryObject fac = new FieldRetrievingFactoryObject(); + fac.TargetObject = this; + fac.TargetType = GetType(); + fac.AfterPropertiesSet(); + } + } + + internal sealed class FinalFielder + { + public const string Name = "WalkingTall"; + + public int Age = 43; + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ListFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ListFactoryObjectTests.cs new file mode 100644 index 00000000..e249ac59 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ListFactoryObjectTests.cs @@ -0,0 +1,120 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the ListFactoryObject class. + /// + /// Rick Evans + /// $Id: ListFactoryObjectTests.cs,v 1.2 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class ListFactoryObjectTests + { + [Test] + [ExpectedException(typeof (ArgumentException), "The Type passed to the TargetListType property must implement the 'System.Collections.IList' interface.")] + public void SetTargetListTypeToNonListType() + { + ListFactoryObject lfo = new ListFactoryObject(); + lfo.TargetListType = typeof (ICollection); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The Type passed to the TargetListType property cannot be an interface; it must be a concrete class that implements the 'System.Collections.IList' interface.")] + public void SetTargetListTypeToDerivedIListInterfaceType() + { + ListFactoryObject lfo = new ListFactoryObject(); + lfo.TargetListType = typeof (IExtendedList); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The Type passed to the TargetListType property cannot be abstract (MustInherit in VisualBasic.NET); it must be a concrete class that implements the 'System.Collections.IList' interface.")] + public void SetTargetListTypeToAbstractIListInterfaceType() + { + ListFactoryObject lfo = new ListFactoryObject(); + lfo.TargetListType = typeof (AbstractList); + } + + private interface IExtendedList : IList + { + } + + private abstract class AbstractList : ArrayList + { + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void SetTargetListTypeToNull() + { + ListFactoryObject lfo = new ListFactoryObject(); + lfo.TargetListType = null; + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The 'SourceList' property cannot be null (Nothing in Visual Basic.NET).")] + public void GetObjectWithoutSupplyingASourceList() + { + ListFactoryObject lfo = new ListFactoryObject(); + lfo.IsSingleton = false; + lfo.GetObject(); + } + + [Test] + public void ObjectTypeReallyIsIList() + { + Assert.AreEqual(typeof (IList), new ListFactoryObject().ObjectType); + } + + [Test] + public void GetObjectReallyDoesPopulateANewIListInstanceWithTheElementsOfTheSourceList() + { + // man, that has got to be my second longest ever test name :D ... + IList source = new TestObject[] + { + new TestObject("Rick", 30), + new TestObject("Mark", 35), + new TestObject("Griffin", 21), + }; + + ListFactoryObject lfo = new ListFactoryObject(); + lfo.SourceList = source; + lfo.AfterPropertiesSet(); + + IList list = (IList) lfo.GetObject(); + Assert.IsNotNull(list); + Assert.AreEqual(source.Count, list.Count); + for (int i = 0; i < list.Count; ++i) + { + TestObject dude = (TestObject) list[i]; + TestObject originalDude = (TestObject) source[i]; + Assert.IsTrue(object.ReferenceEquals(dude, originalDude)); + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/LogFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/LogFactoryObjectTests.cs new file mode 100644 index 00000000..a8bf2d32 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/LogFactoryObjectTests.cs @@ -0,0 +1,136 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the LogFactoryObject class. + /// + /// Rick Evans (.NET) + /// $Id: LogFactoryObjectTests.cs,v 1.1 2006/11/15 20:31:08 aseovic Exp $ + [TestFixture] + public sealed class LogFactoryObjectTests + { + [Test] + [ExpectedException(typeof (ArgumentException))] + public void CheckThatLogNamePropertyMustBeSet() + { + LogFactoryObject fac = new LogFactoryObject(); + fac.GetObject(); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void CheckLogNamePropertyWithNullName() + { + LogFactoryObject fac = new LogFactoryObject(); + fac.LogName = null; + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void CheckLogNamePropertyWithEmptyName() + { + LogFactoryObject fac = new LogFactoryObject(); + fac.LogName = string.Empty; + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void CheckInstantiationWithNullName() + { + new LogFactoryObject(null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void CheckInstantiationWithEmptyName() + { + new LogFactoryObject(string.Empty); + } + + [Test] + public void CheckLogNamePropertyStripsRedundantWhitespaceFromName() + { + LogFactoryObject fac = new LogFactoryObject(); + fac.LogName = " Foo "; + Assert.AreEqual("Foo", fac.LogName); + } + + [Test] + public void CheckGetObjectWorksWithValidLogName() + { + LogFactoryObject fac = new LogFactoryObject(); + fac.LogName = "Foo"; + ILog log = fac.GetObject() as ILog; + Assert.IsNotNull(log, "Mmm... pulled a null ILog instance from a properly configured LogFactoryObject instance."); + } + + [Test] + public void CheckGetObjectReturnsSharedInstance() + { + LogFactoryObject fac = new LogFactoryObject(); + fac.LogName = "Foo"; + ILog log = fac.GetObject() as ILog; + ILog anotherLogInstance = fac.GetObject() as ILog; + Assert.IsTrue(log == anotherLogInstance, + "Okay, the LogFactoryObject ain't returning shared instances (it should)."); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The 'LogName' property has not been set.")] + public void CheckAfterPropertiesSetBlowsUpIfNotCorrectlyConfigured() + { + LogFactoryObject fac = new LogFactoryObject(); + fac.AfterPropertiesSet(); + } + + [Test] + public void CheckAfterPropertiesSetPassesIfCorrectlyConfigured() + { + LogFactoryObject fac = new LogFactoryObject("Bing!"); + fac.AfterPropertiesSet(); + } + + [Test] + public void ItsDefinitelyASingletonYeah() + { + LogFactoryObject fac = new LogFactoryObject("Bing!"); + Assert.IsTrue(fac.IsSingleton, + "The LogFactoryObject class must be configured to return shared instances (it currently ain't)."); + } + + [Test] + public void ObjectTypePropertyYieldsTheCorrectType() + { + LogFactoryObject fac = new LogFactoryObject("Bing!"); + Assert.AreEqual(typeof (ILog), fac.ObjectType, + "Mmm... the LogFactoryObject class ain't giving back ILog types (it must)."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/MethodInvokingFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/MethodInvokingFactoryObjectTests.cs new file mode 100644 index 00000000..2a77ba77 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/MethodInvokingFactoryObjectTests.cs @@ -0,0 +1,297 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; +using Spring.Core; +using Spring.Objects.Support; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Set of unit tests for the MethodInvokingFactoryObject. + /// + /// Colin Sampaleanu + /// Simon White (.NET) + [TestFixture] + public class MethodInvokingFactoryObjectTests + { +#if NET_2_0 + [Test] + public void InvokeGenericMethod() + { + TestClass1 tc1 = new TestClass1(); + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof(Activator); + mcfo.TargetMethod = "CreateInstance"; + mcfo.AfterPropertiesSet(); + + object obj = mcfo.GetObject(); + Assert.IsNotNull(obj); + Assert.IsTrue(obj is TestObject); + } +#endif + [Test] + public void GetSingletonNonStatic() + { + TestClass1 tc1 = new TestClass1(); + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetObject = tc1; + mcfo.TargetMethod = "Method1"; + mcfo.AfterPropertiesSet(); + int i = (int) mcfo.GetObject(); + Assert.IsTrue(i == 1); + i = (int) mcfo.GetObject(); + Assert.IsTrue(i == 1); + Assert.IsTrue(mcfo.IsSingleton); + } + + [Test] + public void GetNonSingletonNonStatic() + { + TestClass1 tc1 = new TestClass1(); + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetObject = tc1; + mcfo.TargetMethod = "Method1"; + mcfo.IsSingleton = false; + mcfo.AfterPropertiesSet(); + int i = (int) mcfo.GetObject(); + Assert.IsTrue(i == 1); + i = (int) mcfo.GetObject(); + Assert.IsTrue(i == 2); + Assert.IsFalse(mcfo.IsSingleton); + } + + [Test] + public void GetSingletonStatic() + { + TestClass1._staticField1 = 0; + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof (TestClass1); + mcfo.TargetMethod = "StaticMethod1"; + mcfo.AfterPropertiesSet(); + int i = (int) mcfo.GetObject(); + Assert.IsTrue(i == 1); + i = (int) mcfo.GetObject(); + Assert.IsTrue(i == 1); + Assert.IsTrue(mcfo.IsSingleton); + } + + [Test] + public void GetNonSingletonStatic() + { + TestClass1._staticField1 = 0; + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof(TestClass1); + mcfo.TargetMethod = "StaticMethod1"; + mcfo.IsSingleton = false; + mcfo.AfterPropertiesSet(); + int i = (int) mcfo.GetObject(); + Assert.IsTrue(i == 1); + i = (int) mcfo.GetObject(); + Assert.IsTrue(i == 2); + Assert.IsFalse(mcfo.IsSingleton); + } + + [Test] + public void InvokingAMethodThatHasAVoidReturnTypeReturnsNullPlaceHolder() + { + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof (TestClass1); + mcfo.TargetMethod = "VoidRetvalMethod"; + mcfo.AfterPropertiesSet(); + Assert.AreEqual(MethodInvoker.Void, mcfo.GetObject()); + } + + [Test] + public void GetSupertypesMatchNumArgs() + { + TestClass1._staticField1 = 0; + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof (TestClass1); + mcfo.TargetMethod = "Supertypes"; + mcfo.Arguments = new Object[] {new ArrayList(), new ArrayList(), "hello"}; + // should pass + mcfo.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "Unable to determine which exact method to call; found '2' matches.")] + public void GetSupertypesTooManyArgs() + { + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof (TestClass1); + mcfo.TargetMethod = "Supertypes2"; + mcfo.Arguments = new Object[] {new ArrayList(), new ArrayList(), "hello", "bogus"}; + mcfo.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (TypeMismatchException))] + public void GetMisMatchedArgumentTypes() + { + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof (TestClass1); + mcfo.TargetMethod = "Supertypes"; + mcfo.Arguments = new Object[] {"1", "2", "3"}; + mcfo.AfterPropertiesSet(); + mcfo.Invoke(); + } + + [Test] + public void GetObjectType() + { + TestClass1 tc1 = new TestClass1(); + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetObject = tc1; + mcfo.TargetMethod = "Method1"; + mcfo.AfterPropertiesSet(); + Assert.IsTrue(typeof (int).Equals(mcfo.ObjectType)); + + mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof (TestClass1); + mcfo.TargetMethod = "VoidRetvalMethod"; + mcfo.AfterPropertiesSet(); + Type objType = mcfo.ObjectType; + Assert.IsTrue(objType.Equals(MethodInvokingFactoryObject.Void.GetType())); + + // verify that we can call a method with args that are subtypes of the + // target method arg types + TestClass1._staticField1 = 0; + mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof (TestClass1); + mcfo.TargetMethod = "Supertypes"; + mcfo.Arguments = new Object[] {new ArrayList(), new ArrayList(), "hello"}; + mcfo.AfterPropertiesSet(); + objType = mcfo.ObjectType; + } + + [Test] + public void ObjectTypeIsNullIfAfterPropertiesSetHasNotYetBeenInvoked() + { + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof (TestClass1); + mcfo.TargetMethod = "VoidRetvalMethod"; + Assert.IsNull(mcfo.ObjectType, + "ObjectType property value must only be set to a non null value " + + "AFTER the AfterPropertiesSet() method has been invoked."); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The 'TargetMethod' property is required.")] + public void BailsIfTheTargetMethodPropertyAintSet() + { + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (MissingMethodException))] + public void AfterPropertiesSetBogusMethod() + { + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetObject = this; + mcfo.TargetMethod = "whatever"; + mcfo.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (MissingMethodException))] + public void AfterPropertiesSetBogusStaticMethod() + { + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof (TestClass1); + mcfo.TargetMethod = "some.bogus.Method.name"; + mcfo.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void AfterPropertiesSetStaticMethodMissingArgs() + { + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetType = typeof (TestClass1); + mcfo.TargetMethod = "Method1"; + mcfo.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void AfterPropertiesSetMissingMethod() + { + MethodInvokingFactoryObject mcfo = new MethodInvokingFactoryObject(); + mcfo.TargetObject = this; + mcfo.AfterPropertiesSet(); + } + + [Test] + public void InvokeWithNullArgument() + { + MethodInvoker methodInvoker = new MethodInvoker(); + methodInvoker.TargetType = GetType(); + methodInvoker.TargetMethod = "NullArgument"; + methodInvoker.Arguments = new object[] {null}; + methodInvoker.Prepare(); + methodInvoker.Invoke(); + } + + public static void NullArgument(object arg) + { + } + + // a test class to work with + public class TestClass1 + { + public static int _staticField1; + public int _field1 = 0; + + public int Method1() + { + return ++_field1; + } + + public static int StaticMethod1() + { + return ++_staticField1; + } + + public static void VoidRetvalMethod() + { + } + + public static void Supertypes(ICollection c, IList l, string s) + { + } + + public static void Supertypes2(ICollection c, IList l, string s, object i) + { + } + + public static void Supertypes2(ICollection c, IList l, string s, string s2) + { + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectDefinitionVisitorTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectDefinitionVisitorTests.cs new file mode 100644 index 00000000..2e441657 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectDefinitionVisitorTests.cs @@ -0,0 +1,173 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Collections.Specialized; + +using NUnit.Framework; +using Spring.Objects.Factory.Support; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the ObjectDefinitionVisitor class + /// + /// Bruno Baia + [TestFixture] + public class ObjectDefinitionVisitorTests + { + private NameValueCollectionVariableSource variableSource; + + [SetUp] + public void SetUp() + { + NameValueCollection nvc = new NameValueCollection(); + nvc.Add("Property", "Value"); + variableSource = new NameValueCollectionVariableSource(nvc); + } + + [Test] + public void VisitObjectTypeName() + { + IObjectDefinition od = new RootObjectDefinition(); + od.ObjectTypeName = "$Property"; + + ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource); + odv.VisitObjectDefinition(od); + + Assert.AreEqual("Value", od.ObjectTypeName); + } + + [Test] + public void VisitPropertyValues() + { + IObjectDefinition od = new RootObjectDefinition(); + od.PropertyValues.Add("PropertyName", "$Property"); + + ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource); + odv.VisitObjectDefinition(od); + + Assert.AreEqual("Value", od.PropertyValues.GetPropertyValue("PropertyName").Value); + } + + + [Test] + public void VisitManagedList() + { + IObjectDefinition od = new RootObjectDefinition(); + ManagedList ml = new ManagedList(); + ml.ElementTypeName = "$Property"; + ml.Add("$Property"); + od.PropertyValues.Add("PropertyName", ml); + + ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource); + odv.VisitObjectDefinition(od); + + ManagedList list = od.PropertyValues.GetPropertyValue("PropertyName").Value as ManagedList; + + Assert.AreEqual("Value", list.ElementTypeName); + Assert.AreEqual("Value", list[0]); + } + + [Test] + public void VisitManagedSet() + { + IObjectDefinition od = new RootObjectDefinition(); + ManagedSet ms = new ManagedSet(); + ms.ElementTypeName = "$Property"; + ms.Add("$Property"); + od.PropertyValues.Add("PropertyName", ms); + + ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource); + odv.VisitObjectDefinition(od); + + ManagedSet set = od.PropertyValues.GetPropertyValue("PropertyName").Value as ManagedSet; + + Assert.AreEqual("Value", set.ElementTypeName); + IEnumerator enumerator = set.GetEnumerator(); + enumerator.MoveNext(); + Assert.AreEqual("Value", enumerator.Current); + } + + [Test] + public void VisitManagedDictionary() + { + IObjectDefinition od = new RootObjectDefinition(); + ManagedDictionary md = new ManagedDictionary(); + md.KeyTypeName = "$Property"; + md.ValueTypeName = "$Property"; + md.Add("Key", "$Property"); + od.PropertyValues.Add("PropertyName", md); + + ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource); + odv.VisitObjectDefinition(od); + + ManagedDictionary dictionary = od.PropertyValues.GetPropertyValue("PropertyName").Value as ManagedDictionary; + + Assert.AreEqual("Value", dictionary.KeyTypeName); + Assert.AreEqual("Value", dictionary.ValueTypeName); + Assert.AreEqual("Value", dictionary["Key"]); + } + + [Test] + public void VisitNameValueCollection() + { + IObjectDefinition od = new RootObjectDefinition(); + NameValueCollection nvc = new NameValueCollection(); + nvc["Key"] = "$Property"; + od.PropertyValues.Add("PropertyName", nvc); + + ObjectDefinitionVisitor odv = new ObjectDefinitionVisitor(variableSource); + odv.VisitObjectDefinition(od); + + NameValueCollection visitedNvc = + od.PropertyValues.GetPropertyValue("PropertyName").Value as NameValueCollection; + + Assert.AreEqual("Value", visitedNvc["Key"]); + } + + #region Helper class + + public class NameValueCollectionVariableSource : IVariableSource + { + private NameValueCollection properties; + + public NameValueCollectionVariableSource(NameValueCollection properties) + { + this.properties = properties; + } + + public string ResolveVariable(string name) + { + if (name.StartsWith("$")) + { + return properties[name.Substring(1)]; + } + else + { + return name; + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectFactoryCreatingFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectFactoryCreatingFactoryObjectTests.cs new file mode 100644 index 00000000..c435115f --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectFactoryCreatingFactoryObjectTests.cs @@ -0,0 +1,109 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using DotNetMock.Dynamic; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the ObjectFactoryCreatingFactoryObject class. + /// + /// Colin Sampaleanu + /// Simon White (.NET) + [TestFixture] + public sealed class ObjectFactoryCreatingFactoryObjectTests + { + [Test] + public void SunnyDay() + { + TestObject dude = new TestObject("Rick Evans", 30); + DynamicMock mock = new DynamicMock(typeof (IObjectFactory)); + const string lookupObjectName = "rick"; + mock.ExpectAndReturn("GetObject", dude, lookupObjectName); + mock.ExpectAndReturn("GetObject", dude, lookupObjectName); + ObjectFactoryCreatingFactoryObject factory = new ObjectFactoryCreatingFactoryObject(); + factory.ObjectFactory = (IObjectFactory) mock.Object; + factory.TargetObjectName = lookupObjectName; + factory.AfterPropertiesSet(); + + IGenericObjectFactory gof = (IGenericObjectFactory) factory.GetObject(); + IGenericObjectFactory gofOther = (IGenericObjectFactory) factory.GetObject(); + Assert.IsTrue(Object.ReferenceEquals(gof, gofOther), + "Not returning a shared instance (Singleton = true)."); + TestObject one = (TestObject) gof.GetObject(); + Assert.IsNotNull(one, "Must never return null (IFactoryObject contract)."); + TestObject two = (TestObject) gof.GetObject(); + Assert.IsNotNull(two, "Must never return null (IFactoryObject contract)."); + Assert.IsTrue(Object.ReferenceEquals(one, two), + "Not returning the same instance."); + mock.Verify(); + } + + [Test] + public void PrototypeModeWithSingletonTarget() + { + TestObject dude = new TestObject("Rick Evans", 30); + DynamicMock mock = new DynamicMock(typeof (IObjectFactory)); + const string lookupObjectName = "rick"; + mock.ExpectAndReturn("GetObject", dude, lookupObjectName); + mock.ExpectAndReturn("GetObject", dude, lookupObjectName); + ObjectFactoryCreatingFactoryObject factory = new ObjectFactoryCreatingFactoryObject(); + factory.ObjectFactory = (IObjectFactory) mock.Object; + factory.TargetObjectName = lookupObjectName; + factory.IsSingleton = false; + factory.AfterPropertiesSet(); + + IGenericObjectFactory gofOne = (IGenericObjectFactory) factory.GetObject(); + IGenericObjectFactory gofTwo = (IGenericObjectFactory) factory.GetObject(); + Assert.IsFalse(Object.ReferenceEquals(gofOne, gofTwo), + "Not returning distinct instances (Prototype = true)."); + TestObject one = (TestObject) gofOne.GetObject(); + Assert.IsNotNull(one, "Must never return null (IFactoryObject contract)."); + TestObject two = (TestObject) gofTwo.GetObject(); + Assert.IsNotNull(two, "Must never return null (IFactoryObject contract)."); + Assert.IsTrue(Object.ReferenceEquals(one, two), + "Not returning the same instance to singleton object."); + mock.Verify(); + } + + [Test] + [ExpectedException(typeof (ArgumentException), + "The 'TargetObjectName' property must have a value.")] + public void WithMissingObjectName() + { + ObjectFactoryCreatingFactoryObject factory + = new ObjectFactoryCreatingFactoryObject(); + factory.AfterPropertiesSet(); + } + + [Test] + public void ObjectTypeReallyIsIGenericObjectFactory() + { + Assert.AreEqual(typeof (IGenericObjectFactory), + new ObjectFactoryCreatingFactoryObject().ObjectType); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectReferenceFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectReferenceFactoryObjectTests.cs new file mode 100644 index 00000000..33176de5 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ObjectReferenceFactoryObjectTests.cs @@ -0,0 +1,129 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using DotNetMock.Dynamic; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the ObjectReferenceFactoryObject class. + /// + /// Rick Evans + /// $Id: ObjectReferenceFactoryObjectTests.cs,v 1.3 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class ObjectReferenceFactoryObjectTests + { + [Test] + [ExpectedException(typeof(ArgumentException))] + public void NullTargetObjectName() + { + ObjectReferenceFactoryObject fac = new ObjectReferenceFactoryObject(); + // simulate IFactoryObjectAware interface... + fac.ObjectFactory = null; + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WhitespaceTargetObjectName() + { + ObjectReferenceFactoryObject fac = new ObjectReferenceFactoryObject(); + fac.TargetObjectName = string.Empty; + // simulate IFactoryObjectAware interface... + fac.ObjectFactory = null; + } + + [Test] + public void FactoryDoesNotContainTargetObject() + { + DynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("ContainsObject", false); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + ObjectReferenceFactoryObject fac = new ObjectReferenceFactoryObject(); + fac.TargetObjectName = "bojangles"; + try + { + // simulate IFactoryObjectAware interface... + fac.ObjectFactory = mockFactory; + Assert.Fail("Must have bailed with a " + + "NoSuchObjectDefinitionException 'cos the object doesn't " + + "exist in the associated factory."); + } + catch (NoSuchObjectDefinitionException) + { + mock.Verify(); + } + } + + [Test] + public void DelegatesThroughToFactoryFor_IsSingleton() + { + DynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("ContainsObject", true); + mock.ExpectAndReturn("IsSingleton", true); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + ObjectReferenceFactoryObject fac = new ObjectReferenceFactoryObject(); + fac.TargetObjectName = "bojangles"; + fac.ObjectFactory = mockFactory; + + Assert.IsTrue(fac.IsSingleton); + mock.Verify(); + } + + [Test] + public void DelegatesThroughToFactoryFor_GetObject() + { + DynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("ContainsObject", true); + mock.ExpectAndReturn("GetObject", "Rick"); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + ObjectReferenceFactoryObject fac = new ObjectReferenceFactoryObject(); + fac.TargetObjectName = "bojangles"; + fac.ObjectFactory = mockFactory; + + Assert.AreEqual("Rick", fac.GetObject()); + mock.Verify(); + } + + [Test] + public void DelegatesThroughToFactoryFor_ObjectType() + { + DynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("ContainsObject", true); + mock.ExpectAndReturn("GetType", GetType()); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + ObjectReferenceFactoryObject fac = new ObjectReferenceFactoryObject(); + fac.TargetObjectName = "bojangles"; + fac.ObjectFactory = mockFactory; + + Assert.AreEqual(GetType(), fac.ObjectType); + mock.Verify(); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyFileVariableSourceTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyFileVariableSourceTests.cs new file mode 100644 index 00000000..e6050d65 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyFileVariableSourceTests.cs @@ -0,0 +1,75 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Core.IO; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the PropertyFileVariableSource class. + /// + /// Aleksandar Seovic + /// $Id: PropertyFileVariableSourceTests.cs,v 1.3 2007/08/08 17:49:04 bbaia Exp $ + [TestFixture] + public sealed class PropertyFileVariableSourceTests + { + [Test] + public void TestVariablesResolutionWithSingleLocation() + { + PropertyFileVariableSource vs = new PropertyFileVariableSource(); + vs.Location = + new AssemblyResource( + "assembly://Spring.Core.Tests/Spring.Data.Spring.Objects.Factory.Config/one.properties"); + + // existing vars + Assert.AreEqual("Aleks Seovic", vs.ResolveVariable("name")); + Assert.AreEqual("32", vs.ResolveVariable("age")); + + // non-existant variable + Assert.IsNull(vs.ResolveVariable("dummy")); + } + + [Test] + public void TestVariablesResolutionWithTwoLocations() + { + PropertyFileVariableSource vs = new PropertyFileVariableSource(); + vs.Locations = new IResource[] + { + new AssemblyResource( + "assembly://Spring.Core.Tests/Spring.Data.Spring.Objects.Factory.Config/one.properties"), + new AssemblyResource( + "assembly://Spring.Core.Tests/Spring.Data.Spring.Objects.Factory.Config/two.properties") + }; + + // existing vars + Assert.AreEqual("Aleksandar Seovic", vs.ResolveVariable("name")); // should be overriden by the second file + Assert.AreEqual("32", vs.ResolveVariable("age")); + Assert.AreEqual("Marija,Ana,Nadja", vs.ResolveVariable("family")); + + // non-existant variable + Assert.IsNull(vs.ResolveVariable("dummy")); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyOverrideConfigurerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyOverrideConfigurerTests.cs new file mode 100644 index 00000000..e579c4f6 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyOverrideConfigurerTests.cs @@ -0,0 +1,190 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections.Specialized; +using Common.Logging; +using Common.Logging.Simple; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Context.Support; +using Spring.Core.IO; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the PropertyOverrideConfigurer class. + /// + /// Rick Evans + /// $Id: PropertyOverrideConfigurerTests.cs,v 1.9 2007/10/10 16:31:50 bbaia Exp $ + [TestFixture] + public sealed class PropertyOverrideConfigurerTests + { + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp() + { + // enable (null appender) logging, just to ensure that the logging code is correct + LogManager.Adapter = new NoOpLoggerFactoryAdapter(); + } + + [Test] + public void AddPropertyValue() + { + StaticApplicationContext ac = new StaticApplicationContext(); + ac.RegisterSingleton("tb1", typeof(TestObject), new MutablePropertyValues()); + ac.RegisterSingleton("tb2", typeof(TestObject), new MutablePropertyValues()); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("Properties", ""); + ac.RegisterSingleton("configurer1", typeof(PropertyOverrideConfigurer), pvs); + pvs = new MutablePropertyValues(); + pvs.Add("Properties", ""); + pvs.Add("order", "0"); + ac.RegisterSingleton("configurer2", typeof(PropertyOverrideConfigurer), pvs); + ac.Refresh(); + TestObject tb1 = (TestObject)ac.GetObject("tb1"); + TestObject tb2 = (TestObject)ac.GetObject("tb2"); + Assert.AreEqual(99, tb1.Age); + Assert.AreEqual(99, tb2.Age); + Assert.AreEqual(null, tb1.Name); + Assert.AreEqual("test", tb2.Name); + } + + [Test] + public void OverridePropertyValue() + { + StaticApplicationContext ac = new StaticApplicationContext(); + + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("Age", 27); + pvs.Add("Name", "Bruno"); + ac.RegisterSingleton("tb1", typeof(TestObject), pvs); + + pvs = new MutablePropertyValues(); + pvs.Add("Properties", ""); + ac.RegisterSingleton("configurer", typeof (PropertyOverrideConfigurer), pvs); + + ac.Refresh(); + TestObject tb1 = (TestObject) ac.GetObject("tb1"); + Assert.AreEqual(99, tb1.Age); + Assert.AreEqual("test", tb1.Name); + } + + [Test] + public void OverridePropertyReference() + { + StaticApplicationContext ac = new StaticApplicationContext(); + + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("Spouse", new RuntimeObjectReference("spouse1")); + ac.RegisterSingleton("tb1", typeof(TestObject), pvs); + + ac.RegisterSingleton("spouse1", typeof(TestObject), new MutablePropertyValues()); + ac.RegisterSingleton("spouse2", typeof(TestObject), new MutablePropertyValues()); + + pvs = new MutablePropertyValues(); + pvs.Add("Properties", ""); + ac.RegisterSingleton("configurer", typeof(PropertyOverrideConfigurer), pvs); + + ac.Refresh(); + TestObject tb1 = (TestObject)ac.GetObject("tb1"); + TestObject spouse2 = (TestObject)ac.GetObject("spouse2"); + Assert.AreEqual(spouse2, tb1.Spouse); + } + + [Test] + public void OverridePropertyExpression() + { + StaticApplicationContext ac = new StaticApplicationContext(); + + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("Age", new ExpressionHolder("26+1")); + ac.RegisterSingleton("tb1", typeof(TestObject), pvs); + + pvs = new MutablePropertyValues(); + pvs.Add("Properties", ""); + ac.RegisterSingleton("configurer", typeof(PropertyOverrideConfigurer), pvs); + + ac.Refresh(); + TestObject tb1 = (TestObject)ac.GetObject("tb1"); + Assert.AreEqual(25, tb1.Age); + } + + [Test] + public void MalformedOverrideKey() + { + IDynamicMock mock = new DynamicMock(typeof (IConfigurableListableObjectFactory)); + IConfigurableListableObjectFactory fac = (IConfigurableListableObjectFactory) mock.Object; + + PropertyOverrideConfigurer cfg = new PropertyOverrideConfigurer(); + NameValueCollection defaultProperties = new NameValueCollection(); + defaultProperties.Add("malformedKey", "Rick Evans"); + cfg.Properties = defaultProperties; + try + { + cfg.PostProcessObjectFactory(fac); + Assert.Fail("Should have had a FatalObjectException at this point because of a malformed key."); + } + catch (FatalObjectException) + { + } + mock.Verify(); + } + + [Test] + public void MissingObjectDefinitionDoesntRaiseFatalException() + { + const string valueTo_NOT_BeOveridden = "Jenny Lewis"; + TestObject foo = new TestObject(valueTo_NOT_BeOveridden, 30); + + IDynamicMock mock = new DynamicMock(typeof (IConfigurableListableObjectFactory)); + mock.ExpectAndReturn("GetObjectDefinition", null, "rubbish"); + IConfigurableListableObjectFactory fac = (IConfigurableListableObjectFactory) mock.Object; + + PropertyOverrideConfigurer cfg = new PropertyOverrideConfigurer(); + NameValueCollection defaultProperties = new NameValueCollection(); + defaultProperties.Add("rubbish.Name", "Rick Evans"); + cfg.Properties = defaultProperties; + cfg.PostProcessObjectFactory(fac); + Assert.AreEqual(valueTo_NOT_BeOveridden, foo.Name, + "Property value was overridden, but a rubbish objectName root was supplied."); + + mock.Verify(); + } + + [Test] + public void ViaXML() + { + IResource resource = new ReadOnlyXmlTestResource("PropertyResourceConfigurerTests.xml", GetType()); + XmlObjectFactory xbf = new XmlObjectFactory(resource); + PropertyOverrideConfigurer poc = (PropertyOverrideConfigurer) xbf.GetObject("OverrideConfigurer"); + Assert.IsNotNull(poc); + poc.PostProcessObjectFactory(xbf); + TestObject to = (TestObject) xbf.GetObject("Test2"); + Assert.AreEqual("Overriden Name", to.Name); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyPathFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyPathFactoryObjectTests.cs new file mode 100644 index 00000000..f4d6a1c3 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyPathFactoryObjectTests.cs @@ -0,0 +1,172 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Core; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the PropertyPathFactoryObject class. + /// + /// Rick Evans + /// $Id: PropertyPathFactoryObjectTests.cs,v 1.3 2007/07/31 00:09:30 markpollack Exp $ + [TestFixture] + public sealed class PropertyPathFactoryObjectTests + { + [Test] + public void GetObject_ViaTargetObjectName() + { + DynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("IsSingleton", true); + mock.ExpectAndReturn("GetObject", new TestObject("Fiona Apple", 28), "foo"); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + PropertyPathFactoryObject fac = new PropertyPathFactoryObject(); + fac.TargetObjectName = "foo"; + fac.PropertyPath = "name"; + fac.ObjectFactory = mockFactory; + string name = (string) fac.GetObject(); + Assert.AreEqual("Fiona Apple", name); + mock.Verify(); + } + + [Test] + public void GetObject_ViaTargetObjectNameWithNestedPropertyPath() + { + DynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("IsSingleton", true); + TestObject target = new TestObject("Fiona Apple", 28); + target.Spouse = target; + mock.ExpectAndReturn("GetObject", target, "foo"); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + PropertyPathFactoryObject fac = new PropertyPathFactoryObject(); + fac.TargetObjectName = "foo"; + fac.PropertyPath = "spouse.name"; + fac.ObjectFactory = mockFactory; + string name = (string) fac.GetObject(); + Assert.AreEqual("Fiona Apple", name); + mock.Verify(); + } + + [Test] + public void GetObject_ViaObjectName() + { + DynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("IsSingleton", true); + mock.ExpectAndReturn("GetObject", new TestObject("Fiona Apple", 28), "foo"); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + PropertyPathFactoryObject fac = new PropertyPathFactoryObject(); + fac.ObjectName = "foo.name"; + fac.ObjectFactory = mockFactory; + string name = (string) fac.GetObject(); + Assert.AreEqual("Fiona Apple", name); + mock.Verify(); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void GetObject_ViaObjectNameThatStartsWithAPeriod() + { + IDynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("IsSingleton", true); + mock.ExpectAndReturn("GetObject", new TestObject("Fiona Apple", 28), "foo"); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + PropertyPathFactoryObject fac = new PropertyPathFactoryObject(); + fac.ObjectName = ".foo.name"; + fac.ObjectFactory = mockFactory; + } + + [Test] + public void GetObject_MakeSureLeadingAndTrailingWhitspaceIsTrimmed() + { + IDynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("IsSingleton", true); + mock.ExpectAndReturn("GetObject", new TestObject("Fiona Apple", 28), "foo"); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + PropertyPathFactoryObject fac = new PropertyPathFactoryObject(); + fac.ObjectName = " \nfoo.name "; + fac.ObjectFactory = mockFactory; + string name = (string) fac.GetObject(); + Assert.AreEqual("Fiona Apple", name); + mock.Verify(); + } + + [Test] + public void GetObject_ViaObjectNameWithNestedPropertyPath() + { + DynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("IsSingleton", true); + TestObject target = new TestObject("Fiona Apple", 28); + target.Spouse = target; + mock.ExpectAndReturn("GetObject", target, "foo"); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + PropertyPathFactoryObject fac = new PropertyPathFactoryObject(); + fac.ObjectName = "foo.spouse.name"; + fac.ObjectFactory = mockFactory; + string name = (string) fac.GetObject(); + Assert.AreEqual("Fiona Apple", name); + mock.Verify(); + } + + [Test] + [ExpectedException(typeof(NullValueInNestedPathException))] + public void GetObject_ViaObjectNameWithNullInNestedPropertyPath() + { + DynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("IsSingleton", true); + mock.ExpectAndReturn("GetObject", new TestObject("Fiona Apple", 28), "foo"); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + PropertyPathFactoryObject fac = new PropertyPathFactoryObject(); + fac.ObjectName = "foo.spouse.name"; + fac.ObjectFactory = mockFactory; + string name = (string) fac.GetObject(); + Assert.AreEqual("Fiona Apple", name); + mock.Verify(); + } + + [Test] + [ExpectedException(typeof(FatalObjectException))] + public void GetObject_PropertyPathEvaluatesToNull() + { + IDynamicMock mock = new DynamicMock(typeof(IObjectFactory)); + mock.ExpectAndReturn("IsSingleton", true); + mock.ExpectAndReturn("GetObject", new TestObject(null, 28), "foo"); + IObjectFactory mockFactory = (IObjectFactory) mock.Object; + + PropertyPathFactoryObject fac = new PropertyPathFactoryObject(); + fac.ObjectName = "foo.name"; + fac.ObjectFactory = mockFactory; + fac.GetObject(); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyPlaceholderConfigurerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyPlaceholderConfigurerTests.cs new file mode 100644 index 00000000..6a4e00db --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyPlaceholderConfigurerTests.cs @@ -0,0 +1,577 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Collections; +using Spring.Context; +using Spring.Context.Support; +using Spring.Core.IO; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the PropertyPlaceholderConfigurer class. + /// + /// Rick Evans + /// $Id: PropertyPlaceholderConfigurerTests.cs,v 1.11 2007/11/26 14:16:29 bbaia Exp $ + [TestFixture] + public sealed class PropertyPlaceholderConfigurerTests + { + private static string testConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=c:\Northwind.mdb;User ID=Admin;Password=;"; + private static string testConnectionStringTwo = @"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=c:\Northwind.mdb;User ID=Admin;Password=Ernie;"; + + [Test] + [ExpectedException(typeof(ObjectInitializationException))] + public void MismatchBetweenNumberOfConfigNamesAndNumberOfLocations() + { + PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); + cfg.Locations = new IResource [] {(IResource) new DynamicMock(typeof(IResource)).Object}; // will never get to the point where we check the validity + cfg.ConfigSections = new string[] { "", "" }; + cfg.PostProcessObjectFactory((IConfigurableListableObjectFactory) new DynamicMock(typeof(IConfigurableListableObjectFactory)).Object); + } + + [Test] + [ExpectedException(typeof(ObjectsException))] + public void OneConfigNameIsOKForLotsOfLocations() + { + PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); + DynamicMock mock = new DynamicMock(typeof(IResource)); + mock.ExpectAndReturn("Exists", true); + mock.ExpectAndThrow("InputStream", new FileNotFoundException()); + cfg.Locations = new IResource [] {(IResource) mock.Object}; + cfg.ConfigSections = new string[] { "" }; + cfg.PostProcessObjectFactory((IConfigurableListableObjectFactory) new DynamicMock(typeof(IConfigurableListableObjectFactory)).Object); + } + + [Test] + [ExpectedException(typeof(ObjectInitializationException))] + public void ChokesOnBadResourceLocationIfIgnoreBadResourcesFlagNotSetToTrue() + { + PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); + cfg.IgnoreResourceNotFound = false; + DynamicMock mock = new DynamicMock(typeof(IResource)); + mock.ExpectAndReturn("Exists", false); + cfg.Locations = new IResource [] {(IResource) mock.Object}; + cfg.ConfigSections = new string[] { "" }; + cfg.PostProcessObjectFactory((IConfigurableListableObjectFactory) new DynamicMock(typeof(IConfigurableListableObjectFactory)).Object); + } + + [Test] + public void DoesNotChokeOnBadResourceLocationIfIgnoreBadResourcesFlagSetToTrue() + { + PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); + cfg.IgnoreResourceNotFound = true; + DynamicMock mockResource = new DynamicMock(typeof(IResource)); + mockResource.ExpectAndReturn("Exists", false); + cfg.Location = (IResource) mockResource.Object; + cfg.ConfigSections = new string[] { "" }; + DynamicMock mockFactory = new DynamicMock(typeof(IConfigurableListableObjectFactory)); + mockFactory.ExpectAndReturn("GetObjectDefinitionNames", new string[] {}); + cfg.PostProcessObjectFactory((IConfigurableListableObjectFactory) mockFactory.Object); + mockResource.Verify(); + mockFactory.Verify(); + } + + [Test] + [ExpectedException(typeof (ObjectDefinitionStoreException))] + public void WithCircularReference() + { + StaticApplicationContext ac = new StaticApplicationContext(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("age", "${age}"); + pvs.Add("name", "name${var}"); + pvs.Add("spouse", new RuntimeObjectReference("${ref}")); + ac.RegisterSingleton("tb1", typeof (TestObject), pvs); + pvs = new MutablePropertyValues(); + pvs.Add("age", "${age}"); + pvs.Add("name", "name${age}"); + ac.RegisterSingleton("tb2", typeof (TestObject), pvs); + pvs = new MutablePropertyValues(); + pvs.Add("Properties", ""); + ac.RegisterSingleton("configurer1", typeof (PropertyPlaceholderConfigurer), pvs); + pvs = new MutablePropertyValues(); + pvs.Add("Properties", ""); + pvs.Add("order", "0"); + ac.RegisterSingleton("configurer2", typeof (PropertyPlaceholderConfigurer), pvs); + ac.Refresh(); + } + + [Test] + public void WithDefaultProperties() + { + const string defName = "foo"; + const string placeholder = "${name}"; + MutablePropertyValues pvs = new MutablePropertyValues(); + + const string theProperty = "name"; + pvs.Add(theProperty, placeholder); + RootObjectDefinition def = new RootObjectDefinition(typeof(TestObject), pvs); + + IDynamicMock mock = new DynamicMock(typeof(IConfigurableListableObjectFactory)); + mock.ExpectAndReturn("GetObjectDefinitionNames", new string [] {defName}); + mock.ExpectAndReturn("GetObjectDefinition", def, defName); + IConfigurableListableObjectFactory fac = (IConfigurableListableObjectFactory) mock.Object; + + PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); + NameValueCollection defaultProperties = new NameValueCollection(); + const string expectedName = "Rick Evans"; + defaultProperties.Add(theProperty, expectedName); + cfg.Properties = defaultProperties; + cfg.PostProcessObjectFactory(fac); + Assert.AreEqual(expectedName, def.PropertyValues.GetPropertyValue(theProperty).Value, + "Property placeholder value was not replaced with the resolved value."); + + mock.Verify(); + } + + /// + /// Fallback is the default mode. Check if the PROCESSOR_ARCHITECTURE + /// variable is replaced. + /// + [Test] + public void WithEnvironmentVariableFallback() + { + StaticApplicationContext ac = new StaticApplicationContext(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("touchy", "${PROCESSOR_ARCHITECTURE}"); + ac.RegisterSingleton("to", typeof (TestObject), pvs); + + pvs = new MutablePropertyValues(); + ac.RegisterSingleton("configurer", typeof (PropertyPlaceholderConfigurer), pvs); + ac.Refresh(); + + TestObject to = (TestObject) ac["to"]; + Assert.AreEqual(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"), + to.Touchy); + } + + /// + /// Fallback is the default mode. Explicity provide a value using the + /// property (NameValueCollection Properties) of PropertyPlaceholder. + /// Fallback mode will not change the value since it has been explicity set. + /// + [Test] + public void WithEnvironmentPropertyNotUsed() + { + StaticApplicationContext ac = new StaticApplicationContext(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("touchy", "${PROCESSOR_ARCHITECTURE}"); + ac.RegisterSingleton("to", typeof (TestObject), pvs); + + pvs = new MutablePropertyValues(); + NameValueCollection nvc = new NameValueCollection(); + nvc.Add("PROCESSOR_ARCHITECTURE", "G5"); + pvs.Add("properties", nvc); + ac.RegisterSingleton("configurer", typeof (PropertyPlaceholderConfigurer), pvs); + ac.Refresh(); + + TestObject to = (TestObject) ac["to"]; + Assert.AreEqual("G5", to.Touchy, "Fallback mode is not respecting previously set values."); + } + + /// + /// Set the environment variable mode to override. Now expect the environment + /// variable setting to override the explicitly defined name value collection. + /// + [Test] + public void WithOverridingEnvironmentProperty() + { + StaticApplicationContext ac = new StaticApplicationContext(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("touchy", "${PROCESSOR_ARCHITECTURE}"); + ac.RegisterSingleton("to", typeof (TestObject), pvs); + + pvs = new MutablePropertyValues(); + NameValueCollection nvc = new NameValueCollection(); + nvc.Add("PROCESSOR_ARCHITECTURE", "G5"); + pvs.Add("properties", nvc); + pvs.Add("environmentVariableMode", EnvironmentVariableMode.Override); + ac.RegisterSingleton("configurer", typeof (PropertyPlaceholderConfigurer), pvs); + ac.Refresh(); + + TestObject to = (TestObject) ac["to"]; + Assert.AreEqual(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"), + to.Touchy); + } + + [Test] + [ExpectedException(typeof (ObjectDefinitionStoreException), + "Error registering object with name 'to' defined in '' : Could not resolve placeholder 'PROCESSOR_ARCHITECTURE'.")] + public void WithUnresolvableEnvironmentProperty() + { + StaticApplicationContext ac = new StaticApplicationContext(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("touchy", "${PROCESSOR_ARCHITECTURE}"); + ac.RegisterSingleton("to", typeof (TestObject), pvs); + + pvs = new MutablePropertyValues(); + pvs.Add("environmentVariableMode", EnvironmentVariableMode.Never); + ac.RegisterSingleton("configurer", typeof (PropertyPlaceholderConfigurer), pvs); + ac.Refresh(); + } + + [Test] + public void WithUnresolvablePlaceholder() + { + StaticApplicationContext ac = new StaticApplicationContext(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("name", "${ref}"); + ac.RegisterSingleton("tb", typeof (TestObject), pvs); + ac.RegisterSingleton("configurer", typeof (PropertyPlaceholderConfigurer), null); + try + { + ac.Refresh(); + Assert.Fail("Should have thrown ObjectDefinitionStoreException"); + } + catch (ObjectDefinitionStoreException ex) + { + // expected + Assert.IsTrue(ex.Message.IndexOf("ref") != -1); + } + } + + [Test] + public void WithExpressionProperty() + { + StaticApplicationContext ac = new StaticApplicationContext(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("age", new ExpressionHolder("${age}")); + ac.RegisterSingleton("to1", typeof(TestObject), pvs); + + pvs = new MutablePropertyValues(); + NameValueCollection nvc = new NameValueCollection(); + nvc.Add("age", "'0x7FFFFFFF'"); + pvs.Add("properties", nvc); + ac.RegisterSingleton("configurer", typeof(PropertyPlaceholderConfigurer), pvs); + ac.Refresh(); + + + TestObject to1 = (TestObject)ac.GetObject("to1");; + Assert.AreEqual(2147483647, to1.Age); + + } + + [Test] + public void SunnyDay() + { + StaticApplicationContext ac = new StaticApplicationContext(); + + + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("age", "${age}"); + RootObjectDefinition def + = new RootObjectDefinition("${fqn}", new ConstructorArgumentValues(), pvs); + ac.RegisterObjectDefinition("tb3", def); + + + + pvs = new MutablePropertyValues(); + pvs.Add("age", "${age}"); + pvs.Add("name", "name${var}${"); + pvs.Add("spouse", new RuntimeObjectReference("${ref}")); + ac.RegisterSingleton("tb1", typeof (TestObject), pvs); + + ConstructorArgumentValues cas = new ConstructorArgumentValues(); + cas.AddIndexedArgumentValue(1, "${age}"); + cas.AddGenericArgumentValue("${var}name${age}"); + + pvs = new MutablePropertyValues(); + ArrayList friends = new ManagedList(); + friends.Add("na${age}me"); + friends.Add(new RuntimeObjectReference("${ref}")); + pvs.Add("friends", friends); + + ISet someSet = new ManagedSet(); + someSet.Add("na${age}me"); + someSet.Add(new RuntimeObjectReference("${ref}")); + pvs.Add("someSet", someSet); + + IDictionary someDictionary = new ManagedDictionary(); + someDictionary["key1"] = new RuntimeObjectReference("${ref}"); + someDictionary["key2"] = "${age}name"; + MutablePropertyValues innerPvs = new MutablePropertyValues(); + someDictionary["key3"] = new RootObjectDefinition(typeof (TestObject), innerPvs); + someDictionary["key4"] = new ChildObjectDefinition("tb1", innerPvs); + pvs.Add("someMap", someDictionary); + + RootObjectDefinition definition = new RootObjectDefinition(typeof (TestObject), cas, pvs); + ac.DefaultListableObjectFactory.RegisterObjectDefinition("tb2", definition); + + pvs = new MutablePropertyValues(); + pvs.Add("Properties", ""); + ac.RegisterSingleton("configurer", typeof (PropertyPlaceholderConfigurer), pvs); + ac.Refresh(); + + TestObject tb1 = (TestObject) ac.GetObject("tb1"); + TestObject tb2 = (TestObject) ac.GetObject("tb2"); + TestObject tb3 = (TestObject) ac.GetObject("tb3"); + Assert.AreEqual(98, tb1.Age); + Assert.AreEqual(98, tb2.Age); + Assert.AreEqual(98, tb3.Age); + Assert.AreEqual("namemyvar${", tb1.Name); + Assert.AreEqual("myvarname98", tb2.Name); + Assert.AreEqual(tb2, tb1.Spouse); + Assert.AreEqual(2, tb2.Friends.Count); + IEnumerator ie = tb2.Friends.GetEnumerator(); + ie.MoveNext(); + Assert.AreEqual("na98me", ie.Current); + ie.MoveNext(); + Assert.AreEqual(tb2, ie.Current); + Assert.AreEqual(2, tb2.SomeSet.Count); + Assert.IsTrue(tb2.SomeSet.Contains("na98me")); + Assert.IsTrue(tb2.SomeSet.Contains(tb2)); + Assert.AreEqual(4, tb2.SomeMap.Count); + Assert.AreEqual(tb2, tb2.SomeMap["key1"]); + Assert.AreEqual("98name", tb2.SomeMap["key2"]); + TestObject inner1 = (TestObject) tb2.SomeMap["key3"]; + TestObject inner2 = (TestObject) tb2.SomeMap["key4"]; + Assert.AreEqual(0, inner1.Age); + Assert.AreEqual(null, inner1.Name); + Assert.AreEqual(98, inner2.Age); + Assert.AreEqual("namemyvar${", inner2.Name); + } + + /// + /// Makes sure that an appropriate exception is raised when trying + /// to resolve this placeholder... ${foo} with this value... foo=ba${foo}r + /// + [Test] + public void ChokesOnCircularReferenceToPlaceHolder() + { + RootObjectDefinition def = new RootObjectDefinition(); + def.ObjectType = typeof (TestObject); + ConstructorArgumentValues args = new ConstructorArgumentValues(); + args.AddNamedArgumentValue("name", "${foo}"); + def.ConstructorArgumentValues = args; + + NameValueCollection properties = new NameValueCollection(); + const string expectedName = "ba${foo}r"; + properties.Add("foo", expectedName); + + DynamicMock mock = new DynamicMock(typeof (IConfigurableListableObjectFactory)); + mock.ExpectAndReturn("GetObjectDefinitionNames", new string[] {"foo"}); + mock.ExpectAndReturn("GetObjectDefinition", def); + IConfigurableListableObjectFactory fac + = (IConfigurableListableObjectFactory) mock.Object; + PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); + cfg.Properties = properties; + try + { + cfg.PostProcessObjectFactory(fac); + Assert.Fail("Should have raised an ObjectDefinitionStoreException by this point."); + } + catch (ObjectDefinitionStoreException) + { + } + mock.Verify(); + } + + [Test] + public void ReplacesNamedCtorArgument() + { + RootObjectDefinition def = new RootObjectDefinition(); + def.ObjectType = typeof (TestObject); + ConstructorArgumentValues args = new ConstructorArgumentValues(); + args.AddNamedArgumentValue("name", "${hope.floats}"); + def.ConstructorArgumentValues = args; + + NameValueCollection properties = new NameValueCollection(); + const string expectedName = "Rick"; + properties.Add("hope.floats", expectedName); + + DynamicMock mock = new DynamicMock(typeof (IConfigurableListableObjectFactory)); + mock.ExpectAndReturn("GetObjectDefinitionNames", new string[] {"foo"}); + mock.ExpectAndReturn("GetObjectDefinition", def); + IConfigurableListableObjectFactory fac + = (IConfigurableListableObjectFactory) mock.Object; + PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); + cfg.Properties = properties; + cfg.PostProcessObjectFactory(fac); + + mock.Verify(); + + Assert.AreEqual(expectedName, + def.ConstructorArgumentValues.GetNamedArgumentValue("name").Value, + "Named argument placeholder value was not replaced."); + } + + [Test] + public void UsingCustomMarkers() + { + RootObjectDefinition def = new RootObjectDefinition(); + def.ObjectType = typeof (TestObject); + ConstructorArgumentValues args = new ConstructorArgumentValues(); + args.AddNamedArgumentValue("name", "#hope.floats#"); + def.ConstructorArgumentValues = args; + + NameValueCollection properties = new NameValueCollection(); + const string expectedName = "Rick"; + properties.Add("hope.floats", expectedName); + + DynamicMock mock = new DynamicMock(typeof (IConfigurableListableObjectFactory)); + mock.ExpectAndReturn("GetObjectDefinitionNames", new string[] {"foo"}); + mock.ExpectAndReturn("GetObjectDefinition", def); + IConfigurableListableObjectFactory fac + = (IConfigurableListableObjectFactory) mock.Object; + PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); + cfg.PlaceholderPrefix = cfg.PlaceholderSuffix = "#"; + cfg.Properties = properties; + cfg.PostProcessObjectFactory(fac); + + mock.Verify(); + + Assert.AreEqual(expectedName, + def.ConstructorArgumentValues.GetNamedArgumentValue("name").Value, + "Named argument placeholder value was not replaced."); + } + + /// + /// Test that properties can be replaced from NameValueConfiguration sections + /// from the main .NET application configuration file. + /// + [Test] + public void WithAppConfigResolution() + { + IApplicationContext ctx = new XmlApplicationContext( + "file://Spring/Objects/Factory/Config/PropertyPlaceholderConfigurerTests.xml"); + TestObjectDAO to = (TestObjectDAO) ctx["testObjectDao"]; + Assert.AreEqual(testConnectionString, to.DbConnection.ConnectionString); + Assert.AreEqual(1000, to.MaxResults); + } + + /// + /// Test that properties can be replaced from NameValueConfiguration sections + /// from the main .NET application configuration file and a seprate .xml file + /// on the file system. + /// + [Test] + public void WithTwoLocations() + { + IApplicationContext ctx = new XmlApplicationContext( + "file://Spring/Objects/Factory/Config/PPCTwoLocationsTwoSectionsTests.xml"); + TestObjectDAO to = (TestObjectDAO) ctx["testObjectDao"]; + Assert.AreEqual(testConnectionStringTwo, to.DbConnection.ConnectionString); + Assert.AreEqual(1000, to.MaxResults); + } + + /// + /// Test that properties can be merged from a NameValueConfiguration section + /// in the main .NET application configuration file and a seprate .xml file + /// on the file system. + /// + [Test] + public void WithTwoLocationsOneSection() + { + IApplicationContext ctx = new XmlApplicationContext( + "file://Spring/Objects/Factory/Config/PPCTwoLocationsOneSectionTests.xml"); + TestObjectDAO to = (TestObjectDAO) ctx["testObjectDao"]; + Assert.AreEqual(testConnectionString, to.DbConnection.ConnectionString); + Assert.AreEqual(1000, to.MaxResults); + } + + /// + /// Test that if two locations configure the same properties they are appended + /// or not depending on the property + /// + [Test(Description="SPRNET-55")] + public void WithAppend() + { + string resourceName = "Spring/Objects/Factory/Config/PPC-SPRNET-55.xml"; + XmlObjectFactory ctx = new XmlObjectFactory(new FileSystemResource(resourceName)); + Assert.IsNotNull(ctx); + IObjectFactoryPostProcessor processor = (IObjectFactoryPostProcessor) ctx["appendConfigurer"]; + processor.PostProcessObjectFactory(ctx); + TestObjectDAO to = (TestObjectDAO) ctx["appendDao"]; + Assert.AreEqual(testConnectionString + "," + testConnectionStringTwo, to.DbConnection.ConnectionString); + Assert.AreEqual(1000, to.MaxResults); + + ctx = new XmlObjectFactory(new FileSystemResource(resourceName)); + processor = (IObjectFactoryPostProcessor) ctx["noAppendConfigurer"]; + processor.PostProcessObjectFactory(ctx); + + to = (TestObjectDAO) ctx["noAppendDao"]; + Assert.AreEqual(testConnectionStringTwo, to.DbConnection.ConnectionString); + Assert.AreEqual(1000, to.MaxResults); + } + + [Test] + public void WithIgnoreUnresolvablePlaceholder() + { + const string defName = "foo"; + const string placeholder = "${name}"; + TestObject foo = new TestObject(placeholder, 30); + MutablePropertyValues pvs = new MutablePropertyValues(); + + pvs.Add("name", placeholder); + RootObjectDefinition def = new RootObjectDefinition(typeof(TestObject), pvs); + + IDynamicMock mock = new DynamicMock(typeof(IConfigurableListableObjectFactory)); + mock.ExpectAndReturn("GetObjectDefinitionNames", new string [] {defName}); + mock.ExpectAndReturn("GetObjectDefinition", def, defName); + IConfigurableListableObjectFactory fac = (IConfigurableListableObjectFactory) mock.Object; + + PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); + cfg.IgnoreUnresolvablePlaceholders = true; + cfg.PostProcessObjectFactory(fac); + Assert.AreEqual(placeholder, foo.Name); + + mock.Verify(); + } + + [Test] + public void ViaXML() + { + IResource resource = new ReadOnlyXmlTestResource("PropertyResourceConfigurerTests.xml", GetType()); + XmlObjectFactory xbf = new XmlObjectFactory(resource); + PropertyPlaceholderConfigurer ppc = (PropertyPlaceholderConfigurer) xbf.GetObject("PlaceholderConfigurer"); + Assert.IsNotNull(ppc); + ppc.PostProcessObjectFactory(xbf); + TestObject to = (TestObject) xbf.GetObject("Test1"); + Assert.AreEqual("A DefName", to.Name); + } + + [Test] + public void WithTypes() + { + IApplicationContext ctx = new XmlApplicationContext( + "file://Spring/Objects/Factory/Config/PPCWithTypesTests.xml"); + + object obj = ctx["testObject"]; + Assert.IsTrue(obj is TestObject); + + TestObject to = (TestObject)obj; + + Assert.AreEqual(2, to.Pets.Count); + Assert.AreEqual(2, to.PeriodicTable.Count); + Assert.AreEqual(2, to.Computers.Count); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyRetrievingFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyRetrievingFactoryObjectTests.cs new file mode 100644 index 00000000..6ce1abfc --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/PropertyRetrievingFactoryObjectTests.cs @@ -0,0 +1,353 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using NUnit.Framework; +using Spring.Core; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the PropertyRetrievingFactoryObject class. + /// + /// Rick Evans + /// $Id: PropertyRetrievingFactoryObjectTests.cs,v 1.6 2007/07/31 00:09:30 markpollack Exp $ + [TestFixture] + public sealed class PropertyRetrievingFactoryObjectTests + { + [Test] + public void Instantiation() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + Assert.IsNotNull(fac.Arguments); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void BailsWhenStaticPropertyIsSetToNull() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.StaticProperty = null; + } + + /// + /// Test support for nested static properties. + /// + [Test] + public void NestedStaticProperty() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.StaticProperty = "Spring.Objects.Factory.Config.PropertyObject.StaticProperty.Age, Spring.Core.Tests"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (int), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(PropertyObject.Age, actual); + } + + /// + /// Test support for nested indexed static and instance properties. + /// + [Test] + public void MixOfNestedIndexedStaticAndInstanceProperty() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.StaticProperty = "Spring.Objects.Factory.Config.PropertyObject.StaticProperty.Item, Spring.Core.Tests"; + fac.Arguments = new object[] {0}; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (int), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(PropertyObject.StaticProperty[0], actual); + } + + /// + /// Test support for really really nested indexed static and instance properties. + /// + [Test] + public void SuperMixOfNestedIndexedStaticAndInstanceProperty() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.StaticProperty = "Spring.Objects.Factory.Config.PropertyObject.StaticProperty.StaticProperty.Item, Spring.Core.Tests"; + fac.Arguments = new object[] {0}; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (int), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(PropertyObject.StaticProperty[0], actual); + } + + [Test] + public void ResistsSettingTheArgumentsToNull() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.Arguments = null; + Assert.IsNotNull(fac.Arguments); + } + + [Test] + public void StaticProperty() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.StaticProperty = "Spring.Objects.Factory.Config.PropertyObject.Age, Spring.Core.Tests"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (int), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(PropertyObject.Age, actual); + } + + [Test] + public void StaticPropertyThatAintAssemblyQualifiedShouldStillBeResolved() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.StaticProperty = "Spring.Objects.Factory.Config.PropertyObject.Age"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (int), fac.ObjectType); + object actual = fac.GetObject(); + Assert.AreEqual(PropertyObject.Age, actual); + } + + [Test] + public void StaticPropertyViaClassAndFieldName() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetProperty = "Age"; + fac.TargetType = typeof (PropertyObject); + fac.AfterPropertiesSet(); + object actual = fac.GetObject(); + Assert.AreEqual(PropertyObject.Age, actual); + } + + [Test] + public void InstanceProperty() + { + PropertyObject expected = new PropertyObject(); + expected.Name = "Haruki Murakami"; + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetObject = expected; + fac.TargetProperty = "Name"; + fac.AfterPropertiesSet(); + object actual = fac.GetObject(); + Assert.AreEqual(expected.Name, actual); + } + + /// + /// Test support for nested properties on an instance. + /// + [Test] + public void NestedInstanceProperty() + { + TestObject person = new TestObject(); + person.Age = 20; + TestObject spouse = new TestObject(); + spouse.Age = 21; + person.Spouse = spouse; + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetObject = person; + fac.TargetProperty = "spouse.age"; + fac.AfterPropertiesSet(); + object actual = fac.GetObject(); + int expectedAge = 21; + Assert.AreEqual(expectedAge, actual); + } + + [Test] + public void IndexedProperty() + { + PropertyObject expected = new PropertyObject(); + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetObject = expected; + fac.TargetProperty = "Item"; + fac.Arguments = new object[] {2}; + fac.AfterPropertiesSet(); + object actual = fac.GetObject(); + Assert.AreEqual(expected[2], actual); + } + + [Test] + [ExpectedException(typeof (FatalObjectException))] + public void BailsWhenReadingIndexedPropertyWithNoArguments() + { + PropertyObject expected = new PropertyObject(); + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetObject = expected; + fac.TargetProperty = "Item"; + fac.AfterPropertiesSet(); + fac.GetObject(); + } + + [Test] + [ExpectedException(typeof (NotWritablePropertyException))] + public void BailsOnWriteOnlyProperty() + { + PropertyObject expected = new PropertyObject(); + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetObject = expected; + fac.TargetProperty = "Greenness"; + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void BailsOnNonExistantProperty() + { + PropertyObject expected = new PropertyObject(); + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetObject = expected; + fac.TargetProperty = "Blister"; + fac.AfterPropertiesSet(); + } + + [Test] + public void IsSingleton() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.IsSingleton = false; + fac.TargetProperty = "Age"; + fac.TargetType = typeof (PropertyObject); + fac.AfterPropertiesSet(); + object actual = fac.GetObject(); + Assert.AreEqual(PropertyObject.Age, actual); + + PropertyObject.Age = 94; + object tryTwo = fac.GetObject(); + Assert.AreEqual(PropertyObject.Age, tryTwo); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "One of the TargetType or TargetObject properties must be set.")] + public void BailsWhenNotConfigured() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void BailsWhenJustTargetPropertyIsSet() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetProperty = "Funk"; + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void BailsWhenJustTargetTypeIsSet() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetType = GetType(); + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The TargetProperty property is required.")] + public void BailsWhenJustTargetObjectIsSet() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetObject = this; + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void BailsWhenStaticPropertyPassedGumpfh() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.StaticProperty = "Boog"; // no field specified + } + + [Test] + public void StaticPropertyCaseINsenSiTiVE() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.StaticProperty = "System.Globalization.CultureInfo.CURRENtUiCultURE, Mscorlib"; + fac.AfterPropertiesSet(); + Assert.AreEqual(typeof (CultureInfo), fac.ObjectType); + CultureInfo actual = fac.GetObject() as CultureInfo; + Assert.IsNotNull(actual); + Assert.AreEqual(CultureInfo.CurrentUICulture, actual); + } + + [Test] + public void GetDateTimeDotNowToTestHandlingOfPrototypesIsCorrect() + { + PropertyRetrievingFactoryObject fac = new PropertyRetrievingFactoryObject(); + fac.TargetType = typeof(DateTime); + fac.TargetProperty = "Now"; + fac.IsSingleton = false; + fac.AfterPropertiesSet(); + DateTime then = (DateTime) fac.GetObject(); + Assert.IsNotNull(then); + DateTime now = (DateTime) fac.GetObject(); + Assert.IsNotNull(now); + Assert.IsFalse(ReferenceEquals(then, now)); + } + } + + internal sealed class PropertyObject + { + private static int _age = 74; + + public static int Age + { + get { return _age; } + set { _age = value; } + } + + private string _name; + + public string Name + { + get { return _name; } + set { _name = value; } + } + + private int[] _battingAverage = new int[] {1000, 350, 400}; + + public int[] Averages + { + get { return _battingAverage; } + } + + public int this[int index] + { + get { return _battingAverage[index]; } + set { _battingAverage[index] = value; } + } + + public bool Greenness + { + set + { + // no-op... just here for non-readability + } + } + + private static readonly PropertyObject _staticProperty = new PropertyObject(); + + public static PropertyObject StaticProperty + { + get { return _staticProperty; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/RegistryVariableSourceTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/RegistryVariableSourceTests.cs new file mode 100644 index 00000000..6549a999 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/RegistryVariableSourceTests.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Microsoft.Win32; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the RegistryVariableSource class. + /// + /// Aleksandar Seovic + /// $Id: RegistryVariableSourceTests.cs,v 1.3 2007/08/03 08:31:25 oakinger Exp $ + [TestFixture] + public sealed class RegistryVariableSourceTests + { + private RegistryKey key; + + [SetUp] + public void SetUp() + { + key = Registry.CurrentUser.CreateSubKey("RegistryVariableSourceTests"); + key.SetValue("name", "Aleks Seovic"); +#if NET_2_0 + key.SetValue("computer_name", "%COMPUTERNAME% is the name of my computer", RegistryValueKind.ExpandString); + key.SetValue("age", 32, RegistryValueKind.DWord); +#endif + key.SetValue("family", new string[] {"Marija", "Ana", "Nadja"}); + key.SetValue("bday", new byte[] {24, 8, 74}); + key.Flush(); + } + + [TearDown] + public void TearDown() + { + Registry.CurrentUser.DeleteSubKey("RegistryVariableSourceTests"); + } + + [Test] + public void TestVariablesResolution() + { + RegistryVariableSource rvs = new RegistryVariableSource(); + rvs.Key = key; + + // existing vars + Assert.AreEqual("Aleks Seovic", rvs.ResolveVariable("name")); +#if NET_2_0 + Assert.AreEqual(Environment.GetEnvironmentVariable("COMPUTERNAME") + " is the name of my computer", + rvs.ResolveVariable("computer_name")); + Assert.AreEqual("32", rvs.ResolveVariable("age")); +#endif + // multi_sz + Assert.AreEqual( "Marija,Ana,Nadja", rvs.ResolveVariable("family")); + // binary + Assert.AreEqual( null, rvs.ResolveVariable("bday")); + + // non-existant variable + Assert.IsNull(rvs.ResolveVariable("xyz")); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ResourceHandlerConfigurerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ResourceHandlerConfigurerTests.cs new file mode 100644 index 00000000..32b7fe67 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ResourceHandlerConfigurerTests.cs @@ -0,0 +1,120 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Collections; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Util; + +namespace Spring.Objects.Factory.Config +{ + + /// + /// Unit tests for the TypeAliasConfigurer class + /// + /// Mark Pollack + [TestFixture] + public class ResourceHandlerConfigurerTests + { + [Test] + public void Serialization() + { + IDictionary resourceHandlers = new Hashtable(); + resourceHandlers.Add("httpsss", typeof(UrlResource)); + + ResourceHandlerConfigurer resourceHandlerConfiguer = new ResourceHandlerConfigurer(); + resourceHandlerConfiguer.ResourceHandlers = resourceHandlers; + + + resourceHandlerConfiguer.Order = 1; + + SerializationTestUtils.SerializeAndDeserialize(resourceHandlerConfiguer); + } + + [Test] + [ExpectedException(typeof(ObjectInitializationException))] + public void UseInvalidTypeForDictionaryValue() + { + IDictionary resourceHandlers = new Hashtable(); + resourceHandlers.Add("httpsss", new Hashtable()); + + ResourceHandlerConfigurer resourceHandlerConfiguer = new ResourceHandlerConfigurer(); + resourceHandlerConfiguer.ResourceHandlers = resourceHandlers; + + + resourceHandlerConfiguer.PostProcessObjectFactory((IConfigurableListableObjectFactory)new DynamicMock(typeof(IConfigurableListableObjectFactory)).Object); + + } + + [Test] + [ExpectedException(typeof(ObjectInitializationException))] + public void UseNonResolvableTypeForDictionaryValue() + { + IDictionary resourceHandlers = new Hashtable(); + resourceHandlers.Add("httpsss", "Spring.Core.IO.UrrrrlResource, Spring.Core"); + + ResourceHandlerConfigurer resourceHandlerConfiguer = new ResourceHandlerConfigurer(); + resourceHandlerConfiguer.ResourceHandlers = resourceHandlers; + + + resourceHandlerConfiguer.PostProcessObjectFactory((IConfigurableListableObjectFactory)new DynamicMock(typeof(IConfigurableListableObjectFactory)).Object); + + } + + [Test] + public void SunnyDayScenarioUsingType() + { + + + IDictionary resourceHandlers = new Hashtable(); + resourceHandlers.Add("httpsss", typeof(UrlResource)); + + CreateConfigurerAndTestNewProtcol(resourceHandlers); + } + + [Test] + public void SunnyDayScenarioUsingTypeString() + { + IDictionary typeAliases = new Hashtable(); + typeAliases.Add("httpsss", "Spring.Core.IO.UrlResource, Spring.Core"); + CreateConfigurerAndTestNewProtcol(typeAliases); + + } + + private static void CreateConfigurerAndTestNewProtcol(IDictionary resourceHandlers) + { + ResourceHandlerConfigurer resourceHandlerConfiguer = new ResourceHandlerConfigurer(); + resourceHandlerConfiguer.ResourceHandlers = resourceHandlers; + + resourceHandlerConfiguer.Order = 1; + + + resourceHandlerConfiguer.PostProcessObjectFactory((IConfigurableListableObjectFactory)new DynamicMock(typeof(IConfigurableListableObjectFactory)).Object); + + //todo investigate mocking the typeregistry, for now ask the actual one for information. + Assert.IsTrue(ResourceHandlerRegistry.IsHandlerRegistered("httpsss"), + "ResourceHandlerConfigurer did not register a protocol handler with the ResourceHandlerRegistry"); + + Assert.IsTrue(ResourceHandlerRegistry.IsHandlerRegistered("httpsss"), "Custom IResource not registered."); + Assert.AreEqual(1, resourceHandlerConfiguer.Order); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ResourceManagerFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ResourceManagerFactoryObjectTests.cs new file mode 100644 index 00000000..1c209e5f --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/ResourceManagerFactoryObjectTests.cs @@ -0,0 +1,85 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +using System; +using System.IO; +using System.Resources; +using NUnit.Framework; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the ResourceManagerFactoryObject class. + /// + /// Mark Pollack + [TestFixture] + public sealed class ResourceManagerFactoryObjectTests + { + /// + /// Test basic sunny day usage of the ResourceManagerFactoryObject. + /// + [Test] + public void CreateResourceManager() + { + ResourceManagerFactoryObject fac = new ResourceManagerFactoryObject(); + fac.BaseName = "Spring.Resources.SampleResources"; + fac.AssemblyName = "Spring.Core.Tests"; + fac.AfterPropertiesSet(); + Assert.AreEqual( typeof(ResourceManager), fac.ObjectType); + object actual = fac.GetObject(); + Assert.IsNotNull(actual); + Assert.AreEqual( typeof(ResourceManager), actual.GetType()); + ResourceManager rm = (ResourceManager) actual; + string message = rm.GetString("message"); + Assert.AreEqual("Hello {0} {1}", message); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void MissingBaseName() + { + ResourceManagerFactoryObject fac = new ResourceManagerFactoryObject(); + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void MissingAssemblyName() + { + ResourceManagerFactoryObject fac = new ResourceManagerFactoryObject(); + fac.BaseName = "Spring.Resources.SampleResources"; + fac.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WithRubbishAssemblyName() + { + ResourceManagerFactoryObject fac = new ResourceManagerFactoryObject(); + fac.BaseName = "Spring.Resources.SampleResources"; + fac.AssemblyName = "I'mAJumpedUpPantryBoy"; + fac.AfterPropertiesSet(); + } + + [Test] + public void ObjectTypeReallyIsResourceManager() + { + Assert.AreEqual(typeof (ResourceManager), new ResourceManagerFactoryObject().ObjectType); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/RuntimeObjectReferenceTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/RuntimeObjectReferenceTests.cs new file mode 100644 index 00000000..8857605a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/RuntimeObjectReferenceTests.cs @@ -0,0 +1,48 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the RuntimeObjectReference class. + /// + /// Rick Evans + /// $Id: RuntimeObjectReferenceTests.cs,v 1.2 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class RuntimeObjectReferenceTests + { + [Test] + public void InstantiationIsImplictlyNotToParent() + { + RuntimeObjectReference ror = new RuntimeObjectReference("foo"); + Assert.IsFalse(ror.IsToParent, + "IsToParent property must default to false if not " + + "using the explicit variant of the ctor."); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/SetFactoryObjectTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/SetFactoryObjectTests.cs new file mode 100644 index 00000000..2628f3f8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/SetFactoryObjectTests.cs @@ -0,0 +1,95 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Collections; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the SetFactoryObject class. + /// + /// Rick Evans + /// $Id: SetFactoryObjectTests.cs,v 1.2 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class SetFactoryObjectTests + { + [Test] + [ExpectedException(typeof (ArgumentException), "The Type passed to the TargetSetType property must implement the 'Spring.Collections.ISet' interface.")] + public void SetTargetSetTypeToNonSetType() + { + SetFactoryObject lfo = new SetFactoryObject(); + lfo.TargetSetType = typeof (ICollection); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The Type passed to the TargetSetType property cannot be an interface; it must be a concrete class that implements the 'Spring.Collections.ISet' interface.")] + public void SetTargetSetTypeToDerivedISetInterfaceType() + { + SetFactoryObject lfo = new SetFactoryObject(); + lfo.TargetSetType = typeof (IExtendedSet); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The Type passed to the TargetSetType property cannot be abstract (MustInherit in VisualBasic.NET); it must be a concrete class that implements the 'Spring.Collections.ISet' interface.")] + public void SetTargetSetTypeToAbstractISetInterfaceType() + { + SetFactoryObject lfo = new SetFactoryObject(); + lfo.TargetSetType = typeof (AbstractSet); + } + + private interface IExtendedSet : ISet + { + } + + private abstract class AbstractSet : HybridSet + { + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void SetTargetSetTypeToNull() + { + SetFactoryObject lfo = new SetFactoryObject(); + lfo.TargetSetType = null; + } + + [Test] + [ExpectedException(typeof (ArgumentException), "The 'SourceSet' property cannot be null (Nothing in Visual Basic.NET).")] + public void GetObjectWithoutSupplyingASourceSet() + { + SetFactoryObject lfo = new SetFactoryObject(); + lfo.IsSingleton = false; + lfo.GetObject(); + } + + [Test] + public void ObjectTypeReallyIsISet() + { + Assert.AreEqual(typeof (ISet), new SetFactoryObject().ObjectType); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/SpecialFolderVariableSourceTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/SpecialFolderVariableSourceTests.cs new file mode 100644 index 00000000..abc8229c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/SpecialFolderVariableSourceTests.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Microsoft.Win32; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the SpecialFolderVariableSource class. + /// + /// Aleksandar Seovic + /// $Id: SpecialFolderVariableSourceTests.cs,v 1.1 2007/03/10 01:44:40 aseovic Exp $ + [TestFixture] + public sealed class SpecialFolderVariableSourceTests + { + [Test] + public void TestVariablesResolution() + { + SpecialFolderVariableSource vs = new SpecialFolderVariableSource(); + + // existing vars + Assert.AreEqual(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + vs.ResolveVariable("ApplicationData")); + Assert.AreEqual(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), + vs.ResolveVariable("desktopDirectory")); + Assert.AreEqual(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), + vs.ResolveVariable("programFiles")); + Assert.AreEqual(Environment.GetFolderPath(Environment.SpecialFolder.Personal), + vs.ResolveVariable("personal")); + + // non-existant variable + Assert.IsNull(vs.ResolveVariable("dummy")); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/TypeAliasConfigurerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/TypeAliasConfigurerTests.cs new file mode 100644 index 00000000..c070f2e6 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/TypeAliasConfigurerTests.cs @@ -0,0 +1,138 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Collections; +using Spring.Context.Support; +using Spring.Core.TypeResolution; +using Spring.Util; +using Spring.Objects.Factory.Support; +using Spring.Context; + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the TypeAliasConfigurer class + /// + /// Mark Pollack + [TestFixture] + public class TypeAliasConfigurerTests + { + [Test] + public void Serialization() + { + IDictionary typeAliases = new Hashtable(); + typeAliases.Add("LinkedList", typeof(LinkedList)); + + TypeAliasConfigurer typeAliasConfigurer = new TypeAliasConfigurer(); + typeAliasConfigurer.TypeAliases = typeAliases; + + typeAliasConfigurer.Order = 1; + + SerializationTestUtils.SerializeAndDeserialize(typeAliasConfigurer); + } + + [Test] + [ExpectedException(typeof(ObjectInitializationException))] + public void UseInvalidTypeForDictionaryValue() + { + IDictionary typeAliases = new Hashtable(); + typeAliases.Add("LinkedList", new LinkedList()); + + TypeAliasConfigurer typeAliasConfigurer = new TypeAliasConfigurer(); + typeAliasConfigurer.TypeAliases = typeAliases; + + typeAliasConfigurer.PostProcessObjectFactory((IConfigurableListableObjectFactory)new DynamicMock(typeof(IConfigurableListableObjectFactory)).Object); + } + + [Test] + [ExpectedException(typeof(ObjectInitializationException))] + public void UseNonResolvableTypeForDictionaryValue() + { + IDictionary typeAliases = new Hashtable(); + typeAliases.Add("LinkedList", "Spring.Collections.LinkkkedList"); + + TypeAliasConfigurer typeAliasConfigurer = new TypeAliasConfigurer(); + typeAliasConfigurer.TypeAliases = typeAliases; + + typeAliasConfigurer.PostProcessObjectFactory((IConfigurableListableObjectFactory)new DynamicMock(typeof(IConfigurableListableObjectFactory)).Object); + } + + [Test] + public void SunnyDayScenarioUsingType() + { + IDictionary typeAliases = new Hashtable(); + typeAliases.Add("LinkedList", typeof(LinkedList)); + + CreateConfigurerAndTestLinkedList(typeAliases); + } + + [Test] + public void SunnyDayScenarioUsingTypeString() + { + IDictionary typeAliases = new Hashtable(); + typeAliases.Add("LinkedList", "Spring.Collections.LinkedList, Spring.Core"); + CreateConfigurerAndTestLinkedList(typeAliases); + } + + [Test] + public void WithinApplicationContext() + { + IApplicationContext ctx = new XmlApplicationContext( + "file://Spring/Objects/Factory/Config/typeAliases.xml"); + + object obj1 = ctx.GetObject("testObject1"); + Assert.IsNotNull(obj1); + Assert.AreEqual(typeof(TestObject), obj1.GetType()); + + object obj2 = ctx.GetObject("testObject2"); + Assert.IsNotNull(obj2); + Assert.AreEqual(typeof(TestObject), obj2.GetType()); + + object obj3 = ctx.GetObject("testObject3"); + Assert.IsNotNull(obj3); + Assert.AreEqual(typeof(TestObject), obj3.GetType()); + Assert.AreEqual("Bruno", ((TestObject)obj3).Name); + Assert.AreEqual(26, ((TestObject)obj3).Age); + } + + private static void CreateConfigurerAndTestLinkedList(IDictionary typeAliases) + { + TypeAliasConfigurer typeAliasConfigurer = new TypeAliasConfigurer(); + typeAliasConfigurer.TypeAliases = typeAliases; + + typeAliasConfigurer.Order = 1; + + + typeAliasConfigurer.PostProcessObjectFactory((IConfigurableListableObjectFactory)new DynamicMock(typeof(IConfigurableListableObjectFactory)).Object); + + //todo investigate mocking the typeregistry, for now ask the actual one for information. + Assert.IsTrue(TypeRegistry.ContainsAlias("LinkedList"), + "TypeAliasConfigurer did not register a type alias with the TypeRegistry"); + + Type linkedListType = TypeRegistry.ResolveType("LinkedList"); + Assert.IsTrue(linkedListType.Equals(typeof(LinkedList)), "Incorrect type resolved."); + Assert.AreEqual(1,typeAliasConfigurer.Order); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/TypedStringValueTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/TypedStringValueTests.cs new file mode 100644 index 00000000..ccbef7d4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/TypedStringValueTests.cs @@ -0,0 +1,93 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Unit tests for the TypedStringValue class. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: TypedStringValueTests.cs,v 1.4 2007/05/29 20:00:47 markpollack Exp $ + [TestFixture] + public sealed class TypedStringValueTests + { + [Test] + public void Instantiation() + { + string expectedNow = DateTime.Now.ToShortDateString(); + TypedStringValue tsv = new TypedStringValue(expectedNow, typeof (DateTime)); + Assert.AreEqual(expectedNow, tsv.Value); + Assert.AreEqual(typeof (DateTime), tsv.TargetType); + tsv = new TypedStringValue(expectedNow); + Assert.AreEqual(expectedNow, tsv.Value); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullType() + { + new TypedStringValue(string.Empty, null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void SetTargetTypePropertyToNullType() + { + TypedStringValue tsv = new TypedStringValue(string.Empty, typeof (DateTime)); + tsv.TargetType = null; + } + + [Test] + public void IsSerializable() + { + Assert.IsTrue(SerializationTestUtils.IsSerializable(new TypedStringValue()), + "Must be marked as [Serializable]."); + } + + [Test] + public void Serialization() + { + TypedStringValue value = new TypedStringValue(); + Type expectedType = typeof(string); + value.TargetType = expectedType; + const string expectedValue = "rilo-kiley"; + value.Value = expectedValue; + + object foo = SerializationTestUtils.SerializeAndDeserialize(value); + Assert.IsNotNull(foo, "Serialization roundtrip must never result in null."); + TypedStringValue deser = foo as TypedStringValue; + Assert.IsNotNull(deser, + "Serialization roundtrip yielded the wrong Type of object."); + Assert.AreEqual(expectedType, deser.TargetType, + "Serialization roundtrip yielded the wrong TargetType."); + Assert.AreEqual(expectedValue, deser.Value, + "Serialization roundtrip yielded the wrong Value."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariableAccessorTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariableAccessorTests.cs new file mode 100644 index 00000000..7b01ebb5 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariableAccessorTests.cs @@ -0,0 +1,265 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// Tests functionality. + /// + /// Erich Eichinger + /// $Id: VariableAccessorTests.cs,v 1.1 2007/08/22 20:17:05 oakinger Exp $ + [TestFixture] + public class VariableAccessorTests + { + private static readonly Guid TESTGUID = Guid.NewGuid(); + private static readonly Guid TESTGUID_DEFAULT = Guid.NewGuid(); + + private static readonly DateTime TESTDATETIME = new DateTime(2007, 07, 06, 11, 12, 13); + private static readonly DateTime TESTDATETIME_DEFAULT = TESTDATETIME.AddDays(-1); + + private class NameValueCollectionVariableSource : IVariableSource + { + private readonly NameValueCollection nameValues = new NameValueCollection(); + + public NameValueCollectionVariableSource Add(string name, string value) + { + nameValues.Add(name, value); + return this; + } + + public string ResolveVariable(string name) + { + return nameValues.Get(name); + } + } + + private readonly IVariableSource _testVariableSource = new NameValueCollectionVariableSource() + .Add("ValidString", "String") + .Add("EmptyString", "") + .Add("ValidChar", "c") + .Add("InvalidChar", "12") + .Add("ValidBoolean", "true") + .Add("InvalidBoolean", "") + .Add("ValidByte", "1") + .Add("InvalidByte", "") + .Add("ValidInt16", "1") + .Add("InvalidInt16", "") + .Add("ValidInt32", "1") + .Add("InvalidInt32", "") + .Add("ValidInt64", "1") + .Add("InvalidInt64", "") + .Add("ValidFloat", "1") + .Add("InvalidFloat", "") + .Add("ValidDouble", "1") + .Add("InvalidDouble", "") + .Add("ValidDecimal", "1") + .Add("InvalidDecimal", "") + .Add("ValidGuid", TESTGUID.ToString()) + .Add("InvalidGuid", "") + .Add("ValidDateTime", TESTDATETIME.ToString()) + .Add("InvalidDateTime", "blabla") + .Add("ValidDateTimeUtcRoundtripFormatted", TESTDATETIME.ToUniversalTime().ToString("u")) + ; + + [Test] + public void AcceptsNullVariableSource() + { + VariableAccessor va = new VariableAccessor(null); + Assert.AreEqual("default", va.GetString("somekey", "default")); + } + + [Test] + public void GetString() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual("String", va.GetString("ValidString", "DefaultString")); + Assert.AreEqual("DefaultString", va.GetString("NonExistingString", "DefaultString")); + } + + [Test] + public void GetStringIgnoresEmptyString() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual("DefaultString", va.GetString("EmptyString", "DefaultString")); + } + + [Test] + public void GetChar() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual('c', va.GetChar("ValidChar", 'a')); + Assert.AreEqual('a', va.GetChar("InvalidChar", 'a', false)); + try + { + va.GetChar("InvalidChar", 'a', true); + Assert.Fail(); + } + catch { } + } + + [Test] + public void GetBoolean() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual(true, va.GetBoolean("ValidBoolean", false)); + Assert.AreEqual(true, va.GetBoolean("InvalidBoolean", true, false)); + try + { + va.GetBoolean("InvalidBoolean", true, true); + Assert.Fail(); + }catch {} + } + + [Test] + public void GetByte() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual((byte)1, va.GetByte("ValidByte", 2)); + Assert.AreEqual((byte)2, va.GetByte("InvalidByte", 2, false)); + try + { + va.GetByte("InvalidByte", 2, true); + Assert.Fail(); + } + catch { } + } + + [Test] + public void GetInt16() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual((short)1, va.GetInt16("ValidInt16", 2)); + Assert.AreEqual((short)2, va.GetInt16("InvalidInt16", 2, false)); + try + { + va.GetInt16("InvalidInt16", 2, true); + Assert.Fail(); + } + catch { } + } + + [Test] + public void GetInt32() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual((int)1, va.GetInt32("ValidInt32", 2)); + Assert.AreEqual((int)2, va.GetInt32("InvalidInt32", 2, false)); + try + { + va.GetInt32("InvalidInt32", 2, true); + Assert.Fail(); + } + catch { } + } + + [Test] + public void GetInt64() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual((long)1, va.GetInt64("ValidInt64", 2)); + Assert.AreEqual((long)2, va.GetInt64("InvalidInt64", 2, false)); + try + { + va.GetInt64("InvalidInt64", 2, true); + Assert.Fail(); + } + catch { } + } + + [Test] + public void GetFloat() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual((float)1, va.GetFloat("ValidFloat", 2.0f)); + Assert.AreEqual((float)2, va.GetFloat("InvalidFloat", 2.0f, false)); + try + { + va.GetFloat("InvalidFloat", 2, true); + Assert.Fail(); + } + catch { } + } + + [Test] + public void GetDouble() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual((double)1, va.GetDouble("ValidDouble", 2.0)); + Assert.AreEqual((double)2, va.GetDouble("InvalidDouble", 2.0, false)); + try + { + va.GetDouble("InvalidDouble", 2, true); + Assert.Fail(); + } + catch { } + } + + [Test] + public void GetDecimal() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual((decimal)1, va.GetDecimal("ValidDecimal", 2.0m)); + Assert.AreEqual((decimal)2, va.GetDecimal("InvalidDecimal", 2.0m, false)); + try + { + va.GetDecimal("InvalidDecimal", 2, true); + Assert.Fail(); + } + catch { } + } + + [Test] + public void GetGuid() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual(TESTGUID, va.GetGuid("ValidGuid", TESTGUID_DEFAULT)); + Assert.AreEqual(TESTGUID_DEFAULT, va.GetGuid("InvalidGuid", TESTGUID_DEFAULT, false)); + try + { + va.GetGuid("InvalidGuid", TESTGUID_DEFAULT, true); + Assert.Fail(); + } + catch { } + } + + [Test] + public void GetDateTime() + { + VariableAccessor va = new VariableAccessor(_testVariableSource); + Assert.AreEqual(TESTDATETIME, va.GetDateTime("ValidDateTime", null, TESTDATETIME_DEFAULT)); + Assert.AreEqual(TESTDATETIME.ToUniversalTime(), va.GetDateTime("ValidDateTimeUtcRoundtripFormatted", "u", TESTDATETIME_DEFAULT)); + Assert.AreEqual(TESTDATETIME_DEFAULT, va.GetDateTime("InvalidDateTime", null, TESTDATETIME_DEFAULT, false)); + try + { + va.GetDateTime("InvalidDateTime", null, TESTDATETIME_DEFAULT, true); + Assert.Fail(); + } + catch { } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariablePlaceholderConfigurerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariablePlaceholderConfigurerTests.cs new file mode 100644 index 00000000..bc48482a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Config/VariablePlaceholderConfigurerTests.cs @@ -0,0 +1,78 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using NUnit.Framework; +using Spring.Context.Support; + +#endregion + +namespace Spring.Objects.Factory.Config +{ + /// + /// This calss contains tests for + /// + /// Mark Pollack + /// $Id: VariablePlaceholderConfigurerTests.cs,v 1.1 2007/07/10 05:48:43 markpollack Exp $ + [TestFixture] + public class VariablePlaceholderConfigurerTests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void SunnyDay() + { + StaticApplicationContext ac = new StaticApplicationContext(); + + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("age", "${maxResults}"); + pvs.Add("name", "${name}"); + ac.RegisterSingleton("tb1", typeof(TestObject), pvs); + + IList variableSources = new ArrayList(); + CommandLineArgsVariableSource vs1 = new CommandLineArgsVariableSource( + new string[] { "program.exe", "file.txt", "/name:Aleks Seovic", "/framework:Spring.NET" }); + variableSources.Add(vs1); + + ConfigSectionVariableSource vs2 = new ConfigSectionVariableSource(); + vs2.SectionName = "DaoConfiguration"; + variableSources.Add(vs2); + + + pvs = new MutablePropertyValues(); + pvs.Add("VariableSources", variableSources); + + ac.RegisterSingleton("configurer", typeof(VariablePlaceholderConfigurer), pvs); + ac.Refresh(); + + TestObject tb1 = (TestObject)ac.GetObject("tb1"); + Assert.AreEqual(1000, tb1.Age); + Assert.AreEqual("Aleks Seovic", tb1.Name); + + } + + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/DefaultListableObjectFactoryPerfTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/DefaultListableObjectFactoryPerfTests.cs new file mode 100644 index 00000000..2ae2f3e8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/DefaultListableObjectFactoryPerfTests.cs @@ -0,0 +1,249 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Diagnostics; +using NUnit.Framework; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Threading; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// This class contains tests related to performance of the object factory implementation. Ignored by default. + /// + /// Mark Pollack + /// $Id: DefaultListableObjectFactoryPerfTests.cs,v 1.1 2008/01/29 20:11:19 markpollack Exp $ + [TestFixture] + public class DefaultListableObjectFactoryPerfTests + { + + private DateTime start, stop; + + [SetUp] + public void Setup() + { + } + + [Test] + [Ignore] + public void Test() + { + int numIterations = 10000; + start = DateTime.Now; + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + InitFactory(factory); + for (int i = 0; i < numIterations; i++) + { + FFoo foo = (FFoo)factory.GetObject("foo"); + } + stop = DateTime.Now; + double timeElapsed = Elapsed; + PrintTest("Creations", numIterations, timeElapsed); + + + } + + private void InitFactory(DefaultListableObjectFactory factory) + { + Console.WriteLine("init factory"); + RootObjectDefinition tee = new RootObjectDefinition(typeof(Tee), true); + tee.IsLazyInit = true; + ConstructorArgumentValues teeValues = new ConstructorArgumentValues(); + teeValues.AddGenericArgumentValue("test"); + tee.ConstructorArgumentValues = teeValues; + + RootObjectDefinition bar = new RootObjectDefinition(typeof(BBar), false); + ConstructorArgumentValues barValues = new ConstructorArgumentValues(); + barValues.AddGenericArgumentValue(new RuntimeObjectReference("tee")); + barValues.AddGenericArgumentValue(5); + bar.ConstructorArgumentValues = barValues; + + RootObjectDefinition foo = new RootObjectDefinition(typeof(FFoo), false); + MutablePropertyValues fooValues = new MutablePropertyValues(); + fooValues.Add("i", 5); + fooValues.Add("bar", new RuntimeObjectReference("bar")); + fooValues.Add("copy", new RuntimeObjectReference("bar")); + fooValues.Add("s", "test"); + foo.PropertyValues = fooValues; + + factory.RegisterObjectDefinition("foo", foo); + factory.RegisterObjectDefinition("bar", bar); + factory.RegisterObjectDefinition("tee", tee); + } + + + private double Elapsed + { + get { return (stop.Ticks - start.Ticks) / 10000000f; } + } + + private static void PrintTest(string name, int iterations, double duration) + { + Debug.WriteLine( + String.Format("{0,-60} {1,12:#,###} {2,12:##0.000} {3,12:#,###}", name, iterations, duration, + iterations / duration)); + } + + + } + + public class BeanFactoryTask : AsyncTestTask + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + + public BeanFactoryTask(int iterations) + : base(iterations) + { + InitializeFactory(); + FFoo foo = (FFoo)factory.GetObject("foo"); + Assert.AreEqual(5, foo.i); + Assert.AreEqual("test", foo.s); + Assert.AreSame(foo.bar.tee, foo.copy.tee); + Assert.AreEqual(5, foo.bar.i); + Assert.AreEqual("test", foo.bar.Tee.S); + } + + private void InitializeFactory() + { + Console.WriteLine("init factory"); + RootObjectDefinition tee = new RootObjectDefinition(typeof(Tee), true); + tee.IsLazyInit = true; + ConstructorArgumentValues teeValues = new ConstructorArgumentValues(); + teeValues.AddGenericArgumentValue("test"); + tee.ConstructorArgumentValues = teeValues; + + RootObjectDefinition bar = new RootObjectDefinition(typeof(BBar), false); + ConstructorArgumentValues barValues = new ConstructorArgumentValues(); + barValues.AddGenericArgumentValue(new RuntimeObjectReference("tee")); + barValues.AddGenericArgumentValue(5); + bar.ConstructorArgumentValues = barValues; + + RootObjectDefinition foo = new RootObjectDefinition(typeof(FFoo), false); + MutablePropertyValues fooValues = new MutablePropertyValues(); + fooValues.Add("i", 5); + fooValues.Add("bar", new RuntimeObjectReference("bar")); + fooValues.Add("copy", new RuntimeObjectReference("bar")); + fooValues.Add("s", "test"); + foo.PropertyValues = fooValues; + + factory.RegisterObjectDefinition("foo", foo); + factory.RegisterObjectDefinition("bar", bar); + factory.RegisterObjectDefinition("tee", tee); + + } + + public override void DoExecute() + { + FFoo foo = (FFoo)factory.GetObject("foo"); + } + } + + public interface ITee + { + string S + { get; } + } + + public class Tee : ITee + { + private string s; + + public Tee(string s) + { + this.s = s; + } + + public string S + { + get { return s; } + } + } + + public interface IBBar + { + ITee Tee + { + get; + } + + int I + { + get; + } + } + + public class BBar : IBBar + { + public Tee tee; + public int i; + + public BBar(Tee tee, int i) + { + this.tee = tee; + this.i = i; + } + + public ITee Tee + { + get { return tee; } + } + + public int I + { + get { return i; } + } + + } + + public class FFoo + { + public BBar bar; + public BBar copy; + public string s; + public int i; + + public int I + { + set { i = value; } + } + + + public BBar Bar + { + set { bar = value; } + } + + public BBar Copy + { + set { copy = value; } + } + + public string S + { + set { s = value; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/DefaultListableObjectFactoryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/DefaultListableObjectFactoryTests.cs new file mode 100644 index 00000000..7e4791d4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/DefaultListableObjectFactoryTests.cs @@ -0,0 +1,1458 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using System.Reflection; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Core.TypeConversion; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Unit tests for the DefaultListableObjectFactory class. + /// + /// Rod Johnson + /// Simon White (.NET) + [TestFixture] + public sealed class DefaultListableObjectFactoryTests + { + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp() + { + // enable (null appender) logging, just to ensure that the logging code is correct :D + //XmlConfigurator.Configure(); + } + + [Test(Description="http://opensource2.atlassian.com/projects/spring/browse/SPRNET-112")] + public void ObjectCreatedViaStaticFactoryMethodUsesReturnTypeOfFactoryMethodAsTheObjectType() + { + RootObjectDefinition def + = new RootObjectDefinition(typeof(TestObjectCreator)); + def.FactoryMethodName = "CreateTestObject"; + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + lof.RegisterObjectDefinition("factoryObject", def); + IDictionary objs = lof.GetObjectsOfType(typeof(TestObject)); + Assert.AreEqual(1, objs.Count); + } + + [Test(Description="http://opensource2.atlassian.com/projects/spring/browse/SPRNET-112")] + public void ObjectCreatedViaInstanceFactoryMethodUsesReturnTypeOfFactoryMethodAsTheObjectType() + { + RootObjectDefinition def + = new RootObjectDefinition(typeof(TestObjectCreator)); + def.FactoryMethodName = "InstanceCreateTestObject"; + def.FactoryObjectName = "target"; + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + lof.RegisterObjectDefinition("factoryObject", def); + lof.RegisterObjectDefinition("target", new RootObjectDefinition(typeof(TestObjectCreator))); + IDictionary objs = lof.GetObjectsOfType(typeof(TestObject)); + Assert.AreEqual(1, objs.Count); + } +#if NET_2_0 + [Test(Description="http://opensource2.atlassian.com/projects/spring/browse/SPRNET-112")] + public void ObjectCreatedViaStaticGenericFactoryMethodUsesReturnTypeOfGenericFactoryMethodAsTheObjectType() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition def + = new RootObjectDefinition(typeof(TestGenericObject)); + def.FactoryMethodName = "CreateList"; + lof.RegisterObjectDefinition("foo", def); + IDictionary objs = lof.GetObjectsOfType(typeof(System.Collections.Generic.List)); + Assert.AreEqual(1, objs.Count); + } + + [Test(Description="http://opensource2.atlassian.com/projects/spring/browse/SPRNET-112")] + public void ObjectCreatedViaInstanceGenericFactoryMethodUsesReturnTypeOfGenericFactoryMethodAsTheObjectType() + { + RootObjectDefinition def + = new RootObjectDefinition(typeof(TestObjectCreator)); + def.FactoryMethodName = "CreateInstance"; + def.FactoryObjectName = "target"; + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + lof.RegisterObjectDefinition("factoryObject", def); + lof.RegisterObjectDefinition("target", new RootObjectDefinition(typeof(TestGenericObject))); + IDictionary objs = lof.GetObjectsOfType(typeof(TestGenericObject)); + Assert.AreEqual(1, objs.Count); + } +#endif + /// + /// Object instantiation through factory method should not require type attribute. + /// + [Test(Description="http://opensource.atlassian.com/projects/spring/browse/SPRNET-130")] + public void SPRNET_130() + { + const string factoryObjectName = "factoryObject"; + const string exampleObjectName = "exampleObject"; + + RootObjectDefinition factoryObjectDefinition + = new RootObjectDefinition(typeof(TestObjectFactory)); + RootObjectDefinition exampleObjectDefinition = new RootObjectDefinition(); + exampleObjectDefinition.FactoryObjectName = factoryObjectName; + exampleObjectDefinition.FactoryMethodName = "GetObject"; + + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + lof.RegisterObjectDefinition(factoryObjectName, factoryObjectDefinition); + lof.RegisterObjectDefinition(exampleObjectName, exampleObjectDefinition); + + object exampleObject = lof.GetObject(exampleObjectName); + Assert.IsNotNull(exampleObject); + object factoryObject = lof.GetObject(factoryObjectName); + Assert.IsNotNull(factoryObject); + } + + [Test(Description="http://opensource.atlassian.com/projects/spring/browse/SPR-1011")] + public void SPR_1011() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition def + = new RootObjectDefinition( + typeof (StaticFactoryMethodObject)); + def.FactoryMethodName = "CreateObject"; + lof.RegisterObjectDefinition("foo", def); + IDictionary objs = lof.GetObjectsOfType(typeof (DBNull)); + Assert.AreEqual(1, objs.Count, + "Must be looking at the RETURN TYPE of the factory method, " + + "and hence get one DBNull object back."); + } + + private sealed class StaticFactoryMethodObject + { + private StaticFactoryMethodObject() + { + } + + public static DBNull CreateObject() + { + return DBNull.Value; + } + } + + [Test(Description="http://opensource.atlassian.com/projects/spring/browse/SPR-1077")] + public void SPR_1077() + { + DisposableTestObject sing = null; + using (DefaultListableObjectFactory lof = new DefaultListableObjectFactory()) + { + RootObjectDefinition singleton + = new RootObjectDefinition(typeof (DisposableTestObject)); + MutablePropertyValues sprops = new MutablePropertyValues(); + sprops.Add("name", "Rick"); + singleton.PropertyValues = sprops; + lof.RegisterObjectDefinition("singleton", singleton); + + RootObjectDefinition prototype + = new RootObjectDefinition(typeof (TestObject)); + MutablePropertyValues pprops = new MutablePropertyValues(); + pprops.Add("name", "Jenny"); + // prototype has dependency on a singleton... + pprops.Add("spouse", new RuntimeObjectReference("singleton")); + prototype.PropertyValues = pprops; + prototype.IsSingleton = false; + lof.RegisterObjectDefinition("prototype", prototype); + + sing = (DisposableTestObject) lof.GetObject("singleton"); + + lof.GetObject("prototype"); + lof.GetObject("prototype"); + lof.GetObject("prototype"); + lof.GetObject("prototype"); + } + Assert.AreEqual(1, sing.NumTimesDisposed); + } + + private sealed class DisposableTestObject : TestObject, IDisposable + { + private int _numTimesDisposed; + + public int NumTimesDisposed + { + get { return _numTimesDisposed; } + } + + public void Dispose() + { + ++_numTimesDisposed; + } + } + + [Test] + public void GetObjectPostProcessorCount() + { + DynamicMock mock1 = new DynamicMock(typeof (IObjectPostProcessor)); + IObjectPostProcessor proc1 = (IObjectPostProcessor) mock1.Object; + DynamicMock mock2 = new DynamicMock(typeof (IObjectPostProcessor)); + IObjectPostProcessor proc2 = (IObjectPostProcessor) mock2.Object; + + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + + const string errMsg = "Wrong number of IObjectPostProcessors being reported by the ObjectPostProcessorCount property."; + Assert.AreEqual(0, lof.ObjectPostProcessorCount, errMsg); + lof.AddObjectPostProcessor(proc1); + Assert.AreEqual(1, lof.ObjectPostProcessorCount, errMsg); + lof.AddObjectPostProcessor(proc2); + Assert.AreEqual(2, lof.ObjectPostProcessorCount, errMsg); + } + + /// + /// The ObjectPostProcessorCount property must only return the count of + /// processors registered with the current factory, and not + /// surf up any hierarchy. + /// + [Test] + public void GetObjectPostProcessorCountDoesntRespectHierarchy() + { + DynamicMock mock1 = new DynamicMock(typeof (IObjectPostProcessor)); + IObjectPostProcessor proc1 = (IObjectPostProcessor) mock1.Object; + DynamicMock mock2 = new DynamicMock(typeof (IObjectPostProcessor)); + IObjectPostProcessor proc2 = (IObjectPostProcessor) mock2.Object; + + DefaultListableObjectFactory child = new DefaultListableObjectFactory(); + DefaultListableObjectFactory parent = new DefaultListableObjectFactory(child); + + const string errMsg = "Wrong number of IObjectPostProcessors being reported by the ObjectPostProcessorCount property."; + Assert.AreEqual(0, child.ObjectPostProcessorCount, errMsg); + Assert.AreEqual(0, parent.ObjectPostProcessorCount, errMsg); + child.AddObjectPostProcessor(proc1); + Assert.AreEqual(1, child.ObjectPostProcessorCount, errMsg); + Assert.AreEqual(0, parent.ObjectPostProcessorCount, errMsg); + parent.AddObjectPostProcessor(proc2); + Assert.AreEqual(1, child.ObjectPostProcessorCount, errMsg); + Assert.AreEqual(1, parent.ObjectPostProcessorCount, errMsg); + child.AddObjectPostProcessor(proc2); + Assert.AreEqual(2, child.ObjectPostProcessorCount, errMsg); + Assert.AreEqual(1, parent.ObjectPostProcessorCount, errMsg); + } + + [Test] + public void TestIInstantiationAwareObjectPostProcessorsInterception() + { + ProxyingInstantiationAwareObjectPostProcessorStub proc + = new ProxyingInstantiationAwareObjectPostProcessorStub("TheAgony"); + + MutablePropertyValues props = new MutablePropertyValues(); + props.Add("Name", "Rick"); + RootObjectDefinition toBeProxied + = new RootObjectDefinition(typeof (TestObject), props); + + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + lof.AddObjectPostProcessor(proc); + lof.RegisterObjectDefinition("toBeProxied", toBeProxied); + + object proxy = lof["toBeProxied"]; + Assert.IsNotNull(proxy); + Assert.AreEqual("TheAgony", proxy); + } + + [Test] + public void TestIInstantiationAwareObjectPostProcessorsPassThrough() + { + NullInstantiationAwareObjectPostProcessorStub proc + = new NullInstantiationAwareObjectPostProcessorStub(); + + MutablePropertyValues props = new MutablePropertyValues(); + props.Add("Name", "Rick"); + RootObjectDefinition not + = new RootObjectDefinition(typeof (TestObject), props); + + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + lof.AddObjectPostProcessor(proc); + lof.RegisterObjectDefinition("notToBeProxied", not); + + object foo = lof["notToBeProxied"]; + Assert.IsNotNull(foo); + Assert.AreEqual(typeof (TestObject), foo.GetType()); + TestObject to = (TestObject) foo; + Assert.AreEqual("Rick", to.Name); + } + + private sealed class NullInstantiationAwareObjectPostProcessorStub + : IInstantiationAwareObjectPostProcessor + { + public NullInstantiationAwareObjectPostProcessorStub() + { + } + + public object PostProcessBeforeInitialization(object obj, string name) + { + return obj; + } + + public object PostProcessBeforeInstantiation(Type objectType, string objectName) + { + //proceed with default instantiation + return null; + } + + public bool PostProcessAfterInstantiation(object objectInstance, string objectName) + { + //proceed to set properties on the object + return true; + } + + #region IInstantiationAwareObjectPostProcessor Members + + public IPropertyValues PostProcessPropertyValues(IPropertyValues pvs, PropertyInfo[] pis, object objectInstance, + string objectName) + { + return pvs; + } + + #endregion + + public object PostProcessAfterInitialization(object obj, string objectName) + { + return obj; + } + } + + private sealed class ProxyingInstantiationAwareObjectPostProcessorStub + : IInstantiationAwareObjectPostProcessor + { + public ProxyingInstantiationAwareObjectPostProcessorStub() + { + } + + public ProxyingInstantiationAwareObjectPostProcessorStub(object proxy) + { + _proxy = proxy; + } + + private object _proxy; + + public object Proxy + { + get { return _proxy; } + set { _proxy = value; } + } + + public object PostProcessBeforeInitialization(object obj, string name) + { + throw new NotImplementedException(); + } + + public object PostProcessBeforeInstantiation(Type objectType, string objectName) + { + return _proxy; + } + + public bool PostProcessAfterInstantiation(object objectInstance, string objectName) + { + return true; + } + + #region IInstantiationAwareObjectPostProcessor Members + + public IPropertyValues PostProcessPropertyValues(IPropertyValues pvs, PropertyInfo[] pis, object objectInstance, + string objectName) + { + return pvs; + } + + #endregion + + public object PostProcessAfterInitialization(object obj, string objectName) + { + throw new NotImplementedException(); + } + } + + [Test] + public void PreInstantiateSingletonsMustNotIgnoreObjectsWithUnresolvedObjectTypes() + { + KnowsIfInstantiated.ClearInstantiationRecord(); + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + Assert.IsTrue(!KnowsIfInstantiated.WasInstantiated, "Singleton appears to be instantiated before the test is even run."); + RootObjectDefinition def = new RootObjectDefinition(); + def.ObjectTypeName = typeof (KnowsIfInstantiated).FullName; + lof.RegisterObjectDefinition("x1", def); + Assert.IsTrue(!KnowsIfInstantiated.WasInstantiated, "Singleton appears to be instantiated before PreInstantiateSingletons() is invoked."); + lof.PreInstantiateSingletons(); + Assert.IsTrue(KnowsIfInstantiated.WasInstantiated, "Singleton was not instantiated by the container (it must be)."); + } + + [Test] + public void LazyInitialization() + { + KnowsIfInstantiated.ClearInstantiationRecord(); + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + + RootObjectDefinition def = new RootObjectDefinition(); + def.ObjectTypeName = typeof (KnowsIfInstantiated).FullName; + def.IsLazyInit = true; + lof.RegisterObjectDefinition("x1", def); + + Assert.IsTrue(!KnowsIfInstantiated.WasInstantiated, "Singleton appears to be instantiated before the test is even run."); + lof.RegisterObjectDefinition("x1", def); + Assert.IsTrue(!KnowsIfInstantiated.WasInstantiated, "Singleton appears to be instantiated before PreInstantiateSingletons() is invoked."); + lof.PreInstantiateSingletons(); + Assert.IsFalse(KnowsIfInstantiated.WasInstantiated, "Singleton was instantiated by the container (it must NOT be 'cos LazyInit was set to TRUE)."); + lof.GetObject("x1"); + Assert.IsTrue(KnowsIfInstantiated.WasInstantiated, "Singleton was not instantiated by the container (it must be)."); + } + + [Test] + public void SingletonFactoryObjectMustNotCreatePrototypeOnPreInstantiateSingletonsCall() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + + RootObjectDefinition def = new RootObjectDefinition(); + def.ObjectType = typeof (DummyFactory); + def.IsSingleton = true; + def.PropertyValues.Add("IsSingleton", false); + + DummyFactory.Reset(); + + Assert.IsFalse(DummyFactory.WasPrototypeCreated, + "Prototype appears to be instantiated before the test is even run."); + lof.RegisterObjectDefinition("x1", def); + Assert.IsFalse(DummyFactory.WasPrototypeCreated, + "Prototype instantiated after object definition registration (must NOT be)."); + lof.PreInstantiateSingletons(); + Assert.IsFalse(DummyFactory.WasPrototypeCreated, + "Prototype instantiated after call to PreInstantiateSingletons(); must NOT be."); + lof.GetObject("x1"); + Assert.IsTrue(DummyFactory.WasPrototypeCreated, "Prototype was not instantiated."); + } + + [Test] + public void Empty() + { + IListableObjectFactory lof = new DefaultListableObjectFactory(); + Assert.IsTrue(lof.GetObjectDefinitionNames() != null, "No objects defined --> array != null"); + Assert.IsTrue(lof.GetObjectDefinitionNames().Length == 0, "No objects defined after no arg constructor"); + Assert.IsTrue(lof.ObjectDefinitionCount == 0, "No objects defined after no arg constructor"); + } + + [Test] + public void ObjectDefinitionCountIsZeroBeforeAnythingIsRegistered() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + Assert.AreEqual(0, lof.ObjectDefinitionCount, "No objects must be defined straight off the bat."); + } + + [Test] + public void ObjectDefinitionOverriding() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + lof.RegisterObjectDefinition("test", new RootObjectDefinition(typeof (TestObject), null)); + lof.RegisterObjectDefinition("test", new RootObjectDefinition(typeof (NestedTestObject), null)); + Assert.IsTrue(lof.GetObject("test") is NestedTestObject); + } + + [Test] + [ExpectedException(typeof (ObjectDefinitionStoreException))] + public void ObjectDefinitionOverridingNotAllowed() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + lof.AllowObjectDefinitionOverriding = false; + lof.RegisterObjectDefinition("test", new RootObjectDefinition(typeof (TestObject), null)); + lof.RegisterObjectDefinition("test", new RootObjectDefinition(typeof (NestedTestObject), null)); + } + + [Test] + public void CustomEditor() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + NumberFormatInfo nfi = new CultureInfo("en-GB", false).NumberFormat; + lof.RegisterCustomConverter(typeof (Single), new CustomNumberConverter(typeof (Single), nfi, true)); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("myFloat", "1.1"); + lof.RegisterObjectDefinition("testObject", new RootObjectDefinition(typeof (TestObject), pvs)); + TestObject testObject = (TestObject) lof.GetObject("testObject"); + Assert.IsTrue(testObject.MyFloat == 1.1f); + } + + [Test] + public void RegisterExistingSingletonWithReference() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + + RootObjectDefinition def = new RootObjectDefinition(); + def.ObjectType = typeof (TestObject); + def.PropertyValues.Add("Name", "Rick"); + def.PropertyValues.Add("Age", 30); + def.PropertyValues.Add("Spouse", new RuntimeObjectReference("singletonObject")); + lof.RegisterObjectDefinition("test", def); + + object singletonObject = new TestObject(); + lof.RegisterSingleton("singletonObject", singletonObject); + Assert.IsTrue(lof.IsSingleton("singletonObject")); + TestObject test = (TestObject) lof.GetObject("test"); + Assert.AreEqual(singletonObject, lof.GetObject("singletonObject")); + Assert.AreEqual(singletonObject, test.Spouse); + Hashtable objectsOfType = (Hashtable) lof.GetObjectsOfType(typeof (TestObject), false, true); + Assert.AreEqual(2, objectsOfType.Count); + Assert.IsTrue(objectsOfType.ContainsValue(test)); + Assert.IsTrue(objectsOfType.ContainsValue(singletonObject)); + } + + [Test] + public void ApplyPropertyValues() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + MutablePropertyValues properties = new MutablePropertyValues(); + properties.Add("age", "99"); + factory.RegisterObjectDefinition("test", new RootObjectDefinition(typeof (TestObject), properties)); + TestObject obj = new TestObject(); + Assert.AreEqual(0, obj.Age); + factory.ApplyObjectPropertyValues(obj, "test"); + Assert.AreEqual(99, obj.Age, "Property values were not applied to the existing instance."); + } + + [Test] + public void ApplyPropertyValuesWithIncompleteDefinition() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + MutablePropertyValues properties = new MutablePropertyValues(); + properties.Add("age", "99"); + factory.RegisterObjectDefinition("test", new RootObjectDefinition(null, properties)); + TestObject obj = new TestObject(); + Assert.AreEqual(0, obj.Age); + factory.ApplyObjectPropertyValues(obj, "test"); + Assert.AreEqual(99, obj.Age, "Property values were not applied to the existing instance."); + } + + [Test] + public void RegisterExistingSingletonWithAutowire() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("name", "Tony"); + pvs.Add("age", "48"); + RootObjectDefinition rod = new RootObjectDefinition(typeof (DependenciesObject), pvs, true); + rod.DependencyCheck = DependencyCheckingMode.Objects; + rod.AutowireMode = AutoWiringMode.ByType; + lof.RegisterObjectDefinition("test", rod); + object singletonObject = new TestObject(); + lof.RegisterSingleton("singletonObject", singletonObject); + Assert.IsTrue(lof.ContainsObject("singletonObject")); + Assert.IsTrue(lof.IsSingleton("singletonObject")); + Assert.AreEqual(0, lof.GetAliases("singletonObject").Length); + DependenciesObject test = (DependenciesObject) lof.GetObject("test"); + Assert.AreEqual(singletonObject, lof.GetObject("singletonObject")); + Assert.AreEqual(singletonObject, test.Spouse); + } + + [Test] + [ExpectedException(typeof (ObjectDefinitionStoreException))] + public void RegisterExistingSingletonWithAlreadyBound() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + object singletonObject = new TestObject(); + lof.RegisterSingleton("singletonObject", singletonObject); + lof.RegisterSingleton("singletonObject", singletonObject); + } + + [Test] + public void AutowireConstructor() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject)); + lof.RegisterObjectDefinition("spouse", rod); + ConstructorDependenciesObject cdo = (ConstructorDependenciesObject) lof.Autowire(typeof (ConstructorDependenciesObject), + AutoWiringMode.Constructor, true); + object spouse = lof.GetObject("spouse"); + Assert.IsTrue(cdo.Spouse1 == spouse); + Assert.IsTrue(ObjectFactoryUtils.ObjectOfType(lof, typeof (TestObject)) == spouse); + } + + [Test] + public void AutowireObjectByName() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rodDefinition = new RootObjectDefinition(typeof (TestObject)); + rodDefinition.PropertyValues.Add("name", "Rod"); + rodDefinition.AutowireMode = AutoWiringMode.ByName; + RootObjectDefinition kerryDefinition = new RootObjectDefinition(typeof (TestObject)); + kerryDefinition.PropertyValues.Add("name", "Kerry"); + lof.RegisterObjectDefinition("rod", rodDefinition); + lof.RegisterObjectDefinition("Spouse", kerryDefinition); + DependenciesObject obj = (DependenciesObject) lof.Autowire(typeof (DependenciesObject), + AutoWiringMode.ByName, true); + TestObject objRod = (TestObject) lof.GetObject("rod"); + Assert.AreEqual(obj.Spouse, objRod.Spouse); + } + + [Test] + public void AutowireObjectByNameIsNotCaseInsensitive() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rodDefinition = new RootObjectDefinition(typeof (TestObject)); + rodDefinition.PropertyValues.Add("name", "Rod"); + rodDefinition.AutowireMode = AutoWiringMode.ByName; + RootObjectDefinition kerryDefinition = new RootObjectDefinition(typeof (TestObject)); + kerryDefinition.PropertyValues.Add("name", "Kerry"); + lof.RegisterObjectDefinition("rod", rodDefinition); + lof.RegisterObjectDefinition("spouse", kerryDefinition); // property name is Spouse (capital S) + TestObject objRod = (TestObject) lof.GetObject("rod"); + Assert.IsNull(objRod.Spouse, "Mmm, Spouse property appears to have been autowired by name, even though there is no object in the factory with a name 'Spouse'."); + } + + [Test] + [ExpectedException(typeof (UnsatisfiedDependencyException))] + public void AutowireObjectByNameWithDependencyCheck() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject)); + lof.RegisterObjectDefinition("Spous", rod); + lof.Autowire(typeof (DependenciesObject), AutoWiringMode.ByName, true); + } + + [Test] + public void AutowireObjectByNameWithNoDependencyCheck() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject)); + lof.RegisterObjectDefinition("Spous", rod); + DependenciesObject obj = (DependenciesObject) lof.Autowire(typeof (DependenciesObject), AutoWiringMode.ByName, false); + Assert.IsNull(obj.Spouse); + } + + [Test] + public void AutowireObjectByType() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject)); + lof.RegisterObjectDefinition("test", rod); + DependenciesObject obj = (DependenciesObject) lof.Autowire(typeof (DependenciesObject), AutoWiringMode.ByType, true); + TestObject test = (TestObject) lof.GetObject("test"); + Assert.AreEqual(obj.Spouse, test); + } + + [Test] + [ExpectedException(typeof (UnsatisfiedDependencyException))] + public void AutowireObjectByTypeWithDependencyCheck() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + lof.Autowire(typeof (DependenciesObject), AutoWiringMode.ByType, true); + Assert.Fail("Should have thrown UnsatisfiedDependencyException"); + } + + [Test] + public void AutowireObjectByTypeWithNoDependencyCheck() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + DependenciesObject obj = (DependenciesObject) lof.Autowire(typeof (DependenciesObject), AutoWiringMode.ByType, false); + Assert.IsNull(obj.Spouse); + } + + [Test] + public void AutowireExistingObjectByName() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject)); + lof.RegisterObjectDefinition("Spouse", rod); + DependenciesObject existingObj = new DependenciesObject(); + lof.AutowireObjectProperties(existingObj, AutoWiringMode.ByName, true); + TestObject spouse = (TestObject) lof.GetObject("Spouse"); + Assert.AreEqual(existingObj.Spouse, spouse); + Assert.IsTrue(ObjectFactoryUtils.ObjectOfType(lof, typeof (TestObject)) == spouse); + } + + [Test] + [ExpectedException(typeof (UnsatisfiedDependencyException))] + public void AutowireExistingObjectByNameWithDependencyCheck() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject)); + lof.RegisterObjectDefinition("Spous", rod); + DependenciesObject existingObj = new DependenciesObject(); + lof.AutowireObjectProperties(existingObj, AutoWiringMode.ByName, true); + Assert.Fail("Should have thrown UnsatisfiedDependencyException"); + } + + [Test] + public void AutowireExistingObjectByNameWithNoDependencyCheck() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject)); + lof.RegisterObjectDefinition("Spous", rod); + DependenciesObject existingObj = new DependenciesObject(); + lof.AutowireObjectProperties(existingObj, AutoWiringMode.ByName, false); + Assert.IsNull(existingObj.Spouse); + } + + [Test] + public void AutowireExistingObjectByType() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject)); + lof.RegisterObjectDefinition("test", rod); + DependenciesObject existingObj = new DependenciesObject(); + lof.AutowireObjectProperties(existingObj, AutoWiringMode.ByType, true); + TestObject test = (TestObject) lof.GetObject("test"); + Assert.AreEqual(existingObj.Spouse, test); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void AutowireByTypeWithInvalidAutowireMode() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + DependenciesObject obj = new DependenciesObject(); + lof.AutowireObjectProperties(obj, AutoWiringMode.Constructor, true); + } + + [Test] + [ExpectedException(typeof (UnsatisfiedDependencyException))] + public void AutowireExistingObjectByTypeWithDependencyCheck() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + DependenciesObject existingObj = new DependenciesObject(); + lof.AutowireObjectProperties(existingObj, AutoWiringMode.ByType, true); + } + + [Test] + public void AutowireExistingObjectByTypeWithNoDependencyCheck() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + DependenciesObject existingObj = new DependenciesObject(); + lof.AutowireObjectProperties(existingObj, AutoWiringMode.ByType, false); + Assert.IsNull(existingObj.Spouse); + } + + [Test] + public void AutowireWithNoDependencies() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject)); + lof.RegisterObjectDefinition("rod", rod); + Assert.AreEqual(1, lof.ObjectDefinitionCount); + object registered = lof.Autowire(typeof (NoDependencies), AutoWiringMode.AutoDetect, false); + Assert.AreEqual(1, lof.ObjectDefinitionCount); + Assert.IsTrue(registered is NoDependencies); + } + + [Test] + public void AutowireWithSatisfiedObjectDependency() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add(new PropertyValue("name", "Rod")); + RootObjectDefinition rood = new RootObjectDefinition(typeof (TestObject), pvs); + lof.RegisterObjectDefinition("rod", rood); + Assert.AreEqual(1, lof.ObjectDefinitionCount); + // Depends on age, name and spouse (TestObject) + object registered = lof.Autowire(typeof (DependenciesObject), AutoWiringMode.AutoDetect, true); + Assert.AreEqual(1, lof.ObjectDefinitionCount); + DependenciesObject kerry = (DependenciesObject) registered; + TestObject rod = (TestObject) lof.GetObject("rod"); + Assert.AreSame(rod, kerry.Spouse); + } + + [Test] + public void AutowireWithSatisfiedConstructorDependency() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add(new PropertyValue("name", "Rod")); + RootObjectDefinition rood = new RootObjectDefinition(typeof (TestObject), pvs); + lof.RegisterObjectDefinition("rod", rood); + Assert.AreEqual(1, lof.ObjectDefinitionCount); + object registered = lof.Autowire(typeof (ConstructorDependency), AutoWiringMode.AutoDetect, false); + Assert.AreEqual(1, lof.ObjectDefinitionCount); + ConstructorDependency kerry = (ConstructorDependency) registered; + TestObject rod = (TestObject) lof.GetObject("rod"); + Assert.AreSame(rod, kerry._spouse); + } + + [Test] + [ExpectedException(typeof (UnsatisfiedDependencyException))] + public void AutowireWithUnsatisfiedConstructorDependency() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add(new PropertyValue("name", "Rod")); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject), pvs); + lof.RegisterObjectDefinition("rod", rod); + Assert.AreEqual(1, lof.ObjectDefinitionCount); + lof.Autowire(typeof (UnsatisfiedConstructorDependency), AutoWiringMode.AutoDetect, true); + Assert.Fail("Should have unsatisfied constructor dependency on SideEffectObject"); + } + + [Test] + public void ExtensiveCircularReference() + { + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + for (int i = 0; i < 1000; i++) + { + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add(new PropertyValue("Spouse", new RuntimeObjectReference("object" + (i < 99 ? i + 1 : 0)))); + RootObjectDefinition rod = new RootObjectDefinition(typeof (TestObject), pvs); + lof.RegisterObjectDefinition("object" + i, rod); + } + lof.PreInstantiateSingletons(); + for (int i = 0; i < 1000; i++) + { + TestObject obj = (TestObject) lof.GetObject("object" + i); + TestObject otherObj = (TestObject) lof.GetObject("object" + (i < 99 ? i + 1 : 0)); + Assert.IsTrue(obj.Spouse == otherObj); + } + } + + [Test] + public void PullingObjectWithFactoryMethodAlsoInjectsDependencies() + { + string expectedName = "Terese Raquin"; + MutablePropertyValues props = new MutablePropertyValues(); + props.Add(new PropertyValue("Name", expectedName)); + + RootObjectDefinition def = new RootObjectDefinition(typeof (MySingleton), props); + def.FactoryMethodName = "GetInstance"; + + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("foo", def); + + object foo = fac["foo"]; + Assert.IsNotNull(foo, "Couldn't pull manually registered instance out of the factory using factory method instantiation."); + MySingleton sing = (MySingleton) foo; + Assert.AreEqual(expectedName, sing.Name, "Dependency was not resolved pulling manually registered instance out of the factory using factory method instantiation."); + } + + [Test(Description = "http://opensource.atlassian.com/projects/spring/browse/SPRNET-368")] + public void GetObjectWithCtorArgsAndCtorAutowiring() + { + using (DefaultListableObjectFactory lof = new DefaultListableObjectFactory()) + { + RootObjectDefinition prototype + = new RootObjectDefinition(typeof(TestObject)); + prototype.IsSingleton = false; + lof.RegisterObjectDefinition("prototype", prototype); + + TestObject to = lof.GetObject("prototype", new object[] { "Bruno", 26, new NestedTestObject("Home") }) as TestObject; + Assert.IsNotNull(to); + Assert.AreEqual(26, to.Age); + Assert.AreEqual("Bruno", to.Name); + Assert.AreEqual("Home", to.Doctor.Company); + } + } + + [Test] + public void GetObjectWithCtorArgsOnPrototype() + { + using (DefaultListableObjectFactory lof = new DefaultListableObjectFactory()) + { + RootObjectDefinition prototype + = new RootObjectDefinition(typeof (TestObject)); + prototype.IsSingleton = false; + lof.RegisterObjectDefinition("prototype", prototype); + + TestObject to = lof.GetObject("prototype", new object[] {"Mark", 35}) as TestObject; + Assert.IsNotNull(to); + Assert.AreEqual(35, to.Age); + Assert.AreEqual("Mark", to.Name); + + TestObject to2 = lof.GetObject("prototype", new object[] {35, "Mark"}) as TestObject; + Assert.IsNotNull(to2); + Assert.AreEqual(35, to2.Age); + Assert.AreEqual("Mark", to2.Name); + } + } + + [Test] + public void GetObjectWithCtorArgsOnSingleton() + { + using (DefaultListableObjectFactory lof = new DefaultListableObjectFactory()) + { + RootObjectDefinition singleton + = new RootObjectDefinition(typeof(TestObject)); + singleton.IsSingleton = true; + lof.RegisterObjectDefinition("singleton", singleton); + + TestObject to = lof.GetObject("singleton", new object[] { "Mark", 35 }) as TestObject; + Assert.IsNotNull(to); + Assert.AreEqual(35, to.Age); + Assert.AreEqual("Mark", to.Name); + } + } + + [Test] + public void GetObjectWithCtorArgsOverrided() + { + using (DefaultListableObjectFactory lof = new DefaultListableObjectFactory()) + { + ConstructorArgumentValues arguments = new ConstructorArgumentValues(); + arguments.AddNamedArgumentValue("age", 27); + arguments.AddNamedArgumentValue("name", "Bruno"); + RootObjectDefinition singleton + = new RootObjectDefinition(typeof(TestObject), arguments, new MutablePropertyValues()); + singleton.IsSingleton = true; + lof.RegisterObjectDefinition("singleton", singleton); + + TestObject to = lof.GetObject("singleton", new object[] { "Mark", 35 }) as TestObject; + Assert.IsNotNull(to); + Assert.AreEqual(35, to.Age); + Assert.AreEqual("Mark", to.Name); + + TestObject to2 = lof.GetObject("singleton") as TestObject; + Assert.IsNotNull(to2); + Assert.AreEqual(27, to2.Age); + Assert.AreEqual("Bruno", to2.Name); + } + } + + [Test] + public void CreateObjectWithAllNamedCtorArguments() + { + string expectedName = "Bingo"; + int expectedAge = 1023; + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddNamedArgumentValue("age", expectedAge); + values.AddNamedArgumentValue("name", expectedName); + RootObjectDefinition def = new RootObjectDefinition(typeof (TestObject), values, new MutablePropertyValues()); + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("foo", def); + + ITestObject foo = fac["foo"] as ITestObject; + Assert.IsNotNull(foo, "Couldn't pull manually registered instance out of the factory."); + Assert.AreEqual(expectedName, foo.Name, "Dependency 'name' was not resolved using a named ctor arg."); + Assert.AreEqual(expectedAge, foo.Age, "Dependency 'age' was not resolved using a named ctor arg."); + } + + [Test] + public void CreateObjectWithAllNamedCtorArgumentsIsCaseInsensitive() + { + string expectedName = "Bingo"; + int expectedAge = 1023; + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddNamedArgumentValue("aGe", expectedAge); + values.AddNamedArgumentValue("naME", expectedName); + RootObjectDefinition def = new RootObjectDefinition(typeof (TestObject), values, new MutablePropertyValues()); + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("foo", def); + + ITestObject foo = fac["foo"] as ITestObject; + Assert.IsNotNull(foo, "Couldn't pull manually registered instance out of the factory."); + Assert.AreEqual(expectedName, foo.Name, "Dependency 'name' was not resolved using a named ctor arg."); + Assert.AreEqual(expectedAge, foo.Age, "Dependency 'age' was not resolved using a named ctor arg."); + } + + [Test] + public void CreateObjectWithMixOfNamedAndIndexedCtorArguments() + { + string expectedName = "Bingo"; + int expectedAge = 1023; + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddNamedArgumentValue("age", expectedAge); + values.AddIndexedArgumentValue(0, expectedName); + RootObjectDefinition def = new RootObjectDefinition(typeof (TestObject), values, new MutablePropertyValues()); + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("foo", def); + + ITestObject foo = fac["foo"] as ITestObject; + Assert.IsNotNull(foo, "Couldn't pull manually registered instance out of the factory."); + Assert.AreEqual(expectedName, foo.Name, "Dependency 'name' was not resolved an indexed ctor arg."); + Assert.AreEqual(expectedAge, foo.Age, "Dependency 'age' was not resolved using a named ctor arg."); + } + + [Test] + public void CreateObjectWithMixOfNamedAndIndexedAndAutowiredCtorArguments() + { + string expectedCompany = "Griffin's Foosball Arcade"; + MutablePropertyValues autoProps = new MutablePropertyValues(); + autoProps.Add(new PropertyValue("Company", expectedCompany)); + RootObjectDefinition autowired = new RootObjectDefinition(typeof (NestedTestObject), autoProps); + + string expectedName = "Bingo"; + int expectedAge = 1023; + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.AddNamedArgumentValue("age", expectedAge); + values.AddIndexedArgumentValue(0, expectedName); + RootObjectDefinition def = new RootObjectDefinition(typeof (TestObject), values, new MutablePropertyValues()); + def.AutowireMode = AutoWiringMode.Constructor; + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("foo", def); + fac.RegisterObjectDefinition("doctor", autowired); + + ITestObject foo = fac["foo"] as ITestObject; + Assert.IsNotNull(foo, "Couldn't pull manually registered instance out of the factory."); + Assert.AreEqual(expectedName, foo.Name, "Dependency 'name' was not resolved an indexed ctor arg."); + Assert.AreEqual(expectedAge, foo.Age, "Dependency 'age' was not resolved using a named ctor arg."); + Assert.AreEqual(expectedCompany, foo.Doctor.Company, "Dependency 'doctor.Company' was not resolved using autowiring."); + } + + [Test] + public void CreateObjectWithMixOfIndexedAndTwoNamedSameTypeCtorArguments() + { + // this object will be passed in as a named constructor argument + string expectedCompany = "Griffin's Foosball Arcade"; + MutablePropertyValues autoProps = new MutablePropertyValues(); + autoProps.Add(new PropertyValue("Company", expectedCompany)); + RootObjectDefinition autowired = new RootObjectDefinition(typeof (NestedTestObject), autoProps); + + // this object will be passed in as a named constructor argument + string expectedLawyersCompany = "Pollack, Pounce, & Pulverise"; + MutablePropertyValues lawyerProps = new MutablePropertyValues(); + lawyerProps.Add(new PropertyValue("Company", expectedLawyersCompany)); + RootObjectDefinition lawyer = new RootObjectDefinition(typeof (NestedTestObject), lawyerProps); + + // this simple string object will be passed in as an indexed constructor argument + string expectedName = "Bingo"; + + // this simple integer object will be passed in as a named constructor argument + int expectedAge = 1023; + + ConstructorArgumentValues values = new ConstructorArgumentValues(); + + // lets mix the order up a little... + values.AddNamedArgumentValue("age", expectedAge); + values.AddIndexedArgumentValue(0, expectedName); + values.AddNamedArgumentValue("doctor", new RuntimeObjectReference("a_doctor")); + values.AddNamedArgumentValue("lawyer", new RuntimeObjectReference("a_lawyer")); + + RootObjectDefinition def = new RootObjectDefinition(typeof (TestObject), values, new MutablePropertyValues()); + + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + // the object we're attempting to resolve... + fac.RegisterObjectDefinition("foo", def); + // the object that will be looked up and passed as a named parameter to a ctor call... + fac.RegisterObjectDefinition("a_doctor", autowired); + // another object that will be looked up and passed as a named parameter to a ctor call... + fac.RegisterObjectDefinition("a_lawyer", lawyer); + + ITestObject foo = fac["foo"] as ITestObject; + Assert.IsNotNull(foo, "Couldn't pull manually registered instance out of the factory."); + Assert.AreEqual(expectedName, foo.Name, "Dependency 'name' was not resolved an indexed ctor arg."); + Assert.AreEqual(expectedAge, foo.Age, "Dependency 'age' was not resolved using a named ctor arg."); + Assert.AreEqual(expectedCompany, foo.Doctor.Company, "Dependency 'doctor.Company' was not resolved using autowiring."); + Assert.AreEqual(expectedLawyersCompany, foo.Lawyer.Company, "Dependency 'lawyer.Company' was not resolved using another named ctor arg."); + } + + [Test] + public void CircularDependencyIsCorrectlyDetected() + { + RootObjectDefinition foo = new RootObjectDefinition(typeof (TestObject)); + foo.ConstructorArgumentValues.AddNamedArgumentValue("spouse", new RuntimeObjectReference("bar")); + RootObjectDefinition bar = new RootObjectDefinition(typeof (TestObject)); + bar.ConstructorArgumentValues.AddNamedArgumentValue("spouse", new RuntimeObjectReference("foo")); + + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("foo", foo); + fac.RegisterObjectDefinition("bar", bar); + + try + { + fac.GetObject("foo"); + } + catch (ObjectCreationException ex) + { + Assert.AreEqual(typeof (ObjectCurrentlyInCreationException), ex.GetBaseException().GetType(), + "Circular dependency was set up; should have caught an ObjectCurrentlyInCreationException instance."); + } + } + + [Test] + public void ConfigurableFactoryObjectInline() + { + DefaultListableObjectFactory dlof = new DefaultListableObjectFactory(); + + RootObjectDefinition everyman = new RootObjectDefinition(); + everyman.PropertyValues = new MutablePropertyValues(); + everyman.PropertyValues.Add("name", "Noone"); + everyman.PropertyValues.Add("age", 9781); + + RootObjectDefinition factory = new RootObjectDefinition(); + factory.ObjectType = typeof(DummyConfigurableFactory); + factory.PropertyValues = new MutablePropertyValues(); + factory.PropertyValues.Add("ProductTemplate", everyman); + dlof.RegisterObjectDefinition("factory", factory); + + TestObject instance = dlof.GetObject("factory") as TestObject; + Assert.IsNotNull(instance); + + Assert.AreEqual("Noone", instance.Name, "Name dependency injected via IConfigurableObjectFactory (instance) failed (was 'Factory singleton')."); + Assert.AreEqual(9781, instance.Age, "Age dependency injected via IObjectFactory.ConfigureObject(instance) failed (was 25)."); + } + + [Test] + public void ConfigurableFactoryObjectReference() + { + DefaultListableObjectFactory dlof = new DefaultListableObjectFactory(); + + RootObjectDefinition everyman = new RootObjectDefinition(); + everyman.IsAbstract = true; + everyman.PropertyValues = new MutablePropertyValues(); + everyman.PropertyValues.Add("name", "Noone"); + everyman.PropertyValues.Add("age", 9781); + dlof.RegisterObjectDefinition("everyman", everyman); + + RootObjectDefinition factory = new RootObjectDefinition(); + factory.ObjectType = typeof(DummyConfigurableFactory); + factory.PropertyValues = new MutablePropertyValues(); + factory.PropertyValues.Add("ProductTemplate", new RuntimeObjectReference("everyman")); + dlof.RegisterObjectDefinition("factory", factory); + + TestObject instance = dlof.GetObject("factory") as TestObject; + Assert.IsNotNull(instance); + + Assert.AreEqual("Noone", instance.Name, "Name dependency injected via IConfigurableObjectFactory (instance) failed (was 'Factory singleton')."); + Assert.AreEqual(9781, instance.Age, "Age dependency injected via IObjectFactory.ConfigureObject(instance) failed (was 25)."); + } + + [Test] + public void ConfigureObject() + { + TestObject instance = new TestObject(); + RootObjectDefinition everyman = new RootObjectDefinition(); + everyman.IsAbstract = true; + everyman.PropertyValues = new MutablePropertyValues(); + everyman.PropertyValues.Add("name", "Noone"); + everyman.PropertyValues.Add("age", 9781); + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition(instance.GetType().FullName, everyman); + fac.ConfigureObject(instance, instance.GetType().FullName); + Assert.AreEqual("Noone", instance.Name, "Name dependency injected via IObjectFactory.ConfigureObject(instance) failed (was null)."); + Assert.AreEqual(9781, instance.Age, "Age dependency injected via IObjectFactory.ConfigureObject(instance) failed (was null)."); + } + + [Test] + public void ConfigureObjectViaExplicitName() + { + TestObject instance = new TestObject(); + RootObjectDefinition everyman = new RootObjectDefinition(); + everyman.IsAbstract = true; + everyman.PropertyValues = new MutablePropertyValues(); + everyman.PropertyValues.Add("name", "Noone"); + everyman.PropertyValues.Add("age", 9781); + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("everyman", everyman); + fac.ConfigureObject(instance, "everyman"); + Assert.AreEqual(true, instance.InitCompleted, "AfterPropertiesSet() was not invoked by IObjectFactory.ConfigureObject(instance)."); + Assert.AreEqual("Noone", instance.Name, "Name dependency injected via IObjectFactory.ConfigureObject(instance) failed (was null)."); + Assert.AreEqual(9781, instance.Age, "Age dependency injected via IObjectFactory.ConfigureObject(instance) failed (was null)."); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void ConfigureObjectViaNullName() + { + TestObject instance = new TestObject(); + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.ConfigureObject(instance, null); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void ConfigureObjectViaLoadOfOldWhitespaceName() + { + TestObject instance = new TestObject(); + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.ConfigureObject(instance, " \t"); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void ConfigureObjectViaEmptyName() + { + TestObject instance = new TestObject(); + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.ConfigureObject(instance, string.Empty); + } + + [Test] + public void DisposeCyclesThroughAllSingletonsEvenIfTheirDisposeThrowsAnException() + { + RootObjectDefinition foo = new RootObjectDefinition(typeof (GoodDisposable)); + foo.IsSingleton = true; + RootObjectDefinition bar = new RootObjectDefinition(typeof (BadDisposable)); + bar.IsSingleton = true; + RootObjectDefinition baz = new RootObjectDefinition(typeof (GoodDisposable)); + baz.IsSingleton = true; + + using (DefaultListableObjectFactory fac = new DefaultListableObjectFactory()) + { + fac.RegisterObjectDefinition("foo", foo); + fac.RegisterObjectDefinition("bar", bar); + fac.RegisterObjectDefinition("baz", baz); + fac.PreInstantiateSingletons(); + } + Assert.AreEqual(2, GoodDisposable.DisposeCount, "All IDisposable singletons must have their Dispose() method called... one of them bailed, and as a result the rest were (apparently) not Dispose()d."); + GoodDisposable.DisposeCount = 0; + } + + [Test] + public void StaticInitializationViaDependsOnSingletonMethodInvokingFactoryObject() + { + RootObjectDefinition initializer = new RootObjectDefinition(typeof (MethodInvokingFactoryObject)); + initializer.PropertyValues.Add("TargetMethod", "Init"); + initializer.PropertyValues.Add("TargetType", typeof (StaticInitializer).AssemblyQualifiedName); + + RootObjectDefinition foo = new RootObjectDefinition(typeof (TestObject)); + foo.DependsOn = new string[] {"force-init"}; + + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("foo", foo); + fac.RegisterObjectDefinition("force-init", initializer); + + fac.GetObject("foo"); + Assert.IsTrue(StaticInitializer.InitWasCalled, "Boing"); + } + + /// + /// There is a similar test in XmlObjectFactoryTests that actually supplies another boolean + /// object in the factory that is used to autowire the object; this test puts no such second + /// object in the factory, so when the factory tries to autowire the second (missing) argument + /// to the ctor, it should (must) choke. + /// + [Test] + [ExpectedException(typeof (UnsatisfiedDependencyException), + "Error creating object with name 'foo' : Unsatisfied dependency " + + "expressed through constructor argument with index 1 of type [System.Boolean] : " + + "There are '0' objects of type [System.Boolean] for autowiring constructor. There " + + "should have been exactly 1 to be able to autowire the 'b2' argument on the constructor of object 'foo'.")] + public void DoubleBooleanAutowire() + { + RootObjectDefinition def = new RootObjectDefinition(typeof (DoubleBooleanConstructorObject)); + ConstructorArgumentValues args = new ConstructorArgumentValues(); + args.AddGenericArgumentValue(true, "bool"); + def.ConstructorArgumentValues = args; + def.AutowireMode = AutoWiringMode.Constructor; + def.IsSingleton = true; + + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("foo", def); + + fac.GetObject("foo"); + } + + [Test] + public void CanSetPropertyThatUsesNewModifierOnDerivedClass() + { + string nick = "Banjo"; + string expectedNickname = DerivedTestObject.NicknamePrefix + nick; + + RootObjectDefinition def = new RootObjectDefinition(typeof (DerivedTestObject)); + def.PropertyValues.Add("Nickname", nick); + + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("foo", def); + + DerivedTestObject tob = (DerivedTestObject) fac.GetObject("foo"); + Assert.AreEqual(expectedNickname, tob.Nickname, + "Property is not being set to the NEWed property on the subclass."); + } + + [Test] + public void CanSetPropertyThatUsesOddNewModifierOnDerivedClass() + { + RootObjectDefinition def = new RootObjectDefinition(typeof (DerivedFoo)); + def.PropertyValues.Add("Bar", new DerivedBar()); + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + fac.RegisterObjectDefinition("foo", def); + DerivedFoo foo = (DerivedFoo) fac.GetObject("foo"); + Assert.AreEqual(typeof(DerivedBar), foo.Bar.GetType()); + } + + [Test] + public void ChildReferencesParentByAnAliasOfTheParent() + { + const string TheParentsAlias = "theParentsAlias"; + const int ExpectedAge = 31; + const string ExpectedName = "Rick Evans"; + + RootObjectDefinition parentDef = new RootObjectDefinition(typeof (TestObject)); + parentDef.IsAbstract = true; + parentDef.PropertyValues.Add("name", ExpectedName); + parentDef.PropertyValues.Add("age", ExpectedAge); + + ChildObjectDefinition childDef = new ChildObjectDefinition(TheParentsAlias); + + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + + fac.RegisterObjectDefinition("parent", parentDef); + fac.RegisterAlias("parent", TheParentsAlias); + fac.RegisterObjectDefinition("child", childDef); + + TestObject child = (TestObject) fac.GetObject("child"); + Assert.AreEqual(ExpectedName, child.Name); + Assert.AreEqual(ExpectedAge, child.Age); + } + + [Test] + public void GetObjectDefinitionResolvesAliases() + { + const string TheParentsAlias = "theParentsAlias"; + const int ExpectedAge = 31; + const string ExpectedName = "Rick Evans"; + + RootObjectDefinition parentDef = new RootObjectDefinition(typeof (TestObject)); + parentDef.IsAbstract = true; + parentDef.PropertyValues.Add("name", ExpectedName); + parentDef.PropertyValues.Add("age", ExpectedAge); + + ChildObjectDefinition childDef = new ChildObjectDefinition(TheParentsAlias); + + DefaultListableObjectFactory fac = new DefaultListableObjectFactory(); + + fac.RegisterObjectDefinition("parent", parentDef); + fac.RegisterAlias("parent", TheParentsAlias); + + IObjectDefinition od = fac.GetObjectDefinition(TheParentsAlias); + Assert.IsNotNull(od); + } + + [Test] + public void IgnoreObjectPostProcessorDuplicates() + { + DynamicMock mock1 = new DynamicMock(typeof(IObjectPostProcessor)); + IObjectPostProcessor proc1 = (IObjectPostProcessor)mock1.Object; + + DefaultListableObjectFactory lof = new DefaultListableObjectFactory(); + + const string errMsg = "Wrong number of IObjectPostProcessors being reported by the ObjectPostProcessorCount property."; + Assert.AreEqual(0, lof.ObjectPostProcessorCount, errMsg); + lof.AddObjectPostProcessor(proc1); + Assert.AreEqual(1, lof.ObjectPostProcessorCount, errMsg); + lof.AddObjectPostProcessor(proc1); + Assert.AreEqual(1, lof.ObjectPostProcessorCount, errMsg); + } + + #region Helper Classes + + public class GoodDisposable : IDisposable + { + public static int DisposeCount = 0; + + public void Dispose() + { + ++DisposeCount; + } + } + + public class BadDisposable : IDisposable + { + public void Dispose() + { + throw new FormatException(); + } + } + + public sealed class MySingleton + { + private MySingleton() + { + } + + private static MySingleton _instance = new MySingleton(); + + public static MySingleton GetInstance() + { + return _instance; + } + + public string Name + { + get { return _name; } + set { _name = value; } + } + + private string _name; + } + + public class NoDependencies + { + } + + public class ConstructorDependency + { + public TestObject _spouse; + + public ConstructorDependency(TestObject spouse) + { + this._spouse = spouse; + } + } + + public class UnsatisfiedConstructorDependency + { + public UnsatisfiedConstructorDependency(TestObject to, SideEffectObject seo) + { + _to = to; + _seo = seo; + } + + public object Seo + { + get { return _seo; } + } + + public TestObject To + { + get { return _to; } + } + + private object _seo; + private TestObject _to; + } + + private sealed class StaticInitializer + { + public static bool InitWasCalled = false; + + public static void Init() + { + InitWasCalled = true; + } + } + + #endregion + } + + public class Foo + { + private IBar bar; + + public IBar Bar + { + get { return bar; } + set { bar = value; } + } + } + + public class DerivedFoo : Foo + { + public new IDerivedBar Bar + { + get { return (IDerivedBar) base.Bar; } + set { base.Bar = value; } + } + } + + public interface IBar {} + + public interface IDerivedBar : IBar {} + + public class Bar : IBar {} + + public class DerivedBar : IDerivedBar {} +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/DummyConfigurableFactory.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/DummyConfigurableFactory.cs new file mode 100644 index 00000000..11299ba7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/DummyConfigurableFactory.cs @@ -0,0 +1,53 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Simple factory based on DummyFactory to allow testing + /// of IConfigurableFactoryObject support in AbstractObjectFactory. + /// + /// Bruno Baia + /// $Id: DummyConfigurableFactory.cs,v 1.2 2007/03/04 20:42:06 bbaia Exp $ + public class DummyConfigurableFactory : DummyFactory, IConfigurableFactoryObject + { + #region Fields + + private IObjectDefinition productTemplate; + + #endregion + + #region IConfigurableFactoryObject Members + + public IObjectDefinition ProductTemplate + { + get { return productTemplate; } + set { productTemplate = value; } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/DummyFactory.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/DummyFactory.cs new file mode 100644 index 00000000..99b16053 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/DummyFactory.cs @@ -0,0 +1,222 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Simple factory to allow testing of IFactoryObject support in AbstractObjectFactory. + /// Depending on whether its singleton property is set, it will return a singleton + /// or a prototype instance. + /// Implements the IInitializingObject interface, so we can check that factories get + /// this lifecycle callback if they want. + /// + /// Rod Johnson + /// Rick Evans (.NET) + public class DummyFactory : + IFactoryObject, + IObjectFactoryAware, + IObjectNameAware, + IInitializingObject, + IDisposable + { + #region Constants + + public const string SINGLETON_NAME = "Factory singleton"; + + #endregion + + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + public DummyFactory() + { + testObject = new TestObject(); + testObject.Name = SINGLETON_NAME; + testObject.Age = 25; + } + + #endregion + + #region Properties + + /// + /// Was this initialized by invocation of the + /// AfterPropertiesSet() method from the IInitializingObject interface? + /// + public virtual bool WasInitialized + { + get { return initialized; } + } + + public static bool WasPrototypeCreated + { + get { return prototypeCreated; } + } + + public virtual bool PostProcessed + { + get { return postProcessed; } + + set { postProcessed = value; } + } + + public virtual ITestObject OtherTestObject + { + get { return otherTestObject; } + + set { otherTestObject = value; } + } + + #endregion + + #region Methods + + /// + /// Clear static state. + /// + public static void Reset() + { + prototypeCreated = false; + } + + #endregion + + #region Fields + + /// Default is for factories to return a singleton instance. + private bool singleton = true; + + private String objectName; + private IAutowireCapableObjectFactory objectFactory; + private bool postProcessed; + private bool initialized; + private static bool prototypeCreated; + private TestObject testObject; + private ITestObject otherTestObject; + + #endregion + + #region IFactoryObject Members + + public Type ObjectType + { + get + { +// if (!initialized) +// { +// throw new InvalidOperationException("'ObjectType' must not be called before AfterPropertiesSet()"); +// } + return testObject.GetType(); + } + } + + public object GetObject() + { +// if (!initialized) +// { +// throw new InvalidOperationException("GetObject() must not be called before AfterPropertiesSet()"); +// } + + if (IsSingleton) + { + return testObject; + } + else + { + TestObject prototype = new TestObject("Prototype created at " + DateTime.Now.Millisecond, 11); + if (objectFactory != null) + { + objectFactory.ApplyObjectPostProcessorsBeforeInitialization(prototype, objectName); + } + prototypeCreated = true; + return prototype; + } + } + + public bool IsSingleton + { + get { return singleton; } + set { singleton = value; } + } + + #endregion + + #region IObjectFactoryAware Members + + public IObjectFactory ObjectFactory + { + get { return objectFactory; } + set + { + objectFactory = (IAutowireCapableObjectFactory) value; + objectFactory.ApplyObjectPostProcessorsBeforeInitialization(testObject, objectName); + } + } + + #endregion + + #region IObjectNameAware Members + + public string ObjectName + { + get { return objectName; } + set { objectName = value; } + } + + #endregion + + #region IInitializingObject Members + + public void AfterPropertiesSet() + { + if (initialized) + { + throw new SystemException( + "Cannot call AfterPropertiesSet twice on the one object."); + } + + initialized = true; + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (testObject != null) + { + testObject.Name = null; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/HasMap.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/HasMap.cs new file mode 100644 index 00000000..b9372d36 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/HasMap.cs @@ -0,0 +1,121 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Collections.Specialized; + +using Spring.Collections; + +#endregion + +namespace Spring.Objects.Factory { + + /// + /// Object exposing a map. Used for object factory tests. + /// + public class HasMap { + + #region Properties + public IDictionary Map + { + get + { + return _map; + } + set + { + _map = value; + } + } + + public Set Set + { + get + { + return _set; + } + set + { + _set = value; + } + } + + public NameValueCollection Props + { + get + { + return _props; + } + set + { + _props = value; + } + } + + public object [] ObjectArray + { + get + { + return _objectArray; + } + set + { + _objectArray = value; + } + } + + virtual public Type [] ClassArray + { + get + { + return _classArray; + } + set + { + _classArray = value; + } + } + + virtual public int [] IntegerArray + { + get + { + return _intArray; + } + set + { + _intArray = value; + } + } + #endregion + + #region Fields + private IDictionary _map; + private Set _set; + private NameValueCollection _props; + private object [] _objectArray; + private Type [] _classArray; + private int [] _intArray; + #endregion + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/ISideEffectObject.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/ISideEffectObject.cs new file mode 100644 index 00000000..fdc523e1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/ISideEffectObject.cs @@ -0,0 +1,27 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +namespace Spring.Objects.Factory +{ + public interface ISideEffectObject + { + int Count { get; set; } + + void doWork(); + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/KnowsIfInstantiated.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/KnowsIfInstantiated.cs new file mode 100644 index 00000000..af0a45be --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/KnowsIfInstantiated.cs @@ -0,0 +1,42 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects.Factory +{ + internal sealed class KnowsIfInstantiated + { + private static bool _instantiated; + + public KnowsIfInstantiated() + { + _instantiated = true; + } + + public static void ClearInstantiationRecord() + { + _instantiated = false; + } + + public static bool WasInstantiated + { + get { return _instantiated; } + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/LifecycleObject.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/LifecycleObject.cs new file mode 100644 index 00000000..2863f741 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/LifecycleObject.cs @@ -0,0 +1,187 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory { + + /// + /// Simple test of IObjectFactory initialization and lifecycle callbacks. + /// + /// Rod Johnson + /// Rick Evans (.NET) + public class LifecycleObject : + IObjectNameAware, + IInitializingObject, + IObjectFactoryAware, + IDisposable { + + #region Methods + public virtual void PostProcessBeforeInit () + { + if (inited) + { + throw new ApplicationException ("Factory called PostProcessBeforeInit after AfterPropertiesSet"); + } + if (postProcessedBeforeInit) + { + throw new SystemException("Factory called PostProcessBeforeInit twice"); + } + postProcessedBeforeInit = true; + } + + /// + /// Dummy business method that will fail unless the factory + /// managed the object's lifecycle correctly + /// + public virtual void BusinessMethod () + { + if (!inited || !postProcessedAfterInit) + { + throw new SystemException ("Factory didn't initialize lifecycle object correctly"); + } + } + + public virtual void PostProcessAfterInit () + { + if (!inited) + { + throw new SystemException("Factory called PostProcessAfterInit before AfterPropertiesSet"); + } + if (postProcessedAfterInit) + { + throw new SystemException("Factory called PostProcessAfterInit twice"); + } + postProcessedAfterInit = true; + } + #endregion + + #region Properties + public bool Destroyed + { + get + { + return destroyed; + } + set + { + destroyed = value; + } + } + #endregion + + #region Fields + private string objectName; + private IObjectFactory owningFactory; + private bool postProcessedBeforeInit; + private bool inited; + private bool postProcessedAfterInit; + private bool destroyed; + #endregion + + #region IObjectNameAware Members + public string ObjectName + { + get + { + return objectName; + } + set + { + objectName = value; + } + } + #endregion + + #region IInitializingObject Members + public void AfterPropertiesSet () + { + if (owningFactory == null) + { + throw new SystemException ("Factory didn't call ObjectFactory before AfterPropertiesSet on lifecycle object"); + } + if (!postProcessedBeforeInit) + { + throw new SystemException ("Factory didn't call PostProcessBeforeInit before AfterPropertiesSet on lifecycle object"); + } + if (inited) + { + throw new SystemException ("Factory called AfterPropertiesSet twice"); + } + inited = true; + } + #endregion + + #region IObjectFactoryAware Members + public IObjectFactory ObjectFactory + { + get + { + return owningFactory; + } + set + { + owningFactory = value; + } + } + #endregion + + #region IDisposable Members + public void Dispose () + { + if (destroyed) + { + throw new SystemException ("Already destroyed..."); + } + destroyed = true; + } + #endregion + + #region Inner Class : PostProcessor + public class PostProcessor : IObjectPostProcessor + { + + public object PostProcessBeforeInitialization (object obj, string name) + { + if (obj is LifecycleObject) + { + ((LifecycleObject) obj).PostProcessBeforeInit (); + } + return obj; + } + + public object PostProcessAfterInitialization (object obj, string objectName) + { + if (obj is LifecycleObject) + { + ((LifecycleObject) obj).PostProcessAfterInit (); + } + return obj; + } + } + #endregion + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/MethodReplacerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/MethodReplacerTests.cs new file mode 100644 index 00000000..2de2739d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/MethodReplacerTests.cs @@ -0,0 +1,615 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Unit tests for the method replacement functionality of the Spring IoC container. + /// + /// + ///

    + /// This encapsulates both generic method replacement and its specialization, the + /// method lookup variant. + ///

    + ///
    + /// Rick Evans + /// $Id: MethodReplacerTests.cs,v 1.4 2008/05/02 20:08:23 markpollack Exp $ + [TestFixture] + public sealed class MethodReplacerTests + { + [Test] + public void SunnyDayReplaceMethod() + { + RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory)); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ReturnsNullNewsFeedManager)); + managerDef.MethodOverrides.Add(new ReplacedMethodOverride("CreateNewsFeed", "replacer")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("replacer", replacerDef); + INewsFeedManager manager = (INewsFeedManager) factory["manager"]; + NewsFeed feed1 = manager.CreateNewsFeed(); + Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced."); + Assert.AreEqual(NewsFeedFactory.DefaultName, feed1.Name); + NewsFeed feed2 = manager.CreateNewsFeed(); + // NewsFeedFactory always yields a new NewsFeed (see class definition below)... + Assert.IsFalse(ReferenceEquals(feed1, feed2)); + } + + [Test] + public void SunnyDayReplaceMethod_WithProtectedVirtual() + { + RootObjectDefinition replacerDef = new RootObjectDefinition(typeof(NewsFeedFactory)); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof(ProtectedReturnsNullNewsFeedManagerWithVirtualMethod)); + managerDef.MethodOverrides.Add(new ReplacedMethodOverride("CreateNewsFeed", "replacer")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("replacer", replacerDef); + ProtectedReturnsNullNewsFeedManagerWithVirtualMethod manager = (ProtectedReturnsNullNewsFeedManagerWithVirtualMethod)factory["manager"]; + NewsFeed feed1 = manager.GrabNewsFeed(); + Assert.IsNotNull(feed1, "The protected CreateNewsFeed() method is not being replaced."); + Assert.AreEqual(NewsFeedFactory.DefaultName, feed1.Name); + NewsFeed feed2 = manager.GrabNewsFeed(); + // NewsFeedFactory always yields a new NewsFeed (see class definition below)... + Assert.IsFalse(ReferenceEquals(feed1, feed2)); + + } + + [Test] + public void SunnyDayReplaceMethod_WithArgumentAcceptingReplacer() + { + RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory)); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (NewsFeedManagerWith_Replace_MethodThatTakesArguments)); + ReplacedMethodOverride theOverride = new ReplacedMethodOverride("CreateNewsFeed", "replacer"); + // we must specify parameter type fragments... + theOverride.AddTypeIdentifier(typeof(string).FullName); + managerDef.MethodOverrides.Add(theOverride); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("replacer", replacerDef); + NewsFeedManagerWith_Replace_MethodThatTakesArguments manager = (NewsFeedManagerWith_Replace_MethodThatTakesArguments) factory["manager"]; + NewsFeed feed1 = manager.CreateNewsFeed("So sad... to be all alone in the world"); + Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced."); + Assert.AreEqual("So sad... to be all alone in the world", feed1.Name); + NewsFeed feed2 = manager.CreateNewsFeed("Oh Muzzy!"); + // NewsFeedFactory always yields a new NewsFeed (see class definition below)... + Assert.IsFalse(ReferenceEquals(feed1, feed2)); + } + + [Test] + public void SunnyDayReplaceMethod_WithArgumentAcceptingReplacerWithNoOverloadingAndNoTypeFragmentsSpecified() + { + RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory)); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (NewsFeedManagerWith_Replace_MethodThatTakesArguments)); + ReplacedMethodOverride theOverride = new ReplacedMethodOverride("CreateNewsFeed", "replacer"); + // no need to specify parameter type fragments if we turn IsOverloaded off (the method is not overloaded)... + theOverride.IsOverloaded = false; + managerDef.MethodOverrides.Add(theOverride); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("replacer", replacerDef); + NewsFeedManagerWith_Replace_MethodThatTakesArguments manager = (NewsFeedManagerWith_Replace_MethodThatTakesArguments) factory["manager"]; + NewsFeed feed1 = manager.CreateNewsFeed("So sad... to be all alone in the world"); + Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced."); + Assert.AreEqual("So sad... to be all alone in the world", feed1.Name); + NewsFeed feed2 = manager.CreateNewsFeed("Oh Muzzy!"); + // NewsFeedFactory always yields a new NewsFeed (see class definition below)... + Assert.IsFalse(ReferenceEquals(feed1, feed2)); + } + + [Test] + public void SunnyDayReplaceMethod_WithReplacerThatReturnsVoid() + { + RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (DoNothingReplacer)); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (NewsFeedManagerThatReturnsVoid)); + ReplacedMethodOverride theOverride = new ReplacedMethodOverride("DoSomething", "replacer"); + managerDef.MethodOverrides.Add(theOverride); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("replacer", replacerDef); + NewsFeedManagerThatReturnsVoid manager = (NewsFeedManagerThatReturnsVoid) factory["manager"]; + manager.DoSomething(); + } + + /// + /// We don't specify any type fragments, so the method overload check will never pass. + /// + [Test] + [ExpectedException(typeof(NotImplementedException))] + public void SunnyDayReplaceMethod_WithArgumentAcceptingReplacerWithNoTypeFragmentsSpecified() + { + RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory)); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (NewsFeedManagerWith_Replace_MethodThatTakesArguments)); + managerDef.MethodOverrides.Add(new ReplacedMethodOverride("CreateNewsFeed", "replacer")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("replacer", replacerDef); + NewsFeedManagerWith_Replace_MethodThatTakesArguments manager = (NewsFeedManagerWith_Replace_MethodThatTakesArguments) factory["manager"]; + manager.CreateNewsFeed("So sad... to be all alone in the world"); + } + + /// + /// A class that requires two lookup method injections. + /// + [Test] + public void LookupMethodMultiple() + { + RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed)); + feedDef.IsSingleton = false; + feedDef.PropertyValues.Add("name", "Bingo"); + + RootObjectDefinition testObjectDef = new RootObjectDefinition(typeof (TestObject)); + testObjectDef.IsSingleton = false; + testObjectDef.PropertyValues.Add("name", "Miki Nakatani"); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (TestObjectAndNewsFeedFactory)); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed")); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateTestObject", "test")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("feed", feedDef); + factory.RegisterObjectDefinition("test", testObjectDef); + TestObjectAndNewsFeedFactory manager = (TestObjectAndNewsFeedFactory) factory["manager"]; + + INewsFeedManager newsFeedManager = manager; + NewsFeed feed1 = newsFeedManager.CreateNewsFeed(); + Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced."); + NewsFeed feed2 = newsFeedManager.CreateNewsFeed(); + // assert that the object (prototype) is definitely being looked up each time... + Assert.IsFalse(ReferenceEquals(feed1, feed2)); + + ITestObjectFactory toFactory = manager; + ITestObject to1 = toFactory.CreateTestObject(); + Assert.IsNotNull(to1, "The CreateTestObject() method is not being replaced."); + ITestObject to2 = toFactory.CreateTestObject(); + // assert that the object (prototype) is definitely being looked up each time... + Assert.IsFalse(ReferenceEquals(to1, to2)); + } + + /// + /// A class that requires both lookup method and replace method injection. + /// + [Test] + public void LookupAndReplaceMethod() + { + RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory)); + + RootObjectDefinition testObjectDef = new RootObjectDefinition(typeof (TestObject)); + testObjectDef.IsSingleton = false; + testObjectDef.PropertyValues.Add("name", "Miki Nakatani"); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (TestObjectAndNewsFeedFactory)); + managerDef.MethodOverrides.Add(new ReplacedMethodOverride("CreateNewsFeed", "replacer")); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateTestObject", "test")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("replacer", replacerDef); + factory.RegisterObjectDefinition("test", testObjectDef); + TestObjectAndNewsFeedFactory manager = (TestObjectAndNewsFeedFactory) factory["manager"]; + + INewsFeedManager newsFeedManager = manager; + NewsFeed feed1 = newsFeedManager.CreateNewsFeed(); + Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced."); + NewsFeed feed2 = newsFeedManager.CreateNewsFeed(); + // assert that the object (prototype) is definitely being looked up each time... + Assert.IsFalse(ReferenceEquals(feed1, feed2)); + + ITestObjectFactory toFactory = manager; + ITestObject to1 = toFactory.CreateTestObject(); + Assert.IsNotNull(to1, "The CreateTestObject() method is not being replaced."); + ITestObject to2 = toFactory.CreateTestObject(); + // assert that the object (prototype) is definitely being looked up each time... + Assert.IsFalse(ReferenceEquals(to1, to2)); + } + + /// + /// Lookup method injection on an (interface) method that returns null. + /// + [Test] + public void LookupMethodWithNullMethod() + { + try + { + RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed)); + feedDef.IsSingleton = false; + feedDef.PropertyValues.Add("name", "Bingo"); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ReturnsNullNewsFeedManager)); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("feed", feedDef); + INewsFeedManager manager = (INewsFeedManager) factory["manager"]; + NewsFeed feed1 = manager.CreateNewsFeed(); + Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced."); + NewsFeed feed2 = manager.CreateNewsFeed(); + // assert that the object (prototype) is definitely being looked up each time... + Assert.IsFalse(ReferenceEquals(feed1, feed2)); + } + catch (Exception ex) + { + Console.Out.WriteLine("ex = {0}", ex); + } + } + + /// + /// Lookup method injection on an (interface) method that returns null. + /// + /// + ///

    + /// The only difference from the previous (similarly named) test is that + /// constructor arguments are passed to the (er) constructor as it (the + /// dynamic subclass) is being instantiated by the container. + ///

    + ///
    + [Test] + public void LookupMethodWithNullMethodInstantiatedWithCtorArg() + { + RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed)); + feedDef.IsSingleton = false; + feedDef.PropertyValues.Add("name", "Bingo"); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ReturnsNullNewsFeedManager)); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed")); + managerDef.ConstructorArgumentValues.AddNamedArgumentValue("name", "Bingo"); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("feed", feedDef); + ReturnsNullNewsFeedManager manager = (ReturnsNullNewsFeedManager) factory["manager"]; + Assert.AreEqual("Bingo", manager.Name); + NewsFeed feed1 = manager.CreateNewsFeed(); + Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced."); + NewsFeed feed2 = manager.CreateNewsFeed(); + // assert that the object (prototype) is definitely being looked up each time... + Assert.IsFalse(ReferenceEquals(feed1, feed2)); + } + + [Test] + public void LookupMethodWithAbstractMethod() + { + RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed)); + feedDef.IsSingleton = false; + feedDef.PropertyValues.Add("name", "Bingo"); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (AbstractNewsFeedManager)); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("feed", feedDef); + INewsFeedManager manager = (INewsFeedManager) factory["manager"]; + NewsFeed feed1 = manager.CreateNewsFeed(); + Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced."); + NewsFeed feed2 = manager.CreateNewsFeed(); + // assert that the object (prototype) is definitely being looked up each time... + Assert.IsFalse(ReferenceEquals(feed1, feed2)); + } + + /// + /// Protected methods can also be method injected. + /// + [Test] + public void LookupMethodWithVirtualProtectedMethod() + { + RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed)); + feedDef.IsSingleton = false; + feedDef.PropertyValues.Add("name", "Bingo"); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ProtectedReturnsNullNewsFeedManagerWithVirtualMethod)); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("feed", feedDef); + ProtectedReturnsNullNewsFeedManagerWithVirtualMethod manager = (ProtectedReturnsNullNewsFeedManagerWithVirtualMethod) factory["manager"]; + NewsFeed feed1 = manager.GrabNewsFeed(); + Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced."); + NewsFeed feed2 = manager.GrabNewsFeed(); + // assert that the object (prototype) is definitely being looked up each time... + Assert.IsFalse(ReferenceEquals(feed1, feed2)); + } + + #region Rainy Day Scenarios + + [Test] + [ExpectedException(typeof (ObjectCreationException))] + public void FailWithLookupMethodOnSealedClass() + { + RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed)); + feedDef.IsSingleton = false; + feedDef.PropertyValues.Add("name", "Bingo"); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (SealedReturnsNullNewsFeedManager)); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("feed", feedDef); + factory.GetObject("manager"); + } + + [Test] + [ExpectedException(typeof (ObjectCreationException))] + public void FailWithReplaceMethodOnSealedClass() + { + RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory)); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (SealedReturnsNullNewsFeedManager)); + managerDef.MethodOverrides.Add(new ReplacedMethodOverride("CreateNewsFeed", "replacer")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("replacer", replacerDef); + factory.GetObject("manager"); + } + + /// + /// Tests that one cannot (obviously) override a protected method that has + /// not been explicitly declared virtual on the original class declaration. + /// + [Test] + [ExpectedException(typeof (ObjectCreationException))] + public void FailOnNonVirtualProtectedMethod() + { + RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed)); + feedDef.IsSingleton = false; + feedDef.PropertyValues.Add("name", "Bingo"); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ProtectedReturnsNullNewsFeedManager)); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("feed", feedDef); + factory.GetObject("manager"); + } + + /// + /// Tests that one cannot (obviously) method inject a method with a void + /// return type. + /// + [Test] + [ExpectedException(typeof (ObjectCreationException))] + public void FailOnMethodWithVoidReturnType() + { + RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed)); + feedDef.IsSingleton = false; + feedDef.PropertyValues.Add("name", "Bingo"); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ReturnsVoidNewsFeedManager)); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("feed", feedDef); + factory.GetObject("manager"); + } + + /// + /// Lookup methods cannot accept any arguments (replace methods can though). + /// + [Test] + [ExpectedException(typeof (ObjectCreationException))] + public void FailOnLookupMethodThatHasArguments() + { + RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed)); + feedDef.IsSingleton = false; + feedDef.PropertyValues.Add("name", "Bingo"); + + RootObjectDefinition managerDef = new RootObjectDefinition(typeof (NewsFeedManagerWithLookupMethodThatTakesArguments)); + managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed")); + + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("manager", managerDef); + factory.RegisterObjectDefinition("feed", feedDef); + factory.GetObject("manager"); + } + + #endregion + } + + + public interface INewsFeedManager + { + NewsFeed CreateNewsFeed(); + } + + public class ReturnsNullNewsFeedManager : INewsFeedManager + { + private string name; + + public ReturnsNullNewsFeedManager() + { + } + + public ReturnsNullNewsFeedManager(string name) + { + this.name = name; + } + + public virtual NewsFeed CreateNewsFeed() + { + return null; + } + + public string Name + { + get { return name; } + } + } + + public sealed class SealedReturnsNullNewsFeedManager : INewsFeedManager + { + public NewsFeed CreateNewsFeed() + { + return null; + } + } + + public class ProtectedReturnsNullNewsFeedManager + { + public NewsFeed GrabNewsFeed() + { + return CreateNewsFeed(); + } + + protected NewsFeed CreateNewsFeed() + { + return null; + } + } + + public class ProtectedReturnsNullNewsFeedManagerWithVirtualMethod + { + public NewsFeed GrabNewsFeed() + { + return CreateNewsFeed(); + } + + protected virtual NewsFeed CreateNewsFeed() + { + return null; + } + } + + /// + /// Just why anyone would want to do method injection on a method with a + /// void return type is a question best left for another (earlier) night. + /// + public class ReturnsVoidNewsFeedManager + { + public virtual void CreateNewsFeed() + { + } + } + + public class NewsFeedManagerWithLookupMethodThatTakesArguments + { + public virtual NewsFeed CreateNewsFeed(string foo, int bar) + { + return null; + } + } + + public class NewsFeedManagerThatReturnsVoid + { + public virtual void DoSomething() + { + } + } + + public class DoNothingReplacer : IMethodReplacer + { + public object Implement(object target, MethodInfo method, params object[] arguments) + { + return null; + } + } + + public class NewsFeedManagerWith_Replace_MethodThatTakesArguments + { + public virtual NewsFeed CreateNewsFeed(string headline) + { + throw new NotImplementedException(); + } + } + + public abstract class AbstractNewsFeedManager : INewsFeedManager + { + public abstract NewsFeed CreateNewsFeed(); + } + + public sealed class NewsFeedFactory : IMethodReplacer + { + public const string DefaultName = "All The News That's Fit To Print"; + + public object Implement(object target, MethodInfo method, params object[] arguments) + { + if (arguments != null && arguments.Length > 0) + { + string headline = (string) arguments[0]; + return new NewsFeed(headline); + } + return new NewsFeed(DefaultName); + } + } + + public sealed class NewsFeed + { + private string name; + + public NewsFeed() + { + } + + public NewsFeed(string name) + { + this.name = name; + } + + public string Name + { + get { return name; } + set { name = value; } + } + } + + public interface ITestObjectFactory + { + ITestObject CreateTestObject(); + } + + public class TestObjectAndNewsFeedFactory : ITestObjectFactory, INewsFeedManager + { + public virtual ITestObject CreateTestObject() + { + throw new NotImplementedException(); + } + + public virtual NewsFeed CreateNewsFeed() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/MustBeInitialized.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/MustBeInitialized.cs new file mode 100644 index 00000000..7d2b7020 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/MustBeInitialized.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory { + + /// + /// Simple test of IObjectFactory initialization. + /// + /// Rod Johnson + /// Rick Evans (.NET) + public class MustBeInitialized : IInitializingObject + { + + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the class. + /// + public MustBeInitialized() {} + #endregion + + #region Methods + public void AfterPropertiesSet () + { + inited = true; + } + + /// + /// Dummy business method that will fail unless the factory + /// managed the object's lifecycle correctly. + /// + public virtual void BusinessMethod () + { + if (!inited) + { + throw new SystemException ( + "Factory didn't call AfterPropertiesSet () on MustBeInitialized object"); + } + } + #endregion + + #region Fields + private bool inited; + #endregion + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/NoSuchObjectDefinitionExceptionTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/NoSuchObjectDefinitionExceptionTests.cs new file mode 100644 index 00000000..c21fa118 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/NoSuchObjectDefinitionExceptionTests.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using NUnit.Framework; + +namespace Spring.Objects.Factory +{ + /// + /// Unit tests for the NoSuchObjectDefinitionException class. + /// + /// Rick Evans + /// $Id: NoSuchObjectDefinitionExceptionTests.cs,v 1.3 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class NoSuchObjectDefinitionExceptionTests + { + public const string NotFoundObjectDefinitionName = "myObject"; + + public static readonly Type NotFoundObjectDefinitionType = typeof (TestObject); + + [Test] + public void SerializesObjectNameFieldCorrectly() + { + NoSuchObjectDefinitionException ex + = new NoSuchObjectDefinitionException(NotFoundObjectDefinitionName, "Cannot dynamically build object key..."); + NoSuchObjectDefinitionException deserializedException = Serialize(ex); + Assert.IsNotNull(deserializedException); + Assert.AreEqual(NotFoundObjectDefinitionName, deserializedException.ObjectName, "'ObjectName' property was not serialized correctly."); + } + + [Test] + public void SerializesObjectTypeFieldCorrectly() + { + NoSuchObjectDefinitionException ex + = new NoSuchObjectDefinitionException(NotFoundObjectDefinitionType, null); + NoSuchObjectDefinitionException deserializedException = Serialize(ex); + Assert.IsNotNull(deserializedException); + Assert.AreEqual(NotFoundObjectDefinitionType, deserializedException.ObjectType, "'ObjectType' property was not serialized correctly."); + } + + private NoSuchObjectDefinitionException Serialize(NoSuchObjectDefinitionException inputException) + { + NoSuchObjectDefinitionException deserializedException = null; + string tempDir = Environment.GetEnvironmentVariable("TEMP"); + string tempFilename = tempDir + @"\foo.dat"; + FileInfo file = new FileInfo(tempFilename); + try + { + Stream outstream = file.OpenWrite(); + new BinaryFormatter().Serialize(outstream, inputException); + outstream.Flush(); + outstream.Close(); + Stream instream = file.OpenRead(); + deserializedException = new BinaryFormatter().Deserialize(instream) as NoSuchObjectDefinitionException; + instream.Close(); + } + finally + { + try + { + file.Delete(); + } + catch + { + } + } + return deserializedException; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/ObjectDefinitionStoreExceptionTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/ObjectDefinitionStoreExceptionTests.cs new file mode 100644 index 00000000..100dbeeb --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/ObjectDefinitionStoreExceptionTests.cs @@ -0,0 +1,111 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Unit tests for the ObjectDefinitionStoreException class. + /// + /// Rick Evans + /// $Id: ObjectDefinitionStoreExceptionTests.cs,v 1.5 2007/08/08 17:49:04 bbaia Exp $ + [TestFixture] + public sealed class ObjectDefinitionStoreExceptionTests + { + [Test] + public void FromResource() + { + string expectedName = "bing"; + string expectedResourceDescription = "mock.resource"; + DynamicMock mock = new DynamicMock(typeof(IResource)); + mock.ExpectAndReturn("Description", expectedResourceDescription); + IResource resource = (IResource) mock.Object; + ObjectDefinitionStoreException inex + = new ObjectDefinitionStoreException(resource, expectedName, "mmm..."); + mock.Verify(); + CheckSerialization(inex, expectedName, expectedResourceDescription); + } + + [Test] + public void FromNullResource() + { + string expectedName = "bing"; + string expectedResourceDescription = string.Empty; + ObjectDefinitionStoreException inex + = new ObjectDefinitionStoreException( + (IResource) null, expectedName, "mmm..."); + CheckSerialization(inex, expectedName, + expectedResourceDescription); + } + + [Test] + public void SerializesWithNoState() + { + ObjectDefinitionStoreException inex + = new ObjectDefinitionStoreException(); + CheckSerialization(inex, string.Empty, string.Empty); + } + + [Test] + public void SerializesWithJustExceptionMessage() + { + string expectedName = string.Empty; + string expectedResourceDescription = string.Empty; + string expectedMessage = "Woppadoosa"; + ObjectDefinitionStoreException inex + = new ObjectDefinitionStoreException(expectedMessage); + CheckSerialization(inex, expectedName, + expectedResourceDescription); + } + + [Test] + public void SerializesAllState() + { + string expectedName = "bing"; + string expectedResourceDescription = "foo.txt"; + ObjectDefinitionStoreException inex + = new ObjectDefinitionStoreException( + expectedResourceDescription, expectedName, "mmm..."); + CheckSerialization(inex, expectedName, + expectedResourceDescription); + } + + private static void CheckSerialization( + ObjectDefinitionStoreException inex, string expectedName, + string expectedResourceDescription) + { + ObjectDefinitionStoreException outex = (ObjectDefinitionStoreException) + SerializationTestUtils.SerializeAndDeserialize(inex); + Assert.AreEqual(expectedName, outex.ObjectName, + "The 'ObjectName' property was not serialized / deserialized correctly."); + Assert.AreEqual(expectedResourceDescription, outex.ResourceDescription, + "The 'ResourceDescription' property was not serialized / deserialized correctly."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/ObjectFactoryUtilsTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/ObjectFactoryUtilsTests.cs new file mode 100644 index 00000000..f955b16b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/ObjectFactoryUtilsTests.cs @@ -0,0 +1,223 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Unit tests for the ObjectFactoryUtils class. + /// + /// Rod Johnson + /// Simon White (.NET) + /// Rick Evans (.NET) + [TestFixture] + public sealed class ObjectFactoryUtilsTests + { + private IConfigurableListableObjectFactory _factory; + + [SetUp] + public void SetUp() + { + IObjectFactory grandparent + = new XmlObjectFactory(new ReadOnlyXmlTestResource("root.xml", GetType())); + IObjectFactory parent + = new XmlObjectFactory(new ReadOnlyXmlTestResource("middle.xml", GetType()), grandparent); + IConfigurableListableObjectFactory child + = new XmlObjectFactory(new ReadOnlyXmlTestResource("leaf.xml", GetType()), parent); + _factory = child; + } + + /// + /// Check that override doesn't count as two separate objects. + /// + [Test] + public void CountObjectsIncludingAncestors() + { + // leaf count... + Assert.AreEqual(1, _factory.ObjectDefinitionCount); + // count minus duplicate... + Assert.AreEqual(6, ObjectFactoryUtils.CountObjectsIncludingAncestors(_factory), + "Should count 6 objects, not " + ObjectFactoryUtils.CountObjectsIncludingAncestors(_factory)); + } + + [Test] + public void ObjectNamesIncludingAncestors() + { + IList names = ObjectFactoryUtils.ObjectNamesIncludingAncestors(_factory); + Assert.AreEqual(6, names.Count); + } + + [Test] + public void ObjectNamesForType() + { + IList names = ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors(_factory, typeof (ITestObject)); + // includes 2 TestObjects from IFactoryObjects (DummyFactory definitions) + Assert.AreEqual(4, names.Count); + Assert.IsTrue(names.Contains("test")); + Assert.IsTrue(names.Contains("test3")); + Assert.IsTrue(names.Contains("testFactory1")); + Assert.IsTrue(names.Contains("testFactory2")); + } + + [Test] + public void CountObjectsIncludingAncestorsWithNonHierarchicalFactory() + { + StaticListableObjectFactory lof = new StaticListableObjectFactory(); + lof.AddObject("t1", new TestObject()); + lof.AddObject("t2", new TestObject()); + Assert.IsTrue(ObjectFactoryUtils.CountObjectsIncludingAncestors(lof) == 2); + } + + [Test] + public void HierarchicalResolutionWithOverride() + { + object test3 = _factory.GetObject("test3"); + object test = _factory.GetObject("test"); + object testFactory1 = _factory.GetObject("testFactory1"); + + IDictionary objects = ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(_factory, typeof (ITestObject), true, false); + Assert.AreEqual(2, objects.Count); + Assert.AreEqual(test3, objects["test3"]); + Assert.AreEqual(test, objects["test"]); + objects = ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(_factory, typeof (ITestObject), false, false); + Assert.AreEqual(1, objects.Count); + Assert.AreEqual(test, objects["test"]); + objects = ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(_factory, typeof (ITestObject), false, true); + Assert.AreEqual(2, objects.Count); + Assert.AreEqual(test, objects["test"]); + Assert.AreEqual(testFactory1, objects["testFactory1"]); + objects = ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(_factory, typeof (ITestObject), true, true); + Assert.AreEqual(4, objects.Count); + Assert.AreEqual(test3, objects["test3"]); + Assert.AreEqual(test, objects["test"]); + Assert.AreEqual(testFactory1, objects["testFactory1"]); + Assert.IsTrue(objects["testFactory2"] is ITestObject); + objects = ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(_factory, typeof (DummyFactory), true, true); + Assert.AreEqual(2, objects.Count); + Assert.AreEqual(_factory.GetObject("&testFactory1"), objects["&testFactory1"]); + Assert.AreEqual(_factory.GetObject("&testFactory2"), objects["&testFactory2"]); + objects = ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(_factory, typeof (IFactoryObject), true, true); + Assert.AreEqual(2, objects.Count); + Assert.AreEqual(_factory.GetObject("&testFactory1"), objects["&testFactory1"]); + Assert.AreEqual(_factory.GetObject("&testFactory2"), objects["&testFactory2"]); + } + + [Test] + [ExpectedException(typeof (NoSuchObjectDefinitionException), + "No unique object of type [Spring.Objects.ITestObject] is defined : Expected single object but found 4")] + public void ObjectOfTypeIncludingAncestorsWithMoreThanOneObjectOfType() + { + ObjectFactoryUtils.ObjectOfTypeIncludingAncestors(_factory, typeof (ITestObject), true, true); + } + + [Test] + public void NoObjectsOfTypeIncludingAncestors() + { + StaticListableObjectFactory lof = new StaticListableObjectFactory(); + lof.AddObject("foo", new object()); + IDictionary objects = ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(lof, typeof (ITestObject), true, false); + Assert.IsTrue(objects.Count == 0); + } + + [Test] + public void ObjectsOfTypeIncludingAncestorsWithStaticFactory() + { + StaticListableObjectFactory lof = new StaticListableObjectFactory(); + TestObject t1 = new TestObject(); + TestObject t2 = new TestObject(); + DummyFactory t3 = new DummyFactory(); + DummyFactory t4 = new DummyFactory(); + t4.IsSingleton = false; + lof.AddObject("t1", t1); + lof.AddObject("t2", t2); + lof.AddObject("t3", t3); + t3.AfterPropertiesSet(); // StaticListableObjectFactory does support lifecycle calls. + lof.AddObject("t4", t4); + t4.AfterPropertiesSet(); // StaticListableObjectFactory does support lifecycle calls. + IDictionary objects = ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(lof, typeof(ITestObject), true, false); + Assert.AreEqual(2, objects.Count); + Assert.AreEqual(t1, objects["t1"]); + Assert.AreEqual(t2, objects["t2"]); + objects = ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(lof, typeof (ITestObject), false, true); + Assert.AreEqual(3, objects.Count); + Assert.AreEqual(t1, objects["t1"]); + Assert.AreEqual(t2, objects["t2"]); + Assert.AreEqual(t3.GetObject(), objects["t3"]); + objects = ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(lof, typeof (ITestObject), true, true); + Assert.AreEqual(4, objects.Count); + Assert.AreEqual(t1, objects["t1"]); + Assert.AreEqual(t2, objects["t2"]); + Assert.AreEqual(t3.GetObject(), objects["t3"]); + Assert.IsTrue(objects["t4"] is TestObject); + } + + [Test] + public void IsFactoryDereferenceWithNonFactoryObjectName() + { + Assert.IsFalse(ObjectFactoryUtils.IsFactoryDereference("roob"), + "Name that didn't start with the factory object prefix is being reported " + + "(incorrectly) as a factory object dereference."); + } + + [Test] + public void IsFactoryDereferenceWithNullName() + { + Assert.IsFalse(ObjectFactoryUtils.IsFactoryDereference(null), + "Null name that (obviously) didn't start with the factory object prefix is being reported " + + "(incorrectly) as a factory object dereference."); + } + + [Test] + public void IsFactoryDereferenceWithEmptyName() + { + Assert.IsFalse(ObjectFactoryUtils.IsFactoryDereference(string.Empty), + "String.Empty name that (obviously) didn't start with the factory object prefix is being reported " + + "(incorrectly) as a factory object dereference."); + } + + [Test] + public void IsFactoryDereferenceWithJustTheFactoryObjectPrefixCharacter() + { + Assert.IsFalse(ObjectFactoryUtils.IsFactoryDereference( + ObjectFactoryUtils.FactoryObjectPrefix), + "Name that consisted solely of the factory object prefix is being reported " + + "(incorrectly) as a factory object dereference."); + } + + [Test] + public void IsFactoryDereferenceSunnyDay() + { + Assert.IsTrue(ObjectFactoryUtils.IsFactoryDereference( + ObjectFactoryUtils.FactoryObjectPrefix + "roob"), + "Name that did start with the factory object prefix is not being reported " + + "(incorrectly) as a factory object dereference."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/SideEffectObject.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/SideEffectObject.cs new file mode 100644 index 00000000..22c59cbf --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/SideEffectObject.cs @@ -0,0 +1,48 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +namespace Spring.Objects.Factory +{ + /// + /// Object that changes state on a business invocation, so that + /// we can check whether it's been invoked. + /// + /// Rod Johnson + /// Simon White (.NET) + public class SideEffectObject : ISideEffectObject + { + private int _count; + + public int Count + { + get + { + return _count; + } + set + { + this._count = value; + } + } + + public void doWork() + { + _count++; + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/AutowireUtilsTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/AutowireUtilsTests.cs new file mode 100644 index 00000000..dcebd7ff --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/AutowireUtilsTests.cs @@ -0,0 +1,297 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the AutowireUtilsTests class. + /// + /// Rick Evans + /// $Id: AutowireUtilsTests.cs,v 1.3 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class AutowireUtilsTests + { + #region GetTypeDifferenceWeight Tests + + [Test] + public void GetTypeDifferenceWeightForZeroArgCtor() + { + int expectedWeight = 0; + int actualWeight = AutowireUtils.GetTypeDifferenceWeight( + typeof (Fable).GetConstructor(Type.EmptyTypes).GetParameters(), new object[] {}); + Assert.AreEqual(expectedWeight, actualWeight, "AutowireUtils.GetTypeDifferenceWeight() was wrong, evidently."); + } + + [Test] + public void GetTypeDifferenceWeightWhenPassingDerivedTypeArgsToBaseTypeCtor() + { + int expectedWeight = 1; + int actualWeight = AutowireUtils.GetTypeDifferenceWeight( + typeof (Fable).GetConstructor( + new Type[] {typeof (NurseryRhymeCharacter)}).GetParameters(), + new object[] {new EnglishCharacter()}); + Assert.AreEqual(expectedWeight, actualWeight, "AutowireUtils.GetTypeDifferenceWeight() was wrong, evidently."); + } + + [Test] + public void GetTypeDifferenceWeightWhenPassingExactTypeMatch() + { + int expectedWeight = 0; + int actualWeight = AutowireUtils.GetTypeDifferenceWeight( + typeof (Fable).GetConstructor( + new Type[] {typeof (EnglishCharacter)}).GetParameters(), + new object[] {new EnglishCharacter()}); + Assert.AreEqual(expectedWeight, actualWeight, "AutowireUtils.GetTypeDifferenceWeight() was wrong, evidently."); + } + + /// + /// We want to use the ctor with the most specific argument type... so in the case of the test + /// classes used in this test, when we create a new instance of the Fable class using a + /// PeterPumpkinEater instance, we want the Fable(PeterPumpkinEater) ctor to be picked in + /// preference to any of the (still valid) ctors that accept Types that the PeterPumpkinEater + /// class inherits from. + /// + [Test] + public void UsingTypeDifferenceWeightPicksMostSpecificCtor() + { + object[] arguments = new object[] {new EnglishCharacter()}; + ConstructorInfo expectedCtor + = typeof (Fable).GetConstructor(new Type[] {typeof (EnglishCharacter)}); + ConstructorInfo pickedCtor = AssertTypeWeightingPicksCorrectCtor(arguments, typeof (Fable)); + Assert.IsNotNull(pickedCtor); + Assert.AreEqual(expectedCtor, pickedCtor); + } + + [Test] + public void UsingTypeDifferenceWeightPicksNothingWhenGivenAnIncompatibleArgument() + { + object[] arguments = new object[] {"This argument is incompatible with any of the Fable ctors."}; + ConstructorInfo pickedCtor = AssertTypeWeightingPicksCorrectCtor(arguments, typeof (Fable)); + Assert.IsNull(pickedCtor); + } + + [Test] + public void UsingTypeDifferenceWeightPicksMostGenericCtorWhenGivenA_NULL_Argument() + { + // using null means that we can match anything that takes an object as an argument... + object[] arguments = new object[] {null}; + // we'll want to pick the 'least' specific (most generic) ctor that we can find... + ConstructorInfo expectedCtor + = typeof (Fable).GetConstructor(new Type[] {typeof (FableCharacter)}); + ConstructorInfo pickedCtor = AssertTypeWeightingPicksCorrectCtor(arguments, typeof (Fable)); + Assert.IsNotNull(pickedCtor); + Assert.AreEqual(expectedCtor, pickedCtor); + } + + private static ConstructorInfo AssertTypeWeightingPicksCorrectCtor(object[] arguments, Type ctorType) + { + ConstructorInfo pickedCtor = null; + // we want to pick the ctor that has the lowest type difference weight... + ConstructorInfo[] ctors = ctorType.GetConstructors(); + int weighting = Int32.MaxValue; + foreach (ConstructorInfo ctor in ctors) + { + ParameterInfo[] parameters = ctor.GetParameters(); + if (parameters.Length == arguments.Length) + { + int weight = AutowireUtils.GetTypeDifferenceWeight(parameters, arguments); + if (weight < weighting) + { + pickedCtor = ctor; + weighting = weight; + } + } + } + return pickedCtor; + } + + [Test] + [ExpectedException(typeof (ArgumentException), + "Cannot calculate the type difference weight for argument types and arguments with differing lengths.")] + public void GetTypeDifferenceWeightWithMismatchedLengths() + { + AutowireUtils.GetTypeDifferenceWeight(new ParameterInfo[] {}, new object[] {1}); + } + + [Test] + [ExpectedException(typeof (NullReferenceException))] + public void GetTypeDifferenceWeightWithNullParameterTypes() + { + AutowireUtils.GetTypeDifferenceWeight(null, new object[] {}); + } + + [Test] + [ExpectedException(typeof (NullReferenceException))] + public void GetTypeDifferenceWeightWithNullArgumentTypes() + { + AutowireUtils.GetTypeDifferenceWeight( + typeof (Fable).GetConstructor(new Type[] {typeof (FableCharacter)}).GetParameters(), null); + } + + [Test] + [ExpectedException(typeof (NullReferenceException))] + public void GetTypeDifferenceWeightWithAllNulls() + { + AutowireUtils.GetTypeDifferenceWeight(null, null); + } + + [Test] + public void GetTypeDifferenceWeightWithSameTypes() + { + int expectedWeight = 0; + int actualWeight = AutowireUtils.GetTypeDifferenceWeight( + typeof (Fool).GetConstructor(new Type[] {typeof (string)}).GetParameters(), + new object[] {"Noob"}); + Assert.AreEqual(expectedWeight, actualWeight, "AutowireUtils.GetTypeDifferenceWeight() was wrong, evidently."); + } + + #endregion + + #region SortConstructors Tests + + [Test] + public void SortCtorsReallyIsGreedy() + { + BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; + Type foolType = typeof (Fool); + ConstructorInfo[] ctors = foolType.GetConstructors(flags); + AutowireUtils.SortConstructors(ctors); + ConstructorInfo[] expected = new ConstructorInfo[] + { + foolType.GetConstructor(new Type[] {typeof (string), typeof (int), typeof (Double), typeof (Fool)}), + foolType.GetConstructor(new Type[] {typeof (string), typeof (int)}), + foolType.GetConstructor(new Type[] {typeof (string)}), + foolType.GetConstructor(Type.EmptyTypes), + }; + Assert.IsTrue(ArrayUtils.AreEqual(expected, ctors), "AutowireUtils.SortConstructors() did not put the greedy public ctors first."); + } + + [Test] + public void SortCtorsReallyDoesFavourPublicOnes() + { + BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + Type foolType = typeof (Fool); + ConstructorInfo[] ctors = foolType.GetConstructors(flags); + AutowireUtils.SortConstructors(ctors); + ConstructorInfo[] expected = new ConstructorInfo[] + { + foolType.GetConstructor(new Type[] {typeof (string), typeof (int), typeof (Double), typeof (Fool)}), + foolType.GetConstructor(new Type[] {typeof (string), typeof (int)}), + foolType.GetConstructor(new Type[] {typeof (string)}), + foolType.GetConstructor(Type.EmptyTypes), + foolType.GetConstructor(flags, null, new Type[] {typeof (string), typeof (int), typeof (Double)}, null), + foolType.GetConstructor(flags, null, new Type[] {typeof (string), typeof (Fool)}, null), + }; + Assert.IsTrue(ArrayUtils.AreEqual(expected, ctors), "AutowireUtils.SortConstructors() did not put the greedy public ctors first."); + } + + [Test] + public void SortCtorsBailsSilentlyWhenGivenNull() + { + AutowireUtils.SortConstructors(null); + } + + #endregion + + #region Inner Class used to test ctor sorting + + private class Fool + { + private string _name; + private int _age; + private Double _money; + private Fool _mate; + + public Fool() + { + } + + public Fool(string name) : this(name, 0) + { + } + + public Fool(string name, int age) : this(name, age, 10.9D) + { + } + + protected Fool(string name, int age, Double money) + { + _name = name; + _age = age; + _money = money; + } + + public Fool(string name, int age, Double money, Fool mate) : this(name, age, money) + { + _mate = mate; + } + + private Fool(string name, Fool mate) : this(name, 0, 10.9D, mate) + { + } + } + + #endregion + + #region Inner Classes (deep inheritance chain) used to test type weighting + + private sealed class Fable + { + public Fable() + { + } + + public Fable(FableCharacter character) + { + } + + public Fable(NurseryRhymeCharacter character) + { + } + + public Fable(EnglishCharacter character) + { + } + } + + private class FableCharacter + { + } + + private class NurseryRhymeCharacter : FableCharacter + { + } + + private sealed class EnglishCharacter : NurseryRhymeCharacter + { + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ChildObjectDefinitionTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ChildObjectDefinitionTests.cs new file mode 100644 index 00000000..37926969 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ChildObjectDefinitionTests.cs @@ -0,0 +1,73 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the ChildObjectDefinition class. + /// + /// Rick Evans + /// $Id: ChildObjectDefinitionTests.cs,v 1.3 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class ChildObjectDefinitionTests + { + [Test] + [ExpectedException(typeof(ObjectDefinitionValidationException))] + public void ChokesIfParentNamePropertyIsNullAtValidationTime() + { + new ChildObjectDefinition(null, null).Validate(); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionValidationException))] + public void ChokesIfParentNamePropertyIsEmptyAtValidationTime() + { + new ChildObjectDefinition(String.Empty, null).Validate(); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionValidationException))] + public void ChokesIfParentNamePropertyIsJustWhitespaceAtValidationTime() + { + new ChildObjectDefinition(" ", null).Validate(); + } + + [Test] + public void Ctor_ParentNameOnly() + { + const string expectedParentName = "foo"; + ChildObjectDefinition def = new ChildObjectDefinition(expectedParentName); + Assert.AreEqual(expectedParentName, def.ParentName, + "ParentName property not initialized correctly by ctor."); + Assert.IsNotNull(def.PropertyValues, + "PropertyValues must be init'd to a non-null collection if not explicitly supplied."); + Assert.AreEqual(0, def.PropertyValues.PropertyValues.Length, + "PropertyValues must be init'd to an empty collection if not explicitly supplied."); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/DefaultObjectDefinitionFactoryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/DefaultObjectDefinitionFactoryTests.cs new file mode 100644 index 00000000..55b12610 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/DefaultObjectDefinitionFactoryTests.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the DefaultObjectDefinitionFactory class. + /// + /// Rick Evans + /// $Id: DefaultObjectDefinitionFactoryTests.cs,v 1.3 2007/05/28 19:14:18 markpollack Exp $ + [TestFixture] + public sealed class DefaultObjectDefinitionFactoryTests + { + [Test] + public void CreateRootDefinition() + { + DefaultObjectDefinitionFactory factory = new DefaultObjectDefinitionFactory(); + IConfigurableObjectDefinition definition + = factory.CreateObjectDefinition( + typeof (TestObject).FullName, null, AppDomain.CurrentDomain); + Assert.IsNotNull(definition, "CreateObjectDefinition with no parent is returning null (it must never do so)."); + Assert.AreEqual(typeof (TestObject), definition.ObjectType); + Assert.AreEqual(0, definition.PropertyValues.PropertyValues.Length, + "Must not have any property values as none were passed in."); + Assert.AreEqual(0, definition.ConstructorArgumentValues.ArgumentCount, + "Must not have any ctor args as none were passed in."); + } + + [Test] + public void CreateChildDefinition() + { + DefaultObjectDefinitionFactory factory = new DefaultObjectDefinitionFactory(); + IConfigurableObjectDefinition definition + = factory.CreateObjectDefinition( + typeof (TestObject).FullName, "Aimee Mann", AppDomain.CurrentDomain); + Assert.IsNotNull(definition, "CreateObjectDefinition with no parent is returning null (it must never do so)."); + Assert.AreEqual(typeof (TestObject), definition.ObjectType); + Assert.AreEqual(0, definition.PropertyValues.PropertyValues.Length, + "Must not have any property values as none were passed in."); + Assert.AreEqual(0, definition.ConstructorArgumentValues.ArgumentCount, + "Must not have any ctor args as none were passed in."); + } + + [Test] + public void DoesNotResolveTypeNameToFullTypeInstanceIfAppDomainIsNull() + { + DefaultObjectDefinitionFactory factory = new DefaultObjectDefinitionFactory(); + IConfigurableObjectDefinition definition + = factory.CreateObjectDefinition( + typeof (TestObject).FullName, null, null); + Assert.IsNotNull(definition, "CreateObjectDefinition with no parent is returning null (it must never do so)."); + Assert.AreEqual(typeof (TestObject).FullName, definition.ObjectTypeName); + Assert.AreEqual(0, definition.PropertyValues.PropertyValues.Length, + "Must not have any property values as none were passed in."); + Assert.AreEqual(0, definition.ConstructorArgumentValues.ArgumentCount, + "Must not have any ctor args as none were passed in."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/DelegatingMethodReplacerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/DelegatingMethodReplacerTests.cs new file mode 100644 index 00000000..ad8fbd55 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/DelegatingMethodReplacerTests.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the DelegatingMethodReplacer class. + /// + /// Rick Evans + /// $Id: DelegatingMethodReplacerTests.cs,v 1.3 2007/07/30 17:52:33 markpollack Exp $ + [TestFixture] + public sealed class DelegatingMethodReplacerTests + { + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullDefinition() + { + IDynamicMock mock = new DynamicMock(typeof (IObjectFactory)); + new DelegatingMethodReplacer(null, (IObjectFactory) mock.Object); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullFactory() + { + IDynamicMock mock = new DynamicMock(typeof(IConfigurableObjectDefinition)); + new DelegatingMethodReplacer((IConfigurableObjectDefinition)mock.Object, null); + } + + [Test] + public void SunnyDayPath() + { + IDynamicMock mockFactory = new DynamicMock(typeof (IObjectFactory)); + IDynamicMock mockDefinition = new DynamicMock(typeof (IConfigurableObjectDefinition)); + IDynamicMock mockReplacer = new DynamicMock(typeof (IMethodReplacer)); + + const string ReplacerObjectName = "replacer"; + mockFactory.ExpectAndReturn("GetObject", mockReplacer.Object, ReplacerObjectName); + + ReplacedMethodOverride ovr = new ReplacedMethodOverride("SunnyDayPath", ReplacerObjectName); + MethodOverrides overrides = new MethodOverrides(); + overrides.Add(ovr); + mockDefinition.ExpectAndReturn("MethodOverrides", overrides); + + mockReplacer.ExpectAndReturn("Implement", null); + DelegatingMethodReplacer replacer = new DelegatingMethodReplacer((IConfigurableObjectDefinition)mockDefinition.Object, (IObjectFactory)mockFactory.Object); + MethodInfo method = (MethodInfo) MethodBase.GetCurrentMethod(); + replacer.Implement(this, method, new object[] {}); + + mockFactory.Verify(); + mockDefinition.Verify(); + mockReplacer.Verify(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/LookupMethodOverrideTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/LookupMethodOverrideTests.cs new file mode 100644 index 00000000..4381e99e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/LookupMethodOverrideTests.cs @@ -0,0 +1,117 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the LookupMethodOverride class. + /// + /// Rick Evans + /// $Id: LookupMethodOverrideTests.cs,v 1.2 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class LookupMethodOverrideTests + { + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullMethodName() + { + new LookupMethodOverride(null, null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithEmptyMethodName() + { + new LookupMethodOverride(string.Empty, null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithWhitespacedMethodName() + { + new LookupMethodOverride(" ", null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullMethodReplacerObjectName() + { + new LookupMethodOverride("foo", null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithEmptyMethodReplacerObjectName() + { + new LookupMethodOverride("foo", string.Empty); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithWhitespacedMethodReplacerObjectName() + { + new LookupMethodOverride("foo", " "); + } + + [Test] + public void Matches_TotallyDifferentMethodName() + { + LookupMethodOverride methodOverride = new LookupMethodOverride("Bingo", "foo"); + Assert.IsFalse(methodOverride.Matches(typeof (Feeder).GetMethod("GetGrub"))); + } + + [Test] + public void Matches_MatchingMethodName() + { + LookupMethodOverride methodOverride = new LookupMethodOverride("GetGrub", "foo"); + Assert.IsTrue(methodOverride.Matches(typeof (Feeder).GetMethod("GetGrub"))); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void MatchesWithNullMethod() + { + LookupMethodOverride methodOverride = new LookupMethodOverride("Execute", "foo"); + methodOverride.Matches(null); + } + + [Test] + public void ToStringWorks() + { + LookupMethodOverride methodOverride = new LookupMethodOverride("GetGrub", "foo"); + Assert.AreEqual(typeof (LookupMethodOverride).Name + " for method 'GetGrub'; will return object 'foo'.", methodOverride.ToString()); + } + + private sealed class Feeder + { + public object GetGrub() + { + return null; + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/LookupMethodReplacerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/LookupMethodReplacerTests.cs new file mode 100644 index 00000000..7f66a77e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/LookupMethodReplacerTests.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the LookupMethodReplacer class. + /// + /// Rick Evans + /// $Id: LookupMethodReplacerTests.cs,v 1.3 2007/07/30 17:52:33 markpollack Exp $ + [TestFixture] + public sealed class LookupMethodReplacerTests + { + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullDefinition() + { + IDynamicMock mock = new DynamicMock(typeof (IObjectFactory)); + new LookupMethodReplacer(null, (IObjectFactory) mock.Object); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullFactory() + { + IDynamicMock mock = new DynamicMock(typeof(IConfigurableObjectDefinition)); + new LookupMethodReplacer((IConfigurableObjectDefinition)mock.Object, null); + } + + [Test] + public void SunnyDayPath() + { + IDynamicMock mockFactory = new DynamicMock(typeof (IObjectFactory)); + IDynamicMock mockDefinition = new DynamicMock(typeof (IConfigurableObjectDefinition)); + + object expectedLookup = new object(); + const string LookupObjectName = "foo"; + mockFactory.ExpectAndReturn("GetObject", expectedLookup, LookupObjectName); + + LookupMethodOverride ovr = new LookupMethodOverride("SunnyDayPath", LookupObjectName); + MethodOverrides overrides = new MethodOverrides(); + overrides.Add(ovr); + mockDefinition.ExpectAndReturn("MethodOverrides", overrides); + + LookupMethodReplacer replacer = new LookupMethodReplacer((IConfigurableObjectDefinition)mockDefinition.Object, (IObjectFactory)mockFactory.Object); + + MethodInfo method = (MethodInfo) MethodBase.GetCurrentMethod(); + + object lookup = replacer.Implement(this, method, new object[] {}); + Assert.AreSame(expectedLookup, lookup); + + mockFactory.Verify(); + mockDefinition.Verify(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ObjectDefinitionBuilderTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ObjectDefinitionBuilderTests.cs new file mode 100644 index 00000000..5ff11cc5 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ObjectDefinitionBuilderTests.cs @@ -0,0 +1,203 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; +using Spring.Context.Support; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// This class contains tests for ObjectDefinitionBuilder + /// + /// Mark Pollack + /// $Id: ObjectDefinitionBuilderTests.cs,v 1.4 2008/01/28 23:03:05 markpollack Exp $ + [TestFixture] + public class ObjectDefinitionBuilderTests + { + private IObjectDefinitionFactory odf; + + [SetUp] + public void CreateObjectDefinitionBuilder() + { + odf = new DefaultObjectDefinitionFactory(); + } + + + [Test] + public void ObjectName() + { + ObjectDefinitionBuilder odb = + ObjectDefinitionBuilder.RootObjectDefinition(odf, typeof(TestObject).FullName); + RootObjectDefinition rod = odb.ObjectDefinition as RootObjectDefinition; + Assert.IsNotNull(rod); + Assert.IsFalse(rod.HasObjectType); + Assert.AreEqual(typeof(TestObject).FullName, rod.ObjectTypeName); + } + + [Test] + public void ObjectNameWithFactoryMethod() + { + ObjectDefinitionBuilder odb = + ObjectDefinitionBuilder.RootObjectDefinition(odf, typeof(TestObject).FullName, "Create"); + RootObjectDefinition rod = odb.ObjectDefinition as RootObjectDefinition; + Assert.IsNotNull(rod); + Assert.IsFalse(rod.HasObjectType); + Assert.AreEqual(typeof(TestObject).FullName, rod.ObjectTypeName); + Assert.AreEqual("Create", rod.FactoryMethodName); + } + + [Test] + public void ObjectNameChild() + { + ObjectDefinitionBuilder odb = ObjectDefinitionBuilder.ChildObjectDefinition(odf, typeof(TestObject).FullName); + ChildObjectDefinition rod = odb.ObjectDefinition as ChildObjectDefinition; + Assert.IsNotNull(rod); + Assert.IsFalse(rod.HasObjectType); + Assert.AreEqual(typeof(TestObject).FullName, rod.ParentName); + } + + + [Test] + public void ObjectType() + { + ObjectDefinitionBuilder odb = ObjectDefinitionBuilder.RootObjectDefinition(odf, typeof (TestObject)); + RootObjectDefinition rod = odb.ObjectDefinition as RootObjectDefinition; + Assert.IsNotNull(rod); + Assert.IsTrue(rod.HasObjectType); + Assert.AreEqual(typeof (TestObject), rod.ObjectType); + } + + [Test] + public void ObjectTypeWithFactoryMethod() + { + ObjectDefinitionBuilder odb = + ObjectDefinitionBuilder.RootObjectDefinition(odf, typeof (TestObject), "Create"); + RootObjectDefinition rod = odb.ObjectDefinition as RootObjectDefinition; + Assert.IsNotNull(rod); + Assert.IsTrue(rod.HasObjectType); + Assert.AreEqual(typeof (TestObject), rod.ObjectType); + Assert.AreEqual("Create", rod.FactoryMethodName); + } + + [Test] + public void ObjectTypeWithSimpleProperty() + { + string[] dependsOn = new string[] {"A", "B", "C"}; + ObjectDefinitionBuilder odb = ObjectDefinitionBuilder.RootObjectDefinition(odf, typeof (TestObject)); + + odb.SetSingleton(false).AddPropertyReference("Age", "15"); + foreach (string dep in dependsOn) + { + odb.AddDependsOn(dep); + } + + RootObjectDefinition rod = odb.ObjectDefinition as RootObjectDefinition; + Assert.IsNotNull(rod); + Assert.IsFalse(rod.IsSingleton); + Assert.AreEqual(typeof (TestObject), rod.ObjectType); + Assert.AreEqual(dependsOn, rod.DependsOn, "DependsOn not as expected"); + Assert.IsTrue(rod.PropertyValues.Contains("Age")); + } + + [Test] + public void Simple() + { + GenericApplicationContext ctx = new GenericApplicationContext(); + IObjectDefinitionFactory objectDefinitionFactory = new DefaultObjectDefinitionFactory(); + + ObjectDefinitionBuilder builder = ObjectDefinitionBuilder.RootObjectDefinition(objectDefinitionFactory, + typeof (TestObject)); + + builder.AddPropertyValue("Age", 22) + .AddPropertyValue("Name", "Joe") + .AddPropertyReference("Spouse", "Spouse") + .SetSingleton(false); + + ctx.RegisterObjectDefinition("TestObject", builder.ObjectDefinition); + + builder = ObjectDefinitionBuilder.RootObjectDefinition(objectDefinitionFactory, typeof(TestObject)); + + IList friends = new ArrayList(); + friends.Add(new TestObject("Dan", 34)); + friends.Add(new TestObject("Mary", 33)); + builder.AddPropertyValue("Friends", friends) + .AddConstructorArg("Susan") + .AddConstructorArg(23) + .SetSingleton(false); + + ctx.RegisterObjectDefinition("Spouse", builder.ObjectDefinition); + + + + + TestObject to = ctx.GetObject("TestObject") as TestObject; + + Assert.IsNotNull(to); + + Assert.AreEqual("Joe", to.Name); + Assert.AreEqual(22, to.Age); + Assert.AreEqual(2,to.Spouse.Friends.Count); + Assert.AreEqual(23, to.Spouse.Age); + + /* + AbstractApplicationContext ctx = ContextRegistry.GetContext() as AbstractApplicationContext; + + + + //XmlObjectFactory objectFactory = ctx.ObjectFactory as XmlObjectFactory; + + IObjectFactory objectFactory = ctx.ObjectFactory; + + //DefaultListableObjectFactory + if (objectFactory != null) + { + Console.WriteLine("hi"); + } + //objectFactory.RegisterObjectDefinition("TestObject", builder.ObjectDefinition); + */ + + + + + } + + public void SimpleReader() + { + GenericApplicationContext applicationContext = new GenericApplicationContext(); + + + IObjectDefinitionReader objectDefinitionReader = new XmlObjectDefinitionReader(applicationContext); + + objectDefinitionReader.LoadObjectDefinitions("assembly://foo"); + + applicationContext.Refresh(); + + + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ObjectDefinitionReaderUtilsTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ObjectDefinitionReaderUtilsTests.cs new file mode 100644 index 00000000..94de5f8e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ObjectDefinitionReaderUtilsTests.cs @@ -0,0 +1,148 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the ObjectDefinitionReaderUtils class. + /// + /// Rick Evans + /// $Id: ObjectDefinitionReaderUtilsTests.cs,v 1.3 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class ObjectDefinitionReaderUtilsTests + { + [Test] + public void RegisterObjectDefinitionSunnyDay() + { + IDynamicMock mockRegistry = new DynamicMock(typeof (IObjectDefinitionRegistry)); + mockRegistry.Expect("RegisterObjectDefinition"); + + IDynamicMock mockDefinition = new DynamicMock(typeof (IObjectDefinition)); + IObjectDefinition definition = (IObjectDefinition) mockDefinition.Object; + + IObjectDefinitionRegistry registry = (IObjectDefinitionRegistry) mockRegistry.Object; + ObjectDefinitionHolder holder = new ObjectDefinitionHolder(definition, "foo"); + + ObjectDefinitionReaderUtils.RegisterObjectDefinition(holder, registry); + mockRegistry.Verify(); + mockDefinition.Verify(); + } + + [Test] + public void RegisterObjectDefinitionSunnyDayWithAliases() + { + IDynamicMock mockRegistry = new DynamicMock(typeof (IObjectDefinitionRegistry)); + mockRegistry.Expect("RegisterObjectDefinition"); + mockRegistry.Expect("RegisterAlias"); + mockRegistry.Expect("RegisterAlias"); + + IDynamicMock mockDefinition = new DynamicMock(typeof (IObjectDefinition)); + IObjectDefinition definition = (IObjectDefinition) mockDefinition.Object; + + IObjectDefinitionRegistry registry = (IObjectDefinitionRegistry) mockRegistry.Object; + ObjectDefinitionHolder holder + = new ObjectDefinitionHolder(definition, "foo", new string[] {"bar", "baz"}); + + ObjectDefinitionReaderUtils.RegisterObjectDefinition(holder, registry); + mockRegistry.Verify(); + mockDefinition.Verify(); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterObjectDefinitionWithNullDefinition() + { + IDynamicMock mockRegistry = new DynamicMock(typeof (IObjectDefinitionRegistry)); + IObjectDefinitionRegistry registry = (IObjectDefinitionRegistry) mockRegistry.Object; + ObjectDefinitionReaderUtils.RegisterObjectDefinition(null, registry); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterObjectDefinitionWithNullRegistry() + { + IDynamicMock mockDefinition = new DynamicMock(typeof (IObjectDefinition)); + IObjectDefinition definition = (IObjectDefinition) mockDefinition.Object; + ObjectDefinitionHolder holder = new ObjectDefinitionHolder(definition, "foo"); + ObjectDefinitionReaderUtils.RegisterObjectDefinition(holder, null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterObjectDefinitionWithAllArgumentsNull() + { + ObjectDefinitionReaderUtils.RegisterObjectDefinition(null, null); + } + + [Test] + public void RegisterObjectDefinitionWithDuplicateAlias() + { + IDynamicMock mockRegistry = new DynamicMock(typeof (IObjectDefinitionRegistry)); + mockRegistry.Expect("RegisterObjectDefinition"); + + // we assume that some other object defition has already been associated with this alias... + mockRegistry.ExpectAndThrow("RegisterAlias", new ObjectDefinitionStoreException()); + IDynamicMock mockDefinition = new DynamicMock(typeof (IObjectDefinition)); + IObjectDefinition definition = (IObjectDefinition) mockDefinition.Object; + + IObjectDefinitionRegistry registry = (IObjectDefinitionRegistry) mockRegistry.Object; + ObjectDefinitionHolder holder + = new ObjectDefinitionHolder(definition, "foo", new string[] { "bing" }); + + try + { + ObjectDefinitionReaderUtils.RegisterObjectDefinition(holder, registry); + Assert.Fail("Must have thrown an ObjectDefinitionStoreException store by this point."); + } + catch (ObjectDefinitionStoreException) + { + // expected... + } + mockRegistry.Verify(); + mockDefinition.Verify(); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void GenerateObjectNameWithNullDefinition() + { + IDynamicMock mockRegistry = new DynamicMock(typeof (IObjectDefinitionRegistry)); + IObjectDefinitionRegistry registry = (IObjectDefinitionRegistry) mockRegistry.Object; + ObjectDefinitionReaderUtils.GenerateObjectName(null, registry); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void GenerateObjectNameWithNullRegistry() + { + IDynamicMock mockDefinition = new DynamicMock(typeof (IConfigurableObjectDefinition)); + ObjectDefinitionReaderUtils.GenerateObjectName((IConfigurableObjectDefinition) mockDefinition.Object, null); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ReplacedMethodOverrideTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ReplacedMethodOverrideTests.cs new file mode 100644 index 00000000..5445634d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/ReplacedMethodOverrideTests.cs @@ -0,0 +1,192 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the ReplacedMethodOverride class. + /// + /// Rick Evans + /// $Id: ReplacedMethodOverrideTests.cs,v 1.2 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class ReplacedMethodOverrideTests + { + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullMethodName() + { + new ReplacedMethodOverride(null, null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithEmptyMethodName() + { + new ReplacedMethodOverride(string.Empty, null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithWhitespacedMethodName() + { + new ReplacedMethodOverride(" ", null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNullMethodReplacerObjectName() + { + new ReplacedMethodOverride("foo", null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithEmptyMethodReplacerObjectName() + { + new ReplacedMethodOverride("foo", string.Empty); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithWhitespacedMethodReplacerObjectName() + { + new ReplacedMethodOverride("foo", " "); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddNullTypeIdentifier() + { + new ReplacedMethodOverride("foo", "foo").AddTypeIdentifier(null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddEmptyTypeIdentifier() + { + new ReplacedMethodOverride("foo", "foo").AddTypeIdentifier(string.Empty); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddWhitespacedTypeIdentifier() + { + new ReplacedMethodOverride("foo", "foo").AddTypeIdentifier("\n "); + } + + [Test] + public void Matches_TotallyDifferentMethodName() + { + ReplacedMethodOverride methodOverride = new ReplacedMethodOverride("Execute", "replacer"); + Assert.IsFalse(methodOverride.Matches(typeof (Executor).GetMethod("Administer"))); + } + + [Test] + public void Matches_MatchingMethodNameNoOverload() + { + ReplacedMethodOverride methodOverride = new ReplacedMethodOverride("Administer", "replacer"); + methodOverride.IsOverloaded = false; + Assert.IsTrue(methodOverride.Matches(typeof (Executor).GetMethod("Administer"))); + } + + [Test] + public void Matches_MatchingMethodNameWithOverload() + { + ReplacedMethodOverride methodOverride = new ReplacedMethodOverride("Execute", "replacer"); + Assert.IsTrue(methodOverride.Matches(typeof (Executor).GetMethod("Execute", new Type[] {}))); + } + + [Test] + public void Matches_MatchingMethodNameWithOverloadAndTypeIdentifiers() + { + ReplacedMethodOverride methodOverride = new ReplacedMethodOverride("Execute", "replacer"); + methodOverride.AddTypeIdentifier(typeof (object).FullName); + Assert.IsTrue(methodOverride.Matches(typeof (Executor).GetMethod("Execute", new Type[] {typeof (object)}))); + } + + [Test] + public void Matches_MatchingMethodNameWithOverloadAndBadTypeIdentifiers() + { + ReplacedMethodOverride methodOverride = new ReplacedMethodOverride("Execute", "replacer"); + methodOverride.AddTypeIdentifier(GetType().FullName); + Assert.IsFalse(methodOverride.Matches(typeof (Executor).GetMethod("Execute", new Type[] {typeof (object)}))); + } + + [Test] + public void Matches_RequiresAllTypeIdentifiers() + { + ReplacedMethodOverride methodOverride = new ReplacedMethodOverride("Execute", "replacer"); + methodOverride.AddTypeIdentifier(typeof (object).FullName); + Assert.IsFalse(methodOverride.Matches(typeof (Executor).GetMethod("Execute", + new Type[] {typeof (object), typeof (object)}))); + } + + [Test] + public void Matches_AllTypeIdentifiers() + { + ReplacedMethodOverride methodOverride = new ReplacedMethodOverride("Execute", "replacer"); + methodOverride.AddTypeIdentifier(typeof (object).FullName); + methodOverride.AddTypeIdentifier(typeof (object).FullName); + Assert.IsTrue(methodOverride.Matches(typeof (Executor).GetMethod("Execute", + new Type[] {typeof (object), typeof (object)}))); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void MatchesWithNullMethod() + { + ReplacedMethodOverride methodOverride = new ReplacedMethodOverride("Execute", "replacer"); + methodOverride.Matches(null); + } + + [Test] + public void ToStringWorks() + { + ReplacedMethodOverride methodOverride = new ReplacedMethodOverride("Execute", "replacer"); + Assert.AreEqual(typeof(ReplacedMethodOverride).Name + " for method 'Execute'; will call object 'replacer'.", methodOverride.ToString()); + } + + private sealed class Executor + { + public void Administer() + { + } + + public void Execute() + { + } + + public void Execute(object sender) + { + } + + public void Execute(object sender, object context) + { + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/RootObjectDefinitionTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/RootObjectDefinitionTests.cs new file mode 100644 index 00000000..dbb60b08 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/RootObjectDefinitionTests.cs @@ -0,0 +1,127 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Objects.Support; +using Spring.Objects.Factory.Config; + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the RootObjectDefinition class. + /// + /// Rick Evans + /// $Id: RootObjectDefinitionTests.cs,v 1.6 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class RootObjectDefinitionTests + { + [Test] + [ExpectedException (typeof (ApplicationException))] + public void InstantiationWithClassName () + { + RootObjectDefinition def + = new RootObjectDefinition (typeof (TestObject).AssemblyQualifiedName, new ConstructorArgumentValues (), new MutablePropertyValues ()); + Assert.IsFalse (def.HasObjectType); + Assert.IsNull (def.ObjectType); // must bail... + } + + [Test] + public void InstantiationFromOther () { + RootObjectDefinition other + = new RootObjectDefinition (typeof (TestObject), AutoWiringMode.Constructor); + other.IsSingleton = false; + other.InitMethodName = "Umberto Eco"; + other.DestroyMethodName = "Pedulismus"; + other.ConstructorArgumentValues = new ConstructorArgumentValues (); + other.DependencyCheck = DependencyCheckingMode.Objects; + other.DependsOn = new string [] {"Jimmy", "Joyce"}; + other.EventHandlerValues = new EventValues (); + other.IsAbstract = true; + other.IsLazyInit = true; + other.FactoryMethodName = "IfINeedYouIllJustUseYourSimpleName"; + other.FactoryObjectName = "PerspirationShop"; + other.ResourceDescription = "Ohhh"; + other.PropertyValues = new MutablePropertyValues (); + other.PropertyValues.Add ("Age", 100); + InstanceEventHandlerValue handler = + new InstanceEventHandlerValue (new TestObject (), "Ping"); + handler.EventName = "Bing"; + other.EventHandlerValues.AddHandler (handler); + other.ConstructorArgumentValues.AddGenericArgumentValue ("Wulf", "Sternhammer"); + + RootObjectDefinition def = new RootObjectDefinition (other); + + // ... + Assert.AreEqual (typeof (TestObject), def.ObjectType); + Assert.AreEqual (AutoWiringMode.Constructor, def.AutowireMode); + Assert.IsFalse (def.IsSingleton); + Assert.AreEqual ("Umberto Eco", def.InitMethodName); + Assert.AreEqual ("Pedulismus", def.DestroyMethodName); + Assert.AreEqual (DependencyCheckingMode.Objects, def.DependencyCheck); + Assert.IsTrue (def.IsAbstract); + Assert.IsTrue (def.IsLazyInit); + Assert.AreEqual ("IfINeedYouIllJustUseYourSimpleName", def.FactoryMethodName); + Assert.AreEqual ("PerspirationShop", def.FactoryObjectName); + Assert.AreEqual ("Ohhh", def.ResourceDescription); + Assert.IsTrue (def.HasConstructorArgumentValues); + Assert.AreEqual (other.ConstructorArgumentValues.ArgumentCount, def.ConstructorArgumentValues.ArgumentCount); + Assert.AreEqual (other.PropertyValues.PropertyValues.Length, def.PropertyValues.PropertyValues.Length); + Assert.AreEqual (other.EventHandlerValues.Events.Count, def.EventHandlerValues.Events.Count); + } + + [Test] + [ExpectedException (typeof (ObjectDefinitionValidationException))] + public void ValidateLazyAndPrototypeCausesBail () + { + RootObjectDefinition def + = new RootObjectDefinition (typeof (TestObject), AutoWiringMode.No); + def.IsLazyInit = true; + def.IsSingleton = false; + def.Validate (); + } + + [Test] + public void InstantiationWithNullCtorArgsAndNullPropsDoesNotResultInNullPropertyValues() + { + RootObjectDefinition def + = new RootObjectDefinition (typeof (TestObject), null, null); + Assert.IsNotNull(def.ConstructorArgumentValues, + "Must never be null, but rather just empty."); + Assert.AreEqual(0, def.ConstructorArgumentValues.ArgumentCount, + "Must be empty if null was passed to the ctor."); + Assert.IsNotNull(def.PropertyValues, + "Must never be null, but rather just empty."); + Assert.AreEqual(0, def.PropertyValues.PropertyValues.Length, + "Must be empty if null was passed to the ctor."); + } + } + + internal class NoPublicCtors + { + protected NoPublicCtors () {} + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Support/SimpleInstantiationStrategyTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/SimpleInstantiationStrategyTests.cs new file mode 100644 index 00000000..bd6f9210 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Support/SimpleInstantiationStrategyTests.cs @@ -0,0 +1,158 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the SimpleInstantiationStrategy class. + /// + /// Rick Evans + /// $Id: SimpleInstantiationStrategyTests.cs,v 1.5 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class SimpleInstantiationStrategyTests + { + #region SetUp + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp() + { + _singletonDefinition = new RootObjectDefinition(typeof (TestObject), AutoWiringMode.No); + _singletonDefinitionWithFactory = new RootObjectDefinition(_singletonDefinition); + _singletonDefinitionWithFactory.FactoryMethodName = "GetObject"; + _singletonDefinitionWithFactory.FactoryObjectName = "TestObjectFactoryDefinition"; + _testObjectFactory = new RootObjectDefinition(typeof (TestObjectFactory), AutoWiringMode.No); + DefaultListableObjectFactory myFactory = new DefaultListableObjectFactory(); + myFactory.RegisterObjectDefinition("SingletonObjectDefinition", SingletonDefinition); + myFactory.RegisterObjectDefinition("SingletonDefinitionWithFactory", SingletonDefinitionWithFactory); + myFactory.RegisterObjectDefinition("TestObjectFactoryDefinition", TestObjectFactoryDefinition); + _factory = myFactory; + } + + #endregion + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiateWithNulls() + { + SimpleInstantiationStrategy strategy = new SimpleInstantiationStrategy(); + strategy.Instantiate(null, null, null); + } + + [Test] + public void InstantiateWithNullObjectName() + { + SimpleInstantiationStrategy strategy = new SimpleInstantiationStrategy(); + object obj = strategy.Instantiate(SingletonDefinition, null, Factory); + Assert.IsNotNull(obj); + Assert.IsTrue(obj is ITestObject); + ITestObject actual = (ITestObject) obj; + Assert.IsNull(actual.Name); + Assert.AreEqual(0, actual.Age); + } + + [Test] + public void InstantiateWithExplicitCtor() + { + SimpleInstantiationStrategy strategy = new SimpleInstantiationStrategy(); + object obj = strategy.Instantiate( + SingletonDefinition, null, Factory, + SingletonDefinition.ObjectType.GetConstructor( + new Type[] {typeof (string), typeof (int)}), + new object[] {"Rick", 19}); + Assert.IsNotNull(obj); + Assert.IsTrue(obj is ITestObject); + ITestObject actual = (ITestObject) obj; + Assert.AreEqual("Rick", actual.Name); + Assert.AreEqual(19, actual.Age); + } + + [Test] + public void InstantiateWithFactoryMethod() + { + SimpleInstantiationStrategy strategy = new SimpleInstantiationStrategy(); + object obj = strategy.Instantiate(SingletonDefinitionWithFactory, + string.Empty, Factory, typeof (TestObjectFactory).GetMethod("GetObject"), null); + Assert.IsNotNull(obj); + Assert.IsTrue(obj is ITestObject); + ITestObject actual = (ITestObject) obj; + Assert.AreEqual(TestObjectFactory.TheName, actual.Name); + Assert.AreEqual(TestObjectFactory.TheAge, actual.Age); + } + + [Test] + public void InstantiateWithDefinitionThatDoesNotHaveAResolvedObjectClass() + { + RootObjectDefinition def = new RootObjectDefinition(); + def.ObjectTypeName = typeof(TestObject).FullName; + + SimpleInstantiationStrategy strategy = new SimpleInstantiationStrategy(); + object foo = strategy.Instantiate(def, "foo", Factory); + Assert.IsNotNull(foo); + Assert.AreEqual(typeof(TestObject), foo.GetType()); + } + + private RootObjectDefinition SingletonDefinition + { + get { return _singletonDefinition; } + } + + private RootObjectDefinition SingletonDefinitionWithFactory + { + get { return _singletonDefinitionWithFactory; } + } + + private RootObjectDefinition TestObjectFactoryDefinition + { + get { return _testObjectFactory; } + } + + private IObjectFactory Factory + { + get { return _factory; } + } + + private RootObjectDefinition _singletonDefinition; + private RootObjectDefinition _testObjectFactory; + private RootObjectDefinition _singletonDefinitionWithFactory; + private IObjectFactory _factory; + } + + public class TestObjectFactory + { + public const string TheName = "Old Goriot"; + public const int TheAge = 78; + + public virtual object GetObject() + { + return new TestObject(TheName, TheAge); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/TestResource.txt b/test/Spring/Spring.Core.Tests/Objects/Factory/TestResource.txt new file mode 100644 index 00000000..ea08c0d2 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/TestResource.txt @@ -0,0 +1 @@ +Spring.Objects.Factory.TestResource.txt diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/UnsupportedObjectDefinitionImplementation.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/UnsupportedObjectDefinitionImplementation.cs new file mode 100644 index 00000000..a8c16967 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/UnsupportedObjectDefinitionImplementation.cs @@ -0,0 +1,121 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +namespace Spring.Objects.Factory +{ + internal class UnsupportedObjectDefinitionImplementation : IObjectDefinition + { + public MutablePropertyValues PropertyValues + { + get { throw new NotImplementedException(); } + } + + public ConstructorArgumentValues ConstructorArgumentValues + { + get { throw new NotImplementedException(); } + } + + public MethodOverrides MethodOverrides + { + get { throw new NotImplementedException(); } + } + + public EventValues EventHandlerValues + { + get { throw new NotImplementedException(); } + } + + public string ResourceDescription + { + get { return "UnsupportedObjectDefinitionImplementation_Resource"; } + } + + public bool IsTemplate + { + get { throw new NotImplementedException(); } + } + + public bool IsAbstract + { + get { throw new NotImplementedException(); } + } + + public bool IsSingleton + { + get { throw new NotImplementedException(); } + } + + public bool IsLazyInit + { + get { throw new NotImplementedException(); } + } + + public Type ObjectType + { + get { throw new NotImplementedException(); } + } + + public string ObjectTypeName + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public AutoWiringMode AutowireMode + { + get { throw new NotImplementedException(); } + } + + public DependencyCheckingMode DependencyCheck + { + get { throw new NotImplementedException(); } + } + + public string[] DependsOn + { + get { throw new NotImplementedException(); } + } + + public string InitMethodName + { + get { throw new NotImplementedException(); } + } + + public string DestroyMethodName + { + get { throw new NotImplementedException(); } + } + + public string FactoryMethodName + { + get { throw new NotImplementedException(); } + } + + public string FactoryObjectName + { + get { throw new NotImplementedException(); } + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/ConstructorDependenciesObject.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/ConstructorDependenciesObject.cs new file mode 100644 index 00000000..dd83ad09 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/ConstructorDependenciesObject.cs @@ -0,0 +1,110 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Simple object used to check constructor dependency checking. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + public class ConstructorDependenciesObject + { + #region Constructor (s) / Destructor + + public ConstructorDependenciesObject(int age) + { + this.age = age; + } + + public ConstructorDependenciesObject(string name) + { + this.name = name; + } + + public ConstructorDependenciesObject(ITestObject spouse1) + { + this.spouse1 = spouse1; + } + + public ConstructorDependenciesObject(ITestObject spouse1, ITestObject spouse2) + { + this.spouse1 = spouse1; + this.spouse2 = spouse2; + } + + public ConstructorDependenciesObject(ITestObject spouse1, ITestObject spouse2, IndexedTestObject other) + { + this.spouse1 = spouse1; + this.spouse2 = spouse2; + this.other = other; + } + + #endregion + + #region Properties + + public int Age + { + get { return age; } + + set { this.age = value; } + } + + public string Name + { + get { return name; } + set { this.name = value; } + } + + public ITestObject Spouse1 + { + get { return spouse1; } + } + + public ITestObject Spouse2 + { + get { return spouse2; } + } + + public IndexedTestObject Other + { + get { return other; } + } + + #endregion + + #region Fields + + private int age; + private string name; + private ITestObject spouse1; + private ITestObject spouse2; + private IndexedTestObject other; + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DependenciesObject.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DependenciesObject.cs new file mode 100644 index 00000000..020eab94 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DependenciesObject.cs @@ -0,0 +1,77 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Simple object used to check dependency checking. + /// + /// Rod Johnson + /// Rick Evans (.NET) + public class DependenciesObject + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the DependenciesObject class. + /// + public DependenciesObject() + { + } + + #endregion + + #region Properties + + public int Age + { + get { return age; } + set { this.age = value; } + } + + public string Name + { + get { return name; } + set { this.name = value; } + } + + public ITestObject Spouse + { + get { return spouse; } + set { this.spouse = value; } + } + + #endregion + + #region Fields + + private int age; + private string name; + private ITestObject spouse; + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DerivedConstructorDependenciesObject.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DerivedConstructorDependenciesObject.cs new file mode 100644 index 00000000..cece1863 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DerivedConstructorDependenciesObject.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Simple object used to check derived constructor dependency checking. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + public class DerivedConstructorDependenciesObject : ConstructorDependenciesObject + { + + public DerivedConstructorDependenciesObject( + ITestObject spouse1, ITestObject spouse2, IndexedTestObject other) + : base(spouse1, spouse2, other) + { + } + + public DerivedConstructorDependenciesObject( + ITestObject spouse1, object spouse2, IndexedTestObject other) + : base(spouse1, null, other) + { + } + + public DerivedConstructorDependenciesObject( + ITestObject spouse1, ITestObject spouse2, IndexedTestObject other, int age, int otherAge) + : base(spouse1, spouse2, other) + { + } + + public DerivedConstructorDependenciesObject( + ITestObject spouse1, ITestObject spouse2, IndexedTestObject other, int age, string name) + : base(spouse1, spouse2, other) + { + Age = age; + Name = name; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DoubleBooleanConstructorObject.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DoubleBooleanConstructorObject.cs new file mode 100644 index 00000000..d55a76f4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DoubleBooleanConstructorObject.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Summary description for DoubleBooleanConstructorObject. + /// + public class DoubleBooleanConstructorObject + { + private bool boolean1; + private bool boolean2; + + public bool Boolean2 + { + get { return this.boolean2; } + set { this.boolean2 = value; } + } + + public bool Boolean1 + { + get { return this.boolean1; } + set { this.boolean1 = value; } + } + + public DoubleBooleanConstructorObject(bool b1, bool b2) + { + this.boolean1 = b1; + this.boolean2 = b2; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DummyBo.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DummyBo.cs new file mode 100644 index 00000000..b67d48c4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DummyBo.cs @@ -0,0 +1,51 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory.Xml { + + /// + /// Summary description for DummyBo. + /// + /// Rod Johnson + /// Rick Evans (.NET) + public class DummyBo : IDummyBo + { + + /// + /// Creates a new instance of the DummyBo class. + /// + public DummyBo (DummyDao dao) { + this.dao = dao; + } + + public void DoSomething () + { + + } + + internal DummyDao dao; + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DummyDao.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DummyDao.cs new file mode 100644 index 00000000..ed1d9291 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DummyDao.cs @@ -0,0 +1,44 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; + +#endregion + +namespace Spring.Objects.Factory.Xml { + + /// + /// Summary description for DummyDao. + /// + /// Rod Johnson + /// Rick Evans (.NET) + public class DummyDao + { + + /// + /// Creates a new instance of the DummyDao class. + /// + public DummyDao () { + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DummyReferencer.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DummyReferencer.cs new file mode 100644 index 00000000..79689b90 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/DummyReferencer.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using Spring.Objects; + +#endregion + +namespace Spring.Objects.Factory.Xml { + + /// + /// Summary description for DummyReferencer. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + public class DummyReferencer + { + + public ITestObject TestObject1 + { + get + { + return testObject1; + } + set + { + this.testObject1 = value; + } + } + + public ITestObject TestObject2 + { + get + { + return testObject2; + } + set + { + this.testObject2 = value; + } + } + + private ITestObject testObject1; + private ITestObject testObject2; + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/EventWiringTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/EventWiringTests.cs new file mode 100644 index 00000000..f9939e49 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/EventWiringTests.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Unit tests that test the event wiring features of the + /// XmlObjectFactory. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + /// Choy Rim (.NET) + [TestFixture] + public class EventWiringTests + { + [Ignore("SPRNET-21")] + [Test] + public virtual void EventWiringInstanceSinkToPrototypeSource() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("event-wiring-prototypes.xml", GetType())); + TestEventHandler instanceHandler = factory["instanceSink"] as TestEventHandler; + ITestObject source = factory["source"] as ITestObject; + // raise the event... handlers should be notified at this point (obviously) + source.OnClick(); + Assert.IsTrue(instanceHandler.EventWasHandled, + "The instance handler did not get notified when the instance event was raised (and was probably not wired up in the first place)."); + } + + [Test] + public virtual void SingletonSourcePrototypeSink() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("event-wiring.xml", GetType())); + ITestObject source = factory["source"] as ITestObject; + TestEventHandler prototypeEventHandler = factory["prototypeEventListener"] as TestEventHandler; + // raise the event... handlers should be notified at this point (obviously) + source.OnClick(); + Assert.IsTrue(prototypeEventHandler.EventWasHandled, + "The prototype instance handler did not get notified when the instance event was raised (and was probably not wired up in the first place)."); + } + + [Test] + public virtual void InstanceEventWiring() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("event-wiring.xml", GetType())); + ITestObject source = factory["source"] as ITestObject; + TestEventHandler instanceHandler = factory["instanceEventListener"] as TestEventHandler; + // raise the event... handlers should be notified at this point (obviously) + source.OnClick(); + Assert.IsTrue(instanceHandler.EventWasHandled, + "The instance handler did not get notified when the instance event was raised (and was probably not wired up in the first place)."); + } + + [Test] + public virtual void StaticEventWiring() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("event-wiring.xml", GetType())); + TestEventHandler staticHandler = factory["staticEventListener"] as TestEventHandler; + // raise the event... handlers should be notified at this point (obviously) + TestObject.OnStaticClick(); + Assert.IsTrue(staticHandler.EventWasHandled, + "The instance handler did not get notified when the static event was raised (and was probably not wired up in the first place)."); + } + + [TestFixtureSetUp] + public void FixtureSetUp() + { + // enable (null appender) logging, to ensure that the logging code is exercised + // XmlConfigurator.Configure (); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/FactoryMethods.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/FactoryMethods.cs new file mode 100644 index 00000000..578a9e44 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/FactoryMethods.cs @@ -0,0 +1,114 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Test class for Spring.NET's ability to create objects using static + /// factory methods, rather than constructors. + /// + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: FactoryMethods.cs,v 1.3 2006/04/09 07:24:51 markpollack Exp $ + public class FactoryMethods + { + #region Constructor (s) / Destructor + + /// + /// Creates a new instance of the + /// class. + /// + /// + ///

    + /// Constructor is private: not for use outside this class, even by the IoC container. + ///

    + ///
    + private FactoryMethods(TestObject obj, string name, int number) + { + _object = obj; + _name = name; + _number = number; + } + + #endregion + + #region Properties + + public TestObject Object + { + get { return _object; } + } + + public string Name + { + get { return _name; } + set { _name = value; } + } + + public string Value + { + get { return _value; } + set { _value = value; } + } + + public int Number + { + get { return _number; } + } + + #endregion + + #region Methods + + public static FactoryMethods DefaultInstance() + { + TestObject obj = new TestObject(); + obj.Name = "defaultInstance"; + return new FactoryMethods(obj, "default", 0); + } + + public static FactoryMethods NewInstance(TestObject obj) + { + return new FactoryMethods(obj, "default", 0); + } + + public static FactoryMethods NewInstance(TestObject obj, string name, int num) + { + return new FactoryMethods(obj, name, num); + } + + #endregion + + #region Fields + + private int _number; + private TestObject _object; + private string _name = "default"; + private string _value; + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/IDummyBo.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/IDummyBo.cs new file mode 100644 index 00000000..ba77f0e0 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/IDummyBo.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory.Xml { + + /// + /// A dummy business object interface :D + /// + /// Rod Johnson + /// Rick Evans (.NET) + public interface IDummyBo + { + + void DoSomething (); + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/InstanceFactory.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/InstanceFactory.cs new file mode 100644 index 00000000..78e996e9 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/InstanceFactory.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Test class for Spring.NET's ability to create objects using static + /// factory methods, rather than constructors. + /// + /// Rod Johnson + /// Rick Evans + /// $Id: InstanceFactory.cs,v 1.3 2007/06/14 19:52:07 markpollack Exp $ + public class InstanceFactory + { + private string _factoryObjectProperty; + + public string FactoryObjectProperty + { + get + { + return _factoryObjectProperty; + + } + set + { + _factoryObjectProperty = value; + } + } + + public FactoryMethods DefaultInstance() + { + TestObject to = new TestObject(); + to.Name = FactoryObjectProperty; + return FactoryMethods.NewInstance (to); + } + + /// + /// Note that overloaded methods are supported. + /// + public FactoryMethods NewInstance (TestObject to) + { + return FactoryMethods.NewInstance (to); + } + + public FactoryMethods NewInstance (TestObject to, int num, string name) + { + return FactoryMethods.NewInstance (to, name, num); + } + + public FactoryMethods CreateInstance() + { + return FactoryMethods.NewInstance(new TestObject(), "DefaultCtor", 0); + } + + public FactoryMethods CreateInstance(DataRow dr) + { + return FactoryMethods.NewInstance(new TestObject(), "DataRowCtor", 0); + } + + public FactoryMethods CreateInstance(IDataRecord dr) + { + return FactoryMethods.NewInstance(new TestObject(), "DataRecordCtor", 0); + } + + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/LocaleTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/LocaleTests.cs new file mode 100644 index 00000000..98848346 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/LocaleTests.cs @@ -0,0 +1,72 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using System.Threading; +using NUnit.Framework; +using Spring.Core.IO; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// This class contains tests for setting properties that do not parse easily in other locales, such + /// as comma delimited strings. + /// + /// Mark Pollack + /// $Id: LocaleTests.cs,v 1.1 2007/08/25 16:08:36 markpollack Exp $ + [TestFixture] + public class LocaleTests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void LocaleTest() + { + CultureInfo oldCulture = Thread.CurrentThread.CurrentCulture; + try + { + Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); + + IResource resource = new ReadOnlyXmlTestResource("locale.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + TestObject to = xof.GetObject("jenny") as TestObject; + Assert.IsNotNull(to); + DateTime d = new DateTime(2007, 10, 30); + Assert.AreEqual(d, to.Date); + Assert.AreEqual(30, to.Size.Height); + Assert.AreEqual(30, to.Size.Width); + } + finally + { + Thread.CurrentThread.CurrentCulture = oldCulture; + } + } + + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/MixedCollectionObject.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/MixedCollectionObject.cs new file mode 100644 index 00000000..1e90e76b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/MixedCollectionObject.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Objects.Factory.Xml { + + /// + /// Summary description for MixedCollectionObject. + /// + /// Rod Johnson + /// Rick Evans (.NET) + public class MixedCollectionObject + { + + public MixedCollectionObject () + { + ++nrOfInstances; + } + + public ICollection Jumble + { + get + { + return jumble; + } + set + { + this.jumble = value; + } + } + + protected internal static int nrOfInstances = 0; + + public static void ResetStaticState () + { + nrOfInstances = 0; + } + + private ICollection jumble; + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NullAppender.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NullAppender.cs new file mode 100644 index 00000000..8325bd75 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/NullAppender.cs @@ -0,0 +1,45 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// A log4Net appender that does nothing (by intention). + /// + /// Rick Evans + /// $Id: NullAppender.cs,v 1.4 2006/11/13 07:04:59 markpollack Exp $ + + public sealed class NullAppender //: AppenderSkeleton + { + /* + protected override void Append(log4net.Core.LoggingEvent loggingEvent) + { + // an explicit no-op + } + */ + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/ObjectFactorySectionHandlerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/ObjectFactorySectionHandlerTests.cs new file mode 100644 index 00000000..065d1017 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/ObjectFactorySectionHandlerTests.cs @@ -0,0 +1,74 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Xml; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Test the usage of the ObjectFactorySectionHandler. + /// + [TestFixture] + public class ObjectFactorySectionHandlerTests + { + private XmlElement _xmlElement; + + [SetUp] + public void CreateXmlElement() + { + XmlDocument xmlDoc = new XmlDocument(); + string xmlData = ""; + xmlDoc.Load(new StringReader(xmlData)); + _xmlElement = xmlDoc.DocumentElement; + } + + /// + /// Test calling the section handler with null values. + /// + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void CreateFactoryUnSuccessful() + { + ObjectFactorySectionHandler objHandler = new ObjectFactorySectionHandler(); + objHandler.Create( null, null, null ); + } + + /// + /// Test calling the section handler with valid XML and make sure no + /// exception occurs and the factory has the right number of + /// objects. + /// + [Test] + public void CreateFactorySuccessful() + { + ObjectFactorySectionHandler objHandler = new ObjectFactorySectionHandler(); + IListableObjectFactory factory = (IListableObjectFactory)objHandler.Create( null, null, _xmlElement ); + Assert.AreEqual(1, factory.ObjectDefinitionCount); + + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/ReadOnlyXmlTestResource.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/ReadOnlyXmlTestResource.cs new file mode 100644 index 00000000..36e6d9b5 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/ReadOnlyXmlTestResource.cs @@ -0,0 +1,144 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Text; + +using Spring.Core.IO; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Centralised resource getter for loading all of those XML object + /// definition files used in the XML based object factory tests. + /// + /// Rick Evans (.NET) + /// Erich Eichinger (.NET) + public class ReadOnlyXmlTestResource : IResource + { + private readonly IResource underlyingResource; + private const string TestDataFolder = "."; + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The filename/resourcename (e.g. foo.txt) of the XML file containing the object + /// definitions. + /// + public ReadOnlyXmlTestResource (string fileName) + { + if (ConfigurableResourceLoader.HasProtocol(fileName)) + { + ConfigurableResourceLoader loader = new ConfigurableResourceLoader(); + underlyingResource = loader.GetResource(fileName); + } + underlyingResource = new FileSystemResource(fileName); + } + + /// + /// Creates a new instance of the + /// class. + /// + /// + /// The filename/resourcename (e.g. foo.txt) of the XML file containing the object + /// definitions. + /// + /// + /// The of the test class utilising said file. + /// + public ReadOnlyXmlTestResource (string fileName, Type associatedTestType) + : this (ReadOnlyXmlTestResource.GetFilePath (fileName, associatedTestType)) + { + } + + public IResource CreateRelative(string relativePath) + { + return this.underlyingResource.CreateRelative(relativePath); + } + + public bool IsOpen + { + get { return this.underlyingResource.IsOpen; } + } + + public Uri Uri + { + get { return this.underlyingResource.Uri; } + } + + public FileInfo File + { + get { return this.underlyingResource.File; } + } + + public string Description + { + get { return this.underlyingResource.Description; } + } + + public bool Exists + { + get { return this.underlyingResource.Exists; } + } + + public Stream InputStream + { + get { return this.underlyingResource.InputStream; } + } + + public static string GetFilePath (string fileName, Type associatedTestType) + { + if (associatedTestType == null) + { + return fileName; + } + + // check filesystem + StringBuilder path = new StringBuilder (TestDataFolder).Append (Path.DirectorySeparatorChar.ToString()); + path.Append (associatedTestType.Namespace.Replace (".", Path.DirectorySeparatorChar.ToString())); + path.Append (Path.DirectorySeparatorChar.ToString()).Append (fileName); + + FileInfo file = new FileInfo(path.ToString()); + if (file.Exists) + { + return path.ToString (); + } + else + { + // interpret as assembly resource + fileName = "assembly://" + associatedTestType.Assembly.FullName + "/" + associatedTestType.Namespace + + "/" + fileName; + return fileName; + } + } + + public override string ToString() + { + return Description; + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/SingleSimpleTypeConstructorObject.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/SingleSimpleTypeConstructorObject.cs new file mode 100644 index 00000000..3f32d95c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/SingleSimpleTypeConstructorObject.cs @@ -0,0 +1,86 @@ +using System; + +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Simple object used to test object creation via constuctors with + /// simple types. + /// + /// Jurgen Hoeller + /// Mark Pollack (.NET) + public class SingleSimpleTypeConstructorObject + { + private bool singleBoolean; + + private bool secondBoolean; + + private string testString; + + /// + /// Create an instance using the singleBoolean + /// + /// true or false + public SingleSimpleTypeConstructorObject(bool singleBoolean) + { + this.singleBoolean = singleBoolean; + } + + /// + /// Create an instance using the second boolean and a string + /// + /// test value + /// true or false + public SingleSimpleTypeConstructorObject(string testString, bool secondBoolean) + { + this.testString = testString; + this.secondBoolean = secondBoolean; + } + + + /// + /// Property TestString (string) + /// + public string TestString + { + get { return this.testString; } + } + + /// + /// Property SecondBoolean (bool) + /// + public bool SecondBoolean + { + get { return this.secondBoolean; } + } + + /// + /// Property SingleBoolean (bool) + /// + public bool SingleBoolean + { + get { return this.singleBoolean; } + } + + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/TestObjectCreator.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/TestObjectCreator.cs new file mode 100644 index 00000000..07048d08 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/TestObjectCreator.cs @@ -0,0 +1,53 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Test class for Spring's ability to create objects using static factory methods, rather + /// than constructors. + /// + /// Rod Johnson + /// Rick Evans (.NET) + /// $Id: TestObjectCreator.cs,v 1.3 2006/04/09 07:24:51 markpollack Exp $ + public class TestObjectCreator + { + public static TestObject CreateTestObject(string name, int age) + { + return new TestObject(name, age); + } + + public static TestObject CreateTestObject() + { + return new TestObject("Tristan", 2); + } + + public TestObject InstanceCreateTestObject() + { + return CreateTestObject(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlListableObjectFactoryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlListableObjectFactoryTests.cs new file mode 100644 index 00000000..15d4ebea --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlListableObjectFactoryTests.cs @@ -0,0 +1,170 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Unit tests for the XmlListableObjectFactory class. + /// + /// Juergen Hoeller + /// Rick Evans (.NET) + [TestFixture] + public class XmlListableObjectFactoryTests : AbstractListableObjectFactoryTests + { + #region Inner Class : AnonymousClassObjectPostProcessor + + private class AnonymousClassObjectPostProcessor : IObjectPostProcessor + { + public AnonymousClassObjectPostProcessor() + { + } + + public virtual object PostProcessBeforeInitialization( + object obj, string name) + { + if (obj is TestObject) + { + ((TestObject) obj).PostProcessed = true; + } + if (obj is DummyFactory) + { + ((DummyFactory) obj).PostProcessed = true; + } + return obj; + } + + public virtual object PostProcessAfterInitialization( + object obj, string objectName) + { + return obj; + } + } + + #endregion + + protected internal override IObjectFactory ObjectFactory + { + get { return factory; } + } + + private DefaultListableObjectFactory parent; + private XmlObjectFactory factory; + + #region Test SetUp + + [SetUp] + protected void SetUp() + { + parent = new DefaultListableObjectFactory(); + IDictionary m = new Hashtable(); + m["name"] = "Albert"; + parent.RegisterObjectDefinition("father", new RootObjectDefinition(typeof (TestObject), new MutablePropertyValues(m))); + m = new Hashtable(); + m["name"] = "Roderick"; + parent.RegisterObjectDefinition("rod", new RootObjectDefinition(typeof (TestObject), new MutablePropertyValues(m))); + + // for testing dynamic ctor arguments + parent.GetObject() call propagation + parent.RegisterObjectDefinition("namedfather", new RootObjectDefinition(typeof(TestObject), false)); + parent.RegisterObjectDefinition("typedfather", new RootObjectDefinition(typeof(TestObject), false)); + + // add unsupported IObjectDefinition implementation... + UnsupportedObjectDefinitionImplementation unsupportedDefinition = new UnsupportedObjectDefinitionImplementation(); + parent.RegisterObjectDefinition("unsupportedDefinition", unsupportedDefinition); + + this.factory = new XmlObjectFactory(new ReadOnlyXmlTestResource("test.xml", GetType()), parent); + + // TODO: should this be allowed? + //this.factory.RegisterObjectDefinition("typedfather", new RootObjectDefinition(typeof(object), false)); + this.factory.AddObjectPostProcessor(new AnonymousClassObjectPostProcessor()); + this.factory.AddObjectPostProcessor(new LifecycleObject.PostProcessor()); + + this.factory.PreInstantiateSingletons(); + } + + #endregion + + [Test] + public virtual void FactoryNesting() + { + ITestObject father = (ITestObject) ObjectFactory.GetObject("father"); + Assert.IsTrue(father != null, "Object from root context"); + + ITestObject rod = (ITestObject) ObjectFactory.GetObject("rod"); + Assert.IsTrue("Rod".Equals(rod.Name), "Object from child context"); + Assert.IsTrue(rod.Spouse == father, "Object has external reference"); + + rod = (ITestObject) parent.GetObject("rod"); + Assert.IsTrue("Roderick".Equals(rod.Name), "Object from root context"); + } + + [Test] + public virtual void FactoryReferences() + { + DummyReferencer dref = (DummyReferencer) ObjectFactory.GetObject("factoryReferencer"); + Assert.IsTrue(dref.TestObject1 == dref.TestObject2); + } + + [Test] + public virtual void PrototypeReferences() + { + // check that not broken by circular reference resolution mechanism + DummyReferencer ref1 = (DummyReferencer) ObjectFactory.GetObject("prototypeReferencer"); + Assert.IsTrue(ref1.TestObject1 != ref1.TestObject2, "Not referencing same Object twice"); + DummyReferencer ref2 = (DummyReferencer) ObjectFactory.GetObject("prototypeReferencer"); + Assert.IsTrue(ref1 != ref2, "Not the same referencer"); + Assert.IsTrue(ref2.TestObject1 != ref2.TestObject2, "Not referencing same Object twice"); + Assert.IsTrue(ref1.TestObject1 != ref2.TestObject1, "Not referencing same Object twice"); + Assert.IsTrue(ref1.TestObject2 != ref2.TestObject2, "Not referencing same Object twice"); + Assert.IsTrue(ref1.TestObject1 != ref2.TestObject2, "Not referencing same Object twice"); + } + + [Test] + public virtual void ObjectPostProcessor() + { + TestObject kerry = (TestObject) ObjectFactory.GetObject("kerry"); + TestObject kathy = (TestObject) ObjectFactory.GetObject("kathy"); + DummyFactory factory = (DummyFactory) ObjectFactory.GetObject("&singletonFactory"); + TestObject factoryCreated = (TestObject) ObjectFactory.GetObject("singletonFactory"); + Assert.IsTrue(kerry.PostProcessed); + Assert.IsTrue(kathy.PostProcessed); + Assert.IsTrue(factory.PostProcessed); + Assert.IsTrue(factoryCreated.PostProcessed); + } + + /// + /// Test the number of singletons in test.xml + /// + [Test] + public virtual void CountSingletons() + { + Assert.AreEqual(10, factory.GetSingletonCount(), "Number of singletons incorrect"); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectCollectionTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectCollectionTests.cs new file mode 100644 index 00000000..81a7b807 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectCollectionTests.cs @@ -0,0 +1,767 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.IO; +using System.Text; +using Common.Logging; +using Common.Logging.Simple; +using NUnit.Framework; +using Spring.Collections; +using Spring.Core.IO; +using Spring.Expressions; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Tests for collections in XML object definitions. + /// + /// Rick Evans + /// $Id: XmlObjectCollectionTests.cs,v 1.9 2007/08/08 17:49:04 bbaia Exp $ + [TestFixture] + public sealed class XmlObjectCollectionTests + { + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp() + { + // enable (null appender) logging, to ensure that the logging code is exercised... + LogManager.Adapter = new NoOpLoggerFactoryAdapter(); + //XmlConfigurator.Configure(); + } + + [Test] + public void RefSubelementsBuildCollection() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + //Assert.IsTrue ("5 objects in reftypes, not " + xof.GetObjectDefinitionCount(), xof.GetObjectDefinitionCount() == 5); + TestObject jen = (TestObject) xof.GetObject("jenny"); + TestObject dave = (TestObject) xof.GetObject("david"); + TestObject rod = (TestObject) xof.GetObject("rod"); + + // Must be a list to support ordering + // Our object doesn't modify the collection: + // of course it could be a different copy in a real object + IList friends = (IList) rod.Friends; + Assert.IsTrue(friends.Count == 2); + Assert.IsTrue(friends[0] == jen, "First friend must be jen, not " + friends[0]); + Assert.IsTrue(friends[1] == dave); + // Should be ordered + } + + [Test] + public void RefSubelementsBuildCollectionWithPrototypes() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + + TestObject jen = (TestObject) xof.GetObject("pJenny"); + TestObject dave = (TestObject) xof.GetObject("pDavid"); + TestObject rod = (TestObject) xof.GetObject("pRod"); + IList friends = (IList) rod.Friends; + Assert.IsTrue(friends.Count == 2); + Assert.IsTrue(friends[0].ToString().Equals(jen.ToString()), "First friend must be jen, not " + friends[0]); + Assert.IsTrue(friends[0] != jen, "Jen not same instance"); + Assert.IsTrue(friends[1].ToString().Equals(dave.ToString())); + Assert.IsTrue(friends[1] != dave, "Dave not same instance"); + + TestObject rod2 = (TestObject) xof.GetObject("pRod"); + IList friends2 = (IList) rod2.Friends; + Assert.IsTrue(friends2.Count == 2); + Assert.IsTrue(friends2[0].ToString().Equals(jen.ToString()), "First friend must be jen, not " + friends2[0]); + Assert.IsTrue(friends2[0] != friends[0], "Jen not same instance"); + Assert.IsTrue(friends2[1].ToString().Equals(dave.ToString())); + Assert.IsTrue(friends2[1] != friends[1], "Dave not same instance"); + } + + [Test] + public void RefSubelementsBuildCollectionFromSingleElement() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + TestObject loner = (TestObject) xof.GetObject("loner"); + TestObject dave = (TestObject) xof.GetObject("david"); + Assert.IsTrue(loner.Friends.Count == 1); + bool contains = false; + foreach (object friend in loner.Friends) + { + if (friend == dave) + { + contains = true; + break; + } + } + Assert.IsTrue(contains); + } + + [Test] + public void BuildCollectionFromMixtureOfReferencesAndValues() + { + MixedCollectionObject.ResetStaticState(); + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + MixedCollectionObject jumble = (MixedCollectionObject) xof.GetObject("jumble"); + Assert.AreEqual(1, MixedCollectionObject.nrOfInstances); + xof.GetObject("david", typeof (TestObject)); + Assert.IsTrue(jumble.Jumble.Count == 4, "Expected 3 elements, not " + jumble.Jumble.Count); + IList l = (IList) jumble.Jumble; + Assert.IsTrue(l[0].Equals(xof.GetObject("david"))); + Assert.IsTrue(l[1].Equals("literal")); + Assert.IsTrue(l[2].Equals(xof.GetObject("jenny"))); + Assert.IsTrue(l[3].Equals("rod")); + } + + /// + /// Test to see if a type safe custom collection class can be set using normal + /// list syntax. + /// + [Test] + public void CustomListCollection() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + TestObject objectWithTypedFriends = (TestObject) xof.GetObject("objectWithTypedFriends"); + TestObjectList tol = objectWithTypedFriends.TypedFriends; + Assert.AreEqual(1, tol.Count); + Assert.IsTrue(tol[0].Equals(xof.GetObject("david"))); + } + + /// + /// Test to see if we can set values on a collection class that uses indexers + /// + [Test] + public void ObjectWithIndexerProperty() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + TestObject testObject = (TestObject) xof.GetObject("objectWithIndexer"); + Assert.IsNotNull(testObject); + Assert.AreEqual("my string value", testObject[0]); + + } + /// + /// Test that properties with name as well as id creating an alias up front. + /// + [Test] + public void AutoAliasing() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + IList objectNames = xof.GetObjectDefinitionNames(); + TestObject tb1 = (TestObject) xof.GetObject("aliased"); + TestObject alias1 = (TestObject) xof.GetObject("myalias"); + Assert.IsTrue(tb1 == alias1); + + IList tb1Aliases = xof.GetAliases("aliased"); + Assert.AreEqual(1, tb1Aliases.Count); + Assert.IsTrue(tb1Aliases.Contains("myalias")); + Assert.IsTrue(objectNames.Contains("aliased")); + Assert.IsFalse(objectNames.Contains("myalias")); + + TestObject tb2 = (TestObject) xof.GetObject("multiAliased"); + TestObject alias2 = (TestObject) xof.GetObject("alias1"); + TestObject alias3 = (TestObject) xof.GetObject("alias2"); + Assert.IsTrue(tb2 == alias2); + Assert.IsTrue(tb2 == alias3); + IList tb2Aliases = xof.GetAliases("multiAliased"); + Assert.AreEqual(2, tb2Aliases.Count); + Assert.IsTrue(tb2Aliases.Contains("alias1")); + Assert.IsTrue(tb2Aliases.Contains("alias2")); + Assert.IsTrue(objectNames.Contains("multiAliased")); + Assert.IsFalse(objectNames.Contains("alias1")); + Assert.IsFalse(objectNames.Contains("alias2")); + + TestObject tb3 = (TestObject) xof.GetObject("aliasWithoutId1"); + TestObject alias4 = (TestObject) xof.GetObject("aliasWithoutId2"); + TestObject alias5 = (TestObject) xof.GetObject("aliasWithoutId3"); + Assert.IsTrue(tb3 == alias4); + Assert.IsTrue(tb3 == alias5); + + IList tb3Aliases = xof.GetAliases("aliasWithoutId1"); + Assert.AreEqual(2, tb2Aliases.Count); + Assert.IsTrue(tb3Aliases.Contains("aliasWithoutId2")); + Assert.IsTrue(tb3Aliases.Contains("aliasWithoutId3")); + Assert.IsTrue(objectNames.Contains("aliasWithoutId1")); + Assert.IsFalse(objectNames.Contains("aliasWithoutId2")); + Assert.IsFalse(objectNames.Contains("aliasWithoutId3")); + + TestObject tb4 = (TestObject) xof.GetObject(typeof (TestObject).FullName); + Assert.AreEqual(null, tb4.Name); + } + + /// + /// Test that we can add elements to an IList that is exposed as a + /// read only property. Many classes in the BCL only expose getters to + /// modify the content of collections. + /// + [Test] + public void AddElementsToReadOnlyList() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + //Stream stream = new FileSystemResource("file:///w:/collections.xml").InputStream; + XmlObjectFactory xof = new XmlObjectFactory(resource); + + TestObject aleks = (TestObject) xof.GetObject("aleks"); + IList pets = (IList) aleks.Pets; + Assert.IsTrue(pets.Count == 1); + Assert.IsTrue(pets[0].Equals("Nadja")); + } + + /// + /// Test that we can add elements to an IDictionary that is exposed as a + /// read-only property. + /// + public void AddElementsToReadOnlyDictionary() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + + TestObject to = (TestObject) xof.GetObject("readOnlyDictionary"); + IDictionary table = to.PeriodicTable; + Assert.IsTrue(table.Count == 2); + Assert.AreEqual("1", table["hydrogen"], "Dictionary Value incorrect"); + Assert.AreEqual("12", table["carbon"], "Dictionary value incorrect"); + } + + /// + /// Test that we can add elements to an ISet that is exposed as a + /// read-only property + /// + public void AddElementsToReadOnlySet() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + + TestObject to = (TestObject) xof.GetObject("readOnlySet"); + ISet computers = to.Computers; + Assert.IsTrue(computers.Count == 2); + bool foundDell = false; + bool foundIbm = false; + foreach (string name in computers) + { + if (name.Equals("Dell")) + { + foundDell = true; + } + if (name.Equals("IBM T41")) + { + foundIbm = true; + } + } + Assert.IsTrue(foundDell, "Dell string not found in ISet"); + Assert.IsTrue(foundIbm, "IBM T41 not found in ISet"); + } + + [Test] + public void EmptyMap() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("emptyMap"); + Assert.IsTrue(hasMap.Map.Count == 0); + } + + [Test] + public void MapWithLiteralsOnly() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("literalMap"); + Assert.IsTrue(hasMap.Map.Count == 3); + Assert.IsTrue(hasMap.Map["foo"].Equals("bar")); + Assert.IsTrue(hasMap.Map["fi"].Equals("fum")); + Assert.IsTrue(hasMap.Map["fa"] == null); + } + + [Test] + public void MapWithLiteralsAndReferences() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("mixedMap"); + Assert.IsTrue(hasMap.Map.Count == 3); + Assert.IsTrue(hasMap.Map["foo"].Equals("bar")); + TestObject jenny = (TestObject) xof.GetObject("jenny"); + Assert.IsTrue(hasMap.Map["jenny"] == jenny); + Assert.IsTrue(hasMap.Map["david"].Equals("david")); + } + + [Test] + public void MapWithLiteralsAndPrototypeReferences() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + + TestObject jenny = (TestObject) xof.GetObject("pJenny"); + HasMap hasMap = (HasMap) xof.GetObject("pMixedMap"); + Assert.IsTrue(hasMap.Map.Count == 2); + Assert.IsTrue(hasMap.Map["foo"].Equals("bar")); + Assert.IsTrue(hasMap.Map["jenny"].ToString().Equals(jenny.ToString())); + Assert.IsTrue(hasMap.Map["jenny"] != jenny, "Not same instance"); + + HasMap hasMap2 = (HasMap) xof.GetObject("pMixedMap"); + Assert.IsTrue(hasMap2.Map.Count == 2); + Assert.IsTrue(hasMap2.Map["foo"].Equals("bar")); + Assert.IsTrue(hasMap2.Map["jenny"].ToString().Equals(jenny.ToString())); + Assert.IsTrue(hasMap2.Map["jenny"] != hasMap.Map["jenny"], "Not same instance"); + } + + [Test] + public void MapWithLiteralsReferencesAndList() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("mixedMapWithList"); + Assert.IsTrue(hasMap.Map.Count == 4); + Assert.IsTrue(hasMap.Map["foo"].Equals("bar")); + TestObject jenny = (TestObject) xof.GetObject("jenny"); + Assert.IsTrue(hasMap.Map["jenny"].Equals(jenny)); + + // Check list + IList l = (IList) hasMap.Map["list"]; + Assert.IsNotNull(l); + Assert.IsTrue(l.Count == 4); + Assert.IsTrue(l[0].Equals("zero")); + Assert.IsTrue(l[3] == null); + + // Check nested map in list + IDictionary m = (IDictionary) l[1]; + Assert.IsNotNull(m); + Assert.IsTrue(m.Count == 2); + Assert.IsTrue(m["fo"].Equals("bar")); + Assert.IsTrue(m["jen"].Equals(jenny), "Map element 'jenny' should be equal to jenny object, not " + m["jen"]); + + // Check nested list in list + l = (IList) l[2]; + Assert.IsNotNull(l); + Assert.IsTrue(l.Count == 2); + Assert.IsTrue(l[0].Equals(jenny)); + Assert.IsTrue(l[1].Equals("ba")); + + // Check nested map + m = (IDictionary) hasMap.Map["map"]; + Assert.IsNotNull(m); + Assert.IsTrue(m.Count == 2); + Assert.IsTrue(m["foo"].Equals("bar")); + Assert.IsTrue(m["jenny"].Equals(jenny), + "Map element 'jenny' should be equal to jenny object, not " + m["jenny"]); + } + + [Test] + public void EmptySet() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("emptySet"); + Assert.IsTrue(hasMap.Set.Count == 0); + } + + [Test] + public void PopulatedSet() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("set"); + Assert.IsTrue(hasMap.Set.Count == 3); + Assert.IsTrue(hasMap.Set.Contains("bar")); + TestObject jenny = (TestObject) xof.GetObject("jenny"); + Assert.IsTrue(hasMap.Set.Contains(jenny)); + Assert.IsTrue(hasMap.Set.Contains(null)); + } + + [Test] + public void EmptyProps() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("emptyProps"); + Assert.IsTrue(hasMap.Props.Count == 0); + } + + /// + /// Test that an empty string value can be placed in a name-value collection + /// + [Test] + public void EmptyValue() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("emptyValue"); + Assert.AreEqual("", hasMap.Props.Get("foo"), "Expected empty string"); + } + + [Test] + public void PopulatedProps() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("props"); + Assert.AreEqual(2, hasMap.Props.Count); + Assert.AreEqual("bar", hasMap.Props["foo"]); + Assert.AreEqual("TWO", hasMap.Props["2"]); + } + + [Test] + public void DelimitedProps() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap)xof.GetObject("delimitedProps"); + Assert.AreEqual(2, hasMap.Props.Count); + Assert.AreEqual(3, hasMap.Props.GetValues("foo").Length); + Assert.AreEqual(7, hasMap.Props.GetValues("days").Length); + Assert.AreEqual("wednesday", hasMap.Props.GetValues("days")[2]); + } + + [Test] + public void ObjectArray() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("objectArray"); + Assert.IsTrue(hasMap.ObjectArray.Length == 2); + Assert.IsTrue(hasMap.ObjectArray[0].Equals("one")); + Assert.IsTrue(hasMap.ObjectArray[1].Equals(xof.GetObject("jenny"))); + } + + [Test] + public void ClassArray() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + HasMap hasMap = (HasMap) xof.GetObject("classArray"); + Assert.IsTrue(hasMap.ClassArray.Length == 2); + Assert.IsTrue(hasMap.ClassArray[0].Equals(typeof (String))); + Assert.IsTrue(hasMap.ClassArray[1].Equals(typeof (Exception))); + } + + [Test] + public void GetDictionaryThatUsesEntryValueShortcut() + { + XmlObjectFactory xof = new XmlObjectFactory( + new ReadOnlyXmlTestResource("collections.xml", GetType())); + IDictionary map = (IDictionary) xof.GetObject("mapWithEntryValueShortcut"); + Assert.AreEqual(2, map.Count); + string v1 = (string) map["rob"]; + Assert.AreEqual("robert", v1); + string v2 = (string) map["jen"]; + Assert.AreEqual("jenny", v2); + } + + [Test] + public void GetDictionaryThatUsesStringKeysSpecifiedAsElements() + { + XmlObjectFactory xof = new XmlObjectFactory( + new ReadOnlyXmlTestResource("collections.xml", GetType())); + IDictionary map = (IDictionary) xof.GetObject("mapWithStringKeysSpecifiedAsElements"); + Assert.AreEqual(2, map.Count); + string v1 = (string) map["rick"]; + Assert.AreEqual("Rick Evans", v1); + string v2 = (string) map["uncleelvis"]; + Assert.AreEqual("Elvis Orten", v2); + } + + [Test] + [ExpectedException(typeof (ObjectDefinitionStoreException))] + public void GetDictionaryThatDoesntSpecifyAnyKeyForAnEntry() + { + new StreamHelperDecorator(new StreamHelperCallback(_GetDictionaryThatDoesntSpecifyAnyKeyForAnEntry)).Run(); + } + + private void _GetDictionaryThatDoesntSpecifyAnyKeyForAnEntry(out Stream stream) + { + const string xml = + @" + + + + + + Rick Evans + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + new XmlObjectFactory(new InputStreamResource(stream, "")); + Assert.Fail("Must have failed to parse element with no key."); + } + + [Test] + public void GetDictionaryWithKeyRefAttributeShortcuts() + { + XmlObjectFactory xof = new XmlObjectFactory( + new ReadOnlyXmlTestResource("collections.xml", GetType())); + IDictionary map = (IDictionary) xof.GetObject("mapWithKeyRefAttributeShortcuts"); + Assert.AreEqual(2, map.Count); + string v1 = (string) map[new TestObject("Rick Evans", 30)]; + Assert.AreEqual("Rick Evans", v1); + string v2 = (string) map[new TestObject("Uncle Elvis", 47)]; + Assert.AreEqual("Elvis Orten", v2); + } + + [Test] + public void GetDictionaryWithValueRefAttributeShortcuts() + { + XmlObjectFactory xof = new XmlObjectFactory( + new ReadOnlyXmlTestResource("collections.xml", GetType())); + IDictionary map = (IDictionary) xof.GetObject("mapWithValueRefAttributeShortcuts"); + Assert.AreEqual(2, map.Count); + foreach(string key in map.Keys) + { + object obj = map[key]; + Assert.IsNotNull(obj); + Assert.AreEqual(typeof(TestObject), obj.GetType(), "Wrong value assigned to value of dictionary."); + TestObject tob = (TestObject) obj; + if(key == "rick") + { + Assert.AreEqual("Rick Evans", tob.Name, "Wrong object value assigned to the 'rick' key."); + } + else if(key == "uncleelvis") + { + Assert.AreEqual("Uncle Elvis", tob.Name, "Wrong object value assigned to the 'uncleelvis' key."); + } + } + } + + [Test] + public void CollectionFactoryDefaults() + { + ListFactoryObject listFactory = new ListFactoryObject(); + listFactory.SourceList = new ArrayList(); + listFactory.AfterPropertiesSet(); + Assert.IsTrue(listFactory.GetObject() is ArrayList); + + SetFactoryObject setFactory = new SetFactoryObject(); + setFactory.SourceSet = new HybridSet(); + setFactory.AfterPropertiesSet(); + Assert.IsTrue(setFactory.GetObject() is HybridSet); + + DictionaryFactoryObject dictionaryFactory = new DictionaryFactoryObject(); + dictionaryFactory.SourceDictionary = new Hashtable(); + dictionaryFactory.AfterPropertiesSet(); + Assert.IsTrue(dictionaryFactory.GetObject() is Hashtable); + } + + [Test] + public void ListFactory() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + IList list = (IList) xof.GetObject("listFactory"); + Assert.IsTrue(list is LinkedList); + Assert.IsTrue(list.Count == 2); + Assert.AreEqual("bar", list[0]); + Assert.AreEqual("jenny", list[1]); + } + + [Test] + public void PrototypeListFactory() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + IList list = (IList) xof.GetObject("pListFactory"); + Assert.IsTrue(list is LinkedList); + Assert.IsTrue(list.Count == 2); + Assert.AreEqual("bar", list[0]); + Assert.AreEqual("jenny", list[1]); + } + + [Test] + public void SetFactory() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + Set mySet = (Set) xof.GetObject("setFactory"); + Assert.IsTrue(mySet is HybridSet); + Assert.IsTrue(mySet.Count == 2); + Assert.IsTrue(mySet.Contains("bar")); + Assert.IsTrue(mySet.Contains("jenny")); + } + + [Test] + public void PrototypeSetFactory() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + Set mySet = (Set) xof.GetObject("pSetFactory"); + Assert.IsTrue(mySet is HybridSet); + Assert.IsTrue(mySet.Count == 2); + Assert.IsTrue(mySet.Contains("bar")); + Assert.IsTrue(mySet.Contains("jenny")); + } + + [Test] + public void DictionaryFactory() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + IDictionary map = (IDictionary) xof.GetObject("mapFactory"); + Assert.IsTrue(map is Hashtable); + Assert.IsTrue(map.Count == 2); + Assert.AreEqual("bar", map["foo"]); + Assert.AreEqual("jenny", map["jen"]); + } + + [Test] + public void PrototypeDictionaryFactory() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + IDictionary map = (IDictionary) xof.GetObject("pMapFactory"); + Assert.IsTrue(map is Hashtable); + Assert.IsTrue(map.Count == 2); + Assert.AreEqual("bar", map["foo"]); + Assert.AreEqual("jenny", map["jen"]); + } + + [Test] + public void GetDictionaryThatUsesNonStringKeys() + { + XmlObjectFactory xof = new XmlObjectFactory( + new ReadOnlyXmlTestResource("collections.xml", GetType())); + IDictionary map = (IDictionary) xof.GetObject("mapWithNonStringKeys"); + Assert.AreEqual(2, map.Count); + string v1 = (string) map[new TestObject("Rick Evans", 30)]; + Assert.AreEqual("Rick Evans", v1); + string v2 = (string) map[new TestObject("Uncle Elvis", 47)]; + Assert.AreEqual("Elvis Orten", v2); + } + + [Test] + public void TypedNonGenericList() + { + XmlObjectFactory xof = new XmlObjectFactory(new ReadOnlyXmlTestResource("collections.xml", GetType())); + NonGenericExpressionHolder obj = (NonGenericExpressionHolder) xof.GetObject("nonGenericExpressionHolder"); + Assert.AreEqual(2, obj[0].GetValue()); + Assert.AreEqual(8, obj[1].GetValue()); + Assert.AreEqual("ALEKSANDAR SEOVIC", obj[2].GetValue()); + Assert.IsTrue((bool) obj[3].GetValue()); + } + + [Test] + public void TypedNonGenericDictionary() + { + XmlObjectFactory xof = new XmlObjectFactory(new ReadOnlyXmlTestResource("collections.xml", GetType())); + NonGenericExpressionHolder obj = (NonGenericExpressionHolder)xof.GetObject("nonGenericExpressionHolder"); + Assert.AreEqual(2, obj["0"].GetValue()); + Assert.AreEqual(8, obj["1"].GetValue()); + Assert.AreEqual("ALEKSANDAR SEOVIC", obj["2"].GetValue()); + Assert.IsTrue((bool)obj["3"].GetValue()); + } + +#if NET_2_0 + [Test] + public void TypedGenericList() + { + XmlObjectFactory xof = new XmlObjectFactory(new ReadOnlyXmlTestResource("collections.xml", GetType())); + GenericExpressionHolder obj = (GenericExpressionHolder)xof.GetObject("genericExpressionHolder"); + Assert.AreEqual(2, obj[0].GetValue()); + Assert.AreEqual(8, obj[1].GetValue()); + Assert.AreEqual("ALEKSANDAR SEOVIC", obj[2].GetValue()); + Assert.IsTrue((bool)obj[3].GetValue()); + } + + [Test] + public void TypedGenericDictionary() + { + XmlObjectFactory xof = new XmlObjectFactory(new ReadOnlyXmlTestResource("collections.xml", GetType())); + GenericExpressionHolder obj = (GenericExpressionHolder)xof.GetObject("genericExpressionHolder"); + Assert.AreEqual(2, obj["0"].GetValue()); + Assert.AreEqual(8, obj["1"].GetValue()); + Assert.AreEqual("ALEKSANDAR SEOVIC", obj["2"].GetValue()); + Assert.IsTrue((bool)obj["3"].GetValue()); + } +#endif + } + + #region Helper classes + + public class NonGenericExpressionHolder + { + private IList expressionsList; + private IDictionary expressionsDictionary; + + public IList ExpressionsList + { + set { this.expressionsList = value; } + } + + public IDictionary ExpressionsDictionary + { + set { this.expressionsDictionary = value; } + } + + public IExpression this[int index] + { + get { return (IExpression)this.expressionsList[index]; } + } + + public IExpression this[string key] + { + get { return (IExpression)this.expressionsDictionary[key]; } + } + } + +#if NET_2_0 + public class GenericExpressionHolder + { + private System.Collections.Generic.IList expressionsList; + private System.Collections.Generic.IDictionary expressionsDictionary; + + public System.Collections.Generic.IList ExpressionsList + { + set { this.expressionsList = value; } + } + + public System.Collections.Generic.IDictionary ExpressionsDictionary + { + set { this.expressionsDictionary = value; } + } + + public IExpression this[int index] + { + get { return this.expressionsList[index]; } + } + + public IExpression this[string key] + { + get { return this.expressionsDictionary[key]; } + } + } +#else + public class GenericExpressionHolder : NonGenericExpressionHolder + {} +#endif + + #endregion + +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectDefinitionReaderTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectDefinitionReaderTests.cs new file mode 100644 index 00000000..967e59de --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectDefinitionReaderTests.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Unit tests for the XmlObjectDefinitionReader class. + /// + /// Rick Evans (.NET) + [TestFixture] + public class XmlObjectDefinitionReaderTests + { + [Test] + public void Instantiation() + { + XmlObjectDefinitionReader reader + = new XmlObjectDefinitionReader( + new DefaultListableObjectFactory()); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void LoadObjectDefinitionsWithNullResource() + { + XmlObjectDefinitionReader reader + = new XmlObjectDefinitionReader( + new DefaultListableObjectFactory()); + reader.LoadObjectDefinitions((string)null); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void LoadObjectDefinitionsWithNonExistentResource() + { + XmlObjectDefinitionReader reader + = new XmlObjectDefinitionReader( + new DefaultListableObjectFactory()); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("/dev/null")); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectFactoryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectFactoryTests.cs new file mode 100644 index 00000000..d240ee38 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectFactoryTests.cs @@ -0,0 +1,1999 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using System.Globalization; +using System.IO; +using System.Text; +using System.Threading; +using System.Web.Services; +using Common.Logging; +using Common.Logging.Simple; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Context.Support; +using Spring.Core.IO; +using Spring.Core.TypeResolution; +using Spring.Expressions; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Util; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Unit tests for the XmlObjectFactory class. + /// + /// + ///

    + /// There are actually a lot of integration tests in this class too. + ///

    + ///
    + /// Juergen Hoeller + /// Rick Evans (.NET) + /// $Id: XmlObjectFactoryTests.cs,v 1.80 2008/05/02 20:08:23 markpollack Exp $ + [TestFixture] + public sealed class XmlObjectFactoryTests + { + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp() + { + // enable (null appender) logging, to ensure that the logging code is exercised... + //XmlConfigurator.Configure(); + LogManager.Adapter = new NoOpLoggerFactoryAdapter(); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void ReplacedMethodWithNoReplacerObjectNameSpecified() + { + new StreamHelperDecorator(new StreamHelperCallback(_ReplacedMethodWithNoReplacerObjectNameSpecified)).Run(); + } + + private void _ReplacedMethodWithNoReplacerObjectNameSpecified(out Stream stream) + { + const string xml = + @" + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void ReplacedMethodWithNoMethodNameSpecified() + { + new StreamHelperDecorator(new StreamHelperCallback(_ReplacedMethodWithNoMethodNameSpecified)).Run(); + } + + private void _ReplacedMethodWithNoMethodNameSpecified(out Stream stream) + { + const string xml = + @" + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void ValidatesReplacedMethodCorrectly() + { + new StreamHelperDecorator(new StreamHelperCallback(_ValidatesReplacedMethodCorrectly)).Run(); + } + + private void _ValidatesReplacedMethodCorrectly(out Stream stream) + { + const string xml = + @" + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + } + + [Test] + public void LookupMethodIsParsedAndOperatesCorrectly_SunnyDay() + { + new StreamHelperDecorator(new StreamHelperCallback(_LookupMethodIsParsedAndOperatesCorrectly_SunnyDay)).Run(); + } + + private void _LookupMethodIsParsedAndOperatesCorrectly_SunnyDay(out Stream stream) + { + const string xml = + @" + + + + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + TestObjectFactory tof = (TestObjectFactory) factory["factory"]; + object to = tof.GetObject(); + Assert.IsNotNull(to); + Assert.AreEqual(typeof(TestObject), to.GetType()); + TestObject target = (TestObject) to; + Assert.AreEqual("Fiona Apple", target.Name); + Assert.AreEqual(47, target.Age); + // pull the prototype out again... + TestObject target2 = (TestObject) tof.GetObject(); + Assert.IsFalse(ReferenceEquals(target, target2)); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void ValidatesLookupMethodCorrectly() + { + new StreamHelperDecorator(new StreamHelperCallback(_ValidatesLookupMethodCorrectly)).Run(); + } + + private void _ValidatesLookupMethodCorrectly(out Stream stream) + { + const string xml = + @" + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void LookupMethodWithNoMethodNameSpecified() + { + new StreamHelperDecorator(new StreamHelperCallback(_LookupMethodWithNoMethodNameSpecified)).Run(); + } + + private void _LookupMethodWithNoMethodNameSpecified(out Stream stream) + { + const string xml = + @" + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + factory.GetObject("foo"); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void LookupMethodWithNoTargetObjectNameSpecified() + { + new StreamHelperDecorator(new StreamHelperCallback(_LookupMethodWithNoTargetObjectNameSpecified)).Run(); + } + + private void _LookupMethodWithNoTargetObjectNameSpecified(out Stream stream) + { + const string xml = + @" + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + factory.GetObject("foo"); + } + + [Test] + public void NonChildObjectDefinitionWithoutATypeReturnsObjectDefinition() + { + new StreamHelperDecorator(new StreamHelperCallback(_NonChildObjectDefinitionWithoutATypeReturnsObjectDefinition)).Run(); + } + + private void _NonChildObjectDefinitionWithoutATypeReturnsObjectDefinition(out Stream stream) + { + const string xml = + @" + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + object obj = factory.GetObject("Applicationimpl"); + Assert.IsTrue(obj is IObjectDefinition); + } + + [Test] + public void RegisterAliasViaAliasElement() + { + new StreamHelperDecorator(new StreamHelperCallback(_RegisterAliasViaAliasElement)).Run(); + } + + private void _RegisterAliasViaAliasElement(out Stream stream) + { + const string xml = + @" + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + // pull object out of the factory using the alias name... + object foo = factory.GetObject("fooAlias"); + Assert.IsNotNull(foo, "Alias was obviously not registered otherwise we would not have got a null object back."); + } + + [Test] + public void RegisterAliasViaAliasElementOrderingIsUnimportant() + { + new StreamHelperDecorator(new StreamHelperCallback(_RegisterAliasViaAliasElementOrderingIsUnimportant)).Run(); + } + + private void _RegisterAliasViaAliasElementOrderingIsUnimportant(out Stream stream) + { + const string xml = + @" + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + // pull object out of the factory using the alias name... + object foo = factory.GetObject("fooAlias"); + Assert.IsNotNull(foo, "Alias was obviously not registered otherwise we would not have got a null object back."); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void IfTypeAttributeIsPresentItMustNotBeTheEmptyStringValue() + { + new StreamHelperDecorator(new StreamHelperCallback(_IfTypeAttributeIsPresentItMustNotBeTheEmptyStringValue)).Run(); + } + + private void _IfTypeAttributeIsPresentItMustNotBeTheEmptyStringValue(out Stream stream) + { + const string xml = + @" + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + factory.GetObject("Applicationimpl"); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void IfTypeAttributeIsPresentItMustNotBeAnOnlyWhitespaceStringValue() + { + new StreamHelperDecorator(new StreamHelperCallback(_IfTypeAttributeIsPresentItMustNotBeAnOnlyWhitespaceStringValue)).Run(); + } + + private void _IfTypeAttributeIsPresentItMustNotBeAnOnlyWhitespaceStringValue(out Stream stream) + { + const string xml = + @" + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + factory.GetObject("Applicationimpl"); + } + + [Test] + public void SimpleStringObject() + { + new StreamHelperDecorator(new StreamHelperCallback(_SimpleStringObject)).Run(); + } + + private void _SimpleStringObject(out Stream stream) + { + const string xml = + @" + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + string myString = (string) factory["myString"]; + Assert.AreEqual("foo", myString); + } + + [Test] + public void MethodInvokingFactoryObjectRefsObject() + { + new StreamHelperDecorator(new StreamHelperCallback(_MethodInvokingFactoryObjectRefsObject)).Run(); + } + + private void _MethodInvokingFactoryObjectRefsObject(out Stream stream) + { + const string xml = + @" + + + + + + + + + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + TestObject foo = (TestObject) factory["foo"]; + Assert.AreEqual("Bingo", foo.Name); + } + + [Test] + [ExpectedException(typeof(ObjectCreationException))] + public void BadParentReference() + { + IResource resource = new ReadOnlyXmlTestResource("wellformed-but-bad.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + + xof.GetObject("no.parent.factory"); + } + + [Test] + public void TypedStringValueIsPickedUp() + { + new StreamHelperDecorator(new StreamHelperCallback(_TypedStringValueIsPickedUp)).Run(); + } + + private void _TypedStringValueIsPickedUp(out Stream stream) + { + const string xml = + @" + + + trilby,fedora + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + XmlObjectFactory fac = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + IObjectDefinition def = fac.GetObjectDefinition("golyadkin"); + PropertyValue value = def.PropertyValues.GetPropertyValue("Hats"); + Assert.AreEqual(typeof(TypedStringValue), value.Value.GetType()); + TestObject obj = (TestObject) fac.GetObject("golyadkin"); + Assert.IsTrue(ArrayUtils.AreEqual(new string[] {"trilby", "fedora"}, obj.Hats)); + } + + [Test] + public void ChildDefinitionWithoutIdOrNameOrALiasGetsOneAutogenerated() + { + new StreamHelperDecorator(new StreamHelperCallback(_ChildDefinitionWithoutIdOrNameOrALiasGetsOneAutogenerated)).Run(); + } + + private void _ChildDefinitionWithoutIdOrNameOrALiasGetsOneAutogenerated(out Stream stream) + { + const string xml = + @" + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + XmlObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + string[] names = factory.GetObjectDefinitionNames(); + // mmm, how is one to test this? I have no idea what the generated name is... + Assert.AreEqual(2, names.Length, "Should have got two object names, one of which is autogenerated."); + } + + /// + /// Tests for the issue described at SPRNET-83. Schema modded to allow + /// zero elements (where previously there HAD to be at least one). + /// + [Test] + public void AnObjectsFileWithNoObjectsIsOk() + { + // test just makes sure that no exception is thrown and that + // said container doesn't complain about having no objects. + IResource resource = new ReadOnlyXmlTestResource("no-objects.xml", GetType()); + new XmlObjectFactory(resource); + } + + [Test] + public void ImportsExternalResourcesCorrectly() + { + IResource resource = new ReadOnlyXmlTestResource("external-resources.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + // object comes from imported file... + ITestObject rick = xof["rick"] as ITestObject; + Assert.IsNotNull(rick); + Assert.AreEqual("Rick", rick.Name); + // object comes from base file... + ITestObject jenny = xof["jenny"] as ITestObject; + Assert.IsNotNull(jenny); + Assert.AreEqual("Jenny", jenny.Name); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void ImportsExternalResourcesBailsOnNonExistentResource() + { + IResource resource = new ReadOnlyXmlTestResource("bad-external-resources.xml", GetType()); + new XmlObjectFactory(resource); + } + + [Test] + public void PropertyInvokingFactoryObjectIsWiredCorrectly() + { + IResource resource = new ReadOnlyXmlTestResource("invoke-factory.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + TestObject actual = xof["culturalObject"] as TestObject; + object expected = CultureInfo.InstalledUICulture; + Assert.AreEqual(expected, actual.MyCulture); + } + + [Test] + public void LoadFromConfig() + { + IObjectFactory factory = ConfigurationUtils.GetSection("objects") as IObjectFactory; + + Assert.IsNotNull(factory, "Factory loaded from config was null."); + Assert.IsTrue(factory is IConfigurableListableObjectFactory); + IConfigurableListableObjectFactory clof + = factory as IConfigurableListableObjectFactory; + Assert.IsTrue(clof.ObjectDefinitionCount == 6); + Assert.IsNotNull(factory["foo"]); + } + + [Test] + public void DescriptionButNoProperties() + { + DefaultListableObjectFactory xof = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(xof); + reader.LoadObjectDefinitions( + new ReadOnlyXmlTestResource("collections.xml", GetType())); + TestObject validEmpty + = (TestObject) xof.GetObject("validEmptyWithDescription"); + Assert.AreEqual(0, validEmpty.Age); + } + + /// + /// Uses a separate factory. + /// + [Test] + public void RefToSeparatePrototypeInstances() + { + DefaultListableObjectFactory xof = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(xof); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("reftypes.xml", GetType())); + Assert.IsTrue(xof.ObjectDefinitionCount == 8, "8 objects in reftypes, not " + xof.ObjectDefinitionCount); + TestObject emma = (TestObject) xof.GetObject("emma"); + TestObject georgia = (TestObject) xof.GetObject("georgia"); + ITestObject emmasJenks = emma.Spouse; + ITestObject georgiasJenks = georgia.Spouse; + Assert.IsTrue(emmasJenks != georgiasJenks, "Emma and georgia think they have a different boyfriend."); + Assert.IsTrue(emmasJenks.Name.Equals("Andrew"), "Emmas jenks has right name"); + Assert.IsTrue(emmasJenks != xof.GetObject("jenks"), "Emmas doesn't equal new ref."); + Assert.IsTrue(emmasJenks.Name.Equals("Andrew"), "Georgias jenks has right name."); + Assert.IsTrue(emmasJenks.Equals(georgiasJenks), "They are object equal."); + Assert.IsTrue(emmasJenks.Equals(xof.GetObject("jenks")), "They object equal direct ref."); + } + + [Test] + public void RefToSingleton() + { + DefaultListableObjectFactory xof = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(xof); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("reftypes.xml", GetType())); + Assert.IsTrue(xof.ObjectDefinitionCount == 8, "8 objects in reftypes, not " + xof.ObjectDefinitionCount); + TestObject jen = (TestObject) xof.GetObject("jenny"); + TestObject dave = (TestObject) xof.GetObject("david"); + TestObject jenks = (TestObject) xof.GetObject("jenks"); + ITestObject davesJen = dave.Spouse; + ITestObject jenksJen = jenks.Spouse; + Assert.IsTrue(davesJen == jenksJen, "1 jen instance"); + Assert.IsTrue(davesJen == jen, "1 jen instance"); + } + + [Test] + public void InnerObjects() + { + DefaultListableObjectFactory xof = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(xof); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("reftypes.xml", GetType())); + TestObject hasInnerObjects = (TestObject) xof.GetObject("hasInnerObjects"); + Assert.AreEqual(5, hasInnerObjects.Age); + Assert.IsNotNull(hasInnerObjects.Spouse); + Assert.AreEqual("inner1", hasInnerObjects.Spouse.Name); + Assert.AreEqual(6, hasInnerObjects.Spouse.Age); + Assert.IsNotNull(hasInnerObjects.Friends); + IList friends = (IList) hasInnerObjects.Friends; + Assert.AreEqual(2, friends.Count); + DerivedTestObject inner2 = (DerivedTestObject) friends[0]; + Assert.AreEqual("inner2", inner2.Name); + Assert.AreEqual(7, inner2.Age); + TestObject innerFactory = (TestObject) friends[1]; + Assert.AreEqual(DummyFactory.SINGLETON_NAME, innerFactory.Name); + Assert.IsNotNull(hasInnerObjects.SomeMap); + Assert.IsFalse((hasInnerObjects.SomeMap.Count == 0)); + TestObject inner3 = (TestObject) hasInnerObjects.SomeMap["someKey"]; + Assert.AreEqual("inner3", inner3.Name); + Assert.AreEqual(8, inner3.Age); + xof.Dispose(); + Assert.IsTrue(inner2.WasDestroyed()); + Assert.IsTrue(innerFactory.Name == null); + } + + [Test] + public void InnerObjectsInPrototype() + { + DefaultListableObjectFactory xof = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(xof); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("reftypes.xml", GetType())); + TestObject hasInnerObjects = (TestObject) xof.GetObject("prototypeHasInnerObjects"); + Assert.AreEqual(5, hasInnerObjects.Age); + Assert.IsNotNull(hasInnerObjects.Spouse); + Assert.AreEqual("inner1", hasInnerObjects.Spouse.Name); + Assert.AreEqual(6, hasInnerObjects.Spouse.Age); + Assert.IsNotNull(hasInnerObjects.Friends); + IList friends = (IList) hasInnerObjects.Friends; + Assert.AreEqual(2, friends.Count); + DerivedTestObject inner2 = (DerivedTestObject) friends[0]; + Assert.AreEqual("inner2", inner2.Name); + Assert.AreEqual(7, inner2.Age); + IList friendsOfInner = (IList) inner2.Friends; + Assert.AreEqual(1, friendsOfInner.Count); + DerivedTestObject innerFriendOfAFriend = (DerivedTestObject) friendsOfInner[0]; + Assert.AreEqual("innerFriendOfAFriend", innerFriendOfAFriend.Name); + Assert.AreEqual(7, innerFriendOfAFriend.Age); + TestObject innerFactory = (TestObject) friends[1]; + Assert.AreEqual(DummyFactory.SINGLETON_NAME, innerFactory.Name); + Assert.IsNotNull(hasInnerObjects.SomeMap); + Assert.IsFalse((hasInnerObjects.SomeMap.Count == 0)); + TestObject inner3 = (TestObject) hasInnerObjects.SomeMap["someKey"]; + Assert.AreEqual("inner3", inner3.Name); + Assert.AreEqual(8, inner3.Age); + xof.Dispose(); + Assert.IsFalse(inner2.WasDestroyed()); + Assert.IsFalse(innerFactory.Name == null); + Assert.IsFalse(innerFriendOfAFriend.WasDestroyed()); + } + + [Test] + public void SingletonInheritanceFromParentFactorySingleton() + { + XmlObjectFactory parent = new XmlObjectFactory(new ReadOnlyXmlTestResource("parent.xml", GetType())); + XmlObjectFactory child = new XmlObjectFactory(new ReadOnlyXmlTestResource("child.xml", GetType()), parent); + TestObject inherits = (TestObject) child.GetObject("inheritsFromParentFactory"); + // Name property value is overriden + Assert.IsTrue(inherits.Name.Equals("override")); + // Age property is inherited from object in parent factory + Assert.IsTrue(inherits.Age == 1); + TestObject inherits2 = (TestObject) child.GetObject("inheritsFromParentFactory"); + Assert.IsTrue(inherits2 == inherits); + } + + [Test] + public void PrototypeInheritanceFromParentFactoryPrototype() + { + XmlObjectFactory parent = new XmlObjectFactory(new ReadOnlyXmlTestResource("parent.xml", GetType())); + XmlObjectFactory child = new XmlObjectFactory(new ReadOnlyXmlTestResource("child.xml", GetType()), parent); + TestObject inherits = (TestObject) child.GetObject("prototypeInheritsFromParentFactoryPrototype"); + // Name property value is overridden + Assert.IsTrue(inherits.Name.Equals("prototype-override")); + // Age property is inherited from object in parent factory + Assert.IsTrue(inherits.Age == 2); + TestObject inherits2 = (TestObject) child.GetObject("prototypeInheritsFromParentFactoryPrototype"); + Assert.IsFalse(inherits2 == inherits); + inherits2.Age = 13; + Assert.IsTrue(inherits2.Age == 13); + // Shouldn't have changed first instance + Assert.IsTrue(inherits.Age == 2); + } + + [Test] + public void PrototypeInheritanceFromParentFactorySingleton() + { + XmlObjectFactory parent = new XmlObjectFactory(new ReadOnlyXmlTestResource("parent.xml", GetType())); + XmlObjectFactory child = new XmlObjectFactory(new ReadOnlyXmlTestResource("child.xml", GetType()), parent); + TestObject inherits = (TestObject) child.GetObject("protoypeInheritsFromParentFactorySingleton"); + // Name property value is overridden + Assert.IsTrue(inherits.Name.Equals("prototypeOverridesInheritedSingleton")); + // Age property is inherited from object in parent factory + Assert.IsTrue(inherits.Age == 1); + TestObject inherits2 = (TestObject) child.GetObject("protoypeInheritsFromParentFactorySingleton"); + Assert.IsFalse(inherits2 == inherits); + inherits2.Age = 13; + Assert.IsTrue(inherits2.Age == 13); + // Shouldn't have changed first instance + Assert.IsTrue(inherits.Age == 1); + } + + [Test] + public void DependenciesMaterializeThis() + { + IResource resource = new ReadOnlyXmlTestResource("dependenciesMaterializeThis.xml", GetType()); + XmlObjectFactory bf = new XmlObjectFactory(resource); + DummyBo bos = (DummyBo) bf.GetObject("boSingleton"); + DummyBo bop = (DummyBo) bf.GetObject("boPrototype"); + Assert.IsFalse(bos == bop); + Assert.AreEqual(bos.dao, bop.dao); + } + + [Test] + public void ChildOverridesParentObject() + { + XmlObjectFactory parent = new XmlObjectFactory(new ReadOnlyXmlTestResource("parent.xml", GetType())); + XmlObjectFactory child = new XmlObjectFactory(new ReadOnlyXmlTestResource("child.xml", GetType()), parent); + TestObject inherits = (TestObject) child.GetObject("inheritedTestObject"); + // Name property value is overridden + Assert.IsTrue(inherits.Name.Equals("overrideParentObject")); + // Age property is inherited from object in parent factory + Assert.IsTrue(inherits.Age == 1); + TestObject inherits2 = (TestObject) child.GetObject("inheritedTestObject"); + Assert.IsTrue(inherits2 == inherits); + } + + /// + /// Check that a prototype can't inherit from a bogus parent. + /// If a singleton does this the factory will fail to load. + /// + [Test] + [ExpectedException(typeof(NoSuchObjectDefinitionException))] + public void BogusParentageFromParentFactory() + { + XmlObjectFactory parent = new XmlObjectFactory(new ReadOnlyXmlTestResource("parent.xml", GetType())); + XmlObjectFactory child = new XmlObjectFactory(new ReadOnlyXmlTestResource("child.xml", GetType()), parent); + child.GetObject("bogusParent"); + } + + /// + /// Note that prototype/singleton distinction is not inherited. + /// It's possible for a subclass singleton not to return independent + /// instances even if derived from a prototype + /// + [Test] + public void SingletonInheritsFromParentFactoryPrototype() + { + XmlObjectFactory parent = new XmlObjectFactory(new ReadOnlyXmlTestResource("parent.xml", GetType())); + XmlObjectFactory child = new XmlObjectFactory(new ReadOnlyXmlTestResource("child.xml", GetType()), parent); + TestObject inherits = (TestObject) child.GetObject("singletonInheritsFromParentFactoryPrototype"); + // Name property value is overriden + Assert.IsTrue(inherits.Name.Equals("prototype-override")); + // Age property is inherited from object in parent factory + Assert.IsTrue(inherits.Age == 2); + TestObject inherits2 = (TestObject) child.GetObject("singletonInheritsFromParentFactoryPrototype"); + Assert.IsTrue(inherits2 == inherits); + } + + [Test] + public void AbstractParentObjects() + { + XmlObjectFactory parent = new XmlObjectFactory(new ReadOnlyXmlTestResource("parent.xml", GetType())); + parent.PreInstantiateSingletons(); + Assert.IsTrue(parent.IsSingleton("inheritedTestObjectWithoutClass")); + + // abstract objects should not match + //TODO add overloaded GetObjectOfType with 1 arg + IDictionary tbs = parent.GetObjectsOfType(typeof(TestObject), true, true); + Assert.AreEqual(2, tbs.Count); + Assert.IsTrue(tbs.Contains("inheritedTestObjectPrototype")); + Assert.IsTrue(tbs.Contains("inheritedTestObjectSingleton")); + + // non-abstract object should work, even if it serves as parent + object o1 = parent.GetObject("inheritedTestObjectPrototype"); + Assert.IsTrue(o1 is TestObject); + + // abstract object should return IObjectInstance itself + object o2 = parent.GetObject("inheritedTestObjectWithoutClass"); + Assert.IsTrue(o2 is IObjectDefinition); + } + + [Test] + public void CircularReferences() + { + DefaultListableObjectFactory xof = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(xof); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("reftypes.xml", GetType())); + TestObject jenny = (TestObject) xof.GetObject("jenny"); + TestObject david = (TestObject) xof.GetObject("david"); + TestObject ego = (TestObject) xof.GetObject("ego"); + Assert.IsTrue(jenny.Spouse == david, "Correct circular reference"); + Assert.IsTrue(david.Spouse == jenny, "Correct circular reference"); + Assert.IsTrue(ego.Spouse == ego, "Correct circular reference"); + } + + [Test] + public void FactoryReferenceCircle() + { + IResource resource = new ReadOnlyXmlTestResource("factoryCircle.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + TestObject tb = (TestObject) xof.GetObject("singletonFactory"); + DummyFactory db = (DummyFactory) xof.GetObject("&singletonFactory"); + Assert.IsTrue(tb == db.OtherTestObject); + } + + [Test] + public void RefSubelement() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + //Assert.IsTrue ("5 objects in reftypes, not " + xof.GetObjectDefinitionCount(), xof.GetObjectDefinitionCount() == 5); + TestObject jen = (TestObject) xof.GetObject("jenny"); + TestObject dave = (TestObject) xof.GetObject("david"); + Assert.IsTrue(jen.Spouse == dave); + } + + [Test] + public void PropertyWithLiteralValueSubelement() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + TestObject verbose = (TestObject) xof.GetObject("verbose"); + Assert.IsTrue(verbose.Name.Equals("verbose")); + } + + [Test] + public void PropertyWithIdRefLocalAttrSubelement() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + TestObject verbose = (TestObject) xof.GetObject("verbose2"); + Assert.IsTrue(verbose.Name.Equals("verbose")); + } + + [Test] + public void PropertyWithIdRefObjectAttrSubelement() + { + IResource resource = new ReadOnlyXmlTestResource("collections.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + TestObject verbose = (TestObject) xof.GetObject("verbose3"); + Assert.IsTrue(verbose.Name.Equals("verbose")); + } + + [Test] + public void EnumProperty() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("enums.xml", GetType())); + TestObject obj = factory.GetObject("rod", typeof(TestObject)) as TestObject; + Assert.IsNotNull(obj.FileMode); + Assert.AreEqual(FileMode.Create, obj.FileMode); + } + + [Test] + public void InitMethodIsInvoked() + { + IResource resource = new ReadOnlyXmlTestResource("initializers.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + DoubleInitializer in_Renamed = (DoubleInitializer) xof.GetObject("init-method1"); + // Initializer should have doubled value + Assert.AreEqual(14, in_Renamed.Num); + } + + /// + /// Test that if a custom initializer throws an exception, it's handled correctly. + /// + [Test] + public void InitMethodThrowsException() + { + IResource resource = new ReadOnlyXmlTestResource("initializers.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + try + { + xof.GetObject("init-method2"); + Assert.Fail(); + } + catch (ObjectCreationException ex) + { + Assert.IsTrue(ex.InnerException is FormatException); + } + } + + [Test] + [ExpectedException(typeof(ObjectCreationException))] + public void NoSuchInitMethod() + { + IResource resource = new ReadOnlyXmlTestResource("initializers.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + xof.GetObject("init-method3"); + Assert.Fail(); + } + + /// + /// Check that InitializingObject method is called first. + /// + [Test] + public void InitializingObjectAndInitMethod() + { + InitAndIB.constructed = false; + IResource resource = new ReadOnlyXmlTestResource("initializers.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + Assert.IsFalse(InitAndIB.constructed); + xof.PreInstantiateSingletons(); + Assert.IsFalse(InitAndIB.constructed); + InitAndIB iib = (InitAndIB) xof.GetObject("init-and-ib"); + Assert.IsTrue(InitAndIB.constructed); + Assert.IsTrue(iib.afterPropertiesSetInvoked && iib.initMethodInvoked); + Assert.IsTrue(!iib.destroyed && !iib.customDestroyed); + xof.Dispose(); + Assert.IsTrue(iib.destroyed && iib.customDestroyed); + xof.Dispose(); + Assert.IsTrue(iib.destroyed && iib.customDestroyed); + } + + [Test] + public void MultiThreadedLazyInit() + { + IResource resource = new ReadOnlyXmlTestResource("lazy-init-multithreaded.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + + LazyWorker lw1 = new LazyWorker(xof); + LazyWorker lw2 = new LazyWorker(xof); + Thread thread1 = new Thread(new ThreadStart(lw1.DoWork)); + Thread thread2 = new Thread(new ThreadStart(lw2.DoWork)); + + thread1.Start(); + Thread.Sleep(1000); + thread2.Start(); + thread1.Join(); + thread2.Join(); + Assert.AreEqual(typeof(LazyTestObject), lw1.ObjectFromContext.GetType()); + Assert.AreEqual(typeof(LazyTestObject), lw2.ObjectFromContext.GetType()); + Assert.AreEqual(1, LazyTestObject.Count); + } + + /// + /// Check that InitializingObject method is called first. + /// + [Test] + public void DefaultLazyInit() + { + InitAndIB.constructed = false; + IResource resource = new ReadOnlyXmlTestResource("default-lazy-init.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + Assert.IsFalse(InitAndIB.constructed); + xof.PreInstantiateSingletons(); + Assert.IsTrue(InitAndIB.constructed); + try + { + xof.GetObject("lazy-and-bad"); + } + catch (ObjectCreationException ex) + { + Assert.IsTrue(ex.InnerException is FormatException); + } + } + + + /// + /// Check that InitializingObject method is called first. + /// + [Test] + public void DefaultLazyInitNoInObjectDef() + { + InitAndIB.constructed = false; + IResource resource = new ReadOnlyXmlTestResource("default-lazy-init.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + Assert.IsFalse(InitAndIB.constructed); + xof.PreInstantiateSingletons(); + Assert.IsTrue(InitAndIB.constructed); + try + { + xof.GetObject("init-and-ib-no-init-in-local-object-def"); + } + catch (ObjectCreationException ex) + { + Assert.IsTrue(ex.InnerException is FormatException); + } + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void NoSuchXmlFile() + { + new XmlObjectFactory( + new ReadOnlyXmlTestResource("missing.xml", GetType())); + } + + [Test] + public void InvalidXmlFile() + { + DefaultListableObjectFactory xof = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(xof); + try + { + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("invalid.xml", GetType())); + Assert.Fail("Should have thrown XmlObjectDefinitionStoreException"); + } +#if !NET_1_0 + catch (XmlObjectDefinitionStoreException e) + { + Assert.AreEqual(0, e.Message.IndexOf("Line 21 in XML document")); + } +#else + catch (XmlObjectDefinitionStoreException) + {} +#endif + } + + [Test] + public void DefaultXmlResolverIsUsedIfNullSuppliedOrSet() + { + DefaultListableObjectFactory xof = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(xof, null); + try + { + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("invalid.xml", GetType())); + Assert.Fail("Should have thrown XmlObjectDefinitionStoreException"); + } +#if !NET_1_0 + catch (XmlObjectDefinitionStoreException e) + { + Assert.AreEqual(0, e.Message.IndexOf("Line 21 in XML document")); + } +#else + catch (XmlObjectDefinitionStoreException) + {} +#endif + + } + + [Test] + [ExpectedException(typeof(UnsatisfiedDependencyException))] + public void UnsatisfiedObjectDependencyCheck() + { + XmlObjectFactory xof + = new XmlObjectFactory( + new ReadOnlyXmlTestResource( + "unsatisfiedObjectDependencyCheck.xml", GetType())); + xof.GetObject("a", typeof(DependenciesObject)); + } + + [Test] + [ExpectedException(typeof(UnsatisfiedDependencyException))] + public void UnsatisfiedSimpleDependencyCheck() + { + XmlObjectFactory xof = + new XmlObjectFactory(new ReadOnlyXmlTestResource("unsatisfiedSimpleDependencyCheck.xml", GetType())); + xof.GetObject("a", typeof(DependenciesObject)); + } + + [Test] + public void SatisfiedObjectDependencyCheck() + { + XmlObjectFactory xof + = new XmlObjectFactory( + new ReadOnlyXmlTestResource("satisfiedObjectDependencyCheck.xml", GetType())); + DependenciesObject a = (DependenciesObject) xof.GetObject("a"); + Assert.IsNotNull(a.Spouse); + } + + [Test] + public void SatisfiedSimpleDependencyCheck() + { + XmlObjectFactory xof = + new XmlObjectFactory( + new ReadOnlyXmlTestResource( + "satisfiedSimpleDependencyCheck.xml", GetType())); + DependenciesObject a = (DependenciesObject) xof.GetObject("a"); + Assert.AreEqual(a.Age, 33); + } + + [Test] + [ExpectedException(typeof(UnsatisfiedDependencyException))] + public void UnsatisfiedAllDependencyCheck() + { + XmlObjectFactory xof + = new XmlObjectFactory( + new ReadOnlyXmlTestResource( + "unsatisfiedAllDependencyCheckMissingObjects.xml", GetType())); + xof.GetObject("a", typeof(DependenciesObject)); + } + + [Test] + public void SatisfiedAllDependencyCheck() + { + XmlObjectFactory xof + = new XmlObjectFactory( + new ReadOnlyXmlTestResource("satisfiedAllDependencyCheck.xml", GetType())); + DependenciesObject a = (DependenciesObject) xof.GetObject("a"); + Assert.AreEqual(a.Age, 33); + Assert.IsNotNull(a.Name); + Assert.IsNotNull(a.Spouse); + } + + public void Autowire() + { + XmlObjectFactory xof = new XmlObjectFactory(new ReadOnlyXmlTestResource("autowire.xml", GetType())); + TestObject spouse = new TestObject("kerry", 0); + xof.RegisterSingleton("spouse", spouse); + DoTestAutowire(xof); + } + + public void AutowireWithParent() + { + XmlObjectFactory xof = new XmlObjectFactory(new ReadOnlyXmlTestResource("autowire.xml", GetType())); + DefaultListableObjectFactory lbf = new DefaultListableObjectFactory(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add("name", "kerry"); + lbf.RegisterObjectDefinition("spouse", new RootObjectDefinition(typeof(TestObject), pvs)); + xof.ParentObjectFactory = lbf; + DoTestAutowire(xof); + } + + private void DoTestAutowire(XmlObjectFactory xof) + { + DependenciesObject rod1 = (DependenciesObject) xof.GetObject("rod1"); + TestObject kerry = (TestObject) xof.GetObject("spouse"); + // Should have been autowired + Assert.AreEqual(kerry, rod1.Spouse); + + DependenciesObject rod1a = (DependenciesObject) xof.GetObject("rod1a"); + // Should have been autowired + Assert.AreEqual(kerry, rod1a.Spouse); + + DependenciesObject rod2 = (DependenciesObject) xof.GetObject("rod2"); + // Should have been autowired + Assert.AreEqual(kerry, rod2.Spouse); + + ConstructorDependenciesObject rod3 = (ConstructorDependenciesObject) xof.GetObject("rod3"); + IndexedTestObject other = (IndexedTestObject) xof.GetObject("other"); + // Should have been autowired + Assert.AreEqual(kerry, rod3.Spouse1); + Assert.AreEqual(kerry, rod3.Spouse2); + Assert.AreEqual(other, rod3.Other); + + ConstructorDependenciesObject rod3a = (ConstructorDependenciesObject) xof.GetObject("rod3a"); + // Should have been autowired + Assert.AreEqual(kerry, rod3a.Spouse1); + Assert.AreEqual(kerry, rod3a.Spouse2); + Assert.AreEqual(other, rod3a.Other); + + try + { + xof.GetObject("rod4", typeof(ConstructorDependenciesObject)); + Assert.Fail("Should not have thrown FatalObjectException"); + } + catch (FatalObjectException) + { + // expected + } + + DependenciesObject rod5 = (DependenciesObject) xof.GetObject("rod5"); + // Should not have been autowired + Assert.IsNotNull(rod5.Spouse); + + IObjectFactory appCtx = (IObjectFactory) xof.GetObject("childAppCtx"); + Assert.IsTrue(appCtx.GetObject("rod1") != null); + Assert.IsTrue(appCtx.GetObject("dependingObject") != null); + Assert.IsTrue(appCtx.GetObject("jenny") != null); + } + + [Test] + public void AutowireWithDefault() + { + XmlObjectFactory xof + = new XmlObjectFactory( + new ReadOnlyXmlTestResource("default-autowire.xml", GetType())); + DependenciesObject rod1 = (DependenciesObject) xof.GetObject("rod1"); + // Should have been autowired + Assert.IsNotNull(rod1.Spouse); + Assert.IsTrue(rod1.Spouse.Name.Equals("Kerry")); + + DependenciesObject rod2 = (DependenciesObject) xof.GetObject("rod2"); + // Should have been autowired + Assert.IsNotNull(rod2.Spouse); + Assert.IsTrue(rod2.Spouse.Name.Equals("Kerry")); + } + + [Test] + public void AutowireByConstructor() + { + XmlObjectFactory xof = new XmlObjectFactory(new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + ConstructorDependenciesObject rod1 = (ConstructorDependenciesObject) xof.GetObject("rod1"); + TestObject kerry = (TestObject) xof.GetObject("kerry2"); + // Should have been autowired + Assert.AreEqual(kerry, rod1.Spouse1); + Assert.AreEqual(0, rod1.Age); + Assert.AreEqual(null, rod1.Name); + + ConstructorDependenciesObject rod2 = (ConstructorDependenciesObject) xof.GetObject("rod2"); + TestObject kerry1 = (TestObject) xof.GetObject("kerry1"); + TestObject kerry2 = (TestObject) xof.GetObject("kerry2"); + // Should have been autowired + Assert.AreEqual(kerry2, rod2.Spouse1); + Assert.AreEqual(kerry1, rod2.Spouse2); + Assert.AreEqual(0, rod2.Age); + Assert.AreEqual(null, rod2.Name); + + ConstructorDependenciesObject rod = (ConstructorDependenciesObject) xof.GetObject("rod3"); + IndexedTestObject other = (IndexedTestObject) xof.GetObject("other"); + // Should have been autowired + Assert.AreEqual(kerry, rod.Spouse1); + Assert.AreEqual(kerry, rod.Spouse2); + Assert.AreEqual(other, rod.Other); + Assert.AreEqual(0, rod.Age); + Assert.AreEqual(null, rod.Name); + xof.GetObject("rod4", typeof(ConstructorDependenciesObject)); + // Should have been autowired + Assert.AreEqual(kerry, rod.Spouse1); + Assert.AreEqual(kerry, rod.Spouse2); + Assert.AreEqual(other, rod.Other); + Assert.AreEqual(0, rod.Age); + Assert.AreEqual(null, rod.Name); + } + + [Test] + public void AutowireByConstructorWithSimpleValues() + { + XmlObjectFactory xof = new XmlObjectFactory(new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + ConstructorDependenciesObject rod5 = (ConstructorDependenciesObject) xof.GetObject("rod5"); + TestObject kerry1 = (TestObject) xof.GetObject("kerry1"); + TestObject kerry2 = (TestObject) xof.GetObject("kerry2"); + IndexedTestObject other = (IndexedTestObject) xof.GetObject("other"); + // Should have been autowired + Assert.AreEqual(kerry2, rod5.Spouse1); + Assert.AreEqual(kerry1, rod5.Spouse2); + Assert.AreEqual(other, rod5.Other); + Assert.AreEqual(99, rod5.Age); + Assert.AreEqual("myname", rod5.Name); + ConstructorDependenciesObject rod6 = (ConstructorDependenciesObject) xof.GetObject("rod6"); + // Should have been autowired + Assert.AreEqual(kerry2, rod6.Spouse1); + Assert.AreEqual(kerry1, rod6.Spouse2); + Assert.AreEqual(other, rod6.Other); + Assert.AreEqual(0, rod6.Age); + Assert.AreEqual(null, rod6.Name); + } + + [Test] + public void ConstructorArgResolution() + { + XmlObjectFactory xof = new XmlObjectFactory(new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + TestObject kerry1 = (TestObject) xof.GetObject("kerry1"); + TestObject kerry2 = (TestObject) xof.GetObject("kerry2"); + ConstructorDependenciesObject rod9 = (ConstructorDependenciesObject) xof.GetObject("rod9"); + Assert.AreEqual(99, rod9.Age); + ConstructorDependenciesObject rod10 = (ConstructorDependenciesObject) xof.GetObject("rod10"); + Assert.AreEqual(null, rod10.Name); + ConstructorDependenciesObject rod11 = (ConstructorDependenciesObject) xof.GetObject("rod11"); + Assert.AreEqual(kerry2, rod11.Spouse1); + + ConstructorDependenciesObject rod12 = (ConstructorDependenciesObject) xof.GetObject("rod12"); + Assert.AreEqual(kerry1, rod12.Spouse1); + Assert.IsNull(rod12.Spouse2); + + ConstructorDependenciesObject rod13 = (ConstructorDependenciesObject) xof.GetObject("rod13"); + Assert.AreEqual(kerry1, rod13.Spouse1); + Assert.AreEqual(kerry2, rod13.Spouse2); + } + + [Test] + [ExpectedException(typeof(UnsatisfiedDependencyException))] + public void ThrowsExceptionOnTooManyArguments() + { + XmlObjectFactory xof = new XmlObjectFactory( + new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + xof.GetObject("rod7", typeof(ConstructorDependenciesObject)); + } + + [Test] + [ExpectedException(typeof(UnsatisfiedDependencyException))] + public void ThrowsExceptionOnAmbiguousResolution() + { + XmlObjectFactory xof = new XmlObjectFactory(new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + xof.GetObject("rod8", typeof(ConstructorDependenciesObject)); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void FactoryObjectDefinedAsPrototype() + { + new XmlObjectFactory( + new ReadOnlyXmlTestResource("invalid-factory.xml", GetType())); + } + + [Test] + public void DependsOn() + { + PreparingObject1.prepared = false; + PreparingObject1.destroyed = false; + PreparingObject2.prepared = false; + PreparingObject2.destroyed = false; + DependingObject.destroyed = false; + IResource resource = new ReadOnlyXmlTestResource("initializers.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + xof.PreInstantiateSingletons(); + xof.Dispose(); + Assert.IsTrue(PreparingObject1.prepared); + Assert.IsTrue(PreparingObject1.destroyed); + Assert.IsTrue(PreparingObject2.prepared); + Assert.IsTrue(PreparingObject2.destroyed); + Assert.IsTrue(DependingObject.destroyed); + } + + [Test] + public void ClassNotFoundWithDefault() + { + try + { + new XmlObjectFactory( + new ReadOnlyXmlTestResource("classNotFound.xml", GetType())); + } + catch (ObjectDefinitionStoreException ex) + { + Assert.IsTrue(ex.InnerException is TypeLoadException); + } + } + + [Test] + public void AnObjectCanBeIstantiatedWithANotFullySpecifiedAssemblyName() + { + IResource resource = new ReadOnlyXmlTestResource("notfullyspecified.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + IDbConnection connection = (IDbConnection) xof.GetObject("connectionNotFullySpecified"); + Assert.IsNotNull(connection); + } + + [Test] + public void ResourceAndInputStream() + { + //string filename = @"C:\temp\spring-test.properties"; + string filename = @"/temp/spring-test.properties"; + FileInfo file = new FileInfo(filename); + bool tempDirWasCreated = false; + try + { + if (file.Exists) + { + file.Delete(); + } + if (!file.Directory.Exists) + { + file.Directory.Create(); + tempDirWasCreated = true; + } + StreamWriter sw = file.CreateText(); + sw.WriteLine("test"); + sw.Close(); + + IResource resource = new ReadOnlyXmlTestResource("resource.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + + ResourceTestObject resource1 = (ResourceTestObject) xof.GetObject("resource1"); + Assert.IsTrue(resource1.Resource is FileSystemResource); + using (StreamReader reader = new StreamReader(resource1.Resource.InputStream)) + { + string fileText = reader.ReadLine(); + Assert.AreEqual("test", fileText, "error using IResource to read file contents"); + } + using (StreamReader reader = new StreamReader(resource1.InputStream)) + { + string fileText = reader.ReadLine(); + Assert.AreEqual("test", fileText, "error using Stream to read file contents"); + } + } + finally + { + if (tempDirWasCreated) + { + file.Delete(); + // sure hope another process hasn't created the directory + file.Directory.Delete(); + } + } + } + + [Test] + public void FactoryMethodsSingletonOnTargetClass() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("factory-methods.xml", GetType())); + + FactoryMethods fm = (FactoryMethods) factory["default"]; + Assert.AreEqual(0, fm.Number); + Assert.AreEqual("default", fm.Name); + Assert.AreEqual("defaultInstance", fm.Object.Name); + Assert.AreEqual("setterString", fm.Value); + + fm = (FactoryMethods) factory["testObjectOnly"]; + Assert.AreEqual(0, fm.Number); + Assert.AreEqual("default", fm.Name); + // This comes from the test object + Assert.AreEqual("Juergen", fm.Object.Name); + + fm = (FactoryMethods) factory["full"]; + Assert.AreEqual(27, fm.Number); + Assert.AreEqual("gotcha", fm.Name); + Assert.AreEqual("Juergen", fm.Object.Name); + + FactoryMethods fm2 = (FactoryMethods) factory["full"]; + Assert.AreSame(fm, fm2); + } + + [Test] + public void FactoryMethodsPrototypeOnTargetClass() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("factory-methods.xml", GetType())); + FactoryMethods fm = (FactoryMethods) factory["defaultPrototype"]; + FactoryMethods fm2 = (FactoryMethods) factory["defaultPrototype"]; + Assert.AreEqual(0, fm.Number); + Assert.AreEqual("default", fm.Name); + Assert.AreEqual("defaultInstance", fm.Object.Name); + Assert.AreEqual("setterString", fm.Value); + Assert.AreEqual(fm.Number, fm2.Number); + Assert.AreEqual(fm.Value, fm2.Value); + // The TestObject is created separately for each object + Assert.IsFalse(ReferenceEquals(fm.Object, fm2.Object)); + Assert.IsFalse(ReferenceEquals(fm, fm2)); + + fm = (FactoryMethods) factory["testObjectOnlyPrototype"]; + fm2 = (FactoryMethods) factory["testObjectOnlyPrototype"]; + Assert.AreEqual(0, fm.Number); + Assert.AreEqual("default", fm.Name); + // This comes from the test object + Assert.AreEqual("Juergen", fm.Object.Name); + Assert.AreEqual(fm.Number, fm2.Number); + Assert.AreEqual(fm.Value, fm2.Value); + // The TestObject reference is resolved to a prototype in the factory + Assert.AreSame(fm.Object, fm2.Object); + Assert.IsFalse(ReferenceEquals(fm, fm2)); + + fm = (FactoryMethods) factory["fullPrototype"]; + fm2 = (FactoryMethods) factory["fullPrototype"]; + Assert.AreEqual(27, fm.Number); + Assert.AreEqual("gotcha", fm.Name); + Assert.AreEqual("Juergen", fm.Object.Name); + Assert.AreEqual(fm.Number, fm2.Number); + Assert.AreEqual(fm.Value, fm2.Value); + // The TestObject reference is resolved to a prototype in the factory + Assert.AreSame(fm.Object, fm2.Object); + Assert.IsFalse(ReferenceEquals(fm, fm2)); + } + + /// + /// Tests where the static factory method is on a different class + /// + [Test] + public void FactoryMethodsOnExternalClass() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("factory-methods.xml", GetType())); + TestObject to = (TestObject) factory["externalFactoryMethodWithoutArgs"]; + Assert.AreEqual(2, to.Age); + Assert.AreEqual("Tristan", to.Name); + + to = (TestObject) factory["externalFactoryMethodWithArgs"]; + Assert.AreEqual(33, to.Age); + Assert.AreEqual("Rod", to.Name); + } + + [Test] + public void InstanceFactoryMethodWithoutArgs() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("factory-methods.xml", GetType())); + FactoryMethods fm = (FactoryMethods) factory["instanceFactoryMethodWithoutArgs"]; + Assert.AreEqual("instanceFactory", fm.Object.Name); + } + + [Test(Description = "http://opensource.atlassian.com/projects/spring/browse/SPRNET-492")] + public void InstanceFactoryMethodWithOverloadedArgs() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("factory-methods.xml", GetType())); + DataTable table = MakeNamesTable(); + DataRow row = table.NewRow(); + //FactoryMethods fm = (FactoryMethods) factory.GetObject("instanceFactoryMethodOverloads", new object[] {row}); + // Assert.AreEqual("DataRowCtor", fm.Name); + + DynamicMock mock = new DynamicMock(typeof(IDataRecord)); + IDataRecord dataRecord = (IDataRecord) mock.Object; + FactoryMethods fm = (FactoryMethods)factory.GetObject("instanceFactoryMethodOverloads", new object[] { dataRecord }); + Assert.AreEqual("DataRecordCtor", fm.Name); + + + } + + + #region Private Helper Methods for InstanceFactoryMethodWithOverloadedargs + private DataTable MakeNamesTable() + { + // Create a new DataTable titled 'Names.' + DataTable namesTable = new DataTable("Names"); + + // Add three column objects to the table. + DataColumn idColumn = new DataColumn(); + idColumn.DataType = System.Type.GetType("System.Int32"); + idColumn.ColumnName = "id"; + idColumn.AutoIncrement = true; + namesTable.Columns.Add(idColumn); + + DataColumn fNameColumn = new DataColumn(); + fNameColumn.DataType = System.Type.GetType("System.String"); + fNameColumn.ColumnName = "Fname"; + fNameColumn.DefaultValue = "Fname"; + namesTable.Columns.Add(fNameColumn); + + DataColumn lNameColumn = new DataColumn(); + lNameColumn.DataType = System.Type.GetType("System.String"); + lNameColumn.ColumnName = "LName"; + namesTable.Columns.Add(lNameColumn); + + // Create an array for DataColumn objects. + DataColumn[] keys = new DataColumn[1]; + keys[0] = idColumn; + namesTable.PrimaryKey = keys; + + // Return the new DataTable. + return namesTable; + } + + #endregion + [Test] + [ExpectedException(typeof(ObjectCreationException))] + public void FactoryMethodNoMatchingStaticMethod() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("factory-methods.xml", GetType())); + factory.GetObject("noMatchPrototype"); + } + + [Test] + public void CanSpecifyFactoryMethodArgumentsOnFactoryMethodPrototype() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("factory-methods.xml", GetType())); + TestObject toArg = new TestObject(); + toArg.Name = "arg1"; + TestObject toArg2 = new TestObject(); + toArg2.Name = "arg2"; + FactoryMethods fm1 = (FactoryMethods) factory.GetObject("testObjectOnlyPrototype", new object[] {toArg}); + FactoryMethods fm2 = (FactoryMethods) factory.GetObject("testObjectOnlyPrototype", new object[] {toArg2}); + + Assert.AreEqual(0, fm1.Number); + Assert.AreEqual("default", fm1.Name); + // This comes from the test object + Assert.AreEqual("arg1", fm1.Object.Name); + Assert.AreEqual("arg2", fm2.Object.Name); + Assert.AreEqual(fm1.Number, fm2.Number); + Assert.AreEqual(fm2.Value, "testObjectOnlyPrototypeDISetterString"); + Assert.AreEqual(fm2.Value, fm2.Value); + // The TestObject reference is resolved to a prototype in the factory + Assert.AreSame(fm2.Object, fm2.Object); + Assert.IsFalse(ReferenceEquals(fm1, fm2)); + } + + //[Test] + //[ExpectedException(typeof(ObjectDefinitionStoreException))] + //public void CannotSpecifyFactoryMethodArgumentsOnSingleton() + //{ + // DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + // XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + // reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("factory-methods.xml", GetType())); + // factory.GetObject("testObjectOnly", new object[] {new TestObject()}); + //} + + //[Test] + //[ExpectedException(typeof(ObjectDefinitionStoreException))] + //public void CannotSpecifyFactoryMethodArgumentsExceptWithFactoryMethod() + //{ + // DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + // XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + // reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("overrides.xml", GetType())); + // factory.GetObject("overrideOnPrototype", new object[] {new TestObject()}); + //} + + [Test] + public void StaticPropertyRetrievingFactoryMethod() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("field-props-factory.xml", GetType())); + MyTestObject obj = factory.GetObject("cultureAware", typeof(MyTestObject)) as MyTestObject; + Assert.IsNotNull(obj.Culture); + Assert.AreEqual(CultureInfo.CurrentUICulture, obj.Culture); + } + + [Test] + public void StaticFieldRetrievingFactoryMethod() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("field-props-factory.xml", GetType())); + MyTestObject obj = factory.GetObject("withTypesField", typeof(MyTestObject)) as MyTestObject; + Assert.IsNotNull(obj.Types); + Assert.AreEqual(Type.EmptyTypes.Length, obj.Types.Length); + } + + [Test] + public void InstancePropertyRetrievingFactoryMethod() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("field-props-factory.xml", GetType())); + MyTestObject obj = factory.GetObject("instancePropertyCultureAware", typeof(MyTestObject)) as MyTestObject; + Assert.IsNotNull(obj.Culture); + Assert.AreEqual(new MyTestObject().MyDefaultCulture, obj.Culture); + } + + [Test] + public void InstanceFieldRetrievingFactoryMethod() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("field-props-factory.xml", GetType())); + MyTestObject obj = factory.GetObject("instanceCultureAware", typeof(MyTestObject)) as MyTestObject; + Assert.IsNotNull(obj.Culture); + Assert.AreEqual(new MyTestObject().Default, obj.Culture); + } + + [Test] + [ExpectedException(typeof(ObjectCreationException))] + public void BailsOnRubbishFieldRetrievingFactoryMethod() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("field-props-factory.xml", GetType())); + factory.GetObject("rubbishField", typeof(MyTestObject)); + } + + [Test] + [ExpectedException(typeof(ObjectCreationException))] + public void BailsOnRubbishPropertyRetrievingFactoryMethod() + { + DefaultListableObjectFactory factory = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("field-props-factory.xml", GetType())); + factory.GetObject("rubbishProperty", typeof(MyTestObject)); + } + + /// + /// Test creating an object using its 1 arg boolean constructor (amongst + /// others) by specifying the constructor arguments type and + /// not its index number. + /// + [Test] + public void ConstructorArgWithSimpleTypeMatch() + { + XmlObjectFactory xof + = new XmlObjectFactory( + new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + SingleSimpleTypeConstructorObject obj = + (SingleSimpleTypeConstructorObject) xof.GetObject("objectWithBoolean"); + Assert.IsTrue(obj.SingleBoolean); + } + + /// + /// Test creating an object using its two argument constructor (amongst + /// others) by specifying both ctor arguments type and not their index + /// numbers. + /// + [Test] + public void ConstructorArgWithDoubleSimpleTypeMatch() + { + XmlObjectFactory xof + = new XmlObjectFactory( + new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + SingleSimpleTypeConstructorObject obj = + (SingleSimpleTypeConstructorObject) xof.GetObject("objectWithBooleanAndString"); + Assert.IsTrue(obj.SecondBoolean); + Assert.AreEqual("A String", obj.TestString); + } + + [Test] + public void DoubleBooleanAutowire() + { + XmlObjectFactory xof + = new XmlObjectFactory( + new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + DoubleBooleanConstructorObject obj = + (DoubleBooleanConstructorObject) xof.GetObject("objectWithDoubleBoolean"); + Assert.IsTrue(obj.Boolean1); + Assert.IsFalse(obj.Boolean2); + } + + [Test] + public void ParsesNamedCtorArgsCorrectly() + { + XmlObjectFactory fac = new XmlObjectFactory( + new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + IObjectDefinition obj = fac.GetObjectDefinition("ctorArgsAllNamed"); + Assert.AreEqual(2, obj.ConstructorArgumentValues.NamedArgumentValues.Count, + "Should have parsed 2 named ctor args."); + ConstructorArgumentValues.ValueHolder nameArg = obj.ConstructorArgumentValues.GetNamedArgumentValue("name"); + Assert.IsNotNull(nameArg, "Should have parsed the 'name' ctor arg."); + Assert.AreEqual("Isaac Newton", nameArg.Value); + ConstructorArgumentValues.ValueHolder ageArg = obj.ConstructorArgumentValues.GetNamedArgumentValue("age"); + Assert.IsNotNull(ageArg, "Should have parsed the 'age' ctor arg."); + Assert.AreEqual("87", ageArg.Value); + } + + [Test] + public void InstantiateObjectViaNamedArgsToInnerMethodInvokingFactoryObject() + { + XmlObjectFactory fac = new XmlObjectFactory( + new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + ITestObject obj = (ITestObject) fac.GetObject("grabCtorArgFromMethodInvokingFactoryObject"); + Assert.AreEqual("Mr Isaac Newton Phd PPQ MC DJ", obj.Name); + Assert.AreEqual(198, obj.Age); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void BailsWhenBothNameAndIndexAttributesAreAppliedToASingleCtorArg() + { + new XmlObjectFactory( + new ReadOnlyXmlTestResource("bad-named-constructor-arg.xml", GetType())); + } + + [Test] + public void GetObjectThatUsesCtorArgValueShortcuts() + { + XmlObjectFactory xof = new XmlObjectFactory( + new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + TestObject to = (TestObject) xof.GetObject("objectWithCtorArgValueShortcuts"); + Assert.AreEqual("Rick", to.Name); + Assert.AreEqual(30, to.Age); + } + + [Test] + public void GetObjectThatUsesCtorArgRefShortcuts() + { + XmlObjectFactory xof = new XmlObjectFactory( + new ReadOnlyXmlTestResource("constructor-arg.xml", GetType())); + TestObject to = (TestObject) xof.GetObject("objectWithCtorArgRefShortcuts"); + ITestObject spouse = to.Spouse; + Assert.IsNotNull(spouse, "Dependency not wired in when using 'ref' attribute shortcut."); + Assert.AreEqual("Kerry2", spouse.Name); + } + + [Test(Description="SPR-1313")] + public void UseStaticFactoryMethodInnerObjectDefinition() + { + new StreamHelperDecorator(new StreamHelperCallback(_UseStaticFactoryMethodInnerObjectDefinition)).Run(); + } + + private void _UseStaticFactoryMethodInnerObjectDefinition(out Stream stream) + { + const string xml = + @" + + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + factory.GetObject("foo"); + } + + [Test(Description="SPR-1313")] + public void UseInstanceFactoryMethodInnerObjectDefinition() + { + new StreamHelperDecorator(new StreamHelperCallback(_UseInstanceFactoryMethodInnerObjectDefinition)).Run(); + } + + private void _UseInstanceFactoryMethodInnerObjectDefinition(out Stream stream) + { + const string xml = + @" + + + + + + + +"; + stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory factory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + factory.GetObject("foo"); + } + + [Test] + public void TestExpressionAttribute() + { + TypeRegistry.RegisterType("WebMethod", typeof(WebMethodAttribute)); + + IResource resource = new ReadOnlyXmlTestResource("expressions.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + + ExpressionTestObject eto = (ExpressionTestObject) xof.GetObject("to1"); + Assert.AreEqual(new DateTime(1974, 8, 24).ToString("m"), eto.SomeString); + Assert.AreEqual(new DateTime(2004, 8, 14), eto.SomeDate); + Assert.IsInstanceOfType(typeof(IExpression), eto.ExpressionOne); + Assert.IsInstanceOfType(typeof(IExpression), eto.ExpressionTwo); + Assert.AreEqual(DateTime.Today, eto.ExpressionOne.GetValue()); + Assert.AreEqual(String.Empty, eto.ExpressionTwo.GetValue()); + Assert.IsInstanceOfType(typeof(WebMethodAttribute), eto.SomeDictionary["method1"]); + Assert.IsInstanceOfType(typeof(WebMethodAttribute), eto.SomeDictionary["method2"]); + Assert.AreEqual("My First Web Method", ((WebMethodAttribute) eto.SomeDictionary["method1"]).Description); + Assert.AreEqual("My Second Web Method", ((WebMethodAttribute) eto.SomeDictionary["method2"]).Description); + } + + [Test] + public void TestExpressionElement() + { + TypeRegistry.RegisterType("WebMethod", typeof(WebMethodAttribute)); + + IResource resource = new ReadOnlyXmlTestResource("expressions.xml", GetType()); + XmlObjectFactory xof = new XmlObjectFactory(resource); + + ExpressionTestObject eto = (ExpressionTestObject)xof.GetObject("to2"); + Assert.AreEqual(new DateTime(1974, 8, 24).ToString("m"), eto.SomeString); + Assert.AreEqual(new DateTime(2004, 8, 14), eto.SomeDate); + Assert.IsInstanceOfType(typeof(IExpression), eto.ExpressionOne); + Assert.IsInstanceOfType(typeof(IExpression), eto.ExpressionTwo); + Assert.AreEqual(DateTime.Today, eto.ExpressionOne.GetValue()); + Assert.AreEqual(String.Empty, eto.ExpressionTwo.GetValue()); + Assert.IsInstanceOfType(typeof(WebMethodAttribute), eto.SomeDictionary["method1"]); + Assert.IsInstanceOfType(typeof(WebMethodAttribute), eto.SomeDictionary["method2"]); + Assert.AreEqual("My First Web Method", ((WebMethodAttribute)eto.SomeDictionary["method1"]).Description); + Assert.AreEqual("My Second Web Method", ((WebMethodAttribute)eto.SomeDictionary["method2"]).Description); + } + + #region Helper Classes + + public class LazyWorker + { + private XmlObjectFactory xof; + private Object objectFromContext; + public LazyWorker(XmlObjectFactory xof) + { + this.xof = xof; + } + public void DoWork() + { + objectFromContext = xof.GetObject("lazyObject"); + } + + public Object ObjectFromContext + { + get { return objectFromContext; } + } + } + public sealed class MyTestObject + { + public readonly CultureInfo Default = new CultureInfo("af-ZA"); + + public Type[] Types + { + get { return _types; } + set { _types = value; } + } + + public CultureInfo Culture + { + get { return _culture; } + set { _culture = value; } + } + + public CultureInfo MyDefaultCulture + { + get { return Default; } + } + + private Type[] _types; + private CultureInfo _culture; + } + + public class BadInitializer + { + public void Init2() + { + throw new FormatException(); + } + } + + public class DoubleInitializer + { + public int Num + { + get { return num; } + set { num = value; } + } + + private int num; + + public void Init() + { + this.num *= 2; + } + } + + public class InitAndIB : IInitializingObject, IDisposable + { + public static bool constructed; + public bool afterPropertiesSetInvoked, initMethodInvoked, destroyed, customDestroyed; + + public InitAndIB() + { + constructed = true; + } + + public void AfterPropertiesSet() + { + if (this.initMethodInvoked) + { + Assert.Fail(); + } + this.afterPropertiesSetInvoked = true; + } + + public void CustomInit() + { + if (!this.afterPropertiesSetInvoked) + { + Assert.Fail(); + } + this.initMethodInvoked = true; + } + + public void Dispose() + { + if (this.customDestroyed) + { + Assert.Fail(); + } + if (this.destroyed) + { + throw new ApplicationException("Already destroyed"); + } + this.destroyed = true; + } + + public void CustomDestroy() + { + if (!this.destroyed) + { + Assert.Fail(); + } + if (this.customDestroyed) + { + throw new ApplicationException("Already customDestroyed"); + } + this.customDestroyed = true; + } + } + + public class PreparingObject1 : IDisposable + { + public static bool prepared = false; + public static bool destroyed = false; + + public PreparingObject1() + { + prepared = true; + } + + public void Dispose() + { + destroyed = true; + } + } + + public class PreparingObject2 : IDisposable + { + public static bool prepared = false; + public static bool destroyed = false; + + public PreparingObject2() + { + prepared = true; + } + + public void Dispose() + { + destroyed = true; + } + } + + public class DependingObject : IDisposable + { + public static bool destroyed = false; + + public DependingObject() + { + if (!(PreparingObject1.prepared && PreparingObject2.prepared)) + { + throw new ApplicationException("Need prepared PreparedObjects!"); + } + } + + public void Dispose() + { + if (PreparingObject1.destroyed || PreparingObject2.destroyed) + { + throw new ApplicationException("Should not be destroyed before PreparedObjects"); + } + destroyed = true; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlParserResolverTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlParserResolverTests.cs new file mode 100644 index 00000000..bda700c3 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlParserResolverTests.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Xml; +using NUnit.Framework; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Unit tests for the XmlParserResolver class. + /// + /// Rick Evans + /// Aleksandar Seovic 2006.11.21 + /// $Id: XmlParserResolverTests.cs,v 1.10 2007/08/07 21:23:59 markpollack Exp $ + [TestFixture] + public sealed class XmlParserResolverTests + { + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterParserWithNullType() + { + NamespaceParserRegistry.RegisterParser((Type) null); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void RegisterParserWithBadParserType() + { + NamespaceParserRegistry.RegisterParser(GetType()); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterParserWithNullNamespaceWithoutDefaultValues() + { + NamespaceParserRegistry.RegisterParser(typeof(NotImplementedXmlObjectDefinitionParser), null, null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void RegisterParserWithEmptyNamespaceWithoutDefaultValues() + { + NamespaceParserRegistry.RegisterParser( + typeof(NotImplementedXmlObjectDefinitionParser), string.Empty, null); + } + + #region Inner Class : NotImplementedXmlObjectDefinitionParser + + private sealed class NotImplementedXmlObjectDefinitionParser : INamespaceParser + { + #region IXmlObjectDefinitionParser Members + + /// + /// Invoked by after construction but before any + /// elements have been parsed. + /// + public void Init() + { + throw new NotImplementedException(); + } + + #endregion + + public IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + throw new NotImplementedException(); + } + + public ObjectDefinitionHolder Decorate(XmlNode node, ObjectDefinitionHolder definition, + ParserContext parserContext) + { + throw new NotImplementedException(); + } + + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/INestedTestObject.cs b/test/Spring/Spring.Core.Tests/Objects/INestedTestObject.cs new file mode 100644 index 00000000..1064516d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/INestedTestObject.cs @@ -0,0 +1,27 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Objects +{ + public interface INestedTestObject + { + string Company { get; set; } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/IOther.cs b/test/Spring/Spring.Core.Tests/Objects/IOther.cs new file mode 100644 index 00000000..803f36a8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/IOther.cs @@ -0,0 +1,39 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +#region Imports +#endregion + +namespace Spring.Objects +{ + /// + /// Alternate nonsense interface used for testing. + /// + /// Spring.Java Folks + /// Choy Rim (.NET) + /// $Id: IOther.cs,v 1.2 2006/04/09 07:24:50 markpollack Exp $ + public interface IOther + { + + /// + /// Nonsense method + /// + void Absquatulate(); + + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/IPerson.cs b/test/Spring/Spring.Core.Tests/Objects/IPerson.cs new file mode 100644 index 00000000..45b36ee2 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/IPerson.cs @@ -0,0 +1,40 @@ +#region License +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +namespace Spring.Objects +{ + /// + /// Extends the normal test object to provide methods that are + /// useful when testing AOP method interception (hence the Get/Set methods). + /// + /// Rod Johnson + /// Simon White (.NET) + public interface IPerson : ITestObject + { + int GetAge(); + void SetAge(int age); + string GetName(); + void SetName(string name); + + /// + /// Test for non-property method matching. + /// If the parameter is an exception, it will be thrown rather than returned. + /// + object Echo(object obj); + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/ITestObject.cs b/test/Spring/Spring.Core.Tests/Objects/ITestObject.cs new file mode 100644 index 00000000..e1497c0a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/ITestObject.cs @@ -0,0 +1,84 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion + +using System; +using System.Collections; + +namespace Spring.Objects +{ + public interface ITestObject + { + event EventHandler Click; + + /// + /// Public method to programmatically raise the Click event + /// while testing. + /// + void OnClick (); + + int Age + { + get; + set; + } + + ICollection Friends + { + get; + } + + INestedTestObject Doctor + { + get; + } + + INestedTestObject Lawyer + { + get; + } + + string Name + { + get; + set; + } + + ITestObject Spouse + { + get; + set; + } + + + /// + /// Gets the exception method call count. + /// + /// The exception method call count. + int ExceptionMethodCallCount + { + get; + } + + void Exceptional (Exception t); + + int ExceptionalWithReturnValue(Exception t); + + object ReturnsThis (); + string GetDescription(); + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/IndexedTestObject.cs b/test/Spring/Spring.Core.Tests/Objects/IndexedTestObject.cs new file mode 100644 index 00000000..5fc302ff --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/IndexedTestObject.cs @@ -0,0 +1,87 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Collections; + +#endregion + +namespace Spring.Objects +{ + /// + /// Another simple object for testing purposes, containing some collection properties. + /// + public class IndexedTestObject + { + private TestObject[] array; + private IList list; + private ISet set; + private IDictionary map; + + public TestObject[] Array + { + get { return array; } + set { array = value; } + } + + public IList List + { + get { return list; } + set { list = value; } + } + + public ISet Set + { + get { return set; } + set { set = value; } + } + + public IDictionary Map + { + get { return map; } + set { map = value; } + } + + public IndexedTestObject() + { + TestObject to0 = new TestObject("name0", 0); + TestObject to1 = new TestObject("name1", 0); + TestObject to2 = new TestObject("name2", 0); + TestObject to3 = new TestObject("name3", 0); + TestObject to4 = new TestObject("name4", 0); + TestObject to5 = new TestObject("name5", 0); + TestObject to6 = new TestObject("name6", 0); + TestObject to7 = new TestObject("name7", 0); + array = new TestObject[] {to0, to1}; + list = new ArrayList(); + list.Add(to2); + list.Add(to3); + set = new HashedSet(); + set.Add(to6); + set.Add(to7); + map = new Hashtable(); + map["key1"] = to4; + map["key2"] = to5; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/LazyTestObject.cs b/test/Spring/Spring.Core.Tests/Objects/LazyTestObject.cs new file mode 100644 index 00000000..1acc0602 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/LazyTestObject.cs @@ -0,0 +1,77 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Threading; + +#endregion + +namespace Spring.Objects +{ + /// + /// Object to help test lazy instantiation of singletons from + /// multiple threads. See MultiThreadedLazyInit in + /// XmlObjectFactoryTests. + /// + /// Mark Pollack (.NET) + /// $Id: LazyTestObject.cs,v 1.1 2006/04/23 19:34:38 markpollack Exp $ + public class LazyTestObject + { + #region Fields + + private static int count = 0; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class + /// and sleeps for 2 seconds. + /// + public LazyTestObject() + { + count++; + Thread.Sleep(2000); + } + + #endregion + + #region Properties + + public static int Count + { + get { return count; } + set { count = value; } + } + + #endregion + + #region Methods + + public override string ToString() + { + return String.Format("LazyTestObject, Count = [{0}]", count); + } + #endregion + + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/MutablePropertyValuesTests.cs b/test/Spring/Spring.Core.Tests/Objects/MutablePropertyValuesTests.cs new file mode 100644 index 00000000..ac5b09cd --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/MutablePropertyValuesTests.cs @@ -0,0 +1,198 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects +{ + /// + /// Unit tests for the MutablePropertyValues class. + /// + /// Rick Evans + /// $Id: MutablePropertyValuesTests.cs,v 1.2 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class MutablePropertyValuesTests + { + #region SetUp + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp () {} + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp () {} + #endregion + + #region TearDown + /// + /// The tear down logic executed after the execution of each individual test. + /// + [TearDown] + public void TearDown () {} + + /// + /// The tear down logic executed after the entire test fixture has executed. + /// + [TestFixtureTearDown] + public void FixtureTearDown () {} + #endregion + + [Test] + public void Instantiation () { + MutablePropertyValues root = new MutablePropertyValues (); + root.Add (new PropertyValue ("Name", "Fiona Apple")); + root.Add (new PropertyValue ("Age", 24)); + MutablePropertyValues props = new MutablePropertyValues (root); + Assert.AreEqual (2, props.PropertyValues.Length); + } + + [Test] + public void InstantiationWithNulls () + { + MutablePropertyValues props = new MutablePropertyValues ((IDictionary) null); + Assert.AreEqual (0, props.PropertyValues.Length); + MutablePropertyValues props2 = new MutablePropertyValues ((IPropertyValues) null); + Assert.AreEqual (0, props2.PropertyValues.Length); + } + + [Test] + public void AddAllInList () + { + MutablePropertyValues props = new MutablePropertyValues (); + props.AddAll (new PropertyValue [] { + new PropertyValue ("Name", "Fiona Apple"), + new PropertyValue ("Age", 24)}); + Assert.AreEqual (2, props.PropertyValues.Length); + } + + [Test] + public void AddAllInNullList () + { + MutablePropertyValues props = new MutablePropertyValues (); + props.Add (new PropertyValue ("Name", "Fiona Apple")); + props.Add (new PropertyValue ("Age", 24)); + props.AddAll ((IList) null); + Assert.AreEqual (2, props.PropertyValues.Length); + } + + [Test] + public void RemoveByName () + { + MutablePropertyValues props = new MutablePropertyValues (); + props.Add (new PropertyValue ("Name", "Fiona Apple")); + props.Add (new PropertyValue ("Age", 24)); + Assert.AreEqual (2, props.PropertyValues.Length); + props.Remove ("name"); + Assert.AreEqual (1, props.PropertyValues.Length); + } + + [Test] + public void RemoveByPropertyValue () + { + MutablePropertyValues props = new MutablePropertyValues (); + PropertyValue propName = new PropertyValue ("Name", "Fiona Apple"); + props.Add (propName); + props.Add (new PropertyValue ("Age", 24)); + Assert.AreEqual (2, props.PropertyValues.Length); + props.Remove (propName); + Assert.AreEqual (1, props.PropertyValues.Length); + } + + [Test] + public void Contains () + { + MutablePropertyValues props = new MutablePropertyValues (); + props.Add (new PropertyValue ("Name", "Fiona Apple")); + props.Add (new PropertyValue ("Age", 24)); + // must be case insensitive to be CLS compliant... + Assert.IsTrue (props.Contains ("nAmE")); + } + + [Test] + public void AddAllInMap () + { + MutablePropertyValues props = new MutablePropertyValues (); + IDictionary map = new Hashtable (); + map.Add ("Name", "Fiona Apple"); + map.Add ("Age", 24); + props.AddAll (map); + Assert.AreEqual (2, props.PropertyValues.Length); + } + + [Test] + public void AddAllInNullMap () + { + MutablePropertyValues props = new MutablePropertyValues (); + props.Add (new PropertyValue ("Name", "Fiona Apple")); + props.AddAll ((IDictionary) null); + Assert.AreEqual (1, props.PropertyValues.Length); + } + + [Test] + public void ChangesSince () + { + IDictionary map = new Hashtable (); + PropertyValue propName = new PropertyValue ("Name", "Fiona Apple"); + map.Add (propName.Name, propName.Value); + map.Add ("Age", 24); + MutablePropertyValues props = new MutablePropertyValues (map); + MutablePropertyValues newProps = new MutablePropertyValues (map); + + // change the name... this is the change we'll be looking for + newProps.SetPropertyValueAt (new PropertyValue (propName.Name, "Naomi Woolf"), 0); + IPropertyValues changes = newProps.ChangesSince (props); + Assert.AreEqual (1, changes.PropertyValues.Length); + // the name was changed, so its the name property that should be in the changed list + Assert.IsTrue (changes.Contains ("name")); + + newProps.Add (new PropertyValue ("Commentator", "Naomi Woolf")); + changes = newProps.ChangesSince (props); + Assert.AreEqual (2, changes.PropertyValues.Length); + // the Commentator was added, so its the Commentator property that should be in the changed list + Assert.IsTrue (changes.Contains ("commentator")); + // the name was changed, so its the name property that should be in the changed list + Assert.IsTrue (changes.Contains ("name")); + } + + [Test] + public void ChangesSinceWithSelf () + { + IDictionary map = new Hashtable (); + map.Add ("Name", "Fiona Apple"); + map.Add ("Age", 24); + MutablePropertyValues props = new MutablePropertyValues (map); + props.Remove ("name"); + // get all of the changes between self and self again (there should be none); + IPropertyValues changes = props.ChangesSince (props); + Assert.AreEqual (0, changes.PropertyValues.Length); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/NestedTestObject.cs b/test/Spring/Spring.Core.Tests/Objects/NestedTestObject.cs new file mode 100644 index 00000000..f26ca597 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/NestedTestObject.cs @@ -0,0 +1,125 @@ +#region License + +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; +using System.Globalization; + +#endregion + +namespace Spring.Objects +{ + /// + /// Simple nested test object used for testing object factories, AOP framework etc. + /// + /// Trevor D. Cook + /// Mark Pollack (.NET) + public class NestedTestObject : INestedTestObject + { + internal string company = string.Empty; + private IDictionary _partners = new Hashtable(); + + public NestedTestObject() + { + } + + public NestedTestObject(string comp) + { + Company = comp; + } + + public NestedTestObject(string comp, IDictionary partners) + { + Company = comp; + Partners = partners; + } + + public ITestObject this[string partnersSurname] + { + get { return Partners[partnersSurname] as ITestObject; } + set { Partners[partnersSurname] = value; } + } + + public string Company + { + get { return this.company; } + set { this.company = value; } + } + + /// + /// Maps names (string) to ITestObjects... + /// + public IDictionary Partners + { + get { return _partners; } + set { _partners = value; } + } + } + + #region Inner Class : NestedTestObjectConverter + + public class NestedTestObjectConverter : TypeConverter + { + public override bool CanConvertTo( + ITypeDescriptorContext context, Type destinationType) + { + if ((destinationType == typeof (NestedTestObjectConverter)) + || (destinationType == typeof (InstanceDescriptor))) + { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + public override bool CanConvertFrom + (ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom + (ITypeDescriptorContext context, CultureInfo culture, object text_obj) + { + if (text_obj is string) + { + string text = (string) text_obj; + NestedTestObject tb = new NestedTestObject(text); + return tb; + } + return base.ConvertFrom(context, culture, text_obj); + } + + public override object ConvertTo( + ITypeDescriptorContext context, CultureInfo culture, object param, Type destinationType) + { + return base.ConvertTo(context, culture, param, destinationType); + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/NotWritablePropertyExceptionTests.cs b/test/Spring/Spring.Core.Tests/Objects/NotWritablePropertyExceptionTests.cs new file mode 100644 index 00000000..a65658fb --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/NotWritablePropertyExceptionTests.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using NUnit.Framework; +using Spring.Core; + +namespace Spring.Objects +{ + /// + /// Unit tests for the NotWritablePropertyException class. + /// + /// Rick Evans + /// $Id: NotWritablePropertyExceptionTests.cs,v 1.4 2007/07/31 00:09:24 markpollack Exp $ + [TestFixture] + public sealed class NotWritablePropertyExceptionTests + { + [Test] + public void InstantiationSupplyingPropertyTypeAndRootException() + { + NotWritablePropertyException ex = new NotWritablePropertyException("Doctor", typeof (TestObject), null); + Assert.AreEqual("Doctor", ex.OffendingPropertyName); + Assert.AreEqual(typeof(TestObject), ex.ObjectType); + } + + [Test] + public void InstantiationSupplyingNullPropertyTypeAndRootException() + { + NotWritablePropertyException ex = new NotWritablePropertyException(null, typeof (TestObject), null); + Assert.AreEqual(null, ex.OffendingPropertyName); + Assert.AreEqual(typeof(TestObject), ex.ObjectType); + } + + [Test] + public void InstantiationSupplyingNullPropertyNullTypeAndRootException() + { + NotWritablePropertyException ex = new NotWritablePropertyException(null, null, null); + Assert.AreEqual(null, ex.OffendingPropertyName); + Assert.AreEqual(null, ex.ObjectType); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/NumberTestObject.cs b/test/Spring/Spring.Core.Tests/Objects/NumberTestObject.cs new file mode 100644 index 00000000..66e76965 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/NumberTestObject.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Objects +{ + /// + /// + /// + /// Juergen Hoeller + public sealed class NumberTestObject + { + private short _myShort; + private int _myInt; + private long _myLong; + + public short MyShort + { + get { return _myShort; } + set { _myShort = value; } + } + + public int MyInt + { + get { return _myInt; } + set { _myInt = value; } + } + + public long MyLong + { + get { return _myLong; } + set { _myLong = value; } + } + + public float MyFloat + { + get { return _myFloat; } + set { _myFloat = value; } + } + + public double MyDouble + { + get { return _myDouble; } + set { _myDouble = value; } + } + + private float _myFloat; + private double _myDouble; + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/ObjectWrapperTests.cs b/test/Spring/Spring.Core.Tests/Objects/ObjectWrapperTests.cs new file mode 100644 index 00000000..ccc0f7c8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/ObjectWrapperTests.cs @@ -0,0 +1,1655 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +#if NET_2_0 +using System.Collections.Generic; +#endif +using System.Collections.Specialized; +using System.ComponentModel; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; + +using Common.Logging; +using Common.Logging.Simple; +using NUnit.Framework; +using Spring.Collections; +using Spring.Core; +using Spring.Core.TypeConversion; +using Spring.Objects.Factory; +using Spring.Util; + +#endregion + +namespace Spring.Objects +{ + /// + /// Unit tests for the ObjectWrapper class. + /// + /// Rod Johnson + /// Mark Pollack (.NET) + /// Rick Evans (.NET) + /// $Id: ObjectWrapperTests.cs,v 1.14 2007/07/31 18:19:58 bbaia Exp $ + [TestFixture] + public sealed class ObjectWrapperTests + { + /// + /// The setup logic executed before the execution of this test fixture. + /// + [TestFixtureSetUp] + public void FixtureSetUp() + { + // enable logging (to nowhere), just to exercisee the logging code... + LogManager.Adapter = new NoOpLoggerFactoryAdapter(); + } + + #region Classes Used During Tests + public class Person + { + private IList favoriteNames = new ArrayList(); + private IDictionary properties = new Hashtable(); + private string sillyString; + + public Person() + { + favoriteNames.Add("p1"); + favoriteNames.Add("p2"); + } + public Person(IList favNums) + { + favoriteNames = favNums; + } + + public string SillyString + { + get { return sillyString; } + } + public IList FavoriteNames + { + get { return favoriteNames; } + } + + public IDictionary Properties + { + get { return properties; } + } + public string this[int index] + { + get { return (string)favoriteNames[index]; } + set { favoriteNames[index] = value; } + } + public string this[string keyName] + { + get { return (string) properties[keyName]; } + set { properties.Add(keyName,value); } + } + public string this[int index, string keyname] + { + get { return sillyString; } + set { sillyString = index + "-" + keyname + ",val=" + value;} + } + + } + +#if NET_2_0 + public class GenericPerson + { + private List favoriteNames = new List(); + private IDictionary properties = new Dictionary(); + + + public GenericPerson() + { + favoriteNames.Add("p1"); + favoriteNames.Add("p2"); + } + public GenericPerson(List favNums) + { + favoriteNames = favNums; + } + + /* + public string this[int index] + { + get { return (string)favoriteNames[index]; } + set { favoriteNames[index] = value; } + } + */ + public string this[string keyName] + { + get { return properties[keyName]; } + set { properties.Add(keyName, value); } + } + + } +#endif + + public class AltPerson + { + private IList favoriteNames = new ArrayList(); + public AltPerson() + { + favoriteNames.Add("ap1"); + favoriteNames.Add("ap2"); + } + + public AltPerson(IList favNums) + { + favoriteNames = favNums; + } + + [IndexerName("FavoriteName")] + public string this[int index] + { + get { return (string)favoriteNames[index]; } + set { favoriteNames[index] = value; } + + } + + } + public class NoNullsList : ArrayList + { + public override int Add(object value) + { + if (value == null) + { + throw new NullReferenceException("Adding nulls is not supported in this implementation."); + } + return base.Add(value); + } + } + + + + private class Honey + { + public Honey(IList akas) + { + _akas = new Aliases(akas); + } + + protected Aliases _akas; + + public Aliases AlsoKnownAs + { + get { return _akas; } + } + + public string this[int index] + { + get { return AlsoKnownAs[index]; } + } + } + + private class NonReadableHoney : Honey + { + public NonReadableHoney(IList akas) : base(akas) + { + } + + new public Aliases AlsoKnownAs + { + set { _akas = value; } + } + } + + private class Milk + { + public Milk(Honey[] honeys) + { + } + + public Honey[] Honeys + { + set + { + } + } + } + + private sealed class Aliases + { + private IList _names; + + public Aliases() : this(new ArrayList()) + { + } + + public Aliases(IList names) + { + _names = names; + } + + public string this[int index] + { + get { return (string) _names[index]; } + set { _names[index] = value; } + } + } + + private sealed class RealNestedTestObject + { + public TestObject Datum + { + get { return _datum; } + set { _datum = value; } + } + + private TestObject _datum; + } + + private sealed class WanPropsClass + { + public bool IsWan + { + get { return true; } + } + } + + private sealed class StringAppenderConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom( + ITypeDescriptorContext context, CultureInfo culture, object val) + { + if (val is string) + { + return "OctopusOil : " + val; + } + return base.ConvertFrom(context, culture, val); + } + } + + private sealed class TestStringArrayConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string[])) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object val) + { + if (val is string[]) + { + return "-" + StringUtils.CollectionToCommaDelimitedString(val as string[]) + "-"; + } + return base.ConvertFrom(context, culture, val); + } + } + + private sealed class TestObjectArrayConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object val) + + { + if (val is string) + { + return new TestObject((string) val, 99); + } + return base.ConvertFrom(context, culture, val); + } + } + + private sealed class ObjectWithTypeProperty + { + private Type _type; + + public Type Type + { + get { return _type; } + set { _type = value; } + } + } + + /// + /// A class for use in testing the object wrapper with read only properties. + /// + private sealed class NoRead + { + public int Age + { + set + { + } + } + } + + private sealed class GetterObject + { + public string Name + { + get + { + if (_name == null) + { + throw new ApplicationException("name property must be set"); + } + return _name; + } + set { _name = value; } + } + + private string _name; + } + + private sealed class ThrowsException + { + public void DoSomething(Exception t) + { + throw t; + } + } + + internal sealed class PrimitiveArrayObject + { + public int[] Array + { + get { return array; } + set { this.array = value; } + } + + private int[] array; + } + + private sealed class PropsTest + { + public NameValueCollection Properties + { + set + { + } + } + + public String Name + { + set + { + } + } + + public String[] StringArray + { + set { this.stringArray = value; } + } + + public int[] IntArray + { + set { this.intArray = value; } + } + + public String[] stringArray; + public int[] intArray; + } + + #endregion + + #region Methods + + /// + /// Factory method for getting an ObjectWrapper instance. + /// + /// A new object wrapper (with no WrappedObject). + private ObjectWrapper GetWrapper() + { + return new ObjectWrapper(); + } + + /// + /// Factory method for getting an ObjectWrapper instance. + /// + /// + /// The object that is to be wrapped by the returned object wrapper. + /// + /// + /// A new object wrapper that wraps the supplied . + /// + private ObjectWrapper GetWrapper(object objectToBeWrapped) + { + return new ObjectWrapper(objectToBeWrapped); + } + + #endregion + + [Test] + [ExpectedException(typeof (TypeMismatchException))] + public void SetPropertyUsingValueThatNeedsConversionWithNoCustomConverterRegistered() + { + ObjectWrapper wrapper = GetWrapper(new TestObject("Rick", 30)); + // needs conversion to NestedTestObject... + wrapper.SetPropertyValue("doctor", "Pollack, Pinch, & Pounce"); + } + + [Test] + [Ignore()] + public void GetValueOfCustomIndexerProperty() + { + ObjectWrapper wrapper = GetWrapper(); + Honey darwin = new Honey(new string[] {"dickens", "of gaunt"}); + wrapper.WrappedInstance = darwin; + string alias = (string) wrapper.GetPropertyValue("AlsoKnownAs[1]"); + Assert.AreEqual("of gaunt", alias); + + alias = (string) wrapper.GetPropertyValue("[1]"); + Assert.AreEqual("of gaunt", alias, "indexer on object not working."); + wrapper.SetPropertyValue("Item[1]", "of foobar"); + alias = (string)wrapper.GetPropertyValue("[1]"); + Assert.AreEqual("of foobar", alias, "indexer on object not working."); + + } + + [Test] + [Ignore()] + public void GetSetIndexerProperties() + { + IList favNames = new ArrayList(); + favNames.Add("Master Shake"); + favNames.Add("Meatwad"); + favNames.Add("Frylock"); + Person p = new Person(favNames); + ObjectWrapper wrapper = GetWrapper(); + wrapper.WrappedInstance = p; + + //This is 'new' Spring.Expressions notation, i.e. not "Item[i]" but plain [i]. + string name = (string)wrapper.GetPropertyValue("[0]"); + Assert.AreEqual("Master Shake", name); + wrapper.SetPropertyValue("[0]", "Carl"); + name = (string)wrapper.GetPropertyValue("[0]"); + Assert.AreEqual("Carl", name); + + //Try with Custom Indexer Name. + favNames = new ArrayList(); + favNames.Add("Master Shake"); + favNames.Add("Meatwad"); + favNames.Add("Frylock"); + AltPerson ap = new AltPerson(favNames); + wrapper = GetWrapper(); + wrapper.WrappedInstance = ap; + + name = (string)wrapper.GetPropertyValue("FavoriteName[0]"); + Assert.AreEqual("Master Shake", name); + + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void GetValueOfCustomIndexerPropertyWithMalformedIndexer() + { + ObjectWrapper wrapper = GetWrapper(); + Honey darwin = new Honey(new string[] {"dickens", "of gaunt"}); + wrapper.WrappedInstance = darwin; + wrapper.GetPropertyValue("AlsoKnownAs[1"); + } + + [Test] + [ExpectedException(typeof (FatalObjectException))] + public void GetPropertyTypeWithNullPropertyPath() + { + GetWrapper().GetPropertyType(null); + } + + [Test] + [ExpectedException(typeof (FatalObjectException))] + public void GetPropertyTypeWithEmptyPropertyPath() + { + GetWrapper().GetPropertyType(string.Empty); + } + + [Test] + [ExpectedException(typeof (FatalObjectException))] + public void GetPropertyTypeWithWhitespacedPropertyPath() + { + GetWrapper().GetPropertyType(" "); + } + + [Test] + public void GetPropertyType() + { + ObjectWrapper wrapper = GetWrapper(new TestObject()); + Type propertyType = wrapper.GetPropertyType("name"); + Assert.AreEqual(typeof (string), propertyType); + } + + [Test] + public void GetPropertyTypeFromField_SPRNET502() + { + ObjectWrapper wrapper = GetWrapper(new TestObject()); + Type propertyType = wrapper.GetPropertyType("FileModeEnum"); + Assert.AreEqual(typeof (FileMode), propertyType); + } + + [Test] + public void GetPropertyTypeWithNestedLookup() + { + TestObject target = new TestObject(); + target.Spouse = new TestObject("Fiona", 28); + ObjectWrapper wrapper = GetWrapper(target); + Type propertyType = wrapper.GetPropertyType("spouse.age"); + Assert.AreEqual(typeof (int), propertyType); + } + + [Test] + [ExpectedException(typeof (NotWritablePropertyException))] + public void SetValueOfCustomIndexerPropertyWithNonReadablePropertyInIndexedPath() + { + ObjectWrapper wrapper = GetWrapper(); + Honey[] honeys = new NonReadableHoney[] {new NonReadableHoney(new string[] {"hsu", "feng"})}; + wrapper.WrappedInstance = new Milk(honeys); + wrapper.SetPropertyValue("Honeys[0][0]", "mei"); + } + + [Test] + [ExpectedException(typeof (TypeMismatchException))] + public void SetPropertyThatRequiresTypeConversionWithNonConvertibleType() + { + ObjectWrapper wrapper = GetWrapper(); + TestObject to = new TestObject(); + wrapper.WrappedInstance = to; + wrapper.SetPropertyValue("RealLawyer", "Noob"); + Console.Out.WriteLine(to.RealLawyer); + } + + /// + /// This test blows up because index is out of range. + /// + [Test] + [ExpectedException(typeof(InvalidPropertyException))] + public void SetIndexedPropertyOnListThatsOutOfRange() + { + TestObject to = new TestObject(); + ObjectWrapper wrapper = GetWrapper(to); + to.Friends = new NoNullsList(); + wrapper.SetPropertyValue("Friends[5]", "Inheritance Tax"); + } + + /// + /// Tests that a property lookup such as foo[0][1]['ben'][12] is ok + /// Well, rather it tests that its valid... 'cos man, that shoh ain't ok :D + /// + [Test] + public void GetChainedIndexers() + { + Honey to = new Honey(new string[] {"wee", "jakey", "ned"}); + ObjectWrapper wrapper = GetWrapper(to); + Assert.AreEqual('j', wrapper.GetPropertyValue("AlsoKnownAs[1][0]")); + } + + /// + /// Test that passing a null value of the object to manipulate with the ObjectWrapper + /// will throw a FatalPropertyException with the correct exception message. + /// + [Test] + [ExpectedException(typeof (FatalObjectException))] + public void NullObject() + { + new ObjectWrapper((object) null); + } + + [Test] + [ExpectedException(typeof (FatalObjectException))] + public void InstantiateWithInterfaceType() + { + new ObjectWrapper(typeof (IList)); + } + + [Test] + [ExpectedException(typeof (FatalObjectException))] + public void InstantiateWithAbstractType() + { + new ObjectWrapper(typeof (AbstractObjectFactoryTests)); + } + + [Test] + public void InstantiateWithOkType() + { + IObjectWrapper wrapper = GetWrapper(typeof (TestObject)); + Assert.IsNotNull(wrapper.WrappedInstance); + } + + [Test] + public void NestedProperties() + { + string doctorCompany = ""; + string lawyerCompany = "Dr. Sueem"; + TestObject to = new TestObject(); + IObjectWrapper wrapper = GetWrapper(to); + wrapper.SetPropertyValue("Doctor.Company", doctorCompany); + wrapper.SetPropertyValue("Lawyer.Company", lawyerCompany); + Assert.AreEqual(doctorCompany, to.Doctor.Company); + Assert.AreEqual(lawyerCompany, to.Lawyer.Company); + } + + [Test] + public void GetterThrowsException() + { + GetterObject go = new GetterObject(); + IObjectWrapper wrapper = GetWrapper(go); + wrapper.SetPropertyValue("Name", "tom"); + Assert.IsTrue(go.Name.Equals("tom"), "Expected name to be set to 'tom'"); + } + + [Test] + [ExpectedException(typeof (NotReadablePropertyException))] + public void TryToReadTheValueOfAWriteOnlyProperty() + { + NoRead nr = new NoRead(); + ObjectWrapper wrapper = GetWrapper(nr); + wrapper.GetPropertyValue("Age"); + } + + [Test] + [ExpectedException(typeof(NullValueInNestedPathException))] + public void TryToReadAnIndexedValueFromANullProperty() + { + TestObject o = new TestObject(); + o.Friends = null; + ObjectWrapper wrapper = GetWrapper(o); + wrapper.GetPropertyValue("Friends[2]"); + } + + /// + /// Test that applying an empty MutablePropertyValues does not modify the object contents. + /// + [Test] + public void EmptyPropertyValuesSet() + { + TestObject t = new TestObject(); + int age = 50; + string name = "Tony"; + t.Age = age; + t.Name = name; + IObjectWrapper wrapper = GetWrapper(t); + Assert.IsTrue(t.Age.Equals(age), "Age is not set correctly"); + Assert.IsTrue(name.Equals(t.Name), "Name is not set correctly"); + wrapper.SetPropertyValues(new MutablePropertyValues()); + Assert.IsTrue(t.Age.Equals(age), "Age is not set correctly"); + Assert.IsTrue(name.Equals(t.Name), "Name is not set correctly"); + } + + /// + /// Test basic ObjectWrapper functionality by setting properties using MutablePropertyValues. + /// + [Test] + public void AllValid() + { + TestObject t = new TestObject(); + string newName = "tony"; + int newAge = 65; + string newTouchy = "valid"; + IObjectWrapper wrapper = GetWrapper(t); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add(new PropertyValue("Age", newAge)); + pvs.Add(new PropertyValue("Name", newName)); + pvs.Add(new PropertyValue("Touchy", newTouchy)); + wrapper.SetPropertyValues(pvs); + Assert.IsTrue(t.Name.Equals(newName), "Validly set property must stick"); + Assert.IsTrue(t.Touchy.Equals(newTouchy), "Validly set property must stick"); + Assert.IsTrue(t.Age == newAge, "Validly set property must stick"); + } + + [Test] + public void IndividualAllValid() + { + TestObject t = new TestObject(); + String newName = "tony"; + int newAge = 65; + string newTouchy = "valid"; + IObjectWrapper wrapper = GetWrapper(t); + wrapper.SetPropertyValue("Age", newAge); + wrapper.SetPropertyValue(new PropertyValue("Name", newName)); + wrapper.SetPropertyValue(new PropertyValue("Touchy", newTouchy)); + Assert.IsTrue(t.Name.Equals(newName), "Validly set property must stick"); + Assert.IsTrue(t.Touchy.Equals(newTouchy), "Validly set property must stick"); + Assert.IsTrue(t.Age == newAge, "Validly set property must stick"); + } + + [Test] + public void SettingAnInvalidValue() + { + TestObject t = new TestObject(); + string newName = "tony"; + try + { + IObjectWrapper wrapper = GetWrapper(t); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.Add(new PropertyValue("Age", "foobar")); + pvs.Add(new PropertyValue("Name", newName)); + wrapper.SetPropertyValues(pvs); + Assert.Fail("Should throw exception when setting an invalid value"); + } + catch (PropertyAccessExceptionsException ex) + { + Assert.IsTrue(ex.ExceptionCount == 1, "Must contain 2 exceptions"); + // Test validly set property matches + Assert.IsTrue(t.Name.Equals(newName), "Validly set property must stick"); + Assert.IsTrue(t.Age == 0, "Invalidly set property must retain old value"); + } + catch (Exception ex) + { + Assert.Fail( + "Shouldn't throw exception other than PropertyAccessExceptions. Exception Message = " + ex.Message); + } + } + + [Test] + public void ArrayToStringConversion() + { + TestObject t = new TestObject(); + IObjectWrapper wrapper = GetWrapper(t); + TypeConverterRegistry.RegisterConverter(typeof(string), new TestStringArrayConverter()); + wrapper.SetPropertyValue("Name", new string[] {"a", "b"}); + Assert.AreEqual("-a,b-", t.Name); + } + + [Test] + public void ArrayToArrayConversion() + { + IndexedTestObject to = new IndexedTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + TypeConverterRegistry.RegisterConverter(typeof(TestObject), new TestObjectArrayConverter()); + wrapper.SetPropertyValue("Array", new string[] {"a", "b"}); + Assert.AreEqual("a", to.Array[0].Name); + Assert.AreEqual("b", to.Array[1].Name); + } + + [Test] + public void PrimitiveArray() + { + PrimitiveArrayObject to = new PrimitiveArrayObject(); + IObjectWrapper wrapper = GetWrapper(to); + wrapper.SetPropertyValue("Array", new String[] {"1", "2"}); + Assert.AreEqual(2, to.Array.Length); + Assert.AreEqual(1, to.Array[0]); + Assert.AreEqual(2, to.Array[1]); + } + + [Test] + public void PrimitiveArrayFromCommaDelimitedString() + { + PrimitiveArrayObject to = new PrimitiveArrayObject(); + IObjectWrapper wrapper = GetWrapper(to); + wrapper.SetPropertyValue("Array", "1,2"); + Assert.AreEqual(2, to.Array.Length); + Assert.AreEqual(1, to.Array[0]); + Assert.AreEqual(2, to.Array[1]); + } + + [Test] + public void ObjectWrapperUpdates() + { + TestObject to = new TestObject("Rick", 2); + int newAge = 33; + ObjectWrapper wrapper = GetWrapper(to); + to.Age = newAge; + object owAge = wrapper.GetPropertyValue("Age"); + Assert.IsTrue(owAge is Int32, "Age is an integer"); + int owi = (int) owAge; + Assert.IsTrue(owi == newAge, "Object wrapper must pick up changes"); + } + + [Test] + public void SetWrappedInstanceOfSameClass() + { + TestObject to = new TestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Age = 11; + + TestObject to2 = new TestObject(); + wrapper.WrappedInstance = to2; + + wrapper.SetPropertyValue("Age", 14); + Assert.IsTrue(to2.Age == 14, "2nd changed"); + Assert.IsTrue(to.Age == 11, "1st didn't change"); + } + + [Test] + public void SetWrappedInstanceOfDifferentClass() + { + ThrowsException tex = new ThrowsException(); + ObjectWrapper wrapper = GetWrapper(tex); + TestObject to2 = new TestObject(); + wrapper.WrappedInstance = to2; + + wrapper.SetPropertyValue("Age", 14); + Assert.IsTrue(to2.Age == 14); + } + + [Test] + [ExpectedException(typeof (TypeMismatchException))] + public void TypeMismatch() + { + TestObject to = new TestObject(); + IObjectWrapper wrapper = GetWrapper(to); + wrapper.SetPropertyValue("Age", "foobar"); + } + + [Test] + [ExpectedException(typeof (TypeMismatchException))] + public void EmptyValueForPrimitiveProperty() + { + TestObject to = new TestObject(); + ObjectWrapper wrapper = GetWrapper(to); + wrapper.SetPropertyValue("Age", ""); + } + + [Test] + public void GetProtectedPropertyValue() + { + TestObject foo = new TestObject(); + IObjectWrapper wrapper = GetWrapper(foo); + string happyPlace = (string) wrapper.GetPropertyValue("HappyPlace"); + Assert.AreEqual(TestObject.DefaultHappyPlace, happyPlace, + "Failed to read the value of a property with 'protected' access."); + } + + [Test] + public void SetProtectedPropertyValue() + { + TestObject foo = new TestObject(); + IObjectWrapper wrapper = GetWrapper(foo); + const string expectedHappyPlace = "The Rockies! Brrr..."; + wrapper.SetPropertyValue(new PropertyValue("HappyPlace", expectedHappyPlace)); + string happyPlace = (string) wrapper.GetPropertyValue("HappyPlace"); + Assert.AreEqual(expectedHappyPlace, happyPlace, + "Failed to read / write the value of a property with 'protected' access."); + } + + [Test] + public void GetPrivatePropertyValue() + { + TestObject foo = new TestObject(); + IObjectWrapper wrapper = GetWrapper(foo); + string[] contents = (string[]) wrapper.GetPropertyValue("SamsoniteSuitcase"); + Assert.IsTrue(ArrayUtils.AreEqual(TestObject.DefaultContentsOfTheSuitcase, contents), + "Failed to read the value of a property with 'private' access."); + } + + [Test] + public void SetPrivatePropertyValue() + { + TestObject foo = new TestObject(); + IObjectWrapper wrapper = GetWrapper(foo); + string[] expectedContents = new string[] {"Lloyd's Soul", "Harry's John Denver Records"}; + wrapper.SetPropertyValue(new PropertyValue("SamsoniteSuitcase", expectedContents)); + string[] contents = (string[]) wrapper.GetPropertyValue("SamsoniteSuitcase"); + Assert.IsTrue(ArrayUtils.AreEqual(expectedContents, contents), + "Failed to read / write the value of a property with 'private' access."); + } + + [Test] + public void GetNestedProperty() + { + ITestObject rod = new TestObject("rod", 31); + ITestObject kerry = new TestObject("kerry", 35); + rod.Spouse = kerry; + kerry.Spouse = rod; + IObjectWrapper wrapper = GetWrapper(rod); + int KA = (int) wrapper.GetPropertyValue("Spouse.Age"); + Assert.IsTrue(KA == 35, "Expected kerry's age to be 35"); + + int RA = (int) wrapper.GetPropertyValue("Spouse.Spouse.Age"); + Assert.IsTrue(RA == 31, "Expected rod's age to be 31"); + + ITestObject spousesSpouse = (ITestObject) wrapper.GetPropertyValue("Spouse.Spouse"); + Assert.IsTrue(rod == spousesSpouse, "spousesSpouse == initial point"); + } + + [Test] + [ExpectedException(typeof (NullValueInNestedPathException))] + public void GetNestedPropertyValueNullValue() + { + TestObject rod = new TestObject("rod", 31); + rod.Doctor = new NestedTestObject(null); + GetWrapper(rod).GetPropertyValue("Doctor.Company.Length"); + } + + [Test] + public void SetNestedProperty() + { + ITestObject rod = new TestObject("rod", 31); + ITestObject kerry = new TestObject("kerry", 0); + IObjectWrapper wrapper = GetWrapper(rod); + wrapper.SetPropertyValue("Spouse", kerry); + + Assert.AreEqual(rod.Spouse, kerry, "nested set did not work"); + wrapper.SetPropertyValue("Spouse.Spouse", rod); + Assert.AreEqual(rod, rod.Spouse.Spouse, "Nested set did not work."); + wrapper.SetPropertyValue("Spouse.Age", 100); + Assert.AreEqual(100, kerry.Age, "Nested setting of primitive property did not work."); + } + + [Test] + public void SetPropertyValue() + { + ITestObject bigby = new TestObject("Bigby", 4500); + ITestObject snow = new TestObject("Snow", 2500); + IObjectWrapper wrapper = GetWrapper(bigby); + wrapper.SetPropertyValue("Spouse", snow); + Assert.AreEqual(snow, bigby.Spouse); + } + + [Test] + [ExpectedException(typeof (NullValueInNestedPathException))] + public void SetIndexedPropertyValueOnUninitializedPath() + { + TestObject obj = new TestObject("Bill", 4500); + IObjectWrapper wrapper = GetWrapper(obj); + wrapper.SetPropertyValue("hats [0]", "Hicks & Co"); + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void SetIndexedPropertyValueOnNonIndexableType() + { + TestObject obj = new TestObject("Bill", 4500); + IObjectWrapper wrapper = GetWrapper(obj); + wrapper.SetPropertyValue("doctor [0]", "Hicks & Co"); + } + + [Test] + [ExpectedException(typeof (TypeMismatchException))] + public void SetPrimitivePropertyToNullReference() + { + TestObject obj = new TestObject("Bill", 4500); + IObjectWrapper wrapper = GetWrapper(obj); + wrapper.SetPropertyValue("Age", null); + } + + [Test] + public void SetPropertyValueIgnoresCase() + { + ITestObject bigby = new TestObject("Bigby", 4500); + ITestObject snow = new TestObject("Snow", 2500); + IObjectWrapper wrapper = GetWrapper(bigby); + wrapper.SetPropertyValue("spouse", snow); + Assert.AreEqual(snow, bigby.Spouse, + "Property setting is not case insensitive with regard to the property name (and for CLS compliance it should be)."); + } + + [Test] + public void IntArrayProperty() + { + PropsTest pt = new PropsTest(); + IObjectWrapper wrapper = GetWrapper(pt); + wrapper.SetPropertyValue("IntArray", new int[] {4, 5, 2, 3}); + Assert.IsTrue(pt.intArray.Length == 4, "intArray length = 4"); + Assert.IsTrue(pt.intArray[0] == 4 && pt.intArray[1] == 5 && pt.intArray[2] == 2 && pt.intArray[3] == 3, + "correct values"); + wrapper.SetPropertyValue("IntArray", new String[] {"4", "5", "2", "3"}); + Assert.IsTrue(pt.intArray.Length == 4, "intArray length = 4"); + Assert.IsTrue(pt.intArray[0] == 4 && pt.intArray[1] == 5 && pt.intArray[2] == 2 && pt.intArray[3] == 3, + "correct values"); + wrapper.SetPropertyValue("IntArray", 1); + Assert.IsTrue(pt.intArray.Length == 1, "intArray length = 1"); + Assert.IsTrue(pt.intArray[0] == 1, "correct values"); + wrapper.SetPropertyValue("IntArray", new String[] {"1"}); + Assert.IsTrue(pt.intArray.Length == 1, "intArray length = 1"); + Assert.IsTrue(pt.intArray[0] == 1, "correct values"); + } + + [Test] + public void NewWrappedInstancePropertyValuesGet() + { + ObjectWrapper wrapper = GetWrapper(); + TestObject t = new TestObject("Tony", 50); + wrapper.WrappedInstance = t; + Assert.AreEqual(t.Age, wrapper.GetPropertyValue("Age"), "Object wrapper returns wrong property value"); + + TestObject u = new TestObject("Udo", 30); + wrapper.WrappedInstance = u; + Assert.AreEqual(u.Age, wrapper.GetPropertyValue("Age"), "Object wrapper returns cached property value"); + } + + [Test] + public void NewWrappedInstanceNestedPropertyValuesGet() + { + IObjectWrapper wrapper = GetWrapper(); + TestObject t = new TestObject("Tony", 50); + t.Spouse = new TestObject("Sue", 40); + wrapper.WrappedInstance = t; + Assert.AreEqual(t.Spouse.Age, wrapper.GetPropertyValue("Spouse.Age"), + "Object wrapper returns wrong nested property value"); + + TestObject u = new TestObject("Udo", 30); + u.Spouse = new TestObject("Vera", 20); + wrapper.WrappedInstance = u; + Assert.AreEqual(u.Spouse.Age, wrapper.GetPropertyValue("Spouse.Age"), + "Object wrapper returns cached nested property value"); + } + + [Test] + public void StringArrayProperty() + { + PropsTest pt = new PropsTest(); + ObjectWrapper wrapper = GetWrapper(pt); + + wrapper.SetPropertyValue("StringArray", "foo,fi,fi,fum"); + Assert.IsTrue(pt.stringArray.Length == 4, "StringArray length = 4"); + Assert.IsTrue( + pt.stringArray[0].Equals("foo") && pt.stringArray[1].Equals("fi") && pt.stringArray[2].Equals("fi") && + pt.stringArray[3].Equals("fum"), "in correct values of string array"); + } + + #region Test for DateTime Properties + + internal class DateTimeTestObject + { + private DateTime _dt; + + public DateTime TriggerDateTime + { + get { return _dt; } + set { _dt = value; } + } + } + + [Test] + public void SetDateTimeProperty() + { + DateTimeTestObject o = new DateTimeTestObject(); + ObjectWrapper wrapper = GetWrapper(o); + + wrapper.SetPropertyValue("TriggerDateTime", "1991-10-10"); + + Assert.AreEqual(1991, ((DateTime) wrapper.GetPropertyValue("TriggerDateTime")).Year); + } + + #endregion + + #region Test for CultureInfo Properties + + internal class CultureTestObject + { + private CultureInfo _ci; + + public CultureInfo Cult + { + get { return _ci; } + set { _ci = value; } + } + } + + [Test] + public void SetCultureInfoProperty() + { + CultureTestObject o = new CultureTestObject(); + ObjectWrapper wrapper = GetWrapper(o); + + wrapper.SetPropertyValue("Cult", "es-ES"); + + Assert.AreEqual("es-ES", + ((CultureInfo) wrapper.GetPropertyValue("Cult")).Name); + } + + #endregion + + #region Tests for URI Properties + + internal class URITestObject + { + private Uri _uri; + + public Uri ResourceIdentifier + { + get { return _uri; } + set { _uri = value; } + } + } + + [Test] + public void SetURIProperty() + { + URITestObject o = new URITestObject(); + ObjectWrapper wrapper = GetWrapper(o); + + wrapper.SetPropertyValue("ResourceIdentifier", "http://www.springframework.net"); + Assert.AreEqual("www.springframework.net", + ((Uri) wrapper.GetPropertyValue("ResourceIdentifier")).Host); + } + + #endregion + + #region Tests for Indexed Array Properties + + [Test] + public void GetIndexedFromArrayProperty() + { + PrimitiveArrayObject to = new PrimitiveArrayObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Array = new int[] {1, 2, 3, 4, 5}; + Assert.AreEqual(1, (int) wrapper.GetPropertyValue("Array[0]")); + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void GetIndexOutofRangeFromArrayProperty() + { + PrimitiveArrayObject to = new PrimitiveArrayObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Array = new int[] {1, 2, 3, 4, 5}; + wrapper.GetPropertyValue("Array[5]"); + } + + [Test] + public void SetIndexedFromArrayProperty() + { + PrimitiveArrayObject to = new PrimitiveArrayObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Array = new int[] {1, 2, 3, 4, 5}; + wrapper.SetPropertyValue("Array[2]", 6); + Assert.AreEqual(6, to.Array[2]); + } + + [Test] + [ExpectedException(typeof(InvalidPropertyException))] + public void SetIndexOutOfRangeFromArrayProperty() + { + PrimitiveArrayObject to = new PrimitiveArrayObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Array = new int[] {1, 2, 3, 4, 5}; + wrapper.SetPropertyValue("Array[5]", 6); + } + + /// + /// Test that we bail when attempting to get an indexed property with some guff for the index + /// + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void GetIndexedPropertyValueWithGuffIndexFromArrayProperty() + { + PrimitiveArrayObject to = new PrimitiveArrayObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Array = new int[] {1, 2, 3, 4, 5}; + Assert.AreEqual(1, (int) wrapper.GetPropertyValue("Array[HungerHurtsButStarvingWorks]")); + } + + /// + /// Test that we bail when attempting to get an indexed property with some guff for the index + /// + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void GetIndexedPropertyValueWithMissingIndexFromArrayProperty() + { + PrimitiveArrayObject to = new PrimitiveArrayObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Array = new int[] {1, 2, 3, 4, 5}; + Assert.AreEqual(1, (int) wrapper.GetPropertyValue("Array[]")); + } + + #endregion + + #region Tests for Indexed List Properties + + internal class ListTestObject + { + private IList _list; + private ArrayList _arrayList; + + public IList List + { + get { return _list; } + set { _list = value; } + } + + public ArrayList ArrayList + { + get { return _arrayList; } + set { _arrayList = value; } + } + } + + [Test] + public void GetIndexedFromListProperty() + { + ListTestObject to = new ListTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.List = new ArrayList(new int[] {1, 2, 3, 4, 5}); + Assert.AreEqual(1, (int) wrapper.GetPropertyValue("List[0]")); + } + + [Test] + public void GetIndexedFromArrayListProperty() + { + ListTestObject to = new ListTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.ArrayList = new ArrayList(new int[] {1, 2, 3, 4, 5}); + Assert.AreEqual(1, (int) wrapper.GetPropertyValue("ArrayList[0]")); + } + + [Test] + [ExpectedException(typeof(InvalidPropertyException))] + public void GetIndexOutofRangeFromListProperty() + { + ListTestObject to = new ListTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.List = new ArrayList(new int[] {1, 2, 3, 4, 5}); + wrapper.GetPropertyValue("List[5]"); + } + + [Test] + public void SetIndexedFromListProperty() + { + ListTestObject to = new ListTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.List = new ArrayList(new int[] {1, 2, 3, 4, 5}); + wrapper.SetPropertyValue("List[0]", 6); + Assert.AreEqual(6, to.List[0]); + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void SetIndexedFromListPropertyUsingMixOfSingleAndDoubleQuotedDelimeters() + { + ListTestObject to = new ListTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.List = new ArrayList(new int[] {1, 2, 3, 4, 5}); + wrapper.SetPropertyValue("List['0\"]", 6); + Assert.AreEqual(6, to.List[0]); + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void SetIndexedFromListPropertyUsingNonNumericValueForTheIndex() + { + ListTestObject to = new ListTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.List = new ArrayList(new int[] {1, 2, 3, 4, 5}); + wrapper.SetPropertyValue("List[bingo]", 6); + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void SetIndexedFromListPropertyUsingEmptyValueForTheIndex() + { + ListTestObject to = new ListTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.List = new ArrayList(new int[] {1, 2, 3, 4, 5}); + wrapper.SetPropertyValue("List[]", 6); + } + + [Test] + [Ignore("Addition of elements to the list via index that is out of range is not supported anymore.")] + public void SetIndexOutOfRangeFromListProperty() + { + ListTestObject to = new ListTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.List = new ArrayList(new int[] {1, 2, 3, 4, 5}); + wrapper.SetPropertyValue("List[6]", 6); + Assert.AreEqual(6, to.List[6]); + Assert.IsNull(to.List[5]); + wrapper.SetPropertyValue("List[7]", 7); + Assert.AreEqual(7, to.List[7]); + } + + /// + /// Test that we bail when attempting to get an indexed property with some guff for the index + /// + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void GetIndexedPropertyValueWithGuffIndexFromListProperty() + { + ListTestObject to = new ListTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.List = new ArrayList(new int[] {1, 2, 3, 4, 5}); + Assert.AreEqual(1, (int) wrapper.GetPropertyValue("List[HungerHurtsButStarvingWorks]")); + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void GetIndexedPropertyValueWithMissingIndexFromListProperty() + { + ListTestObject to = new ListTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.List = new ArrayList(new int[] {1, 2, 3, 4, 5}); + Assert.AreEqual(1, (int) wrapper.GetPropertyValue("List[]")); + } + + #endregion + + #region Tests for Indexed Dictionary Properties + + internal class DictionaryTestObject + { + private IDictionary _dictionary; + + public IDictionary Dictionary + { + get { return _dictionary; } + set { _dictionary = value; } + } + } + + [Test] + public void GetIndexedFromDictionaryProperty() + { + DictionaryTestObject to = new DictionaryTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Dictionary = new Hashtable(); + to.Dictionary.Add("key1", "value1"); + Assert.AreEqual("value1", (string) wrapper.GetPropertyValue("Dictionary['key1']")); + } + + [Test] + public void GetIndexMissingFromDictionaryProperty() + { + DictionaryTestObject to = new DictionaryTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Dictionary = new Hashtable(); + Assert.IsNull(wrapper.GetPropertyValue("Dictionary['notthere']")); + } + + [Test] + public void SetIndexedFromDictionaryProperty() + { + DictionaryTestObject to = new DictionaryTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Dictionary = new Hashtable(); + wrapper.SetPropertyValue("Dictionary['key1']", "value1"); + Assert.AreEqual("value1", to.Dictionary["key1"]); + } + + [Test] + public void SettingADictionaryPropertyJustAddsTheValuesToTheExistingDictionary() + { + TestObject to = new TestObject(); + to.AddPeriodicElement("Hsu", "Feng"); + to.AddPeriodicElement("Piao", "Jin"); + ObjectWrapper wrapper = GetWrapper(to); + IDictionary elements = new Hashtable(); + elements.Add("Weekend", "News"); + wrapper.SetPropertyValue("PeriodictabLE", elements); + Assert.AreEqual(3, to.PeriodicTable.Count); + } + + #endregion + + #region Tests for Indexed Set Properties + + internal class SetTestObject + { + private ISet _set; + private HybridSet _aSet; + + public ISet Set + { + get { return _set; } + set { _set = value; } + } + + public HybridSet HybridSet + { + get { return _aSet; } + set { _aSet = value; } + } + } + + [Test] + public void SettingASetPropertyJustAddsTheValuesToTheExistingSet() + { + TestObject to = new TestObject(); + to.AddComputerName("Atari 2900"); + to.AddComputerName("JCN"); + ObjectWrapper wrapper = GetWrapper(to); + wrapper.SetPropertyValue("Computers", new HybridSet(new string[] {"Trusty SNES"})); + Assert.AreEqual(3, to.Computers.Count); + } + + [Test] + [ExpectedException(typeof(InvalidPropertyException))] + public void GetIndexFromSetProperty() + { + SetTestObject to = new SetTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Set = new ListSet(new int[] { 1, 2, 3, 4, 5 }); + Assert.AreEqual(1, (int)wrapper.GetPropertyValue("Set[1]")); + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void GetIndexOutofRangeFromSetProperty() + { + SetTestObject to = new SetTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Set = new ListSet(new int[] {1, 2, 3, 4, 5}); + Assert.AreEqual(1, (int) wrapper.GetPropertyValue("Set[23]")); + } + + /// + /// Test that we bail when attempting to get an indexed property with some guff for the index + /// + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void GetIndexedPropertyValueWithGuffIndexFromSetProperty() + { + SetTestObject to = new SetTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Set = new ListSet(new int[] {1, 2, 3, 4, 5}); + Assert.AreEqual(1, (int) wrapper.GetPropertyValue("Set[HungerHurtsButStarvingWorks]")); + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void GetIndexedPropertyValueWithMissingIndexFromSetProperty() + { + SetTestObject to = new SetTestObject(); + IObjectWrapper wrapper = GetWrapper(to); + to.Set = new ListSet(new int[] {1, 2, 3, 4, 5}); + object o = wrapper.GetPropertyValue("Set[]"); + Assert.AreEqual(1, (int) wrapper.GetPropertyValue("Set[]")); + } + + #endregion + + #region Test for Enumeration Properties + + internal class EnumTestObject + { + private FileMode FileModeEnum; + + public FileMode FileMode + { + get { return FileModeEnum; } + set { FileModeEnum = value; } + } + } + + [Test] + public void SetEnumProperty() + { + EnumTestObject o = new EnumTestObject(); + ObjectWrapper wrapper = GetWrapper(o); + wrapper.SetPropertyValue("FileMode", FileMode.Create); + Assert.AreEqual(FileMode.Create, (FileMode) wrapper.GetPropertyValue("FileMode")); + } + + #endregion + + [Test] + public void SetTypePropertyWithString() + { + ObjectWithTypeProperty to = new ObjectWithTypeProperty(); + IObjectWrapper wrapper = GetWrapper(to); + wrapper.SetPropertyValue("Type", "System.DateTime"); + Assert.AreEqual(typeof (DateTime), to.Type); + } + + [Test] + [ExpectedException(typeof (InvalidPropertyException))] + public void GetIndexedPropertyValueWithNonIndexedType() + { + TestObject to = new TestObject(); + IObjectWrapper wrapper = GetWrapper(to); + wrapper.GetPropertyValue("FileMode[0]"); + } + + [Test] + [ExpectedException(typeof (NullValueInNestedPathException))] + public void SetPropertyValuesWithUnknownProperty() + { + TestObject to = new TestObject(); + to.Doctor = null; + ObjectWrapper wrapper = GetWrapper(to); + wrapper.SetPropertyValue("Doctor.Company", "Bingo"); + } + + [Test] + [ExpectedException(typeof(InvalidPropertyException))] + public void SetPropertyValuesFailsWhenSettingNonExistantProperty() + { + TestObject to = new TestObject(); + ObjectWrapper wrapper = GetWrapper(to); + MutablePropertyValues values = new MutablePropertyValues(); + values.Add("JeepersCreepersWhereDidYaGetThosePeepers", "OhThisWeirdBatGuySoldEmToMe..."); + values.Add("Age", 19); + // the unknown and ridiculously named property should fail + wrapper.SetPropertyValues(values, false); + } + + [Test] + public void SetPropertyValuesDoesNotFailWhenSettingNonExistantPropertyWithIgnorUnknownSetToTrue() + { + TestObject to = new TestObject(); + ObjectWrapper wrapper = GetWrapper(to); + MutablePropertyValues values = new MutablePropertyValues(); + values.Add("Age", 19); + values.Add("JeepersCreepersWhereDidYaGetThosePeepers", "OhThisWeirdBatGuySoldEmToMe..."); + // the unknown and ridiculously named property should fail + wrapper.SetPropertyValues(values, true); + Assert.AreEqual(19, to.Age, "The single good property in the property values should have been set though..."); + } + + [Test] + [ExpectedException(typeof(NotWritablePropertyException))] + public void SetPropertyValuesFailsWhenSettingReadOnlyProperty() + { + TestObject to = new TestObject(); + ObjectWrapper wrapper = GetWrapper(to); + MutablePropertyValues values = new MutablePropertyValues(); + values.Add("ReadOnlyObjectNumber", 123); + wrapper.SetPropertyValues(values, false); + } + + [Test] + public void SetPropertyValuesDoesNotFailWhenSettingReadOnlyPropertyWithIgnorUnknownSetToTrue() + { + TestObject to = new TestObject(); + ObjectWrapper wrapper = GetWrapper(to); + MutablePropertyValues values = new MutablePropertyValues(); + values.Add("ObjectNumber", 123); + values.Add("Age", 19); + wrapper.SetPropertyValues(values, true); + Assert.AreEqual(19, to.Age, "The single good property in the property values should have been set though..."); + } + + [Test] + public void SetArrayPropertyValue() + { + string[] expected = new string[] {"Fedora", "Gacy", "Banjo"}; + TestObject to = new TestObject(); + ObjectWrapper wrapper = GetWrapper(to); + wrapper.SetPropertyValue("Hats", expected); + Assert.IsNotNull(to.Hats); + Assert.AreEqual(expected.Length, to.Hats.Length); + for (int i = 0; i < expected.Length; ++i) + { + Assert.AreEqual(expected[i], to.Hats[i]); + } + } + + [Test] + public void TestToString() + { + ObjectWrapper wrapper = GetWrapper(); + Assert.IsTrue(wrapper.ToString().IndexOf("Exception encountered") >= 0); + wrapper.WrappedInstance = new WanPropsClass(); + string expected = string.Format( + "{0}: wrapping class [{1}]; IsWan={2}", + wrapper.GetType().Name, wrapper.WrappedType.FullName, "{True}"); + Assert.AreEqual(expected, wrapper.ToString()); + } + + [Test] + [ExpectedException(typeof (FatalObjectException))] + public void GetPropertyInfoWithNullArgument() + { + ObjectWrapper wrapper = GetWrapper(new TestObject()); + wrapper.GetPropertyInfo(null); + } + + [Test] + [ExpectedException(typeof (FatalReflectionException))] + public void GetPropertyInfoWithNonPropertyExpression() + { + ObjectWrapper wrapper = GetWrapper(new TestObject()); + wrapper.GetPropertyInfo("2 + 2"); + } + + [Test] + [ExpectedException(typeof (FatalObjectException))] + public void GetPropertyInfoWithNonParsableExpression() + { + ObjectWrapper wrapper = GetWrapper(new TestObject()); + wrapper.GetPropertyInfo("["); + } + + [Test] + public void GetNestedPropertyInfo() + { + RealNestedTestObject o = new RealNestedTestObject(); + o.Datum = new TestObject(); + o.Datum.Doctor = new NestedTestObject("Modlin"); + ObjectWrapper wrapper = GetWrapper(o); + PropertyInfo info = wrapper.GetPropertyInfo("Datum.Doctor.Company"); + Assert.IsNotNull(info); + } + + [Test(Description="SPRNET-198")] + public void AmbiguousPropertyLookupIsHandledProperlyByLookingAtDerivedClassOnly() + { + ObjectWrapper wrapper = GetWrapper(new DerivedFoo()); + wrapper.GetPropertyInfo("Bar"); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/PropertyAccessExceptionsExceptionTests.cs b/test/Spring/Spring.Core.Tests/Objects/PropertyAccessExceptionsExceptionTests.cs new file mode 100644 index 00000000..be0a59ae --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/PropertyAccessExceptionsExceptionTests.cs @@ -0,0 +1,72 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; +using System.Reflection; +using NUnit.Framework; +using Spring.Core; + +namespace Spring.Objects +{ + /// + /// Unit tests for the PropertyAccessExceptionsException class. + /// + /// + /// $Id: PropertyAccessExceptionsExceptionTests.cs,v 1.4 2007/07/31 00:09:24 markpollack Exp $ + [TestFixture] + public sealed class PropertyAccessExceptionsExceptionTests + { + [Test] + public void GetNonExistantPropertyException() + { + ObjectWrapper wrapper = new ObjectWrapper(new TestObject("Rick", 23)); + PropertyAccessExceptionsException ex + = new PropertyAccessExceptionsException(wrapper, new PropertyAccessException[] + { + }); + PropertyAccessException nullException = ex.GetPropertyAccessException("Age"); + Assert.IsNull(nullException); + } + + [Test] + public void GetPropertyExceptions() + { + ObjectWrapper wrapper = new ObjectWrapper(new TestObject("Rick", 23)); + PropertyAccessExceptionsException ex + = new PropertyAccessExceptionsException(wrapper, new PropertyAccessException[] + { + new TypeMismatchException(new PropertyChangeEventArgs("Doctor", new NestedTestObject("Foo"), "rubbish"), typeof (INestedTestObject)), + new MethodInvocationException(new TargetInvocationException("Bad format", new FormatException("'12' is not a valid argument for 'foo'..")), new PropertyChangeEventArgs("Friends", new ArrayList(), "trash")) + }); + PropertyAccessException doctorPropertyException = ex.GetPropertyAccessException("Doctor"); + Assert.IsNotNull(doctorPropertyException); + Assert.AreEqual("typeMismatch", doctorPropertyException.ErrorCode); + Assert.AreEqual("Foo", ((NestedTestObject) doctorPropertyException.PropertyChangeArgs.OldValue).Company); + PropertyAccessException friendsPropertyException = ex.GetPropertyAccessException("Friends"); + Assert.IsNotNull(friendsPropertyException); + Assert.AreEqual("methodInvocation", friendsPropertyException.ErrorCode); + Assert.AreEqual(wrapper, ex.ObjectWrapper); + Assert.AreEqual(wrapper.WrappedInstance, ex.BindObject); + Assert.AreEqual(ex.Message, ex.ToString()); + + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/PropertyValueTests.cs b/test/Spring/Spring.Core.Tests/Objects/PropertyValueTests.cs new file mode 100644 index 00000000..06162169 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/PropertyValueTests.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Objects +{ + /// + /// Unit tests for the PropertyValue class. + /// + /// Rick Evans + /// $Id: PropertyValueTests.cs,v 1.6 2006/04/09 07:24:50 markpollack Exp $ + [TestFixture] + public sealed class PropertyValueTests + { + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithNulls() + { + new PropertyValue(null, null); + } + + [Test] + public void Instantiation() + { + PropertyValue val = new PropertyValue("Age", 12); + Assert.AreEqual("Age", val.Name); + Assert.AreEqual(12, val.Value); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithEmptyStringPropertyName() + { + new PropertyValue(string.Empty, 12); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationWithWhitespacePropertyName() + { + new PropertyValue(" ", 12); + } + + [Test] + public void TestEquals() + { + PropertyValue one = new PropertyValue("Age", 12); + PropertyValue two = new PropertyValue("Age", 12); + Assert.AreEqual(one, two); + Assert.AreEqual(one.GetHashCode(), two.GetHashCode()); + Assert.AreEqual(one, one); + Assert.IsFalse(one.Equals("Foo")); + } + + [Test] + public void TestToString() + { + PropertyValue one = new PropertyValue("Age", 12); + Assert.AreEqual("PropertyValue: name='Age'; value=[12].", one.ToString()); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/ResourceTestObject.cs b/test/Spring/Spring.Core.Tests/Objects/ResourceTestObject.cs new file mode 100644 index 00000000..6a908eb5 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/ResourceTestObject.cs @@ -0,0 +1,57 @@ +#region License +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion +#region Imports +using System; +using System.IO; +using Spring.Core.IO; +#endregion +namespace Spring.Objects +{ + /// + /// Object for testing IResource instances. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class ResourceTestObject + { + private IResource resource; + private Stream inputStream; + public ResourceTestObject() + { + } + public ResourceTestObject(IResource resource) + { + this.resource = resource; + } + public ResourceTestObject(IResource resource, Stream inputStream) + { + this.resource = resource; + this.inputStream = inputStream; + } + public IResource Resource + { + get { return this.resource; } + set { this.resource = value; } + } + public Stream InputStream + { + get { return this.inputStream; } + set { this.inputStream = value; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/SerializablePerson.cs b/test/Spring/Spring.Core.Tests/Objects/SerializablePerson.cs new file mode 100644 index 00000000..83f589b2 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/SerializablePerson.cs @@ -0,0 +1,81 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports +using System; +using System.Runtime.Serialization; +#endregion + +namespace Spring.Objects +{ + /// + /// Serializable implementation of the IPerson interface. + /// + /// Rod Johnson + /// Simon White (.NET) + [Serializable] + public class SerializablePerson : TestObject, IPerson, ISerializable + { + public SerializablePerson() + { + } + + protected SerializablePerson(SerializationInfo info, StreamingContext ctxt) + { + this.Age = (int) info.GetValue("Age", typeof(int)); + this.Name = (string) info.GetValue("Name", typeof(string)); + } + + public void GetObjectData(SerializationInfo info, StreamingContext ctxt) + { + info.AddValue("Age", this.Age); + info.AddValue("Name", this.Name); + } + + public int GetAge() + { + return this.Age; + } + + public void SetAge(int age) + { + this.Age = age; + } + + public string GetName() + { + return this.Name; + } + + public void SetName(string name) + { + this.Name = name; + } + + public object Echo(object obj) + { + if (obj is Exception) + { + throw (Exception)obj; + } + return obj; + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/StaticTestEventHandler.cs b/test/Spring/Spring.Core.Tests/Objects/StaticTestEventHandler.cs new file mode 100644 index 00000000..0de5a8ac --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/StaticTestEventHandler.cs @@ -0,0 +1,51 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects { + + /// + /// Exposes a static method that has a signature compatible with the + /// EventHandler delegate. + /// + /// $Id: StaticTestEventHandler.cs,v 1.3 2006/04/09 07:24:50 markpollack Exp $ + public class StaticTestEventHandler + { + public static void HandleArbitraryEvent (object sender, EventArgs e) + { + _eventWasHandled = true; + } + + public static bool EventWasHandled + { + get + { + return _eventWasHandled; + } + } + + protected static bool _eventWasHandled; + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Support/ArgumentConvertingMethodInvokerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Support/ArgumentConvertingMethodInvokerTests.cs new file mode 100644 index 00000000..ff6481a3 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Support/ArgumentConvertingMethodInvokerTests.cs @@ -0,0 +1,151 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Unit tests for the ArgumentConvertingMethodInvoker class. + /// + /// Rick Evans + /// $Id: ArgumentConvertingMethodInvokerTests.cs,v 1.4 2006/04/09 07:24:51 markpollack Exp $ + [TestFixture] + public sealed class ArgumentConvertingMethodInvokerTests + { + [Test] + public void Invoke () { + ArgumentConvertingMethodInvoker vkr = new ArgumentConvertingMethodInvoker (); + vkr.TargetObject = new Voker (); + vkr.TargetMethod = "Hi"; + vkr.Arguments = new object [] {"Rick, Mark, Griffin, Si, Choy, Aleks"}; + vkr.Prepare (); + string actual = vkr.Invoke () as string; + Assert.IsNotNull (actual); + Assert.AreEqual ("Hi Rick, Mark, Griffin, Si, Choy, Aleks", actual); + } + + [Test] + public void InvokeWithConversion () + { + ArgumentConvertingMethodInvoker vkr = new ArgumentConvertingMethodInvoker (); + Voker instance = new Voker (); + vkr.TargetObject = instance; + vkr.TargetMethod = "HiEverybody"; + // CSV string should be converted to string [] + vkr.Arguments = new object [] {"Rick, Mark, Griffin, Federico, Choy, Aleks"}; + vkr.Prepare (); + string actual = vkr.Invoke () as string; + Assert.IsNotNull (actual); + Assert.AreEqual ("Hi ya'll", actual); + Assert.IsNotNull (instance.developers); + } + + [Test] + public void InvokeAllNamedArgumentsWithConversion () + { + ArgumentConvertingMethodInvoker vkr = new ArgumentConvertingMethodInvoker (); + Voker instance = new Voker (); + vkr.TargetObject = instance; + vkr.TargetMethod = "HiEverybody"; + // CSV string should be converted to string [] + vkr.AddNamedArgument("nameS", new object [] {"Rick, Mark, Griffin, Federico, Choy, Aleks"}); + vkr.Prepare (); + string actual = vkr.Invoke () as string; + Assert.IsNotNull (actual); + Assert.AreEqual ("Hi ya'll", actual); + Assert.IsNotNull (instance.developers); + } + + [Test] + public void InvokeWithRegisteredConversion () + { + ArgumentConvertingMethodInvoker vkr = new ArgumentConvertingMethodInvoker (); + // see if custom registration filters thru... + vkr.RegisterCustomConverter (typeof (Voker), new VokerConverter ()); + vkr.TargetType = typeof (Voker); + vkr.TargetMethod = "HiVoker"; + // arg should be converted to Voker + vkr.Arguments = new object [] {"Lebowski"}; + vkr.Prepare (); + string actual = vkr.Invoke () as string; + Assert.IsNotNull (actual); + Assert.AreEqual ("Hi Lebowski", actual); + } + } + + public sealed class Voker + { + public Voker () : this ("HiroProtagonist") {} + + public Voker (string name) + { + this.name = name; + } + + public string Hi (string name) + { + return "Hi " + name; + } + + public string HiEverybody (string [] names) + { + developers = names; + return "Hi ya'll"; + } + + public static string HiVoker (Voker buddy) + { + return "Hi " + buddy.name; + } + + public string [] developers; + public string name; + } + + public sealed class VokerConverter : System.ComponentModel.TypeConverter + { + public override bool CanConvertFrom ( + System.ComponentModel.ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom (context, sourceType); + } + + public override object ConvertFrom ( + System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + if (value is string) + { + return new Voker (value as string); + } + return base.ConvertFrom (context, culture, value); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Support/AutoWiringEventHandlerValueTests.cs b/test/Spring/Spring.Core.Tests/Objects/Support/AutoWiringEventHandlerValueTests.cs new file mode 100644 index 00000000..cf493419 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Support/AutoWiringEventHandlerValueTests.cs @@ -0,0 +1,339 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Unit tests for the AutoWiringEventHandlerValue class. + /// + /// Rick Evans + /// $Id: AutoWiringEventHandlerValueTests.cs,v 1.3 2006/04/09 07:24:51 markpollack Exp $ + [TestFixture] + public class AutoWiringEventHandlerValueTests + { + /// + /// Kinda defeats the purpose of the AutoWiringEventHandlerValue class, but + /// still a supported and legal option. + /// + [Test] + public void AutowireExplicitly () + { + // create the event source, and a listener to wire to an event (s) on the source + PingSource source = new PingSource (); + PingListener listener = new PingListener (); + + // wire them up + IEventHandlerValue wirer = new AutoWiringEventHandlerValue (); + wirer.EventName = "Ping"; + wirer.MethodName = "OnPing"; + wirer.Wire (source, listener); + + // raise the event on the source + source.OnPing (); + + // ascertain that the event listener was indeed notified of the raised event + Assert.IsTrue (listener.GotPing, "The listener was not notified of the raised event."); + Assert.IsTrue (listener.PingCount == 1, "The listener was notified more than once for a single raising of the source event (registered to listen more than once?)"); + Assert.IsFalse (listener.GotPinging, "The listener was (incorrectly) notified of an event that wasn't raised."); + Assert.IsFalse (listener.GotPinged, "The listener was (incorrectly) notified of an event that wasn't raised."); + } + + [Test] + public void AutowireExplicitlyWithBadSignatureMethod () + { + // create the event source, and a listener to wire to an event (s) on the source + PingSource source = new PingSource (); + PingListener listener = new PingListener (); + + // wire them up + IEventHandlerValue wirer = new AutoWiringEventHandlerValue (); + wirer.EventName = "Ping"; + wirer.MethodName = "OnPinged"; // signature is incompatible + wirer.Wire (source, listener); + + // raise the event on the source + source.OnPing (); + + // ascertain that the event listener was not notified of the raised event + Assert.IsFalse (listener.GotPing, "The listener was (incorrectly) notified of the raised event."); + Assert.IsFalse (listener.GotPinging, "The listener was (incorrectly) notified of an event that wasn't raised."); + Assert.IsFalse (listener.GotPinged, "The listener was (incorrectly) notified of an event that wasn't raised."); + } + + [Test] + public void AutowireAllHandlersWithCompatibleSignatures () + { + // create the event source, and a listener to wire to an event (s) on the source + PingSource source = new PingSource (); + PingListener listener = new PingListener (); + + // wire them up (any methods on the listener that have a compatible signature will be wired up) + IEventHandlerValue wirer = new AutoWiringEventHandlerValue (); + wirer.EventName = "^Ping$"; + wirer.Wire (source, listener); + + // raise the event on the source + source.OnPing (); + + // ascertain that the event handler was indeed notified of the raised event + Assert.IsTrue (listener.GotPing, "The listener was not notified of the raised event."); + Assert.IsFalse (listener.GotPinging, "The listener was (incorrectly) notified of an event that wasn't raised."); + Assert.IsFalse (listener.GotPinged, "The listener was (incorrectly) notified of an event that wasn't raised."); + } + + [Test] + public void AutowireAllEventsOnSource () + { + // create the event source, and a listener to wire to an event (s) on the source + PingSource source = new PingSource (); + PingListener listener = new PingListener (); + + // wire them up (any methods on the listener that have a signature compatible + // with any event exposed on the source will be wired up) + IEventHandlerValue wirer = new AutoWiringEventHandlerValue (); + wirer.Wire (source, listener); + + // raise the event (s) on the source + source.OnPinging (); + source.OnPing (); + source.OnPinged (); + + // ascertain that the event listener was indeed notified of the raised event + Assert.IsTrue (listener.GotPinging, "The listener was not notified of the raised event."); + Assert.IsTrue (listener.GotPing, "The listener was not notified of the raised event."); + Assert.IsTrue (listener.GotPinged, "The listener was not notified of the raised event."); + } + + [Test] + public void AutowiringIsCaseInsensitive () + { + // create the event source, and a listener to wire to an event (s) on the source + PingSource source = new PingSource (); + PingListener listener = new PingListener (); + + // wire them up + IEventHandlerValue wirer = new AutoWiringEventHandlerValue (); + wirer.EventName = "pInG"; + wirer.MethodName = "onpiNg"; + wirer.Wire (source, listener); + + // raise the event (s) on the source + source.OnPing (); + + // ascertain that the event listener was indeed notified of the raised event + Assert.IsFalse (listener.GotPinging, "The listener was (incorrectly) notified of the raised event."); + Assert.IsTrue (listener.GotPing, "The listener was not notified of the raised event."); + Assert.IsFalse (listener.GotPinged, "The listener was (incorrectly) notified of the raised event."); + } + + [Test] + public void AllCompatibleSignaturesAreWired () + { + // create the event source, and a listener to wire to an event (s) on the source + PingSource source = new PingSource (); + PingListener listener = new PingListener (); + + // wire them up + IEventHandlerValue wirer = new AutoWiringEventHandlerValue (); + wirer.EventName = "Pinging"; + wirer.MethodName = ".+"; // both OnPinging and OnPinged have compatible sigs :'( + wirer.Wire (source, listener); + + // raise the event (s) on the source + source.OnPinging (); + + // ascertain that the event listener was indeed notified of the raised event + Assert.IsTrue (listener.GotPinging, "The listener was not notified of the raised event."); + Assert.IsFalse (listener.GotPing, "The listener was (incorrectly) notified of the raised event."); + // the moral of the story is... + // don't use greedy method name regex's for autowiring, be explicit + Assert.IsTrue (listener.GotPinged, "The listener was not notified of the raised event."); + } + + [Test] + public void TestDefaultMethodMatching () + { + // create the event source, and a listener to wire to an event (s) on the source + PingSource source = new PingSource (); + PingListener listener = new PerversePingListener (); + + // wire them up + IEventHandlerValue wirer = new AutoWiringEventHandlerValue (); + wirer.EventName = "^Ping$"; + wirer.MethodName = ".+${event}.+"; // matches 'WhatClubDoYouUsePingAhGood' + wirer.Wire (source, listener); + + // raise the event on the source + source.OnPing (); + + // ascertain that the event listener was not notified of the raised event + Assert.IsTrue (listener.GotPing, "The listener was not notified of the raised event."); + Assert.IsFalse (listener.GotPinging, "The listener was (incorrectly) notified of an event that wasn't raised."); + Assert.IsFalse (listener.GotPinged, "The listener was (incorrectly) notified of an event that wasn't raised."); + } + } + + internal class PingEventArgs : EventArgs + { + } + + internal delegate bool PingEventHandler (object sender, PingEventArgs e); + + internal class PingSource + { + public event EventHandler Pinging; + + public event PingEventHandler Ping; + + public event EventHandler Pinged; + + public void OnPinging () + { + if (Pinging != null) + { + Pinging (this, EventArgs.Empty); + } + } + + public void OnPing () + { + if (Ping != null) + { + Ping (this, new PingEventArgs ()); + } + } + + public void OnPinged () + { + if (Pinged != null) + { + Pinged (this, EventArgs.Empty); + } + } + } + + internal class PingListener + { + public void OnPinging (object sender, EventArgs e) + { + _pinging = true; + ++_pingingCount; + } + + private bool OnPing (object sender, PingEventArgs e) + { + _ping = true; + ++_pingCount; + return true; + } + + public void OnPinged (object sender, EventArgs e) + { + _pinged = true; + ++_pingedCount; + } + + public bool GotPinging + { + get + { + return _pinging; + } + } + + public bool GotPing + { + get + { + return _ping; + } + } + + public bool GotPinged + { + get + { + return _pinged; + } + } + + public int PingingCount + { + get + { + return _pingingCount; + } + } + + public int PingCount + { + get + { + return _pingCount; + } + } + + public int PingedCount + { + get + { + return _pingedCount; + } + } + + protected bool _pinging; + protected bool _ping; + protected bool _pinged; + + protected int _pingingCount; + protected int _pingCount; + protected int _pingedCount; + } + + /// + /// Specialisation with obtuse names for listener methods. + /// + internal class PerversePingListener : PingListener + { + public void PingingListener (object sender, EventArgs e) + { + _pinging = true; + } + + public bool WhatClubDoYouUsePingAhGood (object sender, PingEventArgs e) + { + _ping = true; + return true; + } + + public void AHotDogImPingedOnMyLiason (object sender, EventArgs e) + { + _pinged = true; + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Support/InstanceEventHandlerValueTests.cs b/test/Spring/Spring.Core.Tests/Objects/Support/InstanceEventHandlerValueTests.cs new file mode 100644 index 00000000..c07a752b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Support/InstanceEventHandlerValueTests.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Unit tests for the InstanceEventHandlerValue class. + /// + /// Rick Evans + /// $Id: InstanceEventHandlerValueTests.cs,v 1.5 2006/04/09 07:24:51 markpollack Exp $ + [TestFixture] + public sealed class InstanceEventHandlerValueTests + { + [Test] + public void Instantiation () + { + PingSource source = new PingSource (); + StaticEventHandlerValue wirer + = new StaticEventHandlerValue (source, "OnPing"); + Assert.IsNotNull (wirer.Source); + Assert.AreEqual ("OnPing", wirer.MethodName); + Assert.IsTrue (wirer.ToString ().IndexOf (wirer.MethodName) > 0); + } + + [Test] + public void Wire () { + InstanceEventHandlerValue wirer = new InstanceEventHandlerValue (); + wirer.EventName = "Ping"; + wirer.MethodName = "OnPing"; + PingSource source = new PingSource (); + PingListener sink = new PingListener (); + wirer.Wire (source, sink); + source.OnPing (); + Assert.IsTrue (sink.GotPing, "The event handler did not get notified when the event was raised."); + Assert.AreEqual (1, sink.PingCount, "The event handler was not get notified exactly once when the event was raised exactly once."); + } + + [Test] + public void WireWithStaticHandler () + { + PingSource source = new PingSource (); + InstanceEventHandlerValue wirer + = new InstanceEventHandlerValue (source, "OnPing"); + wirer.EventName = "Ping"; + wirer.Wire (source, typeof (StaticPingListener)); + } + + [Test] + [ExpectedException (typeof (FatalObjectException))] + public void BailsIfMethodDoesntExist () + { + PingSource source = new PingSource (); + InstanceEventHandlerValue wirer + = new InstanceEventHandlerValue (source, "Roo"); + wirer.EventName = "Ping"; + wirer.Wire (source, typeof (StaticPingListener)); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Support/MethodInvokerTests.cs b/test/Spring/Spring.Core.Tests/Objects/Support/MethodInvokerTests.cs new file mode 100644 index 00000000..7668c8b1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Support/MethodInvokerTests.cs @@ -0,0 +1,335 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; +using Spring.Core; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Unit tests for the MethodInvoker class. + /// + /// Rick Evans + /// $Id: MethodInvokerTests.cs,v 1.4 2007/08/04 01:05:41 bbaia Exp $ + [TestFixture] + public sealed class MethodInvokerTests + { + [Test] + public void Instantiation() + { + MethodInvoker vkr = new MethodInvoker(); + Assert.IsNotNull(vkr.Arguments); + } + + [Test] + public void SettingNamedArgumentsToNullJustClearsOutAnyNamedArguments() + { + MethodInvoker vkr = new MethodInvoker(); + vkr.AddNamedArgument("age", 10); + vkr.NamedArguments = null; + Assert.IsNotNull(vkr.NamedArguments); + Assert.AreEqual(0, vkr.NamedArguments.Count); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "One of either the 'TargetType' or 'TargetObject' properties is required.")] + public void PrepareWithOnlyTargetMethodSet() + { + MethodInvoker vkr = new MethodInvoker(); + vkr.TargetMethod = "Foo"; + vkr.Prepare(); + } + + [Test] + public void ArgumentsProperty() + { + MethodInvoker vkr = new MethodInvoker(); + vkr.Arguments = null; + Assert.IsNotNull(vkr.Arguments); // should always be the empty object array, never null + Assert.AreEqual(0, vkr.Arguments.Length); + vkr.Arguments = new string[] {"Chank Pop"}; + Assert.AreEqual(1, vkr.Arguments.Length); + } + + [Test] + public void InvokeWithStaticMethod() + { + MethodInvoker mi = new MethodInvoker(); + mi.TargetType = typeof(Int32); + mi.TargetMethod = "Parse"; + mi.Arguments = new object[] { "27" }; + mi.Prepare(); + object actual = mi.Invoke(); + Assert.IsNotNull(actual); + Assert.AreEqual(typeof(int), actual.GetType()); + Assert.AreEqual(27, (int)actual); + } + +#if NET_2_0 + [Test] + public void InvokeWithGenericStaticMethod() + { + MethodInvoker mi = new MethodInvoker(); + mi.TargetType = typeof(Activator); + mi.TargetMethod = "CreateInstance"; + mi.Prepare(); + object actual = mi.Invoke(); + Assert.IsNotNull(actual); + Assert.AreEqual(typeof(TestObject), actual.GetType()); + } +#endif + + [Test] + public void InvokeWithOKArguments() + { + Foo foo = new Foo(); + foo.Age = 88; + MethodInvoker vkr = new MethodInvoker(); + vkr.TargetObject = foo; + vkr.TargetMethod = "GrowOlder"; + vkr.Arguments = new object[] {10}; + vkr.Prepare(); + object actual = vkr.Invoke(); + Assert.AreEqual(98, actual); + } + + [Test] + public void InvokeWithOKArgumentsAndMixedCaseMethodName() + { + Foo foo = new Foo(); + foo.Age = 88; + MethodInvoker vkr = new MethodInvoker(); + vkr.TargetObject = foo; + vkr.TargetMethod = "growolder"; + vkr.Arguments = new object[] {10}; + vkr.Prepare(); + object actual = vkr.Invoke(); + Assert.AreEqual(98, actual); + } + + [Test] + public void InvokeWithNamedArgument() + { + Foo foo = new Foo(); + foo.Age = 88; + MethodInvoker vkr = new MethodInvoker(); + vkr.TargetObject = foo; + vkr.TargetMethod = "growolder"; + vkr.AddNamedArgument("years", 10); + vkr.Prepare(); + object actual = vkr.Invoke(); + Assert.AreEqual(98, actual); + } + + [Test] + public void InvokeWithMixOfNamedAndPlainVanillaArguments() + { + int maximumAge = 95; + Foo foo = new Foo(); + foo.Age = 88; + MethodInvoker vkr = new MethodInvoker(); + vkr.TargetObject = foo; + vkr.TargetMethod = "GrowOlderUntilMaximumAgeReached"; + vkr.AddNamedArgument("years", 10); + vkr.Arguments = new object[] {maximumAge}; + vkr.Prepare(); + object actual = vkr.Invoke(); + Assert.AreEqual(maximumAge, actual); + } + + [Test] + public void InvokeWithMixOfNamedAndPlainVanillaArgumentsOfDifferingTypes() + { + int maximumAge = 95; + string expectedStatus = "Old Age Pensioner"; + Foo foo = new Foo(); + foo.Age = 88; + MethodInvoker vkr = new MethodInvoker(); + vkr.TargetObject = foo; + vkr.TargetMethod = "GrowOlderUntilMaximumAgeReachedAndSetStatusName"; + vkr.AddNamedArgument("years", 10); + vkr.AddNamedArgument("status", expectedStatus); + vkr.Arguments = new object[] {maximumAge}; + vkr.Prepare(); + object actual = vkr.Invoke(); + Assert.AreEqual(maximumAge, actual); + Assert.AreEqual(expectedStatus, foo.Status); + } + + [Test] + [ExpectedException( + typeof (ArgumentException), + "The named argument 'southpaw' could not be found on the 'GrowOlder' method of class [Spring.Objects.Support.MethodInvokerTests+Foo].")] + public void InvokeWithNamedArgumentThatDoesNotExist() + { + Foo foo = new Foo(); + foo.Age = 88; + MethodInvoker vkr = new MethodInvoker(); + vkr.TargetObject = foo; + vkr.TargetMethod = "growolder"; + vkr.AddNamedArgument("southpaw", 10); + vkr.Prepare(); + object actual = vkr.Invoke(); + Assert.AreEqual(98, actual); + } + + /// + /// Tests CLS case insensitivity compliance... + /// + [Test] + public void InvokeWithWeIRdLyCasedNamedArgument() + { + Foo foo = new Foo(); + foo.Age = 88; + MethodInvoker vkr = new MethodInvoker(); + vkr.TargetObject = foo; + vkr.TargetMethod = "gROwOldeR"; + vkr.AddNamedArgument("YEarS", 10); + vkr.Prepare(); + object actual = vkr.Invoke(); + Assert.AreEqual(98, actual); + } + + [Test] + [ExpectedException(typeof (MethodInvocationException), "At least one of the arguments passed to this MethodInvoker was incompatible with the signature of the invoked method.")] + public void InvokeWithArgumentOfWrongType() + { + Foo foo = new Foo(); + foo.Age = 88; + MethodInvoker vkr = new MethodInvoker(); + vkr.TargetObject = foo; + vkr.TargetMethod = "growolder"; + vkr.AddNamedArgument("years", "Bingo"); + vkr.Prepare(); + vkr.Invoke(); + } + + [Test] + public void NamedArgumentsOverwriteEachOther() + { + Foo foo = new Foo(); + foo.Age = 88; + MethodInvoker vkr = new MethodInvoker(); + vkr.TargetObject = foo; + vkr.TargetMethod = "growolder"; + vkr.AddNamedArgument("years", 10); + // this second setting must overwrite the first... + vkr.AddNamedArgument("years", 200); + vkr.Prepare(); + object actual = vkr.Invoke(); + Assert.IsFalse(98.Equals(actual), "The first named argument setter is sticking; must allow itslf to be overwritten."); + Assert.AreEqual(288, actual, "The second named argument was not applied (it must be)."); + } + + [Test] + public void PreparedArgumentsIsNeverNull() + { + MyMethodInvoker vkr = new MyMethodInvoker(); + Assert.IsNotNull(vkr.GetPreparedArguments(), + "PreparedArguments is null even before Prepare() is called; must NEVER be null."); + vkr.NullOutPreparedArguments(); + Assert.IsNotNull(vkr.GetPreparedArguments(), + "PreparedArguments should revert to the empty object[] when set to null; must NEVER be null."); + } + + #region Inner Class : Foo + + private sealed class Foo + { + public Foo() + { + Age = 0; + Status = "Baby"; + } + + public int GrowOlder() + { + return GrowOlder(1); + } + + public int GrowOlder(int years) + { + _age += years; + return _age; + } + + public int GrowOlderUntilMaximumAgeReached(int years, int maximumAge) + { + _age += years; + if (_age > maximumAge) + { + _age = maximumAge; + } + return _age; + } + + public int GrowOlderUntilMaximumAgeReachedAndSetStatusName( + int years, int maximumAge, string status) + { + Status = status; + return GrowOlderUntilMaximumAgeReached(years, maximumAge); + } + + private int _age; + private string _status; + + public int Age + { + get { return _age; } + set { _age = value; } + } + + public string Status + { + get { return _status; } + set { _status = value; } + } + } + + #endregion + + #region Inner Class : MyMethodInvoker + + private sealed class MyMethodInvoker : MethodInvoker + { + public MyMethodInvoker() + { + } + + public void NullOutPreparedArguments() + { + PreparedArguments = null; + } + + public object[] GetPreparedArguments() + { + return PreparedArguments; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Support/MutableSortDefinitionTests.cs b/test/Spring/Spring.Core.Tests/Objects/Support/MutableSortDefinitionTests.cs new file mode 100644 index 00000000..7187c285 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Support/MutableSortDefinitionTests.cs @@ -0,0 +1,95 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Unit tests for the MutableSortDefinition class. + /// + /// Rick Evans + /// $Id: MutableSortDefinitionTests.cs,v 1.3 2006/04/09 07:24:51 markpollack Exp $ + [TestFixture] + public sealed class MutableSortDefinitionTests + { + [Test] + public void Instantiation () { + MutableSortDefinition def = new MutableSortDefinition (); + Assert.IsTrue (def.IgnoreCase); + Assert.IsTrue (def.Ascending); + Assert.IsNotNull (def.Property); + } + + [Test] + public void InstantiationWithCopy () { + ISortDefinition copy = new MutableSortDefinition ("Bing!", false, false); + MutableSortDefinition def = new MutableSortDefinition (copy); + Assert.IsFalse (def.IgnoreCase); + Assert.IsFalse (def.Ascending); + Assert.IsNotNull (def.Property); + Assert.AreEqual (copy.Property, def.Property); + } + + [Test] + public void TestEquals () { + ISortDefinition copy = new MutableSortDefinition ("Rab", false, false); + MutableSortDefinition def = new MutableSortDefinition ("Bing!", false, false); + def.Property = "Rab"; + Assert.AreEqual (copy, def); + Assert.AreEqual (copy.GetHashCode (), def.GetHashCode ()); + Assert.IsFalse (def.Equals (null)); + } + + [Test] + public void PropertySetter () { + MutableSortDefinition def = new MutableSortDefinition (); + def.Property = null; + Assert.IsNotNull (def.Property); + Assert.AreEqual (string.Empty, def.Property); + } + + [Test] + public void TogglesOkWhenPropertyIsSetAgain () + { + MutableSortDefinition def = new MutableSortDefinition (true); + bool originalAscendingValue = def.Ascending; + def.Property = "Name"; + def.Property = "Name"; // sort order should (must!) be reversed now... + Assert.IsFalse (def.Ascending == originalAscendingValue); + } + + [Test] + public void DoesNotTogglesWhenPropertyIsSetAgain () + { + MutableSortDefinition def = new MutableSortDefinition (true); + bool originalAscendingValue = def.Ascending; + def.Property = "Name"; + def.Property = "Age"; // sort order must not be toggled (different property) + Assert.IsTrue (def.Ascending == originalAscendingValue); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/Support/PropertyComparatorTests.cs b/test/Spring/Spring.Core.Tests/Objects/Support/PropertyComparatorTests.cs new file mode 100644 index 00000000..ca2dad56 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Support/PropertyComparatorTests.cs @@ -0,0 +1,224 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; +using Spring.Core; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Unit tests for the PropertyComparator class. + /// + /// + ///

    + /// The sort algorithm for arrays changed between versions 1.1 and 2.0 of the .NET + /// Framework. Hence there are two batches of tests in this suite, one for versions + /// 1.1 and 2.0 of the .NET Framework respectively. + ///

    + ///
    + /// Rick Evans + /// Michael Loll + /// $Id: PropertyComparatorTests.cs,v 1.10 2007/07/31 00:09:35 markpollack Exp $ + [TestFixture] + public sealed class PropertyComparatorTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void InstantiationWithNullArgument() + { + new PropertyComparator(null); + } + + [Test] + public void Compare() + { + ISortDefinition definition = new MutableSortDefinition("Age", false, true); + + PropertyComparator cmp = new PropertyComparator(definition); + TestObject one = new TestObject("Rick", 19); + TestObject two = new TestObject("Balzac", 205); + int actual = cmp.Compare(one, two); + Assert.IsTrue(actual < 0); // 19 is less than 205 + } + + [Test] + public void CompareNestedProperty() + { + ISortDefinition definition = new MutableSortDefinition("Lawyer.Company", true, true); + + PropertyComparator cmp = new PropertyComparator(definition); + TestObject one = new TestObject("Rick", 19); + one.Lawyer = new NestedTestObject("Gizajoab"); + TestObject two = new TestObject("Balzac", 205); + two.Lawyer = new NestedTestObject("Wallpaperer"); + int actual = cmp.Compare(one, two); + Assert.IsTrue(actual < 0); // Gizajoab is less than Wallpaperer + } + + [Test] + public void CompareWithNullProperty() + { + ISortDefinition definition = new MutableSortDefinition("Lawyer.Company", true, true); + + PropertyComparator cmp = new PropertyComparator(definition); + TestObject one = new TestObject("Rick", 19); + one.Lawyer = new NestedTestObject("Gizajoab"); + TestObject two = new TestObject("Balzac", 205); + // no Lawyer.Company property set on object two... + int actual = cmp.Compare(one, two); + Assert.IsTrue(actual > 0); // Gizajoab is greater than null + } + + [Test] + [ExpectedException(typeof(InvalidPropertyException))] + public void CompareWithNonExistantProperty() + { + ISortDefinition definition = new MutableSortDefinition("Deborahs.Banjo", true, true); + + PropertyComparator cmp = new PropertyComparator(definition); + TestObject one = new TestObject("Rick", 19); + TestObject two = new TestObject("Balzac", 205); + int actual = cmp.Compare(one, two); + Assert.IsTrue(actual == 0); + } + + [Test] + public void Sort() + { + ISortDefinition definition = new MutableSortDefinition("NAmE", true, true); + + TestObject one = new TestObject("Rick", 19); + TestObject two = new TestObject("Balzac", 205); + TestObject three = new TestObject("Jenny", 89); + TestObject[] actual = new TestObject[] {one, two, three}; + TestObject[] expected = new TestObject[] {two /*Balzac*/, three /*Jenny*/, one /*Rick*/}; + PropertyComparator.Sort(actual, definition); + for (int i = 0; i < actual.Length; ++i) + { + Assert.AreEqual(expected[i].Name, actual[i].Name); + } + } + + [Test] + public void SortInDescendingOrder() + { + ISortDefinition definition = new MutableSortDefinition("NAmE", true, false); + + TestObject one = new TestObject("Rick", 19); + TestObject two = new TestObject("Balzac", 205); + TestObject three = new TestObject("Jenny", 89); + TestObject[] actual = new TestObject[] {one, two, three}; + // Rick comes after Jenny comes after Balzac (descending sort order)... + TestObject[] expected = new TestObject[] {one /*Rick*/, three /*Jenny*/, two /*Balzac*/}; + PropertyComparator.Sort(actual, definition); + for (int i = 0; i < actual.Length; ++i) + { + Assert.AreEqual(expected[i].Name, actual[i].Name); + } + } + + [Test] + [Ignore("Sort ordering is not preserved (unstable) with equal elements (c.f. System.Array.Sort (Array, IComparer)))")] + public void OrderingIsUnperturbedWithEqualProps() + { + ISortDefinition definition = new MutableSortDefinition("Age", false, true); + + TestObject one = new TestObject("Rick", 19); + TestObject two = new TestObject("Balzac", 19); + TestObject three = new TestObject("Jenny", 19); + TestObject[] actual = new TestObject[] {one, two, three}; + TestObject[] expected = new TestObject[] {one /*Rick*/, two /*Balzac*/, three /*Jenny*/}; + PropertyComparator.Sort(actual, definition); + for (int i = 0; i < actual.Length; ++i) + { + Assert.AreEqual(expected[i].Name, actual[i].Name); + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void SortWithNullDefinition() + { + PropertyComparator.Sort(Type.EmptyTypes, null); + } + + [Test] + public void CompareWithNullArguments() + { + ISortDefinition definition = new MutableSortDefinition("Nails", false, true); + + Funk one = new Funk(1, "long"); + PropertyComparator cmp = new PropertyComparator(definition); + Assert.AreEqual(0, cmp.Compare(null, null)); + // nulls are always last (i.e. greater than) + Assert.AreEqual(1, cmp.Compare(null, one)); + // any non-null instance comes before null (i.e. less than). + Assert.AreEqual(-1, cmp.Compare(one, null)); + } + + /// + /// Validates that a PropertyComparator can return its internal ISortDefinition + /// to a consumer for use. + /// + [Test(Description = "SPRNET-171")] + public void CanGetSortDefinition() + { + ISortDefinition definition = new MutableSortDefinition("Age", false, true); + + PropertyComparator cmp = new PropertyComparator(definition); + ISortDefinition sortDefinition = cmp.SortDefinition; + + Assert.AreSame(definition, sortDefinition); + Assert.AreEqual(definition.Property, sortDefinition.Property); + Assert.AreEqual(definition.Ascending, sortDefinition.Ascending); + Assert.AreEqual(definition.IgnoreCase, sortDefinition.IgnoreCase); + } + + internal sealed class Funk + { + public Funk(int id, string nails) + { + _id = id; + _nails = nails; + } + + private string _nails; + private int _id; + + public string Nails + { + get { return _nails; } + } + + public int Id + { + get { return _id; } + set { _id = value; } + } + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/Support/StaticEventHandlerValueTests.cs b/test/Spring/Spring.Core.Tests/Objects/Support/StaticEventHandlerValueTests.cs new file mode 100644 index 00000000..adaf807c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Support/StaticEventHandlerValueTests.cs @@ -0,0 +1,99 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Objects.Support +{ + /// + /// Unit tests for the StaticEventHandlerValue class. + /// + /// Rick Evans + /// $Id: StaticEventHandlerValueTests.cs,v 1.4 2006/04/09 07:24:51 markpollack Exp $ + [TestFixture] + public sealed class StaticEventHandlerValueTests + { + [Test] + public void Instantiation () + { + PingSource source = new PingSource (); + StaticEventHandlerValue wirer + = new StaticEventHandlerValue (source, "OnPing"); + Assert.IsNotNull (wirer.Source); + Assert.AreEqual ("OnPing", wirer.MethodName); + Assert.IsTrue (wirer.ToString ().IndexOf (wirer.MethodName) > 0); + } + + [Test] + public void Wire () + { + PingSource source = new PingSource (); + StaticEventHandlerValue wirer + = new StaticEventHandlerValue (source, "OnPing"); + wirer.EventName = "Ping"; + Type sink = typeof (StaticPingListener); + wirer.Wire (source, sink); + source.OnPing (); + Assert.IsTrue (StaticPingListener.GotPing, "The event handler did not get notified when the event was raised."); + Assert.AreEqual (1, StaticPingListener.PingCount, "The event handler was not get notified exactly once when the event was raised exactly once."); + } + + [Test] + [ExpectedException (typeof (FatalObjectException))] + public void WireWithInstanceHandler () + { + StaticEventHandlerValue wirer = new StaticEventHandlerValue (); + wirer.EventName = "Ping"; + wirer.MethodName = "OnPing"; + PingSource source = new PingSource (); + wirer.Wire (source, new PingListener ()); + } + } + + internal class StaticPingListener { + + public static bool OnPing (object sender, PingEventArgs e) { + _ping = true; + ++_pingCount; + return true; + } + + public static bool GotPing { + get { + return _ping; + } + } + + public static int PingCount { + get { + return _pingCount; + } + } + + protected static bool _ping; + protected static int _pingCount; + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/TestEventHandler.cs b/test/Spring/Spring.Core.Tests/Objects/TestEventHandler.cs new file mode 100644 index 00000000..c5d3452b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/TestEventHandler.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Objects { + + internal class TestEventHandler + { + public virtual void HandleEvent (object sender, EventArgs e) + { + _eventWasHandled = true; + } + + public virtual bool EventWasHandled + { + get + { + return _eventWasHandled; + } + } + + protected bool _eventWasHandled; + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/TestGenericObject.cs b/test/Spring/Spring.Core.Tests/Objects/TestGenericObject.cs new file mode 100644 index 00000000..a0376d4a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/TestGenericObject.cs @@ -0,0 +1,109 @@ +#if NET_2_0 +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Generic; + +#endregion + +namespace Spring.Objects +{ + /// + /// Simple test object used for testing generic objects. + /// + /// Bruno Baia + /// $Id: TestGenericObject.cs,v 1.2 2006/04/09 07:24:50 markpollack Exp $ + public class TestGenericObject + { + #region Fields + + private int _id = 0; + private string _name = string.Empty; + private IList _someGenericList = new List(); + private IDictionary _someGenericDictionary = new Dictionary(); + + #endregion + + #region Properties + + public int ID + { + get { return _id; } + set { _id = value; } + } + + public string Name + { + get { return _name; } + set { this._name = value; } + } + + public IList SomeGenericList + { + get { return _someGenericList; } + set { this._someGenericList = value; } + } + + public IDictionary SomeGenericDictionary + { + get { return _someGenericDictionary; } + set { this._someGenericDictionary = value; } + } + + #endregion + + #region Constructor (s) / Destructor + + public TestGenericObject() + { + } + + public TestGenericObject(int id) + { + this._id = id; + } + + public TestGenericObject(int id, string name) + { + this._id = id; + this._name = name; + } + + #endregion + + #region Methods + + public static List CreateList() + { + return new List(); + } + + public TestGenericObject CreateInstance() + { + return new TestGenericObject(); + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/TestObject.cs b/test/Spring/Spring.Core.Tests/Objects/TestObject.cs new file mode 100644 index 00000000..2336b089 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/TestObject.cs @@ -0,0 +1,629 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Text; +using Spring.Collections; +using Spring.Context; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Objects +{ + /// + /// Simple test object used for testing object factories, AOP framework etc. + /// + /// Rod Johnson + /// Mark Pollack (.NET) + public class TestObject : MarshalByRefObject, ITestObject, IObjectFactoryAware, IComparable, IOther, IApplicationContextAware, IObjectNameAware, IInitializingObject + { + #region Event testing members + + public event EventHandler Click; + + public static event EventHandler StaticClick; + + /// + /// Public method to programmatically raise the Click event + /// while testing. + /// + public void OnClick() + { + if (Click != null) + { + Click(this, EventArgs.Empty); + } + } + + /// + /// Public method to programmatically raise the static + /// Click event while testing. + /// + public static void OnStaticClick() + { + if (TestObject.StaticClick != null) + { + TestObject.StaticClick(typeof (TestObject), EventArgs.Empty); + } + } + + #endregion + + #region Properties + + public int ObjectNumber + { + get { return objectNumber; } + set { objectNumber = value; } + } + + // for use in ObjectWrapperTests.SetPropertyValuesFailsWhenSettingReadOnlyProperty + public int ReadOnlyObjectNumber + { + get { return objectNumber; } + } + + public IndexedTestObject NestedIndexedObject + { + get { return nestedIndexedObject; } + set { nestedIndexedObject = value; } + } + + /// + /// Public Enumeration Property for FileMode. + /// + public FileMode FileMode + { + get { return FileModeEnum; } + set { FileModeEnum = value; } + } + + public IObjectFactory ObjectFactory + { + get { return objectFactory; } + set { objectFactory = value; } + } + + public CultureInfo MyCulture + { + get { return myCulture; } + set { myCulture = value; } + } + + public string Touchy + { + get { return touchy; } + set + { + if (value.IndexOf('.') != - 1) + { + throw new Exception("Can't contain a ."); + } + if (value.IndexOf(',') != - 1) + { + throw new FormatException("Number format exception: contains a ,"); + } + this.touchy = value; + } + } + + public bool PostProcessed + { + get { return postProcessed; } + + set { this.postProcessed = value; } + } + + public string Name + { + get { return name; } + set { this.name = value; } + } + + public string Nickname + { + get { return nickname; } + set { this.nickname = value; } + } + + public virtual int Age + { + get { return age; } + + set { this.age = value; } + } + + public virtual DateTime Date + { + get { return date; } + + set { this.date = value; } + } + + public virtual Single MyFloat + { + get { return myFloat; } + set { this.myFloat = value; } + } + + public virtual ITestObject Spouse + { + get { return spouse; } + set { this.spouse = value; } + } + + public virtual ITestObject Sibling + { + get { return sibling; } + set { this.sibling = value; } + } + + public virtual INestedTestObject Doctor + { + get { return doctor; } + set { this.doctor = value; } + } + + public virtual INestedTestObject Lawyer + { + get { return lawyer; } + set { this.lawyer = value; } + } + + public virtual NestedTestObject RealLawyer + { + get { return myRealLawyer; } + set { this.myRealLawyer = value; } + } + + /// + /// A collection of friends. + /// + public virtual ICollection Friends + { + get { return friends; } + set { this.friends = value; } + } + + /// + /// A read-only collection of pets. + /// + public virtual ICollection Pets + { + get { return pets; } + } + + public virtual TestObjectList TypedFriends + { + get { return typedFriends; } + set { typedFriends = value; } + } + + /// + /// A read-only map of periodic table values. + /// + public virtual IDictionary PeriodicTable + { + get { return periodicTable; } + } + + /// + /// A read-only set of computer names + /// + public virtual ISet Computers + { + get { return computers; } + } + + public virtual Set SomeSet + { + get { return someSet; } + set { this.someSet = value; } + } + + public virtual string[] Hats + { + get { return hats; } + set { hats = value; } + } + + public virtual IDictionary SomeMap + { + get { return someMap; } + set { this.someMap = value; } + } + + protected virtual string HappyPlace + { + get { return _happyPlace; } + set { _happyPlace = value; } + } + + // used in reflective tests, so don't remove this property... + private string[] SamsoniteSuitcase + { + get { return _samsoniteSuitcase; } + set { _samsoniteSuitcase = value; } + } + + public Type ClassProperty + { + get { return classProperty; } + set { classProperty = value; } + } + + public IApplicationContext ApplicationContext + { + get { return applicationContext; } + set { applicationContext = value; } + } + + public string ObjectName + { + get { return objectName; } + set { objectName = value; } + } + + public int ExceptionMethodCallCount + { + get { return exceptionMethodCallCount; } + } + + public bool InitCompleted + { + get { return initCompleted; } + set { initCompleted = value; } + } + + public Size Size + { + get { return size; } + set { size = value;} + } + #endregion + + #region Indexers + + public string this[int index] + { + get + { + if (index < 0 || index >= favoriteQuotes.Length) + { + throw new ArgumentException("Index out of range"); + } + return favoriteQuotes[index]; + } + set + { + if (index < 0 || index >= favoriteQuotes.Length) + { + throw new ArgumentException("index is out of range."); + } + favoriteQuotes[index] = value; + } + } + + #endregion + + #region Fields + + private int exceptionMethodCallCount; + private int objectNumber = 0; + public FileMode FileModeEnum; + private IObjectFactory objectFactory; + private bool postProcessed; + private int age; + private string name; + private string nickname; + private ITestObject spouse; + private ITestObject sibling; + private string touchy; + private ICollection friends = new LinkedList(); + private TestObjectList typedFriends = new TestObjectList(); + private ICollection pets = new LinkedList(); + private IDictionary periodicTable = new Hashtable(); + + private string[] favoriteQuotes = new string[] + { + "He who ha-ha ho-ho", + "The quick brown fox jumped over the lazy dogs." + }; + + private Type classProperty; + private Set computers = new HybridSet(); + private Set someSet = new HybridSet(); + private IDictionary someMap = new Hashtable(); + private DateTime date = DateTime.Now; + private Single myFloat = (float) 0.0; + private CultureInfo myCulture = CultureInfo.InvariantCulture; + private string[] hats = null; + private INestedTestObject doctor = new NestedTestObject(); + private INestedTestObject lawyer = new NestedTestObject(); + private NestedTestObject myRealLawyer = new NestedTestObject(); + private IndexedTestObject nestedIndexedObject; + private string _happyPlace = DefaultHappyPlace; + private string[] _samsoniteSuitcase = DefaultContentsOfTheSuitcase; + + public const string DefaultHappyPlace = "The_SeaBass_Diner"; + + public static readonly string[] DefaultContentsOfTheSuitcase + = new string[] {"John Pryor's 'Leaving On A Jet Plane' LP"}; + + private IApplicationContext applicationContext; + private string objectName; + private Size size; + private bool initCompleted; + + #endregion + + #region Constructor (s) / Destructor + + public TestObject() + { + } + + public TestObject(string name, int age) + { + this.name = name; + this.age = age; + } + + public TestObject(string name, int age, INestedTestObject doctor) + { + this.name = name; + this.age = age; + this.doctor = doctor; + } + + public TestObject(string name, int age, INestedTestObject doctor, INestedTestObject lawyer) + { + this.name = name; + this.age = age; + this.doctor = doctor; + this.lawyer = lawyer; + } + + public TestObject(ITestObject spouse) + { + this.spouse = spouse; + } + + #endregion + + #region Static Methods + + public static TestObject Create(string name) + { + return new TestObject(name, 30); + } + + #endregion + + #region Methods + + public void AfterPropertiesSet() + { + initCompleted = true; + } + + public void AddComputerName(string name) + { + if (computers == null) + { + computers = new HybridSet(); + } + computers.Add(name); + } + + public void AddPeriodicElement(string name, string element) + { + if (periodicTable == null) + { + periodicTable = new Hashtable(); + } + periodicTable.Add(name, element); + } + + public string GetNameWithHonorific(bool isFemale, params string[] lettersAfterName) + { + StringBuilder buffer = new StringBuilder(); + buffer.Append(isFemale ? "Ms " : "Mr "); + buffer.Append(Name); + buffer.Append(" "); + foreach (string letters in lettersAfterName) + { + buffer.Append(letters); + } + return buffer.ToString(); + } + + public override bool Equals(object other) + { + return Equals(this, other); + } + + new public static bool Equals(object @this, object other) + { + if (other == null || !(other is ITestObject)) + { + return false; + } + ITestObject tb2 = (ITestObject) other; + ITestObject tb1 = (ITestObject) @this; + if (tb2.Age != tb1.Age) + { + return false; + } + + if ((object) tb1.Name == null) + { + return (object) tb2.Name == null; + } + + if (!tb2.Name.Equals(tb1.Name)) + { + return false; + } + + return true; + } + + public override int GetHashCode() + { + return (name != null ? name.GetHashCode() : base.GetHashCode()); + } + + public virtual int CompareTo(object other) + { + if ((object) this.name != null && other is TestObject) + { + return String.CompareOrdinal(this.name, ((TestObject) other).name); + } + else + { + return 1; + } + } + + public virtual string GetDescription() + { + string s = "name=" + name + "; age=" + age + "; touchy=" + touchy; + s += ("; spouse={" + (spouse != null ? spouse.Name : null) + "}"); + return s; + } + + public override string ToString() + { + string s = "name=" + name + "; age=" + age + "; touchy=" + touchy; + s += ("; spouse={" + (spouse != null ? spouse.Name : null) + "}"); + return s; + } + + /// + /// Throw the given exception + /// + /// An exception to throw. + public virtual void Exceptional(Exception t) + { + exceptionMethodCallCount++; + if (t != null) + { + throw t; + } + } + + + /// + /// Throw the given exception + /// + /// An exception to throw. + /// 314 if exception is null + public virtual int ExceptionalWithReturnValue(Exception t) + { + exceptionMethodCallCount++; + if (t != null) + { + throw t; + } + return 314; + } + + /// + /// Return a reference to the object itself. 'Return this' + /// + /// a reference to the object itse.f + public virtual object ReturnsThis() + { + return this; + } + + #endregion + + #region IOther Implementation + + /// + /// Funny Named method. + /// + public virtual void Absquatulate() + { + } + + #endregion + } + + #region Inner Class : TestObjectConverter + + public class TestObjectConverter : TypeConverter + { + public override bool CanConvertTo( + ITypeDescriptorContext context, Type destinationType) + { + if ((destinationType == typeof (TestObjectConverter)) + || (destinationType == typeof (InstanceDescriptor))) + { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + public override bool CanConvertFrom + (ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom + (ITypeDescriptorContext context, CultureInfo culture, object text_obj) + { + if (text_obj is string) + { + string text = (string) text_obj; + TestObject tb = new TestObject(); + string[] split = text.Split(new char[] {'_'}); + tb.Name = split[0]; + tb.Age = int.Parse(split[1]); + return tb; + } + return base.ConvertFrom(context, culture, text_obj); + } + + public override object ConvertTo( + ITypeDescriptorContext context, CultureInfo culture, object param, Type destinationType) + { + return base.ConvertTo(context, culture, param, destinationType); + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/TestObject.resx b/test/Spring/Spring.Core.Tests/Objects/TestObject.resx new file mode 100644 index 00000000..431d10b4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/TestObject.resx @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Mark + + + 35 + + + Rick + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/TestObjectDAO.cs b/test/Spring/Spring.Core.Tests/Objects/TestObjectDAO.cs new file mode 100644 index 00000000..bace79e2 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/TestObjectDAO.cs @@ -0,0 +1,74 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Data; + +namespace Spring.Objects +{ + /// + /// Test class to help test PropertyPlaceholderConfigurer + /// + public class TestObjectDAO + { + /// + /// Create an instance of TestObjectDAO + /// + public TestObjectDAO() + { + } + + + private int maxResults = 100; + + private IDbConnection dbConnection; + + /// + /// The database connection property + /// + public IDbConnection DbConnection + { + get + { + return dbConnection; + } + set + { + dbConnection = value; + } + } + + + /// + /// Maximum number of results to return in a query page. + /// + public int MaxResults + { + get + { + return maxResults; + } + set + { + maxResults = value; + } + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Objects/TestObjectList.cs b/test/Spring/Spring.Core.Tests/Objects/TestObjectList.cs new file mode 100644 index 00000000..953f15e1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/TestObjectList.cs @@ -0,0 +1,556 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +#endregion + +namespace Spring.Objects +{ + public interface ITestObjectCollection + { + int Count { get; } + + bool IsSynchronized { get; } + + object SyncRoot { get; } + + void CopyTo(TestObject[] array, int arrayIndex); + + ITestObjectEnumerator GetEnumerator(); + } + + public interface ITestObjectList : ITestObjectCollection + { + bool IsFixedSize { get; } + + bool IsReadOnly { get; } + + TestObject this[int index] { get; set; } + + int Add(TestObject value); + + void Clear(); + + bool Contains(TestObject value); + + int IndexOf(TestObject value); + + void Insert(int index, TestObject value); + + void Remove(TestObject value); + + void RemoveAt(int index); + } + + public interface ITestObjectEnumerator + { + TestObject Current { get; } + + bool MoveNext(); + + void Reset(); + } + + [Serializable] + public class TestObjectList : ITestObjectList, IList, ICloneable + { + private const int _defaultCapacity = 16; + + private TestObject[] _array = null; + private int _count = 0; + + [NonSerialized] private int _version = 0; + + public TestObjectList() + { + this._array = new TestObject[_defaultCapacity]; + } + + public TestObjectList(int capacity) + { + if (capacity < 0) + throw new ArgumentOutOfRangeException("capacity", + capacity, "Argument cannot be negative."); + + this._array = new TestObject[capacity]; + } + + public TestObjectList(TestObjectList collection) + { + if (collection == null) + throw new ArgumentNullException("collection"); + + this._array = new TestObject[collection.Count]; + AddRange(collection); + } + + public TestObjectList(TestObject[] array) + { + if (array == null) + throw new ArgumentNullException("array"); + + this._array = new TestObject[array.Length]; + AddRange(array); + } + + protected virtual TestObject[] InnerArray + { + get { return this._array; } + } + + public virtual int Capacity + { + get { return this._array.Length; } + set + { + if (value == this._array.Length) return; + + if (value < this._count) + throw new ArgumentOutOfRangeException("Capacity", + value, "Value cannot be less than Count."); + + if (value == 0) + { + this._array = new TestObject[_defaultCapacity]; + return; + } + + TestObject[] newArray = new TestObject[value]; + Array.Copy(this._array, newArray, this._count); + this._array = newArray; + } + } + + public virtual int Count + { + get { return this._count; } + } + + public virtual bool IsFixedSize + { + get { return false; } + } + + public virtual bool IsReadOnly + { + get { return false; } + } + + public virtual bool IsSynchronized + { + get { return false; } + } + + public virtual bool IsUnique + { + get { return false; } + } + + public virtual TestObject this[int index] + { + get + { + ValidateIndex(index); + return this._array[index]; + } + set + { + ValidateIndex(index); + ++this._version; + this._array[index] = value; + } + } + + object IList.this[int index] + { + get { return this[index]; } + set { this[index] = (TestObject) value; } + } + + public virtual object SyncRoot + { + get { return this; } + } + + public virtual int Add(TestObject value) + { + if (this._count == this._array.Length) + EnsureCapacity(this._count + 1); + + ++this._version; + this._array[this._count] = value; + return this._count++; + } + + int IList.Add(object value) + { + return Add((TestObject) value); + } + + public virtual void AddRange(TestObjectList collection) + { + if (collection == null) + throw new ArgumentNullException("collection"); + + if (collection.Count == 0) return; + if (this._count + collection.Count > this._array.Length) + EnsureCapacity(this._count + collection.Count); + + ++this._version; + Array.Copy(collection.InnerArray, 0, + this._array, this._count, collection.Count); + this._count += collection.Count; + } + + public virtual void AddRange(TestObject[] array) + { + if (array == null) + throw new ArgumentNullException("array"); + + if (array.Length == 0) return; + if (this._count + array.Length > this._array.Length) + EnsureCapacity(this._count + array.Length); + + ++this._version; + Array.Copy(array, 0, this._array, this._count, array.Length); + this._count += array.Length; + } + + public virtual int BinarySearch(TestObject value) + { + return Array.BinarySearch(this._array, 0, this._count, value); + } + + public virtual void Clear() + { + if (this._count == 0) return; + + ++this._version; + Array.Clear(this._array, 0, this._count); + this._count = 0; + } + + public virtual object Clone() + { + TestObjectList collection = new TestObjectList(this._count); + + Array.Copy(this._array, 0, collection._array, 0, this._count); + collection._count = this._count; + collection._version = this._version; + + return collection; + } + + public bool Contains(TestObject value) + { + return (IndexOf(value) >= 0); + } + + bool IList.Contains(object value) + { + return Contains((TestObject) value); + } + + public virtual void CopyTo(TestObject[] array) + { + CheckTargetArray(array, 0); + Array.Copy(this._array, array, this._count); + } + + public virtual void CopyTo(TestObject[] array, int arrayIndex) + { + CheckTargetArray(array, arrayIndex); + Array.Copy(this._array, 0, array, arrayIndex, this._count); + } + + void ICollection.CopyTo(Array array, int arrayIndex) + { + CheckTargetArray(array, arrayIndex); + CopyTo((TestObject[]) array, arrayIndex); + } + + public virtual ITestObjectEnumerator GetEnumerator() + { + return new Enumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator) GetEnumerator(); + } + + public virtual int IndexOf(TestObject value) + { + return Array.IndexOf(this._array, value, 0, this._count); + } + + int IList.IndexOf(object value) + { + return IndexOf((TestObject) value); + } + + public virtual void Insert(int index, TestObject value) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index", + index, "Argument cannot be negative."); + + if (index > this._count) + throw new ArgumentOutOfRangeException("index", + index, "Argument cannot exceed Count."); + + if (this._count == this._array.Length) + EnsureCapacity(this._count + 1); + + ++this._version; + if (index < this._count) + Array.Copy(this._array, index, + this._array, index + 1, this._count - index); + + this._array[index] = value; + ++this._count; + } + + void IList.Insert(int index, object value) + { + Insert(index, (TestObject) value); + } + + public virtual void Remove(TestObject value) + { + int index = IndexOf(value); + if (index >= 0) RemoveAt(index); + } + + void IList.Remove(object value) + { + Remove((TestObject) value); + } + + public virtual void RemoveAt(int index) + { + ValidateIndex(index); + + ++this._version; + if (index < --this._count) + Array.Copy(this._array, index + 1, + this._array, index, this._count - index); + + this._array[this._count] = null; + } + + public virtual void RemoveRange(int index, int count) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index", + index, "Argument cannot be negative."); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", + count, "Argument cannot be negative."); + + if (index + count > this._count) + throw new ArgumentException( + "Arguments denote invalid range of elements."); + + if (count == 0) return; + + ++this._version; + this._count -= count; + + if (index < this._count) + Array.Copy(this._array, index + count, + this._array, index, this._count - index); + + Array.Clear(this._array, this._count, count); + } + + public virtual void Reverse() + { + if (this._count <= 1) return; + ++this._version; + Array.Reverse(this._array, 0, this._count); + } + + public virtual void Reverse(int index, int count) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index", + index, "Argument cannot be negative."); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", + count, "Argument cannot be negative."); + + if (index + count > this._count) + throw new ArgumentException( + "Arguments denote invalid range of elements."); + + if (count <= 1 || this._count <= 1) return; + ++this._version; + Array.Reverse(this._array, index, count); + } + + public virtual void Sort() + { + if (this._count <= 1) return; + ++this._version; + Array.Sort(this._array, 0, this._count); + } + + public virtual void Sort(IComparer comparer) + { + if (this._count <= 1) return; + ++this._version; + Array.Sort(this._array, 0, this._count, comparer); + } + + public virtual void Sort(int index, int count, IComparer comparer) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index", + index, "Argument cannot be negative."); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", + count, "Argument cannot be negative."); + + if (index + count > this._count) + throw new ArgumentException( + "Arguments denote invalid range of elements."); + + if (count <= 1 || this._count <= 1) return; + ++this._version; + Array.Sort(this._array, index, count, comparer); + } + + public virtual TestObject[] ToArray() + { + TestObject[] array = new TestObject[this._count]; + Array.Copy(this._array, array, this._count); + return array; + } + + public virtual void TrimToSize() + { + Capacity = this._count; + } + + private void CheckEnumIndex(int index) + { + if (index < 0 || index >= this._count) + throw new InvalidOperationException( + "Enumerator is not on a collection element."); + } + + private void CheckEnumVersion(int version) + { + if (version != this._version) + throw new InvalidOperationException( + "Enumerator invalidated by modification to collection."); + } + + private void CheckTargetArray(Array array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException("array"); + if (array.Rank > 1) + throw new ArgumentException( + "Argument cannot be multidimensional.", "array"); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("arrayIndex", + arrayIndex, "Argument cannot be negative."); + if (arrayIndex >= array.Length) + throw new ArgumentException( + "Argument must be less than array length.", "arrayIndex"); + + if (this._count > array.Length - arrayIndex) + throw new ArgumentException( + "Argument section must be large enough for collection.", "array"); + } + + private void EnsureCapacity(int minimum) + { + int newCapacity = (this._array.Length == 0 ? + _defaultCapacity : this._array.Length*2); + + if (newCapacity < minimum) newCapacity = minimum; + Capacity = newCapacity; + } + + private void ValidateIndex(int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index", + index, "Argument cannot be negative."); + + if (index >= this._count) + throw new ArgumentOutOfRangeException("index", + index, "Argument must be less than Count."); + } + + [Serializable] + private sealed class Enumerator : ITestObjectEnumerator, IEnumerator + { + private readonly TestObjectList _collection; + private readonly int _version; + private int _index; + + internal Enumerator(TestObjectList collection) + { + this._collection = collection; + this._version = collection._version; + this._index = -1; + } + + public TestObject Current + { + get + { + this._collection.CheckEnumIndex(this._index); + this._collection.CheckEnumVersion(this._version); + return this._collection[this._index]; + } + } + + object IEnumerator.Current + { + get { return Current; } + } + + public bool MoveNext() + { + this._collection.CheckEnumVersion(this._version); + return (++this._index < this._collection.Count); + } + + public void Reset() + { + this._collection.CheckEnumVersion(this._version); + this._index = -1; + } + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/TypeMismatchExceptionTests.cs b/test/Spring/Spring.Core.Tests/Objects/TypeMismatchExceptionTests.cs new file mode 100644 index 00000000..40044198 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/TypeMismatchExceptionTests.cs @@ -0,0 +1,57 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using NUnit.Framework; +using Spring.Core; + +namespace Spring.Objects +{ + /// + /// Unit tests for the TypeMismatchException class. + /// + /// Rick Evans + /// $Id: TypeMismatchExceptionTests.cs,v 1.4 2007/07/31 00:09:24 markpollack Exp $ + [TestFixture] + public sealed class TypeMismatchExceptionTests + { + [Test] + public void InstantiationSupplyingPropertyChangeArgsType() + { + TypeMismatchException ex = new TypeMismatchException(new PropertyChangeEventArgs("Doctor", new NestedTestObject("Foo"), new TestObject("Hershey", 12)), typeof (INestedTestObject)); + Assert.AreEqual("typeMismatch", ex.ErrorCode); + } + + [Test] + public void InstantiationSupplyingPropertyChangeArgsTypeAndRootException() + { + TypeMismatchException ex = new TypeMismatchException(new PropertyChangeEventArgs("Doctor", new NestedTestObject("Foo"), new TestObject("Hershey", 12)), typeof (INestedTestObject), null); + Assert.AreEqual("typeMismatch", ex.ErrorCode); + } + + [Test] + public void InstantiationSupplyingNullPropertyChangeArgsAndNullType() + { + TypeMismatchException ex = new TypeMismatchException(null, null, null); + Assert.AreEqual("typeMismatch", ex.ErrorCode); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/ValidatorTestObject.cs b/test/Spring/Spring.Core.Tests/Objects/ValidatorTestObject.cs new file mode 100644 index 00000000..e9f2620b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/ValidatorTestObject.cs @@ -0,0 +1,52 @@ +using System; + +namespace Spring.Objects + +{ + /// + /// Test class used for Validator tests. + /// + /// Goran Milosavljevic + public class Contact + { + private String m_name; + private int m_age; + private int m_loan; + private string m_creditcard; + + public Contact() + { + } + + public Contact(string name, int age, int loan) + { + this.m_name = name; + this.m_age = age; + this.m_loan = loan; + } + + public string Creditcard + { + get { return m_creditcard; } + set { m_creditcard = value; } + } + + public int Age + { + get { return m_age; } + set { m_age = value; } + } + + public int Loan + { + get { return m_loan; } + set { m_loan = value; } + } + + public string Name + { + get { return m_name; } + set { m_name = value; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Pool/Support/SimplePoolTest.cs b/test/Spring/Spring.Core.Tests/Pool/Support/SimplePoolTest.cs new file mode 100644 index 00000000..0a4a6522 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Pool/Support/SimplePoolTest.cs @@ -0,0 +1,442 @@ +#region License + +/* +* Copyright © 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Runtime.InteropServices; +using System.Threading; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Pool.Support; +using Spring.Threading; + +#endregion + +namespace Spring.Pool +{ + #region Inner Class : Helper + + public class Helper + { + private Latch latch; + private IObjectPool objectPool; + private ISync sync; + + public bool gone = false; + public object gotFromPool; + + public Helper(ISync sync, Latch latch) + { + Init(latch, sync, null); + } + + public Helper(Latch latch, ISync sync, IObjectPool objectPool) + { + Init(latch, sync, objectPool); + } + + public void Init(Latch latch, ISync sync, IObjectPool objectPool) + { + this.sync = sync; + this.latch = latch; + this.objectPool = objectPool; + } + + public void Go() + { + latch.Release(); + sync.Acquire(); + gone = true; + sync.Release(); + } + + public void UsePool() + { + gotFromPool = objectPool.BorrowObject(); + latch.Release(); + } + } + + #endregion + + /// + /// Unit tests for the SimplePool class. + /// + [TestFixture] + public sealed class SimplePoolTests + { + #region Inner Class : MyFactory (IPoolableObjectFactory implementation) + + private sealed class MyFactory : IPoolableObjectFactory + { + public object MakeObject() + { + return new object(); + } + + public void DestroyObject(object o) + { + } + + public bool ValidateObject(object o) + { + return true; + } + + public void ActivateObject(object o) + { + } + + public void PassivateObject(object o) + { + } + } + + #endregion + + private DynamicMock mock; + private IPoolableObjectFactory factory; + private SimplePool pool; + private bool usingMock; + + [SetUp] + public void SetUp() + { + mock = new DynamicMock(typeof (IPoolableObjectFactory)); + mock.ExpectAndReturn("MakeObject", new object()); + mock.ExpectAndReturn("ValidateObject", true); + mock.Strict = false; + factory = (IPoolableObjectFactory) mock.Object; + usingMock = true; + pool = new SimplePool(factory, 1); + } + + [TearDown] + public void TearDown() + { + if (usingMock) + { + mock.Verify(); + } + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiateWithNullPoolableObjectFactory() + { + usingMock = false; + new SimplePool(null, 10); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void InstantiateSpecifyingZeroPooledItems() + { + usingMock = false; + new SimplePool(factory, 0); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void InstantiateSpecifyingNegativePooledItems() + { + usingMock = false; + new SimplePool(factory, -10000); + } + + [Test] + public void ActivateOnObjectOnBorrow() + { + mock.Expect("ActivateObject"); + Assert.AreEqual(0, pool.NumActive, "active wrong"); + Assert.AreEqual(1, pool.NumIdle, "idle wrong"); + pool.BorrowObject(); + Assert.AreEqual(1, pool.NumActive, "active wrong"); + Assert.AreEqual(0, pool.NumIdle, "idle wrong"); + } + + [Test] + public void PassivateBusyObjectsBeforeClose() + { + object o = pool.BorrowObject(); + mock.Expect("PassivateObject", o); + pool.Close(); + } + + [Test, ExpectedException(typeof (PoolException))] + public void NoMoreUsableAfterClose() + { + PassivateBusyObjectsBeforeClose(); + pool.BorrowObject(); + } + + [Test, ExpectedException(typeof (PoolException))] + public void ThrowsExceptionWhenOutOfItemsBecauseFailedValidation() + { + mock = new DynamicMock(typeof (IPoolableObjectFactory)); + mock.ExpectAndReturn("MakeObject", new object()); + mock.ExpectAndReturn("ValidateObject", false); + factory = (IPoolableObjectFactory) mock.Object; + pool = new SimplePool(factory, 1); + pool.BorrowObject(); + } + + [Test] + public void PassivateObjectOnReturn() + { + mock.Expect("PassivateObject"); + pool.ReturnObject(pool.BorrowObject()); + } + + [Test] + public void DestroyObjectOnClose() + { + mock.Expect("DestroyObject"); + pool.BorrowObject(); + pool.Close(); + } + + [Test] + public void WaitOnBorrowWhenExausted() + { + usingMock = false; + int n = 100; + object[] objects = new object[n]; + pool = new SimplePool(new MyFactory(), n); + for (int i = 0; i < n; i++) + { + objects[i] = pool.BorrowObject(); + } + Latch latch = new Latch(); + ISync sync = new Latch(); + Helper helper = new Helper(latch, sync, pool); + Thread thread = new Thread(new ThreadStart(helper.UsePool)); + thread.Start(); + Assert.AreEqual(n, pool.NumActive); + Assert.AreEqual(0, pool.NumIdle); + object released = objects[n - 1]; + pool.ReturnObject(released); + latch.Acquire(); + Assert.AreEqual(n, pool.NumActive); + Assert.AreEqual(0, pool.NumIdle); + Assert.IsNotNull(helper.gotFromPool, "helper did not get from pool"); + Assert.AreSame(released, helper.gotFromPool, "got unexpected object"); + } + } + + [TestFixture] + public sealed class StessSimplePool + { + public sealed class QueryPerformance + { + public interface IQueried + { + void Run(); + } + + [DllImport("KERNEL32")] + private static extern bool QueryPerformanceCounter(ref long lpPerformanceCount); + + [DllImport("KERNEL32")] + private static extern bool QueryPerformanceFrequency(ref long lpFrequency); + + public static float Query(IQueried queried) + { + long frequency = 0; + QueryPerformanceFrequency(ref frequency); + + long startTime = 0; + QueryPerformanceCounter(ref startTime); + + queried.Run(); + + long endTime = 0; + QueryPerformanceCounter(ref endTime); + + float elapsed = (float) (endTime - startTime)/frequency; + // Console.WriteLine("{0:0000.000}ms ", elapsed); + // Console.ReadLine(); + return elapsed; + } + } + + private sealed class Pooled : IPoolableObjectFactory, QueryPerformance.IQueried + { + private int creationTime; + private int executionTime; + + public Pooled(int creationTime, int executionTime) + { + this.creationTime = creationTime; + this.executionTime = executionTime; + Thread.Sleep(creationTime); + } + + public void Run() + { + Thread.Sleep(executionTime); + } + + public object MakeObject() + { + return new Pooled(creationTime, executionTime); + } + + public void DestroyObject(object o) + { + } + + public bool ValidateObject(object o) + { + return true; + } + + public void ActivateObject(object o) + { + } + + public void PassivateObject(object o) + { + } + } + + private sealed class Client : QueryPerformance.IQueried + { + private int repeat; + private static int nRun = 0, created = 0; + private int id; + private ISync run; + private SimplePool pool; + public bool runned; + + public Client(int id, ISync run, SimplePool pool, int repeat) + { + this.run = run; + this.pool = pool; + this.id = id; + lock (this.GetType()) + { + created++; + //Console.Out.WriteLine("#{0} created (created/run: {1}/{2})...", id, created, nRun); + } + this.repeat = repeat; + } + + public void Run() + { + lock (this.GetType()) + { + //Console.Out.WriteLine("#{0} running (created/run: {1}/{2})...", id, created, nRun); + nRun++; + } + for (int i = 0; i < repeat; i++) + { + QueryPerformance.IQueried queried = pool.BorrowObject() as QueryPerformance.IQueried; + queried.Run(); + pool.ReturnObject(queried); + runned = true; + } + run.Release(); + } + } + + private sealed class Job : QueryPerformance.IQueried + { + private int repeat; + private IList clients = new ArrayList(); + private int poolSize; + private int creationTime; + private int clientSize; + private ISync run; + private int executionTime; + + public Job(int size, int creationTime, int clientSize, ISync start, int executionTime, int repeat) + { + this.poolSize = size; + this.creationTime = creationTime; + this.clientSize = clientSize; + this.run = start; + this.executionTime = executionTime; + this.repeat = repeat; + } + + public void Run() + { + SimplePool pool = new SimplePool(new Pooled(creationTime, executionTime), poolSize); + for (int i = 0; i < clientSize; i++) + { + Client client = new Client(i, run, pool, repeat); + Thread thread = new Thread(new ThreadStart(client.Run)); + thread.Start(); + clients.Add(client); + } + run.Acquire(); + + foreach (Client client in clients) + Assert.IsTrue(client.runned, "\nclient " + clients.IndexOf(client) + " not runned"); + } + + public static string Do(int poolSize, int clientSize, int executionTime, int repeat, int creationTime) + { + ISync start = new Spring.Threading.Semaphore(-(clientSize - 1)); + Job job = new Job(poolSize, creationTime, clientSize, start, executionTime, repeat); + float elapsed = QueryPerformance.Query(job); + return String.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5:0.000} ", + creationTime, executionTime, poolSize, clientSize, repeat, elapsed); + } + } + + [Test, Ignore("a try for a stress")] + public void ScalingAgainstIncreasingNumberOfThreads() + { + Console.Out.WriteLine("creationTime\texecutionTime\tpoolSize\tclientSize\trepeat\telapsed"); + + Console.Out.WriteLine("ramp pools size"); + Console.Out.WriteLine(Job.Do(10, 1000, 100, 1, 1)); + Console.Out.WriteLine(Job.Do(100, 1000, 100, 1, 1)); + Console.Out.WriteLine(Job.Do(200, 1000, 100, 1, 1)); + Console.Out.WriteLine(Job.Do(500, 1000, 100, 1, 1)); + Console.Out.WriteLine(Job.Do(1000, 1000, 100, 1, 1)); + + Console.Out.WriteLine("ramp client size"); + Console.Out.WriteLine(Job.Do(10, 10, 100, 1, 1)); + Console.Out.WriteLine(Job.Do(10, 100, 100, 1, 1)); + Console.Out.WriteLine(Job.Do(10, 200, 100, 1, 1)); + Console.Out.WriteLine(Job.Do(10, 1000, 100, 1, 1)); + + Console.Out.WriteLine("ramp load"); + Console.Out.WriteLine(Job.Do(10, 10, 100, 1, 1)); + Console.Out.WriteLine(Job.Do(10, 10, 100, 5, 1)); + Console.Out.WriteLine(Job.Do(10, 10, 100, 10, 1)); + Console.Out.WriteLine(Job.Do(10, 10, 100, 50, 1)); + Console.Out.WriteLine(Job.Do(10, 10, 100, 100, 1)); + + Console.Out.WriteLine("ramp client size - 2"); + Console.Out.WriteLine(Job.Do(10, 1, 100, 1, 1)); + Console.Out.WriteLine(Job.Do(10, 5, 100, 5, 1)); + Console.Out.WriteLine(Job.Do(10, 10, 100, 10, 1)); + Console.Out.WriteLine(Job.Do(10, 50, 100, 50, 1)); + Console.Out.WriteLine(Job.Do(10, 100, 100, 100, 1)); + Console.Out.WriteLine(Job.Do(10, 1000, 100, 100, 1)); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Proxy/AbstractProxyTypeBuilderTests.cs b/test/Spring/Spring.Core.Tests/Proxy/AbstractProxyTypeBuilderTests.cs new file mode 100644 index 00000000..33bd0487 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Proxy/AbstractProxyTypeBuilderTests.cs @@ -0,0 +1,945 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Net; +using System.Security.Permissions; +using System.Reflection; +using System.Reflection.Emit; +using System.Collections; +using System.Diagnostics; +using System.Web.Services; +using System.Runtime.InteropServices; + +using NUnit.Framework; + +using Spring.Objects; +using Spring.Util; + +#endregion + +namespace Spring.Proxy +{ + /// + /// A base set of unit tests for the various AbstractProxyTypeBuilder subclasses. + /// + /// Rick Evans + /// Bruno Baia + /// $Id: AbstractProxyTypeBuilderTests.cs,v 1.21 2007/12/05 00:28:39 bbaia Exp $ + public abstract class AbstractProxyTypeBuilderTests + { + [Test] + public void GeneratesProxyNameIfNoneExplicitlySupplied() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + Assert.IsNotNull(builder.Name); + } + + [Test] + public void DoesNotGenerateProxyNameIfOneIsExplicitlySupplied() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.Name = "Bing"; + Assert.AreEqual("Bing", builder.Name); + } + + [Test] + public void AppliesAttributeToType() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(MarkerClass); + builder.TypeAttributes = new Attribute[] { new MarkerAttribute() }; + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object[] atts = proxy.GetCustomAttributes(false); + Assert.IsNotNull(atts, "Should have had 1 custom attribute applied to the generated proxy type."); + Assert.AreEqual(1, atts.Length, "Should have had 1 custom attribute applied to the generated proxy type."); + Assert.AreEqual(typeof(MarkerAttribute), atts[0].GetType(), "Wrong System.Type of Attribute applied to the generated proxy type."); + } + + [Test] + public void AppliesStarredMemberAttributesToAllMethods() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(TargetObjectTest); + IDictionary atts = new Hashtable(); + string methodPrefix = typeof(ITargetObjectTest).FullName + "."; + atts.Add("*", new Attribute[] { new MarkerAttribute() }); + builder.MemberAttributes = atts; + Type proxy = builder.BuildProxyType(); + object foo = Activator.CreateInstance(proxy); + + Type fooType = foo.GetType(); + MethodInfo method = ReflectionUtils.GetMethod(fooType, methodPrefix + "InterfaceMethodWithArguments", new Type[] { typeof(string) }); + object[] appliedAttributes = method.GetCustomAttributes(false); + Assert.AreEqual(1, appliedAttributes.Length, + "Custom attribute not applied to member method."); + method = ReflectionUtils.GetMethod(fooType, methodPrefix + "InterfaceVirtualMethodWithNoArguments", Type.EmptyTypes); + appliedAttributes = method.GetCustomAttributes(false); + Assert.AreEqual(1, appliedAttributes.Length, + "Custom attribute not applied to member method."); + } + + [Test] + public void AppliesSpecificMemberAttributesToSpecificMethodOnly() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(TargetObjectTest); + IDictionary atts = new Hashtable(); + atts.Add("InterfaceVirtualMethodWithNoArguments", new Attribute[] { new MarkerAttribute() }); + builder.MemberAttributes = atts; + + Type proxy = builder.BuildProxyType(); + object foo = Activator.CreateInstance(proxy); + + Type fooType = foo.GetType(); + MethodInfo method = ReflectionUtils.GetMethod(fooType, "InterfaceMethodWithArguments", new Type[] { typeof(string) }); + if (method == null) + { + method = ReflectionUtils.GetMethod(fooType, "Spring.Proxy.ITargetObjectTest.InterfaceMethodWithArguments", new Type[] { typeof(string) }); + } + object[] appliedAttributes = method.GetCustomAttributes(false); + Assert.AreEqual(0, appliedAttributes.Length, + "Custom attribute (erroneously) applied to member method."); + method = ReflectionUtils.GetMethod(fooType, "InterfaceVirtualMethodWithNoArguments", Type.EmptyTypes); + if (method == null) + { + method = ReflectionUtils.GetMethod(fooType, "Spring.Proxy.ITargetObjectTest.InterfaceVirtualMethodWithNoArguments", Type.EmptyTypes); + } + appliedAttributes = method.GetCustomAttributes(false); + Assert.AreEqual(1, appliedAttributes.Length, + "Custom attribute not applied to member method."); + } + + [Test] + public void ImplementsInterfaceHierarchy() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(MultipleInterfaces); + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is IBase); + Assert.IsTrue(foo is IInherited); + + // try to call proxied interface methods + ((IBase)foo).Base(); + ((IInherited)foo).Base(); + ((IInherited)foo).Inherited(); + } + + [Test] + public void ProxySpecificTargetTypeAttributeWithReadOnlyProperty() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ClassWithGuidAttribute); + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object[] attrs = proxy.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type."); + Assert.AreEqual(typeof(GuidAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type."); + + GuidAttribute ga = attrs[0] as GuidAttribute; + Assert.AreEqual("7cfc9607-e81a-48ac-a080-bda88193607b", ga.Value); + } + + [Test] + public void ProxySpecificTargetTypeAttributeWithPropertySetterChangingDefaultBehavior() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ClassWithFakeXmlElementAttribute); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + object[] attrs = method.GetParameters()[0].GetCustomAttributes(false); + Assert.IsNotNull(method); + Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(typeof(FakeXmlElementAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method."); + + FakeXmlElementAttribute xea = attrs[0] as FakeXmlElementAttribute; + Assert.AreEqual(-1, xea.Order); + } + + [Test] + public void ProxySpecificTargetTypeAttributeNotInstantiableWithDefaultValues() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ClassWithFakeGenerateScriptTypeAttribute); + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object[] attrs = proxy.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type."); + Assert.AreEqual(typeof(FakeGenerateScriptTypeAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type."); + + FakeGenerateScriptTypeAttribute fgsta = attrs[0] as FakeGenerateScriptTypeAttribute; + Assert.AreEqual("ScriptName", fgsta.Name); + } + + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=296032 + // should not fail + // Not fixed by .NET 2.0 SP1. + [Test] + public void ProxySpecificTargetTypeAttributeWithPublicEnumProperty() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ClassWithPublicEnumPropertyAttribute); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(typeof(PublicEnumPropertyAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method."); + + PublicEnumPropertyAttribute pepa = attrs[0] as PublicEnumPropertyAttribute; + Assert.AreEqual(AttributeTargets.All, pepa.Data); + } + + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=161522 + // should not fail + // Use CustomAttributeData if patch applied (.NET 2.0 SP1). + // Use Attribute instance if patch not applied (.NET 2.0 SP1). + [Test] + public void ProxyWebServiceAttribute() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ClassWithWebServiceAttribute); + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object[] attrs = proxy.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type."); + Assert.AreEqual(typeof(WebServiceAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type."); + + WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; + Assert.AreEqual("blah", wsa.Name); + Assert.AreEqual("http://mynamespace.com", wsa.Namespace); + } + + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94803 + [Test] +#if !NET_2_0 + [Ignore("GetCustomAttributes() does not return SecurityAttribute in .NET 1.x")] +#endif + public void ProxySecurityAttribute() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ClassWithSecurityAttribute); + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object[] attrs = proxy.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 2 attribute applied to the target type."); + Assert.AreEqual(2, attrs.Length, "Should have had 2 attribute applied to the target type."); + } + + [Test] + public void ProxySpecificTargetTypeAttributeWithArrayConstructor() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ClassWithArrayConstructorAttribute); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(typeof(ArrayConstructorPropertyAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method."); + + ArrayConstructorPropertyAttribute acpa = attrs[0] as ArrayConstructorPropertyAttribute; + Assert.AreEqual(false, acpa.ReadOnly); + Assert.AreEqual(1, acpa.Types.Length); + Assert.AreEqual(typeof(string), acpa.Types[0]); + } + + [Test] + public void ProxySpecificTargetTypeAttributeWithArrayProperty() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ClassWithArrayPropertyAttribute); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + MethodInfo method = proxy.GetMethod("Spring.Proxy.IAnotherMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(typeof(ArrayConstructorPropertyAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method."); + + ArrayConstructorPropertyAttribute acpa = attrs[0] as ArrayConstructorPropertyAttribute; + Assert.AreEqual(true, acpa.ReadOnly); + Assert.AreEqual(2, acpa.Types.Length); + Assert.AreEqual(typeof(int), acpa.Types[0]); + Assert.AreEqual(typeof(string), acpa.Types[1]); + } + + [Test] + public void ProxyTargetTypeAttributes() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(AnotherMarkerClass); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + object[] attrs = proxy.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type."); + } + + [Test] + public void DoesNotProxyTargetTypeAttributesWithProxyTargetAttributesEqualsFalse() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(AnotherMarkerClass); + builder.ProxyTargetAttributes = false; + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + object[] attrs = proxy.GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the target type."); + } + + [Test] + public void ProxyTargetMethodAttributes() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(AnotherMarkerClass); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("Spring.Proxy.IAnotherMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method."); + } + + [Test] + public void DoesNotProxyTargetMethodAttributesWithProxyTargetAttributesEqualsFalse() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(AnotherMarkerClass); + builder.ProxyTargetAttributes = false; + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("Spring.Proxy.IAnotherMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the target method."); + } + + [Test] + public void ProxyTargetMethodParameterAttributes() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(SomeMarkerClass); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + object[] attrs = method.GetParameters()[1].GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's parameter."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's parameter."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's parameter."); + } + + [Test] + public void DoesNotProxyTargetMethodParameterAttributesWithProxyTargetAttributesEqualsFalse() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(SomeMarkerClass); + builder.ProxyTargetAttributes = false; + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + object[] attrs = method.GetParameters()[1].GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's parameter."); + } + +#if NET_2_0 + [Test] + public void ProxyTargetMethodReturnValueAttributes() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(SomeMarkerClass); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's return value."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's return value."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's return value."); + } + + [Test] + public void DoesNotProxyTargetMethodReturnValueAttributesWithProxyTargetAttributesEqualsFalse() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(SomeMarkerClass); + builder.ProxyTargetAttributes = false; + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + method = proxy.GetMethod("MarkerMethod"); + } + Assert.IsNotNull(method); + object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's return value."); + } + + #region ProxyGenericMethod + + [Test] + public void ProxyGenericMethod() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ClassWithGenericMethod); + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + InterfaceWithGenericMethod foo = Activator.CreateInstance(proxy) as InterfaceWithGenericMethod; + Assert.IsNotNull(foo); + + foo.PolymorphicMethod(); + + string result1 = foo.PolymorphicMethod("coucou"); + Assert.AreEqual("coucou", result1); + + foo.WithGenericParameter(); + + foo.WithGenericParameterAndGenericArgument("ola"); + + string result2 = foo.WithGenericParameterAndGenericArgumentAndGenericReturnType("ola"); + Assert.AreEqual("ola", result2); + + foo.WithInterfaceConstraint(); + + foo.WithBaseTypeConstraint(); + + foo.WithBaseTypeAndInterfaceConstraints(); + + foo.WithMixedConstraint(); + } + + public interface InterfaceWithGenericMethod + { + void PolymorphicMethod(); + + string PolymorphicMethod(string param); + + void WithGenericParameter(); + + void WithGenericParameterAndGenericArgument(T obj); + + T WithGenericParameterAndGenericArgumentAndGenericReturnType(T obj); + + void WithInterfaceConstraint() where Z : IComparable; + + void WithBaseTypeConstraint() where Z : TestObject; + + void WithBaseTypeAndInterfaceConstraints() where Z : TestObject, IComparable, IDisposable; + + void WithMixedConstraint() + where X : IComparable, new() + where Y : TestObject; + } + + public class ClassWithGenericMethod : InterfaceWithGenericMethod + { + public void PolymorphicMethod() { } + + public string PolymorphicMethod(string param) + { + return param; + } + + public void WithGenericParameter() { } + + public void WithGenericParameterAndGenericArgument(T obj) { } + + public T WithGenericParameterAndGenericArgumentAndGenericReturnType(T obj) + { + return obj; + } + + public void WithInterfaceConstraint() where Z : IComparable { } + + public void WithBaseTypeConstraint() where Z : TestObject { } + + public void WithBaseTypeAndInterfaceConstraints() where Z : TestObject, IComparable, IDisposable { } + + public void WithMixedConstraint() + where X : IComparable, new() + where Y : TestObject + { } + } + + #endregion + + #region ProxyGenericInterface + + [Test] + public void ProxyGenericInterface() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ClassThatImplementsGenericInterface); + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + GenericInterface foo = Activator.CreateInstance(proxy) as GenericInterface; + Assert.IsNotNull(foo); + + TestObject to1 = foo.Create(); + Assert.IsNotNull(to1); + + TestObject to2 = foo.Populate(new TestObject()); + Assert.IsNotNull(to2); + Assert.AreEqual("Populated", to2.Name); + } + + public interface GenericInterface + where T : ITestObject, new() + { + T Create(); + + T Populate(T testObject); + } + + public class ClassThatImplementsGenericInterface : GenericInterface + where T : ITestObject, new() + { + public T Create() + { + return new T(); + } + + public T Populate(T testObject) + { + testObject.Name = "Populated"; + + return testObject; + } + } + + #endregion +#endif + + protected abstract IProxyTypeBuilder GetProxyBuilder(); + + #region Helper inner classes definition + + public interface InnerInterface + { + } + + public class InnerClass : InnerInterface + { + } + + #endregion + + } + + #region Helper classes definition + + public class DoesntImplementAnyInterfaces + { + } + + public interface IMarkerInterface + { + } + + public class MarkerClass : IMarkerInterface + { + } + + [GuidAttribute("7cfc9607-e81a-48ac-a080-bda88193607b")] + public class ClassWithGuidAttribute : IMarkerInterface + { + } + + public interface IAnotherMarkerInterface + { + void MarkerMethod(); + } + + public interface ISomeMarkerInterface + { + string MarkerMethod(int param1, string param2); + } + + // XmlElementAttribute.Order property is only available in .NET 2.0 + public sealed class FakeXmlElementAttribute : Attribute + { + private int _order; + public int Order + { + get + { + return this._order; + } + set + { + if (value < 0) + { + throw new ArgumentException("Negative values are prohibited.", "Order"); + } + this._order = value; + } + } + + private string _elementName; + public string ElementName + { + get { return _elementName; } + set { _elementName = value; } + } + + public FakeXmlElementAttribute() + { + this._order = -1; + } + + public FakeXmlElementAttribute(string elementName) + { + this._order = -1; + this._elementName = elementName; + } + } + + public sealed class FakeGenerateScriptTypeAttribute : Attribute + { + public FakeGenerateScriptTypeAttribute(string name) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + this._name = name; + } + + private string _name; + public string Name + { + get + { + return this._name; + } + } + } + + public class ClassWithFakeXmlElementAttribute : ISomeMarkerInterface + { + public string MarkerMethod([FakeXmlElement("parameter1")] int param1, string param2) + { + return null; + } + } + + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=161522 + [WebService(Namespace = "http://mynamespace.com", Name = "blah")] + public class ClassWithWebServiceAttribute : IMarkerInterface + { + } + + [FakeGenerateScriptType("ScriptName")] + public class ClassWithFakeGenerateScriptTypeAttribute : IMarkerInterface + { + } + + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=296032 + public class ClassWithPublicEnumPropertyAttribute : ISomeMarkerInterface + { + [PublicEnumProperty(AttributeTargets.All)] + public string MarkerMethod(int param1, string param2) + { + return null; + } + } + + // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94803 + [Marker] + [WebPermission(SecurityAction.Deny)] + public class ClassWithSecurityAttribute : IMarkerInterface + { + } + + public class ClassWithArrayConstructorAttribute : ISomeMarkerInterface + { + [ArrayConstructorPropertyAttribute(false, new Type[1] { typeof(string) })] + public string MarkerMethod(int param1, string param2) + { + return null; + } + } + + public class ClassWithArrayPropertyAttribute : IAnotherMarkerInterface + { + [ArrayConstructorPropertyAttribute(ReadOnly = true, Types = new Type[2] { typeof(int), typeof(string) })] + public void MarkerMethod() + { + } + } + + public class SomeMarkerClass : ISomeMarkerInterface + { + [return: Marker] + public string MarkerMethod(int param1, [Marker]string param2) + { + return string.Empty; + } + + [return: Marker] + public virtual string MarkerVirtualMethod(int param1, [Marker]string param2) + { + return string.Empty; + } + } + + [Marker] + public class AnotherMarkerClass : IAnotherMarkerInterface + { + [Marker] + public void MarkerMethod() + { + } + + [Marker] + public virtual void MarkerVirtualMethod() + { + } + } + + public class ArrayConstructorPropertyAttribute : Attribute + { + private bool _readOnly = false; + private Type[] _types = Type.EmptyTypes; + + public ArrayConstructorPropertyAttribute() + { + } + + public ArrayConstructorPropertyAttribute(bool readOnly, Type[] types) + { + this._readOnly = readOnly; + this._types = types; + } + + public bool ReadOnly + { + get { return _readOnly; } + set { _readOnly = value; } + } + + public Type[] Types + { + get { return _types; } + set { _types = value; } + } + } + + public class PublicEnumPropertyAttribute : Attribute + { + private Enum _data; + + public PublicEnumPropertyAttribute(AttributeTargets data) + { + _data = data; + } + + public PublicEnumPropertyAttribute(TypeCode data) + { + _data = data; + } + + public Enum Data + { + get { return _data; } + } + } + + public sealed class MarkerAttribute : Attribute + { + private int _count; + + public int Count + { + get { return _count; } + set { _count = value; } + } + } + + public interface IBase + { + void Base(); + } + + public interface IInherited : IBase + { + void Inherited(); + } + + public class MultipleInterfaces : IInherited + { + #region IInherited Members + + public void Inherited() + { + } + + #endregion + + #region IBase Members + + public void Base() + { + } + + #endregion + } + + public interface ITargetObjectTest + { + string InterfaceProperty { get; set; } + + bool InterfaceMethodWithArguments(string code); + + void InterfaceVirtualMethodWithNoArguments(); + } + + public class TargetObjectTest : ITargetObjectTest + { + private string _interfaceProperty; + + public TargetObjectTest() { } + + public TargetObjectTest(string interfaceProperty) + { + _interfaceProperty = interfaceProperty; + } + + public virtual string InterfaceProperty + { + get { return _interfaceProperty; } + set { _interfaceProperty = value; } + } + + public bool InterfaceMethodWithArguments(string code) + { + return true; + } + + public virtual void InterfaceVirtualMethodWithNoArguments() + { + } + + public void MethodWithArguments(string code) + { + } + } + + [ProxyIgnore] + public interface IFrameworkInterface + { + void FrameworkMethod(); + } + + public interface IApplicationInterface + { + void ApplicationMethod(); + } + + public class ApplicationClass : IApplicationInterface, IFrameworkInterface + { + public void FrameworkMethod() { } + + public void ApplicationMethod() { } + } + + #endregion + +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Proxy/CompositionProxyTypeBuilderTests.cs b/test/Spring/Spring.Core.Tests/Proxy/CompositionProxyTypeBuilderTests.cs new file mode 100644 index 00000000..a0201e8c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Proxy/CompositionProxyTypeBuilderTests.cs @@ -0,0 +1,138 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Unit tests for the CompositionProxyTypeBuilder class. + /// + /// Rick Evans + /// $Id: CompositionProxyTypeBuilderTests.cs,v 1.6 2006/11/16 02:30:51 bbaia Exp $ + [TestFixture] + public class CompositionProxyTypeBuilderTests : AbstractProxyTypeBuilderTests + { + [Test] + [ExpectedException(typeof (ArgumentException), + "Composition proxy target must implement at least one interface.")] + public void OnClassThatDoesntImplementAnyInterfaces() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof (DoesntImplementAnyInterfaces); + builder.BuildProxyType(); + } + + [Test] + [ExpectedException(typeof (NullReferenceException))] + public void BuildProxyWithNothingSet() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.BuildProxyType(); + } + + [Test] + public void ProxyInnerClass() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(InnerClass); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is InnerInterface); + Assert.IsFalse(foo is InnerClass); + } + + [Test] + public void CheckInterfaceImplementation() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof (MarkerClass); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is IMarkerInterface); + Assert.IsFalse(foo is MarkerClass); + } + + [Test] + public void SetsInterfacesToProxy() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(MultipleInterfaces); + builder.Interfaces = new Type[] { typeof(IBase) }; + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is IBase); + Assert.IsFalse(foo is IInherited); + + // try to call proxied interface methods + ((IBase)foo).Base(); + } + + [Test] + public void DoesNotProxyNonProxiableInterface() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ApplicationClass); + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is IApplicationInterface); + Assert.IsFalse(foo is IFrameworkInterface); + + // try to call proxied interface methods + ((IApplicationInterface)foo).ApplicationMethod(); + } + + [Test] + public void ForcesNonProxiableInterfacesToBeProxied() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ApplicationClass); + builder.Interfaces = new Type[] { typeof(IFrameworkInterface) }; + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object foo = Activator.CreateInstance(proxy); + Assert.IsFalse(foo is IApplicationInterface); + Assert.IsTrue(foo is IFrameworkInterface); + + // try to call proxied interface methods + ((IFrameworkInterface)foo).FrameworkMethod(); + } + + protected override IProxyTypeBuilder GetProxyBuilder() + { + return new CompositionProxyTypeBuilder(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Proxy/DynamicProxyManagerTests.cs b/test/Spring/Spring.Core.Tests/Proxy/DynamicProxyManagerTests.cs new file mode 100644 index 00000000..c7d4e9f7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Proxy/DynamicProxyManagerTests.cs @@ -0,0 +1,123 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection.Emit; +using System.Threading; +using NUnit.Framework; + +#endregion + +namespace Spring.Proxy +{ + /// + /// + /// Erich Eichinger + /// $Id: DynamicProxyManagerTests.cs,v 1.2 2007/05/02 11:35:43 bbaia Exp $ + [TestFixture] + public class DynamicProxyManagerTests + { + #region WorkerThread Class + + public class WorkerThread + { + private Exception _exception; + private Thread _thread; + private WaitCallback _callback; + private object _arg; + + public WorkerThread(WaitCallback callback,object arg) + { + this._arg = arg; + this._callback = callback; + _thread = new Thread( new ThreadStart( Run ) ); + } + + public void Start() + { + _thread.Start(); + } + + public void Join() + { + _thread.Join(); + } + + public Exception Exception + { + get { return this._exception; } + } + + private void Run() + { + try + { + _callback( _arg ); + } + catch(Exception ex) + { + _exception = ex; + } + } + } + + #endregion WorkerThread Class + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void CreateTypeBuilderMustNotBeCalledTwiceWithSameArguments() + { + TypeBuilder tb1 = DynamicProxyManager.CreateTypeBuilder("testtypename", null); + TypeBuilder tb2 = DynamicProxyManager.CreateTypeBuilder("testtypename", typeof(AbstractProxyTypeBuilder) ); + } + + [Test] + public void CreateTypeBuilderIsThreadSafe() + { + const int WORKERS = 20; + + WorkerThread[] workers = new WorkerThread[WORKERS]; + for(int i=0;i + /// Unit tests for the CompositionProxyTypeBuilder class. + /// + /// Rick Evans + /// $Id: ExplicitCompositionProxyTypeBuilderTests.cs,v 1.3 2006/08/10 18:45:58 bbaia Exp $ + [TestFixture] + public sealed class ExplicitCompositionProxyTypeBuilderTests : CompositionProxyTypeBuilderTests + { + [Test] + public void ExplicitInterfaceImplementation() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(MultipleInterfaces); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is IBase); + Assert.IsTrue(foo is IInherited); + + Type fooType = foo.GetType(); + MethodInfo[] methods = fooType.GetMethods(BindingFlags.DeclaredOnly); + Assert.AreEqual(0, methods.Length, "Methods array should be empty."); + + methods = fooType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Instance); + Assert.AreEqual(2, methods.Length, "Methods array should have two elements."); + Assert.IsTrue(methods[0].Name.IndexOf('.') > 0, "Method should have '.' in the name"); + Assert.IsTrue(methods[1].Name.IndexOf('.') > 0, "Method should have '.' in the name"); + + } + + protected override IProxyTypeBuilder GetProxyBuilder() + { + CompositionProxyTypeBuilder pb = new CompositionProxyTypeBuilder(); + pb.ExplicitInterfaceImplementation = true; + return pb; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Proxy/InheritanceProxyTypeBuilderTests.cs b/test/Spring/Spring.Core.Tests/Proxy/InheritanceProxyTypeBuilderTests.cs new file mode 100644 index 00000000..012448df --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Proxy/InheritanceProxyTypeBuilderTests.cs @@ -0,0 +1,313 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using NUnit.Framework; + +using Spring.Util; + +#endregion + +namespace Spring.Proxy +{ + /// + /// Unit tests for the InheritanceProxyTypeBuilder class. + /// + /// Rick Evans + /// Bruno Baia + /// $Id: InheritanceProxyTypeBuilderTests.cs,v 1.9 2007/06/27 15:34:47 bbaia Exp $ + [TestFixture] + public sealed class InheritanceProxyTypeBuilderTests : AbstractProxyTypeBuilderTests + { + [Test] + public void OnClassThatDoesntImplementAnyInterfaces() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(DoesntImplementAnyInterfaces); + builder.BuildProxyType(); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WithSealedClass() + { + InheritanceProxyTypeBuilder builder = (InheritanceProxyTypeBuilder) GetProxyBuilder(); + builder.TargetType = typeof(SealedClass); + builder.BuildProxyType(); + } + + [Test] + public void ProxyInnerClass() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(InnerClass); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is InnerInterface); + Assert.IsTrue(foo is InnerClass); + } + + [Test] + public void CheckInheritType() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(DoesntImplementAnyInterfaces); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is DoesntImplementAnyInterfaces, + "Using inheritance proxying, so the proxied object must inherit from the base type."); + } + + [Test] + public void OverrideVirtualMethod() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(TargetObjectTest); + Type proxy = builder.BuildProxyType(); + + MethodInfo method = ReflectionUtils.GetMethod(proxy, "InterfaceVirtualMethodWithNoArguments", Type.EmptyTypes); + + Assert.IsNotNull(method, "Should define this method."); + Assert.AreEqual(proxy, method.DeclaringType, "Method should be overrided in the proxy class."); + + // call overrided method + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is TargetObjectTest); + ((TargetObjectTest)foo).InterfaceVirtualMethodWithNoArguments(); + } + + [Test] + public void ImplementFinalMethodThatImplementsInterface() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(TargetObjectTest); + Type proxy = builder.BuildProxyType(); + + MethodInfo method = ReflectionUtils.GetMethod(proxy, "Spring.Proxy.ITargetObjectTest.InterfaceMethodWithArguments", new Type[] { typeof(string) }); + + Assert.IsNotNull(method, "Should define this method."); + Assert.AreEqual(proxy, method.DeclaringType, "Method should be defined in the proxy class."); + + // call final interfaced method + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is ITargetObjectTest); + ((ITargetObjectTest)foo).InterfaceMethodWithArguments("code"); + } + + [Test] + public void DoesNotImplementFinalMethodThatDoesNotImplementInterface() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(TargetObjectTest); + Type proxy = builder.BuildProxyType(); + + MethodInfo method = ReflectionUtils.GetMethod(proxy, "MethodWithArguments", new Type[] { typeof(string) }); + + Assert.IsNotNull(method, "Should define this method."); + Assert.AreNotEqual(proxy, method.DeclaringType, "Method can't be proxied."); + + // call base method + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is TargetObjectTest); + ((TargetObjectTest)foo).MethodWithArguments("code"); + } + + [Test] + public void SetsInterfacesToProxy() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(MultipleInterfaces); + builder.Interfaces = new Type[] { typeof(IAnotherMarkerInterface) }; + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is IBase); + Assert.IsTrue(foo is IInherited); + Assert.IsTrue(foo is IAnotherMarkerInterface); + + // try to call proxied interface methods + ((IBase)foo).Base(); + ((IInherited)foo).Base(); + ((IInherited)foo).Inherited(); + } + + + [Test] + public void DoesNotProxyNonProxiableInterface() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ApplicationClass); + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is IApplicationInterface); + Assert.IsTrue(foo is IFrameworkInterface); + + // proxy implements IApplicationInterface and proxy methods + Assert.IsNotNull(foo.GetType().GetMethod("Spring.Proxy.IApplicationInterface.ApplicationMethod", BindingFlags.Instance | BindingFlags.NonPublic)); + + // proxy implements IFrameworkInterface but should not proxy methods + Assert.IsNull(foo.GetType().GetMethod("Spring.Proxy.IFrameworkInterface.FrameworkMethod", BindingFlags.Instance | BindingFlags.NonPublic)); + } + + [Test] + public void ForcesNonProxiableInterfacesToBeProxied() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(ApplicationClass); + builder.Interfaces = new Type[] { typeof(IFrameworkInterface) }; + + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + + object foo = Activator.CreateInstance(proxy); + Assert.IsTrue(foo is IApplicationInterface); + Assert.IsTrue(foo is IFrameworkInterface); + + // proxy implements IFrameworkInterface and proxy methods + Assert.IsNotNull(foo.GetType().GetMethod("Spring.Proxy.IFrameworkInterface.FrameworkMethod", BindingFlags.Instance | BindingFlags.NonPublic)); + + // proxy implements IApplicationInterface but should not proxy methods + Assert.IsNull(foo.GetType().GetMethod("Spring.Proxy.IApplicationInterface.ApplicationMethod", BindingFlags.Instance | BindingFlags.NonPublic)); + } + + [Test] + public void ProxyTargetVirtualMethodAttributes() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(AnotherMarkerClass); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method."); + } + + [Test] + public void DoesNotProxyTargetVirtualMethodAttributesWithProxyTargetAttributesEqualsFalse() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(AnotherMarkerClass); + builder.ProxyTargetAttributes = false; + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + object[] attrs = method.GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the target method."); + } + + [Test] + public void ProxyTargetVirtualMethodParameterAttributes() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(SomeMarkerClass); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + object[] attrs = method.GetParameters()[1].GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's parameter."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's parameter."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's parameter."); + } + + [Test] + public void DoesNotProxyTargetVirtualMethodParameterAttributesWithProxyTargetAttributesEqualsFalse() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(SomeMarkerClass); + builder.ProxyTargetAttributes = false; + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + object[] attrs = method.GetParameters()[1].GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's parameter."); + } + +#if NET_2_0 + [Test] + public void ProxyTargetVirtualMethodReturnValueAttributes() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(SomeMarkerClass); + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false); + Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's return value."); + Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's return value."); + Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's return value."); + } + + [Test] + public void DoesNotProxyTargetVirtualMethodReturnValueAttributesWithProxyTargetAttributesEqualsFalse() + { + IProxyTypeBuilder builder = GetProxyBuilder(); + builder.TargetType = typeof(SomeMarkerClass); + builder.ProxyTargetAttributes = false; + Type proxy = builder.BuildProxyType(); + Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null."); + MethodInfo method = proxy.GetMethod("MarkerVirtualMethod"); + Assert.IsNotNull(method); + object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false); + Assert.IsNotNull(attrs); + Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's return value."); + } +#endif + + protected override IProxyTypeBuilder GetProxyBuilder() + { + return new InheritanceProxyTypeBuilder(); + } + } + + #region Helper Classes + + public class NotSealedClass + { + public bool IsAnyoneThere() + { + return false; + } + } + + public sealed class SealedClass + {} + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/BasePropertyTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/BasePropertyTests.cs new file mode 100644 index 00000000..17c9a505 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/BasePropertyTests.cs @@ -0,0 +1,304 @@ +using System; +using System.Diagnostics; +using System.Reflection; +using NUnit.Framework; +using Spring.Context.Support; + +namespace Spring.Reflection.Dynamic +{ + /// + /// Unit tests common for all IDynamicProperty implementations. + /// + /// Aleksandar Seovic + /// Erich Eichinger + /// $Id: BasePropertyTests.cs,v 1.1 2008/05/17 11:05:27 oakinger Exp $ + public abstract class BasePropertyTests + { + protected Inventor tesla; + protected Inventor pupin; + protected Society ieee; + + #region SetUp and TearDown + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp() + { + ContextRegistry.Clear(); + tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + tesla.Inventions = new string[] + { + "Telephone repeater", "Rotating magnetic field principle", + "Polyphase alternating-current system", "Induction motor", + "Alternating-current power transmission", "Tesla coil transformer", + "Wireless communication", "Radio", "Fluorescent lights" + }; + tesla.PlaceOfBirth.City = "Smiljan"; + + pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), "Serbian"); + pupin.Inventions = new string[] { "Long distance telephony & telegraphy", "Secondary X-Ray radiation", "Sonar" }; + pupin.PlaceOfBirth.City = "Idvor"; + pupin.PlaceOfBirth.Country = "Serbia"; + + ieee = new Society(); + ieee.Members.Add(tesla); + ieee.Members.Add(pupin); + ieee.Officers["president"] = pupin; + ieee.Officers["advisors"] = new Inventor[] { tesla, pupin }; // not historically accurate, but I need an array in the map ;-) + } + + [TestFixtureTearDown] + public void TearDown() + { + //DynamicReflectionManager.SaveAssembly(); + } + + #endregion + + protected abstract IDynamicProperty Create(PropertyInfo property); + + [Test] + public void TestInstanceProperties() + { + IDynamicProperty placeOfBirth = Create(typeof(Inventor).GetProperty("PlaceOfBirth")); + Assert.AreEqual(tesla.PlaceOfBirth, placeOfBirth.GetValue(tesla)); + + IDynamicProperty dateOfBirth = Create(typeof(Inventor).GetProperty("DOB")); + Assert.AreEqual(tesla.DOB, dateOfBirth.GetValue(tesla)); + dateOfBirth.SetValue(tesla, new DateTime(2004, 8, 14)); + Assert.AreEqual(new DateTime(2004, 8, 14), dateOfBirth.GetValue(tesla)); + + DateTime mostImportantDayInTheWorldEver = new DateTime(2004, 8, 14); + IDynamicProperty year = Create(typeof(DateTime).GetProperty("Year")); + Assert.AreEqual(mostImportantDayInTheWorldEver.Year, year.GetValue(mostImportantDayInTheWorldEver)); + } + + [Test] + public void TestStaticProperties() + { + IDynamicProperty today = Create(typeof(DateTime).GetProperty("Today")); + Assert.AreEqual(DateTime.Today, today.GetValue(null)); + + IDynamicProperty myProperty = Create(typeof(MyStaticClass).GetProperty("MyProperty")); + myProperty.SetValue(null, "here we go..."); + Assert.AreEqual("here we go...", myProperty.GetValue(null)); + } + +#if NET_2_0 + [Test, Ignore("test N/A anymore due to System.Reflection.Emit.DynamicMethod")] + [ExpectedException(typeof(MethodAccessException))] + public void TestForRestrictiveGetter() + { + Something something = new Something(); + + IDynamicProperty third = Create(typeof(Something).GetProperty("Third")); + //this should be ok, because both get and set of the "Third" property are public + third.SetValue(something, 456); + Assert.AreEqual(456, third.GetValue(something)); + + IDynamicProperty first = Create(typeof(Something).GetProperty("First")); + first.SetValue(something, 123); + //this should cause MethodAccessException, because get is private + Assert.AreEqual(123, first.GetValue(something)); + } + + [Test, Ignore("test N/A anymore due to System.Reflection.Emit.DynamicMethod")] + [ExpectedException(typeof(MethodAccessException))] + public void TestForRestrictiveSetter() + { + Something something = new Something(); + + IDynamicProperty third = Create(typeof(Something).GetProperty("Third")); + third.SetValue(something, 456); + //this should be ok, because both get and set of the "Third" property are public + Assert.AreEqual(456, third.GetValue(something)); + + IDynamicProperty second = Create(typeof(Something).GetProperty("Second")); + Assert.AreEqual(2, second.GetValue(something)); + //this should cause MethodAccessException, because set is private in "Second" property + second.SetValue(something, 123); + + //this should never execute + Assert.AreEqual(123, second.GetValue(something)); + } +#endif + + #region Performance tests + + private DateTime start, stop; + + [Test] + [Explicit] + public void PerformanceTests() + { + int n = 10000000; + object x = null; + + // tesla.PlaceOfBirth + start = DateTime.Now; + for (int i = 0; i < n; i++) + { + x = tesla.PlaceOfBirth; + } + stop = DateTime.Now; + PrintTest("tesla.PlaceOfBirth (direct)", n, Elapsed); + + start = DateTime.Now; + IDynamicProperty placeOfBirth = DynamicProperty.Create(typeof(Inventor).GetProperty("PlaceOfBirth")); + for (int i = 0; i < n; i++) + { + x = placeOfBirth.GetValue(tesla); + } + stop = DateTime.Now; + PrintTest("tesla.PlaceOfBirth (dynamic reflection)", n, Elapsed); + + start = DateTime.Now; + PropertyInfo placeOfBirthPi = typeof(Inventor).GetProperty("PlaceOfBirth"); + for (int i = 0; i < n; i++) + { + x = placeOfBirthPi.GetValue(tesla, null); + } + stop = DateTime.Now; + PrintTest("tesla.PlaceOfBirth (standard reflection)", n, Elapsed); + } + + private double Elapsed + { + get { return (stop.Ticks - start.Ticks) / 10000000f; } + } + + private void PrintTest(string name, int iterations, double duration) + { + Debug.WriteLine(String.Format("{0,-60} {1,12:#,###} {2,12:##0.000} {3,12:#,###}", name, iterations, duration, iterations / duration)); + } + + #endregion + } + + #region IL generation helper classes (they help if you look at them in Reflector ;-) + + public class ValueTypeProperty : IDynamicProperty + { + public object GetValue(object target) + { + return ((Inventor)target).DOB; + } + + public void SetValue(object target, object value) + { + ((Inventor)target).DOB = (DateTime)value; + } + } + + public class ValueTypeTarget : IDynamicProperty + { + public object GetValue(object target) + { + return ((MyStruct)target).Year; + } + + public void SetValue(object target, object value) + { + MyStruct o = (MyStruct)target; + o.Year = (int)value; + } + } + + public class StaticProperty : IDynamicProperty + { + public object GetValue(object target) + { + return MyStaticClass.MyProperty; + } + + public void SetValue(object target, object value) + { + MyStaticClass.MyProperty = (string)value; + } + } + + #endregion + + public class MyStaticClass + { + public const Int64 MyConst = 3456; + public static readonly string myReadonlyField = "hohoho"; + public static string myField; + + public static string MyProperty + { + get { return myField; } + set { myField = value; } + } + } + + public struct MyStaticStruct + { + public const int constant = 20; + public static int staticYear; + + public static int StaticYear + { + get { return staticYear; } + set { staticYear = value; } + } + } + + public struct MyStruct + { + public int year; + + public int Year + { + get { return year; } + set { year = value; } + } + } + + public class ClassWithNonReadableProperty + { + private string myProperty; + + public string MyProperty + { + set { myProperty = value; } + } + } + +#if NET_2_0 + public class Something + { + public int First + { + private get { return first; } + set { first = value; } + } + + public int Second + { + get { return second; } + private set { second = value; } + } + + public int Third + { + get { return third; } + set { third = value; } + } + public Something() + { + first = 1; + second = 2; + third = 3; + } + private int first; + private int second; + private int third; + + } +#endif + +} + diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicConstructorTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicConstructorTests.cs new file mode 100644 index 00000000..7f6a13d0 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicConstructorTests.cs @@ -0,0 +1,152 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Diagnostics; +using System.Reflection; +using NUnit.Framework; +using Spring.Context.Support; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + /// + /// Unit tests for the DynamicConstructor class. + /// + /// Aleksandar Seovic + /// $Id: DynamicConstructorTests.cs,v 1.1 2007/08/08 04:06:01 bbaia Exp $ + [TestFixture] + public sealed class DynamicConstructorTests + { + private Inventor tesla; + private Inventor pupin; + private Society ieee; + + #region SetUp and TearDown + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp() + { + ContextRegistry.Clear(); + tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + tesla.Inventions = new string[] + { + "Telephone repeater", "Rotating magnetic field principle", + "Polyphase alternating-current system", "Induction motor", + "Alternating-current power transmission", "Tesla coil transformer", + "Wireless communication", "Radio", "Fluorescent lights" + }; + tesla.PlaceOfBirth.City = "Smiljan"; + + pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), "Serbian"); + pupin.Inventions = new string[] { "Long distance telephony & telegraphy", "Secondary X-Ray radiation", "Sonar" }; + pupin.PlaceOfBirth.City = "Idvor"; + pupin.PlaceOfBirth.Country = "Serbia"; + + ieee = new Society(); + ieee.Members.Add(tesla); + ieee.Members.Add(pupin); + ieee.Officers["president"] = pupin; + ieee.Officers["advisors"] = new Inventor[] { tesla, pupin }; // not historically accurate, but I need an array in the map ;-) + } + + [TestFixtureTearDown] + public void TearDown() + { + //DynamicReflectionManager.SaveAssembly(); + } + + #endregion + + [Test] + public void TestConstructors() + { + IDynamicConstructor newInventor = DynamicConstructor.Create( + typeof(Inventor).GetConstructor(new Type[] { typeof(string), typeof(DateTime), typeof(string) })); + Inventor ana = (Inventor) newInventor.Invoke(new object[] {"Ana Maria Seovic", new DateTime(2004, 8, 14), "Serbian"}); + Assert.AreEqual("Ana Maria Seovic", ana.Name); + + IDynamicConstructor newDate = DynamicConstructor.Create( + typeof(DateTime).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int) })); + Assert.AreEqual(DateTime.Today, newDate.Invoke(new object[] { DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day })); + } + + #region Performance tests + + private DateTime start, stop; + + //[Test] + public void PerformanceTests() + { + int n = 10000000; + object x = null; + + // new Inventor() + start = DateTime.Now; + for (int i = 0; i < n; i++) + { + x = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + } + stop = DateTime.Now; + PrintTest("new Inventor() (direct)", n, Elapsed); + + start = DateTime.Now; + IDynamicConstructor newInventor = DynamicConstructor.Create( + typeof(Inventor).GetConstructor(new Type[] { typeof(string), typeof(DateTime), typeof(string) })); + for (int i = 0; i < n; i++) + { + object[] args = new object[] { "Nikola Tesla", new DateTime(1856, 7, 9), "Serbian" }; + x = newInventor.Invoke(args); + } + stop = DateTime.Now; + PrintTest("new Inventor() (dynamic reflection)", n, Elapsed); + + start = DateTime.Now; + ConstructorInfo newInventorCi = + typeof(Inventor).GetConstructor(new Type[] { typeof(string), typeof(DateTime), typeof(string) }); + for (int i = 0; i < n; i++) + { + object[] args = new object[] { "Nikola Tesla", new DateTime(1856, 7, 9), "Serbian" }; + x = newInventorCi.Invoke(args); + } + stop = DateTime.Now; + PrintTest("new Inventor() (standard reflection)", n, Elapsed); + } + + private double Elapsed + { + get { return (stop.Ticks - start.Ticks) / 10000000f; } + } + + private void PrintTest(string name, int iterations, double duration) + { + Debug.WriteLine(String.Format("{0,-60} {1,12:#,###} {2,12:##0.000} {3,12:#,###}", name, iterations, duration, iterations / duration)); + } + + #endregion + + } +} diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicFieldTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicFieldTests.cs new file mode 100644 index 00000000..5ed6c81a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicFieldTests.cs @@ -0,0 +1,267 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Diagnostics; +using System.Reflection; +using NUnit.Framework; +using Spring.Context.Support; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + /// + /// Unit tests for the DynamicField class. + /// + /// Aleksandar Seovic + /// $Id: DynamicFieldTests.cs,v 1.2 2008/05/16 10:02:41 oakinger Exp $ + [TestFixture] + public class DynamicFieldTests + { + protected Inventor tesla; + protected Inventor pupin; + protected Society ieee; + + #region SetUp and TearDown + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp() + { + ContextRegistry.Clear(); + tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + tesla.Inventions = new string[] + { + "Telephone repeater", "Rotating magnetic field principle", + "Polyphase alternating-current system", "Induction motor", + "Alternating-current power transmission", "Tesla coil transformer", + "Wireless communication", "Radio", "Fluorescent lights" + }; + tesla.PlaceOfBirth.City = "Smiljan"; + + pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), "Serbian"); + pupin.Inventions = new string[] { "Long distance telephony & telegraphy", "Secondary X-Ray radiation", "Sonar" }; + pupin.PlaceOfBirth.City = "Idvor"; + pupin.PlaceOfBirth.Country = "Serbia"; + + ieee = new Society(); + ieee.Members.Add(tesla); + ieee.Members.Add(pupin); + ieee.Officers["president"] = pupin; + ieee.Officers["advisors"] = new Inventor[] { tesla, pupin }; // not historically accurate, but I need an array in the map ;-) + } + + [TestFixtureTearDown] + public void TearDown() + { + //DynamicReflectionManager.SaveAssembly(); + } + + #endregion + + protected virtual IDynamicField Create(FieldInfo field) + { + return DynamicField.Create(field); + } + + [Test] + public void TestInstanceFields() + { + IDynamicField name = Create(typeof(Inventor).GetField("Name")); + Assert.AreEqual(tesla.Name, name.GetValue(tesla)); + name.SetValue(tesla, "Tesla, Nikola"); + Assert.AreEqual("Tesla, Nikola", tesla.Name); + Assert.AreEqual("Tesla, Nikola", name.GetValue(tesla)); + + MyStruct myYearHolder = new MyStruct(); + myYearHolder.Year = 2004; + IDynamicField year = Create(typeof(MyStruct).GetField("year", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)); + Assert.AreEqual(2004, year.GetValue(myYearHolder)); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void TestAttemptingToSetFieldOfValueTypeInstance() + { + MyStruct myYearHolder = new MyStruct(); + IDynamicField year = Create(typeof(MyStruct).GetField("year")); + year.SetValue(myYearHolder, 2004); + } + + [Test] + public void TestStaticFieldsOfClass() + { + IDynamicField myField = Create(typeof(MyStaticClass).GetField("myField")); + myField.SetValue(null, "here we go..."); + + IDynamicField myConst = Create(typeof(MyStaticClass).GetField("MyConst")); + Assert.AreEqual(3456, myConst.GetValue(null)); + try { + myConst.SetValue(null, 7890); + } + catch(InvalidOperationException){} + + IDynamicField myReadonlyField = Create(typeof(MyStaticClass).GetField("myReadonlyField")); + Assert.AreEqual("hohoho", myReadonlyField.GetValue(null)); + try { + myReadonlyField.SetValue(null, "some other string"); + } + catch(InvalidOperationException){} + } + + [Test] + public void TestStaticFieldsOfStruct() + { + // static readonly + IDynamicField maxValue = Create(typeof(DateTime).GetField("MaxValue")); + Assert.AreEqual(DateTime.MaxValue, maxValue.GetValue(null)); + try { + maxValue.SetValue(null, DateTime.Now); + } + catch(InvalidOperationException){} + + // const + IDynamicField int64max = Create(typeof(Int64).GetField("MaxValue", BindingFlags.Public | BindingFlags.Static)); + Assert.AreEqual(Int64.MaxValue, int64max.GetValue(null)); + try { + int64max.SetValue(null, 0); + } + catch(InvalidOperationException){} + + // pure static + IDynamicField myField = Create(typeof(MyStaticStruct).GetField("staticYear")); + myField.SetValue(null, 2008); + Assert.AreEqual(2008, myField.GetValue(null)); + } + + #region Performance tests + + private DateTime start, stop; + + //[Test] + public void PerformanceTests() + { + int n = 10000000; + object x = null; + + // tesla.Name + start = DateTime.Now; + for (int i = 0; i < n; i++) + { + x = tesla.Name; + } + stop = DateTime.Now; + PrintTest("tesla.Name (direct)", n, Elapsed); + + start = DateTime.Now; + IDynamicField placeOfBirth = Create(typeof(Inventor).GetField("Name")); + for (int i = 0; i < n; i++) + { + x = placeOfBirth.GetValue(tesla); + } + stop = DateTime.Now; + PrintTest("tesla.Name (dynamic reflection)", n, Elapsed); + + start = DateTime.Now; + FieldInfo placeOfBirthFi = typeof(Inventor).GetField("Name"); + for (int i = 0; i < n; i++) + { + x = placeOfBirthFi.GetValue(tesla); + } + stop = DateTime.Now; + PrintTest("tesla.Name (standard reflection)", n, Elapsed); + } + + private double Elapsed + { + get { return (stop.Ticks - start.Ticks) / 10000000f; } + } + + private void PrintTest(string name, int iterations, double duration) + { + Debug.WriteLine(String.Format("{0,-60} {1,12:#,###} {2,12:##0.000} {3,12:#,###}", name, iterations, duration, iterations / duration)); + } + + #endregion + + } + + #region IL generation helper classes (they help if you look at them in Reflector ;-) + + public class ValueTypeField : IDynamicField + { + public object GetValue(object target) + { + return ((Inventor) target).Name; + } + + public void SetValue(object target, object value) + { + ((Inventor)target).Name = (string) value; + } + } + + public class ValueTypeTargetField : IDynamicField + { + public object GetValue(object target) + { + return ((MyStruct) target).year; + } + + public void SetValue(object target, object value) + { + MyStruct o = (MyStruct) target; + o.Year = (int) value; + } + } + + public class StaticField : IDynamicField + { + public object GetValue(object target) + { + return MyStaticClass.myField; + } + + public void SetValue(object target, object value) + { + MyStaticClass.myField = (string) value; + } + } + + public class StaticConst : IDynamicField + { + public object GetValue(object target) + { + return MyStaticClass.MyConst; + } + + public void SetValue(object target, object value) + { + } + } + + #endregion + +} diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicIndexerTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicIndexerTests.cs new file mode 100644 index 00000000..934fa3b2 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicIndexerTests.cs @@ -0,0 +1,155 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Diagnostics; +using System.Reflection; +using NUnit.Framework; +using Spring.Context.Support; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + /// + /// Unit tests for the DynamicIndexer class. + /// + /// Aleksandar Seovic + /// $Id: DynamicIndexerTests.cs,v 1.1 2007/08/08 04:06:01 bbaia Exp $ + [TestFixture] + public sealed class DynamicIndexerTests + { + private Inventor tesla; + private Inventor pupin; + private Society ieee; + + #region SetUp and TearDown + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp() + { + ContextRegistry.Clear(); + tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + tesla.Inventions = new string[] + { + "Telephone repeater", "Rotating magnetic field principle", + "Polyphase alternating-current system", "Induction motor", + "Alternating-current power transmission", "Tesla coil transformer", + "Wireless communication", "Radio", "Fluorescent lights" + }; + tesla.PlaceOfBirth.City = "Smiljan"; + + pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), "Serbian"); + pupin.Inventions = new string[] { "Long distance telephony & telegraphy", "Secondary X-Ray radiation", "Sonar" }; + pupin.PlaceOfBirth.City = "Idvor"; + pupin.PlaceOfBirth.Country = "Serbia"; + + ieee = new Society(); + ieee.Members.Add(tesla); + ieee.Members.Add(pupin); + ieee.Officers["president"] = pupin; + ieee.Officers["advisors"] = new Inventor[] { tesla, pupin }; // not historically accurate, but I need an array in the map ;-) + } + + [TestFixtureTearDown] + public void TearDown() + { + //DynamicReflectionManager.SaveAssembly(); + } + + #endregion + + [Test] + public void TestIndexers() + { + IDynamicIndexer members = DynamicIndexer.Create(typeof(ArrayList).GetProperty("Item")); + Inventor nikola = (Inventor) members.GetValue(ieee.Members, new object[] { 0 }); + Assert.AreEqual(tesla, nikola); + members.SetValue(ieee.Members, new object[] { 0 }, new Inventor("Ana Maria Seovic", new DateTime(2004, 8, 14), "Serbian")); + Assert.AreEqual("Ana Maria Seovic", ((Inventor) members.GetValue(ieee.Members, 0)).Name); + members.SetValue(ieee.Members, 1, tesla); + Assert.AreEqual("Nikola Tesla", ((Inventor) members.GetValue(ieee.Members, 1)).Name); + + IDynamicIndexer officers = DynamicIndexer.Create(typeof(Hashtable).GetProperty("Item")); + Assert.AreEqual(pupin, officers.GetValue(ieee.Officers, new object[] {"president"})); + officers.SetValue(ieee.Officers, "president", + new Inventor("Aleksandar Seovic", new DateTime(1974, 8, 24), "Serbian")); + Assert.AreEqual("Aleksandar Seovic", ((Inventor)officers.GetValue(ieee.Officers, "president")).Name); + } + + #region Performance tests + + private DateTime start, stop; + + //[Test] + public void PerformanceTests() + { + int n = 10000000; + object x = null; + + // ieee.Members[0] + start = DateTime.Now; + for (int i = 0; i < n; i++) + { + x = ieee.Members[0]; + } + stop = DateTime.Now; + PrintTest("ieee.Members[0] (direct)", n, Elapsed); + + start = DateTime.Now; + IDynamicIndexer members = DynamicIndexer.Create(typeof(ArrayList).GetProperty("Item")); + for (int i = 0; i < n; i++) + { + x = members.GetValue(ieee.Members, 0); + } + stop = DateTime.Now; + PrintTest("ieee.Members[0] (dynamic reflection)", n, Elapsed); + + start = DateTime.Now; + PropertyInfo membersPi = typeof(ArrayList).GetProperty("Item"); + object[] indexArgs = new object[] { 0 }; + for (int i = 0; i < n; i++) + { + x = membersPi.GetValue(ieee.Members, indexArgs); + } + stop = DateTime.Now; + PrintTest("ieee.Members[0] (standard reflection)", n, Elapsed); + } + + private double Elapsed + { + get { return (stop.Ticks - start.Ticks) / 10000000f; } + } + + private void PrintTest(string name, int iterations, double duration) + { + Debug.WriteLine(String.Format("{0,-60} {1,12:#,###} {2,12:##0.000} {3,12:#,###}", name, iterations, duration, iterations / duration)); + } + + #endregion + + } +} diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs new file mode 100644 index 00000000..2b485327 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs @@ -0,0 +1,296 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Diagnostics; +using System.Reflection; +using NUnit.Framework; +using Spring.Context.Support; +using Spring.Util; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + /// + /// Unit tests for the DynamicMethod class. + /// + /// Aleksandar Seovic + /// $Id: DynamicMethodTests.cs,v 1.1 2007/08/08 04:06:01 bbaia Exp $ + [TestFixture] + public sealed class DynamicMethodTests + { + private Inventor tesla; + private Inventor pupin; + private Society ieee; + + #region SetUp and TearDown + + /// + /// The setup logic executed before the execution of each individual test. + /// + [SetUp] + public void SetUp() + { + ContextRegistry.Clear(); + tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + tesla.Inventions = new string[] + { + "Telephone repeater", "Rotating magnetic field principle", + "Polyphase alternating-current system", "Induction motor", + "Alternating-current power transmission", "Tesla coil transformer", + "Wireless communication", "Radio", "Fluorescent lights" + }; + tesla.PlaceOfBirth.City = "Smiljan"; + + pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), "Serbian"); + pupin.Inventions = new string[] { "Long distance telephony & telegraphy", "Secondary X-Ray radiation", "Sonar" }; + pupin.PlaceOfBirth.City = "Idvor"; + pupin.PlaceOfBirth.Country = "Serbia"; + + ieee = new Society(); + ieee.Members.Add(tesla); + ieee.Members.Add(pupin); + ieee.Officers["president"] = pupin; + ieee.Officers["advisors"] = new Inventor[] { tesla, pupin }; // not historically accurate, but I need an array in the map ;-) + } + + [TestFixtureTearDown] + public void TearDown() + { + //DynamicReflectionManager.SaveAssembly(); + } + + #endregion + + [Test] + public void TestInstanceMethods() + { + IDynamicMethod getAge = DynamicMethod.Create(typeof(Inventor).GetMethod("GetAge")); + Assert.AreEqual(tesla.GetAge(DateTime.Today), getAge.Invoke(tesla, new object[] {DateTime.Today})); + + MethodTarget target = new MethodTarget(); + IDynamicMethod test = DynamicMethod.Create(typeof(MethodTarget).GetMethod("MethodReturningString")); + Assert.AreEqual(tesla.Name, test.Invoke(target, new object[] { 5, DateTime.Today, new String[] {"xyz", "abc"}, tesla})); + + ArrayList list = new ArrayList(new string[] {"one", "two", "three"}); + IDynamicMethod removeAt = DynamicMethod.Create(typeof(ArrayList).GetMethod("RemoveAt")); + removeAt.Invoke(list, new object[] {1}); + Assert.AreEqual(2, list.Count); + Assert.AreEqual("three", list[1]); + } + + [Test] + public void TestStaticMethods() + { + IDynamicMethod isNullOrEmpty = DynamicMethod.Create(typeof(StringUtils).GetMethod("IsNullOrEmpty")); + Assert.IsTrue((bool) isNullOrEmpty.Invoke(null, new object[] { null })); + Assert.IsTrue((bool) isNullOrEmpty.Invoke(null, new object[] { String.Empty })); + Assert.IsFalse((bool) isNullOrEmpty.Invoke(null, new object[] { "Ana Maria" })); + } + + [Test] + public void TestRefOutMethods() + { + IDynamicMethod refMethod = DynamicMethod.Create(typeof(MethodTarget).GetMethod("MethodWithRefParameter")); + + MethodTarget target = new MethodTarget(); + object[] args = new object[] {"aleks", 5}; + refMethod.Invoke(target, args); + Assert.AreEqual("ALEKS", args[0]); + Assert.AreEqual(25, args[1]); + + IDynamicMethod outMethod = DynamicMethod.Create(typeof(MethodTarget).GetMethod("MethodWithOutParameter")); + args = new object[] { "aleks", null }; + outMethod.Invoke(target, args); + Assert.AreEqual("ALEKS", args[1]); + + IDynamicMethod refOutMethod = DynamicMethod.Create(typeof(RefOutTestObject).GetMethod("DoIt")); + RefOutTestObject refOutTarget = new RefOutTestObject(); + + args = new object[] { 0, 1, null }; + refOutMethod.Invoke(refOutTarget, args); + Assert.AreEqual(2, args[1]); + Assert.AreEqual("done", args[2]); + refOutMethod.Invoke(refOutTarget, args); + Assert.AreEqual(3, args[1]); + Assert.AreEqual("done", args[2]); + + int count = 0; + string done; + target.DoItCaller(0, ref count, out done); + Assert.AreEqual(1, count); + Assert.AreEqual("done", done); + } + + #region Performance tests + + private DateTime start, stop; + + //[Test] + public void PerformanceTests() + { + int n = 10000000; + object x = null; + + // tesla.GetAge + start = DateTime.Now; + for (int i = 0; i < n; i++) + { + x = tesla.GetAge(DateTime.Today); + } + stop = DateTime.Now; + PrintTest("tesla.GetAge (direct)", n, Elapsed); + + start = DateTime.Now; + IDynamicMethod getAge = DynamicMethod.Create(typeof(Inventor).GetMethod("GetAge")); + for (int i = 0; i < n; i++) + { + object[] args = new object[] { DateTime.Today }; + x = getAge.Invoke(tesla, args); + } + stop = DateTime.Now; + PrintTest("tesla.GetAge (dynamic reflection)", n, Elapsed); + + start = DateTime.Now; + MethodInfo getAgeMi = typeof(Inventor).GetMethod("GetAge"); + for (int i = 0; i < n; i++) + { + object[] args = new object[] { DateTime.Today }; + x = getAgeMi.Invoke(tesla, args); + } + stop = DateTime.Now; + PrintTest("tesla.GetAge (standard reflection)", n, Elapsed); + } + + private double Elapsed + { + get { return (stop.Ticks - start.Ticks) / 10000000f; } + } + + private void PrintTest(string name, int iterations, double duration) + { + Debug.WriteLine(String.Format("{0,-60} {1,12:#,###} {2,12:##0.000} {3,12:#,###}", name, iterations, duration, iterations / duration)); + } + + #endregion + + } + + #region IL generation helper classes (they help if you look at them in Reflector ;-) + + public class InstanceMethod : IDynamicMethod + { + public object Invoke(object target, object[] args) + { + return ((MethodTarget) target).MethodReturningString( + (int) args[0], (DateTime) args[1], (string[]) args[2], (Inventor) args[3]); + } + + public object InvokeVoid(object target, object[] args) + { + ((MethodTarget) target).RemoveAt(5); + return null; + } + + public object InvokeWithOut(object target, object[] args) + { + string outVar = null; + ((MethodTarget)target).MethodWithOutParameter((string) args[0], out outVar); + args[1] = outVar; + return null; + } + + public object InvokeWithRef(object target, object[] args) + { + string refVar1 = (string) args[0]; + int refVar2 = (int) args[0]; + ((MethodTarget)target).MethodWithRefParameter(ref refVar1, ref refVar2); + args[0] = refVar1; + args[1] = refVar2; + return null; + } + + public object InvokeDoIt(object target, object[] args) + { + int reference = (int) args[1]; + string output; + ((RefOutTestObject) target).DoIt((int) args[0], ref reference, out output); + args[1] = reference; + args[2] = output; + return null; + } + } + + public class MethodTarget + { + public string MethodReturningString(int arg1, DateTime arg2, string[] arg3, Inventor arg4) + { + return arg4.Name; + } + + public void RemoveAt(int index) + {} + + public void MethodWithOutParameter(string lower, out string upper) + { + upper = lower.ToUpper(); + } + + public void MethodWithRefParameter(ref string lowerUpper, ref int square) + { + lowerUpper = lowerUpper.ToUpper(); + square = square * square; + } + + public void DoItCaller(int count, ref int reference, out string output) + { + InstanceMethod caller = new InstanceMethod(); + + RefOutTestObject target = new RefOutTestObject(); + object[] args = new object[] {count, reference, null}; + caller.InvokeDoIt(target, args); + + reference = (int) args[1]; + output = (string) args[2]; + } + } + + public interface IRefOutTestObject + { + void DoIt(int count, ref int reference, out string output); + } + + public class RefOutTestObject : IRefOutTestObject + { + public void DoIt(int count, ref int reference, out string output) + { + output = "done"; + reference++; + } + } + + #endregion + + +} diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicPropertyTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicPropertyTests.cs new file mode 100644 index 00000000..021a7893 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicPropertyTests.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Diagnostics; +using System.Reflection; +using NUnit.Framework; +using Spring.Context.Support; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + /// + /// Unit tests for the DynamicProperty class. + /// + /// Aleksandar Seovic + /// $Id: DynamicPropertyTests.cs,v 1.3 2008/05/17 11:05:27 oakinger Exp $ + [TestFixture] + public class DynamicPropertyTests : BasePropertyTests + { + protected override IDynamicProperty Create(PropertyInfo property) + { + return DynamicProperty.Create(property); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void TestNonReadableProperties() + { + IDynamicProperty nonReadableProperty = + Create(typeof(ClassWithNonReadableProperty).GetProperty("MyProperty")); + nonReadableProperty.GetValue(null); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void TestNonWritableInstanceProperty() + { + IDynamicProperty nonWritableProperty = + Create(typeof(Inventor).GetProperty("PlaceOfBirth")); + nonWritableProperty.SetValue(null, null); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void TestAttemptingToSetPropertyOfValueTypeInstance() + { + MyStruct myYearHolder = new MyStruct(); + IDynamicProperty year = Create(typeof(MyStruct).GetProperty("Year")); + year.SetValue(myYearHolder, 2004); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void TestNonWritableStaticProperty() + { + IDynamicProperty nonWritableProperty = + Create(typeof(DateTime).GetProperty("Today")); + nonWritableProperty.SetValue(null, null); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/SafeFieldTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/SafeFieldTests.cs new file mode 100644 index 00000000..0a366d40 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/SafeFieldTests.cs @@ -0,0 +1,307 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + /// + /// Unit tests for the SafeField class. SafeField must pass the same tests + /// as DynamicField plus tests for accessing private members. + /// + /// Erich Eichinger + /// $Id: SafeFieldTests.cs,v 1.1 2008/05/16 10:02:41 oakinger Exp $ + [TestFixture] + public class SafeFieldTests : DynamicFieldTests + { + protected override IDynamicField Create(FieldInfo field) + { + return new SafeField(field); + } + + private static FieldInfo IField(Type type, string fieldName) + { + return type.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + } + + private static FieldInfo SField(Type t, string fieldName) + { + return t.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ThrowsOnNullField() + { + SafeField field = new SafeField(null); + } + + [Test] + public void TestStaticMembersOfStruct() + { + TestStaticMembersOf(typeof(MyStructWithPrivateFields), MyStructWithPrivateFields.GetStaticReadonlyRefValue()); + } + + [Test] + public void TestStaticMembersOfClass() + { + TestStaticMembersOf(typeof(MyClassWithPrivateFields), MyClassWithPrivateFields.GetStaticReadonlyRefValue()); + } + + private void TestStaticMembersOf(Type type, object expectedRoRefValue) + { + // ro const int + SafeField constField = new SafeField(SField(type, "constantValue")); + Assert.AreEqual(5, constField.GetValue(null)); + try { constField.SetValue(null, 3); } + catch (InvalidOperationException) { } + + // ro static readonly int + SafeField roVtField = new SafeField(SField(type, "staticReadonlyVTValue")); + Assert.AreEqual(11, roVtField.GetValue(null)); + try { roVtField.SetValue(null, 10); } + catch (InvalidOperationException) { } + + // ro static readonly object + SafeField roRefField = new SafeField(SField(type, "staticReadonlyRefValue")); + Assert.AreSame(expectedRoRefValue, roRefField.GetValue(null)); + try { roRefField.SetValue(null, new object()); } + catch (InvalidOperationException) { } + + // rw static int + SafeField vtField = new SafeField(SField(type, "staticVTValue")); + vtField.SetValue(null, 10); + Assert.AreEqual(10, vtField.GetValue(null)); + + // rw static object + SafeField refField = new SafeField(SField(type, "staticRefValue")); + object o = new object(); + refField.SetValue(null, o); + Assert.AreSame(o, refField.GetValue(null)); + } + + [Test] + public void TestInstanceMembersOfStruct() + { + object testref1 = new object(); + object testref2 = new object(); + MyStructWithPrivateFields myStruct; + + // ro readonly int + myStruct = new MyStructWithPrivateFields(123, testref1, 456, testref2); + SafeField instanceReadonlyVtField = new SafeField(IField(myStruct.GetType(), "instanceReadonlyVTValue")); + Assert.AreEqual(123, instanceReadonlyVtField.GetValue(myStruct)); + try { instanceReadonlyVtField.SetValue(myStruct, 10); } + catch (InvalidOperationException) { } + + // ro readonly object + myStruct = new MyStructWithPrivateFields(123, testref1, 456, testref2); + SafeField instanceReadonlyRefField = new SafeField(IField(myStruct.GetType(), "instanceReadonlyRefValue")); + Assert.AreSame(testref1, instanceReadonlyRefField.GetValue(myStruct)); + try { instanceReadonlyRefField.SetValue(myStruct, this); } + catch (InvalidOperationException) { } + + // ro int + myStruct = new MyStructWithPrivateFields(123, testref1, 456, testref2); + SafeField instanceVtField = new SafeField(IField(myStruct.GetType(), "instanceVTValue")); + Assert.AreEqual(456, instanceVtField.GetValue(myStruct)); + try { instanceVtField.SetValue(myStruct, 10); } + catch (InvalidOperationException) { } + + // ro object + myStruct = new MyStructWithPrivateFields(123, testref1, 456, testref2); + SafeField instanceRefField = new SafeField(IField(myStruct.GetType(), "instanceRefValue")); + Assert.AreSame(testref2, instanceRefField.GetValue(myStruct)); + try { instanceRefField.SetValue(myStruct, 10); } + catch (InvalidOperationException) { } + } + + [Test] + public void TestInstanceMembersOfClass() + { + object testref1 = new object(); + object testref2 = new object(); + MyClassWithPrivateFields myClass; + + // ro readonly int + myClass = new MyClassWithPrivateFields(123, testref1, 456, testref2); + SafeField instanceReadonlyVtField = new SafeField(IField(myClass.GetType(), "instanceReadonlyVTValue")); + Assert.AreEqual(123, instanceReadonlyVtField.GetValue(myClass)); + try { instanceReadonlyVtField.SetValue(myClass, 10); } + catch (InvalidOperationException) { } + + // ro readonly object + myClass = new MyClassWithPrivateFields(123, testref1, 456, testref2); + SafeField instanceReadonlyRefField = new SafeField(IField(myClass.GetType(), "instanceReadonlyRefValue")); + Assert.AreSame(testref1, instanceReadonlyRefField.GetValue(myClass)); + try { instanceReadonlyRefField.SetValue(myClass, this); } + catch (InvalidOperationException) { } + + // rw int + myClass = new MyClassWithPrivateFields(123, testref1, 456, testref2); + SafeField instanceVtField = new SafeField(IField(myClass.GetType(), "instanceVTValue")); + Assert.AreEqual(456, instanceVtField.GetValue(myClass)); + instanceVtField.SetValue(myClass, 9182); + Assert.AreEqual(9182, instanceVtField.GetValue(myClass)); + + // rw object + myClass = new MyClassWithPrivateFields(123, testref1, 456, testref2); + SafeField instanceRefField = new SafeField(IField(myClass.GetType(), "instanceRefValue")); + Assert.AreSame(testref2, instanceRefField.GetValue(myClass)); + instanceRefField.SetValue(myClass, testref1); + Assert.AreSame(testref1, instanceRefField.GetValue(myClass)); + } + + [Test, Explicit] + public void SafeFieldPerformanceTests() + { + int runs = 10000000; + object myClass = new MyClassWithPrivateFields(123, new object(), 456, new object()); + FieldInfo fieldClassRefValue = IField(myClass.GetType(), "instanceRefValue"); + FieldInfo fieldClassVtValue = IField(myClass.GetType(), "instanceVTValue"); + + StopWatch stopWatch = new StopWatch(); + using (stopWatch.Start("Duration Class Set/Get field value: {0}")) + { + for(int i=0;i + /// Unit tests for the SafeProperty class. SafeProperty must pass the same tests + /// as DynamicField plus tests for accessing private members. + /// + /// Erich Eichinger + /// $Id: SafePropertyTests.cs,v 1.1 2008/05/17 11:05:27 oakinger Exp $ + [TestFixture] + public class SafePropertyTests : BasePropertyTests + { + protected override IDynamicProperty Create(PropertyInfo property) + { + return new SafeProperty(property); + } + +#if NET_2_0 + [Test] + public void TestForRestrictiveSetterWithSafeWrapper() + { + Something something = new Something(); + + IDynamicProperty third = Create(typeof(Something).GetProperty("Third")); + third.SetValue(something, 456); + //this should be ok, because both get and set of the "Third" property are public + Assert.AreEqual(456, third.GetValue(something)); + + IDynamicProperty second = Create(typeof(Something).GetProperty("Second")); + Assert.AreEqual(2, second.GetValue(something)); + //this should not cause MethodAccessException because "second" is created using "CreateSafe" + second.SetValue(something, 123); + + Assert.AreEqual(123, second.GetValue(something)); + } + + [Test] + public void TestForRestrictiveGetterWithSafeWrapper() + { + Something something = new Something(); + //new SafeProperty() + IDynamicProperty third = Create(typeof(Something).GetProperty("Third")); + //this should be ok, because both get and set of the "Third" property are public + third.SetValue(something, 456); + Assert.AreEqual(456, third.GetValue(something)); + + IDynamicProperty first = Create(typeof(Something).GetProperty("First")); + first.SetValue(something, 123); + //this should not cause MethodAccessException, "first" is createtd using "CreateSafe" + Assert.AreEqual(123, first.GetValue(something)); + } +#endif + } + + #region Test Classes + + public class MyClassWithPrivateProperties + { + public static object s_staticRef; + public static int s_staticVt; + public object _ref; + public int _vt; + + public MyClassWithPrivateProperties(object @ref, int vt) + { + _ref = @ref; + _vt = vt; + } + + private static object StaticRef + { + get { return s_staticRef; } + set { s_staticRef = value; } + } + + private static object StaticRefRo + { + get { return s_staticRef; } + } + + private static object StaticRefWo + { + set { s_staticRef = value; } + } + + private static int StaticVt + { + get { return s_staticVt; } + set { s_staticVt = value; } + } + + private static int StaticVtRo + { + get { return s_staticVt; } + } + + private static int StaticVtWo + { + set { s_staticVt = value; } + } + + private object Ref + { + get { return _ref; } + set { _ref = value; } + } + + private object RefRo + { + get { return _ref; } + } + + private object RefWo + { + set { _ref = value; } + } + + private int Vt + { + get { return _vt; } + set { _vt = value; } + } + + private int VtRo + { + get { return _vt; } + } + + private int VtWo + { + set { _vt = value; } + } + } + + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/Images.resx b/test/Spring/Spring.Core.Tests/Resources/Images.resx new file mode 100644 index 00000000..b725b654 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/Images.resx @@ -0,0 +1,430 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + R0lGODlhkgB3APcAAP///zk5OUJCQjkxMXNaWlpCQoxaWoQ5OaVrY4RSSqVjWueUhNaEc5RaSs6Ea3NC + Ma1jSmMxIbVzWloxId6Ma0o5MTkYCIRSOUIhENaMY6VrSu+ca8Z7Uq1jObVzSlIpEIRrWlpCMe+lc86E + UkIpGIxSKVopCGsxCIRjSu+cWpxjOWM5GK1jKXtCGIxKGIRCEJRrSpRjOWtCIcZ7OaVaGHNaQko5KSEY + EPelWueUSr1rIcZrGJxSEFJKQmtSOWNKMVpCKXNSMb2ESu+cSnNKIZxjKZRaIb1zKc57Ka1jGKVzObV7 + OfelSq1zMb17MeeUOa1rIcZzGNZ7GIRjOXtaMeecQs6MOZRrMb2EOYxjKe+lQpxrKeecOdaMKeeUKYxr + OXtaKfetQv+1Qt6cOe+lOeecMbV7IZRrKfetOf+1Od6cMe+lMfetMf+1MVpKKUo5GEpCMZRrEFJKEFpS + ECEhGEpKMUJKCEJKGCk5EDE5KSE5GBgxEBAhEEJSSilCORgxKUJKSggQEBAhISlaYylKUilCSkJaY1LG + 90JSWiExOTlaa0paY0qt51Jre1Kt5yFKYzlSY0JjeyE5Skql50qt7zlacylKY0Kc3kpzlDlrlEKEvUqc + 3kKU1jlCSkpjezmM1ikxOSEpMUJac0JjhDFKYzFSc0qMzjmEzlJaY1KEvTlrpTlztTl7xlJznEpzpTla + hEJrnDFajDFzxjFCWjlScyE5WjljnDFrvSljtSlrxjlKYzFShDFKcyFSpSFatRghMSlKhDFapSFKlEJS + czlShDFSlDFKhAgQITlKcyE5czlCWjFCcxg5lCExYzlKjBAxpRAxrTE5WiEpShghShAYOQgQMSExhBAh + hAgYlBAYawgQYwgQezk5QlJSYyEhKUJCjDk5ewgIOQgIUggIYwgIazEpe1JKe0I5c0pCazkxWjkpczEh + Y1JCcwgAGEIpa0oxY1o5a2tCe0pCSiEYIQgACJRSjEoxQnNKY6VrjJRSc4xKY61re3NCSqVrc6VjawAA + ACwAAAAAkgB3AAAI/wCPUcvW69MkRpdOCatFLdC8WRhMWDjxgkQFF0ky0uDh4kSeWnz4/CJlidq0aeGq + Cbt06dMnSr6ukdNWLOY1a9vUwbPmK5s+CSPilfOQIoeCR8D8JAJV5IiOHUlexCkzg4OBBCO8aHnihc0T + NmDDejEjqVizFWvWeJHSwo+qab2SYeARwk+tdv/y0vtHTxi1vIAB79VbzRKrSwhxMatFxwKGEzwyRoni + Y0kSH1CkSOnihbPmLIX84OE2LVQgPokSHavW61oza7k+YdOWTdatW8KYXdvmLp6zePr83TPGrsqCFAYe + PUrWLBSJAKD++PEzjceMGQg8gH3SZk0XK2zSqv8FC+aRpUcuvB4RUEpSqWaWLHHjscLPr7+D957ky3dv + fv/TaEIJLrUkQsIKLkCBhBReNOhgF0iE4ESDZXTWoBpqVLEFIQO4QEURNLjwwgp6CDLPL8cE0k4zp/RS + 0Cm44NILLrcks44/FPSjDjm98MNABimwkxQvuyQSQgxFBFBKLXlwI4AAD5TRRlhrjFFGeFWsEZ4XVxix + AjArNNjHI534UYwwGCDRRB15/BFINMf0N1gtcebXH3//TGNJIhaY8EIUXYyRQw6CquFgZz8EceihapTR + RQ5UcEMDGw46oYMUNMyBx4kpJkPJJqx8csotpzhyim7p6MNPNrcAo88CDBj/8I4vwDQzACgRrPGEAYi4 + IAULuijDzQM6PPGVsZSm9URaXXkRRylAIOGFAVQc0QIvyfCwVgR/EEIIKNLgxR898yQTyJ149nfMHzfw + 8N0MagQ6hhVWzIuhF030gEWjDXJRRpVWdNGFFDoAMUAUT1ToAQwOYqqpN95M08xKlFAyCSef3NLLOOKI + k8wtteRTjzvZxGgLKB2w4MMMWjhx5RMzLCGFEZKAIkMXU4ZXxrK6iscGC8CkJwUSLMxQRhAyQOFCHoTE + EskfqE1DDz3n5iXNL3pNLSdgz1VgRBdqWFHF2DlUUUajVTjhgxNqVchFFVz5e0YFAXAzgCQhWFmhrl4w + /+tFFHPQ8Ys37YQTzCeXVHwKK8KsEowswYjzzjqwCSNMMOlQMEQZOYzgwRNacGAFGmwgUQghtUzQFc9a + 6JpD32qxUMoJD/RBSA9sr6EDIKXYgswPYPjghzTNiBPI1HsFAk6cgo079QAT3ApEB2dbkUO9ZhvqAxV9 + l1GFlV78W0YZR/jxRyneTFBLEQnDneUTZISv1hzU/JKINMckk401lFwSzC2mYAQjrGENcVxDIdsQxi2k + oY8MPGFsIBiB6JZFhjU0AQo+mIYRwoMGY+XgCWOYwVe8IINIJIIUtQACg9TyA1tYwgZJcFAS7qALa4Qj + RVRrx36Qd6fByKMbdRhAIv+4UQIkPCEFVvDeGNZggAKoIS3eW9b4uMCVKNQhPgE4QhAGwAKxJWwNVuAK + s8DghxPgIRGWwB89gDGJU5yCEoy4jTCsMarLCaMXwAiAP1KQAgZIwAES6GAH16AFLSChD9KAwhMgBDsZ + umEQsUhEIbzBAi2VAQylIOIS/5UWM+AhGtsQRzvaQbVj1CJ5gzlef8zAggbUIBqJIIQNSjACPuYAQj0I + 5Bp2Nr4p/ssLIQhiKSLgBSREQwY7e0K8RMisQxrhbyu4gSQsIZDEtYQVt4gRLjJWjGI4Yx3LsEQB8iGB + cnKADEMgw1a8spUokMIGO+hBDcCyli3UgRCACIAl9BD/vQcgAQsPAIQlXLCGD5KBC32Tgh0CUQ5yiOMY + pKRHM5rxn+YFDwtI0AAB+lAKS1RAAVUoWw2UoLPx7XJ8DVKLE7qQhEU0wQtP6EAhXKCFMdj0gVxBQh3g + kFIv6EAOdOCFJWohDFMEgxWcuEUuEjKJTzAjG/l4xzKyUYtslMMHutIC3NAQBq8sqwa1MMKEvMCCIBRi + FkFggRSyQIgkGOERtfhBA5CQgFJEYQ1DqAIXyBAeGvyBGPoTxw1JeQxwHM8/yDuXH3ogBUOVQW2xkA8N + pFCEC/xyDRXsGxk4ucuUcsYLZHhCDaQxg11qNUtk8EAhiiCetHSBB3iQxDJ+UY1q/3Cif5fgxCUOwYhe + WCMf+khGMK7BClukqWxP6OBW1MmGIljiAzsogg860Y0rMKhBNQBEFPjBiwlgQVcw0AXC1MCFsmE2DpZw + BiHWwbFqQLQdtZjGPw6LJ0HMghtBAFujuuCEBnSDQwGAQt/Cg9n4VWmEV1pDFfiqlkX2IARlSMGyvrIG + JLihCwkLLfzKEAU7hIIXhKBHMJJ6i9syYhPDXUcBssGKU9iiFNOYwBMGlYOtVGErXnDBI0DhrUQIuG+k + 80EhuMGCLxwhimRYgi6gADd/fREJcviDN6hxDcG24xj0yB9eksefagBhC1M4wqG8N4MSDKISASjCZ8NT + wWSp5f/N8NOssY7AjQ3+a1leMIIPvBJaMtTYWEbwAx9IwQtq3KIY08DFJg5hCoUkw4CX0BhzKgA3Gmvh + OpUdgCVIEQIo2KMIOatQCIoBCCRsR6sVRgQQQMsFLXCBC2qJQh8EvQ6HVmOUEpWa1gYzjySwQQ1jcJBa + blwEOIABEJIIABWc8EQthCdZlAoPQilVyIKCgRs6IDCl6tAAPpMhpN9+AhHgANtEZKIZwKBGMQKIC1ko + RBinCIYqTmGNaYhDHzMYwQdncIFZJOMRQPBDCLoSA1OTga8RisQEylDBKkHgSgYgRCVbDTe+JmEQc7BD + IkJ5a4hWIxnIC3kgJiS68FHIC0f/mAULiumDmskTBXNtVmuz+m3vdWGuP6gATLXghTMUAgloQMPBh5CD + IYygCoC4QOlIVApp0GMawTiEqC4hC0rcwhrCwCYzhJGNCPCDHz3gxSwI0AAPJGEWJWBDGYQ+7OucQREV + kAIZUrAVepWOGBVAQl71qgUp9KATSTDDDdwxjnCE48rtqDfVdv0PLFTobJCvUBCcUVrvqWARAXBBE7oB + iBqMVTxqGNTY4FaGMJouPWjwQh184IWVku7gKRjCDKJRhDAk7AV02IUl/kENTbAkjrLoBdevQapc9MIX + vhBGMvhxBC15IQuziMIgd0mAz7HhCoooRLYPrqWaskHJFeiA/5aegIQelIIGDZLDL0Jp+JRMQxhbvpMV + 1tCos8mvCX4ogVrajIQKdMIISBADtcANgPAFC9IgT5BXE6YWV4AERcANUMAGNEAIS9AGVPADCBhSndMD + SFBxakADe0Bo4uAMweBGj5MLisEMvXAJmoALvsAMpAUzVtAGOoBWBDYEHDACEqAGYEEFflAI/MA6HQQ3 + bKACpUAIIUAEQTAIpMBaahEHeNBQHdN+wKBr5zI1XdBZasBsahF2NeAFaNAFL3UEbmAE3RACNkAIoZAO + DdADcFABRFACLGBqbLAVY3ABPmAEdRAAL1AHU+AVgSYDP6AEYsAAQ+AEPTAG8FM2ZWAEev8wCrswDdaQ + DMBgCi0Rb7KQC7nACqwQDNYgDfxgAEhSArPQB05wh1hQFFqiK2zAD27gBlgFP1riVWiwBt2gC92ACItQ + AzrQN80iB9JQQIJleNkADDx0LmzjKD6ACEhgQT7gAVYAWl3gA28wAEUwBl1QAaXgCaEQJmAgCYRQCZYQ + DZ3wATTQBUPAFU4gD3+gCKEACjrQBvZECokgA03gARDgBEAACgJmLGGwFhPgB5UQC/TQDsBgKpeAYrKA + CyjIDNYADOdgCc2gDITADVQwAxKQAUCxBkJXi5TSA1BwBgNgalrikT1jBSJkKFDEV10RB3wghVMYDsxQ + DSH3D0/kHaj/IABm0AVfAB7aBgWFIAAsYCxdQAWLEAnc8AIvQAiNMAqqoAq2EE6dpwRbUARpWAiiAAqz + 5AM+UAegMAUwUABXMwukAAoBUAAJUAMXIA+gUAiJMAyWUA3TYAq3wAqUoFu3MFzMgQASAAEaoAJYoIhW + oAAZ8EGYJXRgwQKzcARI0AlQ0B0cWUHhxlUcpGDz1yxdYAe6cA3Z0DEdUw7AEAiHRQ+cYQZmEAd1IAUw + wDYGRilTsAVSUAP04mdQ0AOv8AfcIAqYMAu1IA4SEyPJUAukMAuIcEKzQAgkgARhEIZfUAZDEAN+EAmD + EAAPUAIR4A2EYAuqUAy7QAu/8AuZIBB5/xkMk3AIl7AQ2vANGSACTDAEQ8AEscIBQ6AF6ISYOOYDBbAG + UtAHMsAGQyB0fFWYXYEEl4Usz+YsoNBQ5DAOnakNwUANIXcGXrAFcMANQKADE7IGrQZF4NEG3ZECaXF9 + i9AH7hgJkQAK8jALnQAI6NANVBADVBACiDAKsUAKhfABpuYFaTAGI9ACkfAKfEACB1ADPVAIuxALu5AM + klAIihANurAL/1ALpxB8kzAJveCQ2bAPDnAP7AAPEsABVUB06nRwQRd0VtAJoIYEiKAMIxAGZEA6bIAG + MEAFPAAHSNAF8uADJtUVlBIHfwAKMkEO2eBQwGANi0cPcOADDTAHff8gAHXAGfvHfdrGBgsWFjTADXCQ + B4kQYGpwAaHwA0hgKF7QBlLgBp2gDKMwCrMwADIABC1ABGh4nKHQCbPQDQkgAKCAFMBACpIQCoXQLfI1 + EMLwCZzgiQ4pDenADMVwDg3QnoU0nx1JOmmwBAIwA6lXAPJQmAAKFvnyB3kgBU3gC52ABNx3JV1QB7PA + UNtADuzKoMGAZaLpBFYwBYUACiswBZzBWQcHFqsYFmzQBkiACjQQAvIQAGqlAgJwD3wKFucaQ0VAq96Q + CLGgC4RQCqQgDd5QBNeBBBdwK4OwC7xADEvyC0phC1hTDa/BCqbgW8HADKtgDM3ADhLQAFaQTgf/R6Zv + 2gY+ME+u2QNesJxxGrRQ0Afz0QVgkAQFNn8sVAh8UAsyMagcYxa4loUTWgN18AUUYlJpIakElhYzgARI + UAI1EAXFJgAtEKoDphZBwA1NgAX80AmKAAmgEAtnlQmFUAgBEAI1IF2gIAAEUADKYAnf8gNF6pYESQ3B + sAyaoAmr4BL0lg3XAA/6IA9OgAZDsAZhkAN8RTpqIA/M1AYq0AdgWEFBh1nPRwh5cARt0AVl0Do64wU+ + EAB2MA84sa4cIwy2MEqBQLXecQRXUANNwIMhGh7p5K//ugRTMAV9UAAo8AVAoAjKQARf0ABKoAExEAKD + 0ANfoAEz4AFUgAh0/xABLEAE3DAPihAJpAAIpOAtE+AE+FgL76ACI5ABD9AJoTALpfAPzQBAq3AN89ay + 2ZAN7oAOCCACNzafN8aRbbAErJcG3OEBBMCnm8sGZNAGVxACJcA9MzAGXbsGSdAHd0AEeKAO27CuDpUN + b9EOu4tQahC83nEFQVACXaBtNeavaqAEjvUFZrAEM9ABcCAAhTAAwUsvOyUFbYByUeACAUAKJDADXlAC + 3sAN8vABP2APhPAFU8CV8tAMF+ABI6AC3RAKkJAIryAN/wAMc8QKjPAJ2uki+qMO+CACfHRj7MQGBXAF + bSB0WmAGoqtMpAOgXvAFrFcGSwCpmIVQ2OsrwP+IDdsAteIQDM0wSp+1wz11BIAgAGJmvJsLwVWABlWg + AVxAL11cAyXgA0FgamqACCzQBkczC7gaH9JQCyzgBBdBBPYABLP0r54DAvegCz3QA6FACInwB6IQCplA + StbACYfAWy7RaLggDNfADwtAAZ1sBUD3BJbcBWlAOm2gBH3ABlaABWDBzVVgBdDYBjAgAyHwADO8bW1V + Bi5Zuws6gsHgXobSwiqZUlZQBPJwBV0xi2HhBFgwJWugBFpSA6TwNQzjA3UmA3XQFU1QADJwOvJABIrA + Cy1gx0BAAAnQCT0wA2WjBUyQAwywD/zQNJBACqVQCJVACLsQCPzjCIfgCJ//4G6rsArFmg0JYACoYAQt + QAVpwAY/sGdl2s0+O851+G2zWGNRoAjBUAs8UEQlRAjyegUjvBvtmg2rMA3tkIVdsARv1lps0AVTYGwL + qyXMBhYLPIMqEAuesANKUAUe0AdwQAhAgDNXgLVO0AROTAXPFQU1oAyfI7bWkwJhGgawAge8WgqxEAu8 + UAmFQAsmISDLzAic0CLkYA3B8Myg0CTzYARt4AQCoM2bi86xGLQHNzr7SgYdsAjEEAAXUANXoNiTQilz + kAiMzK7ZMA6qkAzVwBlcOHMhOqHyAAZdIZkoAHQCUwbVkgQ2UAQxQFJpQQSEYAnGLdrZVotaUAY+wMR9 + /xsF3P0FmSsC/QADTzAC91AJgvAIkWCxpZAMyMANr3A806AKmyDTh9AinAkM3ZQIeyAJJCAFMnABefym + YJHFcRp0ecwEN/ttZNAALJAER6DNUoAIAyAFadEGZjAP5YDV7FoMwBAOYPN5ws2nXpC8zciRMGCAPjAA + J1DX8qBqX7AsaLCfcEAFAmCABcANP1BJa5AGH1wJN0BkVpDj/rkB8cALF4AFClAK5hZZszANpTAMJ2TG + x5ANwlAM9404n1gAHeADu0AIuDkP3AB0r4cGZdADVPBs9CIEGTACQYdOdBUEQECdSQAEjyAJGT0lXXDV + jcyu1qAK4vAdYJNSz3boYf9xBWsDFliwBFiAvaPADZ0ACvMwAF/IkVfgs0igAl9QAC6gjZ0gBTVeAISQ + fXxQADlwAVMQBlowBPgAzQYwA+hgC7OwC4/gBwMgDZFACHwQDcdTDcBQDqbAC8UQI7XyAOpTseB44X9M + OkgwDICQAAmgAkIgNitJKTXQByGgCKoACt4ACXkwC0ZgQdIyB7/AyCa8DaqQDUewBLAj1j4TbWqABK/J + BmOgBBZGCJhQCJW+CKIwCxegFj6wBWwwA0Jg1Ez6hV0gAIrwy6MwRDOABfOkBTmQAv0ADuBwAX1QJO6h + DAOgDcOJGrvXDiq4CuAADC5iDZbwb5kgCX5QCrQwDTb/AAYzEAZVUGa60JNWwFfbjAahTDpUgCs7oAM2 + UAqu4A1JEAVZ4QNgEAV3QAe1a8KrAAzM9u7CPR7+WtAGbQUhAAckoAuGMACEUAiRAAQ+0ARbwHqkU0hp + YAUtrkhClpzlFwmrNQMFgATt2QHdIA3AsAyFUAGVkAmvUAuSAAyPQAiSIAjDQEqqQAmmACrPUA7ZEA6+ + qQrAIPiZoArBIA104AJEsAJNQgMeGsoMV9RHQAh64FF9qAiWMAskMH+LJAV18Ad6YA27UcLkIG9j1Ugd + TCXGi7xeMAM1MAUxYA+hwA3eMAtF4AUFIMRhEecdFAMCAAcRYAjcAAZk4HeQEABS/6ACPtAFTPADmLAI + ESAPBDADAvALTfcItvAKxjALf3A/9JAMjOsINC0Lll8MmtAMyWAJg5AIANFnFq9avxJJevRrRZs2bITA + QBMRDRswlmDtUvSL1KM9iqa5YOMhhhcwhbhJw7ZN5TZrwbp4KbNG5ho2NNnUvIkz5xolNdS0QdPmSggq + PmB4QSKvhxc2EclMfLqmQKcXA1QtQpImySJFLZAI6HHERSFENC40kCBPWqRXllTtSkaIDh9ego5NK8bJ + 0aZLnIrFsiWpmR8/fw4CW7ZMWiFz024YeTlkHxmgaLygIARs1qhK836VyqTIAg83hXa8IKRHkLWV264F + 21Jzpv/NnFrI5GzjRU1OJ1nKeCHTpQYLpl6uENrSMOIaLmnSrFHhwUOdPyRm5MhRhUgkUCWQ9FDWB1Kn + JA2+OAFF6NUrar5KlZJEAo+kZfToHUt2ixWuYtmaJUvGklJqSSQPG95Y4Y5ODEvGG1AK8MGHK5p4aQ0v + lCCFFFVSIaQTbgYhZZRYBOEGiBf68OONPcpJaSVnBDBDtzXUUEO3I7roQogqvLACCSSaKKIJIZsIwQcn + ZkDiixqKmEEFbpZqao2omkAiBizS0KIOSEJpoYwqUuiih1FIKEOKPhAhhBsZSoBDB36A6COWSGzZpRRS + 5sFgD1rauS8WTljpRRhhbOGFlEr/fjFhhwpromGRWZqhBoMorOgJBXkEIGqFUkZxxZIBbCgqAFFWsSQR + DOoghIYj8JgGm2tUuuaaPlBowgo1rHBCDSUUqcGJI67I4ocQ4LAhAFBIsKHYRAohgkrhlDiijiYqa6qN + LgQYQIk0gushkkWUsQGKESgAAxJSJpBijS368GGKEGYJIQgjCrBllUxGmYYQQiTxg5Bp/plmlVNYOeWT + XHKJJRFu4AiiDDTIgJiMMuq4I5plQnEhDTbaWMOJWVFAoQdACElkggeKYIGHPCCZp49BBsFACi/mkAYa + bFpkRo0ylvDCC5u8cGGFHvr4IYYtzMDRCSTOUAKpJqgAJAAf/8AoQoogfPBioqbKuKIHQwb4AQs0ZnCj + ElEgiYSQASJIpw9Mvi4hiQK6qMmKC8YLIJNdSMnkF0JmKeECPqShJ5BegjHFFFk+ueSWWci8gAqJ0Hji + ih+isMOaWEDh4YkwtNCaizG8KAKIOiIpBRRuOvEDkxv6DWCQN3aIgw5hnrkZm5zZuNWmLraoow8wYmSo + qY23oIKmoNo4gxAbhgXCj+SaeqoMovsIwgku0AgDilAK8QYTNGcphBBEIlHEjwrAiEiL5/owBA5CRrmI + ml0qKCOBPWY5hp5qbgmGI1hxCVMEIxMkQAIHBtCFMDTFCz2Y1hyasQxJcIMI1GtfRGagA/8wlIIbPDhD + EOogCj9AwgUvqIQlTJAEPCTDZjd7xhWcYIUrXCEKcaiDH84gM4bQhAtVYIMSvuCwNaQgDBqbQgWkcAQf + EAIISOAYG8bgAQv94AhAIYMWhECABhTBG25rRCckoQhFLAIUeehDVrQwhDAgoQ8zmMEAIlGIAVgiAilg + gR9CQThvMKMXq1iFJjhxCWeEYgINEAALnhAUJRRCCm2Iwy+KQYcARAMIUQAKG8iwBi3kgHdJmMET1iAF + O+zCEhOIQhJWkL4T2EEavmDGzZhxBSUs4Qp9KMQjSlaEJVDIZ2t4wgxioAQ2NLCIDrBCGh5oAyOYAQk+ + SOQTKFUGNqT/YQlWeBgabJMDCnSgEJAwBCjgwA1CYEIRnrDAA9I4BC1oYQ0NmMEFuDEKbsijFBPAI/hm + cR9rrKIYmggGLj4RDGnIAAICuAAZntAFblzADCXIgh6M4Y0kNAERATCCJp8gTcqoawZDmIEM6gCLX5Dg + DRWYABBKkYc9zAN3uMOGGmQyM0vAAQpNoOUXUFCDKfygAIjgBpPU0AUt3AMbxfDHE2oQiVA4gQ1eyEIA + 6nCFbGohDBG5KveGUIUeQOIC/oiHPy7Alj+QEQMhCIEVhkCGMDzgAdyoRCICYI9X/EEFD5AEJF5xjGOU + AxebuIUsmKE5SfDAm254AhsusBQoECEJ/3pQRyB20AYePOKUWSvD6KRQgln8IKSDsMQuSDCBQZnGCGnq + QzBe2IsuyOQM3AADU3LSszIgoQ6l8AEKCDAFGFwgBujAxS26EYNCvIIWRvBCGpAQhE6EAAnaHAI7bdPO + ITDhCp2tAgP0UYAS2MMTiOADJpRhggr4wAoKRUEiFJG6aMwCl9KYhh9mMQxp/IManODEKXBxDWk0YxDe + IAEhrvAEDxSiCF6IgQzAoIdaTIMHbNgCLyxRiydqcgouU8RoZ1GKP8TCGy4IwC9c0LM59GERznhhTL0Q + Bz9AQTa/tEwXroACLOSADFWISRWsAAJwEAMdInsPQmNQgB/wIABtev8CE7Io3SGgIQ0XqIUPimAPQijC + EnQYQCYQ4YdWgGIes2CBczSQiO8mohaE8MQs/qYISSSCcMcIxiZMgYtsaEMYcpJEKKDgBXl0IitdCAAh + 8GBmEzTFDDSggiIGkAQvfKEPnSBEKQoRghcIIBYBcAEUzuAFLrCBCH/oRDRsdg1sdMEM0pPNbC6DiCZo + 8zbUqwIXcECBfUhACzFIgAoKkAhC1CAL0BxZDWKwBCxYYQxPqEIVnuCDQoCiFrEQRSpSgQo6IKIZJsbE + L2pRgCdk4B6lgMQ0AKQIvY5CbSvYQyH6lx/F9QIXrFAFKXhRiBM8oBMqyI4X3DAIPeghGxZIrhj/lKkB + RBRiAkXYgUpn4YIumIkXBZhhz3pWh0qAIhTPIPU1Th3b2cikC1SARA2Y4k5NZpELt0mBCJgwBghUtwGE + KIEHxJADNUChD9z4gRJggAIYwKAGNfABN0BRCUy0AhOYaMTX7FELahCiFaKoxSwO0IBkYAJtmcgEJiKR + jFhA4gNQIIEiEvGParQjGaaYBCecEQxgGIMXgcB5G27shR/UoQ56iIYgSoClpzzhCItIXQUE8IMklOEJ + KugGGJBQhjI4AQwvOYMfEkGHWJLaD0GQSUxosoYm1EAJL5FNNrO4yXZWYQ3GZoMTelKBDmhyDWnQASEK + QE3mVGEGQlBCDwhh/whPuMIViBAFmhJBimY8ohSp8EQlquENZBw9M8VYxShEISAu5cHN9JCGKZIRjFOs + whrCAEfUJfEDYKrBjaFYAQ3u0IxskEAKEZuRCxSBiUSswAsce0IMOhEFNGzvCITAPDYwAznYA3BwFWao + g3Qpg01agy5QAhSwgoZ4tc2TEikpOTYYASzwAixQgTWoAn5wAtsYAhwQglHgBSNAthvrpDDQgEMhBFcw + Olown0hwBUVQhVJwhVRoBWngBVgYhU7gBVXIhKebk4oLBULgg0SgB2E4hE04hVMQBlwQBlMqhQE4gjby + ARdYhDSxATtIBGEAhgBogSVIgAIYgD4gBEsghP95OAEjOAIpKIAe6IIxiAkv8DWcMIMWwh1mMIMZmREz + mIIasIKbAI6a2KSTyyIpuTEySAEYGAEl+Dw0QIJZ4DYyyIEMoBQ36AQnEAOIsQ2IUQJAWJaTmIZOkIGf + moVpiAVX0ARTWIRRuJdGkMVWWIRCkARbeIVK8INA4ANvaIdq0IRNmARKCIZeKAboIwVvmAEPQIcCmIEm + 8gZC2INQAAZakIbRSgQ7UQRAGIBWcAVRKIVo8IZdcC4rmBErmIFfWgMkeINowLhS44IGLIkQoJHZuAmZ + 4KYFRAMpWSMJoIIawAI1KAAiCIIQ6AErCANMrK5l8wE2+MSHYasZQAEBqID/PiCAJRiDNZCBSJAGScCE + V0iFRoCLXZiGZsiETRkF0MCEUSgVwxCEfwAGUzgERrgEX8AFVegFaZiHGcCCTOiGDJCAApAEYIiFQrAG + asgDoQMCKNgBKHCB6CmFSEgEs6kEAWgADZgBCNAAK7CCCokDPKAFmKKRJXgXN4DAzbsNm+CkBrqJfXQI + QKgBGFgkKCCEAJACfiCA6BqCHNgANOgCOGgC21iD9iGD53iCEaA5hXoCJyAASyiEQSiFoquFUhiGNuMG + SVAGcQANYAgGYXiFRAgFJawGVTCFTcgvWVgFXOgFtXkAI3CCEeAAboCE6JOGZFiGWRAAUKgAGoCJHsiD + /w+wgD8ohUQYhURwg1wZAzVwgiWoJSW4AgHoA2WIpVmagi+goRhwAs1jwHskg06TCZzwgm6Qh+ciA+MI + gR+4Ag6QB3OEmCrQgjb4gh4YTIiMiKbgHtuIiCpIgysohV14hVZIhmKwhVh4BURgSVKgA0KABGMAhlvo + BVHYBUEgHGnghFvQBE1ghVW4BVwIBl5IBmlIB2WQh3R4hEGQSj5ohmkIBBPAAD8oERoIgADIA0S4AVDI + ulk4gTXILGpig5goAyjIg1kAhVjChitoLd6Zgc2Lx3uUDe+EGJmgAmXAAg1ILCuYgpdQgjGoAQ0QTES8 + lsCciIdZgy/AAogJgzC4sf+tCoFRKDpFCIdrOMlSaARboBNnm79S8IViGIZY+IVfoIdawC/TVAWBksLh + DAV+iEN2AAREKAVC8AOL8YM8KAVF+B5v4AYrg4VQoANlwIRMoIMoGAOurEMvcIIVQJF5IFIzKDYrWIKZ + eFLZuIlNipjbcAIf4IAqQIGY0IJFuo0qaAAfsI0q2KQRYIMsuAD7rB4f0JZg8oAl6IAcKII+GAVP6IQ+ + UMVVeIVMiAVVaIVGCAB+4Ib+FMJXKAQ+ULds0As5G5hPkAVhaIZgMIboSAEDOAAb4IYWkIMvPEJIkEE6 + 6IRHeAVbqIQ/4IZlwYRoOAE1kKYZwCkzIAJ9wQM+sIb/m+HKGJiCgJSpnNAJRRw9NlgCAVACHOACURJW + djq9IRACeVCAZOs7MkACODgC0QuDJkMC3aoBkZEHboiEUWiElTSETLiFWMCEWtgFWBgGb+AHzrSGXAgG + WqiFZsMPR+AE0pSFW7iFU5CFYpAGeEAAD9AHc7CHWRgAGcgDP2iGULABDFAEUggEQlgFZCCF8oEDIuiD + 9ZqGEzgCIiCBX6gAIhgAQgiFOdgDYLiZKViCGSoDNZgBYrMCLqCmhojViBiDGAgBAaiCtaoJ01PEThoC + AtCAvWQnYZUKH+Ck9swBLY2BKvAAGyiFV4AERUAG6XubWPAFVkQHS/gPXZjKa4AG/4RxBmCYhlCghmko + TYKRhWC42oESh3V4h3TIBmEIhqQEN2+wBUKwgBWwBENIhFlIBVrY3gfoGa/Bg0iohRv4g32hgxsYBDjg + gRbQg2XAnRqpkTKwgsRlVQ+oJRgISM9hgxlogALoBgfQJojUghRIgehag+rSAALQgg3IAC0QVjZogh5Q + g/q0ghr4gQvggHuoBDb1BE8YBljQhFFAh0fIBFcYhVbIhGSohWYghUigBeR1BVWIhVgoBWTwU2CAPk1w + UGdQBTAsBWDYBaJUBVvQBUAoBMJwBmIABWVwhVKYhkdwhUywhDzgPyRABUBIAhnoF1zQBF5AnT/AgB+4 + gz+ohf/4hQkKTEvGG4PF3Tmg44cDmIVuSIFLzIFdrYIMWIDsGIPLHYECyIAU2IAmgxgn6IHn4p7FLAAg + CNesgwVRaAVLCFBYiAVgUAVn4AVYdAViiARDqBNYgAUh1IVR6GQlHN5qCKRjxAViiAVeKAZFeIQG04aC + MgIZ+ANrUAVRkARYCAZYoAVXCAZnkIYJcAG6mocPyAM+sIRcAAZQcIVXCIREUMNAkIZYqhEuiMfM+04p + 0ZpnWoQAmIXHYQE0GIFAFoIZGAEhEALYvA4rEAAFcIAUcE+rChMlYAKrcrILSIRIiIRWIAVQPsleKIVW + SAVe9j1NwATfE4WDiIViUAVYcIb/ZJgFP5BQmHSEVQiGVTCFT9gEW5iFeXiAHuApAjCEZcAU6kgGZKAF + XVAGWqAFUsCEZRAQyVOEUZCEMcoEEWY7byCFadgFM/uFQrBmzftOWI3cGagBDygBgDWlAAibIpIAH3CA + DbCxNcqADlCGAqAABsiBIUgBd5qCGEiBfWACJCgBEoiERvC9FD46b/Q9TAiGXFAFTWiFWEgFTaCFRRAF + UciE1AQGZqCGP+CDUAAYSriET8BoQOoFamiB7BiCJ8gBJGkCH+iDAACHZBAEYygFYlAFYOAFzSCBFvAD + QqEFHpaFqvUF4B0FVigGDdGFUIglGNCZbabtTaqSJbAetWkB/xkIgFrgBqPgh1pQAQcYgTXCjtSqBX4Y + BXbohwxwMiqQhxmQALDLBEKIBFE4OmJAOlEYhZqmQXgjhlFg02FI6FlosyAWBgEVBD3gg3+ZBkdwBJrM + hWJwBk1IhgBwAAcQAibgAMQsAxqQBEJABmEQBVpoBVXIPrZLhHmoNkuQhH/2hV6wBWlYBlpYBV/Ym1gI + hmmoZmxwgib4JdpLNTa4AkW4gjYogvKxgd0GgyCoBW/4W0uoJ0RIALWaAV0IBkvwhnWIFRQQAzHIAl6w + hwBQhp5VBKIbhWGY62HwhGUABmD4PlWYa2lT69glhVoABmFQw1bmhUQwCHpoB2C4hJnMUP9bUIViSG4F + YIEYOIAvMEM92JQB9wRB4INZIAVNKIZmkAbrTgRp5mVfWAVVWIZXmBNosIZd6IVnwAUpjKlrqoklnQ0n + uAACcIInIIBHCIUbKAZEiAIv8IFSkAZLKAADOIdhKIQEGIEhaIEBaQZruIRcgAfncANLqIT0ecxB8IRG + 2JCVTIWsiwRnqARY8L09R4VOAIVY5gwB0YWSKtuEEIRAaIc4uwRWiEJfKMoWhnJmSAZh6AVgeA9SiIXV + TIQ/4BtWuAVuP22COQVN8IXT7uVkwAtNUE1ocLdgsOYLkSkdpUDm7BkdrexC+AOTYAExaIBBkYYOwAEH + QAB/QAACSID/HsiEdUiGa6hadSgBMJCvPzAINGuESlgGYgBhdBiFQhBvSNAFQ5AGVagLwjCEZqiFUaAF + 8klGBXACKpADPyCcdtCEvfgEXGj3YXjaQtgFeV8Fc78FVygGTCAGayCFVxCFWCBhYWAFW1i7iHYGTLgF + Db0FZwguYUAGXshoZEgGWyiGmJoRJ6DAHl2CGRBTNpgCfpiBL4CEyQuAIhgGj06GHyhgEeD7DEAAFECH + WbCEYrCGbn8EYMBupJPFmoY6Z5iFTgWGRrDpR6CvaZhrWuCGcP4DYBgFUIgEWJCGCIAADgiDLtADP9X5 + TXCEgjl3UvADSQgAbtCF1xkFHhYGXUKH/2II+IBPhBsggUIoBFKYhU4ohD5vYV0w81tQheIXBG8ABnBY + N7M/vYy8CTVYgowdzO/gACYYgiWoAW7gBnFLUTVsgAUQgQXAgQJ2AGRIgAYwh1IoBldAupW8bkjoYE/A + hFRAYbW+F12ISm8ACG/JSu1SxE0UolKSgCmKNiqUEyY5qni5I+hYLVWbHK3q1UtYgBvc8qjogKTICRKE + LMUitAxYIW6JIM0acIJGkRrd/BAiRUjSH2lCecFyVQsUN26xMnmThk3NmjJsrKhho2bJmDVr2Gj14WQI + GTIpylhR8kVdL2vSLKWjsGFDFSspHETzwICCglncIFUiBMlns1qEBv8t8iQq2ShCw2YhghRp1iBXihSR + ChVNEQkV92K1BMViiBYtXfB4o1YsmMZVwJgFQ0RIkbQHT9KQ6VJHkZ9SjTJlm/cAzqBQoGaFkOIlSh9R + pCpVmpVJGilSiWYREyTITyVXr5A9japVyRIrWtmQYcPmyhQ2aMKCZoIDxz1ouFq4ISQhRQoJGnIg2FWD + AwMbMHAPP7pgkskorvg0GSKKlFJLDywowcIFhBCCCCiQLNKKKLrMokgeByhgTzK1JJOOFWFogYQev/zT + jCmrmOLLNdbEYhAJkIByxBNjPDEFKZAMw0001pAghRvcFOEDILUA8gASOizCkyR9uKLJjYLsQYr/JYTw + MUsikjjTnRfmKQGCeVxtZUUNa6ChHhparDfEBvyccosLUUjCTwpvibABCKTAwIQDOeTAgAMEkBJNAPz4 + EIA0s+QBiSeIADFDGvDFUosloBRSCCmNKDLIMIRI00kt1jzYTARTBaEHNYG0UwwrmnTUSymlkJCEAIT0 + YAUaaHghAyl+/EGLkTwg8oMXT3QRAzqxBOBCC7uQAoogiSyTCSl8SFJJKLS8UgodfPByDVRrXKWGFVhU + sRUaXfhQFRtahBHselqk4E8xvhhQBiCFWLEBfhR0IwEOQxg6xBA4xGBhARzMwI8klvwxkg0h+FBDBwFE + UsoslujyQA+qhALJ/yCGCEcEOtaSIskELqwgyC/L7EJNRo7YYkkhA/SARDezFMKCGFqo6wYkecxizTw2 + wHEEV2l4oYMPBQhAgq72DNPCAIrckkklfyTyhy7SCVIINmN4sS4bbYQXxhpe+DAFaOSRsV4OczKRzzf4 + LECAJFAoPBcBDKuIn8I+JDZNAyN0o8gohdjwBQpUPBAAN4Q0MgwgBRTxxAy6+NDAA4QAkg4QD0TziDQg + j/2HQIfcQs00MRJTiwtNePEDKdJUYEVYM7hQx8XWJBJAEWx4gQQLPvTAjwsyFDJIHqC8w08RRCBCDCKz + /CHJKNwoIohTaqjhRBlbqbdEGW004UOZcb6Jxv8YwJs3hAgivHfFIxcwofAoEICDMJCBCSJIwRC4UINo + jG4JLOBGKVChiAi4KS5K8AE3XDMPommBBRWogQSQIAADjCAGPQCGMB7xB0sARRHtmIYjNnGLWkxDFauI + BQl0YIQAjIIUtfjBGrSgBDjIIA+ECEYijICEI4QgAIWAAxAKQQgbvEAUtJBEKZqRAC/EoQ88EUUmamGs + QiQCG0rAQlTSxIYxKMEMP+hCG8yDhjWQgQtkiOObyCCCAKHBCYqQhxVygAJP5IAJZAjDvbSQBjX0oQRu + GUEfStGHTxXAC4YTAxmaMAUCKEMAJZABInrwgxFwABAcCINVvlCJUiTjEYT/uFg72qGJU+TCF8BoBmqk + YQFuWGIUlrCEPVxghS70oQ96kISRvOAFOJTiFYlgQRcmMAtCYKATrhgFKOAAAySsAQlUgINrQuENDPgh + GthwgprUZ54aEOINV1AjGqrABTXebQbKEEIYkNAHUmDABoqAgIpUxAQ0hCENW+hBDhaQghEUQBQ8EcAR + 8NUGNLQhDW0oAxIeILRhCMAGF4iBPEaQA0RWoQM9KMUjWvKLQNBjFZuQBS5ukQxixKJihEjGK4BhDUI0 + QwVS6AM37DCLbLSgDBUKRiwKgZMBjKISgRBfJkBRghJAgQ1l8MIRdtACbsxCS7pgBlTStxX1gYEQ3GjC + /0TbRh45qsc8ORBCBsKghh7EhBRUwIGc7nVIYfVgCmGwwghwYIUmHAEGRXDCE9iwhAQkYApOSAMTxDCG + I1DBENwARSdAUQEpkFQLTMCCMniBjGKAgh7tUMUhGCGLUxTDFqUQBCFoUYxM8OJB3EDCF+DgBzxYYhou + aEMRFMGK25VAHp/CxDQiUYlEIOMYpeCGC6DQBSRk4QUfwEQpgvIUNa21DUfIQx76sIU4qrGt5TFPGPDz + hDKEYBBALQETmKCiOJFBDGYQwAys8AM1ILKgYrAC/L5QgG16oAc1SIFnxeCFAsABFH1ABCBqoIQyPCFY + WqhBMkAxjF8cwxqpcAQjLv9BS1yUYh6/mIUzVLEMUnDjAsP0whT0kIxpJCENZhgGLTrxgCt0IhGIwIQN + k0EEfhDiF8EARiJIgIdH+OEGpWDFMmqBjRaoYQxc2EobtlAAM1BFCWpoA3mDBSc1aoEMZcgCBH3QhUAa + Mlj3IuYWxJCDBlhBvha+1xXUrEkjEKEQHEhDQasAgzLAIBK1+MADJDHVuRVBHsmwxC/acQxTUGITm+BE + MHAhC174pBKZCEYxptGCQIohDU+YQzNCAbUnhEAGTvACFvKQiOduJwIj6AIqLOGNRSjDEqWIBhwgWIpR + 2OIaAzADWbAwhiv0AY5xLEuaxNzWN7GBC0tQQg1qEID/WUihovG9V7B2VwM2hGGgEikoZBXZACKwAFS7 + mIANsIBJNHgABUxgwS5swQ06cOMNfggADGoggNxEIxDL0IQpEr4JVtxCFoQohChiAQtiSEMSF9CCwuaJ + 6nl04U01+IF5rACIPlTAG7OggwqGgIQIZCIULiiBo6DQgRAA4hGCQMY16hCC8XihB18YTxnYOYX2sSEN + FDWP0a0NAiFU4QluAIQayhxfOVF0C5JoQtHslYMRABrQcprBAAKQhwGQgt8zYEIa2EAAGLTBB8nIxKd4 + AgQk0AYJK+AJPSy9Clbg4hSm4Lsl8nA2XhhkHi6OSxgq0gwSdDwMM1CqGpBAhA/A/6ETHyhECSbmKVLM + AwlW8LwVZgCEQSQiGtuwDRjahh7zzLMN7foCFsAs7TfFEcwDXUMPAIGEu8nXAVqwbx+ywIYhxBc/aRCD + hdGQBi4U4Chjh8MXhJCDIYTBByUIQg2KMMlCzEISK6gwRcHwB2okwxmr0ASoccFwVQzABcEGgjx0UYgf + zIAMVYhXHpphAvWA+QoCGAAQKIJZEUIEFIAN/Eb2LBcJyIAPmEQL+EExCAU2rIEZSEIWdMHPcVfbxBEW + cEFardW0DcEatIEXyIM8eEFoTMQaSA03PEAbhAGd7AMmcB0ikYGcaAEXNIEAeIMPzEDRSV8aIEEncEMB + wJE+yf8AFPRANOjAvayBHPxCLZiCK1iDahCDLfTCKjgDCbhABZSAF4zB+xDCD5RBnJhBKDTDCRQdF8Te + EfSBPKBCAbwBdPWAH9RCEHiBC4yCKvxCInzAB8jajAlCM1xD+5iB2CFBG6hTmlzUEpgHHqlRGoAFGkhB + IRTAE9xNFVRBGtjGuNVLBowAOJhDQSGS0a3HenTBFPTAFRzB+fwAGAiAMhQBf4WBGDRABTjBERRAHvgA + FkCBHnhDFCLcKqxCM4ADLvRCUkWDDZRAESZWCRRCADTAGsRBISTDCdwNWbyJsxiHD7gBDfiBKCRCC3QB + FPjBKtyOIPhEMaiCWmTCNkBFzxH/AhiUifqQV9vUT9qNGbXBjdvMQgGMwAhk4hggQQ/Aj9GFgRDcgzUg + AKANgfLhy92A2QzUQAH4gA/IQw+EgBKIwRN4VhgEwSPYwxNUwQ8kwgpMwEUAwyaoAivIwiasQjIIg6oU + AijYgDKUgqutwRg4Qc21gAxIgjDYgBrcTR6hUhyxAAsQASEowyx4wxfsQB5UQi1I0TTMgjGYCC2Awzuy + QRYEQRQEgBt4gT2qketZQSLKCf/p1RqgADCcARlMBBpcgR8EAZyIRT4wgzngTbC0gUM6AT8AARV0gUXR + kRaAIQwAS7lJARRwQzCEAgtkwAz8QB7sQShUAytQgiwIQy+w/8JLAoPrEMIEzEAHxMAUNAAWqMEF+MQe + lINmaQAd2ZFOksEabIEN+JsrOAcwAEEScMMqWAIkwMKn0IIzNANPvaMhdgEbyEsebAFywlPbYAWYOWK9 + mGIREMIRpMEadAEVFEIWlEkOkEEa+MMzOAOmyM96oFINtMQrFEAZdJ3yeYAPjEGcWIEueEMe7MIskABA + FgE3hMIvxMIhmIIsBIMwJEMxmIIteI8UeYYmkoEVLIEKXEEIeBE40MEOVAHTmVlYcIEhqgIi0MEiDMAA + cAMcSAERVMIyCAIv8MEehMwshEIyXAOboJN5rEEW+EEngIGzTRSYkcESuImbtE1YtE0WnP9gF/yAF7FA + RcVJGozANwADFqRBKeILGqgBIIzCKCCEEgAaG5TaRJaiEiRVBJRCMdRCAYgBEtyBJCxDgJ4CK/QCMPzC + OiSDK+wC933MADhBE8hnRa2BFYBBIQDDPLgAVwgBB1iBVHABFHSCJgQADSRBor0BEnCBbSzXLMQCNZQC + L0hDNMRoD8TAeHAFG5hBH+RBBQRBF4wgj47BErTBHQ1p28hNABhBHRTCz0wUGlAfB5iDMBhAHSFSsBid + GGBBCCBCH3glFqjAVCjBGqhADahBFTieIthDB6ACLxTDbbHAHlDDKhxCpjFcMSSDPbiDL7yCJAgAHABC + CawBFlwiUbb/gRkkAjBAAjf4gBIgwRgMAQeggBp0gRtUAihIwRLIgw8oa9vEgSUMgB7EFi1MQyyQQjZc + Q5toBbVlpwAAARRkgShdgRlUhes5QUVdVSKqQRYITRAgwRa4gatqgaEaADgwAwG0DcOIWbAuWxGwQIq0 + QR/oaABEACIUQQcQlBUcVhgYgCTwAigkARAExSaYAjNcQzDcgjAUA/mtQve4QBkIpvKFQRUQJRrMgVD8 + Qc9QQcBdWwHMwBr4gF5owGCdJheYBBTMAqL5AR9YQi2gAyZswzWkKlTMk7qAgQ0UwKtJQQsAAQBOQRCA + QQ9QwRVcwRdMwRd8Ux9EAZjJzRXYkQKg/0MyWIM/oF1/4UsppsESWKQmtoESCID0+IElFMESTF8hhUEO + OAAcmIoF6IE0UMMmFEM5ZIM6MAMzbEoz2AIi/MEPIAEBhUVo4AsXzEEyJEIJOAEcPIAXPCgVXIABFEEF + 0MEE2IPaXNURdMIWQAEhrMAPKIMMMBMpiILedkEXmIGEzRE7/UANTAEKJAAikGgFLKMN/IFZDcAUbMER + 6AAcoAL7WsEYqAAc8FcK7IMz9MNfSV+9GB1tEGUaWEG0NuEkTdI8VoGyEVScjAAzWcwsJAMwAAMuAMM0 + 6EM8WIONCIMAggIoFEGwkAH15ZHU3EEzzMMRVEEXCAARiGUTdAEW/P8AJCjCi/LDESwJEBRBF9AAIcwd + jxyAK0kDC/dBN4BBDTRi2k7BVlyVGjSBE7QL+3TBFQSBKq5ZL85CDZRB+oAZFRQBnTBAP+RAG2jAIpTB + 3RRUsAZLqYVuGyBBCZQVIqKBu4CbGMzASZFCJDRDMphCMchCmfIDA+yDNRRDLGACKQABHGDeDKDBBjik + etgYKNRCC4xkGswAIKSeGswRjClCc4wyN/yCEmGBDVjCCkzvGCRABdguM1iDGbCv+nTBHZJXj6pqHMnm + VIxB7R0BIJjBkrZNGfQAB4hABkSrwlTYlN6LHk+pBKPBF7iAQcYTPnEBIqUADPgBJESDNKhCMDj/Ayfk + AjAMgAeIgDkAAyyMgjQkQQdIxxUYHVGGRhzQUBKgARfc3xF4EpxwgRckQQtgECYk7I5hbylMALTCpxHg + wTpYA7qwrxdsBceOlXnI01hSxZtgwQYGwQ8k4nl53A8YSnr9D0UFKy1SKaA9JKBFcyLUwAsSEApoIhqI + wQggQR0kQiiQwiH0wjQ8gikkQzRAQRgowDuUwjAUggusQQxYQgvU5d2wAfPSgRRwQTyFhQp0QhF4ARcw + ABZEgRR0ARDEAiioQBZYjR9UBgt4wQUMgBzsQYxmg/lUgVTIC1TUKBmMQexZFEURsxWYJRIsyw4EQfuI + 2RVwwxHgqnoQkPJJ/ykfby24Bas+ucLcKUEBIMESLIG9QNYRBMAveIMwUMIqiAM1KIItTAMcgA4gYAcd + hMIFHEEN1EIJiIFlb2IdZMMKkNsYTARZNEGOKoECAEIpAIIUYJs8NAAZSN4sPEQhhMAApCgfZAMzkIP5 + BPYPXEEcjUd5vMv8pF1FgZlfGwEgAMFcRidFrcEUqOxESbC9BKuUdp3X5rcZVMIrcEMFhMAjXEEZfEEp + hoER+MEvJMMmUMIpWAM/TACxSUIL/MAr9IWHsMAc9UAi6IDRHawkTEMc0EbXagF5eAEIAIMMWMFJRUMD + 5IAXdAEKLAGMPcIyfEARgAIvyFQisDA5UMVWyP+L+mgFkRd00eHRHH14E0CCEcSBALgByIpZHAXBeGH2 + QYaule+x8rUBGERCIeRBAUhBE6hBBd8LmupBIjC4I/BdMkDAGADBp3kDU1mCHsxCJ3QymgJCJ0RBGsDA + GchBMnTeh9vg/HRBDfSAEyABFBgBOijBu1zUGjQBi0XBEZSk3RLD05LDlVlFCCCnOoF0FbTNQ4JuG1wB + IZTAFpTJl4UyRcmL1oKunKgBCrSnlk/pFwjAGZG5GJSaFTwWV5NPg8sQMLxDBoigDGRCJhACHLjBAJTC + jsiJGDjBLADCEViBEeSfDLiqDVabSd+REIKCDnQBGQiBFTwBFsDAUIqvDHj/QQ0kQDpIAx9IQzGQwziY + T6lTQY2C9HjYkRzFkdG1gRrIgxGEAKouaX4HqxlQQUV9eNpFex+oAfLVNPIFqxNsqW8TVKApAxScwLFE + 4UcEQzbEwwbEFxIEgCZwAw2wgVBbQgyUYhsQgSKEwg4wL421AdUNNgdcGQYKACl4QycYwXqsAQTMixeE + gCWwQBGsQzOoxR64wzXQe/10QQjQy1gRuVaEheyR+hXUwA70gSXIgKqOWXr7gBl089GpACLAkQd3s9Ed + ppTetxhEhQ/QwR5gginASC9oQywIwzvs0QhEdiSAAhIoHwvkQQh0ZNGZwcPpgYyBAlqFBUEHURVkQQAc + /4EUEMBK8IO466QPRoE/PkEMAMOR/cIfAMM1jIM4VIEZgIF4i+qQT4V4gGeS86gZkHU5KkIhdJvyFV0p + ioG8FN1lv8kXEAIK7DSWE7QS7LSbiYFZlIAfSMM7N4MNlTAwuMM6oIPQ2ACoJAHy5aBnMIGzuIAf6AIh + gMI0fMCL1+VWkEERPEDsdUE3hAIUdC0dkQEW+AAhfE4NDAAtuILYOANAZBsnTg0YH13YrGGTcE1DNmXK + sKmChg2ZhWjQoOhWxguQUrbOtGGTZuRFLWyuXGmDxmIbNU1CzDqShiYammw81JiRJoxNNGHCkEHy5k8z + SsGELftUq1msXsCWGZiBxf+IJRtltKBZg8rejCs9/BDSE2DZsSRcOOSw2aaJDx8PwDRZ6IFWCS9rtJBp + Q8NSIShrLjxIB8gPKWbkBirJc2WhQoaPF1Zhg6YKGZtdAJlxWeBECBVjVqbhIqTNSjFqejhJY3NMDScx + ABWA0WY1zzRH/HQBGiZrzzQ0BCXTdOrar0SssmFYZslevgVDmCDpU8tIGjJhCCSiY2kAjRB6/gCjg2T1 + CCtgHgAqJI8QoR9e1HhB0QlJm6xl4Fia8Jafgn0H8NjFGsTEAeMNLyBzSCE1sGgjh4QWamONOqYozYsl + 4FtjiTVKI4OM1TBq44waaEtDiRrU0ACLNZRAoQwQ25j/YhYpQOypjS9swEMVSjjpRRt78sjklgEOsOSe + DfBJYQgygvCDGzNWm4KQRCTBAIkr9CCkFmlaoK2NC2Lh5oy7nvhiFh8S6qKPHxTqgohBImiiiGhqcMCB + OOgghhxyBJIjCwgBXQOLLlxasTKG2sjCEitKY6O00sbY8NHS0ihtjSC6WE2NBFDA4qYCetBBQjD6IEWG + JjBCQ4s0iOBjFkpWwQWXbOApQRJNpKEBkQJyYEJJiszooQLdSiBkFEkkecEMPbKhY5ZOjrDJiEda8KKN + I2IAYpACrFDIiVmM8IKHUjqRYgwrEHCAgirmCKUYxAbiJg4IFdLqWjfAUIkNIdSQ/3CNJAqhIkKKFlKj + C1KPGEMNhdvgwlEz3HC0Q+toyiIWQGSgwYbCBgDjidrK+EAQVYIRp5dTeoEngwl2uWWCCzpxwteeuGiD + CEKAmKGOXSzhxRI98OBmGgssmIUEHXRQpgIPhmAhHUIUiYUIIThAwgtUAjBhkFlYQKMLCRgIO4o3FBkw + G3LEsWELkRZqmw0z/KgDITasAM0LIrgRgAdCuVhDjSWscKKOAAppQg01rFADDSsYP4ObJkpL1aY0WCgl + kl0GiMCPR4xow7c1TPCjlGCasUcdWazJBwcdBnBEmhJ6qIEJJrQACokA7iAkAh94ISYTYpThoxlBEilk + Ozq4Kf+GhC6q6KaUf14p5UA0OFDhBUIemYWGh8pgwAF8GGABj2W2gVccN8xwiGCXYDAjCC8wqqIhN0qp + hBtQGlyjjIZKM+N+OMoQOUc1pgk2OIITuDC51bABDISohCX01oNM2QR0v3DEKoohDn+4QxjCgEcKmPCC + 0ZngHsuAQK9sIh0eBKEQESjFKjJhiSk14xftIcQsSrGLTCTDBV4gwC7ygAlgzIMFXnBCBeiQCWCcwAtk + qIIVGLAAc3QgDnoYEIHC0S2HuE1iTkDIGqxQhi5wIxZ0cBKhLDIwJWTBDFOIAYdGQhGaxAgMXdgf40Kz + Bh8QAQ4FGAH8LOMFE/zBEZwARjD/1tGBfbAjGODgAA6sQIJVTGMChehBDoZgBSw0qQVE8AMJClEMYkjD + GzQsxA+gMIMLKIMUlkjGBNSAhCz0oRSlSAQGgICB9rgyAl6wQhVyEEUKsMAOidjGQMQhjmrcZQ1eiEhj + GuM3NThBCWswAyHc4IYzQEEJXCwYh9pgBSWshCJsS0MX6uAFklgBBliI4wVYcIExeGAiaPACBn6xiUNY + gxqZsMYBcDCCWFijAUNIAQ8iYQo6dMINToyJHwYhiTq4IA+CsEQikJGIZoCiWk8QwxBqMItazKIQD/CC + F+ZACF7wghC1JMUHeFCHRLxgDcBcAAPIYIY9SCMbyQxHONrR/4WTNoSoHFFCE7qghoZYIQqdqAF8oNk2 + trmtDViwglSlSgUqrCQMbFDDDDCEty+0gQwjeEIULEAIfcqCGvMY3QRyUAV+MIMf+RhBF0jgiGV4AxBI + eEIQBnFDIkhBCn2QBDfykIhlRIMOEXiAD3QyBCMMIiyzEEABAuAHYqhiF9KwZbXeYAlBfAAJKcCHP7zg + gj1YYyA/bccxknoXZyokcU5g5v6K0INuWCshWgkgoCCzEMCJhCQsaYMXgqCQnqBhDE4oQiFq4U6KuOAG + paAEK07BDGnwYxa44MYYxmAAYEwjAVoYwgsMYYoalsBrRahDIZJQBETkoRB6uMMfjOGN6v8OghrTEMAT + 9PiIw3YiGbGAhCpmoYtalOIRJ6DBH2JRjLY6wXtS0AMotuHTY7SDHkk9XHycUIb4KKgMSehEN5DABi5w + AQtLKAMXwPvMNThsDVppyBLU4M00ZKEJxUWDGLxQh0jkKg1eOAEfrJsLpDCjHPrghiZAcQQyPKEbwajF + BTLwhA+sYh0kgIMXqtCGGHCjBCpQAhHAMIs/SCOjhChEIUgRilrQQAxI6EGykLCFCZSCFn/QBSxWsQwS + YEARrgCGM5ZBh7vSYA/LQFs4qtGOQNAjDmowwxac6WGHQAQKgCjFX/S3BoUtZMUyJipRvaqCernNClfR + ihaqQIM8RGL/GidIAgb8YItg5CIWybCGNeLBgBJgIhkskAwMhAGMaORADDrogTO8UQgjoKENS3gfRjjS + wHIUAnvzCEUnQsE1jByBFDYoQg4uEAlYlEISs6AFKOggCVcUAxwC8obV5PCHcogD0hymBz2YaAYPHy4L + W0DcvwphifedWpoDdJtjtki3blIkjWyoAxxyrIUSPKIUU+JDIBQxCVyoQhjTkEQyshEPCsyAG6voSho6 + UIpm8EMCHvDCBKyRjEjYAH5reMASQFTkmxVjS9woRCikEQIEaWEGYEjGAGbgBEQ84qIkaAEPJvCIaQCj + FJagxgHUEAU8SGMckebwpAMhgDN40WBC/wUDKGyQhSRwgxADcEIz8d7M9CWoYCe9wlXZsIQlRMZhbTDD + NKAgoR+0RxCFSEQxKCEMaqzCGhNogDLWkY8MpOADrshGByhXCGtsFxEVmEABgJGIWiS+DF5M1Ui8AAaZ + R0MQ0ljGk8jKBSt0oQe1aEEVCJAMS0TjBDxwwyPcYIQKgMISobDtC/bgDrNPmh6TrsADlLCwpJpBBoXI + wwm4IQ8enEENHBmqGnQABjMwBOJvC8IcBiAFojbBCQvJgQTIsAYwAMGOLQgLIVzBFByhF0DBAjKhF06A + CSCAFNhhBMLABUpBFSZACoKAEGJBGB4BEBogPrrhacCADbSACSojVTsqxQtkgBaIQYYEgAbaYH+eYAye + oAkAQRpcQDp4ARgegRtm4QWkoAsewB4KgRuSQAqKSRyq79+QkB4CAgA7 + + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/SampleResources.resx b/test/Spring/Spring.Core.Tests/Resources/SampleResources.resx new file mode 100644 index 00000000..0efe8412 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/SampleResources.resx @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Hello {0} {1} + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/SimpleAppContext.xml b/test/Spring/Spring.Core.Tests/Resources/SimpleAppContext.xml new file mode 100644 index 00000000..035c87ea --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/SimpleAppContext.xml @@ -0,0 +1,14 @@ + + + + + + George + + + 25 + + + + + diff --git a/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.pt-BR.resx b/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.pt-BR.resx new file mode 100644 index 00000000..8114e529 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.pt-BR.resx @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Isso e {0}{1} + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.resx b/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.resx new file mode 100644 index 00000000..955d5a01 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + This is {0}{1} + + + Visual Studio loves {0}{1} + + + + + + {0} is required {1} + + + First name + + + message1 + + + message3 + + + {0}, {1} + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.sr-Cyrl-CS.resx b/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.sr-Cyrl-CS.resx new file mode 100644 index 00000000..fd7e371b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.sr-Cyrl-CS.resx @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ово је {0}{1} + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.sr-SP-Cyrl.resx b/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.sr-SP-Cyrl.resx new file mode 100644 index 00000000..fd7e371b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.sr-SP-Cyrl.resx @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ово је {0}{1} + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.sr.resx b/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.sr.resx new file mode 100644 index 00000000..b3290dff --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/Spring.Context.Tests.sr.resx @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ovo je {0}{1} + + + Visual Studio voli {0}{1} + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/Tesla.resx b/test/Spring/Spring.Core.Tests/Resources/Tesla.resx new file mode 100644 index 00000000..4c327d8d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/Tesla.resx @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + Private + + + + Nikola Tesla + + + Serbian + + + 7/9/1856 + + + Croatia + + + Smiljan + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/Tesla.sr-Cyrl-CS.resx b/test/Spring/Spring.Core.Tests/Resources/Tesla.sr-Cyrl-CS.resx new file mode 100644 index 00000000..54318df6 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/Tesla.sr-Cyrl-CS.resx @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + Private + + + + Ðикола ТеÑла + + + Србин + + + ХрватÑка + + + Смиљан + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/Tesla.sr-SP-Cyrl.resx b/test/Spring/Spring.Core.Tests/Resources/Tesla.sr-SP-Cyrl.resx new file mode 100644 index 00000000..54318df6 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/Tesla.sr-SP-Cyrl.resx @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + Private + + + + Ðикола ТеÑла + + + Србин + + + ХрватÑка + + + Смиљан + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Resources/Tesla.sr.resx b/test/Spring/Spring.Core.Tests/Resources/Tesla.sr.resx new file mode 100644 index 00000000..f8a2665c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Resources/Tesla.sr.resx @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + Private + + + + Srbin + + + Hrvatska + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2002.csproj b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2002.csproj new file mode 100644 index 00000000..46e78f2a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2002.csproj @@ -0,0 +1,1734 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj new file mode 100644 index 00000000..bd7d0bc4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj @@ -0,0 +1,1934 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2005.csproj b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2005.csproj new file mode 100644 index 00000000..235e7b81 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2005.csproj @@ -0,0 +1,871 @@ + + + Local + 8.0.50727 + 2.0 + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904} + Debug + AnyCPU + + + + + Spring.Core.Tests + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + ..\..\..\build\VS.Net.2005\Spring.Core.Tests\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0;DEBUG_DYNAMIC + + + true + 4096 + false + 67, 618 + false + false + false + false + 4 + full + prompt + false + + + ..\..\..\build\VS.Net.2005\Spring.Core.Tests\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + 618 + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\antlr.runtime.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\Net\2.0\DotNetMock.dll + + + False + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + False + ..\..\..\lib\Net\2.0\Rhino.Mocks.dll + + + System + + + + System.Data + + + System.Drawing + + + + + + + + + System.XML + + + + + Code + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + + Code + + + Code + + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + Code + + + + + + + + + Code + + + + + + + + + + + + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + Code + + + Code + + + Code + + + + Code + + + Code + + + + + + + + + + + Code + + + + Code + + + + + + + + + + + + + Code + + + Code + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + + + + + + + + + + Code + + + Code + + + + + + Code + + + + + + + + + + + Code + + + Code + + + Designer + + + + + testobject.xsd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + + + + + echo "Copying .xml files for tests" +xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2005\Spring.Core.Tests\$(ConfigurationName)\ /y /s /q /d +xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2005\Spring.Core.Tests\$(ConfigurationName)\ /y /s /q + + \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.build b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.build new file mode 100644 index 00000000..60044192 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.build @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.dll-1.1.config b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.dll-1.1.config new file mode 100644 index 00000000..c7957adb --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.dll-1.1.config @@ -0,0 +1,199 @@ + + + + + +
    +
    + +
    +
    + + +
    + + + +
    +
    +
    + +
    + + + + + + +
    +
    +
    + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 12 + John + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Rod + + + 31 + + + + + Roderick + + + + + Kerry + + + 34 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.dll.config b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.dll.config new file mode 100644 index 00000000..b3173801 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.dll.config @@ -0,0 +1,204 @@ + + + + + +
    +
    + +
    +
    + + +
    + + + +
    +
    +
    + +
    + + + + + + +
    +
    +
    + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 12 + John + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Rod + + + 31 + + + + + Roderick + + + + + Kerry + + + 34 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/StandardsComplianceTest.cs b/test/Spring/Spring.Core.Tests/StandardsComplianceTest.cs new file mode 100644 index 00000000..3f9e600a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/StandardsComplianceTest.cs @@ -0,0 +1,93 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +#endregion + +namespace Spring +{ + /// + /// Base class for tests that check standards compliance. + /// + public abstract class StandardsComplianceTest + { + #region Constructor (s) / Destructor + /// + /// Creates a new instance of the + /// class. + /// + /// + /// + /// This is an class, and as such has no publicly visible + /// constructors. + /// + /// + protected StandardsComplianceTest () {} + #endregion + + #region Properties + protected Type CheckedType { + get { + return checkedType; + } + set { + checkedType = value; + } + } + #endregion + + #region Methods + private bool IsCheckedType (Type type) { + if (CheckedType.IsInterface) { + Type iface = type.GetInterface (CheckedType.Name, false); + return iface != null; + } else { + Type baseType = null; + while ((baseType = type.BaseType) != null) { + if (baseType == CheckedType) { + return true; + } + type = baseType; + } + } + return false; + } + + protected void ProcessAssembly (Assembly a) { + foreach (Type t in a.GetTypes ()) { + if (IsCheckedType (t)) { + CheckStandardsCompliance (a, t); + } + } + } + + protected abstract void CheckStandardsCompliance ( + Assembly assembly, Type t); + #endregion + + #region Fields + private Type checkedType; + #endregion + } +} diff --git a/test/Spring/Spring.Core.Tests/StopWatch.cs b/test/Spring/Spring.Core.Tests/StopWatch.cs new file mode 100644 index 00000000..667762e0 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/StopWatch.cs @@ -0,0 +1,100 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring +{ + /// + /// A simple StopWatch implemetion for use in Performance Tests + /// + /// + /// The following example shows the usage: + /// + /// StopWatch watch = new StopWatch(); + /// using (watch.Start("Duration: {0}")) + /// { + /// // do some work... + /// } + /// + /// The format string passed to the start method is used to print the result message. + /// The example above will print Duration: 0.123s. + /// + /// Erich Eichinger + /// $Id: StopWatch.cs,v 1.1 2008/05/16 10:02:42 oakinger Exp $ + public class StopWatch + { + private DateTime _startTime; + private TimeSpan _elapsed; + + private class Stopper : IDisposable + { + private readonly StopWatch _owner; + private readonly string _format; + + public Stopper(StopWatch owner, string format) + { + _owner = owner; + _format = format; + } + + public void Dispose() + { + _owner.Stop(_format); + GC.SuppressFinalize(this); + } + } + + /// + /// Starts the timer and returns and "handle" that must be disposed to stop the timer. + /// + /// the output format string, that is used to render the result message + /// the handle to dispose for stopping the timer + public IDisposable Start(string outputFormat) + { + Stopper stopper = new Stopper(this, outputFormat); + _startTime = DateTime.Now; + return stopper; + } + + private void Stop(string outputFormat) + { + _elapsed = DateTime.Now.Subtract(_startTime); + if (outputFormat != null) + { + Console.WriteLine(outputFormat, _elapsed); + } + } + + public DateTime StartTime + { + get { return _startTime; } + } + + public TimeSpan Elapsed + { + get { return _elapsed; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/StreamHelperDecorator.cs b/test/Spring/Spring.Core.Tests/StreamHelperDecorator.cs new file mode 100644 index 00000000..bb9a6fa2 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/StreamHelperDecorator.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.IO; + +namespace Spring +{ + public delegate void StreamHelperCallback(out Stream stream); + + /// + /// Helper class for template style Stream usage. + /// + /// Rick Evans + /// $Id: StreamHelperDecorator.cs,v 1.2 2006/04/09 07:24:48 markpollack Exp $ + public class StreamHelperDecorator + { + private StreamHelperCallback callback; + + public StreamHelperDecorator(StreamHelperCallback callback) + { + this.callback = callback; + } + + public void Run() + { + Stream stream = null; + try + { + this.callback(out stream); + } + finally + { + if (stream != null) + { + try + { + stream.Close(); + } + catch + { + } + } + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/StringResource.cs b/test/Spring/Spring.Core.Tests/StringResource.cs new file mode 100644 index 00000000..2bac8839 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/StringResource.cs @@ -0,0 +1,116 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Text; +using Spring.Core.IO; +using Spring.Util; + +#endregion + +namespace Spring +{ + /// + /// A adapter implementation encapsulating a simply string. + /// + /// Erich Eichinger + /// $Id: StringResource.cs,v 1.1 2008/03/20 10:35:54 oakinger Exp $ + public class StringResource : AbstractResource + { + #region Fields + + private readonly string _description; + private readonly string _content; + private readonly Encoding _encoding; + + #endregion + + /// + /// Creates a new instance of the class. + /// + public StringResource(string content) + : this(content, Encoding.Default, null) + { + } + + /// + /// Creates a new instance of the class. + /// + public StringResource(string content, Encoding encoding) + : this(content, encoding, null) + { + } + + /// + /// Creates a new instance of the class. + /// + public StringResource(string content, Encoding encoding, string description) + { + AssertUtils.ArgumentNotNull(encoding, "encoding"); + + _content = content==null ? string.Empty : content; + _encoding = encoding; + _description = description == null ? string.Empty : description; + } + + /// + /// Get the to + /// for accessing this resource. + /// + public override Stream InputStream + { + get + { + return new MemoryStream(_encoding.GetBytes(_content), false); + } + } + + /// + /// Returns a description for this resource. + /// + /// + /// A description for this resource. + /// + /// + public override string Description + { + get { return _description; } + } + + /// + /// This implementation always returns true + /// + public override bool IsOpen + { + get { return true; } + } + + /// + /// This implemementation always returns true + /// + public override bool Exists + { + get { return true; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/TestResource.txt b/test/Spring/Spring.Core.Tests/TestResource.txt new file mode 100644 index 00000000..0b7ad580 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/TestResource.txt @@ -0,0 +1 @@ +Spring.TestResource.txt diff --git a/test/Spring/Spring.Core.Tests/Threading/AsyncTestMethod.cs b/test/Spring/Spring.Core.Tests/Threading/AsyncTestMethod.cs new file mode 100644 index 00000000..49b2d1dc --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Threading/AsyncTestMethod.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Threading; +using NUnit.Framework; + +#endregion + +namespace Spring.Threading +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: AsyncTestMethod.cs,v 1.2 2008/02/02 10:24:38 oakinger Exp $ + public class AsyncTestMethod : AsyncTestTask + { + public delegate object TestMethod(object[] args); + + private readonly TestMethod callback; + private readonly object[] args; + private object result; + + private class ThreadStartTestMethodAdapter + { + private readonly ThreadStart threadStart; + + public ThreadStartTestMethodAdapter(ThreadStart threadStart) + { + Assert.IsNotNull(threadStart); + this.threadStart = threadStart; + } + + public object Execute(object[] args) + { + threadStart(); + return null; + } + } + + public AsyncTestMethod(int iterations, ThreadStart callback, params object[] args) + : this(iterations, new TestMethod(new ThreadStartTestMethodAdapter(callback).Execute), args) + { + } + + + public AsyncTestMethod(int iterations, TestMethod callback, params object[] args) : base(iterations) + { + Assert.IsNotNull(callback); + this.args = args; + this.callback = callback; + } + + public override void DoExecute() + { + result = callback(args); + } + + public object Result + { + get { return result; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Threading/AsyncTestTask.cs b/test/Spring/Spring.Core.Tests/Threading/AsyncTestTask.cs new file mode 100644 index 00000000..bfe616f9 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Threading/AsyncTestTask.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Threading; + +#endregion + +namespace Spring.Threading +{ + /// + /// Base class for async test operations + /// + /// Erich Eichinger + /// $Id: AsyncTestTask.cs,v 1.2 2008/02/02 10:24:38 oakinger Exp $ + public abstract class AsyncTestTask + { + private Exception exception; + private Thread t; + private readonly int iterations; + private int iterationno; + + public AsyncTestTask(int iterations) + { + this.iterations = iterations; + t = new Thread(new ThreadStart(Run)); + t.IsBackground = true; + } + + public abstract void DoExecute(); + + public AsyncTestTask Start() + { + t.Start(); + return this; + } + + public void Run() + { + int loop = 0; + try + { + iterationno = 0; + for(loop = 0; loop < iterations; loop++) + { + DoExecute(); + Thread.Sleep(0); + } + } + catch(Exception ex) + { + iterationno = loop; + exception = ex; + } + } + + public void AssertNoException() + { + t.Join(); + if(exception != null) + { + throw new Exception("Exception occured in testtask on iteration " + iterationno, exception); + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Threading/CallContextStorageTests.cs b/test/Spring/Spring.Core.Tests/Threading/CallContextStorageTests.cs new file mode 100644 index 00000000..6d191aaa --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Threading/CallContextStorageTests.cs @@ -0,0 +1,42 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +#endregion + +namespace Spring.Threading +{ + /// + /// Apply common thread-storage tests for + /// + /// Erich Eichinger + /// $Id: CallContextStorageTests.cs,v 1.1 2007/02/02 21:31:08 oakinger Exp $ + [TestFixture] + public class CallContextStorageTests : CommonThreadStorageTests + { + protected override IThreadStorage CreateStorage() + { + return new CallContextStorage(); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Threading/CommonThreadStorageTests.cs b/test/Spring/Spring.Core.Tests/Threading/CommonThreadStorageTests.cs new file mode 100644 index 00000000..4ec9de82 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Threading/CommonThreadStorageTests.cs @@ -0,0 +1,168 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Threading; +using NUnit.Framework; + +#endregion + +namespace Spring.Threading +{ + /// + /// Any implementation must pass these tests. + /// + /// Erich Eichinger + /// $Id: CommonThreadStorageTests.cs,v 1.2 2007/05/14 21:31:32 oakinger Exp $ + public abstract class CommonThreadStorageTests + { + protected abstract IThreadStorage CreateStorage(); + + protected virtual void ThreadSetup() + {} + + protected virtual void ThreadCleanup() + {} + + [TestFixtureSetUp] + public void FixtureSetUp() + { + ThreadSetup(); + } + + [TestFixtureTearDown] + public void FixtureTearDown() + { + ThreadCleanup(); + } + + [TearDown] + public void TearDown() + { + // cleanup storage + IThreadStorage storage = CreateStorage(); + storage.FreeNamedDataSlot("key"); + storage.FreeNamedDataSlot("KEY"); + storage.FreeNamedDataSlot("KeY"); + } + + [Test] + public void IsCaseSensitive() + { + IThreadStorage storage = CreateStorage(); + + object value1 = new object(); + object value2 = new object(); + object value3 = new object(); + + storage.SetData("key", value1); + storage.SetData("KEY", value2); + storage.SetData("KeY", value3); + + Assert.AreSame(value1, storage.GetData("key")); + Assert.AreSame(value2, storage.GetData("KEY")); + Assert.AreSame(value3, storage.GetData("KeY")); + } + + [Test] + public void AllowReplaceData() + { + IThreadStorage storage = CreateStorage(); + + object value1 = new object(); + object value2 = new object(); + + storage.SetData("key", value1); + Assert.AreSame(value1, storage.GetData("key")); + storage.SetData("key", value2); + Assert.AreSame(value2, storage.GetData("key")); + storage.SetData("key", null); + Assert.AreSame(null, storage.GetData("key")); + } + + [Test] + public void UnknownKeyReturnsNull() + { + IThreadStorage storage = CreateStorage(); + + Assert.AreSame(null, storage.GetData("key")); + } + + [Test] + public void FreeNamedDataSlotRemovesData() + { + IThreadStorage storage = CreateStorage(); + + object value1 = new object(); + storage.SetData("key", value1); + Assert.AreSame(value1, storage.GetData("key")); + storage.FreeNamedDataSlot("key"); + Assert.AreSame(null, storage.GetData("key")); + } + + [Test] + public void UsesDistinguishedStorageOnDifferentThreads() + { + IThreadStorage storage = CreateStorage(); + + object value1 = new object(); + storage.SetData("key", value1); + + ThreadTestHelper helper = new ThreadTestHelper(this,storage); + helper.Execute(); + + Assert.AreNotSame( value1, helper.value ); + Assert.IsNull(helper.value); + } + + #region Test Utility Classes + + private class ThreadTestHelper + { + private CommonThreadStorageTests outer; + private IThreadStorage storage; + + public object value; + + public ThreadTestHelper(CommonThreadStorageTests outer, IThreadStorage storage) + { + this.outer = outer; + this.storage = storage; + } + + public void Execute() + { + Thread t = new Thread(new ThreadStart(this.Run)); + t.Start(); + t.Join(); + } + + private void Run() + { + outer.ThreadSetup(); + value = this.storage.GetData("key"); + } + } + + #endregion Test Utility Classes + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Threading/LatchTest.cs b/test/Spring/Spring.Core.Tests/Threading/LatchTest.cs new file mode 100644 index 00000000..0623a318 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Threading/LatchTest.cs @@ -0,0 +1,83 @@ +using System; +using System.Threading; +using Spring.Threading; +using NUnit.Framework; + +#region Licence + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +namespace Spring.Threading +{ + [TestFixture] + public class LatchTest + { + + class Worker + { + public bool worked; + ISync startPermit; + ISync workedSync; + + public Worker (ISync startPermit, ISync workedSync) + { + this.startPermit = startPermit; + this.workedSync = workedSync; + } + + public void Work() + { + // very slow worker initialization ... + // ... attach to messaging system + // ... connect to database + startPermit.Acquire(); + // ... use resources initialized in Mush + // ... do real work + worked = true; + // ... we are finished + workedSync.Release(); + } + } + + [Test] + public void BossWorkerDemo() + { + int n = 10; + + Latch startPermit = new Latch(); + Semaphore wait = new Semaphore(-(n - 1)); + + Worker [] workers = new Worker[n]; + for (int i=0; i + /// Test behaviour of LogicalThreadContext + /// + /// Erich Eichinger + /// $Id: LogicalThreadContextTest.cs,v 1.1 2007/02/02 21:31:08 oakinger Exp $ + [TestFixture] + public class LogicalThreadContextTest + { + private class MockStorage : IThreadStorage + { + internal Hashtable data = new Hashtable(); + + public object GetData(string name) + { + return data[name]; + } + + public void SetData(string name, object value) + { + data[name] = value; + } + + public void FreeNamedDataSlot(string name) + { + data.Remove(name); + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void StorageMustNotBeNull() + { + LogicalThreadContext.SetStorage(null); + } + + [Test] + public void StorageMayBeSetMoreThanOnce() + { + LogicalThreadContext.SetStorage(new MockStorage()); + LogicalThreadContext.SetStorage(new MockStorage()); + LogicalThreadContext.SetStorage(new MockStorage()); + } + + [Test] + public void StorageIsUsedByFacadeMethods() + { + MockStorage storage = new MockStorage(); + LogicalThreadContext.SetStorage(storage); + + object value = new object(); + + LogicalThreadContext.SetData("key", value); + Assert.AreSame( value, storage.data["key"] ); + + object data = LogicalThreadContext.GetData("key"); + Assert.AreSame(value, data); + + LogicalThreadContext.FreeNamedDataSlot("key"); + + Assert.IsFalse( storage.data.ContainsKey("key") ); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Threading/SemaphoreTest.cs b/test/Spring/Spring.Core.Tests/Threading/SemaphoreTest.cs new file mode 100644 index 00000000..cc2902a1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Threading/SemaphoreTest.cs @@ -0,0 +1,219 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#endregion + +using System; +using System.Threading; +using Spring.Pool; +using Spring.Threading; +using NUnit.Framework; + + +namespace Spring.Threading +{ + [TestFixture] + public class SemaphoreTest + { + private static long SHORT_DELAY_MS = 300; + [Test] + public void UsingLikeAMutex () + { + + Semaphore semaphore = new Semaphore (1); + Latch latch = new Latch (); + Helper helper = new Helper (semaphore, latch); + Thread thread = new Thread (new ThreadStart (helper.Go)); + semaphore.Acquire (); + thread.Start (); + latch.Acquire (); + Assert.IsFalse (helper.gone); + semaphore.Release (); + thread.Join (); + Assert.IsTrue (helper.gone, "not gone"); + + } + [Test] + public void AquireInSameThrad() + { + Semaphore s = new Semaphore(2); + Assert.AreEqual(2, s.Permits); + s.Acquire(); + s.Acquire(); + Assert.AreEqual(0, s.Permits); + + } + + [Test] + public void ReleaseMultipleTimesInSameThread() + { + Semaphore s = new Semaphore(2); + Assert.AreEqual(2, s.Permits); + s.Acquire(); + s.Acquire(); + s.Release(2); + Assert.AreEqual(2, s.Permits); + } + + [Test] + public void AttemptWithNegativeMillisInSameThread() + { + Semaphore s = new Semaphore(2); + Assert.AreEqual(2, s.Permits); + s.Acquire(); + Assert.IsTrue(s.Attempt(-400)); + s.Release(2); + Assert.AreEqual(2, s.Permits); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void ReleaseMultipleBadArgument() + { + Semaphore s = new Semaphore(2); + Assert.AreEqual(2, s.Permits); + s.Acquire(); + s.Acquire(); + s.Release(-2); + Assert.AreEqual(2, s.Permits); + } + + [Test] + public void AttemptInSameThread() + { + Semaphore s = new Semaphore(1); + Assert.IsTrue(s.Attempt(SHORT_DELAY_MS)); + s.Release(); + Assert.IsTrue(s.Attempt(SHORT_DELAY_MS)); + s.Release(); + Assert.IsTrue(s.Attempt(SHORT_DELAY_MS)); + s.Release(); + Assert.IsTrue(s.Attempt(SHORT_DELAY_MS)); + s.Release(); + Assert.IsTrue(s.Attempt(SHORT_DELAY_MS)); + s.Release(); + Assert.AreEqual(1, s.Permits); + + } + + //TODO tests that interrupt a thread. + + [Test] + public void AquireReleaseInSameThread() + { + Semaphore s = new Semaphore(1); + s.Acquire(); + s.Release(); + s.Acquire(); + s.Release(); + s.Acquire(); + s.Release(); + s.Acquire(); + s.Release(); + s.Acquire(); + s.Release(); + s.Acquire(); + s.Release(); + Assert.AreEqual(1, s.Permits); + } + + [Test] + public void AcquireReleaseInDifferentThreads() + { + Semaphore s = new Semaphore(0); + AcquireReleaseWorker worker1 = new AcquireReleaseWorker(s); + Thread thread1 = new Thread(new ThreadStart(worker1.DoWork)); + + thread1.Start(); + Thread.Sleep(300); + s.Release(); + s.Release(); + s.Acquire(); + s.Acquire(); + s.Release(); + thread1.Join(); + + } + + [Test] + public void AttemptReleaseInDifferentThreads() + { + Semaphore s = new Semaphore(0); + AttemptReleaseWorker worker1 = new AttemptReleaseWorker(s); + Thread thread1 = new Thread(new ThreadStart(worker1.DoWork)); + + thread1.Start(); + //TODO investigate... + //Assert.IsTrue(s.Attempt(SHORT_DELAY_MS)); + s.Attempt(SHORT_DELAY_MS); + s.Release(); + //Assert.IsTrue(s.Attempt(SHORT_DELAY_MS)); + s.Attempt(SHORT_DELAY_MS); + s.Release(); + s.Release(); + thread1.Join(); + } + + [Test] + public void TimetoMillis() + { + long millis = Utils.CurrentTimeMillis; + //hmm should be using UtcNow, the impl in CurrentTimeMillis doesn't use UTC. + long epochTime = ((DateTime.Now-new DateTime (1970, 1, 1)).Ticks)/TimeSpan.TicksPerMillisecond; + long delta = epochTime-millis; + Assert.IsTrue(delta < 500); + } + } + + public class AcquireReleaseWorker + { + private Semaphore sem; + public AcquireReleaseWorker(Semaphore s) + { + sem = s; + } + + public void DoWork() + { + sem.Acquire(); + sem.Release(); + sem.Release(); + sem.Acquire(); + } + } + + public class AttemptReleaseWorker + { + private Semaphore sem; + public AttemptReleaseWorker(Semaphore s) + { + sem = s; + } + + public void DoWork() + { + long SHORT_DELAY_MS = 300; + sem.Release(); + + //TODO can we do Assert.IsTrue here? + sem.Attempt(SHORT_DELAY_MS); + sem.Release(); + sem.Attempt(SHORT_DELAY_MS); + } + } + +} diff --git a/test/Spring/Spring.Core.Tests/Threading/SyncHolderTest.cs b/test/Spring/Spring.Core.Tests/Threading/SyncHolderTest.cs new file mode 100644 index 00000000..ba292412 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Threading/SyncHolderTest.cs @@ -0,0 +1,68 @@ +using System.Threading; +using DotNetMock.Dynamic; +using NUnit.Framework; + +namespace Spring.Threading +{ + [TestFixture] + public class SyncHolderTest + { + DynamicMock mock; + ISync sync; + + [SetUp] + public void SetUp () + { + mock = new DynamicMock(typeof(ISync)); + sync = (ISync) mock.Object; + } + + [TearDown] + public void TearDown () + { + mock.Verify(); + } + + class MySemaphore : Semaphore + { + public MySemaphore (long initialPermits) : base (initialPermits) + {} + } + + [Test] + public void CanBeUsedWithTheUsingCSharpIdiomToAttemptOnAnISync () + { + MySemaphore sync = new MySemaphore(1); + using (new SyncHolder(sync, 100)) + { + Assert.AreEqual(0, sync.Permits); + } + Assert.AreEqual(1, sync.Permits); + + sync = new MySemaphore(0); + try + { + using (new SyncHolder(sync, 100)) + { + Assert.IsTrue(false, "wrongly entered sync block"); + } + } + catch (TimeoutException) + { + Assert.AreEqual(0, sync.Permits); + } + } + + [Test] + [ExpectedException(typeof(ThreadStateException))] + public void CanBeUsedWithTheUsingCSharpIdiomToAcquireAnIsync() + { + mock.Expect("Acquire"); + mock.Expect("Release"); + using (new SyncHolder(sync)) + { + throw new ThreadStateException(); + } + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Threading/ThreadStaticStorageTests.cs b/test/Spring/Spring.Core.Tests/Threading/ThreadStaticStorageTests.cs new file mode 100644 index 00000000..7356ba00 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Threading/ThreadStaticStorageTests.cs @@ -0,0 +1,42 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +#endregion + +namespace Spring.Threading +{ + /// + /// Apply common thread-storage tests for + /// + /// Erich Eichinger + /// $Id: ThreadStaticStorageTests.cs,v 1.1 2007/02/02 21:31:08 oakinger Exp $ + [TestFixture] + public class ThreadStaticStorageTests : CommonThreadStorageTests + { + protected override IThreadStorage CreateStorage() + { + return new ThreadStaticStorage(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Util/ArrayUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/ArrayUtilsTests.cs new file mode 100644 index 00000000..44e5dc88 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/ArrayUtilsTests.cs @@ -0,0 +1,69 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the ArrayUtils class. + /// + /// Rick Evans + /// $Id: ArrayUtilsTests.cs,v 1.5 2007/02/02 12:33:30 oakinger Exp $ + [TestFixture] + public sealed class ArrayUtilsTests + { + [Test] + public void AreEqual () + { + object [] one = new string [] {"Foo", "Bar", "Baz"}; + object [] two = new string [] {"Foo", "Bar", "Baz"}; + Assert.IsTrue (ArrayUtils.AreEqual (one, two)); + object [] three = new string [] {"Foo", "Ben", "Baz"}; + Assert.IsFalse (ArrayUtils.AreEqual (one, three)); + } + + [Test] + public void AreEqualWithBadArguments () + { + Assert.IsTrue (ArrayUtils.AreEqual (null, null)); + object [] one = new string [] {"Foo", "Bar", "Baz"}; + object [] two = null; + Assert.IsFalse (ArrayUtils.AreEqual (one, two)); + object [] three = new string [] {"Foo", "Bar"}; + Assert.IsFalse (ArrayUtils.AreEqual (one, three)); + } + + [Test] + public void HasLengthTests() + { + Assert.IsFalse(ArrayUtils.HasLength(null)); + Assert.IsFalse(ArrayUtils.HasLength(new byte[0])); + Assert.IsTrue(ArrayUtils.HasLength(new byte[1])); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Util/AssertUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/AssertUtilsTests.cs new file mode 100644 index 00000000..3008a480 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/AssertUtilsTests.cs @@ -0,0 +1,126 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the AssertUtils class. + /// + /// Rick Evans + /// $Id: AssertUtilsTests.cs,v 1.8 2007/05/30 22:39:28 markpollack Exp $ + [TestFixture] + public sealed class AssertUtilsTests + { + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void StateTrue() + { + AssertUtils.State(false,"foo"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentNotNull () + { + AssertUtils.ArgumentNotNull(null, "foo"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentNotNullWithMessage () + { + AssertUtils.ArgumentNotNull(null, "foo", "Bang!"); + } + + [Test] + public void ArgumentHasTextWithValidText() + { + AssertUtils.ArgumentHasText("... and no-one's getting fat 'cept Mama Cas!", "foo"); + } + + [Test] + public void ArgumentHasTextWithValidTextAndMessage() + { + AssertUtils.ArgumentHasText("... and no-one's getting fat 'cept Mama Cas!", "foo", "Bang!"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentHasText () + { + AssertUtils.ArgumentHasText(null, "foo"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentHasTextWithMessage () + { + AssertUtils.ArgumentHasText(null, "foo", "Bang!"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentHasLengthArgumentIsNull() + { + AssertUtils.ArgumentHasLength(null, "foo"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentHasLengthArgumentIsNullWithMessage() + { + AssertUtils.ArgumentHasLength(null, "foo", "Bang!"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentHasLengthArgumentIsEmpty() + { + AssertUtils.ArgumentHasLength(new byte[0], "foo"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentHasLengthArgumentIsEmptyWithMessage() + { + AssertUtils.ArgumentHasLength(new byte[0], "foo", "Bang!"); + } + + [Test] + public void ArgumentHasLengthArgumentHasElements() + { + AssertUtils.ArgumentHasLength(new byte[1], "foo"); + } + + [Test] + public void ArgumentHasLengthArgumentHasElementsWithMessage() + { + AssertUtils.ArgumentHasLength(new byte[1], "foo", "Bang!"); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Util/CollectionUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/CollectionUtilsTests.cs new file mode 100644 index 00000000..ca211426 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/CollectionUtilsTests.cs @@ -0,0 +1,305 @@ +using System; +using System.Collections; +using NUnit.Framework; + +namespace Spring.Util +{ + [TestFixture] + public class CollectionUtilsTests + { + internal class NoContainsNoAddCollection : ICollection + { + internal class Iterator : IEnumerator + { + #region IEnumerator Members + + public void Reset() + { + // TODO: Add Iterator.Reset implementation + } + + public object Current + { + get + { + // TODO: Add Iterator.Current getter implementation + return null; + } + } + + public bool MoveNext() + { + // TODO: Add Iterator.MoveNext implementation + return false; + } + + #endregion + + } + + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + public int Count + { + get { return 0; } + } + + public object SyncRoot + { + get { throw new NotImplementedException(); } + } + + public bool IsSynchronized + { + get { throw new NotImplementedException(); } + } + + public IEnumerator GetEnumerator() + { + return new Iterator(); + } + } + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ContainsNullCollection() + { + CollectionUtils.Contains(null, null); + } + [Test] + public void ContainsNullObject() + { + ArrayList list = new ArrayList(); + list.Add(null); + Assert.IsTrue(CollectionUtils.Contains(list, null)); + } + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void ContainsCollectionDoesNotImplementContains() + { + NoContainsNoAddCollection noAddCollection = new NoContainsNoAddCollection(); + CollectionUtils.Contains(noAddCollection, new object()); + } + [Test] + public void ContainsValidElement() + { + ArrayList list = new ArrayList(); + list.Add(1); + list.Add(2); + list.Add(3); + list.Add(4); + + Assert.IsTrue(CollectionUtils.Contains(list, 3)); + } + [Test] + public void ContainsDoesNotContainElement() + { + ArrayList list = new ArrayList(); + list.Add(1); + list.Add(2); + list.Add(3); + list.Add(4); + + Assert.IsFalse(CollectionUtils.Contains(list, 5)); + } + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void AddNullCollection() + { + CollectionUtils.Add(null, null); + } + [Test] + public void AddNullObject() + { + ArrayList list = new ArrayList(); + CollectionUtils.Add(list, null); + Assert.IsTrue(list.Count == 1); + } + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void AddCollectionDoesNotImplementAdd() + { + NoContainsNoAddCollection noAddCollection = new NoContainsNoAddCollection(); + CollectionUtils.Add(noAddCollection, null); + } + [Test] + public void AddValidElement() + { + ArrayList list = new ArrayList(); + object obj1 = new object(); + CollectionUtils.Add(list, obj1); + Assert.IsTrue(list.Count == 1); + } + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ContainsAllNullTargetCollection() + { + CollectionUtils.ContainsAll(null, new ArrayList()); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ContainsAllSourceNullCollection() + { + CollectionUtils.ContainsAll(new ArrayList(), null); + } + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void ContainsAllDoesNotImplementContains() + { + CollectionUtils.ContainsAll(new NoContainsNoAddCollection(), new ArrayList()); + } + [Test] + public void DoesNotContainAllElements() + { + ArrayList target = new ArrayList(); + target.Add(1); + target.Add(2); + target.Add(3); + + ArrayList source = new ArrayList(); + source.Add(1); + + Assert.IsTrue(CollectionUtils.ContainsAll(target, source)); + } + [Test] + public void ContainsAllElements() + { + ArrayList target = new ArrayList(); + target.Add(1); + target.Add(2); + target.Add(3); + + ArrayList source = new ArrayList(); + source.Add(1); + source.Add(2); + source.Add(3); + + Assert.IsTrue(CollectionUtils.ContainsAll(target, source)); + } + [Test] + public void ContainsAllElementsWithNoElementsInSourceCollection() + { + ArrayList target = new ArrayList(); + target.Add(1); + target.Add(2); + target.Add(3); + + ArrayList source = new ArrayList(); + Assert.IsTrue(CollectionUtils.ContainsAll(target, source)); + } + [Test] + public void ContainsAllElementsWithNoElementsEitherCollection() + { + ArrayList target = new ArrayList(); + ArrayList source = new ArrayList(); + Assert.IsFalse(CollectionUtils.ContainsAll(target, source)); + } + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ToArrayNullTargetCollection() + { + CollectionUtils.ToArrayList(null); + } + [Test] + public void ToArrayAllElements() + { + ArrayList target = new ArrayList(); + target.Add(1); + target.Add(2); + target.Add(3); + + ArrayList source = CollectionUtils.ToArrayList(target); + + Assert.AreEqual(target.Count, source.Count); + } + [Test] + public void EmptyArrayElements() + { + ArrayList source = CollectionUtils.ToArrayList(new NoContainsNoAddCollection()); + Assert.AreEqual(0, source.Count); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void RemoveAllTargetNullCollection() + { + CollectionUtils.RemoveAll(null, new ArrayList()); + } + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void RemoveAllSourceNullCollection() + { + CollectionUtils.RemoveAll(new ArrayList(), null); + } + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void RemoveAllTargetCollectionDoesNotImplementContains() + { + CollectionUtils.RemoveAll(new NoContainsNoAddCollection(), new ArrayList()); + } + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void RemoveAllTargetCollectionDoesNotImplementRemove() + { + CollectionUtils.RemoveAll(new NoContainsNoAddCollection(), new ArrayList()); + } + [Test] + public void RemoveAllNoElements() + { + ArrayList target = new ArrayList(); + target.Add(1); + target.Add(2); + target.Add(3); + + ArrayList source = new ArrayList(); + source.Add(4); + source.Add(5); + source.Add(6); + + CollectionUtils.RemoveAll(target, source); + Assert.IsTrue(3 == target.Count); + } + [Test] + public void RemoveAllSomeElements() + { + ArrayList target = new ArrayList(); + target.Add(1); + target.Add(2); + target.Add(3); + target.Add(4); + target.Add(5); + + ArrayList source = new ArrayList(); + source.Add(4); + source.Add(5); + source.Add(6); + + CollectionUtils.RemoveAll(target, source); + Assert.IsTrue(3 == target.Count); + } + [Test] + public void RemoveAllAllElements() + { + ArrayList target = new ArrayList(); + target.Add(1); + target.Add(2); + target.Add(3); + target.Add(4); + target.Add(5); + + ArrayList source = new ArrayList(); + source.Add(1); + source.Add(2); + source.Add(3); + source.Add(4); + source.Add(5); + source.Add(6); + + CollectionUtils.RemoveAll(target, source); + Assert.IsTrue(0 == target.Count); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Util/DefensiveEventRaiserTests.cs b/test/Spring/Spring.Core.Tests/Util/DefensiveEventRaiserTests.cs new file mode 100644 index 00000000..3487e919 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/DefensiveEventRaiserTests.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the DefensiveEventRaiser class. + /// + /// Rick Evans + /// $Id: DefensiveEventRaiserTests.cs,v 1.2 2006/04/09 07:24:51 markpollack Exp $ + [TestFixture] + public sealed class DefensiveEventRaiserTests + { + [Test] + public void RaiseSwallowsExceptionRaisedByHandlers () + { + OneThirstyDude dude = new OneThirstyDude (); + Soda bru = new Soda (); + bru.Pop += new PopHandler (dude.HandlePopWithException); + bru.OnPop ("Iron Brew", new DefensiveEventRaiser ()); + Assert.AreEqual ("Iron Brew", dude.Soda); // should have got through before exception was thrown + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Util/DelegateInfoTests.cs b/test/Spring/Spring.Core.Tests/Util/DelegateInfoTests.cs new file mode 100644 index 00000000..9dabc074 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/DelegateInfoTests.cs @@ -0,0 +1,186 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the DelegateInfo class. + /// + /// Rick Evans + /// $Id: DelegateInfoTests.cs,v 1.6 2006/04/09 07:24:51 markpollack Exp $ + [TestFixture] + public sealed class DelegateInfoTests + { + [Test] + public void Instantiation() + { + new DelegateInfo(typeof (EventHandler)); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void InstantiationWithBadType() + { + new DelegateInfo(typeof (string)); + } + + [Test] + public void IsDelegateWithBadType() + { + Assert.IsFalse(DelegateInfo.IsDelegate(typeof (string))); + } + + [Test] + public void IsDelegateWithNullType() + { + Assert.IsFalse(DelegateInfo.IsDelegate(null)); + } + + [Test] + public void IsDelegate() + { + Assert.IsTrue(DelegateInfo.IsDelegate(typeof (EventHandler))); + } + + [Test] + public void GetReturnType() + { + DelegateInfo del = new DelegateInfo(typeof (EventHandler)); + Assert.AreEqual(typeof (void), del.GetReturnType()); + } + + [Test] + public void GetReturnTypeWithNonVoidReturningDelegate() + { + DelegateInfo del = new DelegateInfo(typeof (FooHandler)); + Assert.AreEqual(typeof (string), del.GetReturnType()); + } + + [Test] + public void GetParameterTypesWithNoArgHandler() + { + DelegateInfo del = new DelegateInfo(typeof (NoParametersHandler)); + Type[] types = del.GetParameterTypes(); + Assert.IsNotNull(types); + Assert.AreEqual(0, types.Length); + } + + [Test] + public void GetParameterTypes() + { + DelegateInfo del = new DelegateInfo(typeof (FooHandler)); + Type[] types = del.GetParameterTypes(); + Assert.IsNotNull(types); + Assert.AreEqual(3, types.Length); + } + + [Test] + public void IsSignatureCompatibleWithBadMethods() + { + DelegateInfo del = new DelegateInfo(typeof (FooHandler)); + + // null method... + MethodInfo method = GetType().GetMethod("SeaBass said that? Well, if that guy over there is SeaBass..."); + Assert.IsFalse(del.IsSignatureCompatible(method)); + + // method that doesn;t have the required number of parameters... + method = GetType().GetMethod("OddNumberOfParametersForFooHandler"); + Assert.IsFalse(del.IsSignatureCompatible(method)); + + // method that doesn't have the required number of parameters... + method = GetType().GetMethod("IncompatibleParametersForFooHandler"); + Assert.IsFalse(del.IsSignatureCompatible(method)); + } + + public string OddNumberOfParametersForFooHandler(string bar, int x) + { + return string.Empty; + } + + public string IncompatibleParametersForFooHandler(string bar, int x, string nope) + { + return string.Empty; + } + + [Test] + public void IsSignatureCompatibleWithInnerClassStaticMethod() + { + DelegateInfo del = new DelegateInfo(typeof (FooHandler)); + MethodInfo method = + typeof (Yossarian).GetMethod( + "DoFoo", + BindingFlags.Public | BindingFlags.Static); + Assert.IsTrue(del.IsSignatureCompatible(method)); + } + + [Test] + public void IsSignatureCompatible() + { + DelegateInfo del = new DelegateInfo(typeof (FooHandler)); + MethodInfo method = GetType().GetMethod("DoFoo"); + Assert.IsFalse(del.IsSignatureCompatible(method)); + } + + [Test] + public void IsSignatureCompatibleStaticVersion() + { + EventInfo evt = typeof (Assembly).GetEvent("ModuleResolve"); + + MethodInfo rubbishMethod = GetType().GetMethod("MethodSignatureIsCompatibleStatic"); + Assert.IsFalse(DelegateInfo.IsSignatureCompatible(evt, rubbishMethod)); + + MethodInfo compatibleMethod = typeof (Yossarian).GetMethod("Resolve"); + Assert.IsTrue(DelegateInfo.IsSignatureCompatible(evt, compatibleMethod)); + + Assert.IsFalse(DelegateInfo.IsSignatureCompatible(null, compatibleMethod)); + Assert.IsFalse(DelegateInfo.IsSignatureCompatible(evt, null)); + } + + private string DoFoo(string bar, int x, EventArgs e) + { + return bar; + } + + internal sealed class Yossarian + { + public static string DoFoo(string bar, int x, EventArgs e) + { + return bar; + } + + public Module Resolve(object sender, ResolveEventArgs e) + { + return null; + } + } + } + + internal delegate string FooHandler(string bar, int x, EventArgs e); + + internal delegate void NoParametersHandler(); +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Util/EventRaiserTests.cs b/test/Spring/Spring.Core.Tests/Util/EventRaiserTests.cs new file mode 100644 index 00000000..f525741e --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/EventRaiserTests.cs @@ -0,0 +1,128 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the EventRaiser class. + /// + /// Rick Evans + /// $Id: EventRaiserTests.cs,v 1.3 2006/04/09 07:24:51 markpollack Exp $ + [TestFixture] + public sealed class EventRaiserTests + { + [Test] + public void Raise () + { + OneThirstyDude dude = new OneThirstyDude (); + Soda bru = new Soda (); + bru.Pop += new PopHandler (dude.HandlePop); + bru.OnPop ("Iron Brew", new EventRaiser ()); + Assert.AreEqual ("Iron Brew", dude.Soda); + } + + [Test] + [ExpectedException (typeof (System.Reflection.TargetParameterCountException))] + public void RaiseWithBadNumberOfArguments () + { + OneThirstyDude dude = new OneThirstyDude (); + Soda bru = new Soda (); + bru.Pop += new PopHandler (dude.HandlePop); + bru.OnPopWithBadNumberOfArguments ("Iron Brew", new EventRaiser ()); + } + + [Test] + public void RaiseWithNullEvent () + { + OneThirstyDude dude = new OneThirstyDude (); + Soda bru = new Soda (); + bru.Pop += new PopHandler (dude.HandlePop); + bru.OnPopWithNullEvent ("Iron Brew", new EventRaiser ()); + Assert.AreEqual (string.Empty, dude.Soda); + } + + [Test][ExpectedException (typeof (FormatException), "Iron Brew")] + public void RaiseWithAnEventHandlerThatThrowsAnException () + { + OneThirstyDude dude = new OneThirstyDude (); + Soda bru = new Soda (); + bru.Pop += new PopHandler (dude.HandlePopWithException); + bru.OnPop ("Iron Brew", new EventRaiser ()); + } + } + + internal delegate void PopHandler (object sender, string soda); + + internal sealed class Soda + { + public event PopHandler Pop; + + public void OnPop (string soda, EventRaiser raiser) + { + raiser.Raise (Pop, this, soda); + } + + public void OnPopWithBadNumberOfArguments (string soda, EventRaiser raiser) + { + raiser.Raise (Pop, /*this,*/ soda); // wrong number of args to the event + } + + public void OnPopWithNullEvent (string soda, EventRaiser raiser) + { + raiser.Raise (null, this, soda); // no event... + } + } + + internal sealed class OneThirstyDude + { + public void HandlePop (object sender, string soda) + { + _soda = soda; + } + + public void HandlePopWithException (object sender, string soda) + { + _soda = soda; + throw new FormatException (soda); + } + + public static void StaticHandlePop(object sender, string soda) + { + } + + public string Soda + { + get + { + return _soda; + } + } + + private string _soda = string.Empty; + } +} diff --git a/test/Spring/Spring.Core.Tests/Util/NumberUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/NumberUtilsTests.cs new file mode 100644 index 00000000..3ce4c19a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/NumberUtilsTests.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the NumberUtils class. + /// + /// Rick Evans + /// $Id: NumberUtilsTests.cs,v 1.2 2006/04/09 07:24:51 markpollack Exp $ + [TestFixture] + public sealed class NumberUtilsTests + { + [Test] + public void IsInteger() + { + Assert.IsTrue(NumberUtils.IsInteger(10)); + Assert.IsTrue(NumberUtils.IsInteger(10L)); + Assert.IsTrue(NumberUtils.IsInteger((short) 10)); + Assert.IsFalse(NumberUtils.IsInteger('e')); + Assert.IsFalse(NumberUtils.IsInteger(null)); + Assert.IsFalse(NumberUtils.IsInteger(9.5D)); + Assert.IsFalse(NumberUtils.IsInteger(9.5F)); + Assert.IsFalse(NumberUtils.IsInteger(this)); + Assert.IsFalse(NumberUtils.IsInteger(null)); + Assert.IsFalse(NumberUtils.IsInteger(string.Empty)); + } + + [Test] + public void IsDecimal() + { + Assert.IsFalse(NumberUtils.IsDecimal(10)); + Assert.IsFalse(NumberUtils.IsDecimal(10L)); + Assert.IsFalse(NumberUtils.IsDecimal((short) 10)); + Assert.IsFalse(NumberUtils.IsDecimal('e')); + Assert.IsFalse(NumberUtils.IsDecimal(null)); + Assert.IsTrue(NumberUtils.IsDecimal(9.5D)); + Assert.IsTrue(NumberUtils.IsDecimal(9.5F)); + Assert.IsFalse(NumberUtils.IsDecimal(this)); + Assert.IsFalse(NumberUtils.IsDecimal(null)); + Assert.IsFalse(NumberUtils.IsDecimal(string.Empty)); + } + + [Test] + public void IsNumber() + { + Assert.IsTrue(NumberUtils.IsNumber(10)); + Assert.IsTrue(NumberUtils.IsNumber(10L)); + Assert.IsTrue(NumberUtils.IsNumber((short) 10)); + Assert.IsFalse(NumberUtils.IsNumber('e')); + Assert.IsFalse(NumberUtils.IsNumber(null)); + Assert.IsTrue(NumberUtils.IsNumber(9.5D)); + Assert.IsTrue(NumberUtils.IsNumber(9.5F)); + Assert.IsFalse(NumberUtils.IsNumber(this)); + Assert.IsFalse(NumberUtils.IsNumber(null)); + Assert.IsFalse(NumberUtils.IsNumber(string.Empty)); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void NegateNull() + { + NumberUtils.Negate(null); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void NegateString() + { + NumberUtils.Negate(null); + } + + [Test] + public void Negate() + { + Assert.AreEqual(-10, NumberUtils.Negate(10)); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Util/ObjectUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/ObjectUtilsTests.cs new file mode 100644 index 00000000..5c52e08a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/ObjectUtilsTests.cs @@ -0,0 +1,354 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +#if NET_2_0 +using System.Collections.Generic; +#endif +using System.Reflection; +using System.Security.Policy; +using NUnit.Framework; +using Spring.Objects; +using Spring.Util; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the ObjectUtils class. + /// + /// Rick Evans (.NET) + /// $Id: ObjectUtilsTests.cs,v 1.4 2007/08/17 02:43:30 markpollack Exp $ + [TestFixture] + public class ObjectUtilsTests + { + [Test] + public void NullSafeEqualsWithBothNull() + { + string first = null; + string second = null; + Assert.IsTrue(ObjectUtils.NullSafeEquals(first, second)); + } + + [Test] + public void NullSafeEqualsWithFirstNull() + { + string first = null; + string second = ""; + Assert.IsFalse(ObjectUtils.NullSafeEquals(first, second)); + } + + [Test] + public void NullSafeEqualsWithSecondNull() + { + string first = ""; + string second = null; + Assert.IsFalse(ObjectUtils.NullSafeEquals(first, second)); + } + + [Test] + public void NullSafeEqualsBothEquals() + { + string first = "this is it"; + string second = "this is it"; + Assert.IsTrue(ObjectUtils.NullSafeEquals(first, second)); + } + + [Test] + public void NullSafeEqualsNotEqual() + { + string first = "this is it"; + int second = 12; + Assert.IsFalse(ObjectUtils.NullSafeEquals(first, second)); + } + + [Test] + public void IsAssignableAndNotTransparentProxyWithProxy() + { + AppDomain domain = null; + try + { + AppDomainSetup setup = new AppDomainSetup(); + setup.ApplicationBase = Environment.CurrentDirectory; + domain = AppDomain.CreateDomain("Spring", new Evidence(AppDomain.CurrentDomain.Evidence), setup); + object foo = domain.CreateInstanceAndUnwrap(GetType().Assembly.FullName, typeof(Foo).FullName); + // the instance is definitely assignable to the supplied interface type... + bool isAssignable = ObjectUtils.IsAssignableAndNotTransparentProxy(typeof (IFoo), foo); + Assert.IsFalse(isAssignable, "Proxied instance was not recognized as such."); + } + finally + { + AppDomain.Unload(domain); + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void InstantiateTypeWithNullType() + { + ObjectUtils.InstantiateType(null); + } + + [Test] + public void InstantiateType() + { + object foo = ObjectUtils.InstantiateType(typeof (TestObject)); + Assert.IsNotNull(foo, "Failed to instantiate an instance of a valid Type."); + Assert.IsTrue(foo is TestObject, "The instantiated instance was not an instance of the type that was passed in."); + } +#if NET_2_0 + [Test] + [ExpectedException(typeof(FatalReflectionException))] + public void InstantiateTypeWithOpenGenericType() + { + ObjectUtils.InstantiateType(typeof(Dictionary<,>)); + } +#endif + + [Test] + [ExpectedException(typeof(FatalReflectionException))] + public void InstantiateTypeWithAbstractType() + { + ObjectUtils.InstantiateType(typeof (AbstractType)); + } + + [Test] + [ExpectedException(typeof(FatalReflectionException))] + public void InstantiateTypeWithInterfaceType() + { + ObjectUtils.InstantiateType(typeof (IList)); + } + + [Test] + [ExpectedException(typeof(FatalReflectionException))] + public void InstantiateTypeWithTypeExposingNoZeroArgCtor() + { + ObjectUtils.InstantiateType(typeof(NoZeroArgConstructorType)); + } + + [Test] + public void InstantiateTypeWithPrivateCtor() + { + ConstructorInfo ctor = typeof (OnlyPrivateCtor).GetConstructor( + BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] {typeof (string)}, + null); + object foo = ObjectUtils.InstantiateType(ctor, new object[] {"Chungking Express"}); + Assert.IsNotNull(foo, "Failed to instantiate an instance of a valid Type."); + Assert.IsTrue(foo is OnlyPrivateCtor, "The instantiated instance was not an instance of the type that was passed in."); + Assert.AreEqual("Chungking Express", ((OnlyPrivateCtor) foo).Name); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void InstantiateTypeWithNullCtor() + { + ObjectUtils.InstantiateType(typeof(IList).GetConstructor(Type.EmptyTypes), new object[] { }); + } + + [Test] + public void InstantiateTypeWithCtorWithNoArgs() + { + Type type = typeof (TestObject); + ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes); + object foo = ObjectUtils.InstantiateType(ctor, ObjectUtils.EmptyObjects); + Assert.IsNotNull(foo, "Failed to instantiate an instance of a valid Type."); + Assert.IsTrue(foo is TestObject, "The instantiated instance was not an instance of the Type that was passed in."); + } + + [Test] + public void InstantiateTypeWithCtorArgs() + { + Type type = typeof (TestObject); + ConstructorInfo ctor = type.GetConstructor(new Type[] {typeof (string), typeof (int)}); + object foo = ObjectUtils.InstantiateType(ctor, new object[] {"Yakov Petrovich Golyadkin", 39}); + Assert.IsNotNull(foo, "Failed to instantiate an instance of a valid Type."); + Assert.IsTrue(foo is TestObject, "The instantiated instance was not an instance of the Type that was passed in."); + TestObject obj = foo as TestObject; + Assert.AreEqual("Yakov Petrovich Golyadkin", obj.Name); + Assert.AreEqual(39, obj.Age); + } + + [Test] + public void InstantiateTypeWithBadCtorArgs() + { + Type type = typeof (TestObject); + ConstructorInfo ctor = type.GetConstructor(new Type[] {typeof(string), typeof(int)}); + try + { + ObjectUtils.InstantiateType(ctor, new object[] { 39, "Yakov Petrovich Golyadkin" }); + Assert.Fail("Should throw an error"); + } + catch + { + // ok... + } + } + + [Test] + public void IsSimpleProperty() + { + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (string))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (long))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (bool))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (int))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (float))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (ushort))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (double))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (ulong))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (char))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (uint))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (string[]))); + Assert.IsTrue(ObjectUtils.IsSimpleProperty(typeof (Type))); + + Assert.IsFalse(ObjectUtils.IsSimpleProperty(typeof (TestObject))); + Assert.IsFalse(ObjectUtils.IsSimpleProperty(typeof (IList[]))); + } + + [Test] + public void EnumerateFirstElement() + { + string expected = "Hiya"; + IList list = new string[] {expected, "Aw!", "Man!"}; + IEnumerator enumerator = list.GetEnumerator(); + object actual = ObjectUtils.EnumerateFirstElement(enumerator); + Assert.AreEqual(expected, actual); + } + + [Test] + public void EnumerateElementAtIndex() + { + string expected = "Mmm..."; + IList list = new string[] {"Aw!", "Man!", expected}; + IEnumerator enumerator = list.GetEnumerator(); + object actual = ObjectUtils.EnumerateElementAtIndex(enumerator, 2); + Assert.AreEqual(expected, actual); + } + + [Test] + public void EnumerateElementAtIndexViaIEnumerable() + { + string expected = "Mmm..."; + IList list = new string[] {"Aw!", "Man!", expected}; + object actual = ObjectUtils.EnumerateElementAtIndex(list, 2); + Assert.AreEqual(expected, actual); + } + + [Test] + [ExpectedException(typeof (ArgumentOutOfRangeException))] + public void EnumerateElementAtOutOfRangeIndex() + { + string expected = "Mmm..."; + IList list = new string[] {"Aw!", "Man!", expected}; + IEnumerator enumerator = list.GetEnumerator(); + object actual = ObjectUtils.EnumerateElementAtIndex(enumerator, 12); + Assert.AreEqual(expected, actual); + } + + [Test] + [ExpectedException(typeof (ArgumentOutOfRangeException))] + public void EnumerateElementAtOutOfRangeIndexViaIEnumerable() + { + string expected = "Mmm..."; + IList list = new string[] {"Aw!", "Man!", expected}; + object actual = ObjectUtils.EnumerateElementAtIndex(list, 12); + Assert.AreEqual(expected, actual); + } + + [Test] + [ExpectedException(typeof (ArgumentOutOfRangeException))] + public void EnumerateElementAtNegativeIndex() + { + string expected = "Mmm..."; + IList list = new string[] {"Aw!", "Man!", expected}; + IEnumerator enumerator = list.GetEnumerator(); + object actual = ObjectUtils.EnumerateElementAtIndex(enumerator, -10); + Assert.AreEqual(expected, actual); + } + + [Test] + [ExpectedException(typeof (ArgumentOutOfRangeException))] + public void EnumerateElementAtNegativeIndexViaIEnumerable() + { + string expected = "Mmm..."; + IList list = new string[] {"Aw!", "Man!", expected}; + object actual = ObjectUtils.EnumerateElementAtIndex(list, -10); + Assert.AreEqual(expected, actual); + } + + #region Helper Classes + + private interface IFoo + { + } + + private sealed class Foo : MarshalByRefObject, IFoo + { + } + + /// + /// A class that doesn't have a parameterless constructor. + /// + private class NoZeroArgConstructorType + { + /// + /// Creates a new instance of the NoZeroArgConstructorType class. + /// + /// A spurious argument (ignored). + public NoZeroArgConstructorType(string foo) + { + } + } + + /// + /// An abstract class. Doh! + /// + private abstract class AbstractType + { + /// + /// Creates a new instance of the AbstractType class. + /// + public AbstractType() + { + } + } + + private class OnlyPrivateCtor + { + private OnlyPrivateCtor(string name) + { + _name = name; + } + + public string Name + { + get { return _name; } + set { _name = value; } + } + + private string _name; + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Util/PathMatcherTest.cs b/test/Spring/Spring.Core.Tests/Util/PathMatcherTest.cs new file mode 100644 index 00000000..cdac14b0 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/PathMatcherTest.cs @@ -0,0 +1,109 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System; +using System.IO; +using NUnit.Framework; + +namespace Spring.Util +{ + [TestFixture] + public class PathMatcherTest + { + string dir = "PathMatcher"; + + [Test] + public void TestFilesInDataPathMatcher () + { + DirectoryInfo info = new DirectoryInfo(dir); + foreach (FileInfo file in info.GetFiles("*.test")) + { + ProcessFile (file); + } + + } + + [Test, Explicit] + public void ThisIsATestForDebuggingPurposes () + { + ProcessFile(new FileInfo(dir + "/Examples.test")); + } + + private static void ProcessFile (FileInfo file) + { + bool trueness = true; + using (StreamReader r = file.OpenText()) + { + string pattern = null; + string line; + int nline = 0; + + bool expectingPattern = false; + while ((line = r.ReadLine()) != null) + { + nline++; + switch (line) + { + case "": + if (expectingPattern) + { + pattern = line; + expectingPattern = false; + continue; + } + break; + case "--match--": + case "# ": + trueness = true; + break; + case "--dont-match--": + case "# ": + trueness = false; + break; + case "--pattern--": + case "# ": + expectingPattern = true; + nline++; + break; + default: + if (line.StartsWith("#")) + { + continue; + } + if (expectingPattern) + { + pattern = line; + expectingPattern = false; + continue; + } + AssertMatches(file, nline, trueness, line, pattern); + AssertMatches(file, nline, trueness, line.Replace("/", "\\"), pattern); + break; + } + } + } + } + + private static void AssertMatches (FileInfo file, int nline, bool trueness, string path, string pattern) + { + Assert.AreEqual(trueness, + PathMatcher.Match(pattern, path), + String.Format("\ntest file '{0}', line {1}:\npath '{2}' does {3} match '{4}' (translated to {5})", + file.Name, nline, path, trueness ? "not" : "", pattern, PathMatcher.BuildRegex(pattern))) ; + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Util/PatternMatchUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/PatternMatchUtilsTests.cs new file mode 100644 index 00000000..49f474ee --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/PatternMatchUtilsTests.cs @@ -0,0 +1,37 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the PatternMatchUtils class. + /// + /// $Id: PatternMatchUtilsTests.cs,v 1.2 2007/07/31 18:19:59 bbaia Exp $ + [TestFixture] + public sealed class PatternMatchUtilsTests + { + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Util/PropertiesTests.cs b/test/Spring/Spring.Core.Tests/Util/PropertiesTests.cs new file mode 100644 index 00000000..db78ba66 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/PropertiesTests.cs @@ -0,0 +1,188 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.IO; +using System.Text; + +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for Spring.Util.Properties. + /// + [TestFixture] + public class PropertiesTests + { + [Test] + public void Instantiation () + { + Properties root = new Properties(); + root.Add ("foo", "this"); + root.Add ("bar", "is"); + Properties props = new Properties(root); + props.SetProperty("myPropertyKey", "myPropertyValue"); + Assert.AreEqual (3, props.Count); + Assert.AreEqual ("this", props.GetProperty ("foo")); + Assert.AreEqual ("is", props.GetProperty ("bar")); + Assert.AreEqual ("myPropertyValue", props.GetProperty ("myPropertyKey")); + } + + [Test] + public void GetPropertyWithDefault () + { + Properties props = new Properties(); + props.Add ("foo", "this"); + props.Add ("bar", "is"); + Assert.AreEqual ("this", props.GetProperty ("foo")); + Assert.AreEqual ("is", props.GetProperty ("bar")); + Assert.AreEqual ("it", props.GetProperty ("baz", "it")); + } + + [Test] + public void Remove () + { + Properties props = new Properties(); + props.Add ("foo", "this"); + props.Add ("bar", "is"); + Assert.AreEqual (2, props.Count); + props.Remove ("foo"); + Assert.AreEqual (1, props.Count); + Assert.IsFalse (props.ContainsKey ("foo")); + } + + [Test] + public void Store () + { + Properties props = new Properties(); + props.Add ("foo", "this"); + props.Add ("bar", "is"); + props.Add ("baz", "it"); + FileInfo file = new FileInfo ("properties.test"); + try + { + // write 'em out with the specified header... + using (Stream cout = file.OpenWrite ()) + { + props.Store (cout, "My Properties"); + } + } + finally + { + try + { + file.Delete (); + } + catch (IOException) + { + } + } + } + + [Test] + public void ListAndLoad () + { + Properties props = new Properties(); + props.Add ("foo", "this"); + props.Add ("bar", "is"); + props.Add ("baz", "it"); + FileInfo file = new FileInfo ("properties.test"); + try + { + // write 'em out... + using (Stream cout = file.OpenWrite ()) + { + props.List (cout); + } + // read 'em back in... + using (Stream cin = file.OpenRead ()) + { + props = new Properties (); + props.Load (cin); + Assert.AreEqual (3, props.Count); + Assert.AreEqual ("this", props.GetProperty ("foo")); + Assert.AreEqual ("is", props.GetProperty ("bar")); + Assert.AreEqual ("it", props.GetProperty ("baz", "it")); + } + } + finally + { + try + { + file.Delete (); + } + catch (IOException) + { + } + } + } + + [Test] + public void SimpleProperties() + { + string input = "key1=value1\r\nkey2:value2\r\n\r\n# a comment line\r\n leadingspace : true"; + Stream s = new MemoryStream(Encoding.ASCII.GetBytes(input)); + Properties props = new Properties(); + props.Load(s); + + Assert.IsTrue("value1".Equals(props["key1"])); + Assert.IsTrue("value2".Equals(props["key2"])); + Assert.IsTrue("true".Equals(props["leadingspace"])); + } + + [Test] + public void Continuation() + { + string input = "continued = this is a long value element \\\r\nthat uses continuation \\\r\n xxx"; + Stream s = new MemoryStream(Encoding.ASCII.GetBytes(input)); + Properties props = new Properties(); + props.Load(s); + + Assert.IsTrue("this is a long value element that uses continuation xxx".Equals(props["continued"])); + } + + [Test] + public void SeperatorEscapedWithinKey() + { + string input = "\\" + ":key:newvalue"; + Stream s = new MemoryStream(Encoding.ASCII.GetBytes(input)); + Properties props = new Properties(); + props.Load(s); + + Assert.IsTrue("newvalue".Equals(props[":key"])); + } + + [Test] + public void EscapedCharactersInValue() + { + string input = "escaped=test\\ttest"; + Stream s = new MemoryStream(Encoding.ASCII.GetBytes(input)); + Properties props = new Properties(); + props.Load(s); + + Assert.IsTrue("test\ttest".Equals(props["escaped"])); + } + + } +} diff --git a/test/Spring/Spring.Core.Tests/Util/ReflectionUtilsMemberwiseCopyTests.cs b/test/Spring/Spring.Core.Tests/Util/ReflectionUtilsMemberwiseCopyTests.cs new file mode 100644 index 00000000..ca7eb606 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/ReflectionUtilsMemberwiseCopyTests.cs @@ -0,0 +1,122 @@ +using System; +using NUnit.Framework; + +namespace Spring.Util +{ + /// + /// Summary description for TestMemberwiseCopy. + /// + [TestFixture] + public class ReflectionUtilsMemberwiseCopyTests + { + [Test] + public void TestSameType() + { + SampleBaseClass i1 = new SampleDerivedClass("1st config val"); + SampleBaseClass i2 = new SampleDerivedClass("2nd config val"); + + ReflectionUtils.MemberwiseCopy(i1, i2); + + Assert.AreEqual(i1, i2); + } + + [Test] + [ExpectedException(typeof (ArgumentException), "object types are not related")] + public void DifferentTypesForbidden() + { + ReflectionUtils.MemberwiseCopy("test", 2); + } + + [Test] + public void TestDerivedTypeAllowed() + { + SampleBaseClass i1 = new SampleDerivedClass("1st config val"); + SampleBaseClass i2 = new SampleFurtherDerivedClass("2nd config val"); + + ReflectionUtils.MemberwiseCopy(i1, i2); + + Assert.AreEqual(i1, i2); + } + + [Test] + public void TestBaseTypeAllowed() + { + SampleBaseClass i1 = new SampleDerivedClass("1st config val"); + SampleBaseClass i2 = new SampleFurtherDerivedClass("2nd config val"); + + ReflectionUtils.MemberwiseCopy(i2, i1); + + Assert.AreEqual(i2, i1); + } + } + + #region Test Support Classes + + public class SampleBaseClass + { + private const string MyConstant = "SampleBaseClass.MyConstant"; + private readonly string _someReadOnlyVal = "SampleBaseClass.SomeReadOnlyVal"; + protected readonly string _someProtectedReadOnlyVal = "SampleBaseClass.SomeProtectedReadOnlyVal"; + private string _someConfigVal = "SampleBaseClass.SomeConfigVal"; + + public SampleBaseClass(string someConfigVal) + { + if (someConfigVal == null) throw new ArgumentNullException("someConfigVal must not be null"); + _someConfigVal = someConfigVal; + _someProtectedReadOnlyVal = "Protected" + someConfigVal; + } + + public override int GetHashCode() + { + int result = _someReadOnlyVal != null ? _someReadOnlyVal.GetHashCode() : 0; + result = 29*result + (_someProtectedReadOnlyVal != null ? _someProtectedReadOnlyVal.GetHashCode() : 0); + result = 29*result + (_someConfigVal != null ? _someConfigVal.GetHashCode() : 0); + return result; + } + + public override bool Equals(object obj) + { + if (this == obj) return true; + SampleBaseClass sampleBaseClass = obj as SampleBaseClass; + if (sampleBaseClass == null) return false; + if (!Equals(_someReadOnlyVal, sampleBaseClass._someReadOnlyVal)) return false; + if (!Equals(_someProtectedReadOnlyVal, sampleBaseClass._someProtectedReadOnlyVal)) return false; + if (!Equals(_someConfigVal, sampleBaseClass._someConfigVal)) return false; + return true; + } + } + + public class SampleDerivedClass : SampleBaseClass + { + private string _someConfigVal = "SampleDerivedClass.SomeConfigVal"; + + public SampleDerivedClass(string someConfigVal) : base(someConfigVal) + { + _someConfigVal = "SampleDerivedClass." + someConfigVal; + } + + public override int GetHashCode() + { + return base.GetHashCode() + 29*(_someConfigVal != null ? _someConfigVal.GetHashCode() : 0); + } + + public override bool Equals(object obj) + { + if (this == obj) return true; + SampleDerivedClass sampleDerivedClass = obj as SampleDerivedClass; + if (sampleDerivedClass == null) return false; + if (!base.Equals(obj)) return false; + if (!Equals(_someConfigVal, sampleDerivedClass._someConfigVal)) return false; + return true; + } + } + + public class SampleFurtherDerivedClass : SampleDerivedClass + { + public SampleFurtherDerivedClass(string someConfigVal) : base(someConfigVal) + { + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Util/ReflectionUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/ReflectionUtilsTests.cs new file mode 100644 index 00000000..62a05448 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/ReflectionUtilsTests.cs @@ -0,0 +1,1133 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Threading; +using System.Runtime.CompilerServices; +using System.Windows.Forms; +using NUnit.Framework; + +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Attributes; + +#endregion + +#if NET_2_0 +[assembly: InternalsVisibleTo("ReflectionUtils.IsTypeVisible.AssemblyTestName, PublicKey=001200000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +#endif + +namespace Spring.Util +{ + /// + /// Unit tests for the ReflectionUtils class. + /// + /// $Id: ReflectionUtilsTests.cs,v 1.22 2008/04/02 16:21:49 markpollack Exp $ + [TestFixture] + public sealed class ReflectionUtilsTests + { + [Test] + public void GetParameterTypes() + { + Type[] expectedParameterTypes = new Type[] { typeof(string) }; + MethodInfo method = typeof(ReflectionUtilsObject).GetMethod("BadSpanglish"); + Type[] parameterTypes = ReflectionUtils.GetParameterTypes(method); + Assert.IsTrue(ArrayUtils.AreEqual(expectedParameterTypes, parameterTypes)); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void GetParameterTypesWithNullMethodInfo() + { + ReflectionUtils.GetParameterTypes((MethodInfo) null); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void GetParameterTypesWithNullParametersArgs() + { + ReflectionUtils.GetParameterTypes((ParameterInfo[]) null); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void GetMatchingMethodsWithNullTypeToFindOn() + { + ReflectionUtils.GetMatchingMethods(null, new MethodInfo[] {}, true); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void GetMatchingMethodsWithNullMethodsToFind() + { + ReflectionUtils.GetMatchingMethods(GetType(), null, true); + } + + [Test] + public void GetMatchingMethodsWithPerfectMatch() + { + MethodInfo[] clonesMethods = typeof(ReflectionUtilsObjectClone).GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance); + MethodInfo[] foundMethods = ReflectionUtils.GetMatchingMethods(typeof(ReflectionUtilsObject), clonesMethods, true); + Assert.AreEqual(clonesMethods.Length, foundMethods.Length); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void GetMatchingMethodsWithBadMatchStrict() + { + // lets include a protected method that ain't declared on the ReflectionUtilsObject class... + MethodInfo[] clonesMethods = typeof(ReflectionUtilsObjectClone).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance); + ReflectionUtils.GetMatchingMethods(typeof(ReflectionUtilsObject), clonesMethods, true); + } + + [Test] + public void GetMatchingMethodsWithBadMatchNotStrict() + { + // lets include a protected method that ain't declared on the ReflectionUtilsObject class... + MethodInfo[] clonesMethods = typeof(ReflectionUtilsObjectClone).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance); + MethodInfo[] foundMethods = ReflectionUtils.GetMatchingMethods(typeof(ReflectionUtilsObject), clonesMethods, false); + // obviously is not strict, 'cos we got here without throwing an exception... + Assert.AreEqual(clonesMethods.Length, foundMethods.Length); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void GetMatchingMethodsWithBadReturnTypeMatchStrict() + { + // lets include a method that return type is different... + MethodInfo[] clonesMethods = typeof(ReflectionUtilsObjectBadClone).GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance); + ReflectionUtils.GetMatchingMethods(typeof(ReflectionUtilsObject), clonesMethods, true); + } + + [Test] + public void GetMatchingMethodsWithBadReturnTypeMatchNotStrict() + { + // lets include a method that return type is different... + MethodInfo[] clonesMethods = typeof(ReflectionUtilsObjectBadClone).GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance); + MethodInfo[] foundMethods = ReflectionUtils.GetMatchingMethods(typeof(ReflectionUtilsObject), clonesMethods, false); + // obviously is not strict, 'cos we got here without throwing an exception... + Assert.AreEqual(clonesMethods.Length, foundMethods.Length); + } + + [Test] + public void ParameterTypesMatch() + { + MethodInfo method = typeof (ReflectionUtilsObject).GetMethod("Spanglish"); + Type[] types = new Type[] {typeof (string), typeof (object[])}; + Assert.IsTrue(ReflectionUtils.ParameterTypesMatch(method, types)); + } + + [Test] + public void ParameterTypesDontMatchWithNonMatchingArgs() + { + Type[] types = new Type[] {typeof (string), typeof (object[])}; + + MethodInfo method = typeof (ReflectionUtilsObject).GetMethod("BadSpanglish"); + Assert.IsFalse(ReflectionUtils.ParameterTypesMatch(method, types)); + method = typeof (ReflectionUtilsObject).GetMethod("WickedSpanglish"); + Assert.IsFalse(ReflectionUtils.ParameterTypesMatch(method, types)); + } + + [Test] + public void GetDefaultValue() + { + Assert.IsNull(ReflectionUtils.GetDefaultValue(GetType())); + Assert.AreEqual(Cuts.Superficial, ReflectionUtils.GetDefaultValue(typeof (Cuts))); + Assert.AreEqual(false, ReflectionUtils.GetDefaultValue(typeof (bool))); + Assert.AreEqual(DateTime.MinValue, ReflectionUtils.GetDefaultValue(typeof (DateTime))); + Assert.AreEqual(Char.MinValue, ReflectionUtils.GetDefaultValue(typeof (char))); + Assert.AreEqual(0, ReflectionUtils.GetDefaultValue(typeof (long))); + Assert.AreEqual(0, ReflectionUtils.GetDefaultValue(typeof (int))); + Assert.AreEqual(0, ReflectionUtils.GetDefaultValue(typeof (short))); + } + + [Test] + public void PropertyIsIndexer() + { + Assert.IsTrue(ReflectionUtils.PropertyIsIndexer("Item", typeof(TestObject))); + Assert.IsFalse(ReflectionUtils.PropertyIsIndexer("Item", typeof(SideEffectObject))); + Assert.IsFalse(ReflectionUtils.PropertyIsIndexer("Count", typeof(SideEffectObject))); + Assert.IsTrue(ReflectionUtils.PropertyIsIndexer("MyItem", typeof(ObjectWithNonDefaultIndexerName))); + } + + [Test] + public void MethodIsOnOneOfTheseInterfaces() + { + MethodInfo method = typeof (ReflectionUtilsObject).GetMethod("Spanglish"); + Assert.IsTrue(ReflectionUtils.MethodIsOnOneOfTheseInterfaces(method, new Type[] {typeof (IFoo)})); + } + + [Test] + [ExpectedException(typeof (ArgumentException))] + public void MethodIsOnOneOfTheseInterfacesWithNonInterfaceType() + { + MethodInfo method = typeof (ReflectionUtilsObject).GetMethod("Spanglish"); + Assert.IsFalse(ReflectionUtils.MethodIsOnOneOfTheseInterfaces(method, new Type[] {GetType()})); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void MethodIsOnOneOfTheseInterfacesWithNullMethod() + { + MethodInfo method = null; + Assert.IsFalse(ReflectionUtils.MethodIsOnOneOfTheseInterfaces(method, new Type[] {GetType()})); + } + + [Test] + public void MethodIsOnOneOfTheseInterfacesWithNullTypes() + { + MethodInfo method = typeof (ReflectionUtilsObject).GetMethod("Spanglish"); + Assert.IsFalse(ReflectionUtils.MethodIsOnOneOfTheseInterfaces(method, null)); + } + + [Test] + public void MethodIsOnOneOfTheseInterfacesMultiple() + { + MethodInfo method = typeof(RequiredTestObject).GetMethod("set_ObjectFactory"); + Assert.IsNotNull(method, "Could not get setter property for ObjectFactory"); + Assert.IsTrue(ReflectionUtils.MethodIsOnOneOfTheseInterfaces(method, new Type[] { typeof (IObjectNameAware), typeof(IObjectFactoryAware)})); + } + + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void ParameterTypesMatchWithNullArgs() + { + ReflectionUtils.ParameterTypesMatch(null, null); + } + + [Test] + public void GetSignature() + { + MethodInfo method = typeof (ReflectionUtilsObject).GetMethod("Spanglish"); + ArrayList list = new ArrayList(); + foreach (ParameterInfo p in method.GetParameters()) + { + list.Add(p.ParameterType); + } + string expected = "Spring.Util.ReflectionUtilsObject::Spanglish(System.String,System.Object[])"; + Type[] pTypes = (Type[]) list.ToArray(typeof (Type)); + string actual = ReflectionUtils.GetSignature(method.DeclaringType, method.Name, pTypes); + Assert.AreEqual(expected, actual); + } + + [Test] + public void ToInterfaceArrayFromType() + { + Type[] expected = new Type[] { typeof(IFoo), typeof(IBar) }; + Type[] actual = ReflectionUtils.ToInterfaceArray(typeof(IBar)); + Assert.AreEqual(expected.Length, actual.Length); + Assert.AreEqual(expected[0], actual[0]); + Assert.AreEqual(expected[1], actual[1]); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ToInterfaceArrayFromTypeWithNonInterface() + { + ReflectionUtils.ToInterfaceArray(typeof(ExplicitFoo)); + } + + + [Test] + public void GetMethod() + { + MethodInfo actual = ReflectionUtils.GetMethod( + typeof (ReflectionUtilsObject), + "Spanglish", + new Type[] {typeof (string), typeof (object[])}); + Assert.IsNotNull(actual); + } + + [Test] + public void GetMethodWithExplicitInterfaceMethod() + { + MethodInfo actual = ReflectionUtils.GetMethod( + typeof (ExplicitFoo), + "Spring.Util.IFoo.Spanglish", + new Type[] {typeof (string), typeof (object[])}); + Assert.IsNotNull(actual); + } + + [Test] + public void GetMethodIsCaseInsensitive() + { + MethodInfo actual = ReflectionUtils.GetMethod( + typeof (ReflectionUtilsObject), + "spAngLISh", + new Type[] {typeof (string), typeof (object[])}); + Assert.IsNotNull(actual, "ReflectionUtils.GetMethod would appear to be case sensitive."); + } + + [Test] + [ExpectedException(typeof (ArgumentException), + "[Spring.Util.ReflectionUtilsTests] does not derive from the [System.Attribute] class.")] + public void CreateCustomAttributeForNonAttributeType() + { + ReflectionUtils.CreateCustomAttribute(GetType()); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void CreateCustomAttributeWithNullType() + { + ReflectionUtils.CreateCustomAttribute((Type) null); + } + + [Test] + public void CreateCustomAttributeSunnyDayScenarios() + { + CustomAttributeBuilder builder = null; + + builder = ReflectionUtils.CreateCustomAttribute(typeof(MyCustomAttribute)); + Assert.IsNotNull(builder); + + builder = ReflectionUtils.CreateCustomAttribute(typeof(MyCustomAttribute), "Rick"); + Assert.IsNotNull(builder); + + builder = ReflectionUtils.CreateCustomAttribute(typeof(MyCustomAttribute), "Rick"); + Assert.IsNotNull(builder); + + builder = ReflectionUtils.CreateCustomAttribute(new MyCustomAttribute("Rick")); + Assert.IsNotNull(builder); + + builder = ReflectionUtils.CreateCustomAttribute(typeof(MyCustomAttribute), new MyCustomAttribute("Rick")); + Assert.IsNotNull(builder); + + builder = ReflectionUtils.CreateCustomAttribute(typeof(MyCustomAttribute), new object[] {"Rick"}, new MyCustomAttribute("Evans")); + Assert.IsNotNull(builder); + + // TODO : actually emit the attribute and check it... + } +#if NET_2_0 + [Test] + public void CreatCustomAttriubtesFromCustomAttributeData() + { + Type control = typeof (Control); + MethodInfo mi = control.GetMethod("get_Font"); + System.Collections.Generic.IList attributes = CustomAttributeData.GetCustomAttributes(mi.ReturnParameter); + CustomAttributeBuilder builder = null; + foreach (CustomAttributeData customAttributeData in attributes) + { + builder = ReflectionUtils.CreateCustomAttribute(customAttributeData); + Assert.IsNotNull(builder); + } + + } +#endif + [Test] + public void CreateCustomAttributeUsingDefaultValuesForTheConstructor() + { + CustomAttributeBuilder builder = null; + builder = ReflectionUtils.CreateCustomAttribute(typeof(AnotherCustomAttribute)); + Assert.IsNotNull(builder); + + AnotherCustomAttribute att + = (AnotherCustomAttribute) CheckForPresenceOfCustomAttribute(builder, typeof(AnotherCustomAttribute)); + Assert.IsNull(att.Name); + Assert.AreEqual(0, att.Age); + Assert.IsFalse(att.HasSwallowedExplosives); + } + + [Test] + public void CreateCustomAttributeFromSourceAttribute() + { + CustomAttributeBuilder builder = null; + AnotherCustomAttribute source = new AnotherCustomAttribute("Rick", 30, true); + builder = ReflectionUtils.CreateCustomAttribute(source); + Assert.IsNotNull(builder); + + AnotherCustomAttribute att + = (AnotherCustomAttribute) CheckForPresenceOfCustomAttribute(builder, typeof(AnotherCustomAttribute)); + Assert.AreEqual(source.Name, att.Name); + Assert.AreEqual(source.Age, att.Age); + Assert.AreEqual(source.HasSwallowedExplosives, att.HasSwallowedExplosives); + } + + [Test] + public void CreateCustomAttributeUsingExplicitValuesForTheConstructor() + { + CustomAttributeBuilder builder = null; + const string expectedName = "Rick"; + const int expectedAge = 30; + builder = ReflectionUtils.CreateCustomAttribute(typeof (AnotherCustomAttribute), new object[] + { + expectedName, expectedAge, true + }); + Assert.IsNotNull(builder); + + AnotherCustomAttribute att + = (AnotherCustomAttribute) CheckForPresenceOfCustomAttribute(builder, typeof (AnotherCustomAttribute)); + Assert.AreEqual(expectedName, att.Name); + Assert.AreEqual(expectedAge, att.Age); + Assert.IsTrue(att.HasSwallowedExplosives); + } + + [Test] + public void CreateCustomAttributeUsingExplicitValuesForTheConstructorAndASourceAttribute() + { + CustomAttributeBuilder builder = null; + const string expectedName = "Rick"; + const int expectedAge = 30; + AnotherCustomAttribute source = new AnotherCustomAttribute(expectedName, expectedAge, false); + builder = ReflectionUtils.CreateCustomAttribute(typeof (AnotherCustomAttribute), new object[] + { + "Hoop", 2, true + }, source); + Assert.IsNotNull(builder); + + AnotherCustomAttribute att + = (AnotherCustomAttribute) CheckForPresenceOfCustomAttribute(builder, typeof (AnotherCustomAttribute)); + Assert.AreEqual(expectedName, att.Name); + Assert.AreEqual(expectedAge, att.Age); + Assert.IsFalse(att.HasSwallowedExplosives); + } + + [Test] + public void HasAtLeastOneMethodWithName() + { + Type testType = typeof (ExtendedReflectionUtilsObject); + // declared method... + Assert.IsTrue(ReflectionUtils.HasAtLeastOneMethodWithName(testType, "Declared")); + // case insensitive method... + Assert.IsTrue(ReflectionUtils.HasAtLeastOneMethodWithName(testType, "deCLAReD")); + // superclass method... + Assert.IsTrue(ReflectionUtils.HasAtLeastOneMethodWithName(testType, "Spanglish")); + // static method... + Assert.IsTrue(ReflectionUtils.HasAtLeastOneMethodWithName(testType, "Static")); + // protected method... + Assert.IsTrue(ReflectionUtils.HasAtLeastOneMethodWithName(testType, "Protected")); + // non existent method... + Assert.IsFalse(ReflectionUtils.HasAtLeastOneMethodWithName(testType, "Sponglish")); + // null type... + Assert.IsFalse(ReflectionUtils.HasAtLeastOneMethodWithName(null, "Spanglish")); + // null method name... + Assert.IsFalse(ReflectionUtils.HasAtLeastOneMethodWithName(testType, null)); + // empty method name... + Assert.IsFalse(ReflectionUtils.HasAtLeastOneMethodWithName(testType, "")); + // all nulls... + Assert.IsFalse(ReflectionUtils.HasAtLeastOneMethodWithName(null, null)); + } + + [Test] + [ExpectedException(typeof(NullReferenceException))] + public void GetTypeOfOrTypeWithNull() + { + ReflectionUtils.TypeOfOrType(null); + } + + [Test] + public void GetTypeOfOrType() + { + Assert.AreEqual(typeof(string), ReflectionUtils.TypeOfOrType(typeof(string))); + } + + [Test] + public void GetTypeOfOrTypeWithNonNullType() + { + Assert.AreEqual(typeof(string), ReflectionUtils.TypeOfOrType(string.Empty)); + } + + [Test] + public void GetTypes() + { + Type[] actual = ReflectionUtils.GetTypes( + new object[] {1, "I've never been to Taco Bell (sighs).", new ReflectionUtilsObject()}); + Type[] expected = new Type[] + { + typeof (int), + typeof (string), + typeof (ReflectionUtilsObject), + }; + Assert.IsTrue(ArrayUtils.AreEqual(expected, actual), "The ReflectionUtils.GetTypes method did not return a correct Type [] array. Yep, that's as helpful as it gets."); + } + + [Test] + public void GetTypesWithEmptyArrayArgument() + { + Type[] actual = ReflectionUtils.GetTypes(new object[] {}); + Assert.IsNotNull(actual, "The ReflectionUtils.GetTypes method returned null when given an empty array. Must return an empty Type [] array."); + Assert.IsTrue(actual.Length == 0, "The ReflectionUtils.GetTypes method returned a Type [] that had some elements in it when given an empty array. Must return an empty Type [] array."); + } + + [Test] + public void GetTypesWithNullArrayArgument() + { + Type[] actual = ReflectionUtils.GetTypes(null); + Assert.IsNotNull(actual, "The ReflectionUtils.GetTypes method returned null when given null. Must return an empty Type [] array."); + Assert.IsTrue(actual.Length == 0, "The ReflectionUtils.GetTypes method returned a Type [] that had some elements in it when given null. Must return an empty Type [] array."); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void GetDefaultValueWithZeroValueEnumType() + { + ReflectionUtils.GetDefaultValue(typeof(EnumWithNoValues)); + } + + [Test] + public void GetParameterTypesWithMethodThatHasRefParameters() + { + MethodInfo method = GetType().GetMethod("Add"); + Type[] types = ReflectionUtils.GetParameterTypes(method); + Assert.IsNotNull(types); + Assert.AreEqual(2, types.Length); + // first method parameter is byRef, so type name must end in '&' + Assert.AreEqual("System.Int32&", types[0].FullName); + Assert.AreEqual("System.Int32", types[1].FullName); + } + + [Test] + public void GetMethodByArgumentValuesResolvesToExactMatchIfAvailable() + { + GetMethodByArgumentValuesTarget.DummyArgumentType[] typedArg = new GetMethodByArgumentValuesTarget.DummyArgumentType[] {}; + GetMethodByArgumentValuesTarget foo = new GetMethodByArgumentValuesTarget(1, typedArg ); + + Type type = typeof(GetMethodByArgumentValuesTarget); + MethodInfo[] candidateMethods = new MethodInfo[] + { + type.GetMethod("MethodWithSimilarArguments", new Type[] {typeof(int), typeof(ICollection)}) + , type.GetMethod("MethodWithSimilarArguments", new Type[] {typeof(int), typeof(GetMethodByArgumentValuesTarget.DummyArgumentType[])}) + , type.GetMethod("MethodWithSimilarArguments", new Type[] {typeof(int), typeof(object[])}) + }; + + // ensure noone changed our test class + Assert.IsNotNull(candidateMethods[0]); + Assert.IsNotNull(candidateMethods[1]); + Assert.IsNotNull(candidateMethods[2]); + Assert.AreEqual("ParamArrayMatch", foo.MethodWithSimilarArguments(1, new object())); + Assert.AreEqual("ExactMatch", foo.MethodWithSimilarArguments(1, (GetMethodByArgumentValuesTarget.DummyArgumentType[])typedArg)); + Assert.AreEqual("AssignableMatch", foo.MethodWithSimilarArguments(1, (ICollection)typedArg)); + + MethodInfo resolvedMethod = ReflectionUtils.GetMethodByArgumentValues(candidateMethods, new object[] { 1, typedArg }); + Assert.AreSame(candidateMethods[1], resolvedMethod); + } + + [Test] + public void GetConstructorByArgumentValuesResolvesToExactMatchIfAvailable() + { + GetMethodByArgumentValuesTarget.DummyArgumentType[] typedArg = new GetMethodByArgumentValuesTarget.DummyArgumentType[] { }; + Type type = typeof(GetMethodByArgumentValuesTarget); + ConstructorInfo[] candidateConstructors = new ConstructorInfo[] + { + type.GetConstructor(new Type[] {typeof(int), typeof(ICollection)}) + , type.GetConstructor(new Type[] {typeof(int), typeof(GetMethodByArgumentValuesTarget.DummyArgumentType[])}) + , type.GetConstructor(new Type[] {typeof(int), typeof(object[])}) + }; + + // ensure noone changed our test class + Assert.IsNotNull(candidateConstructors[0]); + Assert.IsNotNull(candidateConstructors[1]); + Assert.IsNotNull(candidateConstructors[2]); + Assert.AreEqual("ParamArrayMatch", new GetMethodByArgumentValuesTarget(1, new object()).SelectedConstructor); + Assert.AreEqual("ExactMatch", new GetMethodByArgumentValuesTarget(1, (GetMethodByArgumentValuesTarget.DummyArgumentType[])typedArg).SelectedConstructor); + Assert.AreEqual("AssignableMatch", new GetMethodByArgumentValuesTarget(1, (ICollection)typedArg).SelectedConstructor); + + ConstructorInfo resolvedConstructor = ReflectionUtils.GetConstructorByArgumentValues(candidateConstructors, new object[] { 1, typedArg }); + Assert.AreSame(candidateConstructors[1], resolvedConstructor); + } + + #region GetInterfaces + + [Test] + public void GetInterfaces() + { + Assert.AreEqual( + typeof(TestObject).GetInterfaces().Length, + ReflectionUtils.GetInterfaces(typeof(TestObject)).Length); + + Assert.AreEqual(1, ReflectionUtils.GetInterfaces(typeof(IInterface1)).Length); + Assert.AreEqual(4, ReflectionUtils.GetInterfaces(typeof(IInterface2)).Length); + } + + public interface IInterface1 + { + } + + public interface IInterface2 : IInterface3 + { + } + + public interface IInterface3 : IInterface4, IInterface5 + { + } + + public interface IInterface4 + { + } + + public interface IInterface5 + { + } + + #endregion + + #region IsTypeVisible Tests + + [Test] + public void IsTypeVisibleWithInternalType() + { + Type type = typeof(InternalType); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithPublicNestedTypeOnInternalType() + { + Type type = typeof(InternalType.PublicNestedType); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithInternalNestedTypeOnInternalType() + { + Type type = typeof(InternalType.InternalNestedType); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithProtectedInternalNestedTypeOnInternalType() + { + Type type = typeof(InternalType.ProtectedInternalNestedType); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithProtectedNestedTypeOnInternalType() + { + Type type = typeof(InternalType).GetNestedType("ProtectedNestedType", BindingFlags.NonPublic); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithPrivateNestedTypeOnInternalType() + { + Type type = typeof(InternalType).GetNestedType("PrivateNestedType", BindingFlags.NonPublic); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithPublicType() + { + Type type = typeof(PublicType); + Assert.IsTrue(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithPublicNestedTypeOnPublicType() + { + Type type = typeof(PublicType.PublicNestedType); + Assert.IsTrue(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithInternalNestedTypeOnPublicType() + { + Type type = typeof(PublicType.InternalNestedType); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithProtectedInternalNestedTypeOnPublicType() + { + Type type = typeof(PublicType.ProtectedInternalNestedType); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithProtectedNestedTypeOnPublicType() + { + Type type = typeof(PublicType).GetNestedType("ProtectedNestedType", BindingFlags.NonPublic); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type)); + } + + [Test] + public void IsTypeVisibleWithPrivateNestedTypeOnPublicType() + { + Type type = typeof(PublicType).GetNestedType("PrivateNestedType", BindingFlags.NonPublic); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type)); + } + +#if NET_2_0 + private static readonly string FRIENDLY_ASSEMBLY_NAME = "ReflectionUtils.IsTypeVisible.AssemblyTestName, PublicKey=001200000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293"; + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithInternalType() + { + Type type = typeof(InternalType); + Assert.IsTrue(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithPublicNestedTypeOnInternalType() + { + Type type = typeof(InternalType.PublicNestedType); + Assert.IsTrue(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithInternalNestedTypeOnInternalType() + { + Type type = typeof(InternalType.InternalNestedType); + Assert.IsTrue(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithProtectedInternalNestedTypeOnInternalType() + { + Type type = typeof(InternalType.ProtectedInternalNestedType); + Assert.IsTrue(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithProtectedNestedTypeOnInternalType() + { + Type type = typeof(InternalType).GetNestedType("ProtectedNestedType", BindingFlags.NonPublic); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithPrivateNestedTypeOnInternalType() + { + Type type = typeof(InternalType).GetNestedType("PrivateNestedType", BindingFlags.NonPublic); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithPublicType() + { + Type type = typeof(PublicType); + Assert.IsTrue(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithPublicNestedTypeOnPublicType() + { + Type type = typeof(PublicType.PublicNestedType); + Assert.IsTrue(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithInternalNestedTypeOnPublicType() + { + Type type = typeof(PublicType.InternalNestedType); + Assert.IsTrue(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithProtectedInternalNestedTypeOnPublicType() + { + Type type = typeof(PublicType.ProtectedInternalNestedType); + Assert.IsTrue(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithProtectedNestedTypeOnPublicType() + { + Type type = typeof(PublicType).GetNestedType("ProtectedNestedType", BindingFlags.NonPublic); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } + + [Test] + public void IsTypeVisibleFromFriendlyAssemblyWithPrivateNestedTypeOnPublicType() + { + Type type = typeof(PublicType).GetNestedType("PrivateNestedType", BindingFlags.NonPublic); + Assert.IsFalse(ReflectionUtils.IsTypeVisible(type, FRIENDLY_ASSEMBLY_NAME)); + } +#endif + + #endregion + + #region GetExplicitBaseException + + [Test] + public void GetExplicitBaseExceptionWithNoInnerException() + { + Exception appEx = new ApplicationException(); + Exception ex = ReflectionUtils.GetExplicitBaseException(appEx); + + Assert.AreEqual(ex, appEx); + } + + [Test] + public void GetExplicitBaseExceptionWithInnerException() + { + Exception dbzEx = new DivideByZeroException(); + Exception appEx = new ApplicationException("Test message", dbzEx); + Exception ex = ReflectionUtils.GetExplicitBaseException(appEx); + + Assert.AreEqual(ex, dbzEx); + } + + [Test] + public void GetExplicitBaseExceptionWithInnerExceptions() + { + Exception dbzEx = new DivideByZeroException(); + Exception sEx = new SystemException("Test message", dbzEx); + Exception appEx = new ApplicationException("Test message", sEx); + Exception ex = ReflectionUtils.GetExplicitBaseException(appEx); + + Assert.AreEqual(ex, dbzEx); + } + + [Test] + public void GetExplicitBaseExceptionWithNullReferenceExceptionAsRootException() + { + Exception nrEx = new NullReferenceException(); + Exception appEx = new ApplicationException("Test message", nrEx); + Exception ex = ReflectionUtils.GetExplicitBaseException(appEx); + + Assert.AreEqual(ex, appEx); + } + + #endregion + + #region Helper Methods + + public int Add(ref int one, int two) + { + return one + two; + } + + private Attribute CheckForPresenceOfCustomAttribute( + CustomAttributeBuilder attBuilder, Type attType) + { + Attribute[] atts = ApplyAndGetCustomAttributes(attBuilder); + Assert.IsNotNull(atts); + Assert.IsTrue(atts.Length == 1); + Attribute att = atts[0]; + Assert.IsNotNull(att); + Assert.AreEqual(attType, att.GetType(), "Wrong Attribute applied to the class."); + return att; + } + + private static Attribute[] ApplyAndGetCustomAttributes(CustomAttributeBuilder attBuilder) + { + Type type = BuildTypeWithThisCustomAttribute(attBuilder); + object[] attributes = type.GetCustomAttributes(true); + return (Attribute[]) ArrayList.Adapter(attributes).ToArray(typeof(Attribute)); + } + + private static Type BuildTypeWithThisCustomAttribute(CustomAttributeBuilder attBuilder) + { + AssemblyName assemblyName = new AssemblyName(); + assemblyName.Name = "AnAssembly"; + AssemblyBuilder asmBuilder = Thread.GetDomain().DefineDynamicAssembly( + assemblyName, AssemblyBuilderAccess.Run); + ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("AModule"); + TypeBuilder classBuilder = modBuilder.DefineType("AClass", TypeAttributes.Public); + classBuilder.SetCustomAttribute(attBuilder); + return classBuilder.CreateType(); + } + + #endregion + } + + #region Simple Helper Classes + + public class PublicType + { + public class PublicNestedType + { + } + + internal class InternalNestedType + { + } + + protected internal class ProtectedInternalNestedType + { + } + + protected class ProtectedNestedType + { + } + + private class PrivateNestedType + { + } + } + + internal class InternalType + { + public class PublicNestedType + { + } + + internal class InternalNestedType + { + } + + protected internal class ProtectedInternalNestedType + { + } + + protected class ProtectedNestedType + { + } + + private class PrivateNestedType + { + } + } + + internal enum EnumWithNoValues {} + + internal enum Cuts + { + Superficial, + Deep, + Severing + } + + internal interface IBar : IFoo + { + } + + internal interface IFoo + { + bool Spanglish(string foo, object[] args); + } + + internal sealed class ExplicitFoo : IFoo + { + bool IFoo.Spanglish(string foo, object[] args) + { + return false; + } + } + + internal class ReflectionUtilsObject : IComparable + { + public bool Spanglish(string foo, object[] args) + { + return false; + } + + public bool BadSpanglish(string foo) + { + return false; + } + + public bool WickedSpanglish(string foo, string bar) + { + return false; + } + + /// + /// Explicit interface implementation for ReflectionUtils.GetMethod tests + /// + /// + /// + int IComparable.CompareTo(object obj) + { + return 0; + } + } + + internal sealed class ExtendedReflectionUtilsObject : ReflectionUtilsObject + { + public void Declared(string name) + { + } + + public void Protected() + { + } + + public static void Static() + { + } + } + + /// + /// Exposes methods with the same names as the ReflectionUtilsObject, used in + /// the tests for the GetMatchingMethods method. + /// + internal class ReflectionUtilsObjectClone + { + public bool Spanglish(string foo, object[] args) + { + return false; + } + + public bool BadSpanglish(string foo) + { + return false; + } + + public bool WickedSpanglish(string foo, string bar) + { + return false; + } + + protected void Bingo() + { + } + } + + /// + /// Exposes methods with the same names as the ReflectionUtilsObject, used in + /// the tests for the GetMatchingMethods method. + /// + internal class ReflectionUtilsObjectBadClone + { + public bool Spanglish(string foo, object[] args) + { + return false; + } + + public string BadSpanglish(string foo) + { + return foo; + } + + public bool WickedSpanglish(string foo, string bar) + { + return false; + } + } + + internal class GetMethodByArgumentValuesTarget + { + public class DummyArgumentType {} + + public string SelectedConstructor; + + public GetMethodByArgumentValuesTarget(int flag, params object[] values) + { + SelectedConstructor = "ParamArrayMatch"; + } + + public GetMethodByArgumentValuesTarget(int flag, DummyArgumentType[] bars) + { + SelectedConstructor = "ExactMatch"; + } + + public GetMethodByArgumentValuesTarget(int flag, ICollection bars) + { + SelectedConstructor = "AssignableMatch"; + } + + public string MethodWithSimilarArguments(int flags, params object[] args) + { + return "ParamArrayMatch"; + } + + public string MethodWithSimilarArguments(int flags, DummyArgumentType[] bars) + { + return "ExactMatch"; + } + + public string MethodWithSimilarArguments(int flags, ICollection bar) + { + return "AssignableMatch"; + } + } + + public sealed class MyCustomAttribute : Attribute + { + public MyCustomAttribute() + { + } + + public MyCustomAttribute(string name) + { + _name = name; + } + + public string Name + { + get { return _name; } + } + + private string _name; + } + + /// + /// Used for testing that default values are supplied for the ctor args. + /// + public sealed class AnotherCustomAttribute : Attribute + { + public AnotherCustomAttribute(string name, int age, bool hasSwallowedExplosives) + { + _name = name; + _age = age; + _hasSwallowedExplosives = hasSwallowedExplosives; + } + + public string Name + { + get { return _name; } + set { _name = value; } + } + + public int Age + { + get { return _age; } + set { _age = value; } + } + + public bool HasSwallowedExplosives + { + get { return _hasSwallowedExplosives; } + set { _hasSwallowedExplosives = value; } + } + + private string _name; + private int _age; + private bool _hasSwallowedExplosives; + } + + public sealed class ObjectWithNonDefaultIndexerName + { + private string[] favoriteQuotes = new string[10]; + + [IndexerName("MyItem")] + public string this[int index] + { + get + { + if (index < 0 || index >= favoriteQuotes.Length) + { + throw new ArgumentException("Index out of range"); + } + return favoriteQuotes[index]; + } + + set + { + if (index < 0 || index >= favoriteQuotes.Length) + { + throw new ArgumentException("index is out of range."); + } + favoriteQuotes[index] = value; + } + + } + } + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Util/SerializationTestUtils.cs b/test/Spring/Spring.Core.Tests/Util/SerializationTestUtils.cs new file mode 100644 index 00000000..1b7ae0cf --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/SerializationTestUtils.cs @@ -0,0 +1,112 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Util +{ + /// + /// Utilities for testing serializability of objects. + /// + /// + /// Exposes static methods for use in other test cases. + /// + /// Rod Johnson + /// Simon White (.NET) + [TestFixture] + public sealed class SerializationTestUtils + { + [Test] + [ExpectedException(typeof (SerializationException))] + public void WithNonSerializableObject() + { + TestObject o = new TestObject(); + Assert.IsFalse(o is ISerializable); + Assert.IsFalse(IsSerializable(o)); + TrySerialization(o); + } + + [Test] + public void WithSerializableObject() + { + IPerson p = new SerializablePerson(); + p.Age = 12; + Assert.IsTrue(p is ISerializable); + TrySerialization(p); + Assert.IsTrue(IsSerializable(p)); + IPerson p2 = (IPerson) SerializeAndDeserialize(p); + Assert.IsTrue(p != p2); + Assert.AreEqual(12, p2.Age); + } + + /// + /// Attempts to serialize the specified object to an in-memory stream. + /// + /// the object to serialize + public static void TrySerialization(object o) + { + using (Stream stream = new MemoryStream()) + { + BinaryFormatter bformatter = new BinaryFormatter(); + bformatter.Serialize(stream, o); + } + } + + /// + /// Tests whether the specified object is serializable. + /// + /// the object to test. + /// true if the object is serializable, otherwise false. + public static bool IsSerializable(object o) + { + return o == null ? true : o.GetType().IsSerializable; + } + + /// + /// Serializes the specified object to an in-memory stream, and returns + /// the result of deserializing the object stream. + /// + /// the object to use. + /// the deserialized object. + public static object SerializeAndDeserialize(object o) + { + using (Stream stream = new MemoryStream()) + { + BinaryFormatter bformatter = new BinaryFormatter(); + bformatter.Serialize(stream, o); + stream.Flush(); + + stream.Seek(0, SeekOrigin.Begin); + object o2 = bformatter.Deserialize(stream); + return o2; + } + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Util/StringUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/StringUtilsTests.cs new file mode 100644 index 00000000..10b75894 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/StringUtilsTests.cs @@ -0,0 +1,435 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the StringUtils class. + /// + /// Aleksandar Seovic + /// Rick Evans + /// Griffin Caprio + /// $Id: StringUtilsTests.cs,v 1.3 2006/04/09 07:24:51 markpollack Exp $ + [TestFixture] + public sealed class StringUtilsTests + { + [Test] + public void SplitTests() + { + string testString = " a,b,, c ,d\n:e "; + string delim = ",\n"; + string[] res; + string[] res1 = new string[] {" a", "b", "", " c ", "d", ":e "}; + string[] res2 = new string[] {" a", "b", " c ", "d", ":e "}; + string[] res3 = new string[] {"a", "b", "", "c", "d", ":e"}; + string[] res4 = new string[] {"a", "b", "c", "d", ":e"}; + + Assert.AreEqual(0, StringUtils.Split(null, null, false, false).Length); + Assert.AreEqual(testString, StringUtils.Split(testString, null, false, false)[0]); + Assert.IsTrue(ArrayUtils.AreEqual(res1, res = StringUtils.Split(testString, delim, false, false)), "Received '" + String.Join(",", res) + "'"); + Assert.IsTrue(ArrayUtils.AreEqual(res2, res = StringUtils.Split(testString, delim, false, true)), "Received '" + String.Join(",", res) + "'"); + Assert.IsTrue(ArrayUtils.AreEqual(res3, res = StringUtils.Split(testString, delim, true, false)), "Received '" + String.Join(",", res) + "'"); + Assert.IsTrue(ArrayUtils.AreEqual(res4, res = StringUtils.Split(testString, delim, true, true)), "Received '" + String.Join(",", res) + "'"); + + Assert.IsTrue(ArrayUtils.AreEqual(new string[] {"one"}, res = StringUtils.Split("one", delim, true, true)), "Received '" + String.Join(",", res) + "'"); + } + + [Test] + public void HasLengthTests() + { + Assert.IsFalse(StringUtils.HasLength(null)); + Assert.IsFalse(StringUtils.HasLength("")); + Assert.IsTrue(StringUtils.HasLength(" ")); + Assert.IsTrue(StringUtils.HasLength("Hello")); + } + + [Test] + public void HasTextTests() + { + Assert.IsFalse(StringUtils.HasText(null)); + Assert.IsFalse(StringUtils.HasText("")); + Assert.IsFalse(StringUtils.HasText(" ")); + Assert.IsTrue(StringUtils.HasText("12345")); + Assert.IsTrue(StringUtils.HasText(" 12345 ")); + } + + [Test] + public void IsNullOrEmptyTests() + { + Assert.IsTrue(StringUtils.IsNullOrEmpty(null)); + Assert.IsTrue(StringUtils.IsNullOrEmpty("")); + Assert.IsTrue(StringUtils.IsNullOrEmpty(" ")); + Assert.IsFalse(StringUtils.IsNullOrEmpty("12345")); + Assert.IsFalse(StringUtils.IsNullOrEmpty(" 12345 ")); + } + + [Test] + public void CollectionToDelimitedString() + { + Foo[] arr = new Foo[] {new Foo("Foo"), new Foo("Bar")}; + Assert.AreEqual( + ":Foo,:Bar", StringUtils.CollectionToCommaDelimitedString(arr)); + + Assert.AreEqual("null", StringUtils.CollectionToCommaDelimitedString(null)); + } + + [Test] + public void ArrayToDelimitedString() + { + Foo[] arr = new Foo[] {new Foo("Foo"), new Foo("Bar")}; + Assert.AreEqual( + ":Foo,:Bar", StringUtils.ArrayToCommaDelimitedString(arr)); + Assert.AreEqual("null", StringUtils.ArrayToCommaDelimitedString(null)); + } + + [Test] + public void DelimitedListToStringArray() + { + string[] expected = new string[] {"Foo", "", "Bar"}; + string input = "Foo,,Bar"; + string[] actual = StringUtils.DelimitedListToStringArray(input, ","); + Assert.IsNotNull(actual); + Assert.AreEqual(expected.Length, actual.Length); + for (int i = 0; i < actual.Length; ++i) + { + Assert.AreEqual(expected[i], actual[i]); + } + } + + [Test] + public void DelimitedListToStringArrayWithEmptyStringDelimiter() + { + string input = "Foo,,Bar"; + string[] expected = new string[] {input}; + string[] actual = StringUtils.DelimitedListToStringArray(input, string.Empty); + Assert.IsNotNull(actual); + Assert.AreEqual(expected.Length, actual.Length); + for (int i = 0; i < actual.Length; ++i) + { + Assert.AreEqual(expected[i], actual[i]); + } + } + + [Test] + public void DelimitedListToStringArrayWithGuff() + { + string[] expected = new string[] {}; + string[] actual = StringUtils.DelimitedListToStringArray(null, null); + Assert.IsNotNull(actual); + Assert.AreEqual(expected.Length, actual.Length); + + string aString = "HungerHurtsButStarvingWorks..."; + expected = new string[] {aString}; + actual = StringUtils.DelimitedListToStringArray(aString, null); + Assert.IsNotNull(actual); + Assert.AreEqual(expected.Length, actual.Length); + Assert.AreEqual(aString, actual[0]); + } + + [Test] + public void StripFirstAndLastCharacterWithNull() + { + string actual = StringUtils.StripFirstAndLastCharacter(null); + Assert.IsNotNull(actual, "StringUtils.StripFirstAndLastCharacter(null) should return String.Empty."); + Assert.AreEqual(String.Empty, actual, "StringUtils.StripFirstAndLastCharacter(null) should return String.Empty."); + } + + [Test] + public void StripFirstAndLastCharacterWithEmptyString() + { + string actual = StringUtils.StripFirstAndLastCharacter(String.Empty); + Assert.IsNotNull(actual, "StringUtils.StripFirstAndLastCharacter(String.Empty) should return String.Empty."); + Assert.AreEqual(String.Empty, actual, "StringUtils.StripFirstAndLastCharacter(String.Empty) should return String.Empty."); + } + + [Test] + public void StripFirstAndLastCharacterWithReallyShortStrings() + { + string actual = StringUtils.StripFirstAndLastCharacter("B"); + Assert.IsNotNull(actual, "StringUtils.StripFirstAndLastCharacter(\"B\") should return String.Empty."); + Assert.AreEqual(String.Empty, actual, "StringUtils.StripFirstAndLastCharacter(\"B\") should return String.Empty."); + + actual = StringUtils.StripFirstAndLastCharacter("BF"); + Assert.IsNotNull(actual, "StringUtils.StripFirstAndLastCharacter(\"BF\") should return String.Empty."); + Assert.AreEqual(String.Empty, actual, "StringUtils.StripFirstAndLastCharacter(\"BF\") should return String.Empty."); + } + + [Test] + public void StripFirstAndLastCharacterWorksLikeACharm() + { + string actual = StringUtils.StripFirstAndLastCharacter("There are 17 steps to winding up a bird chronicle correctly"); + Assert.IsNotNull(actual); + Assert.AreEqual("here are 17 steps to winding up a bird chronicle correctl", actual); + } + + [Test] + public void GetAntExpressionsWithNull() + { + IList actual = StringUtils.GetAntExpressions(null); + Assert.IsNotNull(actual); + string[] expected = new string[] {}; + Assert.IsTrue(ArrayUtils.AreEqual(expected, (string[]) ArrayList.Adapter(actual).ToArray(typeof (string)))); + } + + [Test] + public void GetAntExpressionsWithEmptyString() + { + IList actual = StringUtils.GetAntExpressions(String.Empty); + Assert.IsNotNull(actual); + string[] expected = new string[] {}; + Assert.IsTrue(ArrayUtils.AreEqual(expected, (string[]) ArrayList.Adapter(actual).ToArray(typeof (string)))); + } + + [Test] + public void GetAntExpressionsWithAStringThatDoesntHaveAnyExpressions() + { + IList actual = StringUtils.GetAntExpressions("I could really go a cup of tea right now... in fact I think I'll go get one."); + Assert.IsNotNull(actual); + string[] expected = new string[] {}; + Assert.IsTrue(ArrayUtils.AreEqual(expected, (string[]) ArrayList.Adapter(actual).ToArray(typeof (string)))); + } + + [Test] + public void GetAntExpressionsWithAValidExpression() + { + IList actual = StringUtils.GetAntExpressions("${slurp}. Ah! That is one good cup of tea. That agent Cooper and his coffee... he sure was missing out on a good thing."); + CheckGetAntExpressions(actual, "slurp"); + } + + [Test] + public void GetAntExpressionsWithANestedExpression() + { + IList actual = StringUtils.GetAntExpressions("And yeah, I've never been a fan of the doughnut... ${blechh${shudder}}"); + CheckGetAntExpressions(actual, "blechh${shudder"); + } + + [Test] + public void GetAntExpressionsWithACoupleOfDuplicatedValidExpressions() + { + IList actual = StringUtils.GetAntExpressions("${sigh}. Laura Palmer though... man, that sure was a tragedy. ${sigh}"); + CheckGetAntExpressions(actual, "sigh"); + } + + [Test] + public void GetAntExpressionsWithACoupleOfUniqueValidExpressions() + { + IList actual = StringUtils.GetAntExpressions("${Mmm}. Has there been any good telly since then... ${thinks}"); + CheckGetAntExpressions(actual, "Mmm", "thinks"); + } + + [Test] + public void GetAntExpressionsWithMalformedExpression() + { + IList actual = StringUtils.GetAntExpressions("Mmm... just what counts as ${a malformed{ expression?"); + CheckGetAntExpressions(actual, new string[] {}); + } + + private static void CheckGetAntExpressions(IList actual, params string[] expected) + { + Assert.IsNotNull(actual); + Assert.IsTrue(ArrayUtils.AreEqual( + expected, + (string[]) ArrayList.Adapter(actual).ToArray(typeof (string)))); + } + + [Test] + [ExpectedException(typeof (FormatException))] + public void GetAntExpressionsIgnoresEmptyExpression() + { + StringUtils.GetAntExpressions("This is an empty expression ${}..."); + } + + [Test] + public void SetAntExpressionWithNullText() + { + string expected = String.Empty; + string actual = StringUtils.SetAntExpression(null, "foo", "bar"); + Assert.IsNotNull(actual); + Assert.AreEqual(expected, actual); + } + + [Test] + public void SetAntExpressionWithEmptyStringExpression() + { + string expected = String.Empty; + string actual = StringUtils.SetAntExpression(" ", "foo", "bar"); + Assert.IsNotNull(actual); + Assert.AreEqual(expected, actual); + } + + [Test] + public void SetAntExpressionWithMultipleEvaluations() + { + string expected = "Hey, do you want to hear the most annoying sound in the world? Mehhh! Mehhh!"; + string actual = StringUtils.SetAntExpression("Hey, do you want to hear the most annoying sound in the world? ${foo}! ${foo}!", "foo", "Mehhh"); + Assert.IsNotNull(actual); + Assert.AreEqual(expected, actual); + } + + [Test] + public void SetAntExpressionWithNullReplacementValue() + { + string expected = "That John Denver... he's full of ."; + string actual = StringUtils.SetAntExpression("That John Denver... he's full of ${bleep}.", "bleep", null); + Assert.IsNotNull(actual); + Assert.AreEqual(expected, actual); + } + + [Test] + public void FullSurroundWithNulls() + { + Assert.AreEqual("Hi", StringUtils.Surround(null, "Hi", null)); + } + + [Test] + public void FullSurroundWithEmptyStrings() + { + Assert.AreEqual("Hi", StringUtils.Surround(string.Empty, "Hi", string.Empty)); + } + + [Test] + public void FullSurround() + { + Assert.AreEqual("[Hi]", StringUtils.Surround("[", "Hi", "]")); + } + + [Test] + public void FullSurroundNullWithEmptyStrings() + { + Assert.AreEqual(string.Empty, StringUtils.Surround(string.Empty, null, string.Empty)); + } + + [Test] + public void FullSurroundEmptyStringWithEmptyStrings() + { + Assert.AreEqual(string.Empty, StringUtils.Surround(string.Empty, string.Empty, string.Empty)); + } + + [Test] + public void FullSurroundNullWithNulls() + { + Assert.AreEqual(string.Empty, StringUtils.Surround(null, null, null)); + } + + [Test] + public void SurroundWithEmptyStrings() + { + Assert.AreEqual("Hi", StringUtils.Surround(string.Empty, "Hi")); + } + + [Test] + public void Surround() + { + Assert.AreEqual("-Hi-", StringUtils.Surround("-", "Hi")); + } + + [Test] + public void SurroundNullWithEmptyStrings() + { + Assert.AreEqual(string.Empty, StringUtils.Surround(string.Empty, null)); + } + + [Test] + public void SurroundEmptyStringWithEmptyStrings() + { + Assert.AreEqual(string.Empty, StringUtils.Surround(string.Empty, string.Empty)); + } + + [Test] + public void SurroundNullWithNulls() + { + Assert.AreEqual(string.Empty, StringUtils.Surround(null, null)); + } + + [Test] + public void FullSurroundObjectWithNulls() + { + Assert.AreEqual(":Hi", StringUtils.Surround(null, new Foo("Hi"), null)); + } + + [Test] + public void FullSurroundObjectWithEmptyStrings() + { + Assert.AreEqual(":Hi", StringUtils.Surround(string.Empty, new Foo("Hi"), string.Empty)); + } + + [Test] + public void FullSurroundObject() + { + Assert.AreEqual("[:Hi]", StringUtils.Surround("[", new Foo("Hi"), "]")); + } + + [Test] + public void SurroundObjectWithEmptyStrings() + { + Assert.AreEqual(":Hi", StringUtils.Surround(string.Empty, new Foo("Hi"), string.Empty)); + } + + [Test] + public void SurroundObject() + { + Assert.AreEqual("[:Hi]", StringUtils.Surround("[", new Foo("Hi"), "]")); + } + + [Test] + public void ConvertEscapedCharactersNoEscapedCharacters() + { + string inputString = "foo bar is a funny term"; + Assert.AreEqual(inputString, StringUtils.ConvertEscapedCharacters(inputString)); + } + + [Test] + public void ConvertEscapedCharactersAll() + { + string inputString = "newline\\n tab\\t return\\r"; + Assert.AreEqual("newline\n tab\t return\r", StringUtils.ConvertEscapedCharacters(inputString)); + } + + [Test] + public void ConvertUnsupportedEscapedCharacters() + { + string inputString = "what is this\\g"; + Assert.AreEqual(inputString, StringUtils.ConvertEscapedCharacters(inputString)); + } + + private sealed class Foo + { + public Foo(string bar) + { + this.bar = bar; + } + + public override string ToString() + { + return ":" + bar; + } + + private string bar; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Util/SystemUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/SystemUtilsTests.cs new file mode 100644 index 00000000..47581d52 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/SystemUtilsTests.cs @@ -0,0 +1,80 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Threading; +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// This class contains tests for SystemUtils + /// + /// Mark Pollack + /// $Id: SystemUtilsTests.cs,v 1.1 2007/09/07 02:47:08 markpollack Exp $ + [TestFixture] + public class SystemUtilsTests + { + private static string threadName = "TestingThread"; + + + [Test] + public void ThreadIdIsName() + { + Thread t = new Thread(new ThreadStart(AssertThreadName)); + t.Name = threadName; + t.Start(); + t.Join(); + } + + + public void AssertThreadName() + { + Assert.AreEqual(threadName, SystemUtils.ThreadId); + } + + [Test] + public void ThreadIdIsInt() + { + Thread t = new Thread(new ThreadStart(AssertThreadIsInt)); + t.Start(); + t.Join(); + + } + + public void AssertThreadIsInt() + { + try + { + int.Parse(SystemUtils.ThreadId); + } + catch (Exception) + { + Assert.Fail("ThreadId should be an integer"); + } + } + + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Util/UniqueKeyTests.cs b/test/Spring/Spring.Core.Tests/Util/UniqueKeyTests.cs new file mode 100644 index 00000000..be6ac8fd --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Util/UniqueKeyTests.cs @@ -0,0 +1,107 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Util +{ + /// + /// Tests functionality. + /// + /// Erich Eichinger + /// $Id: UniqueKeyTests.cs,v 1.1 2007/08/22 20:17:05 oakinger Exp $ + [TestFixture] + public class UniqueKeyTests + { + private class TestObject + {} + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void MustNotCallObjectSignatureWithType() + { + Type myType = typeof(TestObject); + UniqueKey.GetInstanceScopedString( (object)myType, "PartialKey"); + } + + [Test] + public void CreateTypeScopedKeyString() + { + string typeScopedKey = UniqueKey.GetTypeScopedString(typeof(TestObject), "PartialKey"); + string expectedKey = string.Format("{0}.{1}", typeof(TestObject).FullName, "PartialKey"); + Assert.AreEqual(expectedKey, typeScopedKey); + } + + [Test] + public void CreateTypeScopedKey() + { + UniqueKey typeScopedKey = UniqueKey.GetTypeScoped(typeof(TestObject), "PartialKey"); + UniqueKey expectedKey = UniqueKey.GetTypeScoped(typeof(TestObject), "PartialKey"); + + string expectedKeyString = string.Format("{0}.{1}", typeof(TestObject).FullName, "PartialKey"); + + // I know testing implementation details is not the best strategy, + // but I want to receive an error if this fails (oakinger) + Assert.AreEqual(expectedKeyString, expectedKey.ToString()); + Assert.AreEqual(expectedKeyString.GetHashCode(), expectedKey.GetHashCode()); + + // different instances... + Assert.AreNotSame(expectedKey, typeScopedKey); + // but equal + Assert.AreEqual(expectedKey, typeScopedKey); + Assert.AreEqual(expectedKey.GetHashCode(), typeScopedKey.GetHashCode()); + } + + [Test] + public void CreateInstanceScopedKeyString() + { + TestObject testObject = new TestObject(); + string typeScopedKey = UniqueKey.GetInstanceScopedString(testObject, "PartialKey"); + string expectedKey = string.Format("{0}[{1}].{2}", typeof(TestObject).FullName, testObject.GetHashCode(), "PartialKey"); + Assert.AreEqual(expectedKey, typeScopedKey); + } + + [Test] + public void CreateInstanceScopedKey() + { + TestObject testObject = new TestObject(); + UniqueKey instanceScopedKey = UniqueKey.GetInstanceScoped(testObject, "PartialKey"); + UniqueKey expectedKey = UniqueKey.GetInstanceScoped(testObject, "PartialKey"); + + string expectedKeyString = string.Format("{0}[{1}].{2}", typeof(TestObject).FullName, testObject.GetHashCode(), "PartialKey"); + + // I know testing implementation details is not the best strategy, + // but I want to receive an error if this fails (oakinger) + Assert.AreEqual(expectedKeyString, expectedKey.ToString()); + Assert.AreEqual(expectedKeyString.GetHashCode(), expectedKey.GetHashCode()); + + // different instances... + Assert.AreNotSame(expectedKey, instanceScopedKey); + // but equal + Assert.AreEqual(expectedKey, instanceScopedKey); + Assert.AreEqual(expectedKey.GetHashCode(), instanceScopedKey.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/Actions/ErrorMessageActionTests.cs b/test/Spring/Spring.Core.Tests/Validation/Actions/ErrorMessageActionTests.cs new file mode 100644 index 00000000..4220f2a8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Actions/ErrorMessageActionTests.cs @@ -0,0 +1,112 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using NUnit.Framework; + +using Spring.Context.Support; +using Spring.Expressions; + +namespace Spring.Validation.Actions +{ + /// + /// Unit tests for the ErrorMessageAction class. + /// + /// Aleksandar Seovic + /// $Id: ErrorMessageActionTests.cs,v 1.4 2008/02/05 20:40:26 aseovic Exp $ + [TestFixture] + public class ErrorMessageActionTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void WithNullMesageId() + { + new ErrorMessageAction(null, "errors"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void WithEmptyMesageId() + { + new ErrorMessageAction("", "errors"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void WithWhitespaceMesageId() + { + new ErrorMessageAction("\t ", "errors"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WithNullProviders() + { + new ErrorMessageAction("error", null); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WithEmptyProviders() + { + new ErrorMessageAction("error", new string[0]); + } + + [Test] + public void WhenValid() + { + Inventor context = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + IValidationErrors errors = new ValidationErrors(); + ErrorMessageAction action = new ErrorMessageAction("error", "errors"); + + action.Execute(true, context, null, errors); + Assert.IsTrue(errors.IsEmpty); + } + + [Test] + public void WhenInvalid() + { + Inventor context = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + IValidationErrors errors = new ValidationErrors(); + + ErrorMessageAction action = new ErrorMessageAction("{0}, {1}", "errors"); + action.Parameters = new IExpression[] {Expression.Parse("Name"), Expression.Parse("Nationality")}; + + action.Execute(false, context, null, errors); + Assert.IsFalse(errors.IsEmpty); + Assert.AreEqual(1, errors.GetErrors("errors").Count); + Assert.AreEqual(context.Name + ", " + context.Nationality, errors.GetResolvedErrors("errors", new NullMessageSource())[0]); + } + + [Test] + public void WhenActionIsNotExecutedBecauseWhenExpressionReturnsFalse() + { + Inventor context = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + IValidationErrors errors = new ValidationErrors(); + + ErrorMessageAction action = new ErrorMessageAction("{0}, {1}", "errors"); + action.When = Expression.Parse("false"); + action.Execute(false, context, null, errors); + Assert.IsTrue(errors.IsEmpty); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/Actions/ExpressionActionTests.cs b/test/Spring/Spring.Core.Tests/Validation/Actions/ExpressionActionTests.cs new file mode 100644 index 00000000..f4462cf4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Actions/ExpressionActionTests.cs @@ -0,0 +1,103 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +using NUnit.Framework; + +using Spring.Expressions; + +namespace Spring.Validation.Actions +{ + /// + /// Unit tests for the ExpressionAction class. + /// + /// Aleksandar Seovic + /// $Id: ExpressionActionTests.cs,v 1.3 2006/04/09 07:24:52 markpollack Exp $ + [TestFixture] + public class ExpressionActionTests + { + [Test] + public void WhenValid() + { + Inventor context = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + IDictionary vars = new Hashtable(); + + ExpressionAction action = new ExpressionAction("#result = 'valid'", "#result = 'invalid'"); + action.Execute(true, context, vars, null); + Assert.AreEqual("valid", vars["result"]); + + action = new ExpressionAction(Expression.Parse("#result = Name"), Expression.Parse("#result = Nationality")); + action.Execute(true, context, vars, null); + Assert.AreEqual(context.Name, vars["result"]); + + action = new ExpressionAction(); + action.Valid = Expression.Parse("#result = DOB.Year"); + action.Invalid = Expression.Parse("#result = DOB.Month"); + action.Execute(true, context, vars, null); + Assert.AreEqual(context.DOB.Year, vars["result"]); + + vars.Clear(); + action = new ExpressionAction(null, "#result = 'invalid'"); + action.Execute(true, context, vars, null); + Assert.IsFalse(vars.Contains("result"), "Result should not exist when valid expression is null."); + } + + [Test] + public void WhenInvalid() + { + Inventor context = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + IDictionary vars = new Hashtable(); + + ExpressionAction action = new ExpressionAction("#result = 'valid'", "#result = 'invalid'"); + action.Execute(false, context, vars, null); + Assert.AreEqual("invalid", vars["result"]); + + action = new ExpressionAction(Expression.Parse("#result = Name"), Expression.Parse("#result = Nationality")); + action.Execute(false, context, vars, null); + Assert.AreEqual(context.Nationality, vars["result"]); + + action = new ExpressionAction(); + action.Valid = Expression.Parse("#result = DOB.Year"); + action.Invalid = Expression.Parse("#result = DOB.Month"); + action.Execute(false, context, vars, null); + Assert.AreEqual(context.DOB.Month, vars["result"]); + + vars.Clear(); + action = new ExpressionAction("#result = 'valid'", null); + action.Execute(false, context, vars, null); + Assert.IsFalse(vars.Contains("result"), "Result should not exist when invalid expression is null."); + } + + [Test] + public void WhenActionIsNotExecutedBecauseWhenExpressionReturnsFalse() + { + Inventor context = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + IDictionary vars = new Hashtable(); + + ExpressionAction action = new ExpressionAction("#result = 'valid'", "#result = 'invalid'"); + action.When = Expression.Parse("false"); + action.Execute(true, context, vars, null); + Assert.IsFalse(vars.Contains("result")); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/AnyValidatorGroupTests.cs b/test/Spring/Spring.Core.Tests/Validation/AnyValidatorGroupTests.cs new file mode 100644 index 00000000..3d63e69c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/AnyValidatorGroupTests.cs @@ -0,0 +1,111 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +using Spring.Expressions; + +#endregion + +namespace Spring.Validation +{ + /// + /// Unit tests for the AnyValidatorGroup class. + /// + /// Aleksandar Seovic + /// $Id: AnyValidatorGroupTests.cs,v 1.4 2008/02/05 20:40:26 aseovic Exp $ + [TestFixture] + public sealed class AnyValidatorGroupTests + { + [Test] + public void WhenAllValidatorsReturnFalse() + { + AnyValidatorGroup vg = new AnyValidatorGroup(); + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new FalseValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsFalse(valid, "Validation should fail when all inner validators return false."); + Assert.AreEqual(3, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + [Test] + public void WhenAllValidatorsReturnTrue() + { + AnyValidatorGroup vg = new AnyValidatorGroup("true"); + vg.Validators.Add(new TrueValidator()); + vg.Validators.Add(new TrueValidator()); + vg.Validators.Add(new TrueValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsTrue(valid, "Validation should succeed when all inner validators return true."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + [Test] + public void WhenSingleValidatorReturnsTrue() + { + AnyValidatorGroup vg = new AnyValidatorGroup(Expression.Parse("true")); + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new TrueValidator()); + vg.Validators.Add(new FalseValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsTrue(valid, "Validation should succeed when single inner validator returns true."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + [Test] + public void WhenGroupIsNotValidatedBecauseWhenExpressionReturnsFalse() + { + AnyValidatorGroup vg = new AnyValidatorGroup("false"); + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new FalseValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsTrue(valid, "Validation should succeed when group validator is not evaluated."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/CollectionValidatorTests.cs b/test/Spring/Spring.Core.Tests/Validation/CollectionValidatorTests.cs new file mode 100644 index 00000000..23e96ac4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/CollectionValidatorTests.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using NUnit.Framework; +using Spring.Collections; +using Spring.Core.IO; +using Spring.Expressions; +using Spring.Objects; +using Spring.Objects.Factory.Xml; +using Spring.Validation.Actions; + +namespace Spring.Validation +{ + /// + /// Unit tests for the CollectionValidator class. + /// + /// Damjan Tomic + [TestFixture] + public class CollectionValidatorTests + { + [Test] + public void TestCollection() + { + IList persons = new ArrayList(); + + persons.Add(new TestObject("Damjan Tomic", 24)); + persons.Add(new TestObject("Goran Milosavljevic", 24)); + persons.Add(new TestObject("Ivan Cikic", 28)); + + RequiredValidator req = new RequiredValidator("Name", "true"); + + RegularExpressionValidator reg = new RegularExpressionValidator("Name", "true", @"[a-z]*\s[a-z]*"); + reg.Options = RegexOptions.IgnoreCase; + + CollectionValidator validator = new CollectionValidator(); + validator.Validators.Add(req); + validator.Validators.Add(reg); + + Assert.IsTrue(validator.Validate(persons, new ValidationErrors())); + } + + [Test] + public void TestDifferentCollectionTypes() + { + const string xml = @" + + + + + + + + + + + + + + + "; + + MemoryStream stream = new MemoryStream(new UTF8Encoding().GetBytes(xml)); + IResource resource = new InputStreamResource(stream, "collectionValidator"); + + XmlObjectFactory objectFactory = new XmlObjectFactory(resource, null); + CollectionValidator validator = (CollectionValidator) objectFactory.GetObject("collectionValidator"); + + IList listPersons = new ArrayList(); + IDictionary dictPersons = new Hashtable(); + ISet setPersons = new ListSet(); + + listPersons.Add(new TestObject("DAMJAN Tomic", 24)); + listPersons.Add(new TestObject("Goran Milosavljevic", 24)); + listPersons.Add(new TestObject("Ivan CIKIC", 28)); + + dictPersons.Add(1, listPersons[0]); + dictPersons.Add(2, listPersons[1]); + dictPersons.Add(3, listPersons[2]); + + setPersons.AddAll(listPersons); + IValidationErrors ve = new ValidationErrors(); + + Assert.IsTrue(validator.Validate(listPersons, ve)); + Assert.IsTrue(ve.IsEmpty); + Assert.IsTrue(validator.Validate(dictPersons, ve)); + Assert.IsTrue(ve.IsEmpty); + Assert.IsTrue(validator.Validate(setPersons, ve)); + Assert.IsTrue(ve.IsEmpty); + } + + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestWithWrongArgumentType() + { + RequiredValidator req = new RequiredValidator("Name", "true"); + CollectionValidator validator = new CollectionValidator(); + validator.Validators.Add(req); + + TestObject tObj = new TestObject("Damjan Tomic", 24); + + //This should cause the ArgumentException because tObj is not a Collection + Assert.IsTrue(validator.Validate(tObj, new ValidationErrors())); + } + + [Test] + public void TestValidationErrorsAreCollected() + { + IList persons = new ArrayList(); + + persons.Add(new TestObject(null, 24)); + persons.Add(new TestObject("Goran Milosavljevic", 24)); + persons.Add(new TestObject("Ivan Cikic", 28)); + persons.Add(new TestObject(null, 20)); + + RequiredValidator req = new RequiredValidator("Name", "true"); + req.Actions.Add(new ErrorMessageAction("1", new string[] { "firstProvider", "secondProvider" })); + + CollectionValidator validator = new CollectionValidator(true,true); + + validator.Validators.Add(req); + + IValidationErrors ve = new ValidationErrors(); + + Assert.IsFalse(validator.Validate(persons, ve)); + Assert.IsFalse(ve.IsEmpty); + + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestWithNull() + { + CollectionValidator validator = new CollectionValidator(); + //This should cause the ArgumentException because we passed null into Validate method + Assert.IsTrue(validator.Validate(null, new ValidationErrors())); + } + + [Test] + public void TestNestingCollectionValidator() + { + Society soc = new Society(); + soc.Members.Add(new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian")); + soc.Members.Add(new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), "Serbian")); + + + RequiredValidator req = new RequiredValidator("Name", "true"); + + RegularExpressionValidator reg = new RegularExpressionValidator("Name", "true", @"[a-z]*\s[a-z]*"); + reg.Options = RegexOptions.IgnoreCase; + + CollectionValidator validator = new CollectionValidator(); + validator.Validators.Add(req); + validator.Validators.Add(reg); + + validator.Context = Expression.Parse("Members"); + + Assert.IsTrue(validator.Validate(soc, new ValidationErrors())); + + validator.Context = null; + Assert.IsTrue(validator.Validate(soc.Members, new ValidationErrors())); + } + + [Test] + public void TestNestingCollectionValidatorWithXMLDescription() + { + const string xml = @" + + + + + + + + + + + + + + + + + + + + + "; + + MemoryStream stream = new MemoryStream(new UTF8Encoding().GetBytes(xml)); + IResource resource = new InputStreamResource(stream, "collection validator test"); + + XmlObjectFactory objectFactory = new XmlObjectFactory(resource, null); + + ValidatorGroup validator = (ValidatorGroup) objectFactory.GetObject("validator"); + Society soc = new Society(); + + soc.Members.Add(new TestObject("Damjan Tomic", 24)); + soc.Members.Add(new TestObject("Goran Milosavljevic", 24)); + soc.Members.Add(new TestObject("Ivan Cikic", 28)); + + IValidationErrors err1 = new ValidationErrors(); + + Assert.IsTrue(validator.Validate(soc, err1)); + + soc.Members.Add(new TestObject("foo", 30)); + soc.Members.Add(new TestObject("bar", 30)); + Assert.IsFalse(validator.Validate(soc, err1)); + + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Validation/ExclusiveValidatorGroupTests.cs b/test/Spring/Spring.Core.Tests/Validation/ExclusiveValidatorGroupTests.cs new file mode 100644 index 00000000..62d75ac9 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/ExclusiveValidatorGroupTests.cs @@ -0,0 +1,121 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +using Spring.Expressions; +using Spring.Validation.Actions; + +#endregion + +namespace Spring.Validation +{ + /// + /// Unit tests for the ExclusiveValidatorGroup class. + /// + /// Aleksandar Seovic + /// $Id: ExclusiveValidatorGroupTests.cs,v 1.4 2008/02/05 20:40:26 aseovic Exp $ + [TestFixture] + public sealed class ExclusiveValidatorGroupTests + { + [Test] + public void WhenAllValidatorsReturnFalse() + { + ExclusiveValidatorGroup vg = new ExclusiveValidatorGroup(); + vg.Actions.Add(new ErrorMessageAction("exclusiveError", "exclusiveErrors")); + + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new FalseValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsFalse(valid, "Validation should fail when all inner validators return false."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("exclusiveErrors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + [Test] + public void WhenAllValidatorsReturnTrue() + { + ExclusiveValidatorGroup vg = new ExclusiveValidatorGroup("true"); + vg.Actions.Add(new ErrorMessageAction("exclusiveError", "exclusiveErrors")); + + vg.Validators.Add(new TrueValidator()); + vg.Validators.Add(new TrueValidator()); + vg.Validators.Add(new TrueValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsFalse(valid, "Validation should fail when all inner validators return true."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("exclusiveErrors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + [Test] + public void WhenSingleValidatorReturnsTrue() + { + ExclusiveValidatorGroup vg = new ExclusiveValidatorGroup(Expression.Parse("true")); + vg.Actions.Add(new ErrorMessageAction("exclusiveError", "exclusiveErrors")); + + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new TrueValidator()); + vg.Validators.Add(new FalseValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsTrue(valid, "Validation should succeed when single inner validator returns true."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + Assert.AreEqual(0, errors.GetErrors("exclusiveErrors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + [Test] + public void WhenGroupIsNotValidatedBecauseWhenExpressionReturnsFalse() + { + ExclusiveValidatorGroup vg = new ExclusiveValidatorGroup("false"); + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new FalseValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsTrue(valid, "Validation should succeed when group validator is not evaluated."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/HelperClasses.cs b/test/Spring/Spring.Core.Tests/Validation/HelperClasses.cs new file mode 100644 index 00000000..d46bbe94 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/HelperClasses.cs @@ -0,0 +1,114 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Collections; + +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Validation.Actions; + +namespace Spring.Validation +{ + /// + /// Helper classes for validation tests. + /// + /// Aleksandar Seovic + /// $Id: HelperClasses.cs,v 1.2 2006/04/09 07:24:52 markpollack Exp $ + public class TrueValidator : BaseValidator + { + public TrueValidator() + {} + + /// + /// Validates test object. + /// + /// Object to validate. + /// True if specified object is valid, False otherwise. + protected override bool Validate(object objectToValidate) + { + return true; + } + } + + public class FalseValidator : BaseValidator + { + public FalseValidator() + { + this.Actions.Add(new ErrorMessageAction("error", "errors")); + } + + /// + /// Validates test object. + /// + /// Object to validate. + /// True if specified object is valid, False otherwise. + protected override bool Validate(object objectToValidate) + { + return false; + } + } + + public sealed class MockObjectDefinitionRegistry : IObjectDefinitionRegistry + { + private IDictionary objects = new Hashtable(); + + public int ObjectDefinitionCount + { + get { return this.objects.Count; } + } + + public string[] GetObjectDefinitionNames() + { + return (string[]) new ArrayList(this.objects.Keys).ToArray(typeof(string)); + } + + public IObjectDefinition[] GetObjectDefinitions() + { + return (IObjectDefinition[]) new ArrayList(this.objects.Values).ToArray(typeof(IObjectDefinition)); + } + + public bool ContainsObjectDefinition(string name) + { + return objects.Contains(name); + } + + public IObjectDefinition GetObjectDefinition(string name) + { + return (IObjectDefinition) objects[name]; + } + + public void RegisterObjectDefinition(string name, IObjectDefinition definition) + { + this.objects[name] = definition; + } + + public string[] GetAliases(string name) + { + throw new NotImplementedException(); + } + + public void RegisterAlias(string name, string theAlias) + { + throw new NotImplementedException(); + } + } + +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/ValidationConfigParserTests.cs b/test/Spring/Spring.Core.Tests/Validation/ValidationConfigParserTests.cs new file mode 100644 index 00000000..94eabda7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/ValidationConfigParserTests.cs @@ -0,0 +1,203 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.IO; +using System.Xml; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Validation.Actions; +using Spring.Validation.Config; + +#endregion + +namespace Spring.Validation +{ + /// + /// Unit tests for the ValidationNamespaceParser class. + /// + /// Rick Evans + /// $Id: ValidationConfigParserTests.cs,v 1.14 2007/08/08 17:48:45 bbaia Exp $ + [TestFixture] + public sealed class ValidationConfigParserTests + { + [Test] + public void WhenConfigFileIsValid() + { + const string xml = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + XmlDocument doc = new XmlDocument(); + + AssemblyResource validationSchema = new AssemblyResource("assembly://Spring.Core/Spring.Validation.Config/spring-validation-1.1.xsd"); + AssemblyResource objectsSchema = new AssemblyResource("assembly://Spring.Core/Spring.Objects.Factory.Xml/spring-objects-1.1.xsd"); + +#if !NET_2_0 + XmlValidatingReader validatingReader = new XmlValidatingReader(xml, XmlNodeType.Document, null); + validatingReader.ValidationType = ValidationType.Schema; + validatingReader.Schemas.Add("http://www.springframework.net", new XmlTextReader(objectsSchema.InputStream)); + validatingReader.Schemas.Add("http://www.springframework.net/validation", new XmlTextReader(validationSchema.InputStream)); +#else + XmlReaderSettings settings = new XmlReaderSettings(); + settings.Schemas.Add("http://www.springframework.net", new XmlTextReader(objectsSchema.InputStream)); + settings.Schemas.Add("http://www.springframework.net/validation", new XmlTextReader(validationSchema.InputStream)); + settings.ValidationType = ValidationType.Schema; + XmlReader validatingReader = XmlReader.Create(new StringReader(xml), settings); +#endif + doc.Load(validatingReader); + + MockObjectDefinitionRegistry registry = new MockObjectDefinitionRegistry(); + IObjectDefinitionDocumentReader reader = new DefaultObjectDefinitionDocumentReader(); + + XmlReaderContext readerContext = new XmlReaderContext(null, new XmlObjectDefinitionReader(registry)); + ObjectDefinitionParserHelper helper = new ObjectDefinitionParserHelper(readerContext); + helper.InitDefaults(doc.DocumentElement); + ParserContext parserContext = new ParserContext(helper.ReaderContext, helper); + + ValidationNamespaceParser parser = new ValidationNamespaceParser(); + foreach (XmlElement element in doc.DocumentElement.ChildNodes) + { + if (element.NamespaceURI == "http://www.springframework.net/validation") + { + parser.ParseElement(element, parserContext); + } + } + IObjectDefinition[] defs = registry.GetObjectDefinitions(); + Assert.AreEqual(2, defs.Length); + + IObjectDefinition def = registry.GetObjectDefinition("destinationAirportValidator"); + Assert.IsTrue(def.IsSingleton); + Assert.IsTrue(def.IsLazyInit); + Assert.IsTrue(typeof(IValidator).IsAssignableFrom(def.ObjectType)); + PropertyValue validatorsProperty = def.PropertyValues.GetPropertyValue("Validators"); + Assert.IsNotNull(validatorsProperty); + object validatorsObject = validatorsProperty.Value; + Assert.AreEqual(typeof(ManagedList), validatorsObject.GetType()); + ManagedList validators = (ManagedList) validatorsObject; + Assert.AreEqual(4, validators.Count); + + def = (IObjectDefinition) validators[3]; + Assert.IsTrue(def.IsSingleton); + Assert.IsTrue(def.IsLazyInit); + Assert.AreEqual(typeof(RegularExpressionValidator), def.ObjectType); + Assert.AreEqual("[A-Z]*", def.PropertyValues.GetPropertyValue("Expression").Value); + + def = registry.GetObjectDefinition("airportCodeValidator"); + Assert.IsTrue(def.IsSingleton); + Assert.IsTrue(def.IsLazyInit); + Assert.IsTrue(typeof(IValidator).IsAssignableFrom(def.ObjectType)); + PropertyValue actionsProperty = def.PropertyValues.GetPropertyValue("Actions"); + Assert.IsNotNull(actionsProperty); + object actionsObject = actionsProperty.Value; + Assert.AreEqual(typeof(ManagedList), actionsObject.GetType()); + ManagedList actions = (ManagedList) actionsObject; + Assert.AreEqual(4, actions.Count); + + IObjectDefinition messageDefinition = (IObjectDefinition) actions[1]; + Assert.AreEqual(typeof(ErrorMessageAction), messageDefinition.ObjectType); + + IObjectDefinition actionDefinition = (IObjectDefinition) actions[2]; + Assert.AreEqual(typeof(ExpressionAction), actionDefinition.ObjectType); + Assert.AreEqual("#now = DateTime.Now", actionDefinition.PropertyValues.GetPropertyValue("Valid").Value); + } + + [Test] + [ExpectedException(typeof(ObjectDefinitionStoreException))] + public void WhenConfigFileIsNotValid() + { + const string xml = @" + + + + + + +"; + XmlDocument doc = new XmlDocument(); + + AssemblyResource validationSchema = new AssemblyResource("assembly://Spring.Core/Spring.Validation.Config/spring-validation-1.1.xsd"); + AssemblyResource objectsSchema = new AssemblyResource("assembly://Spring.Core/Spring.Objects.Factory.Xml/spring-objects-1.1.xsd"); + +#if !NET_2_0 + XmlValidatingReader validatingReader = new XmlValidatingReader(xml, XmlNodeType.Document, null); + validatingReader.ValidationType = ValidationType.Schema; + validatingReader.Schemas.Add("http://www.springframework.net", new XmlTextReader(objectsSchema.InputStream)); + validatingReader.Schemas.Add("http://www.springframework.net/validation", new XmlTextReader(validationSchema.InputStream)); +#else + XmlReaderSettings settings = new XmlReaderSettings(); + settings.Schemas.Add("http://www.springframework.net", new XmlTextReader(objectsSchema.InputStream)); + settings.Schemas.Add("http://www.springframework.net/validation", new XmlTextReader(validationSchema.InputStream)); + settings.ValidationType = ValidationType.Schema; + XmlReader validatingReader = XmlReader.Create(new StringReader(xml), settings); +#endif + doc.Load(validatingReader); + + MockObjectDefinitionRegistry registry = new MockObjectDefinitionRegistry(); + IObjectDefinitionDocumentReader reader = new DefaultObjectDefinitionDocumentReader(); + + XmlReaderContext readerContext = new XmlReaderContext(null, new XmlObjectDefinitionReader(registry)); + ObjectDefinitionParserHelper helper = new ObjectDefinitionParserHelper(readerContext); + helper.InitDefaults(doc.DocumentElement); + ParserContext parserContext = new ParserContext(helper.ReaderContext, helper); + + ValidationNamespaceParser parser = new ValidationNamespaceParser(); + foreach (XmlElement element in doc.DocumentElement.ChildNodes) + { + if (element.NamespaceURI == "http://www.springframework.net/validation") + { + parser.ParseElement(element, parserContext); + } + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/ValidationErrorsTests.cs b/test/Spring/Spring.Core.Tests/Validation/ValidationErrorsTests.cs new file mode 100644 index 00000000..d3102db7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/ValidationErrorsTests.cs @@ -0,0 +1,187 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.IO; +using System.Xml.Serialization; +using NUnit.Framework; + +using Spring.Context.Support; + +#endregion + +namespace Spring.Validation +{ + /// + /// Unit tests for the ValidationErrors class. + /// + /// Rick Evans + /// Goran Milosavljevic + /// $Id: ValidationErrorsTests.cs,v 1.7 2008/02/05 20:40:26 aseovic Exp $ + [TestFixture] + public sealed class ValidationErrorsTests + { + private const string GoodErrorKey = "key"; + private ErrorMessage ErrorMessageTwo = new ErrorMessage("This Is Eva Green", null); + private ErrorMessage ErrorMessageOne = new ErrorMessage("Kissing Leads To Brain Disease", null); + + [Test] + public void ContainsNoErrorsDirectlyAfterInstantiation() + { + IValidationErrors errors = new ValidationErrors(); + Assert.IsTrue(errors.IsEmpty); + Assert.IsNotNull(errors.GetErrors(GoodErrorKey)); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void AddErrorWithNullMessage() + { + new ValidationErrors().AddError(GoodErrorKey, null); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void AddErrorWithNullKey() + { + IValidationErrors errors = new ValidationErrors(); + errors.AddError(null, ErrorMessageOne); + } + + [Test] + public void AddErrorSunnyDay() + { + IValidationErrors errors = new ValidationErrors(); + errors.AddError(GoodErrorKey, ErrorMessageOne); + Assert.IsFalse(errors.IsEmpty); + Assert.IsNotNull(errors.GetErrors(GoodErrorKey)); + Assert.AreEqual(1, errors.GetErrors(GoodErrorKey).Count); + } + + [Test] + public void AddTwoErrorsSameKey() + { + IValidationErrors errors = new ValidationErrors(); + errors.AddError(GoodErrorKey, ErrorMessageOne); + errors.AddError(GoodErrorKey, ErrorMessageTwo); + Assert.IsFalse(errors.IsEmpty); + Assert.IsNotNull(errors.GetErrors(GoodErrorKey)); + Assert.AreEqual(2, errors.GetErrors(GoodErrorKey).Count); + } + + [Test] + public void EmptyErrorsReturnEmptyCollections() + { + IValidationErrors errors = new ValidationErrors(); + + IList typedErrors = errors.GetErrors("xyz"); + Assert.IsNotNull(typedErrors); + Assert.AreEqual(0, typedErrors.Count); + + IList resolvedErrors = errors.GetResolvedErrors("xyz", new NullMessageSource()); + Assert.IsNotNull(resolvedErrors); + Assert.AreEqual(0, resolvedErrors.Count); + } + + [Test] + public void MergeErrorsWithNull() + { + IValidationErrors errors = new ValidationErrors(); + errors.AddError(GoodErrorKey, ErrorMessageOne); + errors.AddError(GoodErrorKey, ErrorMessageTwo); + errors.MergeErrors(null); + + // must be unchanged with no Exception thrown... + Assert.IsFalse(errors.IsEmpty); + Assert.IsNotNull(errors.GetErrors(GoodErrorKey)); + Assert.AreEqual(2, errors.GetErrors(GoodErrorKey).Count); + } + + [Test] + public void MergeErrors() + { + ValidationErrors otherErrors = new ValidationErrors(); + const string anotherKey = "anotherKey"; + otherErrors.AddError(anotherKey, ErrorMessageTwo); + otherErrors.AddError(GoodErrorKey, ErrorMessageTwo); + + + IValidationErrors errors = new ValidationErrors(); + errors.AddError(GoodErrorKey, ErrorMessageOne); + errors.MergeErrors(otherErrors); + + Assert.IsFalse(errors.IsEmpty); + IList mergedErrors = errors.GetErrors(GoodErrorKey); + Assert.IsNotNull(mergedErrors); + Assert.AreEqual(2, mergedErrors.Count); + Assert.AreEqual(ErrorMessageOne, mergedErrors[0]); + Assert.AreEqual(ErrorMessageTwo, mergedErrors[1]); + + IList otherErrorsForKey = errors.GetErrors(anotherKey); + Assert.IsNotNull(otherErrorsForKey); + Assert.AreEqual(1, otherErrorsForKey.Count); + Assert.AreEqual(ErrorMessageTwo, otherErrorsForKey[0]); + } + + [Test] + public void SerializeErrors() + { + ValidationErrors errors = new ValidationErrors(); + ErrorMessageOne = new ErrorMessage("Kissing Leads To Brain Disease", new object[] {"Param11", 5, "Param13"}); + ErrorMessageTwo = new ErrorMessage("This Is Eva Green", new object[] { "Param21", 'g' , new object[] {"Goran", "Milosavljevic"} }); + ErrorMessage ErrorMessageThree = new ErrorMessage("Third error message", null); + ErrorMessage ErrorMessageFour = new ErrorMessage("Fourth error message", new object[]{}); + errors.AddError("key1", ErrorMessageOne); + errors.AddError("key1", ErrorMessageTwo); + errors.AddError("key2", ErrorMessageThree); + errors.AddError("key3", ErrorMessageFour); + + Stream streamBefore = new MemoryStream(); + Stream streamAfter = new MemoryStream(); + + // serialize ValidationErrors + XmlSerializer serializer = new XmlSerializer(typeof(ValidationErrors)); + serializer.Serialize(streamBefore, errors); + streamBefore.Position = 0; + + // deserialize ValidationErrors + ValidationErrors result = (ValidationErrors) serializer.Deserialize(streamBefore); + + // serialize ValidationErrors + serializer.Serialize(streamAfter, result); + + // compare ValidationErrors instances + byte[] byteBefore = new byte[streamBefore.Length]; + byte[] byteAfter = new byte[streamAfter.Length]; + + Assert.AreEqual(byteAfter.Length, byteBefore.Length); + + streamBefore.Position = 0; + streamAfter.Position = 0; + streamBefore.Read(byteBefore, 0, (int) streamBefore.Length); + streamAfter.Read(byteAfter, 0, (int) streamAfter.Length); + + Assert.AreEqual(byteBefore, byteAfter); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Validation/ValidationExceptionTests.cs b/test/Spring/Spring.Core.Tests/Validation/ValidationExceptionTests.cs new file mode 100644 index 00000000..2971989d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/ValidationExceptionTests.cs @@ -0,0 +1,86 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using NUnit.Framework; + +namespace Spring.Validation +{ + /// + /// Unit tests for the ValidationException class. + /// + /// Aleksandar Seovic + /// $Id: ValidationExceptionTests.cs,v 1.1 2008/02/05 20:40:27 aseovic Exp $ + [TestFixture] + public sealed class ValidationExceptionTests + { + [Test] + public void InstantiationUsingDefaultConstructor() + { + ValidationException ex = new ValidationException(); + Assert.IsNull(ex.ValidationErrors); + } + + [Test] + public void InstantiationSupplyingValidationErrors() + { + ValidationException ex = new ValidationException(new ValidationErrors()); + Assert.IsTrue(ex.ValidationErrors.IsEmpty); + } + + [Test] + public void InstantiationSupplyingMessageAndValidationErrors() + { + ValidationException ex = new ValidationException("my message", new ValidationErrors()); + Assert.AreEqual("my message", ex.Message); + Assert.IsTrue(ex.ValidationErrors.IsEmpty); + } + + [Test] + public void InstantiationSupplyingMessageValidationErrorsAndRootCause() + { + Exception rootCause = new Exception("root cause"); + ValidationException ex = new ValidationException("my message", rootCause, new ValidationErrors()); + Assert.AreEqual("my message", ex.Message); + Assert.IsTrue(ex.ValidationErrors.IsEmpty); + Assert.AreEqual(rootCause, ex.InnerException); + Assert.AreEqual("root cause", ex.InnerException.Message); + } + + [Test] + public void TestExceptionSerialization() + { + MemoryStream buffer = new MemoryStream(); + BinaryFormatter serializer = new BinaryFormatter(); + + Exception rootCause = new Exception("root cause"); + ValidationException e1 = new ValidationException("my message", rootCause, new ValidationErrors()); + serializer.Serialize(buffer, e1); + buffer.Position = 0; + ValidationException e2 = (ValidationException)serializer.Deserialize(buffer); + + Assert.AreEqual("my message", e2.Message); + Assert.IsTrue(e2.ValidationErrors.IsEmpty); + Assert.AreEqual("root cause", e2.InnerException.Message); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/ValidatorGroupTests.cs b/test/Spring/Spring.Core.Tests/Validation/ValidatorGroupTests.cs new file mode 100644 index 00000000..c9a0fa9b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/ValidatorGroupTests.cs @@ -0,0 +1,123 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; + +using NUnit.Framework; + +using Spring.Expressions; + +#endregion + +namespace Spring.Validation +{ + /// + /// Unit tests for the ValidatorGroup class. + /// + /// Aleksandar Seovic + /// $Id: ValidatorGroupTests.cs,v 1.4 2008/02/05 20:40:26 aseovic Exp $ + [TestFixture] + public sealed class ValidatorGroupTests : ValidatorGroup + { + [Test] + public void WhenAllValidatorsReturnFalse() + { + ValidatorGroup vg = new ValidatorGroup(); + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new FalseValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsFalse(valid, "Validation should fail when all inner validators return false."); + Assert.AreEqual(3, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + [Test] + public void WhenAllValidatorsReturnTrue() + { + ValidatorGroup vg = new ValidatorGroup("true"); + vg.Validators.Add(new TrueValidator()); + vg.Validators.Add(new TrueValidator()); + vg.Validators.Add(new TrueValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsTrue(valid, "Validation should succeed when all inner validators return true."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + [Test] + public void WhenSingleValidatorReturnsTrue() + { + ValidatorGroup vg = new ValidatorGroup(Expression.Parse("true")); + vg.Validators.Add(new FalseValidator()); + vg.Validators.Add(new TrueValidator()); + vg.Validators.Add(new FalseValidator()); + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsFalse(valid, "Validation should fail when single inner validator returns true."); + Assert.AreEqual(2, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + [Test] + public void WhenGroupIsNotValidatedBecauseWhenExpressionReturnsFalse() + { + ValidatorGroup vg = new ValidatorGroup("false"); + IList validators = new ArrayList(); + validators.Add(new FalseValidator()); + validators.Add(new FalseValidator()); + vg.Validators = validators; + + IValidationErrors errors = new ValidationErrors(); + errors.AddError("existingErrors", new ErrorMessage("error", null)); + + bool valid = vg.Validate(new object(), errors); + + Assert.IsTrue(valid, "Validation should succeed when group validator is not evaluated."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + Assert.AreEqual(1, errors.GetErrors("existingErrors").Count); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void TestNonSupportedValidateMethod() + { + this.Validate("xyz"); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/ValidatorReferenceTests.cs b/test/Spring/Spring.Core.Tests/Validation/ValidatorReferenceTests.cs new file mode 100644 index 00000000..3a22c0b0 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/ValidatorReferenceTests.cs @@ -0,0 +1,100 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +using NUnit.Framework; + +using Spring.Expressions; +using Spring.Objects.Factory.Support; + +namespace Spring.Validation +{ + /// + /// Unit tests for ValidatorReference class. + /// + /// Aleksandar Seovic + /// $Id: ValidatorReferenceTests.cs,v 1.3 2008/02/05 20:40:27 aseovic Exp $ + [TestFixture] + public class ValidatorReferenceTests + { + [Test] + public void TrueValidatorReference() + { + StaticListableObjectFactory factory = new StaticListableObjectFactory(); + factory.AddObject("validator", new TrueValidator()); + + ValidatorReference v = new ValidatorReference(); + v.ObjectFactory = factory; + v.Name = "validator"; + + IValidationErrors errors = new ValidationErrors(); + + Assert.IsTrue(v.Validate(null, null, errors)); + Assert.IsTrue(v.Validate(null, errors)); + } + + [Test] + public void FalseValidatorReference() + { + StaticListableObjectFactory factory = new StaticListableObjectFactory(); + factory.AddObject("validator", new FalseValidator()); + + ValidatorReference v = new ValidatorReference(); + v.ObjectFactory = factory; + v.Name = "validator"; + + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(v.Validate(null, null, errors)); + Assert.IsFalse(v.Validate(null, errors)); + } + + [Test] + public void ContextNarrowing() + { + Inventor context = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + + ConditionValidator cv1 = new ConditionValidator("DOB.Year == 1856", null); + ConditionValidator cv2 = new ConditionValidator("Year == 1856", null); + + StaticListableObjectFactory factory = new StaticListableObjectFactory(); + factory.AddObject("cv1", cv1); + factory.AddObject("cv2", cv2); + + ValidatorReference v1 = new ValidatorReference(); + v1.ObjectFactory = factory; + v1.Name = "cv1"; + + IValidationErrors errors = new ValidationErrors(); + + Assert.IsTrue(v1.Validate(context, null, errors)); + Assert.IsTrue(v1.Validate(context, errors)); + + ValidatorReference v2 = new ValidatorReference(); + v2.ObjectFactory = factory; + v2.Name = "cv2"; + v2.Context = Expression.Parse("DOB"); + + Assert.IsTrue(v2.Validate(context, null, errors)); + Assert.IsTrue(v2.Validate(context, errors)); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/ConditionValidatorTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/ConditionValidatorTests.cs new file mode 100644 index 00000000..805609ea --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/ConditionValidatorTests.cs @@ -0,0 +1,100 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; + +using NUnit.Framework; + +using Spring.Context.Support; +using Spring.Expressions; +using Spring.Validation.Actions; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Unit tests for the ConditionValidator class. + /// + /// Rick Evans + /// $Id: ConditionValidatorTests.cs,v 1.9 2008/02/05 20:40:27 aseovic Exp $ + [TestFixture] + public sealed class ConditionValidatorTests + { + [Test] + public void StraightTrue() + { + ConditionValidator validator = new ConditionValidator(); + validator.Test = Expression.Parse("true"); + Assert.IsTrue(validator.Validate(null, new ValidationErrors())); + } + + [Test] + public void StraightFalse() + { + ConditionValidator validator = new ConditionValidator("false", null); + Assert.IsFalse(validator.Validate(null, new ValidationErrors())); + } + + [Test] + public void TrueScalarExpression() + { + Inventor tesla = new Inventor(); + tesla.Name = "Nikola Tesla"; + + ConditionValidator validator = new ConditionValidator(Expression.Parse("Name == 'Nikola Tesla'"), null); + Assert.IsTrue(validator.Validate(tesla, new ValidationErrors())); + } + + [Test] + public void FalseScalarExpression() + { + Inventor tesla = new Inventor(); + tesla.Name = "Soltan Gris"; + + ConditionValidator validator = new ConditionValidator(Expression.Parse("Name == 'Nikola Tesla'"), null); + validator.Actions = new ErrorMessageAction[] {new ErrorMessageAction("Wrong name", "InventorValidator") }; + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(tesla, errors)); + Assert.IsFalse(errors.IsEmpty); + IList namedErrors = errors.GetResolvedErrors("InventorValidator", new NullMessageSource()); + Assert.AreEqual(1, namedErrors.Count); + string error = (string) namedErrors[0]; + Assert.AreEqual("Wrong name", error); + } + + [Test] + public void WhenValidatorIsNotEvaluatedBecauseWhenExpressionReturnsFalse() + { + ConditionValidator validator = new ConditionValidator(); + validator.Test = Expression.Parse("false"); + validator.When = Expression.Parse("false"); + IValidationErrors errors = new ValidationErrors(); + + bool valid = validator.Validate(new object(), null, errors); + + Assert.IsTrue(valid, "Validation should succeed when condition validator is not evaluated."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/CreditCardValidatorTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/CreditCardValidatorTests.cs new file mode 100644 index 00000000..7d0edaf5 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/CreditCardValidatorTests.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Unit tests for the UrlValidator class. + /// + /// Goran Milosavljevic + [TestFixture] + public sealed class CreditCardValidatorTests + { + [Test] + public void Validate() + { + CreditCardValidator validator = new CreditCardValidator(); + validator.CardType = new Amex(); + Assert.IsTrue(validator.Validate("378282246310005", new ValidationErrors())); + Assert.IsFalse(validator.Validate("444444444", new ValidationErrors())); + Assert.IsTrue(validator.Validate(" ", new ValidationErrors())); + Assert.IsTrue(validator.Validate("", new ValidationErrors())); + Assert.IsTrue(validator.Validate(null, new ValidationErrors())); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/EmailValidatorTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/EmailValidatorTests.cs new file mode 100644 index 00000000..6fd12878 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/EmailValidatorTests.cs @@ -0,0 +1,62 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Text.RegularExpressions; +using NUnit.Framework; + +using Spring.Context.Support; +using Spring.Expressions; +using Spring.Validation.Actions; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Unit tests for the EmailValidator class. + /// + /// Goran Milosavljevic + [TestFixture] + public sealed class EmailValidatorTests + { + [Test] + public void Validate() + { + EmailValidator validator = new EmailValidator(); + Assert.IsTrue(validator.Validate("goran@eu.s4hc.com", new ValidationErrors())); + Assert.IsTrue(validator.Validate("goran.milosavljevic@s4hc.com", new ValidationErrors())); + Assert.IsTrue(validator.Validate("g.m.m@web_ask.com", new ValidationErrors())); + + Assert.IsFalse(validator.Validate("@eu.s4hc.com", new ValidationErrors())); + Assert.IsFalse(validator.Validate("g @s4hc.com", new ValidationErrors())); + Assert.IsFalse(validator.Validate("g&@s", new ValidationErrors())); + Assert.IsFalse(validator.Validate("goran@s", new ValidationErrors())); + Assert.IsFalse(validator.Validate("goran@@", new ValidationErrors())); + Assert.IsFalse(validator.Validate("goran@eu s4hc.com", new ValidationErrors())); + Assert.IsTrue(validator.Validate(" ", new ValidationErrors())); + Assert.IsTrue(validator.Validate("", new ValidationErrors())); + Assert.IsTrue(validator.Validate(null, new ValidationErrors())); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/ISBNValidatorTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/ISBNValidatorTests.cs new file mode 100644 index 00000000..d5679410 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/ISBNValidatorTests.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Unit tests for the ISBNValidator class. + /// + /// Goran Milosavljevic + [TestFixture] + public sealed class ISBNValidatorTests + { + [Test] + public void Validate() + { + ISBNValidator validator = new ISBNValidator(); + // validate ISBN10 + Assert.IsTrue(validator.Validate("90-70002-34-5", new ValidationErrors())); + Assert.IsTrue(validator.Validate("1575843013", new ValidationErrors())); + Assert.IsTrue(validator.Validate("81-7525-766-0", new ValidationErrors())); + Assert.IsTrue(validator.Validate("1905158793", new ValidationErrors())); + + // validate ISBN13 + Assert.IsTrue(validator.Validate("978-1-905158-79-9", new ValidationErrors())); + Assert.IsTrue(validator.Validate("978-81-7525-766-5", new ValidationErrors())); + Assert.IsTrue(validator.Validate("978-90-70002-34-3", new ValidationErrors())); + Assert.IsTrue(validator.Validate("9789070002343", new ValidationErrors())); + Assert.IsTrue(validator.Validate("978907000234-3", new ValidationErrors())); + Assert.IsTrue(validator.Validate("9789070002-34-3", new ValidationErrors())); + + Assert.IsFalse(validator.Validate("9789g70002-34-3", new ValidationErrors())); + Assert.IsFalse(validator.Validate("a789g70002343", new ValidationErrors())); + Assert.IsFalse(validator.Validate("978907000234x", new ValidationErrors())); + Assert.IsTrue(validator.Validate("", new ValidationErrors())); + Assert.IsTrue(validator.Validate(" ", new ValidationErrors())); + Assert.IsTrue(validator.Validate(null, new ValidationErrors())); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/CreditCardValidatorIntegrationTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/CreditCardValidatorIntegrationTests.cs new file mode 100644 index 00000000..579ecabd --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/CreditCardValidatorIntegrationTests.cs @@ -0,0 +1,118 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Text; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Objects; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// CreditCardValidator integration tests. + /// + /// Goran Milosavljevic + [TestFixture] + public class CreditCardValidatorIntegrationTests + { + [Test] + public void CreditCardValidatorTests() + { + const string xml = @" + + + + + + + "; + + MemoryStream stream = new MemoryStream(new UTF8Encoding().GetBytes(xml)); + IResource resource = new InputStreamResource(stream, "ccValidator"); + + XmlObjectFactory objectFactory = new XmlObjectFactory(resource, null); + object obj = objectFactory.GetObject("ccValidator"); + + Assert.IsTrue(obj is CreditCardValidator); + CreditCardValidator validator = obj as CreditCardValidator; + Assert.IsNotNull(validator.CardType); + Assert.IsTrue(validator.CardType is Amex); + Assert.IsTrue(validator.Validate("378282246310005", new ValidationErrors())); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WithNullCardType() + { + const string xml = @" + + + + + "; + + MemoryStream stream = new MemoryStream(new UTF8Encoding().GetBytes(xml)); + IResource resource = new InputStreamResource(stream, "ccValidator"); + + XmlObjectFactory objectFactory = new XmlObjectFactory(resource, null); + object obj = objectFactory.GetObject("ccValidator"); + + Assert.IsTrue(obj is CreditCardValidator); + CreditCardValidator validator = obj as CreditCardValidator; + Assert.IsNull(validator.CardType); + Assert.IsTrue(validator.Validate("378282246310005", new ValidationErrors())); + } + + [Test] + public void ErrorTests() + { + const string xml = @" + + + + + + + "; + + MemoryStream stream = new MemoryStream(new UTF8Encoding().GetBytes(xml)); + IResource resource = new InputStreamResource(stream, "ccValidator"); + + XmlObjectFactory objectFactory = new XmlObjectFactory(resource, null); + + IValidationErrors errors = new ValidationErrors(); + Contact contact = new Contact(); + IValidator validator = (IValidator)objectFactory.GetObject("ccValidator"); + + contact.Creditcard = "378282246310"; + bool result = validator.Validate(contact, errors); + + Assert.IsNotNull(errors.GetErrors("validationSummary")); + Assert.IsFalse(result); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/EmailValidatorIntegrationTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/EmailValidatorIntegrationTests.cs new file mode 100644 index 00000000..da3db52f --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/EmailValidatorIntegrationTests.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.IO; +using System.Text; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// EmailValidatorIntegration integration tests. + /// + /// Goran Milosavljevic + [TestFixture] + public class EmailValidatorIntegrationTests + { + [Test] + public void EmailValidatorTests() + { + const string xml = @" + + + "; + + MemoryStream stream = new MemoryStream(new UTF8Encoding().GetBytes(xml)); + IResource resource = new InputStreamResource(stream, "emailValidator"); + + XmlObjectFactory objectFactory = new XmlObjectFactory(resource, null); + object obj = objectFactory.GetObject("emailValidator"); + + Assert.IsTrue(obj is IValidator); + IValidator validator = obj as IValidator; + Assert.IsTrue(validator.Validate("goran@goran.com", new ValidationErrors())); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/ISBNValidatorIntegrationTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/ISBNValidatorIntegrationTests.cs new file mode 100644 index 00000000..c9abd336 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/ISBNValidatorIntegrationTests.cs @@ -0,0 +1,61 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.IO; +using System.Text; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// ISBNValidatorIntegration integration tests. + /// + /// Goran Milosavljevic + [TestFixture] + public class ISBNValidatorIntegrationTests + { + [Test] + public void ISBNValidatorTests() + { + const string xml = @" + + + + + "; + + MemoryStream stream = new MemoryStream(new UTF8Encoding().GetBytes(xml)); + IResource resource = new InputStreamResource(stream, "isbnValidator"); + + XmlObjectFactory objectFactory = new XmlObjectFactory(resource, null); + object obj = objectFactory.GetObject("isbnValidator"); + + Assert.IsTrue(obj is IValidator); + IValidator validator = obj as IValidator; + Assert.IsTrue(validator.Validate("978-1-905158-79-9", new ValidationErrors())); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/UrlValidatorIntegrationTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/UrlValidatorIntegrationTests.cs new file mode 100644 index 00000000..0ae69b93 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/Integration/UrlValidatorIntegrationTests.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.IO; +using System.Text; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// UrlValidatorIntegration integration tests. + /// + /// Goran Milosavljevic + [TestFixture] + public class UrlValidatorIntegrationTests + { + [Test] + public void ISBNValidatorTests() + { + const string xml = @" + + + "; + + MemoryStream stream = new MemoryStream(new UTF8Encoding().GetBytes(xml)); + IResource resource = new InputStreamResource(stream, "urlValidator"); + + XmlObjectFactory objectFactory = new XmlObjectFactory(resource, null); + object obj = objectFactory.GetObject("urlValidator"); + + Assert.IsTrue(obj is IValidator); + IValidator validator = obj as IValidator; + Assert.IsTrue(validator.Validate("http://www.springframework.net", new ValidationErrors())); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/RegularExpressionValidatorTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/RegularExpressionValidatorTests.cs new file mode 100644 index 00000000..a1e393a3 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/RegularExpressionValidatorTests.cs @@ -0,0 +1,122 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Text.RegularExpressions; + +using NUnit.Framework; + +using Spring.Expressions; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Unit tests for the RegularExpressionValidator class. + /// + /// Rick Evans + [TestFixture] + public sealed class RegularExpressionValidatorTests + { + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WithNonString() + { + RegularExpressionValidator validator = new RegularExpressionValidator(); + validator.Validate(this, new ValidationErrors()); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WithNull() + { + RegularExpressionValidator validator = new RegularExpressionValidator(); + validator.Validate(null, new ValidationErrors()); + } + + [Test] + public void EmptyStringValidatesToTrue() + { + RegularExpressionValidator validator = new RegularExpressionValidator(); + Assert.IsTrue(validator.Validate(string.Empty, new ValidationErrors())); + } + + [Test] + public void WhitespaceStringDoesntEvaluateToTrueByDefault() + { + RegularExpressionValidator validator = new RegularExpressionValidator(); + Assert.IsFalse(validator.Validate(" ", new ValidationErrors())); + } + + [Test] + public void WhitespaceStringOnlyValidatesToTrueWhenGivenMatchingRegex() + { + RegularExpressionValidator validator = new RegularExpressionValidator(); + validator.Expression = @"\s*"; + Assert.IsTrue(validator.Validate(" ", new ValidationErrors())); + } + + [Test] + public void CaseSensitiveStringMatching() + { + RegularExpressionValidator validator = new RegularExpressionValidator("ToString()", "true", @"[A-Z][a-z]*"); + Assert.IsTrue(validator.Validate("Aleksandar", new ValidationErrors())); + Assert.IsFalse(validator.Validate("ALEKSANDAR", new ValidationErrors())); + Assert.IsFalse(validator.Validate("aleksandar", new ValidationErrors())); + } + + [Test] + public void CaseInsensitiveStringMatching() + { + RegularExpressionValidator validator = new RegularExpressionValidator("ToString()", "true", @"[A-Z][a-z]*"); + validator.Options = RegexOptions.IgnoreCase; + Assert.IsTrue(validator.Validate("Aleksandar", new ValidationErrors())); + Assert.IsTrue(validator.Validate("ALEKSANDAR", new ValidationErrors())); + Assert.IsTrue(validator.Validate("aleksandar", new ValidationErrors())); + } + + [Test] + public void SunnyDayFailure_Invalid() + { + RegularExpressionValidator validator = new RegularExpressionValidator(Expression.Parse("'ljwdf87cwbh'"), Expression.Parse("true"), @"((\d{1,2}\.\d{1,3}\.\d{1,3}\.\d{1,3}))"); + Assert.IsFalse(validator.Validate("ljwdf87cwbh", new ValidationErrors())); + } + + [Test] + public void SunnyDay_Valid() + { + RegularExpressionValidator validator = new RegularExpressionValidator(); + validator.Expression = @"((\d{1,2}\.\d{1,3}\.\d{1,3}\.\d{1,3}))"; + Assert.IsTrue(validator.Validate("11.222.333.444", new ValidationErrors())); + } + + [Test] + public void WhenValidatorIsNotEvaluatedBecauseWhenExpressionReturnsFalse() + { + RegularExpressionValidator validator = new RegularExpressionValidator("'ljwdf87cwbh'", "false", @"((\d{1,2}\.\d{1,3}\.\d{1,3}\.\d{1,3}))"); + bool valid = validator.Validate(null, new ValidationErrors()); + Assert.IsTrue(valid, "Validation should succeed when regex validator is not evaluated."); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/RequiredValidatorTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/RequiredValidatorTests.cs new file mode 100644 index 00000000..d82ec0cf --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/RequiredValidatorTests.cs @@ -0,0 +1,195 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +using Spring.Expressions; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Unit tests for the RequiredValidator class. + /// + /// Rick Evans + /// $Id: RequiredValidatorTests.cs,v 1.7 2008/02/05 20:40:27 aseovic Exp $ + [TestFixture] + public sealed class RequiredValidatorTests + { + [Test] + public void WithNull() + { + RequiredValidator validator = new RequiredValidator(); + validator.Test = Expression.Parse("null"); + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(null, errors)); + } + + [Test] + public void WithZeroNumber() + { + RequiredValidator validator = new RequiredValidator("0", null); + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(null, errors)); + } + + [Test] + public void WithPositiveNumber() + { + RequiredValidator validator = new RequiredValidator(Expression.Parse("100"), null); + IValidationErrors errors = new ValidationErrors(); + Assert.IsTrue(validator.Validate(null, errors)); + } + + [Test] + public void WithNegativeNumber() + { + RequiredValidator validator = new RequiredValidator(); + validator.Test = Expression.Parse("-100"); + IValidationErrors errors = new ValidationErrors(); + Assert.IsTrue(validator.Validate(null, errors)); + } + + [Test] + public void WithEmptyString() + { + RequiredValidator validator = new RequiredValidator("''", null); + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(null, errors)); + } + + [Test] + public void WithWhitespaceOnlyString() + { + RequiredValidator validator = new RequiredValidator(Expression.Parse("' '"), null); + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(null, errors)); + } + + [Test] + public void WithKosherString() + { + RequiredValidator validator = new RequiredValidator(); + validator.Test = Expression.Parse("'some non-empty string'"); + IValidationErrors errors = new ValidationErrors(); + Assert.IsTrue(validator.Validate(null, errors)); + } + + [Test] + public void WithKosherDate() + { + RequiredValidator validator = new RequiredValidator("DateTime.Today", null); + IValidationErrors errors = new ValidationErrors(); + Assert.IsTrue(validator.Validate(null, errors)); + } + + [Test] + public void WithMinDate() + { + RequiredValidator validator = new RequiredValidator(Expression.Parse("DateTime.MinValue"), null); + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(null, errors)); + } + + [Test] + public void WithMaxDate() + { + RequiredValidator validator = new RequiredValidator(); + validator.Test = Expression.Parse("DateTime.MaxValue"); + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(null, errors)); + } + + [Test] + public void WithZeroFloat() + { + RequiredValidator validator = new RequiredValidator("0.00F", null); + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(null, errors)); + } + + [Test] + public void WithKosherFloat() + { + RequiredValidator validator = new RequiredValidator(Expression.Parse("5.25F"), null); + IValidationErrors errors = new ValidationErrors(); + Assert.IsTrue(validator.Validate(null, errors)); + } + + [Test] + public void WithZeroDouble() + { + RequiredValidator validator = new RequiredValidator("0.00D", null); + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(null, errors)); + } + + [Test] + public void WithKosherDouble() + { + RequiredValidator validator = new RequiredValidator("5.25D", null); + IValidationErrors errors = new ValidationErrors(); + Assert.IsTrue(validator.Validate(null, errors)); + } + + [Test] + public void WithMinChar() + { + RequiredValidator validator = new RequiredValidator(); + validator.Test = Expression.Parse("char.MinValue"); + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(null, errors)); + } + + [Test] + public void WithWhitespaceChar() + { + RequiredValidator validator = new RequiredValidator(); + validator.Test = Expression.Parse("' '.ToCharArray()[0]"); + IValidationErrors errors = new ValidationErrors(); + Assert.IsFalse(validator.Validate(null, errors)); + } + + [Test] + public void WithKosherChar() + { + RequiredValidator validator = new RequiredValidator(); + validator.Test = Expression.Parse("'xyz'.ToCharArray()[1]"); + IValidationErrors errors = new ValidationErrors(); + Assert.IsTrue(validator.Validate(null, errors)); + } + + [Test] + public void WhenValidatorIsNotEvaluatedBecauseWhenExpressionReturnsFalse() + { + RequiredValidator validator = new RequiredValidator("DateTime.MinValue", "false"); + IValidationErrors errors = new ValidationErrors(); + + bool valid = validator.Validate(new object(), null, errors); + + Assert.IsTrue(valid, "Validation should succeed when required validator is not evaluated."); + Assert.AreEqual(0, errors.GetErrors("errors").Count); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/Validators/UrlValidatorTests.cs b/test/Spring/Spring.Core.Tests/Validation/Validators/UrlValidatorTests.cs new file mode 100644 index 00000000..cd430002 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Validation/Validators/UrlValidatorTests.cs @@ -0,0 +1,67 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Text.RegularExpressions; +using NUnit.Framework; + +using Spring.Context.Support; +using Spring.Expressions; +using Spring.Validation.Actions; + +#endregion + +namespace Spring.Validation.Validators +{ + /// + /// Unit tests for the UrlValidator class. + /// + /// Goran Milosavljevic + [TestFixture] + public sealed class UrlValidatorTests + { + [Test] + public void Validate() + { + UrlValidator validator = new UrlValidator(); + Assert.IsTrue(validator.Validate("http://puzzle.com", new ValidationErrors())); + Assert.IsTrue(validator.Validate("http://www.com", new ValidationErrors())); + Assert.IsTrue(validator.Validate("www.1.org", new ValidationErrors())); + Assert.IsTrue(validator.Validate("ww.1.com", new ValidationErrors())); + Assert.IsTrue(validator.Validate("http://www.google-com.123.com", new ValidationErrors())); + Assert.IsTrue(validator.Validate("https://www.google-com.com", new ValidationErrors())); + Assert.IsTrue(validator.Validate("http://google-com.com", new ValidationErrors())); + // just to make sure that we are ready for the Japanese market :) + Assert.IsTrue(validator.Validate("www.amazon.co.jp/C-ã«ã‚ˆã‚‹ãƒ—ログラミングWindows-上-Charles-Petzold/dp/4891002921", new ValidationErrors())); + + Assert.IsFalse(validator.Validate("http://.com", new ValidationErrors())); + Assert.IsFalse(validator.Validate("http://www.google-com.123", new ValidationErrors())); + Assert.IsFalse(validator.Validate("http://1.1", new ValidationErrors())); + Assert.IsFalse(validator.Validate("ht:1.1", new ValidationErrors())); + Assert.IsFalse(validator.Validate("/:www.1.com", new ValidationErrors())); + Assert.IsTrue(validator.Validate("", new ValidationErrors())); + Assert.IsTrue(validator.Validate(" ", new ValidationErrors())); + Assert.IsTrue(validator.Validate(null, new ValidationErrors())); + } + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/AssemblyInfo.cs b/test/Spring/Spring.Data.Integration.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..6263a7ed --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("Spring.Data.Integration Tests")] +[assembly: AssemblyDescription("Integration tests for Spring.Data assembly")] \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/AccountCreditDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/AccountCreditDao.cs new file mode 100644 index 00000000..50a9f67c --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/AccountCreditDao.cs @@ -0,0 +1,24 @@ +using System; +using System.Data; +using Spring.Data.Core; +using Spring.Transaction.Interceptor; + +namespace Spring.Data +{ + public class AccountCreditDao : AdoDaoSupport, IAccountCreditDao + { + + public AccountCreditDao() + { + } + + [Transaction()] + public void CreateCredit(float creditAmount) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + String.Format("insert into Credits (CreditAmount) VALUES ({0})", + creditAmount)); + } + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/AccountDebitDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/AccountDebitDao.cs new file mode 100644 index 00000000..16e26a82 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/AccountDebitDao.cs @@ -0,0 +1,23 @@ +using System; +using System.Data; +using Spring.Data.Core; +using Spring.Data.Support; + +namespace Spring.Data +{ + public class AccountDebitDao : AdoDaoSupport, IAccountDebitDao + { + + public AccountDebitDao() + { + } + + public void DebitAccount(float debitAmount) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, String.Format("insert into dbo.Debits (DebitAmount) VALUES ({0})", + debitAmount)); + } + + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/AccountManager.cs b/test/Spring/Spring.Data.Integration.Tests/Data/AccountManager.cs new file mode 100644 index 00000000..7de83921 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/AccountManager.cs @@ -0,0 +1,40 @@ + +using System; +using System.Data; +using Spring.Transaction.Interceptor; + +namespace Spring.Data +{ + public class AccountManager : IAccountManager + { + private IAccountCreditDao creditDao; + private IAccountDebitDao debitDao; + + public AccountManager() + { + } + + public IAccountCreditDao AccountCreditDao + { + get { return creditDao; } + set { creditDao = value; } + } + + public IAccountDebitDao AccountDebitDao + { + get { return debitDao; } + set { debitDao = value; } + } + + [Transaction] + public void DoTransfer(float creditAmount, float debitAmount) + { + creditDao.CreateCredit(creditAmount); + debitDao.DebitAccount(debitAmount); + + } + + } + + +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/AdoDaoTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/AdoDaoTests.cs new file mode 100644 index 00000000..120708b7 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/AdoDaoTests.cs @@ -0,0 +1,87 @@ +using System; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Dao; +using Spring.Objects; + +namespace Spring.Data +{ + /// + /// Summary description for AdoTemplateTests. + /// + [TestFixture] + public class AdoDaoTests + { + public AdoDaoTests() + { + } + + [Test] + public void SimpleCreate() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/nativeAdoTests.xml"); + Assert.IsNotNull(ctx); + ITestObjectDao dao = (ITestObjectDao)ctx["testObjectDao"]; + Assert.IsNotNull(dao); + dao.Create("John", 44); + } + [Test] + public void SimpleDao() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/templateTests.xml"); + Assert.IsNotNull(ctx); + TestObjectDao dao = (TestObjectDao)ctx["testObjectDao"]; + Assert.IsNotNull(dao); + Assert.AreEqual(10, dao.GetCount()); + + Assert.AreEqual(3, dao.GetCount(33)); + Assert.AreEqual(3, dao.GetCountByAltMethod(33)); + Assert.AreEqual(3, dao.GetCountByCommandSetter(33)); + Assert.AreEqual(2, dao.GetCount(33,"George")); + + + } + + [Test] + public void SimpleDao2() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/templateTests.xml"); + Assert.IsNotNull(ctx); + TestObjectDao dao = (TestObjectDao)ctx["testObjectDao"]; + Assert.IsNotNull(dao); + Assert.AreEqual(1, dao.GetCountByDelegate()); + } + + [Test] + public void DaoOperations() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/templateTests.xml"); + Assert.IsNotNull(ctx); + TestObjectDao dao = (TestObjectDao)ctx["testObjectDao"]; + dao.Create("George", 33); + TestObject to = dao.FindByName("George"); + Assert.IsNotNull(to); + Assert.AreEqual("George", to.Name); + Assert.AreEqual(33, to.Age); + + to.Age=34; + dao.Update(to); + + TestObject to2 = dao.FindByName("George"); + Assert.AreEqual(34, to2.Age); + + dao.Delete("George"); + + TestObject to3 = dao.FindByName("George"); + Assert.IsNull(to3); + + + + } + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/AdoTemplatePerformanceTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/AdoTemplatePerformanceTests.cs new file mode 100644 index 00000000..40606197 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/AdoTemplatePerformanceTests.cs @@ -0,0 +1,269 @@ +#if NET_2_0 +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using System.Data.SqlClient; +using System.Diagnostics; +using System.Threading; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Core; +using Spring.Reflection.Dynamic; +using Spring.Transaction; +using Spring.Transaction.Support; +using Spring.Util; + +#endregion + +namespace Spring.Data +{ + /// + /// This calss contains tests for + /// + /// Mark Pollack + /// $Id: AdoTemplatePerformanceTests.cs,v 1.1 2008/02/04 22:44:59 markpollack Exp $ + [TestFixture] + public class AdoTemplatePerformanceTests + { + private AdoTemplate adoTemplate; + private string cmdText = "insert into TestObjects(Age, Name) VALUES (24, 'John')"; + private DateTime start, stop; + + [SetUp] + public void CreateAdoTemplate() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/adoTemplateTests.xml"); + Assert.IsNotNull(ctx); + adoTemplate = ctx["adoTemplate"] as AdoTemplate; + Assert.IsNotNull(adoTemplate); + //CleanDb(); + } + + private void CleanDb() + { + adoTemplate.ExecuteNonQuery(CommandType.Text, "truncate table TestObjects"); + } + + [Test] + public void PerformanceWithOneTxTests() + { + UseAdoTemplateApiOneTx(1000); + CleanDb(); + } + + [Test] + public void PerformanceTests() + { + for (int i = 0; i < 10; i++) + DoTest(10000, true, 5000, true); + } + + [Test] + public void ObjectInstantiatonTests() + { + int numIterations = 1000000; + + + start = DateTime.Now; + Type t = typeof (AccountCreditDao); + for (int i = 0; i < numIterations; i++) + { + //ObjectUtils.IsInstantiable(t); + IDynamicConstructor dc = DynamicConstructor.Create(t.GetConstructor(Type.EmptyTypes)); + dc.Invoke(ObjectUtils.EmptyObjects); + } + stop = DateTime.Now; + double timeElapsed = Elapsed; + PrintTest("SafeConstructor", numIterations, timeElapsed); + + start = DateTime.Now; + for (int i = 0; i < numIterations; i++) + { + ObjectUtils.InstantiateType(typeof (AccountCreditDao)); + } + stop = DateTime.Now; + timeElapsed = Elapsed; + PrintTest("InstantiateType", numIterations, timeElapsed); + + } + + private void DoTest(int numIterations, bool standardApiFirst, int sleepTimeMs, bool oneTx) + { + Console.WriteLine("sleeping for " + sleepTimeMs); + Thread.Sleep(sleepTimeMs); + double stdTime; + double templateTime; + if (standardApiFirst) + { + if (oneTx) + { + stdTime = UseStandardApiOneTx(numIterations); + } + else + { + stdTime = UseStandardApi(numIterations); + } + + CleanDb(); + Console.WriteLine("sleeping for " + sleepTimeMs); + Thread.Sleep(sleepTimeMs); + + if (oneTx) + { + templateTime = UseAdoTemplateApiOneTx(numIterations); + } + else + { + templateTime = UseAdoTemplateApi(numIterations); + } + + CleanDb(); + } + else + { + if (oneTx) + { + templateTime = UseAdoTemplateApiOneTx(numIterations); + } + else + { + templateTime = UseAdoTemplateApi(numIterations); + } + + CleanDb(); + Console.WriteLine("sleeping for " + sleepTimeMs); + Thread.Sleep(sleepTimeMs); + + if (oneTx) + { + stdTime = UseStandardApiOneTx(numIterations); + } + else + { + stdTime = UseStandardApi(numIterations); + } + + CleanDb(); + } + Console.WriteLine("stdTime = " + stdTime + ", templateTime = " + templateTime); + double percentDiff = ((2*Math.Abs(stdTime - templateTime))/(stdTime + templateTime))*100; + Console.WriteLine("stdTime-templateTime=" + (stdTime - templateTime)); + Console.WriteLine("% diff = " + percentDiff); + } + + private double UseAdoTemplateApiOneTx(int numIterations) + { + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(adoTemplate.DbProvider); + + TransactionTemplate tt = new TransactionTemplate(tm); + double timeElapsed = 0; + tt.Execute(delegate(ITransactionStatus status) + { + start = DateTime.Now; + for (int i = 0; i < numIterations; i++) + { + adoTemplate.ExecuteNonQuery(CommandType.Text, cmdText); + } + stop = DateTime.Now; + timeElapsed = Elapsed; + PrintTest("AdoTemplateApi", numIterations, timeElapsed); + return null; + }); + return timeElapsed; + } + + private double UseAdoTemplateApi(int numIterations) + { + start = DateTime.Now; + for (int i = 0; i < numIterations; i++) + { + adoTemplate.ExecuteNonQuery(CommandType.Text, cmdText); + } + stop = DateTime.Now; + double timeElapsed = Elapsed; + PrintTest("AdoTemplateApi", numIterations, timeElapsed); + return timeElapsed; + } + + private double UseStandardApiOneTx(int numIterations) + { + start = DateTime.Now; + using (SqlConnection connection = new SqlConnection(adoTemplate.DbProvider.ConnectionString)) + { + using (SqlCommand command = new SqlCommand(cmdText, connection)) + { + connection.Open(); + SqlTransaction transaction = connection.BeginTransaction(); + command.Transaction = transaction; + for (int i = 0; i < numIterations; i++) + { + command.ExecuteNonQuery(); + } + // no error handling.... + transaction.Commit(); + } + } + stop = DateTime.Now; + double timeElapsed = Elapsed; + PrintTest("StandardAPI", numIterations, timeElapsed); + return timeElapsed; + } + + private double UseStandardApi(int numIterations) + { + start = DateTime.Now; + for (int i = 0; i < numIterations; i++) + { + using (SqlConnection connection = new SqlConnection(adoTemplate.DbProvider.ConnectionString)) + { + using (SqlCommand command = new SqlCommand(cmdText, connection)) + { + connection.Open(); + command.ExecuteNonQuery(); + } + } + } + stop = DateTime.Now; + double timeElapsed = Elapsed; + PrintTest("StandardAPI", numIterations, timeElapsed); + return timeElapsed; + } + + + private double Elapsed + { + get { return (stop.Ticks - start.Ticks)/10000000f; } + } + + private static void PrintTest(string name, int iterations, double duration) + { + Debug.WriteLine( + String.Format("{0,-60} {1,12:#,###} {2,12:##0.000} {3,12:#,###}", name, iterations, duration, + iterations/duration)); + } + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/AdoTemplateTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/AdoTemplateTests.cs new file mode 100644 index 00000000..553e98af --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/AdoTemplateTests.cs @@ -0,0 +1,288 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using System.Data.Common; +using Common.Logging; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Data.Core; +using Spring.Data.Support; +using Spring.Objects; + +#endregion + +namespace Spring.Data +{ + /// + /// Simple exercising of the AdoTemplate + /// + /// Mark Pollack (.NET) + /// $Id: AdoTemplateTests.cs,v 1.11 2008/01/29 18:22:33 markpollack Exp $ + [TestFixture] + public class AdoTemplateTests + { + #region Fields + + IAdoOperations adoOperations; + IDbProvider dbProvider; + + #endregion + + #region Constants + + /// + /// The shared ILog instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (AdoTemplateTests)); + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public AdoTemplateTests() + { + + } + + #endregion + + #region Methods + + [SetUp] + public void CreateAdoTemplate() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/adoTemplateTests.xml"); + Assert.IsNotNull(ctx); + dbProvider = ctx["DbProvider"] as IDbProvider; + Assert.IsNotNull(dbProvider); + UserCredentialsDbProvider userCredentialsDbProvider = dbProvider as UserCredentialsDbProvider; + Assert.IsNotNull(userCredentialsDbProvider); + + //userCredentialsDbProvider.Username = "User ID=springqa"; + //userCredentialsDbProvider.Password = "Password=springqa"; + userCredentialsDbProvider.SetCredentialsForCurrentThread("User ID=springqa", "Password=springqa"); + adoOperations = new AdoTemplate(userCredentialsDbProvider); + + } + + + + [Test] + public void FillDataSetNoParams() + { + String sql = "select TestObjectNo, Age, Name from TestObjects"; + DataSet dataSet = new DataSet(); + adoOperations.DataSetFill(dataSet, CommandType.Text, sql); + Assert.AreEqual(1, dataSet.Tables.Count); + Assert.AreEqual(4, dataSet.Tables["Table"].Rows.Count); + + dataSet = new DataSet(); + adoOperations.DataSetFill(dataSet, CommandType.Text, sql, new string[] {"TestObjects"}); + Assert.AreEqual(1, dataSet.Tables.Count); + Assert.AreEqual(4, dataSet.Tables["TestObjects"].Rows.Count); + + dataSet = new DataSet(); + DataTableMappingCollection mappingCollection = + new DataTableMappingCollection(); + DataTableMapping testObjectsMapping = mappingCollection.Add("Table", "TestObjects"); + testObjectsMapping.ColumnMappings.Add("TestObjectNo", "UserID"); + testObjectsMapping.ColumnMappings.Add("Name", "UserName"); + adoOperations.DataSetFill(dataSet, CommandType.Text, sql, mappingCollection); + Assert.AreEqual(1, dataSet.Tables.Count); + Assert.AreEqual(4, dataSet.Tables["TestObjects"].Rows.Count); + foreach (DataRow testObjectRow in dataSet.Tables["TestObjects"].Rows) + { + Assert.IsNotNull(testObjectRow["UserID"]); + Assert.IsNotNull(testObjectRow["Age"]); + Assert.IsNotNull(testObjectRow["UserName"]); + } + + } + + [Test] + public void UpdateDataSet() + { + String sql = "select TestObjectNo, Age, Name from TestObjects"; + DataSet dataSet = new DataSet(); + adoOperations.DataSetFill(dataSet, CommandType.Text, sql, new string[] {"TestObjects"}); + + //Create and add new row. + DataRow myDataRow = dataSet.Tables["TestObjects"].NewRow(); + myDataRow["Age"] = 101; + myDataRow["Name"] = "OldManWinter"; + dataSet.Tables["TestObjects"].Rows.Add(myDataRow); + + //TODO - think about api... + IDbCommand insertCommand = dbProvider.CreateCommand(); + insertCommand.CommandText = "insert into TestObjects(Age,Name) values (@Age,@Name)"; + IDbParameters parameters = adoOperations.CreateDbParameters(); + parameters.Add("Name", DbType.String, 12, "Name"); + //TODO - remembering the -1 isn't all that natural... add string name, dbtype, string sourceCol) + //or AddSourceCol("Age", SqlDbType.Int); would copy into source col? + parameters.Add("Age", SqlDbType.Int, -1, "Age"); + + //TODO - this isn't all that natural... + ParameterUtils.CopyParameters(insertCommand, parameters); + + //insertCommand.Parameters.Add() + + adoOperations.DataSetUpdate(dataSet, "TestObjects", + insertCommand, + null, + null); + + //TODO avoid param Utils copy by adding argument... + + //adoOperations.DataSetUpdate(dataSet, "TestObjects", + // insertCommand, parameters, + // null, null, + // null, null); + + //adoOperations.DataSetUpdate(dataSet, "TestObjects", + // CommandType type, string sql, parameters, + // null, null, + // null, null); + + //TODO how about breaking up the operations... + + } + + [Test] + public void ExecuteQueryWithResultSetExtractor() + { + IResultSetExtractor rse = new TestObjectExtractor(); + String sql = "select TestObjectNo, Age, Name from TestObjects"; + IList testObjectList = (IList)adoOperations.QueryWithResultSetExtractor(CommandType.Text, sql, rse); + Assert.AreEqual(1, testObjectList.Count); + + + + } + + + [Test] + public void ExecuteNonQueryText() + { + int age = 18; + int counter = 0; + String sql = String.Format("insert into TestObjects(Age, Name) VALUES ({0}, '{1}')", + age++, "George" + counter++); + adoOperations.ExecuteNonQuery(CommandType.Text, sql); + + + sql = "insert into TestObjects(Age,Name) values (@Age,@Name)"; + + //One liners are hard due to no standard 'fallback' to use of '?' for property + //placeholders. Providers that use named parameters must always use named + //parameters in SQL string. + + //NamedParameterAdoOperations ... + + + //More portable IDbDataParameterCollection implemenation. + // -> IDbParameters + //Functionality of .NET 2.0 DbParameterCollection + common helper methods that + //are commonly found (still) in subclasses. + + //How to create parameter collections? + //1. Get as much milage/portabiliyt out of basic provider interface and + // IDbDataParameter/IDataParameterCollection + // DbParameter/DbParameterCollection + // a. Must use only base DbType (can't cast as no shared base enumeration) + // b. Must use parameter prefix + // c. CLR null and DBNull.Value mapping. + // c. IsNullable is not writable in IDbDataParameter interface (1.1 only) + // c2. SourceColumnNullMapping? + // d. No convenient Add( parameter data ) methods in + // base Parameter Collection classes + // despite prevalence in provider implementations. + // d1. re-use of parameters - parameters are aware if they have been added to + // a collection of another command object. + // e. verbose + + + IDbParameters parametersCreated = new DbParameters(dbProvider); + + IDbDataParameter p = dbProvider.CreateParameter(); + p.ParameterName = "@Name"; + p.DbType = DbType.String; + p.Size = 12; + p.Value = "George" + counter++; + parametersCreated.AddParameter(p); + + IDbDataParameter p2 = dbProvider.CreateParameter(); + p2.ParameterName = "@Age"; + p2.DbType = DbType.Int32; + p2.Value = age++; + parametersCreated.AddParameter(p2); + + + + adoOperations.ExecuteNonQuery(CommandType.Text, sql, parametersCreated); + + //2. Use IDbParameters abstraction. + // e. less verbose... + IDbParameters parameters = adoOperations.CreateDbParameters(); + parameters.Add("Name", DbType.String, 12).Value = "George" + counter++; + parameters.Add("Age", SqlDbType.Int).Value = age++; + + //Better to use date example...people like to pick provider specific subtype.. + //parameters.AddWithValue("Age", age++); + + //parameters get 'cloned' before association with command, output values + //are re-associated, and so the parameter collection is re-usable. + adoOperations.ExecuteNonQuery(CommandType.Text, sql, parameters); + + } + + + + #endregion + + private class TestObjectExtractor : IResultSetExtractor + { + public object ExtractData(IDataReader reader) + { + IList testObjects = new ArrayList(); + while(reader.Read()) + { + TestObject to = new TestObject(); + to.ObjectNumber = reader.GetInt32(0); + to.Age = reader.GetInt32(1); + to.Name = reader.GetString(2); + testObjects.Add(to); + } + return testObjects; + } + } + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/AutoDeclarativeTxTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/AutoDeclarativeTxTests.cs new file mode 100644 index 00000000..4956d835 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/AutoDeclarativeTxTests.cs @@ -0,0 +1,76 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Transaction; + +#endregion + +namespace Spring.Data +{ + /// + /// Test case that uses the approach of automatically creating + /// declarative transaction interceptors for objects identified via means + /// of transaction attributes. + /// + /// Mark Pollack (.NET) + /// $Id: AutoDeclarativeTxTests.cs,v 1.4 2007/05/26 00:43:01 markpollack Exp $ + [TestFixture] + public class AutoDeclarativeTxTests + { + private IDbProvider dbProvider; + + private IPlatformTransactionManager transactionManager; + + private IApplicationContext ctx; + + [SetUp] + public void SetUp() + { + ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/autoDeclarativeServices.xml"); + dbProvider = ctx["DbProvider"] as IDbProvider; + transactionManager = ctx["adoTransactionManager"] as IPlatformTransactionManager; + + } + + [Test] + public void DeclarativeWithAttributes() + { + ITestObjectManager mgr = ctx["testObjectManager"] as ITestObjectManager; + TestObjectDao dao = (TestObjectDao)ctx["testObjectDao"]; + TransactionTemplateTests.PerformOperations(mgr, dao); + } + + [Test] + public void CoordinatorDeclarativeWithAttributes() + { + ITestCoordinator coord = ctx["testCoordinator"] as ITestCoordinator; + TestObjectDao dao = (TestObjectDao)ctx["testObjectDao"]; + TransactionTemplateTests.PerformOperations(coord, dao); + } + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/CallCreateTestObject.cs b/test/Spring/Spring.Data.Integration.Tests/Data/CallCreateTestObject.cs new file mode 100644 index 00000000..b292e42a --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/CallCreateTestObject.cs @@ -0,0 +1,62 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Data.Common; +using Spring.Data.Objects; + +#endregion + +namespace Spring.Data +{ + /// + /// Simple stored procedure with only 'in' args to create a testobject record + /// + /// Mark Pollack (.NET) + /// $Id: CallCreateTestObject.cs,v 1.2 2006/06/21 14:07:30 markpollack Exp $ + public class CallCreateTestObject : StoredProcedure + { + private static string procedureName = "CreateTestObject"; + + public CallCreateTestObject(IDbProvider dbProvider) : base(dbProvider, procedureName) + { + DeriveParameters(); + Compile(); + } + + public void Create(string name, int age) + { + //if know the ordering of input parameters for the SP + ExecuteNonQuery(name, age); + + //if want to use named params + /* + IDictionary inParams = new Hashtable(); + inParams["@name"] = name; + inParams["@age"] = age; + IDictionary outParams = ExecuteNonQuery(inParams); + */ + } + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/ConsoleLoggingAroundAdvice.cs b/test/Spring/Spring.Data.Integration.Tests/Data/ConsoleLoggingAroundAdvice.cs new file mode 100644 index 00000000..63b1aaf4 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/ConsoleLoggingAroundAdvice.cs @@ -0,0 +1,18 @@ +using System; +using AopAlliance.Intercept; +using Common.Logging; + +namespace Spring.Data +{ + public class ConsoleLoggingAroundAdvice : IMethodInterceptor + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(ConsoleLoggingAroundAdvice)); + public object Invoke(IMethodInvocation invocation) + { + LOG.Debug("Advice executing; calling the advised method [" + invocation.Method.Name + "]"); + object returnValue = invocation.Proceed(); + LOG.Debug("Advice executed; advised method [" + invocation.Method.Name + "] returned " + returnValue); + return returnValue; + } + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/CreateTestObject.sql b/test/Spring/Spring.Data.Integration.Tests/Data/CreateTestObject.sql new file mode 100644 index 00000000..f5943fd0 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/CreateTestObject.sql @@ -0,0 +1,7 @@ +CREATE PROCEDURE CreateTestObject +{ + @Age int, + @Name varchar(15) +} AS + +INSERT INTO into TestObjects(Age, Name) VALUES (@Age, @Name) diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/CreateTestObjectNonQuery.cs b/test/Spring/Spring.Data.Integration.Tests/Data/CreateTestObjectNonQuery.cs new file mode 100644 index 00000000..c5446208 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/CreateTestObjectNonQuery.cs @@ -0,0 +1,55 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using Spring.Data.Common; +using Spring.Data.Objects; + +#endregion + +namespace Spring.Data +{ + /// + /// Simple insert non query object with 'in' args to create a testobject. + /// + /// Mark Pollack (.NET) + /// $Id: CreateTestObjectNonQuery.cs,v 1.2 2008/01/29 18:22:33 markpollack Exp $ + public class CreateTestObjectNonQuery : AdoNonQuery + { + private static string sql = "insert into TestObjects(Age,Name) values (@Age,@Name)"; + + public CreateTestObjectNonQuery(IDbProvider dbProvider) : base(dbProvider, sql) + { + DeclaredParameters.Add("Age", DbType.Int32); + DeclaredParameters.Add("Name", SqlDbType.NVarChar, 16); + Compile(); + } + + public void Create(string name, int age) + { + ExecuteNonQuery(name, age); + } + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/CreditsDebitsSchema.sql b/test/Spring/Spring.Data.Integration.Tests/Data/CreditsDebitsSchema.sql new file mode 100644 index 00000000..6566603a --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/CreditsDebitsSchema.sql @@ -0,0 +1,51 @@ +USE [CreditsAndDebits] +CREATE TABLE [Credits]( + [CreditID] [int] IDENTITY NOT NULL, + [CreditAmount] [float] NOT NULL, + CONSTRAINT [PK_CreditID] PRIMARY KEY CLUSTERED +( + [CreditID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + + +USE [CreditsAndDebits] +GO +CREATE TABLE [Debits]( + [DebitID] [int] IDENTITY NOT NULL, + [DebitAmount] [float] NOT NULL, + CONSTRAINT [PK_DebitID] PRIMARY KEY CLUSTERED +( + [DebitID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + + + +USE [Credits] +GO +CREATE TABLE [Credits]( + [CreditID] [int] IDENTITY NOT NULL, + [CreditAmount] [float] NOT NULL, + CONSTRAINT [PK_CreditID] PRIMARY KEY CLUSTERED +( + [CreditID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + + +USE [Debits] +GO +CREATE TABLE [Debits]( + [DebitID] [int] IDENTITY NOT NULL, + [DebitAmount] [float] NOT NULL, + CONSTRAINT [PK_DebitID] PRIMARY KEY CLUSTERED +( + [DebitID] ASC +) ON [PRIMARY] +) ON [PRIMARY] +GO + diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/DTC1.1AppContext.xml b/test/Spring/Spring.Data.Integration.Tests/Data/DTC1.1AppContext.xml new file mode 100644 index 00000000..ea5eb9aa --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/DTC1.1AppContext.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spring.Data.IAccountManager + + + transactionInterceptor + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/DTCAppContext.xml b/test/Spring/Spring.Data.Integration.Tests/Data/DTCAppContext.xml new file mode 100644 index 00000000..14554243 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/DTCAppContext.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/DTCAppContextNoInterfaces.xml b/test/Spring/Spring.Data.Integration.Tests/Data/DTCAppContextNoInterfaces.xml new file mode 100644 index 00000000..5fd2c76c --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/DTCAppContextNoInterfaces.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/DTCTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/DTCTests.cs new file mode 100644 index 00000000..c7dfefc3 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/DTCTests.cs @@ -0,0 +1,65 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using NUnit.Framework; +using Spring.Aop.Config; +using Spring.Context; +using Spring.Context.Support; +using Common.Logging; +using Common.Logging.Simple; +using Spring.Objects.Factory.Xml; +using Spring.Transaction.Config; + +#endregion + +namespace Spring.Data +{ + [TestFixture] + public class DTCTests + { + private IApplicationContext ctx; + + [SetUp] + public void SetUp() + { + //BasicConfigurator.Configure(); + Console.WriteLine("Hello"); + LogManager.Adapter = new ConsoleOutLoggerFactoryAdapter(new NameValueCollection()); + NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser)); + NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser)); + string ctxName = "DTCAppContext.xml"; // for .NET 2.0 + //string ctxName = "DTC1.1AppContext.xml"; // for .NET 1.1 + ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/" + ctxName); + } + + [Test] + public void DeclarativeWithAttributes() + { + IAccountManager mgr = ctx["accountManager"] as IAccountManager; + mgr.DoTransfer(100, 100); + } + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/DTCTestsNoInterfaces.cs b/test/Spring/Spring.Data.Integration.Tests/Data/DTCTestsNoInterfaces.cs new file mode 100644 index 00000000..aa1d3df6 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/DTCTestsNoInterfaces.cs @@ -0,0 +1,54 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; + +#endregion + +namespace Spring.Data +{ + [TestFixture] + public class DTCTestsNoInterfaces + { + private IApplicationContext ctx; + + [SetUp] + public void SetUp() + { + //BasicConfigurator.Configure(); + string ctxName = "DTCAppContextNoInterfaces.xml"; // for .NET 2.0 + //string ctxName = "DTC1.1AppContextNoInterfaces.xml"; // for .NET 1.1 + ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/" + ctxName); + } + + [Test] + public void DeclarativeWithAttributes() + { + SimpleAccountManager mgr = ctx["accountManager"] as SimpleAccountManager; + mgr.DoTransfer(115, 115); + } + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/DeclarativeTxTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/DeclarativeTxTests.cs new file mode 100644 index 00000000..efeba24a --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/DeclarativeTxTests.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Transaction; + +#endregion + +namespace Spring.Data +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: DeclarativeTxTests.cs,v 1.4 2006/11/15 20:31:08 aseovic Exp $ + [TestFixture] + public class DeclarativeTxTests + { + private IDbProvider dbProvider; + + private IPlatformTransactionManager transactionManager; + + private IApplicationContext ctx; + + [SetUp] + public void SetUp() + { + ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/declarativeServices.xml"); + dbProvider = ctx["DbProvider"] as IDbProvider; + transactionManager = ctx["adoTransactionManager"] as IPlatformTransactionManager; + + } + + [Test] + public void DeclarativeWithAttributes() + { + ITestObjectManager mgr = ctx["testObjectManager"] as ITestObjectManager; + TestObjectDao dao = (TestObjectDao)ctx["testObjectDao"]; + TransactionTemplateTests.PerformOperations(mgr, dao); + } + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Generic/GenericAdoTemplateTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Generic/GenericAdoTemplateTests.cs new file mode 100644 index 00000000..95674ed9 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Generic/GenericAdoTemplateTests.cs @@ -0,0 +1,128 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +#if NET_2_0 + +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Data.SqlClient; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects; + +namespace Spring.Data.Generic +{ + [TestFixture] + public class GenericAdoTemplateTests + { + private AdoTemplate adoTemplate; + + [SetUp] + public void SetUp() + { + IApplicationContext ctx = + new XmlApplicationContext( + "assembly://Spring.Data.Integration.Tests/Spring.Data.Generic/GenericAdoTemplateTests.xml"); + + adoTemplate = ctx["adoTemplate"] as AdoTemplate; + } + + [Test] + public void CommandDelegateUsage() + { + string postalCode = "1010"; + int count = adoTemplate.Execute(delegate(DbCommand command) + { + command.CommandText = + "select count(*) from Customers where PostalCode = @PostalCode"; + + DbParameter p = command.CreateParameter(); + p.ParameterName = "@PostalCode"; + p.Value = postalCode; + command.Parameters.Add(p); + + return (int) command.ExecuteScalar(); + }); + Assert.AreEqual(3, count); + } + + public void CommandDelegateUsageDownCast() + { + string postalCode = "1010"; + int count = adoTemplate.Execute(delegate(DbCommand command) + { + SqlCommand sqlCommand = command as SqlCommand; + command.CommandText = + "select count(*) from Customers where PostalCode = @PostalCode"; + + sqlCommand.Parameters.AddWithValue("@PostalCode", postalCode); + + return (int) command.ExecuteScalar(); + }); + Assert.AreEqual(3, count); + } + + [Test] + public void TestCallbacks() + { + TestObjectDao testDao = new TestObjectDao(); + testDao.AdoTemplate = adoTemplate; + IList testObjects = testDao.FindAll(); + Assert.IsNotNull(testObjects); + } + + [Test] + public void TestQueryWithCommandCreators() + { + IDbCommandCreatorFactory ccf = new IDbCommandCreatorFactory(adoTemplate.DbProvider, CommandType.Text, "select TestObjectNo, Age, Name from TestObjects", null); + IDbCommandCreator cc = ccf.NewDbCommandCreator(null); + IList testObjects = adoTemplate.QueryWithCommandCreator(cc, + new TestObjectResultSetExtractor>()); + Assert.IsNotNull(testObjects); + Assert.AreEqual(2, testObjects.Count); + foreach (TestObject o in testObjects) + { + Console.WriteLine(o); + } + } + } + + internal class TestObjectResultSetExtractor : IResultSetExtractor where T : IList, new() + { + public T ExtractData(IDataReader reader) + { + T testObjectList = new T(); + while(reader.Read()) + { + TestObject to = new TestObject(); + to.ObjectNumber = reader.GetInt32(0); + to.Age = reader.GetInt32(1); + to.Name = reader.GetString(2); + testObjectList.Add(to); + } + return testObjectList; + } + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Generic/GenericAdoTemplateTests.xml b/test/Spring/Spring.Data.Integration.Tests/Data/Generic/GenericAdoTemplateTests.xml new file mode 100644 index 00000000..852a0163 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Generic/GenericAdoTemplateTests.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Generic/ITestObjectDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Generic/ITestObjectDao.cs new file mode 100644 index 00000000..9fbc1b9c --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Generic/ITestObjectDao.cs @@ -0,0 +1,35 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +#if NET_2_0 + +using System.Collections.Generic; +using Spring.Objects; + +namespace Spring.Data.Generic +{ + public interface ITestObjectDao + { + IList FindAll(); + } +} + +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Generic/TestObjectDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Generic/TestObjectDao.cs new file mode 100644 index 00000000..1401bd62 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Generic/TestObjectDao.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +#if NET_2_0 + +using System.Collections.Generic; +using System.Data; +using Spring.Objects; + +namespace Spring.Data.Generic +{ + /// + /// This is + /// + /// + /// + /// + /// Mark Pollack + /// $Id: TestObjectDao.cs,v 1.3 2008/01/29 18:22:33 markpollack Exp $ + public class TestObjectDao : AdoDaoSupport, ITestObjectDao + { + public IList FindAll() + { + return AdoTemplate.QueryWithRowMapper(CommandType.Text, + "select TestObjectNo, Age, Name from TestObjects", + new TestObjectRowMapper()); + } + + public TestObject FindOne() + { + return AdoTemplate.QueryForObject(CommandType.Text, + "", + new TestObjectRowMapper()); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Generic/TestObjectRowMapper.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Generic/TestObjectRowMapper.cs new file mode 100644 index 00000000..07cc7751 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Generic/TestObjectRowMapper.cs @@ -0,0 +1,52 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +#if NET_2_0 + +using System.Data; +using Spring.Objects; +using Spring.Data.Generic; + +namespace Spring.Data.Generic +{ + /// + /// This is + /// + /// + /// + /// + /// Mark Pollack + /// $Id: TestObjectRowMapper.cs,v 1.3 2007/08/07 19:50:44 markpollack Exp $ + public class TestObjectRowMapper : IRowMapper where T : TestObject, new() + { + public T MapRow(IDataReader reader, int rowNum) + { + if (reader == null) return new T(); + T to = new T(); + to.ObjectNumber = reader.GetInt32(0); + to.Age = reader.GetInt32(1); + to.Name = reader.GetString(2); + return to; + } + } +} + +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/IAccountCreditDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/IAccountCreditDao.cs new file mode 100644 index 00000000..098cb67c --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/IAccountCreditDao.cs @@ -0,0 +1,9 @@ + + +namespace Spring.Data +{ + public interface IAccountCreditDao + { + void CreateCredit(float creditAmount); + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/IAccountDebitDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/IAccountDebitDao.cs new file mode 100644 index 00000000..1ae6cc46 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/IAccountDebitDao.cs @@ -0,0 +1,8 @@ + +namespace Spring.Data +{ + public interface IAccountDebitDao + { + void DebitAccount(float debitAmount); + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/IAccountManager.cs b/test/Spring/Spring.Data.Integration.Tests/Data/IAccountManager.cs new file mode 100644 index 00000000..9fb9daec --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/IAccountManager.cs @@ -0,0 +1,8 @@ + +namespace Spring.Data +{ + public interface IAccountManager + { + void DoTransfer(float creditAmount, float debitAmount); + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/ITestCoordinator.cs b/test/Spring/Spring.Data.Integration.Tests/Data/ITestCoordinator.cs new file mode 100644 index 00000000..c2700fa4 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/ITestCoordinator.cs @@ -0,0 +1,15 @@ + +using Spring.Objects; + +namespace Spring.Data +{ + public interface ITestCoordinator + { + ITestObjectManager TestObjectManager + { + get; set; + } + + void WorkOn(TestObject to1, TestObject to2); + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/ITestObjectDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/ITestObjectDao.cs new file mode 100644 index 00000000..bc7c22dd --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/ITestObjectDao.cs @@ -0,0 +1,23 @@ +using System.Collections; +using Spring.Objects; + +namespace Spring.Data +{ + public interface ITestObjectDao + { + void Create(string name, int age); + void Update(TestObject to); + void Delete(string name); + TestObject FindByName(string name); + IList FindAll(); + int GetCount(); + int GetCountByDelegate(); + + int GetCount(int lowerAgeLimit); + int GetCount(int lowerAgeLimit, string name); + + // + int GetCountByAltMethod(int lowerAgeLimit); + int GetCountByCommandSetter(int lowerAgeLimit); + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/ITestObjectManager.cs b/test/Spring/Spring.Data.Integration.Tests/Data/ITestObjectManager.cs new file mode 100644 index 00000000..1ad32bd3 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/ITestObjectManager.cs @@ -0,0 +1,11 @@ +using Spring.Objects; + +namespace Spring.Data +{ + public interface ITestObjectManager + { + void SaveTwoTestObjects(TestObject to1, TestObject to2); + + void DeleteTwoTestObjects(string name1, string name2); + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/MappingAdoQueryTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/MappingAdoQueryTests.cs new file mode 100644 index 00000000..969ececc --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/MappingAdoQueryTests.cs @@ -0,0 +1,100 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Common.Logging; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: MappingAdoQueryTests.cs,v 1.3 2006/11/13 07:05:00 markpollack Exp $ + [TestFixture] + public class MappingAdoQueryTests + { + #region Fields + IDbProvider dbProvider; + #endregion + + #region Constants + + /// + /// The shared ILog instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (MappingAdoQueryTests)); + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public MappingAdoQueryTests() + { + + } + + #endregion + + #region Properties + + #endregion + + #region Methods + [SetUp] + public void CreateDbProvider() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/adoTemplateTests.xml"); + Assert.IsNotNull(ctx); + dbProvider = ctx["DbProvider"] as IDbProvider; + Assert.IsNotNull(dbProvider); + } + + [Test] + public void MappingAdoQuery() + { + TestObjectQuery testObjectQuery = new TestObjectQuery(dbProvider); + IDictionary inParams = new Hashtable(); + inParams.Add("UName", "George"); + IList testObjectList = testObjectQuery.QueryByNamedParam(inParams); + Assert.AreEqual(2, testObjectList.Count); + } + + + + #endregion + + + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/NativeAdoTestObjectDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/NativeAdoTestObjectDao.cs new file mode 100644 index 00000000..14592522 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/NativeAdoTestObjectDao.cs @@ -0,0 +1,205 @@ +#region Imports + +using System; +using System.Collections; +using System.Data; +using System.Data.SqlClient; +using System.Text; +using Spring.Objects; + +#endregion + +namespace Spring.Data +{ + public class NativeAdoTestObjectDao : ITestObjectDao + { + private String connectionString; + + public string ConnectionString + { + get { return connectionString; } + set { connectionString = value; } + } + + + public IList ExplicitTx() + { + try + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + connection.Open(); + + using (SqlTransaction transaction = connection.BeginTransaction()) + { + using (SqlCommand command = new SqlCommand("sql")) + { + command.Connection = connection; + command.Transaction = transaction; + + /* + * dbParameters.Add("savings", DbType.Decimal).Value = b.Savings.AsDecimal(); + dbParameters.Add("accountId", DbType.Int).Value = account.EntityId; + dbParameters.Add("name", DbType.String).Value = b.Name; + */ + command.Parameters.Add("@savings", SqlDbType.Decimal).Value = 1;// = b.Savings.AsDecimal(); + command.Parameters.Add("@accountId", SqlDbType.Int).Value = 2; + command.Parameters.Add("@name", SqlDbType.NVarChar, 40).Value = 3; + command.ExecuteNonQuery(); + //foreach (Benificiary b in Account.Beneficiaries) + //{ + + //} + + transaction.Commit(); + } + } + } + } catch (Exception) + { + //log exception and rethrow + } + return null; + } + + public IList FindAllPeople() + { + IList results = new ArrayList(); + try + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + string sql = "select Name, Age from ..."; + + using (SqlCommand command = new SqlCommand(sql, connection)) + { + connection.Open(); + using (SqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + //process the result set - populate Person object, add to array + } + } + } + } + } + catch (Exception) + { + //throw application exception + } + return results; + } + + public void Create2(string name, int age) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + String sql = + String.Format("insert into TestObjects(Age, Name) " + + "VALUES ({0}, '{1}')", + age, name); + + using (SqlCommand cmd = new SqlCommand(sql, connection)) + { + connection.Open(); + cmd.ExecuteNonQuery(); + } + } + } + + public void Create(string name, int age) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + SqlParameter p1 = new SqlParameter("name", SqlDbType.NVarChar); + p1.Value = "asdf"; + + /* + string strSql = "insert into TestObjects(Age,Name) values (@Age,@Name)"; + + + SqlCommand comm = connection.CreateCommand(); + comm.CommandText = strSql; + SqlParameter p1 = new SqlParameter(); + p1.ParameterName = "@Age"; + p1.Value = age; + + SqlParameter p2 = new SqlParameter(); + p2.ParameterName = "@Name"; + p2.Value = name; + + comm.Parameters.Add(p1); + comm.Parameters.Add(p2); + + */ + + StringBuilder sb = new StringBuilder(); + sb.AppendFormat( + "insert into TestObjects(Age, Name) VALUES ({0}, '{1}')", + age, name); + + SqlCommand comm = connection.CreateCommand(); + comm.CommandText = sb.ToString(); + + + connection.Open(); + comm.ExecuteNonQuery(); + } + } + + public void Update(TestObject to) + { + // TODO: Add NativeAdoTestObjectDao.Update implementation + } + + public void Delete(string name) + { + // TODO: Add NativeAdoTestObjectDao.Delete implementation + } + + public TestObject FindByName(string name) + { + // TODO: Add NativeAdoTestObjectDao.FindByName implementation + return null; + } + + public System.Collections.IList FindAll() + { + // TODO: Add NativeAdoTestObjectDao.FindAll implementation + return null; + } + + public int GetCount() + { + // TODO: Add NativeAdoTestObjectDao.GetCount implementation + return 0; + } + + public int GetCountByDelegate() + { + // TODO: Add NativeAdoTestObjectDao.GetCountByDelegate implementation + return 0; + } + + public int GetCount(int lowerAgeLimit) + { + throw new NotImplementedException(); + } + + public int GetCount(int lowerAgeLimit, string name) + { + throw new NotImplementedException(); + } + + public int GetCountByAltMethod(int lowerAgeLimit) + { + throw new NotImplementedException(); + } + + public int GetCountByCommandSetter(int lowerAgeLimit) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/NativeAdoTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/NativeAdoTests.cs new file mode 100644 index 00000000..447233e7 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/NativeAdoTests.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using System.Data.SqlClient; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; + +#endregion + +namespace Spring.Data +{ + [TestFixture] + public class NativeAdoTests + { + [Test] + public void SimpleUsage() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/nativeAdoTests.xml"); + Assert.IsNotNull(ctx); + ITestObjectDao dao = (ITestObjectDao)ctx["testObjectDao"]; + Assert.IsNotNull(dao); + dao.Create("John", 45); + } + + [Test] + public void Helloworld() + { + string connString = + @"Data Source=MARKT60\SQL2005;Initial Catalog=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"; + + SqlConnection conn = new SqlConnection(connString); + conn.Open(); + //conn.BeginTransaction(IsolationLevel.Unspecified); + SqlTransaction trans = conn.BeginTransaction(); + Console.WriteLine(trans.IsolationLevel); + + + + } + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/NestedTxScopeTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/NestedTxScopeTests.cs new file mode 100644 index 00000000..6ac147cd --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/NestedTxScopeTests.cs @@ -0,0 +1,176 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +using System.Data; +using System.Data.SqlClient; +using System.Transactions; +using NUnit.Framework; +using Spring.Data.Common; +using Spring.Data.Core; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data +{ + /// + /// This class contains tests for nesting transaction scopes. + /// + /// Mark Pollack + /// $Id: NestedTxScopeTests.cs,v 1.3 2008/05/02 20:08:29 markpollack Exp $ + [TestFixture] + public class NestedTxScopeTests + { + + + + [Test] + public void TxTemplate() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); + dbProvider.ConnectionString = @"Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"; + //IPlatformTransactionManager tm = new ServiceDomainPlatformTransactionManager(); + //IPlatformTransactionManager tm = new TxScopeTransactionManager(); + IPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + AdoTemplate adoTemplate = new AdoTemplate(dbProvider); + + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.Required; + tt.Execute(delegate(ITransactionStatus status) + { + if (System.Transactions.Transaction.Current != null) Console.WriteLine("tx 1 id = " + System.Transactions.Transaction.Current.TransactionInformation.LocalIdentifier); + Console.WriteLine("tx 1 'IsNewTransaction' = " + status.IsNewTransaction); + adoTemplate.ExecuteNonQuery(CommandType.Text, "insert into Credits (CreditAmount) VALUES (@amount)", "amount", DbType.Decimal, 0,444); + TransactionTemplate tt2 = new TransactionTemplate(tm); + tt2.PropagationBehavior = TransactionPropagation.RequiresNew; + + tt2.Execute(delegate(ITransactionStatus status2) + { + if (System.Transactions.Transaction.Current != null) Console.WriteLine("tx 2 = " + System.Transactions.Transaction.Current.TransactionInformation.LocalIdentifier); + Console.WriteLine("tx 2 'IsNewTransaction' = " + status2.IsNewTransaction); + adoTemplate.ExecuteNonQuery(CommandType.Text, "insert into dbo.Debits (DebitAmount) VALUES (@amount)", "amount", DbType.Decimal, 0,555); + //throw new ArithmeticException("can't do the math."); + status2.RollbackOnly = true; + return null; + }); + + if (System.Transactions.Transaction.Current != null) Console.WriteLine("tx id1 = " + System.Transactions.Transaction.Current.TransactionInformation.LocalIdentifier); + return null; + }); + } + + [Test] + public void TxScope() + { + Method1(); + } + private void Method1() + { + string updateSql1 = "insert into Credits (CreditAmount) VALUES (333)"; + using (TransactionScope ts = + new TransactionScope(TransactionScopeOption.Required)) + { + Console.WriteLine("tx id1 = " + + System.Transactions.Transaction.Current.TransactionInformation.LocalIdentifier); + using (SqlConnection cn2005 = new SqlConnection()) + { + cn2005.ConnectionString = + @"Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"; + SqlCommand cmd = new SqlCommand(updateSql1, cn2005); + cn2005.Open(); + cmd.ExecuteNonQuery(); + } + Method2(); + Console.WriteLine("tx id1 = " + + System.Transactions.Transaction.Current.TransactionInformation.LocalIdentifier); + ts.Complete(); + } + } + + private void Method2() + { + bool rollback = true; + string updateSql2 = "insert into Debits (DebitAmount) VALUES (222)"; + using (TransactionScope ts = + new TransactionScope(TransactionScopeOption.RequiresNew)) + { + Console.WriteLine("tx id2 = " + + System.Transactions.Transaction.Current.TransactionInformation.LocalIdentifier); + using (SqlConnection cn2005 = new SqlConnection()) + { + cn2005.ConnectionString = @"Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"; + SqlCommand cmd = new SqlCommand(updateSql2, cn2005); + cn2005.Open(); + cmd.ExecuteNonQuery(); + } + if (rollback) + { + System.Transactions.Transaction.Current.Rollback(); + } + else + { + ts.Complete(); + } + Console.WriteLine("end of 2nd data access operation"); + + } + } + + [Test] + public void UnwantedPromotion() + { + TransactionOptions transactionoptions = new TransactionOptions(); + transactionoptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted; + using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, transactionoptions)) + { + InnerMethod(); + InnerMethod(); + ts.Complete(); + } + } + + private void InnerMethod() + { + string updateSql2 = "insert into Debits (DebitAmount) VALUES (222)"; + using (SqlConnection cn2005 = new SqlConnection()) + { + cn2005.ConnectionString = @"Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"; + SqlCommand cmd = new SqlCommand(updateSql2, cn2005); + cn2005.Open(); + cmd.ExecuteNonQuery(); + + #region logging + Console.WriteLine("TransactionSynchronizationManager.CurrentTransactionIsolationLevel = " + + TransactionSynchronizationManager.CurrentTransactionIsolationLevel); + Console.WriteLine("System.Transactions.Transaction.Current.IsolationLevel = " + System.Transactions.Transaction.Current.IsolationLevel); + string name = TransactionSynchronizationManager.CurrentTransactionName; + bool read = TransactionSynchronizationManager.CurrentTransactionReadOnly; + #endregion + } + } + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/AdoTemplateShipperDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/AdoTemplateShipperDao.cs new file mode 100644 index 00000000..ffce1db9 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/AdoTemplateShipperDao.cs @@ -0,0 +1,63 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using Spring.Data.Common; +using Spring.Data.Core; +using Spring.Data.Support; + +#endregion + +namespace Spring.Data.Northwind +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: AdoTemplateShipperDao.cs,v 1.3 2007/08/03 19:51:22 markpollack Exp $ + public class AdoTemplateShipperDao : AdoDaoSupport, IShipperDao + { + #region IShipperDao Members + + public Shipper Create(string name, string phone) + { + string sql = "INSERT INTO Shippers (CompanyName, Phone) VALUES (@CompanyName, @Phone) SET @ShipperID = SCOPE_IDENTITY()"; + IDbParameters dbParameters = AdoTemplate.CreateDbParameters(); + dbParameters.Add("CompanyName", SqlDbType.NVarChar, 40).Value = name; + + //Can we add automatic nullable support? + if (phone.Length == 0) + dbParameters.Add("Phone", SqlDbType.NVarChar, 24).Value = DBNull.Value; + else + dbParameters.Add("Phone", SqlDbType.NVarChar, 24).Value = phone; + + dbParameters.AddOut("ShipperID", SqlDbType.Int); + + int id = AdoTemplate.ExecuteNonQuery(CommandType.Text, sql, dbParameters); + + return new Shipper(id, name, phone); //10 + } + + #endregion + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/IShipperDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/IShipperDao.cs new file mode 100644 index 00000000..a3adc553 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/IShipperDao.cs @@ -0,0 +1,38 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Data.Northwind +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: IShipperDao.cs,v 1.1 2006/06/21 14:07:30 markpollack Exp $ + public interface IShipperDao + { + Shipper Create(string name, string phone); + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/NativeAdoShipperDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/NativeAdoShipperDao.cs new file mode 100644 index 00000000..6eaa9ebe --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/NativeAdoShipperDao.cs @@ -0,0 +1,97 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using System.Data.SqlClient; + +#endregion + +namespace Spring.Data.Northwind +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: NativeAdoShipperDao.cs,v 1.1 2006/06/21 14:07:30 markpollack Exp $ + public class NativeAdoShipperDao : IShipperDao + { + #region IShipperDao Members + + public Shipper Create(string name, string phone) + { + string connectionString = "Data Source=NYCSUMPOLLL;Initial Catalog=Northwind;Persist Security Info=True;User ID=springqa"; + int id = 0; + using (SqlConnection connection = new SqlConnection(connectionString)) + { + using (SqlCommand insertCommand = connection.CreateCommand()) + { + insertCommand.CommandText = + "INSERT INTO Shippers (CompanyName, Phone) VALUES (@CompanyName, @Phone) SET @ShipperID = SCOPE_IDENTITY()"; + SqlParameter companyNameParameter = new SqlParameter("@CompanyName", SqlDbType.NVarChar, 40); + companyNameParameter.Value = name; + insertCommand.Parameters.Add(companyNameParameter); + SqlParameter phoneParameter = new SqlParameter("@Phone", SqlDbType.NVarChar, 24); + if (phone.Length == 0) + phoneParameter.Value = DBNull.Value; + else + phoneParameter.Value = phone; + insertCommand.Parameters.Add(phoneParameter); + SqlParameter shipperIDParameter = new SqlParameter("@ShipperID", SqlDbType.Int); + shipperIDParameter.Direction = ParameterDirection.Output; + insertCommand.Parameters.Add(shipperIDParameter); + insertCommand.Connection.Open(); + insertCommand.ExecuteNonQuery(); + id = (int) shipperIDParameter.Value; + } + } + return new Shipper(id, name, phone); //24 + } + + + public Shipper CreateShorter(string name, string phone) + { + string connectionString = "Data Source=NYCSUMPOLLL;Initial Catalog=Northwind;Persist Security Info=True;User ID=springqa"; + int id = 0; + using (SqlConnection connection = new SqlConnection(connectionString)) + { + using (SqlCommand insertCommand = connection.CreateCommand()) + { + insertCommand.CommandText = + "INSERT INTO Shippers (CompanyName, Phone) VALUES (@CompanyName, @Phone) SET @ShipperID = SCOPE_IDENTITY()"; + insertCommand.Parameters.Add("@CompanyName", SqlDbType.NVarChar, 40).Value = name; + if (phone.Length == 0) + insertCommand.Parameters.Add("Phone", SqlDbType.NVarChar, 24).Value = DBNull.Value; + else + insertCommand.Parameters.Add("Phone", SqlDbType.NVarChar, 24).Value = phone; + insertCommand.Parameters.Add("@ShipperID", SqlDbType.Int).Direction = ParameterDirection.Output; + insertCommand.Connection.Open(); + insertCommand.ExecuteNonQuery(); + id = (int) insertCommand.Parameters["@ShipperID"].Value; + } + } + return new Shipper(id, name, phone); //17 + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/Shipper.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/Shipper.cs new file mode 100644 index 00000000..e610df5f --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Northwind/Shipper.cs @@ -0,0 +1,88 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; + +#endregion + +namespace Spring.Data.Northwind +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: Shipper.cs,v 1.1 2006/06/21 14:07:30 markpollack Exp $ + public class Shipper + { + #region Fields + + public int Id + { + get { return id; } + set { id = value; } + } + + public string Name + { + get { return name; } + set { name = value; } + } + + public string Phone + { + get { return phone; } + set { phone = value; } + } + + private int id; + private string name; + private string phone; + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public Shipper() + { + + } + + public Shipper(int id, string name, string phone) + { + this.id = id; + this.name = name; + this.phone = phone; + } + + #endregion + + #region Properties + + #endregion + + #region Methods + + #endregion + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Objects/Generic/StoredProcedureTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Objects/Generic/StoredProcedureTests.cs new file mode 100644 index 00000000..85726274 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Objects/Generic/StoredProcedureTests.cs @@ -0,0 +1,180 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +#if NET_2_0 + +#region Imports + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using NUnit.Framework; +using Spring.Data.Common; +using Spring.Data.Generic; +using Spring.Data.Objects.Generic; +using Spring.Objects; + +#endregion + +namespace Spring.Data.Objects.Generic +{ + /// + /// This calss contains tests for + /// + /// Mark Pollack + /// $Id: StoredProcedureTests.cs,v 1.3 2007/08/07 19:50:44 markpollack Exp $ + [TestFixture] + public class StoredProcedureTests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void TestReflection() + { + IRowMapper rm = new TestObjectRowMapper(); + + NamedResultSetProcessor rsp = new NamedResultSetProcessor("Test", rm); + + IDictionary dict = new Hashtable(); + dict.Add("Test", rsp); + DoWork(dict); + + } + + private void DoWork(IDictionary dict) + { + BindingFlags BINDING_FLAGS = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | + BindingFlags.IgnoreCase; + object rsp = dict["Test"]; + Assert.IsNotNull(rsp); + + Type closedType = rsp.GetType(); + foreach (Type parameter in closedType.GetGenericArguments()) + { + Console.WriteLine("closed type param = " + parameter.ToString()); + } + + Console.WriteLine("closed type = " + closedType); + + // typeof(NamedResultSetProcessor<>); + Type[] genericArgumentType = closedType.GetGenericArguments(); + foreach (Type type in genericArgumentType) + { + Console.WriteLine("arg type= " + type); + } + Assert.AreEqual(1, genericArgumentType.Length); + //Type closedGenericType = openType.MakeGenericType(genericArgumentType); + + PropertyInfo propertyInfo = closedType.GetProperty("Name", BINDING_FLAGS); + Assert.IsNotNull(propertyInfo); + object returnValue = propertyInfo.GetValue(rsp, null); + + Assert.AreEqual("Test", returnValue.ToString()); + + PropertyInfo extractorPropInfo = closedType.GetProperty("RowMapper", BINDING_FLAGS); + + Assert.IsNotNull(extractorPropInfo); + + object rowmapper = extractorPropInfo.GetValue(rsp, null); + + Type rowMapperclosedType = rowmapper.GetType(); + + Console.WriteLine("rowmapper closed type= " + rowMapperclosedType); + + MethodInfo methodInfo = rowMapperclosedType.GetMethod("MapRow", BINDING_FLAGS); + + //MethodInfo genMethodInfo = methodInfo.MakeGenericMethod(genericArgumentType); + object retVal = methodInfo.Invoke(rowmapper, new object[] { null, null}); + Console.WriteLine("return val = " + retVal); + + + } + + [Test] + public void Test() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); + dbProvider.ConnectionString = + @"Data Source=MARKT60\SQL2005;Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"; + + TestObjectStoredProc sp = new TestObjectStoredProc(dbProvider); + IList testObjectList = sp.GetByName("George"); + + Assert.IsNotNull(testObjectList); + + TestObjectandVacationStoredProc vsp = new TestObjectandVacationStoredProc(dbProvider); + + System.Collections.IDictionary outParams = vsp.ExecStoreProc("George"); + + + testObjectList = outParams["testObjectRowMapper"] as IList; + Assert.IsNotNull(testObjectList); + Assert.AreEqual(1, testObjectList.Count); + + IList vacationList = outParams["vacationRowMapper"] as IList; + Assert.IsNotNull(vacationList); + Assert.AreEqual(2, vacationList.Count); + } + + } + + public class TestObjectandVacationStoredProc : StoredProcedure + { + public TestObjectandVacationStoredProc(IDbProvider dbProvider) + : base(dbProvider, "SelectTestObjectAndVacations") + { + DeriveParameters(); + AddRowMapper("testObjectRowMapper", new TestObjectRowMapper()); + AddRowMapper("vacationRowMapper", new VacationRowMapper()); + Compile(); + } + + public System.Collections.IDictionary ExecStoreProc(string name) + { + return Query(name); + } + } + + + public class TestObjectStoredProc : StoredProcedure + { + public TestObjectStoredProc(IDbProvider dbProvider) + : base(dbProvider, "SelectByName") + { + DeriveParameters(); + AddRowMapper("testObjectRowMapper", new TestObjectRowMapper() ); + Compile(); + } + + public IList GetByName(string name) + { + System.Collections.IDictionary outParams = Query(name); + return outParams["testObjectRowMapper"] as IList; + } + + } +} + +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Objects/Generic/Vacation.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Objects/Generic/Vacation.cs new file mode 100644 index 00000000..59d52966 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Objects/Generic/Vacation.cs @@ -0,0 +1,86 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Data.Objects.Generic +{ + + public class Vacation + { + private int id; + private string firstName; + private string lastName; + private int employeeId; + private DateTime startDate; + private DateTime endDate; + + + public int Id + { + get { return id; } + set { id = value; } + } + + public string FirstName + { + get { return firstName; } + set { firstName = value; } + } + + public string LastName + { + get { return lastName; } + set { lastName = value; } + } + + public int EmployeeId + { + get { return employeeId; } + set { employeeId = value; } + } + + public DateTime StartDate + { + get { return startDate; } + set { startDate = value; } + } + + public DateTime EndDate + { + get { return endDate; } + set { endDate = value; } + } + + + public override bool Equals(object obj) + { + if (this == obj) return true; + Vacation vacation = obj as Vacation; + if (vacation == null) return false; + return employeeId == vacation.employeeId && Equals(startDate, vacation.startDate); + } + + public override int GetHashCode() + { + return employeeId + 29 * startDate.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Objects/Generic/VacationRowMapper.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Objects/Generic/VacationRowMapper.cs new file mode 100644 index 00000000..640a1320 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Objects/Generic/VacationRowMapper.cs @@ -0,0 +1,54 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +#if NET_2_0 + +using System; +using System.Data; +using Spring.Data.Generic; + +namespace Spring.Data.Objects.Generic +{ + /// + /// This is + /// + /// + /// + /// + /// Mark Pollack + /// $Id: VacationRowMapper.cs,v 1.2 2007/07/25 18:43:28 markpollack Exp $ + public class VacationRowMapper : IRowMapper where T : Vacation, new() + { + public T MapRow(IDataReader reader, int rowNum) + { + T vacation = new T(); + vacation.Id = Decimal.ToInt32(reader.GetDecimal(0)); + vacation.FirstName = reader.GetString(1); + vacation.LastName = reader.GetString(2); + vacation.EmployeeId = Decimal.ToInt32(reader.GetDecimal(3)); + vacation.StartDate = reader.GetDateTime(4); + vacation.EndDate = reader.GetDateTime(5); + return vacation; + } + } +} + +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/OracleAdoTemplateTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/OracleAdoTemplateTests.cs new file mode 100644 index 00000000..112926aa --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/OracleAdoTemplateTests.cs @@ -0,0 +1,263 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if (!NET_1_0) + +#region Imports + +using System; +using System.Collections; +using System.Data; +using System.Data.Common; +using System.Data.OracleClient; +using Common.Logging; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Data.Core; +using Spring.Data.Support; +using Spring.Objects; + +#endregion + +namespace Spring.Data +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: OracleAdoTemplateTests.cs,v 1.8 2007/08/03 19:51:22 markpollack Exp $ + [TestFixture] + public class OracleAdoTemplateTests + { + #region Fields + + private IAdoOperations adoOperations; + private IDbProvider dbProvider; + #endregion + + #region Constants + + /// + /// The shared ILog instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (OracleAdoTemplateTests)); + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public OracleAdoTemplateTests() + { + + } + + #endregion + + #region Methods + [SetUp] + public void CreateAdoTemplate() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/oracleAdoTemplateTests.xml"); + Assert.IsNotNull(ctx); + dbProvider = ctx["DbProvider"] as IDbProvider; + Assert.IsNotNull(dbProvider); + adoOperations = new AdoTemplate(dbProvider); + + } + + [Test] + public void DataSetFillNoParams() + { + String sql = "select USER_ID, USER_NAME from USER_TABLE"; + DataSet dataSet = new DataSet(); + adoOperations.DataSetFill(dataSet, CommandType.Text, sql); + Assert.AreEqual(1, dataSet.Tables.Count); + Assert.AreEqual(18, dataSet.Tables["Table"].Rows.Count); + + dataSet = new DataSet(); + adoOperations.DataSetFill(dataSet, CommandType.Text, sql, new string[] {"TestObjects"}); + Assert.AreEqual(1, dataSet.Tables.Count); + Assert.AreEqual(18, dataSet.Tables["TestObjects"].Rows.Count); + + dataSet = new DataSet(); + DataTableMappingCollection mappingCollection = + new DataTableMappingCollection(); + DataTableMapping testObjectsMapping = mappingCollection.Add("Table", "TestObjects"); + testObjectsMapping.ColumnMappings.Add("USER_ID", "UserID"); + testObjectsMapping.ColumnMappings.Add("USER_NAME", "UserName"); + adoOperations.DataSetFill(dataSet, CommandType.Text, sql, mappingCollection); + Assert.AreEqual(1, dataSet.Tables.Count); + Assert.AreEqual(18, dataSet.Tables["TestObjects"].Rows.Count); + foreach (DataRow testObjectRow in dataSet.Tables["TestObjects"].Rows) + { + Assert.IsNotNull(testObjectRow["UserID"]); + Assert.IsNotNull(testObjectRow["UserName"]); + } + + } + + [Test] + public void DataSetFillWithParameters() + { + String sql = "select USER_ID, USER_NAME from USER_TABLE where USER_ID < :maxId"; + DataSet dataSet = new DataSet(); + IDbParameters parameters = adoOperations.CreateDbParameters(); + parameters.Add("maxId", OracleType.Int32).Value = 10; + adoOperations.DataSetFillWithParameters(dataSet, CommandType.Text, sql, + parameters, + new string[] {"TestObjects"}); + Assert.AreEqual(1, dataSet.Tables.Count); + Assert.AreEqual(6, dataSet.Tables["TestObjects"].Rows.Count); + } + + [Test] + public void DataSetUpdate() + { + //'pretend' unique key is the age... + String sql = "select USER_ID, USER_NAME from USER_TABLE"; + DataSet dataSet = new DataSet(); + adoOperations.DataSetFill(dataSet, CommandType.Text, sql, new string[] {"TestObjects"}); + + //Create and add new row. + DataRow myDataRow = dataSet.Tables["TestObjects"].NewRow(); + myDataRow["USER_ID"] = 101; + myDataRow["USER_NAME"] = "OldManWinter"; + dataSet.Tables["TestObjects"].Rows.Add(myDataRow); + + IDbParameters parameters = adoOperations.CreateDbParameters(); + //TODO - remembering the -1 isn't all that natural... add string name, dbtype, string sourceCol) + //or AddSourceCol("age", SqlDbType.Int); would copy into source col? + parameters.Add("id", OracleType.Int32, -1, "USER_ID"); + parameters.Add("name", DbType.String, 12, "USER_NAME"); + + + //Extanious CommandTypes.... + adoOperations.DataSetUpdate(dataSet, "TestObjects", + CommandType.Text, "insert into USER_TABLE(USER_ID, USER_NAME) values (:id,:name)", parameters, + CommandType.Text, null, null, + CommandType.Text, null, null); + + + //TODO - think about api... + /* + IDbCommand insertCommand = dbProvider.CreateCommand(); + insertCommand.CommandText = "insert into USER_TABLE(USER_ID, USER_NAME) values (:id,:name)"; + parameters = adoOperations.NewDbParameters(); + //TODO - remembering the -1 isn't all that natural... add string name, dbtype, string sourceCol) + //or AddSourceCol("age", SqlDbType.Int); would copy into source col? + parameters.Add("id", OracleType.Int32, -1, "USER_ID"); + parameters.Add("name", DbType.String, 12, "USER_NAME"); + + //TODO - this isn't all that natural... + ParameterUtils.CopyParameters(insertCommand, parameters); + + + adoOperations.DataSetUpdate(dataSet, "TestObjects", + insertCommand, + null, + null); + + */ + + //TODO avoid param Utils copy by adding argument... + + //adoOperations.DataSetUpdate(dataSet, "TestObjects", + // insertCommand, parameters, + // null, null, + // null, null); + + // or avoid all ref to command object.... + + //adoOperations.DataSetUpdate(dataSet, "TestObjects", + // CommandType type, string sql, parameters, + // null, null, + // null, null); + + //TODO how about breaking up the operations... + + } + + + [Test] + public void ExecuteQueryWithResultSetExtractor() + { + IResultSetExtractor rse = new TestObjectExtractor(); + String sql = "select USER_ID, USER_NAME from USER_TABLE"; + IList testObjectList = (IList)adoOperations.QueryWithResultSetExtractor(CommandType.Text, sql, rse); + Assert.AreEqual(18, testObjectList.Count); + + } + + [Test] + public void ExecuteNonQueryText() + { + int counter = 0; + + int user_id = 100; + + string user_name = "George0"; + + String sql = String.Format("insert into USER_TABLE(USER_ID, USER_NAME) VALUES ({0}, '{1}')", + user_id, user_name); + adoOperations.ExecuteNonQuery(CommandType.Text, sql); + + + sql = "insert into USER_TABLE(USER_ID, USER_NAME) values (:id,:name)"; + IDbParameters parameters = adoOperations.CreateDbParameters(); + + int user_id1 = 101; + string user_name1 = "George1"; + parameters.Add("id", OracleType.Int32).Value = user_id1; + counter++; + parameters.Add("name", DbType.String, 12).Value = user_name1; + + adoOperations.ExecuteNonQuery(CommandType.Text, sql, parameters); + } + + #endregion + + private class TestObjectExtractor : IResultSetExtractor + { + public object ExtractData(IDataReader reader) + { + IList testObjects = new ArrayList(); + while(reader.Read()) + { + TestObject to = new TestObject(); + //object foo = reader.GetDataTypeName(0); + to.ObjectNumber = (int) reader.GetInt64(0); + to.Name = reader.GetString(1); + testObjects.Add(to); + } + return testObjects; + } + } + + + } +} + +#endif // (!NET_1_0) \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/SQLiteTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/SQLiteTests.cs new file mode 100644 index 00000000..6182dce5 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/SQLiteTests.cs @@ -0,0 +1,414 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 + +#region Imports + +using System; +//using System.Collections; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.Odbc; +using System.Data.OracleClient; +using NUnit.Framework; +using Spring.Data.Common; +using Spring.Data.Generic; +using Spring.Data.Objects; +//using Sybase.Data.AseClient; + +#endregion + +namespace Spring.Data +{ + /// + /// This class contains misc tests for specific providers + /// + /// Mark Pollack + /// $Id: SQLiteTests.cs,v 1.1 2008/02/04 22:49:42 markpollack Exp $ + [TestFixture] + public class SQLiteTests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void SqlServerTest() + { + string errorCode = "544"; + string[] errorCodes = new string[4] {"544", "2627", "8114", "8115"}; + //Array.IndexOf() + //Array.Sort(errorCodes); + foreach (string code in errorCodes) + { + Console.WriteLine(code); + } + //if (Array.BinarySearch(errorCodes, errorCode) >= 0) + if (Array.IndexOf(errorCodes, errorCode) >= 0) + { + Console.WriteLine("yes"); + } + else + { + Assert.Fail("did not find error code"); + } + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); + dbProvider.ConnectionString = + @"Data Source=MARKT60\SQL2005;Initial Catalog=Spring;Persist Security Info=True;User ID=springqa;Password=springqa"; + AdoTemplate adoTemplate = new AdoTemplate(dbProvider); + try + { + adoTemplate.ExecuteNonQuery(CommandType.Text, "insert into Vacation (id) values (1)"); + } catch (Exception e) + { + Console.Write(e); + throw; + } + } + [Test] + public void OracleTest() + { + //Data Source=XE;User ID=hr;Unicode=True + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.OracleClient"); + dbProvider.ConnectionString = "Data Source=XE;User ID=hr;Password=hr;Unicode=True"; + AdoTemplate adoTemplate = new AdoTemplate(dbProvider); + decimal count = (decimal) adoTemplate.ExecuteScalar(CommandType.Text, "select count(*) from emp"); + Assert.AreEqual(14, count); + + EmpProc empProc = new EmpProc(dbProvider); + IDictionary dict = empProc.GetEmployees(); + foreach (DictionaryEntry entry in dict) + { + Console.WriteLine("Key = " + entry.Key + ", Value = " + entry.Value); + } + IList employeeList = dict["employees"] as IList; + foreach (Employee employee in employeeList) + { + Console.WriteLine(employee); + } + + } + + [Test] + public void Test() + { + //IDbProvider dbProvider = DbProviderFactory.GetDbProvider("SybaseAse1.15"); + // dbProvider.ConnectionString = "Data Source='MARKT60';Port='5000';UID='sa';PWD='';Database='pubs2';"; + + + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("Odbc-2.0"); + dbProvider.ConnectionString = + "Driver={Adaptive Server Enterprise};server=MARKT60;port=5000;Database=pubs2;uid=sa;pwd=;"; + Assert.IsNotNull(dbProvider); + AdoTemplate adoTemplate = new AdoTemplate(dbProvider); + IList authorList = + adoTemplate.QueryWithRowMapperDelegate(CommandType.Text, "select au_lname from authors", + delegate(IDataReader dataReader, int rowNum) { return dataReader.GetString(0); }); + foreach (string s in authorList) + { + Console.WriteLine(s); + } + Assert.IsTrue(authorList.Count > 0); + } +/* + [Test] + public void StoredProc() + { + //IDbProvider dbProvider = DbProviderFactory.GetDbProvider("SybaseAse1.15"); + //dbProvider.ConnectionString = "Data Source='MARKT60';Port='5000';UID='sa';PWD='';Database='pubs2';"; + + + //IDbProvider dbProvider = DbProviderFactory.GetDbProvider("Odbc-2.0"); + //dbProvider.ConnectionString = + // "Driver={Adaptive Server Enterprise};server=MARKT60;port=5000;Database=pubs2;uid=sa;pwd=;"; + + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("SybaseAse-15"); + dbProvider.ConnectionString = "Data Source='MARKT60';Port='5000';UID='sa';PWD='';Database='pubs2';"; + HelloProc proc = new HelloProc(dbProvider); + IDictionary dict = proc.GetResults(); + + Assert.AreEqual("Go Sybase", dict["@inoutParam"]); + Assert.AreEqual("Hello mango", dict["@outParam"]); + Assert.AreEqual(101, (int) dict["RETURN_VALUE"]); + foreach (DictionaryEntry entry in dict) + { + Console.WriteLine("Key = " + entry.Key + ", Value = " + entry.Value); + } + } + + [Test] + public void StordProcAdoTemplate() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("SybaseAse-15"); + dbProvider.ConnectionString = "Data Source='MARKT60';Port='5000';UID='sa';PWD='';Database='pubs2';"; + AdoTemplate adoTemplate = new AdoTemplate(dbProvider); + IDbParameters parameters = new DbParameters(dbProvider); + parameters.Add("inParam", AseDbType.VarChar, 32).Value = "mango"; + parameters.AddInOut("inoutParam", AseDbType.VarChar, 64).Value = "Sybase"; + parameters.AddOut("outParam", AseDbType.VarChar, 64); + parameters.AddReturn("retValue", AseDbType.Integer); + adoTemplate.ExecuteNonQuery(CommandType.StoredProcedure, "sp_hello", parameters); + + + Assert.AreEqual("Go Sybase", parameters["@inoutParam"].Value); + Assert.AreEqual("Hello mango", parameters[2].Value); + Assert.AreEqual(101, (int) parameters[3].Value); + } +*/ + [Test] + public void DeriveParams() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("SybaseAse-15"); + dbProvider.ConnectionString = "Data Source='MARKT60';Port='5000';UID='sa';PWD='';Database='pubs2';"; + AdoTemplate adoTemplate = new AdoTemplate(dbProvider); + IDataParameter[] derivedParameters = adoTemplate.DeriveParameters("@sp_hello", true); + if (derivedParameters.Length == 0) + { + Console.WriteLine("Derived Parameters = 0!!!!"); + } + for (int i = 0; i < derivedParameters.Length; i++) + { + Console.WriteLine("Parameter " + i + ", Name = " + derivedParameters[i].ParameterName + ", Value = " + + derivedParameters[i].Value); + } + } + +/* + [Test] + public void RawDeriveParams() + { + using ( + AseConnection conn = + new AseConnection("Data Source='MARKT60';Port='5000';UID='sa';PWD='';Database='pubs2';")) + { + using (AseCommand cmd = new AseCommand("@sp_hello", conn)) + { + conn.Open(); + cmd.CommandType = CommandType.StoredProcedure; + AseCommandBuilder.DeriveParameters(cmd); + Console.WriteLine("Number of parameters = " + cmd.Parameters.Count); + } + } + } +*/ + [Test] + public void QueryWithMapper() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("SybaseAse-15"); + dbProvider.ConnectionString = "Data Source='MARKT60';Port='5000';UID='sa';PWD='';Database='pubs2';"; + PubDao pubDao = new PubDao(dbProvider); + IList sales = pubDao.GetSales("5023"); + Assert.AreEqual(50, sales.Count); + } + + [Test] + public void QueryWithMapperODBC() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("Odbc-2.0"); + dbProvider.ConnectionString = + "Driver={Adaptive Server Enterprise};server=MARKT60;port=5000;Database=pubs2;uid=sa;pwd=;"; + PubDao pubDao = new PubDao(dbProvider); + IList sales = pubDao.GetSales("5023"); + Assert.AreEqual(50, sales.Count); + } + + [Test] + public void QueryRawODBC() + { + using ( + OdbcConnection conn = + new OdbcConnection( + "Driver={Adaptive Server Enterprise};server=MARKT60;port=5000;Database=pubs2;uid=sa;pwd=;")) + { + using (OdbcCommand cmd = new OdbcCommand("history_proc @stor_id", conn)) + { + conn.Open(); + cmd.CommandType = CommandType.StoredProcedure; + cmd.Parameters.Add("stor_id", OdbcType.NVarChar).Value = "5023"; + int i = 0; + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + i++; + } + } + Assert.AreEqual(50, i); + } + } + } + } + + public class PubDao : AdoDaoSupport + { + public PubDao(IDbProvider provider) + { + DbProvider = provider; + } + + public IList GetSales(string storeId) + { + return AdoTemplate.QueryWithRowMapperDelegate(CommandType.StoredProcedure, "history_proc", + delegate(IDataReader dataReader, int rowNum) + { + Sale sale = new Sale(); + sale.Date = dataReader.GetDateTime(0); + sale.OrderNumber = dataReader.GetString(1); + sale.Quantity = dataReader.GetInt32(2); + sale.Title = dataReader.GetString(3); + sale.Discount = dataReader.GetFloat(4); + sale.Price = dataReader.GetFloat(5); + sale.Total = dataReader.GetFloat(6); + return sale; + }, "stor_id", + DbType.String, 0, storeId); + } + } + + public class Sale + { + private DateTime date; + private string orderNumber; + private int quantity; + private string title; + private float discount; + private float price; + private float total; + + + public DateTime Date + { + get { return date; } + set { date = value; } + } + + public float Discount + { + get { return discount; } + set { discount = value; } + } + + public string OrderNumber + { + get { return orderNumber; } + set { orderNumber = value; } + } + + public int Quantity + { + get { return quantity; } + set { quantity = value; } + } + + public string Title + { + get { return title; } + set { title = value; } + } + + public float Price + { + get { return price; } + set { price = value; } + } + + public float Total + { + get { return total; } + set { total = value; } + } + } +/* + public class HelloProc : StoredProcedure + { + public HelloProc(IDbProvider provider) : base(provider, "sp_hello") + { + DeclaredParameters.Add("inParam", AseDbType.VarChar, 32).Value = "mango"; + DeclaredParameters.AddInOut("inoutParam", AseDbType.VarChar, 64).Value = "Sybase"; + DeclaredParameters.AddOut("outParam", AseDbType.VarChar, 64); + DeclaredParameters.AddReturn("retValue", AseDbType.Integer); + Compile(); + } + + public IDictionary GetResults() + { + return Query("mango", "Sybase"); + } + } +*/ + public class EmpProc : StoredProcedure + { + public EmpProc(IDbProvider provider) : base(provider, "TEST.Get1CurOut") + { + //DeriveParameters(); + DeclaredParameters.AddOut("P_CURSOR1", OracleType.Cursor); + AddRowMapper("employees", new EmployeeRowMapper()); + Compile(); + } + + public IDictionary GetEmployees() + { + for (int i=0; i< DeclaredParameters.Count; i++) + { + Console.WriteLine("decarled parameter name = " + DeclaredParameters[i].ParameterName + ", type = " + DeclaredParameters[i].DbType); + } + return Query(); + } + } + + internal class EmployeeRowMapper : IRowMapper + { + public object MapRow(IDataReader reader, int rowNum) + { + Employee emp = new Employee(); + emp.Number = reader.GetDecimal(0); + emp.Name = reader.GetString(1); + return emp; + } + } + + internal class Employee + { + private decimal number; + private string name; + + public decimal Number + { + get { return number; } + set { number = value; } + } + + public string Name + { + get { return name; } + set { name = value; } + } + + public override string ToString() + { + return "Number = " + number + ", Name = " + name; + } + } +} + +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/SimpleAccountCreditDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/SimpleAccountCreditDao.cs new file mode 100644 index 00000000..d351a87a --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/SimpleAccountCreditDao.cs @@ -0,0 +1,37 @@ +using System; +using System.Data; +using Spring.Data.Core; +using Spring.Data.Support; + +namespace Spring.Data +{ + public class SimpleAccountCreditDao : AdoDaoSupport + { + + public SimpleAccountCreditDao() + { + } + + /// + /// Insert into the credits table a credit amount + /// + /// + /// Note that this method is not declared as virtual since + /// the transaction demarcation is done in the SimpleAccountManager + /// class and does not need to have a transactional proxy created for + /// it. If one was not using a higher level class to + /// demarcate transactions and instead wanted to demarcate at the DAO + /// directly, the method would be declared as virtual and the + /// transaction attributes (or corresponding XML declarations) would + /// be applied to this method. + /// + /// the credit amount + public void CreateCredit(float creditAmount) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + String.Format("insert into Credits(creditAmount) VALUES ({0})", + creditAmount)); + } + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/SimpleAccountDebitDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/SimpleAccountDebitDao.cs new file mode 100644 index 00000000..2b1d9502 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/SimpleAccountDebitDao.cs @@ -0,0 +1,37 @@ +using System; +using System.Data; +using Spring.Data.Core; +using Spring.Data.Support; + +namespace Spring.Data +{ + public class SimpleAccountDebitDao : AdoDaoSupport + { + + public SimpleAccountDebitDao() + { + } + + /// + /// Insert into the debits table a debit amount + /// + /// + /// Note that this method is not declared as virtual since + /// the transaction demarcation is done in the SimpleAccountManager + /// class and does not need to have a transactional proxy created for + /// it. If one was not using a higher level class to + /// demarcate transactions and instead wanted to demarcate at the DAO + /// directly, the method would be declared as virtual and the + /// transaction attributes (or corresponding XML declarations) would + /// be applied to this method. + /// + /// the debit amount + public void DebitAccount(float debitAmount) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, String.Format("insert into dbo.Debits (DebitAmount) VALUES ({0})", + debitAmount)); + } + + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/SimpleAccountManager.cs b/test/Spring/Spring.Data.Integration.Tests/Data/SimpleAccountManager.cs new file mode 100644 index 00000000..23544c7a --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/SimpleAccountManager.cs @@ -0,0 +1,48 @@ + +using Spring.Transaction.Interceptor; + +namespace Spring.Data +{ + + public class SimpleAccountManager + { + private SimpleAccountCreditDao creditDao; + private SimpleAccountDebitDao debitDao; + + public SimpleAccountManager() + { + } + + public SimpleAccountCreditDao AccountCreditDao + { + get { return creditDao; } + set { creditDao = value; } + } + + public SimpleAccountDebitDao AccountDebitDao + { + get { return debitDao; } + set { debitDao = value; } + } + + /// + /// Transfer money, performing both credit and debit database operations + /// within one transaction. + /// + /// + /// The method is marked as virtual so that a transactional AOP proxy + /// can be created for it. + /// + /// the credit amount. + /// the debit amount. + [Transaction()] + public virtual void DoTransfer(float creditAmount, float debitAmount) + { + creditDao.CreateCredit(creditAmount); + debitDao.DebitAccount(debitAmount); + } + + } + + +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/StoredProcedureTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/StoredProcedureTests.cs new file mode 100644 index 00000000..ff467699 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/StoredProcedureTests.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Data.Core; + +#endregion + +namespace Spring.Data +{ + /// + /// Integration tests for StoredProcedure support. + /// + /// Mark Pollack (.NET) + /// $Id: StoredProcedureTests.cs,v 1.5 2008/01/29 18:22:33 markpollack Exp $ + [TestFixture] + public class StoredProcedureTests + { + + #region Fields + + IAdoOperations adoOperations; + IDbProvider dbProvider; + + #endregion + + [SetUp] + public void CreateAdoTemplate() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/adoTemplateTests.xml"); + Assert.IsNotNull(ctx); + dbProvider = ctx["DbProvider"] as IDbProvider; + Assert.IsNotNull(dbProvider); + adoOperations = new AdoTemplate(dbProvider); + + } + + [Test] + public void ExecuteNonQueryTest() + { + CallCreateTestObject createTestObjectProcedure = + new CallCreateTestObject(dbProvider); + + createTestObjectProcedure.Create("Gabriel", 1); + } + + [Test] + public void OutParamTest() + { + TestObjectStoredProcedure sproc = new TestObjectStoredProcedure(dbProvider); + IDbParameters parameters = sproc.DeclaredParameters; + Console.WriteLine("\n\n\n"); + for (int i = 0; i < parameters.Count; i++) + { + Console.WriteLine("- Declared Parameter " + parameters[i].ParameterName); + } + IDictionary results = sproc.GetResults("George"); + foreach (DictionaryEntry entry in results) + { + Console.WriteLine(entry.Key + + ", " + entry.Value); + } + + IDictionary inParams = new Hashtable(); + inParams.Add("@Name", "George"); + results = sproc.GetResultsUsingInDictionary(inParams); + Console.WriteLine("\n\n\n"); + foreach (DictionaryEntry entry in results) + { + Console.WriteLine(entry.Key + + ", " + entry.Value); + } + + } + + [Test] + public void OutReturnValueTestWithAdoTemplate() + { + IDbParameters parameters = adoOperations.CreateDbParameters(); + parameters.AddOut("Count", DbType.Int32); + parameters.AddReturn("RETURN_VALUE", DbType.Int32); + parameters.AddWithValue("Name", "George"); + IList queryResults = adoOperations.QueryWithRowMapper(CommandType.StoredProcedure, "SelectByNameWithReturnAndOutValue", + new SimpleRowMapper(), + parameters); + for (int i = 0; i < parameters.Count;i++ ) + { + Console.WriteLine("parameter " + i + + " name = " + parameters[i].ParameterName + + " value = " + parameters[i].Value); + } + // Console.WriteLine("out parameter 'count' = " + parameters["count"]); + //Console.WriteLine("RETURN_VALE = " + parameters["RETURN_VALUE"]); + + } + } + + internal class SimpleRowMapper : IRowMapper + { + public object MapRow(IDataReader reader, int rowNum) + { + return "hello"; + } + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/Support/SimpleExceptionTranslationTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/Support/SimpleExceptionTranslationTests.cs new file mode 100644 index 00000000..e2c9bdb7 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/Support/SimpleExceptionTranslationTests.cs @@ -0,0 +1,95 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using Common.Logging; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Data.Core; + +#endregion + +namespace Spring.Data.Support +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: SimpleExceptionTranslationTests.cs,v 1.4 2007/08/03 19:51:23 markpollack Exp $ + [TestFixture] + public class SimpleExceptionTranslationTests + { + #region Fields + private IDbProvider dbProvider; + private IAdoOperations adoOperations; + #endregion + + #region Constants + + /// + /// The shared ILog instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (SimpleExceptionTranslationTests)); + + #endregion + + #region Methods + + [SetUp] + public void CreateAdoTemplate() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/adoTemplateTests.xml"); + Assert.IsNotNull(ctx); + dbProvider = ctx["DbProvider"] as IDbProvider; + Assert.IsNotNull(dbProvider); + adoOperations = new AdoTemplate(dbProvider); + } + + [Test] + public void ExecuteNonQueryText() + { + + string badSql = "insert into TestObjects(Age, Name) VALS (33, 'foo')"; + try + { + adoOperations.ExecuteNonQuery(CommandType.Text, badSql); + } catch (BadSqlGrammarException e) + { + + log.Error("caught correct exception", e); + } catch (Exception e) + { + log.Error("caught incorrect exception ", e); + + Assert.Fail("did not throw exception of type BadSqlGrammerException"); + } + + } + #endregion + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/TestCoordinator.cs b/test/Spring/Spring.Data.Integration.Tests/Data/TestCoordinator.cs new file mode 100644 index 00000000..71a83c1d --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/TestCoordinator.cs @@ -0,0 +1,27 @@ + + +using Spring.Objects; + +namespace Spring.Data +{ + public class TestCoordinator : ITestCoordinator + { + private ITestObjectManager testObjectManager; + + + public ITestObjectManager TestObjectManager + { + get { return testObjectManager; } + set { testObjectManager = value; } + } + + #region ITestCoordinator Members + + public void WorkOn(TestObject to1, TestObject to2) + { + testObjectManager.SaveTwoTestObjects(to1,to2); + } + + #endregion + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectDao.cs b/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectDao.cs new file mode 100644 index 00000000..d5729c13 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectDao.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections; +using System.Data; +using Spring.Data.Common; +using Spring.Data.Core; +using Spring.Data.Support; +using Spring.Objects; + +namespace Spring.Data +{ + /// + /// AdoTemplate based implementation of ITestObjectDao data access layer. + /// + public class TestObjectDao : AdoDaoSupport, ITestObjectDao + { + + public void Create(string name, int age) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + String.Format("insert into TestObjects(Age, Name) VALUES ({0}, '{1}')", + age, name)); + } + + public void Update(TestObject to) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + String.Format("UPDATE TestObjects SET Age={0}, Name='{1}' where TestObjectNo = {2}", + to.Age, + to.Name, + to.ObjectNumber)); + } + + public void Delete(string name) + { + AdoTemplate.ExecuteNonQuery(CommandType.Text, + String.Format("delete from TestObjects where Name = '{0}'", + name)); + } + + public TestObject FindByName(string name) + { + IList toList = AdoTemplate.QueryWithRowMapper(CommandType.Text, + "select TestObjectNo, Age, Name from TestObjects where Name='"+name+"'", + new TestObjectRowMapper()); + if (toList.Count > 0) + { + return (TestObject)toList[0]; + } + else + { + return null; + } + } + + public IList FindAll() + { + return AdoTemplate.QueryWithRowMapper(CommandType.Text, + "select TestObjectNo, Age, Name from TestObjects", + new TestObjectRowMapper()); + } + + public int GetCount() + { + return (int)AdoTemplate.ExecuteScalar(CommandType.Text, "SELECT COUNT(*) FROM TestObjects"); + } + + public int GetCountByDelegate() + { + + return (int)AdoTemplate.Execute(new CommandDelegate(MyDelegate)); + } + + public int GetCount(int lowerAgeLimit) + { + return (int) AdoTemplate.ExecuteScalar(CommandType.Text, + "SELECT COUNT(*) FROM TestObjects where age > @age","age",DbType.Int32, 0, lowerAgeLimit); + } + + public int GetCount(int lowerAgeLimit, string name) + { + IDbParameters dbParameters = AdoTemplate.CreateDbParameters(); + dbParameters.Add("age", DbType.Int32).Value = lowerAgeLimit; + dbParameters.Add("name", DbType.String).Value = name; + return (int)AdoTemplate.ExecuteScalar(CommandType.Text, "SELECT COUNT(*) FROM TestObjects where age > @age and name = @name", dbParameters); + + } + + public int GetCountByAltMethod(int lowerAgeLimit) + { + return (int)AdoTemplate.ExecuteScalar(CommandType.Text, + "SELECT COUNT(*) FROM TestObjects where age > @age", + "age", DbType.Int32, 0, lowerAgeLimit); + + } + + public int GetCountByCommandSetter(int lowerAgeLimit) + { + return (int) AdoTemplate.ExecuteScalar(CommandType.Text, + "SELECT COUNT(*) FROM TestObjects where age > @age", + new SimpleCommandSetter(lowerAgeLimit)); + } + + private class SimpleCommandSetter : ICommandSetter + { + private int lowerLimit; + public SimpleCommandSetter(int limit) + { + lowerLimit = limit; + } + + public void SetValues(IDbCommand dbCommand) + { + //wouldn't typically set param values this way....intended to set other IDbCommand props + dbCommand.CommandTimeout = 999999; + + IDbDataParameter parameter = dbCommand.CreateParameter(); + parameter.ParameterName = "age"; + parameter.DbType = DbType.Int32; + parameter.Value = lowerLimit; + dbCommand.Parameters.Add(parameter); + } + } + + + private Object MyDelegate(IDbCommand command) + { + command.CommandType = CommandType.Text; + command.CommandText = "SELECT COUNT(*) FROM TestObjects"; + return command.ExecuteScalar(); + } + + + private class TestObjectRowMapper : IRowMapper + { + #region IRowMapper Members + + public object MapRow(IDataReader dataReader, int rowNum) + { + TestObject to = new TestObject(); + to.ObjectNumber = dataReader.GetInt32(0); + to.Age = dataReader.GetInt32(1); + to.Name = dataReader.GetString(2); + return to; + } + + #endregion + + } + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectManager.cs b/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectManager.cs new file mode 100644 index 00000000..d38b5c74 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectManager.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using Spring.Objects; +using Spring.Transaction.Interceptor; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data +{ + /// + /// Group together multiple ITestObjectDao operations. + /// + /// Mark Pollack (.NET) + /// $Id: TestObjectManager.cs,v 1.4 2008/03/21 14:10:23 markpollack Exp $ + public class TestObjectManager : ITestObjectManager + { + #region Fields + ITestObjectDao testObjectDao; + private static readonly ILog LOG = LogManager.GetLogger(typeof(TestObjectManager)); + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public TestObjectManager() + { + + } + + #endregion + + #region Properties + + public ITestObjectDao TestObjectDao + { + get { return testObjectDao; } + set { testObjectDao = value; } + } + + #endregion + + #region Methods + + [Transaction] + public void SaveTwoTestObjects(TestObject to1, TestObject to2) + { + LOG.Debug("TransactionActive = " + TransactionSynchronizationManager.ActualTransactionActive); + //Console.WriteLine("TransactionSynchronizationManager.CurrentTransactionIsolationLevel = " + + // TransactionSynchronizationManager.CurrentTransactionIsolationLevel); + //Console.WriteLine("System.Transactions.Transaction.Current.IsolationLevel = " + System.Transactions.Transaction.Current.IsolationLevel); + testObjectDao.Create(to1.Name, to1.Age); + testObjectDao.Create(to2.Name, to2.Age); + } + + [Transaction()] + public void DeleteTwoTestObjects(string name1, string name2) + { + testObjectDao.Delete(name1); + testObjectDao.Delete(name2); + } + + #endregion + } + + +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectQuery.cs b/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectQuery.cs new file mode 100644 index 00000000..6c90a7ab --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectQuery.cs @@ -0,0 +1,66 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using Spring.Data.Common; +using Spring.Data.Objects; +using Spring.Objects; + +#endregion + +namespace Spring.Data +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: TestObjectQuery.cs,v 1.2 2006/06/12 08:43:41 markpollack Exp $ + public class TestObjectQuery : MappingAdoQuery + { + private static string sql = "select TestObjectNo, Age, Name from TestObjects where Name = @UName"; + + public TestObjectQuery(IDbProvider dbProvider) + : base(dbProvider, sql) + { + //DeclaredParameters = new DbParameters(dbProvider); + try + { + DeclaredParameters.Add("UName", SqlDbType.VarChar, 50); + } catch (Exception e) + { + Console.WriteLine(e); + } + CommandType = CommandType.Text; + Compile(); + } + + protected override object MapRow(IDataReader reader, int num) + { + TestObject to = new TestObject(); + to.ObjectNumber = reader.GetInt32(0); + to.Age = reader.GetInt32(1); + to.Name = reader.GetString(2); + return to; + } + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectStoredProcedure.cs b/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectStoredProcedure.cs new file mode 100644 index 00000000..eb74ec6b --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/TestObjectStoredProcedure.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using Spring.Data.Common; +using Spring.Data.Objects; + +#endregion + +namespace Spring.Data +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: TestObjectStoredProcedure.cs,v 1.3 2008/01/29 18:22:33 markpollack Exp $ + public class TestObjectStoredProcedure : StoredProcedure + { + private static string procedureName = "SelectByNameWithReturnAndOutValue"; + + public TestObjectStoredProcedure(IDbProvider dbProvider) : base(dbProvider, procedureName) + { + //include return value + DeriveParameters(true); + Compile(); + } + + public IDictionary GetResults(string name) + { + //the 100 is for the out parameter that is incorrectly classified as input-output + return Query(name); + + } + + public IDictionary GetResultsUsingInDictionary(IDictionary inParams) + { + return ExecuteNonQueryByNamedParam(inParams); + } + + + + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/TestTxIsolationLevel.xml b/test/Spring/Spring.Data.Integration.Tests/Data/TestTxIsolationLevel.xml new file mode 100644 index 00000000..49bc9d6f --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/TestTxIsolationLevel.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TestObjectManager + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/TestTxIsolationLevelTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/TestTxIsolationLevelTests.cs new file mode 100644 index 00000000..f6f1223d --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/TestTxIsolationLevelTests.cs @@ -0,0 +1,97 @@ +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if NET_2_0 +#region Imports + +using System; +using System.Data.SqlClient; +using System.Transactions; +using NUnit.Framework; +using Spring.Aop.Config; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Config; +using Spring.Objects; +using Spring.Objects.Factory.Xml; +using Spring.Transaction.Config; + +#endregion + +namespace Spring.Data +{ + /// + /// Simple exercising of the AdoTemplate + /// + /// Mark Pollack (.NET) + /// $Id: TestTxIsolationLevelTests.cs,v 1.1 2008/04/03 05:41:46 markpollack Exp $ + [TestFixture] + public class TestTxIsolationLevelTests + { + //investigate + //1. matching against methods in adotemplate when the pointcut for type should already exclude it? + //2. is isolaiton level applied correctly + // a) TxScope w/ attributes + // b) TxScope w/ xml + // c) ado w/ attriutes + // d) ado w/ xml + private ITestObjectManager testObjectManager; + + [SetUp] + public void RollbackTestSetup() + { + NamespaceParserRegistry.RegisterParser(typeof (DatabaseNamespaceParser)); + NamespaceParserRegistry.RegisterParser(typeof (TxNamespaceParser)); + NamespaceParserRegistry.RegisterParser(typeof (AopNamespaceParser)); + IApplicationContext ctx = + new XmlApplicationContext( + "assembly://Spring.Data.Integration.Tests/Spring.Data/TestTxIsolationLevel.xml"); + Assert.IsNotNull(ctx); + testObjectManager = ctx["testObjectManager"] as ITestObjectManager; + Assert.IsNotNull(testObjectManager); + } + + [Test] + public void DoWork() + { + TestObject to1 = new TestObject("joe", 32); + TestObject to2 = new TestObject("mary", 35); + testObjectManager.SaveTwoTestObjects(to1, to2); + } + + [Test] + public void Dowork2() + { + using (TransactionScope txScope = new TransactionScope()) + { + string updateSql2 = "insert into Debits (DebitAmount) VALUES (222)"; + using (SqlConnection cn2005 = new SqlConnection()) + { + cn2005.ConnectionString = + @"Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"; + SqlCommand cmd = new SqlCommand(updateSql2, cn2005); + cn2005.Open(); + Console.WriteLine("Isolation level = " + System.Transactions.Transaction.Current.IsolationLevel); + } + } + } + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/TransactionTemplateTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/TransactionTemplateTests.cs new file mode 100644 index 00000000..9f3de8b8 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/TransactionTemplateTests.cs @@ -0,0 +1,230 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Data.Core; +using Spring.Objects; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data +{ + /// + /// Integration tests for transaction template functionality + /// + /// Mark Pollack + /// $Id: TransactionTemplateTests.cs,v 1.7 2007/08/03 19:51:22 markpollack Exp $ + [TestFixture] + public class TransactionTemplateTests + { + + private IDbProvider dbProvider; + + private IPlatformTransactionManager transactionManager; + + private IApplicationContext ctx; + + private IAdoOperations adoOperations; + + [SetUp] + public void SetUp() + { + ctx = + new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/templateTests.xml"); + dbProvider = ctx["DbProvider"] as IDbProvider; + transactionManager = ctx["adoTransactionManager"] as IPlatformTransactionManager; + adoOperations = ctx["adoTemplate"] as IAdoOperations; + + } + + + /// + /// Test using ObjectNameAutoProxyCreator for declarative tx mgmt. + /// + /// Note asserts to not actually check if same tx is used + /// for the multiple save and delete operations in TestObjectManager. + /// Useful for stepping through w/ debugger. + /// + [Test] + public void DeclarativeViaAutoProxyCreator() + { + ITestObjectManager mgr = ctx["testObjectManager"] as ITestObjectManager; + TestObjectDao dao = (TestObjectDao)ctx["testObjectDao"]; + PerformOperations(mgr, dao); + } + + /// + /// Test using TransactionProxyFactoryObject for declarative tx mgmt. + /// + /// Note asserts to not actually check if same tx is used + /// for the multiple save and delete operations in TestObjectManager. + /// Useful for stepping through w/ debugger. + /// + [Test] + public void DeclarativeViaTransactionProxyFactoryObject() + { + ITestObjectManager mgr = ctx["testObjectManagerTP"] as ITestObjectManager; + ITestObjectDao dao = (ITestObjectDao)ctx["testObjectDao"]; + PerformOperations(mgr, dao); + } + /// + /// Test using ProxyFactory with a transaction interceptor for declarative tx mgmt. + /// + /// Note asserts to not actually check if same tx is used + /// for the multiple save and delete operations in TestObjectManager. + /// Useful for stepping through w/ debugger. + [Test] + public void DeclarativeViaProxyFactoryObject() + { + ITestObjectManager mgr = ctx["testObjectManagerPF"] as ITestObjectManager; + TestObjectDao dao = (TestObjectDao)ctx["testObjectDao"]; + PerformOperations(mgr, dao); + + } + + public static void PerformOperations(ITestCoordinator coordinator, ITestObjectDao dao) + { + TestObject to1 = new TestObject(); + to1.Name = "Jack"; + to1.Age = 7; + TestObject to2 = new TestObject(); + to2.Name = "Jill"; + to2.Age = 8; + + coordinator.WorkOn(to1, to2); + + coordinator.TestObjectManager.DeleteTwoTestObjects("Jack", "Jill"); + } + + public static void PerformOperations(ITestObjectManager mgr, + ITestObjectDao dao) + { + Assert.IsNotNull(mgr); + TestObject to1 = new TestObject(); + to1.Name = "Jack"; + to1.Age = 7; + TestObject to2 = new TestObject(); + to2.Name = "Jill"; + to2.Age = 8; + mgr.SaveTwoTestObjects(to1, to2); + + TestObject to = dao.FindByName("Jack"); + Assert.IsNotNull(to); + + to = dao.FindByName("Jill"); + Assert.IsNotNull(to); + Assert.AreEqual("Jill", to.Name); + + mgr.DeleteTwoTestObjects("Jack", "Jill"); + + to = dao.FindByName("Jack"); + Assert.IsNull(to); + + to = dao.FindByName("Jill"); + Assert.IsNull(to); + } + + [Test] + public void ExecuteTemplate() + { + TransactionTemplate tt = new TransactionTemplate(transactionManager); + object result = tt.Execute(new SimpleTransactionCallback(dbProvider)); + Assert.AreEqual(2, (int)result); + + } + + [Test] + public void ExecuteTransactionManager() + { + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + def.PropagationBehavior = TransactionPropagation.Required; + + //TODO change to property of name TransactionStatus... + ITransactionStatus status = transactionManager.GetTransaction(def); + + int iCount = 0; + try + { + iCount = (int)adoOperations.ExecuteScalar(CommandType.Text, "SELECT COUNT(*) FROM TestObjects"); + /* + IAdoCommand cmd = new AdoCommand(dbProvider, CommandType.Text); + cmd.CommandText = "SELECT COUNT(*) FROM TestObjects"; + iCount = (int)cmd.ExecuteScalar(); + */ + + //other AdoCommands can be executed within same tx. + } catch (Exception e) + { + transactionManager.Rollback(status); + throw e; + } + transactionManager.Commit(status); + Assert.AreEqual(2, iCount); + + } + + + private class SimpleTransactionCallback : ITransactionCallback + { + private IDbProvider dbProvider; + + public SimpleTransactionCallback(IDbProvider dbp) + { + dbProvider = dbp; + } + /// + /// Gets called by TransactionTemplate.Execute within a + /// transaction context. + /// + /// The associated transaction status. + /// a result object or null + public object DoInTransaction(ITransactionStatus status) + { + AdoTemplate adoTemplate = new AdoTemplate(dbProvider); + return adoTemplate.Execute(new TestCommandCallback()); + } + } + + private class TestCommandCallback : ICommandCallback + { + + public Object DoInCommand(IDbCommand cmd) + { + cmd.CommandText = "SELECT COUNT(*) FROM TestObjects"; + int count = (int)cmd.ExecuteScalar(); + + cmd.CommandText = "SELECT COUNT(*) FROM TestObjects"; + count = (int)cmd.ExecuteScalar(); + + return count; + + } + } + } +} diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/adoTemplateTests.xml b/test/Spring/Spring.Data.Integration.Tests/Data/adoTemplateTests.xml new file mode 100644 index 00000000..289a7da1 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/adoTemplateTests.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/autoDeclarativeServices.xml b/test/Spring/Spring.Data.Integration.Tests/Data/autoDeclarativeServices.xml new file mode 100644 index 00000000..aba45ad8 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/autoDeclarativeServices.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + aroundAdvisor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spring.Data.TestCoordinator.Work* + + + + + + + diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/declarativeServices.xml b/test/Spring/Spring.Data.Integration.Tests/Data/declarativeServices.xml new file mode 100644 index 00000000..2e4f16ba --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/declarativeServices.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Spring.Data.ITestObjectManager + + + transactionInterceptor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/nativeAdoTests.xml b/test/Spring/Spring.Data.Integration.Tests/Data/nativeAdoTests.xml new file mode 100644 index 00000000..1a68fb54 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/nativeAdoTests.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/oracleAdoTemplateTests.xml b/test/Spring/Spring.Data.Integration.Tests/Data/oracleAdoTemplateTests.xml new file mode 100644 index 00000000..db101150 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/oracleAdoTemplateTests.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/templateTests.xml b/test/Spring/Spring.Data.Integration.Tests/Data/templateTests.xml new file mode 100644 index 00000000..fd1cf8e1 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/templateTests.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SetterAndMethodLoggingAdvisor + transactionInterceptor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spring.Data.ITestObjectManager + + + transactionInterceptor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/testobjects-sqlserver.sql b/test/Spring/Spring.Data.Integration.Tests/Data/testobjects-sqlserver.sql new file mode 100644 index 00000000..2ca45e5a --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Data/testobjects-sqlserver.sql @@ -0,0 +1,65 @@ + + +USE Spring +CREATE TABLE TestObjects +( + TestObjectNo int IDENTITY NOT NULL, + Age int, + Name varchar(50) +) + + +CREATE PROCEDURE SelectByName +( + @Name varchar(50) + +) + +as + select TestObjectNo, Age,Name from TestObjects where Name = @Name + +return + +CREATE PROCEDURE SelectByNameWithReturnValue +( + @Name varchar(50) + +) + +as + select * from TestObjects where Name = @Name + +return 5 + + +CREATE PROCEDURE SelectByNameWithReturnAndOutValue +( + @Name varchar(50), + @Count int output + +) + +as + select * from TestObjects where Name = @Name + set @Count = 10 +return 5 + + +CREATE PROCEDURE CreateTestObject +( + @Name varchar(50), + @Age int +) + +as + insert into TestObjects(Name, Age) Values (@Name, @Age) + + + + + +INSERT INTO TestObjects + (Age, Name) +VALUES + (1, 'Gabriel') + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.2003.csproj b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.2003.csproj new file mode 100644 index 00000000..89fe78d2 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.2003.csproj @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.2005.csproj b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.2005.csproj new file mode 100644 index 00000000..be5be8ee --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.2005.csproj @@ -0,0 +1,194 @@ + + + Local + 8.0.50727 + 2.0 + {91766D21-C568-459F-9BEA-759B011F23CF} + Debug + AnyCPU + + + + + Spring.Data.Integration.Tests + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + ..\..\..\build\VS.Net.2005\Spring.Data.Integration.Tests\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + 67, 618 + false + false + false + false + 4 + full + prompt + + + ..\..\..\build\VS.Net.2005\Spring.Data.Integration.Tests\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\Net\2.0\DotNetMock.dll + + + False + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + System + + + + System.Data + + + + System.Drawing + + + + System.XML + + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 + + + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904} + Spring.Core.Tests.2005 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.build b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.build new file mode 100644 index 00000000..7a83d2b3 --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.build @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.dll.config b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.dll.config new file mode 100644 index 00000000..770d107a --- /dev/null +++ b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.dll.config @@ -0,0 +1,31 @@ + + + + + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/AssemblyInfo.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..3da885ea --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Spring.Data.NHibernate Integration Tests")] +[assembly: AssemblyDescription("Integration tests for Spring.Data.NHibernate assembly")] diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AccountCreditDao.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AccountCreditDao.cs new file mode 100644 index 00000000..321c4b85 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AccountCreditDao.cs @@ -0,0 +1,22 @@ +using System; +using Spring.Data; +using Spring.Data.NHibernate.Support; + +namespace Spring.Data.NHibernate +{ + public class AccountCreditDao : HibernateDaoSupport, IAccountCreditDao + { + + public AccountCreditDao() + { + } + + public void CreateCredit(float creditAmount) + { + Credit c = new Credit(); + c.Amount = creditAmount; + HibernateTemplate.SaveOrUpdate(c); + } + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AccountDebitDao.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AccountDebitDao.cs new file mode 100644 index 00000000..14edf586 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AccountDebitDao.cs @@ -0,0 +1,22 @@ + +using Spring.Data.NHibernate.Support; + +namespace Spring.Data.NHibernate +{ + public class AccountDebitDao : HibernateDaoSupport, IAccountDebitDao + { + + public AccountDebitDao() + { + } + + public void DebitAccount(float debitAmount) + { + Debit d = new Debit(); + d.Amount = debitAmount; + HibernateTemplate.SaveOrUpdate(d); + } + + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AccountManager.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AccountManager.cs new file mode 100644 index 00000000..e6a1b1a6 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AccountManager.cs @@ -0,0 +1,95 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +using System; +using Spring.Transaction.Interceptor; + +namespace Spring.Data.NHibernate +{ + public class AccountManager : IAccountManager + { + + private IAccountCreditDao creditDao; + private IAccountDebitDao debitDao; + private IAuditDao auditDao; + + private bool throwException = false; + private bool throwExceptionAtEnd = false; + + public AccountManager() + { + } + + public IAccountCreditDao AccountCreditDao + { + get { return creditDao; } + set { creditDao = value; } + } + + public IAccountDebitDao AccountDebitDao + { + get { return debitDao; } + set { debitDao = value; } + } + + public IAuditDao AuditDao + { + get { return auditDao; } + set { auditDao = value; } + } + + + public bool ThrowException + { + get { return throwException; } + set { throwException = value; } + } + + public bool ThrowExceptionAtEnd + { + get { return throwExceptionAtEnd; } + set { throwExceptionAtEnd = value; } + } + + + [Transaction()] + public void DoTransfer(float creditAmount, float debitAmount) + { + creditDao.CreateCredit(creditAmount); + if (ThrowException) + { + throw new ArithmeticException("Couldn't do the math...."); + } + debitDao.DebitAccount(debitAmount); + if (AuditDao != null) + { + AuditDao.AuditOperation(DateTime.Now.ToString()); + } + if (ThrowExceptionAtEnd) + { + throw new ArgumentException("Almost there...but not quite."); + } + } + + } + + +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AuditDao.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AuditDao.cs new file mode 100644 index 00000000..50fb5656 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/AuditDao.cs @@ -0,0 +1,21 @@ +using System.Data; +using log4net; +using Spring.Data.Core; +using Spring.Data.Support; + +namespace Spring.Data.NHibernate +{ + public class AuditDao : AdoDaoSupport, IAuditDao + { + protected static readonly ILog logger = + LogManager.GetLogger(typeof(AuditDao)); + public void AuditOperation(string operationIdenfitier) + { + logger.Debug("Executing AUDIT operation."); + AdoTemplate.ExecuteNonQuery(CommandType.Text, + "insert into AuditTable (AuditId) values (@AuditId)", + "AuditId", DbType.String, 100, operationIdenfitier); + logger.Debug("AUDIT operation done."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Credit.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Credit.cs new file mode 100644 index 00000000..81ec4b12 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Credit.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Data.NHibernate +{ + public class Credit + { + #region Fields + + private int creditId; + private float amount; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public Credit() + { + + } + + #endregion + + #region Properties + + public int CreditID + { + get { return creditId; } + set { creditId = value; } + } + + public float Amount + { + get { return amount; } + set { amount = value; } + } + #endregion + + #region Methods + + #endregion + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Credit.hbm.xml b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Credit.hbm.xml new file mode 100644 index 00000000..3447ab1c --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Credit.hbm.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/DbProviderTemplateTests.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/DbProviderTemplateTests.cs new file mode 100644 index 00000000..27193782 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/DbProviderTemplateTests.cs @@ -0,0 +1,78 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using log4net.Config; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// This class contains tests for execution of misc Hibernate usage, including userCredentialsDbProvider + /// + /// Mark Pollack + /// $Id: DbProviderTemplateTests.cs,v 1.2 2008/03/21 14:10:37 markpollack Exp $ + [TestFixture] + public class DbProviderTemplateTests + { + private IApplicationContext ctx; + private UserCredentialsDbProvider userCredentialsDbProvider; + + [SetUp] + public void SetUp() + { + BasicConfigurator.Configure(); + ctx = + new XmlApplicationContext("assembly://Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate/dbProviderTemplateTests.xml"); + userCredentialsDbProvider = ctx["DbProvider"] as UserCredentialsDbProvider; + Assert.IsNotNull(userCredentialsDbProvider); + + } + + [Test] + public void UserCredentialsDbProvider() + { + ITestObjectDao dao = (ITestObjectDao)ctx["testObjectDaoTransProxy"]; + + userCredentialsDbProvider.SetCredentialsForCurrentThread("User ID=springqa", "Password=springqa"); + TestObject toGeorge = new TestObject(); + toGeorge.Name = "George"; + toGeorge.Age = 33; + dao.Create(toGeorge); + + userCredentialsDbProvider.SetCredentialsForCurrentThread("User ID=springqa2", "Password=springqa2"); + + TestObject toMary = new TestObject(); + toMary.Name = "Mary"; + toMary.Age = 34; + dao.Create(toMary); + } + + + + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Debit.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Debit.cs new file mode 100644 index 00000000..5df4c0a6 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Debit.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +namespace Spring.Data.NHibernate +{ + public class Debit + { + #region Fields + + private int debitId; + private float amount; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public Debit() + { + + } + + #endregion + + #region Properties + + public int DebitID + { + get { return debitId; } + set { debitId = value; } + } + + public float Amount + { + get { return amount; } + set { amount = value; } + } + #endregion + + #region Methods + + #endregion + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Debit.hbm.xml b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Debit.hbm.xml new file mode 100644 index 00000000..5ebfcef7 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/Debit.hbm.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAccountCreditDao.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAccountCreditDao.cs new file mode 100644 index 00000000..724cbb65 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAccountCreditDao.cs @@ -0,0 +1,9 @@ + + +namespace Spring.Data.NHibernate +{ + public interface IAccountCreditDao + { + void CreateCredit(float creditAmount); + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAccountDebitDao.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAccountDebitDao.cs new file mode 100644 index 00000000..beba4fec --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAccountDebitDao.cs @@ -0,0 +1,8 @@ + +namespace Spring.Data.NHibernate +{ + public interface IAccountDebitDao + { + void DebitAccount(float debitAmount); + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAccountManager.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAccountManager.cs new file mode 100644 index 00000000..437cfe23 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAccountManager.cs @@ -0,0 +1,17 @@ + +namespace Spring.Data.NHibernate +{ + public interface IAccountManager + { + void DoTransfer(float creditAmount, float debitAmount); + + /// + /// For testing purposes... + /// + bool ThrowException + { + get; + set; + } + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAuditDao.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAuditDao.cs new file mode 100644 index 00000000..e60dffa9 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/IAuditDao.cs @@ -0,0 +1,28 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +namespace Spring.Data.NHibernate +{ + public interface IAuditDao + { + void AuditOperation(string operationIdenfitier); + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/ITestObjectDao.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/ITestObjectDao.cs new file mode 100644 index 00000000..8a25a5ec --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/ITestObjectDao.cs @@ -0,0 +1,42 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + + +namespace Spring.Data.NHibernate +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: ITestObjectDao.cs,v 1.1 2007/05/31 20:25:14 markpollack Exp $ + public interface ITestObjectDao + { + void Create(TestObject to); + void Update(TestObject to); + void Delete(TestObject to); + TestObject FindByName(string name); + //IList FindAll(); + //int GetCount(); + //int GetCountByDelegate(); + void CreateUpdateRollback(TestObject to); + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/MultipleDbTests.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/MultipleDbTests.cs new file mode 100644 index 00000000..3f1a54b2 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/MultipleDbTests.cs @@ -0,0 +1,102 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using log4net; +using log4net.Config; +using NHibernate; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Data.Support; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Use of Hibernate Template against database. + /// + /// Mark Pollack (.NET) + /// $Id: MultipleDbTests.cs,v 1.1 2007/05/31 20:25:14 markpollack Exp $ + [TestFixture] + public class MultipleDbTests + { + #region Fields + + private IApplicationContext ctx; + + #endregion + + + #region Constants + + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (TemplateTests)); + + #endregion + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public MultipleDbTests() + { + + } + + #endregion + + [SetUp] + public void SetUp() + { + BasicConfigurator.Configure(); + ctx = + new XmlApplicationContext("assembly://Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate/MultipleDbTests.xml"); + + } + + [Test] + public void MultipleDBAccess() + { + float transferAmount = 114; + IAccountManager mgr = null; + + Assert.IsNotNull(ctx, "Application Context is null"); + mgr = ctx["accountManager"] as IAccountManager; + Assert.IsNotNull(mgr, "accountManager not of expected type. Type = " + ctx["accountManager"].GetType().ToString()); + mgr.DoTransfer(transferAmount, transferAmount); + + + + } + + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/MultipleDbTests.xml b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/MultipleDbTests.xml new file mode 100644 index 00000000..70040ad8 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/MultipleDbTests.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + Spring.Data.NHibernate.Integration.Tests + + + + + + + + + + + + + + + + + + + + + + Spring.Data.NHibernate.Integration.Tests + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + transactionInterceptor + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NHDAOTests.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NHDAOTests.cs new file mode 100644 index 00000000..8526dc8e --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NHDAOTests.cs @@ -0,0 +1,149 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using log4net.Config; +using NHibernate; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Data.NHibernate.Support; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: NHDAOTests.cs,v 1.1 2007/05/31 20:25:14 markpollack Exp $ + [TestFixture] + public class NHDAOTests + { + #region Fields + + private IApplicationContext ctx; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public NHDAOTests() + { + + } + + #endregion + + #region Properties + + #endregion + + + #region Methods + + [SetUp] + public void SetUp() + { + BasicConfigurator.Configure(); + + ctx = + new XmlApplicationContext("assembly://Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate/NHDAOTests.xml"); + ctx.Name = AbstractApplicationContext.DefaultRootContextName; + if (!ContextRegistry.IsContextRegistered(AbstractApplicationContext.DefaultRootContextName)) + { + ContextRegistry.RegisterContext(ctx); + } + } + + [Test] + public void DbProviderTransalation() + { + + ISessionFactory sf = ctx["SessionFactory"] as ISessionFactory; + IDbProvider dbProvider = SessionFactoryUtils.GetDbProvider(sf); +#if NET_2_0 + Assert.AreEqual("Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0", + dbProvider.DbMetadata.ProductName); +#else + Assert.AreEqual("Microsoft SQL Server, provider V1.0.5000.0 in framework .NET V1.1", + dbProvider.DbMetadata.ProductName); +#endif + } + + + [Test] + public void SessionScopeOutsideTransaction() + { + float transferAmount = 113; + using (new SessionScope()) + { + IAccountManager mgr = null; + mgr = ctx["accountManager"] as IAccountManager; + Assert.IsNotNull(mgr, "accountManager not of expected type. Type = " + ctx["accountManager"].GetType().ToString()); + mgr.DoTransfer(transferAmount, transferAmount); + } + } + + [Test] + public void DeclarativeWithAttributes() + { + float transferAmount = 113; + IAccountManager mgr = null; + try + { + Assert.IsNotNull(ctx,"Application Context is null"); + mgr = ctx["accountManager"] as IAccountManager; + Assert.IsNotNull(mgr,"accountManager not of expected type. Type = " + ctx["accountManager"].GetType().ToString()); + mgr.DoTransfer(transferAmount, transferAmount); + Assert.IsTrue(ContainsNewData(transferAmount)); + } catch (Exception e) + { + if (mgr.ThrowException) + { + Console.WriteLine(e.Message); + Console.WriteLine(e.StackTrace); + Assert.IsTrue(DoesNotContainNewData(transferAmount)); + } + } + } + + private bool ContainsNewData(float amount) + { + //to be done + return true; + } + + private bool DoesNotContainNewData(float amount) + { + //to be done + return true; + } + + #endregion + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NHDAOTests.xml b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NHDAOTests.xml new file mode 100644 index 00000000..d5c2ba2b --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NHDAOTests.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + Spring.Data.NHibernate.Integration.Tests + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + transactionInterceptor + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NHTestObjectDao.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NHTestObjectDao.cs new file mode 100644 index 00000000..646cd20f --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NHTestObjectDao.cs @@ -0,0 +1,100 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NHibernate.Type; +using Spring.Data.NHibernate.Support; +using Spring.Transaction.Interceptor; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: NHTestObjectDao.cs,v 1.1 2007/05/31 20:25:14 markpollack Exp $ + public class NHTestObjectDao : HibernateDaoSupport, ITestObjectDao + { + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public NHTestObjectDao() + { + + } + + #endregion + + #region ITestObjectDao Members + + [Transaction()] + public void Create(TestObject to) + { + HibernateTemplate.Save(to); + } + + [Transaction()] + public void Update(TestObject to) + { + HibernateTemplate.SaveOrUpdate(to); + } + + [Transaction()] + public void Delete(TestObject to) + { + HibernateTemplate.Delete(to); + } + + public TestObject FindByName(string name) + { + IList result = HibernateTemplate.Find( + "select from TestObject as to where to.Name=?", + name, + TypeFactory.GetStringType(50) + ); + + if (result.Count > 0) + { + return (TestObject)result[0]; + } + else + { + return null; + } + } + + public void CreateUpdateRollback(TestObject to) + { + HibernateTemplate.Save(to); + to.Name = "Updated Name"; + HibernateTemplate.SaveOrUpdate(to); + throw new Exception("My expected exception for test purposes."); + } + + #endregion + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NativeNHTestObjectDao.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NativeNHTestObjectDao.cs new file mode 100644 index 00000000..cdef2842 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NativeNHTestObjectDao.cs @@ -0,0 +1,114 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NHibernate; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: NativeNHTestObjectDao.cs,v 1.1 2007/05/31 20:25:14 markpollack Exp $ + public class NativeNHTestObjectDao : ITestObjectDao + { + public ISessionFactory SessionFactory + { + get { return sessionFactory; } + set { sessionFactory = value; } + } + + private ISessionFactory sessionFactory; + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public NativeNHTestObjectDao() + { + + } + + #endregion + + + #region Methods + + #endregion + + #region ITestObjectDao Members + + public void Create(TestObject to) + { + ISession session = null; + ITransaction transaction = null; + + try + { + session = SessionFactory.OpenSession(); + + transaction = session.BeginTransaction(); + + session.Save(to); + + transaction.Commit(); + } + catch + { + if(transaction != null) + transaction.Rollback(); + throw; + } + finally + { + if(session != null) + session.Close(); + } + + } + + public void Update(TestObject to) + { + // TODO: Add NativeNHTestObjectDao.Update implementation + } + + public void Delete(TestObject to) + { + // TODO: Add NativeNHTestObjectDao.Delete implementation + } + + public TestObject FindByName(string name) + { + // TODO: Add NativeNHTestObjectDao.FindByName implementation + return null; + } + + public void CreateUpdateRollback(TestObject to) + { + // TODO: Add NativeNHTestObjectDao.CreateUpdateRollback implementation + } + + #endregion + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NativeNHTests.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NativeNHTests.cs new file mode 100644 index 00000000..a034b4c2 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/NativeNHTests.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NHibernate; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects; +using Spring.Util; + +#endregion + +namespace Spring.Data.NHibernate +{ + [TestFixture] + public class NativeNHTests + { + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public NativeNHTests() + { + + } + + #endregion + + + #region Methods + + [Test] + public void CreateNative() + { + IApplicationContext ctx = + new XmlApplicationContext("assembly://Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate/templateTests.xml"); + ITestObjectDao dao = (ITestObjectDao)ctx.GetObject("nativeNHTestObjectDao"); + + TestObject toGeorge = new TestObject(); + toGeorge.Name = "George"; + toGeorge.Age = 34; + dao.Create(toGeorge); + + } + #endregion + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/TemplateTests.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/TemplateTests.cs new file mode 100644 index 00000000..5c5c3c3d --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/TemplateTests.cs @@ -0,0 +1,268 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using log4net; +using log4net.Config; +using NHibernate; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Data.Common; +using Spring.Data.Config; +using Spring.Data.Support; +using Spring.Objects.Factory.Xml; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// Use of Hibernate Template against database. + /// + /// Mark Pollack (.NET) + /// $Id: TemplateTests.cs,v 1.2 2008/02/04 22:42:50 markpollack Exp $ + [TestFixture] + public class TemplateTests + { + #region Fields + private IDbProvider dbProvider; + + private IPlatformTransactionManager transactionManager; + + private IApplicationContext ctx; + #endregion + + #region Constants + + /// + /// The shared instance for this class (and derived classes). + /// + protected static readonly ILog log = + LogManager.GetLogger(typeof (TemplateTests)); + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public TemplateTests() + { + + } + + #endregion + + [SetUp] + public void SetUp() + { + //NamespaceParserRegistry.RegisterParser(typeof(DatabaseNamespaceParser)); + BasicConfigurator.Configure(); + ctx = + new XmlApplicationContext("assembly://Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate/templateTests.xml"); + dbProvider = ctx["DbProvider"] as IDbProvider; + transactionManager = ctx["hibernateTransactionManager"] as IPlatformTransactionManager; + + } + + [Test] + public void ExceptionTranslator() + { + ISessionFactory sessionFactory = ctx["SessionFactory"] as ISessionFactory; + HibernateTemplate template = new HibernateTemplate(sessionFactory); + IAdoExceptionTranslator translator = template.AdoExceptionTranslator; + Assert.IsNotNull(translator, "ADO.NET exception translator should not be null"); + + + Assert.IsInstanceOfType(typeof(ErrorCodeExceptionTranslator), translator); + } + + [Test] + public void FallbackExceptionTranslator() + { + //ISessionFactory sessionFactory = ctx["SessionFactory"] as ISessionFactory; + //HibernateTemplate template = new HibernateTemplate(sessionFactory); + IAdoExceptionTranslator fallbackTranslator = new FallbackExceptionTranslator(); + fallbackTranslator.Translate("test", "sql", new Exception("foo")); + + } + + [Test] + [Ignore("Just for demo purposes")] + public void DemoDao() + { + + ITestObjectDao dao = (ITestObjectDao)ctx["testObjectDaoViaTxAttributes"]; + TestObject toGeorge = new TestObject(); + toGeorge.Name = "George"; + toGeorge.Age = 33; + dao.Create(toGeorge); + + } + + [Test] + [Ignore("Just for demo purposes")] + public void SimpleDao() + { + ITestObjectDao dao = (ITestObjectDao)ctx["NHTestObjectDao"]; + Assert.IsNotNull(dao); + TestObject to = dao.FindByName("Gabriel"); + Assert.IsNotNull(to); + Assert.AreEqual("Gabriel", to.Name); + + } + + /// + /// Test simple data base operations using attributes for + /// declarative transaction demarcation. + /// + [Test] + public void DaoOperationsViaProxyFactoryWithTxAttributes() + { + + ITestObjectDao dao = (ITestObjectDao)ctx["testObjectDaoViaTxAttributes"]; + ExecuteDaoOperations(dao); + + } + + + [Test] + public void DaoOperationsViaTransactionProxy() + { + + ITestObjectDao dao = (ITestObjectDao)ctx["testObjectDaoTransProxy"]; + ExecuteDaoOperations(dao); + + } + + [Test] + public void DaoOperationsWithRollback() + { + ITestObjectDao dao = (ITestObjectDao)ctx["testObjectDaoTransProxy"]; + try + { + ExecuteAndRollbackDaoOperations(dao); + } catch (Exception e) + { + TestObject to = dao.FindByName("Bugs"); + Assert.IsNull(to); + + //just to get rid of compiler warning... + Assert.IsNotNull(e); + } + } + + private static void ExecuteAndRollbackDaoOperations(ITestObjectDao dao) + { + TestObject toBugs = new TestObject(); + toBugs.Name = "Bugs"; + toBugs.Age = 33; + dao.CreateUpdateRollback(toBugs); + + } + private static void ExecuteDaoOperations(ITestObjectDao dao) + { + TestObject toGeorge = new TestObject(); + toGeorge.Name = "George"; + toGeorge.Age = 33; + dao.Create(toGeorge); + TestObject to = dao.FindByName("George"); + Assert.IsNotNull(to, "FindByName for George should not return null"); + Assert.AreEqual("George", to.Name); + Assert.AreEqual(33, to.Age); + + to.Age=34; + dao.Update(to); + + TestObject to2 = dao.FindByName("George"); + Assert.AreEqual(34, to2.Age); + + dao.Delete(to); + + TestObject to3 = dao.FindByName("George"); + Assert.IsNull(to3, "Should not have found TestObject with name George. TestObject = " + to.ToString() ); + } + + + [Test] + public void ExecuteTemplate() + { + ITestObjectDao dao = (ITestObjectDao)ctx["NHTestObjectDao"]; + TransactionTemplate tt = new TransactionTemplate(transactionManager); + object result = tt.Execute(new SimpleTransactionCallback(dbProvider, dao)); + TestObject to = result as TestObject; + Assert.IsNotNull(to,"FindByName for Gabriel should not return null"); + Assert.AreEqual("Gabriel", to.Name); + } + + [Test] + public void ExecuteTransactionManager() + { + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + def.PropagationBehavior = TransactionPropagation.Required; + + ITransactionStatus status = transactionManager.GetTransaction(def); + + ITestObjectDao dao = (ITestObjectDao)ctx["NHTestObjectDao"]; + TestObject to; + try + { + to = dao.FindByName("Gabriel"); + } + catch (Exception e) + { + transactionManager.Rollback(status); + throw e; + } + transactionManager.Commit(status); + Assert.IsNotNull(to,"FindByName for Gabriel should not return null"); + Assert.AreEqual("Gabriel", to.Name); + } + + private class SimpleTransactionCallback : ITransactionCallback + { + private IDbProvider dbProvider; + private ITestObjectDao dao; + + public SimpleTransactionCallback(IDbProvider dbp, ITestObjectDao dao) + { + dbProvider = dbp; + this.dao = dao; + } + /// + /// Gets called by TransactionTemplate.Execute within a + /// transaction context. + /// + /// The associated transaction status. + /// a result object or null + public object DoInTransaction(ITransactionStatus status) + { + return dao.FindByName("Gabriel"); + } + } + + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/TestObject.cs b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/TestObject.cs new file mode 100644 index 00000000..9a744c8f --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/TestObject.cs @@ -0,0 +1,76 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +namespace Spring.Data.NHibernate +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: TestObject.cs,v 1.2 2008/02/04 22:42:50 markpollack Exp $ + public class TestObject + { + #region Fields + private int age; + private string name; + private int objectNumber; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public TestObject() + { + + } + + #endregion + + #region Properties + + public virtual int Age + { + get { return age; } + set { age = value; } + } + + public virtual string Name + { + get { return name; } + set { name = value; } + } + + public virtual int ObjectNumber + { + get { return objectNumber; } + set { objectNumber = value; } + } + + #endregion + + #region Methods + + #endregion + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/TestObject.hbm.xml b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/TestObject.hbm.xml new file mode 100644 index 00000000..f7905780 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/TestObject.hbm.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/creditdebit.sql b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/creditdebit.sql new file mode 100644 index 00000000..88aaf966 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/creditdebit.sql @@ -0,0 +1,18 @@ + +CREATE TABLE [Credits]( + [CreditID] [int] IDENTITY NOT NULL, + [CreditAmount] [float] NOT NULL, + CONSTRAINT [PK_CreditID] PRIMARY KEY CLUSTERED +( + [CreditID] ASC +) ON [PRIMARY] +) ON [PRIMARY] + +CREATE TABLE [Debits]( + [DebitID] [int] IDENTITY NOT NULL, + [DebitAmount] [float] NOT NULL, + CONSTRAINT [PK_DebitID] PRIMARY KEY CLUSTERED +( + [DebitID] ASC +) ON [PRIMARY] +) ON [PRIMARY] \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/dbProviderTemplateTests.xml b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/dbProviderTemplateTests.xml new file mode 100644 index 00000000..436decff --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/dbProviderTemplateTests.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + assembly://Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate/TestObject.hbm.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/templateTests.xml b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/templateTests.xml new file mode 100644 index 00000000..c4872b4d --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Data/NHibernate/templateTests.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + assembly://Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate/TestObject.hbm.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.2003.csproj b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.2003.csproj new file mode 100644 index 00000000..175e4527 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.2003.csproj @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.2005.csproj b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.2005.csproj new file mode 100644 index 00000000..c1d9cdd8 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.2005.csproj @@ -0,0 +1,129 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {4A07E150-ED90-407C-8CAD-4760444DDFD9} + Library + Properties + Spring + Spring.Data.NHibernate.Integration.Tests + + + true + full + false + ..\..\..\build\VS.Net.2005\Spring.Data.NHibernate.Integration.Tests\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + ..\..\..\build\VS.Net.2005\Spring.Data.NHibernate.Integration.Tests\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\lib\NHibernate12\net\2.0\Castle.DynamicProxy.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\NHibernate12\net\2.0\Iesi.Collections.dll + + + False + ..\..\..\lib\NHibernate12\net\2.0\log4net.dll + + + False + ..\..\..\lib\NHibernate12\net\2.0\NHibernate.dll + + + False + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + + + + + + + + + + + + + + + + + + Code + + + + + + + Code + + + + + + + + + + + + + + + + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {90F2D070-6F98-4926-A626-BD7A6071D6D9} + Spring.Data.NHibernate12.2005 + + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 + + + + + + + + + echo "Copying .xml files for tests" +xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2005\Spring.Data.NHibernate.Integration.Tests\$(ConfigurationName)\ /y /s /q /d +xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2005\Spring.Data.NHibernate.Integration.Tests\$(ConfigurationName)\ /y /s /q + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.build b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.build new file mode 100644 index 00000000..f5e254bd --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.build @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.dll.config b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.dll.config new file mode 100644 index 00000000..12e5ecbf --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.dll.config @@ -0,0 +1,40 @@ + + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/AssemblyInfo.cs b/test/Spring/Spring.Data.NHibernate.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..3da885ea --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Spring.Data.NHibernate Integration Tests")] +[assembly: AssemblyDescription("Integration tests for Spring.Data.NHibernate assembly")] diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Config/AopConfiguration.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Config/AopConfiguration.cs new file mode 100644 index 00000000..62cf9e99 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Config/AopConfiguration.cs @@ -0,0 +1,83 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Aop.Framework; +using Spring.Aop.Framework.DynamicProxy; +using Spring.Aop.Support; +using Spring.Context; +using Spring.Context.Support; +using Spring.Transaction; + +#endregion + +namespace Spring.Data.NHibernate.Config +{ + /// + /// This class contains tests for + /// + /// Mark Pollack + /// $Id: AopConfiguration.cs,v 1.2 2007/08/01 17:51:58 markpollack Exp $ + [TestFixture] + public class AopConfiguration + { + private IApplicationContext ctx; + + [SetUp] + public void Setup() + { + ctx = + new XmlApplicationContext("assembly://Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Config/AopConfiguration.xml"); + + } + + [Test] + public void ProxyDataAccessAndServiceLayer() + { + Assert.IsFalse(AopUtils.IsAopProxy( ctx["DbProvider"] )); + Assert.IsFalse(AopUtils.IsAopProxy( ctx["SessionFactory"] )); + Assert.IsFalse(AopUtils.IsAopProxy(ctx["hibernateTransactionManager"])); + Assert.IsFalse(AopUtils.IsAopProxy(ctx["transactionManager"])); + //Assert.IsTrue(AopUtils.IsAopProxy(ctx["testObjectDaoTransProxy"])); + Assert.IsTrue(AopUtils.IsAopProxy(ctx["TestObjectDao"])); + Assert.IsTrue(AopUtils.IsAopProxy(ctx["SimpleService"])); + + CallCountingTransactionManager ccm = ctx["transactionManager"] as CallCountingTransactionManager; + Assert.IsNotNull(ccm); + Assert.AreEqual(0, ccm.begun); + Assert.AreEqual(0, ccm.commits); + + LoggingAroundAdvice caa = ctx["loggingAroundAdvice"] as LoggingAroundAdvice; + Assert.IsNotNull(caa); + Assert.AreEqual(0, caa.numInvoked); + + ISimpleService simpleService = ctx["SimpleService"] as ISimpleService; + Assert.IsNotNull(simpleService); + simpleService.DoWork(new TestObject()); + Assert.AreEqual(1, ccm.begun); + Assert.AreEqual(1, ccm.commits); + Assert.AreEqual(1, caa.numInvoked); + } + + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Config/AopConfiguration.xml b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Config/AopConfiguration.xml new file mode 100644 index 00000000..bd470351 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Config/AopConfiguration.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + assembly://Spring.Data.NHibernate.Tests/Spring.Data.NHibernate/TestObject.hbm.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + loggingAroundAdvice + + + + + + + + + + TestObjectDao + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Config/AopConfigurationTxPointcut.xml b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Config/AopConfigurationTxPointcut.xml new file mode 100644 index 00000000..2e6cdae6 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Config/AopConfigurationTxPointcut.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + assembly://Spring.Data.NHibernate.Tests/Spring.Data.NHibernate/TestObject.hbm.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 12341234asdf* + + + + + + + + + loggingAdvisor + + + + + + + + + + TestObjectDao + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/HibernateTransactionManagerTests.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/HibernateTransactionManagerTests.cs new file mode 100644 index 00000000..baae9730 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/HibernateTransactionManagerTests.cs @@ -0,0 +1,1068 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Data; +using NHibernate; +using NHibernate.Cfg; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Data.Support; +using Spring.Support; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// This class contains tests for the HibernateTransactionManager + /// + /// Mark Pollack + /// $Id: HibernateTransactionManagerTests.cs,v 1.5 2008/01/29 20:07:38 markpollack Exp $ + [TestFixture] + public class HibernateTransactionManagerTests + { + private MockRepository mocks; + + [SetUp] + public void Setup() + { + mocks = new MockRepository(); + } + + [Test] + public void TransactionCommit() + { + IDbProvider provider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + ISession session = (ISession)mocks.CreateMock(typeof(ISession)); + ITransaction transaction = (ITransaction) mocks.CreateMock(typeof (ITransaction)); + IQuery query = (IQuery) mocks.CreateMock(typeof (IQuery)); + + IList list = new ArrayList(); + list.Add("test"); + using (mocks.Ordered()) + { + Expect.Call(sessionFactory.OpenSession()).Return(session); + Expect.Call(session.Connection).Return(connection); + Expect.Call(session.BeginTransaction(IsolationLevel.Serializable)).Return(transaction); + Expect.Call(session.IsOpen).Return(true); + Expect.Call(session.CreateQuery("some query string")).Return(query); + Expect.Call(query.List()).Return(list); + transaction.Commit(); + LastCall.On(transaction).Repeat.Once(); + Expect.Call(session.Close()).Return(null); + } + + mocks.ReplayAll(); + + + LocalSessionFactoryObjectStub lsfo = new LocalSessionFactoryObjectStub(sessionFactory); + lsfo.AfterPropertiesSet(); + + ISessionFactory sfProxy = lsfo.GetObject() as ISessionFactory; + Assert.IsNotNull(sfProxy); + + HibernateTransactionManager tm = new HibernateTransactionManager(); + tm.AdoExceptionTranslator = new FallbackExceptionTranslator(); + tm.SessionFactory = sfProxy; + tm.DbProvider = provider; + TransactionTemplate tt = new TransactionTemplate(tm); + + tt.TransactionIsolationLevel = IsolationLevel.Serializable; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sfProxy),"Hasn't thread session"); + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + Assert.IsTrue(!TransactionSynchronizationManager.ActualTransactionActive, "Actual transaction not active"); + + object result = tt.Execute(new TransactionCommitTxCallback(sfProxy, provider)); + + Assert.IsTrue(result == list, "Incorrect result list"); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sfProxy), "Hasn't thread session"); + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + Assert.IsTrue(!TransactionSynchronizationManager.ActualTransactionActive, "Actual transaction not active"); + + + mocks.VerifyAll(); + + } + + + [Test] + public void TransactionRollback() + { + + IDbProvider provider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + ISession session = (ISession)mocks.CreateMock(typeof(ISession)); + ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction)); + + using (mocks.Ordered()) + { + Expect.Call(sessionFactory.OpenSession()).Return(session); + Expect.Call(session.Connection).Return(connection); + Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(session.IsOpen).Return(true); + + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + + Expect.Call(session.Close()).Return(null); + } + mocks.ReplayAll(); + + HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory); + TransactionTemplate tt = new TransactionTemplate(tm); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + try + { + tt.Execute(new TransactionRollbackTxCallback(sessionFactory)); + Assert.Fail("Should have thrown exception"); + } catch (ArgumentException) + { + + } + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider"); + + mocks.VerifyAll(); + } + + [Test] + public void TransactionRollbackOnly() + { + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + ISession session = (ISession)mocks.CreateMock(typeof(ISession)); + ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction)); + + using (mocks.Ordered()) + { + Expect.Call(sessionFactory.OpenSession()).Return(session); + Expect.Call(session.Connection).Return(connection); + Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(session.IsOpen).Return(true); + Expect.Call(session.FlushMode).Return(FlushMode.Auto); + session.Flush(); + LastCall.On(session).Repeat.Once(); + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + Expect.Call(session.Close()).Return(null); + } + mocks.ReplayAll(); + + + HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory); + TransactionTemplate tt = new TransactionTemplate(tm); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + + tt.Execute(new TransactionRollbackOnlyTxCallback(sessionFactory)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + + mocks.VerifyAll(); + + } + + [Test] + public void ParticipatingTransactionWithCommit() + { + IDbConnection connection = (IDbConnection) mocks.CreateMock(typeof (IDbConnection)); + ISessionFactory sessionFactory = (ISessionFactory) mocks.CreateMock(typeof (ISessionFactory)); + ISession session = (ISession) mocks.CreateMock(typeof (ISession)); + ITransaction transaction = (ITransaction) mocks.CreateMock(typeof (ITransaction)); + + using (mocks.Ordered()) + { + Expect.Call(sessionFactory.OpenSession()).Return(session); + Expect.Call(session.Connection).Return(connection); + Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(session.IsOpen).Return(true); + Expect.Call(session.FlushMode).Return(FlushMode.Auto); + session.Flush(); + LastCall.On(session).Repeat.Once(); + transaction.Commit(); + LastCall.On(transaction).Repeat.Once(); + Expect.Call(session.Close()).Return(null); + } + + mocks.ReplayAll(); + + HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory); + TransactionTemplate tt = new TransactionTemplate(tm); + IList list = new ArrayList(); + list.Add("test"); + + object result = tt.Execute(new ParticipatingTransactionWithCommitTxCallback(sessionFactory, list)); + Assert.IsTrue(result == list); + + mocks.VerifyAll(); + + } + + [Test] + public void ParticipatingTransactionWithRollback() + { + IDbConnection connection = (IDbConnection) mocks.CreateMock(typeof (IDbConnection)); + ISessionFactory sessionFactory = (ISessionFactory) mocks.CreateMock(typeof (ISessionFactory)); + ISession session = (ISession) mocks.CreateMock(typeof (ISession)); + ITransaction transaction = (ITransaction) mocks.CreateMock(typeof (ITransaction)); + + using (mocks.Ordered()) + { + Expect.Call(sessionFactory.OpenSession()).Return(session); + Expect.Call(session.Connection).Return(connection); + Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(session.IsOpen).Return(true); + Expect.Call(session.FlushMode).Return(FlushMode.Auto); + + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + Expect.Call(session.Close()).Return(null); + } + mocks.ReplayAll(); + + + HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory); + TransactionTemplate tt = new TransactionTemplate(tm); + try + { + tt.Execute(new ParticipatingTransactionWithRollbackTxCallback(sessionFactory)); + Assert.Fail("Should have thrown exception"); + } + catch (ArgumentException) + { + + } + + mocks.VerifyAll(); + } + + [Test] + public void ParticipatingTransactionWithRollbackOnly() + { + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + ISession session = (ISession)mocks.CreateMock(typeof(ISession)); + ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction)); + + using (mocks.Ordered()) + { + Expect.Call(sessionFactory.OpenSession()).Return(session); + Expect.Call(session.Connection).Return(connection); + Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(session.IsOpen).Return(true); + + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + Expect.Call(session.Close()).Return(null); + } + mocks.ReplayAll(); + + + HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory); + TransactionTemplate tt = new TransactionTemplate(tm); + IList list = new ArrayList(); + list.Add("test"); + try + { + tt.Execute(new ParticipatingTransactionWithRollbackOnlyTxCallback(tt,sessionFactory,list)); + Assert.Fail("Should have thrown UnexpectedRollbackException"); + } + catch (UnexpectedRollbackException) + { + + } + + mocks.VerifyAll(); + } + + [Test] + public void ParticipatingTransactionWithWithRequiresNew() + { + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + ISession session1 = (ISession)mocks.CreateMock(typeof(ISession)); + ISession session2 = (ISession)mocks.CreateMock(typeof(ISession)); + ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction)); + + //using (mocks.Ordered()) + //{ + Expect.Call(sessionFactory.OpenSession()).Return(session1); + Expect.Call(session1.Connection).Return(connection); + Expect.Call(session1.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(session1.IsOpen).Return(true); + + Expect.Call(sessionFactory.OpenSession()).Return(session2); + Expect.Call(session2.Connection).Return(connection); + Expect.Call(session2.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(session2.IsOpen).Return(true); + + Expect.Call(session2.FlushMode).Return(FlushMode.Auto); + session2.Flush(); + LastCall.On(session2).Repeat.Once(); + + transaction.Commit(); + LastCall.On(transaction).Repeat.Twice(); + + Expect.Call(session1.Close()).Return(null); + Expect.Call(session2.Close()).Return(null); + //} + + mocks.ReplayAll(); + + HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + + tt.Execute(new ParticipatingTransactionWithWithRequiresNewTxCallback(tt, sessionFactory)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + + mocks.VerifyAll(); + + } + + [Test] + public void ParticipatingTransactionWithWithNotSupported() + { + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + ISession session = (ISession)mocks.CreateMock(typeof(ISession)); + ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction)); + + //using (mocks.Ordered()) + //{ + Expect.Call(sessionFactory.OpenSession()).Return(session).Repeat.Twice(); + Expect.Call(session.Connection).Return(connection); + + + Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(session.IsOpen).Return(true); + Expect.Call(session.FlushMode).Return(FlushMode.Auto).Repeat.Twice(); + session.Flush(); + LastCall.On(session).Repeat.Twice(); + transaction.Commit(); + LastCall.On(transaction).Repeat.Once(); + Expect.Call(session.Close()).Return(null).Repeat.Twice(); + //} + mocks.ReplayAll(); + + + HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + + tt.Execute(new ParticipatingTransactionWithWithNotSupportedTxCallback(tt, sessionFactory)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + + mocks.VerifyAll(); + + + } + + [Test] + public void TransactionWithPropagationSupports() + { + ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + ISession session = (ISession)mocks.CreateMock(typeof(ISession)); + + Expect.Call(sessionFactory.OpenSession()).Return(session); + Expect.Call(session.FlushMode).Return(FlushMode.Never); + session.FlushMode = FlushMode.Auto; + LastCall.IgnoreArguments(); + session.Flush(); + LastCall.IgnoreArguments(); + session.FlushMode = FlushMode.Never; + Expect.Call(session.FlushMode).Return(FlushMode.Never); + Expect.Call(session.Close()).Return(null); + + mocks.ReplayAll(); + + + LocalSessionFactoryObjectStub lsfo = new LocalSessionFactoryObjectStub(sessionFactory); + lsfo.AfterPropertiesSet(); + ISessionFactory sfProxy = (ISessionFactory) lsfo.GetObject(); + Assert.IsNotNull(sfProxy); + + HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.Supports; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + + tt.Execute(new TransactionWithPropagationSupportsTxCallback(sessionFactory)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + + mocks.VerifyAll(); + + } + + [Test] + public void TransactionWithPropagationSupportsAndInnerTransaction() + { + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + ISession session1 = (ISession)mocks.CreateMock(typeof(ISession)); + ISession session2 = (ISession)mocks.CreateMock(typeof(ISession)); + ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction)); + + Expect.Call(sessionFactory.OpenSession()).Return(session1); + Expect.Call(session1.Connection).Return(connection); + Expect.Call(session1.SessionFactory).Return(sessionFactory); + Expect.Call(session1.FlushMode).Return(FlushMode.Auto).Repeat.Twice(); + + session1.Flush(); + LastCall.IgnoreArguments().Repeat.Twice(); + Expect.Call(session1.Close()).Return(null); + + Expect.Call(sessionFactory.OpenSession()).Return(session2); + Expect.Call(session2.Connection).Return(connection).Repeat.Twice(); + Expect.Call(session2.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(session2.FlushMode).Return(FlushMode.Auto); + session2.Flush(); + LastCall.IgnoreArguments(); + Expect.Call(session2.IsOpen).Return(true); + + + transaction.Commit(); + LastCall.On(transaction).Repeat.Once(); + + + Expect.Call(session2.Close()).Return(null); + + mocks.ReplayAll(); + + LocalSessionFactoryObjectStub lsfo = new LocalSessionFactoryObjectStub(sessionFactory); + lsfo.AfterPropertiesSet(); + ISessionFactory sfProxy = (ISessionFactory)lsfo.GetObject(); + Assert.IsNotNull(sfProxy); + + HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory); + + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.Supports; + TransactionTemplate tt2 = new TransactionTemplate(tm); + tt2.PropagationBehavior = TransactionPropagation.Required; + + HibernateTemplate ht = new HibernateTemplate(sessionFactory); + ht.TemplateFlushMode = TemplateFlushMode.Eager; + ht.ExposeNativeSession = true; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + + tt.Execute(new TransactionWithPropagationSupportsAndInnerTransactionTxCallback(tt2, sessionFactory, ht, session1, session2)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + + mocks.ReplayAll(); + + } + + + [Test] + public void TransactionCommitWithFlushFailure() + { + DoTransactionCommitWithFlushFailure(false); + } + + [Test] + public void TransactionCommitWithFlushFailureAndFallbackTranslation() + { + DoTransactionCommitWithFlushFailure(true); + } + + /// + /// Does the test transaction commit with flush failure. + /// + /// if set to true if the exception throw + /// is of the type NHibernate.ADOException, in which case HibernateTransactionManager + /// will 'fallback' to using the error codes in the underlying exception thrown by + /// the provider, ie. a SqlException, MySqlException. Otherwise, if it is + /// another subclass of HibernateException, then perform a direct maping as + /// found in SessionFactoryUtils.ConvertHibernateAccessException. + private void DoTransactionCommitWithFlushFailure(bool fallbackTranslation) + { + #region Mock Setup + + IDbProvider provider = new TestDbProvider(); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + ISession session = (ISession)mocks.CreateMock(typeof(ISession)); + ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction)); + Exception rootCause = null; + using (mocks.Ordered()) + { + Expect.Call(sessionFactory.OpenSession()).Return(session); + Expect.Call(session.Connection).Return(connection); + Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(session.IsOpen).Return(true); + transaction.Commit(); + Exception sqlException = new TestSqlException("mymsg", "2627"); + if (fallbackTranslation) + { + //error code 2627 will map to a DataAccessIntegrity exception in sqlserver, which is the metadata + //used by TestDbProvider. + rootCause = sqlException; + LastCall.On(transaction).Throw(new ADOException("mymsg", sqlException)); + } + else + { + rootCause = new PropertyValueException("mymsg", typeof(string), "Name"); + LastCall.On(transaction).Throw(rootCause); + } + + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + Expect.Call(session.Close()).Return(null); + } + + #endregion + + mocks.ReplayAll(); + + + HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory); + tm.DbProvider = provider; + + TransactionTemplate tt = new TransactionTemplate(tm); + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + IList list = new ArrayList(); + list.Add("test"); + try + { + tt.Execute(new TransactionCommitWithFlushFailureCallback(sessionFactory, list)); + Assert.Fail("Should have thrown DataIntegrityViolationException"); + } + catch (DataIntegrityViolationException ex) + { + Assert.AreEqual(rootCause, ex.InnerException); + Assert.IsTrue(ex.Message.IndexOf("mymsg") != -1); + } + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + mocks.VerifyAll(); + + + } + + } + + + + #region Supporting classes for test TransactionCommit + + public class TransactionCommitTxCallback : ITransactionCallback + { + private ISessionFactory sfProxy; + private IDbProvider provider; + public TransactionCommitTxCallback(ISessionFactory sessionFactory, IDbProvider provider) + { + sfProxy = sessionFactory; + this.provider = provider; + } + + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(sfProxy),"Has thread session"); + Assert.IsTrue(TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + HibernateTemplate ht = new HibernateTemplate(sfProxy); + return ht.Find("some query string"); + } + } + + public class LocalSessionFactoryObjectStub : LocalSessionFactoryObject + { + private ISessionFactory sf; + public LocalSessionFactoryObjectStub(ISessionFactory sf) + { + this.sf = sf; + } + + protected override ISessionFactory NewSessionFactory(Configuration config) + { + return sf; + } + } + + #endregion + + #region Supporting classes for test TransactionRollback + + public class TransactionRollbackTxCallback : ITransactionCallback + { + private ISessionFactory sf; + public TransactionRollbackTxCallback(ISessionFactory sf) + { + this.sf = sf; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf),"Has thread session"); + HibernateTemplate ht = new HibernateTemplate(sf); + return ht.ExecuteFind(new ThrowExceptionHibernateCallback()); + } + } + + public class ThrowExceptionHibernateCallback : IHibernateCallback + { + public object DoInHibernate(ISession session) + { + throw new ArgumentException("arg exception"); + } + } + + #endregion + + #region Supporting classes for test TransactionRollbackOnly + + public class TransactionRollbackOnlyTxCallback : ITransactionCallback + { + private ISessionFactory sf; + public TransactionRollbackOnlyTxCallback(ISessionFactory sf) + { + this.sf = sf; + } + + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session"); + HibernateTemplate ht = new HibernateTemplate(sf); + ht.TemplateFlushMode = TemplateFlushMode.Eager; + ht.Execute(new HibernateDelegate(Del)); + status.RollbackOnly = true; + return null; + } + + private object Del(ISession session) + { + return null; + } + } + #endregion + + #region Supporting classes for test ParticipatingTransactionWithCommit + + public class ParticipatingTransactionWithCommitTxCallback : ITransactionCallback + { + private ISessionFactory sf; + private IList list; + public ParticipatingTransactionWithCommitTxCallback(ISessionFactory sf, IList list) + { + this.sf = sf; + this.list = list; + } + + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session"); + HibernateTemplate ht = new HibernateTemplate(sf); + ht.TemplateFlushMode = TemplateFlushMode.Eager; + return ht.Execute(new HibernateDelegate(Del)); + } + + private object Del(ISession session) + { + return list; + } + } + + + #endregion + + #region Supporting classes for test ParticipatingTransactionWithRollback + public class ParticipatingTransactionWithRollbackTxCallback : ITransactionCallback + { + private ISessionFactory sf; + public ParticipatingTransactionWithRollbackTxCallback(ISessionFactory sf) + { + this.sf = sf; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session"); + HibernateTemplate ht = new HibernateTemplate(sf); + ht.TemplateFlushMode = TemplateFlushMode.Eager; + return ht.ExecuteFind(new ThrowExceptionHibernateCallback()); + } + + } + + #endregion + + #region Supporting classes for test ParticipatingTransactionWithRollbackOnly + + public class ParticipatingTransactionWithRollbackOnlyTxCallback : ITransactionCallback + { + private TransactionTemplate tt; + private ISessionFactory sf; + private IList list; + public ParticipatingTransactionWithRollbackOnlyTxCallback(TransactionTemplate tt, ISessionFactory sf, IList list) + { + this.tt = tt; + this.sf = sf; + this.list = list; + } + + public object DoInTransaction(ITransactionStatus status) + { + return tt.Execute(new TransactionDelegate(TransactionMethod)); + } + + private object TransactionMethod(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session"); + HibernateTemplate ht = new HibernateTemplate(sf); + object returnValue = ht.Execute(new HibernateDelegate(Del)); + status.RollbackOnly = true; + return null; + } + + private object Del(ISession session) + { + return list; + } + + } + #endregion + + #region Supporting classes for test ParticipatingTransactionWithWithRequiresNew + + public class ParticipatingTransactionWithWithRequiresNewTxCallback : ITransactionCallback + { + private TransactionTemplate tt; + private ISessionFactory sf; + + public ParticipatingTransactionWithWithRequiresNewTxCallback(TransactionTemplate tt, ISessionFactory sf) + { + this.tt = tt; + this.sf = sf; + } + + public object DoInTransaction(ITransactionStatus status) + { + SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.GetResource(sf); + Assert.IsNotNull(holder,"Has thread session"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + + tt.Execute(new RequiresNewTxCallback(sf, holder)); + + Assert.IsTrue(holder.Session == SessionFactoryUtils.GetSession(sf, false),"Same thread session as before"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + return null; + } + + + } + + public class RequiresNewTxCallback : ITransactionCallback + { + private SessionHolder holder; + private ISessionFactory sf; + + public RequiresNewTxCallback(ISessionFactory sf, SessionHolder holder) + { + this.holder = holder; + this.sf = sf; + } + + public object DoInTransaction(ITransactionStatus status) + { + HibernateTemplate ht = new HibernateTemplate(sf); + ht.TemplateFlushMode = TemplateFlushMode.Eager; + return ht.ExecuteFind(new RequiresNewTxCallbackInner(holder)); + } + } + + public class RequiresNewTxCallbackInner : IHibernateCallback + { + private SessionHolder holder; + + public RequiresNewTxCallbackInner(SessionHolder holder) + { + this.holder = holder; + } + + public object DoInHibernate(ISession session) + { + Assert.IsTrue(session != holder.Session,"Not enclosing session"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + return null; + } + } + + #endregion + + + #region Supporting classes for test ParticipatingTransactionWithWithNotSupported + + public class ParticipatingTransactionWithWithNotSupportedTxCallback : ITransactionCallback + { + private TransactionTemplate tt; + private ISessionFactory sf; + + public ParticipatingTransactionWithWithNotSupportedTxCallback(TransactionTemplate tt, ISessionFactory sf) + { + this.tt = tt; + this.sf = sf; + } + + public object DoInTransaction(ITransactionStatus status) + { + SessionHolder holder = (SessionHolder)TransactionSynchronizationManager.GetResource(sf); + Assert.IsNotNull(holder, "Has thread session"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + tt.PropagationBehavior = TransactionPropagation.NotSupported; + tt.Execute(new NotSupportedTxCallback(sf)); + + Assert.IsTrue(holder.Session == SessionFactoryUtils.GetSession(sf, false), "Same thread session as before"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + return null; + } + + + } + + public class NotSupportedTxCallback : ITransactionCallback + { + private ISessionFactory sf; + + public NotSupportedTxCallback(ISessionFactory sf) + { + this.sf = sf; + } + + public object DoInTransaction(ITransactionStatus status) + { + + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sf), "Hasn't thread session"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive); + + HibernateTemplate ht = new HibernateTemplate(sf); + ht.TemplateFlushMode = TemplateFlushMode.Eager; + return ht.ExecuteFind(new NotSupportedTxCallbackInner()); + } + } + + public class NotSupportedTxCallbackInner : IHibernateCallback + { + + public object DoInHibernate(ISession session) + { + return null; + } + } + + #endregion + + #region Supporting classes for test TransactionWithPropagationSupports + + public class TransactionWithPropagationSupportsTxCallback : ITransactionCallback + { + private ISessionFactory sf; + public TransactionWithPropagationSupportsTxCallback(ISessionFactory sf) + { + + this.sf = sf; + + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sf), "Hasn't thread session"); + Assert.IsTrue(!status.IsNewTransaction, "Is not new transaction"); + + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive); + + HibernateTemplate ht = new HibernateTemplate(sf); + ht.TemplateFlushMode = TemplateFlushMode.Eager; + object returnValue = ht.Execute(new HibernateDelegate(Del)); + Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session"); + return null; + } + + private object Del(ISession session) + { + return null; + } + } + + #endregion + + + #region Supporting classes for test ParticipatingTransactionWithWithNotSupported + + public class TransactionWithPropagationSupportsAndInnerTransactionTxCallback : ITransactionCallback + { + private TransactionTemplate tt; + private ISessionFactory sf; + private HibernateTemplate ht; + private ISession session1; + private ISession session2; + + public TransactionWithPropagationSupportsAndInnerTransactionTxCallback(TransactionTemplate tt, + ISessionFactory sf, HibernateTemplate ht, ISession session1, ISession session2) + { + this.tt = tt; + this.sf = sf; + this.ht = ht; + this.session1 = session1; + this.session2 = session2; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sf), "Hasn't thread session"); + Assert.IsTrue(!status.IsNewTransaction, "Is not new transaction"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive); + + ht.Execute(new HibernateDelegate(HibernateDelegate)); + + Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session"); + + tt.Execute(new PropagationSupportsTxCallback(ht, session2)); + + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive); + + return null; + } + + private object HibernateDelegate(ISession session) + { + Assert.AreSame(session1, session); + return null; + } + + public class PropagationSupportsTxCallback : ITransactionCallback + { + private HibernateTemplate ht; + private ISession session2; + + public PropagationSupportsTxCallback(HibernateTemplate ht, ISession session2) + { + this.ht = ht; + this.session2 = session2; + } + + public object DoInTransaction(ITransactionStatus status) + { + return ht.Execute(new HibernateDelegate(HibernateDelegate)); + } + + private object HibernateDelegate(ISession session) + { + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + Assert.AreSame(session2, session); + return null; + } + } + + + } + + + + #endregion + + #region Supporting classes for DoTransactionCommitWithFlushFailure + + public class TransactionCommitWithFlushFailureCallback : ITransactionCallback + { + private ISessionFactory sessionFactory; + private IList list; + + public TransactionCommitWithFlushFailureCallback(ISessionFactory sessionFactory, IList list) + { + this.sessionFactory = sessionFactory; + this.list = list; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(sessionFactory), "Has thread session"); + HibernateTemplate ht = new HibernateTemplate(sessionFactory); + return ht.ExecuteFind(new TransactionCommitWithFlushFailureHibernateCallback(list)); + } + + + } + + public class TransactionCommitWithFlushFailureHibernateCallback : IHibernateCallback + { + private IList list; + public TransactionCommitWithFlushFailureHibernateCallback(IList list) + { + this.list = list; + } + + public object DoInHibernate(ISession session) + { + return list; + } + } + + #endregion + +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/ISimpleService.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/ISimpleService.cs new file mode 100644 index 00000000..99ef989b --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/ISimpleService.cs @@ -0,0 +1,14 @@ +/* + * Created by: + * Created: Monday, July 16, 2007 + */ + +using Spring.Objects; + +namespace Spring.Data.NHibernate +{ + public interface ISimpleService + { + void DoWork(TestObject testObject); + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/ITestObjectDao.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/ITestObjectDao.cs new file mode 100644 index 00000000..46e48760 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/ITestObjectDao.cs @@ -0,0 +1,41 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +namespace Spring.Data.NHibernate +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: ITestObjectDao.cs,v 1.1 2007/07/18 18:44:57 oakinger Exp $ + public interface ITestObjectDao + { + void Create(TestObject to); + void Update(TestObject to); + void Delete(TestObject to); + TestObject FindByName(string name); + //IList FindAll(); + //int GetCount(); + //int GetCountByDelegate(); + void CreateUpdateRollback(TestObject to); + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/LocalSessionFactoryObjectTests.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/LocalSessionFactoryObjectTests.cs new file mode 100644 index 00000000..877eac9d --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/LocalSessionFactoryObjectTests.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.IO; +using NHibernate; +using NHibernate.Cfg; +using NUnit.Framework; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// This class contains tests for LocalSessionFactoryObject + /// + /// Mark Pollack + /// $Id: LocalSessionFactoryObjectTests.cs,v 1.1 2008/01/29 20:07:18 markpollack Exp $ + [TestFixture] + public class LocalSessionFactoryObjectTests + { + + [Test] + public void LocalSessionFactoryObjectWithDbProviderAndProperties() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); + dbProvider.ConnectionString = "badConnectionString"; + LocalSessionFactoryObject sfo = new LocalSessionFactoryObject(); + sfo.DbProvider = dbProvider; + IDictionary properties = new Hashtable(); + properties.Add("hibernate.connection.driver_class", "NHibernate.Driver.SqlClientDriver"); + properties.Add("hibernate.connection.provider", "NHibernate.Connection.DriverConnectionProvider"); + sfo.HibernateProperties = properties; + sfo.AfterPropertiesSet(); + + Assert.IsNotNull(sfo.Configuration); + Assert.AreEqual(sfo.Configuration.Properties[Environment.ConnectionProvider].ToString(), + "NHibernate.Connection.DriverConnectionProvider"); + } + + [Test] + [ExpectedException(typeof(FileNotFoundException))] + public void LocalSessionFactoryObjectWithInvalidMapping() + { + LocalSessionFactoryObject sfo = new LocalSessionFactoryObject(); + sfo.MappingResources = new string[] { "mapping.hbm.xml"}; + sfo.AfterPropertiesSet(); + + } + + [Test] + [ExpectedException(typeof(HibernateException))] + public void LocalSessionFactoryObjectWithInvalidProperties() + { + LocalSessionFactoryObject sfo = new LocalSessionFactoryObject(); + IDictionary properties = new Hashtable(); + properties.Add(Environment.ConnectionProvider, "myClass"); + sfo.HibernateProperties = properties; + sfo.AfterPropertiesSet(); + } + } + + +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/NHTestObjectDao.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/NHTestObjectDao.cs new file mode 100644 index 00000000..b5637055 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/NHTestObjectDao.cs @@ -0,0 +1,101 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using NHibernate.Type; +using Spring.Data.NHibernate.Support; + + +#endregion + +namespace Spring.Data.NHibernate +{ + /// + /// NHibernate based DAO implementation of ITestObjectDAao: + /// + /// Mark Pollack (.NET) + /// $Id: NHTestObjectDao.cs,v 1.1 2007/07/18 18:44:57 oakinger Exp $ + public class TestObjectDao : HibernateDaoSupport, ITestObjectDao + { + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public TestObjectDao() + { + + } + + #endregion + + #region ITestObjectDao Members + + + public void Create(TestObject to) + { + //HibernateTemplate.Save(to); + } + + + public void Update(TestObject to) + { + HibernateTemplate.SaveOrUpdate(to); + } + + + public void Delete(TestObject to) + { + HibernateTemplate.Delete(to); + } + + public TestObject FindByName(string name) + { + IList result = HibernateTemplate.Find( + "select from TestObject as to where to.Name=?", + name, + TypeFactory.GetStringType(50) + ); + + if (result.Count > 0) + { + return (TestObject)result[0]; + } + else + { + return null; + } + } + + public void CreateUpdateRollback(TestObject to) + { + HibernateTemplate.Save(to); + to.Name = "Updated Name"; + HibernateTemplate.SaveOrUpdate(to); + throw new Exception("My expected exception for test purposes."); + } + + #endregion + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/SimpleService.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/SimpleService.cs new file mode 100644 index 00000000..0c6b2de5 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/SimpleService.cs @@ -0,0 +1,51 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +using Spring.Transaction.Interceptor; + +namespace Spring.Data.NHibernate +{ + /// + /// This is + /// + /// + /// + /// + /// Mark Pollack + /// $Id: SimpleService.cs,v 1.1 2007/07/18 18:44:57 oakinger Exp $ + public class SimpleService : ISimpleService + { + private ITestObjectDao testObjectDao; + + + public ITestObjectDao TestObjectDao + { + get { return testObjectDao; } + set { testObjectDao = value; } + } + + [Transaction()] + public void DoWork(TestObject testObject) + { + testObjectDao.Create(testObject); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Support/ConfigSectionSessionScopeSettingsTests.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Support/ConfigSectionSessionScopeSettingsTests.cs new file mode 100644 index 00000000..a2cbcbf9 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Support/ConfigSectionSessionScopeSettingsTests.cs @@ -0,0 +1,124 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using NHibernate; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Context.Support; +using Spring.Objects.Factory.Config; + +#endregion + +namespace Spring.Data.NHibernate.Support +{ + /// + /// Tests behaviour. + /// + /// Erich Eichinger + /// $Id: ConfigSectionSessionScopeSettingsTests.cs,v 1.1 2007/08/22 20:17:43 oakinger Exp $ + [TestFixture] + public class ConfigSectionSessionScopeSettingsTests + { + private class NameValueCollectionVariableSource : IVariableSource + { + private readonly NameValueCollection nameValuePairs = new NameValueCollection(); + + public NameValueCollectionVariableSource Add(string key, string value) + { + nameValuePairs.Add(key, value); + return this; + } + + public string ResolveVariable(string name) + { + return nameValuePairs[name]; + } + } + + [Test] + public void CanCreateWithDefaults() + { + string SESSIONFACTORY_OBJECTNAME = ConfigSectionSessionScopeSettings.DEFAULT_SESSION_FACTORY_OBJECT_NAME; + + // setup expected values + MockRepository mocks = new MockRepository(); + ISessionFactory expectedSessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + IInterceptor expectedEntityInterceptor = null; + bool expectedSingleSession = SessionScopeSettings.SINGLESESSION_DEFAULT; + FlushMode expectedDefaultFlushMode = SessionScopeSettings.FLUSHMODE_DEFAULT; + + // create and register context + StaticApplicationContext appCtx = new StaticApplicationContext(); + appCtx.Name = AbstractApplicationContext.DefaultRootContextName; + appCtx.ObjectFactory.RegisterSingleton(SESSIONFACTORY_OBJECTNAME, expectedSessionFactory); + ContextRegistry.Clear(); + ContextRegistry.RegisterContext(appCtx); + + ConfigSectionSessionScopeSettings settings = new ConfigSectionSessionScopeSettings(this.GetType(), (IVariableSource)null); + + Assert.AreEqual(expectedSessionFactory, settings.SessionFactory); + Assert.AreEqual(expectedEntityInterceptor, settings.EntityInterceptor); + Assert.AreEqual(expectedSingleSession, settings.SingleSession); + Assert.AreEqual(expectedDefaultFlushMode, settings.DefaultFlushMode); + } + + [Test] + public void CanCreateFromExplicitConfiguration() + { + string SESSIONFACTORY_OBJECTNAME = "SessionFactory"; + string ENTITYINTERCEPTOR_OBJECTNAME = "EntityInterceptor"; + + MockRepository mocks = new MockRepository(); + ISessionFactory expectedSessionFactory = (ISessionFactory) mocks.CreateMock(typeof(ISessionFactory)); + IInterceptor expectedEntityInterceptor = (IInterceptor) mocks.CreateMock(typeof(IInterceptor)); + bool expectedSingleSession = false; + FlushMode expectedDefaultFlushMode = FlushMode.Auto; + + // create and register context + StaticApplicationContext appCtx = new StaticApplicationContext(); + appCtx.Name = AbstractApplicationContext.DefaultRootContextName; + appCtx.ObjectFactory.RegisterSingleton(SESSIONFACTORY_OBJECTNAME, expectedSessionFactory); + appCtx.ObjectFactory.RegisterSingleton(ENTITYINTERCEPTOR_OBJECTNAME, expectedEntityInterceptor); + ContextRegistry.Clear(); + ContextRegistry.RegisterContext(appCtx); + + // simulate config section + string thisTypeName = this.GetType().FullName; + NameValueCollectionVariableSource variableSource = new NameValueCollectionVariableSource() + .Add(thisTypeName + ".SessionFactoryObjectName", SESSIONFACTORY_OBJECTNAME) + .Add(thisTypeName + ".EntityInterceptorObjectName", ENTITYINTERCEPTOR_OBJECTNAME) + .Add(thisTypeName + ".SingleSession", expectedSingleSession.ToString().ToLower() ) // case insensitive! + .Add(thisTypeName + ".DefaultFlushMode", expectedDefaultFlushMode.ToString().ToLower() ) // case insensitive! + ; + + + ConfigSectionSessionScopeSettings settings = new ConfigSectionSessionScopeSettings(this.GetType(), variableSource); + + Assert.AreEqual( expectedSessionFactory, settings.SessionFactory ); + Assert.AreEqual( expectedEntityInterceptor, settings.EntityInterceptor ); + Assert.AreEqual( expectedSingleSession, settings.SingleSession ); + Assert.AreEqual( expectedDefaultFlushMode, settings.DefaultFlushMode ); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Support/SessionScopeSettingsTests.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Support/SessionScopeSettingsTests.cs new file mode 100644 index 00000000..e71a5fa6 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Support/SessionScopeSettingsTests.cs @@ -0,0 +1,162 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NHibernate; +using NUnit.Framework; +using Rhino.Mocks; + +#endregion + +namespace Spring.Data.NHibernate.Support +{ + /// + /// Tests functionality. + /// + /// Erich Eichinger + /// $Id: SessionScopeSettingsTests.cs,v 1.2 2008/04/25 13:25:09 markpollack Exp $ + [TestFixture] + public class SessionScopeSettingsTests + { + private class DerivedSessionScopeSettings : SessionScopeSettings + { + public DerivedSessionScopeSettings() + :base() // note, that we're calling default ctor here + { + } + } + + private class LazyResolvingSessionScopeSettings : SessionScopeSettings + { + private readonly ISessionFactory sessionFactory; + private readonly IInterceptor entityInterceptor; + + + public LazyResolvingSessionScopeSettings(ISessionFactory sessionFactory, IInterceptor entityInterceptor) + : base() // note, that we're calling default ctor here + { + this.sessionFactory = sessionFactory; + this.entityInterceptor = entityInterceptor; + } + + protected override ISessionFactory ResolveSessionFactory() + { + // simulates lazy resolving + return this.sessionFactory; + } + + protected override IInterceptor ResolveEntityInterceptor() + { + // simulates lazy resolving + return this.entityInterceptor; + } + } + + [Test] + public void CheckDefaults() + { + Assert.IsTrue( SessionScopeSettings.SINGLESESSION_DEFAULT ); + Assert.AreEqual( FlushMode.Never, SessionScopeSettings.FLUSHMODE_DEFAULT ); + } + + [Test] + public void WorksAsExpected() + { + MockRepository mocks = new MockRepository(); + ISessionFactory sessionFactory = (ISessionFactory) mocks.CreateMock(typeof(ISessionFactory)); + IInterceptor entityInterceptor = (IInterceptor) mocks.CreateMock(typeof(IInterceptor)); + Assert.AreNotEqual( FlushMode.Auto, SessionScopeSettings.FLUSHMODE_DEFAULT ); // ensure noone changed our assumptions + SessionScopeSettings sss = new SessionScopeSettings(sessionFactory, entityInterceptor, !SessionScopeSettings.SINGLESESSION_DEFAULT, FlushMode.Auto ); + + Assert.AreEqual( sessionFactory, sss.SessionFactory ); + Assert.AreEqual( entityInterceptor, sss.EntityInterceptor); + Assert.AreEqual( !SessionScopeSettings.SINGLESESSION_DEFAULT, sss.SingleSession ); + Assert.AreEqual( FlushMode.Auto, sss.DefaultFlushMode ); + } + + [Test] + public void CallingDefaultConstructorLeavesReferencesMarkedUninitialized() + { + DerivedSessionScopeSettings sss = new DerivedSessionScopeSettings(); + // config values are set to their defaults + Assert.AreEqual(SessionScopeSettings.SINGLESESSION_DEFAULT, sss.SingleSession); + Assert.AreEqual(SessionScopeSettings.FLUSHMODE_DEFAULT, sss.DefaultFlushMode); + + IInterceptor interceptor = sss.EntityInterceptor; + Assert.IsNull(interceptor); + + try + { + ISessionFactory sessionFactory = sss.SessionFactory; + Assert.Fail("should fail, because derived classes must override ResolveSessionFactory()"); + } + catch (NotImplementedException) { } + } + + [Test] + public void CallingDefaultConstructorCausesLazyResolvingReferences() + { + MockRepository mocks = new MockRepository(); + ISessionFactory expectedSessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + IInterceptor expectedEntityInterceptor = (IInterceptor)mocks.CreateMock(typeof(IInterceptor)); + + SessionScopeSettings sss = new LazyResolvingSessionScopeSettings(expectedSessionFactory, expectedEntityInterceptor); + + // config values are set to their defaults + Assert.AreEqual(SessionScopeSettings.SINGLESESSION_DEFAULT, sss.SingleSession); + Assert.AreEqual(SessionScopeSettings.FLUSHMODE_DEFAULT, sss.DefaultFlushMode); + + Assert.AreSame(expectedSessionFactory, sss.SessionFactory); + Assert.AreSame(expectedEntityInterceptor, sss.EntityInterceptor); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void MissingSessionFactoryCausesArgumentExceptionDuringLazyResolving() + { + SessionScopeSettings sss = new LazyResolvingSessionScopeSettings(null, null); + + // config values are set to their defaults + Assert.AreEqual(SessionScopeSettings.SINGLESESSION_DEFAULT, sss.SingleSession); + Assert.AreEqual(SessionScopeSettings.FLUSHMODE_DEFAULT, sss.DefaultFlushMode); + + ISessionFactory sessionFactory = sss.SessionFactory; + } + + [Test] + public void MissingEntityInterceptorIsOkDuringLazyResolving() + { + MockRepository mocks = new MockRepository(); + ISessionFactory expectedSessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory)); + + SessionScopeSettings sss = new LazyResolvingSessionScopeSettings(expectedSessionFactory, null); + + // config values are set to their defaults + Assert.AreEqual(SessionScopeSettings.SINGLESESSION_DEFAULT, sss.SingleSession); + Assert.AreEqual(SessionScopeSettings.FLUSHMODE_DEFAULT, sss.DefaultFlushMode); + + Assert.AreSame(expectedSessionFactory, sss.SessionFactory); + Assert.AreSame(null, sss.EntityInterceptor); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Support/SessionScopeTests.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Support/SessionScopeTests.cs new file mode 100644 index 00000000..64bf223f --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/Support/SessionScopeTests.cs @@ -0,0 +1,324 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NHibernate; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.NHibernate.Support +{ + /// + /// Tests for SessionScope + /// + /// Erich Eichinger + /// $Id: SessionScopeTests.cs,v 1.5 2008/05/13 14:22:47 oakinger Exp $ + [TestFixture] + public class SessionScopeTests + { + private MockRepository repository; + private ISessionFactory expectedSessionFactory; + private IInterceptor expectedEntityInterceptor; + private bool expectedSingleSession; + private FlushMode expectedDefaultFlushMode; + + [SetUp] + public void SetUp() + { + repository = new MockRepository(); + expectedSessionFactory = (ISessionFactory)repository.CreateMock(typeof(ISessionFactory)); + expectedEntityInterceptor = (IInterceptor)repository.CreateMock(typeof(IInterceptor)); + expectedSingleSession = SessionScopeSettings.SINGLESESSION_DEFAULT; + expectedDefaultFlushMode = SessionScopeSettings.FLUSHMODE_DEFAULT; + } + + [Test] + public void CanCreateAndClose() + { + using (SessionScope scope = new SessionScope(expectedSessionFactory, expectedEntityInterceptor, expectedSingleSession, expectedDefaultFlushMode, false)) + { + // no op - just create & dispose + Assert.AreSame(expectedSessionFactory, scope.SessionFactory); + Assert.AreSame(expectedEntityInterceptor, scope.EntityInterceptor); + Assert.AreEqual(expectedSingleSession, scope.SingleSession); + Assert.AreEqual(expectedDefaultFlushMode, scope.DefaultFlushMode); + + // ensure nothing got registered with TSM + Assert.IsFalse(TransactionSynchronizationManager.HasResource(expectedSessionFactory)); + + scope.Close(); + } + } + + [Test] + public void CanCreateAndCloseSimpleCtor() + { + using (repository.Ordered()) + { + ISession session = (ISession) repository.CreateMock(typeof (ISession)); + Expect.Call(expectedSessionFactory.OpenSession()).Return(session); + session.FlushMode = FlushMode.Never; + Expect.Call(session.Close()).Return(null); + } + repository.ReplayAll(); + using (SessionScope scope = new SessionScope(expectedSessionFactory, true)) + { + // no op - just create & dispose + Assert.AreSame(expectedSessionFactory, scope.SessionFactory); + //Assert.AreSame(null, scope.EntityInterceptor); + Assert.AreEqual(expectedSingleSession, scope.SingleSession); + Assert.AreEqual(expectedDefaultFlushMode, scope.DefaultFlushMode); + + // ensure SessionHolder object is registered with TSM + Assert.IsTrue(TransactionSynchronizationManager.HasResource(expectedSessionFactory)); + + SessionHolder sessionHolder = + (SessionHolder) TransactionSynchronizationManager.GetResource(expectedSessionFactory); + // by default session is lazy, so ask for it. + Assert.IsNotNull(sessionHolder.Session); + scope.Close(); + } + repository.VerifyAll(); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void OpeningTwiceThrowsInvalidOperationException() + { + using (SessionScope scope = new SessionScope(expectedSessionFactory, true)) + { + scope.Open(); + } + } + + [Test] + public void ClosingTwiceIsIgnored() + { + using (SessionScope scope = new SessionScope(expectedSessionFactory, true)) + { + scope.Close(); + scope.Close(); + } + } + + [Test] + public void DisposeClosesScope() + { + SessionScope scope = new SessionScope(expectedSessionFactory, true); + Assert.IsTrue(scope.IsOpen); + scope.Dispose(); + Assert.IsFalse(scope.IsOpen); + } + + [Test] + public void DoesOpenImmediatelyOnOpenIsTrue() + { + SessionScope scope = null; + using (scope = new SessionScope(expectedSessionFactory, true)) + { + // ensure is open + Assert.IsTrue(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + + scope.Close(); + // ensure is closed + Assert.IsFalse(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + } + Assert.IsFalse(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + + using (scope = new SessionScope(expectedSessionFactory, true)) + { + // ensure is open + Assert.IsTrue(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + } + // ensure dispose closes scope + Assert.IsFalse(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + } + + [Test] + public void DoesNotOpenImmediatelyOnOpenIsFalse() + { + SessionScope scope = null; + using (scope = new SessionScope(expectedSessionFactory, false)) + { + // ensure is *not* open + Assert.IsFalse(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + + scope.Open(); + // ensure is open now + Assert.IsTrue(scope.IsOpen); + + scope.Close(); + // ensure is closed + Assert.IsFalse(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + } + // ensure is closed + Assert.IsFalse(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + } + + [Test] + public void SingleSessionRegistersSessionHolderWithTSM() + { + SessionScope scope = null; + using (scope = new SessionScope(expectedSessionFactory, null, true, FlushMode.Auto, true)) + { + // ensure is open + Assert.IsTrue(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + + // ensure registered sessionholder with TSM + Assert.IsTrue(TransactionSynchronizationManager.HasResource(expectedSessionFactory)); + // ensure a sessionHolder is registered + SessionHolder sessionHolder = TransactionSynchronizationManager.GetResource(expectedSessionFactory) as SessionHolder; + Assert.IsNotNull(sessionHolder); + } + // ensure scope is closed and sessionHolder is unregistered from TSM + Assert.IsFalse(scope.IsOpen); + Assert.IsFalse(TransactionSynchronizationManager.HasResource(expectedSessionFactory)); + } + + [Test] + public void SingleSessionAppliesDefaultFlushModeOnOpenSessionAndClosesSession() + { + ISession expectedSession = (ISession)repository.CreateMock(typeof(ISession)); + + Expect.Call(expectedSessionFactory.OpenSession()).Return(expectedSession); + expectedSession.FlushMode = FlushMode.Auto; + Expect.Call(expectedSession.Close()).Return(null); + repository.ReplayAll(); + + SessionScope scope = null; + using (scope = new SessionScope(expectedSessionFactory, null, true, FlushMode.Auto, true)) + { + SessionHolder sessionHolder = (SessionHolder)TransactionSynchronizationManager.GetResource(expectedSessionFactory); + Assert.IsTrue(sessionHolder.ContainsSession(expectedSession)); + } + // ensure scope is closed and sessionHolder is unregistered from TSM + Assert.IsFalse(scope.IsOpen); + Assert.IsFalse(TransactionSynchronizationManager.HasResource(expectedSessionFactory)); + + repository.VerifyAll(); + } + + [Test] + public void SingleSessionNestedSessionParticipatesInParentScopeSessionFactory() + { + SessionScope scope = null; + using (scope = new SessionScope(expectedSessionFactory, null, true, FlushMode.Auto, true)) + { + Assert.IsTrue(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + + using (SessionScope innerScope = new SessionScope(expectedSessionFactory, true)) + { + // outer scope didn't change + Assert.IsTrue(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + + // participating only - no SessionHolder will be registered! + Assert.IsTrue(innerScope.IsOpen); + Assert.IsTrue(innerScope.IsParticipating); + + innerScope.Close(); + + Assert.IsFalse(innerScope.IsOpen); + Assert.IsFalse(innerScope.IsParticipating); + + // outer scope didn't change + Assert.IsTrue(scope.IsOpen); + Assert.IsFalse(scope.IsParticipating); + Assert.IsTrue(TransactionSynchronizationManager.HasResource(expectedSessionFactory)); + } + } + + // ensure scope is closed and sessionHolder is unregistered from TSM + Assert.IsFalse(scope.IsOpen); + Assert.IsFalse(TransactionSynchronizationManager.HasResource(expectedSessionFactory)); + } + + public class TestSessionScopeSettings : SessionScopeSettings + { + public TestSessionScopeSettings(ISessionFactory sessionFactory) + : base(sessionFactory) + { + } + + protected override IInterceptor ResolveEntityInterceptor() + { + return DoResolveEntityInterceptor(); + } + + public virtual IInterceptor DoResolveEntityInterceptor() + { + return base.ResolveEntityInterceptor(); + } + } + + [Test] + public void ResolvesEntityInterceptorOnEachOpen() + { + TestSessionScopeSettings sss = + (TestSessionScopeSettings)repository.PartialMock(typeof(TestSessionScopeSettings), expectedSessionFactory); + ISession expectedSession = (ISession)repository.CreateMock(typeof(ISession)); + sss.DefaultFlushMode = FlushMode.Never; + + SessionScope sc = new SessionScope(sss, false); + + using (repository.Ordered()) + { + Expect.Call(sss.DoResolveEntityInterceptor()).Return(expectedEntityInterceptor); + Expect.Call(expectedSessionFactory.OpenSession(expectedEntityInterceptor)).Return(expectedSession); + expectedSession.FlushMode = FlushMode.Never; + Expect.Call(expectedSession.Close()).Return(null); + + Expect.Call(sss.DoResolveEntityInterceptor()).Return(expectedEntityInterceptor); + Expect.Call(expectedSessionFactory.OpenSession(expectedEntityInterceptor)).Return(expectedSession); + expectedSession.FlushMode = FlushMode.Never; + Expect.Call(expectedSession.Close()).Return(null); + } + repository.ReplayAll(); + + sc.Open(); + SessionHolder sessionHolder = (SessionHolder)TransactionSynchronizationManager.GetResource(expectedSessionFactory); + sessionHolder.ContainsSession(null); // force opening session + sc.Close(); + + sc.Open(); + sessionHolder = (SessionHolder)TransactionSynchronizationManager.GetResource(expectedSessionFactory); + sessionHolder.ContainsSession(null); // force opening session + sc.Close(); + + repository.VerifyAll(); + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/TestObject.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/TestObject.cs new file mode 100644 index 00000000..5076a2b0 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/TestObject.cs @@ -0,0 +1,76 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +namespace Spring.Data.NHibernate +{ + /// + /// TODO: + /// + /// Mark Pollack (.NET) + /// $Id: TestObject.cs,v 1.1 2007/07/18 18:44:57 oakinger Exp $ + public class TestObject + { + #region Fields + private int age; + private string name; + private int objectNumber; + + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public TestObject() + { + + } + + #endregion + + #region Properties + + public int Age + { + get { return age; } + set { age = value; } + } + + public string Name + { + get { return name; } + set { name = value; } + } + + public int ObjectNumber + { + get { return objectNumber; } + set { objectNumber = value; } + } + + #endregion + + #region Methods + + #endregion + + } +} diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/TestObject.hbm.xml b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/TestObject.hbm.xml new file mode 100644 index 00000000..60bd056e --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/TestObject.hbm.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.2003.csproj b/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.2003.csproj new file mode 100644 index 00000000..571fac78 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.2003.csproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.2005.csproj b/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.2005.csproj new file mode 100644 index 00000000..eca1d86a --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.2005.csproj @@ -0,0 +1,123 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {94E4E1B4-D424-4EB9-BF34-2EE8CC3D7048} + Library + Properties + Spring + Spring.Data.NHibernate.Tests + + + true + full + false + ..\..\..\build\VS.Net.2005\Spring.Data.NHibernate.Tests\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + ..\..\..\build\VS.Net.2005\Spring.Data.NHibernate.Tests\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\lib\NHibernate10\net\2.0\Castle.DynamicProxy.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\NHibernate10\net\2.0\Iesi.Collections.dll + + + False + ..\..\..\lib\NHibernate10\net\2.0\log4net.dll + + + False + ..\..\..\lib\NHibernate10\net\2.0\NHibernate.dll + + + False + ..\..\..\lib\net\2.0\nunit.framework.dll + + + False + ..\..\..\lib\Net\2.0\Rhino.Mocks.dll + + + + + + + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {130E1609-45A7-4F65-B112-105F2DD3E2CE} + Spring.Data.NHibernate.2005 + + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 + + + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0} + Spring.Data.Tests.2005 + + + + + + + + + + + + + + + + + + + + + + Always + + + + + + + + + echo "Copying .xml files for tests" +xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2005\Spring.Data.NHibernate.Tests\$(ConfigurationName)\ /y /s /q /d +xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2005\Spring.Data.NHibernate.Tests\$(ConfigurationName)\ /y /s /q + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.build b/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.build new file mode 100644 index 00000000..126b1971 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.build @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.dll.config b/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.dll.config new file mode 100644 index 00000000..3f8da4d1 --- /dev/null +++ b/test/Spring/Spring.Data.NHibernate.Tests/Spring.Data.NHibernate.Tests.dll.config @@ -0,0 +1,30 @@ + + + + +
    + + +
    + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/AssemblyInfo.cs b/test/Spring/Spring.Data.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..5bee2530 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("Spring.Data Tests")] +[assembly: AssemblyDescription("Unit tests for Spring.Data assembly")] diff --git a/test/Spring/Spring.Data.Tests/Dao/IncorrectResultSizeDataAccessExceptionTests.cs b/test/Spring/Spring.Data.Tests/Dao/IncorrectResultSizeDataAccessExceptionTests.cs new file mode 100644 index 00000000..25645794 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Dao/IncorrectResultSizeDataAccessExceptionTests.cs @@ -0,0 +1,15 @@ +using NUnit.Framework; +namespace Spring.Dao +{ + [TestFixture] + public class IncorrectResultSizeDataAccessExceptionTests + { + [Test] + public void SizeGetter( ) + { + IncorrectResultSizeDataAccessException ex = new IncorrectResultSizeDataAccessException( 1, 4 ); + Assert.AreEqual( 1, ex.ExpectedSize ); + Assert.AreEqual( 4, ex.ActualSize ); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Dao/IncorrectUpdateSemanticsDataAccessExceptionTests.cs b/test/Spring/Spring.Data.Tests/Dao/IncorrectUpdateSemanticsDataAccessExceptionTests.cs new file mode 100644 index 00000000..55b67c59 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Dao/IncorrectUpdateSemanticsDataAccessExceptionTests.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.Serialization; +using NUnit.Framework; +namespace Spring.Dao +{ + [TestFixture] + public class IncorrectUpdateSemanticsDataAccessExceptionTests + { + [Serializable] + public class SampleException : IncorrectUpdateSemanticsDataAccessException + { + public SampleException() : base() {} + public SampleException( string message ) : base( message ) {} + public SampleException( string message, Exception inner ) : base( message, inner ) {} + protected SampleException( SerializationInfo info, StreamingContext context ) : base( info, context ) {} + public override bool DataWasUpdated + { + get + { + return true; + } + } + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Dao/UncategorizedDataAccessExceptionTests.cs b/test/Spring/Spring.Data.Tests/Dao/UncategorizedDataAccessExceptionTests.cs new file mode 100644 index 00000000..1a5f6d00 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Dao/UncategorizedDataAccessExceptionTests.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.Serialization; +using NUnit.Framework; +namespace Spring.Dao +{ + [TestFixture] + public class UncategorizedDataAccessExceptionTests + { + [Serializable] + public class SampleException : UncategorizedDataAccessException + { + public SampleException() : base() {} + public SampleException( string message ) : base( message ) {} + public SampleException( string message, Exception inner ) : base( message, inner ) {} + protected SampleException( SerializationInfo info, StreamingContext context ) : base( info, context ) {} + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Data/AdoPlatformTransactionManagerTests.cs b/test/Spring/Spring.Data.Tests/Data/AdoPlatformTransactionManagerTests.cs new file mode 100644 index 00000000..fccacd8d --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/AdoPlatformTransactionManagerTests.cs @@ -0,0 +1,1479 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using System.Threading; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Dao; +using Spring.Data.Common; +using Spring.Data.Core; +using Spring.Data.Support; +using Spring.Support; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data +{ + /// + /// This class contains tests for AdoPlatformTransactionManager + /// + /// Mark Pollack + /// $Id: AdoPlatformTransactionManagerTests.cs,v 1.8 2007/12/07 07:31:05 markpollack Exp $ + [TestFixture] + public class AdoPlatformTransactionManagerTests + { + private MockRepository mocks; + + [SetUp] + public void Setup() + { + mocks = new MockRepository(); + } + + [TearDown] + public void TearDown() + { + Assert.IsTrue(TransactionSynchronizationManager.ResourceDictionary.Count == 0); + Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive); + } + + [Test] + public void TransactionCommit() + { + #region Mock setup + IDbProvider dbProvider = (IDbProvider) mocks.CreateMock(typeof (IDbProvider)); + IDbConnection connection = (IDbConnection) mocks.CreateMock(typeof (IDbConnection)); + IDbTransaction transaction = (IDbTransaction) mocks.CreateMock(typeof (IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + //standard tx timeout. + transaction.Commit(); + LastCall.On(transaction).Repeat.Once(); + connection.Dispose(); + } + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + + TransactionTemplate tt = new TransactionTemplate(tm); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + tt.Execute(new TransactionCommitTxCallback(dbProvider)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + mocks.VerifyAll(); + } + + [Test] + public void TransactionRollback() + { + #region Mock Setup + + IDbProvider dbProvider = (IDbProvider) mocks.CreateMock(typeof (IDbProvider)); + IDbConnection connection = (IDbConnection) mocks.CreateMock(typeof (IDbConnection)); + IDbTransaction transaction = (IDbTransaction) mocks.CreateMock(typeof (IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + //standard tx timeout. + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + + connection.Dispose(); + } + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + Exception ex = new ArgumentException("test exception"); + try + { + tt.Execute(new TransactionRollbackTxCallback(dbProvider, ex)); + Assert.Fail("Should have thrown exception"); + } + catch (ArgumentException e) + { + Assert.AreEqual(ex, e); + } + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + + mocks.VerifyAll(); + } + + [Test] + public void ParticipatingTransactionWithRollbackOnly() + { + IDbProvider dbProvider = (IDbProvider) mocks.CreateMock(typeof (IDbProvider)); + IDbConnection connection = (IDbConnection) mocks.CreateMock(typeof (IDbConnection)); + IDbTransaction transaction = (IDbTransaction) mocks.CreateMock(typeof (IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + //standard tx timeout. + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + connection.Dispose(); + } + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + ITransactionStatus ts = tm.GetTransaction(new DefaultTransactionDefinition()); + TestTransactionSynchronization synch = + new TestTransactionSynchronization(dbProvider, TransactionSynchronizationStatus.Rolledback); + TransactionSynchronizationManager.RegisterSynchronization(synch); + + bool outerTransactionBoundaryReached = false; + try + { + Assert.IsTrue(ts.IsNewTransaction); + TransactionTemplate tt = new TransactionTemplate(tm); + TransactionTemplate tt2 = new TransactionTemplate(tm); + tt.Execute(new ParticipatingTxWithRollbackOnlyTxCallback(tt2, dbProvider)); + outerTransactionBoundaryReached = true; + tm.Commit(ts); + Assert.Fail("Should have thrown UnexpectedRollbackException"); + } + catch (UnexpectedRollbackException) + { + // expected + if (!outerTransactionBoundaryReached) + { + tm.Rollback(ts); + } + Assert.IsTrue(outerTransactionBoundaryReached); + } + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsFalse(synch.beforeCommitCalled); + Assert.IsTrue(synch.beforeCompletionCalled); + Assert.IsFalse(synch.afterCommitCalled); + Assert.IsTrue(synch.afterCompletionCalled); + + mocks.VerifyAll(); + } + + [Test] + [Ignore] + public void ParticipatingTransactionWithTransactionStartedFromSynch() + { + } + + [Test] + [Ignore] + public void ParticipatingTransactionWithRollbackOnlyAndInnerSynch() + { + } + + [Test] + public void PropagationRequiresNewWithExistingTransaction() + { + #region Mock Setup + IDbProvider dbProvider = (IDbProvider) mocks.CreateMock(typeof (IDbProvider)); + IDbConnection connection = (IDbConnection) mocks.CreateMock(typeof (IDbConnection)); + IDbTransaction transaction = (IDbTransaction) mocks.CreateMock(typeof (IDbTransaction)); + + Expect.Call(dbProvider.CreateConnection()).Return(connection).Repeat.Twice(); + connection.Open(); + LastCall.On(connection).Repeat.Twice(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction).Repeat.Twice(); + //standard tx timeout. + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + + transaction.Commit(); + LastCall.On(transaction).Repeat.Once(); + + connection.Dispose(); + LastCall.On(connection).Repeat.Twice(); + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + tt.Execute(new PropagationRequiresNewWithExistingTransactionCallback(tt, dbProvider)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + mocks.VerifyAll(); + } + + [Test] + public void PropagationRequiresNewWithExistingTransactionAndUnrelatedDataSource() + { + IDbProvider dbProvider = (IDbProvider) mocks.CreateMock(typeof (IDbProvider)); + IDbConnection connection = (IDbConnection) mocks.CreateMock(typeof (IDbConnection)); + IDbTransaction transaction = (IDbTransaction) mocks.CreateMock(typeof (IDbTransaction)); + + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + transaction.Commit(); + LastCall.On(transaction).Repeat.Once(); + connection.Dispose(); + + IDbProvider dbProvider2 = (IDbProvider) mocks.CreateMock(typeof (IDbProvider)); + IDbConnection connection2 = (IDbConnection) mocks.CreateMock(typeof (IDbConnection)); + IDbTransaction transaction2 = (IDbTransaction) mocks.CreateMock(typeof (IDbTransaction)); + + Expect.Call(dbProvider2.CreateConnection()).Return(connection2); + connection2.Open(); + LastCall.On(connection2).Repeat.Once(); + Expect.Call(connection2.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction2); + transaction2.Rollback(); + LastCall.On(transaction2).Repeat.Once(); + connection2.Dispose(); + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + + AdoPlatformTransactionManager tm2 = new AdoPlatformTransactionManager(dbProvider2); + TransactionTemplate tt2 = new TransactionTemplate(tm2); + tt2.PropagationBehavior = TransactionPropagation.RequiresNew; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider2), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + + tt.Execute(new PropagationRequiresNewWithExistingTransactionCallback(tt2, dbProvider)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider2), "Hasn't thread db provider"); + + mocks.VerifyAll(); + } + + [Test] + public void PropagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSource() + { + #region Mock Setup + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + connection.Dispose(); + + IDbProvider dbProvider2 = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection2 = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction2 = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + Expect.Call(dbProvider2.CreateConnection()).Return(connection2); + connection2.Open(); + Exception failure = new Exception("can't open connection"); + LastCall.On(connection2).Throw(failure); + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + + AdoPlatformTransactionManager tm2 = new AdoPlatformTransactionManager(dbProvider2); + tm2.TransactionSynchronization = TransactionSynchronizationState.Never; + TransactionTemplate tt2 = new TransactionTemplate(tm2); + tt2.PropagationBehavior = TransactionPropagation.RequiresNew; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider2), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + try + { + tt.Execute( + new PropagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSourceCallback(tt2)); + Assert.Fail("Should have thrown CannotCreateTransactionException"); + } catch(CannotCreateTransactionException ex) + { + Assert.AreSame(failure, ex.InnerException); + } + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider2), "Hasn't thread db provider"); + mocks.VerifyAll(); + } + + [Test] + public void PropagationNotSupportedWithExistingTransaction() + { + #region Mock Setup + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + //standard tx timeout. + transaction.Commit(); + LastCall.On(transaction).Repeat.Once(); + connection.Dispose(); + } + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + tt.Execute(new PropagationNotSupportedWithExistingTransactionCallback(tt, dbProvider)); + + mocks.VerifyAll(); + } + + [Test] + public void PropagationNeverWithExistingTransaction() + { + #region Mock Setup + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + //standard tx timeout. + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + + connection.Dispose(); + } + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + try + { + tt.Execute(new PropagationNeverWithExistingTransactionCallback(tt)); + Assert.Fail("Should have thrown IllegalTransactionStateException"); + } catch (IllegalTransactionStateException) + { + //expected. + } + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + mocks.VerifyAll(); + } + + [Test] + public void PropagationRequiresNewWithExistingConnection() + { + #region Mock Setup + + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + connection.Dispose(); + + IDbConnection connection2 = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction2 = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + Expect.Call(dbProvider.CreateConnection()).Return(connection2); + connection2.Open(); + LastCall.On(connection2).Repeat.Once(); + Expect.Call(connection2.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction2); + transaction2.Commit(); + LastCall.On(transaction2).Repeat.Once(); + connection2.Dispose(); + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.Supports; + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + tt.Execute(new PropagationRequiresNewWithExistingConnectionCallback(tt, connection, connection2, dbProvider)); + + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + mocks.VerifyAll(); + } + + [Test] + public void TransactionWithIsolation() + { + #region Mock setup + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.Serializable)).Return(transaction); + //standard tx timeout. + transaction.Commit(); + LastCall.On(transaction).Repeat.Once(); + connection.Dispose(); + } + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + tt.TransactionIsolationLevel = IsolationLevel.Serializable; + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + tt.Execute(new TransactionCommitTxCallback(dbProvider)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + mocks.VerifyAll(); + } + + [Test] + public void TransactionWithLongTimeout() + { + DoTransactionWithTimeout(10); + } + + [Test] + public void TransactionWithShortTimeout() + { + DoTransactionWithTimeout(1); + } + + private void DoTransactionWithTimeout(int timeout) + { + #region Mock setup + + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + IDbCommand command = (IDbCommand) mocks.CreateMock(typeof (IDbCommand)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + Expect.Call(connection.CreateCommand()).Return(command); + command.CommandText = "some SQL statement"; + LastCall.On(command).Repeat.Once(); + if (timeout > 1) + { + command.CommandTimeout = (timeout - 1); + transaction.Commit(); + } else + { + transaction.Rollback(); + } + LastCall.On(transaction).Repeat.Once(); + connection.Dispose(); + } + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.TransactionTimeout = timeout; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + try + { + tt.Execute(new TransactionWithTimeoutCallback(dbProvider)); + if (timeout <= 1) + { + Assert.Fail("Should have thrown TransactionTimedOutException"); + } + } catch (TransactionTimedOutException) + { + if (timeout <=1 ) + { + //expected + } else + { + throw; + } + } + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + mocks.VerifyAll(); + } + + [Test] + public void TransactionWithExceptionOnBegin() + { + #region Mock setup + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + + // CreateConnection is called in AdoPlatformTransactionManager.DoBegin + Expect.Call(dbProvider.CreateConnection()).Throw(new TestSqlException("Cannot begin", "314")); + + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + try + { + tt.Execute(new TransactionDelegate(TransactionWithExceptionNoOp)); + } catch (CannotCreateTransactionException) + { + // expected + } + + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + mocks.VerifyAll(); + } + + private object TransactionWithExceptionNoOp(ITransactionStatus status) + { + return null; + } + + [Test] + public void TransactionWithExceptionOnCommit() + { + #region Mock setup + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + //standard tx timeout. + transaction.Commit(); + LastCall.On(transaction).Throw(new TestSqlException("Cannot commit", "314")); + connection.Dispose(); + } + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + + try + { + tt.Execute(new TransactionDelegate(TransactionWithExceptionNoOp)); + } catch (TransactionSystemException) + { + //expected + } + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + mocks.VerifyAll(); + + } + + + [Test] + public void TransactionWithExceptionOnCommitAndRollbackOnCommitFailure() + { + #region Mock Setup + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + transaction.Commit(); + LastCall.On(transaction).Throw(new TestSqlException("Cannot commit", "314")); + + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + + connection.Dispose(); + } + #endregion + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + tm.RollbackOnCommitFailure = true; + TransactionTemplate tt = new TransactionTemplate(tm); + + try + { + tt.Execute(new TransactionDelegate(TransactionWithExceptionNoOp)); + } + catch (TransactionSystemException) + { + //expected + } + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + mocks.VerifyAll(); + } + + [Test] + public void TransactionWithExceptionOnRollback() + { + #region Mock Setup + + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + //standard tx timeout. + transaction.Rollback(); + LastCall.On(transaction).Throw(new TestSqlException("Cannot commit", "314")); + + connection.Dispose(); + } + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + + try + { + tt.Execute(new TransactionDelegate(TransactionWithExceptionOnRollbackMethod)); + } + catch (TransactionSystemException) + { + //expected + } + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + mocks.VerifyAll(); + + } + + private object TransactionWithExceptionOnRollbackMethod(ITransactionStatus status) + { + status.RollbackOnly = true; + return null; + } + + [Test] + public void TransactionWithPropagationSupports() + { + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.Supports; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + tt.Execute(new TransactionWithPropagationSupportsCallback(dbProvider)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + mocks.VerifyAll(); + } + + [Test] + public void TransactionWithPropagationNotSupported() + { + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.NotSupported; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + tt.Execute(new TransactionWithPropagationNotSupportedCallback(dbProvider)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + mocks.VerifyAll(); + } + + [Test] + public void TransactionWithPropagationNever() + { + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.Never; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + + tt.Execute(new TransactionWithPropagationNotSupportedCallback(dbProvider)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + mocks.VerifyAll(); + } + + [Test] + public void ExistingTransactionWithPropagationNestedNotSupported() + { + #region Mock setup + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + connection.Dispose(); + } + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.Nested; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + try + { + tt.Execute(new ExistingTransactionWithPropagationNestedCallback(dbProvider, tt)); + Assert.Fail("Should have thrown NestedTransactionNotSupportedException"); + } catch (NestedTransactionNotSupportedException) + { + // expected + } + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + mocks.VerifyAll(); + } + + [Test] + public void TransactionWithPropagationNested() + { + #region Mock setup + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + //standard tx timeout. + transaction.Commit(); + LastCall.On(transaction).Repeat.Once(); + connection.Dispose(); + } + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.Nested; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + + tt.Execute(new TransactionDelegate(TransactionWithPropagationNestedMethod)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + mocks.VerifyAll(); + + + } + + private object TransactionWithPropagationNestedMethod(ITransactionStatus status) + { + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + return null; + } + + [Test] + public void TransactionWithPropagationNestedAndRollback() + { + #region Mock setup + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + IDbTransaction transaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction)); + + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + connection.Open(); + LastCall.On(connection).Repeat.Once(); + Expect.Call(connection.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction); + transaction.Rollback(); + LastCall.On(transaction).Repeat.Once(); + connection.Dispose(); + } + + #endregion + + mocks.ReplayAll(); + + AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.Nested; + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + + tt.Execute(new TransactionDelegate(TransactionWithPropagationNestedAndRollbackMethod)); + + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider"); + mocks.VerifyAll(); + + + } + + private object TransactionWithPropagationNestedAndRollbackMethod(ITransactionStatus status) + { + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + status.RollbackOnly = true; + return null; + } + } + + internal class ExistingTransactionWithPropagationNestedCallback : ITransactionCallback + { + private IDbProvider dbProvider; + private TransactionTemplate tt; + + public ExistingTransactionWithPropagationNestedCallback(IDbProvider dbProvider, TransactionTemplate transactionTemplate) + { + this.dbProvider = dbProvider; + tt = transactionTemplate; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + //TODO: Note no support for savepoints at this time (1.1), so can't check that a savepoint isn't present. + + tt.Execute(new ExistingTransactionWithPropagationNestedCallback2(dbProvider)); + + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + //TODO: Note no support for savepoints at this time (1.1), so can't check that a savepoint isn't present. + return null; + } + } + + internal class ExistingTransactionWithPropagationNestedCallback2 : ITransactionCallback + { + private IDbProvider dbProvider; + + public ExistingTransactionWithPropagationNestedCallback2(IDbProvider provider) + { + dbProvider = provider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(dbProvider), "Has thread db provider"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronizations active"); + Assert.IsTrue(!status.IsNewTransaction, "Isn't new transaction"); + //TODO: Note no support for savepoints at this time (1.1), so can't check that a savepoint is present. + return null; + } + } + + #region Supporting class for TransactionWithPropagationNotSupported + internal class TransactionWithPropagationNotSupportedCallback : ITransactionCallback + { + private IDbProvider provider; + + public TransactionWithPropagationNotSupportedCallback(IDbProvider provider) + { + this.provider = provider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider"); + Assert.IsTrue(!status.IsNewTransaction, "Is not new transaction"); + return null; + } + } + #endregion + + #region Supporting class for TransactionWithPropagationSupports + internal class TransactionWithPropagationSupportsCallback : ITransactionCallback + { + private IDbProvider provider; + + public TransactionWithPropagationSupportsCallback(IDbProvider provider) + { + this.provider = provider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider"); + Assert.IsTrue(!status.IsNewTransaction,"Is not new transaction"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive); + return null; + } + } + #endregion + + + #region Supporting class for TransactionWithTimeout + + internal class TransactionWithTimeoutCallback : ITransactionCallback + { + private IDbProvider provider; + public TransactionWithTimeoutCallback(IDbProvider provider) + { + this.provider = provider; + } + + public object DoInTransaction(ITransactionStatus status) + { + try + { + Thread.Sleep(1500); + } catch (Exception) + { + + } + try + { + IDbConnection con = ConnectionUtils.GetConnection(provider); + IDbCommand cmd = con.CreateCommand(); + cmd.CommandText = "some SQL statement"; + ConnectionUtils.ApplyTransactionTimeout(cmd, provider); + + } catch (Exception e) + { + if (e.GetType() != typeof(TransactionTimedOutException)) + { + throw new DataAccessResourceFailureException("", e); + } + throw; + } + return null; + } + } + #endregion + + #region Supporting class for PropagationRequiresNewWithExistingConnection + + internal class PropagationRequiresNewWithExistingConnectionCallback : ITransactionCallback + { + private TransactionTemplate tt; + private IDbProvider dbProvider; + private IDbConnection connection; + private IDbConnection connection2; + + public PropagationRequiresNewWithExistingConnectionCallback(TransactionTemplate transactionTemplate, IDbConnection connection, IDbConnection connection2, IDbProvider provider) + { + tt = transactionTemplate; + this.connection = connection; + this.connection2 = connection2; + dbProvider = provider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronizations active"); + Assert.AreSame(connection, ConnectionUtils.GetConnection(dbProvider)); + Assert.AreSame(connection, ConnectionUtils.GetConnection(dbProvider)); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + tt.ReadOnly = true; + tt.Execute(new PropagationRequiresNewWithExistingConnectionCallback2(dbProvider, connection2)); + Assert.AreSame(connection, ConnectionUtils.GetConnection(dbProvider)); + return null; + } + } + + internal class PropagationRequiresNewWithExistingConnectionCallback2 : ITransactionCallback + { + private IDbProvider dbProvider; + private IDbConnection connection2; + + + public PropagationRequiresNewWithExistingConnectionCallback2(IDbProvider dbProvider, IDbConnection connection2) + { + this.dbProvider = dbProvider; + this.connection2 = connection2; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(dbProvider), "Has thread db provider"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronizations active"); + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.AreSame(connection2, ConnectionUtils.GetConnection(dbProvider)); + Assert.AreSame(connection2, ConnectionUtils.GetConnection(dbProvider)); + + return null; + } + } + + #endregion + + #region Supporting classes for PropagationNeverWithExistingTransaction + + internal class PropagationNeverWithExistingTransactionCallback : ITransactionCallback + { + private TransactionTemplate innerTxTemplate; + + public PropagationNeverWithExistingTransactionCallback(TransactionTemplate transactionTemplate) + { + innerTxTemplate = transactionTemplate; + } + + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + innerTxTemplate.PropagationBehavior = TransactionPropagation.Never; + innerTxTemplate.Execute(new PropagationNeverWithExistingTransactionCallback2()); + Assert.Fail("Should have thrown IllegalTransactionStateException"); + return null; + } + } + + internal class PropagationNeverWithExistingTransactionCallback2 : ITransactionCallback + { + + public object DoInTransaction(ITransactionStatus status) + { + Assert.Fail("Should have thrown IllegalTransactionStateException"); + return null; + } + } + + #endregion + + #region Supporting classes for PropagationNotSupportedWithExistingTransaction + + internal class PropagationNotSupportedWithExistingTransactionCallback : ITransactionCallback + { + private TransactionTemplate innerTxTemplate; + private IDbProvider dbProvider; + + public PropagationNotSupportedWithExistingTransactionCallback(TransactionTemplate transactionTemplate, IDbProvider provider) + { + innerTxTemplate = transactionTemplate; + dbProvider = provider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronization active"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + innerTxTemplate.PropagationBehavior = TransactionPropagation.NotSupported; + innerTxTemplate.Execute(new PropagationNotSupportedWithExistingTransactionCallback2(dbProvider)); + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + return null; + } + } + + internal class PropagationNotSupportedWithExistingTransactionCallback2 : ITransactionCallback + { + private IDbProvider provider; + + public PropagationNotSupportedWithExistingTransactionCallback2(IDbProvider provider) + { + this.provider = provider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsTrue(!status.IsNewTransaction, "Isn't new transaction"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive); + status.RollbackOnly = true; + return null; + } + } + + #endregion + + #region Supporting class for PropagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSource + + internal class PropagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSourceCallback : ITransactionCallback + { + private TransactionTemplate innerTxTemplate; + + + public PropagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSourceCallback(TransactionTemplate transactionTemplate) + { + innerTxTemplate = transactionTemplate; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronization active"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + innerTxTemplate.Execute(new PropagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSourceCallback2()); + return null; + } + } + + internal class PropagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSourceCallback2 : ITransactionCallback + { + public object DoInTransaction(ITransactionStatus status) + { + status.RollbackOnly = true; + return null; + } + } + + #endregion + + #region Supporting class for PropagationRequiresNewWithExistingTransaction + + internal class PropagationRequiresNewWithExistingTransactionCallback : ITransactionCallback + { + private TransactionTemplate innerTxTemplate; + private IDbProvider dbProvider; + + public PropagationRequiresNewWithExistingTransactionCallback(TransactionTemplate transactionTemplate, + IDbProvider provider) + { + innerTxTemplate = transactionTemplate; + dbProvider = provider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronization active"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + innerTxTemplate.Execute(new PropagationRequiresNewWithExistingTransactionCallback2(dbProvider)); + + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + return null; + } + } + + internal class PropagationRequiresNewWithExistingTransactionCallback2 : ITransactionCallback + { + private IDbProvider dbProvider; + + public PropagationRequiresNewWithExistingTransactionCallback2(IDbProvider dbProvider) + { + this.dbProvider = dbProvider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(dbProvider), "Has thread connection"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronization active"); + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + status.RollbackOnly = true; + return null; + } + } + + #endregion + + #region Supporting class for TransactionCommit test + + internal class TransactionCommitTxCallback : ITransactionCallback + { + private IDbProvider provider; + + public TransactionCommitTxCallback(IDbProvider provider) + { + this.provider = provider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(provider), "Has thread db provider"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + return null; + } + } + + #endregion + + #region Supporting class for TransactionRollback test + + internal class TransactionRollbackTxCallback : ITransactionCallback + { + private IDbProvider provider; + private Exception exception; + + public TransactionRollbackTxCallback(IDbProvider provider, Exception exception) + { + this.provider = provider; + this.exception = exception; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + throw exception; + } + } + + #endregion + + #region Supporting class for ParticipatingTxWithRollbackOnly test + + internal class ParticipatingTxWithRollbackOnlyTxCallback : ITransactionCallback + { + private TransactionTemplate innerTxTemplate; + private IDbProvider dbProvider; + + + public ParticipatingTxWithRollbackOnlyTxCallback(TransactionTemplate transactionTemplate, IDbProvider dbProvider) + { + innerTxTemplate = transactionTemplate; + this.dbProvider = dbProvider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(!status.IsNewTransaction, "Is existing transaction"); + Assert.IsFalse(status.RollbackOnly, "Is not rollback-only"); + innerTxTemplate.Execute(new ParticipatingTxWithRollbackOnlyTxCallback2(dbProvider)); + Assert.IsTrue(!status.IsNewTransaction, "Is existing transaction"); + Assert.IsTrue(status.RollbackOnly, "Is rollback-only"); + return null; + } + } + + internal class ParticipatingTxWithRollbackOnlyTxCallback2 : ITransactionCallback + { + private IDbProvider dbProvider; + + public ParticipatingTxWithRollbackOnlyTxCallback2(IDbProvider dbProvider) + { + this.dbProvider = dbProvider; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.HasResource(dbProvider), "Has thread connection"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronization active"); + Assert.IsTrue(!status.IsNewTransaction, "Is existing transaction"); + status.RollbackOnly = true; + return null; + } + } + + #endregion + + + + #region Helper class + + internal class TestTransactionSynchronization : ITransactionSynchronization + { + private IDbProvider provider; + private TransactionSynchronizationStatus status; + + public bool beforeCommitCalled; + public bool beforeCompletionCalled; + public bool afterCommitCalled; + public bool afterCompletionCalled; + + public TestTransactionSynchronization(IDbProvider provider, + TransactionSynchronizationStatus synchronizationStatus) + { + this.provider = provider; + status = synchronizationStatus; + } + + + public void Suspend() + { + } + + public void Resume() + { + } + + public void BeforeCommit(bool readOnly) + { + if (status == TransactionSynchronizationStatus.Committed) + { + Assert.Fail("Should never be called"); + } + Assert.IsFalse(beforeCommitCalled); + beforeCommitCalled = true; + } + + public void AfterCommit() + { + if (status != TransactionSynchronizationStatus.Committed) + { + Assert.Fail("Should never be called"); + } + Assert.IsFalse(afterCommitCalled); + afterCommitCalled = true; + } + + public void BeforeCompletion() + { + Assert.IsFalse(beforeCompletionCalled); + beforeCompletionCalled = true; + } + + public void AfterCompletion(TransactionSynchronizationStatus syncStatus) + { + Assert.IsFalse(afterCompletionCalled); + afterCompletionCalled = true; + Assert.IsTrue(syncStatus == status); + Assert.IsTrue(TransactionSynchronizationManager.HasResource(provider)); + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/AutoDeclarativeTxTests.cs b/test/Spring/Spring.Data.Tests/Data/AutoDeclarativeTxTests.cs new file mode 100644 index 00000000..89cb582e --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/AutoDeclarativeTxTests.cs @@ -0,0 +1,95 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Aop.Framework; +using Spring.Aop.Framework.DynamicProxy; +using Spring.Aop.Support; +using Spring.Context; +using Spring.Context.Support; +using Common.Logging; +using Common.Logging.Simple; +using Spring.Objects; +using Spring.Transaction; + +#endregion + +namespace Spring.Data +{ + /// + /// Test case that uses the approach of automatically creating + /// declarative transaction interceptors for objects identified via means + /// of transaction attributes. + /// + /// Mark Pollack (.NET) + /// $Id: AutoDeclarativeTxTests.cs,v 1.4 2007/08/21 19:26:04 markpollack Exp $ + [TestFixture] + public class AutoDeclarativeTxTests + { + private IApplicationContext ctx; + + [SetUp] + public void SetUp() + { + //LogManager.Adapter = new ConsoleOutLoggerFactoryAdapter(); + ctx = + new XmlApplicationContext("assembly://Spring.Data.Tests/Spring.Data/AutoDeclarativeTxTests.xml"); + + } + + + + [Test] + public void CoordinatorDeclarativeWithAttributes() + { + ITestCoord coord = ctx["testCoordinator"] as ITestCoord; + Assert.IsNotNull(coord); + CallCountingTransactionManager ccm = ctx["transactionManager"] as CallCountingTransactionManager; + Assert.IsNotNull(ccm); + LoggingAroundAdvice advice = (LoggingAroundAdvice)ctx["consoleLoggingAroundAdvice"]; + Assert.IsNotNull(advice); + + ITestObjectMgr testObjectMgr = ctx["testObjectManager"] as ITestObjectMgr; + Assert.IsNotNull(testObjectMgr); + //Proxied due to NameMatchMethodPointcutAdvisor + Assert.IsTrue(AopUtils.IsAopProxy(coord)); + + //Proxied due to DefaultAdvisorAutoProxyCreator + Assert.IsTrue(AopUtils.IsAopProxy(testObjectMgr)); + + + TestObject to1 = new TestObject("Jack", 7); + TestObject to2 = new TestObject("Jill", 6); + + Assert.AreEqual(0, ccm.begun); + Assert.AreEqual(0, ccm.commits); + Assert.AreEqual(0, advice.numInvoked); + + coord.WorkOn(to1,to2); + + Assert.AreEqual(1, ccm.begun); + Assert.AreEqual(1, ccm.commits); + Assert.AreEqual(1, advice.numInvoked); + } + + } +} diff --git a/test/Spring/Spring.Data.Tests/Data/AutoDeclarativeTxTests.xml b/test/Spring/Spring.Data.Tests/Data/AutoDeclarativeTxTests.xml new file mode 100644 index 00000000..c53a6496 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/AutoDeclarativeTxTests.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WorkOn + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.Tests/Data/Common/AdditionalProviders.xml b/test/Spring/Spring.Data.Tests/Data/Common/AdditionalProviders.xml new file mode 100644 index 00000000..8252971e --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/Common/AdditionalProviders.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 156,170,207,208 + + + 229 + + + 544,2627,8114,8115 + + + 1205 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 156,170,207,208 + + + 229 + + + 544,2627,8114,8115 + + + 1205 + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/Common/AdditonalProviders.xml b/test/Spring/Spring.Data.Tests/Data/Common/AdditonalProviders.xml new file mode 100644 index 00000000..066dd068 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/Common/AdditonalProviders.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 1054,1064,1146 + + + 1 + + + 1062 + + + 1205 + + + 1213 + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/Common/DbParametersTests.cs b/test/Spring/Spring.Data.Tests/Data/Common/DbParametersTests.cs new file mode 100644 index 00000000..1506bbb8 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/Common/DbParametersTests.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if (NET_2_0) +#region Imports + +using System.Data; +using System.Data.OracleClient; +using NUnit.Framework; + +#endregion + +namespace Spring.Data.Common +{ + /// + /// This class contains tests for DbParameters + /// + /// Mark Pollack + /// $Id: DbParametersTests.cs,v 1.3 2007/12/06 06:58:54 markpollack Exp $ + [TestFixture] + public class DbParametersTests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void OracleClient() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.OracleClient"); + DbParameters dbParameters = new DbParameters(dbProvider); + dbParameters.Add("p1", DbType.String); + IDataParameter parameter = dbParameters[0]; + Assert.AreEqual("p1", parameter.ParameterName); + dbParameters.SetValue("p1", "foo"); + IDbDataParameter springParameter = dbParameters.GetValue("p1") as IDbDataParameter; + Assert.IsNotNull(springParameter); + Assert.AreEqual("foo", springParameter.Value); + } + + [Test] + public void SqlClient() + { + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); + DbParameters dbParameters = new DbParameters(dbProvider); + dbParameters.Add("p1", DbType.String); + IDataParameter parameter = dbParameters[0]; + Assert.AreEqual("@p1", parameter.ParameterName); + dbParameters.SetValue("p1", "foo"); + IDbDataParameter springParameter = dbParameters.GetValue("p1") as IDbDataParameter; + Assert.IsNotNull(springParameter); + Assert.AreEqual("foo", springParameter.Value); + } + + private static void TestParameterNaming(DbParameters dbParameters) + { + + } + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/Common/DbProviderFactoryTests.cs b/test/Spring/Spring.Data.Tests/Data/Common/DbProviderFactoryTests.cs new file mode 100644 index 00000000..796bb27b --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/Common/DbProviderFactoryTests.cs @@ -0,0 +1,250 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; +using System.Globalization; +using System.Reflection; +using System.Threading; +using NUnit.Framework; +using Spring.Context; +using Spring.Core.IO; +using Spring.Threading; +using Spring.Util; + +namespace Spring.Data.Common +{ + /// + /// Test for loading of DbProviders + /// + /// Mark Pollack (.NET) + /// $Id: DbProviderFactoryTests.cs,v 1.13 2008/04/02 19:37:20 markpollack Exp $ + [TestFixture] + public class DbProviderFactoryTests + { + #region Helper classes for threading tests + + public class AsyncTestDbProviderFactory : AsyncTestTask + { + private string providerName; + + public AsyncTestDbProviderFactory(int iterations, string providerName) + : base(iterations) + { + this.providerName = providerName; + } + + public override void DoExecute() + { + object result = DbProviderFactory.GetDbProvider(providerName); + } + } + + #endregion + + private static string altConfig = "assembly://Spring.Data.Tests/Spring.Data.Common/AdditionalProviders.xml"; + + + [SetUp] + public void Setup() + { + DbProviderFactory.DBPROVIDER_ADDITIONAL_RESOURCE_NAME = altConfig; + } + +#if NET_2_0 + + [Test] + public void ThreadSafety() + { + AsyncTestTask t1 = new AsyncTestDbProviderFactory(1000, "SqlServer-2.0").Start(); + AsyncTestTask t2 = new AsyncTestDbProviderFactory(1000, "SqlServer-2.0").Start(); + AsyncTestTask t3 = new AsyncTestDbProviderFactory(1000, "SqlServer-2.0").Start(); + AsyncTestTask t4 = new AsyncTestDbProviderFactory(1000, "SqlServer-2.0").Start(); + + t1.AssertNoException(); + t2.AssertNoException(); + t3.AssertNoException(); + t4.AssertNoException(); + + } + + [Test] + [Ignore("Can't guarantee test order")] + public void AdditionalResourceName() + { + + IDbProvider provider = DbProviderFactory.GetDbProvider("Test-SqlServer-2.0"); + Assert.IsNotNull(provider); + } + + [Test] + [Ignore("Can't guarantee test order")] + public void BadErrorExpression() + { + + IDbProvider provider = DbProviderFactory.GetDbProvider("Test-SqlServer-2.0-BadErrorCodeExpression"); + Assert.IsNotNull(provider); + string errorCode = provider.ExtractError(new Exception("foo")); + Assert.AreEqual("156",errorCode); + } + + [Test] + public void DefaultInstanceWithSqlServer2005() + { + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", false); + IApplicationContext ctx = DbProviderFactory.ApplicationContext; + Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR", false); + IDbProvider provider = DbProviderFactory.GetDbProvider("SqlServer-2.0"); + AssertIsSqlServer2005(provider); + provider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); + AssertIsSqlServer2005(provider); + Assert.IsNull(provider.ConnectionString); + Assert.IsNotNull(provider.CreateCommand()); + Assert.IsNotNull(provider.CreateCommandBuilder()); + Assert.IsNotNull(provider.CreateConnection()); + Assert.IsNotNull(provider.CreateDataAdapter()); + Assert.IsNotNull(provider.CreateParameter()); + Assert.AreEqual("@Foo", provider.CreateParameterName("Foo")); + } + + + [Test] + public void DefaultInstanceWithOleDb20() + { + IDbProvider provider = DbProviderFactory.GetDbProvider("OleDb-2.0"); + Assert.AreEqual("OleDb, provider V2.0.0.0 in framework .NET V2", provider.DbMetadata.ProductName); + Assert.IsNotNull(provider.CreateCommand()); + Assert.IsNotNull(provider.CreateCommandBuilder()); + Assert.IsNotNull(provider.CreateConnection()); + Assert.IsNotNull(provider.CreateDataAdapter()); + Assert.IsNotNull(provider.CreateParameter()); + Assert.AreEqual("?", provider.CreateParameterName("Foo")); + } + + [Test] + public void DefaultInstanceWithMicrsoftOracleClient20() + { + IDbProvider provider = DbProviderFactory.GetDbProvider("OracleClient-2.0"); + Assert.AreEqual("Oracle, Microsoft provider V2.0.0.0", provider.DbMetadata.ProductName); + Assert.IsNotNull(provider.CreateCommand()); + Assert.IsNotNull(provider.CreateCommandBuilder()); + Assert.IsNotNull(provider.CreateConnection()); + Assert.IsNotNull(provider.CreateDataAdapter()); + Assert.IsNotNull(provider.CreateParameter()); + Assert.AreEqual(":Foo", provider.CreateParameterName("Foo")); + } +#endif + + [Test] + [Ignore("until find out if can add oracle.dll to cvs repository")] + public void DefaultInstanceWithOracleClient20() + { + IDbProvider provider = DbProviderFactory.GetDbProvider("OracleODP-2.0"); + Assert.AreEqual("Oracle, Oracle provider V2.102.2.20", provider.DbMetadata.ProductName); + Assert.IsNotNull(provider.CreateCommand()); + Assert.IsNotNull(provider.CreateCommandBuilder()); + Assert.IsNotNull(provider.CreateConnection()); + Assert.IsNotNull(provider.CreateDataAdapter()); + Assert.IsNotNull(provider.CreateParameter()); + Assert.AreEqual(":Foo", provider.CreateParameterName("Foo")); + + } + + /* + [Test] + public void DefaultInstanceWithMySql() + { + DbProviderFactory.DBPROVIDER_ADDITIONAL_RESOURCE_NAME = + "assembly://Spring.Data.Tests/Spring.Data.Common/AdditonalProviders.xml"; + IDbProvider provider = DbProviderFactory.GetDbProvider("MySqlPersonal"); + Assert.AreEqual("MySQL, MySQL provider 1.0.7.30072", provider.DbMetadata.ProductName); + + } + + */ + + [Test] + public void TestDb2() + { + IApplicationContext ctx = DbProviderFactory.ApplicationContext; + string[] dbProviderNames = ctx.GetObjectNamesForType(typeof(IDbProvider)); + Console.WriteLine( + String.Format("{0} DbProviders Available. [{1}]", dbProviderNames.Length, + StringUtils.ArrayToCommaDelimitedString(dbProviderNames))); + + } + + +#if NET_1_1 + + [Test] + public void DefaultInstanceWithOleDb11() + { + IDbProvider provider = DbProviderFactory.GetDbProvider("OleDb-1.1"); + Assert.AreEqual("OleDb, provider V1.0.5000.0 in framework .NET V1.1", provider.DbMetadata.ProductName); + Assert.IsNotNull(provider.CreateCommand()); + Assert.IsNotNull(provider.CreateCommandBuilder()); + Assert.IsNotNull(provider.CreateConnection()); + Assert.IsNotNull(provider.CreateDataAdapter()); + Assert.IsNotNull(provider.CreateParameter()); + Assert.AreEqual("?", provider.CreateParameterName("Foo")); + } + + [Test] + public void DefaultInstanceWithSqlServer2000() + { + IDbProvider provider = DbProviderFactory.GetDbProvider("SqlServer-1.1"); + AssertIsSqlServer2000(provider); + Assert.IsNull(provider.ConnectionString); + Assert.IsNotNull(provider.CreateCommand()); + Assert.IsNotNull(provider.CreateCommandBuilder()); + Assert.IsNotNull(provider.CreateConnection()); + Assert.IsNotNull(provider.CreateDataAdapter()); + Assert.IsNotNull(provider.CreateParameter()); + Assert.AreEqual("@Foo", provider.CreateParameterName("Foo")); + } + + private void AssertIsSqlServer2000(IDbProvider provider) + { + Assert.AreEqual("Microsoft SQL Server, provider V1.0.5000.0 in framework .NET V1.1", + provider.DbMetadata.ProductName); + AssertCommonSqlServerErrorCodes(provider); + } +#endif + private void AssertIsSqlServer2005(IDbProvider provider) + { + Assert.AreEqual("Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0", + provider.DbMetadata.ProductName); + AssertCommonSqlServerErrorCodes(provider); + } + + private static void AssertCommonSqlServerErrorCodes(IDbProvider provider) + { + ErrorCodes codes = provider.DbMetadata.ErrorCodes; + Assert.IsTrue(codes.BadSqlGrammarCodes.Length > 0); + Assert.IsTrue(codes.DataIntegrityViolationCodes.Length > 0); + // This had better be a Bad SQL Grammar code + Assert.IsTrue(Array.IndexOf(codes.BadSqlGrammarCodes, "156") >= 0); + // This had better NOT be + Assert.IsFalse(Array.IndexOf(codes.BadSqlGrammarCodes, "1xx56") >= 0); + } + + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/Common/UserCredentialsDbProviderTests.cs b/test/Spring/Spring.Data.Tests/Data/Common/UserCredentialsDbProviderTests.cs new file mode 100644 index 00000000..199e744b --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/Common/UserCredentialsDbProviderTests.cs @@ -0,0 +1,114 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Data; +using NUnit.Framework; +using Rhino.Mocks; + +#endregion + +namespace Spring.Data.Common +{ + /// + /// This class contains tests for UserCredentialsDbProvider + /// + /// Mark Pollack + /// $Id: UserCredentialsDbProviderTests.cs,v 1.1 2008/01/29 18:17:48 markpollack Exp $ + [TestFixture] + public class UserCredentialsDbProviderTests + { + private MockRepository mocks; + + [SetUp] + public void Setup() + { + mocks = new MockRepository(); + } + + [Test] + public void StaticCredentials() + { + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + Expect.Call(dbProvider.ConnectionString).Return( + @"Data Source=MARKT60\SQL2005;Database=Spring;Trusted_Connection=False"); + Expect.Call(connection.ConnectionString = @"Data Source=MARKT60\SQL2005;Database=Spring;Trusted_Connection=False;User ID=springqa;Password=springqa"); + } + + mocks.ReplayAll(); + + UserCredentialsDbProvider provider = new UserCredentialsDbProvider(); + provider.TargetDbProvider = dbProvider; + provider.Username = "User ID=springqa"; + provider.Password = "Password=springqa"; + Assert.AreEqual(connection, provider.CreateConnection()); + + mocks.VerifyAll(); + } + + [Test] + public void NoCredentials() + { + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + } + + mocks.ReplayAll(); + + UserCredentialsDbProvider provider = new UserCredentialsDbProvider(); + provider.TargetDbProvider = dbProvider; + Assert.AreEqual(connection, provider.CreateConnection()); + + mocks.VerifyAll(); + } + + [Test] + public void ThreadBoundCredentials() + { + IDbProvider dbProvider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider)); + IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection)); + using (mocks.Ordered()) + { + Expect.Call(dbProvider.CreateConnection()).Return(connection); + Expect.Call(dbProvider.ConnectionString).Return( + @"Data Source=MARKT60\SQL2005;Database=Spring;Trusted_Connection=False"); + Expect.Call(connection.ConnectionString = @"Data Source=MARKT60\SQL2005;Database=Spring;Trusted_Connection=False;User ID=springqa;Password=springqa"); + } + + mocks.ReplayAll(); + + UserCredentialsDbProvider provider = new UserCredentialsDbProvider(); + provider.TargetDbProvider = dbProvider; + provider.SetCredentialsForCurrentThread("User ID=springqa", "Password=springqa"); + Assert.AreEqual(connection, provider.CreateConnection()); + + mocks.VerifyAll(); + + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/Core/ServiceDomainTransactionManagerTests.cs b/test/Spring/Spring.Data.Tests/Data/Core/ServiceDomainTransactionManagerTests.cs new file mode 100644 index 00000000..0805b5ef --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/Core/ServiceDomainTransactionManagerTests.cs @@ -0,0 +1,274 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +#if (!NET_1_0) + +#region Imports + +using System; +using System.EnterpriseServices; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Data.Support; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.Core +{ + /// + /// This class contains tests for ServiceDomainTransactionManager + /// + /// Mark Pollack + /// $Id: ServiceDomainTransactionManagerTests.cs,v 1.5 2007/12/07 07:31:05 markpollack Exp $ + [TestFixture] + public class ServiceDomainTransactionManagerTests + { + private MockRepository mocks; + + [SetUp] + public void Setup() + { + mocks = new MockRepository(); + } + + [Test] + public void TransactionCommit() + { + #region Mock Setup + IServiceDomainAdapter txAdapter = (IServiceDomainAdapter) mocks.CreateMock(typeof (IServiceDomainAdapter)); + using (mocks.Ordered()) + { + Expect.Call(txAdapter.IsInTransaction).Return(false); + SimpleServiceConfig serviceConfig = new SimpleServiceConfig(); + ConfigureServiceConfig(serviceConfig, true); + txAdapter.Enter(serviceConfig); + + //ProcessCommit - status.GlobalRollbackOnly check + Expect.Call(txAdapter.MyTransactionVote).Return(TransactionVote.Commit); + //DoCommit - status.GlobalRollbackOnly check + Expect.Call(txAdapter.MyTransactionVote).Return(TransactionVote.Commit); + + Expect.Call(txAdapter.IsInTransaction).Return(true); + //DoCommit - check to call SetComplete or SetAbort + Expect.Call(txAdapter.MyTransactionVote).Return(TransactionVote.Commit); + txAdapter.SetComplete(); + Expect.Call(txAdapter.Leave()).Return(TransactionStatus.Commited); + + } + #endregion + + mocks.ReplayAll(); + + IPlatformTransactionManager tm = new ServiceDomainPlatformTransactionManager(txAdapter); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.Execute(new TransactionDelegate(TransactionCommitMethod)); + + Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + + mocks.VerifyAll(); + + } + + private object TransactionCommitMethod(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + return null; + } + + [Test] + public void TransactionRollback() + { + #region Mock Setup + IServiceDomainAdapter txAdapter = (IServiceDomainAdapter)mocks.CreateMock(typeof(IServiceDomainAdapter)); + using (mocks.Ordered()) + { + + Expect.Call(txAdapter.IsInTransaction).Return(false); + SimpleServiceConfig serviceConfig = new SimpleServiceConfig(); + ConfigureServiceConfig(serviceConfig, true); + txAdapter.Enter(serviceConfig); + Expect.Call(txAdapter.IsInTransaction).Return(true); + txAdapter.SetAbort(); + Expect.Call(txAdapter.Leave()).Return(TransactionStatus.Commited); + + } + #endregion + + mocks.ReplayAll(); + + IPlatformTransactionManager tm = new ServiceDomainPlatformTransactionManager(txAdapter); + TransactionTemplate tt = new TransactionTemplate(tm); + + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + Exception ex = new ArgumentException("test exception"); + try + { + tt.Execute(new TransactionRollbackTxCallback(ex)); + Assert.Fail("Should have thrown exception"); + } + catch (ArgumentException e) + { + Assert.AreEqual(ex, e); + } + + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + + mocks.VerifyAll(); + } + + + [Test] + public void PropagationRequiresNewWithExistingTransaction() + { + #region Mock Setup + IServiceDomainAdapter txAdapter = (IServiceDomainAdapter)mocks.CreateMock(typeof(IServiceDomainAdapter)); + using (mocks.Ordered()) + { + + Expect.Call(txAdapter.IsInTransaction).Return(false); + SimpleServiceConfig serviceConfig = new SimpleServiceConfig(); + ConfigureServiceConfig(serviceConfig, true); + txAdapter.Enter(serviceConfig); + + + Expect.Call(txAdapter.IsInTransaction).Return(true); + // inner tx + ConfigureServiceConfig(serviceConfig, false); + serviceConfig.TransactionOption = TransactionOption.RequiresNew; + serviceConfig.IsolationLevel = TransactionIsolationLevel.ReadCommitted; + txAdapter.Enter(serviceConfig); + Expect.Call(txAdapter.IsInTransaction).Return(true); + txAdapter.SetAbort(); + Expect.Call(txAdapter.Leave()).Return(TransactionStatus.Aborted); + // innter tx aborted + + + //ProcessCommit - status.GlobalRollbackOnly check + Expect.Call(txAdapter.MyTransactionVote).Return(TransactionVote.Commit); + //DoCommit - status.GlobalRollbackOnly check + Expect.Call(txAdapter.MyTransactionVote).Return(TransactionVote.Commit); + + Expect.Call(txAdapter.IsInTransaction).Return(true); + //DoCommit - check to call SetComplete or SetAbort + Expect.Call(txAdapter.MyTransactionVote).Return(TransactionVote.Commit); + txAdapter.SetComplete(); + + Expect.Call(txAdapter.Leave()).Return(TransactionStatus.Commited); + + } + #endregion + + mocks.ReplayAll(); + + IPlatformTransactionManager tm = new ServiceDomainPlatformTransactionManager(txAdapter); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + tt.Execute(new PropagationRequiresNewWithExistingTransactionCallbackSD(tt)); + mocks.VerifyAll(); + } + + #region Helper Methods + private SimpleServiceConfig ConfigureServiceConfig(SimpleServiceConfig serviceConfig, bool standardIsolationAndProp) + { + serviceConfig.TransactionDescription = null; + + serviceConfig.TrackingEnabled = true; + serviceConfig.TrackingAppName = "Spring.NET"; + serviceConfig.TrackingComponentName = "ServiceDomainPlatformTransactionManager"; + if (standardIsolationAndProp) + { + serviceConfig.TransactionOption = TransactionOption.Required; + serviceConfig.IsolationLevel = TransactionIsolationLevel.ReadCommitted; + } + return serviceConfig; + + } + #endregion + + + #region Supporting class for TransactionRollback test + + internal class TransactionRollbackTxCallback : ITransactionCallback + { + private Exception exception; + + public TransactionRollbackTxCallback(Exception exception) + { + this.exception = exception; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + throw exception; + } + } + + #endregion + + #region Supporting class for PropagationRequiresNewWithExistingTransactionCallback test + internal class PropagationRequiresNewWithExistingTransactionCallbackSD : ITransactionCallback + { + private TransactionTemplate tt; + + public PropagationRequiresNewWithExistingTransactionCallbackSD(TransactionTemplate tt) + { + this.tt = tt; + } + + public object DoInTransaction(ITransactionStatus status) + { + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronization active"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + tt.Execute(new TransactionDelegate(TransactionMethod)); + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + return null; + } + + private object TransactionMethod(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronization active"); + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + status.RollbackOnly = true; + return null; + } + } + + #endregion + } + + +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerIntegrationTests.cs b/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerIntegrationTests.cs new file mode 100644 index 00000000..5da87c9c --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerIntegrationTests.cs @@ -0,0 +1,150 @@ +#if NET_2_0 + +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Reflection; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Data.Core; +using Spring.Objects; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.Core +{ + /// + /// This class contains tests for TxScopeTransactionManager and will directly a real TransactionScope object + /// but does not access any database + /// + /// Mark Pollack + /// $Id: TxScopeTransactionManagerIntegrationTests.cs,v 1.1 2007/11/30 18:38:59 markpollack Exp $ + [TestFixture] + public class TxScopeTransactionManagerIntegrationTests + { + private MockRepository mocks; + [SetUp] + public void Setup() + { + mocks = new MockRepository(); + } + + [TearDown] + public void TearDown() + { + Assert.IsTrue(TransactionSynchronizationManager.ResourceDictionary.Count == 0); + Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsNull(TransactionSynchronizationManager.CurrentTransactionName); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.AreEqual(System.Data.IsolationLevel.Unspecified, TransactionSynchronizationManager.CurrentTransactionIsolationLevel); + Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive); + + } + + [Test] + public void Commit() + { + TxScopeTransactionManager tm = new TxScopeTransactionManager(); + TransactionTemplate tt = new TransactionTemplate(tm); + + //tt.Name = "txName"; + + Assert.AreEqual(TransactionSynchronizationState.Always, tm.TransactionSynchronization); + Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsNull(TransactionSynchronizationManager.CurrentTransactionName); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + tt.Execute(CommitTxDelegate); + Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsNull(TransactionSynchronizationManager.CurrentTransactionName); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + } + + public object CommitTxDelegate(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + + return null; + } + + [Test] + public void TransactionInformation() + { + TxScopeTransactionManager tm = new TxScopeTransactionManager(); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.TransactionIsolationLevel = System.Data.IsolationLevel.ReadUncommitted; + tt.Execute(TransactionInformationTxDelegate); + } + + public object TransactionInformationTxDelegate(ITransactionStatus status) + { + + Assert.AreEqual(System.Transactions.IsolationLevel.ReadUncommitted, + System.Transactions.Transaction.Current.IsolationLevel); + + Assert.AreEqual(System.Data.IsolationLevel.ReadUncommitted, + TransactionSynchronizationManager.CurrentTransactionIsolationLevel); + return null; + } + + + [Test] + public void Rollback() + { + ITransactionSynchronization sync = + (ITransactionSynchronization) mocks.DynamicMock(typeof (ITransactionSynchronization)); + sync.BeforeCompletion(); + LastCall.On(sync).Repeat.Once(); + sync.AfterCompletion(TransactionSynchronizationStatus.Rolledback); + LastCall.On(sync).Repeat.Once(); + mocks.ReplayAll(); + + + TxScopeTransactionManager tm = new TxScopeTransactionManager(); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.TransactionTimeout = 10; + tt.Name = "txName"; + + Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsNull(TransactionSynchronizationManager.CurrentTransactionName); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + tt.Execute(delegate (ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive); + TransactionSynchronizationManager.RegisterSynchronization(sync); + Assert.AreEqual("txName", TransactionSynchronizationManager.CurrentTransactionName); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + status.RollbackOnly = true; + return null; + } + ); + + mocks.VerifyAll(); + + } + + + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerTests.cs b/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerTests.cs new file mode 100644 index 00000000..ae839b1e --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerTests.cs @@ -0,0 +1,190 @@ +#if NET_2_0 + +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Transactions; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Data.Support; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Data.Core +{ + /// + /// This calss contains tests for + /// + /// Mark Pollack + /// $Id: TxScopeTransactionManagerTests.cs,v 1.3 2007/12/07 07:31:05 markpollack Exp $ + [TestFixture] + public class TxScopeTransactionManagerTests + { + private MockRepository mocks; + + [SetUp] + public void Setup() + { + mocks = new MockRepository(); + } + + [Test] + + public void TransactionCommit() + { + ITransactionScopeAdapter txAdapter = (ITransactionScopeAdapter) mocks.CreateMock(typeof(ITransactionScopeAdapter)); + + using (mocks.Ordered()) + { + Expect.Call(txAdapter.IsExistingTransaction).Return(false); + TransactionOptions txOptions = new TransactionOptions(); + txOptions.IsolationLevel = IsolationLevel.ReadCommitted; + txAdapter.CreateTransactionScope(TransactionScopeOption.Required, txOptions, EnterpriseServicesInteropOption.None); + + Expect.Call(txAdapter.RollbackOnly).Return(false); + txAdapter.Complete(); + txAdapter.Dispose(); + } + mocks.ReplayAll(); + + IPlatformTransactionManager tm = new TxScopeTransactionManager(txAdapter); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.Execute(delegate(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + return null; + }); + + Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + + mocks.VerifyAll(); + + + } + + [Test] + public void TransactionRollback() + { + ITransactionScopeAdapter txAdapter = (ITransactionScopeAdapter)mocks.CreateMock(typeof(ITransactionScopeAdapter)); + + using (mocks.Ordered()) + { + Expect.Call(txAdapter.IsExistingTransaction).Return(false); + TransactionOptions txOptions = new TransactionOptions(); + txOptions.IsolationLevel = IsolationLevel.ReadCommitted; + txAdapter.CreateTransactionScope(TransactionScopeOption.Required, txOptions, EnterpriseServicesInteropOption.None); + txAdapter.Dispose(); + } + mocks.ReplayAll(); + + IPlatformTransactionManager tm = new TxScopeTransactionManager(txAdapter); + TransactionTemplate tt = new TransactionTemplate(tm); + + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + Exception ex = new ArgumentException("test exception"); + try + { + tt.Execute(delegate(ITransactionStatus status) + { + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + if (ex != null) throw ex; + return null; + }); + Assert.Fail("Should have thrown exception"); + } + catch (ArgumentException e) + { + Assert.AreEqual(ex, e); + } + + Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active"); + + + mocks.VerifyAll(); + } + + [Test] + public void PropagationRequiresNewWithExistingTransaction() + { + + ITransactionScopeAdapter txAdapter = (ITransactionScopeAdapter)mocks.CreateMock(typeof(ITransactionScopeAdapter)); + + using (mocks.Ordered()) + { + Expect.Call(txAdapter.IsExistingTransaction).Return(false); + TransactionOptions txOptions = new TransactionOptions(); + txOptions.IsolationLevel = IsolationLevel.ReadCommitted; + txAdapter.CreateTransactionScope(TransactionScopeOption.RequiresNew, txOptions, EnterpriseServicesInteropOption.None); + + //inner tx actions + Expect.Call(txAdapter.IsExistingTransaction).Return(true); + txAdapter.CreateTransactionScope(TransactionScopeOption.RequiresNew, txOptions, EnterpriseServicesInteropOption.None); + txAdapter.Dispose(); + //end inner tx actions + + Expect.Call(txAdapter.RollbackOnly).Return(false); + txAdapter.Complete(); + txAdapter.Dispose(); + + } + mocks.ReplayAll(); + + IPlatformTransactionManager tm = new TxScopeTransactionManager(txAdapter); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + tt.Execute(delegate(ITransactionStatus status) + { + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronization active"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + + tt.Execute(delegate(ITransactionStatus status2) + { + Assert.IsTrue(TransactionSynchronizationManager.SynchronizationActive, "Synchronization active"); + Assert.IsTrue(status2.IsNewTransaction, "Is new transaction"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + status2.RollbackOnly = true; + return null; + }); + + + Assert.IsTrue(status.IsNewTransaction, "Is new transaction"); + Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly); + Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive); + return null; + }); + + mocks.VerifyAll(); + } + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/ITestCoord.cs b/test/Spring/Spring.Data.Tests/Data/ITestCoord.cs new file mode 100644 index 00000000..4d895fc8 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/ITestCoord.cs @@ -0,0 +1,15 @@ + +using Spring.Objects; + +namespace Spring.Data +{ + public interface ITestCoord + { + ITestObjectMgr TestObjectMgr + { + get; set; + } + + void WorkOn(TestObject to1, TestObject to2); + } +} diff --git a/test/Spring/Spring.Data.Tests/Data/ITestObjectMgr.cs b/test/Spring/Spring.Data.Tests/Data/ITestObjectMgr.cs new file mode 100644 index 00000000..93dd5d5d --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/ITestObjectMgr.cs @@ -0,0 +1,11 @@ +using Spring.Objects; + +namespace Spring.Data +{ + public interface ITestObjectMgr + { + void SaveTwoTestObjects(TestObject to1, TestObject to2); + + void DeleteTwoTestObjects(string name1, string name2); + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/LoggingAroundAdvice.cs b/test/Spring/Spring.Data.Tests/Data/LoggingAroundAdvice.cs new file mode 100644 index 00000000..ceb22779 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/LoggingAroundAdvice.cs @@ -0,0 +1,25 @@ + +using AopAlliance.Intercept; +using Common.Logging; + +namespace Spring.Data +{ + public class LoggingAroundAdvice : IMethodInterceptor + { + public int numInvoked; + private static readonly ILog LOG = LogManager.GetLogger(typeof(LoggingAroundAdvice)); + public object Invoke(IMethodInvocation invocation) + { + try + { + LOG.Debug("Advice executing; calling the advised method [" + invocation.Method.Name + "]"); + object returnValue = invocation.Proceed(); + LOG.Debug("Advice executed; advised method [" + invocation.Method.Name + "] returned " + returnValue); + return returnValue; + } finally + { + numInvoked++; + } + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Data/Objects/AdoQueryTests.cs b/test/Spring/Spring.Data.Tests/Data/Objects/AdoQueryTests.cs new file mode 100644 index 00000000..728bbf33 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/Objects/AdoQueryTests.cs @@ -0,0 +1,157 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Data; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Data.Common; + +#endregion + +namespace Spring.Data.Objects +{ + /// + /// This class contains tests for AdoQuery subclasses + /// + /// Mark Pollack + /// $Id: AdoQueryTests.cs,v 1.3 2007/07/25 08:25:34 markpollack Exp $ + [TestFixture] + public class AdoQueryTests + { + private MockRepository mocks; + + [SetUp] + public void Setup() + { + mocks = new MockRepository(); + } + +/* + [Test] + public void QueryWithoutContextRhino() + { + IDbProvider provider = (IDbProvider) mocks.DynamicMock(typeof (IDbProvider)); + + IDbConnection connection = (IDbConnection) mocks.DynamicMock(typeof (IDbConnection)); + + Expect.Call(provider.CreateConnection()).Return(connection); + + IDbCommand command = (IDbCommand) mocks.DynamicMock(typeof (IDbCommand)); + + IDataReader reader = (IDataReader) mocks.DynamicMock(typeof (IDataReader)); + Expect.Call(reader.Read()).Return(true); + Expect.Call(reader.GetInt32(0)).Return(1); + Expect.Call(reader.Read()).Return(false); + + Expect.Call(command.ExecuteReader()).Return(reader); + Expect.Call(provider.CreateCommand()).Return(command); + mocks.ReplayAll(); + + IntMappingQueryWithNoContext queryWithNoContext = new IntMappingQueryWithNoContext(provider); + queryWithNoContext.Compile(); + IList list = queryWithNoContext.QueryByNamedParam(null, null); + Assert.IsTrue(list.Count != 0); + foreach (int count in list) + { + Assert.AreEqual(1, count); + } + + mocks.VerifyAll(); + + + + } + */ + [Test] + public void QueryWithoutContext() + { + + //Prepare provider + IDynamicMock mockProvider = new DynamicMock(typeof(IDbProvider)); + + //Prepare connection to return from provider + IDynamicMock mockConnection = new DynamicMock(typeof(IDbConnection)); + IDbConnection connection = (IDbConnection) mockConnection.Object; + mockProvider.ExpectAndReturn("CreateConnection", connection); + + //Prepare command + IDynamicMock mockCommand = new DynamicMock(typeof(IDbCommand)); + + //Prepare reader to return from command + IDynamicMock mockReader = new DynamicMock(typeof (IDataReader)); + mockReader.ExpectAndReturn("Read", true); + mockReader.ExpectAndReturn("GetInt32", 1, 0); + mockReader.ExpectAndReturn("Read", false); + IDataReader reader = (IDataReader) mockReader.Object; + mockCommand.ExpectAndReturn("ExecuteReader", reader); + + //Preppare command to return from provider + IDbCommand command = (IDbCommand)mockCommand.Object; + mockProvider.ExpectAndReturn("CreateCommand", command); + + + IDbProvider dbProvider = (IDbProvider)mockProvider.Object; + + + //Test MappingAdoQueryWithContext + IntMappingQueryWithNoContext queryWithNoContext = new IntMappingQueryWithNoContext(dbProvider); + queryWithNoContext.Compile(); + //IList list = queryWithNoContext.QueryByNamedParam(null, null); + IList list = queryWithNoContext.Query(); + Assert.IsTrue(list.Count != 0); + foreach (int count in list) + { + Assert.AreEqual(1, count); + } + + + + + + + + } + + public class IntMappingQueryWithNoContext : MappingAdoQueryWithContext + { + private static string sql = "select id from custmr"; + + public IntMappingQueryWithNoContext(IDbProvider dbProvider) + : base(dbProvider, sql) + { + CommandType = CommandType.Text; + } + + + protected override object MapRow(IDataReader reader, int rowNum, IDictionary inParams, + IDictionary callingContext) + { + Assert.IsNull(inParams); + Assert.IsNull(callingContext); + return reader.GetInt32(0); + } + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Data/TestCoord.cs b/test/Spring/Spring.Data.Tests/Data/TestCoord.cs new file mode 100644 index 00000000..97d0b69f --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/TestCoord.cs @@ -0,0 +1,27 @@ + + +using Spring.Objects; + +namespace Spring.Data +{ + public class TestCoord : ITestCoord + { + private ITestObjectMgr testObjectMgr; + + + public ITestObjectMgr TestObjectMgr + { + get { return testObjectMgr; } + set { testObjectMgr = value; } + } + + #region ITestCoordinator Members + + public void WorkOn(TestObject to1, TestObject to2) + { + testObjectMgr.SaveTwoTestObjects(to1,to2); + } + + #endregion + } +} diff --git a/test/Spring/Spring.Data.Tests/Data/TestObjectMgr.cs b/test/Spring/Spring.Data.Tests/Data/TestObjectMgr.cs new file mode 100644 index 00000000..6792cfec --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Data/TestObjectMgr.cs @@ -0,0 +1,85 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using Common.Logging; +using Spring.Objects; +using Spring.Transaction; +using Spring.Transaction.Interceptor; +using Spring.Transaction.Support; +using IsolationLevel=System.Data.IsolationLevel; + +#endregion + +namespace Spring.Data +{ + /// + /// Group together multiple ITestObjectDao operations. + /// + /// Mark Pollack (.NET) + /// $Id: TestObjectMgr.cs,v 1.3 2007/10/21 18:32:41 markpollack Exp $ + public class TestObjectMgr : ITestObjectMgr + { + #region Fields + + private static readonly ILog LOG = LogManager.GetLogger(typeof(TestObjectMgr)); + #endregion + + #region Constructor (s) + /// + /// Initializes a new instance of the class. + /// + public TestObjectMgr() + { + + } + + #endregion + + + + #region Methods + + [Transaction()] + public void SaveTwoTestObjects(TestObject to1, TestObject to2) + { + LOG.Debug("TransactionActive = " + TransactionSynchronizationManager.ActualTransactionActive); + } +#if !NET_1_0 && !NET_1_1 + [Transaction(TransactionPropagation.Required, IsolationLevel.Unspecified, Timeout = 50, + RollbackFor = new Type[]{typeof(ArgumentNullException)}, + ReadOnly = false, + EnterpriseServicesInteropOption = System.Transactions.EnterpriseServicesInteropOption.Automatic, + NoRollbackFor = new Type[] { typeof(ArithmeticException), typeof(NotSupportedException) })] +#else + [Transaction(TransactionPropagation.Required, IsolationLevel.Unspecified, Timeout = 50, + RollbackFor = new Type[]{typeof(ArgumentNullException)}, + ReadOnly = false, + NoRollbackFor = new Type[] { typeof(ArithmeticException), typeof(NotSupportedException) })] +#endif + public void DeleteTwoTestObjects(string name1, string name2) + { + } + + #endregion + } +} diff --git a/test/Spring/Spring.Data.Tests/DataExceptionTests.cs b/test/Spring/Spring.Data.Tests/DataExceptionTests.cs new file mode 100644 index 00000000..f3da3195 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/DataExceptionTests.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using NUnit.Framework; + +#endregion + +namespace Spring +{ + /// + /// Unit tests for all of the exception classes in the Spring.Data library... + /// + /// Rick Evans + /// $Id: DataExceptionTests.cs,v 1.6 2006/05/18 21:37:53 markpollack Exp $ + [TestFixture] + public sealed class DataExceptionTests : ExceptionsTest + { + [TestFixtureSetUp] + public void FixtureSetUp () + { + AssemblyToCheck = Assembly.GetAssembly (typeof (Spring.Transaction.CannotCreateTransactionException)); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Spring.Data.Tests.2003.csproj b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.2003.csproj new file mode 100644 index 00000000..c3390c9d --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.2003.csproj @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.Tests/Spring.Data.Tests.2005.csproj b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.2005.csproj new file mode 100644 index 00000000..579090e8 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.2005.csproj @@ -0,0 +1,190 @@ + + + Local + 8.0.50727 + 2.0 + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0} + Debug + AnyCPU + + + + + Spring.Data.Tests + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + ..\..\..\build\VS.Net.2005\Spring.Data.Tests\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + 67, 618 + false + false + false + false + 4 + full + prompt + true + + + ..\..\..\build\VS.Net.2005\Spring.Data.Tests\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + 618 + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\Net\2.0\DotNetMock.dll + + + False + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + False + ..\..\..\lib\Net\2.0\Rhino.Mocks.dll + + + System + + + + System.Data + + + + System.Drawing + + + + + System.XML + + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 + + + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904} + Spring.Core.Tests.2005 + + + + + + + CommonAssemblyInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + echo "Copying .xml files for tests" +xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2005\Spring.Data.Tests\$(ConfigurationName)\ /y /s /q /d +xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2005\Spring.Data.Tests\$(ConfigurationName)\ /y /s /q + + \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Spring.Data.Tests.build b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.build new file mode 100644 index 00000000..5b1e09db --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.build @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.Tests/Spring.Data.Tests.dll.config b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.dll.config new file mode 100644 index 00000000..259adbad --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.dll.config @@ -0,0 +1,80 @@ + + + + + + + +
    + + + +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.Tests/Support/ErrorCodeExceptionTranslatorTests.cs b/test/Spring/Spring.Data.Tests/Support/ErrorCodeExceptionTranslatorTests.cs new file mode 100644 index 00000000..ab43bdfa --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Support/ErrorCodeExceptionTranslatorTests.cs @@ -0,0 +1,111 @@ +#if !NET_1_0 +#region Licence + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Dao; +using Spring.Data; +using Spring.Data.Common; +using Spring.Data.Support; + +#endregion + +namespace Spring.Support +{ + /// + /// Test the database exception translation. + /// + /// Mark Pollack (.NET) + /// $Id: ErrorCodeExceptionTranslatorTests.cs,v 1.8 2007/12/06 20:14:39 markpollack Exp $ + [TestFixture] + public class ErrorCodeExceptionTranslatorTests + { + #region Fields + private static ErrorCodes ERROR_CODES = new ErrorCodes(); + + private IDbProvider dbProvider = new TestDbProvider(); + #endregion + + + #region Constructor (s) + + /// + /// Initializes a new instance of the class. + /// + public ErrorCodeExceptionTranslatorTests() + { + + } + + #endregion + + #region Properties + + #endregion + + + + [SetUp] + public void SetUp() + { + ERROR_CODES.BadSqlGrammarCodes = (new String[] { "1", "2" }); + ERROR_CODES.InvalidResultSetAccessCodes = (new String[] { "3", "4" }); + ERROR_CODES.DataAccessResourceFailureCodes = (new String[] { "5" }); + ERROR_CODES.DataIntegrityViolationCodes = (new String[] { "544", "2627", "8114", "8115" }); + ERROR_CODES.CannotAcquireLockCodes = (new String[] { "7" }); + ERROR_CODES.DeadlockLoserCodes = (new String[] { "8" }); + ERROR_CODES.CannotSerializeTransactionCodes = (new String[] { "9" }); + } + + [Test] + public void ErrorCodeTransation() + { + IAdoExceptionTranslator exceptionTranslator = new ErrorCodeExceptionTranslator(dbProvider, ERROR_CODES); + TestSqlException badSqlEx = new TestSqlException("", "1"); + + BadSqlGrammarException bsgex = (BadSqlGrammarException)exceptionTranslator.Translate("task","SQL", badSqlEx); + Assert.AreEqual("SQL", bsgex.Sql); + Assert.AreEqual(badSqlEx, bsgex.InnerException); + + TestSqlException invResEx = new TestSqlException("", "4"); + InvalidResultSetAccessException irsex = (InvalidResultSetAccessException) exceptionTranslator.Translate("task", "SQL", invResEx); + Assert.AreEqual("SQL", irsex.Sql); + Assert.AreEqual(invResEx, irsex.InnerException); + + CheckTranslation(exceptionTranslator, "5", typeof(DataAccessResourceFailureException)); + CheckTranslation(exceptionTranslator, "544", typeof(DataIntegrityViolationException)); + CheckTranslation(exceptionTranslator, "7", typeof(CannotAcquireLockException)); + CheckTranslation(exceptionTranslator, "8", typeof(DeadlockLoserDataAccessException)); + CheckTranslation(exceptionTranslator, "9", typeof(CannotSerializeTransactionException)); + } + + private void CheckTranslation(IAdoExceptionTranslator translator, string errorCode, Type exType) + { + TestSqlException sex = new TestSqlException("", errorCode); + DataAccessException ex = translator.Translate("", "", sex); + Assert.IsTrue(exType.IsAssignableFrom(ex.GetType())); + Assert.IsTrue(ex.InnerException == sex); + } + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Support/TestDbProvider.cs b/test/Spring/Spring.Data.Tests/Support/TestDbProvider.cs new file mode 100644 index 00000000..94addf3e --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Support/TestDbProvider.cs @@ -0,0 +1,167 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using Spring.Data.Common; + +#endregion + +namespace Spring.Support +{ + /// + /// A fake provider class used to test exception translation functionality + /// + /// Mark Pollack (.NET) + /// $Id: TestDbProvider.cs,v 1.11 2007/12/06 05:48:32 markpollack Exp $ + public class TestDbProvider : IDbProvider + { + private string connectionString; + private IDbConnection connection; + + protected IDbMetadata dbMetadata; + + public TestDbProvider() + { + IDbProvider provider = DbProviderFactory.GetDbProvider("SqlServer-1.1"); + dbMetadata = provider.DbMetadata; + + } + + /// + /// Return metadata information about the database. + /// + public IDbMetadata DbMetadata + { + get + { + return dbMetadata; + } + } + /// + /// Connection string used to create connections. + /// + public string ConnectionString + { + set { connectionString = value; } + get { return connectionString; } + } + public IDbCommand CreateCommand() + { + throw new NotImplementedException(); + } + + public IDbConnection CreateConnection() + { + return connection; + } + + public IDbDataParameter CreateParameter() + { + throw new NotImplementedException(); + } + + + public IDbDataAdapter CreateDataAdapter() + { + throw new NotImplementedException(); + } + + public object CreateCommandBuilder() + { + throw new NotImplementedException(); + } + + public string ExtractError(Exception e) + { + TestSqlException testSqlException = (TestSqlException)e; + return testSqlException.ErrorNumber; + } + + public bool IsDataAccessException(Exception e) + { +#if NET_2_0 + if (e is System.Data.Common.DbException) + { + return true; + } + else + { + return false; + } +#else + return IsDataAccessExceptionBCL11(e); +#endif + } + + public bool IsDataAccessExceptionBCL11(Exception e) + { + return true; + } + + public string CreateParameterName(string name) + { + if (dbMetadata.BindByName) + { + if (DbMetadata.UseParameterPrefixInSql) + { + return DbMetadata.ParameterNamePrefix + name; + } + else + { + return name; + } + } + else + { + return DbMetadata.ParameterNamePrefix; + } + } + + public string CreateParameterNameForCollection(string name) + { + if (dbMetadata.BindByName) + { + if (DbMetadata.UseParameterNamePrefixInParameterCollection) + { + return DbMetadata.ParameterNamePrefix + name; + } + else + { + return name; + } + } + else + { + return DbMetadata.ParameterNamePrefix; + } + } + + #region Stub method to set behavior + + public IDbConnection ConnectionToCreate + { + set { connection = value;} + } + #endregion + } +} diff --git a/test/Spring/Spring.Data.Tests/Support/TestSqlException.cs b/test/Spring/Spring.Data.Tests/Support/TestSqlException.cs new file mode 100644 index 00000000..b7fb34cf --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Support/TestSqlException.cs @@ -0,0 +1,84 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Serialization; + +#endregion + +namespace Spring.Support +{ + /// + /// A simple exception class to test exception translation functionality + /// + /// Mark Pollack (.NET) + /// $Id: TestSqlException.cs,v 1.2 2006/06/26 18:57:31 markpollack Exp $ + [Serializable] + public class TestSqlException : Exception + { + + + private string errorNumber; + + public TestSqlException() {} + + + public TestSqlException( string message ) : base( message ) {} + + public TestSqlException(string message, string errorNumber) :base (message) + { + ErrorNumber = errorNumber; + } + + public TestSqlException( string message, Exception rootCause) + : base( message , rootCause ) {} + + protected TestSqlException( + SerializationInfo info, StreamingContext context ) : base( info, context ) + { + errorNumber = info.GetString( "errorNumber" ); + } + + public string ErrorNumber + { + get { return errorNumber; } + set { errorNumber = value; } + } + /// + /// Override of + /// to allow for private serialization. + /// + /// + /// The + /// that holds the serialized object data about the exception. + /// + /// + /// The + /// that contains contextual information about the source or destination. + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue( "errorNumber", errorNumber ); + base.GetObjectData( info, context ); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/CallCountingTransactionManager.cs b/test/Spring/Spring.Data.Tests/Transaction/CallCountingTransactionManager.cs new file mode 100644 index 00000000..7341e7fc --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/CallCountingTransactionManager.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using Spring.Transaction.Support; + +namespace Spring.Transaction +{ + /// + /// This is a test implementation to count the number of times PlatformTransactionManager methods + /// have been called. + /// + /// Mark Pollack + /// $Id: CallCountingTransactionManager.cs,v 1.1 2007/05/26 00:43:07 markpollack Exp $ + public class CallCountingTransactionManager : AbstractPlatformTransactionManager + { + + public int begun; + public int commits; + public int rollbacks; + public int inflight; + + + protected override object DoGetTransaction() + { + return new object(); + } + + protected override void DoBegin(object transaction, ITransactionDefinition definition) + { + ++begun; + ++inflight; + } + + protected override void DoCommit(DefaultTransactionStatus status) + { + ++commits; + --inflight; + } + + protected override void DoRollback(DefaultTransactionStatus status) + { + ++rollbacks; + --inflight; + } + + public void Clear() + { + begun = commits = rollbacks = inflight = 0; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Transaction/CommonTypes.cs b/test/Spring/Spring.Data.Tests/Transaction/CommonTypes.cs new file mode 100644 index 00000000..c094dc8c --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/CommonTypes.cs @@ -0,0 +1,370 @@ +using System; +using System.Data; +using DotNetMock; +using Spring.Transaction.Support; + +namespace Spring.Transaction +{ + public class MyMockTxnObject : MockObject, ISmartTransactionObject + { + private ExpectationCounter _isRollbackOnlyCalls = new ExpectationCounter( "IsRollbackOnlyCalls" ); + private bool _isRollbackOnly = false; + + public MyMockTxnObject() : this( "MyMockTxnObject" ) {} + public MyMockTxnObject( string name ) : base( name ) {} + + + + public void SetExpectedRollbackOnlyCalls( int calls ) + { + _isRollbackOnlyCalls.Expected = calls; + } + public void SetExpectedIsRollBackOnlyValue( bool isRollbackOnly ) + { + _isRollbackOnly = isRollbackOnly; + } + #region ISmartTransactionObject Members + + public bool RollbackOnly + { + get + { + _isRollbackOnlyCalls.Inc(); + return _isRollbackOnly; + } + } + + #endregion + } + public class MyMockTxnObjectSavepointMgr : MockObject, ISavepointManager, ISmartTransactionObject + { + private string _savepoint; + private ExpectationString _expectedSavepoint = new ExpectationString("Savepoint"); + + public void SetSavepointToReturn( string savepoint ) + { + _savepoint = savepoint; + } + public void SetExpectedSavepoint( string savepoint ) + { + _expectedSavepoint.Expected = savepoint; + } + #region ISmartTransactionObject Members + + public bool RollbackOnly + { + get + { // TODO: Add MyMockTxnObjectSavepointMgr.IsRollbackOnly implementation + return false; + } + } + + #endregion + + #region ISavepointManager Members + public void ReleaseSavepoint(string savepoint) + { + _expectedSavepoint.Actual = savepoint; + } + + public void CreateSavepoint( string savepoint ) + { + _expectedSavepoint.Actual = savepoint; + } + + public void RollbackToSavepoint(string savepoint) + { + _expectedSavepoint.Actual = savepoint; + } + + #endregion + } + + public class MockTxnPlatformMgr : MockObject, IPlatformTransactionManager + { + private ExpectationCounter _commitCalls = new ExpectationCounter("CommitCalls"); + private ExpectationCounter _getTransactionCalls = new ExpectationCounter("GetTxnCalls"); + private ExpectationCounter _rollbackCalls = new ExpectationCounter("RollbackCalls"); + private bool _throwRollbackException = false; + + #region IPlatformTransactionManager Members + public void SetExpectedCommitCallCount( int count ) + { + _commitCalls.Expected = count; + } + public void SetExpectedRollbackCallCount( int count ) + { + _rollbackCalls.Expected = count; + } + public void SetExpectedGetTxnCallCount( int count ) + { + _getTransactionCalls.Expected = count; + } + public bool ThrowRollbackException + { + set { _throwRollbackException = value; } + } + public void Rollback(ITransactionStatus transactionStatus) + { + _rollbackCalls.Inc(); + if ( _throwRollbackException ) + { + throw new Exception("Rollback"); + } + } + + public void Commit(ITransactionStatus transactionStatus) + { + _commitCalls.Inc(); + } + + public ITransactionStatus GetTransaction(ITransactionDefinition definition) + { + _getTransactionCalls.Inc(); + return null; + } + + #endregion + + } + + public class MockTxnSync : MockObject, ITransactionSynchronization + { + #region ITransactionSynchronization Members + + public void AfterCompletion(Spring.Transaction.Support.TransactionSynchronizationStatus status) + { + // TODO: Add MockTxnSync.AfterCompletion implementation + } + + public void BeforeCommit(bool readOnly) + { + // TODO: Add MockTxnSync.BeforeCommit implementation + } + + public void AfterCommit() + { + + } + + public void Resume() + { + // TODO: Add MockTxnSync.Resume implementation + } + + public void BeforeCompletion() + { + // TODO: Add MockTxnSync.BeforeCompletion implementation + } + + public void Suspend() + { + // TODO: Add MockTxnSync.Suspend implementation + } + + #endregion + } + + public class MockTxnDefinition : MockObject, ITransactionDefinition + { + private int _transactionTimeout = DefaultTransactionDefinition.TIMEOUT_DEFAULT; + private TransactionPropagation _transactionPropagation = TransactionPropagation.NotSupported; + private bool _readOnly = false; + private string _name = null; +#if NET_2_0 + private System.Transactions.EnterpriseServicesInteropOption _esInteropOption; + +#endif + #region ITransactionDefinition Members + + public bool ReadOnly + { + get + { + return _readOnly; + } + set + { + _readOnly = value; + } + } + + public int TransactionTimeout + { + get + { + return _transactionTimeout; + } + set + { + _transactionTimeout = value; + } + } + + public IsolationLevel TransactionIsolationLevel + { + get + { + return IsolationLevel.Unspecified; + } + } + + public TransactionPropagation PropagationBehavior + { + get + { + return _transactionPropagation; + } + set + { + _transactionPropagation = value; + } + } + + public string Name + { + get + { + return _name; + } + } + + +#if NET_2_0 + public System.Transactions.EnterpriseServicesInteropOption EnterpriseServicesInteropOption + { + get { return _esInteropOption; } + set { _esInteropOption = value; } + } +#endif + + #endregion + + } + public class MockTxnPlatformMgrAbstract : AbstractPlatformTransactionManager, IMockObject + { + private object _transaction; + private bool _isVerified; + private string _mockName; + private bool _isExistingTransaction; + + private ExpectationCounter _doBeginCalls = new ExpectationCounter("DoBegin Calls"); + private ExpectationCounter _doGetTxnCalls = new ExpectationCounter("DoGetTransaction Calls"); + private ExpectationCounter _isExistingTxnCalls = new ExpectationCounter("IsExistingTransaction Calls"); + + + private bool _useSavepointForNestedTransaction; + + public void SetExpectedCalls( string method, int calls ) + { + switch ( method ) + { + case "DoBegin": + _doBeginCalls.Expected = calls; + break; + case "IsExistingTransaction": + _isExistingTxnCalls.Expected = calls; + break; + case "DoGetTransaction": + _doGetTxnCalls.Expected = calls; + break; + default: + break; + } + } + public void SetTransaction( object transaction ) + { + _transaction = transaction; + _isExistingTransaction = true; + } + public object Transaction + { + get { return _transaction; } + } + public bool Savepoints + { + set { _useSavepointForNestedTransaction = value; } + } + #region IMockObject Members + + public void NotImplemented(string methodName) + { + // TODO: Add MockTxnPlatformMgrAbstract.NotImplemented implementation + } + + public string MockName + { + get { return _mockName; } + set { _mockName = value;} + } + + #endregion + + #region IVerifiable Members + + public void Verify() + { + Verifier.Verify( this ); + _isVerified = true; + } + + public bool IsVerified + { + get { return _isVerified; } + } + + #endregion + + #region AbstractPlatformTransactionManager Impls + protected override void DoResume(object transaction, object suspendedResources) + { + + } + protected override void DoCommit(DefaultTransactionStatus status) + { + + } + protected override object DoGetTransaction() + { + _doGetTxnCalls.Inc(); + if ( null == _transaction ) + { + return new object(); + } + else + { + return _transaction; + } + } + protected override object DoSuspend(object transaction) + { + return null; + } + protected override void DoBegin(object transaction, ITransactionDefinition definition) + { + _doBeginCalls.Inc(); + } + protected override void DoSetRollbackOnly(DefaultTransactionStatus status) + { + + } + protected override void DoRollback(DefaultTransactionStatus status) + { + + } + protected override bool IsExistingTransaction(object transaction) + { + _isExistingTxnCalls.Inc(); + return _isExistingTransaction; + } + + protected override bool UseSavepointForNestedTransaction() + { + return _useSavepointForNestedTransaction; + } + + #endregion + + } + +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Config/TxNamespaceParserTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Config/TxNamespaceParserTests.cs new file mode 100644 index 00000000..8bccb431 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Config/TxNamespaceParserTests.cs @@ -0,0 +1,115 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; + +using NUnit.Framework; + +using Spring.Aop.Config; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects; +using Spring.Objects.Factory.Xml; +using Spring.Transaction.Interceptor; + +#endregion + +namespace Spring.Transaction.Config +{ + [TestFixture] + public class TxNamespaceParserTests + { + private IApplicationContext ctx; + + [SetUp] + public void SetUp() + { + NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser)); + NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser)); + ctx = new XmlApplicationContext("assembly://Spring.Data.Tests/Spring.Transaction.Config/TxNamespaceParserTests.xml"); + } + + [Test] + public void Registered() + { + Assert.IsNotNull(NamespaceParserRegistry.GetParser("http://www.springframework.net/tx")); + Assert.IsTrue(ctx.ContainsObjectDefinition(AopNamespaceUtils.AUTO_PROXY_CREATOR_OBJECT_NAME)); + Assert.IsTrue(ctx.ContainsObjectDefinition("Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor")); + } + + + [Test] + public void InvokeTransactional() + { + ITestObject testObject = TestObject; + CallCountingTransactionManager ptm = ctx["transactionManager"] as CallCountingTransactionManager; + Assert.IsNotNull(ptm); + + // try with transactional + Assert.AreEqual(0, ptm.begun,"Should not have started any transactions"); + testObject.GetDescription(); + Assert.AreEqual(1, ptm.begun, "Should have 1 started transaction"); + Assert.AreEqual(1, ptm.commits, "Should have 1 committed transaction"); + + // try with non-transaction + int i = testObject.Age; + Assert.AreEqual(1, ptm.begun, "Should not have started another transaction"); + + // try with exceptional + try + { + testObject.Exceptional(new ArgumentNullException()); + Assert.Fail("Should not get here"); + } catch (Exception) + { + Assert.AreEqual(2, ptm.begun, "Should have another started transaction"); + Assert.AreEqual(1, ptm.rollbacks, "Should have 1 rolled back transaction"); + } + } + + private ITestObject TestObject + { + get + { + return ctx["testObject"] as + ITestObject; + } + } + + [Test] + public void RollbackRules() + { + TransactionInterceptor txInterceptor = ctx.GetObject("txRollbackAdvice") as TransactionInterceptor; + Assert.IsNotNull(txInterceptor); + + MethodInfo getDescriptionMethod = typeof(ITestObject).GetMethod("GetDescription"); + MethodInfo exceptionalMethod = typeof (ITestObject).GetMethod("Exceptional"); + ITransactionAttributeSource txAttrSource = txInterceptor.TransactionAttributeSource; + ITransactionAttribute txAttr = txAttrSource.ReturnTransactionAttribute(getDescriptionMethod, typeof (ITestObject)); + Assert.IsTrue(txAttr.RollbackOn(new System.ApplicationException())); + + txAttr = txAttrSource.ReturnTransactionAttribute(exceptionalMethod, typeof (ITestObject)); + Assert.IsFalse(txAttr.RollbackOn(new System.ArithmeticException())); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Config/TxNamespaceParserTests.xml b/test/Spring/Spring.Data.Tests/Transaction/Config/TxNamespaceParserTests.xml new file mode 100644 index 00000000..78bda4d5 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Config/TxNamespaceParserTests.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Data.Tests/Transaction/HeuristicCompletionExceptionTests.cs b/test/Spring/Spring.Data.Tests/Transaction/HeuristicCompletionExceptionTests.cs new file mode 100644 index 00000000..10968348 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/HeuristicCompletionExceptionTests.cs @@ -0,0 +1,35 @@ +using System; +using Spring.Transaction; +using NUnit.Framework; + +namespace Spring.Transaction +{ + [TestFixture] + public class HeuristicCompletionExceptionTests + { + [Test] + public void TransactionOutcomeStateGetter() + { + HeuristicCompletionException ex = new HeuristicCompletionException( TransactionOutcomeState.Unknown, new Exception() ); + Assert.IsTrue( TransactionOutcomeState.Unknown == ex.OutcomeState ); + } + [Test] + public void TransactionOutcomeStateGetterCommittted() + { + HeuristicCompletionException ex = new HeuristicCompletionException( TransactionOutcomeState.Committed, new Exception() ); + Assert.IsTrue( TransactionOutcomeState.Committed == ex.OutcomeState ); + } + [Test] + public void TransactionOutcomeStateGetterMixed() + { + HeuristicCompletionException ex = new HeuristicCompletionException( TransactionOutcomeState.Mixed, new Exception() ); + Assert.IsTrue( TransactionOutcomeState.Mixed == ex.OutcomeState ); + } + [Test] + public void TransactionOutcomeStateGetterRolledback() + { + HeuristicCompletionException ex = new HeuristicCompletionException( TransactionOutcomeState.Rolledback, new Exception() ); + Assert.IsTrue( TransactionOutcomeState.Rolledback == ex.OutcomeState ); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/AbstractTransactionAspectTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/AbstractTransactionAspectTests.cs new file mode 100644 index 00000000..1b0da2e3 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/AbstractTransactionAspectTests.cs @@ -0,0 +1,252 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Data; +using Spring.Objects; + +#endregion + +namespace Spring.Transaction.Interceptor +{ + /// + /// Mock object based tests for transaction aspects. + /// + /// Mark Pollack + /// $Id: AbstractTransactionAspectTests.cs,v 1.5 2007/10/21 18:16:58 markpollack Exp $ + [TestFixture] + public abstract class AbstractTransactionAspectTests + { + private MockRepository mocks; + + [SetUp] + public void Setup() + { + mocks = new MockRepository(); + } + + [Test] + public void CopyAttributes() + { + + IPlatformTransactionManager ptm = PlatformTxManagerForNewTransaction(); + AttributesTransactionAttributeSource tas = new AttributesTransactionAttributeSource(); + TestObjectMgr to = new TestObjectMgr(); + ITestObjectMgr ito = (ITestObjectMgr)Advised(to, ptm, tas); + + ito.DeleteTwoTestObjects("foo", "bar"); + + } + + [Test] + public void CannotCommitTransaction() + { + ITransactionAttribute txatt = new DefaultTransactionAttribute(); + MethodInfo m = typeof (ITestObject).GetMethod("GetDescription"); + MethodMapTransactionAttributeSource tas = new MethodMapTransactionAttributeSource(); + tas.AddTransactionalMethod(m, txatt); + + + IPlatformTransactionManager ptm = PlatformTxManagerForNewTransaction(); + + ITransactionStatus status = TransactionStatusForNewTransaction(); + Expect.On(ptm).Call(ptm.GetTransaction(txatt)).Return(status); + UnexpectedRollbackException ex = new UnexpectedRollbackException("foobar", null); + ptm.Commit(status); + LastCall.On(ptm).Throw(ex); + mocks.ReplayAll(); + + TestObject to = new TestObject(); + ITestObject ito = (ITestObject) Advised(to, ptm, tas); + + try + { + ito.GetDescription(); + Assert.Fail("Shouldn't have succeeded"); + } catch (UnexpectedRollbackException thrown) + { + Assert.IsTrue(thrown == ex); + } + + mocks.VerifyAll(); + + + + } + + private IPlatformTransactionManager PlatformTxManagerForNewTransaction() + { + return (IPlatformTransactionManager) mocks.DynamicMock(typeof(IPlatformTransactionManager)); + } + + + [Test] + public void ProgrammaticRollback() + { + ITransactionAttribute txatt = new DefaultTransactionAttribute(); + MethodInfo m = typeof(RollbackTestObject).GetMethod("GetDescription"); + + MethodMapTransactionAttributeSource tas = new MethodMapTransactionAttributeSource(); + tas.AddTransactionalMethod(m, txatt); + + ITransactionStatus status = TransactionStatusForNewTransaction(); + + IPlatformTransactionManager ptm = PlatformTxManagerForNewTransaction(); + + Expect.Call(ptm.GetTransaction(txatt)).Return(status).Repeat.Once(); + ptm.Commit(status); + LastCall.On(ptm).Repeat.Once(); + + mocks.ReplayAll(); + + RollbackTestObject to = new RollbackTestObject(); + + ITestObject ito = (ITestObject) Advised(to, ptm, tas); + + Assert.AreEqual("test description", ito.GetDescription()); + + mocks.VerifyAll(); + + + } + + [Test] + public void RollbackOnException() + { + DoTestRollbackOnException(new Exception(), true, false); + } + + [Test] + public void NoRollbackOnException() + { + DoTestRollbackOnException(new Exception(), false, false); + } + + [Test] + public void RollbackOnExceptionWithRollbackException() + { + DoTestRollbackOnException(new Exception(), true, true); + } + + [Test] + public void NoRollbackOnExceptionWithRollbackException() + { + DoTestRollbackOnException(new Exception(), false, true); + } + + private void DoTestRollbackOnException(Exception exception, bool shouldRollback, bool rollbackException) + { + ITransactionAttribute txatt = new ConfigurableTransactionAttribute(shouldRollback); + + + MethodInfo mi = typeof (ITestObject).GetMethod("Exceptional"); + + MethodMapTransactionAttributeSource tas = new MethodMapTransactionAttributeSource(); + tas.AddTransactionalMethod(mi, txatt); + ITransactionStatus status = TransactionStatusForNewTransaction(); + + IPlatformTransactionManager ptm = + (IPlatformTransactionManager) mocks.DynamicMock(typeof (IPlatformTransactionManager)); + + + Expect.On(ptm).Call(ptm.GetTransaction(txatt)).Return(status); + + + if (shouldRollback) + { + ptm.Rollback(status); + } + else + { + ptm.Commit(status); + } + TransactionSystemException tex = new TransactionSystemException("system exception"); + if (rollbackException) + { + LastCall.On(ptm).Throw(tex).Repeat.Once(); + } + else + { + LastCall.On(ptm).Repeat.Once(); + } + mocks.ReplayAll(); + + TestObject to = new TestObject(); + ITestObject ito = (ITestObject) Advised(to, ptm, tas); + + try + { + ito.Exceptional(exception); + Assert.Fail("Should have thrown exception"); + } catch (Exception e) + { + if (rollbackException) + { + Assert.AreEqual(tex, e); + } + else + { + Assert.AreEqual(exception, e); + } + } + + mocks.VerifyAll(); + + } + + private ITransactionStatus TransactionStatusForNewTransaction() + { + return (ITransactionStatus) mocks.DynamicMock(typeof (ITransactionStatus)); + } + + protected abstract object Advised(object target, IPlatformTransactionManager ptm, + ITransactionAttributeSource tas); + } + + internal class RollbackTestObject : TestObject + { + public override string GetDescription() + { + ITransactionStatus txStatus = TransactionInterceptor.CurrentTransactionStatus; + txStatus.RollbackOnly = true; + return "test description"; + } + + } + + internal class ConfigurableTransactionAttribute : DefaultTransactionAttribute + { + private bool shouldRollback; + public ConfigurableTransactionAttribute(bool shouldRollback) + { + this.shouldRollback = shouldRollback; + } + + public override bool RollbackOn(Exception exception) + { + return shouldRollback; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/DefaultTransactionAttributeTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/DefaultTransactionAttributeTests.cs new file mode 100644 index 00000000..e43e933d --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/DefaultTransactionAttributeTests.cs @@ -0,0 +1,24 @@ +using System; +using NUnit.Framework; + +namespace Spring.Transaction.Interceptor +{ + [TestFixture] + public class DefaultTransactionAttributeTests + { + [Test] + public void RollbackOnTests() + { + DefaultTransactionAttribute dta = new DefaultTransactionAttribute(); + Assert.IsTrue( dta.RollbackOn( new SystemException())); + //mlp 3/17 changed rollback to rollback on all exceptions. + Assert.IsTrue( dta.RollbackOn( new TransactionSystemException())); + } + [Test] + public void ToStringTests() + { + DefaultTransactionAttribute dta = new DefaultTransactionAttribute(); + Assert.AreEqual( "PROPAGATION_Required,ISOLATION_ReadCommitted,-System.Exception", dta.ToString()); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/MatchAlwaysTransactionAttributeSourceTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/MatchAlwaysTransactionAttributeSourceTests.cs new file mode 100644 index 00000000..c6f4e821 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/MatchAlwaysTransactionAttributeSourceTests.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections; +using System.Data; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Core.TypeResolution; + +namespace Spring.Transaction.Interceptor +{ + /// + /// Test MatchAlwaysTransactionAttributeSourceTests + /// + /// Mark Pollack + /// $Id: MatchAlwaysTransactionAttributeSourceTests.cs,v 1.7 2008/05/27 19:07:03 markpollack Exp $ + [TestFixture] + public class MatchAlwaysTransactionAttributeSourceTests + { + [Test] + public void GetTransactionAttribute() + { + MatchAlwaysTransactionAttributeSource tas = new MatchAlwaysTransactionAttributeSource(); + ITransactionAttribute ta = tas.ReturnTransactionAttribute(typeof (Object).GetMethod("GetHashCode"), null); + Assert.IsNotNull(ta); + Assert.IsTrue(TransactionPropagation.Required == ta.PropagationBehavior); + tas.TransactionAttribute = new DefaultTransactionAttribute(TransactionPropagation.Supports); + ta = + tas.ReturnTransactionAttribute(typeof (DataException).GetType().GetMethod("GetHashCode"), + typeof (DataException)); + Assert.IsNotNull(ta); + Assert.IsTrue(TransactionPropagation.Supports == ta.PropagationBehavior); + } + + + [Test] + public void CanConfigureInAppContext() + { + TypeRegistry.RegisterType("TransactionPropagation", typeof(TransactionPropagation)); + IApplicationContext ctx = + new XmlApplicationContext( + "assembly://Spring.Data.Tests/Spring.Transaction.Interceptor/MatchAlwaysTransactionAttributeSourceTests.xml"); + MatchAlwaysTransactionAttributeSource tas = + (MatchAlwaysTransactionAttributeSource) ctx.GetObject("MatchAlwaysTransactionAttributeSource"); + Assert.AreEqual(TransactionPropagation.RequiresNew, + tas.ReturnTransactionAttribute(null, null).PropagationBehavior); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/MatchAlwaysTransactionAttributeSourceTests.xml b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/MatchAlwaysTransactionAttributeSourceTests.xml new file mode 100644 index 00000000..c1df9f7b --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/MatchAlwaysTransactionAttributeSourceTests.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/NoRollbackRuleAttributeTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/NoRollbackRuleAttributeTests.cs new file mode 100644 index 00000000..40999406 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/NoRollbackRuleAttributeTests.cs @@ -0,0 +1,52 @@ +using System; +using System.Data; +using System.Text; +using NUnit.Framework; +using Spring.Aop.Framework; + +namespace Spring.Transaction.Interceptor +{ + [TestFixture] + public class NoNoRollbackRuleAttributeTests + { + [Test] + public void FoundImmediatelyWithString() + { + NoRollbackRuleAttribute rr = new NoRollbackRuleAttribute("System.Exception"); + Assert.IsTrue(rr.GetDepth(typeof(Exception)) == 0); + } + [Test] + public void FoundImmediatelyWithClass() + { + NoRollbackRuleAttribute rr = new NoRollbackRuleAttribute(typeof(Exception)); + Assert.IsTrue(rr.GetDepth(typeof(Exception)) == 0); + } + [Test] + public void NotFound() + { + NoRollbackRuleAttribute rr = new NoRollbackRuleAttribute("System.Data.DataException"); + Assert.IsTrue(rr.GetDepth(typeof(ApplicationException)) == -1); + } + [Test] + public void Ancestry() + { + NoRollbackRuleAttribute rr = new NoRollbackRuleAttribute("System.Exception"); + Assert.IsTrue(rr.GetDepth(typeof(DataException)) == 2); + } + [Test] + public void AlwaysTrue() + { + NoRollbackRuleAttribute rr = new NoRollbackRuleAttribute("System.Exception"); + Assert.IsTrue(rr.GetDepth(typeof(SystemException)) > 0); + Assert.IsTrue(rr.GetDepth(typeof(ApplicationException)) > 0); + Assert.IsTrue(rr.GetDepth(typeof(DataException)) > 0); + Assert.IsTrue(rr.GetDepth(typeof(TransactionSystemException)) > 0); + } + [Test] + [ExpectedException(typeof(AopConfigException))] + public void ConstructorArgMustBeAExceptionClass() + { + new NoRollbackRuleAttribute( typeof( StringBuilder ) ); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/RollbackRuleAttributeTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/RollbackRuleAttributeTests.cs new file mode 100644 index 00000000..1bf95286 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/RollbackRuleAttributeTests.cs @@ -0,0 +1,101 @@ +#region License + +/* + * Copyright 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + + +using System; +using System.Data; +using System.Text; +using NUnit.Framework; +using Spring.Aop.Framework; + +namespace Spring.Transaction.Interceptor +{ + [TestFixture] + public class RollbackRuleAttributeTests + { + [Test] + public void FoundImmediatelyWithString() + { + RollbackRuleAttribute rr = new RollbackRuleAttribute("System.Exception"); + Assert.IsTrue(rr.GetDepth(typeof (Exception)) == 0); + } + + [Test] + public void FoundImmediatelyWithClass() + { + RollbackRuleAttribute rr = new RollbackRuleAttribute(typeof (Exception)); + Assert.IsTrue(rr.GetDepth(new Exception()) == 0); + } + + [Test] + public void FoundImmediatelyWithType() + { + RollbackRuleAttribute rr = new RollbackRuleAttribute(typeof (Exception)); + Assert.IsTrue(rr.GetDepth(typeof (Exception)) == 0); + } + + [Test] + public void NotFound() + { + RollbackRuleAttribute rr = new RollbackRuleAttribute("System.Data.DataException"); + Assert.IsTrue(rr.GetDepth(typeof (ApplicationException)) == -1); + } + + [Test] + public void Ancestry() + { + RollbackRuleAttribute rr = new RollbackRuleAttribute("System.Exception"); + Assert.IsTrue(rr.GetDepth(typeof (DataException)) == 2); + } + + [Test] + public void AlwaysTrue() + { + RollbackRuleAttribute rr = new RollbackRuleAttribute("System.Exception"); + Assert.IsTrue(rr.GetDepth(typeof (SystemException)) > 0); + Assert.IsTrue(rr.GetDepth(typeof (ApplicationException)) > 0); + Assert.IsTrue(rr.GetDepth(typeof (DataException)) > 0); + Assert.IsTrue(rr.GetDepth(typeof (TransactionSystemException)) > 0); + } + + [Test] + [ExpectedException(typeof (AopConfigException))] + public void ConstructorArgMustBeAExceptionClass() + { + new RollbackRuleAttribute(typeof (StringBuilder)); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ConstructorArgMustBeAExceptionClassWithNullThrowableType() + { + new RollbackRuleAttribute((Type)null); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ConstructorArgExceptionStringNameVersionWithNull() + { + new RollbackRuleAttribute((String)null); + } + + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/RuleBasedTransactionAttributeTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/RuleBasedTransactionAttributeTests.cs new file mode 100644 index 00000000..9ac6438a --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/RuleBasedTransactionAttributeTests.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections; +using System.Data; +using NUnit.Framework; +using Spring.Collections; + +namespace Spring.Transaction.Interceptor +{ + [TestFixture] + public class RuleBasedTransactionAttributeTests + { + [Test] + public void DefaultRule() + { + RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(); + Assert.IsTrue(rta.RollbackOn(new SystemException())); + //mlp 3/17 changed rollback to rollback on all exceptions. + Assert.IsTrue(rta.RollbackOn(new ApplicationException())); + Assert.IsTrue( rta.RollbackOn(new TransactionSystemException())); + } + [Test] + public void RuleForRollbackOnApplicationException() + { + IList list = new LinkedList(); + list.Add(new RollbackRuleAttribute("Spring.Transaction.TransactionSystemException")); + RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute( TransactionPropagation.Required, list ); + + Assert.IsTrue( rta.RollbackOn(new SystemException())); + //mlp 3/17 changed rollback to rollback on all exceptions. + Assert.IsTrue( rta.RollbackOn(new ApplicationException())); + Assert.IsTrue(( rta.RollbackOn( new TransactionSystemException()))); + } + [Test] + public void RuleForCommitOnUnchecked() + { + IList list = new LinkedList(); + list.Add( new NoRollbackRuleAttribute("System.SystemException")); + list.Add(new RollbackRuleAttribute("Spring.Transaction.TransactionSystemException")); + + RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute( TransactionPropagation.Required, list ); + Assert.IsFalse( rta.RollbackOn(new SystemException())); + Assert.IsTrue( rta.RollbackOn(new TransactionSystemException())); + } + [Test] + public void RuleForSelectiveRollbackOnCheckedWithString() + { + IList list = new LinkedList(); + list.Add( new RollbackRuleAttribute("Spring.Transaction.TransactionSystemException")); + RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute( TransactionPropagation.Required, list ); + ruleForSelectionRollbackOnChecked( rta ); + } + [Test] + public void RuleForSelectiveRollbackOnCheckedWithClass() + { + IList list = new LinkedList(); + list.Add( new RollbackRuleAttribute(typeof(TransactionSystemException))); + RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute( TransactionPropagation.Required, list ); + ruleForSelectionRollbackOnChecked( rta ); + } + private void ruleForSelectionRollbackOnChecked( RuleBasedTransactionAttribute rta ) + { + Assert.IsTrue(rta.RollbackOn(new SystemException())); + Assert.IsTrue( rta.RollbackOn(new TransactionSystemException())); + } + [Test] + public void RuleForCommitOnSubclassOfChecked() + { + IList list = new LinkedList(); + list.Add( new RollbackRuleAttribute("System.Data.DataException")); + list.Add( new NoRollbackRuleAttribute("Spring.Transaction.TransactionSystemException")); + RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute( TransactionPropagation.Required, list ); + + Assert.IsTrue( rta.RollbackOn(new SystemException())); + Assert.IsFalse( rta.RollbackOn(new TransactionSystemException())); + } + [Test] + public void RollbackNever() + { + IList list = new LinkedList(); + list.Add( new NoRollbackRuleAttribute("System.Exception")); + RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute( TransactionPropagation.Required, list ); + + Assert.IsFalse(rta.RollbackOn(new SystemException())); + Assert.IsFalse(rta.RollbackOn(new DataException())); + Assert.IsFalse(rta.RollbackOn(new ApplicationException())); + Assert.IsFalse(rta.RollbackOn(new TransactionSystemException())); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeEditorTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeEditorTests.cs new file mode 100644 index 00000000..c5e87b5a --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeEditorTests.cs @@ -0,0 +1,158 @@ +using System; +using System.Data; +using System.Runtime.Remoting; +using NUnit.Framework; + +namespace Spring.Transaction.Interceptor +{ + [TestFixture] + public class TransactionAttributeEditorTests + { + [Test] + public void NullTest() + { + TransactionAttributeEditor editor = new TransactionAttributeEditor( ); + editor.SetAsText( null ); + ITransactionAttribute ta = editor.Value; + Assert.IsNull( ta ); + } + + [Test] + public void EmptyStringTest() + { + TransactionAttributeEditor editor = new TransactionAttributeEditor( ); + editor.SetAsText( String.Empty ); + ITransactionAttribute ta = editor.Value; + Assert.IsNull( ta ); + } + + [Test] + public void ValidPropagationCodeOnly() + { + TransactionAttributeEditor editor = new TransactionAttributeEditor( ); + editor.SetAsText( "PROPAGATION_REQUIRED" ); + ITransactionAttribute ta = editor.Value; + Assert.IsTrue( ta != null ); + Assert.IsTrue( ta.PropagationBehavior == TransactionPropagation.Required ); + Assert.IsTrue( ta.TransactionIsolationLevel == IsolationLevel.ReadCommitted ); + Assert.IsFalse( ta.ReadOnly ); + } + + [Test] + [ExpectedException( typeof ( ArgumentException ) )] + public void InvalidPropagationCodeOnly() + { + TransactionAttributeEditor editor = new TransactionAttributeEditor( ); + editor.SetAsText( "INVALIDPROPAGATIONCODE" ); + } + + [Test] + public void ValidPropagationCodeAndIsolationCode() + { + TransactionAttributeEditor editor = new TransactionAttributeEditor( ); + editor.SetAsText( "PROPAGATION_REQUIRED, ISOLATION_READUNCOMMITTED" ); + ITransactionAttribute ta = editor.Value; + Assert.IsTrue( ta != null ); + Assert.IsTrue( ta.PropagationBehavior == TransactionPropagation.Required ); + Assert.IsTrue( ta.TransactionIsolationLevel == IsolationLevel.ReadUncommitted ); + } + + [Test] + [ExpectedException( typeof ( ArgumentException ) )] + public void ValidPropagationAndIsolationCodeAndInvalidRollbackRule() + { + TransactionAttributeEditor editor = new TransactionAttributeEditor( ); + editor.SetAsText( "PROPAGATION_REQUIRED,ISOLATION_READUNCOMMITTED,XXX" ); + } + + [Test] + public void ValidPropagationCodeAndIsolationCodeAndRollbackRules1() + { + TransactionAttributeEditor editor = new TransactionAttributeEditor( ); + editor.SetAsText( "PROPAGATION_MANDATORY,ISOLATION_REPEATABLEREAD,timeout_10,-DataException,+RemotingException" ); + ITransactionAttribute ta = editor.Value; + Assert.IsNotNull( ta ); + Assert.IsTrue( ta.PropagationBehavior == TransactionPropagation.Mandatory ); + Assert.IsTrue( ta.TransactionIsolationLevel == IsolationLevel.RepeatableRead ); + Assert.IsTrue( ta.TransactionTimeout == 10 ); + Assert.IsFalse( ta.ReadOnly ); + Assert.IsTrue( ta.RollbackOn( new SystemException( ) ) ); + // Check for our bizarre customized rollback rules + Assert.IsTrue( ta.RollbackOn( new DataException( ) ) ); + Assert.IsTrue( !ta.RollbackOn( new RemotingException( ) ) ); + } + + [Test] + public void ValidPropagationCodeAndIsolationCodeAndRollbackRules2() + { + TransactionAttributeEditor editor = new TransactionAttributeEditor( ); + editor.SetAsText( "+DataException,readOnly,ISOLATION_READCOMMITTED,-RemotingException,PROPAGATION_SUPPORTS" ); + ITransactionAttribute ta = editor.Value; + Assert.IsNotNull( ta ); + Assert.IsTrue( ta.PropagationBehavior == TransactionPropagation.Supports ); + Assert.IsTrue( ta.TransactionIsolationLevel == IsolationLevel.ReadCommitted ); + Assert.IsTrue( ta.TransactionTimeout == -1 ); + Assert.IsTrue( ta.ReadOnly ); + Assert.IsTrue( ta.RollbackOn( new SystemException( ) ) ); + // Check for our bizarre customized rollback rules + Assert.IsFalse( ta.RollbackOn( new DataException( ) ) ); + Assert.IsTrue( ta.RollbackOn( new RemotingException( ) ) ); + } + + [Test] + public void DefaultTransactionAttributeToString() + { + DefaultTransactionAttribute source = new DefaultTransactionAttribute( ); + source.PropagationBehavior = TransactionPropagation.Supports; + source.TransactionIsolationLevel = IsolationLevel.RepeatableRead; + source.TransactionTimeout = 10; + source.ReadOnly = true; + + TransactionAttributeEditor editor = new TransactionAttributeEditor( ); + editor.SetAsText( source.ToString( ) ); + ITransactionAttribute ta = editor.Value; + Assert.AreEqual( source, ta ); + Assert.AreEqual( ta.PropagationBehavior, TransactionPropagation.Supports ); + Assert.AreEqual( ta.TransactionIsolationLevel, IsolationLevel.RepeatableRead ); + Assert.AreEqual( ta.TransactionTimeout, 10 ); + Assert.IsTrue( ta.ReadOnly ); + Assert.IsTrue( ta.RollbackOn( new SystemException( ) ) ); + //mlp 3/17 changed rollback to rollback on all exceptions. + Assert.IsTrue( ta.RollbackOn( new ApplicationException( ) ) ); + + source.TransactionTimeout = 9; + Assert.IsFalse( ta == source ); + source.TransactionTimeout = 10; + Assert.AreEqual( ta, source ); + } + + [Test] + public void RuleBasedTransactionAttributeToString() + { + RuleBasedTransactionAttribute source = new RuleBasedTransactionAttribute(); + source.PropagationBehavior = TransactionPropagation.Supports; + source.TransactionIsolationLevel = IsolationLevel.RepeatableRead; + source.TransactionTimeout = 10; + source.ReadOnly = true; + source.AddRollbackRule( new RollbackRuleAttribute("ArgumentException")); + source.AddRollbackRule( new NoRollbackRuleAttribute("IllegalTransactionStateException")); + + TransactionAttributeEditor editor = new TransactionAttributeEditor(); + editor.SetAsText( source.ToString() ); + ITransactionAttribute ta = editor.Value; + Assert.AreEqual( source, ta ); + Assert.AreEqual( ta.PropagationBehavior, TransactionPropagation.Supports ); + Assert.AreEqual( ta.TransactionIsolationLevel, IsolationLevel.RepeatableRead ); + Assert.AreEqual( ta.TransactionTimeout, 10 ); + Assert.IsTrue( ta.ReadOnly ); + Assert.IsTrue(ta.RollbackOn(new ArgumentException())); + Assert.IsFalse( ta.RollbackOn(new IllegalTransactionStateException())); + + source.ClearRollbackRules(); + Assert.IsFalse( ta == source ); + source.AddRollbackRule( new RollbackRuleAttribute("ArgumentException")); + source.AddRollbackRule( new NoRollbackRuleAttribute("IllegalTransactionStateException")); + Assert.AreEqual( ta, source ); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeSourceAdvisorTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeSourceAdvisorTests.cs new file mode 100644 index 00000000..3adb1c9e --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeSourceAdvisorTests.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Specialized; +using NUnit.Framework; +using Spring.Util; + +namespace Spring.Transaction.Interceptor +{ + [TestFixture] + public class TransactionAttributeSourceAdvisorTests + { + [Test] + public void Serializability() + { + TransactionInterceptor ti = new TransactionInterceptor(); + ti.TransactionAttributes = new NameValueCollection(); + TransactionAttributeSourceAdvisor tas = new TransactionAttributeSourceAdvisor(ti); + SerializationTestUtils.SerializeAndDeserialize(tas); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeSourceEditorTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeSourceEditorTests.cs new file mode 100644 index 00000000..8da00917 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeSourceEditorTests.cs @@ -0,0 +1,66 @@ +using System; +using System.Data; +using System.Reflection; +using NUnit.Framework; + +namespace Spring.Transaction.Interceptor +{ + [TestFixture] + public class TransactionAttributeSourceEditorTests + { + [Test] + public void Null() + { + TransactionAttributeSourceEditor pe = new TransactionAttributeSourceEditor(); + pe.SetAsText(null); + ITransactionAttributeSource tas = pe.Value; + MethodInfo m = typeof(object).GetMethod("GetHashCode"); + Assert.IsTrue(tas.ReturnTransactionAttribute(m, null) == null); + } + [Test] + [ExpectedException(typeof(ArgumentException))] + public void Invalid() + { + TransactionAttributeSourceEditor pe = new TransactionAttributeSourceEditor(); + pe.SetAsText("foo=bar"); + } + [Test] + public void MatchesSpecific() + { + TransactionAttributeSourceEditor pe = new TransactionAttributeSourceEditor(); + pe.SetAsText("System.Object.GetHashCode, Mscorlib =PROPAGATION_REQUIRED\n" + + "System.Object.Equals, Mscorlib =PROPAGATION_MANDATORY\n" + + "System.Object.*pe, Mscorlib=PROPAGATION_SUPPORTS"); + ITransactionAttributeSource tas = pe.Value; + + checkTransactionProperties(tas, typeof(object).GetMethod("GetHashCode"), TransactionPropagation.Required); + checkTransactionProperties(tas, typeof(object).GetMethod("Equals", new Type[] { typeof(object) }),TransactionPropagation.Mandatory); + checkTransactionProperties(tas, typeof(object).GetMethod( "GetType" ), TransactionPropagation.Supports); + checkTransactionProperties(tas, typeof(object).GetMethod("ToString") ); + } + [Test] + public void MatchesAll() + { + TransactionAttributeSourceEditor pe = new TransactionAttributeSourceEditor(); + pe.SetAsText("System.Object.*, Mscorlib=PROPAGATION_REQUIRED"); + ITransactionAttributeSource tas = pe.Value; + + checkTransactionProperties(tas, typeof(object).GetMethod("GetHashCode"), TransactionPropagation.Required); + checkTransactionProperties(tas, typeof(object).GetMethod("Equals", new Type[] { typeof(object) }),TransactionPropagation.Required); + checkTransactionProperties(tas, typeof(object).GetMethod("GetType"), TransactionPropagation.Required); + checkTransactionProperties(tas, typeof(object).GetMethod("ToString"), TransactionPropagation.Required); + } + private void checkTransactionProperties( ITransactionAttributeSource tas, MethodInfo method ) + { + ITransactionAttribute ta = tas.ReturnTransactionAttribute( method, null ); + Assert.IsNull(ta); + } + private void checkTransactionProperties( ITransactionAttributeSource tas, MethodInfo method, TransactionPropagation transactionPropagation ) + { + ITransactionAttribute ta = tas.ReturnTransactionAttribute( method, null ); + Assert.IsTrue( ta != null ); + Assert.IsTrue( ta.TransactionIsolationLevel == IsolationLevel.ReadCommitted ); + Assert.IsTrue( ta.PropagationBehavior == transactionPropagation); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeSourceTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeSourceTests.cs new file mode 100644 index 00000000..d65bdd59 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionAttributeSourceTests.cs @@ -0,0 +1,128 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Collections.Specialized; +using NUnit.Framework; + +#endregion + +namespace Spring.Transaction.Interceptor +{ + /// + /// This class contains tests for the various + /// implementations. + /// + /// Mark Pollack + /// $Id: TransactionAttributeSourceTests.cs,v 1.1 2008/05/27 19:07:03 markpollack Exp $ + [TestFixture] + public class TransactionAttributeSourceTests + { + [Test] + public void MethodMapTransactionAttributeSource() + { + MethodMapTransactionAttributeSource tas = new MethodMapTransactionAttributeSource(); + IDictionary methodMap = new Hashtable(); + methodMap.Add("System.Object.GetHashCode, mscorlib", "PROPAGATION_REQUIRED"); + methodMap.Add("System.Object.ToString, mscorlib", + new DefaultTransactionAttribute(TransactionPropagation.Supports)); + tas.MethodMap = methodMap; + + ITransactionAttribute ta = tas.ReturnTransactionAttribute(typeof(object).GetMethod("GetHashCode"), null); + Assert.IsNotNull(ta); + Assert.AreEqual(TransactionPropagation.Required, ta.PropagationBehavior); + + ta = tas.ReturnTransactionAttribute(typeof(object).GetMethod("ToString"), null); + Assert.IsNotNull(ta); + Assert.AreEqual(TransactionPropagation.Supports, ta.PropagationBehavior); + } + + + /// + /// Test that configuration of the IDictionary baesd map supports transaction attribute information + /// set via object (i.e. DefaultTransactionAttribute) as well as strings. + /// + [Test] + public void NameMatchTransactionAttributeSource() + { + NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource(); + IDictionary methodMap = new Hashtable(); + methodMap.Add("GetHashCode", "PROPAGATION_REQUIRED"); + methodMap.Add("ToString", new DefaultTransactionAttribute(TransactionPropagation.Supports)); + tas.NameMap = methodMap; + ITransactionAttribute ta = tas.ReturnTransactionAttribute(typeof (object).GetMethod("GetHashCode"), null); + Assert.IsNotNull(ta); + Assert.AreEqual(TransactionPropagation.Required, ta.PropagationBehavior); + ta = tas.ReturnTransactionAttribute(typeof (object).GetMethod("ToString"), null); + Assert.IsNotNull(ta); + Assert.AreEqual(TransactionPropagation.Supports, ta.PropagationBehavior); + } + + [Test] + public void NameMatchTransactionAttributeSourceWithStarAtStartOfMethodName() + { + NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource(); + NameValueCollection attributes = new NameValueCollection(); + attributes.Add("*ashCode", "PROPAGATION_REQUIRED"); + tas.NameProperties = attributes; + ITransactionAttribute ta = tas.ReturnTransactionAttribute(typeof (object).GetMethod("GetHashCode"), null); + Assert.IsNotNull(ta); + Assert.AreEqual(TransactionPropagation.Required, ta.PropagationBehavior); + } + + [Test] + public void NameMatchTransactionAttributeSourceWithStarAtEndOfMethodName() + { + NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource(); + NameValueCollection attributes = new NameValueCollection(); + attributes.Add("GetHashCod*", "PROPAGATION_REQUIRED"); + tas.NameProperties = attributes; + ITransactionAttribute ta = tas.ReturnTransactionAttribute(typeof(object).GetMethod("GetHashCode"), null); + Assert.IsNotNull(ta); + Assert.AreEqual(TransactionPropagation.Required, ta.PropagationBehavior); + } + + [Test] + public void NameMatchTransactionAttributeSourceMostSpecificMethodNameIsDefinitelyMatched() + { + NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource(); + NameValueCollection attributes = new NameValueCollection(); + attributes.Add("*", "PROPAGATION_REQUIRED"); + attributes.Add("GetHashCode", "PROPAGATION_MANDATORY"); + tas.NameProperties = attributes; + ITransactionAttribute ta = tas.ReturnTransactionAttribute(typeof(object).GetMethod("GetHashCode"), null); + Assert.IsNotNull(ta); + Assert.AreEqual(TransactionPropagation.Mandatory, ta.PropagationBehavior); + } + + [Test] + public void NameMatchTransactionAttributeSourceWithEmptyMethodName() + { + NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource(); + NameValueCollection attributes = new NameValueCollection(); + attributes.Add("", "PROPAGATION_MANDATORY"); + tas.NameProperties = attributes; + ITransactionAttribute ta = tas.ReturnTransactionAttribute(typeof(object).GetMethod("GetHashCode"), null); + Assert.IsNull(ta); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionInterceptorTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionInterceptorTests.cs new file mode 100644 index 00000000..65ce4dfb --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Interceptor/TransactionInterceptorTests.cs @@ -0,0 +1,53 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Aop.Framework; + +#endregion + +namespace Spring.Transaction.Interceptor +{ + /// + /// This class contains mock objects tests the TransactionInterceptor + /// + /// Mark Pollack + /// $Id: TransactionInterceptorTests.cs,v 1.3 2007/10/21 18:16:58 markpollack Exp $ + [TestFixture] + public class TransactionInterceptorTests : AbstractTransactionAspectTests + { + + + protected override object Advised(object target, IPlatformTransactionManager ptm, + ITransactionAttributeSource tas) + { + TransactionInterceptor ti = new TransactionInterceptor(); + ti.TransactionManager = ptm; + Assert.AreEqual(ptm, ti.TransactionManager); + ti.TransactionAttributeSource = tas; + Assert.AreEqual(tas, ti.TransactionAttributeSource); + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(0, ti); + return pf.GetProxy(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Data.Tests/Transaction/InvalidTimeoutExceptionTests.cs b/test/Spring/Spring.Data.Tests/Transaction/InvalidTimeoutExceptionTests.cs new file mode 100644 index 00000000..2179fae9 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/InvalidTimeoutExceptionTests.cs @@ -0,0 +1,17 @@ +using System; +using Spring.Transaction; +using NUnit.Framework; + +namespace Spring.Transaction +{ + [TestFixture] + public class InvalidTimeoutExceptionTests + { + [Test] + public void TimeoutGetter() + { + InvalidTimeoutException ex = new InvalidTimeoutException( "bad timeout", 2000 ); + Assert.IsTrue( 2000 == ex.Timeout ); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Support/AbstractPlatformTransactionManagerTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Support/AbstractPlatformTransactionManagerTests.cs new file mode 100644 index 00000000..1a01a600 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Support/AbstractPlatformTransactionManagerTests.cs @@ -0,0 +1,220 @@ +using System; +using NUnit.Framework; + +namespace Spring.Transaction.Support +{ + [TestFixture] + public class AbstractPlatformTransactionManagerTests + { + private MockTxnPlatformMgrAbstract _mockTxnMgr; + + [SetUp] + public void Init() + { + _mockTxnMgr = new MockTxnPlatformMgrAbstract(); + if ( TransactionSynchronizationManager.SynchronizationActive ) + { + TransactionSynchronizationManager.ClearSynchronization(); + } + } + [TearDown] + public void Destroy() + { + _mockTxnMgr.Verify(); + _mockTxnMgr = null; + if ( TransactionSynchronizationManager.SynchronizationActive ) + { + TransactionSynchronizationManager.ClearSynchronization(); + } + } + [Test] + public void VanillaProperties() + { + Assert.AreEqual( TransactionSynchronizationState.Always, _mockTxnMgr.TransactionSynchronization); + Assert.IsTrue(!_mockTxnMgr.NestedTransactionsAllowed); + Assert.IsTrue(!_mockTxnMgr.RollbackOnCommitFailure); + + _mockTxnMgr.NestedTransactionsAllowed = true; + _mockTxnMgr.RollbackOnCommitFailure = true; + _mockTxnMgr.TransactionSynchronization = TransactionSynchronizationState.OnActualTransaction; + + Assert.AreEqual( TransactionSynchronizationState.OnActualTransaction, _mockTxnMgr.TransactionSynchronization); + Assert.IsTrue(_mockTxnMgr.NestedTransactionsAllowed); + Assert.IsTrue(_mockTxnMgr.RollbackOnCommitFailure); + } + [Test] + [ExpectedException(typeof(InvalidTimeoutException), "Invalid transaction timeout")] + public void DefinitionInvalidTimeoutException() + { + MockTxnDefinition def = new MockTxnDefinition(); + def.TransactionTimeout = -1000; + _mockTxnMgr.GetTransaction( def ); + } + [Test] + [ExpectedException(typeof(IllegalTransactionStateException), "Transaction propagation 'mandatory' but no existing transaction found")] + public void DefinitionInvalidPropagationState() + { + MockTxnDefinition def = new MockTxnDefinition(); + def.PropagationBehavior = TransactionPropagation.Mandatory; + _mockTxnMgr.GetTransaction( def ); + } + + [Test] + [ExpectedException(typeof(IllegalTransactionStateException), "Transaction propagation 'never' but existing transaction found.")] + public void NeverPropagateState() + { + MockTxnDefinition def = new MockTxnDefinition(); + def.PropagationBehavior = TransactionPropagation.Never; + setGeneralGetTransactionExpectations(); + _mockTxnMgr.GetTransaction(def); + } + [Test] + [ExpectedException(typeof(NestedTransactionNotSupportedException), "Transaction manager does not allow nested transactions by default - specify 'NestedTransactionsAllowed' property with value 'true'")] + public void NoNestedTransactionsAllowed() + { + MockTxnDefinition def = new MockTxnDefinition(); + def.PropagationBehavior = TransactionPropagation.Nested; + setGeneralGetTransactionExpectations(); + _mockTxnMgr.GetTransaction(def); + } + [Test] + public void TransactionSuspendedSuccessfully() + { + MockTxnDefinition def = new MockTxnDefinition(); + def.PropagationBehavior = TransactionPropagation.NotSupported; + def.ReadOnly = false; + setGeneralGetTransactionExpectations(); + + DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def); + Assert.IsNull( status.Transaction ); + Assert.IsTrue( !status.IsNewTransaction ); + Assert.IsTrue( status.NewSynchronization ); + Assert.IsTrue( !status.ReadOnly ); + Assert.IsNotNull( status.SuspendedResources); + } + [Test] + public void TransactionCreatedSuccessfully() + { + MockTxnDefinition def = new MockTxnDefinition(); + def.PropagationBehavior = TransactionPropagation.RequiresNew; + def.ReadOnly = false; + + setGeneralGetTransactionExpectations(); + + DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def); + Assert.AreEqual( _mockTxnMgr.Transaction, status.Transaction ); + Assert.IsTrue( status.IsNewTransaction ); + Assert.IsTrue( status.NewSynchronization ); + Assert.IsTrue( !status.ReadOnly ); + Assert.IsNotNull( status.SuspendedResources); + } + [Test] + public void NestedTransactionSuccessfully() + { + MockTxnDefinition def = new MockTxnDefinition(); + def.PropagationBehavior = TransactionPropagation.Nested; + def.ReadOnly = false; + + setGeneralGetTransactionExpectations(); + _mockTxnMgr.SetExpectedCalls( "DoBegin", 1 ); + _mockTxnMgr.Savepoints = false; + _mockTxnMgr.NestedTransactionsAllowed = true; + + DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def); + Assert.AreEqual( _mockTxnMgr.Transaction, status.Transaction ); + Assert.IsTrue( status.IsNewTransaction ); + Assert.AreEqual( true, status.NewSynchronization ); + Assert.IsTrue( !status.ReadOnly ); + Assert.IsNull( status.SuspendedResources); + } + + [Test] + public void NestedTransactionWithSavepoint() + { + MockTxnDefinition def = new MockTxnDefinition(); + def.PropagationBehavior = TransactionPropagation.Nested; + def.ReadOnly = false; + setVanillaGetTransactionExpectations(); + _mockTxnMgr.SetTransaction( new MyMockTxnObjectSavepointMgr()); + _mockTxnMgr.SetExpectedCalls("DoBegin", 0); + _mockTxnMgr.Savepoints = true; + _mockTxnMgr.NestedTransactionsAllowed = true; + + DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def); + Assert.AreEqual( _mockTxnMgr.Transaction, status.Transaction ); + Assert.IsFalse( status.IsNewTransaction ); + Assert.IsFalse( status.NewSynchronization ); + Assert.IsTrue( !status.ReadOnly ); + Assert.IsNull( status.SuspendedResources); + } + [Test] + public void DefaultPropagationBehavior() + { + MockTxnDefinition def = new MockTxnDefinition(); + def.PropagationBehavior = TransactionPropagation.Required; + def.ReadOnly = true; + setGeneralGetTransactionExpectations(); + + DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def); + Assert.AreEqual( _mockTxnMgr.Transaction, status.Transaction ); + Assert.IsTrue( !status.IsNewTransaction ); + Assert.IsTrue( status.NewSynchronization ); + Assert.IsTrue( status.ReadOnly ); + Assert.IsNull( status.SuspendedResources); + } + [Test] + public void DefaultPropagationBehaviorWithNullDefinition() + { + setGeneralGetTransactionExpectations(); + + DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(null); + Assert.AreEqual( _mockTxnMgr.Transaction, status.Transaction ); + Assert.IsTrue( !status.IsNewTransaction ); + Assert.IsTrue( status.NewSynchronization ); + Assert.IsTrue( !status.ReadOnly ); + Assert.IsNull( status.SuspendedResources); + } + [Test] + public void DefaultNoExistingTransaction() + { + setVanillaGetTransactionExpectations(); + _mockTxnMgr.SetExpectedCalls( "DoBegin", 1 ); + + DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(null); + Assert.IsNotNull( status.Transaction ); + Assert.IsTrue( status.IsNewTransaction ); + Assert.IsTrue( status.NewSynchronization ); + Assert.IsTrue( !status.ReadOnly ); + Assert.IsNull( status.SuspendedResources); + } + [Test] + public void DefaultBehaviorDefaultPropagationNoExistingTransaction() + { + setVanillaGetTransactionExpectations(); + _mockTxnMgr.SetExpectedCalls( "DoBegin", 0 ); + + MockTxnDefinition def = new MockTxnDefinition(); + def.PropagationBehavior = TransactionPropagation.Never; + def.ReadOnly = true; + + DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def); + Assert.IsNull( status.Transaction ); + Assert.IsTrue( !status.IsNewTransaction ); + Assert.IsTrue( status.NewSynchronization ); + Assert.IsTrue( status.ReadOnly ); + Assert.IsNull( status.SuspendedResources); + } + private void setGeneralGetTransactionExpectations() + { + _mockTxnMgr.SetTransaction( new object() ); + setVanillaGetTransactionExpectations(); + } + + private void setVanillaGetTransactionExpectations() + { + _mockTxnMgr.SetExpectedCalls( "DoGetTransaction", 1); + _mockTxnMgr.SetExpectedCalls( "IsExistingTransaction", 1); + } + + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Support/DefaultTransactionDefinitionTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Support/DefaultTransactionDefinitionTests.cs new file mode 100644 index 00000000..277f3253 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Support/DefaultTransactionDefinitionTests.cs @@ -0,0 +1,65 @@ +using System; +using System.Data; +using NUnit.Framework; + +namespace Spring.Transaction.Support +{ + [TestFixture] + public class DefaultTransactionDefinitionTests + { + [Test] + public void VanillaTest() + { + DefaultTransactionDefinition def = new DefaultTransactionDefinition( TransactionPropagation.NotSupported); + Assert.IsTrue( def.PropagationBehavior == TransactionPropagation.NotSupported ); + def.PropagationBehavior = TransactionPropagation.Nested; + Assert.IsTrue( def.PropagationBehavior == TransactionPropagation.Nested ); + def.TransactionIsolationLevel = IsolationLevel.ReadCommitted; + Assert.IsTrue( def.TransactionIsolationLevel == IsolationLevel.ReadCommitted); + def.TransactionTimeout = 1000; + Assert.IsTrue( 1000 == def.TransactionTimeout ); + Assert.IsTrue( false == def.ReadOnly ); + def.ReadOnly = true; + Assert.IsTrue( true == def.ReadOnly ); + } + [Test] + public void IsolationLeveNonDefaultl() + { + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + Assert.IsTrue( def.PropagationBehavior == TransactionPropagation.Required ); + } + [Test] + [ExpectedException(typeof(ArgumentException))] + public void InvalidTimeout() + { + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + def.TransactionTimeout = -1000; + } + [Test] + public void DefinitionString() + { + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + DefaultTransactionDefinition def2 = new DefaultTransactionDefinition(); + + Assert.IsTrue( def.ToString() == def2.ToString()); + def.Equals(def2); + } + [Test] + public void DefinitionStringFilled() + { + DefaultTransactionDefinition def = new DefaultTransactionDefinition( TransactionPropagation.Never ); + def.TransactionIsolationLevel = IsolationLevel.Chaos; + def.TransactionTimeout = 1000; + def.ReadOnly = true; + Assert.AreEqual( "PROPAGATION_Never,ISOLATION_Chaos,timeout_1000,readOnly", def.ToString()); + + DefaultTransactionDefinition def2 = new DefaultTransactionDefinition( TransactionPropagation.Never ); + def2.TransactionIsolationLevel = IsolationLevel.Chaos; + def2.TransactionTimeout = 1000; + def2.ReadOnly = true; + + Assert.IsTrue( def2.Equals(def)); + Assert.IsTrue( def.GetHashCode() == def2.GetHashCode()); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Support/DefaultTransactionStatusTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Support/DefaultTransactionStatusTests.cs new file mode 100644 index 00000000..37b40211 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Support/DefaultTransactionStatusTests.cs @@ -0,0 +1,104 @@ +using System; +using NUnit.Framework; +using Spring.Transaction; + +namespace Spring.Transaction.Support +{ + [TestFixture] + public class DefaultTransactionStatusTests + { + [Test] + public void DefaultConstructorTests() + { + MyMockTxnObject txn = new MyMockTxnObject(); + txn.SetExpectedIsRollBackOnlyValue( false ); + txn.SetExpectedRollbackOnlyCalls( 1 ); + + DefaultTransactionStatus stat = new DefaultTransactionStatus( txn, true, false, false, true, new object() ); + Assert.IsNotNull( stat.Transaction ); + Assert.IsTrue( !stat.ReadOnly ); + Assert.IsTrue( !stat.NewSynchronization ); + Assert.IsNotNull( stat.SuspendedResources ); + Assert.IsTrue( stat.IsNewTransaction ); + Assert.IsTrue( ! stat.RollbackOnly ); + stat.RollbackOnly = true; + Assert.IsTrue( stat.RollbackOnly ); + txn.Verify(); + } + [Test] + [ExpectedException(typeof(NestedTransactionNotSupportedException))] + public void CreateSavepointException() + { + DefaultTransactionStatus stat = new DefaultTransactionStatus( new MyMockTxnObject(), true, false, false, true, new object() ); + stat.CreateSavepoint( "mySavePoint" ); + } + [Test] + [ExpectedException(typeof(NestedTransactionNotSupportedException))] + public void RollbackSavepointException() + { + DefaultTransactionStatus stat = new DefaultTransactionStatus( new MyMockTxnObject(), true, false, false, true, new object() ); + stat.RollbackToSavepoint(null); + } + [Test] + [ExpectedException(typeof(NestedTransactionNotSupportedException))] + public void ReleaseSavepointException() + { + DefaultTransactionStatus stat = new DefaultTransactionStatus( new MyMockTxnObject(), true, false, false, true, new object() ); + stat.ReleaseSavepoint(null); + } + [Test] + public void CreateSaveAndHoldValidSavepoint() + { + MyMockTxnObjectSavepointMgr saveMgr = new MyMockTxnObjectSavepointMgr(); + saveMgr.SetSavepointToReturn( "savepoint" ); + DefaultTransactionStatus status = new DefaultTransactionStatus( saveMgr , true, false, false, true, new object()); + status.CreateAndHoldSavepoint( "savepoint" ); + Assert.IsTrue( status.HasSavepoint ); + Assert.AreEqual( "savepoint", status.Savepoint ); + } + [Test] + [ExpectedException(typeof(TransactionUsageException))] + public void RollbackHeldSavepointException() + { + DefaultTransactionStatus stat = new DefaultTransactionStatus( new MyMockTxnObject(), true, false, false, true, new object() ); + stat.RollbackToHeldSavepoint(); + } + [Test] + public void RollbackHeldSavepointSuccess() + { + MyMockTxnObjectSavepointMgr saveMgr = new MyMockTxnObjectSavepointMgr(); + string savepoint = "savepoint"; + saveMgr.SetExpectedSavepoint( savepoint ); + saveMgr.SetSavepointToReturn( savepoint ); + DefaultTransactionStatus status = new DefaultTransactionStatus( saveMgr , true, false, false, true, new object()); + status.CreateAndHoldSavepoint( savepoint ); + Assert.IsTrue( status.HasSavepoint ); + Assert.AreEqual( savepoint, status.Savepoint ); + + status.RollbackToHeldSavepoint(); + saveMgr.Verify(); + } + [Test] + [ExpectedException(typeof(TransactionUsageException))] + public void ReleaseHeldSavepointException() + { + DefaultTransactionStatus stat = new DefaultTransactionStatus( new MyMockTxnObject(), true, false, false, true, new object() ); + stat.ReleaseHeldSavepoint(); + } + [Test] + public void ReleaseHeldSavepointSuccess() + { + MyMockTxnObjectSavepointMgr saveMgr = new MyMockTxnObjectSavepointMgr(); + string savepoint = "savepoint"; + saveMgr.SetExpectedSavepoint( savepoint ); + saveMgr.SetSavepointToReturn( savepoint ); + DefaultTransactionStatus status = new DefaultTransactionStatus( saveMgr , true, false, false, true, new object()); + status.CreateAndHoldSavepoint( savepoint ); + Assert.IsTrue( status.HasSavepoint ); + Assert.AreEqual( savepoint, status.Savepoint ); + + status.ReleaseHeldSavepoint(); + saveMgr.Verify(); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Support/ResourceHolderSupportTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Support/ResourceHolderSupportTests.cs new file mode 100644 index 00000000..4c47fea0 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Support/ResourceHolderSupportTests.cs @@ -0,0 +1,75 @@ +using System; +using NUnit.Framework; + +namespace Spring.Transaction.Support +{ + [TestFixture] + public class ResourceHolderSupportTests : ResourceHolderSupport + { + [TearDown] + public void Destroy() + { + Clear(); + } + [Test] + public void PropertiesTest() + { + Assert.AreEqual( DateTime.MinValue, Deadline); + Assert.IsTrue( !SynchronizedWithTransaction ); + Assert.IsTrue( !RollbackOnly); + + //TODO investigate + /* + Deadline = DateTime.Now.AddDays(2); + Assert.IsTrue( HasDeadline ); + Assert.AreEqual( DateTime.Now.AddDays(2).Date, Deadline.Date ); + */ + + SynchronizedWithTransaction = true; + Assert.IsTrue( SynchronizedWithTransaction ); + + RollbackOnly = true; + Assert.IsTrue( RollbackOnly); + } + [Test] + [ExpectedException(typeof(ArgumentException))] + public void InvalidDeadline() + { + int time = TimeToLiveInSeconds; + } + + /* + [Ignore("investigate...")] + [Test] + public void ZeroMilliseconds() + { + Deadline = DateTime.Now.AddDays(-1); + Assert.AreEqual( 0, TimeToLiveInMilliseconds ); + } + */ + /* + [Test] + public void MillisecondToLive() + { + Deadline = DateTime.Now.AddDays(1); + Assert.IsTrue( TimeToLiveInMilliseconds >= 0); + } + */ + /* + [Test] + public void SetDeadline() + { + SetDeadlineInSeconds( 10 ); + Assert.IsTrue( TimeToLiveInSeconds >= 0); + } + */ + /* + [Test] + public void SetDeadlineMillis() + { + SetDeadlineInMilliseconds(1000); + Assert.IsTrue( TimeToLiveInMilliseconds >= 0); + } + */ + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Support/TransactionSynchronizationAdapterTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Support/TransactionSynchronizationAdapterTests.cs new file mode 100644 index 00000000..8152c9c1 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Support/TransactionSynchronizationAdapterTests.cs @@ -0,0 +1,19 @@ +using System; +using NUnit.Framework; + +namespace Spring.Transaction.Support +{ + [TestFixture] + public class TransactionSynchronizationAdapterTests : TransactionSynchronizationAdapter + { + [Test] + public void CoverageTests() + { + AfterCompletion( TransactionSynchronizationStatus.Committed ); + BeforeCommit(false); + BeforeCompletion(); + Resume(); + Suspend(); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Support/TransactionSynchronizationManagerTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Support/TransactionSynchronizationManagerTests.cs new file mode 100644 index 00000000..bb9c96b1 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Support/TransactionSynchronizationManagerTests.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using NUnit.Framework; + +namespace Spring.Transaction.Support +{ + [TestFixture] + public class TransactionSynchronizationManagerTests + { + [SetUp] + public void Init() + { + if ( TransactionSynchronizationManager.SynchronizationActive ) + { + TransactionSynchronizationManager.ClearSynchronization(); + } + } + [TearDown] + public void Destory() + { + if ( TransactionSynchronizationManager.SynchronizationActive ) + { + TransactionSynchronizationManager.ClearSynchronization(); + } + } + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void SynchronizationsInvalid() + { + IList syncs = TransactionSynchronizationManager.Synchronizations; + } + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void InitSynchronizationsInvalid() + { + TransactionSynchronizationManager.InitSynchronization(); + TransactionSynchronizationManager.InitSynchronization(); + } + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void SynchronizationsClearInvalid() + { + TransactionSynchronizationManager.ClearSynchronization(); + } + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void RegisterSyncsInvalid() + { + TransactionSynchronizationManager.RegisterSynchronization(new MockTxnSync()); + } + [Test] + public void SynchronizationsLifeCycle() + { + TransactionSynchronizationManager.InitSynchronization(); + IList syncs = TransactionSynchronizationManager.Synchronizations; + Assert.AreEqual( 0, syncs.Count ); + TransactionSynchronizationManager.RegisterSynchronization( new MockTxnSync() ); + syncs = TransactionSynchronizationManager.Synchronizations; + Assert.AreEqual( 1, syncs.Count ); + TransactionSynchronizationManager.ClearSynchronization(); + } + } +} diff --git a/test/Spring/Spring.Data.Tests/Transaction/Support/TransactionTemplateTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Support/TransactionTemplateTests.cs new file mode 100644 index 00000000..c9f2e536 --- /dev/null +++ b/test/Spring/Spring.Data.Tests/Transaction/Support/TransactionTemplateTests.cs @@ -0,0 +1,80 @@ +using System; +using NUnit.Framework; + +namespace Spring.Transaction.Support +{ + [TestFixture] + public class TransactionTemplateTests + { + [Test] + [ExpectedException(typeof(ArgumentException))] + public void NoTxnMgr() + { + TransactionTemplate temp = new TransactionTemplate(); + temp.AfterPropertiesSet(); + } + [Test] + public void TxnMgr() + { + TransactionTemplate temp = new TransactionTemplate(); + temp.PlatformTransactionManager = new MockTxnPlatformMgr(); + temp.AfterPropertiesSet(); + } + [Test] + public void ExecuteException() + { + MockTxnPlatformMgr mock = new MockTxnPlatformMgr(); + mock.SetExpectedGetTxnCallCount(1); + mock.SetExpectedRollbackCallCount(1); + TransactionTemplate temp = new TransactionTemplate(mock); + try + { + temp.Execute(new TransactionDelegate(DummyExceptionMethod)); + Assert.Fail("Should throw exception"); + } catch + { + + } + mock.Verify(); + } + [Test] + public void ExecuteExceptionRollbackException() + { + MockTxnPlatformMgr mock = new MockTxnPlatformMgr(); + mock.SetExpectedGetTxnCallCount(1); + mock.SetExpectedRollbackCallCount(1); + mock.ThrowRollbackException = true; + TransactionTemplate temp = new TransactionTemplate(mock); + try + { + temp.Execute(new TransactionDelegate(DummyExceptionMethod)); + Assert.Fail("Should throw exception"); + } + catch + { + + } + mock.Verify(); + } + [Test] + public void NullResult() + { + MockTxnPlatformMgr mock = new MockTxnPlatformMgr(); + mock.SetExpectedGetTxnCallCount(1); + mock.SetExpectedCommitCallCount(1); + TransactionTemplate temp = new TransactionTemplate(mock); + temp.AfterPropertiesSet(); + Assert.AreEqual( mock, temp.PlatformTransactionManager); + Assert.IsNull( temp.Execute(new TransactionDelegate(DummyTransactionMethod) ) ); + mock.Verify(); + } + public object DummyTransactionMethod( ITransactionStatus status ) + { + return status; + } + public object DummyExceptionMethod( ITransactionStatus status ) + { + throw new Exception("Bad Error"); + } + } +} diff --git a/test/Spring/Spring.Messaging.Nms.Tests/AssemblyInfo.cs b/test/Spring/Spring.Messaging.Nms.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..177a4f0e --- /dev/null +++ b/test/Spring/Spring.Messaging.Nms.Tests/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.2003.csproj b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.2003.csproj new file mode 100644 index 00000000..f08c4c0e --- /dev/null +++ b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.2003.csproj @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.2005.csproj b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.2005.csproj new file mode 100644 index 00000000..0ad153e4 --- /dev/null +++ b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.2005.csproj @@ -0,0 +1,60 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {FA7A6931-7DBE-4A32-A312-51FAD2E80332} + Library + Properties + Spring + Spring.Messaging.Nms.Tests + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\lib\net\2.0\antlr.runtime.dll + + + False + ..\..\..\lib\net\2.0\nunit.framework.dll + + + + + + + + {AEB1578C-9018-4D49-B440-789F38DD2F29} + Spring.Messaging.Nms.2005 + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.build b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.build new file mode 100644 index 00000000..12cdae9b --- /dev/null +++ b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.build @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/AdaptableJobFactoryTest.cs b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/AdaptableJobFactoryTest.cs new file mode 100644 index 00000000..d6e9a38f --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/AdaptableJobFactoryTest.cs @@ -0,0 +1,93 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System; + +using NUnit.Framework; + +using Quartz; +using Quartz.Job; +using Quartz.Spi; + +using Spring.Scheduling.Quartz; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Tests for . + /// + /// Marko Lahma (.NET) + [TestFixture] + public class AdaptableJobFactoryTest + { + private AdaptableJobFactory jobFactory; + + [SetUp] + public void SetUp() + { + jobFactory = new AdaptableJobFactory(); + } + + [Test] + public void TestNewJob_IncompatibleJob() + { + try + { + // this actually fails already in Quartz level + TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof(object)); + jobFactory.NewJob(bundle); + Assert.Fail("Created job which was not an IJob"); + } + catch (ArgumentException) + { + // ok + } + catch (SchedulerException) + { + // ok + } + catch (Exception) + { + Assert.Fail("Got exception that was not instance of SchedulerException or ArgumentException"); + } + } + + [Test] + public void TestNewJob_ThreadStartJob() + { + // TODO ThreadStart is not the way to go + } + + [Test] + public void TestNewJob_NormalIJob() + { + TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof(NoOpJob)); + IJob job = jobFactory.NewJob(bundle); + Assert.IsNotNull(job, "Returned job was null"); + } + + + + + } + + internal class NoOpThreadStartJob : NoOpJob + { + public void Execute() + { + Execute(null); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/CronTriggerObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/CronTriggerObjectTest.cs new file mode 100644 index 00000000..52d07ecc --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/CronTriggerObjectTest.cs @@ -0,0 +1,92 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; + +using NUnit.Framework; + +using Quartz; +using Quartz.Job; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Tests for . + /// + /// Marko Lahma (.NET) + [TestFixture] + public class CronTriggerObjectTest : TriggerObjectTest + { + private CronTriggerObject cronTrigger; + + [SetUp] + public void SetUp() + { + cronTrigger = new CronTriggerObject(); + cronTrigger.ObjectName = TRIGGER_NAME; + Trigger = cronTrigger; + } + + /// + /// Tests all possible misfire instructions for cron trigger + /// from strings to int. + /// + [Test] + public void TestMisfireInstructionNames() + { + string[] names = new string[] { "DoNothing", "FireOnceNow", "SmartPolicy" }; + foreach (string name in names) + { + cronTrigger.MisfireInstructionName = name; + } + } + + [Test] + public override void TestAfterPropertiesSet_Defaults() + { + cronTrigger.AfterPropertiesSet(); + base.TestAfterPropertiesSet_Defaults(); + AssertDateTimesEqualityWithAllowedDelta(DateTime.UtcNow, cronTrigger.StartTimeUtc, 1000); + Assert.AreEqual(TimeZone.CurrentTimeZone, cronTrigger.TimeZone, "trigger time zone mismatch"); + } + + [Test] + public override void TestAfterPropertiesSet_ValuesGiven() + { + TimeZone TZ = TimeZone.CurrentTimeZone; + cronTrigger.TimeZone = TZ; + cronTrigger.AfterPropertiesSet(); + base.TestAfterPropertiesSet_ValuesGiven(); + Assert.AreSame(TZ, cronTrigger.TimeZone, "trigger time zone mismatch"); + } + + + [Test] + public override void TestAfterPropertiesSet_JobDetailGiven() + { + const string jobName = "jobName"; + const string jobGroup = "jobGroup"; + JobDetail jd = new JobDetail(jobName, jobGroup, typeof (NoOpJob)); + cronTrigger.JobDetail = jd; + cronTrigger.AfterPropertiesSet(); + base.TestAfterPropertiesSet_JobDetailGiven(); + Assert.AreSame(jd, cronTrigger.JobDetail, "job details weren't same"); + } + + + } + +} diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/JobDetailObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/JobDetailObjectTest.cs new file mode 100644 index 00000000..a3e82eda --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/JobDetailObjectTest.cs @@ -0,0 +1,132 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Collections; + +using NUnit.Framework; + +using Quartz; +using Quartz.Job; + +using Spring.Context.Support; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Tests for . + /// + /// Marko Lahma (.NET) + [TestFixture] + public class JobDetailObjectTest + { + private JobDetailObject jobDetail; + + [SetUp] + public void SetUp() + { + jobDetail = new JobDetailObject(); + } + + [Test] + [ExpectedException(ExceptionType = typeof(ArgumentException))] + public void TestJobType_Null() + { + jobDetail.JobType = null; + } + + [Test] + public void TestJobType_NonIJob() + { + jobDetail.JobType = typeof(object); + Assert.AreEqual(typeof(object), jobDetail.JobType, "JobDetail did not create same type as expected"); + } + + [Test] + public void TestJobType_IJob() + { + Type CORRECT_IJOB = typeof (NoOpJob); + jobDetail.JobType = CORRECT_IJOB; + Assert.AreEqual(jobDetail.JobType, CORRECT_IJOB, "JobDetail did not register correct job type"); + } + + + [Test] + [ExpectedException(ExceptionType = typeof(ArgumentException))] + public void TestJobDataAsMap_Null() + { + jobDetail.JobDataAsMap = null; + } + + [Test] + public void TestJobDataAsMap_ProperValues() + { + IDictionary values = new Hashtable(); + values["baz"] = "foo"; + values["foo"] = 123; + values["bar"] = null; + jobDetail.JobDataAsMap = values; + Assert.AreEqual(values.Count, jobDetail.JobDataMap.Count, "Data of inequal size"); + CollectionAssert.AreEqual(values.Keys, jobDetail.JobDataMap.Keys, "JobDataMap values not equal"); + } + + [Test] + public void TestAfterPropertiesSet_Defaults() + { + const string objectName = "springJobDetailObject"; + jobDetail.ObjectName = objectName; + jobDetail.Group = null; + jobDetail.AfterPropertiesSet(); + Assert.AreEqual(SchedulerConstants.DEFAULT_GROUP, jobDetail.Group, "Groups differ"); + Assert.AreEqual(objectName, jobDetail.Name, "Names differ"); + } + + [Test] + public void TestAfterPropertiesSet_CustomNameAndGroup() + { + const string objectName = "springJobDetailObject"; + const string jobDetailName = "jobDetailName"; + const string jobDetailGroup = "jobDetailGroup"; + jobDetail.ObjectName = objectName; + jobDetail.Name = jobDetailName; + jobDetail.Group = jobDetailGroup; + jobDetail.AfterPropertiesSet(); + Assert.AreEqual(jobDetailGroup, jobDetail.Group, "Groups differ"); + Assert.AreEqual(jobDetailName, jobDetail.Name, "Names differ"); + } + + [Test] + public void TestAfterPropertiesSet_ApplicationContextJobDataKeySetWithApplicationContext() + { + const string objectName = "springJobDetailObject"; + jobDetail.ObjectName = objectName; + jobDetail.ApplicationContext = new XmlApplicationContext(); + jobDetail.ApplicationContextJobDataKey = "applicationContextJobDataKey"; + jobDetail.AfterPropertiesSet(); + } + + [Test] + [ExpectedException(ExceptionType = typeof(ArgumentException))] + public void TestAfterPropertiesSet_ApplicationContextJobDataKeySetWithoutApplicationContext() + { + const string objectName = "springJobDetailObject"; + jobDetail.ObjectName = objectName; + jobDetail.ApplicationContextJobDataKey = "applicationContextJobDataKey"; + jobDetail.AfterPropertiesSet(); + } + + } +} diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/MethodInvokingJobDetailFactoryObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/MethodInvokingJobDetailFactoryObjectTest.cs new file mode 100644 index 00000000..1be2228a --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/MethodInvokingJobDetailFactoryObjectTest.cs @@ -0,0 +1,75 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using NUnit.Framework; + +using Quartz; + +namespace Spring.Scheduling.Quartz +{ + /// + /// + /// + /// Marko Lahma (.NET) + [TestFixture] + public class MethodInvokingJobDetailFactoryObjectTest + { + private const string FACTORY_NAME = "springObjectFactory"; + private MethodInvokingJobDetailFactoryObject factory; + + [SetUp] + public void SetUp() + { + factory = new MethodInvokingJobDetailFactoryObject(); + factory.ObjectName = FACTORY_NAME; + factory.TargetMethod = "Invoke"; + factory.TargetObject = new InvocationCountingJob(); + } + + [Test] + public void TestGetObject_MinimalDefaults() + { + factory.AfterPropertiesSet(); + JobDetail jd = (JobDetail) factory.GetObject(); + Assert.IsNotNull(jd, "job detail was null"); + Assert.AreEqual(FACTORY_NAME, jd.Name, "job name did not default to factory name"); + Assert.AreEqual(jd.JobType, typeof(MethodInvokingJob), "factory did not create method invoking job"); + Assert.IsTrue(jd.Durable, "job was not durable"); + Assert.IsTrue(jd.Volatile, "job was not volatile"); + } + + [Test] + public void TestGetObject_ConcurrentJob() + { + factory.Concurrent = false; + factory.AfterPropertiesSet(); + JobDetail jd = (JobDetail)factory.GetObject(); + Assert.IsNotNull(jd, "job detail was null"); + Assert.AreEqual(jd.JobType, typeof(StatefulMethodInvokingJob), "factory did not create stateful method invoking job"); + } + + [Test] + public void TestGetObject_TriggerListenersSet() + { + string[] LISTENER_NAMES = new string[] {"Foo", "Bar"}; + factory.JobListenerNames = LISTENER_NAMES; + factory.AfterPropertiesSet(); + JobDetail jd = (JobDetail)factory.GetObject(); + CollectionAssert.AreEquivalent(LISTENER_NAMES, jd.JobListenerNames); + } + + } +} diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/MethodInvokingJobTest.cs b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/MethodInvokingJobTest.cs new file mode 100644 index 00000000..02f0e319 --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/MethodInvokingJobTest.cs @@ -0,0 +1,139 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Threading; + +using NUnit.Framework; + +using Quartz; +using Quartz.Job; +using Quartz.Spi; + +using Rhino.Mocks; + +using Spring.Objects.Support; + +namespace Spring.Scheduling.Quartz +{ + /// + /// + /// + /// Marko Lahma (.NET) + [TestFixture] + public class MethodInvokingJobTest + { + private MethodInvokingJob methodInvokingJob; + + [SetUp] + public void SetUp() + { + methodInvokingJob = new MethodInvokingJob(); + } + + [Test] + [ExpectedException(ExceptionType = typeof(ArgumentException))] + public void TestMethodInvoker_SetWithNull() + { + methodInvokingJob.MethodInvoker = null; + } + + [Test] + [ExpectedException(ExceptionType = typeof(JobExecutionException))] + public void TestMethodInvocation_NullMethodInvokder() + { + methodInvokingJob.Execute(CreateMinimalJobExecutionContext()); + } + + [Test] + public void TestMethodInvoker_MethodSetCorrectly() + { + InvocationCountingJob job = new InvocationCountingJob(); + MethodInvoker mi = new MethodInvoker(); + mi.TargetObject = job; + mi.TargetMethod = "Invoke"; + mi.Prepare(); + methodInvokingJob.MethodInvoker = mi; + methodInvokingJob.Execute(CreateMinimalJobExecutionContext()); + Assert.AreEqual(1, job.CounterValue, "Job was not invoked once"); + } + + [Test] + public void TestMethodInvoker_MethodSetCorrectlyThrowsException() + { + InvocationCountingJob job = new InvocationCountingJob(); + MethodInvoker mi = new MethodInvoker(); + mi.TargetObject = job; + mi.TargetMethod = "InvokeAndThrowException"; + mi.Prepare(); + methodInvokingJob.MethodInvoker = mi; + try + { + methodInvokingJob.Execute(CreateMinimalJobExecutionContext()); + Assert.Fail("Successful invoke when method threw exception"); + } + catch (JobExecutionException) + { + // ok + } + Assert.AreEqual(1, job.CounterValue, "Job was not invoked once"); + } + + + private static JobExecutionContext CreateMinimalJobExecutionContext() + { + MockRepository repo = new MockRepository(); + IScheduler sched = (IScheduler) repo.DynamicMock(typeof (IScheduler)); + JobExecutionContext ctx = new JobExecutionContext(sched, ConstructMinimalTriggerFiredBundle(), null); + return ctx; + } + + private static TriggerFiredBundle ConstructMinimalTriggerFiredBundle() + { + JobDetail jd = new JobDetail("jobName", "jobGroup", typeof(NoOpJob)); + SimpleTrigger trigger = new SimpleTrigger("triggerName", "triggerGroup"); + TriggerFiredBundle retValue = new TriggerFiredBundle(jd, trigger, null, false, null, null, null, null); + + return retValue; + } + + } + + /// + /// Test class for method invoker. + /// + public class InvocationCountingJob + { + private int counter; + + public void Invoke() + { + Interlocked.Increment(ref counter); + } + + public void InvokeAndThrowException() + { + Interlocked.Increment(ref counter); + throw new Exception(); + } + + + public int CounterValue + { + get { return counter; } + } + } +} diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/SchedulerFactoryObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/SchedulerFactoryObjectTest.cs new file mode 100644 index 00000000..a6375e05 --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/SchedulerFactoryObjectTest.cs @@ -0,0 +1,309 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Collections; +using System.Threading; + +using NUnit.Framework; + +using Quartz; +using Quartz.Impl; + +using Rhino.Mocks; + +namespace Spring.Scheduling.Quartz +{ + /// + /// + /// + /// Marko Lahma (.NET) + [TestFixture] + public class SchedulerFactoryObjectTest + { + private MockRepository mockery = null; + private SchedulerFactoryObject factory; + + [SetUp] + public void SetUp() + { + factory = new SchedulerFactoryObject(); + TestSchedulerFactory.Mockery.BackToRecordAll(); + } + + [Test] + public void TestAfterPropertiesSet_Defaults() + { + factory.AfterPropertiesSet(); + TestSchedulerFactory.Mockery.ReplayAll(); + } + + [Test] + public void TestAfterPropertiesSet_NullJobFactory() + { + factory.JobFactory = null; + factory.AfterPropertiesSet(); + TestSchedulerFactory.Mockery.ReplayAll(); + } + + [Test] + public void TestAfterPropertiesSet_NoAutoStartup() + { + // set expectations + TestSchedulerFactory.MockScheduler.JobFactory = null; + LastCall.IgnoreArguments(); + TestSchedulerFactory.Mockery.ReplayAll(); + + factory.SchedulerFactoryType = typeof(TestSchedulerFactory); + factory.AutoStartup = false; + factory.AfterPropertiesSet(); + } + + [Test] + public void TestAfterPropertiesSet_AutoStartup() + { + InitForAfterPropertiesSetTest(); + + TestSchedulerFactory.MockScheduler.Start(); + TestSchedulerFactory.Mockery.ReplayAll(); + + factory.SchedulerFactoryType = typeof (TestSchedulerFactory); + factory.AutoStartup = true; + factory.AfterPropertiesSet(); + } + + [Test] + public void TestAfterPropertiesSet_AddListeners() + { + mockery = new MockRepository(); + InitForAfterPropertiesSetTest(); + + factory.SchedulerListeners = new ISchedulerListener[] { (ISchedulerListener)mockery.CreateMock(typeof(ISchedulerListener)) }; + TestSchedulerFactory.MockScheduler.AddSchedulerListener(null); + LastCall.IgnoreArguments(); + + factory.GlobalJobListeners = new IJobListener[] { (IJobListener)mockery.CreateMock(typeof(IJobListener)) }; + TestSchedulerFactory.MockScheduler.AddGlobalJobListener(null); + LastCall.IgnoreArguments(); + + factory.JobListeners = new IJobListener[] { (IJobListener)mockery.CreateMock(typeof(IJobListener)) }; + TestSchedulerFactory.MockScheduler.AddJobListener(null); + LastCall.IgnoreArguments(); + + factory.GlobalTriggerListeners = new ITriggerListener[] { (ITriggerListener)mockery.CreateMock(typeof(ITriggerListener)) }; + TestSchedulerFactory.MockScheduler.AddGlobalTriggerListener(null); + LastCall.IgnoreArguments(); + + factory.TriggerListeners = new ITriggerListener[] { (ITriggerListener)mockery.CreateMock(typeof(ITriggerListener)) }; + TestSchedulerFactory.MockScheduler.AddTriggerListener(null); + LastCall.IgnoreArguments(); + + TestSchedulerFactory.Mockery.ReplayAll(); + mockery.ReplayAll(); + factory.AfterPropertiesSet(); + } + + [Test] + public void TestAfterPropertiesSet_Calendars() + { + mockery = new MockRepository(); + InitForAfterPropertiesSetTest(); + + const string calendarName = "calendar"; + ICalendar cal = (ICalendar) mockery.CreateMock(typeof (ICalendar)); + Hashtable calTable = new Hashtable(); + calTable[calendarName] = cal; + factory.Calendars = calTable; + TestSchedulerFactory.MockScheduler.AddCalendar(calendarName, cal, true, true); + + TestSchedulerFactory.Mockery.ReplayAll(); + mockery.ReplayAll(); + factory.AfterPropertiesSet(); + } + + [Test] + public void TestAfterPropertiesSet_Trigger_TriggerExists() + { + mockery = new MockRepository(); + InitForAfterPropertiesSetTest(); + + const string TRIGGER_NAME = "trigName"; + const string TRIGGER_GROUP = "trigGroup"; + SimpleTrigger trigger = new SimpleTrigger(TRIGGER_NAME, TRIGGER_GROUP); + factory.Triggers = new Trigger[] { trigger }; + + Expect.Call(TestSchedulerFactory.MockScheduler.GetTrigger(TRIGGER_NAME, TRIGGER_GROUP)).Return(trigger); + + TestSchedulerFactory.Mockery.ReplayAll(); + mockery.ReplayAll(); + factory.AfterPropertiesSet(); + } + + [Test] + public void TestAfterPropertiesSet_Trigger_TriggerDoesntExist() + { + mockery = new MockRepository(); + InitForAfterPropertiesSetTest(); + + const string TRIGGER_NAME = "trigName"; + const string TRIGGER_GROUP = "trigGroup"; + SimpleTrigger trigger = new SimpleTrigger(TRIGGER_NAME, TRIGGER_GROUP); + factory.Triggers = new Trigger[] { trigger }; + + Expect.Call(TestSchedulerFactory.MockScheduler.GetTrigger(TRIGGER_NAME, TRIGGER_GROUP)).Return(null); + TestSchedulerFactory.MockScheduler.ScheduleJob(trigger); + LastCall.IgnoreArguments().Return(DateTime.UtcNow); + + + TestSchedulerFactory.Mockery.ReplayAll(); + mockery.ReplayAll(); + factory.AfterPropertiesSet(); + + } + + + private void InitForAfterPropertiesSetTest() + { + factory.AutoStartup = false; + // set expectations + factory.SchedulerFactoryType = typeof(TestSchedulerFactory); + TestSchedulerFactory.MockScheduler.JobFactory = null; + LastCall.IgnoreArguments(); + } + + [Test] + public void TestAfterPropertiesSet_AutoStartup_WithDelay() + { + // set expectations + TestSchedulerFactory.MockScheduler.JobFactory = null; + LastCall.IgnoreArguments(); + Expect.Call(TestSchedulerFactory.MockScheduler.SchedulerName).Return("schedName"); + TestSchedulerFactory.MockScheduler.Start(); + TestSchedulerFactory.Mockery.ReplayAll(); + + factory.SchedulerFactoryType = typeof(TestSchedulerFactory); + factory.AutoStartup = true; + factory.StartupDelay = 2; + factory.AfterPropertiesSet(); + Thread.Sleep(TimeSpan.FromSeconds(3)); + + } + + [Test] + public void TestStart() + { + // set expectations + TestSchedulerFactory.MockScheduler.JobFactory = null; + LastCall.IgnoreArguments(); + TestSchedulerFactory.MockScheduler.Start(); + TestSchedulerFactory.Mockery.ReplayAll(); + + factory.SchedulerFactoryType = typeof(TestSchedulerFactory); + factory.AutoStartup = false; + factory.AfterPropertiesSet(); + factory.Start(); + } + + [Test] + public void TestStop() + { + // set expectations + TestSchedulerFactory.MockScheduler.JobFactory = null; + LastCall.IgnoreArguments(); + TestSchedulerFactory.MockScheduler.Standby(); + TestSchedulerFactory.Mockery.ReplayAll(); + + factory.SchedulerFactoryType = typeof(TestSchedulerFactory); + factory.AutoStartup = false; + factory.AfterPropertiesSet(); + factory.Stop(); + } + + [Test] + public void TestGetObject() + { + factory.AfterPropertiesSet(); + TestSchedulerFactory.Mockery.ReplayAll(); + IScheduler sched = (IScheduler)factory.GetObject(); + Assert.IsNotNull(sched, "scheduler was null"); + } + + [Test] + [ExpectedException(ExceptionType = typeof(ArgumentException))] + public void TestSchedulerFactoryType_InvalidType() + { + TestSchedulerFactory.Mockery.ReplayAll(); + factory.SchedulerFactoryType = typeof(SchedulerFactoryObjectTest); + } + + [Test] + public void TestSchedulerFactoryType_ValidType() + { + TestSchedulerFactory.Mockery.ReplayAll(); + factory.SchedulerFactoryType = typeof(StdSchedulerFactory); + } + + [TearDown] + public void TearDown() + { + TestSchedulerFactory.Mockery.VerifyAll(); + if (mockery != null) + { + mockery.VerifyAll(); + } + } + + + } + + public class TestSchedulerFactory : ISchedulerFactory + { + private static readonly MockRepository mockery = new MockRepository(); + private static readonly IScheduler mockScheduler; + + static TestSchedulerFactory() + { + mockScheduler = (IScheduler) mockery.CreateMock(typeof (IScheduler)); + } + + public static MockRepository Mockery + { + get { return mockery; } + } + + public static IScheduler MockScheduler + { + get { return mockScheduler; } + } + + public IScheduler GetScheduler() + { + return mockScheduler; + } + + public IScheduler GetScheduler(string schedName) + { + return mockScheduler; + } + + public ICollection AllSchedulers + { + get { return new ArrayList(); } + } + } + + +} diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/SimpleTriggerObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/SimpleTriggerObjectTest.cs new file mode 100644 index 00000000..19ae7f61 --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/SimpleTriggerObjectTest.cs @@ -0,0 +1,97 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; + +using NUnit.Framework; + +using Quartz; +using Quartz.Job; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Tests for . + /// + /// Marko Lahma (.NET) + [TestFixture] + public class SimpleTriggerObjectTest : TriggerObjectTest + { + private SimpleTriggerObject simpleTrigger; + + [SetUp] + public void SetUp() + { + simpleTrigger = new SimpleTriggerObject(); + simpleTrigger.ObjectName = TRIGGER_NAME; + Trigger = simpleTrigger; + } + + /// + /// Tests all possible misfire instructions for cron trigger + /// from strings to int. + /// + [Test] + public void TestMisfireInstructionNames() + { + string[] names = new string[] { "FireNow", "RescheduleNextWithExistingCount", "RescheduleNextWithRemainingCount", "RescheduleNowWithExistingRepeatCount", "RescheduleNowWithRemainingRepeatCount", "SmartPolicy" }; + foreach (string name in names) + { + simpleTrigger.MisfireInstructionName = name; + } + } + + [Test] + public override void TestAfterPropertiesSet_Defaults() + { + simpleTrigger.AfterPropertiesSet(); + base.TestAfterPropertiesSet_Defaults(); + } + + [Test] + public override void TestAfterPropertiesSet_ValuesGiven() + { + simpleTrigger.StartDelay = 100; + simpleTrigger.AfterPropertiesSet(); + base.TestAfterPropertiesSet_ValuesGiven(); + } + + [Test] + public void TestAfterPropertiesSet_StartDelayGiven() + { + const int START_DELAY = 100000; + simpleTrigger.StartDelay = START_DELAY; + DateTime startTime = DateTime.UtcNow; + simpleTrigger.AfterPropertiesSet(); + AssertDateTimesEqualityWithAllowedDelta(startTime.AddMilliseconds(START_DELAY), simpleTrigger.StartTimeUtc, 1000); + } + + + [Test] + public override void TestAfterPropertiesSet_JobDetailGiven() + { + const string jobName = "jobName"; + const string jobGroup = "jobGroup"; + JobDetail jd = new JobDetail(jobName, jobGroup, typeof(NoOpJob)); + simpleTrigger.JobDetail = jd; + simpleTrigger.AfterPropertiesSet(); + base.TestAfterPropertiesSet_JobDetailGiven(); + Assert.AreSame(jd, simpleTrigger.JobDetail, "job details weren't same"); + } + + } + +} diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/SpringObjectJobFactoryTest.cs b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/SpringObjectJobFactoryTest.cs new file mode 100644 index 00000000..7fe40a1f --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/SpringObjectJobFactoryTest.cs @@ -0,0 +1,103 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System.Collections; + +using NUnit.Framework; + +using Quartz; +using Quartz.Job; +using Quartz.Spi; + +namespace Spring.Scheduling.Quartz +{ + /// + /// + /// + /// Marko Lahma (.NET) + [TestFixture] + public class SpringObjectJobFactoryTest + { + private SpringObjectJobFactory factory; + + [SetUp] + public void SetUp() + { + factory = new SpringObjectJobFactory(); + } + + [Test] + public void TestCreateJobInstance_SimpleDefaults() + { + Trigger trigger = new SimpleTrigger(); + TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof (NoOpJob), trigger); + + IJob job = factory.NewJob(bundle); + Assert.IsNotNull(job, "Created job was null"); + } + + [Test] + public void TestCreateJobInstance_SchedulerContextGiven() + { + IDictionary items = new Hashtable(); + items["foo"] = "bar"; + items["number"] = 123; + factory.SchedulerContext = new SchedulerContext(items); + Trigger trigger = new SimpleTrigger(); + TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof(InjectableJob), trigger); + + InjectableJob job = (InjectableJob) factory.NewJob(bundle); + Assert.IsNotNull(job, "Created job was null"); + Assert.AreEqual("bar", job.Foo, "string injection failed"); + Assert.AreEqual(123, job.Number, "integer injection failed"); + } + + [Test] + public void TestCreateJobInstance_IgnoredProperties() + { + factory.IgnoredUnknownProperties = new string[] {"foo", "baz"}; + Trigger trigger = new SimpleTrigger(); + trigger.JobDataMap["foo"] = "should not be injected"; + trigger.JobDataMap["number"] = 123; + TriggerFiredBundle bundle = TestUtil.CreateMinimalFiredBundleWithTypedJobDetail(typeof(InjectableJob), trigger); + + InjectableJob job = (InjectableJob)factory.NewJob(bundle); + Assert.IsNotNull(job, "Created job was null"); + Assert.AreEqual(123, job.Number, "integer injection failed"); + Assert.IsNull(job.Foo, "foo was injected when it was not supposed to "); + } + + } + + public class InjectableJob : NoOpJob + { + private int number; + private string foo; + + + public int Number + { + get { return number; } + set { number = value; } + } + + public string Foo + { + get { return foo; } + set { foo = value; } + } + } +} diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/TestUtil.cs b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/TestUtil.cs new file mode 100644 index 00000000..ffaace6b --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/TestUtil.cs @@ -0,0 +1,55 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; + +using Quartz; +using Quartz.Spi; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Quartz.NET integration testing helpers. + /// + /// Marko Lahma (.NET) + public class TestUtil + { + /// + /// Creates the minimal fired bundle with job detail that has + /// given job type. + /// + /// Type of the job. + /// Minimal TriggerFiredBundle + public static TriggerFiredBundle CreateMinimalFiredBundleWithTypedJobDetail(Type jobType) + { + return CreateMinimalFiredBundleWithTypedJobDetail(jobType, null); + } + + /// + /// Creates the minimal fired bundle with job detail that has + /// given job type. + /// + /// Type of the job. + /// The trigger. + /// Minimal TriggerFiredBundle + public static TriggerFiredBundle CreateMinimalFiredBundleWithTypedJobDetail(Type jobType, Trigger trigger) + { + JobDetail jd = new JobDetail("jobName", "jobGroup", jobType); + TriggerFiredBundle bundle = new TriggerFiredBundle(jd, trigger, null, false, null, null, null, null); + return bundle; + } + } +} diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/TriggerObjectTest.cs b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/TriggerObjectTest.cs new file mode 100644 index 00000000..e819f68c --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Scheduling/Quartz/TriggerObjectTest.cs @@ -0,0 +1,89 @@ +/* +* Copyright 2002-2005 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; + +using NUnit.Framework; + +using Quartz; + +namespace Spring.Scheduling.Quartz +{ + /// + /// Base class for testing triggers. Contains common functionality. + /// + [TestFixture] + public abstract class TriggerObjectTest + { + private Trigger trigger; + protected const string TRIGGER_NAME = "trigger"; + + + protected Trigger Trigger + { + set { trigger = value; } + } + + [Test] + public virtual void TestAfterPropertiesSet_Defaults() + { + Assert.AreEqual(TRIGGER_NAME, trigger.Name, "trigger name mismatch"); + Assert.AreEqual(SchedulerConstants.DEFAULT_GROUP, trigger.Group, "trigger group name mismatch"); + AssertDateTimesEqualityWithAllowedDelta(DateTime.UtcNow, trigger.StartTimeUtc, 1000); + Assert.IsNull(trigger.JobName, "trigger job name not null"); + Assert.AreEqual(SchedulerConstants.DEFAULT_GROUP, trigger.JobGroup, "trigger job group was not default"); + } + + [Test] + public virtual void TestAfterPropertiesSet_ValuesGiven() + { + const string NAME = "newName"; + const string GROUP = "newGroup"; + DateTime START_TIME = new DateTime(10000000); + trigger.Name = NAME; + trigger.Group = GROUP; + trigger.StartTimeUtc = START_TIME; + Assert.AreEqual(NAME, trigger.Name, "trigger name mismatch"); + Assert.AreEqual(GROUP, trigger.Group, "trigger group name mismatch"); + AssertDateTimesEqualityWithAllowedDelta(START_TIME, trigger.StartTimeUtc, 1000); + } + + + [Test] + public virtual void TestAfterPropertiesSet_JobDetailGiven() + { + const string jobName = "jobName"; + const string jobGroup = "jobGroup"; + Assert.AreEqual(jobName, trigger.JobName, "trigger job name was not from job detail"); + Assert.AreEqual(jobGroup, trigger.JobGroup, "trigger job group was not from job detail"); + } + + [Test] + public virtual void TestTriggerListenerNames_Valis() + { + string[] LISTENER_NAMES = new string[] {"Foo", "Bar", "Baz"}; + trigger.TriggerListenerNames = LISTENER_NAMES; + CollectionAssert.AreEqual(LISTENER_NAMES, trigger.TriggerListenerNames, "Trigger listeners were not equal"); + } + + protected static void AssertDateTimesEqualityWithAllowedDelta(DateTime d1, DateTime d2, int allowedDeltaInMilliseconds) + { + int diffInMillis = (int) Math.Abs((d1 - d2).TotalMilliseconds); + Assert.LessOrEqual(diffInMillis, allowedDeltaInMilliseconds, "too much difference in times"); + } + } + +} diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.2003.csproj b/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.2003.csproj new file mode 100644 index 00000000..5d64c651 --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.2003.csproj @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.2005.csproj b/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.2005.csproj new file mode 100644 index 00000000..0f965b95 --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.2005.csproj @@ -0,0 +1,82 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {9FE720ED-2BD9-4FB9-89C8-FFFA4A491CB5} + Library + Properties + Spring + Spring.Scheduling.Quartz.Tests + + + true + full + false + ..\..\..\build\VS.NET.2005\Spring.Scheduling.Quartz.Tests\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + ..\..\..\build\VS.NET.2005\Spring.Scheduling.Quartz.Tests\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\lib\net\2.0\nunit.framework.dll + + + False + ..\..\..\lib\net\2.0\Quartz.dll + + + False + ..\..\..\lib\Net\2.0\Rhino.Mocks.dll + + + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {E823D54C-CE82-4868-929F-5F95A999F61E} + Spring.Scheduling.Quartz.2005 + + + + + + + + + + + + + + + + + PreserveNewest + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.build b/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.build new file mode 100644 index 00000000..6968b985 --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.build @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.dll.config b/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.dll.config new file mode 100644 index 00000000..245cdf36 --- /dev/null +++ b/test/Spring/Spring.Scheduling.Quartz.Tests/Spring.Scheduling.Quartz.Tests.dll.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/AssemblyInfo.cs b/test/Spring/Spring.Services.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..0b3cf995 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/AssemblyInfo.cs @@ -0,0 +1,25 @@ +#region License + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Spring.Services Tests")] +[assembly: AssemblyDescription("Unit tests for Spring.Services assembly")] diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/autowire.xml b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/autowire.xml new file mode 100644 index 00000000..fc872c1e --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/autowire.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/cao.xml b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/cao.xml new file mode 100644 index 00000000..29610b35 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/cao.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + 7 + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/caoLifetimeService.xml b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/caoLifetimeService.xml new file mode 100644 index 00000000..a4ef8145 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/caoLifetimeService.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/remotingConfigurer.xml b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/remotingConfigurer.xml new file mode 100644 index 00000000..951a60a4 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/remotingConfigurer.xml @@ -0,0 +1,15 @@ + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoLifetimeService.xml b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoLifetimeService.xml new file mode 100644 index 00000000..2b9d795c --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoLifetimeService.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoSingleCall.xml b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoSingleCall.xml new file mode 100644 index 00000000..3b728479 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoSingleCall.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoSingleton-aop.xml b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoSingleton-aop.xml new file mode 100644 index 00000000..e3514d39 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoSingleton-aop.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + countAdvisor + + + + + Spring.Remoting.ISimpleCounter, Spring.Services.Tests + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoSingleton.xml b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoSingleton.xml new file mode 100644 index 00000000..3aca1bbb --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Remoting/saoSingleton.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/Service.cs.fyi b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/Service.cs.fyi new file mode 100644 index 00000000..9416791d --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/Service.cs.fyi @@ -0,0 +1,46 @@ +using System; +using System.Web; +using System.Web.Services; +using System.Web.Services.Protocols; +using System.Web.Services.Description; +using System.Xml.Serialization; + +/// +/// Test class used to generate the wsdl. +/// +[WebService(Namespace = "http://www.springframwework.net")] +#if NET_2_0 +[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] +#endif +//[SoapRpcService(Use=SoapBindingUse.Literal)] +public class HelloWorldService : System.Web.Services.WebService +{ + public HelloWorldService() + { + } + + [WebMethod] + public string SayHelloWorld() + { + return "Hello World !"; + } + + [WebMethod] + [return: XmlElement("out")] + public string SayHello(string name) + { + return String.Format("Hello {0} !", name); + } + + [WebMethod] + [SoapDocumentMethod(OneWay = true)] + //[SoapRpcMethod(Use = SoapBindingUse.Literal, OneWay = true)] + public void LogHelloWorld() + { + } + + [WebMethod(MessageName = "MyLogHello")] + public void LogHello(string name) + { + } +} diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/configurableFactory.xml b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/configurableFactory.xml new file mode 100644 index 00000000..d87dd47c --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/configurableFactory.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/document-literal.wsdl b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/document-literal.wsdl new file mode 100644 index 00000000..cdf8055a --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/document-literal.wsdl @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/nestedSchema.wsdl b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/nestedSchema.wsdl new file mode 100644 index 00000000..c0163225 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/nestedSchema.wsdl @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/nestedSchema.xsd b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/nestedSchema.xsd new file mode 100644 index 00000000..475746d9 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/nestedSchema.xsd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/rpc-literal.wsdl b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/rpc-literal.wsdl new file mode 100644 index 00000000..ce4fe596 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/Web/Services/rpc-literal.wsdl @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/localizer.xml b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/localizer.xml new file mode 100644 index 00000000..e8875505 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/localizer.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/service.config b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/service.config new file mode 100644 index 00000000..89a442fa --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/service.config @@ -0,0 +1,22 @@ + + + + + +
    +
    + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/service.xml b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/service.xml new file mode 100644 index 00000000..f42b3f2c --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/service.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/watcher.xml b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/watcher.xml new file mode 100644 index 00000000..16c4f45a --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/watcher.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + wwwroot/bin/*.* + service.config + service.xml + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/wwwroot/bin/Cassini.dll b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/wwwroot/bin/Cassini.dll new file mode 100644 index 00000000..60ca15b0 Binary files /dev/null and b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/wwwroot/bin/Cassini.dll differ diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/service.config b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/service.config new file mode 100644 index 00000000..714332af --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/service.config @@ -0,0 +1,23 @@ + + + + + + +
    +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/service.xml b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/service.xml new file mode 100644 index 00000000..8a9ca1d7 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/service.xml @@ -0,0 +1,27 @@ + + + + + + ${port} + + + + + + file://~/service.config + + + + + appSettings + + + + + + diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/watcher.xml b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/watcher.xml new file mode 100644 index 00000000..686e969c --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/watcher.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + **/** + + + + + + + + diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/service.config b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/service.config new file mode 100644 index 00000000..d76f9bae --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/service.config @@ -0,0 +1,19 @@ + + + + + +
    +
    + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/service.xml b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/service.xml new file mode 100644 index 00000000..0b0a5406 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/service.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + ${myPrefix.application.name} + + + ${myPrefix.application.fullpath} + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/watcher.xml b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/watcher.xml new file mode 100644 index 00000000..686e969c --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/watcher.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + **/** + + + + + + + + diff --git a/test/Spring/Spring.Services.Tests/Data/Xml/watcher-0.xml b/test/Spring/Spring.Services.Tests/Data/Xml/watcher-0.xml new file mode 100644 index 00000000..268467c4 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Xml/watcher-0.xml @@ -0,0 +1,4 @@ + + diff --git a/test/Spring/Spring.Services.Tests/Data/Xml/watcher-1.xml b/test/Spring/Spring.Services.Tests/Data/Xml/watcher-1.xml new file mode 100644 index 00000000..325a405d --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Xml/watcher-1.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + **/** + + + + + diff --git a/test/Spring/Spring.Services.Tests/Data/Xml/watcher-simple.xml b/test/Spring/Spring.Services.Tests/Data/Xml/watcher-simple.xml new file mode 100644 index 00000000..eedce295 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Data/Xml/watcher-simple.xml @@ -0,0 +1,26 @@ + + + + + + + + + foo/foo.bar + + + + + *.* + + + + + + diff --git a/test/Spring/Spring.Services.Tests/EnterpriseServices/ServicedComponentExporterTests.cs b/test/Spring/Spring.Services.Tests/EnterpriseServices/ServicedComponentExporterTests.cs new file mode 100644 index 00000000..b96b91f2 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/EnterpriseServices/ServicedComponentExporterTests.cs @@ -0,0 +1,99 @@ +#region License + +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if (!NET_1_0) + +#region Imports + +using System; +using System.Collections; +using System.EnterpriseServices; +using System.Reflection; +using System.Reflection.Emit; + +using NUnit.Framework; +using DotNetMock.Dynamic; +using Spring.Objects.Factory; +using Spring.Objects; + +#endregion + +namespace Spring.EnterpriseServices +{ + /// + /// Unit tests for the ServicedComponentExporter class. + /// + /// Bruno Baia + /// $Id: ServicedComponentExporterTests.cs,v 1.2 2007/05/16 10:03:25 oakinger Exp $ + [TestFixture] + public class ServicedComponentExporterTests + { + [Test] + [ExpectedException(typeof(ArgumentException))] + public void BailsWhenNotConfigured () + { + ServicedComponentExporter exp = new ServicedComponentExporter(); + exp.AfterPropertiesSet (); + } + + [Test] + public void RegistersSimpleObjectWithNonDefaultTransactionOption() + { + ServicedComponentExporter exp = new ServicedComponentExporter(); + exp.TargetName = "objectTest"; + exp.ObjectName = "objectTestProxy"; + exp.TypeAttributes = new ArrayList(); + exp.TypeAttributes.Add(new TransactionAttribute(TransactionOption.RequiresNew)); + exp.AfterPropertiesSet(); + + Type type = CreateWrapperType(exp, typeof(TestObject)); + + TransactionAttribute[] attrs = (TransactionAttribute[])type.GetCustomAttributes(typeof(TransactionAttribute), false); + Assert.AreEqual(1, attrs.Length); + Assert.AreEqual(TransactionOption.RequiresNew, attrs[0].Value); + } + + #region Private helpers classes + + private Type CreateWrapperType(ServicedComponentExporter exporter, Type type) + { + MethodInfo createWrapperTypeMethodInfo = typeof(ServicedComponentExporter).GetMethod("CreateWrapperType", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.IsNotNull(createWrapperTypeMethodInfo); + + AssemblyName an = new AssemblyName(); + an.Name = "Spring.EnterpriseServices.Tests"; + AssemblyBuilder proxyAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave); + ModuleBuilder module = proxyAssembly.DefineDynamicModule(an.Name, an.Name + ".dll", true); + + IDynamicMock mockObjectFactory = new DynamicMock(typeof(IObjectFactory)); + mockObjectFactory.ExpectAndReturn("GetType", type, new object[]{ exporter.TargetName }); + + object result = createWrapperTypeMethodInfo.Invoke(exporter, new object[] { module, mockObjectFactory.Object }); + Assert.IsNotNull(result); + Assert.IsTrue(result is Type); + + return (Type)result; + } + + #endregion + } +} + +#endif // (!NET_1_0) diff --git a/test/Spring/Spring.Services.Tests/Remoting/BaseRemotingTestFixture.cs b/test/Spring/Spring.Services.Tests/Remoting/BaseRemotingTestFixture.cs new file mode 100644 index 00000000..aeaa5478 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Remoting/BaseRemotingTestFixture.cs @@ -0,0 +1,53 @@ +using System; +using System.Runtime.Remoting.Channels; +using System.Runtime.Remoting.Channels.Tcp; +using NUnit.Framework; +using Spring.Context.Support; + +namespace Spring.Remoting +{ + public class BaseRemotingTestFixture + { + protected static TcpChannel channel; + private static object lockObject = new object(); + + [TestFixtureSetUp] + public virtual void FixtureSetUp() + { + lock (lockObject) + { + if (channel == null) + { + try + { + foreach (IChannel registeredChannel in ChannelServices.RegisteredChannels) + { + if (registeredChannel is TcpChannel) + { + ((TcpChannel) registeredChannel).StopListening(null); + } + ChannelServices.UnregisterChannel(registeredChannel); + } + + channel = new TcpChannel(8005); +#if !NET_2_0 + ChannelServices.RegisterChannel(channel); +#else + ChannelServices.RegisterChannel(channel, false); +#endif + } + catch + { + // ignore duplicate registration exception if it occurs... + } + } + } + } + + [TearDown] + public virtual void TearDown() + { + ContextRegistry.Clear(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Remoting/CaoExporterTests.cs b/test/Spring/Spring.Services.Tests/Remoting/CaoExporterTests.cs new file mode 100644 index 00000000..c9d4ebae --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Remoting/CaoExporterTests.cs @@ -0,0 +1,82 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Remoting.Lifetime; + +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Remoting.Support; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Unit tests for the CaoExporter class. + /// + /// Bruno Baia + /// $Id: CaoExporterTests.cs,v 1.11 2007/02/20 19:39:47 aseovic Exp $ + [TestFixture] + public class CaoExporterTests : BaseRemotingTestFixture + { + [Test] + [ExpectedException(typeof(ArgumentException))] + public void BailsWhenNotConfigured () + { + CaoExporter exp = new CaoExporter(); + exp.AfterPropertiesSet (); + } + + [Test] + public void RegistersSimpleObject() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Remoting/caoLifetimeService.xml"); + ContextRegistry.RegisterContext(ctx); + + ICaoRemoteFactory caoFactory = Activator.GetObject(typeof(ICaoRemoteFactory), "tcp://localhost:8005/counter2") as ICaoRemoteFactory; + Assert.IsNotNull(caoFactory, "Cao factory is null even though it has been registered."); + + MarshalByRefObject cao = caoFactory.GetObject() as MarshalByRefObject; + Assert.IsNotNull(cao); + } + + [Test] + public void RegistersWithLifetimeService() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Remoting/caoLifetimeService.xml"); + ContextRegistry.RegisterContext(ctx); + + ICaoRemoteFactory caoFactory = Activator.GetObject(typeof(ICaoRemoteFactory), "tcp://localhost:8005/counter2") as ICaoRemoteFactory; + Assert.IsNotNull(caoFactory, "Cao factory is null even though it has been registered."); + + MarshalByRefObject cao = caoFactory.GetObject() as MarshalByRefObject; + Assert.IsNotNull(cao); + + ILease lease = (ILease)cao.GetLifetimeService(); + Assert.AreEqual(TimeSpan.FromMilliseconds(10000), lease.InitialLeaseTime, "InitialLeaseTime"); + Assert.AreEqual(TimeSpan.FromMilliseconds(1000), lease.RenewOnCallTime, "RenewOnCallTime"); + Assert.AreEqual(TimeSpan.FromMilliseconds(100), lease.SponsorshipTimeout, "SponsorshipTimeout"); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/Remoting/CaoFactoryObjectTests.cs b/test/Spring/Spring.Services.Tests/Remoting/CaoFactoryObjectTests.cs new file mode 100644 index 00000000..aa16f428 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Remoting/CaoFactoryObjectTests.cs @@ -0,0 +1,78 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Unit tests for the CaoFactoryObject class. + /// + /// Bruno Baia + /// $Id: CaoFactoryObjectTests.cs,v 1.12 2007/02/20 19:39:52 aseovic Exp $ + [TestFixture] + public class CaoFactoryObjectTests : BaseRemotingTestFixture + { + [Test] + [ExpectedException(typeof(ArgumentException))] + public void BailsWhenNotConfigured () + { + CaoFactoryObject cfo = new CaoFactoryObject(); + cfo.AfterPropertiesSet (); + } + + [Test] + public void GetSimpleObject() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Remoting/cao.xml"); + ContextRegistry.RegisterContext(ctx); + + object obj = ctx.GetObject("remoteCaoCounter1"); + + Assert.IsNotNull(obj, "Object is null even though a object has been registered."); + Assert.IsTrue((obj is ISimpleCounter), "Object should implement 'ISimpleCounter' interface."); + + ISimpleCounter sc = (ISimpleCounter) obj; + Assert.AreEqual(7, sc.Counter, "Remote object hasn't been activated by the client."); + sc.Count(); + Assert.AreEqual(8, sc.Counter); + } + + [Test] + public void DisconnectFromClient() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Remoting/cao.xml"); + ContextRegistry.RegisterContext(ctx); + + object obj = ctx.GetObject("remoteCaoCounter1"); + Assert.IsNotNull(obj, "CAO is null even though a CAO has been registered."); + + IDisposable cao = obj as IDisposable; + Assert.IsNotNull(cao, "CAO should implement 'IDisposable' interface."); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/Remoting/ISimpleCounter.cs b/test/Spring/Spring.Services.Tests/Remoting/ISimpleCounter.cs new file mode 100644 index 00000000..2033f6a8 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Remoting/ISimpleCounter.cs @@ -0,0 +1,40 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + +#endregion + +namespace Spring.Remoting +{ + public interface ISimpleCounter + { + /// + /// Gets or Sets the Counter's value. + /// + int Counter { get; set; } + + /// + /// Increments the counter by one. + /// + void Count(); + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Remoting/RemoteObjectFactoryTests.cs b/test/Spring/Spring.Services.Tests/Remoting/RemoteObjectFactoryTests.cs new file mode 100644 index 00000000..754fa29d --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Remoting/RemoteObjectFactoryTests.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Remoting.Lifetime; + +using NUnit.Framework; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Unit tests for the RemoteObjectFactory class. + /// + /// Bruno Baia + /// $Id: RemoteObjectFactoryTests.cs,v 1.5 2007/08/22 20:17:19 oakinger Exp $ + [TestFixture] + public class RemoteObjectFactoryTests + { + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage="The Target property is required.")] + public void BailsWhenNotConfigured() + { + RemoteObjectFactory factory = new RemoteObjectFactory(); + factory.AfterPropertiesSet(); + } + + [Test] + public void CreateRemoteObject() + { + RemoteObjectFactory factory = new RemoteObjectFactory(); + factory.Target = new SimpleCounter(); + factory.AfterPropertiesSet(); + + object obj = factory.GetObject(); + + Assert.IsTrue((obj is MarshalByRefObject), "Object should derive from MarshalByRefObject."); + } + + [Test] + public void ReturnsInterfaceImplementation() + { + RemoteObjectFactory factory = new RemoteObjectFactory(); + factory.Target = new SimpleCounter(); + factory.AfterPropertiesSet(); + + object obj = factory.GetObject(); + + Assert.IsTrue((obj is ISimpleCounter), "Object should implement an interface."); + } + + [Test] + public void CreateRemoteObjectWithLeaseInfo() + { + RemoteObjectFactory factory = new RemoteObjectFactory(); + factory.Target = new SimpleCounter(); + factory.Infinite = false; + factory.InitialLeaseTime = TimeSpan.FromMilliseconds(10000); + factory.RenewOnCallTime = TimeSpan.FromMilliseconds(1000); + factory.SponsorshipTimeout = TimeSpan.FromMilliseconds(100); + MarshalByRefObject remoteObject = (MarshalByRefObject) factory.GetObject(); + + ILease lease = (ILease) remoteObject.InitializeLifetimeService(); + Assert.AreEqual(TimeSpan.FromMilliseconds(10000), lease.InitialLeaseTime, "InitialLeaseTime"); + Assert.AreEqual(TimeSpan.FromMilliseconds(1000), lease.RenewOnCallTime, "RenewOnCallTime"); + Assert.AreEqual(TimeSpan.FromMilliseconds(100), lease.SponsorshipTimeout, "SponsorshipTimeout"); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/Remoting/RemotingConfigParserTests.cs b/test/Spring/Spring.Services.Tests/Remoting/RemotingConfigParserTests.cs new file mode 100644 index 00000000..34d4f5cb --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Remoting/RemotingConfigParserTests.cs @@ -0,0 +1,38 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Unit tests for the RemotingConfigParserTests class. + /// + /// Bruno Baia + /// $Id: RemotingConfigParserTests.cs,v 1.1 2006/05/20 22:16:59 bbaia Exp $ + [TestFixture] + public class RemotingConfigParserTests + { + } +} diff --git a/test/Spring/Spring.Services.Tests/Remoting/RemotingConfigurerTests.cs b/test/Spring/Spring.Services.Tests/Remoting/RemotingConfigurerTests.cs new file mode 100644 index 00000000..585d2e15 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Remoting/RemotingConfigurerTests.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Runtime.Remoting.Channels; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Unit tests for the RemotingConfigurerTests class. + /// + /// Bruno Baia + /// $Id: RemotingConfigurerTests.cs,v 1.3 2007/02/20 19:40:02 aseovic Exp $ + [TestFixture] + public class RemotingConfigurerTests : BaseRemotingTestFixture + { + [Test] + [Explicit] + public void ConfiguresUsingFilename() + { + Assert.AreEqual(1, ChannelServices.RegisteredChannels.Length); + Assert.AreEqual(channel, ChannelServices.RegisteredChannels[0]); + ChannelServices.UnregisterChannel(channel); + Assert.AreEqual(0, ChannelServices.RegisteredChannels.Length); + + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Remoting/remotingConfigurer.xml"); + ContextRegistry.RegisterContext(ctx); + + Assert.AreEqual(1, ChannelServices.RegisteredChannels.Length); + Assert.AreEqual("tcp", ChannelServices.RegisteredChannels[0].ChannelName); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/Remoting/SaoExporterTests.cs b/test/Spring/Spring.Services.Tests/Remoting/SaoExporterTests.cs new file mode 100644 index 00000000..699b6db4 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Remoting/SaoExporterTests.cs @@ -0,0 +1,47 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Unit tests for the SaoExporter class. + /// + /// Bruno Baia + /// Mark Pollack + /// $Id: SaoExporterTests.cs,v 1.4 2007/02/20 19:40:12 aseovic Exp $ + [TestFixture] + public class SaoExporterTests : BaseRemotingTestFixture + { + [Test] + [ExpectedException(typeof(ArgumentException))] + public void BailsWhenNotConfigured () + { + SaoExporter exp = new SaoExporter(); + exp.AfterPropertiesSet(); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/Remoting/SaoFactoryObjectTests.cs b/test/Spring/Spring.Services.Tests/Remoting/SaoFactoryObjectTests.cs new file mode 100644 index 00000000..030786dd --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Remoting/SaoFactoryObjectTests.cs @@ -0,0 +1,158 @@ +#region License + +/* + * Copyright 2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Lifetime; +using NUnit.Framework; +using Spring.Aop.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.Objects; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Remoting +{ + /// + /// Unit tests for the SaoFactoryObject class. + /// + /// Mark Pollack + /// Bruno Baia + /// $Id: SaoFactoryObjectTests.cs,v 1.12 2007/10/09 22:25:31 markpollack Exp $ + [TestFixture] + public class SaoFactoryObjectTests : BaseRemotingTestFixture + { + [Test] + [ExpectedException(typeof(ArgumentException))] + public void BailsWhenNotConfigured () + { + SaoFactoryObject sfo = new SaoFactoryObject(); + sfo.AfterPropertiesSet(); + } + + /// + /// Make sure that transparent proxies are recognized + /// correctly by the core object factory. + /// + /// + /// This is a test for SPRNET-200 + /// + [Test] + public void WiringWithTransparentProxy() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Remoting/autowire.xml"); + ContextRegistry.RegisterContext(ctx); + + TestObject to = (TestObject) ctx.GetObject("kerry3"); + Assert.IsNotNull(to, "Object is null even though a object has been configured."); + Assert.IsTrue(RemotingServices.IsTransparentProxy(to.Sibling), "IsTransparentProxy"); + Assert.AreEqual("Kerry3", to.Name, "Name"); + + to = (TestObject) ctx.GetObject("objectWithCtorArgRefShortcuts"); + Assert.IsTrue(RemotingServices.IsTransparentProxy(to.Spouse), "IsTransparentProxy"); + + ConstructorDependenciesObject cdo = (ConstructorDependenciesObject) ctx.GetObject("rod2"); + Assert.IsTrue(RemotingServices.IsTransparentProxy(cdo.Spouse1), "IsTransparentProxy"); + Assert.IsTrue(RemotingServices.IsTransparentProxy(cdo.Spouse2), "IsTransparentProxy"); + } + + [Test] + public void GetSaoWithSingletonMode() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Remoting/saoSingleton.xml"); + ContextRegistry.RegisterContext(ctx); + + object obj = ctx.GetObject("remoteSaoSingletonCounter"); + + Assert.IsNotNull(obj, "Object is null even though a object has been exported."); + Assert.IsTrue((obj is ISimpleCounter), "Object should implement 'ISimpleCounter' interface."); + + ISimpleCounter sc = (ISimpleCounter) obj; + Assert.AreEqual(1, sc.Counter, "Remote object hasn't been activated by the server."); + sc.Count(); + Assert.AreEqual(2, sc.Counter, "Remote object doesn't work in a 'Singleton' mode."); + } + + [Test] + public void GetSaoWithSingletonModeAndAop() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Remoting/saoSingleton-aop.xml"); + ContextRegistry.RegisterContext(ctx); + + //object saoFactory = ctx.GetObject("&remoteCounter"); + //Assert.IsNotNull(saoFactory); + + object obj = ctx.GetObject("remoteCounter"); + + Assert.IsNotNull(obj, "Object is null even though a object has been exported."); + Assert.IsTrue(AopUtils.IsAopProxy(obj)); + Assert.IsTrue((obj is ISimpleCounter), "Object should implement 'ISimpleCounter' interface."); + + + MethodCounter aopCounter = ctx.GetObject("countingBeforeAdvice") as MethodCounter; + Assert.IsNotNull(aopCounter); + + int aopCount = aopCounter.GetCalls("Count"); + Assert.AreEqual(0, aopCount); + + ISimpleCounter sc = (ISimpleCounter)obj; + Assert.AreEqual(1, sc.Counter, "Remote object hasn't been activated by the server."); + sc.Count(); + Assert.AreEqual(2, sc.Counter, "Remote object doesn't work in a 'Singleton' mode."); + Assert.AreEqual(1, aopCounter.GetCalls("Count")); + } + + [Test] + public void GetSaoWithSingleCallMode() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Remoting/saoSingleCall.xml"); + ContextRegistry.RegisterContext(ctx); + + object obj = ctx.GetObject("remoteSaoSingleCallCounter"); + + Assert.IsNotNull(obj, "Object is null even though a object has been exported."); + Assert.IsTrue((obj is ISimpleCounter), "Object should implement 'ISimpleCounter' interface."); + + ISimpleCounter sc = (ISimpleCounter) obj; + Assert.IsNotNull(sc, "Object is null even though a object has been exported."); + Assert.AreEqual(1, sc.Counter, "Remote object hasn't been activated by the server."); + sc.Count(); + Assert.AreEqual(1, sc.Counter, "Remote object don't works in a 'SingleCall' mode."); + } + + [Test] + public void GetSaoWithLifetimeService() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Remoting/saoLifetimeService.xml"); + ContextRegistry.RegisterContext(ctx); + + MarshalByRefObject obj = (MarshalByRefObject) ctx.GetObject("remoteSaoCounter"); + ILease lease = (ILease) obj.GetLifetimeService(); + + Assert.AreEqual(TimeSpan.FromMilliseconds(10000), lease.InitialLeaseTime, "InitialLeaseTime"); + Assert.AreEqual(TimeSpan.FromMilliseconds(1000), lease.RenewOnCallTime, "RenewOnCallTime"); + Assert.AreEqual(TimeSpan.FromMilliseconds(100), lease.SponsorshipTimeout, "SponsorshipTimeout"); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/Remoting/SimpleCounter.cs b/test/Spring/Spring.Services.Tests/Remoting/SimpleCounter.cs new file mode 100644 index 00000000..0142d181 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Remoting/SimpleCounter.cs @@ -0,0 +1,68 @@ +#region License + +/* + * Copyright 2004 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + + +#endregion + +namespace Spring.Remoting +{ + /// + /// A simple remote class. + /// + public class SimpleCounter : ISimpleCounter + { + protected int _counter; + + /// + /// Create an instance of the Counter class. + /// + public SimpleCounter() + { + _counter = 0; + } + + /// + /// Create an instance of the Counter class. + /// + /// Initial counter value. + public SimpleCounter(int initialCounter) + { + _counter = initialCounter; + } + + /// + /// Get or Set the Counter's value. + /// + public int Counter + { + get { return _counter; } + set { _counter = value; } + } + /// + /// Increment the counter by one. + /// + public void Count() + { + _counter++; + } + } +} diff --git a/test/Spring/Spring.Services.Tests/ServiceModel/ServiceExporterTests.cs b/test/Spring/Spring.Services.Tests/ServiceModel/ServiceExporterTests.cs new file mode 100644 index 00000000..5b033b6e --- /dev/null +++ b/test/Spring/Spring.Services.Tests/ServiceModel/ServiceExporterTests.cs @@ -0,0 +1,278 @@ +#if NET_3_0 +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.ServiceModel; + +using NUnit.Framework; +using Spring.ServiceModel; + +#endregion + +namespace Spring.ServiceModel +{ + /// + /// Unit tests for the ServiceExporter class. + /// + /// Bruno Baia + /// $Id: ServiceExporterTests.cs,v 1.1 2007/09/21 14:27:35 bbaia Exp $ + [TestFixture] + public sealed class ServiceExporterTests + { + + #region Test classes + + public interface IService + { + string SomeMethod(int param); + } + + [ServiceContract(Namespace = "http://Spring.Services.Tests")] + public interface IDecoratedService + { + [OperationContract] + string SomeMethod(int param); + } + + public class Service : IDecoratedService + { + public string SomeMethod(int param) + { + return param.ToString(); + } + } + + [ServiceContract(Namespace = "http://Spring.Services.Tests")] + public class DecoratedService : IService + { + [OperationContract] + public string SomeMethod(int param) + { + return param.ToString(); + } + } + + #endregion + +/* + WebServiceExporter wse = null; + + [SetUp] + public void SetUp() + { + const string xml = + @" + + + +"; + Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory objectFactory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + + wse = new WebServiceExporter(); + wse.ObjectFactory = objectFactory; + } + + [Test] + [ExpectedException(typeof(ArgumentException), "The TargetName property is required.")] + public void NullConfig() + { + wse.ObjectName = "NullConfig"; + wse.AfterPropertiesSet(); + } + + [Test] + public void ProxiesTargetInterfaces() + { + wse.ObjectName = "ProxiesTargetInterfaces"; + wse.TargetName = "noDecoratedService"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + Assert.IsTrue(typeof(IService).IsAssignableFrom(proxyType)); + } + + [Test] + public void CreatesWebServiceAttributeWithNoDecoratedClassAndMinimalConfig() + { + wse.ObjectName = "CreatesWebServiceAttributeWithNoDecoratedClassAndMinimalConfig"; + wse.TargetName = "noDecoratedService"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + object[] attrs = proxyType.GetCustomAttributes(typeof(WebServiceAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; + Assert.AreEqual("CreatesWebServiceAttributeWithNoDecoratedClassAndMinimalConfig", wsa.Name); + Assert.AreEqual(string.Empty, wsa.Description); + Assert.AreEqual(WebServiceAttribute.DefaultNamespace, wsa.Namespace); + } + + [Test] + public void CreatesWebServiceAttributeWithNoDecoratedClassAndFullConfig() + { + wse.ObjectName = "CreatesWebServiceAttributeWithNoDecoratedClassAndFullConfig"; + wse.TargetName = "noDecoratedService"; + wse.Name = "My web service name"; + wse.Description = "My web service description"; + wse.Namespace = "http://www.springframework.net"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + object[] attrs = proxyType.GetCustomAttributes(typeof(WebServiceAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; + Assert.AreEqual(wse.Name, wsa.Name); + Assert.AreEqual(wse.Description, wsa.Description); + Assert.AreEqual(wse.Namespace, wsa.Namespace); + } + + [Test] + public void CreatesDefaultWebMethodAttributeWithNoDecoratedMethod() + { + wse.ObjectName = "CreatesDefaultWebMethodAttributeWithNoDecoratedMethod"; + wse.TargetName = "noDecoratedService"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + MethodInfo method = proxyType.GetMethod("SomeMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + } + + [Test] + public void CreatesCustomWebMethodAttributeWithNoDecoratedMethod() + { + wse.ObjectName = "CreatesCustomWebMethodAttributeWithNoDecoratedMethod"; + wse.TargetName = "noDecoratedService"; + wse.MemberAttributes.Add("SomeMethod", new WebMethodAttribute(true)); // default value is false + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + MethodInfo method = proxyType.GetMethod("SomeMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebMethodAttribute wma = attrs[0] as WebMethodAttribute; + Assert.AreEqual(true, wma.EnableSession); + } + + [Test] + public void OverridesExistingWebServiceAttributeWithDecoratedClass() + { + wse.ObjectName = "OverridesExistingWebServiceAttributeWithDecoratedClass"; + wse.TargetName = "decoratedService"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + object[] attrs = proxyType.GetCustomAttributes(typeof(WebServiceAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; + Assert.AreEqual("OverridesExistingWebServiceAttributeWithDecoratedClass", wsa.Name); + Assert.AreEqual(string.Empty, wsa.Description); + Assert.AreEqual(WebServiceAttribute.DefaultNamespace, wsa.Namespace); + } + + [Test] + public void UsesExistingWebMethodAttributeWithDecoratedMethod() + { + wse.ObjectName = "UsesExistingWebMethodAttributeWithDecoratedMethod"; + wse.TargetName = "decoratedService"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + MethodInfo method = proxyType.GetMethod("SomeMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebMethodAttribute wma = attrs[0] as WebMethodAttribute; + Assert.AreEqual("SomeMethod description", wma.Description); + } + + // TODO : attributes override + [Test] + [Ignore("Attributes override not implemented.")] + public void OverridesExistingWebMethodAttributeWithDecoratedMethod() + { + wse.ObjectName = "OverridesExistingWebMethodAttributeWithDecoratedMethod"; + wse.TargetName = "decoratedService"; + wse.MemberAttributes.Add("SomeMethod", new WebMethodAttribute(true)); // default value is false + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + MethodInfo method = proxyType.GetMethod("SomeMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebMethodAttribute wma = attrs[0] as WebMethodAttribute; + Assert.AreEqual(true, wma.EnableSession); + } + + #region Test classes + + public interface IService + { + string SomeMethod(int param); + } + + public class NoDecoratedService : IService + { + public string SomeMethod(int param) + { + return param.ToString(); + } + } + + [WebService(Name = "Decorated service")] + public class DecoratedService : IService + { + [WebMethod(Description="SomeMethod description")] + public string SomeMethod(int param) + { + return param.ToString(); + } + } + + #endregion +*/ + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2003.csproj b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2003.csproj new file mode 100644 index 00000000..32f72256 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2003.csproj @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2005.csproj b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2005.csproj new file mode 100644 index 00000000..dd74a801 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2005.csproj @@ -0,0 +1,188 @@ + + + Local + 8.0.50727 + 2.0 + {EB2687B7-8B26-4FBC-852A-4128D5CACAFC} + Debug + AnyCPU + + + + + Spring.Services.Tests + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + ..\..\..\build\VS.Net.2005\Spring.Services.Tests\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + 0618 + false + false + false + true + 4 + full + prompt + true + + + ..\..\..\build\VS.Net.2005\Spring.Services.Tests\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\DotNetMock.dll + + + nunit.framework + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + System + + + System.Data + + + + + + System.XML + + + + + Code + + + + + Code + + + Code + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + Designer + + + + + PreserveNewest + + + + + + + + + + + + + + + + + + + + + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {B58E34CF-6E70-481D-AC87-1BC2D13C21FB} + Spring.Services.2005 + + + {2111596A-0327-4C9D-8919-294FBD988A23} + Spring.Aop.Tests.2005 + + + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904} + Spring.Core.Tests.2005 + + + + + + + + + + + + + echo "Copying .xml files for Spring.Services tests" +xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2005\Spring.Services.Tests\$(ConfigurationName)\ /y /s /q /d +xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2005\Spring.Services.Tests\$(ConfigurationName)\ /y /s /q + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2008.csproj b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2008.csproj new file mode 100644 index 00000000..9605e10b --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2008.csproj @@ -0,0 +1,192 @@ + + + Local + 8.0.50727 + 2.0 + {4374F018-9738-46BF-A399-4594CEE75B21} + Debug + AnyCPU + + + + + Spring.Services.Tests + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + + + + + ..\..\..\build\VS.Net.2008\Spring.Services.Tests\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0;NET_3_0 + + + true + 4096 + false + + + false + false + false + true + 4 + full + prompt + true + + + ..\..\..\build\VS.Net.2008\Spring.Services.Tests\Release\ + false + 285212672 + false + + + TRACE;NET_2_0;NET_3_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + ..\..\..\lib\Net\2.0\DotNetMock.dll + + + nunit.framework + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + System + + + System.Data + + + + + + + + System.XML + + + + + Code + + + + + Code + + + Code + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + Designer + + + + + PreserveNewest + + + + + + + + + + + + + + + + + + + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Spring.Services.2008 + + + {2111596A-0327-4C9D-8919-294FBD988A23} + Spring.Aop.Tests.2005 + + + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904} + Spring.Core.Tests.2005 + + + + + + + + + + echo "Copying .xml files for Spring.Services tests" +xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2008\Spring.Services.Tests\$(ConfigurationName)\ /y /s /q /d +xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2008\Spring.Services.Tests\$(ConfigurationName)\ /y /s /q + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Spring.Services.Tests.build b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.build new file mode 100644 index 00000000..29371eed --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.build @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Services.Tests/Spring.Services.Tests.dll.config b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.dll.config new file mode 100644 index 00000000..b8a6a51e --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.dll.config @@ -0,0 +1,50 @@ + + + + + + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/Spring.Services.WindowsService.Samples.csproj b/test/Spring/Spring.Services.Tests/Spring.Services.WindowsService.Samples.csproj new file mode 100644 index 00000000..0bbb0a80 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Spring.Services.WindowsService.Samples.csproj @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Services.Tests/Spring.Services.WindowsService.Tests.build b/test/Spring/Spring.Services.Tests/Spring.Services.WindowsService.Tests.build new file mode 100644 index 00000000..3e224dbc --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Spring.Services.WindowsService.Tests.build @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Services.Tests/Spring.Services.WindowsService.Tests.csproj b/test/Spring/Spring.Services.Tests/Spring.Services.WindowsService.Tests.csproj new file mode 100644 index 00000000..844d3e58 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Spring.Services.WindowsService.Tests.csproj @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Services.Tests/Web/Services/WebServiceProxyFactoryTests.cs b/test/Spring/Spring.Services.Tests/Web/Services/WebServiceProxyFactoryTests.cs new file mode 100644 index 00000000..8e70cc22 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/Web/Services/WebServiceProxyFactoryTests.cs @@ -0,0 +1,585 @@ +#region License + +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Reflection; +using System.Xml.Serialization; +using System.Web.Services; +using System.Web.Services.Protocols; +using System.Web.Services.Description; +#if NET_3_0 +using System.ServiceModel; +using System.ServiceModel.Description; +using System.Runtime.Serialization; +#endif + +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Context; +using Spring.Context.Support; +using Spring.Util; + +#endregion + +namespace Spring.Web.Services +{ + /// + /// Unit tests for the WebServiceProxyFactory class. + /// + /// Bruno Baia + /// $Id: WebServiceProxyFactoryTests.cs,v 1.8 2007/12/07 20:26:26 bbaia Exp $ + [TestFixture] + public class WebServiceProxyFactoryTests + { + [Test] + [ExpectedException(typeof(ArgumentException))] + public void BailsWhenNotConfigured() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.AfterPropertiesSet(); + } + + [Test] + public void DocumentLiteralWithDefaultConfig() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ServiceUri = new AssemblyResource("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/document-literal.wsdl"); + wspf.ServiceInterface = typeof(IHelloWorld); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + object[] typeAttrs = proxyType.GetCustomAttributes(typeof(WebServiceBindingAttribute), false); + Assert.IsTrue(typeAttrs.Length > 0); + + WebServiceBindingAttribute wsba = (WebServiceBindingAttribute)typeAttrs[0]; + Assert.AreEqual("HelloWorldServiceSoap", wsba.Name); + Assert.AreEqual("http://www.springframwework.net", wsba.Namespace); + + MethodInfo sayHelloWorldMethod = proxyType.GetMethod("SayHelloWorld"); + Assert.IsNotNull(sayHelloWorldMethod); + + object[] sdmaAttrs = sayHelloWorldMethod.GetCustomAttributes(typeof(SoapDocumentMethodAttribute), false); + Assert.IsTrue(sdmaAttrs.Length > 0); + + SoapDocumentMethodAttribute sdma = (SoapDocumentMethodAttribute)sdmaAttrs[0]; + Assert.AreEqual("http://www.springframwework.net/SayHelloWorld", sdma.Action); + Assert.AreEqual(SoapParameterStyle.Wrapped, sdma.ParameterStyle); + Assert.AreEqual(string.Empty, sdma.Binding); + Assert.AreEqual(false, sdma.OneWay); + Assert.AreEqual(string.Empty, sdma.RequestElementName); + Assert.AreEqual("http://www.springframwework.net", sdma.RequestNamespace); + Assert.AreEqual(string.Empty, sdma.ResponseElementName); + Assert.AreEqual("http://www.springframwework.net", sdma.ResponseNamespace); + Assert.AreEqual(SoapBindingUse.Literal, sdma.Use); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + [Test] + public void DocumentLiteralWithOneWay() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ServiceUri = new AssemblyResource("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/document-literal.wsdl"); + wspf.ServiceInterface = typeof(IHelloWorld); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + MethodInfo logHelloWorldMethod = proxyType.GetMethod("LogHelloWorld"); + Assert.IsNotNull(logHelloWorldMethod); + + object[] methodAttrs = logHelloWorldMethod.GetCustomAttributes(typeof(SoapDocumentMethodAttribute), false); + Assert.IsTrue(methodAttrs.Length > 0); + + SoapDocumentMethodAttribute sdma = (SoapDocumentMethodAttribute)methodAttrs[0]; + Assert.AreEqual("http://www.springframwework.net/LogHelloWorld", sdma.Action); + Assert.AreEqual(SoapParameterStyle.Wrapped, sdma.ParameterStyle); + Assert.AreEqual(string.Empty, sdma.Binding); + Assert.AreEqual(true, sdma.OneWay); + Assert.AreEqual(string.Empty, sdma.RequestElementName); + Assert.AreEqual("http://www.springframwework.net", sdma.RequestNamespace); + Assert.AreEqual(string.Empty, sdma.ResponseElementName); + Assert.AreEqual(null, sdma.ResponseNamespace); + Assert.AreEqual(SoapBindingUse.Literal, sdma.Use); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + [Test] + public void DocumentLiteralWithMessageName() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ServiceUri = new AssemblyResource("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/document-literal.wsdl"); + wspf.ServiceInterface = typeof(IHelloWorld); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + MethodInfo logHelloMethod = proxyType.GetMethod("LogHello"); + Assert.IsNotNull(logHelloMethod); + + object[] methodAttrs = logHelloMethod.GetCustomAttributes(typeof(SoapDocumentMethodAttribute), false); + Assert.IsTrue(methodAttrs.Length > 0); + + SoapDocumentMethodAttribute sdma = (SoapDocumentMethodAttribute)methodAttrs[0]; + Assert.AreEqual("http://www.springframwework.net/MyLogHello", sdma.Action); + Assert.AreEqual(SoapParameterStyle.Wrapped, sdma.ParameterStyle); + Assert.AreEqual(string.Empty, sdma.Binding); + Assert.AreEqual(false, sdma.OneWay); + Assert.AreEqual("MyLogHello", sdma.RequestElementName); + Assert.AreEqual("http://www.springframwework.net", sdma.RequestNamespace); + Assert.AreEqual("MyLogHelloResponse", sdma.ResponseElementName); + Assert.AreEqual("http://www.springframwework.net", sdma.ResponseNamespace); + Assert.AreEqual(SoapBindingUse.Literal, sdma.Use); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + +#if NET_2_0 + [Test] + public void DocumentLiteralWithNamedOutParameter() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ServiceUri = new AssemblyResource("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/document-literal.wsdl"); + wspf.ServiceInterface = typeof(IHelloWorld); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + MethodInfo sayHelloMethod = proxyType.GetMethod("SayHello"); + Assert.IsNotNull(sayHelloMethod); + + object[] sdmaAttrs = sayHelloMethod.GetCustomAttributes(typeof(SoapDocumentMethodAttribute), false); + Assert.IsTrue(sdmaAttrs.Length > 0); + + SoapDocumentMethodAttribute sdma = (SoapDocumentMethodAttribute)sdmaAttrs[0]; + Assert.AreEqual("http://www.springframwework.net/SayHello", sdma.Action); + Assert.AreEqual(SoapParameterStyle.Wrapped, sdma.ParameterStyle); + Assert.AreEqual(string.Empty, sdma.Binding); + Assert.AreEqual(false, sdma.OneWay); + Assert.AreEqual(string.Empty, sdma.RequestElementName); + Assert.AreEqual("http://www.springframwework.net", sdma.RequestNamespace); + Assert.AreEqual(string.Empty, sdma.ResponseElementName); + Assert.AreEqual("http://www.springframwework.net", sdma.ResponseNamespace); + Assert.AreEqual(SoapBindingUse.Literal, sdma.Use); + + object[] xeAttrs = sayHelloMethod.ReturnTypeCustomAttributes.GetCustomAttributes(typeof(XmlElementAttribute), false); + Assert.IsTrue(xeAttrs.Length > 0); + + XmlElementAttribute xea = (XmlElementAttribute)xeAttrs[0]; + Assert.AreEqual("out", xea.ElementName); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + [Test] + public void RpcLiteralWithNamedOutParameter() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ServiceUri = new AssemblyResource("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/rpc-literal.wsdl"); + wspf.ServiceInterface = typeof(IHelloWorld); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + MethodInfo sayHelloMethod = proxyType.GetMethod("SayHello"); + Assert.IsNotNull(sayHelloMethod); + + object[] srmaAttrs = sayHelloMethod.GetCustomAttributes(typeof(SoapRpcMethodAttribute), false); + Assert.IsTrue(srmaAttrs.Length > 0); + + SoapRpcMethodAttribute srma = (SoapRpcMethodAttribute)srmaAttrs[0]; + Assert.AreEqual("http://www.springframwework.net/SayHello", srma.Action); + Assert.AreEqual(string.Empty, srma.Binding); + Assert.AreEqual(false, srma.OneWay); + Assert.AreEqual(string.Empty, srma.RequestElementName); + Assert.AreEqual("http://www.springframwework.net", srma.RequestNamespace); + Assert.AreEqual(string.Empty, srma.ResponseElementName); + Assert.AreEqual("http://www.springframwework.net", srma.ResponseNamespace); + Assert.AreEqual(SoapBindingUse.Literal, srma.Use); + + object[] xeAttrs = sayHelloMethod.ReturnTypeCustomAttributes.GetCustomAttributes(typeof(XmlElementAttribute), false); + Assert.IsTrue(xeAttrs.Length > 0); + + XmlElementAttribute xea = (XmlElementAttribute)xeAttrs[0]; + Assert.AreEqual("out", xea.ElementName); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + [Test] + public void RpcLiteralWithDefaultConfig() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ServiceUri = new AssemblyResource("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/rpc-literal.wsdl"); + wspf.ServiceInterface = typeof(IHelloWorld); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + object[] typeAttrs = proxyType.GetCustomAttributes(typeof(WebServiceBindingAttribute), false); + Assert.IsTrue(typeAttrs.Length > 0); + + WebServiceBindingAttribute wsba = (WebServiceBindingAttribute)typeAttrs[0]; + Assert.AreEqual("HelloWorldServiceSoap", wsba.Name); + Assert.AreEqual("http://www.springframwework.net", wsba.Namespace); + + MethodInfo sayHelloWorldMethod = proxyType.GetMethod("SayHelloWorld"); + Assert.IsNotNull(sayHelloWorldMethod); + + object[] srmaAttrs = sayHelloWorldMethod.GetCustomAttributes(typeof(SoapRpcMethodAttribute), false); + Assert.IsTrue(srmaAttrs.Length > 0); + + SoapRpcMethodAttribute srma = (SoapRpcMethodAttribute)srmaAttrs[0]; + Assert.AreEqual("http://www.springframwework.net/SayHelloWorld", srma.Action); + Assert.AreEqual(string.Empty, srma.Binding); + Assert.AreEqual(false, srma.OneWay); + Assert.AreEqual(string.Empty, srma.RequestElementName); + Assert.AreEqual("http://www.springframwework.net", srma.RequestNamespace); + Assert.AreEqual(string.Empty, srma.ResponseElementName); + Assert.AreEqual("http://www.springframwework.net", srma.ResponseNamespace); + Assert.AreEqual(SoapBindingUse.Literal, srma.Use); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + [Test] + public void RpcLiteralWithOneWay() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ServiceUri = new AssemblyResource("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/rpc-literal.wsdl"); + wspf.ServiceInterface = typeof(IHelloWorld); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + MethodInfo logHelloWorldMethod = proxyType.GetMethod("LogHelloWorld"); + Assert.IsNotNull(logHelloWorldMethod); + + object[] methodAttrs = logHelloWorldMethod.GetCustomAttributes(typeof(SoapRpcMethodAttribute), false); + Assert.IsTrue(methodAttrs.Length > 0); + + SoapRpcMethodAttribute srma = (SoapRpcMethodAttribute)methodAttrs[0]; + Assert.AreEqual("http://www.springframwework.net/LogHelloWorld", srma.Action); + Assert.AreEqual(string.Empty, srma.Binding); + Assert.AreEqual(true, srma.OneWay); + Assert.AreEqual(string.Empty, srma.RequestElementName); + Assert.AreEqual("http://www.springframwework.net", srma.RequestNamespace); + Assert.AreEqual(string.Empty, srma.ResponseElementName); + Assert.AreEqual(null, srma.ResponseNamespace); + Assert.AreEqual(SoapBindingUse.Literal, srma.Use); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + [Test] + public void RpcLiteralWithMessageName() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ServiceUri = new AssemblyResource("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/rpc-literal.wsdl"); + wspf.ServiceInterface = typeof(IHelloWorld); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + MethodInfo logHelloMethod = proxyType.GetMethod("LogHello"); + Assert.IsNotNull(logHelloMethod); + + object[] methodAttrs = logHelloMethod.GetCustomAttributes(typeof(SoapRpcMethodAttribute), false); + Assert.IsTrue(methodAttrs.Length > 0); + + SoapRpcMethodAttribute srma = (SoapRpcMethodAttribute)methodAttrs[0]; + Assert.AreEqual("http://www.springframwework.net/MyLogHello", srma.Action); + Assert.AreEqual(string.Empty, srma.Binding); + Assert.AreEqual(false, srma.OneWay); + Assert.AreEqual(string.Empty, srma.RequestElementName); + Assert.AreEqual("http://www.springframwework.net", srma.RequestNamespace); + Assert.AreEqual(string.Empty, srma.ResponseElementName); + Assert.AreEqual("http://www.springframwework.net", srma.ResponseNamespace); + Assert.AreEqual(SoapBindingUse.Literal, srma.Use); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } +#endif + + [Test] + public void WrapProxyType() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ProxyType = typeof(FakeProxyClass); + wspf.ServiceInterface = typeof(IHelloWorld); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + [Test] + public void ConfigureSoapHttpClientProtocolWithServiceUri() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/configurableFactory.xml"); + ContextRegistry.Clear(); + ContextRegistry.RegisterContext(ctx); + + object proxy = ctx.GetObject("withServiceUri"); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + SoapHttpClientProtocol shcp = proxy as SoapHttpClientProtocol; + Assert.AreEqual("http://www.springframework.org/", shcp.Url); + Assert.AreEqual(10000, shcp.Timeout); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + [Test] + public void ConfigureSoapHttpClientProtocolWithProxyType() + { + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/configurableFactory.xml"); + ContextRegistry.Clear(); + ContextRegistry.RegisterContext(ctx); + + object proxy = ctx.GetObject("withProxyType"); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + SoapHttpClientProtocol shcp = proxy as SoapHttpClientProtocol; + Assert.AreEqual("http://www.springframework.org/", shcp.Url); + Assert.AreEqual(10000, shcp.Timeout); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + [Test] + public void UseCustomClientProtocolType() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ServiceUri = new AssemblyResource("assembly://Spring.Services.Tests/Spring.Data.Spring.Web.Services/document-literal.wsdl"); + wspf.ServiceInterface = typeof(IHelloWorld); + wspf.WebServiceProxyBaseType = typeof(CustomClientProtocol); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IHelloWorld); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + Assert.IsTrue(proxy is CustomClientProtocol); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + #region NestedSchema + + [Test] + public void NestedSchema() + { + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + // cf Post Build Events + wspf.ServiceUri = new FileSystemResource("file://~/Spring/Web/Services/nestedSchema.wsdl"); + wspf.ServiceInterface = typeof(INestedSchema); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is INestedSchema); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + // Try to instantiate the proxy type + ObjectUtils.InstantiateType(proxyType); + } + + public interface INestedSchema + { + string testMethod(UserCredentials userCredentials); + } + + public class UserCredentials + { + } + + #endregion + +#if NET_3_0 + public void WcfBasicHttpBinding() + { + using (ServiceHost host = new ServiceHost(typeof(WcfServiceImpl), new Uri("http://localhost:8000/WcfBasicHttpBinding/Service"))) + { + host.AddServiceEndpoint(typeof(IWcfService), new BasicHttpBinding(), string.Empty); + + ServiceMetadataBehavior smb = new ServiceMetadataBehavior(); + smb.HttpGetEnabled = true; + host.Description.Behaviors.Add(smb); + + host.Open(); + + WebServiceProxyFactory wspf = new WebServiceProxyFactory(); + wspf.ServiceUri = new UrlResource("http://localhost:8000/WcfBasicHttpBinding/Service"); + wspf.ServiceInterface = typeof(IWcfService); + wspf.AfterPropertiesSet(); + + object proxy = wspf.GetObject(); + Assert.IsNotNull(proxy); + + Type proxyType = proxy.GetType(); + Assert.IsTrue(proxy is IWcfService); + Assert.IsTrue(proxy is SoapHttpClientProtocol); + + IWcfService srv = proxy as IWcfService; + Assert.AreEqual("5", srv.WcfMethod(5)); + } + } + + [ServiceContract(Namespace="http://www.springframework.net/")] + public interface IWcfService + { + [OperationContract] + string WcfMethod(int count); + } + + public class WcfServiceImpl : IWcfService + { + public string WcfMethod(int count) + { + return count.ToString(); + } + } +#endif + + #region Test Classes + + public interface IHelloWorld + { + string SayHelloWorld(); + string SayHello(string name); + void LogHelloWorld(); + void LogHello(string name); + } + + /// + /// Fake Web Service proxy that does not implement any interface. + /// + /// + /// Note that methods matchs IHelloWorld interface methods. + /// + [WebServiceBinding(Name="FakeWebService")] + public class FakeProxyClass : SoapHttpClientProtocol + { + public FakeProxyClass() + { + this.Url = "http://www.fcporto.pt/"; + } + + public string SayHelloWorld() + { + return "Hello World !"; + } + + public string SayHello(string name) + { + return String.Format("Hello {0} !", name); + } + + public void LogHelloWorld() + { + } + + public void LogHello(string name) + { + } + } + + public class CustomClientProtocol : SoapHttpClientProtocol + { + } + + #endregion + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/ApplicationHostTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/ApplicationHostTest.cs new file mode 100644 index 00000000..e250c709 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/ApplicationHostTest.cs @@ -0,0 +1,76 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System; +using System.Reflection; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Objects.Factory.Config; +using Spring.Services.WindowsService.Common; + +namespace Spring.Services.WindowsService.Common +{ + [TestFixture] + public class ApplicationHostTest + { + ApplicationHost host; + IConfigurableListableObjectFactory factory; + IDynamicMock factoryMock; + + private ApplicationHost NewApplicationHost (AppDomain domain, IConfigurableListableObjectFactory factory) + { + BindingFlags InstantiationBindingFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance; + ConstructorInfo c = typeof(ApplicationHost).GetConstructor(InstantiationBindingFlags, null, new Type[] {typeof (AppDomain), factory.GetType()} , null); + return (ApplicationHost) c.Invoke(new object[] {domain, factory}); + } + + [SetUp] + public void SetUp () + { + factoryMock = new DynamicMock(typeof(IConfigurableListableObjectFactory)); + factory = (IConfigurableListableObjectFactory) factoryMock.Object; + host = NewApplicationHost (AppDomain.CurrentDomain, factory); + } + + [TearDown] + public void TearDown () + { + factoryMock.Verify (); + } + + [Test] + public void WhenStartedPreInstantiateSingletonsOnFactory() + { + factoryMock.Expect("PreInstantiateSingletons"); + host.Start(); + } + + [Test] + public void WhenStoppedDestroySingletons() + { + host.Start(); + factoryMock.Expect("Dispose"); + host.Stop(); + } + + [Test] + public void GivesAnInfiniteLifetimeByPreventingALeaseFromBeingCreated () + { + Assert.IsNull(host.InitializeLifetimeService()); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/ApplicationTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/ApplicationTest.cs new file mode 100644 index 00000000..0a9feb2c --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/ApplicationTest.cs @@ -0,0 +1,112 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System.Collections.Specialized; +using System.IO; +using NUnit.Framework; +using Spring.Services.WindowsService.Common; + +namespace Spring.Services.WindowsService.Common +{ + [TestFixture] + public class ApplicationTest + { + Application application; + + [SetUp] + public void SetUp () + { + application = new Application("testApp"); + } + + [Test] + public void ApplicationFullPathEqualsApplicationBase () + { + Assert.AreEqual(application.FullPath, + application.DomainSetup.ApplicationBase, + "unexpected application base"); + } + + [Test] + public void DefaultPrivateBinPathIsBin() + { + Assert.AreEqual( + "bin", + application.DomainSetup.PrivateBinPath, + "bin subdirectory not in private path"); + } + + [Test] + public void ConfigurationFileNameIsHardCodedAsServiceDotConfig() + { + string expected = + Path.Combine(application.FullPath, Application.ServiceConfig); + Assert.AreEqual(expected, application.DomainSetup.ConfigurationFile, + "unexpected configuration file name"); + + } + + [Test] + public void AFullPathAsNameIsReducedToLastPart () + { + string fullpath = @"c:\spring\deploy\sample"; + application = new Application(fullpath); + Assert.AreEqual("sample", application.Name); + Assert.AreEqual( + Path.Combine (fullpath, Application.ServiceConfig), + application.DomainSetup.ConfigurationFile); + } + + [Test] + public void BaseDirectoryCanBeSpecifiedWithFullPath () + { + application = new Application(@"c:\spring\deploy\sample"); + Assert.AreEqual(@"c:\spring\deploy\sample", application.ApplicationBase); + Assert.AreEqual("sample", application.Name); + } + + [Test] + public void DomainSetupIsConfiguredToShadowCopyFiles () + { + Assert.AreEqual("true", application.DomainSetup.ShadowCopyFiles); + } + + [Test] + public void HashCodeIsTheSameOfTheFullPath () + { + Assert.AreEqual(application.FullPath.GetHashCode(), application.GetHashCode()); + } + + [Test] + public void ShouldHonoursEqualsContract() + { + Assert.IsFalse(application.Equals(new object())); + Assert.IsFalse(application.Equals(null)); + Assert.IsTrue(application.Equals(application)); + Assert.IsTrue(application.Equals(new Application(application.FullPath))); + } + + [Test] + public void DefaultConfigurerAllowToReferenceTheApplicationFullPathAndName () + { + NameValueCollection collection = Application.DefaultConfigurer(application).Properties; + Assert.AreEqual(application.FullPath, collection[Application.ApplicationFullPathPlaceholder]); + Assert.AreEqual(application.Name, collection[Application.ApplicationNamePlaceholder]); + Assert.AreEqual(2, collection.Count); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/AggregatedDeployEventDispatcherTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/AggregatedDeployEventDispatcherTest.cs new file mode 100644 index 00000000..a1405c02 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/AggregatedDeployEventDispatcherTest.cs @@ -0,0 +1,40 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Services.WindowsService.Common.Deploy; + +namespace Spring.Services.WindowsService.Common.Deploy +{ + [TestFixture] + public class AggregatedDeployEventDispatcherTest + { + [Test] + public void DisposeTriggerWhenDisposed () + { + DynamicMock mock = new DynamicMock(typeof(ITrigger)); + ITrigger trigger = (ITrigger) mock.Object; + + mock.Expect("Dispose"); + AggregatedDeployEventDispatcher dispatcher = new AggregatedDeployEventDispatcher(trigger); + dispatcher.Dispose(); + mock.Verify(); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/ApplicationWatcherManagerTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/ApplicationWatcherManagerTest.cs new file mode 100644 index 00000000..6fc0cdde --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/ApplicationWatcherManagerTest.cs @@ -0,0 +1,203 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System; +using System.Collections; +using System.IO; +using System.Reflection; +using System.Threading; +using log4net; +using NUnit.Framework; +using Spring.Services.WindowsService.Common.Deploy.FileSystem; +using Spring.Threading; + +namespace Spring.Services.WindowsService.Common.Deploy +{ + [TestFixture] + public class ApplicationWatcherManagerTest + { + ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private IApplication application; + private string appName; + private ApplicationWatcherManager manager; + private ISync reconfigured; + private int nReconfigured; + + [SetUp] + public void SetUp () + { + nReconfigured = 0; + appName = Guid.NewGuid().ToString(); + application = new Application(appName); + Directory.CreateDirectory(application.FullPath); + manager = new ApplicationWatcherManager(application, 10); + manager.Reconfigured += new EventHandler(manager_Reconfigured); + reconfigured = new Semaphore(0); + } + + [TearDown] + public void TearDown () + { + manager.Dispose (); + Directory.Delete(application.FullPath, true); + } + + [Test] + public void RecreatesTheWatcherWhenItsDefinitonChanges() + { + Assert.IsTrue(manager.Watcher is NullApplicationWatcher); + File.Copy("Data/Xml/watcher-0.xml", application.WatcherXmlFullPath, true); + reconfigured.Acquire(); + Assert.IsTrue(manager.Watcher is NullApplicationWatcher); + TestUtils.SafeCopyFile("Data/Xml/watcher-1.xml", application.WatcherXmlFullPath, true); + reconfigured.Acquire(); + Assert.IsTrue(manager.Watcher is FileSystemApplicationWatcher); + TestUtils.SafeDeleteFile(application.WatcherXmlFullPath); + reconfigured.Acquire(); + Assert.IsTrue(manager.Watcher is NullApplicationWatcher); + Assert.AreEqual(3, nReconfigured); + } + + class Watcher : IApplicationWatcher + { + IApplication application; + public bool started, stopped, disposed, filters; + + public IApplication Application + { + get { return application; } + set { application = value; } + } + + public void StartWatching (IDeployEventDispatcher dispatcher) + { + started = true; + } + + public void StopWatching () + { + stopped = true; + } + + public void SetFilters (IList allows, IList disallows) + { + filters = true; + } + + public void Dispose () + { + disposed = true; + } + } + + class Factory : IApplicationWatcherFactory + { + public Watcher lastWatcher; + public int called = 0; + + public IApplicationWatcher CreateApplicationWatcher (IApplication application) + { + called++; + lastWatcher = new Watcher(); + lastWatcher.Application = application; + return lastWatcher; + } + + public ApplicationWatcherManager CreateApplicationWatcherMonitor (IApplication application) + { + throw new NotImplementedException (); + } + } + + [Test] + public void StopsAndDisposeTheOldWatcherAndStartTheNewWatcherWhenItChanges () + { + TestUtils.ConfigureLog4Net(); + IApplication application = new Application(appName); + Factory factory = new Factory(); + ApplicationWatcherManager m = + new ApplicationWatcherManager(factory, application, 100); + m.StartWatching(new ForwardingDeployEventDispatcher()); + manager.Reconfigured -= new EventHandler(manager_Reconfigured); + m.Reconfigured += new EventHandler(manager_Reconfigured); + + + File.Copy("Data/Xml/watcher-0.xml", application.WatcherXmlFullPath, true); + reconfigured.Acquire(); + log.Debug("first empty definition copied"); + Assert.IsTrue(manager.Watcher is NullApplicationWatcher); + Watcher watcher1 = factory.lastWatcher; + Assert.IsTrue(watcher1.started); + + bool ok = true; + do + { + try + { + File.Copy("Data/Xml/watcher-1.xml", application.WatcherXmlFullPath, true); + } + catch (Exception e) + { + ok = false; + Thread.Sleep(100); + log.Error("error copying file", e); + } + } while (ok == false); + reconfigured.Acquire(); + log.Debug("good definition copied"); + while (!(manager.Watcher is FileSystemApplicationWatcher)) + Thread.Sleep(10); + Watcher watcher2 = factory.lastWatcher; + Assert.IsTrue(watcher2.started); + Assert.IsTrue(watcher1.stopped); + Assert.IsTrue(watcher1.disposed); + Assert.IsTrue(manager.Watcher is FileSystemApplicationWatcher); + + do + { + try + { + File.Delete(application.WatcherXmlFullPath); + } + catch (Exception e) + { + log.Error("error cancelling file", e); + } + } while (File.Exists(application.WatcherXmlFullPath)); + + reconfigured.Acquire(); + log.Debug("good definition deleted"); + while (!(manager.Watcher is NullApplicationWatcher)) + Thread.Sleep(10); + Watcher watcher3 = factory.lastWatcher; + Assert.IsTrue(watcher3.started); + Assert.IsTrue(watcher2.stopped); + Assert.IsTrue(watcher2.disposed); + + Assert.IsTrue(manager.Watcher is NullApplicationWatcher); + Assert.AreEqual(3, nReconfigured); + Assert.AreEqual(3 + 1, factory.called); // +1 in ApplicationWatcherManager ctor + } + + private void manager_Reconfigured(object sender, EventArgs e) + { + ++nReconfigured; + log.Debug("releasing reconfigured sync. n. of reconfigs: " + nReconfigured); + reconfigured.Release(); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/DeployEventAggregatorTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/DeployEventAggregatorTest.cs new file mode 100644 index 00000000..2aecaf03 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/DeployEventAggregatorTest.cs @@ -0,0 +1,99 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using NUnit.Framework; +using Spring.Services.WindowsService.Common.Deploy; + +namespace Spring.Services.WindowsService.Common.Deploy +{ + [TestFixture] + public class DeployEventAggregatorTest + { + DeployEventAggregator aggregator; + + [SetUp] + public void SetUp () + { + aggregator = new DeployEventAggregator(); + } + + [Test] + public void AdditionsAbsorbUpdates () + { + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationAdded)); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationUpdated)); + Assert.AreEqual(DeployEventType.ApplicationAdded, aggregator.Result.EventType); + } + + [Test] + public void RemovalsEliminateAdditions () + { + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationAdded)); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationRemoved)); + Assert.AreEqual(DeployEventType.ApplicationNeverAdded, aggregator.Result.EventType); + } + + [Test] + public void RemovalsEliminateAdditionsButThenCanAdd () + { + RemovalsEliminateAdditions(); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationAdded)); + Assert.AreEqual(DeployEventType.ApplicationAdded, aggregator.Result.EventType); + } + + [Test] + public void MultipleEventsOfTheSameTypeCollapse () + { + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationAdded)); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationAdded)); + Assert.AreEqual(DeployEventType.ApplicationAdded, aggregator.Result.EventType); + } + + [Test] + public void RemovalsSupercedesUpdatesButAdditionSuperceedesThem () + { + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationUpdated)); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationRemoved)); + Assert.AreEqual(DeployEventType.ApplicationRemoved, aggregator.Result.EventType); + // remove + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationAdded)); + Assert.AreEqual(DeployEventType.ApplicationUpdated, aggregator.Result.EventType); + } + + [Test] + public void ARemovalFollowedByAnAdditionResultInAnUpdate () + { + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationRemoved)); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationAdded)); + Assert.AreEqual(DeployEventType.ApplicationUpdated, aggregator.Result.EventType); + } + + [Test] + public void ARemovalAfterAnAdditionFrozeStateUntilASecondAddition() + { + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationAdded)); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationUpdated)); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationRemoved)); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationUpdated)); + Assert.AreEqual(DeployEventType.ApplicationNone, aggregator.Result.EventType); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationAdded)); + Assert.AreEqual(DeployEventType.ApplicationAdded, aggregator.Result.EventType); + aggregator.Aggregate(new DeployEventArgs(null, DeployEventType.ApplicationUpdated)); + Assert.AreEqual(DeployEventType.ApplicationAdded, aggregator.Result.EventType); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/DeployManagerTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/DeployManagerTest.cs new file mode 100644 index 00000000..5971a864 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/DeployManagerTest.cs @@ -0,0 +1,256 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion + +using System; +using System.Collections; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using DotNetMock.Dynamic; +using NUnit.Framework; +using Spring.Services.WindowsService.Common; +using Spring.Services.WindowsService.Common.Deploy; +using Spring.Threading; + +namespace Spring.Services.WindowsService.Common.Deploy +{ + internal class MyDeploy : IDeployLocation + { + public event DeployEventHandler DeployEvent; + private ArrayList applications = new ArrayList (); + internal bool wasDisposed = false; + + public void Raise (DeployEventArgs eventArgs) + { + if (DeployEvent != null) + DeployEvent (this, eventArgs); + } + + public IList Applications + { + get { return applications; } + } + + public void Add (IApplication application) + { + Raise (new DeployEventArgs (application, DeployEventType.ApplicationAdded)); + } + + public void Remove (IApplication application) + { + Raise (new DeployEventArgs (application, DeployEventType.ApplicationRemoved)); + } + + public void Update (IApplication application) + { + Raise(new DeployEventArgs(application, DeployEventType.ApplicationUpdated)); + } + + public void Dispose () + { + wasDisposed = true; + } + } + + [TestFixture] + public class DeployManagerTest + { + MyDeploy deployLocation; + IDeployer deployer; + DeployManager deployManager; + IDynamicMock deployerMock; + bool needVerifyDeployer = true; + + class NullSpringDeployer : ISpringAssembliesDeployer + { + public void DeployForApplication (IApplication application) + { + } + } + + [SetUp] + public void SetUp () + { + deployerMock = new DynamicMock(typeof(IDeployer)); + deployerMock.Strict = false; + deployer = (IDeployer) deployerMock.Object; + deployLocation = new MyDeploy (); + deployManager = new DeployManager (new DirectExecutor(), new NullSpringDeployer(), deployLocation, deployer); + } + + [TearDown] + public void TearDown () + { + if (needVerifyDeployer) + deployerMock.Verify(); + } + + [Test] + public void ItIsOKToStopWhenNotStarted () + { + needVerifyDeployer = false; + deployManager.Stop(); + } + + [Test] + public void WhenStartedDeploysExisitingApplications () + { + foreach (IApplication application in deployManager.DeployLocation.Applications) + { + Assert.IsFalse (deployManager.IsDeployed (application), "application has not been deployed"); + } + deployLocation.Applications.Add (new Application ("testApp")); + deployManager.Start(); + foreach (IApplication application in deployLocation.Applications) + { + Assert.IsTrue (deployManager.IsDeployed (application), "application has not been deployed"); + } + DeployInformation[] infos = deployManager.DeployInformations; + Assert.AreEqual(deployLocation.Applications.Count, infos.Length); + foreach (DeployInformation info in infos) + { + Assert.AreEqual(DeployStatus.Deployed, info.Status); + } + deployManager.Stop(); + } + + + [Test] + public void WhenAnApplicationGetsCreatedDeploysIt () + { + NewAssertedDeployed (); + } + + [Test] + public void WhenAnApplicationGetsRemovedUndeployIt () + { + IApplication application = NewAssertedDeployed (); + deployerMock.Expect("UnDeploy"); + deployLocation.Remove (application); + Assert.IsFalse (deployManager.IsDeployed (application), "application has not been un-deployed"); + } + + [Test] + public void WhenAnApplicationGetsUpdatedUndeployAndRedeployIt () + { + IApplication application = NewAssertedDeployed (); + deployerMock.Expect("UnDeploy"); + deployerMock.Expect("Deploy"); + deployLocation.Update(application); + Assert.IsTrue(deployManager.IsDeployed(application)); + } + + [Test] + public void DisposeLocationAndDeployerOnDispose () + { + deployerMock.Expect("Dispose"); + deployManager.Dispose(); + Assert.IsTrue(deployLocation.wasDisposed); + } + + [Test] + public void IfAnApplicationCannotBeDeployedTheOccurredExceptionWillBeAvailableToBeQueried () + { + string noDeployReason = ""; + Exception exception = new Exception(noDeployReason); + + IApplication application = new Application("testApp"); + deployerMock.ExpectAndThrow("Deploy", exception, application); + deployManager.Start(); + deployLocation.Add(application); + Assert.IsFalse(deployManager.IsDeployed(application), "application considered deployed"); + deployManager.Stop(); + Assert.IsFalse(deployManager.IsDeployed(application), "application considered deployed"); + + + DeployInformation[] infos = deployManager.DeployInformations; + Assert.AreEqual(1, infos.Length); + Assert.AreEqual(DeployStatus.CannotDeploy, infos[0].Status); + Assert.AreEqual(exception, infos[0].Error); + } + + [Test] + public void AfterAnApplicationHasBeenRemovedItsInfosCannotBeQueried () + { + IApplication application = new Application("testApp"); + deployerMock.Expect("Deploy", application); + deployManager.Start(); + deployLocation.Add(application); + Assert.IsTrue(deployManager.IsDeployed(application), "application considered deployed"); + + deployLocation.Remove(application); + DeployInformation[] infos = deployManager.DeployInformations; + Assert.AreEqual(0, infos.Length); + deployManager.Stop(); + } + + [Test] + public void KeepUndeployingApplicationsEvenIfThereAreExceptionsWhileUndeploying() + { + IApplication application = NewAssertedDeployed (); + deployerMock.ExpectAndThrow("UnDeploy", new Exception()); + deployManager.Start(); + deployManager.Stop(); + Assert.IsFalse(deployManager.IsDeployed(application), "failed to undeploy application still considered deployed"); + } + + private IApplication NewAssertedDeployed () + { + IApplication application = new Application ("testApp"); + deployLocation.Add (application); + Assert.IsTrue (deployManager.IsDeployed (application), "application has not been deployed"); + deployerMock.Expect("Deploy"); + return application; + } + + } + + [TestFixture] + public class DeployInformationTest + { + [Test] + public void SerializationOfDeployInformation () + { + string tempDir = Environment.GetEnvironmentVariable("TEMP"); + string tempFilename = Path.Combine(tempDir,"foo.dat"); + FileInfo file = new FileInfo (tempFilename); + FileStream instream = null; + try + { + DeployInformation ex = new DeployInformation(new Application("testApp"), DeployStatus.CannotDeploy, new Exception("bar")); + Stream outstream = file.OpenWrite (); + new BinaryFormatter ().Serialize (outstream, ex); + outstream.Flush (); + outstream.Close (); + instream = file.OpenRead (); + DeployInformation inex = (DeployInformation) new BinaryFormatter ().Deserialize (instream); + Assert.AreEqual(DeployStatus.CannotDeploy, inex.Status); + Assert.AreEqual("testApp", inex.Application.Name); + Assert.AreEqual("bar", inex.Error.Message); + } + finally + { + try + { + instream.Close (); + file.Delete (); + } + catch {} + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/DefaultApplicationWatcherFactoryTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/DefaultApplicationWatcherFactoryTest.cs new file mode 100644 index 00000000..c5b20eff --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/DefaultApplicationWatcherFactoryTest.cs @@ -0,0 +1,80 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System; +using System.IO; +using NUnit.Framework; + +namespace Spring.Services.WindowsService.Common.Deploy.FileSystem +{ + [TestFixture] + public class DefaultApplicationWatcherFactoryTest + { + string xml = String.Format(@" + + + + + + + + + + **/** + {1} + {2} + + + + +", DefaultApplicationWatcherFactory.InjectedApplicationName, + "${spring.services.application.name}", + "${spring.services.application.fullpath}"); + + private string xmlFile; + private IApplication application; + private IApplicationWatcher w; + + [TearDown] + public void TearDown () + { + File.Delete(xmlFile); + } + + [SetUp] + public void SetUp() + { + xmlFile = Application.WatcherXml; + using (StreamWriter writer = File.CreateText(xmlFile)) + { + writer.WriteLine(xml); + } + application = new Application(Path.GetFullPath(".")); + w = DefaultApplicationWatcherFactory.Instance.CreateApplicationWatcher(application); + } + + [Test] + public void InjectTheApplicationToBeWatchedInTheApplicationContextDefinedForTheWatcher () + { + Assert.IsNotNull(w); + Assert.IsTrue(w is FileSystemApplicationWatcher, "not a " + typeof(FileSystemApplicationWatcher)); + Assert.AreSame(application, w.Application); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/FileSystemApplicationWatcherTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/FileSystemApplicationWatcherTest.cs new file mode 100644 index 00000000..f7cb248b --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/FileSystemApplicationWatcherTest.cs @@ -0,0 +1,353 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System; +using System.Collections; +using System.IO; +using System.Reflection; +using log4net; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Objects.Factory.Xml; +using Spring.Threading; + +namespace Spring.Services.WindowsService.Common.Deploy.FileSystem +{ + public class TestingDispatcher : IDeployEventDispatcher + { + ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + IList events = new ArrayList(); + ISync wait = new Semaphore(0); + + public void PerformDispatch (object sender) + { + lock (this) + { + log.Debug(String.Format("performing dispatch: {0} events", events.Count)); + foreach (DeployEventArgs eventArgs in events) + { + if (DeployEvent != null) + { + log.Debug(String.Format("raising event {0}", eventArgs.EventType)); + DeployEvent(sender, eventArgs); + } + } + log.Debug(String.Format("clearing events list", events.Count)); + events.Clear(); + } + } + + public void Dispatch (IDeployLocation sender, DeployEventType eventType, IApplication application) + { + lock (this) + { + log.Debug(String.Format("collecting event {0} (collected so far: {1})", eventType, events.Count)); + events.Add(new DeployEventArgs(application, DeployEventType.ApplicationUpdated)); + wait.Release(); + } + } + + public event DeployEventHandler DeployEvent; + + public void Dispose () + { + } + + public void Wait() + { + wait.Acquire(); + } + + public void WaitAndPerformDispatch(object o) + { + Wait(); + PerformDispatch(o); + } + } + + + // problems with dynamic mocks: too many calls + public class MockFileSystemEventFilter : IFileSystemEventFilter + { + public bool called = false; + public bool accept; + + public MockFileSystemEventFilter (bool accept) + { + this.accept = accept; + } + + public bool Filter (FileSystemEventArgs args) + { + called = true; + return accept; + } + } + + + [TestFixture] + public class FilteringSupportTest + { + IList allow; + IList disallow; + FileSystemEventArgs anEvent; + FilteringSupport support; + FileSystemEventArgs springAssemblyEvent; + IApplication application; + + [SetUp] + public void SetUp () + { + allow = new ArrayList(); + disallow = new ArrayList(); + application = new Application(Path.GetFullPath("foo")); + anEvent = new FileSystemEventArgs(WatcherChangeTypes.All, "foo", "foo.dll"); + springAssemblyEvent = + new FileSystemEventArgs(WatcherChangeTypes.All, + Path.GetFullPath(SpringAssembliesDeployer.PrivateBinPathPrefix + "foo"), "foo.dll"); + support = new FilteringSupport(allow, disallow, SpringAssembliesDeployer.DisallowFilter); + } + + [Test] + public void AnEventIsAcceptedIfThereIsNoFilterDefined () + { + Assert.IsTrue(support.Accept(anEvent), "event not accepted with no filter defined"); + } + + [Test] + public void EventsRelatedToSpringAssembliesAreNeverAccepted () + { + Assert.IsFalse(support.Accept(springAssemblyEvent), "spring assemblies event accepted"); + allow.Add(new RegularExpressionFilter( + String.Format ("**/{0}*/**", SpringAssembliesDeployer.PrivateBinPathPrefix), true)); + Assert.IsFalse(support.Accept(springAssemblyEvent), "spring assemblies event accepted"); + } + + [Test] + public void AnEventIsAcceptedIfAtLeastOneAllowFilterFiltersIt () + { + + allow.Add(new MockFileSystemEventFilter(false)); + Assert.IsFalse(support.Accept(anEvent), "allowed when no allow filter allows"); + allow.Add(new MockFileSystemEventFilter(true)); + Assert.IsTrue(support.Accept(anEvent), "not allowed when one allow filter allows"); + allow.Add(new MockFileSystemEventFilter(false)); + Assert.IsTrue(support.Accept(anEvent), "not allowed when one allow filter allows"); + } + + [Test] + public void AnEventIsNotAcceptedIfAtLeastOneDisallowFilterFiltersIt () + { + disallow.Add(new MockFileSystemEventFilter(false)); + Assert.IsTrue(support.Accept(anEvent), "disallowed when no disallow filter disallows"); + disallow.Add(new MockFileSystemEventFilter(true)); + Assert.IsFalse(support.Accept(anEvent), "allowed when one disallow filter disallows"); + disallow.Add(new MockFileSystemEventFilter(false)); + Assert.IsFalse(support.Accept(anEvent), "allowed when one disallow filter disallows"); + } + + [Test] + public void ADisallowingFilterDominatesAllowFilters () + { + disallow.Add(new MockFileSystemEventFilter(true)); + allow.Add(new MockFileSystemEventFilter(false)); + Assert.IsFalse(support.Accept(anEvent), "event accepted when a disallow filter disallows"); + allow.Add(new MockFileSystemEventFilter(true)); + Assert.IsFalse(support.Accept(anEvent), "event accepted when a disallow filter disallows"); + } + + [Test] + public void WithNoDisallowingFilterItDependsOnThePresenceOfAnAllowingFilters () + { + disallow.Add(new MockFileSystemEventFilter(false)); + allow.Add(new MockFileSystemEventFilter(false)); + Assert.IsFalse(support.Accept(anEvent), "event accepted when a disallow filter disallows"); + allow.Add(new MockFileSystemEventFilter(true)); + Assert.IsTrue(support.Accept(anEvent), "event not accepted when an allow filter allows and no disallow filter disallows"); + disallow.Add(new MockFileSystemEventFilter(true)); + Assert.IsFalse(support.Accept(anEvent), "event accepted when a disallow filter disallows"); + } + + [Test] + public void IgnoreEventsRegardingTheDirectoryOfTheApplication() + { + FileSystemEventArgs e = + new FileSystemEventArgs(WatcherChangeTypes.All, + Directory.GetCurrentDirectory(), "foo"); + Assert.IsFalse(support.IsApplicationEvent(e, application, true)); + + e = new FileSystemEventArgs(WatcherChangeTypes.All, + Path.GetFullPath("foo"), "foo.dll"); + Assert.IsTrue(support.IsApplicationEvent(e, application, true)); + + e = new FileSystemEventArgs(WatcherChangeTypes.All, + Directory.GetCurrentDirectory(), "foo/foo.dll"); + Assert.IsTrue(support.IsApplicationEvent(e, application, true)); + + e = new FileSystemApplicationWatcher(application, + new NullWatcherConfigurer()).GenericApplicationEvent; + Assert.IsTrue(support.IsApplicationEvent(e, application, true)); + } + + [Test] + public void CanDecideIfAnEventIsAnApplicationEventIgnoringCase() + { + FileSystemEventArgs e = + new FileSystemEventArgs(WatcherChangeTypes.All, + Directory.GetCurrentDirectory(), "FOO/FOO.DLL"); + application = new Application(Path.GetFullPath("foo").ToUpper()); + Assert.IsTrue(support.IsApplicationEvent(e, application, true), "failed to match without case"); + e = new FileSystemEventArgs(WatcherChangeTypes.All, "C:/", "foo/foo.dll"); + Assert.IsFalse(support.IsApplicationEvent(e, application, false), "unexpected match with case"); + } + + } + + + [TestFixture] + public class FileSystemApplicationWatcherTest + { + ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + Latch eventLatch; + string appName, anotherAppName; + string appFullPath, anotherAppFullPath; + private IDeployEventDispatcher dispatcher; + private FileSystemApplicationWatcher watcher; + private IApplication application; + private bool dispatched; + private ISync controlledEventLatch; + + + [SetUp] + public void SetUp () + { + TestUtils.ConfigureLog4Net(); + dispatched = false; + appName = Guid.NewGuid().ToString(); + appFullPath = Path.GetFullPath(appName); + anotherAppName = Guid.NewGuid().ToString(); + anotherAppFullPath = Path.GetFullPath(anotherAppName); + Directory.CreateDirectory(appFullPath); + Directory.CreateDirectory(anotherAppFullPath); + eventLatch = new Latch(); + controlledEventLatch = new Latch(); + dispatcher = new ForwardingDeployEventDispatcher(); + application = new Application(appFullPath); + dispatcher.DeployEvent += new DeployEventHandler(dispatcher_DeployEvent); + watcher = new FileSystemApplicationWatcher(application); + } + + [TearDown] + public void TearDown () + { + if (watcher != null) + { + watcher.StopWatching(); + watcher.Dispose(); + } + dispatcher.DeployEvent -= new DeployEventHandler(dispatcher_DeployEvent); + Directory.Delete(appFullPath, true); + Directory.Delete(anotherAppFullPath, true); + } + + [Test] + public void ProduceUpdateEventsForWatchedApplication() + { + TestingDispatcher controlledDispatcher = new TestingDispatcher(); + controlledDispatcher.DeployEvent += new DeployEventHandler(controlledDispatcher_DeployEvent); + watcher.StartWatching(controlledDispatcher); + string subDir = Path.Combine(appFullPath, "foo"); + Directory.CreateDirectory(subDir); + controlledDispatcher.WaitAndPerformDispatch(null); + + using (File.Create(Path.Combine (subDir, "foo.bar"))) {} + controlledDispatcher.WaitAndPerformDispatch(null); + } + + [Test] + public void DoNotProduceUpdateEventsForOtherApplications() + { + TestingDispatcher controlledDispatcher = new TestingDispatcher(); + controlledDispatcher.DeployEvent += new DeployEventHandler(controlledDispatcher_DeployEvent); + watcher.StartWatching(controlledDispatcher); + string subDir = Path.Combine(anotherAppFullPath, "foo"); + Directory.CreateDirectory(subDir); + using (File.Create(Path.Combine (subDir, "foo.bar"))) {} + dispatched = false; + controlledDispatcher.PerformDispatch(null); + Assert.IsFalse(dispatched); + } + + [Test] + public void UsesEventFiltersToAllowAnEventToPropagated () + { + MockFileSystemEventFilter filter = new MockFileSystemEventFilter(true); + watcher.StartWatching(dispatcher); + watcher.AddAllowFilter (filter); + string subDir = Path.Combine(appFullPath, "foo"); + Directory.CreateDirectory(subDir); + using (File.Create(Path.Combine (appFullPath, "foo.bar"))) {} + eventLatch.Acquire(); + Assert.IsTrue(filter.called); + } + + [Test] + public void CanBeConfiguredToIncludeOrExcludePathsForEvents () + { + string simple = "Data/Xml/watcher-simple.xml"; + XmlObjectFactory f = new XmlObjectFactory(new FileSystemResource(simple)); + f.RegisterSingleton(DefaultApplicationWatcherFactory.InjectedApplicationName, application); + watcher = f["watcher"] as FileSystemApplicationWatcher; + Assert.IsNotNull(watcher, + String.Format("test file [{0}] should define a file sistem resource!", simple)); + Assert.AreEqual(1, watcher.Excludes.Count); + Assert.AreEqual(1, watcher.Includes.Count); + + watcher.StartWatching(dispatcher); + + // propagated + string subDir = Path.Combine(appFullPath, "foo"); + Directory.CreateDirectory(subDir); + using (File.Create(Path.Combine (subDir, "foo.bar"))) {} + eventLatch.Acquire(); + Assert.IsFalse(dispatched); + } + + [Test] + public void ByDefaultTheCaseIsIngoredButCanBeConfigured () + { + Assert.IsTrue(watcher.IgnoreCase); + watcher.IgnoreCase = false; + Assert.IsFalse(watcher.IgnoreCase); + } + + private void dispatcher_DeployEvent(object sender, DeployEventArgs args) + { + log.Debug("releasing latch"); + eventLatch.Release(); + log.Debug("latch released"); + } + + private void controlledDispatcher_DeployEvent(object sender, DeployEventArgs args) + { + dispatched = true; + log.Debug(String.Format("application = {0}", args.Application.FullPath)); + log.Debug(String.Format("anotherAppFullPath = {0}", anotherAppFullPath)); + controlledEventLatch.Release(); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/FileSystemDeployLocationTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/FileSystemDeployLocationTest.cs new file mode 100644 index 00000000..b29dfe67 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/FileSystemDeployLocationTest.cs @@ -0,0 +1,577 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System; +using System.IO; +using System.Reflection; +using System.Threading; +using DotNetMock.Dynamic; +using log4net; +using NUnit.Framework; +using Spring.Threading; +using Spring.Services.WindowsService.Common; + +namespace Spring.Services.WindowsService.Common.Deploy.FileSystem +{ + public abstract class FileSystemDeployLocationTestsBase + { + protected ILog log = LogManager.GetLogger (MethodBase.GetCurrentMethod ().DeclaringType); + + protected string deployPath; + protected string deploy = "deploy"; + protected string sampleDir, sampleDir2; + + protected FileSystemDeployLocation location; + protected TestingHandler handler; + protected TestingHandler dontUseHandler; + protected IDeployEventDispatcher dispatcher; + protected ITrigger trigger; + protected string serviceXml; + protected string watcherXml; + protected Latch triggeredLatch; + + protected void SetUp_ () + { + deployPath = Guid.NewGuid ().ToString (); + sampleDir = Path.Combine (deployPath, deploy); + sampleDir2 = Path.Combine (deployPath, deploy + "2"); + serviceXml = Path.Combine (sampleDir, Application.ServiceXml); + watcherXml = Path.Combine (sampleDir, Application.WatcherXml); + triggeredLatch = new Latch(); + trigger = new ThreadingTimerTrigger (50); + trigger.Triggered += new EventHandler(trigger_Triggered); + dispatcher = new AggregatedDeployEventDispatcher (trigger); + TestUtils.ConfigureLog4Net (); + } + + protected void AddApplication () + { + AddApplication (sampleDir); + } + + protected void AddApplication (string dir) + { + Directory.CreateDirectory (dir); + File.Copy("Data/Xml/watcher-1.xml", new Application(dir).WatcherXmlFullPath); + using (File.Create (Path.Combine (dir, Application.ServiceXml))) + {} + Assert.IsTrue (Directory.Exists (dir), "directory does not exist: " + dir); + } + + protected void trigger_Triggered (object sender, EventArgs e) + { + triggeredLatch.Release(); + } + + protected void InitHandlerAndStartLocation (ISync sync) + { + GC.SuppressFinalize (location); + InitHandler (sync); + location.StartWatching (); + } + + protected void InitHandler (ISync sync) + { + location = new FileSystemDeployLocation (dispatcher, deployPath); + handler = NewHandler (sync); + } + + protected TestingHandler NewHandler (ISync sync) + { + try + { + log.Debug("disconnecting testing handler"); + location.DeployEvent -= new DeployEventHandler (dontUseHandler.Handle); + } + catch + {} + dontUseHandler = new TestingHandler (sync); + log.Debug("connecting testing handler"); + location.DeployEvent += new DeployEventHandler (dontUseHandler.Handle); + return dontUseHandler; + } + + public void TearDown_ () + { + log.Debug("disposing location "); + if (location != null) + { + location.Dispose (); + } + try + { + TestUtils.SafeDeleteDirectory(Path.GetFullPath (deployPath)); + } + catch (Exception e) + { + log.Error("deploy folder not deleted", e); + } + try + { + TestUtils.SafeDeleteDirectory(Path.GetFullPath (deployPath)); + } + catch (Exception e) + { + log.Error("test folder not deleted", e); + } + Assert.IsFalse(Directory.Exists(Path.GetFullPath (deployPath)), "the root of the deploy directory still exist"); + } + } + + [TestFixture] + public class FileSystemDeployLocationTest_Mocked : FileSystemDeployLocationTestsBase + { + private DynamicMock factoryMock; + private DynamicMock watcherMock; + private IApplicationWatcherFactory factory; + private IApplicationWatcher watcher; + private bool deployEventDispatched; + private ISync forwardDispatcherSync; + + [SetUp] + public void SetUp () + { + base.SetUp_(); + factoryMock = new DynamicMock(typeof(IApplicationWatcherFactory)); + watcherMock = new DynamicMock(typeof(IApplicationWatcher)); + factory = (IApplicationWatcherFactory) factoryMock.Object; + watcher = (IApplicationWatcher) watcherMock.Object; + deployEventDispatched = false; + forwardDispatcherSync = new Latch(); + } + + [TearDown] + public void TearDown () + { + base.TearDown_(); + factoryMock.Verify(); + watcherMock.Verify(); + } + + [Test] + public void DisposeDispatcherOnDispose () + { + DynamicMock mock = new DynamicMock (typeof (IDeployEventDispatcher)); + IDeployEventDispatcher dispatcher = (IDeployEventDispatcher) mock.Object; + mock.Expect ("Dispose"); + IDeployLocation location = new FileSystemDeployLocation (dispatcher, deployPath); + location.Dispose (); + mock.Verify (); + } + + [Test] + public void ANewWatcherGetsCreatedAndAssociatedToAnyExistingApplication () + { + factoryMock.ExpectAndReturn("CreateApplicationWatcherMonitor", watcher); + watcherMock.Expect("StartWatching"); + AddApplication (); + location = new FileSystemDeployLocation (new ForwardingDeployEventDispatcher(), factory, deployPath, true); + } + + [Test] + public void TheAssociatedWatcherIsStoppedAndDisposedWhenAnApplicationGetsRemoved () + { + factoryMock.ExpectAndReturn("CreateApplicationWatcherMonitor", watcher); + watcherMock.Expect("StartWatching"); + watcherMock.Expect("StopWatching"); + watcherMock.Expect("Dispose"); + AddApplication(); + location = new FileSystemDeployLocation (new ForwardingDeployEventDispatcher(), factory, deployPath, true); + Directory.Delete(sampleDir, true); + triggeredLatch.Acquire(); + } + + [Test] + public void TheAssociatedWatcherIsStoppedAndDisposedOnDispose () + { + factoryMock.ExpectAndReturn("CreateApplicationWatcherMonitor", watcher); + watcherMock.Expect("StartWatching"); + watcherMock.Expect("StopWatching"); + watcherMock.Expect("Dispose"); + AddApplication(); + location = new FileSystemDeployLocation (new ForwardingDeployEventDispatcher(), factory, deployPath, true); + triggeredLatch.Acquire(); + location.Dispose(); + } + + [Test] + public void IgnoresExceptionAddingExistingApplication () + { + factoryMock.ExpectAndThrow("CreateApplicationWatcherMonitor", new Exception("exception generated to test behaviour adding application")); + AddApplication(); + location = new FileSystemDeployLocation (new ForwardingDeployEventDispatcher(), factory, deployPath, true); + } + + [Test] // Bug fix + public void NotInfiniteLoopRemovingAnApplicationWithAManagerThatThrowsExceptionWhenStopped () + { + factoryMock.ExpectAndReturn("CreateApplicationWatcherMonitor", watcher); + watcherMock.Expect("StartWatching"); + watcherMock.ExpectAndThrow("StopWatching", new Exception()); + AddApplication(); + ForwardingDeployEventDispatcher dispatcher = new ForwardingDeployEventDispatcher(); + dispatcher.DeployEvent += new DeployEventHandler(dispatcher_DeployEvent); + location = new FileSystemDeployLocation (dispatcher, factory, deployPath, true); + Directory.Delete(sampleDir, true); + forwardDispatcherSync.Acquire(); + Assert.IsTrue(deployEventDispatched, "removal not dispatched in case of error on application watcher"); + } + + [Test] // Bug fix + public void NotInfiniteLoopRemovingAnApplicationWithAManagerThatThrowsExceptionWhenDisposed () + { + factoryMock.ExpectAndReturn("CreateApplicationWatcherMonitor", watcher); + watcherMock.Expect("StartWatching"); + watcherMock.Expect("StopWatching"); + watcherMock.ExpectAndThrow("Dispose", new Exception()); + AddApplication(); + ForwardingDeployEventDispatcher dispatcher = new ForwardingDeployEventDispatcher(); + dispatcher.DeployEvent += new DeployEventHandler(dispatcher_DeployEvent); + location = new FileSystemDeployLocation (dispatcher, factory, deployPath, true); + Directory.Delete(sampleDir, true); + forwardDispatcherSync.Acquire(); + Assert.IsTrue(deployEventDispatched, "removal not dispatched in case of error on application watcher"); + } + + class ExceptionDispatcher : IDeployEventDispatcher + { + public event DeployEventHandler DeployEvent; + ISync started, canContinue; + + public ExceptionDispatcher (ISync started, ISync canContinue) + { + this.started = started; + this.canContinue = canContinue; + } + + public void Dispose () + { + } + + public void Dispatch (IDeployLocation sender, DeployEventType eventType, IApplication application) + { + if (DeployEvent != null) DeployEvent(null, new DeployEventArgs(application, eventType)); + started.Release(); + canContinue.Acquire(); + throw new Exception ("this dispatcher is for testing and always raises exception"); + } + + } + + [Test] + public void IgnoreExceptionOnDispatcherWhenAdding () + { + factoryMock.ExpectAndReturn("CreateApplicationWatcherMonitor", watcher); + factoryMock.ExpectAndReturn("CreateApplicationWatcherMonitor", watcher); + ISync canContinue = new Semaphore(0); + ISync started = new Semaphore(0); + IDeployEventDispatcher dispatcher = new ExceptionDispatcher(started, canContinue); + location = new FileSystemDeployLocation (dispatcher, factory, deployPath, true); + AddApplication(); + started.Acquire(); + canContinue.Release(); + Assert.AreEqual(1, location.Applications.Count); + AddApplication(sampleDir2); + started.Acquire(); + canContinue.Release(); + Assert.AreEqual(2, location.Applications.Count); + } + + [Test] + public void IgnoreExceptionOnDispatcherWhenRemoving () + { + factoryMock.ExpectAndReturn("CreateApplicationWatcherMonitor", watcher); + factoryMock.ExpectAndReturn("CreateApplicationWatcherMonitor", watcher); + watcherMock.Expect("StartWatching"); + watcherMock.Expect("StopWatching"); + watcherMock.Expect("Dispose"); + watcherMock.Expect("StartWatching"); + watcherMock.Expect("StopWatching"); + watcherMock.Expect("Dispose"); + ISync canContinue = new Semaphore(0); + ISync started = new Semaphore(0); + AddApplication(); + AddApplication(sampleDir2); + IDeployEventDispatcher dispatcher = new ExceptionDispatcher(started, canContinue); + location = new FileSystemDeployLocation (dispatcher, factory, deployPath, true); + Assert.AreEqual(2, location.Applications.Count); + Directory.Delete(sampleDir, true); + started.Acquire(); + canContinue.Release(); + Assert.AreEqual(1, location.Applications.Count); + Directory.Delete(sampleDir2, true); + started.Acquire(); + canContinue.Release(); + Assert.AreEqual(0, location.Applications.Count); + } + + [Test] + public void OnlyApplicationsWithAManagerSuccesfullyStartedAreListed () + { + factoryMock.ExpectAndReturn("CreateApplicationWatcherMonitor", watcher); + watcherMock.ExpectAndThrow("StartWatching", new Exception()); + AddApplication(); + dispatcher.DeployEvent += new DeployEventHandler(dispatcher_DeployEvent); + location = new FileSystemDeployLocation (dispatcher, factory, deployPath, true); + triggeredLatch.Acquire(); + Assert.AreEqual(0, location.Applications.Count); + Assert.IsFalse(deployEventDispatched, "add dispatched in case of error on application watcher"); + } + + private void dispatcher_DeployEvent (object sender, DeployEventArgs args) + { + deployEventDispatched = true; + forwardDispatcherSync.Release(); + } + } + + [TestFixture] + public class FileSystemDeployLocationTest : FileSystemDeployLocationTestsBase + { + [SetUp] + public void SetUp () + { + base.SetUp_ (); + InitHandler (new NullSync ()); + } + + [TearDown] + public void TearDown () + { + log.Debug("tear down"); + base.TearDown_(); + } + + private void AddInvalidApplication () + { + Directory.CreateDirectory (sampleDir); + Assert.IsTrue (Directory.Exists (sampleDir), "directory does not exist: " + sampleDir); + } + + + [Test] + public void CreateDeployLocationPath () + { + Assert.IsTrue (Directory.Exists (location.FullPath)); + Assert.AreEqual (0, location.Applications.Count); + } + + [Test] + public void ListExistingDirectoriesAsApplications () + { + log.Debug ("start ListExistingDirectoriesAsApplications"); + AddApplication (); + location = new FileSystemDeployLocation (deployPath); + Assert.AreEqual (1, location.Applications.Count); + log.Debug ("end ListExistingDirectoriesAsApplications"); + } + + [Test] + public void CreateAFileToPreventDeployDirectoryDeletion () + { + location.StartWatching (); + Assert.IsTrue (File.Exists (location.LockFileName)); + } + + [Test] + public void LockFileIsDeletedOnDisposeOrStop () + { + FileSystemDeployLocation location = + new FileSystemDeployLocation (dispatcher, deployPath); + location.StartWatching (); + location.Dispose(); + Assert.IsFalse (File.Exists (location.LockFileName)); + } + + [Test] + public void RaiseEventAddingAnApplication () + { + Semaphore sync = new Semaphore (0); + InitHandlerAndStartLocation (sync); + AddApplication (); + sync.Acquire (); + Assert.IsTrue (handler.applicationAdded, "application not added"); + Assert.AreEqual (1, location.Applications.Count); + } + + [Test] + public void RaiseEventRemovingAnApplication () + { + AddApplication (); + Semaphore sync = new Semaphore (0); + InitHandlerAndStartLocation (sync); + Directory.Delete (sampleDir, true); + Thread.SpinWait(1); + Assert.IsFalse(Directory.Exists(sampleDir), "directory still exists"); + log.Debug("directory deleted"); + sync.Acquire (); + log.Debug("sync acquired"); + Assert.IsFalse (Directory.Exists (sampleDir), "directory still exists: " + sampleDir); + Assert.IsTrue (handler.applicationRemoved, "application not removed"); + Assert.AreEqual (0, location.Applications.Count); + } + + [Test] + public void RenamingADirectoryHostingAnApplicationItEqualsRemoveOldApplicationAndAddUnderNewName () + { + Semaphore sync = new Semaphore (0); + InitHandlerAndStartLocation (sync); + AddApplication (); + sync.Acquire (); + Assert.IsTrue (handler.applicationAdded, "application not added"); + Directory.Move (sampleDir, sampleDir2); + sync.Acquire (); // add or remove + sync.Acquire (); // add or remove + Assert.IsTrue (handler.applicationRemoved, "application was not removed"); + } + + + //[Test, Ignore("Gets stuck when running entire WindowsService.Tests dll")] + [Test] + public void EventsAreRaisedOnlyForValidatedApplication () + { + Semaphore sync = new Semaphore (0); + InitHandlerAndStartLocation (sync); + AddInvalidApplication (); + triggeredLatch.Acquire(); + Assert.IsFalse (handler.applicationAdded, "invalid application was added"); + Assert.AreEqual (0, location.Applications.Count, "invalid application was added"); + } + + [Test] + public void RemoveAfterSpringAssemblyDeploy () + { + Semaphore sync = new Semaphore (0); + InitHandlerAndStartLocation (sync); + AddApplication(); + sync.Acquire(); + string springPrivateBin = Path.Combine(sampleDir, SpringAssembliesDeployer.PrivateBinPathPrefix); + Directory.CreateDirectory(springPrivateBin); + using (File.Create(Path.Combine(springPrivateBin, "foo.dll"))) {} + Directory.Delete(sampleDir, true); + Assert.IsFalse(Directory.Exists(sampleDir), "directory still exists"); + log.Debug (String.Format ("directory {0} removed to simulate application removal", sampleDir)); + sync.Acquire(); + Assert.IsTrue (handler.applicationRemoved, "application not removed"); + Assert.AreEqual (0, location.Applications.Count, "application not removed"); + } + + [Test] + public void AddingRemovingUpdatingOrRenamingAFileUnderAnApplicationDirectoryItGetsUpdated () + { + Semaphore sync = new Semaphore (0); + AddApplication(); + InitHandlerAndStartLocation (sync); + string subDir = Path.Combine(sampleDir, "foo"); + Directory.CreateDirectory(subDir); + string aFile = Path.Combine(subDir, "foo.bar"); + sync.Acquire(); + + // create + log.Debug (String.Format ("creating a file")); + using (File.Create(aFile)) {} + sync.Acquire(); + Assert.IsTrue(handler.applicationUpdated, "new file created but application not updated"); + + // update + log.Debug (String.Format ("updating a file")); + handler.applicationUpdated = false; + File.SetLastWriteTime(aFile, DateTime.Now); + sync.Acquire(); + Assert.IsTrue(handler.applicationUpdated, "new file created but application not updated"); + + // delete + log.Debug (String.Format ("deleting a file")); + handler.applicationUpdated = false; + File.Delete(aFile); + sync.Acquire(); + Assert.IsTrue(handler.applicationUpdated, "new file created but application not updated"); + } + + [Test] + public void CanBeDisposedMoreThanOnce () + { + IDeployLocation location = new FileSystemDeployLocation(dispatcher, + DefaultApplicationWatcherFactory.Instance, deployPath, true); + location.Dispose(); + location.Dispose(); + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void CanBeStartedExplicitlyStartedButIsImplicitlyStoppedWhenDisposed () + { + FileSystemDeployLocation location = new FileSystemDeployLocation(deployPath); + location.StartWatching(); + location.Dispose(); + location.StartWatching(); + } + + [Test] + public void EventsRelatedToFilesInDeployPathAreIgnored () + { + FieldInfo f = location.GetType().GetField("_fileSystemWatcher", + BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic); + Assert.IsNotNull(f); + //FileSystemWatcher watcher = (FileSystemWatcher) f.GetValue(location); + FileSystemMonitor watcher = (FileSystemMonitor) f.GetValue(location); + Assert.IsNotNull(watcher); + Assert.AreEqual(NotifyFilters.DirectoryName | NotifyFilters.Attributes, watcher.NotifyFilter); + Assert.AreEqual( + Enum.Parse(typeof(NotifyFilters), 0.ToString()) , + watcher.NotifyFilter & NotifyFilters.FileName); + } + + [Test] + public void AnInvalidApplicationIsNotUpdatedNorRemovedButItWillNotBeListed () + { + Semaphore sync = new Semaphore (0); + AddApplication(); + InitHandlerAndStartLocation (sync); + File.Delete(serviceXml); + Assert.IsFalse(sync.Attempt(1000), "some events propagated, expecting no one"); + Assert.IsFalse (handler.applicationUpdated, "application wrongly updated"); + Assert.IsFalse(handler.applicationRemoved, "application wrongly removed"); + Assert.AreEqual (0, location.Applications.Count, "application listed"); + + location.Dispose(); + location = new FileSystemDeployLocation (deployPath); + Assert.AreEqual (0, location.Applications.Count, "invalid application listed"); + } + + [Test] + public void AnInvalidApplicationIsNotUpdatedNorRemovedButWhenRemovedStillRaisesTheRemovedEvent () + { + Semaphore sync = new Semaphore (0); + AddApplication(); + InitHandlerAndStartLocation (sync); + File.Delete(serviceXml); + Assert.IsFalse(sync.Attempt(1000), "some events propagated, expecting no one"); + + // remove + TestUtils.SafeDeleteDirectory(sampleDir); + Thread.SpinWait(1); + Assert.IsFalse(Directory.Exists(sampleDir), "directory still exists"); + log.Debug("directory deleted"); + sync.Acquire (); + log.Debug("sync acquired"); + Assert.IsFalse (Directory.Exists (sampleDir), "directory still exists: " + sampleDir); + Assert.IsTrue (handler.applicationRemoved, "application not removed"); + Assert.AreEqual (0, location.Applications.Count); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/FileSystemMonitorTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/FileSystemMonitorTest.cs new file mode 100644 index 00000000..8c5a03fe --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/FileSystemMonitorTest.cs @@ -0,0 +1,261 @@ +using System; +using System.IO; +using System.Threading; +using NUnit.Framework; +using Spring.Threading; + +namespace Spring.Services.WindowsService.Common.Deploy.FileSystem +{ + class FileSystemMonitorTester + { + private string _path; + AutoResetEvent _resetEvent = new AutoResetEvent(false); + + public FileSystemMonitorTester(string path) + { + this._path = path; + } + + public void CreateFile(string fileName) + { + Directory.CreateDirectory(Path.Combine(_path, Path.GetDirectoryName(fileName))); + using (File.CreateText(Path.Combine(_path, fileName))) + {} + } + + public void DeleteFile(string file) + { + File.Delete(Path.Combine(_path, file)); + } + + public void ChangeFile(string aFile) + { + using (StreamWriter writer = File.AppendText(Path.Combine(_path, aFile))) + { + writer.WriteLine("Random data"); + } + } + + public void RenameFile(string file, string newName) + { + File.Move(Path.Combine(_path, file), Path.Combine(_path, newName)); + } + + public bool TryToCauseAnError (ISync sync, long msecs) + { + new Thread(new ThreadStart(Go)).Start(); + bool got = sync.Attempt(msecs); + _resetEvent.Set(); + return got; + } + + private void Go () + { + string subDir = Guid.NewGuid().ToString(); + Directory.CreateDirectory(Path.Combine (_path, subDir)); + while (true) + { + if (_resetEvent.WaitOne(0, true)) + break; + string file = Path.Combine(subDir, Guid.NewGuid().ToString()); + CreateFile(file); + ChangeFile(file); + DeleteFile(file); + } + } + } + + [TestFixture] + public class FileSystemMonitorTest + { + FileSystemMonitor monitor; + string _path; + ISync sync; + long msecs; + FileSystemMonitorTester tester; + string aFile; + + [SetUp] + public void SetUp () + { + _path = Path.GetFullPath(Guid.NewGuid().ToString()); + Directory.CreateDirectory(_path); + monitor = new FileSystemMonitor(_path); + monitor.Start(); + tester = new FileSystemMonitorTester(_path); + sync = new Semaphore(0); + msecs = 1000; + + aFile = "foo.txt"; + } + + [TearDown] + public void TearDown () + { + try + { + monitor.Stop(); + Directory.Delete(_path, true); + } + catch (IOException ex) + { + Console.Write(ex); + } + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void DoesNotAcceptANonExistingDirectory () + { + new FileSystemMonitor("oh-no"); + } + + [Test] + public void CanBeStoppedEvenIfNotStarted () + { + monitor.Stop(); + + monitor = new FileSystemMonitor(_path); + monitor.Stop(); + Assert.IsTrue(true); + } + + [Test] + public void AllowRegisteringToCreatedEvents () + { + monitor.Created += new FileSystemEventHandler(OnCreated); + tester.CreateFile(aFile); + Assert.IsTrue(sync.Attempt(msecs), "created event timed out"); + } + + [Test] + public void AllowRegisteringToDeletedEvents () + { + tester.CreateFile(aFile); + + monitor.Deleted += new FileSystemEventHandler(OnDeleted); + tester.DeleteFile(aFile); + Assert.IsTrue(sync.Attempt(msecs), "deleted event timed out"); + } + + [Test] + public void AllowRegisteringToChangedEvents () + { + tester.CreateFile(aFile); + monitor.Changed += new FileSystemEventHandler(OnChanged); + tester.ChangeFile(aFile); + Assert.IsTrue(sync.Attempt(msecs), "changed event timed out"); + } + + [Test] + public void AllowRegisteringToRenamedEvents () + { + tester.CreateFile(aFile); + monitor.Renamed += new RenamedEventHandler(OnRenamed); + tester.RenameFile(aFile, "oh-what"); + Assert.IsTrue(sync.Attempt(msecs), "renamed event timed out"); + } + + [Test] + public void ErrorEventsAreNotFiltered () + { + monitor.InternalBufferSize = 4096; + monitor.PathMatcherExcludes = "**/*.*"; + monitor.Error += new ErrorEventHandler(OnError); + monitor.Created += new FileSystemEventHandler(noOp); + monitor.Deleted += new FileSystemEventHandler(noOp); + Assert.IsTrue(tester.TryToCauseAnError(sync, msecs * 10), "error event timed out"); + } + + [Test] + public void SupportsNantLikePattensToFilterAllowedEvents () + { + monitor.NotifyFilter = NotifyFilters.FileName; + monitor.IncludeSubdirectories = true; + monitor.PathMatcherIncludes = "**/sub/*.txt"; + + monitor.Created += new FileSystemEventHandler(OnCreated); + tester.CreateFile("sub/Bar.bar"); + Assert.IsFalse(sync.Attempt(msecs), "created event NOT filtered and propagated"); + + tester.CreateFile("sub/Bar.txt"); + Assert.IsTrue(sync.Attempt(msecs), "created event filtered and NOT propagated"); + } + + [Test] + public void SupportsNantLikePattensToFilterNotAllowedEvents () + { + monitor.NotifyFilter = NotifyFilters.FileName; + monitor.IncludeSubdirectories = true; + monitor.PathMatcherExcludes = "**/sub/*.txt"; + + monitor.Created += new FileSystemEventHandler(OnCreated); + tester.CreateFile("sub/Bar.bar"); + Assert.IsTrue(sync.Attempt(msecs), "created event filtered and NOT propagated"); + + tester.CreateFile("sub/Bar.txt"); + Assert.IsFalse(sync.Attempt(msecs), "created event NOT filtered and propagated"); + } + + [Test] + public void CanBeStoppedOnAnEvent () + { + //TODO: test with a buffer of capacity 1 to have blocked thread + // when shut down + + monitor.Created += new FileSystemEventHandler(ShutDownOnCreated); + + // TODO: simulate a big number of + // events and randomly shut down + tester.CreateFile(aFile); + tester.CreateFile("buzzWords"); + Assert.IsTrue(sync.Attempt(msecs), "created event timed out"); + + tester.DeleteFile(aFile); + + // no more events: the monitor is stopped + sync = new Latch(); + tester.CreateFile(aFile); + Assert.IsFalse(sync.Attempt(msecs), "created event NOT timed out"); + } + + private void OnCreated(object sender, FileSystemEventArgs e) + { + Assert.AreEqual(WatcherChangeTypes.Created, e.ChangeType); + sync.Release(); + } + + private void OnDeleted(object sender, FileSystemEventArgs e) + { + Assert.AreEqual(WatcherChangeTypes.Deleted, e.ChangeType); + sync.Release(); + } + + private void OnChanged(object sender, FileSystemEventArgs e) + { + Assert.AreEqual(WatcherChangeTypes.Changed, e.ChangeType); + sync.Release(); + } + + private void OnRenamed(object sender, RenamedEventArgs e) + { + Assert.AreEqual(WatcherChangeTypes.Renamed, e.ChangeType); + sync.Release(); + } + + private void OnError (object sender, ErrorEventArgs e) + { + sync.Release(); + } + + private void ShutDownOnCreated(object sender, FileSystemEventArgs e) + { + monitor.Stop(); + sync.Release(); + } + + private void noOp (object sender, FileSystemEventArgs e) + { + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/RegularExpressionFilterConfigurerTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/RegularExpressionFilterConfigurerTest.cs new file mode 100644 index 00000000..a4127e0e --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/FileSystem/RegularExpressionFilterConfigurerTest.cs @@ -0,0 +1,130 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System; +using System.IO; +using System.Text; +using NUnit.Framework; +using Spring.Core.IO; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +namespace Spring.Services.WindowsService.Common.Deploy.FileSystem +{ + [TestFixture] + public class RegularExpressionFilterConfigurerTest + { + + string xml = String.Format (@" + + + + + + + + + bin/**/*.* + + + + + **/*.log + + + + +", DefaultApplicationWatcherFactory.InjectedApplicationName); + + private string xmlFile; + + [TearDown] + public void TearDown () + { + File.Delete(xmlFile); + } + + [SetUp] + public void SetUp() + { + xmlFile = Application.WatcherXml; + using (StreamWriter w = File.CreateText(xmlFile)) + { + w.WriteLine(xml); + } + } + + [Test] + public void CreationViaARegexFilterConfigurerPrependsApplicationFullPathToRegex() + { + IApplication application = new Application(Path.GetFullPath(".")); + FileSystemApplicationWatcher w = + (FileSystemApplicationWatcher) DefaultApplicationWatcherFactory.Instance.CreateApplicationWatcher(application); + Assert.AreEqual(1, w.DisallowFilters.Count); + Assert.AreEqual(1, w.AllowFilters.Count); + RegularExpressionFilter filter1 = w.AllowFilters[0] as RegularExpressionFilter; + Assert.AreEqual( + PathMatcher.ForwardifySlashes(application.FullPath + "/bin/**/*.*"), filter1.Patterns[0]); + RegularExpressionFilter filter2 = w.DisallowFilters[0] as RegularExpressionFilter; + Assert.AreEqual( + PathMatcher.ForwardifySlashes(application.FullPath + "/**/*.log"), filter2.Patterns[0]); + } + + [Test] + public void AnObjectCanBeInjectedFromCodeIntoTheContext () + { + string inject = @" + + + + + + + + +"; + XmlObjectFactory factory = new XmlObjectFactory(new InputStreamResource(new MemoryStream(Encoding.Default.GetBytes(inject)), "")); + IApplication app = new Application("foo"); + factory.RegisterSingleton("app", app); + Inject injected = (Inject) factory.GetObject("inject"); + Assert.IsNotNull(injected); + Assert.IsNotNull(injected.Application); + Assert.AreEqual(app, injected.Application); + } + + } + + public class Inject + { + IApplication application; + public IApplication Application + { + get { return application; } + set { application = value; } + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/NullSync.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/NullSync.cs new file mode 100644 index 00000000..c4784416 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/NullSync.cs @@ -0,0 +1,38 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using Spring.Threading; + +namespace Spring.Services.WindowsService.Common.Deploy +{ + internal class NullSync : ISync + { + public void Acquire () + { + } + + public void Release () + { + } + + public bool Attempt (long msecs) + { + return true; + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/SpringAssembliesDeployerTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/SpringAssembliesDeployerTest.cs new file mode 100644 index 00000000..79efc732 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/SpringAssembliesDeployerTest.cs @@ -0,0 +1,47 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System; +using System.IO; +using NUnit.Framework; + +namespace Spring.Services.WindowsService.Common.Deploy +{ + [TestFixture] + public class SpringAssembliesDeployerTest + { + [Test] + public void RepeatedConfiguresDontAugmentPrivateBinPathList() + { + IApplication app = new Application("app"); + SpringAssembliesDeployer deployer = new SpringAssembliesDeployer(String.Empty); + deployer.ConfigurePrivateBinForApplication(app); + deployer.ConfigurePrivateBinForApplication(app); + Assert.AreEqual(2, app.DomainSetup.PrivateBinPath.Split(';').Length); + } + + [Test] + public void FilterMatchSpringAssembliesSubDir () + { + string directory = Path.Combine("c:/", + SpringAssembliesDeployer.PrivateBinPathPrefix + "foo/foo.dll"); + FileSystemEventArgs args = + new FileSystemEventArgs(WatcherChangeTypes.All, directory, "foo.dll"); + Assert.IsTrue(SpringAssembliesDeployer.DisallowFilter.Filter(args)); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/TestingHandler.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/TestingHandler.cs new file mode 100644 index 00000000..f93c00ef --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Deploy/TestingHandler.cs @@ -0,0 +1,63 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using System; +using System.Reflection; +using log4net; +using Spring.Services.WindowsService.Common.Deploy; +using Spring.Threading; + +namespace Spring.Services.WindowsService.Common.Deploy +{ + public class TestingHandler + { + ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public ISync sync; + public bool applicationAdded = false; + public bool applicationRemoved = false; + public bool applicationUpdated = false; + public bool eventGot = false; + + public TestingHandler (ISync sync) + { + this.sync = sync; + } + + public void Handle (object sender, DeployEventArgs args) + { + log.Debug(String.Format("handler #{0} application {1}, args.EventType {2}", + GetHashCode(), args.Application.FullPath, args.EventType)); + + eventGot = true; + + switch (args.EventType) + { + case DeployEventType.ApplicationAdded: + applicationAdded = true; + break; + case DeployEventType.ApplicationRemoved: + applicationRemoved = true; + break; + case DeployEventType.ApplicationUpdated: + applicationUpdated = true; + break; + } + sync.Release (); + log.Debug("sync released"); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/Gui/ApplicationMonitorTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/Gui/ApplicationMonitorTest.cs new file mode 100644 index 00000000..7ffbc974 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/Gui/ApplicationMonitorTest.cs @@ -0,0 +1,409 @@ +using System; +using System.Collections; +using System.Drawing.Imaging; +using System.Threading; +using System.Windows.Forms; +using DotNetMock.Dynamic; +using NUnit.Extensions.Forms; +using NUnit.Framework; +using Spring.Objects.Factory.Config; +using Spring.Services.WindowsService.Common; +using Spring.Services.WindowsService.Common.Deploy; +using Spring.Threading; + +namespace Spring.Services.WindowsService.Common.Gui +{ + class MockApplication : IApplication + { + static int count = 0; + + public static void ResetCount() + { + count = 0; + } + + int c; + + public MockApplication() + { + c = count++; + } + + public string Name + { + get + { + return "Mock" + c; + } + } + + public AppDomainSetup DomainSetup + { + get { throw new NotImplementedException(); } + } + + public string ApplicationBase + { + get { throw new NotImplementedException(); } + } + + public string FullPath + { + get { throw new NotImplementedException(); } + } + + public string ServiceXmlFullPath + { + get { throw new NotImplementedException(); } + } + + public bool IsValid + { + get { throw new NotImplementedException(); } + } + + public string WatcherXmlFullPath + { + get { throw new NotImplementedException(); } + } + + public IConfigurableListableObjectFactory ObjectFactory + { + get { throw new NotImplementedException(); } + } + + public IObjectFactoryPostProcessor ObjectFactoryPostProcessor + { + get { throw new NotImplementedException(); } + } + + public string DefaultPrivateBinPath + { + get { throw new NotImplementedException(); } + } + } + + class MockService : ISpringService, ISpringServiceFactory + { + /// + /// Detailed informations about the deployed applications + /// + public DeployInformation[] DeployInformations + { + get + { + return new DeployInformation[] + { + new DeployInformation(new MockApplication(), DeployStatus.Deployed, null), + new DeployInformation(new MockApplication(), DeployStatus.CannotDeploy, new Exception("fatal")), + }; + } + } + + /// + /// Obtain a reference (possibly remote) to the spring service + /// + public ISpringService SpringService + { + get + { + return new MockService(); + } + } + + public string ServiceUrl + { + get { return "mock://mock.rem"; } + set { } + } + + } + + class MyListBoxTester : ListBoxTester + { + public MyListBoxTester(string name, Form form) : base(name, form) + { + } + + public void SelectByStringExact (string what) + { + int index = Properties.FindStringExact(what); + if (index != -1) + { + Select(index); + } + else + { + Assert.Fail("listbox " + name + ": text " + what + " not found "); + } + } + + } + + [TestFixture] + public class ApplicationMonitorTest + { + Form form; + ISync started; + LabelTester appStatus; + MyListBoxTester boxTester; + ApplicationMonitor monitor; + LabelTester serviceStatus; + + [SetUp] + public void SetUp () + { + MockApplication.ResetCount(); + started = new Latch(); + new Thread(new ThreadStart(Go)).Start(); + started.Acquire(); + boxTester = new MyListBoxTester("applicationList", form); + appStatus = new LabelTester("applicationStatus", form); + serviceStatus = new LabelTester("serviceStatus", form); + } + + [TearDown] + public void TearDown () + { + form.Close(); + } + + [Test, Explicit] + public void Interactive () + { + Go(); + } + + [Test] + public void ListsEachApplication() + { + Assert.AreEqual(2, boxTester.Properties.Items.Count); + } + + [Test] + public void AListBoxShowsApplicationNames () + { + boxTester.SelectByStringExact("Mock1"); + Assert.AreEqual("Mock1", boxTester.Properties.Text); + } + + [Test] + public void ShowsApplicationStatus() + { + boxTester.Select(0); + IApplication application = boxTester.Properties.SelectedItem as IApplication; + Assert.AreEqual(application.Name + ": deployed", appStatus.Text); + Assert.AreEqual("the service seems to run normally, 2 application(s) found", serviceStatus.Text); + ScreenShot ("application deployed status.gif"); + + boxTester.Select(1); + application = boxTester.Properties.SelectedItem as IApplication; + Assert.AreEqual(application.Name + ": cannot deploy: fatal", appStatus.Text); + ScreenShot ("application deploy error status.gif"); + } + + [Test] + public void Updating () + { + ButtonTester buttonTester = new ButtonTester("update", form); + monitor.ServiceFactory = new StoleScreenShotWhileUpdating(this); + buttonTester.Click(); + } + + private class StoleScreenShotWhileUpdating : ISpringServiceFactory + { + ApplicationMonitorTest test; + + public StoleScreenShotWhileUpdating (ApplicationMonitorTest test) + { + this.test = test; + } + + public ISpringService SpringService + { + get + { + Assert.AreEqual("updating ...", test.serviceStatus.Text); + test.ScreenShot("updating.gif"); + return null; + } + } + + /// + /// The .NET remoting url for the remote service + /// + public string ServiceUrl + { + get { return ""; } + set { } + } + + } + + [Test] + public void TheServiceFactoryCanBeSetToNull () + { + monitor.ServiceFactory = null; + Assert.AreEqual(String.Empty, appStatus.Text); + Assert.AreEqual("service not available", serviceStatus.Text); + } + + [Test] + public void ErrorsWhileGettingTheReferenceToTheServiceAreVisibleToTheUser () + { + DynamicMock mockFactory = new DynamicMock(typeof(ISpringServiceFactory)); + DynamicMock mockService = new DynamicMock(typeof(ISpringService)); + + mockService.ExpectAndThrow("DeployInformations", new Exception("this and that")); + mockFactory.ExpectAndReturn("SpringService", mockService.Object); + + monitor.ServiceFactory = mockFactory.Object as ISpringServiceFactory; + Assert.AreEqual("service not available: this and that", serviceStatus.Text); + Assert.AreEqual(String.Empty, appStatus.Text); + ScreenShot ("service not available.gif"); + } + + private void ScreenShot (string name) + { + monitor.Refresh(); + AgileDocs.Core.ScreenShot.TakeScreenShot( + monitor.Handle.ToInt32(), ImageFormat.Png, name); + } + + ListView view; + + [Test, Explicit] + public void ListView () + { + view = new ListView(); + view.Dock = DockStyle.Fill; + view.FullRowSelect = true; + view.MultiSelect = false; + view.HeaderStyle = ColumnHeaderStyle.Clickable; + view.GridLines = true; + view.View = View.Details; + view.Sorting = SortOrder.Ascending; + + view.Columns.Add("Name", -1, HorizontalAlignment.Left); + view.Columns.Add("Status", -1, HorizontalAlignment.Left); + + view.Items.Add(new ListViewItem(new string[] {"Mock1", "Deployed"})); + view.Items.Add(new ListViewItem(new string[] {"Mock2", "Error"})); + + view.ColumnClick += new ColumnClickEventHandler(ColumnClick); + + view.SelectedIndexChanged +=new EventHandler(view_SelectedIndexChanged); + + view.MouseDown += new MouseEventHandler(view_MouseDown); + + form = new Form(); + form.Controls.Add(view); + System.Windows.Forms.Application.Run(form); + } + + // Implements the manual sorting of items by columns. + class ListViewItemComparer : IComparer + { + private int col; + + public ListViewItemComparer() + { + col=0; + } + public ListViewItemComparer(int column) + { + col=column; + } + public int Compare(object x, object y) + { + ListViewItem a = x as ListViewItem; + ListViewItem b = y as ListViewItem; + return String.Compare(a.SubItems[col].Text, b.SubItems[col].Text); + //return String.Compare(a.Text, b.Text); + } + } + + private void ColumnClick(object o, ColumnClickEventArgs e) + { + // Set the ListViewItemSorter property to a new ListViewItemComparer object. + view.ListViewItemSorter = new ListViewItemComparer(e.Column); + // Call the sort method to manually sort the column based on the ListViewItemComparer implementation. + view.Sort(); + } + + private void view_SelectedIndexChanged(object sender, EventArgs e) + { + ListView v = (ListView) sender; + if (v.SelectedIndices.Count > 0) + { + int index = v.SelectedIndices[0]; + ListViewItem item = v.Items[index]; + form.Text = "item: " + item.SubItems[0].Text; + } + } + + [Test, Explicit] + public void CreateMySplitControls() + { + // Create TreeView, ListView, and Splitter controls. + TreeView treeView1 = new TreeView(); + ListView listView1 = new ListView(); + Splitter splitter1 = new Splitter(); + + // Set the TreeView control to dock to the left side of the form. + treeView1.Dock = DockStyle.Left; + // Set the Splitter to dock to the left side of the TreeView control. + splitter1.Dock = DockStyle.Left; + // Set the minimum size the ListView control can be sized to. + splitter1.MinExtra = 100; + // Set the minimum size the TreeView control can be sized to. + splitter1.MinSize = 75; + // Set the ListView control to fill the remaining space on the form. + listView1.Dock = DockStyle.Fill; + // Add a TreeView and a ListView item to identify the controls on the form. + treeView1.Nodes.Add("TreeView Node"); + listView1.Items.Add("ListView Item"); + + // Add the controls in reverse order to the form to ensure proper location. + form = new Form(); + form.Controls.AddRange(new Control[]{listView1, splitter1, treeView1}); + System.Windows.Forms.Application.Run(form); + } + + private void Go() + { + form = new Form(); + form.Load += new EventHandler(form_Load); + monitor = new ApplicationMonitor(); + monitor.ServiceFactory = new MockService(); + monitor.Dock = DockStyle.Fill; + form.Controls.Add(monitor); + System.Windows.Forms.Application.Run(form); + } + + private void form_Load(object sender, EventArgs e) + { + started.Release(); + } + + ContextMenu menu = null; + private void view_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Right) + { + ListView v = sender as ListView; + v.ContextMenu = null; + ListViewItem itemAt = v.GetItemAt(e.X, e.Y); + if (itemAt == null) + { + return; + } + itemAt.Selected = true; + menu = new ContextMenu(); + menu.MenuItems.Add("one"); + menu.MenuItems.Add("two"); + v.ContextMenu = menu; + } + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/LocalizerTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/LocalizerTest.cs new file mode 100644 index 00000000..b4ef5e41 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/LocalizerTest.cs @@ -0,0 +1,57 @@ +using System; +using NUnit.Framework; + +namespace Spring.Services.WindowsService.Common +{ + [TestFixture] + public class Localizer_ForProcessTest + { + [Test] + public void ByDefaultIgnoreUnresolvablePlaceholders () + { + Assert.IsTrue(new Localizer.ForProcess().IgnoreUnresolvablePlaceholders); + } + + [Test] + public void YouCanDefineACustomPrefix() + { + string prefix = Guid.NewGuid().ToString(); + + Localizer.ForProcess localizer = new Localizer.ForProcess(); + + Assert.AreEqual(Localizer.DefaultPrefix, localizer.Prefix); + + localizer.Prefix = prefix; + foreach (string property in localizer.Properties) + { + Assert.IsTrue(property.StartsWith(prefix)); + } + } + } + + [TestFixture] + public class Localizer_ForApplicationTest + { + [Test] + public void ByDefaultIgnoreUnresolvablePlaceholders () + { + Assert.IsTrue(new Localizer.ForApplication().IgnoreUnresolvablePlaceholders); + } + + [Test] + public void YouCanDefineACustomPrefix() + { + string prefix = "FOO"; + + Localizer.ForApplication localizer = new Localizer.ForApplication(); + + Assert.AreEqual(Localizer.DefaultPrefix, localizer.Prefix); + + localizer.Prefix = prefix; + foreach (string property in localizer.Properties) + { + Assert.IsTrue(property.StartsWith(prefix)); + } + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/ServiceSupportTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/ServiceSupportTest.cs new file mode 100644 index 00000000..2776c0d5 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/ServiceSupportTest.cs @@ -0,0 +1,217 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion + +using System; +using System.Reflection; +using System.Threading; +using log4net; +using NUnit.Framework; +using Spring.Threading; + +namespace Spring.Services.WindowsService.Common +{ + class StoppingHelper + { + private readonly ServiceSupport serviceSupport; + private readonly ISync stopping; + private readonly ISync stopped; + public bool wasStopped = false; + + public StoppingHelper (ServiceSupport serviceSupport, ISync stopping, ISync stopped) + { + this.stopping = stopping; + this.stopped = stopped; + this.serviceSupport = serviceSupport; + } + + public void Stop () + { + ServiceSupportTest.Log ("stopping ..."); + stopping.Release(); + ServiceSupportTest.Log ("stopping sync released ..."); + serviceSupport.Stop(true); + wasStopped = true; + ServiceSupportTest.Log ("releasing stopped sync ..."); + stopped.Release(); + ServiceSupportTest.Log ("stopped ..."); + } + } + + class BlockWhileStartingExecutor : IExecutor + { + private readonly ISync started; + private readonly ISync starting; + MyServiceSupport serviceSupport; + public bool wasStarted = false; + + public BlockWhileStartingExecutor (ISync starting, ISync started) + { + this.starting = starting; + this.started = started; + } + + public MyServiceSupport ServiceSupport + { + get { return serviceSupport; } + set { serviceSupport = value; } + } + + public void Start () + { + serviceSupport.Start(true); + } + + public void Execute (IRunnable runnable) + { + if (runnable == serviceSupport.StartedCommand) + { + ServiceSupportTest.Log ("waiting to start ..."); + starting.Acquire(); + ServiceSupportTest.Log ("starting ..."); + runnable.Run(); + wasStarted = true; + ServiceSupportTest.Log ("releasing started sync ..."); + started.Release(); + ServiceSupportTest.Log ("started ..."); + } + else + { + ServiceSupportTest.Log ("unexpected runnable: " + runnable); + runnable.Run(); + } + } + } + + class MyServiceSupport : ServiceSupport + { + public MyServiceSupport(IExecutor executor, IServiceable serviceable) : base(executor, serviceable) + { + } + + public IRunnable StartedCommand + { + get + { + return base.startedCommand; + } + } + + + } + + class MyService : ServiceSupport.IServiceable + { + ISync _sync1; + ISync _sync2; + + public MyService (ISync sync1, ISync sync2) + { + this._sync1 = sync1; + this._sync2 = sync2; + } + public void PerformStart () + { + } + + public void PerformStop () + { + } + + public void Dispose () + { + lock (this) + { + _sync1.Release(); + _sync2.Acquire(); + } + } + } + + [TestFixture] + public class ServiceSupportTest + { + MyService serviceable; + ServiceSupport serviceSupport; + ISync sync1; + ISync sync2; + + static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + [SetUp] + public void SetUp () + { + sync1 = new Semaphore(0); + sync2 = new Semaphore(0); + serviceable = new MyService(sync1, sync2); + serviceSupport = new ServiceSupport(serviceable); + } + + [Test(Description="Bugfix")] + public void DoesNotLockTheServiceableWhenQueryingForStatus() + { + new Thread(new ThreadStart(serviceable.Dispose)).Start(); + sync1.Acquire(); + Assert.IsFalse(serviceSupport.StopRequested); + sync2.Release(); + } + + [Test] + public void WaitForStartedBeforeStopping() + { + MyService serviceable = new MyService(sync1, sync2); + ISync starting = new Latch(); + ISync started = new Latch(); + BlockWhileStartingExecutor executor = + new BlockWhileStartingExecutor(starting, started); + MyServiceSupport support = new MyServiceSupport(executor, serviceable); + executor.ServiceSupport = support; + Thread startThread = new Thread(new ThreadStart(executor.Start)); + startThread.Name = "start"; + startThread.Start(); + Log ("start thread started"); + Latch stopping = new Latch(); + Latch stopped = new Latch(); + StoppingHelper helper = new StoppingHelper(support, stopping, stopped); + Thread stopThread = new Thread(new ThreadStart(helper.Stop)); + stopThread.Name = "stop"; + stopThread.Start(); + Log ("stop thread started: waiting for stopping ..."); + stopping.Acquire(); + Log ("stopping in progress ..."); + Assert.IsFalse(executor.wasStarted); + Assert.IsFalse(helper.wasStopped, "helper could stop before expected"); + Log ("allow to start ..."); + starting.Release(); + Log ("waiting for started ..."); + started.Acquire(); + Assert.IsTrue(executor.wasStarted); + stopped.Acquire(); + Log ("waiting for stop ..."); + Assert.IsTrue(helper.wasStopped); + Log ("stopped ..."); + } + + public static void Log (string msg) + { + Thread currentThread = Thread.CurrentThread; + string message = String.Format("thread [#{1}-{2}], msg = {0}", msg, currentThread.GetHashCode(), currentThread.Name); + log.Debug(message); + Console.Out.WriteLine (message); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Common/UtilsTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/Common/UtilsTest.cs new file mode 100644 index 00000000..dbab9969 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Common/UtilsTest.cs @@ -0,0 +1,36 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion +using NUnit.Framework; + +namespace Spring.Services.WindowsService.Common +{ + [TestFixture] + public class UtilsTest + { + [Test] + public void CanBeUsedToCheckOnePathIsChildOfAnotherOne () + { + Assert.IsTrue(Utils.AreParentAndChild(@"c:\parent", @"c:\parent\child")); + Assert.IsTrue(Utils.AreParentAndChild(@"parent", @"parent\child")); + Assert.IsTrue(Utils.AreParentAndChild(@"\\server\share", @"\\server\share\app")); + Assert.IsTrue(Utils.AreParentAndChild(@"c:\parent", @"c:\parent\child\foo.bar")); + Assert.IsFalse(Utils.AreParentAndChild(@"c:\parent", @"c:\parent2")); + Assert.IsFalse(Utils.AreParentAndChild(@"c:\parent", @"d:\parent\child")); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/IntegrationTest.cs b/test/Spring/Spring.Services.Tests/WindowsService/IntegrationTest.cs new file mode 100644 index 00000000..9640fc59 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/IntegrationTest.cs @@ -0,0 +1,184 @@ +#region License +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#endregion + +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Threading; +using NUnit.Framework; +using Spring.Services.WindowsService.Common; +using Spring.Services.WindowsService.Common.Deploy; +using Spring.Services.WindowsService.Common.Deploy.FileSystem; +using Spring.Threading; +using Spring.Util; + +namespace Spring.Services.WindowsService +{ + [TestFixture] + public class IntegrationTest + { + public class SyncedDeployer : IDeployer + { + private readonly ISync sync; + IDeployer helper = new DefaultDeployer(new SeparateAppDomainHostManager()); + private Exception ex; + + public SyncedDeployer(ISync sync) + { + this.sync = sync; + } + + public void Deploy(IApplication application) + { + try + { + helper.Deploy(application); + } + catch (Exception e) + { + ex = e; + sync.Release(); + } + sync.Release(); + } + + public void UnDeploy(IApplication application) + { + helper.UnDeploy(application); + } + + public void Dispose() + { + helper.Dispose(); + } + + public Exception Ex + { + get { return ex; } + } + } + + DeployManager deployManager; + ISpringAssembliesDeployer springAssembliesDeployer; + FileSystemDeployLocation location; + SyncedDeployer defaultDeployer; + string deployPath = String.Format("deploy-{0}", Guid.NewGuid()); + string baseOfConfigFiles = "Data/Spring/WindowsService"; + Latch sync; + + [SetUp] + public void SetUp () + { + springAssembliesDeployer = new SpringAssembliesDeployer("."); + location = new FileSystemDeployLocation(deployPath); + location.StartWatching(); + sync = new Latch(); + defaultDeployer = new SyncedDeployer(sync); + deployManager = new DeployManager(springAssembliesDeployer, location, defaultDeployer); + + deployManager.Start(); + } + + [TearDown] + public void TearDown () + { + if (defaultDeployer.Ex != null) + { + Console.Out.WriteLine("defaultDeployer exception = {0}", defaultDeployer.Ex); + } + deployManager.Stop(); + location.Dispose(); + TestUtils.SafeDeleteDirectory(deployPath, 25); + } + + [Test(Description="Any PropertyPlaceholderConfigurer in context will apply")] + public void ServiceContextCanSpecifyAPropertyPlaceholderConfigurer () + { + Deploy("Echo"); + + // test echo server just deployed + using (TcpClient client = new TcpClient()) + { + client.Connect(IPAddress.Loopback, 10); + using (NetworkStream networkStream = client.GetStream()) + { + string guid = Guid.NewGuid().ToString(); + Receive(networkStream); + Send (networkStream, guid); + String responseData = Receive (networkStream); + Assert.IsTrue(responseData.IndexOf(guid) != -1); + } + } + } + + [Test()] + public void DeployPathAndDeployNameAreAvailableToTheApplication () + { + string where = Deploy ("Simple"); + + using (TextReader reader = File.OpenText(Path.Combine(where, "simple.txt"))) + { + string name = "simple"; + string fullPath = Path.GetFullPath(where).ToLower(); + Assert.AreEqual(name, reader.ReadLine()); + Assert.AreEqual(fullPath, reader.ReadLine().ToLower()); + Assert.AreEqual(String.Format("{0},{1}", name, fullPath), reader.ReadLine().ToLower(), "failed to configure in ctor"); + } + } + + private string Deploy(string app) + { + string src = Path.Combine(baseOfConfigFiles, app); + string dest = Path.Combine(deployPath, app); + Directory.CreateDirectory(dest); + string[] files = Directory.GetFiles(src); + foreach(string file in files) + { + File.Copy( + file, + Path.Combine(dest, Path.GetFileName(file))); + } + // create new application + string fileName = Assembly.GetExecutingAssembly().Location; + File.Copy(fileName, + Path.Combine(dest, Path.GetFileName(fileName))); + + Assert.IsTrue(sync.Attempt(20 * 1000), "Timeout expired"); + + return dest; + } + + private string Receive(Stream stream) + { + byte [] data = new byte[1024]; + String responseData = String.Empty; + Int32 bytes = stream.Read(data, 0, data.Length); + responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes); + Console.WriteLine("Received: [{0}]", responseData); + return responseData; + } + + private void Send(Stream stream, string message) + { + byte [] data = System.Text.Encoding.ASCII.GetBytes(message + Environment.NewLine); + stream.Write(data, 0, data.Length); + } + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Samples/ASMXExecutor.cs b/test/Spring/Spring.Services.Tests/WindowsService/Samples/ASMXExecutor.cs new file mode 100644 index 00000000..54d05331 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Samples/ASMXExecutor.cs @@ -0,0 +1,147 @@ +using System; +using System.Globalization; +using System.IO; +using System.Runtime.Remoting; +using System.Web; +using System.Web.Hosting; + +namespace Spring.Service.Sample +{ + public class SoapWorkerRequest : SimpleWorkerRequest + { + private string soapstr; + private string soapAction; + + public SoapWorkerRequest (string page, string soapstr, + string soapAction, TextWriter output) : base (page, null, output) + { + this.soapstr = soapstr; + this.soapAction = soapAction; + } + + public override string GetHttpVerbName () + { + return "POST"; + } + + public override int ReadEntityBody (byte[] buffer, int size) + { + char[] chars = soapstr.ToCharArray (); + for (int i = 0; i < chars.Length; i++) + buffer [i] = (byte) chars [i]; + return chars.Length; + } + + public override string GetKnownRequestHeader (int index) + { + string retval; + switch (index) + { + case 11: + retval = String.Format ("{0}", soapstr.Length); + break; + case 12: + retval = "text/xml; charset=utf-8"; + break; + default: + retval = null; + break; + } + return retval; + } + + public override string[][] GetUnknownRequestHeaders () + { + string[] vals = {"SOAPAction", soapAction}; + string[][] namesvals = {vals}; + return namesvals; + } + + public override string MapPath (string path) + { + if (path == GetFilePath ()) + return GetFilePathTranslated (); + return base.MapPath (path); + } + } + + public class MyExeHost : MarshalByRefObject + { + public void ProcessRequest (String page, string soapmsg, string soapAction) + { + HttpWorkerRequest hwr = + new SoapWorkerRequest (page, soapmsg, soapAction, Console.Out); + HttpRuntime.ProcessRequest (hwr); + } + } + + internal class MyAspHost + { + public static object CreateApplicationHost (Type hostType, + string virtualDir, string physicalDir) + { + if (!(physicalDir.EndsWith ("\\"))) + physicalDir = physicalDir + "\\"; + string aspDir = HttpRuntime.AspInstallDirectory; + string domainId = DateTime.Now.ToString ( + DateTimeFormatInfo.InvariantInfo).GetHashCode ().ToString ("x"); + string appName = (virtualDir + physicalDir).GetHashCode ().ToString ("x"); + AppDomainSetup setup = new AppDomainSetup (); + setup.ApplicationName = appName; + setup.ConfigurationFile = "web.config"; + AppDomain ad = AppDomain.CreateDomain (domainId, null, setup); + ad.SetData (".appDomain", "*"); + ad.SetData (".appPath", physicalDir); + ad.SetData (".appVPath", virtualDir); + ad.SetData (".domainId", domainId); + ad.SetData (".hostingVirtualPath", virtualDir); + ad.SetData (".hostingInstallDir", aspDir); + ObjectHandle oh = ad.CreateInstance (hostType.Module.Assembly.FullName, + hostType.FullName); + return oh.Unwrap (); + } + + private static void Main (string[] args) + { + MyExeHost myHost = (MyExeHost) CreateApplicationHost (typeof (MyExeHost), + "/", Directory.GetCurrentDirectory ()); + myHost.ProcessRequest (args [0], Console.In.ReadToEnd (), args [1]); + } + } +} + +//To compile the program, open a Visual Studio .NET command prompt (or any other command prompt session with the C# compiler in the path) and run this command: +//csc /t:exe /r:System.Web.dll WsHostTest.cs +//Put the exe file in a directory with an ASMX file. Here’s the simple file I’ve used in my tests: +//<%@ WebService Language="C#" Debug="true" Class="foo" %> +//using System.Web.Services; +// +// +//public class foo +//{ +// [WebMethod()] +// public string echo(string input) +// { +// return input; +// } +//} +// +//I also created a file with the SOAP message I wanted to use as input for the program. Here is the contents of this file: +// +// +// +// +// +// +//Howdy +// +// +// +// +// +//To call the program, I entered this command line: +// +//WsHostTest echo.asmx http://tempuri.org/echo +using System.Web.Services; + + +public class foo +{ + [WebMethod()] + public string echo(string input) + { + return input; + } +} diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Samples/service.config b/test/Spring/Spring.Services.Tests/WindowsService/Samples/service.config new file mode 100644 index 00000000..9fe17e53 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Samples/service.config @@ -0,0 +1,52 @@ + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Samples/service.xml b/test/Spring/Spring.Services.Tests/WindowsService/Samples/service.xml new file mode 100644 index 00000000..0dc767bd --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Samples/service.xml @@ -0,0 +1,20 @@ + + + + ${spring.services.application.name} + + + ${spring.services.application.fullpath} + + + + + 10 + + diff --git a/test/Spring/Spring.Services.Tests/WindowsService/Samples/watcher.xml b/test/Spring/Spring.Services.Tests/WindowsService/Samples/watcher.xml new file mode 100644 index 00000000..4915add3 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/Samples/watcher.xml @@ -0,0 +1,16 @@ + + + + + + + + + + **/** + + + + diff --git a/test/Spring/Spring.Services.Tests/WindowsService/TestUtils.cs b/test/Spring/Spring.Services.Tests/WindowsService/TestUtils.cs new file mode 100644 index 00000000..e8d1c2f5 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/WindowsService/TestUtils.cs @@ -0,0 +1,123 @@ +#region License + +/* +* Copyright 2002-2004 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#endregion + +using System.IO; +using System.Threading; +using log4net.Config; +using Spring.Core.IO; + +namespace Spring.Services.WindowsService +{ + public class TestUtils + { + public static void ConfigureLog4Net() + { + AssemblyResource resource = + new AssemblyResource("assembly://Spring.Services.WindowsService.Tests/Spring.Services/tests.config"); + XmlConfigurator.Configure(resource.InputStream); + } + + public static void SafeDeleteFile (string file) + { + while(File.Exists(file)) + { + try + { + File.Delete(file); + } + catch + { + Thread.Sleep(100); + } + } + } + + public static void SafeDeleteFile (string file, int maxTimes) + { + int times = 0; + while(File.Exists(file)) + { + try + { + File.Delete(file); + } + catch + { + if (++times > maxTimes) + { + return; + } + Thread.Sleep(100); + } + } + } + + public static void SafeDeleteDirectory (string directory) + { + while(Directory.Exists(directory)) + { + try + { + Directory.Delete(directory, true); + } + catch + { + Thread.Sleep(100); + } + } + } + + public static void SafeDeleteDirectory (string directory, int maxTimes) + { + int times = 0; + while(Directory.Exists(directory)) + { + try + { + Directory.Delete(directory, true); + } + catch + { + if (++times > maxTimes) + { + return; + } + Thread.Sleep(100); + } + } + } + + public static void SafeCopyFile (string filePath, string destPath, bool overWrite) + { + while(true) + { + try + { + File.Copy(filePath, destPath, overWrite); + return; + } + catch + { + Thread.Sleep(100); + } + } + } + } +} diff --git a/test/Spring/Spring.Services.Tests/tests.config b/test/Spring/Spring.Services.Tests/tests.config new file mode 100644 index 00000000..458e5c84 --- /dev/null +++ b/test/Spring/Spring.Services.Tests/tests.config @@ -0,0 +1,56 @@ + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Testing.NUnit.Tests/AssemblyInfo.cs b/test/Spring/Spring.Testing.NUnit.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..145f4d0a --- /dev/null +++ b/test/Spring/Spring.Testing.NUnit.Tests/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("Spring.Testing.NUnit Tests")] +[assembly: AssemblyDescription("Unit tests for Spring.Testing.NUnit assembly")] \ No newline at end of file diff --git a/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.2003.csproj b/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.2003.csproj new file mode 100644 index 00000000..ab14073a --- /dev/null +++ b/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.2003.csproj @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.2005.csproj b/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.2005.csproj new file mode 100644 index 00000000..36d8f0cf --- /dev/null +++ b/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.2005.csproj @@ -0,0 +1,89 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {4D6D616B-7643-4D6B-8E5E-14ECFDB9AF82} + Library + Properties + Spring + Spring.Testing.NUnit.Tests + + + + + true + full + false + ..\..\..\build\VS.Net.2005\Spring.Testing.NUnit.Tests\Debug\ + TRACE;DEBUG;NET_2_0 + prompt + 4 + + + pdbonly + true + ..\..\..\build\VS.Net.2005\Spring.Testing.NUnit.Tests\Release\ + TRACE;NET_2_0 + prompt + 4 + + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + + + + + + + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 + + + {ED204A7B-832F-44C7-BFE3-504AEBE1BCC8} + Spring.Testing.NUnit.2005 + + + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904} + Spring.Core.Tests.2005 + + + {ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0} + Spring.Data.Tests.2005 + + + + + + + + Always + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.build b/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.build new file mode 100644 index 00000000..b720384b --- /dev/null +++ b/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.build @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.dll.config b/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.dll.config new file mode 100644 index 00000000..0cfa9c7b --- /dev/null +++ b/test/Spring/Spring.Testing.NUnit.Tests/Spring.Testing.NUnit.Tests.dll.config @@ -0,0 +1,46 @@ + + + + + + + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/AnotherTest/LoadAppContextTests.cs b/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/AnotherTest/LoadAppContextTests.cs new file mode 100644 index 00000000..85227876 --- /dev/null +++ b/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/AnotherTest/LoadAppContextTests.cs @@ -0,0 +1,58 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#if !NET_1_0 + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Testing.NUnit.Another +{ + /// + /// This class should be the first one executed that inherits from BaseSpringTests to be loaded by + /// NUnit. The second one should be CachedAppContextTests. The package name starting with the + /// letter 'A' is on purpose, since this seems to match how NUnit iterates over tests in this + /// assembly. + /// + /// Mark Pollack + /// $Id: LoadAppContextTests.cs,v 1.3 2007/08/25 14:26:35 oakinger Exp $ + [TestFixture] + public class LoadAppContextTests : BaseTestAppContextTests + { + + /// + /// Loads the application context. If this test fails, see if you can coerce NUnit to + /// execut it before CachedAppContextTests, or check if application context caching in + /// Spring.Testing.NUnit is broken. + /// + [Test] + public void LoadApplicationContext() + { + Assert.AreEqual(1, LoadCount, "Either caching of application context is broken or this test was executed before CachedAppContextTests."); + } + + + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/BaseTestAppContextTests.cs b/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/BaseTestAppContextTests.cs new file mode 100644 index 00000000..b74faf34 --- /dev/null +++ b/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/BaseTestAppContextTests.cs @@ -0,0 +1,41 @@ +#region License + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System; + +namespace Spring.Testing.NUnit +{ + /// + /// This is + /// + /// + /// + /// + /// Mark Pollack + /// $Id: BaseTestAppContextTests.cs,v 1.1 2007/08/20 18:38:11 markpollack Exp $ + public class BaseTestAppContextTests : AbstractTransactionalDbProviderSpringContextTests + { + + protected override string[] ConfigLocations + { + get { return new string[] {"assembly://Spring.Testing.NUnit.Tests/Spring.Testing.NUnit/TestApplicationContext.xml"}; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/CachedAppContextTests.cs b/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/CachedAppContextTests.cs new file mode 100644 index 00000000..c7605df0 --- /dev/null +++ b/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/CachedAppContextTests.cs @@ -0,0 +1,55 @@ +#if !NET_1_0 +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Testing.NUnit +{ + /// + /// This class should be the second one executed that inherits from BaseSpringTests to be loaded by + /// NUnit. The first one should be LoadAppContextTests. + /// + /// Mark Pollack + /// $Id: CachedAppContextTests.cs,v 1.2 2007/08/21 19:26:14 markpollack Exp $ + [TestFixture] + public class CachedAppContextTests : BaseTestAppContextTests + { + + /// + /// Loads the cached application context. If this test fails, see if you can coerce NUnit to + /// execut it before CachedAppContextTests, or check if application context caching in + /// Spring.Testing.NUnit is broken. + /// + [Test] + public void CachedApplicationContext() + { + Assert.AreEqual(0, LoadCount); + } + + + } +} +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/TestApplicationContext.xml b/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/TestApplicationContext.xml new file mode 100644 index 00000000..a5702f11 --- /dev/null +++ b/test/Spring/Spring.Testing.NUnit.Tests/Testing/NUnit/TestApplicationContext.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/AssemblyInfo.cs b/test/Spring/Spring.Web.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..f1a18ca1 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/AssemblyInfo.cs @@ -0,0 +1,24 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Reflection; + +[assembly: AssemblyTitle("Spring.Web Tests")] +[assembly: AssemblyDescription("Unit tests for the Spring.Web assembly")] diff --git a/test/Spring/Spring.Web.Tests/Caching/AspNetCacheTests.cs b/test/Spring/Spring.Web.Tests/Caching/AspNetCacheTests.cs new file mode 100644 index 00000000..2c6ebb53 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Caching/AspNetCacheTests.cs @@ -0,0 +1,216 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Threading; +using System.Web; +using System.Web.Caching; +using NUnit.Framework; +using Rhino.Mocks; + +#endregion + +namespace Spring.Caching +{ + /// + /// Test AspNetCache behaviour. + /// + /// Erich Eichinger + /// $Id: AspNetCacheTests.cs,v 1.2 2007/08/25 10:22:13 oakinger Exp $ + [TestFixture] + public class AspNetCacheTests + { + private AspNetCache thisCache; + private AspNetCache otherCache; + private readonly Cache aspCache = HttpRuntime.Cache; + private readonly TimeSpan ttl10Seconds = new TimeSpan(0, 0, 10); + + MockRepository mocks; + AspNetCache.IRuntimeCache mockedRuntimeCache; + AspNetCache mockedCache; + + [SetUp] + public void SetUp() + { + // cleanup underlying static asp.net cache + foreach (DictionaryEntry entry in HttpRuntime.Cache) + { + aspCache.Remove((string)entry.Key); + } + + thisCache = new AspNetCache(); + otherCache = new AspNetCache(); + + mocks = new MockRepository(); + mockedRuntimeCache = (AspNetCache.IRuntimeCache)mocks.CreateMock(typeof(AspNetCache.IRuntimeCache)); + mockedCache = new AspNetCache(mockedRuntimeCache); + } + + [Test] + public void Get() + { + // set expectations + Expect.Call(mockedRuntimeCache.Get(mockedCache.GenerateKey("key"))).Return(null); + mocks.ReplayAll(); + // verify + mockedCache.Get("key"); + mocks.VerifyAll(); + } + + [Test] + public void Remove() + { + // set expectations + Expect.Call(mockedRuntimeCache.Remove(mockedCache.GenerateKey("key"))).Return(null); + mocks.ReplayAll(); + // verify + mockedCache.Remove("key"); + mocks.VerifyAll(); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void DoesNotAcceptNullKeysOnInsert() + { + thisCache.Insert(null, "value", TimeSpan.Zero, true); + } + + [Test] + public void GetNullKeysReturnsNull() + { + Assert.IsNull(thisCache.Get(null)); + } + + [Test] + public void IgnoreNullKeysOnRemove() + { + thisCache.Remove(null); + } + + [Test] + public void ReturnsOnlyKeysOwnedByCache() + { + DictionaryEntry[] mockedRuntimeCacheEntries = + { + new DictionaryEntry(mockedCache.GenerateKey("keyA"), null) + , new DictionaryEntry(mockedCache.GenerateKey("keyB"), null) + , new DictionaryEntry(thisCache.GenerateKey("keyC"), null) + , new DictionaryEntry(otherCache.GenerateKey("keyD"), null) + }; + + // set expectations + Expect.Call(mockedRuntimeCache.GetEnumerator()).Return(mockedRuntimeCacheEntries.GetEnumerator()); + mocks.ReplayAll(); + // verify + ICollection keys = mockedCache.Keys; + Assert.AreEqual( new string[] { "keyA", "keyB" }, new ArrayList(keys).ToArray(typeof(string)) ); + mocks.VerifyAll(); + } + + [Test] + public void AddItemToCacheNoExpiration() + { + thisCache.Insert( "key", "thisValue", TimeSpan.Zero, false ); + Assert.AreEqual(1, thisCache.Count); + Assert.AreEqual("thisValue", aspCache.Get(thisCache.GenerateKey("key"))); + + otherCache.Insert("key", "otherValue", TimeSpan.Zero, false); + Assert.AreEqual(1, otherCache.Count); + Assert.AreEqual("otherValue", aspCache.Get(otherCache.GenerateKey("key"))); + } + + [Test] + public void AddItemToCacheAbsoluteExpiration() + { + thisCache.Insert("key", "thisValue", ttl10Seconds, false); + Assert.AreEqual(1, thisCache.Count); + Assert.AreEqual("thisValue", aspCache.Get(thisCache.GenerateKey("key"))); + + otherCache.Insert("key", "otherValue", ttl10Seconds, false); + Assert.AreEqual(1, otherCache.Count); + Assert.AreEqual("otherValue", aspCache.Get(otherCache.GenerateKey("key"))); + } + + [Test] + public void AddItemToCacheSlidingExpiration() + { + thisCache.Insert("key", "thisValue", ttl10Seconds, true); + Assert.AreEqual(1, thisCache.Count); + Assert.AreEqual("thisValue", aspCache.Get(thisCache.GenerateKey("key"))); + + otherCache.Insert("key", "otherValue", ttl10Seconds, true); + Assert.AreEqual(1, otherCache.Count); + Assert.AreEqual("otherValue", aspCache.Get(otherCache.GenerateKey("key"))); + } + + [Test] + public void DifferentCacheNamesCauseSameKeyToBeDifferent() + { + Assert.AreNotEqual("key", thisCache.GenerateKey("key")); + Assert.AreNotEqual(thisCache.GenerateKey("key"), otherCache.GenerateKey("key")); + } + + [Test] + public void PassesParametersToRuntimeCache() + { + MockRepository mocks = new MockRepository(); + AspNetCache.IRuntimeCache runtimeCache = (AspNetCache.IRuntimeCache) mocks.CreateMock(typeof(AspNetCache.IRuntimeCache)); + AspNetCache cache = new AspNetCache(runtimeCache); + + DateTime expectedAbsoluteExpiration = DateTime.Now; + TimeSpan expectedSlidingExpiration = ttl10Seconds; + CacheItemPriority expectedPriority = CacheItemPriority.Low; + +// TODO: find way to test non-sliding expiration case +// runtimeCache.Insert(cache.GenerateKey("key"), "value", null, DateTime.Now.Add(ttl10Seconds), Cache.NoSlidingExpiration, expectedPriority, null); + runtimeCache.Insert(cache.GenerateKey("key"), "value", null, Cache.NoAbsoluteExpiration, ttl10Seconds, expectedPriority, null); + + mocks.ReplayAll(); + +// cache.Insert( "key", "value", ttl10Seconds, false, expectedPriority ); + cache.Insert("key", "value", ttl10Seconds, true, expectedPriority); + + mocks.VerifyAll(); + } + + [Test] + public void ZeroTTLCausesNoExpiration() + { + MockRepository mocks = new MockRepository(); + AspNetCache.IRuntimeCache runtimeCache = (AspNetCache.IRuntimeCache)mocks.CreateMock(typeof(AspNetCache.IRuntimeCache)); + AspNetCache cache = new AspNetCache(runtimeCache); + + CacheItemPriority expectedPriority = CacheItemPriority.Low; + + runtimeCache.Insert(cache.GenerateKey("key"), "value", null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, expectedPriority, null); + runtimeCache.Insert(cache.GenerateKey("key"), "value", null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, expectedPriority, null); + + mocks.ReplayAll(); + + cache.Insert("key", "value", TimeSpan.Zero, true, expectedPriority); + cache.Insert("key", "value", TimeSpan.Zero, false, expectedPriority); + + mocks.VerifyAll(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Context/Support/HttpApplicationConfigurerTests.cs b/test/Spring/Spring.Web.Tests/Context/Support/HttpApplicationConfigurerTests.cs new file mode 100644 index 00000000..b034fe62 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Context/Support/HttpApplicationConfigurerTests.cs @@ -0,0 +1,262 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Configuration; +using System.Reflection; +using System.Web; +using Common.Logging; +using NUnit.Framework; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: HttpApplicationConfigurerTests.cs,v 1.1 2007/07/27 13:29:21 oakinger Exp $ + [TestFixture] + public class HttpApplicationConfigurerTests + { + [TestFixtureSetUp] + public void SetUpFixture() + { + LogManager.Adapter = new Common.Logging.Simple.TraceLoggerFactoryAdapter(); + } + + [SetUp] + public void SetUp() + { + HttpApplicationConfigurer h1 = new HttpApplicationConfigurer(); + h1.ApplicationTemplate = null; + h1.ModuleTemplates.Clear(); + } + + /// + /// All instances share the same underlying ApplicationTemplate + /// + [Test] + public void ShareApplicationTemplate() + { + RootObjectDefinition rod = new RootObjectDefinition(); + + HttpApplicationConfigurer h1 = new HttpApplicationConfigurer(); + h1.ApplicationTemplate = rod; + Assert.AreEqual(rod, h1.ApplicationTemplate); + + // h2 returns same template as h1 + HttpApplicationConfigurer h2 = new HttpApplicationConfigurer(); + Assert.AreEqual(rod, h2.ApplicationTemplate); + Assert.AreEqual(h2.ApplicationTemplate, h1.ApplicationTemplate); + + // allows overriding + rod = new RootObjectDefinition(); + h2.ApplicationTemplate = rod; + Assert.AreEqual(rod, h1.ApplicationTemplate); + } + + /// + /// All instances share the same underlying ModuleTemplates table + /// + [Test] + public void ShareModuleTemplates() + { + // is never null + HttpApplicationConfigurer h1; + HttpApplicationConfigurer h2; + RootObjectDefinition rod; + + // is shared + h1 = new HttpApplicationConfigurer(); + Assert.IsNotNull(h1.ModuleTemplates); + h2 = new HttpApplicationConfigurer(); + Assert.AreEqual(h1.ModuleTemplates, h2.ModuleTemplates); + + // allows overriding + rod = new RootObjectDefinition(); + h1.ModuleTemplates.Add("test", rod); + Assert.AreEqual(rod, h1.ModuleTemplates["test"]); + h2.ModuleTemplates.Add("test", rod); + Assert.AreEqual(1, h2.ModuleTemplates.Count); + Assert.AreEqual(rod, h2.ModuleTemplates["test"]); + } + + [Test] + public void ConfiguresApplicationAndModulesFromTemplate() + { + StaticApplicationContext appContext = CreateTestContext(); + + HttpApplicationConfigurer h; + RootObjectDefinition rod; + + h = new HttpApplicationConfigurer(); + rod = new RootObjectDefinition(); + rod.PropertyValues.Add("TestObject", new RuntimeObjectReference("testObject")); + h.ApplicationTemplate = rod; + rod = new RootObjectDefinition(); + rod.PropertyValues.Add("TestObject", new RuntimeObjectReference("testObject1")); + h.ModuleTemplates.Add("TestModule1", rod); + rod = new RootObjectDefinition(); + rod.PropertyValues.Add("TestObject", new RuntimeObjectReference("testObject2")); + h.ModuleTemplates.Add("TestModule2", rod); + + TestModule m1 = new TestModule(); + TestModule m2 = new TestModule(); + + TestApplication appObject = new TestApplication(new ModuleEntry[] + { + new ModuleEntry("TestModule1", m1) + , new ModuleEntry("TestModule2", m2), + }); + HttpApplicationConfigurer.Configure(appContext, appObject); + // app configured + Assert.AreEqual(appContext.GetObject("testObject"), appObject.TestObject); + // modules configured + Assert.AreEqual(appContext.GetObject("testObject1"), m1.TestObject); + Assert.AreEqual(appContext.GetObject("testObject2"), m2.TestObject); + } + + [Test] +#if NET_2_0 + [ExpectedException(typeof(ConfigurationErrorsException))] +#else + [ExpectedException(typeof(System.Configuration.ConfigurationException))] +#endif + public void ThrowsOnUnapplicableModuleTemplate() + { + StaticApplicationContext appContext = CreateTestContext(); + + HttpApplicationConfigurer h; + RootObjectDefinition rod; + + h = new HttpApplicationConfigurer(); + rod = new RootObjectDefinition(); + rod.PropertyValues.Add("TestObject", new RuntimeObjectReference("testObject1")); + h.ModuleTemplates.Add("TestModule1", rod); + + TestApplication appObject = new TestApplication(null); + HttpApplicationConfigurer.Configure(appContext, appObject); + } + + [Test] + public void ConfigureUsingXmlApplicationContext() + { + XmlApplicationContext appContext = new XmlApplicationContext(false, ReadOnlyXmlTestResource.GetFilePath("HttpApplicationConfigurerTests.xml", typeof(HttpApplicationConfigurerTests))); + + TestModule m1 = new TestModule(); + TestModule m2 = new TestModule(); + + TestApplication appObject = new TestApplication(new ModuleEntry[] + { + new ModuleEntry("TestModule1", m1) + , new ModuleEntry("TestModule2", m2), + }); + HttpApplicationConfigurer.Configure( appContext, appObject ); + // app configured + Assert.AreEqual(appContext.GetObject("testObject"), appObject.TestObject); + // modules configured + Assert.AreEqual(appContext.GetObject("testObject1"), m1.TestObject); + Assert.AreEqual(null, m2.TestObject); + } + + private static StaticApplicationContext CreateTestContext() + { + object testObject; + StaticApplicationContext appContext = new StaticApplicationContext(); + appContext.RegisterSingleton("testObject", typeof(object), null); + appContext.RegisterSingleton("testObject1", typeof(object), null); + appContext.RegisterSingleton("testObject2", typeof(object), null); + appContext.Refresh(); + testObject = appContext.GetObject("testObject"); + Assert.IsNotNull(testObject); + return appContext; + } + + #region Test Classes + + public class ModuleEntry + { + // Fields + public readonly string Name; + public readonly IHttpModule Module; + + public ModuleEntry(string name, IHttpModule module) + { + Name = name; + Module = module; + } + } + + public class TestApplication : HttpApplication + { + private static readonly MethodInfo miAddModule = + typeof(HttpModuleCollection).GetMethod("AddModule", BindingFlags.Instance | BindingFlags.NonPublic); + + private object testObject; + + public TestApplication(ModuleEntry[] testModule) + { + HttpModuleCollection modules = this.Modules; + if (testModule != null) + { + ModuleEntry moduleEntry = null; + for (int i = 0; i < testModule.Length; i++) + { + moduleEntry = testModule[i]; + miAddModule.Invoke(modules, new object[] {moduleEntry.Name, moduleEntry.Module}); + } + } + } + + public object TestObject + { + get { return this.testObject; } + set { this.testObject = value; } + } + } + + public class TestModule : IHttpModule + { + private object testObject; + + public object TestObject + { + get { return this.testObject; } + set { this.testObject = value; } + } + + public void Init(HttpApplication context) + { + } + + public void Dispose() + { + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Context/Support/HttpApplicationConfigurerTests.xml b/test/Spring/Spring.Web.Tests/Context/Support/HttpApplicationConfigurerTests.xml new file mode 100644 index 00000000..8f62ca34 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Context/Support/HttpApplicationConfigurerTests.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Context/Support/WebApplicationContextTests.cs b/test/Spring/Spring.Web.Tests/Context/Support/WebApplicationContextTests.cs new file mode 100644 index 00000000..ea0be410 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Context/Support/WebApplicationContextTests.cs @@ -0,0 +1,92 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Threading; +using System.Web; +using NUnit.Extensions.Asp.AspTester; +using NUnit.Framework; +using NUnitAspEx; +using Spring.Objects.Factory; +using Spring.TestSupport; +using Spring.Threading; + +#endregion + +namespace Spring.Context.Support +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: WebApplicationContextTests.cs,v 1.1 2008/02/02 10:24:39 oakinger Exp $ + [AspTestFixture("/Test", "/Spring/Context/Support/WebApplicationContextTests")] + public class WebApplicationContextTests : WebFormTestCase + { + [Test] + public void CanAccessContextFromNonWebThread() + { + IApplicationContext ctx; + + using (TestWebContext requestContext = new TestWebContext("/Test", "/DoesNotExist.oaspx")) + { + ctx = WebApplicationContext.Current; + } + + AsyncTestMethod testMethod = new AsyncTestMethod(1, new AsyncTestMethod.TestMethod(DoBackgroundWork), ctx); + testMethod.Start(); + testMethod.AssertNoException(); + } + + private static object DoBackgroundWork(object[] args) + { + IApplicationContext ctx = (IApplicationContext) args[0]; + + object o; + o = ctx.GetObject("singletonObject"); Assert.IsNotNull(o); + o = ctx.GetObject("prototypeObject"); Assert.IsNotNull(o); + o = ctx.GetObject("applicationScopedObject"); Assert.IsNotNull(o); + try + { + o = ctx.GetObject("requestScopedObject"); Assert.IsNotNull(o); + Assert.Fail("shouldn't be allowed"); + } + catch(ObjectCreationException oce1) + { + Assert.IsTrue(-1 < oce1.Message.IndexOf("without a valid HttpContext.Current instance")); + } + + try + { + o = ctx.GetObject("sessionScopedObject"); Assert.IsNotNull(o); + Assert.Fail("shouldn't be allowed"); + } + catch (ObjectCreationException oce1) + { + Assert.IsTrue(-1 < oce1.Message.IndexOf("without a valid HttpContext.Current instance")); + } + + return null; + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Core/IO/WebResourceTests.cs b/test/Spring/Spring.Web.Tests/Core/IO/WebResourceTests.cs new file mode 100644 index 00000000..f178f5d6 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Core/IO/WebResourceTests.cs @@ -0,0 +1,57 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.TestSupport; + +#endregion + +namespace Spring.Core.IO +{ + /// + /// Unit tests for the WebResource class. + /// + /// Erich Eichinger + /// $Id: WebResourceTests.cs,v 1.6 2008/03/14 12:02:45 oakinger Exp $ + [TestFixture] + public class WebResourceTests : FileSystemResourceCommonTests + { + private VirtualEnvironmentMock testVirtualEnvironment; + + [TestFixtureSetUp] + public void SetUpFixture() + { + testVirtualEnvironment = new VirtualEnvironmentMock("/some.request", "somepathinfo", "/", true); + } + + [TestFixtureTearDown] + public void ShutDownFixture() + { + testVirtualEnvironment.Dispose(); + } + + protected override FileSystemResource CreateResourceInstance(string resourceName) + { + return new WebResource(resourceName); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Context/Support/WebApplicationContextTests/Dummy.aspx b/test/Spring/Spring.Web.Tests/Data/Spring/Context/Support/WebApplicationContextTests/Dummy.aspx new file mode 100644 index 00000000..0a7c23bd --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Context/Support/WebApplicationContextTests/Dummy.aspx @@ -0,0 +1,13 @@ +<%@ Page language="c#" EnableSessionState="false" AutoEventWireup="false" Inherits="Spring.Web.UI.Page" %>OK \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Context/Support/WebApplicationContextTests/Web.Config b/test/Spring/Spring.Web.Tests/Data/Spring/Context/Support/WebApplicationContextTests/Web.Config new file mode 100644 index 00000000..a537a5aa --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Context/Support/WebApplicationContextTests/Web.Config @@ -0,0 +1,61 @@ + + + + + +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Objects/Factory/Support/TestForm.aspx b/test/Spring/Spring.Web.Tests/Data/Spring/Objects/Factory/Support/TestForm.aspx new file mode 100644 index 00000000..48b83c2b --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Objects/Factory/Support/TestForm.aspx @@ -0,0 +1,16 @@ +<%@ Page Language="c#" CodeBehind="TestForm.aspx.cs" Inherits="Spring.Data.Objects.Factory.Support.TestForm" AutoEventWireup="false" %> + + + + + + Untitled Page + + +
    +
    + Look Ma! +
    +
    + + diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Objects/Factory/Support/TestForm.aspx.cs b/test/Spring/Spring.Web.Tests/Data/Spring/Objects/Factory/Support/TestForm.aspx.cs new file mode 100644 index 00000000..7a6cfe5b --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Objects/Factory/Support/TestForm.aspx.cs @@ -0,0 +1,20 @@ +using System; +using Common.Logging; + +namespace Spring.Data.Objects.Factory.Support +{ + public class TestForm : Spring.Web.UI.Page + { + private ILog _log = LogManager.GetLogger(typeof(TestForm)); + + public TestForm() + { + this.Load += new EventHandler(Page_Load); + } + + protected void Page_Load(object sender, EventArgs e) + { + _log.Debug("loaded page!"); + } + } +} diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/DisablesSession.aspx b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/DisablesSession.aspx new file mode 100644 index 00000000..0a7c23bd --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/DisablesSession.aspx @@ -0,0 +1,13 @@ +<%@ Page language="c#" EnableSessionState="false" AutoEventWireup="false" Inherits="Spring.Web.UI.Page" %>OK \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/MaintainsSession1.aspx b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/MaintainsSession1.aspx new file mode 100644 index 00000000..ca6ac839 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/MaintainsSession1.aspx @@ -0,0 +1,7 @@ +<%@ Page language="c#" AutoEventWireup="false" Inherits="Spring.Web.UI.Page" %>OK \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/MaintainsSession2.aspx b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/MaintainsSession2.aspx new file mode 100644 index 00000000..197bff68 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/MaintainsSession2.aspx @@ -0,0 +1,7 @@ +<%@ Page language="c#" AutoEventWireup="false" Inherits="Spring.Web.UI.Page" %>OK \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/TransferAfterSetResult.aspx b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/TransferAfterSetResult.aspx new file mode 100644 index 00000000..2a669b6c --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/TransferAfterSetResult.aspx @@ -0,0 +1,24 @@ +<%@ Page language="c#" AutoEventWireup="false" Inherits="Spring.Web.UI.Page" ClassName="TransferAfterSetResult" %> + + + + + GuestBook + + +
    +

    Guest Book:

    + + Enter your name: + + +
    Name:
    + +
    + + diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/TransferAfterSetResultSave.aspx b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/TransferAfterSetResultSave.aspx new file mode 100644 index 00000000..6b18833f --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/TransferAfterSetResultSave.aspx @@ -0,0 +1,12 @@ +<%@ Page language="c#" AutoEventWireup="false" EnableViewStateMac="false" Inherits="Spring.Web.UI.Page" %> +<%@ Reference Page="TransferAfterSetResult.aspx" %> +OK \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/Web.Config.net-1.1 b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/Web.Config.net-1.1 new file mode 100644 index 00000000..d2c081ef --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/Web.Config.net-1.1 @@ -0,0 +1,64 @@ + + + + + +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/Web.Config.net-2.0 b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/Web.Config.net-2.0 new file mode 100644 index 00000000..48e00f1d --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Data/Spring/Web/Support/PageHandlerFactoryTests/Web.Config.net-2.0 @@ -0,0 +1,90 @@ + + + + + +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Web.Tests/Globalization/Resolvers/DefaultWebCultureResolverTests.cs b/test/Spring/Spring.Web.Tests/Globalization/Resolvers/DefaultWebCultureResolverTests.cs new file mode 100644 index 00000000..f041d292 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Globalization/Resolvers/DefaultWebCultureResolverTests.cs @@ -0,0 +1,121 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Globalization; +using NUnit.Framework; +using Rhino.Mocks; + +#endregion + +namespace Spring.Globalization.Resolvers +{ + /// + /// Tests DefaultWebCultureResolver behaviour. + /// + /// Erich Eichinger + /// $Id: DefaultWebCultureResolverTests.cs,v 1.1 2007/08/25 14:26:36 oakinger Exp $ + [TestFixture] + public class DefaultWebCultureResolverTests + { + #region TestDefaultWebCultureResolver utility class + + /// + /// Override GetRequestLanguage() to return test language instead of HttpRequest.UserLanguages[0] + /// + public class TestDefaultWebCultureResolver : DefaultWebCultureResolver + { + private string _requestLanguage; + + // convenience setter method + public TestDefaultWebCultureResolver SetRequestLanguage(string requestLanguage) + { + _requestLanguage = requestLanguage; + return this; + } + + // override to return our test language instead of HttpRequest.UserLanguages[0]! + protected override string GetRequestLanguage() + { + return _requestLanguage; + } + } + + #endregion + + private readonly CultureInfo EXPECTED_NEUTRALCULTURE = new CultureInfo("fr"); + + [TestFixtureSetUp] + public void TestFixtureSetUp() + { + // ensure, uiCulture and culture are set to different cultures + CultureTestScope.Set(); + } + + [TestFixtureTearDown] + public void TestFixtureTearDown() + { + CultureTestScope.Reset(); + } + + [Test] + public void DefaultCultureDefaultsToNull() + { + TestDefaultWebCultureResolver r = new TestDefaultWebCultureResolver(); + Assert.IsNull(r.DefaultCulture); + } + + [Test] + public void AlwaysReturnsDefaultCultureIfDefaultCultureIsSet() + { + TestDefaultWebCultureResolver r = new TestDefaultWebCultureResolver(); + r.DefaultCulture = EXPECTED_NEUTRALCULTURE; + + r.SetRequestLanguage(null); + Assert.AreEqual(EXPECTED_NEUTRALCULTURE, r.ResolveCulture()); + + r.SetRequestLanguage("de"); + Assert.AreEqual(EXPECTED_NEUTRALCULTURE, r.ResolveCulture()); + } + + [Test] + public void ReturnsRequestCultureIfNoDefaultCulture() + { + TestDefaultWebCultureResolver r = new TestDefaultWebCultureResolver(); + + r.SetRequestLanguage(EXPECTED_NEUTRALCULTURE.Name); + Assert.AreEqual(EXPECTED_NEUTRALCULTURE, r.ResolveCulture()); + } + + [Test] + public void ReturnsCurrentUICultureIfNoDefaultCultureIsSetAndNoOrInvalidRequestLanguage() + { + TestDefaultWebCultureResolver r = new TestDefaultWebCultureResolver(); + + r.SetRequestLanguage(null); + Assert.AreEqual(CultureInfo.CurrentUICulture, r.ResolveCulture()); + + r.SetRequestLanguage("invalid culture name"); + Assert.AreEqual(CultureInfo.CurrentUICulture, r.ResolveCulture()); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/MyNUnitAddinHelper.cs b/test/Spring/Spring.Web.Tests/MyNUnitAddinHelper.cs new file mode 100644 index 00000000..412db0cf --- /dev/null +++ b/test/Spring/Spring.Web.Tests/MyNUnitAddinHelper.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Core.Extensibility; + +#endregion + +namespace Spring +{ + /// + /// Required for auto-registering NUnitAspEx extension with NUnit for this assembly + /// + /// Erich Eichinger + /// $Id: MyNUnitAddinHelper.cs,v 1.1 2007/12/03 16:11:31 oakinger Exp $ + [NUnitAddin] + public class MyNUnitAddinHelper : NUnitAspEx.NUnitAddinHelper + { + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectDefinitionFactoryTests.cs b/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectDefinitionFactoryTests.cs new file mode 100644 index 00000000..31d1d9d8 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectDefinitionFactoryTests.cs @@ -0,0 +1,125 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using NUnitAspEx; +using Spring.TestSupport; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the WebObjectDefinitionFactory class. + /// + /// Erich Eichinger + /// $Id: WebObjectDefinitionFactoryTests.cs,v 1.4 2008/03/14 12:02:45 oakinger Exp $ + [AspTestFixture("/Test", "/Spring/Objects/Factory/Support")] + public class WebObjectDefinitionFactoryTests + { + [Test] + public void CreateRootDefinition() + { + WebObjectDefinitionFactory factory = new WebObjectDefinitionFactory(); + IConfigurableObjectDefinition definition + = factory.CreateObjectDefinition( + typeof(TestObject).FullName, null, AppDomain.CurrentDomain); + Assert.IsNotNull(definition, "CreateObjectDefinition with no parent is returning null (it must never do so)."); + Assert.AreEqual(typeof(TestObject), definition.ObjectType); + Assert.AreEqual(0, definition.PropertyValues.PropertyValues.Length, + "Must not have any property values as none were passed in."); + Assert.AreEqual(0, definition.ConstructorArgumentValues.ArgumentCount, + "Must not have any ctor args as none were passed in."); + } + + [Test] + public void CreateChildDefinition() + { + WebObjectDefinitionFactory factory = new WebObjectDefinitionFactory(); + IConfigurableObjectDefinition definition + = factory.CreateObjectDefinition( + typeof(TestObject).FullName, "Aimee Mann", AppDomain.CurrentDomain); + Assert.IsNotNull(definition, "CreateObjectDefinition with no parent is returning null (it must never do so)."); + Assert.AreEqual(typeof(TestObject), definition.ObjectType); + Assert.AreEqual(0, definition.PropertyValues.PropertyValues.Length, + "Must not have any property values as none were passed in."); + Assert.AreEqual(0, definition.ConstructorArgumentValues.ArgumentCount, + "Must not have any ctor args as none were passed in."); + } + + [Test] + public void DoesNotResolveTypeNameToFullTypeInstanceIfAppDomainIsNull() + { + WebObjectDefinitionFactory factory = new WebObjectDefinitionFactory(); + IConfigurableObjectDefinition definition + = factory.CreateObjectDefinition( + typeof(TestObject).FullName, null, null); + Assert.IsNotNull(definition, "CreateObjectDefinition with no parent is returning null (it must never do so)."); + Assert.AreEqual(typeof(TestObject).FullName, definition.ObjectTypeName); + Assert.AreEqual(0, definition.PropertyValues.PropertyValues.Length, + "Must not have any property values as none were passed in."); + Assert.AreEqual(0, definition.ConstructorArgumentValues.ArgumentCount, + "Must not have any ctor args as none were passed in."); + } + + [Test] + public void ResolvesToPageRootDefinitionIfEndsWithASPX() + { + using (TestWebContext ctx = new TestWebContext("/Test", "testform.aspx")) + { + WebObjectDefinitionFactory factory = new WebObjectDefinitionFactory(); + IWebObjectDefinition definition + = (IWebObjectDefinition)factory.CreateObjectDefinition("/Test/testform.aspx", null, AppDomain.CurrentDomain); + Assert.IsNotNull(definition, "CreateObjectDefinition with no parent is returning null (it must never do so)."); + Assert.IsTrue(definition.IsPage, ".aspx extension must result in a page instance"); + Assert.AreEqual(typeof(RootWebObjectDefinition), definition.GetType()); + } + } + + [Test] + public void ResolvesToPageChildDefinitionIfEndsWithASPX() + { + using (TestWebContext ctx = new TestWebContext("/Test", "testform.aspx")) + { + WebObjectDefinitionFactory factory = new WebObjectDefinitionFactory(); + IWebObjectDefinition definition + = (IWebObjectDefinition)factory.CreateObjectDefinition("/Test/testform.aspx", "parentdefinition", AppDomain.CurrentDomain); + Assert.IsNotNull(definition, "CreateObjectDefinition with parent is returning null (it must never do so)."); + Assert.IsTrue(definition.IsPage, ".aspx extension must result in a page instance"); + Assert.AreEqual(typeof(ChildWebObjectDefinition), definition.GetType()); + } + } + + [Test] + [ExpectedException(typeof(ObjectCreationException))] + public void ThrowsArgumentExceptionOnNonExistingPath() + { + using (TestWebContext ctx = new TestWebContext("/Test", "testform.aspx")) + { + WebObjectDefinitionFactory factory = new WebObjectDefinitionFactory(); + IWebObjectDefinition definition + = (IWebObjectDefinition)factory.CreateObjectDefinition("/Test/DoesNotExist.aspx", "parentdefinition", AppDomain.CurrentDomain); + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectFactoryTests.cs b/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectFactoryTests.cs new file mode 100644 index 00000000..6111a7fb --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Objects/Factory/Support/WebObjectFactoryTests.cs @@ -0,0 +1,86 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Objects.Factory.Config; +using Spring.TestSupport; + +#endregion + +namespace Spring.Objects.Factory.Support +{ + /// + /// Unit tests for the WebObjectFactory class. + /// + /// Rick Evans + /// $Id: WebObjectFactoryTests.cs,v 1.5 2008/03/14 12:02:45 oakinger Exp $ + [TestFixture] + public sealed class WebObjectFactoryTests + { + [Test] + public void CanBeUsedOnNonWebThread() + { + WebObjectFactory wof; + RootWebObjectDefinition rwod; + + // we need to create WOF within a valid HttpContext environment 'cause we will + // make use of 'request' and 'session' scope. + using (new VirtualEnvironmentMock("/somedir/some.file", null, "/", true)) + { + wof = new WebObjectFactory("/somedir/", false); + } + + rwod = new RootWebObjectDefinition(typeof(object), new ConstructorArgumentValues(), new MutablePropertyValues()); + rwod.Scope = ObjectScope.Application; + wof.RegisterObjectDefinition("applicationScopedObject", rwod); + + rwod = new RootWebObjectDefinition(typeof(object), new ConstructorArgumentValues(), new MutablePropertyValues()); + rwod.Scope = ObjectScope.Request; + wof.RegisterObjectDefinition("requestScopedObject", rwod); + + rwod = new RootWebObjectDefinition(typeof(object), new ConstructorArgumentValues(), new MutablePropertyValues()); + rwod.Scope = ObjectScope.Session; + wof.RegisterObjectDefinition("sessionScopedObject", rwod); + + object o; + o = wof.GetObject("applicationScopedObject"); + Assert.IsNotNull(o); + + AssertGetObjectThrows(typeof(ObjectCreationException), wof, "requestScopedObject"); + AssertGetObjectThrows(typeof(ObjectCreationException), wof, "sessionScopedObject"); + } + + private void AssertGetObjectThrows(Type exceptionType, WebObjectFactory wof, string objectName) + { + try + { + wof.GetObject(objectName); + Assert.Fail("must not reach this line"); + } + catch (Exception ex) + { + Assert.AreEqual(exceptionType, ex.GetType()); + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2003.csproj b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2003.csproj new file mode 100644 index 00000000..793e85e4 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2003.csproj @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2005.csproj b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2005.csproj new file mode 100644 index 00000000..6cd494bb --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2005.csproj @@ -0,0 +1,198 @@ + + + Local + 8.0.50727 + 2.0 + {C67E47AA-1ACD-41B4-A465-4D336A2319CA} + Debug + AnyCPU + + + + + Spring.Web.Tests + + + JScript + Grid + IE50 + false + Library + Spring + OnBuildSuccess + + + + + ..\..\..\build\VS.Net.2005\Spring.Web.Tests\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;NET_2_0 + + + true + 4096 + false + 0618 + false + false + false + true + 4 + full + prompt + true + + + ..\..\..\build\VS.Net.2005\Spring.Web.Tests\Release\ + false + 285212672 + false + + + TRACE;NET_2_0 + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + Code + + + + + + + TestForm.aspx + ASPXCodeBehind + + + + + + Code + + + + ASPXCodeBehind + + + + + + + + + ASPXCodeBehind + + + + Code + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + ..\..\..\lib\Net\2.0\antlr.runtime.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\Net\2.0\nunit.core.interfaces.dll + + + False + ..\..\..\lib\Net\2.0\nunit.framework.dll + + + False + ..\..\..\lib\Net\2.0\NUnitAsp.dll + + + False + ..\..\..\lib\Net\2.0\NUnitAspEx.dll + + + False + ..\..\..\lib\Net\2.0\Rhino.Mocks.dll + + + + + + + + + + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {BA4789EB-281A-48EA-8763-28B9F0596A18} + Spring.Web.2005 + + + {44B16BAA-6DF8-447C-9D7F-3AD3D854D904} + Spring.Core.Tests.2005 + + + + + + + echo "Copying .xml files for tests" +xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2005\Spring.Web.Tests\$(ConfigurationName)\ /y /s /q /d + + + \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Spring.Web.Tests.build b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.build new file mode 100644 index 00000000..62314393 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.build @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Web.Tests/TestSupport/NUnitAdapter.cs b/test/Spring/Spring.Web.Tests/TestSupport/NUnitAdapter.cs new file mode 100644 index 00000000..88ce07c2 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/TestSupport/NUnitAdapter.cs @@ -0,0 +1,285 @@ +#region Copyright (c) 2002, 2005 by James Shore +/******************************************************************************************************************** +' +' Copyright (c) 2002, 2005 by James Shore +' +' Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +' documentation files (the "Software"), to deal in the Software without restriction, including without limitation +' the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +' to permit persons to whom the Software is furnished to do so, subject to the following conditions: +' +' The above copyright notice and this permission notice shall be included in all copies or substantial portions +' of the Software. +' +' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +' THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +' CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +' DEALINGS IN THE SOFTWARE. +' +'******************************************************************************************************************/ +#endregion + +#region Instructions +/******************************************************************************************************************** + * + * This file allows NUnitAsp to be used with NUnit. To use, copy this file + * into your test project. For additional information, see the NUnitAsp + * documentation in your download package or visit + * http://nunitasp.sourceforge.net. + * + *******************************************************************************************************************/ +#endregion + +using System; +using NUnit.Extensions.Asp; +using NUnit.Framework; + +namespace Spring.TestSupport +{ + /// + /// Base class for NUnitAsp test fixtures. Extend this class to use NUnitAsp. + /// + [TestFixture] + public abstract class WebFormTestCase : CompatibilityAdapter + { + private bool setupCalled = false; + + /// + /// Do not call. For use by NUnit only. + /// + [SetUp] + public void MasterSetUp() + { + setupCalled = true; + HttpClient.Default = new HttpClient(); + SetUp(); + } + + /// + /// Executed before each test method is run. Override in subclasses to do subclass + /// set up. NOTE: The [SetUp] attribute cannot be used in subclasses because it is already + /// in use. + /// + protected virtual void SetUp() + { + } + + /// + /// Do not call. For use by NUnit only. + /// + [TearDown] + public void MasterTearDown() + { + TearDown(); + } + + /// + /// Executed after each test method is run. Override in subclasses to do subclass + /// clean up. NOTE: [TearDown] attribute cannot be used in subclasses because it is + /// already in use. + /// + protected virtual void TearDown() + { + } + + /// + /// The web form currently loaded by the browser. + /// + protected WebFormTester CurrentWebForm + { + get + { + AssertSetUp(); + return new WebFormTester(HttpClient.Default); + } + } + + /// + /// The web browser. + /// + protected HttpClient Browser + { + get + { + AssertSetUp(); + return HttpClient.Default; + } + } + + private void AssertSetUp() + { + if (!setupCalled) + { + Fail("A required setup method in WebFormTestCase was not called. This is probably because you used the [SetUp] attribute in a subclass of WebFormTestCase. That is not supported. Override the SetUp() method instead."); + } + } + } + + + + + + + + + +// Everything below this line is for backwards compatibility and may be deleted. + + /// + /// For backwards compatibility; will be deprecated in the future. + /// This class provides convenience methods for common assertions. You + /// should use Assert and WebAssert methods instead. + /// + public class CompatibilityAdapter + { + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertTrue(bool condition) + { + Assert.IsTrue(condition); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertTrue(string message, bool condition) + { + Assert.IsTrue(condition, message); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertEquals(object expected, object actual) + { + Assert.AreEqual(expected, actual); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertEquals(string message, object expected, object actual) + { + Assert.AreEqual(expected, actual, message); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertNotNull(object o) + { + Assert.IsNotNull(o); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertNotNull(string message, object o) + { + Assert.IsNotNull(o, message); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertNull(object o) + { + Assert.IsNull(o); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertNull(string message, object o) + { + Assert.IsNull(o, message); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertSame(object expected, object actual) + { + Assert.AreSame(expected, actual); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertSame(string message, object expected, object actual) + { + Assert.AreSame(expected, actual, message); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void Fail(string message) + { + Assert.Fail(message); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertVisibility(ControlTester tester, bool expectedVisibility) + { + if (expectedVisibility) WebAssert.Visible(tester); + else WebAssert.NotVisible(tester); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertEquals(string[] expected, string[] actual) + { + WebAssert.AreEqual(expected, actual); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + public static void AssertEquals(string message, string[] expected, string[] actual) + { + WebAssert.AreEqual(expected, actual, message); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + //[CLSCompliant(false)] + public static void AssertEquals(string[][] expected, string[][] actual) + { + WebAssert.AreEqual(expected, actual); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + //[CLSCompliant(false)] + public static void AssertEquals(string message, string[][] expected, string[][] actual) + { + WebAssert.AreEqual(expected, actual, message); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + //[CLSCompliant(false)] + public static void AssertEqualsIgnoreOrder(string message, string[][] expected, string[][] actual) + { + WebAssert.AreEqualIgnoringOrder(expected, actual, message); + } + + /// + /// For backwards compatibility; will be deprecated in the future. + /// + //[CLSCompliant(false)] + public static void AssertSortOrder(string message, string[][] data, int column, bool isAscending, DataType type) + { + WebAssert.Sorted(data, column, isAscending, type, message); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/TestSupport/TestPage.cs b/test/Spring/Spring.Web.Tests/TestSupport/TestPage.cs new file mode 100644 index 00000000..ff9e781a --- /dev/null +++ b/test/Spring/Spring.Web.Tests/TestSupport/TestPage.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Reflection; +using System.Web; +using System.Web.UI; +using Page=Spring.Web.UI.Page; + +namespace Spring.TestSupport +{ + public class TestPage : Page + { + public TestPage() + { + this.SharedState = CollectionsUtil.CreateCaseInsensitiveHashtable(); + } + + public TestPage( HttpContext context ) + :this() + { + SetIntrinsics(context); + } + + public virtual void SetIntrinsics(HttpContext context) + { + MethodInfo miSetIntrinsics = typeof(System.Web.UI.Page).GetMethod("SetIntrinsics",BindingFlags.Instance|BindingFlags.NonPublic, null, new Type[] { typeof(HttpContext) }, null); + miSetIntrinsics.Invoke(this, new object[] {context}); + } + + public virtual void InitRecursive( Control namingContainer ) + { + MethodInfo miInitRecursive = typeof(System.Web.UI.Control).GetMethod("InitRecursive", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Control) }, null); + miInitRecursive.Invoke(this, new object[] {null}); + } + + public new virtual void InitializeCulture() + { + base.InitializeCulture(); + } + + public override System.Web.SessionState.HttpSessionState Session + { + get + { + return null; + } + } +// protected override IDictionary CreateValidatorParameters() +// { +// return null; +// } + + public new void SetResult(string resultName) + { + base.SetResult(resultName); + } + + public string Render(string newLine) + { + StringWriter sw = new StringWriter(); + HtmlTextWriter writer = new HtmlTextWriter(sw, ""); + writer.NewLine = newLine; + base.Render(writer); + writer.Flush(); + writer.Close(); + string result = sw.GetStringBuilder().ToString(); + return result; + } + } +} diff --git a/test/Spring/Spring.Web.Tests/TestSupport/TestWebContext.cs b/test/Spring/Spring.Web.Tests/TestSupport/TestWebContext.cs new file mode 100644 index 00000000..62d5c1fa --- /dev/null +++ b/test/Spring/Spring.Web.Tests/TestSupport/TestWebContext.cs @@ -0,0 +1,93 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections.Specialized; +using System.IO; +using System.Threading; +using System.Web; +using System.Web.Hosting; + +#endregion + +namespace Spring.TestSupport +{ + public class TestWebContext : IDisposable + { + private readonly TextWriter _out; + private readonly HttpWorkerRequest _wr; + [ThreadStatic] private static TestWebContext _wc; + + public static void Create(string virtualPath, string page) + { + _wc = new TestWebContext(virtualPath, page); + } + + public static void Release() + { + if (_wc != null) + { + _wc.Dispose(); + } + } + + public TestWebContext(string virtualPath, string page) + { + _out = new StringWriter(); + HttpWorkerRequest wr; + object appPath = Thread.GetDomain().GetData(".appPath"); + if (appPath != null) + { + wr = new SimpleWorkerRequest(page, string.Empty, _out); + } + else + { + string physDir = AppDomain.CurrentDomain.BaseDirectory + "\\"; + wr = new SimpleWorkerRequest(virtualPath, physDir, page, string.Empty, _out); + } + HttpContext ctx = new HttpContext(wr); + HttpContext.Current = ctx; + HttpBrowserCapabilities browser = new HttpBrowserCapabilities(); +#if NET_2_0 + browser.Capabilities = CollectionsUtil.CreateCaseInsensitiveHashtable(); + browser.Capabilities[string.Empty] = "Test User Agent"; // string.Empty is the key for "user agent" +#endif + ctx.Request.Browser = browser; + _wr = wr; + } + + public HttpWorkerRequest HttpWorkerRequest + { + get { return _wr; } + } + + public TextWriter Out + { + get { return _out; } + } + + public void Dispose() + { + HttpContext.Current = null; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/TestSupport/TestWebContextTests.cs b/test/Spring/Spring.Web.Tests/TestSupport/TestWebContextTests.cs new file mode 100644 index 00000000..e499218c --- /dev/null +++ b/test/Spring/Spring.Web.Tests/TestSupport/TestWebContextTests.cs @@ -0,0 +1,62 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Globalization; + +#endregion + +namespace Spring.TestSupport +{ + /// + /// The base class for tests to run within a TestWebContext + /// + /// Erich Eichinger + /// $Id: TestWebContextTests.cs,v 1.1 2008/03/15 11:19:39 oakinger Exp $ + public abstract class TestWebContextTests + { + [SetUp] + public void SetUp() + { + // ensure, uiCulture and culture are set to different cultures + CultureTestScope.Set(); + TestWebContext.Create("/apppath", "testpage.aspx"); + DoSetUp(); + } + + [TearDown] + public void TearDown() + { + DoTearDown(); + TestWebContext.Release(); + CultureTestScope.Reset(); + } + + protected virtual void DoSetUp() + { + } + + protected void DoTearDown() + { + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/TestSupport/VirtualEnvironmentMock.cs b/test/Spring/Spring.Web.Tests/TestSupport/VirtualEnvironmentMock.cs new file mode 100644 index 00000000..5c63a4ae --- /dev/null +++ b/test/Spring/Spring.Web.Tests/TestSupport/VirtualEnvironmentMock.cs @@ -0,0 +1,72 @@ +using System; +using System.IO; +using Spring.Util; + +namespace Spring.TestSupport +{ + /// + /// Test environment implementation. + /// + /// Erich Eichinger + /// $Id: VirtualEnvironmentMock.cs,v 1.5 2008/03/14 12:02:46 oakinger Exp $ + internal class VirtualEnvironmentMock : IVirtualEnvironment, IDisposable + { + private readonly IVirtualEnvironment _prevEnvironment; + + private readonly string _currentVirtualFilePath; + private readonly string _pathInfo; + private string _currentExecutionFilePath; + private readonly string _applicationVirtualPath; + + public VirtualEnvironmentMock(string currentVirtualFilePath, string pathInfo, string applicationVirtualPath, bool autoInitialize) + { + _currentVirtualFilePath = currentVirtualFilePath; + _pathInfo = (pathInfo == null || pathInfo.Length == 0) ? "" : "/" + pathInfo.TrimStart('/'); // prevent null string and ensure '/' prefixed + _applicationVirtualPath = "/" + ("" + applicationVirtualPath).Trim('/'); + if (!_applicationVirtualPath.EndsWith("/")) _applicationVirtualPath = _applicationVirtualPath + "/"; + + _prevEnvironment = VirtualEnvironment.SetInstance(this); + if (autoInitialize) + { + VirtualEnvironment.SetInitialized(); + } + } + + public string ApplicationVirtualPath + { + get { return _applicationVirtualPath; } + } + + public string CurrentVirtualPath + { + get + { + return _currentVirtualFilePath + _pathInfo; + } + } + + public string CurrentVirtualFilePath + { + get { return _currentVirtualFilePath; } + } + + public string CurrentExecutionFilePath + { + get { return this._currentExecutionFilePath; } + set { this._currentExecutionFilePath = value; } + } + + public string MapPath(string virtualPath) + { + string basePath = Path.GetDirectoryName(new Uri(GetType().Assembly.CodeBase).LocalPath); + string resultPath = WebUtils.CreateAbsolutePath(this.ApplicationVirtualPath, virtualPath); + resultPath = basePath.TrimEnd('\\') + "\\" + resultPath.Replace('/', '\\').TrimStart('\\'); + return resultPath; + } + + public void Dispose() + { + VirtualEnvironment.SetInstance(_prevEnvironment); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/TestSupport/VoidDelegate.cs b/test/Spring/Spring.Web.Tests/TestSupport/VoidDelegate.cs new file mode 100644 index 00000000..55b6fb14 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/TestSupport/VoidDelegate.cs @@ -0,0 +1,7 @@ +namespace Spring.TestSupport +{ + /// + /// Signature of a method with no args and no return type + /// + internal delegate void VoidDelegate(); +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Threading/HttpContextStorageTests.cs b/test/Spring/Spring.Web.Tests/Threading/HttpContextStorageTests.cs new file mode 100644 index 00000000..69129c2b --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Threading/HttpContextStorageTests.cs @@ -0,0 +1,54 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.IO; +using System.Web; +using NUnit.Framework; + +#endregion + +namespace Spring.Threading +{ + /// + /// Apply common thread-storage tests for + /// + /// Erich Eichinger + /// $Id: HttpContextStorageTests.cs,v 1.1 2007/02/02 21:31:18 oakinger Exp $ + [TestFixture] + public class HttpContextStorageTests : CommonThreadStorageTests + { + protected override IThreadStorage CreateStorage() + { + return new HttpContextStorage(); + } + + protected override void ThreadSetup() + { + HttpContext.Current = new HttpContext(new HttpRequest("/page.aspx", "http://localhost/page.aspx", null), new HttpResponse(new StringWriter())); + } + + protected override void ThreadCleanup() + { + HttpContext.Current = null; + } + } +} diff --git a/test/Spring/Spring.Web.Tests/Threading/HybridContextStorageTests.cs b/test/Spring/Spring.Web.Tests/Threading/HybridContextStorageTests.cs new file mode 100644 index 00000000..1ff54849 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Threading/HybridContextStorageTests.cs @@ -0,0 +1,54 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.IO; +using System.Web; +using NUnit.Framework; + +#endregion + +namespace Spring.Threading +{ + /// + /// Apply common thread-storage tests for + /// + /// Erich Eichinger + /// $Id: HybridContextStorageTests.cs,v 1.1 2007/02/02 21:31:18 oakinger Exp $ + [TestFixture] + public class HybridContextStorageTests : CommonThreadStorageTests + { + protected override IThreadStorage CreateStorage() + { + return new HybridContextStorage(); + } + + protected override void ThreadSetup() + { + HttpContext.Current = new HttpContext(new HttpRequest("/page.aspx", "http://localhost/page.aspx", null), new HttpResponse(new StringWriter())); + } + + protected override void ThreadCleanup() + { + HttpContext.Current = null; + } + } +} diff --git a/test/Spring/Spring.Web.Tests/Util/ControlInterceptionTests.cs b/test/Spring/Spring.Web.Tests/Util/ControlInterceptionTests.cs new file mode 100644 index 00000000..abe77f38 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Util/ControlInterceptionTests.cs @@ -0,0 +1,296 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web.UI; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.TestSupport; +using Spring.Web.Support; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the ControlInterceptor class. + /// + /// Erich Eichinger + /// $Id: ControlInterceptionTests.cs,v 1.9 2008/03/14 12:02:46 oakinger Exp $ + [TestFixture] + public class ControlInterceptionTests + { + private const string RES_OBJECTS = + "assembly://Spring.Web.Tests/Spring.Util/ControlInterceptionTests.objects.xml"; + + static ControlInterceptionTests() + { + Common.Logging.LogManager.Adapter = new Common.Logging.Simple.TraceLoggerFactoryAdapter(); + } + + [SetUp] + public void SetUp() + { + string physDir = AppDomain.CurrentDomain.BaseDirectory + "\\"; + IApplicationContext appContext = + new XmlApplicationContext(AbstractApplicationContext.DefaultRootContextName, false, RES_OBJECTS); + ContextRegistry.RegisterContext(appContext); + appContext = new XmlApplicationContext("/controls/", false, appContext); + ContextRegistry.RegisterContext(appContext); + } + + [TearDown] + public void TearDown() + { + ContextRegistry.Clear(); + } + + [Test] + public void ADDandADDATInjectDependencies() + { + Control ctl = GetRootControl(); + + MockControl mock = new MockControl(); + ctl.Controls.Add(mock); + Assert.IsTrue(mock.GotIt); + + mock = new MockControl(); + ctl.Controls.AddAt(0, mock); + Assert.IsTrue(mock.GotIt); + } + + [Test] + public void HierarchyGetsInjectedAfterAttachToTree() + { + MockControl subControl = new MockControl(); + MockControl subSubControl = new MockControl(); + subControl.Controls.Add(subSubControl); + + // DI occcurs *after* adding control(s) to rooted hierarchy + Assert.IsFalse(subControl.GotIt); + Assert.IsFalse(subSubControl.GotIt); + + Control rootControl = GetRootControl(); + rootControl.Controls.Add(subControl); + Assert.IsTrue(subControl.GotIt); + Assert.IsTrue(subSubControl.GotIt); + + MockControl subSubSubControl = new MockControl(); + subSubControl.Controls.Add(subSubSubControl); + Assert.IsTrue(subSubSubControl.GotIt); + } + + [Test] + public void DealsWithNoninheritableControlCollections() + { + Control rootControl = GetRootControl(); + + Control ctl = new ControlWithNoninheritableCollectionType(); + rootControl.Controls.Add(ctl); + + MockControl subControl = new MockControl(); + ctl.Controls.Add(subControl); + Assert.IsTrue(subControl.GotIt); + + MockControl subControl2 = new MockControl(); + ctl.Controls.AddAt(0, subControl2); + Assert.IsTrue(subControl2.GotIt); + } + + [Test] + public void DealsWithControlCollectionsHavingNonStandardConstructor() + { + Control rootControl = GetRootControl(); + + Control ctl = new ControlWithCollectionTypeHavingNonStandardConstructor(); + rootControl.Controls.Add(ctl); + + MockControl subControl = new MockControl(); + ctl.Controls.Add(subControl); + Assert.IsTrue(subControl.GotIt); + + MockControl subControl2 = new MockControl(); + ctl.Controls.AddAt(0, subControl2); + Assert.IsTrue(subControl2.GotIt); + } + + [Test] + public void DealsWithControlsImplementingISupportsWebDependencyInjection() + { + Control rootControl = GetRootControl(); + + Control ctl = new ControlSupportingWebDI(); + rootControl.Controls.Add(ctl); + + MockControl subControl = new MockControl(); + ctl.Controls.Add(subControl); + Assert.IsTrue(subControl.GotIt); + + MockControl subControl2 = new MockControl(); + ctl.Controls.AddAt(0, subControl2); + Assert.IsTrue(subControl2.GotIt); + } + + [Test] + public void SetsApplicationContextOnIApplicationContextAware() + { + using (new TestWebContext("/", "context.aspx")) + { + Control rootControl = GetRootControl(); + + Control ctl = new ControlSupportingWebDI(); + rootControl.Controls.Add(ctl); + + MockControl subControl = new MockControl(); + ctl.Controls.Add(subControl); + Assert.IsTrue(subControl.GotIt); + Assert.IsNotNull(subControl.ApplicationContext); + Assert.IsTrue(ContextRegistry.GetContext() == subControl.ApplicationContext); + + // controls get AppContext from to their location (if any) + MockControl subControl2 = new MockControl("/controls"); + ctl.Controls.AddAt(0, subControl2); + Assert.IsTrue(subControl2.GotIt); + Assert.IsNotNull(subControl2.ApplicationContext); + Assert.IsTrue(ContextRegistry.GetContext("/controls/") == subControl2.ApplicationContext); + } + } + + private Control GetRootControl() + { + Page ctl = new Page(); + WebDependencyInjectionUtils.InjectDependenciesRecursive(ContextRegistry.GetContext(), ctl); + return ctl; + } + } + + #region Test Support Classes + + public class MockControl : UserControl, IApplicationContextAware + { + private bool _gotIt = false; + private IApplicationContext _applicationContext; + private string _templateSourceDirectory = string.Empty; + + public MockControl() + { + } + + public MockControl(string _templateSourceDirectory) + { + this._templateSourceDirectory = _templateSourceDirectory; + } + + public bool GotIt + { + get { return this._gotIt; } + set + { + // ensure DI occurs only once + Assert.IsFalse(GotIt); + this._gotIt = value; + } + } + + public IApplicationContext ApplicationContext + { + get { return _applicationContext; } + set { _applicationContext = value; } + } + + public override string TemplateSourceDirectory + { + get { return _templateSourceDirectory; } + } + } + + public class SimpleControl : Control + { + } + + internal class NoninheritableControlCollection : ControlCollection + { + public NoninheritableControlCollection(Control owner) + : base(owner) + { + } + } + + public class ControlCollectionWithNonStandardConstructor : ControlCollection + { + private int _someValue; + private string _someAdditionalParameter; + + public ControlCollectionWithNonStandardConstructor(Control owner, string someAdditionalParamater, int someValue) + : base(owner) + { + _someAdditionalParameter = someAdditionalParamater; + _someValue = someValue; + } + + public int SomeValue + { + get { return this._someValue; } + } + + public string SomeAdditionalParameter + { + get { return this._someAdditionalParameter; } + } + } + + public class ControlWithNoninheritableCollectionType : Control + { + protected override ControlCollection CreateControlCollection() + { + return new NoninheritableControlCollection(this); + } + } + + public class ControlWithCollectionTypeHavingNonStandardConstructor : Control + { + protected override ControlCollection CreateControlCollection() + { + return new ControlCollectionWithNonStandardConstructor(this, "someAdditionalParameter", 10); + } + } + + public class ControlSupportingWebDI : Control, ISupportsWebDependencyInjection + { + private IApplicationContext _defaultApplicationContext; + + public IApplicationContext DefaultApplicationContext + { + get { return this._defaultApplicationContext; } + set { this._defaultApplicationContext = value; } + } + + protected override void AddedControl(Control control, int index) + { + WebDependencyInjectionUtils.InjectDependenciesRecursive(_defaultApplicationContext, control); + base.AddedControl(control, index); + } + } + + #endregion Test Support Classes +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Util/ControlInterceptionTests.objects.xml b/test/Spring/Spring.Web.Tests/Util/ControlInterceptionTests.objects.xml new file mode 100644 index 00000000..320c4faf --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Util/ControlInterceptionTests.objects.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Util/WebDIPerformanceTests.cs b/test/Spring/Spring.Web.Tests/Util/WebDIPerformanceTests.cs new file mode 100644 index 00000000..313a6c5d --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Util/WebDIPerformanceTests.cs @@ -0,0 +1,140 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Data; +using System.Web.UI.WebControls; +using NUnit.Framework; +using Spring.Context; +using Spring.Context.Support; +using Spring.TestSupport; + +#if NET_2_0 +using System.Collections; +using System.Collections.Generic; +#endif + +#endregion + +namespace Spring.Util +{ + [TestFixture] + public class WebDIPerformanceTests + { + private DataView CreateDataSource() + { + DataTable dt = new DataTable(); + DataRow dr; + + dt.Columns.Add(new DataColumn("IntegerValue", typeof(Int32))); + dt.Columns.Add(new DataColumn("StringValue", typeof(string))); + dt.Columns.Add(new DataColumn("CurrencyValue", typeof(double))); + + for (int i = 0; i < 100; i++) + { + dr = dt.NewRow(); + + dr[0] = i; + dr[1] = "Item " + i; + dr[2] = 1.23 * (i + 1); + + dt.Rows.Add(dr); + } + + DataView dv = new DataView(dt); + return dv; + } + + [Test, Explicit] + public void RunTestWithoutDI() + { + DataView dv = CreateDataSource(); + + int runs = 1000; + + StopWatch watch = new StopWatch(); + using (watch.Start("Duration: {0}")) + { + for (int i = 0; i < runs; i++) + { + DataGrid grid = new DataGrid(); + grid.DataSource = dv; + grid.DataBind(); + } + } + } + + [Test, Explicit] + public void RunTestWithDI() + { + DataView dv = CreateDataSource(); + IApplicationContext ctx = new StaticApplicationContext(); + + int runs = 1000; + + StopWatch watch = new StopWatch(); + using (watch.Start("Duration: {0}")) + { + for (int i = 0; i < runs; i++) + { + DataGrid grid = new DataGrid(); + Spring.Web.Support.WebDependencyInjectionUtils.InjectDependenciesRecursive(ctx, grid); + grid.DataSource = dv; + grid.DataBind(); + } + } + } + +#if NET_2_0 + [Test, Explicit] + public void TestHashtableVsDictionary() + { + int runs = 10000000; + Hashtable ht = new Hashtable(); + Dictionary dict = new Dictionary(); + + for(int i = 0; i < 100000; i++) + { + ht.Add(i, new object()); + dict.Add(new object(), new object()); + } + + StopWatch watch = new StopWatch(); + using (watch.Start("Duration Hashtable: {0}")) + { + for (int i = 0; i < runs; i++) + { + ht.ContainsKey(i); + } + } + + using (watch.Start("Duration Dictionary: {0}")) + { + for (int i = 0; i < runs; i++) + { + dict.ContainsKey(i); + } + } + } +#endif + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Util/WebUtilsTests.cs b/test/Spring/Spring.Web.Tests/Util/WebUtilsTests.cs new file mode 100644 index 00000000..d8a903da --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Util/WebUtilsTests.cs @@ -0,0 +1,220 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Support; +using Spring.TestSupport; + +#endregion + +namespace Spring.Util +{ + /// + /// Unit tests for the WebUtilsTests class. + /// + /// Rick Evans + /// $Id: WebUtilsTests.cs,v 1.9 2008/03/19 18:05:08 oakinger Exp $ + [TestFixture] + public sealed class WebUtilsTests + { + private const string ExpectedPageName = "foo"; + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void GetPageTypeWithNullPageName() + { + WebObjectUtils.GetPageType(null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void GetPageTypeWithEmptyStringPageName() + { + WebObjectUtils.GetPageType(string.Empty); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void GetPageTypeWithWhitespacedPageName() + { + WebObjectUtils.GetPageType(" "); + } + + [Test] + [ExpectedException(typeof (ObjectCreationException))] + public void CreatePageInstanceWhenNotRunningInServerContext() + { + WebObjectUtils.CreatePageInstance("foo.aspx"); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void GetPageNameWithNullUrl() + { + WebUtils.GetPageName(null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void GetPageNameWithEmptyUrl() + { + WebUtils.GetPageName(string.Empty); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void GetPageNameWithWhitespacedUrl() + { + WebUtils.GetPageName(" "); + } + + [Test] + public void GetPageNameSunnyDay() + { + string pageName = WebUtils.GetPageName("foo.aspx"); + Assert.AreEqual(ExpectedPageName, pageName); + pageName = WebUtils.GetPageName("~/foo.aspx"); + Assert.AreEqual(ExpectedPageName, pageName); + pageName = WebUtils.GetPageName("~/Bingo/foo.aspx"); + Assert.AreEqual(ExpectedPageName, pageName); + pageName = WebUtils.GetPageName("/Bingo/foo.aspx"); + Assert.AreEqual(ExpectedPageName, pageName); + pageName = WebUtils.GetPageName("Bingo/foo.aspx"); + Assert.AreEqual(ExpectedPageName, pageName); + pageName = WebUtils.GetPageName("~/Bingo/Rope/foo.aspx"); + Assert.AreEqual(ExpectedPageName, pageName); + pageName = WebUtils.GetPageName("/Bingo/Rope/foo.aspx"); + Assert.AreEqual(ExpectedPageName, pageName); + pageName = WebUtils.GetPageName("Bingo/Rope/foo.aspx"); + Assert.AreEqual(ExpectedPageName, pageName); + pageName = WebUtils.GetPageName("http://www.madeleine.org/Bingo/Rope/foo.aspx"); + Assert.AreEqual(ExpectedPageName, pageName); + } + + [Test] + public void GetPageNameWherePageHasOddExtension() + { + string pageName = WebUtils.GetPageName("foo.odd"); + Assert.AreEqual(ExpectedPageName, pageName); + } + + /// + /// Dont quite know how this would get mapped by IIS, but + /// WebUtils.GetPageName should return the page as-is in any case. + /// + [Test] + public void GetPageNameWherePageHasNoExtension() + { + string pageName = WebUtils.GetPageName("foo"); + Assert.AreEqual(ExpectedPageName, pageName); + pageName = WebUtils.GetPageName("http://www.madeleine.org/Bingo/Rope/foo"); + Assert.AreEqual(ExpectedPageName, pageName); + } + + [Test] + public void TestCreateAbsolutePath() + { + Assert.AreEqual("/", WebUtils.CreateAbsolutePath("", null)); + Assert.AreEqual("/", WebUtils.CreateAbsolutePath("/", null)); + Assert.AreEqual("/MyApp/", WebUtils.CreateAbsolutePath("/MyApp", null)); + Assert.AreEqual("/MyApp/", WebUtils.CreateAbsolutePath("/MyApp", string.Empty)); + + Assert.AreEqual("/MyPath", WebUtils.CreateAbsolutePath(null, "/MyPath")); + Assert.AreEqual("/MyPath", WebUtils.CreateAbsolutePath(null, "~/MyPath")); + + Assert.AreEqual("/MyPath", WebUtils.CreateAbsolutePath(string.Empty, "/MyPath")); + Assert.AreEqual("/MyPath", WebUtils.CreateAbsolutePath(string.Empty, "~/MyPath")); + + Assert.AreEqual("/MyPath", WebUtils.CreateAbsolutePath("/", "MyPath")); + Assert.AreEqual("/MyPath", WebUtils.CreateAbsolutePath("/", "~/MyPath")); + + Assert.AreEqual("/MyApp/MyPath", WebUtils.CreateAbsolutePath("/MyApp", "MyPath")); + Assert.AreEqual("/MyApp/MyPath", WebUtils.CreateAbsolutePath("/MyApp", "~/MyPath")); + Assert.AreEqual("/MyApp/MyPath", WebUtils.CreateAbsolutePath("/MyApp/", "MyPath")); + Assert.AreEqual("/MyApp/MyPath", WebUtils.CreateAbsolutePath("/MyApp/", "~/MyPath")); + Assert.AreEqual("/MyApp/MyPath", WebUtils.CreateAbsolutePath("/MyApp", "/MyApp/MyPath")); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void CombineVirtualPathsDoesntAcceptNullRoot() + { + WebUtils.CombineVirtualPaths(null, string.Empty); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void CombineVirtualPathsDoesntAcceptEmptyRoot() + { + WebUtils.CombineVirtualPaths(string.Empty, string.Empty); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void CombineVirtualPathsDoesntAcceptNonRootedRoot() + { + WebUtils.CombineVirtualPaths("mypath", string.Empty); + } + + [Test] + public void CombineVirtualPathsInRootWeb() + { + // emulate root website context + using( new VirtualEnvironmentMock("/somedir/some.file", null, "/", true) ) + { + CombineVirtualPathsSuite( "/" ); + } + } + + [Test] + public void CombineVirtualPathsInChildWeb() + { + // emulate child website context + using( new VirtualEnvironmentMock("/somedir/some.file", null, "/myapp", true) ) + { + CombineVirtualPathsSuite( "/myapp/" ); + } + } + + private void CombineVirtualPathsSuite( string appPath ) + { + Assert.AreEqual("/myotherdir/mypath", WebUtils.CombineVirtualPaths("/myotherdir", "mypath")); + Assert.AreEqual("/mypath", WebUtils.CombineVirtualPaths("/irrelevantdir", "/mypath")); // note: that's the difference from CreateAbsolutePath() + Assert.AreEqual(appPath + "mypath", WebUtils.CombineVirtualPaths("/irrelevantdir", "~/mypath")); + Assert.AreEqual(appPath + "some/~/mypath", WebUtils.CombineVirtualPaths("/mydir", "~/some/~/mypath")); + Assert.AreEqual("/mydir/some/~/mypath", WebUtils.CombineVirtualPaths("/mydir", "some/~/mypath")); + Assert.AreEqual("/myotherdir/mypath/my.file", WebUtils.CombineVirtualPaths("/myotherdir/some.file", "mypath/my.file")); + Assert.AreEqual(appPath + "mypath/my.file", WebUtils.CombineVirtualPaths("/myotherdir/some.file", "~/mypath/my.file")); + } + + [Test] + public void GetRelativePath() + { + // case-insensitive + Assert.AreEqual("/Mypath", WebUtils.GetRelativePath("/mydir/", "/myDir/Mypath")); + Assert.AreEqual("/mYpath", WebUtils.GetRelativePath("/Mydir", "/mYdir/mYpath")); + Assert.AreEqual("/myotherdir/mypath", WebUtils.GetRelativePath("/mydir", "/myotherdir/mypath")); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/Services/WebServiceExporterTests.cs b/test/Spring/Spring.Web.Tests/Web/Services/WebServiceExporterTests.cs new file mode 100644 index 00000000..583a9d90 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/Services/WebServiceExporterTests.cs @@ -0,0 +1,283 @@ +#region License + +/* + * Copyright © 2002-2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Text; +using System.Reflection; +using System.Reflection.Emit; +using System.Web.Services; + +using NUnit.Framework; + +using Spring.Util; +using Spring.Core.IO; +using Spring.Objects.Factory.Xml; +using Spring.Objects.Factory; + +#endregion + +namespace Spring.Web.Services +{ + /// + /// Unit tests for the WebServiceExporter class. + /// + /// Bruno Baia + /// $Id: WebServiceExporterTests.cs,v 1.7 2007/12/07 20:26:38 bbaia Exp $ + [TestFixture] + public sealed class WebServiceExporterTests + { + WebServiceExporter wse = null; + + [SetUp] + public void SetUp() + { + const string xml = + @" + + + +"; + Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + IObjectFactory objectFactory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); + + wse = new WebServiceExporter(); + wse.ObjectFactory = objectFactory; + } + + [Test] + [ExpectedException(typeof(ArgumentException), "The TargetName property is required.")] + public void NullConfig() + { + wse.ObjectName = "NullConfig"; + wse.AfterPropertiesSet(); + } + + [Test] + public void ProxiesTargetInterfaces() + { + wse.ObjectName = "ProxiesTargetInterfaces"; + wse.TargetName = "noDecoratedService"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + Assert.IsTrue(typeof(IService).IsAssignableFrom(proxyType)); + } + + [Test] + public void CreatesWebServiceAttributeWithNoDecoratedClassAndMinimalConfig() + { + wse.ObjectName = "CreatesWebServiceAttributeWithNoDecoratedClassAndMinimalConfig"; + wse.TargetName = "noDecoratedService"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + object[] attrs = proxyType.GetCustomAttributes(typeof(WebServiceAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; + Assert.AreEqual("CreatesWebServiceAttributeWithNoDecoratedClassAndMinimalConfig", wsa.Name); + Assert.AreEqual(string.Empty, wsa.Description); + Assert.AreEqual(WebServiceAttribute.DefaultNamespace, wsa.Namespace); + } + + [Test] + public void CreatesWebServiceAttributeWithNoDecoratedClassAndFullConfig() + { + wse.ObjectName = "CreatesWebServiceAttributeWithNoDecoratedClassAndFullConfig"; + wse.TargetName = "noDecoratedService"; + wse.Name = "My web service name"; + wse.Description = "My web service description"; + wse.Namespace = "http://www.springframework.net"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + object[] attrs = proxyType.GetCustomAttributes(typeof(WebServiceAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; + Assert.AreEqual(wse.Name, wsa.Name); + Assert.AreEqual(wse.Description, wsa.Description); + Assert.AreEqual(wse.Namespace, wsa.Namespace); + } + + [Test] + public void CreatesDefaultWebMethodAttributeWithNoDecoratedMethod() + { + wse.ObjectName = "CreatesDefaultWebMethodAttributeWithNoDecoratedMethod"; + wse.TargetName = "noDecoratedService"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + MethodInfo method = proxyType.GetMethod("SomeMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + } + + [Test] + public void CreatesCustomWebMethodAttributeWithNoDecoratedMethod() + { + wse.ObjectName = "CreatesCustomWebMethodAttributeWithNoDecoratedMethod"; + wse.TargetName = "noDecoratedService"; + wse.MemberAttributes.Add("SomeMethod", new WebMethodAttribute(true)); // default value is false + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + MethodInfo method = proxyType.GetMethod("SomeMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebMethodAttribute wma = attrs[0] as WebMethodAttribute; + Assert.AreEqual(true, wma.EnableSession); + } + + [Test] + public void OverridesExistingWebServiceAttributeWithDecoratedClass() + { + wse.ObjectName = "OverridesExistingWebServiceAttributeWithDecoratedClass"; + wse.TargetName = "decoratedService"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + object[] attrs = proxyType.GetCustomAttributes(typeof(WebServiceAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; + Assert.AreEqual("OverridesExistingWebServiceAttributeWithDecoratedClass", wsa.Name); + Assert.AreEqual(string.Empty, wsa.Description); + Assert.AreEqual(WebServiceAttribute.DefaultNamespace, wsa.Namespace); + } + + [Test] + public void UsesExistingWebMethodAttributeWithDecoratedMethod() + { + wse.ObjectName = "UsesExistingWebMethodAttributeWithDecoratedMethod"; + wse.TargetName = "decoratedService"; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + MethodInfo method = proxyType.GetMethod("SomeMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebMethodAttribute wma = attrs[0] as WebMethodAttribute; + Assert.AreEqual("SomeMethod description", wma.Description); + } + + // TODO : attributes override + [Test] + [Ignore("Attributes override not implemented.")] + public void OverridesExistingWebMethodAttributeWithDecoratedMethod() + { + wse.ObjectName = "OverridesExistingWebMethodAttributeWithDecoratedMethod"; + wse.TargetName = "decoratedService"; + wse.MemberAttributes.Add("SomeMethod", new WebMethodAttribute(true)); // default value is false + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + MethodInfo method = proxyType.GetMethod("SomeMethod"); + Assert.IsNotNull(method); + + object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebMethodAttribute wma = attrs[0] as WebMethodAttribute; + Assert.AreEqual(true, wma.EnableSession); + } + + + [Test] + public void AppliesCustomAttributeBuilderToType() + { + wse.ObjectName = "AppliesCustomAttributeBuilderToType"; + wse.TargetName = "noDecoratedService"; + wse.TypeAttributes = new CustomAttributeBuilder[] { + new CustomAttributeBuilder(typeof(WebServiceAttribute).GetConstructor(Type.EmptyTypes), ObjectUtils.EmptyObjects) }; + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + + object[] attrs = proxyType.GetCustomAttributes(typeof(WebServiceAttribute), true); + Assert.IsNotEmpty(attrs); + Assert.AreEqual(1, attrs.Length); + + WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; + Assert.AreEqual("AppliesCustomAttributeBuilderToType", wsa.Name); + } + + [Test] + public void UseCustomWebServiceType() + { + wse.ObjectName = "UseCustomWebServiceType"; + wse.TargetName = "noDecoratedService"; + wse.WebServiceBaseType = typeof(CustomWebService); + wse.AfterPropertiesSet(); + + Type proxyType = wse.ObjectType; + Assert.IsTrue(typeof(CustomWebService).IsAssignableFrom(proxyType)); + } + + #region Test classes + + public interface IService + { + string SomeMethod(int param); + } + + public class NoDecoratedService : IService + { + public string SomeMethod(int param) + { + return param.ToString(); + } + } + + [WebService(Name = "Decorated service")] + public class DecoratedService : IService + { + [WebMethod(Description="SomeMethod description")] + public string SomeMethod(int param) + { + return param.ToString(); + } + } + + public class CustomWebService : WebService + { + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/Support/AbstractHandlerFactoryTests.cs b/test/Spring/Spring.Web.Tests/Web/Support/AbstractHandlerFactoryTests.cs new file mode 100644 index 00000000..153946fc --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/Support/AbstractHandlerFactoryTests.cs @@ -0,0 +1,86 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web; +using NUnit.Framework; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: AbstractHandlerFactoryTests.cs,v 1.1 2008/03/19 18:05:08 oakinger Exp $ + [TestFixture] + public class AbstractHandlerFactoryTests : AbstractHandlerFactory + { + private class Type1 {} + + [Test] + public void FindWebObjectDefinition() + { + NamedObjectDefinition nod; + + nod = Find("/path/o1.ext", "/path/o1.ext"); + Assert.AreEqual( typeof(Type1), nod.ObjectDefinition.ObjectType); + Assert.AreEqual( "/path/o1.ext", nod.Name); + + nod = Find("/path/o1.ext", "/o1.ext"); + Assert.IsNull(nod); + + nod = Find("/path/o1.ext", "/path/o1"); + Assert.IsNull(nod); + + nod = Find("/path/o1.ext", "o1.ext"); + Assert.AreEqual(typeof(Type1), nod.ObjectDefinition.ObjectType); + Assert.AreEqual("o1.ext", nod.Name); + + nod = Find("/path/o1.ext", "o1"); + Assert.AreEqual(typeof(Type1), nod.ObjectDefinition.ObjectType); + Assert.AreEqual("o1", nod.Name); + } + + private NamedObjectDefinition Find(string url, string objectName) + { + DefaultListableObjectFactory of = new DefaultListableObjectFactory(); + RootObjectDefinition rod = new RootObjectDefinition(typeof(Type1)); + of.RegisterObjectDefinition(objectName, rod); + + return FindWebObjectDefinition( url, of ); + } + + #region AbstractHandlerFactory implementations + + public override IHttpHandler GetHandler(HttpContext context, string requestType, string url, + string pathTranslated) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/Support/MimeMediaTypeTests.cs b/test/Spring/Spring.Web.Tests/Web/Support/MimeMediaTypeTests.cs new file mode 100644 index 00000000..e1ba007e --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/Support/MimeMediaTypeTests.cs @@ -0,0 +1,94 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Tests the MediaType class according to http://www.iana.org/assignments/media-types/ + /// + /// Erich Eichinger + /// $Id: MimeMediaTypeTests.cs,v 1.1 2007/11/28 23:26:28 oakinger Exp $ + [TestFixture] + public class MimeMediaTypeTests + { + [Test] + public void CreateInstance() + { + Assert.AreEqual( MimeMediaType.Application.Octet, new MimeMediaType() ); + MimeMediaType mt = new MimeMediaType("ApPlicatioN"); + Assert.AreEqual("application", mt.ContentType); + Assert.AreEqual("*", mt.SubType); + + Assert.AreEqual("*", new MimeMediaType("ApPlicatioN", null).SubType); + Assert.AreEqual("*", new MimeMediaType("ApPlicatioN", "").SubType); + Assert.AreEqual("bla", new MimeMediaType("ApPlicatioN", "bla").SubType); + } + + [Test] + public void ToStringReturnsMimeTypeString() + { + Assert.AreEqual("application/subtype", new MimeMediaType("ApPlicatioN", "SubType").ToString()); + } + + [Test] + public void Parse() + { + MimeMediaType mt = MimeMediaType.Parse("text/html"); + Assert.AreEqual("text", mt.ContentType); + Assert.AreEqual("html", mt.SubType); + + try { mt = MimeMediaType.Parse(null); Assert.Fail(); } + catch (ArgumentNullException) { /* ok */ } + try { mt = MimeMediaType.Parse(""); Assert.Fail(); } + catch (ArgumentException) { /* ok */ } + try { mt = MimeMediaType.Parse("aasdfaDF"); Assert.Fail(); } + catch (ArgumentException) { /* ok */ } + try { mt = MimeMediaType.Parse("*"); Assert.Fail(); } + catch (ArgumentException) { /* ok */ } + } + + [Test] + public void Equals() + { + MimeMediaType mt = MimeMediaType.Parse("text/html"); + Assert.AreEqual(MimeMediaType.Text.Html, mt); + Assert.AreEqual(MimeMediaType.Parse("text/html"), mt); + + Assert.AreNotEqual(MimeMediaType.Parse("text/xml"), mt); + } + + [Test] + public void GetHashcode() + { + MimeMediaType mt = MimeMediaType.Parse("text/html"); + Assert.AreEqual(MimeMediaType.Text.Html.GetHashCode(), mt.GetHashCode()); + Assert.AreEqual(MimeMediaType.Parse("text/html").GetHashCode(), mt.GetHashCode()); + + Assert.AreNotEqual(MimeMediaType.Parse("text/xml").GetHashCode(), mt.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/Support/PageHandlerFactoryTests.cs b/test/Spring/Spring.Web.Tests/Web/Support/PageHandlerFactoryTests.cs new file mode 100644 index 00000000..39606add --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/Support/PageHandlerFactoryTests.cs @@ -0,0 +1,129 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web; +using System.IO; +using NUnit.Extensions.Asp.AspTester; +using NUnit.Framework; +using NUnitAspEx; +using Spring.TestSupport; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: PageHandlerFactoryTests.cs,v 1.4 2008/01/27 23:29:55 oakinger Exp $ + [AspTestFixture("/Test", "/Spring/Web/Support/PageHandlerFactoryTests")] + public class PageHandlerFactoryTests : WebFormTestCase + { + [Test] + public void DisablesSessions() + { + AspTestClient client = new AspTestClient(); + // a session-less page - checks, if session is correctly disabled + string result = client.GetPage("DisablesSession.aspx"); + Assert.AreEqual("OK", result); + } + + [Test] + public void MaintainsSession() + { + AspTestClient client = new AspTestClient(); + string result = client.GetPage("MaintainsSession1.aspx"); + Assert.AreEqual("OK", result); + Assert.AreEqual("somevalue", AspTestContext.HttpContext.Session["maintainsSession"]); + + // checks previously set session variable + result = client.GetPage("MaintainsSession2.aspx"); + Assert.AreEqual("OK", result); + } + + [Test] + public void BCLPageHandlerFactoryBehavior() + { + using (TestWebContext ctx = new TestWebContext("/Test", "DoesNotExist.oaspx")) + { + try + { + IHttpHandlerFactory phf = (IHttpHandlerFactory)Activator.CreateInstance(typeof(System.Web.UI.Page).Assembly.GetType("System.Web.UI.PageHandlerFactory"), true); + phf.GetHandler(HttpContext.Current, "GET", ctx.HttpWorkerRequest.GetFilePath(), ctx.HttpWorkerRequest.GetFilePathTranslated()); + } +#if NET_2_0 + catch (HttpException e) + { + Assert.AreEqual(404, e.GetHttpCode()); + Assert.IsTrue(e.Message.IndexOf(ctx.HttpWorkerRequest.GetFilePath()) > 0); + } +#else + catch (FileNotFoundException) + { + } +#endif + } + } + + [Test] + public void PageHandlerFactoryBehavesLikeSystemPageHandlerFactory() + { + using (TestWebContext ctx = new TestWebContext("/Test", "DoesNotExist.aspx")) + { + try + { + IHttpHandlerFactory phf = new PageHandlerFactory(); + phf.GetHandler(HttpContext.Current, "GET", ctx.HttpWorkerRequest.GetFilePath(), ctx.HttpWorkerRequest.GetFilePathTranslated()); + } +#if NET_2_0 + catch (HttpException e) + { + Assert.AreEqual(404, e.GetHttpCode()); + Assert.IsTrue(e.Message.IndexOf(ctx.HttpWorkerRequest.GetFilePath()) > 0); + } +#else + catch (FileNotFoundException) + { + } +#endif + } + } + +#if NET_2_0 + [Test] + public void TransferAfterSetResult() + { + TextBoxTester name = new TextBoxTester("name", CurrentWebForm); + ButtonTester save = new ButtonTester("save", CurrentWebForm); + + Browser.GetPage("asptest://localhost/TransferAfterSetResult.aspx"); + // Note, that page TransferAfterSetResultSave.aspx has 'EnableViewStateMac="false"' + // otherwise ViewState validation will fail on a Server.Transfer during a Postback! + save.Click(); + string result = Browser.CurrentPageText; + Assert.AreEqual("OK", result); + } +#endif + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/Support/ResultTests.cs b/test/Spring/Spring.Web.Tests/Web/Support/ResultTests.cs new file mode 100644 index 00000000..ea6a44b1 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/Support/ResultTests.cs @@ -0,0 +1,338 @@ +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Unit tests for the Result class. + /// + /// Rick Evans + /// $Id: ResultTests.cs,v 1.4 2007/04/19 07:49:36 oakinger Exp $ + [TestFixture] + public sealed class ResultTests + { + private const string ExpectedTargetPageName = "Foo.aspx"; + + [Test] + public void DoesItDefaultToTheDefaultResultMode() + { + Result result = new Result(); + Assert.AreEqual(Result.DefaultResultMode, result.Mode, "Not defaulting to the default ResultMode."); + } + + [Test] + public void DefaultsToTransferMode() + { + Assert.AreEqual(ResultMode.Transfer, Result.DefaultResultMode, + "Hey! Be sure to change the documentation since you've " + + "obviously gone and changed the default transfer mode."); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationBailsWithNullResultString() + { + new Result(null); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationBailsWithEmptyResultString() + { + new Result(string.Empty); + } + + [Test] + [ExpectedException(typeof (ArgumentNullException))] + public void InstantiationBailsWithAllWhitespaceResultString() + { + new Result(" \n\t "); + } + + [Test] + public void WithNoResultModeAndNoParameters() + { + Result result = new Result(ExpectedTargetPageName); + Assert.AreEqual(Result.DefaultResultMode, result.Mode, "Not defaulting to the default ResultMode."); + Assert.IsNull(result.Parameters, + "No parameters were passed but the Parameters property appears to be set to a non-null value anyway."); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + public void WithWhitespacePageAndNoResultModeAndNoParameters() + { + Result result = new Result(" " + ExpectedTargetPageName + "\n"); + Assert.AreEqual(Result.DefaultResultMode, result.Mode, "Not defaulting to the default ResultMode."); + Assert.IsNull(result.Parameters, + "No parameters were passed but the Parameters property appears to be set to a non-null value anyway."); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + public void WithTransferResultModeButNoParameters() + { + Result result = new Result("transfer:" + ExpectedTargetPageName); + Assert.AreEqual(ResultMode.Transfer, result.Mode, "Not extracting the correct ResultMode " + + "from the result string passed into the ctor."); + Assert.AreEqual(true, result.PreserveForm, "ResultMode.Transfer must result in PreserveForm being 'true'"); + Assert.IsNull(result.Parameters, + "No parameters were passed but the Parameters property appears to be set to a non-null value anyway."); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + public void WithTransferNoPreserveResultMode() + { + Result result = new Result( "transfernopreserve:" + ExpectedTargetPageName ); + Assert.AreEqual( ResultMode.TransferNoPreserve,result.Mode,"Not extracting the correct ResultMode " + + "from the result string passed into the ctor." ); + Assert.AreEqual( false,result.PreserveForm,"ResultMode.TransferNoPreserver must result in PreserveForm being 'false'" ); + } + + [Test] + public void WithWhitespacedRedirectResultMode() + { + Result result = new Result(" redirect:" + ExpectedTargetPageName); + Assert.AreEqual(ResultMode.Redirect, result.Mode, "Not extracting the correct ResultMode " + + "from the result string passed into the ctor."); + Assert.IsNull(result.Parameters, + "No parameters were passed but the Parameters property appears to be set to a non-null value anyway."); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + public void WithRedirectResultMode() + { + Result result = new Result("redirect:" + ExpectedTargetPageName); + Assert.AreEqual(ResultMode.Redirect, result.Mode, "Not extracting the correct ResultMode " + + "from the result string passed into the ctor."); + Assert.IsNull(result.Parameters, + "No parameters were passed but the Parameters property appears to be set to a non-null value anyway."); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void WithRubbishResultMode() + { + // note the incorrect spelling of the result mode prefix... + new Result("redirct:" + ExpectedTargetPageName); + } + + [Test] + public void WithResultModeAndEmptyParameterDelimiterOnly() + { + Result result = new Result("transfer:" + ExpectedTargetPageName + "?"); + Assert.AreEqual(ResultMode.Transfer, result.Mode, "Not extracting the correct ResultMode " + + "from the result string passed into the ctor."); + Assert.IsNull(result.Parameters, + "No parameters were passed (just the parameter delimiter) but the " + + "Parameters property appears to be set to a non-null value anyway."); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + public void WithResultModeAndWhitespaceParameterDelimiterOnly() + { + Result result = new Result("redirect:" + ExpectedTargetPageName + "? "); + Assert.AreEqual(ResultMode.Redirect, result.Mode, "Not extracting the correct ResultMode " + + "from the result string passed into the ctor."); + Assert.IsNull(result.Parameters, + "No parameters were passed (just the parameter delimiter) but the " + + "Parameters property appears to be set to a non-null value anyway."); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + public void WithNoResultModeAndWhitespaceParameterDelimiterOnly() + { + Result result = new Result(ExpectedTargetPageName + "? "); + Assert.AreEqual(Result.DefaultResultMode, result.Mode, "Not defaulting to the default ResultMode."); + Assert.IsNull(result.Parameters, + "No parameters were passed (just the parameter delimiter) but the " + + "Parameters property appears to be set to a non-null value anyway."); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + public void WithNoResultModeAndOneParameter() + { + const string key = "id"; + const string value = "Nina Persson"; + Result result = new Result(ExpectedTargetPageName + "?" + key + "=" + value); + Assert.AreEqual(Result.DefaultResultMode, result.Mode, "Not defaulting to the default ResultMode."); + Assert.IsNotNull(result.Parameters, + "Parameters were passed but the Parameters property appears to be null (incorrectly)."); + Assert.AreEqual(1, result.Parameters.Count, + "Wrong number of query parameters parsed (only supplied one key-value pair)."); + Assert.IsTrue(result.Parameters.Contains(key), "The 'id' parameter is not being correctly extracted " + + "from the result string passed into the ctor."); + Assert.AreEqual(value, result.Parameters[key]); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + public void WithTransferResultModeAndOneParameter() + { + const string key = "id"; + const string value = "Nina Persson"; + Result result = new Result("transfer:" + ExpectedTargetPageName + "?" + key + "=" + value); + Assert.AreEqual(ResultMode.Transfer, result.Mode, "Not extracting the correct ResultMode " + + "from the result string passed into the ctor."); + Assert.IsNotNull(result.Parameters, + "Parameters were passed but the Parameters property appears to be null (incorrectly)."); + Assert.AreEqual(1, result.Parameters.Count, + "Wrong number of query parameters parsed (only supplied one key-value pair)."); + Assert.IsTrue(result.Parameters.Contains(key), "The 'id' parameter is not being correctly extracted " + + "from the result string passed into the ctor."); + Assert.AreEqual(value, result.Parameters[key]); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + [Ignore("How does one escape ampersands then?")] + public void ParametersHavingEmbeddedCharactersThatHaveToBeEscaped() + { + const string key = "id"; + const string value = "Nina Persson & The Cardigans"; // notice the use of the embedded '&'... + Result result = new Result(ExpectedTargetPageName + "?" + key + "=" + value); + Assert.AreEqual(Result.DefaultResultMode, result.Mode, "Not defaulting to the default ResultMode."); + Assert.IsNotNull(result.Parameters, + "Parameters were passed but the Parameters property appears to be null (incorrectly)."); +// Assert.AreEqual(1, result.Parameters.Count, +// "Wrong number of query parameters parsed (only supplied one key-value pair)."); +// Assert.IsTrue(result.Parameters.Contains(key), "The 'id' parameter is not being correctly extracted " + +// "from the result string passed into the ctor."); +// Assert.AreEqual(value, result.Parameters[key]); +// Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, +// "The TargetPage property is not being correctly extracted " + +// "from the result string passed into the ctor."); + } + + [Test] + public void WithTransferResultModeAndTwoParametersSplitUsingAmpersand() + { + const string pKey1 = "id"; + const string pValue1 = "Nina Persson"; + const string pKey2 = "verdict"; + const string pValue2 = "schwing"; + Result result = + new Result(ExpectedTargetPageName + "?" + pKey1 + "=" + pValue1 + "&" + pKey2 + "=" + pValue2); + Assert.AreEqual(ResultMode.Transfer, result.Mode, "Not extracting the correct ResultMode " + + "from the result string passed into the ctor."); + Assert.IsNotNull(result.Parameters, + "Parameters were passed but the Parameters property appears to be null (incorrectly)."); + Assert.AreEqual(2, result.Parameters.Count, + "Wrong number of query parameters parsed (supplied two key-value pairs)."); + Assert.IsTrue(result.Parameters.Contains(pKey1), "The 'id' parameter is not being correctly extracted " + + "from the result string passed into the ctor."); + Assert.AreEqual(pValue1, result.Parameters[pKey1]); + Assert.IsTrue(result.Parameters.Contains(pKey2), + "The 'verdict' parameter is not being correctly extracted " + + "from the result string passed into the ctor."); + Assert.AreEqual(pValue2, result.Parameters[pKey2]); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + public void WithWhitespacedPageAndTransferResultModeAndTwoParametersUsingAmpersands() + { + const string pKey1 = "id"; + const string pValue1 = "Nina Persson"; + const string pKey2 = "verdict"; + const string pValue2 = "schwing"; + Result result = + new Result("\n" + ExpectedTargetPageName + " ?" + pKey1 + "=" + pValue1 + "&" + pKey2 + "=" + pValue2); + Assert.AreEqual(ResultMode.Transfer, result.Mode, "Not extracting the correct ResultMode " + + "from the result string passed into the ctor."); + Assert.IsNotNull(result.Parameters, + "Parameters were passed but the Parameters property appears to be null (incorrectly)."); + Assert.AreEqual(2, result.Parameters.Count, + "Wrong number of query parameters parsed (supplied two key-value pairs)."); + Assert.IsTrue(result.Parameters.Contains(pKey1), "The 'id' parameter is not being correctly extracted " + + "from the result string passed into the ctor."); + Assert.AreEqual(pValue1, result.Parameters[pKey1]); + Assert.IsTrue(result.Parameters.Contains(pKey2), + "The 'verdict' parameter is not being correctly extracted " + + "from the result string passed into the ctor."); + Assert.AreEqual(pValue2, result.Parameters[pKey2]); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + + [Test] + public void WithWhitespacedPageAndTransferResultModeAndTwoParametersUsingCommas() + { + const string pKey1 = "id"; + const string pValue1 = "Nina Persson"; + const string pKey2 = "verdict"; + const string pValue2 = "schwing"; + Result result = + new Result("\n" + ExpectedTargetPageName + " ?" + pKey1 + "=" + pValue1 + "," + pKey2 + "=" + pValue2); + Assert.AreEqual(ResultMode.Transfer, result.Mode, "Not extracting the correct ResultMode " + + "from the result string passed into the ctor."); + Assert.IsNotNull(result.Parameters, + "Parameters were passed but the Parameters property appears to be null (incorrectly)."); + Assert.AreEqual(2, result.Parameters.Count, + "Wrong number of query parameters parsed (supplied two key-value pairs)."); + Assert.IsTrue(result.Parameters.Contains(pKey1), "The 'id' parameter is not being correctly extracted " + + "from the result string passed into the ctor."); + Assert.AreEqual(pValue1, result.Parameters[pKey1]); + Assert.IsTrue(result.Parameters.Contains(pKey2), + "The 'verdict' parameter is not being correctly extracted " + + "from the result string passed into the ctor."); + Assert.AreEqual(pValue2, result.Parameters[pKey2]); + Assert.AreEqual(ExpectedTargetPageName, result.TargetPage, + "The TargetPage property is not being correctly extracted " + + "from the result string passed into the ctor."); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs b/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs new file mode 100644 index 00000000..2f395ec7 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs @@ -0,0 +1,129 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web.UI; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Context; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: AbstractValidationControlTests.cs,v 1.1 2008/03/19 12:07:15 oakinger Exp $ + [TestFixture] + public class AbstractValidationControlTests + { + public class TestValidationControl : AbstractValidationControl + { + private IValidationContainer _vc; + + public TestValidationControl() + { + } + + public TestValidationControl(IValidationContainer testVc) + { + _vc = testVc; + } + + protected override Spring.Web.UI.Validation.IValidationErrorsRenderer CreateValidationErrorsRenderer() + { + throw new Exception("The method or operation is not implemented."); + } + + protected override IValidationContainer ValidationContainer + { + get + { + if (_vc != null) return _vc; + return base.ValidationContainer; + } + } + + public IMessageSource TheMessageSource + { + get { return base.MessageSource; } + } + + public IValidationContainer TheValidationContainer + { + get { return base.ValidationContainer; } + } + } + + [Test] + public void ResolveValidationContainerToEnclosingParentRecursively() + { + // direct containment + UserControl uc = new UserControl(); + TestValidationControl vc = new TestValidationControl(); + uc.Controls.Add(vc); + Assert.AreSame(uc, vc.TheValidationContainer); + + // indirect containment + uc = new UserControl(); + vc = new TestValidationControl(); + Control inBetweenControl = new Panel(); + inBetweenControl.Controls.Add(vc); + uc.Controls.Add(inBetweenControl); + Assert.AreSame(uc, vc.TheValidationContainer); + } + + [Test] + public void ResolveValidationContainerToEnclosingPageRecursively() + { + // direct containment + Page page = new Page(); + TestValidationControl vc = new TestValidationControl(); + page.Controls.Add(vc); + Assert.AreSame(page, vc.TheValidationContainer); + + // indirect containment + page = new Page(); + vc = new TestValidationControl(); + Control inBetweenControl = new System.Web.UI.WebControls.Panel(); + inBetweenControl.Controls.Add(vc); + page.Controls.Add(inBetweenControl); + Assert.AreSame(page, vc.TheValidationContainer); + } + + [Test] + public void DefaultsToValidationContainerMessageSource() + { + MockRepository mocks = new MockRepository(); + IMessageSource messageSource = (IMessageSource)mocks.DynamicMock(typeof(IMessageSource)); + IValidationContainer container = (IValidationContainer)mocks.DynamicMock(typeof(IValidationContainer)); + TestValidationControl ctl = new TestValidationControl(container); + + Expect.Call(container.MessageSource).Return(messageSource); + mocks.ReplayAll(); + + Assert.AreEqual(messageSource, ctl.TheMessageSource); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/UI/Controls/HeadTests.cs b/test/Spring/Spring.Web.Tests/Web/UI/Controls/HeadTests.cs new file mode 100644 index 00000000..27c10ff8 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/UI/Controls/HeadTests.cs @@ -0,0 +1,164 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.IO; +using System.Web; +using System.Web.UI; +using System.Web.UI.HtmlControls; +using NUnit.Framework; +using Spring.TestSupport; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// Tests behaviour of the <Head> server control + /// + /// Erich Eichinger + /// $Id: HeadTests.cs,v 1.1 2007/11/28 23:26:28 oakinger Exp $ + [TestFixture] + public class HeadTests + { + private const string CRLF = "\r\n"; + +#if NET_2_0 + [Test] + public void DontRenderHeadTagIfNestedWithinStandardHeadControl() + { + using (new TestWebContext("/", "testpage.aspx")) + { + TestPage page = new TestPage(HttpContext.Current); + HtmlHead htmlHead = new HtmlHead(); + Head head = new Head(); + head.Controls.Add(new LiteralControl("literal child")); + htmlHead.Controls.Add(head); + page.Controls.Add(htmlHead); + + // initialize page to force head control initialization + page.InitRecursive(null); + string result = page.Render(string.Empty); + string expect = @"literal child"; + Assert.AreEqual(expect, result); + } + } +#endif + + [Test] + public void StyleBlockRendersTypeAttribute() + { + TestPage page = new TestPage(); + Head head = new Head(); + page.Controls.Add(head); + + page.Styles.Add("stylename", "stylevalue"); + + string result = page.Render(string.Empty); + string expect = @""; + Assert.AreEqual(expect, result); + } + + [Test] + public void StyleFileRendersTypeAttributeAndUsesCssRootPath() + { + const string STYLESHEETPATH = "filepath"; + + using (new TestWebContext("/", "testpage.aspx")) + { + TestPage page = new TestPage(HttpContext.Current); + Head head = new Head(); + page.Controls.Add(head); + + page.CssRoot = "testcssroot"; + page.StyleFiles.Add("filename", STYLESHEETPATH); + + string result = page.Render(string.Empty); + string expect = string.Format(@"", STYLESHEETPATH); + Assert.AreEqual(expect, result); + } + } + + [Test] + public void ScriptBlockRendersTypeAttribute() + { + TestPage page = new TestPage(); + Head head = new Head(); + page.Controls.Add(head); + + page.RegisterHeadScriptBlock( "scriptname", "scriptcode" ); + + string result = page.Render(string.Empty); + string expect = @""; + Assert.AreEqual(expect, result); + } + + [Test] + public void ScriptEventRendersTypeAttribute() + { + TestPage page = new TestPage(); + Head head = new Head(); + page.Controls.Add(head); + + page.RegisterHeadScriptEvent("scriptname", "elementname", "eventname", "scriptcode"); + + string result = page.Render(string.Empty); + string expect = @""; + Assert.AreEqual(expect, result); + } + + [Test] + public void ScriptFileRendersTypeAttributeAndUsesScriptsRootPath() + { + const string SCRIPTFILEPATH = "filepath"; + + using (new TestWebContext("/", "testpage.aspx")) + { + TestPage page = new TestPage(HttpContext.Current); + Head head = new Head(); + page.Controls.Add(head); + + page.ScriptsRoot = "testscriptroot"; + page.RegisterHeadScriptFile("filename", SCRIPTFILEPATH); + + string result = page.Render(string.Empty); + string expect = string.Format(@"", SCRIPTFILEPATH); + Assert.AreEqual(expect, result); + } + } + + [Test] + public void RenderChildrenFirst() + { + TestPage page = new TestPage(); + Head head = new Head(); + page.Controls.Add(head); + + head.Controls.Add(new LiteralControl("literal child")); + page.Styles.Add("stylename", "stylevalue"); + + string result = page.Render(string.Empty); + string expect = @"literal child"; + Assert.AreEqual( expect, result); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/UI/Controls/ValidationErrorTests.cs b/test/Spring/Spring.Web.Tests/Web/UI/Controls/ValidationErrorTests.cs new file mode 100644 index 00000000..c68393b0 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/UI/Controls/ValidationErrorTests.cs @@ -0,0 +1,118 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Web; +using System.Web.UI; +using System.Web.UI.HtmlControls; +using Spring.Util; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// This control allows for suppressing output of the 'action' attribute. + /// + /// + /// the 'action' attribute rendered by the default control causes troubles + /// in case of URL-rewriting. See e.g. 'thescripts.com' forum + /// and also JIRA SPRNET-560 for more info. + /// + /// Erich Eichinger + /// $Id: ValidationErrorTests.cs,v 1.1 2008/03/19 12:07:15 oakinger Exp $ + public class ValidationErrorTests : HtmlForm + { + private bool suppressAction = false; + private string action = null; + + /// + /// Sets or Gets a value indicating if the 'action' attribute shall be rendered. Defaults to 'false' + /// + /// + /// The following possibilites are available: + /// + /// If is 'true', rendering of the 'action' attribute is suppressed. + /// If is 'false' and is not set, + /// 'action' attribute will.be rendered to + /// + /// If is 'false' and is set, + /// 'action' attribute will.be rendered to + /// + /// + /// + public bool SuppressAction + { + get { return this.suppressAction; } + set { this.suppressAction = value; } + } + + /// + /// Sets or Gets an explicit url to be rendered + /// + /// + /// The url specified here is only rendered, if is true. + /// + public string Action + { + get { return this.action; } + set { this.action = value; } + } + + /// + /// Renders attributes but performs 'action' suppressing logic. + /// + /// + protected override void RenderAttributes(HtmlTextWriter writer) + { + base.RenderAttributes(new ActionSupressingHtmlTextWriter(writer)); + if (!this.suppressAction) + { + string url = (StringUtils.HasText(this.action)) ? this.action : Context.Request.RawUrl; + writer.WriteAttribute("action", url, true); + } + } + + #region Nested type: ActionSupressingHtmlTextWriter + + /// + /// This wrapper suppresses output of 'action' attributes. + /// + private class ActionSupressingHtmlTextWriter : HtmlTextWriter + { + public ActionSupressingHtmlTextWriter(HtmlTextWriter wrappedWriter) + : base(wrappedWriter.InnerWriter) + { + } + + public override void WriteAttribute(string name, string value, bool fEncode) + { + if (string.Compare(name, "action", true) != 0) + { + base.WriteAttribute(name, value, fEncode); + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/UI/Controls/ValidationErrorsTests.cs b/test/Spring/Spring.Web.Tests/Web/UI/Controls/ValidationErrorsTests.cs new file mode 100644 index 00000000..576b1dbc --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/UI/Controls/ValidationErrorsTests.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Web.UI.Validation; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: ValidationErrorsTests.cs,v 1.1 2008/03/19 12:07:15 oakinger Exp $ + [TestFixture] + public class ValidationErrorsTests + { + [Test] + public void DefaultsToSpanValidationErrorsRenderer() + { + ValidationError vs = new ValidationError(); + Assert.AreEqual(typeof(SpanValidationErrorsRenderer), vs.Renderer.GetType()); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/UI/Controls/ValidationSummaryTests.cs b/test/Spring/Spring.Web.Tests/Web/UI/Controls/ValidationSummaryTests.cs new file mode 100644 index 00000000..5e5172bc --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/UI/Controls/ValidationSummaryTests.cs @@ -0,0 +1,46 @@ +#region License + +/* + * Copyright © 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using NUnit.Framework; +using Spring.Web.UI.Validation; + +#endregion + +namespace Spring.Web.UI.Controls +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: ValidationSummaryTests.cs,v 1.1 2008/03/19 12:07:17 oakinger Exp $ + [TestFixture] + public class ValidationSummaryTests + { + [Test] + public void DefaultsToDivValidationErrorsRenderer() + { + ValidationSummary vs = new ValidationSummary(); + Assert.AreEqual(typeof (DivValidationErrorsRenderer), vs.Renderer.GetType()); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Web.Tests/Web/UI/PageTests.cs b/test/Spring/Spring.Web.Tests/Web/UI/PageTests.cs new file mode 100644 index 00000000..adb01c75 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/UI/PageTests.cs @@ -0,0 +1,122 @@ +#region License + +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Globalization; +using System.Threading; +using System.Web; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Globalization; +using Spring.Globalization.Resolvers; +using Spring.Objects; +using Spring.TestSupport; +using Spring.Validation; +using Spring.Web.Support; + +#endregion + +namespace Spring.Web.UI +{ + /// + /// Unit tests for the Page class. + /// + /// Goran Milosavljevic + /// Erich Eichinger + /// $Id: PageTests.cs,v 1.7 2008/03/15 11:19:39 oakinger Exp $ + [TestFixture] + public class PageTests : TestWebContextTests + { + [Test] + public void Validate() + { + Page page = new TestPage(HttpContext.Current); + IValidator[] validators = new IValidator[] {new RequiredValidator("Name", null), new ConditionValidator("Loan == 0", "Age > 21")}; + Contact contact = new Contact("Goran", 24, 0); + bool result = page.Validate(contact, validators); + Assert.IsTrue(result); + + contact = new Contact(null, 24, 0); + result = page.Validate(contact, validators); + Assert.IsFalse(result); + + contact = new Contact("Goran", 24, 1); + result = page.Validate(contact, validators); + Assert.IsFalse(result); + } + + [Test] + public void DefaultsToDefaultWebCultureResolver() + { + TestPage page = new TestPage(); + Assert.AreEqual( typeof(DefaultWebCultureResolver), page.CultureResolver.GetType() ); + } + + [Test] + public void AllowsNeutralUserCulture() + { + TestPage page = new TestPage(); + // DefaultWebCultureResolver does not allow culture to be set + page.CultureResolver = new DefaultCultureResolver(); + page.UserCulture = new CultureInfo("de"); + + page.InitializeCulture(); + Assert.AreEqual( page.UserCulture, Thread.CurrentThread.CurrentUICulture ); + Assert.AreEqual( CultureInfo.CreateSpecificCulture(page.UserCulture.Name), Thread.CurrentThread.CurrentCulture); + } + + [Test] + public void SetResultThrowsVerboseExceptionOnUnknownResultName() + { + string RESULTNAME = "nonexistant result"; + + TestPage page = new TestPage(); + try + { + page.SetResult(RESULTNAME); + Assert.Fail(); + } + catch(ArgumentException ae) + { + string expected = string.Format("No URL mapping found for the specified result '{0}'.", RESULTNAME); + string msg = ae.Message.Substring(0, expected.Length); + Assert.AreEqual(expected, msg); + } + } + + [Test] + public void SetResultSelectsCorrectResult() + { + MockRepository mocks = new MockRepository(); + TestPage page = new TestPage(); + + Result theResult = (Result) mocks.CreateMock(typeof (Result)); + theResult.Navigate(page); + mocks.ReplayAll(); + + page.Results.Add( "theResult", theResult ); + page.SetResult("theResult"); + mocks.VerifyAll(); + } + } +} diff --git a/test/Spring/Spring.Web.Tests/Web/UI/UserControlTests.cs b/test/Spring/Spring.Web.Tests/Web/UI/UserControlTests.cs new file mode 100644 index 00000000..c8750d97 --- /dev/null +++ b/test/Spring/Spring.Web.Tests/Web/UI/UserControlTests.cs @@ -0,0 +1,111 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Web; +using System.Web.UI; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.TestSupport; +using Spring.Validation; +using Spring.Web.Support; + +#endregion + +namespace Spring.Web.UI +{ + /// + /// + /// + /// Erich Eichinger + /// $Id: UserControlTests.cs,v 1.2 2008/03/15 11:19:39 oakinger Exp $ + [TestFixture] + public class UserControlTests : TestWebContextTests + { + public class TestUserControl : UserControl + { + public TestUserControl() + { + } + + public TestUserControl(Control parent) + { + parent.Controls.Add(this); + } + + public new void SetResult(string name) + { + base.SetResult(name); + } + } + + [Test] + public void SetResultSelectsCorrectResult() + { + MockRepository mocks = new MockRepository(); + TestUserControl uc = new TestUserControl(); + Result theResult = (Result)mocks.CreateMock(typeof(Result)); + + using (mocks.Ordered()) + { + theResult.Navigate(uc); + } + mocks.ReplayAll(); + + uc.Results.Add("theResult", theResult); + uc.SetResult("theResult"); + mocks.VerifyAll(); + } + + [Test] + public void SetResultBubblesUpHierarchyUntilFirstMatch() + { + MockRepository mocks = new MockRepository(); + TestUserControl c1 = new TestUserControl(); + Control c11 = new Control(); c1.Controls.Add(c11); + TestUserControl c111 = new TestUserControl(c11); + Result theResult = (Result)mocks.CreateMock(typeof(Result)); + + using (mocks.Ordered()) + { + theResult.Navigate(c1); + } + mocks.ReplayAll(); + + c1.Results.Add("theResult", theResult); + c111.SetResult("theResult"); + mocks.VerifyAll(); + } + + [Test] + public void ValidateSetsDefaultVariables() + { + TestPage page = new TestPage(HttpContext.Current); + TestUserControl c1 = new TestUserControl(); + page.Controls.Add(c1); + + ConditionValidator v1 = new ConditionValidator("#page == #this.Page", null); + ConditionValidator v2 = new ConditionValidator("#usercontrol == #this", null); + + Assert.IsTrue(c1.Validate(c1, v1, v2)); + } + } +} \ No newline at end of file